aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax/i2400m/rx.c
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2008-12-20 19:57:47 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-01-07 13:00:19 -0500
commitaa5a7acabe31ec27a212cbd25cad9f72aa476591 (patch)
tree1008c15e2288ca03580ee2b403006ad879938e51 /drivers/net/wimax/i2400m/rx.c
parent467cc396fb4665957bc7d182c96e45a4d7c575e4 (diff)
i2400m: RX and TX data/control paths
Handling of TX/RX data to/from the i2400m device (IP packets, control and diagnostics). On RX, this parses the received read transaction from the device, breaks it in chunks and passes it to the corresponding subsystems (network and control). Transmission to the device is done through a software FIFO, as data/control frames can be coalesced (while the device is reading the previous tx transaction, others accumulate). A FIFO is used because at the end it is resource-cheaper that scatter/gather over USB. As well, most traffic is going to be download (vs upload). Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/net/wimax/i2400m/rx.c')
-rw-r--r--drivers/net/wimax/i2400m/rx.c534
1 files changed, 534 insertions, 0 deletions
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c
new file mode 100644
index 000000000000..6922022710ac
--- /dev/null
+++ b/drivers/net/wimax/i2400m/rx.c
@@ -0,0 +1,534 @@
1/*
2 * Intel Wireless WiMAX Connection 2400m
3 * Handle incoming traffic and deliver it to the control or data planes
4 *
5 *
6 * Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 * * Neither the name of Intel Corporation nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 *
34 *
35 * Intel Corporation <linux-wimax@intel.com>
36 * Yanir Lubetkin <yanirx.lubetkin@intel.com>
37 * - Initial implementation
38 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
39 * - Use skb_clone(), break up processing in chunks
40 * - Split transport/device specific
41 * - Make buffer size dynamic to exert less memory pressure
42 *
43 *
44 * This handles the RX path.
45 *
46 * We receive an RX message from the bus-specific driver, which
47 * contains one or more payloads that have potentially different
48 * destinataries (data or control paths).
49 *
50 * So we just take that payload from the transport specific code in
51 * the form of an skb, break it up in chunks (a cloned skb each in the
52 * case of network packets) and pass it to netdev or to the
53 * command/ack handler (and from there to the WiMAX stack).
54 *
55 * PROTOCOL FORMAT
56 *
57 * The format of the buffer is:
58 *
59 * HEADER (struct i2400m_msg_hdr)
60 * PAYLOAD DESCRIPTOR 0 (struct i2400m_pld)
61 * PAYLOAD DESCRIPTOR 1
62 * ...
63 * PAYLOAD DESCRIPTOR N
64 * PAYLOAD 0 (raw bytes)
65 * PAYLOAD 1
66 * ...
67 * PAYLOAD N
68 *
69 * See tx.c for a deeper description on alignment requirements and
70 * other fun facts of it.
71 *
72 * ROADMAP
73 *
74 * i2400m_rx
75 * i2400m_rx_msg_hdr_check
76 * i2400m_rx_pl_descr_check
77 * i2400m_rx_payload
78 * i2400m_net_rx
79 * i2400m_rx_ctl
80 * i2400m_msg_size_check
81 * i2400m_report_hook_work [in a workqueue]
82 * i2400m_report_hook
83 * wimax_msg_to_user
84 * i2400m_rx_ctl_ack
85 * wimax_msg_to_user_alloc
86 * i2400m_rx_trace
87 * i2400m_msg_size_check
88 * wimax_msg
89 */
90#include <linux/kernel.h>
91#include <linux/if_arp.h>
92#include <linux/netdevice.h>
93#include <linux/workqueue.h>
94#include "i2400m.h"
95
96
97#define D_SUBMODULE rx
98#include "debug-levels.h"
99
100struct i2400m_report_hook_args {
101 struct sk_buff *skb_rx;
102 const struct i2400m_l3l4_hdr *l3l4_hdr;
103 size_t size;
104};
105
106
107/*
108 * Execute i2400m_report_hook in a workqueue
109 *
110 * Unpacks arguments from the deferred call, executes it and then
111 * drops the references.
112 *
113 * Obvious NOTE: References are needed because we are a separate
114 * thread; otherwise the buffer changes under us because it is
115 * released by the original caller.
116 */
117static
118void i2400m_report_hook_work(struct work_struct *ws)
119{
120 struct i2400m_work *iw =
121 container_of(ws, struct i2400m_work, ws);
122 struct i2400m_report_hook_args *args = (void *) iw->pl;
123 i2400m_report_hook(iw->i2400m, args->l3l4_hdr, args->size);
124 kfree_skb(args->skb_rx);
125 i2400m_put(iw->i2400m);
126 kfree(iw);
127}
128
129
130/*
131 * Process an ack to a command
132 *
133 * @i2400m: device descriptor
134 * @payload: pointer to message
135 * @size: size of the message
136 *
137 * Pass the acknodledgment (in an skb) to the thread that is waiting
138 * for it in i2400m->msg_completion.
139 *
140 * We need to coordinate properly with the thread waiting for the
141 * ack. Check if it is waiting or if it is gone. We loose the spinlock
142 * to avoid allocating on atomic contexts (yeah, could use GFP_ATOMIC,
143 * but this is not so speed critical).
144 */
145static
146void i2400m_rx_ctl_ack(struct i2400m *i2400m,
147 const void *payload, size_t size)
148{
149 struct device *dev = i2400m_dev(i2400m);
150 struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
151 unsigned long flags;
152 struct sk_buff *ack_skb;
153
154 /* Anyone waiting for an answer? */
155 spin_lock_irqsave(&i2400m->rx_lock, flags);
156 if (i2400m->ack_skb != ERR_PTR(-EINPROGRESS)) {
157 dev_err(dev, "Huh? reply to command with no waiters\n");
158 goto error_no_waiter;
159 }
160 spin_unlock_irqrestore(&i2400m->rx_lock, flags);
161
162 ack_skb = wimax_msg_alloc(wimax_dev, NULL, payload, size, GFP_KERNEL);
163
164 /* Check waiter didn't time out waiting for the answer... */
165 spin_lock_irqsave(&i2400m->rx_lock, flags);
166 if (i2400m->ack_skb != ERR_PTR(-EINPROGRESS)) {
167 d_printf(1, dev, "Huh? waiter for command reply cancelled\n");
168 goto error_waiter_cancelled;
169 }
170 if (ack_skb == NULL) {
171 dev_err(dev, "CMD/GET/SET ack: cannot allocate SKB\n");
172 i2400m->ack_skb = ERR_PTR(-ENOMEM);
173 } else
174 i2400m->ack_skb = ack_skb;
175 spin_unlock_irqrestore(&i2400m->rx_lock, flags);
176 complete(&i2400m->msg_completion);
177 return;
178
179error_waiter_cancelled:
180 if (ack_skb)
181 kfree_skb(ack_skb);
182error_no_waiter:
183 spin_unlock_irqrestore(&i2400m->rx_lock, flags);
184 return;
185}
186
187
188/*
189 * Receive and process a control payload
190 *
191 * @i2400m: device descriptor
192 * @skb_rx: skb that contains the payload (for reference counting)
193 * @payload: pointer to message
194 * @size: size of the message
195 *
196 * There are two types of control RX messages: reports (asynchronous,
197 * like your every day interrupts) and 'acks' (reponses to a command,
198 * get or set request).
199 *
200 * If it is a report, we run hooks on it (to extract information for
201 * things we need to do in the driver) and then pass it over to the
202 * WiMAX stack to send it to user space.
203 *
204 * NOTE: report processing is done in a workqueue specific to the
205 * generic driver, to avoid deadlocks in the system.
206 *
207 * If it is not a report, it is an ack to a previously executed
208 * command, set or get, so wake up whoever is waiting for it from
209 * i2400m_msg_to_dev(). i2400m_rx_ctl_ack() takes care of that.
210 *
211 * Note that the sizes we pass to other functions from here are the
212 * sizes of the _l3l4_hdr + payload, not full buffer sizes, as we have
213 * verified in _msg_size_check() that they are congruent.
214 *
215 * For reports: We can't clone the original skb where the data is
216 * because we need to send this up via netlink; netlink has to add
217 * headers and we can't overwrite what's preceeding the payload...as
218 * it is another message. So we just dup them.
219 */
220static
221void i2400m_rx_ctl(struct i2400m *i2400m, struct sk_buff *skb_rx,
222 const void *payload, size_t size)
223{
224 int result;
225 struct device *dev = i2400m_dev(i2400m);
226 const struct i2400m_l3l4_hdr *l3l4_hdr = payload;
227 unsigned msg_type;
228
229 result = i2400m_msg_size_check(i2400m, l3l4_hdr, size);
230 if (result < 0) {
231 dev_err(dev, "HW BUG? device sent a bad message: %d\n",
232 result);
233 goto error_check;
234 }
235 msg_type = le16_to_cpu(l3l4_hdr->type);
236 d_printf(1, dev, "%s 0x%04x: %zu bytes\n",
237 msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET",
238 msg_type, size);
239 d_dump(2, dev, l3l4_hdr, size);
240 if (msg_type & I2400M_MT_REPORT_MASK) {
241 /* These hooks have to be ran serialized; as well, the
242 * handling might force the execution of commands, and
243 * that might cause reentrancy issues with
244 * bus-specific subdrivers and workqueues. So we run
245 * it in a separate workqueue. */
246 struct i2400m_report_hook_args args = {
247 .skb_rx = skb_rx,
248 .l3l4_hdr = l3l4_hdr,
249 .size = size
250 };
251 if (unlikely(i2400m->ready == 0)) /* only send if up */
252 return;
253 skb_get(skb_rx);
254 i2400m_queue_work(i2400m, i2400m_report_hook_work,
255 GFP_KERNEL, &args, sizeof(args));
256 result = wimax_msg(&i2400m->wimax_dev, NULL, l3l4_hdr, size,
257 GFP_KERNEL);
258 if (result < 0)
259 dev_err(dev, "error sending report to userspace: %d\n",
260 result);
261 } else /* an ack to a CMD, GET or SET */
262 i2400m_rx_ctl_ack(i2400m, payload, size);
263error_check:
264 return;
265}
266
267
268
269
270/*
271 * Receive and send up a trace
272 *
273 * @i2400m: device descriptor
274 * @skb_rx: skb that contains the trace (for reference counting)
275 * @payload: pointer to trace message inside the skb
276 * @size: size of the message
277 *
278 * THe i2400m might produce trace information (diagnostics) and we
279 * send them through a different kernel-to-user pipe (to avoid
280 * clogging it).
281 *
282 * As in i2400m_rx_ctl(), we can't clone the original skb where the
283 * data is because we need to send this up via netlink; netlink has to
284 * add headers and we can't overwrite what's preceeding the
285 * payload...as it is another message. So we just dup them.
286 */
287static
288void i2400m_rx_trace(struct i2400m *i2400m,
289 const void *payload, size_t size)
290{
291 int result;
292 struct device *dev = i2400m_dev(i2400m);
293 struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
294 const struct i2400m_l3l4_hdr *l3l4_hdr = payload;
295 unsigned msg_type;
296
297 result = i2400m_msg_size_check(i2400m, l3l4_hdr, size);
298 if (result < 0) {
299 dev_err(dev, "HW BUG? device sent a bad trace message: %d\n",
300 result);
301 goto error_check;
302 }
303 msg_type = le16_to_cpu(l3l4_hdr->type);
304 d_printf(1, dev, "Trace %s 0x%04x: %zu bytes\n",
305 msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET",
306 msg_type, size);
307 d_dump(2, dev, l3l4_hdr, size);
308 if (unlikely(i2400m->ready == 0)) /* only send if up */
309 return;
310 result = wimax_msg(wimax_dev, "trace", l3l4_hdr, size, GFP_KERNEL);
311 if (result < 0)
312 dev_err(dev, "error sending trace to userspace: %d\n",
313 result);
314error_check:
315 return;
316}
317
318
319/*
320 * Act on a received payload
321 *
322 * @i2400m: device instance
323 * @skb_rx: skb where the transaction was received
324 * @single: 1 if there is only one payload, 0 otherwise
325 * @pld: payload descriptor
326 * @payload: payload data
327 *
328 * Upon reception of a payload, look at its guts in the payload
329 * descriptor and decide what to do with it.
330 */
331static
332void i2400m_rx_payload(struct i2400m *i2400m, struct sk_buff *skb_rx,
333 unsigned single, const struct i2400m_pld *pld,
334 const void *payload)
335{
336 struct device *dev = i2400m_dev(i2400m);
337 size_t pl_size = i2400m_pld_size(pld);
338 enum i2400m_pt pl_type = i2400m_pld_type(pld);
339
340 switch (pl_type) {
341 case I2400M_PT_DATA:
342 d_printf(3, dev, "RX: data payload %zu bytes\n", pl_size);
343 i2400m_net_rx(i2400m, skb_rx, single, payload, pl_size);
344 break;
345 case I2400M_PT_CTRL:
346 i2400m_rx_ctl(i2400m, skb_rx, payload, pl_size);
347 break;
348 case I2400M_PT_TRACE:
349 i2400m_rx_trace(i2400m, payload, pl_size);
350 break;
351 default: /* Anything else shouldn't come to the host */
352 if (printk_ratelimit())
353 dev_err(dev, "RX: HW BUG? unexpected payload type %u\n",
354 pl_type);
355 }
356}
357
358
359/*
360 * Check a received transaction's message header
361 *
362 * @i2400m: device descriptor
363 * @msg_hdr: message header
364 * @buf_size: size of the received buffer
365 *
366 * Check that the declarations done by a RX buffer message header are
367 * sane and consistent with the amount of data that was received.
368 */
369static
370int i2400m_rx_msg_hdr_check(struct i2400m *i2400m,
371 const struct i2400m_msg_hdr *msg_hdr,
372 size_t buf_size)
373{
374 int result = -EIO;
375 struct device *dev = i2400m_dev(i2400m);
376 if (buf_size < sizeof(*msg_hdr)) {
377 dev_err(dev, "RX: HW BUG? message with short header (%zu "
378 "vs %zu bytes expected)\n", buf_size, sizeof(*msg_hdr));
379 goto error;
380 }
381 if (msg_hdr->barker != cpu_to_le32(I2400M_D2H_MSG_BARKER)) {
382 dev_err(dev, "RX: HW BUG? message received with unknown "
383 "barker 0x%08x (buf_size %zu bytes)\n",
384 le32_to_cpu(msg_hdr->barker), buf_size);
385 goto error;
386 }
387 if (msg_hdr->num_pls == 0) {
388 dev_err(dev, "RX: HW BUG? zero payload packets in message\n");
389 goto error;
390 }
391 if (le16_to_cpu(msg_hdr->num_pls) > I2400M_MAX_PLS_IN_MSG) {
392 dev_err(dev, "RX: HW BUG? message contains more payload "
393 "than maximum; ignoring.\n");
394 goto error;
395 }
396 result = 0;
397error:
398 return result;
399}
400
401
402/*
403 * Check a payload descriptor against the received data
404 *
405 * @i2400m: device descriptor
406 * @pld: payload descriptor
407 * @pl_itr: offset (in bytes) in the received buffer the payload is
408 * located
409 * @buf_size: size of the received buffer
410 *
411 * Given a payload descriptor (part of a RX buffer), check it is sane
412 * and that the data it declares fits in the buffer.
413 */
414static
415int i2400m_rx_pl_descr_check(struct i2400m *i2400m,
416 const struct i2400m_pld *pld,
417 size_t pl_itr, size_t buf_size)
418{
419 int result = -EIO;
420 struct device *dev = i2400m_dev(i2400m);
421 size_t pl_size = i2400m_pld_size(pld);
422 enum i2400m_pt pl_type = i2400m_pld_type(pld);
423
424 if (pl_size > i2400m->bus_pl_size_max) {
425 dev_err(dev, "RX: HW BUG? payload @%zu: size %zu is "
426 "bigger than maximum %zu; ignoring message\n",
427 pl_itr, pl_size, i2400m->bus_pl_size_max);
428 goto error;
429 }
430 if (pl_itr + pl_size > buf_size) { /* enough? */
431 dev_err(dev, "RX: HW BUG? payload @%zu: size %zu "
432 "goes beyond the received buffer "
433 "size (%zu bytes); ignoring message\n",
434 pl_itr, pl_size, buf_size);
435 goto error;
436 }
437 if (pl_type >= I2400M_PT_ILLEGAL) {
438 dev_err(dev, "RX: HW BUG? illegal payload type %u; "
439 "ignoring message\n", pl_type);
440 goto error;
441 }
442 result = 0;
443error:
444 return result;
445}
446
447
448/**
449 * i2400m_rx - Receive a buffer of data from the device
450 *
451 * @i2400m: device descriptor
452 * @skb: skbuff where the data has been received
453 *
454 * Parse in a buffer of data that contains an RX message sent from the
455 * device. See the file header for the format. Run all checks on the
456 * buffer header, then run over each payload's descriptors, verify
457 * their consistency and act on each payload's contents. If
458 * everything is succesful, update the device's statistics.
459 *
460 * Note: You need to set the skb to contain only the length of the
461 * received buffer; for that, use skb_trim(skb, RECEIVED_SIZE).
462 *
463 * Returns:
464 *
465 * 0 if ok, < 0 errno on error
466 *
467 * If ok, this function owns now the skb and the caller DOESN'T have
468 * to run kfree_skb() on it. However, on error, the caller still owns
469 * the skb and it is responsible for releasing it.
470 */
471int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb)
472{
473 int i, result;
474 struct device *dev = i2400m_dev(i2400m);
475 const struct i2400m_msg_hdr *msg_hdr;
476 size_t pl_itr, pl_size, skb_len;
477 unsigned long flags;
478 unsigned num_pls;
479
480 skb_len = skb->len;
481 d_fnstart(4, dev, "(i2400m %p skb %p [size %zu])\n",
482 i2400m, skb, skb_len);
483 result = -EIO;
484 msg_hdr = (void *) skb->data;
485 result = i2400m_rx_msg_hdr_check(i2400m, msg_hdr, skb->len);
486 if (result < 0)
487 goto error_msg_hdr_check;
488 result = -EIO;
489 num_pls = le16_to_cpu(msg_hdr->num_pls);
490 pl_itr = sizeof(*msg_hdr) + /* Check payload descriptor(s) */
491 num_pls * sizeof(msg_hdr->pld[0]);
492 pl_itr = ALIGN(pl_itr, I2400M_PL_PAD);
493 if (pl_itr > skb->len) { /* got all the payload descriptors? */
494 dev_err(dev, "RX: HW BUG? message too short (%u bytes) for "
495 "%u payload descriptors (%zu each, total %zu)\n",
496 skb->len, num_pls, sizeof(msg_hdr->pld[0]), pl_itr);
497 goto error_pl_descr_short;
498 }
499 /* Walk each payload payload--check we really got it */
500 for (i = 0; i < num_pls; i++) {
501 /* work around old gcc warnings */
502 pl_size = i2400m_pld_size(&msg_hdr->pld[i]);
503 result = i2400m_rx_pl_descr_check(i2400m, &msg_hdr->pld[i],
504 pl_itr, skb->len);
505 if (result < 0)
506 goto error_pl_descr_check;
507 i2400m_rx_payload(i2400m, skb, num_pls == 1, &msg_hdr->pld[i],
508 skb->data + pl_itr);
509 pl_itr += ALIGN(pl_size, I2400M_PL_PAD);
510 cond_resched(); /* Don't monopolize */
511 }
512 kfree_skb(skb);
513 /* Update device statistics */
514 spin_lock_irqsave(&i2400m->rx_lock, flags);
515 i2400m->rx_pl_num += i;
516 if (i > i2400m->rx_pl_max)
517 i2400m->rx_pl_max = i;
518 if (i < i2400m->rx_pl_min)
519 i2400m->rx_pl_min = i;
520 i2400m->rx_num++;
521 i2400m->rx_size_acc += skb->len;
522 if (skb->len < i2400m->rx_size_min)
523 i2400m->rx_size_min = skb->len;
524 if (skb->len > i2400m->rx_size_max)
525 i2400m->rx_size_max = skb->len;
526 spin_unlock_irqrestore(&i2400m->rx_lock, flags);
527error_pl_descr_check:
528error_pl_descr_short:
529error_msg_hdr_check:
530 d_fnend(4, dev, "(i2400m %p skb %p [size %zu]) = %d\n",
531 i2400m, skb, skb_len, result);
532 return result;
533}
534EXPORT_SYMBOL_GPL(i2400m_rx);