diff options
Diffstat (limited to 'net/llc/llc_station.c')
-rw-r--r-- | net/llc/llc_station.c | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c new file mode 100644 index 000000000000..8fe48a24bad5 --- /dev/null +++ b/net/llc/llc_station.c | |||
@@ -0,0 +1,713 @@ | |||
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/config.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/module.h> | ||
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 | * | ||
33 | * @state - state of station | ||
34 | * @xid_r_count - XID response PDU counter | ||
35 | * @mac_sa - MAC source address | ||
36 | * @sap_list - list of related SAPs | ||
37 | * @ev_q - events entering state mach. | ||
38 | * @mac_pdu_q - PDUs ready to send to MAC | ||
39 | */ | ||
40 | struct llc_station { | ||
41 | u8 state; | ||
42 | u8 xid_r_count; | ||
43 | struct timer_list ack_timer; | ||
44 | u8 retry_count; | ||
45 | u8 maximum_retry; | ||
46 | struct { | ||
47 | struct sk_buff_head list; | ||
48 | spinlock_t lock; | ||
49 | } ev_q; | ||
50 | struct sk_buff_head mac_pdu_q; | ||
51 | }; | ||
52 | |||
53 | /* Types of events (possible values in 'ev->type') */ | ||
54 | #define LLC_STATION_EV_TYPE_SIMPLE 1 | ||
55 | #define LLC_STATION_EV_TYPE_CONDITION 2 | ||
56 | #define LLC_STATION_EV_TYPE_PRIM 3 | ||
57 | #define LLC_STATION_EV_TYPE_PDU 4 /* command/response PDU */ | ||
58 | #define LLC_STATION_EV_TYPE_ACK_TMR 5 | ||
59 | #define LLC_STATION_EV_TYPE_RPT_STATUS 6 | ||
60 | |||
61 | /* Events */ | ||
62 | #define LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK 1 | ||
63 | #define LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK 2 | ||
64 | #define LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY 3 | ||
65 | #define LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY 4 | ||
66 | #define LLC_STATION_EV_RX_NULL_DSAP_XID_C 5 | ||
67 | #define LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ 6 | ||
68 | #define LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ 7 | ||
69 | #define LLC_STATION_EV_RX_NULL_DSAP_TEST_C 8 | ||
70 | #define LLC_STATION_EV_DISABLE_REQ 9 | ||
71 | |||
72 | struct llc_station_state_ev { | ||
73 | u8 type; | ||
74 | u8 prim; | ||
75 | u8 prim_type; | ||
76 | u8 reason; | ||
77 | struct list_head node; /* node in station->ev_q.list */ | ||
78 | }; | ||
79 | |||
80 | static __inline__ struct llc_station_state_ev * | ||
81 | llc_station_ev(struct sk_buff *skb) | ||
82 | { | ||
83 | return (struct llc_station_state_ev *)skb->cb; | ||
84 | } | ||
85 | |||
86 | typedef int (*llc_station_ev_t)(struct sk_buff *skb); | ||
87 | |||
88 | #define LLC_STATION_STATE_DOWN 1 /* initial state */ | ||
89 | #define LLC_STATION_STATE_DUP_ADDR_CHK 2 | ||
90 | #define LLC_STATION_STATE_UP 3 | ||
91 | |||
92 | #define LLC_NBR_STATION_STATES 3 /* size of state table */ | ||
93 | |||
94 | typedef int (*llc_station_action_t)(struct sk_buff *skb); | ||
95 | |||
96 | /* Station component state table structure */ | ||
97 | struct llc_station_state_trans { | ||
98 | llc_station_ev_t ev; | ||
99 | u8 next_state; | ||
100 | llc_station_action_t *ev_actions; | ||
101 | }; | ||
102 | |||
103 | struct llc_station_state { | ||
104 | u8 curr_state; | ||
105 | struct llc_station_state_trans **transitions; | ||
106 | }; | ||
107 | |||
108 | static struct llc_station llc_main_station; | ||
109 | |||
110 | static int llc_stat_ev_enable_with_dup_addr_check(struct sk_buff *skb) | ||
111 | { | ||
112 | struct llc_station_state_ev *ev = llc_station_ev(skb); | ||
113 | |||
114 | return ev->type == LLC_STATION_EV_TYPE_SIMPLE && | ||
115 | ev->prim_type == | ||
116 | LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK ? 0 : 1; | ||
117 | } | ||
118 | |||
119 | static int llc_stat_ev_enable_without_dup_addr_check(struct sk_buff *skb) | ||
120 | { | ||
121 | struct llc_station_state_ev *ev = llc_station_ev(skb); | ||
122 | |||
123 | return ev->type == LLC_STATION_EV_TYPE_SIMPLE && | ||
124 | ev->prim_type == | ||
125 | LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK ? 0 : 1; | ||
126 | } | ||
127 | |||
128 | static int llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry(struct sk_buff *skb) | ||
129 | { | ||
130 | struct llc_station_state_ev *ev = llc_station_ev(skb); | ||
131 | |||
132 | return ev->type == LLC_STATION_EV_TYPE_ACK_TMR && | ||
133 | llc_main_station.retry_count < | ||
134 | llc_main_station.maximum_retry ? 0 : 1; | ||
135 | } | ||
136 | |||
137 | static int llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry(struct sk_buff *skb) | ||
138 | { | ||
139 | struct llc_station_state_ev *ev = llc_station_ev(skb); | ||
140 | |||
141 | return ev->type == LLC_STATION_EV_TYPE_ACK_TMR && | ||
142 | llc_main_station.retry_count == | ||
143 | llc_main_station.maximum_retry ? 0 : 1; | ||
144 | } | ||
145 | |||
146 | static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb) | ||
147 | { | ||
148 | struct llc_station_state_ev *ev = llc_station_ev(skb); | ||
149 | struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); | ||
150 | |||
151 | return ev->type == LLC_STATION_EV_TYPE_PDU && | ||
152 | LLC_PDU_IS_CMD(pdu) && /* command PDU */ | ||
153 | LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ | ||
154 | LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID && | ||
155 | !pdu->dsap ? 0 : 1; /* NULL DSAP value */ | ||
156 | } | ||
157 | |||
158 | static int llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq(struct sk_buff *skb) | ||
159 | { | ||
160 | struct llc_station_state_ev *ev = llc_station_ev(skb); | ||
161 | struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); | ||
162 | |||
163 | return ev->type == LLC_STATION_EV_TYPE_PDU && | ||
164 | LLC_PDU_IS_RSP(pdu) && /* response PDU */ | ||
165 | LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ | ||
166 | LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID && | ||
167 | !pdu->dsap && /* NULL DSAP value */ | ||
168 | !llc_main_station.xid_r_count ? 0 : 1; | ||
169 | } | ||
170 | |||
171 | static int llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq(struct sk_buff *skb) | ||
172 | { | ||
173 | struct llc_station_state_ev *ev = llc_station_ev(skb); | ||
174 | struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); | ||
175 | |||
176 | return ev->type == LLC_STATION_EV_TYPE_PDU && | ||
177 | LLC_PDU_IS_RSP(pdu) && /* response PDU */ | ||
178 | LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ | ||
179 | LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID && | ||
180 | !pdu->dsap && /* NULL DSAP value */ | ||
181 | llc_main_station.xid_r_count == 1 ? 0 : 1; | ||
182 | } | ||
183 | |||
184 | static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb) | ||
185 | { | ||
186 | struct llc_station_state_ev *ev = llc_station_ev(skb); | ||
187 | struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); | ||
188 | |||
189 | return ev->type == LLC_STATION_EV_TYPE_PDU && | ||
190 | LLC_PDU_IS_CMD(pdu) && /* command PDU */ | ||
191 | LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ | ||
192 | LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST && | ||
193 | !pdu->dsap ? 0 : 1; /* NULL DSAP */ | ||
194 | } | ||
195 | |||
196 | static int llc_stat_ev_disable_req(struct sk_buff *skb) | ||
197 | { | ||
198 | struct llc_station_state_ev *ev = llc_station_ev(skb); | ||
199 | |||
200 | return ev->type == LLC_STATION_EV_TYPE_PRIM && | ||
201 | ev->prim == LLC_DISABLE_PRIM && | ||
202 | ev->prim_type == LLC_PRIM_TYPE_REQ ? 0 : 1; | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * llc_station_send_pdu - queues PDU to send | ||
207 | * @skb: Address of the PDU | ||
208 | * | ||
209 | * Queues a PDU to send to the MAC layer. | ||
210 | */ | ||
211 | static void llc_station_send_pdu(struct sk_buff *skb) | ||
212 | { | ||
213 | skb_queue_tail(&llc_main_station.mac_pdu_q, skb); | ||
214 | while ((skb = skb_dequeue(&llc_main_station.mac_pdu_q)) != NULL) | ||
215 | if (dev_queue_xmit(skb)) | ||
216 | break; | ||
217 | } | ||
218 | |||
219 | static int llc_station_ac_start_ack_timer(struct sk_buff *skb) | ||
220 | { | ||
221 | mod_timer(&llc_main_station.ack_timer, jiffies + LLC_ACK_TIME * HZ); | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int llc_station_ac_set_retry_cnt_0(struct sk_buff *skb) | ||
226 | { | ||
227 | llc_main_station.retry_count = 0; | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static int llc_station_ac_inc_retry_cnt_by_1(struct sk_buff *skb) | ||
232 | { | ||
233 | llc_main_station.retry_count++; | ||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | static int llc_station_ac_set_xid_r_cnt_0(struct sk_buff *skb) | ||
238 | { | ||
239 | llc_main_station.xid_r_count = 0; | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static int llc_station_ac_inc_xid_r_cnt_by_1(struct sk_buff *skb) | ||
244 | { | ||
245 | llc_main_station.xid_r_count++; | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static int llc_station_ac_send_null_dsap_xid_c(struct sk_buff *skb) | ||
250 | { | ||
251 | int rc = 1; | ||
252 | struct sk_buff *nskb = llc_alloc_frame(); | ||
253 | |||
254 | if (!nskb) | ||
255 | goto out; | ||
256 | llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, 0, LLC_PDU_CMD); | ||
257 | llc_pdu_init_as_xid_cmd(nskb, LLC_XID_NULL_CLASS_2, 127); | ||
258 | rc = llc_mac_hdr_init(nskb, llc_station_mac_sa, llc_station_mac_sa); | ||
259 | if (rc) | ||
260 | goto free; | ||
261 | llc_station_send_pdu(nskb); | ||
262 | out: | ||
263 | return rc; | ||
264 | free: | ||
265 | kfree_skb(skb); | ||
266 | goto out; | ||
267 | } | ||
268 | |||
269 | static int llc_station_ac_send_xid_r(struct sk_buff *skb) | ||
270 | { | ||
271 | u8 mac_da[ETH_ALEN], dsap; | ||
272 | int rc = 1; | ||
273 | struct sk_buff* nskb = llc_alloc_frame(); | ||
274 | |||
275 | if (!nskb) | ||
276 | goto out; | ||
277 | rc = 0; | ||
278 | nskb->dev = skb->dev; | ||
279 | llc_pdu_decode_sa(skb, mac_da); | ||
280 | llc_pdu_decode_ssap(skb, &dsap); | ||
281 | llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); | ||
282 | llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127); | ||
283 | rc = llc_mac_hdr_init(nskb, llc_station_mac_sa, mac_da); | ||
284 | if (rc) | ||
285 | goto free; | ||
286 | llc_station_send_pdu(nskb); | ||
287 | out: | ||
288 | return rc; | ||
289 | free: | ||
290 | kfree_skb(skb); | ||
291 | goto out; | ||
292 | } | ||
293 | |||
294 | static int llc_station_ac_send_test_r(struct sk_buff *skb) | ||
295 | { | ||
296 | u8 mac_da[ETH_ALEN], dsap; | ||
297 | int rc = 1; | ||
298 | struct sk_buff *nskb = llc_alloc_frame(); | ||
299 | |||
300 | if (!nskb) | ||
301 | goto out; | ||
302 | rc = 0; | ||
303 | nskb->dev = skb->dev; | ||
304 | llc_pdu_decode_sa(skb, mac_da); | ||
305 | llc_pdu_decode_ssap(skb, &dsap); | ||
306 | llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); | ||
307 | llc_pdu_init_as_test_rsp(nskb, skb); | ||
308 | rc = llc_mac_hdr_init(nskb, llc_station_mac_sa, mac_da); | ||
309 | if (rc) | ||
310 | goto free; | ||
311 | llc_station_send_pdu(nskb); | ||
312 | out: | ||
313 | return rc; | ||
314 | free: | ||
315 | kfree_skb(skb); | ||
316 | goto out; | ||
317 | } | ||
318 | |||
319 | static int llc_station_ac_report_status(struct sk_buff *skb) | ||
320 | { | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | /* COMMON STATION STATE transitions */ | ||
325 | |||
326 | /* dummy last-transition indicator; common to all state transition groups | ||
327 | * last entry for this state | ||
328 | * all members are zeros, .bss zeroes it | ||
329 | */ | ||
330 | static struct llc_station_state_trans llc_stat_state_trans_end; | ||
331 | |||
332 | /* DOWN STATE transitions */ | ||
333 | |||
334 | /* state transition for LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK event */ | ||
335 | static llc_station_action_t llc_stat_down_state_actions_1[] = { | ||
336 | [0] = llc_station_ac_start_ack_timer, | ||
337 | [1] = llc_station_ac_set_retry_cnt_0, | ||
338 | [2] = llc_station_ac_set_xid_r_cnt_0, | ||
339 | [3] = llc_station_ac_send_null_dsap_xid_c, | ||
340 | [4] = NULL, | ||
341 | }; | ||
342 | |||
343 | static struct llc_station_state_trans llc_stat_down_state_trans_1 = { | ||
344 | .ev = llc_stat_ev_enable_with_dup_addr_check, | ||
345 | .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, | ||
346 | .ev_actions = llc_stat_down_state_actions_1, | ||
347 | }; | ||
348 | |||
349 | /* state transition for LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK event */ | ||
350 | static llc_station_action_t llc_stat_down_state_actions_2[] = { | ||
351 | [0] = llc_station_ac_report_status, /* STATION UP */ | ||
352 | [1] = NULL, | ||
353 | }; | ||
354 | |||
355 | static struct llc_station_state_trans llc_stat_down_state_trans_2 = { | ||
356 | .ev = llc_stat_ev_enable_without_dup_addr_check, | ||
357 | .next_state = LLC_STATION_STATE_UP, | ||
358 | .ev_actions = llc_stat_down_state_actions_2, | ||
359 | }; | ||
360 | |||
361 | /* array of pointers; one to each transition */ | ||
362 | static struct llc_station_state_trans *llc_stat_dwn_state_trans[] = { | ||
363 | [0] = &llc_stat_down_state_trans_1, | ||
364 | [1] = &llc_stat_down_state_trans_2, | ||
365 | [2] = &llc_stat_state_trans_end, | ||
366 | }; | ||
367 | |||
368 | /* UP STATE transitions */ | ||
369 | /* state transition for LLC_STATION_EV_DISABLE_REQ event */ | ||
370 | static llc_station_action_t llc_stat_up_state_actions_1[] = { | ||
371 | [0] = llc_station_ac_report_status, /* STATION DOWN */ | ||
372 | [1] = NULL, | ||
373 | }; | ||
374 | |||
375 | static struct llc_station_state_trans llc_stat_up_state_trans_1 = { | ||
376 | .ev = llc_stat_ev_disable_req, | ||
377 | .next_state = LLC_STATION_STATE_DOWN, | ||
378 | .ev_actions = llc_stat_up_state_actions_1, | ||
379 | }; | ||
380 | |||
381 | /* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */ | ||
382 | static llc_station_action_t llc_stat_up_state_actions_2[] = { | ||
383 | [0] = llc_station_ac_send_xid_r, | ||
384 | [1] = NULL, | ||
385 | }; | ||
386 | |||
387 | static struct llc_station_state_trans llc_stat_up_state_trans_2 = { | ||
388 | .ev = llc_stat_ev_rx_null_dsap_xid_c, | ||
389 | .next_state = LLC_STATION_STATE_UP, | ||
390 | .ev_actions = llc_stat_up_state_actions_2, | ||
391 | }; | ||
392 | |||
393 | /* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */ | ||
394 | static llc_station_action_t llc_stat_up_state_actions_3[] = { | ||
395 | [0] = llc_station_ac_send_test_r, | ||
396 | [1] = NULL, | ||
397 | }; | ||
398 | |||
399 | static struct llc_station_state_trans llc_stat_up_state_trans_3 = { | ||
400 | .ev = llc_stat_ev_rx_null_dsap_test_c, | ||
401 | .next_state = LLC_STATION_STATE_UP, | ||
402 | .ev_actions = llc_stat_up_state_actions_3, | ||
403 | }; | ||
404 | |||
405 | /* array of pointers; one to each transition */ | ||
406 | static struct llc_station_state_trans *llc_stat_up_state_trans [] = { | ||
407 | [0] = &llc_stat_up_state_trans_1, | ||
408 | [1] = &llc_stat_up_state_trans_2, | ||
409 | [2] = &llc_stat_up_state_trans_3, | ||
410 | [3] = &llc_stat_state_trans_end, | ||
411 | }; | ||
412 | |||
413 | /* DUP ADDR CHK STATE transitions */ | ||
414 | /* state transition for LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ | ||
415 | * event | ||
416 | */ | ||
417 | static llc_station_action_t llc_stat_dupaddr_state_actions_1[] = { | ||
418 | [0] = llc_station_ac_inc_xid_r_cnt_by_1, | ||
419 | [1] = NULL, | ||
420 | }; | ||
421 | |||
422 | static struct llc_station_state_trans llc_stat_dupaddr_state_trans_1 = { | ||
423 | .ev = llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq, | ||
424 | .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, | ||
425 | .ev_actions = llc_stat_dupaddr_state_actions_1, | ||
426 | }; | ||
427 | |||
428 | /* state transition for LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ | ||
429 | * event | ||
430 | */ | ||
431 | static llc_station_action_t llc_stat_dupaddr_state_actions_2[] = { | ||
432 | [0] = llc_station_ac_report_status, /* DUPLICATE ADDRESS FOUND */ | ||
433 | [1] = NULL, | ||
434 | }; | ||
435 | |||
436 | static struct llc_station_state_trans llc_stat_dupaddr_state_trans_2 = { | ||
437 | .ev = llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq, | ||
438 | .next_state = LLC_STATION_STATE_DOWN, | ||
439 | .ev_actions = llc_stat_dupaddr_state_actions_2, | ||
440 | }; | ||
441 | |||
442 | /* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */ | ||
443 | static llc_station_action_t llc_stat_dupaddr_state_actions_3[] = { | ||
444 | [0] = llc_station_ac_send_xid_r, | ||
445 | [1] = NULL, | ||
446 | }; | ||
447 | |||
448 | static struct llc_station_state_trans llc_stat_dupaddr_state_trans_3 = { | ||
449 | .ev = llc_stat_ev_rx_null_dsap_xid_c, | ||
450 | .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, | ||
451 | .ev_actions = llc_stat_dupaddr_state_actions_3, | ||
452 | }; | ||
453 | |||
454 | /* state transition for LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY | ||
455 | * event | ||
456 | */ | ||
457 | static llc_station_action_t llc_stat_dupaddr_state_actions_4[] = { | ||
458 | [0] = llc_station_ac_start_ack_timer, | ||
459 | [1] = llc_station_ac_inc_retry_cnt_by_1, | ||
460 | [2] = llc_station_ac_set_xid_r_cnt_0, | ||
461 | [3] = llc_station_ac_send_null_dsap_xid_c, | ||
462 | [4] = NULL, | ||
463 | }; | ||
464 | |||
465 | static struct llc_station_state_trans llc_stat_dupaddr_state_trans_4 = { | ||
466 | .ev = llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry, | ||
467 | .next_state = LLC_STATION_STATE_DUP_ADDR_CHK, | ||
468 | .ev_actions = llc_stat_dupaddr_state_actions_4, | ||
469 | }; | ||
470 | |||
471 | /* state transition for LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY | ||
472 | * event | ||
473 | */ | ||
474 | static llc_station_action_t llc_stat_dupaddr_state_actions_5[] = { | ||
475 | [0] = llc_station_ac_report_status, /* STATION UP */ | ||
476 | [1] = NULL, | ||
477 | }; | ||
478 | |||
479 | static struct llc_station_state_trans llc_stat_dupaddr_state_trans_5 = { | ||
480 | .ev = llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry, | ||
481 | .next_state = LLC_STATION_STATE_UP, | ||
482 | .ev_actions = llc_stat_dupaddr_state_actions_5, | ||
483 | }; | ||
484 | |||
485 | /* state transition for LLC_STATION_EV_DISABLE_REQ event */ | ||
486 | static llc_station_action_t llc_stat_dupaddr_state_actions_6[] = { | ||
487 | [0] = llc_station_ac_report_status, /* STATION DOWN */ | ||
488 | [1] = NULL, | ||
489 | }; | ||
490 | |||
491 | static struct llc_station_state_trans llc_stat_dupaddr_state_trans_6 = { | ||
492 | .ev = llc_stat_ev_disable_req, | ||
493 | .next_state = LLC_STATION_STATE_DOWN, | ||
494 | .ev_actions = llc_stat_dupaddr_state_actions_6, | ||
495 | }; | ||
496 | |||
497 | /* array of pointers; one to each transition */ | ||
498 | static struct llc_station_state_trans *llc_stat_dupaddr_state_trans[] = { | ||
499 | [0] = &llc_stat_dupaddr_state_trans_6, /* Request */ | ||
500 | [1] = &llc_stat_dupaddr_state_trans_4, /* Timer */ | ||
501 | [2] = &llc_stat_dupaddr_state_trans_5, | ||
502 | [3] = &llc_stat_dupaddr_state_trans_1, /* Receive frame */ | ||
503 | [4] = &llc_stat_dupaddr_state_trans_2, | ||
504 | [5] = &llc_stat_dupaddr_state_trans_3, | ||
505 | [6] = &llc_stat_state_trans_end, | ||
506 | }; | ||
507 | |||
508 | static struct llc_station_state | ||
509 | llc_station_state_table[LLC_NBR_STATION_STATES] = { | ||
510 | [LLC_STATION_STATE_DOWN - 1] = { | ||
511 | .curr_state = LLC_STATION_STATE_DOWN, | ||
512 | .transitions = llc_stat_dwn_state_trans, | ||
513 | }, | ||
514 | [LLC_STATION_STATE_DUP_ADDR_CHK - 1] = { | ||
515 | .curr_state = LLC_STATION_STATE_DUP_ADDR_CHK, | ||
516 | .transitions = llc_stat_dupaddr_state_trans, | ||
517 | }, | ||
518 | [LLC_STATION_STATE_UP - 1] = { | ||
519 | .curr_state = LLC_STATION_STATE_UP, | ||
520 | .transitions = llc_stat_up_state_trans, | ||
521 | }, | ||
522 | }; | ||
523 | |||
524 | /** | ||
525 | * llc_exec_station_trans_actions - executes actions for transition | ||
526 | * @trans: Address of the transition | ||
527 | * @skb: Address of the event that caused the transition | ||
528 | * | ||
529 | * Executes actions of a transition of the station state machine. Returns | ||
530 | * 0 if all actions complete successfully, nonzero otherwise. | ||
531 | */ | ||
532 | static u16 llc_exec_station_trans_actions(struct llc_station_state_trans *trans, | ||
533 | struct sk_buff *skb) | ||
534 | { | ||
535 | u16 rc = 0; | ||
536 | llc_station_action_t *next_action = trans->ev_actions; | ||
537 | |||
538 | for (; next_action && *next_action; next_action++) | ||
539 | if ((*next_action)(skb)) | ||
540 | rc = 1; | ||
541 | return rc; | ||
542 | } | ||
543 | |||
544 | /** | ||
545 | * llc_find_station_trans - finds transition for this event | ||
546 | * @skb: Address of the event | ||
547 | * | ||
548 | * Search thru events of the current state of the station until list | ||
549 | * exhausted or it's obvious that the event is not valid for the current | ||
550 | * state. Returns the address of the transition if cound, %NULL otherwise. | ||
551 | */ | ||
552 | static struct llc_station_state_trans * | ||
553 | llc_find_station_trans(struct sk_buff *skb) | ||
554 | { | ||
555 | int i = 0; | ||
556 | struct llc_station_state_trans *rc = NULL; | ||
557 | struct llc_station_state_trans **next_trans; | ||
558 | struct llc_station_state *curr_state = | ||
559 | &llc_station_state_table[llc_main_station.state - 1]; | ||
560 | |||
561 | for (next_trans = curr_state->transitions; next_trans[i]->ev; i++) | ||
562 | if (!next_trans[i]->ev(skb)) { | ||
563 | rc = next_trans[i]; | ||
564 | break; | ||
565 | } | ||
566 | return rc; | ||
567 | } | ||
568 | |||
569 | /** | ||
570 | * llc_station_free_ev - frees an event | ||
571 | * @skb: Address of the event | ||
572 | * | ||
573 | * Frees an event. | ||
574 | */ | ||
575 | static void llc_station_free_ev(struct sk_buff *skb) | ||
576 | { | ||
577 | struct llc_station_state_ev *ev = llc_station_ev(skb); | ||
578 | |||
579 | if (ev->type == LLC_STATION_EV_TYPE_PDU) | ||
580 | kfree_skb(skb); | ||
581 | } | ||
582 | |||
583 | /** | ||
584 | * llc_station_next_state - processes event and goes to the next state | ||
585 | * @skb: Address of the event | ||
586 | * | ||
587 | * Processes an event, executes any transitions related to that event and | ||
588 | * updates the state of the station. | ||
589 | */ | ||
590 | static u16 llc_station_next_state(struct sk_buff *skb) | ||
591 | { | ||
592 | u16 rc = 1; | ||
593 | struct llc_station_state_trans *trans; | ||
594 | |||
595 | if (llc_main_station.state > LLC_NBR_STATION_STATES) | ||
596 | goto out; | ||
597 | trans = llc_find_station_trans(skb); | ||
598 | if (trans) { | ||
599 | /* got the state to which we next transition; perform the | ||
600 | * actions associated with this transition before actually | ||
601 | * transitioning to the next state | ||
602 | */ | ||
603 | rc = llc_exec_station_trans_actions(trans, skb); | ||
604 | if (!rc) | ||
605 | /* transition station to next state if all actions | ||
606 | * execute successfully; done; wait for next event | ||
607 | */ | ||
608 | llc_main_station.state = trans->next_state; | ||
609 | } else | ||
610 | /* event not recognized in current state; re-queue it for | ||
611 | * processing again at a later time; return failure | ||
612 | */ | ||
613 | rc = 0; | ||
614 | out: | ||
615 | llc_station_free_ev(skb); | ||
616 | return rc; | ||
617 | } | ||
618 | |||
619 | /** | ||
620 | * llc_station_service_events - service events in the queue | ||
621 | * | ||
622 | * Get an event from the station event queue (if any); attempt to service | ||
623 | * the event; if event serviced, get the next event (if any) on the event | ||
624 | * queue; if event not service, re-queue the event on the event queue and | ||
625 | * attempt to service the next event; when serviced all events in queue, | ||
626 | * finished; if don't transition to different state, just service all | ||
627 | * events once; if transition to new state, service all events again. | ||
628 | * Caller must hold llc_main_station.ev_q.lock. | ||
629 | */ | ||
630 | static void llc_station_service_events(void) | ||
631 | { | ||
632 | struct sk_buff *skb; | ||
633 | |||
634 | while ((skb = skb_dequeue(&llc_main_station.ev_q.list)) != NULL) | ||
635 | llc_station_next_state(skb); | ||
636 | } | ||
637 | |||
638 | /** | ||
639 | * llc_station_state_process: queue event and try to process queue. | ||
640 | * @skb: Address of the event | ||
641 | * | ||
642 | * Queues an event (on the station event queue) for handling by the | ||
643 | * station state machine and attempts to process any queued-up events. | ||
644 | */ | ||
645 | static void llc_station_state_process(struct sk_buff *skb) | ||
646 | { | ||
647 | spin_lock_bh(&llc_main_station.ev_q.lock); | ||
648 | skb_queue_tail(&llc_main_station.ev_q.list, skb); | ||
649 | llc_station_service_events(); | ||
650 | spin_unlock_bh(&llc_main_station.ev_q.lock); | ||
651 | } | ||
652 | |||
653 | static void llc_station_ack_tmr_cb(unsigned long timeout_data) | ||
654 | { | ||
655 | struct sk_buff *skb = alloc_skb(0, GFP_ATOMIC); | ||
656 | |||
657 | if (skb) { | ||
658 | struct llc_station_state_ev *ev = llc_station_ev(skb); | ||
659 | |||
660 | ev->type = LLC_STATION_EV_TYPE_ACK_TMR; | ||
661 | llc_station_state_process(skb); | ||
662 | } | ||
663 | } | ||
664 | |||
665 | /* | ||
666 | * llc_station_rcv - send received pdu to the station state machine | ||
667 | * @skb: received frame. | ||
668 | * | ||
669 | * Sends data unit to station state machine. | ||
670 | */ | ||
671 | static void llc_station_rcv(struct sk_buff *skb) | ||
672 | { | ||
673 | struct llc_station_state_ev *ev = llc_station_ev(skb); | ||
674 | |||
675 | ev->type = LLC_STATION_EV_TYPE_PDU; | ||
676 | ev->reason = 0; | ||
677 | llc_station_state_process(skb); | ||
678 | } | ||
679 | |||
680 | int __init llc_station_init(void) | ||
681 | { | ||
682 | u16 rc = -ENOBUFS; | ||
683 | struct sk_buff *skb; | ||
684 | struct llc_station_state_ev *ev; | ||
685 | |||
686 | skb_queue_head_init(&llc_main_station.mac_pdu_q); | ||
687 | skb_queue_head_init(&llc_main_station.ev_q.list); | ||
688 | spin_lock_init(&llc_main_station.ev_q.lock); | ||
689 | init_timer(&llc_main_station.ack_timer); | ||
690 | llc_main_station.ack_timer.data = (unsigned long)&llc_main_station; | ||
691 | llc_main_station.ack_timer.function = llc_station_ack_tmr_cb; | ||
692 | |||
693 | skb = alloc_skb(0, GFP_ATOMIC); | ||
694 | if (!skb) | ||
695 | goto out; | ||
696 | rc = 0; | ||
697 | llc_set_station_handler(llc_station_rcv); | ||
698 | ev = llc_station_ev(skb); | ||
699 | memset(ev, 0, sizeof(*ev)); | ||
700 | llc_main_station.ack_timer.expires = jiffies + 3 * HZ; | ||
701 | llc_main_station.maximum_retry = 1; | ||
702 | llc_main_station.state = LLC_STATION_STATE_DOWN; | ||
703 | ev->type = LLC_STATION_EV_TYPE_SIMPLE; | ||
704 | ev->prim_type = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK; | ||
705 | rc = llc_station_next_state(skb); | ||
706 | out: | ||
707 | return rc; | ||
708 | } | ||
709 | |||
710 | void __exit llc_station_exit(void) | ||
711 | { | ||
712 | llc_set_station_handler(NULL); | ||
713 | } | ||