|
@@ -51,49 +51,89 @@ void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
|
|
|
value, index, data, size);
|
|
|
}
|
|
|
|
|
|
-int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
|
|
+int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
|
|
|
+ struct asix_rx_fixup_info *rx)
|
|
|
{
|
|
|
int offset = 0;
|
|
|
|
|
|
- while (offset + sizeof(u32) < skb->len) {
|
|
|
- struct sk_buff *ax_skb;
|
|
|
- u16 size;
|
|
|
- u32 header = get_unaligned_le32(skb->data + offset);
|
|
|
-
|
|
|
- offset += sizeof(u32);
|
|
|
-
|
|
|
- /* get the packet length */
|
|
|
- size = (u16) (header & 0x7ff);
|
|
|
- if (size != ((~header >> 16) & 0x07ff)) {
|
|
|
- netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n");
|
|
|
- return 0;
|
|
|
+ while (offset + sizeof(u16) <= skb->len) {
|
|
|
+ u16 remaining = 0;
|
|
|
+ unsigned char *data;
|
|
|
+
|
|
|
+ if (!rx->size) {
|
|
|
+ if ((skb->len - offset == sizeof(u16)) ||
|
|
|
+ rx->split_head) {
|
|
|
+ if(!rx->split_head) {
|
|
|
+ rx->header = get_unaligned_le16(
|
|
|
+ skb->data + offset);
|
|
|
+ rx->split_head = true;
|
|
|
+ offset += sizeof(u16);
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ rx->header |= (get_unaligned_le16(
|
|
|
+ skb->data + offset)
|
|
|
+ << 16);
|
|
|
+ rx->split_head = false;
|
|
|
+ offset += sizeof(u16);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ rx->header = get_unaligned_le32(skb->data +
|
|
|
+ offset);
|
|
|
+ offset += sizeof(u32);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* get the packet length */
|
|
|
+ rx->size = (u16) (rx->header & 0x7ff);
|
|
|
+ if (rx->size != ((~rx->header >> 16) & 0x7ff)) {
|
|
|
+ netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n",
|
|
|
+ rx->header, offset);
|
|
|
+ rx->size = 0;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ rx->ax_skb = netdev_alloc_skb_ip_align(dev->net,
|
|
|
+ rx->size);
|
|
|
+ if (!rx->ax_skb)
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
- if ((size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) ||
|
|
|
- (size + offset > skb->len)) {
|
|
|
+ if (rx->size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) {
|
|
|
netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
|
|
|
- size);
|
|
|
+ rx->size);
|
|
|
+ kfree_skb(rx->ax_skb);
|
|
|
return 0;
|
|
|
}
|
|
|
- ax_skb = netdev_alloc_skb_ip_align(dev->net, size);
|
|
|
- if (!ax_skb)
|
|
|
- return 0;
|
|
|
|
|
|
- skb_put(ax_skb, size);
|
|
|
- memcpy(ax_skb->data, skb->data + offset, size);
|
|
|
- usbnet_skb_return(dev, ax_skb);
|
|
|
+ if (rx->size > skb->len - offset) {
|
|
|
+ remaining = rx->size - (skb->len - offset);
|
|
|
+ rx->size = skb->len - offset;
|
|
|
+ }
|
|
|
+
|
|
|
+ data = skb_put(rx->ax_skb, rx->size);
|
|
|
+ memcpy(data, skb->data + offset, rx->size);
|
|
|
+ if (!remaining)
|
|
|
+ usbnet_skb_return(dev, rx->ax_skb);
|
|
|
|
|
|
- offset += (size + 1) & 0xfffe;
|
|
|
+ offset += (rx->size + 1) & 0xfffe;
|
|
|
+ rx->size = remaining;
|
|
|
}
|
|
|
|
|
|
if (skb->len != offset) {
|
|
|
- netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d\n",
|
|
|
- skb->len);
|
|
|
+ netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d, %d\n",
|
|
|
+ skb->len, offset);
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct asix_common_private *dp = dev->driver_priv;
|
|
|
+ struct asix_rx_fixup_info *rx = &dp->rx_fixup_info;
|
|
|
+
|
|
|
+ return asix_rx_fixup_internal(dev, skb, rx);
|
|
|
+}
|
|
|
+
|
|
|
struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
|
|
|
gfp_t flags)
|
|
|
{
|