llc_station.c revision f83f1768f833cb45bc93429fdc552252a4f55ac3
1/* 2 * llc_station.c - station component of LLC 3 * 4 * Copyright (c) 1997 by Procom Technology, Inc. 5 * 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br> 6 * 7 * This program can be redistributed or modified under the terms of the 8 * GNU General Public License as published by the Free Software Foundation. 9 * This program is distributed without any warranty or implied warranty 10 * of merchantability or fitness for a particular purpose. 11 * 12 * See the GNU General Public License for more details. 13 */ 14#include <linux/init.h> 15#include <linux/module.h> 16#include <net/llc.h> 17#include <net/llc_sap.h> 18#include <net/llc_conn.h> 19#include <net/llc_c_ac.h> 20#include <net/llc_s_ac.h> 21#include <net/llc_c_ev.h> 22#include <net/llc_c_st.h> 23#include <net/llc_s_ev.h> 24#include <net/llc_s_st.h> 25#include <net/llc_pdu.h> 26 27/** 28 * struct llc_station - LLC station component 29 * 30 * SAP and connection resource manager, one per adapter. 31 * 32 * @state - state of station 33 * @xid_r_count - XID response PDU counter 34 * @mac_sa - MAC source address 35 * @sap_list - list of related SAPs 36 * @ev_q - events entering state mach. 37 * @mac_pdu_q - PDUs ready to send to MAC 38 */ 39struct llc_station { 40 u8 state; 41 u8 xid_r_count; 42 struct timer_list ack_timer; 43 u8 retry_count; 44 u8 maximum_retry; 45 struct { 46 struct sk_buff_head list; 47 spinlock_t lock; 48 } ev_q; 49 struct sk_buff_head mac_pdu_q; 50}; 51 52#define LLC_STATION_ACK_TIME (3 * HZ) 53 54int sysctl_llc_station_ack_timeout = LLC_STATION_ACK_TIME; 55 56/* Types of events (possible values in 'ev->type') */ 57#define LLC_STATION_EV_TYPE_SIMPLE 1 58#define LLC_STATION_EV_TYPE_CONDITION 2 59#define LLC_STATION_EV_TYPE_PRIM 3 60#define LLC_STATION_EV_TYPE_PDU 4 /* command/response PDU */ 61#define LLC_STATION_EV_TYPE_ACK_TMR 5 62#define LLC_STATION_EV_TYPE_RPT_STATUS 6 63 64/* Events */ 65#define LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK 1 66#define LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK 2 67#define LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY 3 68#define LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY 4 69#define LLC_STATION_EV_RX_NULL_DSAP_XID_C 5 70#define LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ 6 71#define LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ 7 72#define LLC_STATION_EV_RX_NULL_DSAP_TEST_C 8 73#define LLC_STATION_EV_DISABLE_REQ 9 74 75struct llc_station_state_ev { 76 u8 type; 77 u8 prim; 78 u8 prim_type; 79 u8 reason; 80 struct list_head node; /* node in station->ev_q.list */ 81}; 82 83static __inline__ struct llc_station_state_ev * 84 llc_station_ev(struct sk_buff *skb) 85{ 86 return (struct llc_station_state_ev *)skb->cb; 87} 88 89typedef int (*llc_station_ev_t)(struct sk_buff *skb); 90 91#define LLC_STATION_STATE_DOWN 1 /* initial state */ 92#define LLC_STATION_STATE_DUP_ADDR_CHK 2 93#define LLC_STATION_STATE_UP 3 94 95#define LLC_NBR_STATION_STATES 3 /* size of state table */ 96 97typedef int (*llc_station_action_t)(struct sk_buff *skb); 98 99/* Station component state table structure */ 100struct llc_station_state_trans { 101 llc_station_ev_t ev; 102 u8 next_state; 103 llc_station_action_t *ev_actions; 104}; 105 106struct llc_station_state { 107 u8 curr_state; 108 struct llc_station_state_trans **transitions; 109}; 110 111static struct llc_station llc_main_station; 112 113static int llc_stat_ev_enable_with_dup_addr_check(struct sk_buff *skb) 114{ 115 struct llc_station_state_ev *ev = llc_station_ev(skb); 116 117 return ev->type == LLC_STATION_EV_TYPE_SIMPLE && 118 ev->prim_type == 119 LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK ? 0 : 1; 120} 121 122static int llc_stat_ev_enable_without_dup_addr_check(struct sk_buff *skb) 123{ 124 struct llc_station_state_ev *ev = llc_station_ev(skb); 125 126 return ev->type == LLC_STATION_EV_TYPE_SIMPLE && 127 ev->prim_type == 128 LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK ? 0 : 1; 129} 130 131static int llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry(struct sk_buff *skb) 132{ 133 struct llc_station_state_ev *ev = llc_station_ev(skb); 134 135 return ev->type == LLC_STATION_EV_TYPE_ACK_TMR && 136 llc_main_station.retry_count < 137 llc_main_station.maximum_retry ? 0 : 1; 138} 139 140static int llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry(struct sk_buff *skb) 141{ 142 struct llc_station_state_ev *ev = llc_station_ev(skb); 143 144 return ev->type == LLC_STATION_EV_TYPE_ACK_TMR && 145 llc_main_station.retry_count == 146 llc_main_station.maximum_retry ? 0 : 1; 147} 148 149static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb) 150{ 151 struct llc_station_state_ev *ev = llc_station_ev(skb); 152 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 153 154 return ev->type == LLC_STATION_EV_TYPE_PDU && 155 LLC_PDU_IS_CMD(pdu) && /* command PDU */ 156 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 157 LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID && 158 !pdu->dsap ? 0 : 1; /* NULL DSAP value */ 159} 160 161static int llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq(struct sk_buff *skb) 162{ 163 struct llc_station_state_ev *ev = llc_station_ev(skb); 164 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 165 166 return ev->type == LLC_STATION_EV_TYPE_PDU && 167 LLC_PDU_IS_RSP(pdu) && /* response PDU */ 168 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 169 LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID && 170 !pdu->dsap && /* NULL DSAP value */ 171 !llc_main_station.xid_r_count ? 0 : 1; 172} 173 174static int llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq(struct sk_buff *skb) 175{ 176 struct llc_station_state_ev *ev = llc_station_ev(skb); 177 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 178 179 return ev->type == LLC_STATION_EV_TYPE_PDU && 180 LLC_PDU_IS_RSP(pdu) && /* response PDU */ 181 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 182 LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID && 183 !pdu->dsap && /* NULL DSAP value */ 184 llc_main_station.xid_r_count == 1 ? 0 : 1; 185} 186 187static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb) 188{ 189 struct llc_station_state_ev *ev = llc_station_ev(skb); 190 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 191 192 return ev->type == LLC_STATION_EV_TYPE_PDU && 193 LLC_PDU_IS_CMD(pdu) && /* command PDU */ 194 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 195 LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST && 196 !pdu->dsap ? 0 : 1; /* NULL DSAP */ 197} 198 199static int llc_stat_ev_disable_req(struct sk_buff *skb) 200{ 201 struct llc_station_state_ev *ev = llc_station_ev(skb); 202 203 return ev->type == LLC_STATION_EV_TYPE_PRIM && 204 ev->prim == LLC_DISABLE_PRIM && 205 ev->prim_type == LLC_PRIM_TYPE_REQ ? 0 : 1; 206} 207 208/** 209 * llc_station_send_pdu - queues PDU to send 210 * @skb: Address of the PDU 211 * 212 * Queues a PDU to send to the MAC layer. 213 */ 214static void llc_station_send_pdu(struct sk_buff *skb) 215{ 216 skb_queue_tail(&llc_main_station.mac_pdu_q, skb); 217 while ((skb = skb_dequeue(&llc_main_station.mac_pdu_q)) != NULL) 218 if (dev_queue_xmit(skb)) 219 break; 220} 221 222static int llc_station_ac_start_ack_timer(struct sk_buff *skb) 223{ 224 mod_timer(&llc_main_station.ack_timer, 225 jiffies + sysctl_llc_station_ack_timeout); 226 return 0; 227} 228 229static int llc_station_ac_set_retry_cnt_0(struct sk_buff *skb) 230{ 231 llc_main_station.retry_count = 0; 232 return 0; 233} 234 235static int llc_station_ac_inc_retry_cnt_by_1(struct sk_buff *skb) 236{ 237 llc_main_station.retry_count++; 238 return 0; 239} 240 241static int llc_station_ac_set_xid_r_cnt_0(struct sk_buff *skb) 242{ 243 llc_main_station.xid_r_count = 0; 244 return 0; 245} 246 247static int llc_station_ac_inc_xid_r_cnt_by_1(struct sk_buff *skb) 248{ 249 llc_main_station.xid_r_count++; 250 return 0; 251} 252 253static int llc_station_ac_send_null_dsap_xid_c(struct sk_buff *skb) 254{ 255 int rc = 1; 256 struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, 257 sizeof(struct llc_xid_info)); 258 259 if (!nskb) 260 goto out; 261 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, 0, LLC_PDU_CMD); 262 llc_pdu_init_as_xid_cmd(nskb, LLC_XID_NULL_CLASS_2, 127); 263 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, skb->dev->dev_addr); 264 if (unlikely(rc)) 265 goto free; 266 llc_station_send_pdu(nskb); 267out: 268 return rc; 269free: 270 kfree_skb(skb); 271 goto out; 272} 273 274static int llc_station_ac_send_xid_r(struct sk_buff *skb) 275{ 276 u8 mac_da[ETH_ALEN], dsap; 277 int rc = 1; 278 struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, 279 sizeof(struct llc_xid_info)); 280 281 if (!nskb) 282 goto out; 283 rc = 0; 284 llc_pdu_decode_sa(skb, mac_da); 285 llc_pdu_decode_ssap(skb, &dsap); 286 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 287 llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127); 288 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da); 289 if (unlikely(rc)) 290 goto free; 291 llc_station_send_pdu(nskb); 292out: 293 return rc; 294free: 295 kfree_skb(skb); 296 goto out; 297} 298 299static int llc_station_ac_send_test_r(struct sk_buff *skb) 300{ 301 u8 mac_da[ETH_ALEN], dsap; 302 int rc = 1; 303 u32 data_size; 304 struct sk_buff *nskb; 305 306 /* The test request command is type U (llc_len = 3) */ 307 data_size = ntohs(eth_hdr(skb)->h_proto) - 3; 308 nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, data_size); 309 310 if (!nskb) 311 goto out; 312 rc = 0; 313 llc_pdu_decode_sa(skb, mac_da); 314 llc_pdu_decode_ssap(skb, &dsap); 315 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 316 llc_pdu_init_as_test_rsp(nskb, skb); 317 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da); 318 if (unlikely(rc)) 319 goto free; 320 llc_station_send_pdu(nskb); 321out: 322 return rc; 323free: 324 kfree_skb(skb); 325 goto out; 326} 327 328static int llc_station_ac_report_status(struct sk_buff *skb) 329{ 330 return 0; 331} 332 333/* COMMON STATION STATE transitions */ 334 335/* dummy last-transition indicator; common to all state transition groups 336 * last entry for this state 337 * all members are zeros, .bss zeroes it 338 */ 339static struct llc_station_state_trans llc_stat_state_trans_end; 340 341/* DOWN STATE transitions */ 342 343/* state transition for LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK event */ 344static llc_station_action_t llc_stat_down_state_actions_1[] = { 345 [0] = llc_station_ac_start_ack_timer, 346 [1] = llc_station_ac_set_retry_cnt_0, 347 [2] = llc_station_ac_set_xid_r_cnt_0, 348 [3] = llc_station_ac_send_null_dsap_xid_c, 349 [4] = NULL, 350}; 351 352static struct llc_station_state_trans llc_stat_down_state_trans_1 = { 353 .ev = llc_stat_ev_enable_with_dup_addr_check, 354 .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 355 .ev_actions = llc_stat_down_state_actions_1, 356}; 357 358/* state transition for LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK event */ 359static llc_station_action_t llc_stat_down_state_actions_2[] = { 360 [0] = llc_station_ac_report_status, /* STATION UP */ 361 [1] = NULL, 362}; 363 364static struct llc_station_state_trans llc_stat_down_state_trans_2 = { 365 .ev = llc_stat_ev_enable_without_dup_addr_check, 366 .next_state = LLC_STATION_STATE_UP, 367 .ev_actions = llc_stat_down_state_actions_2, 368}; 369 370/* array of pointers; one to each transition */ 371static struct llc_station_state_trans *llc_stat_dwn_state_trans[] = { 372 [0] = &llc_stat_down_state_trans_1, 373 [1] = &llc_stat_down_state_trans_2, 374 [2] = &llc_stat_state_trans_end, 375}; 376 377/* UP STATE transitions */ 378/* state transition for LLC_STATION_EV_DISABLE_REQ event */ 379static llc_station_action_t llc_stat_up_state_actions_1[] = { 380 [0] = llc_station_ac_report_status, /* STATION DOWN */ 381 [1] = NULL, 382}; 383 384static struct llc_station_state_trans llc_stat_up_state_trans_1 = { 385 .ev = llc_stat_ev_disable_req, 386 .next_state = LLC_STATION_STATE_DOWN, 387 .ev_actions = llc_stat_up_state_actions_1, 388}; 389 390/* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */ 391static llc_station_action_t llc_stat_up_state_actions_2[] = { 392 [0] = llc_station_ac_send_xid_r, 393 [1] = NULL, 394}; 395 396static struct llc_station_state_trans llc_stat_up_state_trans_2 = { 397 .ev = llc_stat_ev_rx_null_dsap_xid_c, 398 .next_state = LLC_STATION_STATE_UP, 399 .ev_actions = llc_stat_up_state_actions_2, 400}; 401 402/* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */ 403static llc_station_action_t llc_stat_up_state_actions_3[] = { 404 [0] = llc_station_ac_send_test_r, 405 [1] = NULL, 406}; 407 408static struct llc_station_state_trans llc_stat_up_state_trans_3 = { 409 .ev = llc_stat_ev_rx_null_dsap_test_c, 410 .next_state = LLC_STATION_STATE_UP, 411 .ev_actions = llc_stat_up_state_actions_3, 412}; 413 414/* array of pointers; one to each transition */ 415static struct llc_station_state_trans *llc_stat_up_state_trans [] = { 416 [0] = &llc_stat_up_state_trans_1, 417 [1] = &llc_stat_up_state_trans_2, 418 [2] = &llc_stat_up_state_trans_3, 419 [3] = &llc_stat_state_trans_end, 420}; 421 422/* DUP ADDR CHK STATE transitions */ 423/* state transition for LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ 424 * event 425 */ 426static llc_station_action_t llc_stat_dupaddr_state_actions_1[] = { 427 [0] = llc_station_ac_inc_xid_r_cnt_by_1, 428 [1] = NULL, 429}; 430 431static struct llc_station_state_trans llc_stat_dupaddr_state_trans_1 = { 432 .ev = llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq, 433 .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 434 .ev_actions = llc_stat_dupaddr_state_actions_1, 435}; 436 437/* state transition for LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ 438 * event 439 */ 440static llc_station_action_t llc_stat_dupaddr_state_actions_2[] = { 441 [0] = llc_station_ac_report_status, /* DUPLICATE ADDRESS FOUND */ 442 [1] = NULL, 443}; 444 445static struct llc_station_state_trans llc_stat_dupaddr_state_trans_2 = { 446 .ev = llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq, 447 .next_state = LLC_STATION_STATE_DOWN, 448 .ev_actions = llc_stat_dupaddr_state_actions_2, 449}; 450 451/* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */ 452static llc_station_action_t llc_stat_dupaddr_state_actions_3[] = { 453 [0] = llc_station_ac_send_xid_r, 454 [1] = NULL, 455}; 456 457static struct llc_station_state_trans llc_stat_dupaddr_state_trans_3 = { 458 .ev = llc_stat_ev_rx_null_dsap_xid_c, 459 .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 460 .ev_actions = llc_stat_dupaddr_state_actions_3, 461}; 462 463/* state transition for LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY 464 * event 465 */ 466static llc_station_action_t llc_stat_dupaddr_state_actions_4[] = { 467 [0] = llc_station_ac_start_ack_timer, 468 [1] = llc_station_ac_inc_retry_cnt_by_1, 469 [2] = llc_station_ac_set_xid_r_cnt_0, 470 [3] = llc_station_ac_send_null_dsap_xid_c, 471 [4] = NULL, 472}; 473 474static struct llc_station_state_trans llc_stat_dupaddr_state_trans_4 = { 475 .ev = llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry, 476 .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 477 .ev_actions = llc_stat_dupaddr_state_actions_4, 478}; 479 480/* state transition for LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY 481 * event 482 */ 483static llc_station_action_t llc_stat_dupaddr_state_actions_5[] = { 484 [0] = llc_station_ac_report_status, /* STATION UP */ 485 [1] = NULL, 486}; 487 488static struct llc_station_state_trans llc_stat_dupaddr_state_trans_5 = { 489 .ev = llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry, 490 .next_state = LLC_STATION_STATE_UP, 491 .ev_actions = llc_stat_dupaddr_state_actions_5, 492}; 493 494/* state transition for LLC_STATION_EV_DISABLE_REQ event */ 495static llc_station_action_t llc_stat_dupaddr_state_actions_6[] = { 496 [0] = llc_station_ac_report_status, /* STATION DOWN */ 497 [1] = NULL, 498}; 499 500static struct llc_station_state_trans llc_stat_dupaddr_state_trans_6 = { 501 .ev = llc_stat_ev_disable_req, 502 .next_state = LLC_STATION_STATE_DOWN, 503 .ev_actions = llc_stat_dupaddr_state_actions_6, 504}; 505 506/* array of pointers; one to each transition */ 507static struct llc_station_state_trans *llc_stat_dupaddr_state_trans[] = { 508 [0] = &llc_stat_dupaddr_state_trans_6, /* Request */ 509 [1] = &llc_stat_dupaddr_state_trans_4, /* Timer */ 510 [2] = &llc_stat_dupaddr_state_trans_5, 511 [3] = &llc_stat_dupaddr_state_trans_1, /* Receive frame */ 512 [4] = &llc_stat_dupaddr_state_trans_2, 513 [5] = &llc_stat_dupaddr_state_trans_3, 514 [6] = &llc_stat_state_trans_end, 515}; 516 517static struct llc_station_state 518 llc_station_state_table[LLC_NBR_STATION_STATES] = { 519 [LLC_STATION_STATE_DOWN - 1] = { 520 .curr_state = LLC_STATION_STATE_DOWN, 521 .transitions = llc_stat_dwn_state_trans, 522 }, 523 [LLC_STATION_STATE_DUP_ADDR_CHK - 1] = { 524 .curr_state = LLC_STATION_STATE_DUP_ADDR_CHK, 525 .transitions = llc_stat_dupaddr_state_trans, 526 }, 527 [LLC_STATION_STATE_UP - 1] = { 528 .curr_state = LLC_STATION_STATE_UP, 529 .transitions = llc_stat_up_state_trans, 530 }, 531}; 532 533/** 534 * llc_exec_station_trans_actions - executes actions for transition 535 * @trans: Address of the transition 536 * @skb: Address of the event that caused the transition 537 * 538 * Executes actions of a transition of the station state machine. Returns 539 * 0 if all actions complete successfully, nonzero otherwise. 540 */ 541static u16 llc_exec_station_trans_actions(struct llc_station_state_trans *trans, 542 struct sk_buff *skb) 543{ 544 u16 rc = 0; 545 llc_station_action_t *next_action = trans->ev_actions; 546 547 for (; next_action && *next_action; next_action++) 548 if ((*next_action)(skb)) 549 rc = 1; 550 return rc; 551} 552 553/** 554 * llc_find_station_trans - finds transition for this event 555 * @skb: Address of the event 556 * 557 * Search thru events of the current state of the station until list 558 * exhausted or it's obvious that the event is not valid for the current 559 * state. Returns the address of the transition if cound, %NULL otherwise. 560 */ 561static struct llc_station_state_trans * 562 llc_find_station_trans(struct sk_buff *skb) 563{ 564 int i = 0; 565 struct llc_station_state_trans *rc = NULL; 566 struct llc_station_state_trans **next_trans; 567 struct llc_station_state *curr_state = 568 &llc_station_state_table[llc_main_station.state - 1]; 569 570 for (next_trans = curr_state->transitions; next_trans[i]->ev; i++) 571 if (!next_trans[i]->ev(skb)) { 572 rc = next_trans[i]; 573 break; 574 } 575 return rc; 576} 577 578/** 579 * llc_station_free_ev - frees an event 580 * @skb: Address of the event 581 * 582 * Frees an event. 583 */ 584static void llc_station_free_ev(struct sk_buff *skb) 585{ 586 struct llc_station_state_ev *ev = llc_station_ev(skb); 587 588 if (ev->type == LLC_STATION_EV_TYPE_PDU) 589 kfree_skb(skb); 590} 591 592/** 593 * llc_station_next_state - processes event and goes to the next state 594 * @skb: Address of the event 595 * 596 * Processes an event, executes any transitions related to that event and 597 * updates the state of the station. 598 */ 599static u16 llc_station_next_state(struct sk_buff *skb) 600{ 601 u16 rc = 1; 602 struct llc_station_state_trans *trans; 603 604 if (llc_main_station.state > LLC_NBR_STATION_STATES) 605 goto out; 606 trans = llc_find_station_trans(skb); 607 if (trans) { 608 /* got the state to which we next transition; perform the 609 * actions associated with this transition before actually 610 * transitioning to the next state 611 */ 612 rc = llc_exec_station_trans_actions(trans, skb); 613 if (!rc) 614 /* transition station to next state if all actions 615 * execute successfully; done; wait for next event 616 */ 617 llc_main_station.state = trans->next_state; 618 } else 619 /* event not recognized in current state; re-queue it for 620 * processing again at a later time; return failure 621 */ 622 rc = 0; 623out: 624 llc_station_free_ev(skb); 625 return rc; 626} 627 628/** 629 * llc_station_service_events - service events in the queue 630 * 631 * Get an event from the station event queue (if any); attempt to service 632 * the event; if event serviced, get the next event (if any) on the event 633 * queue; if event not service, re-queue the event on the event queue and 634 * attempt to service the next event; when serviced all events in queue, 635 * finished; if don't transition to different state, just service all 636 * events once; if transition to new state, service all events again. 637 * Caller must hold llc_main_station.ev_q.lock. 638 */ 639static void llc_station_service_events(void) 640{ 641 struct sk_buff *skb; 642 643 while ((skb = skb_dequeue(&llc_main_station.ev_q.list)) != NULL) 644 llc_station_next_state(skb); 645} 646 647/** 648 * llc_station_state_process: queue event and try to process queue. 649 * @skb: Address of the event 650 * 651 * Queues an event (on the station event queue) for handling by the 652 * station state machine and attempts to process any queued-up events. 653 */ 654static void llc_station_state_process(struct sk_buff *skb) 655{ 656 spin_lock_bh(&llc_main_station.ev_q.lock); 657 skb_queue_tail(&llc_main_station.ev_q.list, skb); 658 llc_station_service_events(); 659 spin_unlock_bh(&llc_main_station.ev_q.lock); 660} 661 662static void llc_station_ack_tmr_cb(unsigned long timeout_data) 663{ 664 struct sk_buff *skb = alloc_skb(0, GFP_ATOMIC); 665 666 if (skb) { 667 struct llc_station_state_ev *ev = llc_station_ev(skb); 668 669 ev->type = LLC_STATION_EV_TYPE_ACK_TMR; 670 llc_station_state_process(skb); 671 } 672} 673 674/* 675 * llc_station_rcv - send received pdu to the station state machine 676 * @skb: received frame. 677 * 678 * Sends data unit to station state machine. 679 */ 680static void llc_station_rcv(struct sk_buff *skb) 681{ 682 struct llc_station_state_ev *ev = llc_station_ev(skb); 683 684 ev->type = LLC_STATION_EV_TYPE_PDU; 685 ev->reason = 0; 686 llc_station_state_process(skb); 687} 688 689int __init llc_station_init(void) 690{ 691 u16 rc = -ENOBUFS; 692 struct sk_buff *skb; 693 struct llc_station_state_ev *ev; 694 695 skb_queue_head_init(&llc_main_station.mac_pdu_q); 696 skb_queue_head_init(&llc_main_station.ev_q.list); 697 spin_lock_init(&llc_main_station.ev_q.lock); 698 setup_timer(&llc_main_station.ack_timer, llc_station_ack_tmr_cb, 699 (unsigned long)&llc_main_station); 700 llc_main_station.ack_timer.expires = jiffies + 701 sysctl_llc_station_ack_timeout; 702 skb = alloc_skb(0, GFP_ATOMIC); 703 if (!skb) 704 goto out; 705 rc = 0; 706 llc_set_station_handler(llc_station_rcv); 707 ev = llc_station_ev(skb); 708 memset(ev, 0, sizeof(*ev)); 709 llc_main_station.maximum_retry = 1; 710 llc_main_station.state = LLC_STATION_STATE_DOWN; 711 ev->type = LLC_STATION_EV_TYPE_SIMPLE; 712 ev->prim_type = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK; 713 rc = llc_station_next_state(skb); 714out: 715 return rc; 716} 717 718void __exit llc_station_exit(void) 719{ 720 llc_set_station_handler(NULL); 721} 722