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

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( &regd );
+	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)