linux/net/llc/llc_station.c
<<
>>
Prefs
   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