llc2: Collapse the station event receive path
[deliverable/linux.git] / net / llc / llc_station.c
CommitLineData
1da177e4
LT
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 */
1da177e4
LT
14#include <linux/init.h>
15#include <linux/module.h>
5a0e3ad6 16#include <linux/slab.h>
1da177e4
LT
17#include <net/llc.h>
18#include <net/llc_sap.h>
19#include <net/llc_conn.h>
20#include <net/llc_c_ac.h>
21#include <net/llc_s_ac.h>
22#include <net/llc_c_ev.h>
23#include <net/llc_c_st.h>
24#include <net/llc_s_ev.h>
25#include <net/llc_s_st.h>
26#include <net/llc_pdu.h>
27
28/**
29 * struct llc_station - LLC station component
30 *
31 * SAP and connection resource manager, one per adapter.
32 *
2c53040f
BH
33 * @mac_sa: MAC source address
34 * @sap_list: list of related SAPs
2c53040f 35 * @mac_pdu_q: PDUs ready to send to MAC
1da177e4
LT
36 */
37struct llc_station {
1da177e4
LT
38 struct sk_buff_head mac_pdu_q;
39};
40
1da177e4
LT
41typedef int (*llc_station_ev_t)(struct sk_buff *skb);
42
1da177e4
LT
43typedef int (*llc_station_action_t)(struct sk_buff *skb);
44
45/* Station component state table structure */
46struct llc_station_state_trans {
47 llc_station_ev_t ev;
1da177e4
LT
48 llc_station_action_t *ev_actions;
49};
50
1da177e4
LT
51static struct llc_station llc_main_station;
52
1da177e4
LT
53static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb)
54{
1da177e4
LT
55 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
56
025e3633 57 return LLC_PDU_IS_CMD(pdu) && /* command PDU */
1da177e4
LT
58 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
59 LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID &&
60 !pdu->dsap ? 0 : 1; /* NULL DSAP value */
61}
62
1da177e4
LT
63static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb)
64{
1da177e4
LT
65 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
66
025e3633 67 return LLC_PDU_IS_CMD(pdu) && /* command PDU */
1da177e4
LT
68 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
69 LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST &&
70 !pdu->dsap ? 0 : 1; /* NULL DSAP */
71}
72
1da177e4
LT
73/**
74 * llc_station_send_pdu - queues PDU to send
75 * @skb: Address of the PDU
76 *
77 * Queues a PDU to send to the MAC layer.
78 */
79static void llc_station_send_pdu(struct sk_buff *skb)
80{
81 skb_queue_tail(&llc_main_station.mac_pdu_q, skb);
82 while ((skb = skb_dequeue(&llc_main_station.mac_pdu_q)) != NULL)
83 if (dev_queue_xmit(skb))
84 break;
85}
86
1da177e4
LT
87static int llc_station_ac_send_xid_r(struct sk_buff *skb)
88{
89 u8 mac_da[ETH_ALEN], dsap;
90 int rc = 1;
f83f1768
JP
91 struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U,
92 sizeof(struct llc_xid_info));
1da177e4
LT
93
94 if (!nskb)
95 goto out;
96 rc = 0;
1da177e4
LT
97 llc_pdu_decode_sa(skb, mac_da);
98 llc_pdu_decode_ssap(skb, &dsap);
99 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
100 llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127);
a5a04819 101 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da);
249ff1c6 102 if (unlikely(rc))
1da177e4
LT
103 goto free;
104 llc_station_send_pdu(nskb);
105out:
106 return rc;
107free:
91d27a86 108 kfree_skb(nskb);
1da177e4
LT
109 goto out;
110}
111
112static int llc_station_ac_send_test_r(struct sk_buff *skb)
113{
114 u8 mac_da[ETH_ALEN], dsap;
115 int rc = 1;
f83f1768
JP
116 u32 data_size;
117 struct sk_buff *nskb;
118
119 /* The test request command is type U (llc_len = 3) */
120 data_size = ntohs(eth_hdr(skb)->h_proto) - 3;
121 nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, data_size);
1da177e4
LT
122
123 if (!nskb)
124 goto out;
125 rc = 0;
1da177e4
LT
126 llc_pdu_decode_sa(skb, mac_da);
127 llc_pdu_decode_ssap(skb, &dsap);
128 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
d57b1869 129 llc_pdu_init_as_test_rsp(nskb, skb);
a5a04819 130 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da);
249ff1c6 131 if (unlikely(rc))
1da177e4
LT
132 goto free;
133 llc_station_send_pdu(nskb);
134out:
135 return rc;
136free:
91d27a86 137 kfree_skb(nskb);
1da177e4
LT
138 goto out;
139}
140
1da177e4
LT
141/* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */
142static llc_station_action_t llc_stat_up_state_actions_2[] = {
143 [0] = llc_station_ac_send_xid_r,
144 [1] = NULL,
145};
146
147static struct llc_station_state_trans llc_stat_up_state_trans_2 = {
148 .ev = llc_stat_ev_rx_null_dsap_xid_c,
1da177e4
LT
149 .ev_actions = llc_stat_up_state_actions_2,
150};
151
152/* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */
153static llc_station_action_t llc_stat_up_state_actions_3[] = {
154 [0] = llc_station_ac_send_test_r,
155 [1] = NULL,
156};
157
158static struct llc_station_state_trans llc_stat_up_state_trans_3 = {
159 .ev = llc_stat_ev_rx_null_dsap_test_c,
1da177e4
LT
160 .ev_actions = llc_stat_up_state_actions_3,
161};
162
163/* array of pointers; one to each transition */
164static struct llc_station_state_trans *llc_stat_up_state_trans [] = {
025e3633
BH
165 &llc_stat_up_state_trans_2,
166 &llc_stat_up_state_trans_3,
167 NULL,
1da177e4
LT
168};
169
170/**
171 * llc_exec_station_trans_actions - executes actions for transition
172 * @trans: Address of the transition
173 * @skb: Address of the event that caused the transition
174 *
175 * Executes actions of a transition of the station state machine. Returns
176 * 0 if all actions complete successfully, nonzero otherwise.
177 */
178static u16 llc_exec_station_trans_actions(struct llc_station_state_trans *trans,
179 struct sk_buff *skb)
180{
181 u16 rc = 0;
182 llc_station_action_t *next_action = trans->ev_actions;
183
184 for (; next_action && *next_action; next_action++)
185 if ((*next_action)(skb))
186 rc = 1;
187 return rc;
188}
189
190/**
191 * llc_find_station_trans - finds transition for this event
192 * @skb: Address of the event
193 *
194 * Search thru events of the current state of the station until list
195 * exhausted or it's obvious that the event is not valid for the current
196 * state. Returns the address of the transition if cound, %NULL otherwise.
197 */
198static struct llc_station_state_trans *
199 llc_find_station_trans(struct sk_buff *skb)
200{
201 int i = 0;
202 struct llc_station_state_trans *rc = NULL;
203 struct llc_station_state_trans **next_trans;
1da177e4 204
025e3633 205 for (next_trans = llc_stat_up_state_trans; next_trans[i]; i++)
1da177e4
LT
206 if (!next_trans[i]->ev(skb)) {
207 rc = next_trans[i];
208 break;
209 }
210 return rc;
211}
212
2c53040f 213/**
1da177e4
LT
214 * llc_station_rcv - send received pdu to the station state machine
215 * @skb: received frame.
216 *
217 * Sends data unit to station state machine.
218 */
219static void llc_station_rcv(struct sk_buff *skb)
220{
04d191c2
BH
221 struct llc_station_state_trans *trans;
222
223 trans = llc_find_station_trans(skb);
224 if (trans)
225 llc_exec_station_trans_actions(trans, skb);
226 kfree_skb(skb);
1da177e4
LT
227}
228
6024935f 229void __init llc_station_init(void)
1da177e4 230{
1da177e4 231 skb_queue_head_init(&llc_main_station.mac_pdu_q);
aadf31de 232 llc_set_station_handler(llc_station_rcv);
1da177e4
LT
233}
234
f4f8720f 235void llc_station_exit(void)
1da177e4
LT
236{
237 llc_set_station_handler(NULL);
238}
This page took 0.776909 seconds and 5 git commands to generate.