Commit | Line | Data |
---|---|---|
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 | */ |
37 | struct llc_station { | |
1da177e4 LT |
38 | struct sk_buff_head mac_pdu_q; |
39 | }; | |
40 | ||
1da177e4 LT |
41 | typedef int (*llc_station_ev_t)(struct sk_buff *skb); |
42 | ||
1da177e4 LT |
43 | typedef int (*llc_station_action_t)(struct sk_buff *skb); |
44 | ||
45 | /* Station component state table structure */ | |
46 | struct llc_station_state_trans { | |
47 | llc_station_ev_t ev; | |
1da177e4 LT |
48 | llc_station_action_t *ev_actions; |
49 | }; | |
50 | ||
1da177e4 LT |
51 | static struct llc_station llc_main_station; |
52 | ||
1da177e4 LT |
53 | static 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 |
63 | static 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 | */ | |
79 | static 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 |
87 | static 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); | |
105 | out: | |
106 | return rc; | |
107 | free: | |
91d27a86 | 108 | kfree_skb(nskb); |
1da177e4 LT |
109 | goto out; |
110 | } | |
111 | ||
112 | static 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); | |
134 | out: | |
135 | return rc; | |
136 | free: | |
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 */ |
142 | static llc_station_action_t llc_stat_up_state_actions_2[] = { | |
143 | [0] = llc_station_ac_send_xid_r, | |
144 | [1] = NULL, | |
145 | }; | |
146 | ||
147 | static 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 */ | |
153 | static llc_station_action_t llc_stat_up_state_actions_3[] = { | |
154 | [0] = llc_station_ac_send_test_r, | |
155 | [1] = NULL, | |
156 | }; | |
157 | ||
158 | static 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 */ | |
164 | static 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 | */ | |
178 | static 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 | */ | |
198 | static 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 | */ | |
219 | static 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 | 229 | void __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 | 235 | void llc_station_exit(void) |
1da177e4 LT |
236 | { |
237 | llc_set_station_handler(NULL); | |
238 | } |