aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/usb/asix_common.c
diff options
context:
space:
mode:
authorLucas Stach <dev@lynxeye.de>2013-01-15 23:24:07 -0500
committerDavid S. Miller <davem@davemloft.net>2013-01-18 14:13:29 -0500
commit8b5b6f5413e97c3e8bafcdd67553d508f4f698cd (patch)
treefb90f82d9232e927f0656f3cb9cb0226e030c31c /drivers/net/usb/asix_common.c
parent5620df65d81292c5fb1beba8d380ef58cd98b53f (diff)
net: asix: handle packets crossing URB boundaries
ASIX AX88772B started to pack data even more tightly. Packets and the ASIX packet header may now cross URB boundaries. To handle this we have to introduce some state between individual calls to asix_rx_fixup(). Signed-off-by: Lucas Stach <dev@lynxeye.de> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb/asix_common.c')
-rw-r--r--drivers/net/usb/asix_common.c90
1 files changed, 65 insertions, 25 deletions
diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c
index 19bc23f20526..f7f623a5390e 100644
--- a/drivers/net/usb/asix_common.c
+++ b/drivers/net/usb/asix_common.c
@@ -51,49 +51,89 @@ void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
51 value, index, data, size); 51 value, index, data, size);
52} 52}
53 53
54int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 54int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
55 struct asix_rx_fixup_info *rx)
55{ 56{
56 int offset = 0; 57 int offset = 0;
57 58
58 while (offset + sizeof(u32) < skb->len) { 59 while (offset + sizeof(u16) <= skb->len) {
59 struct sk_buff *ax_skb; 60 u16 remaining = 0;
60 u16 size; 61 unsigned char *data;
61 u32 header = get_unaligned_le32(skb->data + offset); 62
62 63 if (!rx->size) {
63 offset += sizeof(u32); 64 if ((skb->len - offset == sizeof(u16)) ||
64 65 rx->split_head) {
65 /* get the packet length */ 66 if(!rx->split_head) {
66 size = (u16) (header & 0x7ff); 67 rx->header = get_unaligned_le16(
67 if (size != ((~header >> 16) & 0x07ff)) { 68 skb->data + offset);
68 netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n"); 69 rx->split_head = true;
69 return 0; 70 offset += sizeof(u16);
71 break;
72 } else {
73 rx->header |= (get_unaligned_le16(
74 skb->data + offset)
75 << 16);
76 rx->split_head = false;
77 offset += sizeof(u16);
78 }
79 } else {
80 rx->header = get_unaligned_le32(skb->data +
81 offset);
82 offset += sizeof(u32);
83 }
84
85 /* get the packet length */
86 rx->size = (u16) (rx->header & 0x7ff);
87 if (rx->size != ((~rx->header >> 16) & 0x7ff)) {
88 netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n",
89 rx->header, offset);
90 rx->size = 0;
91 return 0;
92 }
93 rx->ax_skb = netdev_alloc_skb_ip_align(dev->net,
94 rx->size);
95 if (!rx->ax_skb)
96 return 0;
70 } 97 }
71 98
72 if ((size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) || 99 if (rx->size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) {
73 (size + offset > skb->len)) {
74 netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", 100 netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
75 size); 101 rx->size);
102 kfree_skb(rx->ax_skb);
76 return 0; 103 return 0;
77 } 104 }
78 ax_skb = netdev_alloc_skb_ip_align(dev->net, size);
79 if (!ax_skb)
80 return 0;
81 105
82 skb_put(ax_skb, size); 106 if (rx->size > skb->len - offset) {
83 memcpy(ax_skb->data, skb->data + offset, size); 107 remaining = rx->size - (skb->len - offset);
84 usbnet_skb_return(dev, ax_skb); 108 rx->size = skb->len - offset;
109 }
110
111 data = skb_put(rx->ax_skb, rx->size);
112 memcpy(data, skb->data + offset, rx->size);
113 if (!remaining)
114 usbnet_skb_return(dev, rx->ax_skb);
85 115
86 offset += (size + 1) & 0xfffe; 116 offset += (rx->size + 1) & 0xfffe;
117 rx->size = remaining;
87 } 118 }
88 119
89 if (skb->len != offset) { 120 if (skb->len != offset) {
90 netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d\n", 121 netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d, %d\n",
91 skb->len); 122 skb->len, offset);
92 return 0; 123 return 0;
93 } 124 }
125
94 return 1; 126 return 1;
95} 127}
96 128
129int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb)
130{
131 struct asix_common_private *dp = dev->driver_priv;
132 struct asix_rx_fixup_info *rx = &dp->rx_fixup_info;
133
134 return asix_rx_fixup_internal(dev, skb, rx);
135}
136
97struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 137struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
98 gfp_t flags) 138 gfp_t flags)
99{ 139{