diff options
-rw-r--r-- | drivers/net/wimax/i2400m/control.c | 9 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/i2400m.h | 2 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/netdev.c | 104 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/rx.c | 117 | ||||
-rw-r--r-- | include/linux/wimax/i2400m.h | 35 |
5 files changed, 234 insertions, 33 deletions
diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c index c3968b240d6..4073c3e93bd 100644 --- a/drivers/net/wimax/i2400m/control.c +++ b/drivers/net/wimax/i2400m/control.c | |||
@@ -1311,6 +1311,7 @@ int i2400m_dev_initialize(struct i2400m *i2400m) | |||
1311 | struct device *dev = i2400m_dev(i2400m); | 1311 | struct device *dev = i2400m_dev(i2400m); |
1312 | struct i2400m_tlv_config_idle_parameters idle_params; | 1312 | struct i2400m_tlv_config_idle_parameters idle_params; |
1313 | struct i2400m_tlv_config_idle_timeout idle_timeout; | 1313 | struct i2400m_tlv_config_idle_timeout idle_timeout; |
1314 | struct i2400m_tlv_config_d2h_data_format df; | ||
1314 | const struct i2400m_tlv_hdr *args[9]; | 1315 | const struct i2400m_tlv_hdr *args[9]; |
1315 | unsigned argc = 0; | 1316 | unsigned argc = 0; |
1316 | 1317 | ||
@@ -1333,6 +1334,14 @@ int i2400m_dev_initialize(struct i2400m *i2400m) | |||
1333 | args[argc++] = &idle_timeout.hdr; | 1334 | args[argc++] = &idle_timeout.hdr; |
1334 | } | 1335 | } |
1335 | } | 1336 | } |
1337 | if (i2400m_ge_v1_4(i2400m)) { | ||
1338 | df.hdr.type = | ||
1339 | cpu_to_le16(I2400M_TLV_CONFIG_D2H_DATA_FORMAT); | ||
1340 | df.hdr.length = cpu_to_le16( | ||
1341 | sizeof(df) - sizeof(df.hdr)); | ||
1342 | df.format = 1; | ||
1343 | args[argc++] = &df.hdr; | ||
1344 | } | ||
1336 | result = i2400m_set_init_config(i2400m, args, argc); | 1345 | result = i2400m_set_init_config(i2400m, args, argc); |
1337 | if (result < 0) | 1346 | if (result < 0) |
1338 | goto error; | 1347 | goto error; |
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 0c60d5c4300..125c30594e6 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h | |||
@@ -593,6 +593,8 @@ extern void i2400m_tx_release(struct i2400m *); | |||
593 | 593 | ||
594 | extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, | 594 | extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, |
595 | const void *, int); | 595 | const void *, int); |
596 | extern void i2400m_net_erx(struct i2400m *, struct sk_buff *, | ||
597 | enum i2400m_cs); | ||
596 | enum i2400m_pt; | 598 | enum i2400m_pt; |
597 | extern int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt); | 599 | extern int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt); |
598 | 600 | ||
diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c index be8be4d0709..2bdd0cdbb31 100644 --- a/drivers/net/wimax/i2400m/netdev.c +++ b/drivers/net/wimax/i2400m/netdev.c | |||
@@ -28,13 +28,12 @@ | |||
28 | * space and from the other side. The world is (sadly) configured to | 28 | * space and from the other side. The world is (sadly) configured to |
29 | * take in only Ethernet devices... | 29 | * take in only Ethernet devices... |
30 | * | 30 | * |
31 | * Because of this, currently there is an copy-each-rxed-packet | 31 | * Because of this, when using firmwares <= v1.3, there is an |
32 | * overhead on the RX path. Each IP packet has to be reallocated to | 32 | * copy-each-rxed-packet overhead on the RX path. Each IP packet has |
33 | * add an ethernet header (as there is no space in what we get from | 33 | * to be reallocated to add an ethernet header (as there is no space |
34 | * the device). This is a known drawback and coming versions of the | 34 | * in what we get from the device). This is a known drawback and |
35 | * device's firmware are being changed to add header space that can be | 35 | * firmwares >= 1.4 add header space that can be used to insert the |
36 | * used to insert the ethernet header without having to reallocate and | 36 | * ethernet header without having to reallocate and copy. |
37 | * copy. | ||
38 | * | 37 | * |
39 | * TX error handling is tricky; because we have to FIFO/queue the | 38 | * TX error handling is tricky; because we have to FIFO/queue the |
40 | * buffers for transmission (as the hardware likes it aggregated), we | 39 | * buffers for transmission (as the hardware likes it aggregated), we |
@@ -67,7 +66,9 @@ | |||
67 | * i2400m_tx_timeout Called when the device times out | 66 | * i2400m_tx_timeout Called when the device times out |
68 | * | 67 | * |
69 | * i2400m_net_rx Called by the RX code when a data frame is | 68 | * i2400m_net_rx Called by the RX code when a data frame is |
70 | * available. | 69 | * available (firmware <= 1.3) |
70 | * i2400m_net_erx Called by the RX code when a data frame is | ||
71 | * available (firmware >= 1.4). | ||
71 | * i2400m_netdev_setup Called to setup all the netdev stuff from | 72 | * i2400m_netdev_setup Called to setup all the netdev stuff from |
72 | * alloc_netdev. | 73 | * alloc_netdev. |
73 | */ | 74 | */ |
@@ -396,30 +397,18 @@ void i2400m_tx_timeout(struct net_device *net_dev) | |||
396 | * Create a fake ethernet header | 397 | * Create a fake ethernet header |
397 | * | 398 | * |
398 | * For emulating an ethernet device, every received IP header has to | 399 | * For emulating an ethernet device, every received IP header has to |
399 | * be prefixed with an ethernet header. | 400 | * be prefixed with an ethernet header. Fake it with the given |
400 | * | 401 | * protocol. |
401 | * What we receive has (potentially) many IP packets concatenated with | ||
402 | * no ETH_HLEN bytes prefixed. Thus there is no space for an eth | ||
403 | * header. | ||
404 | * | ||
405 | * We would have to reallocate or do ugly fragment tricks in order to | ||
406 | * add it. | ||
407 | * | ||
408 | * But what we do is use the header space of the RX transaction | ||
409 | * (*msg_hdr) as we don't need it anymore; then we'll point all the | ||
410 | * data skbs there, as they share the same backing store. | ||
411 | * | ||
412 | * We only support IPv4 for v3 firmware. | ||
413 | */ | 402 | */ |
414 | static | 403 | static |
415 | void i2400m_rx_fake_eth_header(struct net_device *net_dev, | 404 | void i2400m_rx_fake_eth_header(struct net_device *net_dev, |
416 | void *_eth_hdr) | 405 | void *_eth_hdr, int protocol) |
417 | { | 406 | { |
418 | struct ethhdr *eth_hdr = _eth_hdr; | 407 | struct ethhdr *eth_hdr = _eth_hdr; |
419 | 408 | ||
420 | memcpy(eth_hdr->h_dest, net_dev->dev_addr, sizeof(eth_hdr->h_dest)); | 409 | memcpy(eth_hdr->h_dest, net_dev->dev_addr, sizeof(eth_hdr->h_dest)); |
421 | memset(eth_hdr->h_source, 0, sizeof(eth_hdr->h_dest)); | 410 | memset(eth_hdr->h_source, 0, sizeof(eth_hdr->h_dest)); |
422 | eth_hdr->h_proto = cpu_to_be16(ETH_P_IP); | 411 | eth_hdr->h_proto = cpu_to_be16(protocol); |
423 | } | 412 | } |
424 | 413 | ||
425 | 414 | ||
@@ -432,6 +421,13 @@ void i2400m_rx_fake_eth_header(struct net_device *net_dev, | |||
432 | * @buf: pointer to the buffer containing the data | 421 | * @buf: pointer to the buffer containing the data |
433 | * @len: buffer's length | 422 | * @len: buffer's length |
434 | * | 423 | * |
424 | * This is only used now for the v1.3 firmware. It will be deprecated | ||
425 | * in >= 2.6.31. | ||
426 | * | ||
427 | * Note that due to firmware limitations, we don't have space to add | ||
428 | * an ethernet header, so we need to copy each packet. Firmware | ||
429 | * versions >= v1.4 fix this [see i2400m_net_erx()]. | ||
430 | * | ||
435 | * We just clone the skb and set it up so that it's skb->data pointer | 431 | * We just clone the skb and set it up so that it's skb->data pointer |
436 | * points to "buf" and it's length. | 432 | * points to "buf" and it's length. |
437 | * | 433 | * |
@@ -478,7 +474,7 @@ void i2400m_net_rx(struct i2400m *i2400m, struct sk_buff *skb_rx, | |||
478 | memcpy(skb_put(skb, buf_len), buf, buf_len); | 474 | memcpy(skb_put(skb, buf_len), buf, buf_len); |
479 | } | 475 | } |
480 | i2400m_rx_fake_eth_header(i2400m->wimax_dev.net_dev, | 476 | i2400m_rx_fake_eth_header(i2400m->wimax_dev.net_dev, |
481 | skb->data - ETH_HLEN); | 477 | skb->data - ETH_HLEN, ETH_P_IP); |
482 | skb_set_mac_header(skb, -ETH_HLEN); | 478 | skb_set_mac_header(skb, -ETH_HLEN); |
483 | skb->dev = i2400m->wimax_dev.net_dev; | 479 | skb->dev = i2400m->wimax_dev.net_dev; |
484 | skb->protocol = htons(ETH_P_IP); | 480 | skb->protocol = htons(ETH_P_IP); |
@@ -493,6 +489,64 @@ error_skb_realloc: | |||
493 | i2400m, buf, buf_len); | 489 | i2400m, buf, buf_len); |
494 | } | 490 | } |
495 | 491 | ||
492 | |||
493 | /* | ||
494 | * i2400m_net_erx - pass a network packet to the stack (extended version) | ||
495 | * | ||
496 | * @i2400m: device descriptor | ||
497 | * @skb: the skb where the packet is - the skb should be set to point | ||
498 | * at the IP packet; this function will add ethernet headers if | ||
499 | * needed. | ||
500 | * @cs: packet type | ||
501 | * | ||
502 | * This is only used now for firmware >= v1.4. Note it is quite | ||
503 | * similar to i2400m_net_rx() (used only for v1.3 firmware). | ||
504 | * | ||
505 | * This function is normally run from a thread context. However, we | ||
506 | * still use netif_rx() instead of netif_receive_skb() as was | ||
507 | * recommended in the mailing list. Reason is in some stress tests | ||
508 | * when sending/receiving a lot of data we seem to hit a softlock in | ||
509 | * the kernel's TCP implementation [aroudn tcp_delay_timer()]. Using | ||
510 | * netif_rx() took care of the issue. | ||
511 | * | ||
512 | * This is, of course, still open to do more research on why running | ||
513 | * with netif_receive_skb() hits this softlock. FIXME. | ||
514 | */ | ||
515 | void i2400m_net_erx(struct i2400m *i2400m, struct sk_buff *skb, | ||
516 | enum i2400m_cs cs) | ||
517 | { | ||
518 | struct net_device *net_dev = i2400m->wimax_dev.net_dev; | ||
519 | struct device *dev = i2400m_dev(i2400m); | ||
520 | int protocol; | ||
521 | |||
522 | d_fnstart(2, dev, "(i2400m %p skb %p [%zu] cs %d)\n", | ||
523 | i2400m, skb, skb->len, cs); | ||
524 | switch(cs) { | ||
525 | case I2400M_CS_IPV4_0: | ||
526 | case I2400M_CS_IPV4: | ||
527 | protocol = ETH_P_IP; | ||
528 | i2400m_rx_fake_eth_header(i2400m->wimax_dev.net_dev, | ||
529 | skb->data - ETH_HLEN, ETH_P_IP); | ||
530 | skb_set_mac_header(skb, -ETH_HLEN); | ||
531 | skb->dev = i2400m->wimax_dev.net_dev; | ||
532 | skb->protocol = htons(ETH_P_IP); | ||
533 | net_dev->stats.rx_packets++; | ||
534 | net_dev->stats.rx_bytes += skb->len; | ||
535 | break; | ||
536 | default: | ||
537 | dev_err(dev, "ERX: BUG? CS type %u unsupported\n", cs); | ||
538 | goto error; | ||
539 | |||
540 | } | ||
541 | d_printf(3, dev, "ERX: receiving %d bytes to the network stack\n", | ||
542 | skb->len); | ||
543 | d_dump(4, dev, skb->data, skb->len); | ||
544 | netif_rx_ni(skb); /* see notes in function header */ | ||
545 | error: | ||
546 | d_fnend(2, dev, "(i2400m %p skb %p [%zu] cs %d) = void\n", | ||
547 | i2400m, skb, skb->len, cs); | ||
548 | } | ||
549 | |||
496 | static const struct net_device_ops i2400m_netdev_ops = { | 550 | static const struct net_device_ops i2400m_netdev_ops = { |
497 | .ndo_open = i2400m_open, | 551 | .ndo_open = i2400m_open, |
498 | .ndo_stop = i2400m_stop, | 552 | .ndo_stop = i2400m_stop, |
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c index c62b8c56416..cd525066d4b 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 | */ | ||
351 | static | ||
352 | void 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); | ||
397 | error_skb_clone: | ||
398 | error: | ||
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 | */ |
330 | static | 422 | static |
331 | void i2400m_rx_payload(struct i2400m *i2400m, struct sk_buff *skb_rx, | 423 | void 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 */ |
diff --git a/include/linux/wimax/i2400m.h b/include/linux/wimax/i2400m.h index 686eeb2b970..ad36e073a70 100644 --- a/include/linux/wimax/i2400m.h +++ b/include/linux/wimax/i2400m.h | |||
@@ -207,6 +207,7 @@ enum i2400m_pt { | |||
207 | I2400M_PT_TRACE, /* For device debug */ | 207 | I2400M_PT_TRACE, /* For device debug */ |
208 | I2400M_PT_RESET_WARM, /* device reset */ | 208 | I2400M_PT_RESET_WARM, /* device reset */ |
209 | I2400M_PT_RESET_COLD, /* USB[transport] reset, like reconnect */ | 209 | I2400M_PT_RESET_COLD, /* USB[transport] reset, like reconnect */ |
210 | I2400M_PT_EDATA, /* Extended RX data */ | ||
210 | I2400M_PT_ILLEGAL | 211 | I2400M_PT_ILLEGAL |
211 | }; | 212 | }; |
212 | 213 | ||
@@ -221,6 +222,32 @@ struct i2400m_pl_data_hdr { | |||
221 | } __attribute__((packed)); | 222 | } __attribute__((packed)); |
222 | 223 | ||
223 | 224 | ||
225 | /* | ||
226 | * Payload for an extended data packet | ||
227 | * | ||
228 | * New in v1.4 | ||
229 | * | ||
230 | * @cs: the type of data in the packet, as defined per (802.16e | ||
231 | * T11.13.19.1). Currently only 2 (IPv4 packet) supported. | ||
232 | * | ||
233 | * This is prefixed to each and every INCOMING DATA packet. | ||
234 | */ | ||
235 | struct i2400m_pl_edata_hdr { | ||
236 | __le32 reorder; | ||
237 | __u8 cs; | ||
238 | __u8 reserved[11]; | ||
239 | } __attribute__((packed)); | ||
240 | |||
241 | enum i2400m_cs { | ||
242 | I2400M_CS_IPV4_0 = 0, | ||
243 | I2400M_CS_IPV4 = 2, | ||
244 | }; | ||
245 | |||
246 | enum i2400m_reorder { | ||
247 | I2400M_REORDER_NEEDED = 0x01, | ||
248 | }; | ||
249 | |||
250 | |||
224 | /* Misc constants */ | 251 | /* Misc constants */ |
225 | enum { | 252 | enum { |
226 | I2400M_PL_PAD = 16, /* Payload data size alignment */ | 253 | I2400M_PL_PAD = 16, /* Payload data size alignment */ |
@@ -382,6 +409,7 @@ enum i2400m_tlv { | |||
382 | I2400M_TLV_DEVICE_RESET_TYPE = 132, | 409 | I2400M_TLV_DEVICE_RESET_TYPE = 132, |
383 | I2400M_TLV_CONFIG_IDLE_PARAMETERS = 601, | 410 | I2400M_TLV_CONFIG_IDLE_PARAMETERS = 601, |
384 | I2400M_TLV_CONFIG_IDLE_TIMEOUT = 611, | 411 | I2400M_TLV_CONFIG_IDLE_TIMEOUT = 611, |
412 | I2400M_TLV_CONFIG_D2H_DATA_FORMAT = 614, | ||
385 | }; | 413 | }; |
386 | 414 | ||
387 | 415 | ||
@@ -518,5 +546,12 @@ struct i2400m_tlv_config_idle_timeout { | |||
518 | * 0 disabled */ | 546 | * 0 disabled */ |
519 | } __attribute__((packed)); | 547 | } __attribute__((packed)); |
520 | 548 | ||
549 | /* New in v1.4 -- for backward compat, will be removed */ | ||
550 | struct i2400m_tlv_config_d2h_data_format { | ||
551 | struct i2400m_tlv_hdr hdr; | ||
552 | __u8 format; /* 0 old format, 1 enhanced */ | ||
553 | __u8 reserved[3]; | ||
554 | } __attribute__((packed)); | ||
555 | |||
521 | 556 | ||
522 | #endif /* #ifndef __LINUX__WIMAX__I2400M_H__ */ | 557 | #endif /* #ifndef __LINUX__WIMAX__I2400M_H__ */ |