diff options
Diffstat (limited to 'drivers/net/usb/asix_common.c')
| -rw-r--r-- | drivers/net/usb/asix_common.c | 94 | 
1 files changed, 67 insertions, 27 deletions
| diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 50d167330d38..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,  			       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)  { @@ -510,8 +550,8 @@ void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)  {  	/* Inherit standard device info */  	usbnet_get_drvinfo(net, info); -	strncpy (info->driver, DRIVER_NAME, sizeof info->driver); -	strncpy (info->version, DRIVER_VERSION, sizeof info->version); +	strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); +	strlcpy(info->version, DRIVER_VERSION, sizeof(info->version));  	info->eedump_len = AX_EEPROM_LEN;  } |