11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * llc_station.c - station component of LLC 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 1997 by Procom Technology, Inc. 51da177e4SLinus Torvalds * 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br> 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program can be redistributed or modified under the terms of the 81da177e4SLinus Torvalds * GNU General Public License as published by the Free Software Foundation. 91da177e4SLinus Torvalds * This program is distributed without any warranty or implied warranty 101da177e4SLinus Torvalds * of merchantability or fitness for a particular purpose. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * See the GNU General Public License for more details. 131da177e4SLinus Torvalds */ 141da177e4SLinus Torvalds #include <linux/init.h> 151da177e4SLinus Torvalds #include <linux/module.h> 161da177e4SLinus Torvalds #include <net/llc.h> 171da177e4SLinus Torvalds #include <net/llc_sap.h> 181da177e4SLinus Torvalds #include <net/llc_conn.h> 191da177e4SLinus Torvalds #include <net/llc_c_ac.h> 201da177e4SLinus Torvalds #include <net/llc_s_ac.h> 211da177e4SLinus Torvalds #include <net/llc_c_ev.h> 221da177e4SLinus Torvalds #include <net/llc_c_st.h> 231da177e4SLinus Torvalds #include <net/llc_s_ev.h> 241da177e4SLinus Torvalds #include <net/llc_s_st.h> 251da177e4SLinus Torvalds #include <net/llc_pdu.h> 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds /** 281da177e4SLinus Torvalds * struct llc_station - LLC station component 291da177e4SLinus Torvalds * 301da177e4SLinus Torvalds * SAP and connection resource manager, one per adapter. 311da177e4SLinus Torvalds * 321da177e4SLinus Torvalds * @state - state of station 331da177e4SLinus Torvalds * @xid_r_count - XID response PDU counter 341da177e4SLinus Torvalds * @mac_sa - MAC source address 351da177e4SLinus Torvalds * @sap_list - list of related SAPs 361da177e4SLinus Torvalds * @ev_q - events entering state mach. 371da177e4SLinus Torvalds * @mac_pdu_q - PDUs ready to send to MAC 381da177e4SLinus Torvalds */ 391da177e4SLinus Torvalds struct llc_station { 401da177e4SLinus Torvalds u8 state; 411da177e4SLinus Torvalds u8 xid_r_count; 421da177e4SLinus Torvalds struct timer_list ack_timer; 431da177e4SLinus Torvalds u8 retry_count; 441da177e4SLinus Torvalds u8 maximum_retry; 451da177e4SLinus Torvalds struct { 461da177e4SLinus Torvalds struct sk_buff_head list; 471da177e4SLinus Torvalds spinlock_t lock; 481da177e4SLinus Torvalds } ev_q; 491da177e4SLinus Torvalds struct sk_buff_head mac_pdu_q; 501da177e4SLinus Torvalds }; 511da177e4SLinus Torvalds 52590232a7SArnaldo Carvalho de Melo #define LLC_STATION_ACK_TIME (3 * HZ) 53590232a7SArnaldo Carvalho de Melo 54590232a7SArnaldo Carvalho de Melo int sysctl_llc_station_ack_timeout = LLC_STATION_ACK_TIME; 55590232a7SArnaldo Carvalho de Melo 561da177e4SLinus Torvalds /* Types of events (possible values in 'ev->type') */ 571da177e4SLinus Torvalds #define LLC_STATION_EV_TYPE_SIMPLE 1 581da177e4SLinus Torvalds #define LLC_STATION_EV_TYPE_CONDITION 2 591da177e4SLinus Torvalds #define LLC_STATION_EV_TYPE_PRIM 3 601da177e4SLinus Torvalds #define LLC_STATION_EV_TYPE_PDU 4 /* command/response PDU */ 611da177e4SLinus Torvalds #define LLC_STATION_EV_TYPE_ACK_TMR 5 621da177e4SLinus Torvalds #define LLC_STATION_EV_TYPE_RPT_STATUS 6 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds /* Events */ 651da177e4SLinus Torvalds #define LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK 1 661da177e4SLinus Torvalds #define LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK 2 671da177e4SLinus Torvalds #define LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY 3 681da177e4SLinus Torvalds #define LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY 4 691da177e4SLinus Torvalds #define LLC_STATION_EV_RX_NULL_DSAP_XID_C 5 701da177e4SLinus Torvalds #define LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ 6 711da177e4SLinus Torvalds #define LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ 7 721da177e4SLinus Torvalds #define LLC_STATION_EV_RX_NULL_DSAP_TEST_C 8 731da177e4SLinus Torvalds #define LLC_STATION_EV_DISABLE_REQ 9 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds struct llc_station_state_ev { 761da177e4SLinus Torvalds u8 type; 771da177e4SLinus Torvalds u8 prim; 781da177e4SLinus Torvalds u8 prim_type; 791da177e4SLinus Torvalds u8 reason; 801da177e4SLinus Torvalds struct list_head node; /* node in station->ev_q.list */ 811da177e4SLinus Torvalds }; 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds static __inline__ struct llc_station_state_ev * 841da177e4SLinus Torvalds llc_station_ev(struct sk_buff *skb) 851da177e4SLinus Torvalds { 861da177e4SLinus Torvalds return (struct llc_station_state_ev *)skb->cb; 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds typedef int (*llc_station_ev_t)(struct sk_buff *skb); 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds #define LLC_STATION_STATE_DOWN 1 /* initial state */ 921da177e4SLinus Torvalds #define LLC_STATION_STATE_DUP_ADDR_CHK 2 931da177e4SLinus Torvalds #define LLC_STATION_STATE_UP 3 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds #define LLC_NBR_STATION_STATES 3 /* size of state table */ 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds typedef int (*llc_station_action_t)(struct sk_buff *skb); 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds /* Station component state table structure */ 1001da177e4SLinus Torvalds struct llc_station_state_trans { 1011da177e4SLinus Torvalds llc_station_ev_t ev; 1021da177e4SLinus Torvalds u8 next_state; 1031da177e4SLinus Torvalds llc_station_action_t *ev_actions; 1041da177e4SLinus Torvalds }; 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds struct llc_station_state { 1071da177e4SLinus Torvalds u8 curr_state; 1081da177e4SLinus Torvalds struct llc_station_state_trans **transitions; 1091da177e4SLinus Torvalds }; 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds static struct llc_station llc_main_station; 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds static int llc_stat_ev_enable_with_dup_addr_check(struct sk_buff *skb) 1141da177e4SLinus Torvalds { 1151da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds return ev->type == LLC_STATION_EV_TYPE_SIMPLE && 1181da177e4SLinus Torvalds ev->prim_type == 1191da177e4SLinus Torvalds LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK ? 0 : 1; 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds static int llc_stat_ev_enable_without_dup_addr_check(struct sk_buff *skb) 1231da177e4SLinus Torvalds { 1241da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds return ev->type == LLC_STATION_EV_TYPE_SIMPLE && 1271da177e4SLinus Torvalds ev->prim_type == 1281da177e4SLinus Torvalds LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK ? 0 : 1; 1291da177e4SLinus Torvalds } 1301da177e4SLinus Torvalds 1311da177e4SLinus Torvalds static int llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry(struct sk_buff *skb) 1321da177e4SLinus Torvalds { 1331da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds return ev->type == LLC_STATION_EV_TYPE_ACK_TMR && 1361da177e4SLinus Torvalds llc_main_station.retry_count < 1371da177e4SLinus Torvalds llc_main_station.maximum_retry ? 0 : 1; 1381da177e4SLinus Torvalds } 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds static int llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry(struct sk_buff *skb) 1411da177e4SLinus Torvalds { 1421da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds return ev->type == LLC_STATION_EV_TYPE_ACK_TMR && 1451da177e4SLinus Torvalds llc_main_station.retry_count == 1461da177e4SLinus Torvalds llc_main_station.maximum_retry ? 0 : 1; 1471da177e4SLinus Torvalds } 1481da177e4SLinus Torvalds 1491da177e4SLinus Torvalds static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb) 1501da177e4SLinus Torvalds { 1511da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 1521da177e4SLinus Torvalds struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds return ev->type == LLC_STATION_EV_TYPE_PDU && 1551da177e4SLinus Torvalds LLC_PDU_IS_CMD(pdu) && /* command PDU */ 1561da177e4SLinus Torvalds LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 1571da177e4SLinus Torvalds LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID && 1581da177e4SLinus Torvalds !pdu->dsap ? 0 : 1; /* NULL DSAP value */ 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds static int llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq(struct sk_buff *skb) 1621da177e4SLinus Torvalds { 1631da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 1641da177e4SLinus Torvalds struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds return ev->type == LLC_STATION_EV_TYPE_PDU && 1671da177e4SLinus Torvalds LLC_PDU_IS_RSP(pdu) && /* response PDU */ 1681da177e4SLinus Torvalds LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 1691da177e4SLinus Torvalds LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID && 1701da177e4SLinus Torvalds !pdu->dsap && /* NULL DSAP value */ 1711da177e4SLinus Torvalds !llc_main_station.xid_r_count ? 0 : 1; 1721da177e4SLinus Torvalds } 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds static int llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq(struct sk_buff *skb) 1751da177e4SLinus Torvalds { 1761da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 1771da177e4SLinus Torvalds struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds return ev->type == LLC_STATION_EV_TYPE_PDU && 1801da177e4SLinus Torvalds LLC_PDU_IS_RSP(pdu) && /* response PDU */ 1811da177e4SLinus Torvalds LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 1821da177e4SLinus Torvalds LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID && 1831da177e4SLinus Torvalds !pdu->dsap && /* NULL DSAP value */ 1841da177e4SLinus Torvalds llc_main_station.xid_r_count == 1 ? 0 : 1; 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb) 1881da177e4SLinus Torvalds { 1891da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 1901da177e4SLinus Torvalds struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds return ev->type == LLC_STATION_EV_TYPE_PDU && 1931da177e4SLinus Torvalds LLC_PDU_IS_CMD(pdu) && /* command PDU */ 1941da177e4SLinus Torvalds LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 1951da177e4SLinus Torvalds LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST && 1961da177e4SLinus Torvalds !pdu->dsap ? 0 : 1; /* NULL DSAP */ 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds static int llc_stat_ev_disable_req(struct sk_buff *skb) 2001da177e4SLinus Torvalds { 2011da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds return ev->type == LLC_STATION_EV_TYPE_PRIM && 2041da177e4SLinus Torvalds ev->prim == LLC_DISABLE_PRIM && 2051da177e4SLinus Torvalds ev->prim_type == LLC_PRIM_TYPE_REQ ? 0 : 1; 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds /** 2091da177e4SLinus Torvalds * llc_station_send_pdu - queues PDU to send 2101da177e4SLinus Torvalds * @skb: Address of the PDU 2111da177e4SLinus Torvalds * 2121da177e4SLinus Torvalds * Queues a PDU to send to the MAC layer. 2131da177e4SLinus Torvalds */ 2141da177e4SLinus Torvalds static void llc_station_send_pdu(struct sk_buff *skb) 2151da177e4SLinus Torvalds { 2161da177e4SLinus Torvalds skb_queue_tail(&llc_main_station.mac_pdu_q, skb); 2171da177e4SLinus Torvalds while ((skb = skb_dequeue(&llc_main_station.mac_pdu_q)) != NULL) 2181da177e4SLinus Torvalds if (dev_queue_xmit(skb)) 2191da177e4SLinus Torvalds break; 2201da177e4SLinus Torvalds } 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds static int llc_station_ac_start_ack_timer(struct sk_buff *skb) 2231da177e4SLinus Torvalds { 224590232a7SArnaldo Carvalho de Melo mod_timer(&llc_main_station.ack_timer, 225590232a7SArnaldo Carvalho de Melo jiffies + sysctl_llc_station_ack_timeout); 2261da177e4SLinus Torvalds return 0; 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds static int llc_station_ac_set_retry_cnt_0(struct sk_buff *skb) 2301da177e4SLinus Torvalds { 2311da177e4SLinus Torvalds llc_main_station.retry_count = 0; 2321da177e4SLinus Torvalds return 0; 2331da177e4SLinus Torvalds } 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds static int llc_station_ac_inc_retry_cnt_by_1(struct sk_buff *skb) 2361da177e4SLinus Torvalds { 2371da177e4SLinus Torvalds llc_main_station.retry_count++; 2381da177e4SLinus Torvalds return 0; 2391da177e4SLinus Torvalds } 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds static int llc_station_ac_set_xid_r_cnt_0(struct sk_buff *skb) 2421da177e4SLinus Torvalds { 2431da177e4SLinus Torvalds llc_main_station.xid_r_count = 0; 2441da177e4SLinus Torvalds return 0; 2451da177e4SLinus Torvalds } 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds static int llc_station_ac_inc_xid_r_cnt_by_1(struct sk_buff *skb) 2481da177e4SLinus Torvalds { 2491da177e4SLinus Torvalds llc_main_station.xid_r_count++; 2501da177e4SLinus Torvalds return 0; 2511da177e4SLinus Torvalds } 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds static int llc_station_ac_send_null_dsap_xid_c(struct sk_buff *skb) 2541da177e4SLinus Torvalds { 2551da177e4SLinus Torvalds int rc = 1; 256d389424eSArnaldo Carvalho de Melo struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev); 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds if (!nskb) 2591da177e4SLinus Torvalds goto out; 2601da177e4SLinus Torvalds llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, 0, LLC_PDU_CMD); 2611da177e4SLinus Torvalds llc_pdu_init_as_xid_cmd(nskb, LLC_XID_NULL_CLASS_2, 127); 2621da177e4SLinus Torvalds rc = llc_mac_hdr_init(nskb, llc_station_mac_sa, llc_station_mac_sa); 263249ff1c6SArnaldo Carvalho de Melo if (unlikely(rc)) 2641da177e4SLinus Torvalds goto free; 2651da177e4SLinus Torvalds llc_station_send_pdu(nskb); 2661da177e4SLinus Torvalds out: 2671da177e4SLinus Torvalds return rc; 2681da177e4SLinus Torvalds free: 2691da177e4SLinus Torvalds kfree_skb(skb); 2701da177e4SLinus Torvalds goto out; 2711da177e4SLinus Torvalds } 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds static int llc_station_ac_send_xid_r(struct sk_buff *skb) 2741da177e4SLinus Torvalds { 2751da177e4SLinus Torvalds u8 mac_da[ETH_ALEN], dsap; 2761da177e4SLinus Torvalds int rc = 1; 277d389424eSArnaldo Carvalho de Melo struct sk_buff* nskb = llc_alloc_frame(NULL, skb->dev); 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds if (!nskb) 2801da177e4SLinus Torvalds goto out; 2811da177e4SLinus Torvalds rc = 0; 2821da177e4SLinus Torvalds llc_pdu_decode_sa(skb, mac_da); 2831da177e4SLinus Torvalds llc_pdu_decode_ssap(skb, &dsap); 2841da177e4SLinus Torvalds llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 2851da177e4SLinus Torvalds llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127); 2861da177e4SLinus Torvalds rc = llc_mac_hdr_init(nskb, llc_station_mac_sa, mac_da); 287249ff1c6SArnaldo Carvalho de Melo if (unlikely(rc)) 2881da177e4SLinus Torvalds goto free; 2891da177e4SLinus Torvalds llc_station_send_pdu(nskb); 2901da177e4SLinus Torvalds out: 2911da177e4SLinus Torvalds return rc; 2921da177e4SLinus Torvalds free: 2931da177e4SLinus Torvalds kfree_skb(skb); 2941da177e4SLinus Torvalds goto out; 2951da177e4SLinus Torvalds } 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds static int llc_station_ac_send_test_r(struct sk_buff *skb) 2981da177e4SLinus Torvalds { 2991da177e4SLinus Torvalds u8 mac_da[ETH_ALEN], dsap; 3001da177e4SLinus Torvalds int rc = 1; 301d389424eSArnaldo Carvalho de Melo struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev); 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds if (!nskb) 3041da177e4SLinus Torvalds goto out; 3051da177e4SLinus Torvalds rc = 0; 3061da177e4SLinus Torvalds llc_pdu_decode_sa(skb, mac_da); 3071da177e4SLinus Torvalds llc_pdu_decode_ssap(skb, &dsap); 3081da177e4SLinus Torvalds llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 3091da177e4SLinus Torvalds llc_pdu_init_as_test_rsp(nskb, skb); 3101da177e4SLinus Torvalds rc = llc_mac_hdr_init(nskb, llc_station_mac_sa, mac_da); 311249ff1c6SArnaldo Carvalho de Melo if (unlikely(rc)) 3121da177e4SLinus Torvalds goto free; 3131da177e4SLinus Torvalds llc_station_send_pdu(nskb); 3141da177e4SLinus Torvalds out: 3151da177e4SLinus Torvalds return rc; 3161da177e4SLinus Torvalds free: 3171da177e4SLinus Torvalds kfree_skb(skb); 3181da177e4SLinus Torvalds goto out; 3191da177e4SLinus Torvalds } 3201da177e4SLinus Torvalds 3211da177e4SLinus Torvalds static int llc_station_ac_report_status(struct sk_buff *skb) 3221da177e4SLinus Torvalds { 3231da177e4SLinus Torvalds return 0; 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds /* COMMON STATION STATE transitions */ 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds /* dummy last-transition indicator; common to all state transition groups 3291da177e4SLinus Torvalds * last entry for this state 3301da177e4SLinus Torvalds * all members are zeros, .bss zeroes it 3311da177e4SLinus Torvalds */ 3321da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_state_trans_end; 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds /* DOWN STATE transitions */ 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK event */ 3371da177e4SLinus Torvalds static llc_station_action_t llc_stat_down_state_actions_1[] = { 3381da177e4SLinus Torvalds [0] = llc_station_ac_start_ack_timer, 3391da177e4SLinus Torvalds [1] = llc_station_ac_set_retry_cnt_0, 3401da177e4SLinus Torvalds [2] = llc_station_ac_set_xid_r_cnt_0, 3411da177e4SLinus Torvalds [3] = llc_station_ac_send_null_dsap_xid_c, 3421da177e4SLinus Torvalds [4] = NULL, 3431da177e4SLinus Torvalds }; 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_down_state_trans_1 = { 3461da177e4SLinus Torvalds .ev = llc_stat_ev_enable_with_dup_addr_check, 3471da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 3481da177e4SLinus Torvalds .ev_actions = llc_stat_down_state_actions_1, 3491da177e4SLinus Torvalds }; 3501da177e4SLinus Torvalds 3511da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK event */ 3521da177e4SLinus Torvalds static llc_station_action_t llc_stat_down_state_actions_2[] = { 3531da177e4SLinus Torvalds [0] = llc_station_ac_report_status, /* STATION UP */ 3541da177e4SLinus Torvalds [1] = NULL, 3551da177e4SLinus Torvalds }; 3561da177e4SLinus Torvalds 3571da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_down_state_trans_2 = { 3581da177e4SLinus Torvalds .ev = llc_stat_ev_enable_without_dup_addr_check, 3591da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_UP, 3601da177e4SLinus Torvalds .ev_actions = llc_stat_down_state_actions_2, 3611da177e4SLinus Torvalds }; 3621da177e4SLinus Torvalds 3631da177e4SLinus Torvalds /* array of pointers; one to each transition */ 3641da177e4SLinus Torvalds static struct llc_station_state_trans *llc_stat_dwn_state_trans[] = { 3651da177e4SLinus Torvalds [0] = &llc_stat_down_state_trans_1, 3661da177e4SLinus Torvalds [1] = &llc_stat_down_state_trans_2, 3671da177e4SLinus Torvalds [2] = &llc_stat_state_trans_end, 3681da177e4SLinus Torvalds }; 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds /* UP STATE transitions */ 3711da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_DISABLE_REQ event */ 3721da177e4SLinus Torvalds static llc_station_action_t llc_stat_up_state_actions_1[] = { 3731da177e4SLinus Torvalds [0] = llc_station_ac_report_status, /* STATION DOWN */ 3741da177e4SLinus Torvalds [1] = NULL, 3751da177e4SLinus Torvalds }; 3761da177e4SLinus Torvalds 3771da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_up_state_trans_1 = { 3781da177e4SLinus Torvalds .ev = llc_stat_ev_disable_req, 3791da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DOWN, 3801da177e4SLinus Torvalds .ev_actions = llc_stat_up_state_actions_1, 3811da177e4SLinus Torvalds }; 3821da177e4SLinus Torvalds 3831da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */ 3841da177e4SLinus Torvalds static llc_station_action_t llc_stat_up_state_actions_2[] = { 3851da177e4SLinus Torvalds [0] = llc_station_ac_send_xid_r, 3861da177e4SLinus Torvalds [1] = NULL, 3871da177e4SLinus Torvalds }; 3881da177e4SLinus Torvalds 3891da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_up_state_trans_2 = { 3901da177e4SLinus Torvalds .ev = llc_stat_ev_rx_null_dsap_xid_c, 3911da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_UP, 3921da177e4SLinus Torvalds .ev_actions = llc_stat_up_state_actions_2, 3931da177e4SLinus Torvalds }; 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */ 3961da177e4SLinus Torvalds static llc_station_action_t llc_stat_up_state_actions_3[] = { 3971da177e4SLinus Torvalds [0] = llc_station_ac_send_test_r, 3981da177e4SLinus Torvalds [1] = NULL, 3991da177e4SLinus Torvalds }; 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_up_state_trans_3 = { 4021da177e4SLinus Torvalds .ev = llc_stat_ev_rx_null_dsap_test_c, 4031da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_UP, 4041da177e4SLinus Torvalds .ev_actions = llc_stat_up_state_actions_3, 4051da177e4SLinus Torvalds }; 4061da177e4SLinus Torvalds 4071da177e4SLinus Torvalds /* array of pointers; one to each transition */ 4081da177e4SLinus Torvalds static struct llc_station_state_trans *llc_stat_up_state_trans [] = { 4091da177e4SLinus Torvalds [0] = &llc_stat_up_state_trans_1, 4101da177e4SLinus Torvalds [1] = &llc_stat_up_state_trans_2, 4111da177e4SLinus Torvalds [2] = &llc_stat_up_state_trans_3, 4121da177e4SLinus Torvalds [3] = &llc_stat_state_trans_end, 4131da177e4SLinus Torvalds }; 4141da177e4SLinus Torvalds 4151da177e4SLinus Torvalds /* DUP ADDR CHK STATE transitions */ 4161da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ 4171da177e4SLinus Torvalds * event 4181da177e4SLinus Torvalds */ 4191da177e4SLinus Torvalds static llc_station_action_t llc_stat_dupaddr_state_actions_1[] = { 4201da177e4SLinus Torvalds [0] = llc_station_ac_inc_xid_r_cnt_by_1, 4211da177e4SLinus Torvalds [1] = NULL, 4221da177e4SLinus Torvalds }; 4231da177e4SLinus Torvalds 4241da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_dupaddr_state_trans_1 = { 4251da177e4SLinus Torvalds .ev = llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq, 4261da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 4271da177e4SLinus Torvalds .ev_actions = llc_stat_dupaddr_state_actions_1, 4281da177e4SLinus Torvalds }; 4291da177e4SLinus Torvalds 4301da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ 4311da177e4SLinus Torvalds * event 4321da177e4SLinus Torvalds */ 4331da177e4SLinus Torvalds static llc_station_action_t llc_stat_dupaddr_state_actions_2[] = { 4341da177e4SLinus Torvalds [0] = llc_station_ac_report_status, /* DUPLICATE ADDRESS FOUND */ 4351da177e4SLinus Torvalds [1] = NULL, 4361da177e4SLinus Torvalds }; 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_dupaddr_state_trans_2 = { 4391da177e4SLinus Torvalds .ev = llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq, 4401da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DOWN, 4411da177e4SLinus Torvalds .ev_actions = llc_stat_dupaddr_state_actions_2, 4421da177e4SLinus Torvalds }; 4431da177e4SLinus Torvalds 4441da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */ 4451da177e4SLinus Torvalds static llc_station_action_t llc_stat_dupaddr_state_actions_3[] = { 4461da177e4SLinus Torvalds [0] = llc_station_ac_send_xid_r, 4471da177e4SLinus Torvalds [1] = NULL, 4481da177e4SLinus Torvalds }; 4491da177e4SLinus Torvalds 4501da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_dupaddr_state_trans_3 = { 4511da177e4SLinus Torvalds .ev = llc_stat_ev_rx_null_dsap_xid_c, 4521da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 4531da177e4SLinus Torvalds .ev_actions = llc_stat_dupaddr_state_actions_3, 4541da177e4SLinus Torvalds }; 4551da177e4SLinus Torvalds 4561da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY 4571da177e4SLinus Torvalds * event 4581da177e4SLinus Torvalds */ 4591da177e4SLinus Torvalds static llc_station_action_t llc_stat_dupaddr_state_actions_4[] = { 4601da177e4SLinus Torvalds [0] = llc_station_ac_start_ack_timer, 4611da177e4SLinus Torvalds [1] = llc_station_ac_inc_retry_cnt_by_1, 4621da177e4SLinus Torvalds [2] = llc_station_ac_set_xid_r_cnt_0, 4631da177e4SLinus Torvalds [3] = llc_station_ac_send_null_dsap_xid_c, 4641da177e4SLinus Torvalds [4] = NULL, 4651da177e4SLinus Torvalds }; 4661da177e4SLinus Torvalds 4671da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_dupaddr_state_trans_4 = { 4681da177e4SLinus Torvalds .ev = llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry, 4691da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 4701da177e4SLinus Torvalds .ev_actions = llc_stat_dupaddr_state_actions_4, 4711da177e4SLinus Torvalds }; 4721da177e4SLinus Torvalds 4731da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY 4741da177e4SLinus Torvalds * event 4751da177e4SLinus Torvalds */ 4761da177e4SLinus Torvalds static llc_station_action_t llc_stat_dupaddr_state_actions_5[] = { 4771da177e4SLinus Torvalds [0] = llc_station_ac_report_status, /* STATION UP */ 4781da177e4SLinus Torvalds [1] = NULL, 4791da177e4SLinus Torvalds }; 4801da177e4SLinus Torvalds 4811da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_dupaddr_state_trans_5 = { 4821da177e4SLinus Torvalds .ev = llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry, 4831da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_UP, 4841da177e4SLinus Torvalds .ev_actions = llc_stat_dupaddr_state_actions_5, 4851da177e4SLinus Torvalds }; 4861da177e4SLinus Torvalds 4871da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_DISABLE_REQ event */ 4881da177e4SLinus Torvalds static llc_station_action_t llc_stat_dupaddr_state_actions_6[] = { 4891da177e4SLinus Torvalds [0] = llc_station_ac_report_status, /* STATION DOWN */ 4901da177e4SLinus Torvalds [1] = NULL, 4911da177e4SLinus Torvalds }; 4921da177e4SLinus Torvalds 4931da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_dupaddr_state_trans_6 = { 4941da177e4SLinus Torvalds .ev = llc_stat_ev_disable_req, 4951da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DOWN, 4961da177e4SLinus Torvalds .ev_actions = llc_stat_dupaddr_state_actions_6, 4971da177e4SLinus Torvalds }; 4981da177e4SLinus Torvalds 4991da177e4SLinus Torvalds /* array of pointers; one to each transition */ 5001da177e4SLinus Torvalds static struct llc_station_state_trans *llc_stat_dupaddr_state_trans[] = { 5011da177e4SLinus Torvalds [0] = &llc_stat_dupaddr_state_trans_6, /* Request */ 5021da177e4SLinus Torvalds [1] = &llc_stat_dupaddr_state_trans_4, /* Timer */ 5031da177e4SLinus Torvalds [2] = &llc_stat_dupaddr_state_trans_5, 5041da177e4SLinus Torvalds [3] = &llc_stat_dupaddr_state_trans_1, /* Receive frame */ 5051da177e4SLinus Torvalds [4] = &llc_stat_dupaddr_state_trans_2, 5061da177e4SLinus Torvalds [5] = &llc_stat_dupaddr_state_trans_3, 5071da177e4SLinus Torvalds [6] = &llc_stat_state_trans_end, 5081da177e4SLinus Torvalds }; 5091da177e4SLinus Torvalds 5101da177e4SLinus Torvalds static struct llc_station_state 5111da177e4SLinus Torvalds llc_station_state_table[LLC_NBR_STATION_STATES] = { 5121da177e4SLinus Torvalds [LLC_STATION_STATE_DOWN - 1] = { 5131da177e4SLinus Torvalds .curr_state = LLC_STATION_STATE_DOWN, 5141da177e4SLinus Torvalds .transitions = llc_stat_dwn_state_trans, 5151da177e4SLinus Torvalds }, 5161da177e4SLinus Torvalds [LLC_STATION_STATE_DUP_ADDR_CHK - 1] = { 5171da177e4SLinus Torvalds .curr_state = LLC_STATION_STATE_DUP_ADDR_CHK, 5181da177e4SLinus Torvalds .transitions = llc_stat_dupaddr_state_trans, 5191da177e4SLinus Torvalds }, 5201da177e4SLinus Torvalds [LLC_STATION_STATE_UP - 1] = { 5211da177e4SLinus Torvalds .curr_state = LLC_STATION_STATE_UP, 5221da177e4SLinus Torvalds .transitions = llc_stat_up_state_trans, 5231da177e4SLinus Torvalds }, 5241da177e4SLinus Torvalds }; 5251da177e4SLinus Torvalds 5261da177e4SLinus Torvalds /** 5271da177e4SLinus Torvalds * llc_exec_station_trans_actions - executes actions for transition 5281da177e4SLinus Torvalds * @trans: Address of the transition 5291da177e4SLinus Torvalds * @skb: Address of the event that caused the transition 5301da177e4SLinus Torvalds * 5311da177e4SLinus Torvalds * Executes actions of a transition of the station state machine. Returns 5321da177e4SLinus Torvalds * 0 if all actions complete successfully, nonzero otherwise. 5331da177e4SLinus Torvalds */ 5341da177e4SLinus Torvalds static u16 llc_exec_station_trans_actions(struct llc_station_state_trans *trans, 5351da177e4SLinus Torvalds struct sk_buff *skb) 5361da177e4SLinus Torvalds { 5371da177e4SLinus Torvalds u16 rc = 0; 5381da177e4SLinus Torvalds llc_station_action_t *next_action = trans->ev_actions; 5391da177e4SLinus Torvalds 5401da177e4SLinus Torvalds for (; next_action && *next_action; next_action++) 5411da177e4SLinus Torvalds if ((*next_action)(skb)) 5421da177e4SLinus Torvalds rc = 1; 5431da177e4SLinus Torvalds return rc; 5441da177e4SLinus Torvalds } 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds /** 5471da177e4SLinus Torvalds * llc_find_station_trans - finds transition for this event 5481da177e4SLinus Torvalds * @skb: Address of the event 5491da177e4SLinus Torvalds * 5501da177e4SLinus Torvalds * Search thru events of the current state of the station until list 5511da177e4SLinus Torvalds * exhausted or it's obvious that the event is not valid for the current 5521da177e4SLinus Torvalds * state. Returns the address of the transition if cound, %NULL otherwise. 5531da177e4SLinus Torvalds */ 5541da177e4SLinus Torvalds static struct llc_station_state_trans * 5551da177e4SLinus Torvalds llc_find_station_trans(struct sk_buff *skb) 5561da177e4SLinus Torvalds { 5571da177e4SLinus Torvalds int i = 0; 5581da177e4SLinus Torvalds struct llc_station_state_trans *rc = NULL; 5591da177e4SLinus Torvalds struct llc_station_state_trans **next_trans; 5601da177e4SLinus Torvalds struct llc_station_state *curr_state = 5611da177e4SLinus Torvalds &llc_station_state_table[llc_main_station.state - 1]; 5621da177e4SLinus Torvalds 5631da177e4SLinus Torvalds for (next_trans = curr_state->transitions; next_trans[i]->ev; i++) 5641da177e4SLinus Torvalds if (!next_trans[i]->ev(skb)) { 5651da177e4SLinus Torvalds rc = next_trans[i]; 5661da177e4SLinus Torvalds break; 5671da177e4SLinus Torvalds } 5681da177e4SLinus Torvalds return rc; 5691da177e4SLinus Torvalds } 5701da177e4SLinus Torvalds 5711da177e4SLinus Torvalds /** 5721da177e4SLinus Torvalds * llc_station_free_ev - frees an event 5731da177e4SLinus Torvalds * @skb: Address of the event 5741da177e4SLinus Torvalds * 5751da177e4SLinus Torvalds * Frees an event. 5761da177e4SLinus Torvalds */ 5771da177e4SLinus Torvalds static void llc_station_free_ev(struct sk_buff *skb) 5781da177e4SLinus Torvalds { 5791da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 5801da177e4SLinus Torvalds 5811da177e4SLinus Torvalds if (ev->type == LLC_STATION_EV_TYPE_PDU) 5821da177e4SLinus Torvalds kfree_skb(skb); 5831da177e4SLinus Torvalds } 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds /** 5861da177e4SLinus Torvalds * llc_station_next_state - processes event and goes to the next state 5871da177e4SLinus Torvalds * @skb: Address of the event 5881da177e4SLinus Torvalds * 5891da177e4SLinus Torvalds * Processes an event, executes any transitions related to that event and 5901da177e4SLinus Torvalds * updates the state of the station. 5911da177e4SLinus Torvalds */ 5921da177e4SLinus Torvalds static u16 llc_station_next_state(struct sk_buff *skb) 5931da177e4SLinus Torvalds { 5941da177e4SLinus Torvalds u16 rc = 1; 5951da177e4SLinus Torvalds struct llc_station_state_trans *trans; 5961da177e4SLinus Torvalds 5971da177e4SLinus Torvalds if (llc_main_station.state > LLC_NBR_STATION_STATES) 5981da177e4SLinus Torvalds goto out; 5991da177e4SLinus Torvalds trans = llc_find_station_trans(skb); 6001da177e4SLinus Torvalds if (trans) { 6011da177e4SLinus Torvalds /* got the state to which we next transition; perform the 6021da177e4SLinus Torvalds * actions associated with this transition before actually 6031da177e4SLinus Torvalds * transitioning to the next state 6041da177e4SLinus Torvalds */ 6051da177e4SLinus Torvalds rc = llc_exec_station_trans_actions(trans, skb); 6061da177e4SLinus Torvalds if (!rc) 6071da177e4SLinus Torvalds /* transition station to next state if all actions 6081da177e4SLinus Torvalds * execute successfully; done; wait for next event 6091da177e4SLinus Torvalds */ 6101da177e4SLinus Torvalds llc_main_station.state = trans->next_state; 6111da177e4SLinus Torvalds } else 6121da177e4SLinus Torvalds /* event not recognized in current state; re-queue it for 6131da177e4SLinus Torvalds * processing again at a later time; return failure 6141da177e4SLinus Torvalds */ 6151da177e4SLinus Torvalds rc = 0; 6161da177e4SLinus Torvalds out: 6171da177e4SLinus Torvalds llc_station_free_ev(skb); 6181da177e4SLinus Torvalds return rc; 6191da177e4SLinus Torvalds } 6201da177e4SLinus Torvalds 6211da177e4SLinus Torvalds /** 6221da177e4SLinus Torvalds * llc_station_service_events - service events in the queue 6231da177e4SLinus Torvalds * 6241da177e4SLinus Torvalds * Get an event from the station event queue (if any); attempt to service 6251da177e4SLinus Torvalds * the event; if event serviced, get the next event (if any) on the event 6261da177e4SLinus Torvalds * queue; if event not service, re-queue the event on the event queue and 6271da177e4SLinus Torvalds * attempt to service the next event; when serviced all events in queue, 6281da177e4SLinus Torvalds * finished; if don't transition to different state, just service all 6291da177e4SLinus Torvalds * events once; if transition to new state, service all events again. 6301da177e4SLinus Torvalds * Caller must hold llc_main_station.ev_q.lock. 6311da177e4SLinus Torvalds */ 6321da177e4SLinus Torvalds static void llc_station_service_events(void) 6331da177e4SLinus Torvalds { 6341da177e4SLinus Torvalds struct sk_buff *skb; 6351da177e4SLinus Torvalds 6361da177e4SLinus Torvalds while ((skb = skb_dequeue(&llc_main_station.ev_q.list)) != NULL) 6371da177e4SLinus Torvalds llc_station_next_state(skb); 6381da177e4SLinus Torvalds } 6391da177e4SLinus Torvalds 6401da177e4SLinus Torvalds /** 6411da177e4SLinus Torvalds * llc_station_state_process: queue event and try to process queue. 6421da177e4SLinus Torvalds * @skb: Address of the event 6431da177e4SLinus Torvalds * 6441da177e4SLinus Torvalds * Queues an event (on the station event queue) for handling by the 6451da177e4SLinus Torvalds * station state machine and attempts to process any queued-up events. 6461da177e4SLinus Torvalds */ 6471da177e4SLinus Torvalds static void llc_station_state_process(struct sk_buff *skb) 6481da177e4SLinus Torvalds { 6491da177e4SLinus Torvalds spin_lock_bh(&llc_main_station.ev_q.lock); 6501da177e4SLinus Torvalds skb_queue_tail(&llc_main_station.ev_q.list, skb); 6511da177e4SLinus Torvalds llc_station_service_events(); 6521da177e4SLinus Torvalds spin_unlock_bh(&llc_main_station.ev_q.lock); 6531da177e4SLinus Torvalds } 6541da177e4SLinus Torvalds 6551da177e4SLinus Torvalds static void llc_station_ack_tmr_cb(unsigned long timeout_data) 6561da177e4SLinus Torvalds { 6571da177e4SLinus Torvalds struct sk_buff *skb = alloc_skb(0, GFP_ATOMIC); 6581da177e4SLinus Torvalds 6591da177e4SLinus Torvalds if (skb) { 6601da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 6611da177e4SLinus Torvalds 6621da177e4SLinus Torvalds ev->type = LLC_STATION_EV_TYPE_ACK_TMR; 6631da177e4SLinus Torvalds llc_station_state_process(skb); 6641da177e4SLinus Torvalds } 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds 6671da177e4SLinus Torvalds /* 6681da177e4SLinus Torvalds * llc_station_rcv - send received pdu to the station state machine 6691da177e4SLinus Torvalds * @skb: received frame. 6701da177e4SLinus Torvalds * 6711da177e4SLinus Torvalds * Sends data unit to station state machine. 6721da177e4SLinus Torvalds */ 6731da177e4SLinus Torvalds static void llc_station_rcv(struct sk_buff *skb) 6741da177e4SLinus Torvalds { 6751da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 6761da177e4SLinus Torvalds 6771da177e4SLinus Torvalds ev->type = LLC_STATION_EV_TYPE_PDU; 6781da177e4SLinus Torvalds ev->reason = 0; 6791da177e4SLinus Torvalds llc_station_state_process(skb); 6801da177e4SLinus Torvalds } 6811da177e4SLinus Torvalds 6821da177e4SLinus Torvalds int __init llc_station_init(void) 6831da177e4SLinus Torvalds { 6841da177e4SLinus Torvalds u16 rc = -ENOBUFS; 6851da177e4SLinus Torvalds struct sk_buff *skb; 6861da177e4SLinus Torvalds struct llc_station_state_ev *ev; 6871da177e4SLinus Torvalds 6881da177e4SLinus Torvalds skb_queue_head_init(&llc_main_station.mac_pdu_q); 6891da177e4SLinus Torvalds skb_queue_head_init(&llc_main_station.ev_q.list); 6901da177e4SLinus Torvalds spin_lock_init(&llc_main_station.ev_q.lock); 691b24b8a24SPavel Emelyanov setup_timer(&llc_main_station.ack_timer, llc_station_ack_tmr_cb, 692b24b8a24SPavel Emelyanov (unsigned long)&llc_main_station); 693590232a7SArnaldo Carvalho de Melo llc_main_station.ack_timer.expires = jiffies + 694590232a7SArnaldo Carvalho de Melo sysctl_llc_station_ack_timeout; 6951da177e4SLinus Torvalds skb = alloc_skb(0, GFP_ATOMIC); 6961da177e4SLinus Torvalds if (!skb) 6971da177e4SLinus Torvalds goto out; 6981da177e4SLinus Torvalds rc = 0; 6991da177e4SLinus Torvalds llc_set_station_handler(llc_station_rcv); 7001da177e4SLinus Torvalds ev = llc_station_ev(skb); 7011da177e4SLinus Torvalds memset(ev, 0, sizeof(*ev)); 7021da177e4SLinus Torvalds llc_main_station.maximum_retry = 1; 7031da177e4SLinus Torvalds llc_main_station.state = LLC_STATION_STATE_DOWN; 7041da177e4SLinus Torvalds ev->type = LLC_STATION_EV_TYPE_SIMPLE; 7051da177e4SLinus Torvalds ev->prim_type = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK; 7061da177e4SLinus Torvalds rc = llc_station_next_state(skb); 7071da177e4SLinus Torvalds out: 7081da177e4SLinus Torvalds return rc; 7091da177e4SLinus Torvalds } 7101da177e4SLinus Torvalds 7111da177e4SLinus Torvalds void __exit llc_station_exit(void) 7121da177e4SLinus Torvalds { 7131da177e4SLinus Torvalds llc_set_station_handler(NULL); 7141da177e4SLinus Torvalds } 715