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; 256f83f1768SJoonwoo Park struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, 257f83f1768SJoonwoo Park sizeof(struct llc_xid_info)); 2581da177e4SLinus Torvalds 2591da177e4SLinus Torvalds if (!nskb) 2601da177e4SLinus Torvalds goto out; 2611da177e4SLinus Torvalds llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, 0, LLC_PDU_CMD); 2621da177e4SLinus Torvalds llc_pdu_init_as_xid_cmd(nskb, LLC_XID_NULL_CLASS_2, 127); 263a5a04819SJoonwoo Park rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, skb->dev->dev_addr); 264249ff1c6SArnaldo Carvalho de Melo if (unlikely(rc)) 2651da177e4SLinus Torvalds goto free; 2661da177e4SLinus Torvalds llc_station_send_pdu(nskb); 2671da177e4SLinus Torvalds out: 2681da177e4SLinus Torvalds return rc; 2691da177e4SLinus Torvalds free: 2701da177e4SLinus Torvalds kfree_skb(skb); 2711da177e4SLinus Torvalds goto out; 2721da177e4SLinus Torvalds } 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds static int llc_station_ac_send_xid_r(struct sk_buff *skb) 2751da177e4SLinus Torvalds { 2761da177e4SLinus Torvalds u8 mac_da[ETH_ALEN], dsap; 2771da177e4SLinus Torvalds int rc = 1; 278f83f1768SJoonwoo Park struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, 279f83f1768SJoonwoo Park sizeof(struct llc_xid_info)); 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds if (!nskb) 2821da177e4SLinus Torvalds goto out; 2831da177e4SLinus Torvalds rc = 0; 2841da177e4SLinus Torvalds llc_pdu_decode_sa(skb, mac_da); 2851da177e4SLinus Torvalds llc_pdu_decode_ssap(skb, &dsap); 2861da177e4SLinus Torvalds llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 2871da177e4SLinus Torvalds llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127); 288a5a04819SJoonwoo Park rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da); 289249ff1c6SArnaldo Carvalho de Melo if (unlikely(rc)) 2901da177e4SLinus Torvalds goto free; 2911da177e4SLinus Torvalds llc_station_send_pdu(nskb); 2921da177e4SLinus Torvalds out: 2931da177e4SLinus Torvalds return rc; 2941da177e4SLinus Torvalds free: 2951da177e4SLinus Torvalds kfree_skb(skb); 2961da177e4SLinus Torvalds goto out; 2971da177e4SLinus Torvalds } 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds static int llc_station_ac_send_test_r(struct sk_buff *skb) 3001da177e4SLinus Torvalds { 3011da177e4SLinus Torvalds u8 mac_da[ETH_ALEN], dsap; 3021da177e4SLinus Torvalds int rc = 1; 303f83f1768SJoonwoo Park u32 data_size; 304f83f1768SJoonwoo Park struct sk_buff *nskb; 305f83f1768SJoonwoo Park 306f83f1768SJoonwoo Park /* The test request command is type U (llc_len = 3) */ 307f83f1768SJoonwoo Park data_size = ntohs(eth_hdr(skb)->h_proto) - 3; 308f83f1768SJoonwoo Park nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, data_size); 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds if (!nskb) 3111da177e4SLinus Torvalds goto out; 3121da177e4SLinus Torvalds rc = 0; 3131da177e4SLinus Torvalds llc_pdu_decode_sa(skb, mac_da); 3141da177e4SLinus Torvalds llc_pdu_decode_ssap(skb, &dsap); 3151da177e4SLinus Torvalds llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 3161da177e4SLinus Torvalds llc_pdu_init_as_test_rsp(nskb, skb); 317a5a04819SJoonwoo Park rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da); 318249ff1c6SArnaldo Carvalho de Melo if (unlikely(rc)) 3191da177e4SLinus Torvalds goto free; 3201da177e4SLinus Torvalds llc_station_send_pdu(nskb); 3211da177e4SLinus Torvalds out: 3221da177e4SLinus Torvalds return rc; 3231da177e4SLinus Torvalds free: 3241da177e4SLinus Torvalds kfree_skb(skb); 3251da177e4SLinus Torvalds goto out; 3261da177e4SLinus Torvalds } 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds static int llc_station_ac_report_status(struct sk_buff *skb) 3291da177e4SLinus Torvalds { 3301da177e4SLinus Torvalds return 0; 3311da177e4SLinus Torvalds } 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds /* COMMON STATION STATE transitions */ 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds /* dummy last-transition indicator; common to all state transition groups 3361da177e4SLinus Torvalds * last entry for this state 3371da177e4SLinus Torvalds * all members are zeros, .bss zeroes it 3381da177e4SLinus Torvalds */ 3391da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_state_trans_end; 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds /* DOWN STATE transitions */ 3421da177e4SLinus Torvalds 3431da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK event */ 3441da177e4SLinus Torvalds static llc_station_action_t llc_stat_down_state_actions_1[] = { 3451da177e4SLinus Torvalds [0] = llc_station_ac_start_ack_timer, 3461da177e4SLinus Torvalds [1] = llc_station_ac_set_retry_cnt_0, 3471da177e4SLinus Torvalds [2] = llc_station_ac_set_xid_r_cnt_0, 3481da177e4SLinus Torvalds [3] = llc_station_ac_send_null_dsap_xid_c, 3491da177e4SLinus Torvalds [4] = NULL, 3501da177e4SLinus Torvalds }; 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_down_state_trans_1 = { 3531da177e4SLinus Torvalds .ev = llc_stat_ev_enable_with_dup_addr_check, 3541da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 3551da177e4SLinus Torvalds .ev_actions = llc_stat_down_state_actions_1, 3561da177e4SLinus Torvalds }; 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK event */ 3591da177e4SLinus Torvalds static llc_station_action_t llc_stat_down_state_actions_2[] = { 3601da177e4SLinus Torvalds [0] = llc_station_ac_report_status, /* STATION UP */ 3611da177e4SLinus Torvalds [1] = NULL, 3621da177e4SLinus Torvalds }; 3631da177e4SLinus Torvalds 3641da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_down_state_trans_2 = { 3651da177e4SLinus Torvalds .ev = llc_stat_ev_enable_without_dup_addr_check, 3661da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_UP, 3671da177e4SLinus Torvalds .ev_actions = llc_stat_down_state_actions_2, 3681da177e4SLinus Torvalds }; 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds /* array of pointers; one to each transition */ 3711da177e4SLinus Torvalds static struct llc_station_state_trans *llc_stat_dwn_state_trans[] = { 3721da177e4SLinus Torvalds [0] = &llc_stat_down_state_trans_1, 3731da177e4SLinus Torvalds [1] = &llc_stat_down_state_trans_2, 3741da177e4SLinus Torvalds [2] = &llc_stat_state_trans_end, 3751da177e4SLinus Torvalds }; 3761da177e4SLinus Torvalds 3771da177e4SLinus Torvalds /* UP STATE transitions */ 3781da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_DISABLE_REQ event */ 3791da177e4SLinus Torvalds static llc_station_action_t llc_stat_up_state_actions_1[] = { 3801da177e4SLinus Torvalds [0] = llc_station_ac_report_status, /* STATION DOWN */ 3811da177e4SLinus Torvalds [1] = NULL, 3821da177e4SLinus Torvalds }; 3831da177e4SLinus Torvalds 3841da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_up_state_trans_1 = { 3851da177e4SLinus Torvalds .ev = llc_stat_ev_disable_req, 3861da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DOWN, 3871da177e4SLinus Torvalds .ev_actions = llc_stat_up_state_actions_1, 3881da177e4SLinus Torvalds }; 3891da177e4SLinus Torvalds 3901da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */ 3911da177e4SLinus Torvalds static llc_station_action_t llc_stat_up_state_actions_2[] = { 3921da177e4SLinus Torvalds [0] = llc_station_ac_send_xid_r, 3931da177e4SLinus Torvalds [1] = NULL, 3941da177e4SLinus Torvalds }; 3951da177e4SLinus Torvalds 3961da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_up_state_trans_2 = { 3971da177e4SLinus Torvalds .ev = llc_stat_ev_rx_null_dsap_xid_c, 3981da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_UP, 3991da177e4SLinus Torvalds .ev_actions = llc_stat_up_state_actions_2, 4001da177e4SLinus Torvalds }; 4011da177e4SLinus Torvalds 4021da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */ 4031da177e4SLinus Torvalds static llc_station_action_t llc_stat_up_state_actions_3[] = { 4041da177e4SLinus Torvalds [0] = llc_station_ac_send_test_r, 4051da177e4SLinus Torvalds [1] = NULL, 4061da177e4SLinus Torvalds }; 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_up_state_trans_3 = { 4091da177e4SLinus Torvalds .ev = llc_stat_ev_rx_null_dsap_test_c, 4101da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_UP, 4111da177e4SLinus Torvalds .ev_actions = llc_stat_up_state_actions_3, 4121da177e4SLinus Torvalds }; 4131da177e4SLinus Torvalds 4141da177e4SLinus Torvalds /* array of pointers; one to each transition */ 4151da177e4SLinus Torvalds static struct llc_station_state_trans *llc_stat_up_state_trans [] = { 4161da177e4SLinus Torvalds [0] = &llc_stat_up_state_trans_1, 4171da177e4SLinus Torvalds [1] = &llc_stat_up_state_trans_2, 4181da177e4SLinus Torvalds [2] = &llc_stat_up_state_trans_3, 4191da177e4SLinus Torvalds [3] = &llc_stat_state_trans_end, 4201da177e4SLinus Torvalds }; 4211da177e4SLinus Torvalds 4221da177e4SLinus Torvalds /* DUP ADDR CHK STATE transitions */ 4231da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ 4241da177e4SLinus Torvalds * event 4251da177e4SLinus Torvalds */ 4261da177e4SLinus Torvalds static llc_station_action_t llc_stat_dupaddr_state_actions_1[] = { 4271da177e4SLinus Torvalds [0] = llc_station_ac_inc_xid_r_cnt_by_1, 4281da177e4SLinus Torvalds [1] = NULL, 4291da177e4SLinus Torvalds }; 4301da177e4SLinus Torvalds 4311da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_dupaddr_state_trans_1 = { 4321da177e4SLinus Torvalds .ev = llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq, 4331da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 4341da177e4SLinus Torvalds .ev_actions = llc_stat_dupaddr_state_actions_1, 4351da177e4SLinus Torvalds }; 4361da177e4SLinus Torvalds 4371da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ 4381da177e4SLinus Torvalds * event 4391da177e4SLinus Torvalds */ 4401da177e4SLinus Torvalds static llc_station_action_t llc_stat_dupaddr_state_actions_2[] = { 4411da177e4SLinus Torvalds [0] = llc_station_ac_report_status, /* DUPLICATE ADDRESS FOUND */ 4421da177e4SLinus Torvalds [1] = NULL, 4431da177e4SLinus Torvalds }; 4441da177e4SLinus Torvalds 4451da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_dupaddr_state_trans_2 = { 4461da177e4SLinus Torvalds .ev = llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq, 4471da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DOWN, 4481da177e4SLinus Torvalds .ev_actions = llc_stat_dupaddr_state_actions_2, 4491da177e4SLinus Torvalds }; 4501da177e4SLinus Torvalds 4511da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */ 4521da177e4SLinus Torvalds static llc_station_action_t llc_stat_dupaddr_state_actions_3[] = { 4531da177e4SLinus Torvalds [0] = llc_station_ac_send_xid_r, 4541da177e4SLinus Torvalds [1] = NULL, 4551da177e4SLinus Torvalds }; 4561da177e4SLinus Torvalds 4571da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_dupaddr_state_trans_3 = { 4581da177e4SLinus Torvalds .ev = llc_stat_ev_rx_null_dsap_xid_c, 4591da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 4601da177e4SLinus Torvalds .ev_actions = llc_stat_dupaddr_state_actions_3, 4611da177e4SLinus Torvalds }; 4621da177e4SLinus Torvalds 4631da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY 4641da177e4SLinus Torvalds * event 4651da177e4SLinus Torvalds */ 4661da177e4SLinus Torvalds static llc_station_action_t llc_stat_dupaddr_state_actions_4[] = { 4671da177e4SLinus Torvalds [0] = llc_station_ac_start_ack_timer, 4681da177e4SLinus Torvalds [1] = llc_station_ac_inc_retry_cnt_by_1, 4691da177e4SLinus Torvalds [2] = llc_station_ac_set_xid_r_cnt_0, 4701da177e4SLinus Torvalds [3] = llc_station_ac_send_null_dsap_xid_c, 4711da177e4SLinus Torvalds [4] = NULL, 4721da177e4SLinus Torvalds }; 4731da177e4SLinus Torvalds 4741da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_dupaddr_state_trans_4 = { 4751da177e4SLinus Torvalds .ev = llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry, 4761da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, 4771da177e4SLinus Torvalds .ev_actions = llc_stat_dupaddr_state_actions_4, 4781da177e4SLinus Torvalds }; 4791da177e4SLinus Torvalds 4801da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY 4811da177e4SLinus Torvalds * event 4821da177e4SLinus Torvalds */ 4831da177e4SLinus Torvalds static llc_station_action_t llc_stat_dupaddr_state_actions_5[] = { 4841da177e4SLinus Torvalds [0] = llc_station_ac_report_status, /* STATION UP */ 4851da177e4SLinus Torvalds [1] = NULL, 4861da177e4SLinus Torvalds }; 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_dupaddr_state_trans_5 = { 4891da177e4SLinus Torvalds .ev = llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry, 4901da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_UP, 4911da177e4SLinus Torvalds .ev_actions = llc_stat_dupaddr_state_actions_5, 4921da177e4SLinus Torvalds }; 4931da177e4SLinus Torvalds 4941da177e4SLinus Torvalds /* state transition for LLC_STATION_EV_DISABLE_REQ event */ 4951da177e4SLinus Torvalds static llc_station_action_t llc_stat_dupaddr_state_actions_6[] = { 4961da177e4SLinus Torvalds [0] = llc_station_ac_report_status, /* STATION DOWN */ 4971da177e4SLinus Torvalds [1] = NULL, 4981da177e4SLinus Torvalds }; 4991da177e4SLinus Torvalds 5001da177e4SLinus Torvalds static struct llc_station_state_trans llc_stat_dupaddr_state_trans_6 = { 5011da177e4SLinus Torvalds .ev = llc_stat_ev_disable_req, 5021da177e4SLinus Torvalds .next_state = LLC_STATION_STATE_DOWN, 5031da177e4SLinus Torvalds .ev_actions = llc_stat_dupaddr_state_actions_6, 5041da177e4SLinus Torvalds }; 5051da177e4SLinus Torvalds 5061da177e4SLinus Torvalds /* array of pointers; one to each transition */ 5071da177e4SLinus Torvalds static struct llc_station_state_trans *llc_stat_dupaddr_state_trans[] = { 5081da177e4SLinus Torvalds [0] = &llc_stat_dupaddr_state_trans_6, /* Request */ 5091da177e4SLinus Torvalds [1] = &llc_stat_dupaddr_state_trans_4, /* Timer */ 5101da177e4SLinus Torvalds [2] = &llc_stat_dupaddr_state_trans_5, 5111da177e4SLinus Torvalds [3] = &llc_stat_dupaddr_state_trans_1, /* Receive frame */ 5121da177e4SLinus Torvalds [4] = &llc_stat_dupaddr_state_trans_2, 5131da177e4SLinus Torvalds [5] = &llc_stat_dupaddr_state_trans_3, 5141da177e4SLinus Torvalds [6] = &llc_stat_state_trans_end, 5151da177e4SLinus Torvalds }; 5161da177e4SLinus Torvalds 5171da177e4SLinus Torvalds static struct llc_station_state 5181da177e4SLinus Torvalds llc_station_state_table[LLC_NBR_STATION_STATES] = { 5191da177e4SLinus Torvalds [LLC_STATION_STATE_DOWN - 1] = { 5201da177e4SLinus Torvalds .curr_state = LLC_STATION_STATE_DOWN, 5211da177e4SLinus Torvalds .transitions = llc_stat_dwn_state_trans, 5221da177e4SLinus Torvalds }, 5231da177e4SLinus Torvalds [LLC_STATION_STATE_DUP_ADDR_CHK - 1] = { 5241da177e4SLinus Torvalds .curr_state = LLC_STATION_STATE_DUP_ADDR_CHK, 5251da177e4SLinus Torvalds .transitions = llc_stat_dupaddr_state_trans, 5261da177e4SLinus Torvalds }, 5271da177e4SLinus Torvalds [LLC_STATION_STATE_UP - 1] = { 5281da177e4SLinus Torvalds .curr_state = LLC_STATION_STATE_UP, 5291da177e4SLinus Torvalds .transitions = llc_stat_up_state_trans, 5301da177e4SLinus Torvalds }, 5311da177e4SLinus Torvalds }; 5321da177e4SLinus Torvalds 5331da177e4SLinus Torvalds /** 5341da177e4SLinus Torvalds * llc_exec_station_trans_actions - executes actions for transition 5351da177e4SLinus Torvalds * @trans: Address of the transition 5361da177e4SLinus Torvalds * @skb: Address of the event that caused the transition 5371da177e4SLinus Torvalds * 5381da177e4SLinus Torvalds * Executes actions of a transition of the station state machine. Returns 5391da177e4SLinus Torvalds * 0 if all actions complete successfully, nonzero otherwise. 5401da177e4SLinus Torvalds */ 5411da177e4SLinus Torvalds static u16 llc_exec_station_trans_actions(struct llc_station_state_trans *trans, 5421da177e4SLinus Torvalds struct sk_buff *skb) 5431da177e4SLinus Torvalds { 5441da177e4SLinus Torvalds u16 rc = 0; 5451da177e4SLinus Torvalds llc_station_action_t *next_action = trans->ev_actions; 5461da177e4SLinus Torvalds 5471da177e4SLinus Torvalds for (; next_action && *next_action; next_action++) 5481da177e4SLinus Torvalds if ((*next_action)(skb)) 5491da177e4SLinus Torvalds rc = 1; 5501da177e4SLinus Torvalds return rc; 5511da177e4SLinus Torvalds } 5521da177e4SLinus Torvalds 5531da177e4SLinus Torvalds /** 5541da177e4SLinus Torvalds * llc_find_station_trans - finds transition for this event 5551da177e4SLinus Torvalds * @skb: Address of the event 5561da177e4SLinus Torvalds * 5571da177e4SLinus Torvalds * Search thru events of the current state of the station until list 5581da177e4SLinus Torvalds * exhausted or it's obvious that the event is not valid for the current 5591da177e4SLinus Torvalds * state. Returns the address of the transition if cound, %NULL otherwise. 5601da177e4SLinus Torvalds */ 5611da177e4SLinus Torvalds static struct llc_station_state_trans * 5621da177e4SLinus Torvalds llc_find_station_trans(struct sk_buff *skb) 5631da177e4SLinus Torvalds { 5641da177e4SLinus Torvalds int i = 0; 5651da177e4SLinus Torvalds struct llc_station_state_trans *rc = NULL; 5661da177e4SLinus Torvalds struct llc_station_state_trans **next_trans; 5671da177e4SLinus Torvalds struct llc_station_state *curr_state = 5681da177e4SLinus Torvalds &llc_station_state_table[llc_main_station.state - 1]; 5691da177e4SLinus Torvalds 5701da177e4SLinus Torvalds for (next_trans = curr_state->transitions; next_trans[i]->ev; i++) 5711da177e4SLinus Torvalds if (!next_trans[i]->ev(skb)) { 5721da177e4SLinus Torvalds rc = next_trans[i]; 5731da177e4SLinus Torvalds break; 5741da177e4SLinus Torvalds } 5751da177e4SLinus Torvalds return rc; 5761da177e4SLinus Torvalds } 5771da177e4SLinus Torvalds 5781da177e4SLinus Torvalds /** 5791da177e4SLinus Torvalds * llc_station_free_ev - frees an event 5801da177e4SLinus Torvalds * @skb: Address of the event 5811da177e4SLinus Torvalds * 5821da177e4SLinus Torvalds * Frees an event. 5831da177e4SLinus Torvalds */ 5841da177e4SLinus Torvalds static void llc_station_free_ev(struct sk_buff *skb) 5851da177e4SLinus Torvalds { 5861da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 5871da177e4SLinus Torvalds 5881da177e4SLinus Torvalds if (ev->type == LLC_STATION_EV_TYPE_PDU) 5891da177e4SLinus Torvalds kfree_skb(skb); 5901da177e4SLinus Torvalds } 5911da177e4SLinus Torvalds 5921da177e4SLinus Torvalds /** 5931da177e4SLinus Torvalds * llc_station_next_state - processes event and goes to the next state 5941da177e4SLinus Torvalds * @skb: Address of the event 5951da177e4SLinus Torvalds * 5961da177e4SLinus Torvalds * Processes an event, executes any transitions related to that event and 5971da177e4SLinus Torvalds * updates the state of the station. 5981da177e4SLinus Torvalds */ 5991da177e4SLinus Torvalds static u16 llc_station_next_state(struct sk_buff *skb) 6001da177e4SLinus Torvalds { 6011da177e4SLinus Torvalds u16 rc = 1; 6021da177e4SLinus Torvalds struct llc_station_state_trans *trans; 6031da177e4SLinus Torvalds 6041da177e4SLinus Torvalds if (llc_main_station.state > LLC_NBR_STATION_STATES) 6051da177e4SLinus Torvalds goto out; 6061da177e4SLinus Torvalds trans = llc_find_station_trans(skb); 6071da177e4SLinus Torvalds if (trans) { 6081da177e4SLinus Torvalds /* got the state to which we next transition; perform the 6091da177e4SLinus Torvalds * actions associated with this transition before actually 6101da177e4SLinus Torvalds * transitioning to the next state 6111da177e4SLinus Torvalds */ 6121da177e4SLinus Torvalds rc = llc_exec_station_trans_actions(trans, skb); 6131da177e4SLinus Torvalds if (!rc) 6141da177e4SLinus Torvalds /* transition station to next state if all actions 6151da177e4SLinus Torvalds * execute successfully; done; wait for next event 6161da177e4SLinus Torvalds */ 6171da177e4SLinus Torvalds llc_main_station.state = trans->next_state; 6181da177e4SLinus Torvalds } else 6191da177e4SLinus Torvalds /* event not recognized in current state; re-queue it for 6201da177e4SLinus Torvalds * processing again at a later time; return failure 6211da177e4SLinus Torvalds */ 6221da177e4SLinus Torvalds rc = 0; 6231da177e4SLinus Torvalds out: 6241da177e4SLinus Torvalds llc_station_free_ev(skb); 6251da177e4SLinus Torvalds return rc; 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds 6281da177e4SLinus Torvalds /** 6291da177e4SLinus Torvalds * llc_station_service_events - service events in the queue 6301da177e4SLinus Torvalds * 6311da177e4SLinus Torvalds * Get an event from the station event queue (if any); attempt to service 6321da177e4SLinus Torvalds * the event; if event serviced, get the next event (if any) on the event 6331da177e4SLinus Torvalds * queue; if event not service, re-queue the event on the event queue and 6341da177e4SLinus Torvalds * attempt to service the next event; when serviced all events in queue, 6351da177e4SLinus Torvalds * finished; if don't transition to different state, just service all 6361da177e4SLinus Torvalds * events once; if transition to new state, service all events again. 6371da177e4SLinus Torvalds * Caller must hold llc_main_station.ev_q.lock. 6381da177e4SLinus Torvalds */ 6391da177e4SLinus Torvalds static void llc_station_service_events(void) 6401da177e4SLinus Torvalds { 6411da177e4SLinus Torvalds struct sk_buff *skb; 6421da177e4SLinus Torvalds 6431da177e4SLinus Torvalds while ((skb = skb_dequeue(&llc_main_station.ev_q.list)) != NULL) 6441da177e4SLinus Torvalds llc_station_next_state(skb); 6451da177e4SLinus Torvalds } 6461da177e4SLinus Torvalds 6471da177e4SLinus Torvalds /** 6481da177e4SLinus Torvalds * llc_station_state_process: queue event and try to process queue. 6491da177e4SLinus Torvalds * @skb: Address of the event 6501da177e4SLinus Torvalds * 6511da177e4SLinus Torvalds * Queues an event (on the station event queue) for handling by the 6521da177e4SLinus Torvalds * station state machine and attempts to process any queued-up events. 6531da177e4SLinus Torvalds */ 6541da177e4SLinus Torvalds static void llc_station_state_process(struct sk_buff *skb) 6551da177e4SLinus Torvalds { 6561da177e4SLinus Torvalds spin_lock_bh(&llc_main_station.ev_q.lock); 6571da177e4SLinus Torvalds skb_queue_tail(&llc_main_station.ev_q.list, skb); 6581da177e4SLinus Torvalds llc_station_service_events(); 6591da177e4SLinus Torvalds spin_unlock_bh(&llc_main_station.ev_q.lock); 6601da177e4SLinus Torvalds } 6611da177e4SLinus Torvalds 6621da177e4SLinus Torvalds static void llc_station_ack_tmr_cb(unsigned long timeout_data) 6631da177e4SLinus Torvalds { 6641da177e4SLinus Torvalds struct sk_buff *skb = alloc_skb(0, GFP_ATOMIC); 6651da177e4SLinus Torvalds 6661da177e4SLinus Torvalds if (skb) { 6671da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 6681da177e4SLinus Torvalds 6691da177e4SLinus Torvalds ev->type = LLC_STATION_EV_TYPE_ACK_TMR; 6701da177e4SLinus Torvalds llc_station_state_process(skb); 6711da177e4SLinus Torvalds } 6721da177e4SLinus Torvalds } 6731da177e4SLinus Torvalds 6741da177e4SLinus Torvalds /* 6751da177e4SLinus Torvalds * llc_station_rcv - send received pdu to the station state machine 6761da177e4SLinus Torvalds * @skb: received frame. 6771da177e4SLinus Torvalds * 6781da177e4SLinus Torvalds * Sends data unit to station state machine. 6791da177e4SLinus Torvalds */ 6801da177e4SLinus Torvalds static void llc_station_rcv(struct sk_buff *skb) 6811da177e4SLinus Torvalds { 6821da177e4SLinus Torvalds struct llc_station_state_ev *ev = llc_station_ev(skb); 6831da177e4SLinus Torvalds 6841da177e4SLinus Torvalds ev->type = LLC_STATION_EV_TYPE_PDU; 6851da177e4SLinus Torvalds ev->reason = 0; 6861da177e4SLinus Torvalds llc_station_state_process(skb); 6871da177e4SLinus Torvalds } 6881da177e4SLinus Torvalds 6891da177e4SLinus Torvalds int __init llc_station_init(void) 6901da177e4SLinus Torvalds { 6911da177e4SLinus Torvalds u16 rc = -ENOBUFS; 6921da177e4SLinus Torvalds struct sk_buff *skb; 6931da177e4SLinus Torvalds struct llc_station_state_ev *ev; 6941da177e4SLinus Torvalds 6951da177e4SLinus Torvalds skb_queue_head_init(&llc_main_station.mac_pdu_q); 6961da177e4SLinus Torvalds skb_queue_head_init(&llc_main_station.ev_q.list); 6971da177e4SLinus Torvalds spin_lock_init(&llc_main_station.ev_q.lock); 698b24b8a24SPavel Emelyanov setup_timer(&llc_main_station.ack_timer, llc_station_ack_tmr_cb, 699b24b8a24SPavel Emelyanov (unsigned long)&llc_main_station); 700590232a7SArnaldo Carvalho de Melo llc_main_station.ack_timer.expires = jiffies + 701590232a7SArnaldo Carvalho de Melo sysctl_llc_station_ack_timeout; 7021da177e4SLinus Torvalds skb = alloc_skb(0, GFP_ATOMIC); 7031da177e4SLinus Torvalds if (!skb) 7041da177e4SLinus Torvalds goto out; 7051da177e4SLinus Torvalds rc = 0; 7061da177e4SLinus Torvalds llc_set_station_handler(llc_station_rcv); 7071da177e4SLinus Torvalds ev = llc_station_ev(skb); 7081da177e4SLinus Torvalds memset(ev, 0, sizeof(*ev)); 7091da177e4SLinus Torvalds llc_main_station.maximum_retry = 1; 7101da177e4SLinus Torvalds llc_main_station.state = LLC_STATION_STATE_DOWN; 7111da177e4SLinus Torvalds ev->type = LLC_STATION_EV_TYPE_SIMPLE; 7121da177e4SLinus Torvalds ev->prim_type = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK; 7131da177e4SLinus Torvalds rc = llc_station_next_state(skb); 7141da177e4SLinus Torvalds out: 7151da177e4SLinus Torvalds return rc; 7161da177e4SLinus Torvalds } 7171da177e4SLinus Torvalds 7181da177e4SLinus Torvalds void __exit llc_station_exit(void) 7191da177e4SLinus Torvalds { 7201da177e4SLinus Torvalds llc_set_station_handler(NULL); 7211da177e4SLinus Torvalds } 722