aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax/i2400m/rx.c
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2009-02-28 18:42:52 -0500
committerDavid S. Miller <davem@davemloft.net>2009-03-02 06:10:26 -0500
commitfd5c565c0c04d2716cfdac3f1de3c2261d6a457d (patch)
tree0f64176368c6ffb5b4f95abf37b422bfba7fdd6e /drivers/net/wimax/i2400m/rx.c
parent347707baa77d273d79258303e00200d40cf3b323 (diff)
wimax/i2400m: support extended data RX protocol (no need to reallocate skbs)
Newer i2400m firmwares (>= v1.4) extend the data RX protocol so that each packet has a 16 byte header. This header is mainly used to implement host reordeing (which is addressed in later commits). However, this header also allows us to overwrite it (once data has been extracted) with an Ethernet header and deliver to the networking stack without having to reallocate the skb (as it happened in fw <= v1.3) to make room for it. - control.c: indicate the device [dev_initialize()] that the driver wants to use the extended data RX protocol. Also involves adding the definition of the needed data types in include/linux/wimax/i2400m.h. - rx.c: handle the new payload type for the extended RX data protocol. Prepares the skb for delivery to netdev.c:i2400m_net_erx(). - netdev.c: Introduce i2400m_net_erx() that adds the fake ethernet address to a prepared skb and delivers it to the networking stack. - cleanup: in most instances in rx.c, the variable 'single' was renamed to 'single_last' for it better conveys its meaning. Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/wimax/i2400m/rx.c')
-rw-r--r--drivers/net/wimax/i2400m/rx.c117
1 files changed, 109 insertions, 8 deletions
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c
index c62b8c564161..cd525066d4b7 100644
--- a/drivers/net/wimax/i2400m/rx.c
+++ b/drivers/net/wimax/i2400m/rx.c
@@ -69,6 +69,22 @@
69 * See tx.c for a deeper description on alignment requirements and 69 * See tx.c for a deeper description on alignment requirements and
70 * other fun facts of it. 70 * other fun facts of it.
71 * 71 *
72 * DATA PACKETS
73 *
74 * In firmwares <= v1.3, data packets have no header for RX, but they
75 * do for TX (currently unused).
76 *
77 * In firmware >= 1.4, RX packets have an extended header (16
78 * bytes). This header conveys information for management of host
79 * reordering of packets (the device offloads storage of the packets
80 * for reordering to the host).
81 *
82 * Currently this information is not used as the current code doesn't
83 * enable host reordering.
84 *
85 * The header is used as dummy space to emulate an ethernet header and
86 * thus be able to act as an ethernet device without having to reallocate.
87 *
72 * ROADMAP 88 * ROADMAP
73 * 89 *
74 * i2400m_rx 90 * i2400m_rx
@@ -76,6 +92,8 @@
76 * i2400m_rx_pl_descr_check 92 * i2400m_rx_pl_descr_check
77 * i2400m_rx_payload 93 * i2400m_rx_payload
78 * i2400m_net_rx 94 * i2400m_net_rx
95 * i2400m_rx_edata
96 * i2400m_net_erx
79 * i2400m_rx_ctl 97 * i2400m_rx_ctl
80 * i2400m_msg_size_check 98 * i2400m_msg_size_check
81 * i2400m_report_hook_work [in a workqueue] 99 * i2400m_report_hook_work [in a workqueue]
@@ -264,8 +282,6 @@ error_check:
264} 282}
265 283
266 284
267
268
269/* 285/*
270 * Receive and send up a trace 286 * Receive and send up a trace
271 * 287 *
@@ -314,32 +330,112 @@ error_check:
314 return; 330 return;
315} 331}
316 332
333/*
334 * Receive and send up an extended data packet
335 *
336 * @i2400m: device descriptor
337 * @skb_rx: skb that contains the extended data packet
338 * @single_last: 1 if the payload is the only one or the last one of
339 * the skb.
340 * @payload: pointer to the packet's data inside the skb
341 * @size: size of the payload
342 *
343 * Starting in v1.4 of the i2400m's firmware, the device can send data
344 * packets to the host in an extended format that; this incudes a 16
345 * byte header (struct i2400m_pl_edata_hdr). Using this header's space
346 * we can fake ethernet headers for ethernet device emulation without
347 * having to copy packets around.
348 *
349 * This function handles said path.
350 */
351static
352void i2400m_rx_edata(struct i2400m *i2400m, struct sk_buff *skb_rx,
353 unsigned single_last, const void *payload, size_t size)
354{
355 struct device *dev = i2400m_dev(i2400m);
356 const struct i2400m_pl_edata_hdr *hdr = payload;
357 struct net_device *net_dev = i2400m->wimax_dev.net_dev;
358 struct sk_buff *skb;
359 enum i2400m_cs cs;
360 unsigned reorder_needed;
361
362 d_fnstart(4, dev, "(i2400m %p skb_rx %p single %u payload %p "
363 "size %zu)\n", i2400m, skb_rx, single_last, payload, size);
364 if (size < sizeof(*hdr)) {
365 dev_err(dev, "ERX: HW BUG? message with short header (%zu "
366 "vs %zu bytes expected)\n", size, sizeof(*hdr));
367 goto error;
368 }
369 reorder_needed = le32_to_cpu(hdr->reorder & I2400M_REORDER_NEEDED);
370 cs = hdr->cs;
371 if (reorder_needed) {
372 dev_err(dev, "ERX: HW BUG? reorder needed, it was disabled\n");
373 goto error;
374 }
375 /* ok, so now decide if we want to clone or reuse the skb,
376 * pull and trim it so the beginning is the space for the eth
377 * header and pass it to i2400m_net_erx() for the stack */
378 if (single_last) {
379 skb = skb_get(skb_rx);
380 d_printf(3, dev, "ERX: reusing single payload skb %p\n", skb);
381 } else {
382 skb = skb_clone(skb_rx, GFP_KERNEL);
383 d_printf(3, dev, "ERX: cloning %p\n", skb);
384 if (skb == NULL) {
385 dev_err(dev, "ERX: no memory to clone skb\n");
386 net_dev->stats.rx_dropped++;
387 goto error_skb_clone;
388 }
389 }
390 /* now we have to pull and trim so that the skb points to the
391 * beginning of the IP packet; the netdev part will add the
392 * ethernet header as needed. */
393 BUILD_BUG_ON(ETH_HLEN > sizeof(*hdr));
394 skb_pull(skb, payload + sizeof(*hdr) - (void *) skb->data);
395 skb_trim(skb, (void *) skb_end_pointer(skb) - payload + sizeof(*hdr));
396 i2400m_net_erx(i2400m, skb, cs);
397error_skb_clone:
398error:
399 d_fnend(4, dev, "(i2400m %p skb_rx %p single %u payload %p "
400 "size %zu) = void\n", i2400m, skb_rx, single_last, payload, size);
401 return;
402}
403
404
405
317 406
318/* 407/*
319 * Act on a received payload 408 * Act on a received payload
320 * 409 *
321 * @i2400m: device instance 410 * @i2400m: device instance
322 * @skb_rx: skb where the transaction was received 411 * @skb_rx: skb where the transaction was received
323 * @single: 1 if there is only one payload, 0 otherwise 412 * @single_last: 1 this is the only payload or the last one (so the
413 * skb can be reused instead of cloned).
324 * @pld: payload descriptor 414 * @pld: payload descriptor
325 * @payload: payload data 415 * @payload: payload data
326 * 416 *
327 * Upon reception of a payload, look at its guts in the payload 417 * Upon reception of a payload, look at its guts in the payload
328 * descriptor and decide what to do with it. 418 * descriptor and decide what to do with it. If it is a single payload
419 * skb or if the last skb is a data packet, the skb will be referenced
420 * and modified (so it doesn't have to be cloned).
329 */ 421 */
330static 422static
331void i2400m_rx_payload(struct i2400m *i2400m, struct sk_buff *skb_rx, 423void i2400m_rx_payload(struct i2400m *i2400m, struct sk_buff *skb_rx,
332 unsigned single, const struct i2400m_pld *pld, 424 unsigned single_last, const struct i2400m_pld *pld,
333 const void *payload) 425 const void *payload)
334{ 426{
335 struct device *dev = i2400m_dev(i2400m); 427 struct device *dev = i2400m_dev(i2400m);
336 size_t pl_size = i2400m_pld_size(pld); 428 size_t pl_size = i2400m_pld_size(pld);
337 enum i2400m_pt pl_type = i2400m_pld_type(pld); 429 enum i2400m_pt pl_type = i2400m_pld_type(pld);
338 430
431 d_printf(7, dev, "RX: received payload type %u, %zu bytes\n",
432 pl_type, pl_size);
433 d_dump(8, dev, payload, pl_size);
434
339 switch (pl_type) { 435 switch (pl_type) {
340 case I2400M_PT_DATA: 436 case I2400M_PT_DATA:
341 d_printf(3, dev, "RX: data payload %zu bytes\n", pl_size); 437 d_printf(3, dev, "RX: data payload %zu bytes\n", pl_size);
342 i2400m_net_rx(i2400m, skb_rx, single, payload, pl_size); 438 i2400m_net_rx(i2400m, skb_rx, single_last, payload, pl_size);
343 break; 439 break;
344 case I2400M_PT_CTRL: 440 case I2400M_PT_CTRL:
345 i2400m_rx_ctl(i2400m, skb_rx, payload, pl_size); 441 i2400m_rx_ctl(i2400m, skb_rx, payload, pl_size);
@@ -347,6 +443,10 @@ void i2400m_rx_payload(struct i2400m *i2400m, struct sk_buff *skb_rx,
347 case I2400M_PT_TRACE: 443 case I2400M_PT_TRACE:
348 i2400m_rx_trace(i2400m, payload, pl_size); 444 i2400m_rx_trace(i2400m, payload, pl_size);
349 break; 445 break;
446 case I2400M_PT_EDATA:
447 d_printf(3, dev, "ERX: data payload %zu bytes\n", pl_size);
448 i2400m_rx_edata(i2400m, skb_rx, single_last, payload, pl_size);
449 break;
350 default: /* Anything else shouldn't come to the host */ 450 default: /* Anything else shouldn't come to the host */
351 if (printk_ratelimit()) 451 if (printk_ratelimit())
352 dev_err(dev, "RX: HW BUG? unexpected payload type %u\n", 452 dev_err(dev, "RX: HW BUG? unexpected payload type %u\n",
@@ -474,7 +574,7 @@ int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb)
474 const struct i2400m_msg_hdr *msg_hdr; 574 const struct i2400m_msg_hdr *msg_hdr;
475 size_t pl_itr, pl_size, skb_len; 575 size_t pl_itr, pl_size, skb_len;
476 unsigned long flags; 576 unsigned long flags;
477 unsigned num_pls; 577 unsigned num_pls, single_last;
478 578
479 skb_len = skb->len; 579 skb_len = skb->len;
480 d_fnstart(4, dev, "(i2400m %p skb %p [size %zu])\n", 580 d_fnstart(4, dev, "(i2400m %p skb %p [size %zu])\n",
@@ -503,7 +603,8 @@ int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb)
503 pl_itr, skb->len); 603 pl_itr, skb->len);
504 if (result < 0) 604 if (result < 0)
505 goto error_pl_descr_check; 605 goto error_pl_descr_check;
506 i2400m_rx_payload(i2400m, skb, num_pls == 1, &msg_hdr->pld[i], 606 single_last = num_pls == 1 || i == num_pls - 1;
607 i2400m_rx_payload(i2400m, skb, single_last, &msg_hdr->pld[i],
507 skb->data + pl_itr); 608 skb->data + pl_itr);
508 pl_itr += ALIGN(pl_size, I2400M_PL_PAD); 609 pl_itr += ALIGN(pl_size, I2400M_PL_PAD);
509 cond_resched(); /* Don't monopolize */ 610 cond_resched(); /* Don't monopolize */