diff options
author | Peter Korsgaard <peter@korsgaard.com> | 2013-12-16 05:35:35 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-01-09 15:24:22 -0500 |
commit | 5fd93067a1e7bc8ce6a889816d2b4c2d19a2e970 (patch) | |
tree | 2bfddfeb1ff90283ca3d869ae88cca18ce4f2312 /drivers/net/usb | |
parent | 1552c3d8e10d1113d55237f3dd0d65c0a3501632 (diff) |
dm9601: work around tx fifo sync issue on dm962x
commit 4263c86dca5198da6bd3ad826d0b2304fbe25776 upstream.
Certain dm962x revisions contain an bug, where if a USB bulk transfer retry
(E.G. if bulk crc mismatch) happens right after a transfer with odd or
maxpacket length, the internal tx hardware fifo gets out of sync causing
the interface to stop working.
Work around it by adding up to 3 bytes of padding to ensure this situation
cannot trigger.
This workaround also means we never pass multiple-of-maxpacket size skb's
to usbnet, so the length adjustment to handle usbnet's padding of those can
be removed.
Reported-by: Joseph Chang <joseph_chang@davicom.com.tw>
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/dm9601.c | 27 |
1 files changed, 19 insertions, 8 deletions
diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index 732a926e3f51..c0bfc818c701 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c | |||
@@ -473,7 +473,7 @@ static int dm9601_rx_fixup(struct usbnet *dev, struct sk_buff *skb) | |||
473 | static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb, | 473 | static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb, |
474 | gfp_t flags) | 474 | gfp_t flags) |
475 | { | 475 | { |
476 | int len; | 476 | int len, pad; |
477 | 477 | ||
478 | /* format: | 478 | /* format: |
479 | b1: packet length low | 479 | b1: packet length low |
@@ -481,12 +481,23 @@ static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb, | |||
481 | b3..n: packet data | 481 | b3..n: packet data |
482 | */ | 482 | */ |
483 | 483 | ||
484 | len = skb->len; | 484 | len = skb->len + DM_TX_OVERHEAD; |
485 | |||
486 | /* workaround for dm962x errata with tx fifo getting out of | ||
487 | * sync if a USB bulk transfer retry happens right after a | ||
488 | * packet with odd / maxpacket length by adding up to 3 bytes | ||
489 | * padding. | ||
490 | */ | ||
491 | while ((len & 1) || !(len % dev->maxpacket)) | ||
492 | len++; | ||
485 | 493 | ||
486 | if (skb_headroom(skb) < DM_TX_OVERHEAD) { | 494 | len -= DM_TX_OVERHEAD; /* hw header doesn't count as part of length */ |
495 | pad = len - skb->len; | ||
496 | |||
497 | if (skb_headroom(skb) < DM_TX_OVERHEAD || skb_tailroom(skb) < pad) { | ||
487 | struct sk_buff *skb2; | 498 | struct sk_buff *skb2; |
488 | 499 | ||
489 | skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, 0, flags); | 500 | skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, pad, flags); |
490 | dev_kfree_skb_any(skb); | 501 | dev_kfree_skb_any(skb); |
491 | skb = skb2; | 502 | skb = skb2; |
492 | if (!skb) | 503 | if (!skb) |
@@ -495,10 +506,10 @@ static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb, | |||
495 | 506 | ||
496 | __skb_push(skb, DM_TX_OVERHEAD); | 507 | __skb_push(skb, DM_TX_OVERHEAD); |
497 | 508 | ||
498 | /* usbnet adds padding if length is a multiple of packet size | 509 | if (pad) { |
499 | if so, adjust length value in header */ | 510 | memset(skb->data + skb->len, 0, pad); |
500 | if ((skb->len % dev->maxpacket) == 0) | 511 | __skb_put(skb, pad); |
501 | len++; | 512 | } |
502 | 513 | ||
503 | skb->data[0] = len; | 514 | skb->data[0] = len; |
504 | skb->data[1] = len >> 8; | 515 | skb->data[1] = len >> 8; |