aboutsummaryrefslogtreecommitdiffstats
path: root/net/llc/llc_station.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-10-02 16:38:27 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-02 16:38:27 -0400
commitaecdc33e111b2c447b622e287c6003726daa1426 (patch)
tree3e7657eae4b785e1a1fb5dfb225dbae0b2f0cfc6 /net/llc/llc_station.c
parenta20acf99f75e49271381d65db097c9763060a1e8 (diff)
parenta3a6cab5ea10cca64d036851fe0d932448f2fe4f (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking changes from David Miller: 1) GRE now works over ipv6, from Dmitry Kozlov. 2) Make SCTP more network namespace aware, from Eric Biederman. 3) TEAM driver now works with non-ethernet devices, from Jiri Pirko. 4) Make openvswitch network namespace aware, from Pravin B Shelar. 5) IPV6 NAT implementation, from Patrick McHardy. 6) Server side support for TCP Fast Open, from Jerry Chu and others. 7) Packet BPF filter supports MOD and XOR, from Eric Dumazet and Daniel Borkmann. 8) Increate the loopback default MTU to 64K, from Eric Dumazet. 9) Use a per-task rather than per-socket page fragment allocator for outgoing networking traffic. This benefits processes that have very many mostly idle sockets, which is quite common. From Eric Dumazet. 10) Use up to 32K for page fragment allocations, with fallbacks to smaller sizes when higher order page allocations fail. Benefits are a) less segments for driver to process b) less calls to page allocator c) less waste of space. From Eric Dumazet. 11) Allow GRO to be used on GRE tunnels, from Eric Dumazet. 12) VXLAN device driver, one way to handle VLAN issues such as the limitation of 4096 VLAN IDs yet still have some level of isolation. From Stephen Hemminger. 13) As usual there is a large boatload of driver changes, with the scale perhaps tilted towards the wireless side this time around. Fix up various fairly trivial conflicts, mostly caused by the user namespace changes. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1012 commits) hyperv: Add buffer for extended info after the RNDIS response message. hyperv: Report actual status in receive completion packet hyperv: Remove extra allocated space for recv_pkt_list elements hyperv: Fix page buffer handling in rndis_filter_send_request() hyperv: Fix the missing return value in rndis_filter_set_packet_filter() hyperv: Fix the max_xfer_size in RNDIS initialization vxlan: put UDP socket in correct namespace vxlan: Depend on CONFIG_INET sfc: Fix the reported priorities of different filter types sfc: Remove EFX_FILTER_FLAG_RX_OVERRIDE_IP sfc: Fix loopback self-test with separate_tx_channels=1 sfc: Fix MCDI structure field lookup sfc: Add parentheses around use of bitfield macro arguments sfc: Fix null function pointer in efx_sriov_channel_type vxlan: virtual extensible lan igmp: export symbol ip_mc_leave_group netlink: add attributes to fdb interface tg3: unconditionally select HWMON support when tg3 is enabled. Revert "net: ti cpsw ethernet: allow reading phy interface mode from DT" gre: fix sparse warning ...
Diffstat (limited to 'net/llc/llc_station.c')
-rw-r--r--net/llc/llc_station.c600
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 */
40struct 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
55int 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
76struct 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
84static __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
90typedef 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
98typedef int (*llc_station_action_t)(struct sk_buff *skb);
99
100/* Station component state table structure */
101struct llc_station_state_trans {
102 llc_station_ev_t ev;
103 u8 next_state;
104 llc_station_action_t *ev_actions;
105};
106
107struct llc_station_state {
108 u8 curr_state;
109 struct llc_station_state_trans **transitions;
110};
111
112static struct llc_station llc_main_station;
113
114static 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
123static 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
132static 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
141static 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
150static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb) 28static 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
162static 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
175static 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
188static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb) 38static 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
200static 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 */
215static 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
223static 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
230static 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
236static 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
242static 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
248static 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
254static 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);
268out:
269 return rc;
270free:
271 kfree_skb(nskb);
272 goto out;
273}
274
275static int llc_station_ac_send_xid_r(struct sk_buff *skb) 48static 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);
293out: 66out:
294 return rc; 67 return rc;
295free: 68free:
@@ -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);
322out: 95out:
323 return rc; 96 return rc;
324free: 97free:
@@ -326,352 +99,6 @@ free:
326 goto out; 99 goto out;
327} 100}
328 101
329static 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 */
340static 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 */
345static 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
353static 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 */
360static llc_station_action_t llc_stat_down_state_actions_2[] = {
361 [0] = llc_station_ac_report_status, /* STATION UP */
362 [1] = NULL,
363};
364
365static 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 */
372static 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 */
380static llc_station_action_t llc_stat_up_state_actions_1[] = {
381 [0] = llc_station_ac_report_status, /* STATION DOWN */
382 [1] = NULL,
383};
384
385static 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 */
392static llc_station_action_t llc_stat_up_state_actions_2[] = {
393 [0] = llc_station_ac_send_xid_r,
394 [1] = NULL,
395};
396
397static 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 */
404static llc_station_action_t llc_stat_up_state_actions_3[] = {
405 [0] = llc_station_ac_send_test_r,
406 [1] = NULL,
407};
408
409static 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 */
416static 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 */
427static 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
432static 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 */
441static 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
446static 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 */
453static llc_station_action_t llc_stat_dupaddr_state_actions_3[] = {
454 [0] = llc_station_ac_send_xid_r,
455 [1] = NULL,
456};
457
458static 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 */
467static 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
475static 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 */
484static llc_station_action_t llc_stat_dupaddr_state_actions_5[] = {
485 [0] = llc_station_ac_report_status, /* STATION UP */
486 [1] = NULL,
487};
488
489static 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 */
496static llc_station_action_t llc_stat_dupaddr_state_actions_6[] = {
497 [0] = llc_station_ac_report_status, /* STATION DOWN */
498 [1] = NULL,
499};
500
501static 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 */
508static 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
518static 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 */
542static 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 */
562static 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 */
585static 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 */
600static 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;
624out:
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 */
640static 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 */
655static 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
663static 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 */
681static void llc_station_rcv(struct sk_buff *skb) 108static 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
690void __init llc_station_init(void) 117void __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