mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-04 02:02:28 +09:00
dm9601: work around tx fifo sync issue on dm962x
commit 4263c86dca 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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
1552c3d8e1
commit
5fd93067a1
@@ -473,7 +473,7 @@ static int dm9601_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
||||
static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
|
||||
gfp_t flags)
|
||||
{
|
||||
int len;
|
||||
int len, pad;
|
||||
|
||||
/* format:
|
||||
b1: packet length low
|
||||
@@ -481,12 +481,23 @@ static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
|
||||
b3..n: packet data
|
||||
*/
|
||||
|
||||
len = skb->len;
|
||||
len = skb->len + DM_TX_OVERHEAD;
|
||||
|
||||
if (skb_headroom(skb) < DM_TX_OVERHEAD) {
|
||||
/* workaround for dm962x errata with tx fifo getting out of
|
||||
* sync if a USB bulk transfer retry happens right after a
|
||||
* packet with odd / maxpacket length by adding up to 3 bytes
|
||||
* padding.
|
||||
*/
|
||||
while ((len & 1) || !(len % dev->maxpacket))
|
||||
len++;
|
||||
|
||||
len -= DM_TX_OVERHEAD; /* hw header doesn't count as part of length */
|
||||
pad = len - skb->len;
|
||||
|
||||
if (skb_headroom(skb) < DM_TX_OVERHEAD || skb_tailroom(skb) < pad) {
|
||||
struct sk_buff *skb2;
|
||||
|
||||
skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, 0, flags);
|
||||
skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, pad, flags);
|
||||
dev_kfree_skb_any(skb);
|
||||
skb = skb2;
|
||||
if (!skb)
|
||||
@@ -495,10 +506,10 @@ static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
|
||||
|
||||
__skb_push(skb, DM_TX_OVERHEAD);
|
||||
|
||||
/* usbnet adds padding if length is a multiple of packet size
|
||||
if so, adjust length value in header */
|
||||
if ((skb->len % dev->maxpacket) == 0)
|
||||
len++;
|
||||
if (pad) {
|
||||
memset(skb->data + skb->len, 0, pad);
|
||||
__skb_put(skb, pad);
|
||||
}
|
||||
|
||||
skb->data[0] = len;
|
||||
skb->data[1] = len >> 8;
|
||||
|
||||
Reference in New Issue
Block a user