aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax/i2400m/rx.c
diff options
context:
space:
mode:
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 */