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 */ 39 struct 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 54 int 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 75 struct 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 83 static __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 89 typedef 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 97 typedef int (*llc_station_action_t)(struct sk_buff *skb); 98 99 /* Station component state table structure */ 100 struct llc_station_state_trans { 101 llc_station_ev_t ev; 102 u8 next_state; 103 llc_station_action_t *ev_actions; 104 }; 105 106 struct llc_station_state { 107 u8 curr_state; 108 struct llc_station_state_trans **transitions; 109 }; 110 111 static struct llc_station llc_main_station; 112 113 static 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 122 static 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 131 static 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 140 static 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 149 static 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 161 static 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 174 static 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 187 static 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 199 static 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 */ 214 static 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 222 static 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 229 static 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 235 static 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 241 static 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 247 static 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 253 static 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); 257 258 if (!nskb) 259 goto out; 260 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, 0, LLC_PDU_CMD); 261 llc_pdu_init_as_xid_cmd(nskb, LLC_XID_NULL_CLASS_2, 127); 262 rc = llc_mac_hdr_init(nskb, llc_station_mac_sa, llc_station_mac_sa); 263 if (unlikely(rc)) 264 goto free; 265 llc_station_send_pdu(nskb); 266 out: 267 return rc; 268 free: 269 kfree_skb(skb); 270 goto out; 271 } 272 273 static int llc_station_ac_send_xid_r(struct sk_buff *skb) 274 { 275 u8 mac_da[ETH_ALEN], dsap; 276 int rc = 1; 277 struct sk_buff* nskb = llc_alloc_frame(NULL, skb->dev); 278 279 if (!nskb) 280 goto out; 281 rc = 0; 282 llc_pdu_decode_sa(skb, mac_da); 283 llc_pdu_decode_ssap(skb, &dsap); 284 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 285 llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127); 286 rc = llc_mac_hdr_init(nskb, llc_station_mac_sa, mac_da); 287 if (unlikely(rc)) 288 goto free; 289 llc_station_send_pdu(nskb); 290 out: 291 return rc; 292 free: 293 kfree_skb(skb); 294 goto out; 295 } 296 297 static int llc_station_ac_send_test_r(struct sk_buff *skb) 298 { 299 u8 mac_da[ETH_ALEN], dsap; 300 int rc = 1; 301 struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev); 302 303 if (!nskb) 304 goto out; 305 rc = 0; 306 llc_pdu_decode_sa(skb, mac_da); 307 llc_pdu_decode_ssap(skb, &dsap); 308 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 309 llc_pdu_init_as_test_rsp(nskb, skb); 310 rc = llc_mac_hdr_init(nskb, llc_station_mac_sa, mac_da); 311 if (unlikely(rc)) 312 goto free; 313 llc_station_send_pdu(nskb); 314 out: 315 return rc; 316 free: 317 kfree_skb(skb); 318 goto out; 319 } 320 321 static int llc_station_ac_report_status(struct sk_buff *skb) 322 { 323 return 0; 324 } 325 326 /* COMMON STATION STATE transitions */ 327 328 /* dummy last-transition indicator; common to all state transition groups 329 * last entry for this state 330 * all members are zeros, .bss zeroes it 331 */ 332 static struct llc_station_state_trans llc_stat_state_trans_end; 333 334 /* DOWN STATE transitions */ 335 336 /* state transition for LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK event */ 337 static llc_station_action_t llc_stat_down_state_actions_1[] = { 338 [0] = llc_station_ac_start_ack_timer, 339 [1] = llc_station_ac_set_retry_cnt_0, 340 [2] = llc_station_ac_set_xid_r_cnt_0, 341 [3] = llc_station_ac_send_null_dsap_xid_c, 342 [4] = NULL, 343 }; 344 345 static struct llc_station_state_trans llc_stat_down_state_trans_1 = { 346 .ev = llc_stat_ev_enable_with_dup_addr_check, 347 .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 348 .ev_actions = llc_stat_down_state_actions_1, 349 }; 350 351 /* state transition for LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK event */ 352 static llc_station_action_t llc_stat_down_state_actions_2[] = { 353 [0] = llc_station_ac_report_status, /* STATION UP */ 354 [1] = NULL, 355 }; 356 357 static struct llc_station_state_trans llc_stat_down_state_trans_2 = { 358 .ev = llc_stat_ev_enable_without_dup_addr_check, 359 .next_state = LLC_STATION_STATE_UP, 360 .ev_actions = llc_stat_down_state_actions_2, 361 }; 362 363 /* array of pointers; one to each transition */ 364 static struct llc_station_state_trans *llc_stat_dwn_state_trans[] = { 365 [0] = &llc_stat_down_state_trans_1, 366 [1] = &llc_stat_down_state_trans_2, 367 [2] = &llc_stat_state_trans_end, 368 }; 369 370 /* UP STATE transitions */ 371 /* state transition for LLC_STATION_EV_DISABLE_REQ event */ 372 static llc_station_action_t llc_stat_up_state_actions_1[] = { 373 [0] = llc_station_ac_report_status, /* STATION DOWN */ 374 [1] = NULL, 375 }; 376 377 static struct llc_station_state_trans llc_stat_up_state_trans_1 = { 378 .ev = llc_stat_ev_disable_req, 379 .next_state = LLC_STATION_STATE_DOWN, 380 .ev_actions = llc_stat_up_state_actions_1, 381 }; 382 383 /* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */ 384 static llc_station_action_t llc_stat_up_state_actions_2[] = { 385 [0] = llc_station_ac_send_xid_r, 386 [1] = NULL, 387 }; 388 389 static struct llc_station_state_trans llc_stat_up_state_trans_2 = { 390 .ev = llc_stat_ev_rx_null_dsap_xid_c, 391 .next_state = LLC_STATION_STATE_UP, 392 .ev_actions = llc_stat_up_state_actions_2, 393 }; 394 395 /* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */ 396 static llc_station_action_t llc_stat_up_state_actions_3[] = { 397 [0] = llc_station_ac_send_test_r, 398 [1] = NULL, 399 }; 400 401 static struct llc_station_state_trans llc_stat_up_state_trans_3 = { 402 .ev = llc_stat_ev_rx_null_dsap_test_c, 403 .next_state = LLC_STATION_STATE_UP, 404 .ev_actions = llc_stat_up_state_actions_3, 405 }; 406 407 /* array of pointers; one to each transition */ 408 static struct llc_station_state_trans *llc_stat_up_state_trans [] = { 409 [0] = &llc_stat_up_state_trans_1, 410 [1] = &llc_stat_up_state_trans_2, 411 [2] = &llc_stat_up_state_trans_3, 412 [3] = &llc_stat_state_trans_end, 413 }; 414 415 /* DUP ADDR CHK STATE transitions */ 416 /* state transition for LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ 417 * event 418 */ 419 static llc_station_action_t llc_stat_dupaddr_state_actions_1[] = { 420 [0] = llc_station_ac_inc_xid_r_cnt_by_1, 421 [1] = NULL, 422 }; 423 424 static struct llc_station_state_trans llc_stat_dupaddr_state_trans_1 = { 425 .ev = llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq, 426 .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 427 .ev_actions = llc_stat_dupaddr_state_actions_1, 428 }; 429 430 /* state transition for LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ 431 * event 432 */ 433 static llc_station_action_t llc_stat_dupaddr_state_actions_2[] = { 434 [0] = llc_station_ac_report_status, /* DUPLICATE ADDRESS FOUND */ 435 [1] = NULL, 436 }; 437 438 static struct llc_station_state_trans llc_stat_dupaddr_state_trans_2 = { 439 .ev = llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq, 440 .next_state = LLC_STATION_STATE_DOWN, 441 .ev_actions = llc_stat_dupaddr_state_actions_2, 442 }; 443 444 /* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */ 445 static llc_station_action_t llc_stat_dupaddr_state_actions_3[] = { 446 [0] = llc_station_ac_send_xid_r, 447 [1] = NULL, 448 }; 449 450 static struct llc_station_state_trans llc_stat_dupaddr_state_trans_3 = { 451 .ev = llc_stat_ev_rx_null_dsap_xid_c, 452 .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 453 .ev_actions = llc_stat_dupaddr_state_actions_3, 454 }; 455 456 /* state transition for LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY 457 * event 458 */ 459 static llc_station_action_t llc_stat_dupaddr_state_actions_4[] = { 460 [0] = llc_station_ac_start_ack_timer, 461 [1] = llc_station_ac_inc_retry_cnt_by_1, 462 [2] = llc_station_ac_set_xid_r_cnt_0, 463 [3] = llc_station_ac_send_null_dsap_xid_c, 464 [4] = NULL, 465 }; 466 467 static struct llc_station_state_trans llc_stat_dupaddr_state_trans_4 = { 468 .ev = llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry, 469 .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 470 .ev_actions = llc_stat_dupaddr_state_actions_4, 471 }; 472 473 /* state transition for LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY 474 * event 475 */ 476 static llc_station_action_t llc_stat_dupaddr_state_actions_5[] = { 477 [0] = llc_station_ac_report_status, /* STATION UP */ 478 [1] = NULL, 479 }; 480 481 static struct llc_station_state_trans llc_stat_dupaddr_state_trans_5 = { 482 .ev = llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry, 483 .next_state = LLC_STATION_STATE_UP, 484 .ev_actions = llc_stat_dupaddr_state_actions_5, 485 }; 486 487 /* state transition for LLC_STATION_EV_DISABLE_REQ event */ 488 static llc_station_action_t llc_stat_dupaddr_state_actions_6[] = { 489 [0] = llc_station_ac_report_status, /* STATION DOWN */ 490 [1] = NULL, 491 }; 492 493 static struct llc_station_state_trans llc_stat_dupaddr_state_trans_6 = { 494 .ev = llc_stat_ev_disable_req, 495 .next_state = LLC_STATION_STATE_DOWN, 496 .ev_actions = llc_stat_dupaddr_state_actions_6, 497 }; 498 499 /* array of pointers; one to each transition */ 500 static struct llc_station_state_trans *llc_stat_dupaddr_state_trans[] = { 501 [0] = &llc_stat_dupaddr_state_trans_6, /* Request */ 502 [1] = &llc_stat_dupaddr_state_trans_4, /* Timer */ 503 [2] = &llc_stat_dupaddr_state_trans_5, 504 [3] = &llc_stat_dupaddr_state_trans_1, /* Receive frame */ 505 [4] = &llc_stat_dupaddr_state_trans_2, 506 [5] = &llc_stat_dupaddr_state_trans_3, 507 [6] = &llc_stat_state_trans_end, 508 }; 509 510 static struct llc_station_state 511 llc_station_state_table[LLC_NBR_STATION_STATES] = { 512 [LLC_STATION_STATE_DOWN - 1] = { 513 .curr_state = LLC_STATION_STATE_DOWN, 514 .transitions = llc_stat_dwn_state_trans, 515 }, 516 [LLC_STATION_STATE_DUP_ADDR_CHK - 1] = { 517 .curr_state = LLC_STATION_STATE_DUP_ADDR_CHK, 518 .transitions = llc_stat_dupaddr_state_trans, 519 }, 520 [LLC_STATION_STATE_UP - 1] = { 521 .curr_state = LLC_STATION_STATE_UP, 522 .transitions = llc_stat_up_state_trans, 523 }, 524 }; 525 526 /** 527 * llc_exec_station_trans_actions - executes actions for transition 528 * @trans: Address of the transition 529 * @skb: Address of the event that caused the transition 530 * 531 * Executes actions of a transition of the station state machine. Returns 532 * 0 if all actions complete successfully, nonzero otherwise. 533 */ 534 static u16 llc_exec_station_trans_actions(struct llc_station_state_trans *trans, 535 struct sk_buff *skb) 536 { 537 u16 rc = 0; 538 llc_station_action_t *next_action = trans->ev_actions; 539 540 for (; next_action && *next_action; next_action++) 541 if ((*next_action)(skb)) 542 rc = 1; 543 return rc; 544 } 545 546 /** 547 * llc_find_station_trans - finds transition for this event 548 * @skb: Address of the event 549 * 550 * Search thru events of the current state of the station until list 551 * exhausted or it's obvious that the event is not valid for the current 552 * state. Returns the address of the transition if cound, %NULL otherwise. 553 */ 554 static struct llc_station_state_trans * 555 llc_find_station_trans(struct sk_buff *skb) 556 { 557 int i = 0; 558 struct llc_station_state_trans *rc = NULL; 559 struct llc_station_state_trans **next_trans; 560 struct llc_station_state *curr_state = 561 &llc_station_state_table[llc_main_station.state - 1]; 562 563 for (next_trans = curr_state->transitions; next_trans[i]->ev; i++) 564 if (!next_trans[i]->ev(skb)) { 565 rc = next_trans[i]; 566 break; 567 } 568 return rc; 569 } 570 571 /** 572 * llc_station_free_ev - frees an event 573 * @skb: Address of the event 574 * 575 * Frees an event. 576 */ 577 static void llc_station_free_ev(struct sk_buff *skb) 578 { 579 struct llc_station_state_ev *ev = llc_station_ev(skb); 580 581 if (ev->type == LLC_STATION_EV_TYPE_PDU) 582 kfree_skb(skb); 583 } 584 585 /** 586 * llc_station_next_state - processes event and goes to the next state 587 * @skb: Address of the event 588 * 589 * Processes an event, executes any transitions related to that event and 590 * updates the state of the station. 591 */ 592 static u16 llc_station_next_state(struct sk_buff *skb) 593 { 594 u16 rc = 1; 595 struct llc_station_state_trans *trans; 596 597 if (llc_main_station.state > LLC_NBR_STATION_STATES) 598 goto out; 599 trans = llc_find_station_trans(skb); 600 if (trans) { 601 /* got the state to which we next transition; perform the 602 * actions associated with this transition before actually 603 * transitioning to the next state 604 */ 605 rc = llc_exec_station_trans_actions(trans, skb); 606 if (!rc) 607 /* transition station to next state if all actions 608 * execute successfully; done; wait for next event 609 */ 610 llc_main_station.state = trans->next_state; 611 } else 612 /* event not recognized in current state; re-queue it for 613 * processing again at a later time; return failure 614 */ 615 rc = 0; 616 out: 617 llc_station_free_ev(skb); 618 return rc; 619 } 620 621 /** 622 * llc_station_service_events - service events in the queue 623 * 624 * Get an event from the station event queue (if any); attempt to service 625 * the event; if event serviced, get the next event (if any) on the event 626 * queue; if event not service, re-queue the event on the event queue and 627 * attempt to service the next event; when serviced all events in queue, 628 * finished; if don't transition to different state, just service all 629 * events once; if transition to new state, service all events again. 630 * Caller must hold llc_main_station.ev_q.lock. 631 */ 632 static void llc_station_service_events(void) 633 { 634 struct sk_buff *skb; 635 636 while ((skb = skb_dequeue(&llc_main_station.ev_q.list)) != NULL) 637 llc_station_next_state(skb); 638 } 639 640 /** 641 * llc_station_state_process: queue event and try to process queue. 642 * @skb: Address of the event 643 * 644 * Queues an event (on the station event queue) for handling by the 645 * station state machine and attempts to process any queued-up events. 646 */ 647 static void llc_station_state_process(struct sk_buff *skb) 648 { 649 spin_lock_bh(&llc_main_station.ev_q.lock); 650 skb_queue_tail(&llc_main_station.ev_q.list, skb); 651 llc_station_service_events(); 652 spin_unlock_bh(&llc_main_station.ev_q.lock); 653 } 654 655 static void llc_station_ack_tmr_cb(unsigned long timeout_data) 656 { 657 struct sk_buff *skb = alloc_skb(0, GFP_ATOMIC); 658 659 if (skb) { 660 struct llc_station_state_ev *ev = llc_station_ev(skb); 661 662 ev->type = LLC_STATION_EV_TYPE_ACK_TMR; 663 llc_station_state_process(skb); 664 } 665 } 666 667 /* 668 * llc_station_rcv - send received pdu to the station state machine 669 * @skb: received frame. 670 * 671 * Sends data unit to station state machine. 672 */ 673 static void llc_station_rcv(struct sk_buff *skb) 674 { 675 struct llc_station_state_ev *ev = llc_station_ev(skb); 676 677 ev->type = LLC_STATION_EV_TYPE_PDU; 678 ev->reason = 0; 679 llc_station_state_process(skb); 680 } 681 682 int __init llc_station_init(void) 683 { 684 u16 rc = -ENOBUFS; 685 struct sk_buff *skb; 686 struct llc_station_state_ev *ev; 687 688 skb_queue_head_init(&llc_main_station.mac_pdu_q); 689 skb_queue_head_init(&llc_main_station.ev_q.list); 690 spin_lock_init(&llc_main_station.ev_q.lock); 691 init_timer(&llc_main_station.ack_timer); 692 llc_main_station.ack_timer.data = (unsigned long)&llc_main_station; 693 llc_main_station.ack_timer.function = llc_station_ack_tmr_cb; 694 llc_main_station.ack_timer.expires = jiffies + 695 sysctl_llc_station_ack_timeout; 696 skb = alloc_skb(0, GFP_ATOMIC); 697 if (!skb) 698 goto out; 699 rc = 0; 700 llc_set_station_handler(llc_station_rcv); 701 ev = llc_station_ev(skb); 702 memset(ev, 0, sizeof(*ev)); 703 llc_main_station.maximum_retry = 1; 704 llc_main_station.state = LLC_STATION_STATE_DOWN; 705 ev->type = LLC_STATION_EV_TYPE_SIMPLE; 706 ev->prim_type = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK; 707 rc = llc_station_next_state(skb); 708 out: 709 return rc; 710 } 711 712 void __exit llc_station_exit(void) 713 { 714 llc_set_station_handler(NULL); 715 } 716