diff options
| -rw-r--r-- | drivers/net/usb/asix.h | 15 | ||||
| -rw-r--r-- | drivers/net/usb/asix_common.c | 86 | ||||
| -rw-r--r-- | drivers/net/usb/asix_devices.c | 23 | ||||
| -rw-r--r-- | drivers/net/usb/ax88172a.c | 11 | 
4 files changed, 107 insertions, 28 deletions
diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h index 7afe8ac078e..346c032aa79 100644 --- a/drivers/net/usb/asix.h +++ b/drivers/net/usb/asix.h @@ -167,6 +167,17 @@ struct asix_data {  	u8 res;  }; +struct asix_rx_fixup_info { +	struct sk_buff *ax_skb; +	u32 header; +	u16 size; +	bool split_head; +}; + +struct asix_common_private { +	struct asix_rx_fixup_info rx_fixup_info; +}; +  /* ASIX specific flags */  #define FLAG_EEPROM_MAC		(1UL << 0)  /* init device MAC from eeprom */ @@ -179,7 +190,9 @@ int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,  void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,  			  u16 index, u16 size, void *data); -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 asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb);  struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,  			      gfp_t flags); diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 19bc23f2052..f7f623a5390 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); +	while (offset + sizeof(u16) <= skb->len) { +		u16 remaining = 0; +		unsigned char *data; -		offset += sizeof(u32); +		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 */ -		size = (u16) (header & 0x7ff); -		if (size != ((~header >> 16) & 0x07ff)) { -			netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n"); -			return 0; +			/* 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; +		} -		offset += (size + 1) & 0xfffe; +		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 += (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)  { diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 0ecc3bc6c3d..37de7db56d6 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)  		dev->rx_urb_size = 2048;  	} +	dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL); +	if (!dev->driver_priv) +		return -ENOMEM; +  	return 0;  } +void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf) +{ +	if (dev->driver_priv) +		kfree(dev->driver_priv); +} +  static const struct ethtool_ops ax88178_ethtool_ops = {  	.get_drvinfo		= asix_get_drvinfo,  	.get_link		= asix_get_link, @@ -829,6 +839,10 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)  		dev->rx_urb_size = 2048;  	} +	dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL); +	if (!dev->driver_priv) +			return -ENOMEM; +  	return 0;  } @@ -875,23 +889,25 @@ static const struct driver_info hawking_uf200_info = {  static const struct driver_info ax88772_info = {  	.description = "ASIX AX88772 USB 2.0 Ethernet",  	.bind = ax88772_bind, +	.unbind = ax88772_unbind,  	.status = asix_status,  	.link_reset = ax88772_link_reset,  	.reset = ax88772_reset,  	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET, -	.rx_fixup = asix_rx_fixup, +	.rx_fixup = asix_rx_fixup_common,  	.tx_fixup = asix_tx_fixup,  };  static const struct driver_info ax88772b_info = {  	.description = "ASIX AX88772B USB 2.0 Ethernet",  	.bind = ax88772_bind, +	.unbind = ax88772_unbind,  	.status = asix_status,  	.link_reset = ax88772_link_reset,  	.reset = ax88772_reset,  	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |  	         FLAG_MULTI_PACKET, -	.rx_fixup = asix_rx_fixup, +	.rx_fixup = asix_rx_fixup_common,  	.tx_fixup = asix_tx_fixup,  	.data = FLAG_EEPROM_MAC,  }; @@ -899,11 +915,12 @@ static const struct driver_info ax88772b_info = {  static const struct driver_info ax88178_info = {  	.description = "ASIX AX88178 USB 2.0 Ethernet",  	.bind = ax88178_bind, +	.unbind = ax88772_unbind,  	.status = asix_status,  	.link_reset = ax88178_link_reset,  	.reset = ax88178_reset,  	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR, -	.rx_fixup = asix_rx_fixup, +	.rx_fixup = asix_rx_fixup_common,  	.tx_fixup = asix_tx_fixup,  }; diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c index fdbab72926b..76ee5410d69 100644 --- a/drivers/net/usb/ax88172a.c +++ b/drivers/net/usb/ax88172a.c @@ -35,6 +35,7 @@ struct ax88172a_private {  	u16 phy_addr;  	u16 oldmode;  	int use_embdphy; +	struct asix_rx_fixup_info rx_fixup_info;  };  /* MDIO read and write wrappers for phylib */ @@ -400,6 +401,14 @@ out:  } +static int ax88172a_rx_fixup(struct usbnet *dev, struct sk_buff *skb) +{ +	struct ax88172a_private *dp = dev->driver_priv; +	struct asix_rx_fixup_info *rx = &dp->rx_fixup_info; + +	return asix_rx_fixup_internal(dev, skb, rx); +} +  const struct driver_info ax88172a_info = {  	.description = "ASIX AX88172A USB 2.0 Ethernet",  	.bind = ax88172a_bind, @@ -409,6 +418,6 @@ const struct driver_info ax88172a_info = {  	.status = ax88172a_status,  	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |  		 FLAG_MULTI_PACKET, -	.rx_fixup = asix_rx_fixup, +	.rx_fixup = ax88172a_rx_fixup,  	.tx_fixup = asix_tx_fixup,  };  |