diff options
Diffstat (limited to 'drivers/uwb/wlp/txrx.c')
-rw-r--r-- | drivers/uwb/wlp/txrx.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/drivers/uwb/wlp/txrx.c b/drivers/uwb/wlp/txrx.c new file mode 100644 index 000000000000..c701bd1a2887 --- /dev/null +++ b/drivers/uwb/wlp/txrx.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /* | ||
2 | * WiMedia Logical Link Control Protocol (WLP) | ||
3 | * Message exchange infrastructure | ||
4 | * | ||
5 | * Copyright (C) 2007 Intel Corporation | ||
6 | * Reinette Chatre <reinette.chatre@intel.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License version | ||
10 | * 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
20 | * 02110-1301, USA. | ||
21 | * | ||
22 | * | ||
23 | * FIXME: Docs | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include <linux/etherdevice.h> | ||
28 | #include <linux/wlp.h> | ||
29 | #define D_LOCAL 5 | ||
30 | #include <linux/uwb/debug.h> | ||
31 | #include "wlp-internal.h" | ||
32 | |||
33 | |||
34 | /** | ||
35 | * Direct incoming association msg to correct parsing routine | ||
36 | * | ||
37 | * We only expect D1, E1, C1, C3 messages as new. All other incoming | ||
38 | * association messages should form part of an established session that is | ||
39 | * handled elsewhere. | ||
40 | * The handling of these messages often require calling sleeping functions | ||
41 | * - this cannot be done in interrupt context. We use the kernel's | ||
42 | * workqueue to handle these messages. | ||
43 | */ | ||
44 | static | ||
45 | void wlp_direct_assoc_frame(struct wlp *wlp, struct sk_buff *skb, | ||
46 | struct uwb_dev_addr *src) | ||
47 | { | ||
48 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
49 | struct wlp_frame_assoc *assoc = (void *) skb->data; | ||
50 | struct wlp_assoc_frame_ctx *frame_ctx; | ||
51 | d_fnstart(5, dev, "wlp %p, skb %p\n", wlp, skb); | ||
52 | frame_ctx = kmalloc(sizeof(*frame_ctx), GFP_ATOMIC); | ||
53 | if (frame_ctx == NULL) { | ||
54 | dev_err(dev, "WLP: Unable to allocate memory for association " | ||
55 | "frame handling.\n"); | ||
56 | kfree_skb(skb); | ||
57 | goto out; | ||
58 | } | ||
59 | frame_ctx->wlp = wlp; | ||
60 | frame_ctx->skb = skb; | ||
61 | frame_ctx->src = *src; | ||
62 | switch (assoc->type) { | ||
63 | case WLP_ASSOC_D1: | ||
64 | d_printf(5, dev, "Received a D1 frame.\n"); | ||
65 | INIT_WORK(&frame_ctx->ws, wlp_handle_d1_frame); | ||
66 | schedule_work(&frame_ctx->ws); | ||
67 | break; | ||
68 | case WLP_ASSOC_E1: | ||
69 | d_printf(5, dev, "Received a E1 frame. FIXME?\n"); | ||
70 | kfree_skb(skb); /* Temporary until we handle it */ | ||
71 | kfree(frame_ctx); /* Temporary until we handle it */ | ||
72 | break; | ||
73 | case WLP_ASSOC_C1: | ||
74 | d_printf(5, dev, "Received a C1 frame.\n"); | ||
75 | INIT_WORK(&frame_ctx->ws, wlp_handle_c1_frame); | ||
76 | schedule_work(&frame_ctx->ws); | ||
77 | break; | ||
78 | case WLP_ASSOC_C3: | ||
79 | d_printf(5, dev, "Received a C3 frame.\n"); | ||
80 | INIT_WORK(&frame_ctx->ws, wlp_handle_c3_frame); | ||
81 | schedule_work(&frame_ctx->ws); | ||
82 | break; | ||
83 | default: | ||
84 | dev_err(dev, "Received unexpected association frame. " | ||
85 | "Type = %d \n", assoc->type); | ||
86 | kfree_skb(skb); | ||
87 | kfree(frame_ctx); | ||
88 | break; | ||
89 | } | ||
90 | out: | ||
91 | d_fnend(5, dev, "wlp %p\n", wlp); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Process incoming association frame | ||
96 | * | ||
97 | * Although it could be possible to deal with some incoming association | ||
98 | * messages without creating a new session we are keeping things simple. We | ||
99 | * do not accept new association messages if there is a session in progress | ||
100 | * and the messages do not belong to that session. | ||
101 | * | ||
102 | * If an association message arrives that causes the creation of a session | ||
103 | * (WLP_ASSOC_E1) while we are in the process of creating a session then we | ||
104 | * rely on the neighbor mutex to protect the data. That is, the new session | ||
105 | * will not be started until the previous is completed. | ||
106 | */ | ||
107 | static | ||
108 | void wlp_receive_assoc_frame(struct wlp *wlp, struct sk_buff *skb, | ||
109 | struct uwb_dev_addr *src) | ||
110 | { | ||
111 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
112 | struct wlp_frame_assoc *assoc = (void *) skb->data; | ||
113 | struct wlp_session *session = wlp->session; | ||
114 | u8 version; | ||
115 | d_fnstart(5, dev, "wlp %p, skb %p\n", wlp, skb); | ||
116 | |||
117 | if (wlp_get_version(wlp, &assoc->version, &version, | ||
118 | sizeof(assoc->version)) < 0) | ||
119 | goto error; | ||
120 | if (version != WLP_VERSION) { | ||
121 | dev_err(dev, "Unsupported WLP version in association " | ||
122 | "message.\n"); | ||
123 | goto error; | ||
124 | } | ||
125 | if (session != NULL) { | ||
126 | /* Function that created this session is still holding the | ||
127 | * &wlp->mutex to protect this session. */ | ||
128 | if (assoc->type == session->exp_message || | ||
129 | assoc->type == WLP_ASSOC_F0) { | ||
130 | if (!memcmp(&session->neighbor_addr, src, | ||
131 | sizeof(*src))) { | ||
132 | session->data = skb; | ||
133 | (session->cb)(wlp); | ||
134 | } else { | ||
135 | dev_err(dev, "Received expected message from " | ||
136 | "unexpected source. Expected message " | ||
137 | "%d or F0 from %02x:%02x, but received " | ||
138 | "it from %02x:%02x. Dropping.\n", | ||
139 | session->exp_message, | ||
140 | session->neighbor_addr.data[1], | ||
141 | session->neighbor_addr.data[0], | ||
142 | src->data[1], src->data[0]); | ||
143 | goto error; | ||
144 | } | ||
145 | } else { | ||
146 | dev_err(dev, "Association already in progress. " | ||
147 | "Dropping.\n"); | ||
148 | goto error; | ||
149 | } | ||
150 | } else { | ||
151 | wlp_direct_assoc_frame(wlp, skb, src); | ||
152 | } | ||
153 | d_fnend(5, dev, "wlp %p\n", wlp); | ||
154 | return; | ||
155 | error: | ||
156 | kfree_skb(skb); | ||
157 | d_fnend(5, dev, "wlp %p\n", wlp); | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * Verify incoming frame is from connected neighbor, prep to pass to WLP client | ||
162 | * | ||
163 | * Verification proceeds according to WLP 0.99 [7.3.1]. The source address | ||
164 | * is used to determine which neighbor is sending the frame and the WSS tag | ||
165 | * is used to know to which WSS the frame belongs (we only support one WSS | ||
166 | * so this test is straight forward). | ||
167 | * With the WSS found we need to ensure that we are connected before | ||
168 | * allowing the exchange of data frames. | ||
169 | */ | ||
170 | static | ||
171 | int wlp_verify_prep_rx_frame(struct wlp *wlp, struct sk_buff *skb, | ||
172 | struct uwb_dev_addr *src) | ||
173 | { | ||
174 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
175 | int result = -EINVAL; | ||
176 | struct wlp_eda_node eda_entry; | ||
177 | struct wlp_frame_std_abbrv_hdr *hdr = (void *) skb->data; | ||
178 | |||
179 | d_fnstart(6, dev, "wlp %p, skb %p \n", wlp, skb); | ||
180 | /*verify*/ | ||
181 | result = wlp_copy_eda_node(&wlp->eda, src, &eda_entry); | ||
182 | if (result < 0) { | ||
183 | if (printk_ratelimit()) | ||
184 | dev_err(dev, "WLP: Incoming frame is from unknown " | ||
185 | "neighbor %02x:%02x.\n", src->data[1], | ||
186 | src->data[0]); | ||
187 | goto out; | ||
188 | } | ||
189 | if (hdr->tag != eda_entry.tag) { | ||
190 | if (printk_ratelimit()) | ||
191 | dev_err(dev, "WLP: Tag of incoming frame from " | ||
192 | "%02x:%02x does not match expected tag. " | ||
193 | "Received 0x%02x, expected 0x%02x. \n", | ||
194 | src->data[1], src->data[0], hdr->tag, | ||
195 | eda_entry.tag); | ||
196 | result = -EINVAL; | ||
197 | goto out; | ||
198 | } | ||
199 | if (eda_entry.state != WLP_WSS_CONNECTED) { | ||
200 | if (printk_ratelimit()) | ||
201 | dev_err(dev, "WLP: Incoming frame from " | ||
202 | "%02x:%02x does is not from connected WSS.\n", | ||
203 | src->data[1], src->data[0]); | ||
204 | result = -EINVAL; | ||
205 | goto out; | ||
206 | } | ||
207 | /*prep*/ | ||
208 | skb_pull(skb, sizeof(*hdr)); | ||
209 | out: | ||
210 | d_fnend(6, dev, "wlp %p, skb %p, result = %d \n", wlp, skb, result); | ||
211 | return result; | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * Receive a WLP frame from device | ||
216 | * | ||
217 | * @returns: 1 if calling function should free the skb | ||
218 | * 0 if it successfully handled skb and freed it | ||
219 | * 0 if error occured, will free skb in this case | ||
220 | */ | ||
221 | int wlp_receive_frame(struct device *dev, struct wlp *wlp, struct sk_buff *skb, | ||
222 | struct uwb_dev_addr *src) | ||
223 | { | ||
224 | unsigned len = skb->len; | ||
225 | void *ptr = skb->data; | ||
226 | struct wlp_frame_hdr *hdr; | ||
227 | int result = 0; | ||
228 | |||
229 | d_fnstart(6, dev, "skb (%p), len (%u)\n", skb, len); | ||
230 | if (len < sizeof(*hdr)) { | ||
231 | dev_err(dev, "Not enough data to parse WLP header.\n"); | ||
232 | result = -EINVAL; | ||
233 | goto out; | ||
234 | } | ||
235 | hdr = ptr; | ||
236 | d_dump(6, dev, hdr, sizeof(*hdr)); | ||
237 | if (le16_to_cpu(hdr->mux_hdr) != WLP_PROTOCOL_ID) { | ||
238 | dev_err(dev, "Not a WLP frame type.\n"); | ||
239 | result = -EINVAL; | ||
240 | goto out; | ||
241 | } | ||
242 | switch (hdr->type) { | ||
243 | case WLP_FRAME_STANDARD: | ||
244 | if (len < sizeof(struct wlp_frame_std_abbrv_hdr)) { | ||
245 | dev_err(dev, "Not enough data to parse Standard " | ||
246 | "WLP header.\n"); | ||
247 | goto out; | ||
248 | } | ||
249 | result = wlp_verify_prep_rx_frame(wlp, skb, src); | ||
250 | if (result < 0) { | ||
251 | if (printk_ratelimit()) | ||
252 | dev_err(dev, "WLP: Verification of frame " | ||
253 | "from neighbor %02x:%02x failed.\n", | ||
254 | src->data[1], src->data[0]); | ||
255 | goto out; | ||
256 | } | ||
257 | result = 1; | ||
258 | break; | ||
259 | case WLP_FRAME_ABBREVIATED: | ||
260 | dev_err(dev, "Abbreviated frame received. FIXME?\n"); | ||
261 | kfree_skb(skb); | ||
262 | break; | ||
263 | case WLP_FRAME_CONTROL: | ||
264 | dev_err(dev, "Control frame received. FIXME?\n"); | ||
265 | kfree_skb(skb); | ||
266 | break; | ||
267 | case WLP_FRAME_ASSOCIATION: | ||
268 | if (len < sizeof(struct wlp_frame_assoc)) { | ||
269 | dev_err(dev, "Not enough data to parse Association " | ||
270 | "WLP header.\n"); | ||
271 | goto out; | ||
272 | } | ||
273 | d_printf(5, dev, "Association frame received.\n"); | ||
274 | wlp_receive_assoc_frame(wlp, skb, src); | ||
275 | break; | ||
276 | default: | ||
277 | dev_err(dev, "Invalid frame received.\n"); | ||
278 | result = -EINVAL; | ||
279 | break; | ||
280 | } | ||
281 | out: | ||
282 | if (result < 0) { | ||
283 | kfree_skb(skb); | ||
284 | result = 0; | ||
285 | } | ||
286 | d_fnend(6, dev, "skb (%p)\n", skb); | ||
287 | return result; | ||
288 | } | ||
289 | EXPORT_SYMBOL_GPL(wlp_receive_frame); | ||
290 | |||
291 | |||
292 | /** | ||
293 | * Verify frame from network stack, prepare for further transmission | ||
294 | * | ||
295 | * @skb: the socket buffer that needs to be prepared for transmission (it | ||
296 | * is in need of a WLP header). If this is a broadcast frame we take | ||
297 | * over the entire transmission. | ||
298 | * If it is a unicast the WSS connection should already be established | ||
299 | * and transmission will be done by the calling function. | ||
300 | * @dst: On return this will contain the device address to which the | ||
301 | * frame is destined. | ||
302 | * @returns: 0 on success no tx : WLP header sucessfully applied to skb buffer, | ||
303 | * calling function can proceed with tx | ||
304 | * 1 on success with tx : WLP will take over transmission of this | ||
305 | * frame | ||
306 | * <0 on error | ||
307 | * | ||
308 | * The network stack (WLP client) is attempting to transmit a frame. We can | ||
309 | * only transmit data if a local WSS is at least active (connection will be | ||
310 | * done here if this is a broadcast frame and neighbor also has the WSS | ||
311 | * active). | ||
312 | * | ||
313 | * The frame can be either broadcast or unicast. Broadcast in a WSS is | ||
314 | * supported via multicast, but we don't support multicast yet (until | ||
315 | * devices start to support MAB IEs). If a broadcast frame needs to be | ||
316 | * transmitted it is treated as a unicast frame to each neighbor. In this | ||
317 | * case the WLP takes over transmission of the skb and returns 1 | ||
318 | * to the caller to indicate so. Also, in this case, if a neighbor has the | ||
319 | * same WSS activated but is not connected then the WSS connection will be | ||
320 | * done at this time. The neighbor's virtual address will be learned at | ||
321 | * this time. | ||
322 | * | ||
323 | * The destination address in a unicast frame is the virtual address of the | ||
324 | * neighbor. This address only becomes known when a WSS connection is | ||
325 | * established. We thus rely on a broadcast frame to trigger the setup of | ||
326 | * WSS connections to all neighbors before we are able to send unicast | ||
327 | * frames to them. This seems reasonable as IP would usually use ARP first | ||
328 | * before any unicast frames are sent. | ||
329 | * | ||
330 | * If we are already connected to the neighbor (neighbor's virtual address | ||
331 | * is known) we just prepare the WLP header and the caller will continue to | ||
332 | * send the frame. | ||
333 | * | ||
334 | * A failure in this function usually indicates something that cannot be | ||
335 | * fixed automatically. So, if this function fails (@return < 0) the calling | ||
336 | * function should not retry to send the frame as it will very likely keep | ||
337 | * failing. | ||
338 | * | ||
339 | */ | ||
340 | int wlp_prepare_tx_frame(struct device *dev, struct wlp *wlp, | ||
341 | struct sk_buff *skb, struct uwb_dev_addr *dst) | ||
342 | { | ||
343 | int result = -EINVAL; | ||
344 | struct ethhdr *eth_hdr = (void *) skb->data; | ||
345 | |||
346 | d_fnstart(6, dev, "wlp (%p), skb (%p) \n", wlp, skb); | ||
347 | if (is_broadcast_ether_addr(eth_hdr->h_dest)) { | ||
348 | d_printf(6, dev, "WLP: handling broadcast frame. \n"); | ||
349 | result = wlp_eda_for_each(&wlp->eda, wlp_wss_send_copy, skb); | ||
350 | if (result < 0) { | ||
351 | if (printk_ratelimit()) | ||
352 | dev_err(dev, "Unable to handle broadcast " | ||
353 | "frame from WLP client.\n"); | ||
354 | goto out; | ||
355 | } | ||
356 | dev_kfree_skb_irq(skb); | ||
357 | result = 1; | ||
358 | /* Frame will be transmitted by WLP. */ | ||
359 | } else { | ||
360 | d_printf(6, dev, "WLP: handling unicast frame. \n"); | ||
361 | result = wlp_eda_for_virtual(&wlp->eda, eth_hdr->h_dest, dst, | ||
362 | wlp_wss_prep_hdr, skb); | ||
363 | if (unlikely(result < 0)) { | ||
364 | if (printk_ratelimit()) | ||
365 | dev_err(dev, "Unable to prepare " | ||
366 | "skb for transmission. \n"); | ||
367 | goto out; | ||
368 | } | ||
369 | } | ||
370 | out: | ||
371 | d_fnend(6, dev, "wlp (%p), skb (%p). result = %d \n", wlp, skb, result); | ||
372 | return result; | ||
373 | } | ||
374 | EXPORT_SYMBOL_GPL(wlp_prepare_tx_frame); | ||