llc_station.c revision b24b8a247ff65c01b252025926fe564209fae4fc
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); 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); 266out: 267 return rc; 268free: 269 kfree_skb(skb); 270 goto out; 271} 272 273static 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); 290out: 291 return rc; 292free: 293 kfree_skb(skb); 294 goto out; 295} 296 297static 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); 314out: 315 return rc; 316free: 317 kfree_skb(skb); 318 goto out; 319} 320 321static 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 */ 332static 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 */ 337static 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 345static 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 */ 352static llc_station_action_t llc_stat_down_state_actions_2[] = { 353 [0] = llc_station_ac_report_status, /* STATION UP */ 354 [1] = NULL, 355}; 356 357static 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 */ 364static 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 */ 372static llc_station_action_t llc_stat_up_state_actions_1[] = { 373 [0] = llc_station_ac_report_status, /* STATION DOWN */ 374 [1] = NULL, 375}; 376 377static 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 */ 384static llc_station_action_t llc_stat_up_state_actions_2[] = { 385 [0] = llc_station_ac_send_xid_r, 386 [1] = NULL, 387}; 388 389static 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 */ 396static llc_station_action_t llc_stat_up_state_actions_3[] = { 397 [0] = llc_station_ac_send_test_r, 398 [1] = NULL, 399}; 400 401static 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 */ 408static 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 */ 419static 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 424static 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 */ 433static 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 438static 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 */ 445static llc_station_action_t llc_stat_dupaddr_state_actions_3[] = { 446 [0] = llc_station_ac_send_xid_r, 447 [1] = NULL, 448}; 449 450static 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 */ 459static 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 467static 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 */ 476static llc_station_action_t llc_stat_dupaddr_state_actions_5[] = { 477 [0] = llc_station_ac_report_status, /* STATION UP */ 478 [1] = NULL, 479}; 480 481static 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 */ 488static llc_station_action_t llc_stat_dupaddr_state_actions_6[] = { 489 [0] = llc_station_ac_report_status, /* STATION DOWN */ 490 [1] = NULL, 491}; 492 493static 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 */ 500static 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 510static 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 */ 534static 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 */ 554static 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 */ 577static 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 */ 592static 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; 616out: 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 */ 632static 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 */ 647static 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 655static 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 */ 673static 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 682int __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 setup_timer(&llc_main_station.ack_timer, llc_station_ack_tmr_cb, 692 (unsigned long)&llc_main_station); 693 llc_main_station.ack_timer.expires = jiffies + 694 sysctl_llc_station_ack_timeout; 695 skb = alloc_skb(0, GFP_ATOMIC); 696 if (!skb) 697 goto out; 698 rc = 0; 699 llc_set_station_handler(llc_station_rcv); 700 ev = llc_station_ev(skb); 701 memset(ev, 0, sizeof(*ev)); 702 llc_main_station.maximum_retry = 1; 703 llc_main_station.state = LLC_STATION_STATE_DOWN; 704 ev->type = LLC_STATION_EV_TYPE_SIMPLE; 705 ev->prim_type = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK; 706 rc = llc_station_next_state(skb); 707out: 708 return rc; 709} 710 711void __exit llc_station_exit(void) 712{ 713 llc_set_station_handler(NULL); 714} 715