aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/usb/asix_common.c
diff options
context:
space:
mode:
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{