patch-2.4.0-test11 linux/drivers/usb/pegasus.c
Next file: linux/drivers/usb/pegasus.h
Previous file: linux/drivers/usb/ov511.c
Back to the patch index
Back to the overall index
- Lines: 557
- Date:
Tue Nov 14 12:57:50 2000
- Orig file:
v2.4.0-test10/linux/drivers/usb/pegasus.c
- Orig date:
Tue Oct 31 12:42:27 2000
diff -u --recursive --new-file v2.4.0-test10/linux/drivers/usb/pegasus.c linux/drivers/usb/pegasus.c
@@ -40,7 +40,6 @@
*/
-#include <linux/module.h>
#include <linux/sched.h>
#include <linux/malloc.h>
#include <linux/init.h>
@@ -48,162 +47,44 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/usb.h>
-
-
-static const char *version = __FILE__ ": v0.4.13 2000/10/13 (C) 1999-2000 Petko Manolov (petkan@dce.bg)";
+#include <linux/module.h>
+#include "pegasus.h"
#define PEGASUS_USE_INTR
+#define PEGASUS_WRITE_EEPROM
+static const char *version = __FILE__ ": v0.4.17 2000/11/13 (C) 1999-2000 Petko Manolov (petkan@dce.bg)";
-#define PEGASUS_II 0x80000000
-#define HAS_HOME_PNA 0x40000000
-#define PEGASUS_MTU 1500
-#define PEGASUS_MAX_MTU 1536
+static int loopback = 0;
+static int mii_mode = 0;
+static int multicast_filter_limit = 32;
-#define EPROM_WRITE 0x01
-#define EPROM_READ 0x02
-#define EPROM_DONE 0x04
-#define EPROM_WR_ENABLE 0x10
-#define EPROM_LOAD 0x20
-
-#define MII_BMCR 0x00
-#define MII_BMSR 0x01
-#define BMSR_MEDIA 0x7808
-#define MII_ANLPA 0x05
-#define ANLPA_100TX_FD 0x0100
-#define ANLPA_100TX_HD 0x0080
-#define ANLPA_10T_FD 0x0040
-#define ANLPA_10T_HD 0x0020
-#define PHY_DONE 0x80
-#define PHY_READ 0x40
-#define PHY_WRITE 0x20
-#define DEFAULT_GPIO_RESET 0x24
-#define LINKSYS_GPIO_RESET 0x24
-#define DEFAULT_GPIO_SET 0x26
-
-#define PEGASUS_PRESENT 0x00000001
-#define PEGASUS_RUNNING 0x00000002
-#define PEGASUS_TX_BUSY 0x00000004
-#define PEGASUS_RX_BUSY 0x00000008
-#define CTRL_URB_RUNNING 0x00000010
-#define CTRL_URB_SLEEP 0x00000020
-#define PEGASUS_UNPLUG 0x00000040
-#define ETH_REGS_CHANGE 0x40000000
-#define ETH_REGS_CHANGED 0x80000000
-
-#define RX_MULTICAST 2
-#define RX_PROMISCUOUS 4
-
-#define REG_TIMEOUT (HZ)
-#define PEGASUS_TX_TIMEOUT (HZ*10)
-
-#define TX_UNDERRUN 0x80
-#define EXCESSIVE_COL 0x40
-#define LATE_COL 0x20
-#define NO_CARRIER 0x10
-#define LOSS_CARRIER 0x08
-#define JABBER_TIMEOUT 0x04
-
-#define PEGASUS_REQT_READ 0xc0
-#define PEGASUS_REQT_WRITE 0x40
-#define PEGASUS_REQ_GET_REGS 0xf0
-#define PEGASUS_REQ_SET_REGS 0xf1
-#define PEGASUS_REQ_SET_REG PEGASUS_REQ_SET_REGS
-#define NUM_CTRL_URBS 0x10
-#define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES)))
-
-enum pegasus_registers {
- EthCtrl0 = 0,
- EthCtrl1 = 1,
- EthCtrl2 = 2,
- EthID = 0x10,
- Reg1d = 0x1d,
- EpromOffset = 0x20,
- EpromData = 0x21, /* 0x21 low, 0x22 high byte */
- EpromCtrl = 0x23,
- PhyAddr = 0x25,
- PhyData = 0x26, /* 0x26 low, 0x27 high byte */
- PhyCtrl = 0x28,
- UsbStst = 0x2a,
- EthTxStat0 = 0x2b,
- EthTxStat1 = 0x2c,
- EthRxStat = 0x2d,
- Reg7b = 0x7b,
- Gpio0 = 0x7e,
- Gpio1 = 0x7f,
- Reg81 = 0x81,
+static struct usb_eth_dev usb_dev_id[] = {
+#define PEGASUS_DEV(pn, vid, pid, flags) \
+ {name:pn, vendor:vid, device:pid, private:flags},
+#include "pegasus.h"
+#undef PEGASUS_DEV
+ {NULL, 0, 0, 0}
};
-
-typedef struct pegasus {
- struct usb_device *usb;
- struct net_device *net;
- struct net_device_stats stats;
- unsigned flags;
- unsigned features;
- int intr_interval;
- struct urb ctrl_urb, rx_urb, tx_urb, intr_urb;
- devrequest dr;
- wait_queue_head_t ctrl_wait;
- struct semaphore ctrl_sem;
- unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]);
- unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]);
- unsigned char ALIGN(intr_buff[8]);
- __u8 eth_regs[4];
- __u8 phy;
- __u8 gpio_res;
-} pegasus_t;
-
-struct usb_eth_dev {
- char *name;
- __u16 vendor;
- __u16 device;
- __u32 private; /* LSB is gpio reset value */
+static struct usb_device_id pegasus_ids[] = {
+#define PEGASUS_DEV(pn, vid, pid, flags) {idVendor:vid, idProduct:pid},
+#include "pegasus.h"
+#undef PEGASUS_DEV
+ { }
};
-static int loopback = 0;
-static int mii_mode = 0;
-static int multicast_filter_limit = 32;
-
-
MODULE_AUTHOR("Petko Manolov <petkan@dce.bg>");
MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver");
MODULE_PARM(loopback, "i");
-MODULE_PARM(mode, "i");
+MODULE_PARM(mii_mode, "i");
MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)");
-MODULE_PARM_DESC(mode, "Enable HomePNA mode (bit 0) - default = MII mode = 0");
+MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0");
-
-static struct usb_eth_dev usb_dev_id[] = {
- {"Billionton USB-100", 0x08dd, 0x0986, DEFAULT_GPIO_RESET},
- {"Corega FEter USB-TX", 0x7aa, 0x0004, DEFAULT_GPIO_RESET},
- {"MELCO/BUFFALO LUA-TX", 0x0411, 0x0001, DEFAULT_GPIO_RESET},
- {"D-Link DSB-650TX", 0x2001, 0x4001, LINKSYS_GPIO_RESET},
- {"D-Link DSB-650TX", 0x2001, 0x4002, LINKSYS_GPIO_RESET},
- {"D-Link DSB-650TX(PNA)", 0x2001, 0x4003,
- HAS_HOME_PNA | DEFAULT_GPIO_RESET},
- {"D-Link DSB-650", 0x2001, 0xabc1, DEFAULT_GPIO_RESET},
- {"D-Link DU-E10", 0x07b8, 0xabc1, DEFAULT_GPIO_RESET},
- {"D-Link DU-E100", 0x07b8, 0x4002, DEFAULT_GPIO_RESET},
- {"Linksys USB10TX", 0x066b, 0x2202, LINKSYS_GPIO_RESET},
- {"Linksys USB100TX", 0x066b, 0x2203, LINKSYS_GPIO_RESET},
- {"Linksys USB100TX", 0x066b, 0x2204, HAS_HOME_PNA | LINKSYS_GPIO_RESET},
- {"Linksys USB Ethernet Adapter", 0x066b, 0x2206, LINKSYS_GPIO_RESET},
- {"SMC 202 USB Ethernet", 0x0707, 0x0200, DEFAULT_GPIO_RESET},
- {"ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986,
- HAS_HOME_PNA | DEFAULT_GPIO_RESET},
- {"Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046,
- DEFAULT_GPIO_RESET},
- {"IO DATA USB ET/TX", 0x04bb, 0x0904, DEFAULT_GPIO_RESET},
- {"LANEED USB Ethernet LD-USB/TX", 0x056e, 0x4002, DEFAULT_GPIO_RESET},
- {"SOHOware NUB100 Ethernet", 0x15e8, 0x9100, DEFAULT_GPIO_RESET},
- {"ADMtek ADM8511 \"Pegasus II\" USB Ethernet", 0x07a6, 0x8511,
- PEGASUS_II | DEFAULT_GPIO_RESET},
- {NULL, 0, 0, 0}
-};
+MODULE_DEVICE_TABLE (usb, pegasus_ids);
static int update_eth_regs_async( pegasus_t * );
@@ -243,7 +124,7 @@
{
int ret;
- if ( pegasus->flags & ETH_REGS_CHANGED ) {
+ while ( pegasus->flags & ETH_REGS_CHANGED ) {
pegasus->flags |= CTRL_URB_SLEEP;
interruptible_sleep_on( &pegasus->ctrl_wait );
}
@@ -274,7 +155,7 @@
{
int ret;
- if ( pegasus->flags & ETH_REGS_CHANGED ) {
+ while ( pegasus->flags & ETH_REGS_CHANGED ) {
pegasus->flags |= CTRL_URB_SLEEP ;
interruptible_sleep_on( &pegasus->ctrl_wait );
}
@@ -305,7 +186,7 @@
{
int ret;
- if ( pegasus->flags & ETH_REGS_CHANGED ) {
+ while ( pegasus->flags & ETH_REGS_CHANGED ) {
pegasus->flags |= CTRL_URB_SLEEP;
interruptible_sleep_on( &pegasus->ctrl_wait );
}
@@ -354,7 +235,7 @@
}
-static int read_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd )
+static int read_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd )
{
int i;
__u8 data[4] = { phy, 0, 0, indx };
@@ -377,7 +258,7 @@
}
-static int write_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd )
+static int write_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd )
{
int i;
__u8 data[4] = { phy, 0, 0, indx };
@@ -505,6 +386,15 @@
}
if ( i == REG_TIMEOUT )
return 1;
+
+ if ( usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
+ usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK1 ) {
+ __u16 auxmode;
+
+ read_mii_word( pegasus, 0, 0x1b, &auxmode );
+ write_mii_word( pegasus, 0, 0x1b, auxmode | 4 );
+ }
+
return 0;
}
@@ -516,13 +406,13 @@
pegasus_t *pegasus = dev->priv;
- if ( read_phy_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) )
- return 2;
+ if ( read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) )
+ return 1;
if ( !(bmsr & 0x20) && !loopback )
warn( "%s: link NOT established (0x%x) - check the cable.",
dev->name, bmsr );
- if ( read_phy_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) )
- return 4;
+ if ( read_mii_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) )
+ return 2;
if ( !(linkpart & 1) )
warn( "link partner stat %x", linkpart );
@@ -536,7 +426,7 @@
data[1] = 0;
data[2] = (loopback & 1) ? 0x09 : 0x01;
- *(unsigned *)pegasus->eth_regs = *(unsigned *)data;
+ memcpy( pegasus->eth_regs, data, sizeof(data) );
set_registers( pegasus, EthCtrl0, 3, data );
@@ -562,23 +452,29 @@
if ( pegasus->flags & PEGASUS_RX_BUSY ) {
pegasus->stats.rx_errors++;
+ dbg("pegasus Rx busy");
return;
}
pegasus->flags |= PEGASUS_RX_BUSY;
- rx_status = *(int *)(pegasus->rx_buff + count - 4);
-
- if (urb->status) {
- dbg("%s: RX status %d", net->name, urb->status);
- goto goon;
+ switch ( urb->status ) {
+ case USB_ST_NOERROR:
+ break;
+ case USB_ST_NORESPONSE:
+ dbg( "reset MAC" );
+ pegasus->flags &= ~PEGASUS_RX_BUSY;
+ break;
+ default:
+ dbg( "%s: RX status %d", net->name, urb->status );
+ goto goon;
}
if ( !count )
goto goon;
+ rx_status = *(int *)(pegasus->rx_buff + count - 4);
if ( rx_status & 0x000e0000 ) {
-
- dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000);
+ dbg("%s: RX packet error %x", net->name, rx_status & 0xe0000);
pegasus->stats.rx_errors++;
if ( rx_status & 0x060000 )
pegasus->stats.rx_length_errors++;
@@ -586,7 +482,6 @@
pegasus->stats.rx_crc_errors++;
if ( rx_status & 0x100000 )
pegasus->stats.rx_frame_errors++;
-
goto goon;
}
@@ -629,6 +524,7 @@
if ( urb->status )
info("%s: TX status %d", pegasus->net->name, urb->status);
+ pegasus->net->trans_start = jiffies;
netif_wake_queue( pegasus->net );
}
@@ -641,6 +537,16 @@
if ( !pegasus )
return;
+
+ switch ( urb->status ) {
+ case USB_ST_NOERROR:
+ break;
+ case USB_ST_URB_KILLED:
+ return;
+ default:
+ info("intr status %d", urb->status);
+ }
+
d = urb->transfer_buffer;
net = pegasus->net;
if ( d[0] & 0xfc ) {
@@ -654,31 +560,23 @@
if ( d[0] & (NO_CARRIER | LOSS_CARRIER) )
pegasus->stats.tx_carrier_errors++;
}
- switch ( urb->status ) {
- case USB_ST_NOERROR:
- break;
- case USB_ST_URB_KILLED:
- break;
- default:
- info("intr status %d", urb->status);
- }
}
#endif
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,48)
static void pegasus_tx_timeout( struct net_device *net )
{
pegasus_t *pegasus = net->priv;
if ( !pegasus )
return;
-
- usb_unlink_urb( &pegasus->tx_urb );
+
warn("%s: Tx timed out.", net->name);
+ pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK;
+ usb_unlink_urb( &pegasus->tx_urb );
pegasus->stats.tx_errors++;
- net->trans_start = jiffies;
-
- netif_wake_queue( net );
}
+#endif
static int pegasus_start_xmit( struct sk_buff *skb, struct net_device *net )
@@ -696,7 +594,6 @@
pegasus->tx_buff, PEGASUS_MAX_MTU,
write_bulk_callback, pegasus );
pegasus->tx_urb.transfer_buffer_length = count;
- pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK;
if ((res = usb_submit_urb(&pegasus->tx_urb))) {
warn("failed tx_urb %d", res);
pegasus->stats.tx_errors++;
@@ -732,6 +629,14 @@
__u8 data[2];
read_eprom_word( pegasus, 4, (__u16 *)data );
+ if ( data[1] < 0x80 ) {
+ info( "intr interval will be changed from %ums to %ums",
+ data[1], 0x80 );
+ data[1] = 0x80;
+#ifdef PEGASUS_WRITE_EEPROM
+ write_eprom_word( pegasus, 4, *(__u16 *)data );
+#endif
+ }
pegasus->intr_interval = data[1];
}
@@ -754,7 +659,6 @@
if ( (res = usb_submit_urb(&pegasus->rx_urb)) )
warn( __FUNCTION__ " failed rx_urb %d", res );
#ifdef PEGASUS_USE_INTR
- get_interrupt_interval( pegasus );
FILL_INT_URB( &pegasus->intr_urb, pegasus->usb,
usb_rcvintpipe(pegasus->usb, 3),
pegasus->intr_buff, sizeof(pegasus->intr_buff),
@@ -781,8 +685,9 @@
usb_unlink_urb( &pegasus->rx_urb );
usb_unlink_urb( &pegasus->tx_urb );
usb_unlink_urb( &pegasus->ctrl_urb );
+#ifdef PEGASUS_USE_INTR
usb_unlink_urb( &pegasus->intr_urb );
-
+#endif
MOD_DEC_USE_COUNT;
return 0;
@@ -798,12 +703,12 @@
case SIOCDEVPRIVATE:
data[0] = pegasus->phy;
case SIOCDEVPRIVATE+1:
- read_phy_word(pegasus, data[0], data[1]&0x1f, &data[3]);
+ read_mii_word(pegasus, data[0], data[1]&0x1f, &data[3]);
return 0;
case SIOCDEVPRIVATE+2:
if ( !capable(CAP_NET_ADMIN) )
return -EPERM;
- write_phy_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]);
+ write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]);
return 0;
default:
return -EOPNOTSUPP;
@@ -838,34 +743,20 @@
}
-static int check_device_ids( __u16 vendor, __u16 product )
-{
- int i=0;
-
- while ( usb_dev_id[i].name ) {
- if ( (usb_dev_id[i].vendor == vendor) &&
- (usb_dev_id[i].device == product) )
- return i;
- i++;
- }
- return -1;
-}
-
-
static __u8 mii_phy_probe( pegasus_t *pegasus )
{
int i;
__u16 tmp;
for ( i=0; i < 32; i++ ) {
- read_phy_word( pegasus, i, MII_BMSR, &tmp );
+ read_mii_word( pegasus, i, MII_BMSR, &tmp );
if ( tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0 )
continue;
else
return i;
}
- return 0;
+ return 0xff;
}
@@ -880,15 +771,12 @@
}
-static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum )
+static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum,
+ const struct usb_device_id *id)
{
- struct net_device *net;
- pegasus_t *pegasus;
- int dev_indx;
-
- if ( (dev_indx = check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct)) == -1 ) {
- return NULL;
- }
+ struct net_device *net;
+ pegasus_t *pegasus;
+ int dev_index = id - pegasus_ids;
if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
err("usb_set_configuration() failed");
@@ -902,7 +790,7 @@
usb_inc_dev_use( dev );
memset(pegasus, 0, sizeof(struct pegasus));
- init_MUTEX( &pegasus-> ctrl_sem );
+ pegasus->dev_index = dev_index;
init_waitqueue_head( &pegasus->ctrl_wait );
net = init_etherdev( NULL, 0 );
@@ -926,7 +814,10 @@
net->get_stats = pegasus_netdev_stats;
net->mtu = PEGASUS_MTU;
- pegasus->features = usb_dev_id[dev_indx].private;
+ pegasus->features = usb_dev_id[dev_index].private;
+#ifdef PEGASUS_USE_INTR
+ get_interrupt_interval( pegasus );
+#endif
if ( reset_mac(pegasus) ) {
err("can't reset MAC");
unregister_netdev( pegasus->net );
@@ -935,21 +826,21 @@
return NULL;
}
+ info( "%s: %s", net->name, usb_dev_id[dev_index].name );
+
set_ethernet_addr( pegasus );
-
+
if ( pegasus->features & PEGASUS_II ) {
info( "setup Pegasus II specific registers" );
setup_pegasus_II( pegasus );
}
pegasus->phy = mii_phy_probe( pegasus );
- if ( !pegasus->phy ) {
+ if ( pegasus->phy == 0xff ) {
warn( "can't locate MII phy, using default" );
pegasus->phy = 1;
}
- info( "%s: %s", net->name, usb_dev_id[dev_indx].name );
-
return pegasus;
}
@@ -975,6 +866,7 @@
name: "pegasus",
probe: pegasus_probe,
disconnect: pegasus_disconnect,
+ id_table: pegasus_ids,
};
int __init pegasus_init(void)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)