diff options
Diffstat (limited to 'drivers/net/usb/asix_common.c')
-rw-r--r-- | drivers/net/usb/asix_common.c | 90 |
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 | ||
54 | int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb) | 54 | int 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 | ||
129 | int 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 | |||
97 | struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, | 137 | struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, |
98 | gfp_t flags) | 138 | gfp_t flags) |
99 | { | 139 | { |