ChangeSet 1.1243.50.7, 2003/06/06 15:08:08-07:00, petkan@users.sourceforge.net [PATCH] USB: pegasus patch better error handling and ethtool ioctl() cleanup. HOME_PNA now should work (at least for the pegasus II based devices). one more vendor and device IDs. drivers/usb/net/pegasus.c | 106 +++++++++++++++++++++++++++++++++------------- drivers/usb/net/pegasus.h | 5 +- 2 files changed, 82 insertions(+), 29 deletions(-) diff -Nru a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c --- a/drivers/usb/net/pegasus.c Tue Jun 10 17:11:47 2003 +++ b/drivers/usb/net/pegasus.c Tue Jun 10 17:11:47 2003 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2002 Petko Manolov (petkan@users.sourceforge.net) + * Copyright (c) 1999-2003 Petko Manolov (petkan@users.sourceforge.net) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -45,7 +45,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.5.10 (2003/04/01)" +#define DRIVER_VERSION "v0.5.12 (2003/06/06)" #define DRIVER_AUTHOR "Petko Manolov " #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" @@ -564,7 +564,14 @@ dbg("%s: reset MAC", net->name); pegasus->flags &= ~PEGASUS_RX_BUSY; break; + case -EPIPE: /* stall, or disconnect from TT */ + /* FIXME schedule work to clear the halt */ + warn("%s: no rx stall recovery", net->name); + return; case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dbg("%s: rx unlink, %d", net->name, urb->status); return; default: dbg("%s: RX status %d", net->name, urb->status); @@ -604,6 +611,9 @@ pegasus->stats.rx_packets++; pegasus->stats.rx_bytes += pkt_len; + if (pegasus->flags & PEGASUS_UNPLUG) + return; + spin_lock(&pegasus->rx_pool_lock); pegasus->rx_skb = pull_skb(pegasus); spin_unlock(&pegasus->rx_pool_lock); @@ -631,24 +641,24 @@ static void rx_fixup(unsigned long data) { pegasus_t *pegasus; + unsigned long flags; pegasus = (pegasus_t *) data; + if (pegasus->flags & PEGASUS_UNPLUG) + return; - spin_lock_irq(&pegasus->rx_pool_lock); + spin_lock_irqsave(&pegasus->rx_pool_lock, flags); fill_skb_pool(pegasus); - spin_unlock_irq(&pegasus->rx_pool_lock); if (pegasus->flags & PEGASUS_RX_URB_FAIL) if (pegasus->rx_skb) goto try_again; if (pegasus->rx_skb == NULL) { - spin_lock_irq(&pegasus->rx_pool_lock); pegasus->rx_skb = pull_skb(pegasus); - spin_unlock_irq(&pegasus->rx_pool_lock); } if (pegasus->rx_skb == NULL) { warn("wow, low on memory"); tasklet_schedule(&pegasus->rx_tl); - return; + goto done; } usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), @@ -661,23 +671,41 @@ } else { pegasus->flags &= ~PEGASUS_RX_URB_FAIL; } +done: + spin_unlock_irqrestore(&pegasus->rx_pool_lock, flags); } static void write_bulk_callback(struct urb *urb, struct pt_regs *regs) { pegasus_t *pegasus = urb->context; + struct net_device *net = pegasus->net; if (!pegasus || !(pegasus->flags & PEGASUS_RUNNING)) return; - if (!netif_device_present(pegasus->net)) + if (!netif_device_present(net)) return; - if (urb->status) - info("%s: TX status %d", pegasus->net->name, urb->status); + switch (urb->status) { + case -EPIPE: + /* FIXME schedule_work() to clear the tx halt */ + netif_stop_queue(net); + warn("%s: no tx stall recovery", net->name); + return; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dbg("%s: tx unlink, %d", net->name, urb->status); + return; + default: + info("%s: TX status %d", net->name, urb->status); + /* FALL THROUGH */ + case 0: + break; + } - pegasus->net->trans_start = jiffies; - netif_wake_queue(pegasus->net); + net->trans_start = jiffies; + netif_wake_queue(net); } static void intr_callback(struct urb *urb, struct pt_regs *regs) @@ -754,8 +782,16 @@ write_bulk_callback, pegasus); if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) { warn("failed tx_urb %d", res); - pegasus->stats.tx_errors++; - netif_start_queue(net); + switch (res) { + case -EPIPE: /* stall, or disconnect from TT */ + /* cleanup should already have been scheduled */ + break; + case -ENODEV: /* disconnect() upcoming */ + break; + default: + pegasus->stats.tx_errors++; + netif_start_queue(net); + } } else { pegasus->stats.tx_packets++; pegasus->stats.tx_bytes += skb->len; @@ -908,6 +944,7 @@ netif_stop_queue(net); if (!(pegasus->flags & PEGASUS_UNPLUG)) disable_net_traffic(pegasus); + tasklet_kill(&pegasus->rx_tl); unlink_all_urbs(pegasus); up(&pegasus->sem); @@ -926,11 +963,15 @@ switch (ethcmd) { /* get driver-specific version/etc. info */ case ETHTOOL_GDRVINFO:{ - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strlcpy(info.driver, driver_name, - sizeof (info.driver)); - strlcpy(info.version, DRIVER_VERSION, - sizeof (info.version)); + struct ethtool_drvinfo info; + memset (&info, 0, sizeof (info)); + info.cmd = ETHTOOL_GDRVINFO; + strncpy(info.driver, driver_name, + sizeof (info.driver) - 1); + strncpy(info.version, DRIVER_VERSION, + sizeof (info.version) - 1); + usb_make_path(pegasus->usb, info.bus_info, + sizeof (info.bus_info)); if (copy_to_user(useraddr, &info, sizeof (info))) return -EFAULT; return 0; @@ -999,12 +1040,15 @@ return -EFAULT; switch (cmd) { case ETHTOOL_GDRVINFO:{ - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strncpy(info.driver, driver_name, sizeof info.driver); + struct ethtool_drvinfo info; + memset (&info, 0, sizeof (info)); + info.cmd = ETHTOOL_GDRVINFO; + strncpy(info.driver, driver_name, + sizeof (info.driver) - 1); strncpy(info.version, DRIVER_VERSION, - ETHTOOL_BUSINFO_LEN); + sizeof (info.version) - 1); usb_make_path(pegasus->usb, info.bus_info, - sizeof info.bus_info); + sizeof (info.bus_info)); if (copy_to_user(uaddr, &info, sizeof (info))) return -EFAULT; return 0; @@ -1012,15 +1056,20 @@ case ETHTOOL_GSET:{ struct ethtool_cmd ecmd; short lpa, bmcr; + u8 port; - memset(&ecmd, 0, sizeof ecmd); + memset(&ecmd, 0, sizeof (ecmd)); ecmd.supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); - ecmd.port = PORT_TP; + get_registers(pegasus, Reg7b, 1, &port); + if (port == 0) + ecmd.port = PORT_MII; + else + ecmd.port = PORT_TP; ecmd.transceiver = XCVR_INTERNAL; ecmd.phy_address = pegasus->phy; read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr); @@ -1142,7 +1191,10 @@ set_register(pegasus, Reg1d, 0); set_register(pegasus, Reg7b, 1); mdelay(100); - set_register(pegasus, Reg7b, 2); + if ((pegasus->features & HAS_HOME_PNA) && mii_mode) + set_register(pegasus, Reg7b, 0); + else + set_register(pegasus, Reg7b, 2); set_register(pegasus, 0x83, data); get_registers(pegasus, 0x83, 1, &data); @@ -1262,7 +1314,6 @@ pegasus->flags |= PEGASUS_UNPLUG; unregister_netdev(pegasus->net); usb_put_dev(interface_to_usbdev(intf)); - unlink_all_urbs(pegasus); free_all_urbs(pegasus); free_skb_pool(pegasus); if (pegasus->rx_skb) @@ -1273,7 +1324,6 @@ } static struct usb_driver pegasus_driver = { - .owner = THIS_MODULE, .name = driver_name, .probe = pegasus_probe, .disconnect = pegasus_disconnect, diff -Nru a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h --- a/drivers/usb/net/pegasus.h Tue Jun 10 17:11:47 2003 +++ b/drivers/usb/net/pegasus.h Tue Jun 10 17:11:47 2003 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2002 Petko Manolov - Petkan (petkan@users.sourceforge.net) + * Copyright (c) 1999-2003 Petko Manolov - Petkan (petkan@users.sourceforge.net) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published @@ -130,6 +130,7 @@ #define VENDOR_ELCON 0x0db7 #define VENDOR_ELSA 0x05cc #define VENDOR_HAWKING 0x0e66 +#define VENDOR_HP 0x03f0 #define VENDOR_IODATA 0x04bb #define VENDOR_KINGSTON 0x0951 #define VENDOR_LANEED 0x056e @@ -224,6 +225,8 @@ DEFAULT_GPIO_RESET ) PEGASUS_DEV( "Hawking UF100 10/100 Ethernet", VENDOR_HAWKING, 0x400c, DEFAULT_GPIO_RESET | PEGASUS_II ) +PEGASUS_DEV( "HP hn210c Ethernet USB", VENDOR_HP, 0x811c, + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "IO DATA USB ET/TX", VENDOR_IODATA, 0x0904, DEFAULT_GPIO_RESET ) PEGASUS_DEV( "IO DATA USB ET/TX-S", VENDOR_IODATA, 0x0913,