Commit 8e3231bc authored by Petko Manolov's avatar Petko Manolov Committed by Linus Torvalds

[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.
parent eeb0426a
/* /*
* 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 * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
/* /*
* Version Information * 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 <petkan@users.sourceforge.net>" #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
...@@ -564,7 +564,14 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs) ...@@ -564,7 +564,14 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs)
dbg("%s: reset MAC", net->name); dbg("%s: reset MAC", net->name);
pegasus->flags &= ~PEGASUS_RX_BUSY; pegasus->flags &= ~PEGASUS_RX_BUSY;
break; 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 -ENOENT:
case -ECONNRESET:
case -ESHUTDOWN:
dbg("%s: rx unlink, %d", net->name, urb->status);
return; return;
default: default:
dbg("%s: RX status %d", net->name, urb->status); dbg("%s: RX status %d", net->name, urb->status);
...@@ -604,6 +611,9 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs) ...@@ -604,6 +611,9 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs)
pegasus->stats.rx_packets++; pegasus->stats.rx_packets++;
pegasus->stats.rx_bytes += pkt_len; pegasus->stats.rx_bytes += pkt_len;
if (pegasus->flags & PEGASUS_UNPLUG)
return;
spin_lock(&pegasus->rx_pool_lock); spin_lock(&pegasus->rx_pool_lock);
pegasus->rx_skb = pull_skb(pegasus); pegasus->rx_skb = pull_skb(pegasus);
spin_unlock(&pegasus->rx_pool_lock); spin_unlock(&pegasus->rx_pool_lock);
...@@ -631,24 +641,24 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs) ...@@ -631,24 +641,24 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs)
static void rx_fixup(unsigned long data) static void rx_fixup(unsigned long data)
{ {
pegasus_t *pegasus; pegasus_t *pegasus;
unsigned long flags;
pegasus = (pegasus_t *) data; 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); fill_skb_pool(pegasus);
spin_unlock_irq(&pegasus->rx_pool_lock);
if (pegasus->flags & PEGASUS_RX_URB_FAIL) if (pegasus->flags & PEGASUS_RX_URB_FAIL)
if (pegasus->rx_skb) if (pegasus->rx_skb)
goto try_again; goto try_again;
if (pegasus->rx_skb == NULL) { if (pegasus->rx_skb == NULL) {
spin_lock_irq(&pegasus->rx_pool_lock);
pegasus->rx_skb = pull_skb(pegasus); pegasus->rx_skb = pull_skb(pegasus);
spin_unlock_irq(&pegasus->rx_pool_lock);
} }
if (pegasus->rx_skb == NULL) { if (pegasus->rx_skb == NULL) {
warn("wow, low on memory"); warn("wow, low on memory");
tasklet_schedule(&pegasus->rx_tl); tasklet_schedule(&pegasus->rx_tl);
return; goto done;
} }
usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,
usb_rcvbulkpipe(pegasus->usb, 1), usb_rcvbulkpipe(pegasus->usb, 1),
...@@ -661,23 +671,41 @@ static void rx_fixup(unsigned long data) ...@@ -661,23 +671,41 @@ static void rx_fixup(unsigned long data)
} else { } else {
pegasus->flags &= ~PEGASUS_RX_URB_FAIL; 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) static void write_bulk_callback(struct urb *urb, struct pt_regs *regs)
{ {
pegasus_t *pegasus = urb->context; pegasus_t *pegasus = urb->context;
struct net_device *net = pegasus->net;
if (!pegasus || !(pegasus->flags & PEGASUS_RUNNING)) if (!pegasus || !(pegasus->flags & PEGASUS_RUNNING))
return; return;
if (!netif_device_present(pegasus->net)) if (!netif_device_present(net))
return; return;
if (urb->status) switch (urb->status) {
info("%s: TX status %d", pegasus->net->name, 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; net->trans_start = jiffies;
netif_wake_queue(pegasus->net); netif_wake_queue(net);
} }
static void intr_callback(struct urb *urb, struct pt_regs *regs) static void intr_callback(struct urb *urb, struct pt_regs *regs)
...@@ -754,8 +782,16 @@ static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) ...@@ -754,8 +782,16 @@ static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
write_bulk_callback, pegasus); write_bulk_callback, pegasus);
if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) { if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) {
warn("failed tx_urb %d", res); warn("failed tx_urb %d", res);
pegasus->stats.tx_errors++; switch (res) {
netif_start_queue(net); 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 { } else {
pegasus->stats.tx_packets++; pegasus->stats.tx_packets++;
pegasus->stats.tx_bytes += skb->len; pegasus->stats.tx_bytes += skb->len;
...@@ -908,6 +944,7 @@ static int pegasus_close(struct net_device *net) ...@@ -908,6 +944,7 @@ static int pegasus_close(struct net_device *net)
netif_stop_queue(net); netif_stop_queue(net);
if (!(pegasus->flags & PEGASUS_UNPLUG)) if (!(pegasus->flags & PEGASUS_UNPLUG))
disable_net_traffic(pegasus); disable_net_traffic(pegasus);
tasklet_kill(&pegasus->rx_tl);
unlink_all_urbs(pegasus); unlink_all_urbs(pegasus);
up(&pegasus->sem); up(&pegasus->sem);
...@@ -926,11 +963,15 @@ static int pegasus_ethtool_ioctl(struct net_device *dev, void *useraddr) ...@@ -926,11 +963,15 @@ static int pegasus_ethtool_ioctl(struct net_device *dev, void *useraddr)
switch (ethcmd) { switch (ethcmd) {
/* get driver-specific version/etc. info */ /* get driver-specific version/etc. info */
case ETHTOOL_GDRVINFO:{ case ETHTOOL_GDRVINFO:{
struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; struct ethtool_drvinfo info;
strlcpy(info.driver, driver_name, memset (&info, 0, sizeof (info));
sizeof (info.driver)); info.cmd = ETHTOOL_GDRVINFO;
strlcpy(info.version, DRIVER_VERSION, strncpy(info.driver, driver_name,
sizeof (info.version)); 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))) if (copy_to_user(useraddr, &info, sizeof (info)))
return -EFAULT; return -EFAULT;
return 0; return 0;
...@@ -999,12 +1040,15 @@ static int pegasus_ethtool_ioctl(struct net_device *net, void *uaddr) ...@@ -999,12 +1040,15 @@ static int pegasus_ethtool_ioctl(struct net_device *net, void *uaddr)
return -EFAULT; return -EFAULT;
switch (cmd) { switch (cmd) {
case ETHTOOL_GDRVINFO:{ case ETHTOOL_GDRVINFO:{
struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; struct ethtool_drvinfo info;
strncpy(info.driver, driver_name, sizeof info.driver); memset (&info, 0, sizeof (info));
info.cmd = ETHTOOL_GDRVINFO;
strncpy(info.driver, driver_name,
sizeof (info.driver) - 1);
strncpy(info.version, DRIVER_VERSION, strncpy(info.version, DRIVER_VERSION,
ETHTOOL_BUSINFO_LEN); sizeof (info.version) - 1);
usb_make_path(pegasus->usb, info.bus_info, usb_make_path(pegasus->usb, info.bus_info,
sizeof info.bus_info); sizeof (info.bus_info));
if (copy_to_user(uaddr, &info, sizeof (info))) if (copy_to_user(uaddr, &info, sizeof (info)))
return -EFAULT; return -EFAULT;
return 0; return 0;
...@@ -1012,15 +1056,20 @@ static int pegasus_ethtool_ioctl(struct net_device *net, void *uaddr) ...@@ -1012,15 +1056,20 @@ static int pegasus_ethtool_ioctl(struct net_device *net, void *uaddr)
case ETHTOOL_GSET:{ case ETHTOOL_GSET:{
struct ethtool_cmd ecmd; struct ethtool_cmd ecmd;
short lpa, bmcr; short lpa, bmcr;
u8 port;
memset(&ecmd, 0, sizeof ecmd); memset(&ecmd, 0, sizeof (ecmd));
ecmd.supported = (SUPPORTED_10baseT_Half | ecmd.supported = (SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full | SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Half |
SUPPORTED_100baseT_Full | SUPPORTED_100baseT_Full |
SUPPORTED_Autoneg | SUPPORTED_Autoneg |
SUPPORTED_TP | SUPPORTED_MII); 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.transceiver = XCVR_INTERNAL;
ecmd.phy_address = pegasus->phy; ecmd.phy_address = pegasus->phy;
read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr); read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr);
...@@ -1142,7 +1191,10 @@ static inline void setup_pegasus_II(pegasus_t * pegasus) ...@@ -1142,7 +1191,10 @@ static inline void setup_pegasus_II(pegasus_t * pegasus)
set_register(pegasus, Reg1d, 0); set_register(pegasus, Reg1d, 0);
set_register(pegasus, Reg7b, 1); set_register(pegasus, Reg7b, 1);
mdelay(100); 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); set_register(pegasus, 0x83, data);
get_registers(pegasus, 0x83, 1, &data); get_registers(pegasus, 0x83, 1, &data);
...@@ -1262,7 +1314,6 @@ static void pegasus_disconnect(struct usb_interface *intf) ...@@ -1262,7 +1314,6 @@ static void pegasus_disconnect(struct usb_interface *intf)
pegasus->flags |= PEGASUS_UNPLUG; pegasus->flags |= PEGASUS_UNPLUG;
unregister_netdev(pegasus->net); unregister_netdev(pegasus->net);
usb_put_dev(interface_to_usbdev(intf)); usb_put_dev(interface_to_usbdev(intf));
unlink_all_urbs(pegasus);
free_all_urbs(pegasus); free_all_urbs(pegasus);
free_skb_pool(pegasus); free_skb_pool(pegasus);
if (pegasus->rx_skb) if (pegasus->rx_skb)
...@@ -1273,7 +1324,6 @@ static void pegasus_disconnect(struct usb_interface *intf) ...@@ -1273,7 +1324,6 @@ static void pegasus_disconnect(struct usb_interface *intf)
} }
static struct usb_driver pegasus_driver = { static struct usb_driver pegasus_driver = {
.owner = THIS_MODULE,
.name = driver_name, .name = driver_name,
.probe = pegasus_probe, .probe = pegasus_probe,
.disconnect = pegasus_disconnect, .disconnect = pegasus_disconnect,
......
/* /*
* 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 * 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 * it under the terms of the GNU General Public License version 2 as published
...@@ -130,6 +130,7 @@ struct usb_eth_dev { ...@@ -130,6 +130,7 @@ struct usb_eth_dev {
#define VENDOR_ELCON 0x0db7 #define VENDOR_ELCON 0x0db7
#define VENDOR_ELSA 0x05cc #define VENDOR_ELSA 0x05cc
#define VENDOR_HAWKING 0x0e66 #define VENDOR_HAWKING 0x0e66
#define VENDOR_HP 0x03f0
#define VENDOR_IODATA 0x04bb #define VENDOR_IODATA 0x04bb
#define VENDOR_KINGSTON 0x0951 #define VENDOR_KINGSTON 0x0951
#define VENDOR_LANEED 0x056e #define VENDOR_LANEED 0x056e
...@@ -224,6 +225,8 @@ PEGASUS_DEV( "Elsa Micolink USB2Ethernet", VENDOR_ELSA, 0x3000, ...@@ -224,6 +225,8 @@ PEGASUS_DEV( "Elsa Micolink USB2Ethernet", VENDOR_ELSA, 0x3000,
DEFAULT_GPIO_RESET ) DEFAULT_GPIO_RESET )
PEGASUS_DEV( "Hawking UF100 10/100 Ethernet", VENDOR_HAWKING, 0x400c, PEGASUS_DEV( "Hawking UF100 10/100 Ethernet", VENDOR_HAWKING, 0x400c,
DEFAULT_GPIO_RESET | PEGASUS_II ) 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, PEGASUS_DEV( "IO DATA USB ET/TX", VENDOR_IODATA, 0x0904,
DEFAULT_GPIO_RESET ) DEFAULT_GPIO_RESET )
PEGASUS_DEV( "IO DATA USB ET/TX-S", VENDOR_IODATA, 0x0913, PEGASUS_DEV( "IO DATA USB ET/TX-S", VENDOR_IODATA, 0x0913,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment