This patch is against the current torvalds git tree with
usb-usbnet-gfp_flags-fix.patch and David Brownell's nine usbnet
modularization patches applied.
I found that the modularization patches broke the ax88772 framing code.
The rx urb's were not being sized large enough to hold the maximum burst
the ax88772 device might send. After some off-line discussions with
David-B, we concluded the best solution was to add a rx_urb_size field
in the usbnet structure that allows a mini-drivers to specify the
required urb size.
I also fixed the mdio byte order problems recently discussed on the
list. This was causing media negotiation problems for a user,
"Magnus", running on a PPC processor. I adopted the endian conversion
wrapper approach.
I replaced some of the ax88772 framing code that caused unaligned access
faults on architectures that are picky about that (mips, for one).
There are still more of these lurking (e.g. when multiple eth frames are
received in a single usb bulk transfer), but this catches the major
cause of those faults. The remaining cases are less common, and messier
to fix, so I'll save that for a future patch.
The asix link_reset code was looking only at the link partner
capabilities. This failed when speeds were forced with ethtool. This
was a bug I introduced in an earlier suggested modification to support
pause frame flow control. I've corrected this by using both the link
partner and advertised capabilities and mii_nway_result to determine the
current media state.
Please review and let me know if there is anything I can improve.
Also, if this is too much for one patch, and you'd rather see the
changes separated, let me know that too.
This is a precursor to my changes to add support for the airlink ax88178
based gige adapter to the asix usbnet minidriver.
--Jamie
diff -ur linux-2.6/drivers/usb/net/asix.c linux-2.6-new/drivers/usb/net/asix.c
--- linux-2.6/drivers/usb/net/asix.c 2005-08-18 22:38:39.000000000 -0600
+++ linux-2.6-new/drivers/usb/net/asix.c 2005-08-18 22:54:16.000000000 -0600
@@ -281,6 +281,12 @@
return res & 0xffff;
}
+/* same as above, but converts resulting value to cpu byte order */
+static int ax8817x_mdio_read_le(struct net_device *netdev, int phy_id, int loc)
+{
+ return le16_to_cpu(ax8817x_mdio_read(netdev,phy_id, loc));
+}
+
static void
ax8817x_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
{
@@ -294,14 +300,25 @@
ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf);
}
+/* same as above, but converts new value to le16 byte order before writing */
+static void
+ax8817x_mdio_write_le(struct net_device *netdev, int phy_id, int loc, int val)
+{
+ ax8817x_mdio_write( netdev, phy_id, loc, cpu_to_le16(val) );
+}
+
static int ax88172_link_reset(struct usbnet *dev)
{
u16 lpa;
+ u16 adv;
+ u16 res;
u8 mode;
mode = AX_MEDIUM_TX_ABORT_ALLOW | AX_MEDIUM_FLOW_CONTROL_EN;
- lpa = ax8817x_mdio_read(dev->net, dev->mii.phy_id, MII_LPA);
- if (lpa & LPA_DUPLEX)
+ lpa = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
+ adv = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+ res = mii_nway_result(lpa|adv);
+ if (res & LPA_DUPLEX)
mode |= AX_MEDIUM_FULL_DUPLEX;
ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
@@ -488,16 +505,11 @@
dev->net->set_multicast_list = ax8817x_set_multicast;
dev->net->ethtool_ops = &ax8817x_ethtool_ops;
- ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
- ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+ ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+ ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
mii_nway_restart(&dev->mii);
- if (dev->driver_info->flags & FLAG_FRAMING_AX) {
- /* REVISIT: adjust hard_header_len too */
- dev->hard_mtu = 2048;
- }
-
return 0;
out2:
kfree(buf);
@@ -634,8 +646,8 @@
dev->net->set_multicast_list = ax8817x_set_multicast;
dev->net->ethtool_ops = &ax88772_ethtool_ops;
- ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
- ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+ ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+ ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_CSMA);
mii_nway_restart(&dev->mii);
@@ -667,6 +679,13 @@
kfree(buf);
+ /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
+ if (dev->driver_info->flags & FLAG_FRAMING_AX) {
+ /* hard_mtu is still the default - the device does not support
+ jumbo eth frames */
+ dev->rx_urb_size = 2048;
+ }
+
return 0;
out2:
@@ -677,24 +696,26 @@
static int ax88772_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
- u32 *header;
+ u8 *head;
+ u32 header;
char *packet;
struct sk_buff *ax_skb;
u16 size;
- header = (u32 *) skb->data;
- le32_to_cpus(header);
- packet = (char *)(header + 1);
+ head = (u8 *) skb->data;
+ memcpy(&header, head, sizeof(header));
+ le32_to_cpus(&header);
+ packet = head + sizeof(header);
skb_pull(skb, 4);
while (skb->len > 0) {
- if ((short)(*header & 0x0000ffff) !=
- ~((short)((*header & 0xffff0000) >> 16))) {
+ if ((short)(header & 0x0000ffff) !=
+ ~((short)((header & 0xffff0000) >> 16))) {
devdbg(dev,"header length data is error");
}
/* get the packet length */
- size = (u16) (*header & 0x0000ffff);
+ size = (u16) (header & 0x0000ffff);
if ((skb->len) - ((size + 1) & 0xfffe) == 0)
return 2;
@@ -717,9 +738,10 @@
if (skb->len == 0)
break;
- header = (u32 *) skb->data;
- le32_to_cpus(header);
- packet = (char *)(header + 1);
+ head = (u8 *) skb->data;
+ memcpy(&header, head, sizeof(header));
+ le32_to_cpus(&header);
+ packet = head + sizeof(header);
skb_pull(skb, 4);
}
@@ -736,8 +758,8 @@
int padlen;
int headroom = skb_headroom(skb);
int tailroom = skb_tailroom(skb);
- u32 *packet_len;
- u32 *padbytes_ptr;
+ u32 packet_len;
+ u32 padbytes = 0xffff0000;
padlen = ((skb->len + 4) % 512) ? 0 : 4;
@@ -756,15 +778,13 @@
return NULL;
}
- packet_len = (u32 *) skb_push(skb, 4);
-
- packet_len = (u32 *) skb->data;
- *packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
+ skb_push(skb, 4);
+ packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
+ memcpy(skb->data, &packet_len, sizeof(packet_len));
if ((skb->len % 512) == 0) {
- padbytes_ptr = (u32 *) skb->tail;
- *padbytes_ptr = 0xffff0000;
- skb_put(skb, padlen);
+ memcpy( skb->tail, &padbytes, sizeof(padbytes));
+ skb_put(skb, sizeof(padbytes));
}
return skb;
}
@@ -772,14 +792,18 @@
static int ax88772_link_reset(struct usbnet *dev)
{
u16 lpa;
+ u16 adv;
+ u16 res;
u16 mode;
mode = AX88772_MEDIUM_DEFAULT;
- lpa = ax8817x_mdio_read(dev->net, dev->mii.phy_id, MII_LPA);
+ lpa = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
+ adv = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+ res = mii_nway_result(lpa|adv);
- if ((lpa & LPA_DUPLEX) == 0)
+ if ((res & LPA_DUPLEX) == 0)
mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
- if ((lpa & LPA_100) == 0)
+ if ((res & LPA_100) == 0)
mode &= ~AX88772_MEDIUM_100MB;
ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
diff -ur linux-2.6/drivers/usb/net/usbnet.c linux-2.6-new/drivers/usb/net/usbnet.c
--- linux-2.6/drivers/usb/net/usbnet.c 2005-08-18 22:39:38.000000000 -0600
+++ linux-2.6-new/drivers/usb/net/usbnet.c 2005-08-18 22:54:16.000000000 -0600
@@ -287,10 +287,12 @@
struct skb_data *entry;
int retval = 0;
unsigned long lockflags;
- size_t size;
+ size_t size = dev->rx_urb_size;
- size = max(dev->net->hard_header_len + dev->net->mtu,
- (unsigned)ETH_FRAME_LEN);
+ // Set the urb size to the default, if the mini driver didn't
+ if (size==0)
+ size = max(dev->net->hard_header_len + dev->net->mtu,
+ (unsigned)ETH_FRAME_LEN);
if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {
if (netif_msg_rx_err (dev))
devdbg (dev, "no rx skb");
@@ -1071,7 +1073,7 @@
strcpy (net->name, "usb%d");
memcpy (net->dev_addr, node_id, sizeof node_id);
dev->hard_mtu = net->mtu + net->hard_header_len;
-
+ dev->rx_urb_size = 0; /* filled in later */
#if 0
// dma_supported() is deeply broken on almost all architectures
// possible with some EHCI controllers
diff -ur linux-2.6/drivers/usb/net/usbnet.h linux-2.6-new/drivers/usb/net/usbnet.h
--- linux-2.6/drivers/usb/net/usbnet.h 2005-08-18 22:39:18.000000000 -0600
+++ linux-2.6-new/drivers/usb/net/usbnet.h 2005-08-18 22:54:16.000000000 -0600
@@ -45,6 +45,7 @@
unsigned long data [5];
u32 xid;
u32 hard_mtu; /* count any extra framing */
+ size_t rx_urb_size; /* size for rx urbs */
struct mii_if_info mii;
/* various kinds of pending driver work */