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