forked from Minki/linux
net: asix: handle packets crossing URB boundaries
ASIX AX88772B started to pack data even more tightly. Packets and the ASIX packet header may now cross URB boundaries. To handle this we have to introduce some state between individual calls to asix_rx_fixup(). Signed-off-by: Lucas Stach <dev@lynxeye.de> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
5620df65d8
commit
8b5b6f5413
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user