blob: 917d700791ba50beafbad41a76e3816cc4ba047a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/init.h>
15#include <linux/module.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090016#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#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 *
Ben Hutchings2c530402012-07-10 10:55:09 +000033 * @mac_sa: MAC source address
34 * @sap_list: list of related SAPs
35 * @ev_q: events entering state mach.
36 * @mac_pdu_q: PDUs ready to send to MAC
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 */
38struct llc_station {
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 struct {
40 struct sk_buff_head list;
41 spinlock_t lock;
42 } ev_q;
43 struct sk_buff_head mac_pdu_q;
44};
45
Linus Torvalds1da177e2005-04-16 15:20:36 -070046typedef int (*llc_station_ev_t)(struct sk_buff *skb);
47
Linus Torvalds1da177e2005-04-16 15:20:36 -070048typedef int (*llc_station_action_t)(struct sk_buff *skb);
49
50/* Station component state table structure */
51struct llc_station_state_trans {
52 llc_station_ev_t ev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 llc_station_action_t *ev_actions;
54};
55
Linus Torvalds1da177e2005-04-16 15:20:36 -070056static struct llc_station llc_main_station;
57
Linus Torvalds1da177e2005-04-16 15:20:36 -070058static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb)
59{
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
61
Ben Hutchings025e3632012-09-15 17:11:18 +000062 return LLC_PDU_IS_CMD(pdu) && /* command PDU */
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
64 LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID &&
65 !pdu->dsap ? 0 : 1; /* NULL DSAP value */
66}
67
Linus Torvalds1da177e2005-04-16 15:20:36 -070068static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb)
69{
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
71
Ben Hutchings025e3632012-09-15 17:11:18 +000072 return LLC_PDU_IS_CMD(pdu) && /* command PDU */
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
74 LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST &&
75 !pdu->dsap ? 0 : 1; /* NULL DSAP */
76}
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078/**
79 * llc_station_send_pdu - queues PDU to send
80 * @skb: Address of the PDU
81 *
82 * Queues a PDU to send to the MAC layer.
83 */
84static void llc_station_send_pdu(struct sk_buff *skb)
85{
86 skb_queue_tail(&llc_main_station.mac_pdu_q, skb);
87 while ((skb = skb_dequeue(&llc_main_station.mac_pdu_q)) != NULL)
88 if (dev_queue_xmit(skb))
89 break;
90}
91
Linus Torvalds1da177e2005-04-16 15:20:36 -070092static int llc_station_ac_send_xid_r(struct sk_buff *skb)
93{
94 u8 mac_da[ETH_ALEN], dsap;
95 int rc = 1;
Joonwoo Parkf83f1762008-03-31 21:02:47 -070096 struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U,
97 sizeof(struct llc_xid_info));
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
99 if (!nskb)
100 goto out;
101 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 llc_pdu_decode_sa(skb, mac_da);
103 llc_pdu_decode_ssap(skb, &dsap);
104 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
105 llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127);
Joonwoo Parka5a04812008-03-28 16:28:36 -0700106 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da);
Arnaldo Carvalho de Melo249ff1c2005-09-22 04:32:10 -0300107 if (unlikely(rc))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 goto free;
109 llc_station_send_pdu(nskb);
110out:
111 return rc;
112free:
Sorin Dumitru91d27a82012-08-06 02:35:58 +0000113 kfree_skb(nskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 goto out;
115}
116
117static int llc_station_ac_send_test_r(struct sk_buff *skb)
118{
119 u8 mac_da[ETH_ALEN], dsap;
120 int rc = 1;
Joonwoo Parkf83f1762008-03-31 21:02:47 -0700121 u32 data_size;
122 struct sk_buff *nskb;
123
124 /* The test request command is type U (llc_len = 3) */
125 data_size = ntohs(eth_hdr(skb)->h_proto) - 3;
126 nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, data_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
128 if (!nskb)
129 goto out;
130 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 llc_pdu_decode_sa(skb, mac_da);
132 llc_pdu_decode_ssap(skb, &dsap);
133 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
YOSHIFUJI Hideakid57b1862007-02-09 23:25:01 +0900134 llc_pdu_init_as_test_rsp(nskb, skb);
Joonwoo Parka5a04812008-03-28 16:28:36 -0700135 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da);
Arnaldo Carvalho de Melo249ff1c2005-09-22 04:32:10 -0300136 if (unlikely(rc))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 goto free;
138 llc_station_send_pdu(nskb);
139out:
140 return rc;
141free:
Sorin Dumitru91d27a82012-08-06 02:35:58 +0000142 kfree_skb(nskb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 goto out;
144}
145
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146/* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */
147static llc_station_action_t llc_stat_up_state_actions_2[] = {
148 [0] = llc_station_ac_send_xid_r,
149 [1] = NULL,
150};
151
152static struct llc_station_state_trans llc_stat_up_state_trans_2 = {
153 .ev = llc_stat_ev_rx_null_dsap_xid_c,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 .ev_actions = llc_stat_up_state_actions_2,
155};
156
157/* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */
158static llc_station_action_t llc_stat_up_state_actions_3[] = {
159 [0] = llc_station_ac_send_test_r,
160 [1] = NULL,
161};
162
163static struct llc_station_state_trans llc_stat_up_state_trans_3 = {
164 .ev = llc_stat_ev_rx_null_dsap_test_c,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 .ev_actions = llc_stat_up_state_actions_3,
166};
167
168/* array of pointers; one to each transition */
169static struct llc_station_state_trans *llc_stat_up_state_trans [] = {
Ben Hutchings025e3632012-09-15 17:11:18 +0000170 &llc_stat_up_state_trans_2,
171 &llc_stat_up_state_trans_3,
172 NULL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173};
174
175/**
176 * llc_exec_station_trans_actions - executes actions for transition
177 * @trans: Address of the transition
178 * @skb: Address of the event that caused the transition
179 *
180 * Executes actions of a transition of the station state machine. Returns
181 * 0 if all actions complete successfully, nonzero otherwise.
182 */
183static u16 llc_exec_station_trans_actions(struct llc_station_state_trans *trans,
184 struct sk_buff *skb)
185{
186 u16 rc = 0;
187 llc_station_action_t *next_action = trans->ev_actions;
188
189 for (; next_action && *next_action; next_action++)
190 if ((*next_action)(skb))
191 rc = 1;
192 return rc;
193}
194
195/**
196 * llc_find_station_trans - finds transition for this event
197 * @skb: Address of the event
198 *
199 * Search thru events of the current state of the station until list
200 * exhausted or it's obvious that the event is not valid for the current
201 * state. Returns the address of the transition if cound, %NULL otherwise.
202 */
203static struct llc_station_state_trans *
204 llc_find_station_trans(struct sk_buff *skb)
205{
206 int i = 0;
207 struct llc_station_state_trans *rc = NULL;
208 struct llc_station_state_trans **next_trans;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209
Ben Hutchings025e3632012-09-15 17:11:18 +0000210 for (next_trans = llc_stat_up_state_trans; next_trans[i]; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 if (!next_trans[i]->ev(skb)) {
212 rc = next_trans[i];
213 break;
214 }
215 return rc;
216}
217
218/**
219 * llc_station_free_ev - frees an event
220 * @skb: Address of the event
221 *
222 * Frees an event.
223 */
224static void llc_station_free_ev(struct sk_buff *skb)
225{
Ben Hutchings025e3632012-09-15 17:11:18 +0000226 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227}
228
229/**
230 * llc_station_next_state - processes event and goes to the next state
231 * @skb: Address of the event
232 *
233 * Processes an event, executes any transitions related to that event and
234 * updates the state of the station.
235 */
236static u16 llc_station_next_state(struct sk_buff *skb)
237{
238 u16 rc = 1;
239 struct llc_station_state_trans *trans;
240
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 trans = llc_find_station_trans(skb);
Ben Hutchings025e3632012-09-15 17:11:18 +0000242 if (trans)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 /* got the state to which we next transition; perform the
244 * actions associated with this transition before actually
245 * transitioning to the next state
246 */
247 rc = llc_exec_station_trans_actions(trans, skb);
Ben Hutchings025e3632012-09-15 17:11:18 +0000248 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 /* event not recognized in current state; re-queue it for
250 * processing again at a later time; return failure
251 */
252 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 llc_station_free_ev(skb);
254 return rc;
255}
256
257/**
258 * llc_station_service_events - service events in the queue
259 *
260 * Get an event from the station event queue (if any); attempt to service
261 * the event; if event serviced, get the next event (if any) on the event
262 * queue; if event not service, re-queue the event on the event queue and
263 * attempt to service the next event; when serviced all events in queue,
264 * finished; if don't transition to different state, just service all
265 * events once; if transition to new state, service all events again.
266 * Caller must hold llc_main_station.ev_q.lock.
267 */
268static void llc_station_service_events(void)
269{
270 struct sk_buff *skb;
271
272 while ((skb = skb_dequeue(&llc_main_station.ev_q.list)) != NULL)
273 llc_station_next_state(skb);
274}
275
276/**
Ben Hutchings2c530402012-07-10 10:55:09 +0000277 * llc_station_state_process - queue event and try to process queue.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 * @skb: Address of the event
279 *
280 * Queues an event (on the station event queue) for handling by the
281 * station state machine and attempts to process any queued-up events.
282 */
283static void llc_station_state_process(struct sk_buff *skb)
284{
285 spin_lock_bh(&llc_main_station.ev_q.lock);
286 skb_queue_tail(&llc_main_station.ev_q.list, skb);
287 llc_station_service_events();
288 spin_unlock_bh(&llc_main_station.ev_q.lock);
289}
290
Ben Hutchings2c530402012-07-10 10:55:09 +0000291/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 * llc_station_rcv - send received pdu to the station state machine
293 * @skb: received frame.
294 *
295 * Sends data unit to station state machine.
296 */
297static void llc_station_rcv(struct sk_buff *skb)
298{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 llc_station_state_process(skb);
300}
301
Ben Hutchings60249352012-08-13 02:49:59 +0000302void __init llc_station_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 skb_queue_head_init(&llc_main_station.mac_pdu_q);
305 skb_queue_head_init(&llc_main_station.ev_q.list);
306 spin_lock_init(&llc_main_station.ev_q.lock);
Ben Hutchingsaadf31d2012-08-13 02:50:55 +0000307 llc_set_station_handler(llc_station_rcv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308}
309
Ben Hutchingsf4f87202012-08-13 02:50:43 +0000310void llc_station_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311{
312 llc_set_station_handler(NULL);
313}