aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/usb
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
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')
-rw-r--r--drivers/net/usb/asix.h15
-rw-r--r--drivers/net/usb/asix_common.c90
-rw-r--r--drivers/net/usb/asix_devices.c23
-rw-r--r--drivers/net/usb/ax88172a.c11
4 files changed, 109 insertions, 30 deletions
diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h
index 7afe8ac078e8..346c032aa795 100644
--- a/drivers/net/usb/asix.h
+++ b/drivers/net/usb/asix.h
@@ -167,6 +167,17 @@ struct asix_data {
167 u8 res; 167 u8 res;
168}; 168};
169 169
170struct asix_rx_fixup_info {
171 struct sk_buff *ax_skb;
172 u32 header;
173 u16 size;
174 bool split_head;
175};
176
177struct asix_common_private {
178 struct asix_rx_fixup_info rx_fixup_info;
179};
180
170/* ASIX specific flags */ 181/* ASIX specific flags */
171#define FLAG_EEPROM_MAC (1UL << 0) /* init device MAC from eeprom */ 182#define FLAG_EEPROM_MAC (1UL << 0) /* init device MAC from eeprom */
172 183
@@ -179,7 +190,9 @@ int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
179void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, 190void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
180 u16 index, u16 size, void *data); 191 u16 index, u16 size, void *data);
181 192
182int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb); 193int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
194 struct asix_rx_fixup_info *rx);
195int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb);
183 196
184struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 197struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
185 gfp_t flags); 198 gfp_t flags);
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{
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index 0ecc3bc6c3d7..37de7db56d63 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -495,9 +495,19 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
495 dev->rx_urb_size = 2048; 495 dev->rx_urb_size = 2048;
496 } 496 }
497 497
498 dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL);
499 if (!dev->driver_priv)
500 return -ENOMEM;
501
498 return 0; 502 return 0;
499} 503}
500 504
505void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf)
506{
507 if (dev->driver_priv)
508 kfree(dev->driver_priv);
509}
510
501static const struct ethtool_ops ax88178_ethtool_ops = { 511static const struct ethtool_ops ax88178_ethtool_ops = {
502 .get_drvinfo = asix_get_drvinfo, 512 .get_drvinfo = asix_get_drvinfo,
503 .get_link = asix_get_link, 513 .get_link = asix_get_link,
@@ -829,6 +839,10 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
829 dev->rx_urb_size = 2048; 839 dev->rx_urb_size = 2048;
830 } 840 }
831 841
842 dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL);
843 if (!dev->driver_priv)
844 return -ENOMEM;
845
832 return 0; 846 return 0;
833} 847}
834 848
@@ -875,23 +889,25 @@ static const struct driver_info hawking_uf200_info = {
875static const struct driver_info ax88772_info = { 889static const struct driver_info ax88772_info = {
876 .description = "ASIX AX88772 USB 2.0 Ethernet", 890 .description = "ASIX AX88772 USB 2.0 Ethernet",
877 .bind = ax88772_bind, 891 .bind = ax88772_bind,
892 .unbind = ax88772_unbind,
878 .status = asix_status, 893 .status = asix_status,
879 .link_reset = ax88772_link_reset, 894 .link_reset = ax88772_link_reset,
880 .reset = ax88772_reset, 895 .reset = ax88772_reset,
881 .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET, 896 .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET,
882 .rx_fixup = asix_rx_fixup, 897 .rx_fixup = asix_rx_fixup_common,
883 .tx_fixup = asix_tx_fixup, 898 .tx_fixup = asix_tx_fixup,
884}; 899};
885 900
886static const struct driver_info ax88772b_info = { 901static const struct driver_info ax88772b_info = {
887 .description = "ASIX AX88772B USB 2.0 Ethernet", 902 .description = "ASIX AX88772B USB 2.0 Ethernet",
888 .bind = ax88772_bind, 903 .bind = ax88772_bind,
904 .unbind = ax88772_unbind,
889 .status = asix_status, 905 .status = asix_status,
890 .link_reset = ax88772_link_reset, 906 .link_reset = ax88772_link_reset,
891 .reset = ax88772_reset, 907 .reset = ax88772_reset,
892 .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | 908 .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
893 FLAG_MULTI_PACKET, 909 FLAG_MULTI_PACKET,
894 .rx_fixup = asix_rx_fixup, 910 .rx_fixup = asix_rx_fixup_common,
895 .tx_fixup = asix_tx_fixup, 911 .tx_fixup = asix_tx_fixup,
896 .data = FLAG_EEPROM_MAC, 912 .data = FLAG_EEPROM_MAC,
897}; 913};
@@ -899,11 +915,12 @@ static const struct driver_info ax88772b_info = {
899static const struct driver_info ax88178_info = { 915static const struct driver_info ax88178_info = {
900 .description = "ASIX AX88178 USB 2.0 Ethernet", 916 .description = "ASIX AX88178 USB 2.0 Ethernet",
901 .bind = ax88178_bind, 917 .bind = ax88178_bind,
918 .unbind = ax88772_unbind,
902 .status = asix_status, 919 .status = asix_status,
903 .link_reset = ax88178_link_reset, 920 .link_reset = ax88178_link_reset,
904 .reset = ax88178_reset, 921 .reset = ax88178_reset,
905 .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR, 922 .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR,
906 .rx_fixup = asix_rx_fixup, 923 .rx_fixup = asix_rx_fixup_common,
907 .tx_fixup = asix_tx_fixup, 924 .tx_fixup = asix_tx_fixup,
908}; 925};
909 926
diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c
index fdbab72926bd..76ee5410d69e 100644
--- a/drivers/net/usb/ax88172a.c
+++ b/drivers/net/usb/ax88172a.c
@@ -35,6 +35,7 @@ struct ax88172a_private {
35 u16 phy_addr; 35 u16 phy_addr;
36 u16 oldmode; 36 u16 oldmode;
37 int use_embdphy; 37 int use_embdphy;
38 struct asix_rx_fixup_info rx_fixup_info;
38}; 39};
39 40
40/* MDIO read and write wrappers for phylib */ 41/* MDIO read and write wrappers for phylib */
@@ -400,6 +401,14 @@ out:
400 401
401} 402}
402 403
404static int ax88172a_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
405{
406 struct ax88172a_private *dp = dev->driver_priv;
407 struct asix_rx_fixup_info *rx = &dp->rx_fixup_info;
408
409 return asix_rx_fixup_internal(dev, skb, rx);
410}
411
403const struct driver_info ax88172a_info = { 412const struct driver_info ax88172a_info = {
404 .description = "ASIX AX88172A USB 2.0 Ethernet", 413 .description = "ASIX AX88172A USB 2.0 Ethernet",
405 .bind = ax88172a_bind, 414 .bind = ax88172a_bind,
@@ -409,6 +418,6 @@ const struct driver_info ax88172a_info = {
409 .status = ax88172a_status, 418 .status = ax88172a_status,
410 .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | 419 .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
411 FLAG_MULTI_PACKET, 420 FLAG_MULTI_PACKET,
412 .rx_fixup = asix_rx_fixup, 421 .rx_fixup = ax88172a_rx_fixup,
413 .tx_fixup = asix_tx_fixup, 422 .tx_fixup = asix_tx_fixup,
414}; 423};