patch-2.4.0-test9 linux/drivers/usb/pegasus.c
Next file: linux/drivers/usb/scanner.c
Previous file: linux/drivers/usb/ov511.h
Back to the patch index
Back to the overall index
- Lines: 1193
- Date:
Mon Oct 2 14:20:39 2000
- Orig file:
v2.4.0-test8/linux/drivers/usb/pegasus.c
- Orig date:
Tue Aug 22 15:23:13 2000
diff -u --recursive --new-file v2.4.0-test8/linux/drivers/usb/pegasus.c linux/drivers/usb/pegasus.c
@@ -13,6 +13,12 @@
** from top-halves.
** v0.4.0 Control messages remained unurbified are now URBs.
** Now we can touch the HW at any time.
+** v0.4.9 Control urbs again use process context to wait. Argh...
+** Some long standing bugs (start_net) fixed. Also nasty
+** trick with resubmiting control urb from interrupt
+** context used. Please let me know how it behaves.
+** Pegasus II support added since this version.
+** TODO: suppressing HCD warnings spewage on disconnect.
*/
/*
@@ -42,20 +48,65 @@
#include <linux/usb.h>
-static const char *version = __FILE__ ": v0.4.3 2000/08/22 (C) 1999-2000 Petko Manolov (petkan@dce.bg)\n";
+static const char *version = __FILE__ ": v0.4.9 2000/09/14 (C) 1999-2000 Petko Manolov (petkan@dce.bg)";
-#define PEGASUS_USE_WAITQ
+#define PEGASUS_USE_INTR
+#define PEGASUS_II 0x80000000
+#define HAS_HOME_PNA 0x40000000
+
#define PEGASUS_MTU 1500
#define PEGASUS_MAX_MTU 1536
+
#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 1
+#define PEGASUS_RUNNING 2
+#define CTRL_URB_RUNNING 4
+#define CTRL_URB_SLEEP 8
+#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 PEGASUS_CTRL_TIMEOUT (HZ*5)
-#define PEGASUS_CTRL_WAIT (1<<31)
-#define PEGASUS_RUNNING 1
+
+#ifdef PEGASUS_USE_INTR
+ #define INTR_IVAL 0x80
+#else
+ #define INTR_IVAL 0
+#endif
+
+#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
@@ -64,12 +115,12 @@
#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,
@@ -80,348 +131,445 @@
EthTxStat0 = 0x2b,
EthTxStat1 = 0x2c,
EthRxStat = 0x2d,
+ Reg7b = 0x7b,
Gpio0 = 0x7e,
Gpio1 = 0x7f,
+ Reg81 = 0x81,
};
-struct pegasus;
-struct ctrl_urb_pool {
- struct pegasus *pegasus;
- struct urb urb;
- devrequest dr;
- __u8 busy;
-};
-
-
-struct pegasus {
+typedef struct pegasus {
struct usb_device *usb;
struct net_device *net;
struct net_device_stats stats;
- int flags;
- struct urb rx_urb, tx_urb, intr_urb;
- struct ctrl_urb_pool ALIGN(ctrl[NUM_CTRL_URBS]);
+ unsigned flags;
+ unsigned features;
+ 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;
- void *private;
+ __u32 private; /* LSB is gpio reset value */
};
static int loopback = 0;
+static int 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_DESC(loopback, "Enable MAC loopback mode (bit 0)");
+MODULE_PARM_DESC(mode, "Enable HomePNA mode (bit 0) - default = MII mode = 0");
static struct usb_eth_dev usb_dev_id[] = {
- {"Billionton USB-100", 0x08dd, 0x0986, NULL},
- {"Corega FEter USB-TX", 0x7aa, 0x0004, NULL},
- {"MELCO/BUFFALO LUA-TX", 0x0411, 0x0001, NULL},
- {"D-Link DSB-650TX", 0x2001, 0x4001, NULL},
- {"D-Link DSB-650TX", 0x2001, 0x4002, NULL},
- {"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, NULL},
- {"D-Link DSB-650", 0x2001, 0xabc1, NULL},
- {"D-Link DU-E10", 0x07b8, 0xabc1, NULL},
- {"D-Link DU-E100", 0x07b8, 0x4002, NULL},
- {"Linksys USB10TX", 0x066b, 0x2202, NULL},
- {"Linksys USB100TX", 0x066b, 0x2203, NULL},
- {"Linksys USB100TX", 0x066b, 0x2204, NULL},
- {"Linksys USB Ethernet Adapter", 0x066b, 0x2206, NULL},
- {"SMC 202 USB Ethernet", 0x0707, 0x0200, NULL},
- {"ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986, NULL},
- {"Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, NULL},
- {"IO DATA USB ET/TX", 0x04bb, 0x0904, NULL},
- {"LANEED USB Ethernet LD-USB/TX", 0x056e, 0x4002, NULL},
- {"SOHOware NUB100 Ethernet", 0x15e8, 0x9100, NULL},
- {NULL, 0, 0, NULL}
+ {"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, 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, 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}
};
-static void pegasus_unlink_ctrl_urbs( struct pegasus *pegasus )
+static int update_eth_regs_async( pegasus_t * );
+/* Aargh!!! I _really_ hate such tweaks */
+static void ctrl_callback( urb_t *urb )
{
- int i;
+ pegasus_t *pegasus = urb->context;
+
+ if ( !pegasus )
+ return;
- for ( i=0; i < NUM_CTRL_URBS; i++ ) {
- if ( pegasus->ctrl[i].urb.status == -EINPROGRESS )
- usb_unlink_urb( &pegasus->ctrl[i].urb );
+ switch ( urb->status ) {
+ case USB_ST_NOERROR:
+ if ( pegasus->flags & ETH_REGS_CHANGE ) {
+ pegasus->flags &= ~ETH_REGS_CHANGE;
+ pegasus->flags |= ETH_REGS_CHANGED;
+ update_eth_regs_async( pegasus );
+ return;
+ }
+ break;
+ case USB_ST_URB_PENDING:
+ return;
+ case USB_ST_URB_KILLED:
+ break;
+ default:
+ warn( __FUNCTION__ " status %d", urb->status);
+ }
+ pegasus->flags &= ~ETH_REGS_CHANGED;
+ if ( pegasus->flags & CTRL_URB_SLEEP ) {
+ pegasus->flags &= ~CTRL_URB_SLEEP;
+ wake_up_interruptible( &pegasus->ctrl_wait );
}
}
-static int pegasus_find_ctrl_urb( struct pegasus *pegasus )
+static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
{
- int i=0;
-
- while( i < NUM_CTRL_URBS && (pegasus->ctrl[i].busy == 1 ||
- (pegasus->ctrl[i].urb.status == -EINPROGRESS)) )
- i++;
+ int ret;
- return i;
-}
-
-
-static void pegasus_ctrl_end( urb_t *urb )
-{
- struct ctrl_urb_pool *ctrl = urb->context;
- struct pegasus *pegasus = ctrl->pegasus;
+ if ( pegasus->flags & ETH_REGS_CHANGED ) {
+ pegasus->flags |= CTRL_URB_SLEEP;
+ interruptible_sleep_on( &pegasus->ctrl_wait );
+ }
+ pegasus->dr.requesttype = PEGASUS_REQT_READ;
+ pegasus->dr.request = PEGASUS_REQ_GET_REGS;
+ pegasus->dr.value = 0;
+ pegasus->dr.index = cpu_to_le16p(&indx);
+ pegasus->dr.length =
+ pegasus->ctrl_urb.transfer_buffer_length = cpu_to_le16p(&size);
+ FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,
+ usb_rcvctrlpipe(pegasus->usb,0),
+ (char *)&pegasus->dr,
+ data, size, ctrl_callback, pegasus );
- if ( !pegasus )
- return;
+ if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) {
+ err( __FUNCTION__ " BAD CTRLs %d", ret);
+ return ret;
+ }
+ pegasus->flags |= CTRL_URB_SLEEP;
+ interruptible_sleep_on( &pegasus->ctrl_wait );
- if ( urb->status )
- warn("ctrl_urb end status %d", urb->status);
- ctrl->busy = 0;
-#ifdef PEGASUS_USE_WAITQ
- wake_up_interruptible( &pegasus->ctrl_wait );
-#endif
+ return ret;
}
-static int pegasus_get_registers( struct pegasus *pegasus, __u16 indx, __u16 size, void *data )
+static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
{
- int ret, i;
- struct ctrl_urb_pool *ctrl;
-
- if ( (i = pegasus_find_ctrl_urb( pegasus )) == NUM_CTRL_URBS ) {
- return -1;
- }
+ int ret;
- ctrl = &pegasus->ctrl[i];
- ctrl->busy = 1;
- ctrl->pegasus = pegasus;
-
- ctrl->dr.requesttype = PEGASUS_REQT_READ;
- ctrl->dr.request = PEGASUS_REQ_GET_REGS;
- ctrl->dr.value = 0;
- ctrl->dr.index = cpu_to_le16p(&indx);
- ctrl->dr.length =
- ctrl->urb.transfer_buffer_length = cpu_to_le16p(&size);
+ if ( pegasus->flags & ETH_REGS_CHANGED ) {
+ pegasus->flags |= CTRL_URB_SLEEP ;
+ interruptible_sleep_on( &pegasus->ctrl_wait );
+ }
+ pegasus->dr.requesttype = PEGASUS_REQT_WRITE;
+ pegasus->dr.request = PEGASUS_REQ_SET_REGS;
+ pegasus->dr.value = 0;
+ pegasus->dr.index = cpu_to_le16p( &indx );
+ pegasus->dr.length =
+ pegasus->ctrl_urb.transfer_buffer_length = cpu_to_le16p( &size );
- FILL_CONTROL_URB( &ctrl->urb, pegasus->usb,
- usb_rcvctrlpipe(pegasus->usb,0),
- (char *)&ctrl->dr,
- data, size, pegasus_ctrl_end, ctrl );
+ FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,
+ usb_sndctrlpipe(pegasus->usb,0),
+ (char *)&pegasus->dr,
+ data, size, ctrl_callback, pegasus );
- if ( (ret = usb_submit_urb( &ctrl->urb )) )
- err( __FUNCTION__ " BAD CTRLs %d", ret);
-#ifdef PEGASUS_USE_WAITQ
+ if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) {
+ err( __FUNCTION__ " BAD CTRL %d", ret);
+ return ret;
+ }
+ pegasus->flags |= CTRL_URB_SLEEP;
interruptible_sleep_on( &pegasus->ctrl_wait );
-#endif
+
return ret;
}
-static int pegasus_set_registers( struct pegasus *pegasus, __u16 indx, __u16 size, void *data )
+static int set_register( pegasus_t *pegasus, __u16 indx, __u8 data )
{
- int ret, i;
- struct ctrl_urb_pool *ctrl;
-
- if ( (i = pegasus_find_ctrl_urb( pegasus )) == NUM_CTRL_URBS ) {
- return -1;
- }
+ int ret;
- ctrl = &pegasus->ctrl[i];
- ctrl->busy = 1;
- ctrl->pegasus = pegasus;
-
- ctrl->dr.requesttype = PEGASUS_REQT_WRITE;
- ctrl->dr.request = PEGASUS_REQ_SET_REGS;
- ctrl->dr.value = 0;
- ctrl->dr.index = cpu_to_le16p( &indx );
- ctrl->dr.length =
- ctrl->urb.transfer_buffer_length = cpu_to_le16p( &size );
+ if ( pegasus->flags & ETH_REGS_CHANGED ) {
+ pegasus->flags |= CTRL_URB_SLEEP;
+ interruptible_sleep_on( &pegasus->ctrl_wait );
+ }
+ pegasus->dr.requesttype = PEGASUS_REQT_WRITE;
+ pegasus->dr.request = PEGASUS_REQ_SET_REG;
+ pegasus->dr.value = data;
+ pegasus->dr.index = cpu_to_le16p( &indx );
+ pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = 1;
- FILL_CONTROL_URB( &ctrl->urb, pegasus->usb,
+ FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,
usb_sndctrlpipe(pegasus->usb,0),
- (char *)&ctrl->dr,
- data, size, pegasus_ctrl_end, ctrl );
+ (char *)&pegasus->dr,
+ &data, 1, ctrl_callback, pegasus );
- if ( (ret = usb_submit_urb( &ctrl->urb )) )
+ if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) {
err( __FUNCTION__ " BAD CTRL %d", ret);
-#ifdef PEGASUS_USE_WAITQ
+ return ret;
+ }
+ pegasus->flags |= CTRL_URB_SLEEP;
interruptible_sleep_on( &pegasus->ctrl_wait );
-#endif
+
return ret;
}
-static int pegasus_set_register( struct pegasus *pegasus, __u16 indx,__u8 data )
+static int update_eth_regs_async( pegasus_t *pegasus )
{
- int ret, i;
- struct ctrl_urb_pool *ctrl;
-
- if ( (i = pegasus_find_ctrl_urb( pegasus )) == NUM_CTRL_URBS ) {
- return -1;
- }
-
- ctrl = &pegasus->ctrl[i];
- ctrl->busy = 1;
- ctrl->pegasus = pegasus;
+ int ret;
- ctrl->dr.requesttype = PEGASUS_REQT_WRITE;
- ctrl->dr.request = PEGASUS_REQ_SET_REG;
- ctrl->dr.value = cpu_to_le16p( &data );
- ctrl->dr.index = cpu_to_le16p( &indx );
- ctrl->dr.length = ctrl->urb.transfer_buffer_length = 1;
+ pegasus->dr.requesttype = PEGASUS_REQT_WRITE;
+ pegasus->dr.request = PEGASUS_REQ_SET_REGS;
+ pegasus->dr.value = 0;
+ pegasus->dr.index = EthCtrl0;
+ pegasus->dr.length =
+ pegasus->ctrl_urb.transfer_buffer_length = 3;
- FILL_CONTROL_URB( &ctrl->urb, pegasus->usb,
+ FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,
usb_sndctrlpipe(pegasus->usb,0),
- (char *)&ctrl->dr,
- &data, 1, pegasus_ctrl_end, ctrl );
+ (char *)&pegasus->dr,
+ pegasus->eth_regs, 3, ctrl_callback, pegasus );
+
+ if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) )
+ err( __FUNCTION__ " BAD CTRL %d, flags %x",ret,pegasus->flags );
- if ( (ret = usb_submit_urb( &ctrl->urb )) )
- err( __FUNCTION__ " BAD CTRL %d", ret);
-#ifdef PEGASUS_USE_WAITQ
- interruptible_sleep_on( &pegasus->ctrl_wait );
-#endif
return ret;
}
-static int pegasus_read_phy_word(struct pegasus *pegasus, __u8 index, __u16 *regdata)
+static int read_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd )
{
- int i;
- __u8 data[4] = { 1, 0, 0, 0x40 + index };
+ int i;
+ __u8 data[4] = { phy, 0, 0, indx };
- pegasus_set_registers(pegasus, PhyAddr, 4, data);
- for (i = 0; i < 100; i++) {
- pegasus_get_registers(pegasus, PhyData, 3, data);
- if (data[2] & 0x80) {
- *regdata = *(__u16 *)(data);
- return 0;
- }
+ set_register( pegasus, PhyCtrl, 0 );
+ set_registers( pegasus, PhyAddr, sizeof(data), data );
+ set_register( pegasus, PhyCtrl, (indx | PHY_READ) );
+ for (i = 0; i < REG_TIMEOUT; i++) {
+ get_registers(pegasus, PhyCtrl, 1, data);
+ if ( data[0] & PHY_DONE )
+ break;
}
- warn("read_phy_word() failed");
+ if ( i < REG_TIMEOUT ) {
+ get_registers( pegasus, PhyData, 2, regd );
+ return 0;
+ }
+ warn( __FUNCTION__ " failed" );
return 1;
}
-static int pegasus_write_phy_word(struct pegasus *pegasus, __u8 index, __u16 regdata)
+static int write_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd )
{
- int i;
- __u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index };
-
- pegasus_set_registers(pegasus, PhyAddr, 4, data);
- for (i = 0; i < 100; i++) {
- pegasus_get_registers(pegasus, PhyCtrl, 1, data);
- if (data[0] & 0x80)
- return 0;
+ int i;
+ __u8 data[4] = { phy, 0, 0, indx };
+
+ *(data + 1) = cpu_to_le16p( ®d );
+ set_register( pegasus, PhyCtrl, 0 );
+ set_registers( pegasus, PhyAddr, 4, data );
+ set_register( pegasus, PhyCtrl, (indx | PHY_WRITE) );
+ for (i = 0; i < REG_TIMEOUT; i++) {
+ get_registers(pegasus, PhyCtrl, 1, data);
+ if ( data[0] & PHY_DONE )
+ break;
}
- warn("write_phy_word() failed");
+ if ( i < REG_TIMEOUT )
+ return 0;
+ warn( __FUNCTION__ " failed" );
return 1;
}
-static int pegasus_rw_eprom_word(struct pegasus *pegasus, __u8 index, __u16 *retdata, __u8 direction)
+static int read_eprom_word( pegasus_t *pegasus, __u8 index, __u16 *retdata )
{
- int i;
- __u8 data[4] = { index, 0, 0, direction };
+ int i, tmp;
- pegasus_set_registers(pegasus, EpromOffset, 4, data);
- for (i = 0; i < 100; i++) {
- pegasus_get_registers(pegasus, EpromCtrl, 1, data);
- if (data[0] & 4) {
- pegasus_get_registers(pegasus, EpromData, 2, data);
- *retdata = *(__u16 *)data;
- return 0;
- }
+ set_register( pegasus, EpromCtrl, 0 );
+ set_register( pegasus, EpromOffset, index );
+ set_register( pegasus, EpromCtrl, EPROM_READ);
+ for ( i=0; i < REG_TIMEOUT; i++ ) {
+ get_registers( pegasus, EpromCtrl, 1, &tmp );
+ if ( tmp & EPROM_DONE )
+ break;
}
- warn("pegasus_rw_eprom_word() failed");
-
- return 1;
+ if ( i < REG_TIMEOUT ) {
+ get_registers( pegasus, EpromData, 2, retdata );
+ return 0;
+ }
+ warn( __FUNCTION__ " failed" );
+
+ return -1;
+}
+
+
+static inline void enable_eprom_write( pegasus_t *pegasus )
+{
+ __u8 tmp;
+
+ get_registers( pegasus, EthCtrl2, 1, &tmp );
+ set_register( pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE );
+}
+
+
+static inline void disable_eprom_write( pegasus_t *pegasus )
+{
+ __u8 tmp;
+
+ get_registers( pegasus, EthCtrl2, 1, &tmp );
+ set_register( pegasus, EpromCtrl, 0 );
+ set_register( pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE );
+}
+
+
+static int write_eprom_word( pegasus_t *pegasus, __u8 index, __u16 data )
+{
+ int i, tmp;
+ __u8 d[4] = {0x3f, 0, 0, EPROM_WRITE};
+
+ set_registers( pegasus, EpromOffset, 4, d );
+ enable_eprom_write( pegasus );
+ set_register( pegasus, EpromOffset, index );
+ set_registers( pegasus, EpromData, 2, &data );
+ set_register( pegasus, EpromCtrl, EPROM_WRITE );
+
+ for ( i=0; i < REG_TIMEOUT; i++ ) {
+ get_registers( pegasus, EpromCtrl, 1, &tmp );
+ if ( tmp & EPROM_DONE )
+ break;
+ }
+ disable_eprom_write( pegasus );
+ if ( i < REG_TIMEOUT )
+ return 0;
+ warn( __FUNCTION__ " failed" );
+ return -1;
}
-static int pegasus_get_node_id(struct pegasus *pegasus, __u8 *id)
+static void set_intr_interval( pegasus_t *pegasus )
+{
+ __u16 d;
+ __u8 tmp;
+
+ read_eprom_word( pegasus, 4, &d );
+ ((__u8 *)&d)[1] = INTR_IVAL;
+ write_eprom_word( pegasus, 4, d );
+ get_registers( pegasus, EthCtrl2, 1, &tmp );
+ set_register( pegasus, EthCtrl2, tmp | EPROM_LOAD );
+ udelay( 10000 );
+ set_register( pegasus, EthCtrl2, tmp );
+#ifdef PEGASUS_DUMP_EEPROM
+ { int i;
+ for ( i=0; i < 0x40; i++ ) {
+ read_eprom_word( pegasus, i, &d );
+ printk( "eepromword %02x-%04x, ", i, d );
+ }
+ printk( "\n" );
+ }
+#endif
+}
+
+
+static inline int get_node_id( pegasus_t *pegasus, __u8 *id )
{
int i;
+
for (i = 0; i < 3; i++)
- if (pegasus_rw_eprom_word(pegasus, i, (__u16 *)&id[i*2], EPROM_READ))
+ if ( read_eprom_word( pegasus, i, (__u16 *)&id[i*2]) )
return 1;
return 0;
}
-static int pegasus_reset_mac(struct pegasus *pegasus)
+static inline int reset_mac( pegasus_t *pegasus )
{
- __u8 data = 0x8;
- int i;
+ __u8 data = 0x8;
+ int i;
- pegasus_set_register(pegasus, EthCtrl1, data);
- for (i = 0; i < 100; i++) {
- pegasus_get_registers(pegasus, EthCtrl1, 1, &data);
+ set_register(pegasus, EthCtrl1, data);
+ for (i = 0; i < REG_TIMEOUT; i++) {
+ get_registers(pegasus, EthCtrl1, 1, &data);
if (~data & 0x08) {
if (loopback & 1)
- return 0;
- pegasus_set_register(pegasus, Gpio0, 0x24);
- pegasus_set_register(pegasus, Gpio0, 0x27);
- return 0;
+ break;
+ if ( mode && (pegasus->features & HAS_HOME_PNA) )
+ set_register( pegasus, Gpio1, 0x34 );
+ else
+ set_register( pegasus, Gpio1, 0x26 );
+ set_register( pegasus, Gpio0, pegasus->features );
+ set_register( pegasus, Gpio0, DEFAULT_GPIO_SET );
+ break;
}
}
-
- return 1;
+ if ( i == REG_TIMEOUT )
+ return 1;
+ return 0;
}
-static int pegasus_start_net(struct net_device *dev, struct usb_device *usb)
+static int start_net( struct net_device *dev, struct usb_device *usb )
{
- __u16 partmedia, temp;
- __u8 node_id[6];
- __u8 data[4];
- struct pegasus *pegasus = dev->priv;
+ __u16 linkpart, bmsr;
+ __u8 node_id[6];
+ __u8 data[4];
+ pegasus_t *pegasus = dev->priv;
- if (pegasus_get_node_id(pegasus, node_id))
+ if ( get_node_id(pegasus, node_id) )
return 1;
- pegasus_set_registers(pegasus, EthID, 6, node_id);
- memcpy(dev->dev_addr, node_id, 6);
- if (pegasus_read_phy_word(pegasus, 1, &temp))
+ set_registers( pegasus, EthID, sizeof(node_id), node_id );
+ memcpy( dev->dev_addr, node_id, sizeof(node_id) );
+ if ( read_phy_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) )
return 2;
-
- if ((~temp & 4) && !loopback) {
- warn("%s: link NOT established (0x%x) - check the cable.",
- dev->name, temp);
- }
-
- if (pegasus_read_phy_word(pegasus, 5, &partmedia))
+ 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 ((partmedia & 0x1f) != 1) {
- warn("party FAIL %x", partmedia);
- }
+ if ( !(linkpart & 1) )
+ warn( "link partner stat %x", linkpart );
data[0] = 0xc9;
- data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0);
+ data[1] = 0;
+ if ( linkpart & (ANLPA_100TX_FD | ANLPA_10T_FD) )
+ data[1] |= 0x20; /* set full duplex */
+ if ( linkpart & (ANLPA_100TX_FD | ANLPA_100TX_HD) )
+ data[1] |= 0x10; /* set 100 Mbps */
+ if ( mode )
+ data[1] = 0;
data[2] = (loopback & 1) ? 0x09 : 0x01;
- pegasus_set_registers(pegasus, EthCtrl0, 3, data);
+ *(unsigned *)pegasus->eth_regs = *(unsigned *)data;
+
+ set_registers( pegasus, EthCtrl0, 3, data );
return 0;
}
-static void pegasus_read_bulk_callback( struct urb *urb )
+static void read_bulk_callback( struct urb *urb )
{
- struct pegasus *pegasus = urb->context;
- struct net_device *net; /* = pegasus->net;*/
+ pegasus_t *pegasus = urb->context;
+ struct net_device *net;
int count = urb->actual_length, res;
- int rx_status; /*= *(int *)(pegasus->rx_buff + count - 4);*/
+ int rx_status;
struct sk_buff *skb;
__u16 pkt_len;
@@ -436,26 +584,26 @@
goto goon;
}
- if (!count)
- goto goon;
-#if 0
- if (rx_status & 0x00010000)
+ if ( !count )
goto goon;
-#endif
- if (rx_status & 0x000e0000) {
+
+ if ( rx_status & 0x000e0000 ) {
dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000);
pegasus->stats.rx_errors++;
- if(rx_status & 0x060000) pegasus->stats.rx_length_errors++;
- if(rx_status & 0x080000) pegasus->stats.rx_crc_errors++;
- if(rx_status & 0x100000) pegasus->stats.rx_frame_errors++;
+ if ( rx_status & 0x060000 )
+ pegasus->stats.rx_length_errors++;
+ if ( rx_status & 0x080000 )
+ pegasus->stats.rx_crc_errors++;
+ if ( rx_status & 0x100000 )
+ pegasus->stats.rx_frame_errors++;
goto goon;
}
pkt_len = (rx_status & 0xfff) - 8;
- if(!(skb = dev_alloc_skb(pkt_len+2)))
+ if ( !(skb = dev_alloc_skb(pkt_len+2)) )
goto goon;
skb->dev = net;
@@ -469,36 +617,60 @@
pegasus->stats.rx_bytes += pkt_len;
goon:
+ pegasus->rx_urb.dev = pegasus->usb;
if ( (res = usb_submit_urb(&pegasus->rx_urb)) )
- warn("(prb)failed rx_urb %d", res);
+ warn( __FUNCTION__ " failed submint rx_urb %d", res);
}
-static void pegasus_irq_callback( urb_t *urb )
+static void write_bulk_callback( struct urb *urb )
{
- __u8 *d = urb->transfer_buffer;
+ pegasus_t *pegasus = urb->context;
+ if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) )
+ return;
+ if ( urb->status )
+ info("%s: TX status %d", pegasus->net->name, urb->status);
- if ( d[0] )
- dbg("txst0=0x%2x", d[0]);
+ netif_wake_queue(pegasus->net);
}
-static void pegasus_write_bulk_callback(struct urb *urb)
+static void intr_callback( struct urb *urb )
{
- struct pegasus *pegasus = urb->context;
+ pegasus_t *pegasus = urb->context;
+ struct net_device *net;
+ __u8 *d;
if ( !pegasus )
return;
-
- if (urb->status)
- info("%s: TX status %d", pegasus->net->name, urb->status);
- netif_wake_queue(pegasus->net);
+ d = urb->transfer_buffer;
+ net = pegasus->net;
+ if ( d[0] & 0xfc ) {
+ pegasus->stats.tx_errors++;
+ if ( d[0] & TX_UNDERRUN )
+ pegasus->stats.tx_fifo_errors++;
+ if ( d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT) )
+ pegasus->stats.tx_aborted_errors++;
+ if ( d[0] & LATE_COL )
+ pegasus->stats.tx_window_errors++;
+ 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);
+ }
}
-static void pegasus_tx_timeout(struct net_device *net)
+
+static void pegasus_tx_timeout( struct net_device *net )
{
- struct pegasus *pegasus = net->priv;
+ pegasus_t *pegasus = net->priv;
if ( !pegasus )
return;
@@ -512,11 +684,11 @@
}
-static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
+static int pegasus_start_xmit( struct sk_buff *skb, struct net_device *net )
{
- struct pegasus *pegasus = net->priv;
- int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3;
- int res;
+ pegasus_t *pegasus = net->priv;
+ int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3;
+ int res;
netif_stop_queue(net);
if ( !(pegasus->flags & PEGASUS_RUNNING) )
@@ -527,6 +699,7 @@
pegasus->tx_urb.transfer_buffer_length = count;
pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK;
+ pegasus->tx_urb.dev = pegasus->usb;
if ((res = usb_submit_urb(&pegasus->tx_urb))) {
warn("failed tx_urb %d", res);
pegasus->stats.tx_errors++;
@@ -543,77 +716,76 @@
}
-static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev)
+static struct net_device_stats *pegasus_netdev_stats( struct net_device *dev )
{
- return &((struct pegasus *)dev->priv)->stats;
+ return &((pegasus_t *)dev->priv)->stats;
}
-static inline void pegasus_stop_net( struct pegasus *pegasus )
+static inline void stop_net( pegasus_t *pegasus )
{
- int tmp;
+ int tmp=0;
- pegasus_get_registers( pegasus, EthCtrl0, 1, &tmp );
- pegasus_set_register( pegasus, EthCtrl0, tmp & 0x3f );
+ set_registers( pegasus, EthCtrl0, 2, &tmp );
}
static int pegasus_open(struct net_device *net)
{
- struct pegasus *pegasus = (struct pegasus *)net->priv;
- int res;
+ pegasus_t *pegasus = (pegasus_t *)net->priv;
+ int res;
- if ((res = pegasus_start_net(net, pegasus->usb))) {
+ if ((res = start_net(net, pegasus->usb))) {
err("can't start_net() - %d", res);
return -EIO;
}
-
- if ((res = usb_submit_urb(&pegasus->rx_urb)))
- warn("(open)failed rx_urb %d", res);
-
- if ((res = usb_submit_urb(&pegasus->intr_urb)))
- warn("(open)failed intr_urb %d", res);
-
- netif_start_queue(net);
+ pegasus->rx_urb.dev = pegasus->usb;
+ if ( (res = usb_submit_urb(&pegasus->rx_urb)) )
+ warn( __FUNCTION__ " failed rx_urb %d", res );
+#ifdef PEGASUS_USE_INTR
+ pegasus->intr_urb.dev = pegasus->usb;
+ if ( (res = usb_submit_urb(&pegasus->intr_urb)) )
+ warn( __FUNCTION__ " failed intr_urb %d", res);
+#endif
+ netif_start_queue( net );
pegasus->flags |= PEGASUS_RUNNING;
return 0;
}
-static int pegasus_close(struct net_device *net)
+static int pegasus_close( struct net_device *net )
{
- struct pegasus *pegasus = net->priv;
+ pegasus_t *pegasus = net->priv;
+ stop_net( pegasus );
pegasus->flags &= ~PEGASUS_RUNNING;
- pegasus_stop_net( pegasus );
-
netif_stop_queue(net);
- usb_unlink_urb(&pegasus->rx_urb);
- usb_unlink_urb(&pegasus->tx_urb);
- usb_unlink_urb(&pegasus->intr_urb);
- pegasus_unlink_ctrl_urbs( pegasus );
+ usb_unlink_urb( &pegasus->rx_urb );
+ usb_unlink_urb( &pegasus->tx_urb );
+ usb_unlink_urb( &pegasus->ctrl_urb );
+ usb_unlink_urb( &pegasus->intr_urb );
return 0;
}
-static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+static int pegasus_ioctl( struct net_device *net, struct ifreq *rq, int cmd )
{
__u16 *data = (__u16 *)&rq->ifr_data;
- struct pegasus *pegasus = net->priv;
+ pegasus_t *pegasus = net->priv;
switch(cmd) {
case SIOCDEVPRIVATE:
- data[0] = 1;
+ data[0] = pegasus->phy;
case SIOCDEVPRIVATE+1:
- pegasus_read_phy_word(pegasus, data[1] & 0x1f, &data[3]);
+ read_phy_word(pegasus, data[0], data[1]&0x1f, &data[3]);
return 0;
case SIOCDEVPRIVATE+2:
- if (!capable(CAP_NET_ADMIN))
+ if ( !capable(CAP_NET_ADMIN) )
return -EPERM;
- pegasus_write_phy_word(pegasus, data[1] & 0x1f, data[2]);
+ write_phy_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]);
return 0;
default:
return -EOPNOTSUPP;
@@ -621,36 +793,29 @@
}
-static void pegasus_set_rx_mode(struct net_device *net)
+static void pegasus_set_multicast( struct net_device *net )
{
-#ifndef PEGASUS_USE_WAITQ
- struct pegasus *pegasus = net->priv;
- __u8 tmp;
-#endif
+ pegasus_t *pegasus = net->priv;
netif_stop_queue(net);
if (net->flags & IFF_PROMISC) {
-#ifndef PEGASUS_USE_WAITQ
- pegasus_get_registers(pegasus, EthCtrl2, 1, &tmp);
- pegasus_set_register(pegasus, EthCtrl2, tmp | 4);
-#endif
+ pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS;
info("%s: Promiscuous mode enabled", net->name);
} else if ((net->mc_count > multicast_filter_limit) ||
(net->flags & IFF_ALLMULTI)) {
-#ifndef PEGASUS_USE_WAITQ
- pegasus_set_register(pegasus, EthCtrl0, 0xfa);
- pegasus_set_register(pegasus, EthCtrl2, 0);
-#endif
+ pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST;
+ pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
info("%s set allmulti", net->name);
} else {
-#ifndef PEGASUS_USE_WAITQ
- pegasus_get_registers(pegasus, EthCtrl2, 1, &tmp);
- pegasus_set_register(pegasus, EthCtrl2, tmp & ~4);
-#endif
+ pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST;
+ pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
info("%s: set Rx mode", net->name);
}
+ pegasus->flags |= ETH_REGS_CHANGE;
+ ctrl_callback( &pegasus->ctrl_urb );
+
netif_wake_queue(net);
}
@@ -669,10 +834,38 @@
}
-static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
+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 );
+ if ( tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0 )
+ continue;
+ else
+ return i;
+ }
+
+ return 0;
+}
+
+
+static inline void setup_pegasus_II( pegasus_t *pegasus )
+{
+ set_register( pegasus, Reg1d, 0 );
+ set_register( pegasus, Reg7b, 2 );
+ if ( pegasus->features & HAS_HOME_PNA )
+ set_register( pegasus, Reg81, 6 );
+ else
+ set_register( pegasus, Reg81, 2 );
+}
+
+
+static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum )
{
struct net_device *net;
- struct pegasus *pegasus;
+ pegasus_t *pegasus;
int dev_indx;
if ( (dev_indx = check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct)) == -1 ) {
@@ -698,34 +891,50 @@
net->tx_timeout = pegasus_tx_timeout;
net->do_ioctl = pegasus_ioctl;
net->hard_start_xmit = pegasus_start_xmit;
- net->set_multicast_list = pegasus_set_rx_mode;
+ net->set_multicast_list = pegasus_set_multicast;
net->get_stats = pegasus_netdev_stats;
net->mtu = PEGASUS_MTU;
init_MUTEX( &pegasus-> ctrl_sem );
init_waitqueue_head( &pegasus->ctrl_wait );
+ usb_inc_dev_use (dev);
pegasus->usb = dev;
pegasus->net = net;
FILL_BULK_URB( &pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1),
pegasus->rx_buff, PEGASUS_MAX_MTU,
- pegasus_read_bulk_callback, pegasus );
+ read_bulk_callback, pegasus );
FILL_BULK_URB( &pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2),
pegasus->tx_buff, PEGASUS_MAX_MTU,
- pegasus_write_bulk_callback, pegasus );
+ write_bulk_callback, pegasus );
FILL_INT_URB( &pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3),
- pegasus->intr_buff, 8, pegasus_irq_callback,
- pegasus, 128 );
+ pegasus->intr_buff, 8, intr_callback,
+ pegasus, INTR_IVAL );
- if (pegasus_reset_mac(pegasus)) {
+ pegasus->features = usb_dev_id[dev_indx].private;
+ if ( reset_mac(pegasus) ) {
err("can't reset MAC");
+ unregister_netdev( pegasus->net );
kfree(pegasus);
pegasus = NULL;
return NULL;
}
-
- info( "%s: %s\n", net->name, usb_dev_id[dev_indx].name );
+
+ if ( pegasus->features & PEGASUS_II ) {
+ info( "setup Pegasus II specific registers" );
+ setup_pegasus_II( pegasus );
+ }
+
+ pegasus->phy = mii_phy_probe( pegasus );
+ if ( !pegasus->phy ) {
+ warn( "can't locate MII phy, using default" );
+ pegasus->phy = 1;
+ }
+
+ set_intr_interval( pegasus );
+
+ info( "%s: %s", net->name, usb_dev_id[dev_indx].name );
MOD_INC_USE_COUNT;
@@ -733,24 +942,22 @@
}
-static void pegasus_disconnect(struct usb_device *dev, void *ptr)
+static void pegasus_disconnect( struct usb_device *dev, void *ptr )
{
struct pegasus *pegasus = ptr;
- if (!pegasus) {
+ if ( !pegasus ) {
warn("unregistering non-existant device");
return;
}
+ stop_net( pegasus );
pegasus->flags &= ~PEGASUS_RUNNING;
- unregister_netdev(pegasus->net);
-
- usb_unlink_urb(&pegasus->rx_urb);
- usb_unlink_urb(&pegasus->tx_urb);
- usb_unlink_urb(&pegasus->intr_urb);
- pegasus_unlink_ctrl_urbs( pegasus );
+ netif_stop_queue( pegasus->net );
+ unregister_netdev( pegasus->net );
- kfree(pegasus);
+ usb_dec_dev_use (pegasus->usb);
+ kfree( pegasus );
pegasus = NULL;
MOD_DEC_USE_COUNT;
@@ -766,13 +973,13 @@
int __init pegasus_init(void)
{
info( "%s", version );
- return usb_register(&pegasus_driver);
+ return usb_register( &pegasus_driver );
}
void __exit pegasus_exit(void)
{
- usb_deregister(&pegasus_driver);
+ usb_deregister( &pegasus_driver );
}
-module_init(pegasus_init);
-module_exit(pegasus_exit);
+module_init( pegasus_init );
+module_exit( pegasus_exit );
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)