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