patch-2.4.0-test10 linux/drivers/usb/pegasus.c

Next file: linux/drivers/usb/plusb.c
Previous file: linux/drivers/usb/net1080.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test9/linux/drivers/usb/pegasus.c linux/drivers/usb/pegasus.c
@@ -14,11 +14,13 @@
 **		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.
+**			Some long standing bugs (enable_net_traffic) fixed.
+**			Also nasty trick about 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.
+**		v0.4.13	Ethernet address is now set at probe(), not at open()
+**			time as this seems to break dhcpd. 
 */
 
 /*
@@ -48,7 +50,7 @@
 #include <linux/usb.h>
 
 
-static const char *version = __FILE__ ": v0.4.9 2000/09/14 (C) 1999-2000 Petko Manolov (petkan@dce.bg)";
+static const char *version = __FILE__ ": v0.4.13 2000/10/13 (C) 1999-2000 Petko Manolov (petkan@dce.bg)";
 
 
 #define	PEGASUS_USE_INTR
@@ -81,10 +83,13 @@
 #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	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
 
@@ -94,12 +99,6 @@
 #define	REG_TIMEOUT		(HZ)
 #define	PEGASUS_TX_TIMEOUT	(HZ*10)
 
-#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
@@ -144,6 +143,7 @@
 	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;
@@ -165,7 +165,7 @@
 
 
 static int loopback = 0;
-static int mode = 0;
+static int mii_mode = 0;
 static int multicast_filter_limit = 32;
 
 
@@ -183,13 +183,14 @@
 	{"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-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, 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, 
@@ -209,7 +210,7 @@
 /* Aargh!!! I _really_ hate such tweaks */
 static void ctrl_callback( urb_t *urb )
 {
-	pegasus_t		*pegasus = urb->context;
+	pegasus_t	*pegasus = urb->context;
 
 	if ( !pegasus )
 		return;
@@ -260,11 +261,11 @@
 
 	if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) {
 		err( __FUNCTION__ " BAD CTRLs %d", ret);
-		return	ret;
+		goto out;
 	}
 	pegasus->flags |= CTRL_URB_SLEEP;
 	interruptible_sleep_on( &pegasus->ctrl_wait );
-
+out:
 	return	ret;
 }
 
@@ -419,7 +420,7 @@
 	return -1;
 }
 
-
+#ifdef	PEGASUS_WRITE_EEPROM
 static inline void enable_eprom_write( pegasus_t *pegasus )
 {
 	__u8	tmp;
@@ -461,40 +462,24 @@
 	warn( __FUNCTION__ " failed" );
 	return	-1;
 }
+#endif	/* PEGASUS_WRITE_EEPROM */
 
-
-static void set_intr_interval( pegasus_t *pegasus )
+static inline void get_node_id( pegasus_t *pegasus, __u8 *id )
 {
-	__u16	d;
-	__u8	tmp;
+	int	i;
 
-	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
+	for (i = 0; i < 3; i++)
+		read_eprom_word( pegasus, i, (__u16 *)&id[i*2]);
 }
 
 
-static inline int get_node_id( pegasus_t *pegasus, __u8 *id )
+static void set_ethernet_addr( pegasus_t *pegasus )
 {
-	int i;
+	__u8	node_id[6];
 
-	for (i = 0; i < 3; i++)
-		if ( read_eprom_word( pegasus, i, (__u16 *)&id[i*2]) )
-			return 1;
-	return 0;
+	get_node_id(pegasus, node_id);
+	set_registers( pegasus, EthID, sizeof(node_id), node_id );
+	memcpy( pegasus->net->dev_addr, node_id, sizeof(node_id) );
 }
 
 
@@ -509,7 +494,7 @@
 		if (~data & 0x08) {
 			if (loopback & 1) 
 				break;
-			if ( mode && (pegasus->features & HAS_HOME_PNA) )
+			if ( mii_mode && (pegasus->features & HAS_HOME_PNA) )
 				set_register( pegasus, Gpio1, 0x34 );
 			else
 				set_register( pegasus, Gpio1, 0x26 );
@@ -524,18 +509,13 @@
 }
 
 
-static int start_net( struct net_device *dev, struct usb_device *usb )
+static int enable_net_traffic( struct net_device *dev, struct usb_device *usb )
 {
 	__u16	linkpart, bmsr;
-	__u8	node_id[6];
 	__u8	data[4];
 	pegasus_t *pegasus = dev->priv;
 
-	if ( get_node_id(pegasus, node_id) ) 
-		return 1;
 
-	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 ( !(bmsr & 0x20) && !loopback ) 
@@ -552,7 +532,7 @@
 		data[1] |= 0x20; /* set full duplex */
 	if ( linkpart & (ANLPA_100TX_FD | ANLPA_100TX_HD) )
 		data[1] |= 0x10; /* set 100 Mbps */
-	if ( mode )
+	if ( mii_mode )
 		data[1] = 0;
 	data[2] = (loopback & 1) ? 0x09 : 0x01;
 
@@ -577,6 +557,15 @@
 		return;
 
 	net = pegasus->net;
+	if ( !netif_device_present(net) )
+		return;
+
+	if ( pegasus->flags & PEGASUS_RX_BUSY ) {
+		pegasus->stats.rx_errors++;
+		return;
+	}
+	pegasus->flags |= PEGASUS_RX_BUSY;
+
 	rx_status = *(int *)(pegasus->rx_buff + count - 4);
 
 	if (urb->status) {
@@ -617,9 +606,13 @@
 	pegasus->stats.rx_bytes += pkt_len;
 
 goon:
-	pegasus->rx_urb.dev = pegasus->usb;
+	FILL_BULK_URB( &pegasus->rx_urb, pegasus->usb,
+			usb_rcvbulkpipe(pegasus->usb, 1),
+			pegasus->rx_buff, PEGASUS_MAX_MTU, 
+			read_bulk_callback, pegasus );
 	if ( (res = usb_submit_urb(&pegasus->rx_urb)) )
 		warn( __FUNCTION__ " failed submint rx_urb %d", res);
+	pegasus->flags &= ~PEGASUS_RX_BUSY;
 }
 
 
@@ -629,13 +622,17 @@
 
 	if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) )
 		return;
+
+	if ( !netif_device_present(pegasus->net) )
+		return;
+		
 	if ( urb->status )
 		info("%s: TX status %d", pegasus->net->name, urb->status);
 
-	netif_wake_queue(pegasus->net);
+	netif_wake_queue( pegasus->net );
 }
 
-
+#ifdef	PEGASUS_USE_INTR
 static void intr_callback( struct urb *urb )
 {
 	pegasus_t *pegasus = urb->context;
@@ -666,7 +663,7 @@
 			info("intr status %d", urb->status);
 	}
 }
-
+#endif
 
 static void pegasus_tx_timeout( struct net_device *net )
 {
@@ -675,12 +672,12 @@
 	if ( !pegasus )
 		return;
 	
-	usb_unlink_urb(&pegasus->tx_urb);
+	usb_unlink_urb( &pegasus->tx_urb );
 	warn("%s: Tx timed out.", net->name);
 	pegasus->stats.tx_errors++;
 	net->trans_start = jiffies;
 
-	netif_wake_queue(net);
+	netif_wake_queue( net );
 }
 
 
@@ -690,20 +687,20 @@
 	int 	count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3;
 	int 	res;
 
-	netif_stop_queue(net);
-	if ( !(pegasus->flags & PEGASUS_RUNNING) )
-		return	0;
-
+	netif_stop_queue( net );
+		
 	((__u16 *)pegasus->tx_buff)[0] = skb->len;
 	memcpy(pegasus->tx_buff+2, skb->data, skb->len);
+	FILL_BULK_URB( &pegasus->tx_urb, pegasus->usb,
+			usb_sndbulkpipe(pegasus->usb, 2),
+			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;
-
-	pegasus->tx_urb.dev = pegasus->usb;
 	if ((res = usb_submit_urb(&pegasus->tx_urb))) {
 		warn("failed tx_urb %d", res);
 		pegasus->stats.tx_errors++;
-		netif_start_queue(net);
+		netif_start_queue( net );
 	} else {
 		pegasus->stats.tx_packets++;
 		pegasus->stats.tx_bytes += skb->len;
@@ -722,7 +719,7 @@
 }
 
 
-static inline void stop_net( pegasus_t *pegasus )
+static inline void disable_net_traffic( pegasus_t *pegasus )
 {
 	int 	tmp=0;
 
@@ -730,23 +727,41 @@
 }
 
 
+static inline void get_interrupt_interval( pegasus_t *pegasus )
+{
+	__u8	data[2];
+
+	read_eprom_word( pegasus, 4, (__u16 *)data );
+	pegasus->intr_interval = data[1];
+}
+
+
 static int pegasus_open(struct net_device *net)
 {
 	pegasus_t *pegasus = (pegasus_t *)net->priv;
 	int	res;
 
-	if ((res = start_net(net, pegasus->usb))) {
-		err("can't start_net() - %d", res);
+	MOD_INC_USE_COUNT;
+	if ( (res = enable_net_traffic(net, pegasus->usb)) ) {
+		err("can't enable_net_traffic() - %d", res);
+		MOD_DEC_USE_COUNT;
 		return -EIO;
 	}
-	pegasus->rx_urb.dev = pegasus->usb;
+	FILL_BULK_URB( &pegasus->rx_urb, pegasus->usb,
+			usb_rcvbulkpipe(pegasus->usb, 1),
+			pegasus->rx_buff, PEGASUS_MAX_MTU, 
+			read_bulk_callback, pegasus );
 	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;
+	get_interrupt_interval( pegasus );
+	FILL_INT_URB( &pegasus->intr_urb, pegasus->usb,
+			usb_rcvintpipe(pegasus->usb, 3),
+			pegasus->intr_buff, sizeof(pegasus->intr_buff),
+			intr_callback, pegasus, pegasus->intr_interval );
 	if ( (res = usb_submit_urb(&pegasus->intr_urb)) )
 		warn( __FUNCTION__ " failed intr_urb %d", res);
-#endif		
+#endif
 	netif_start_queue( net );
 	pegasus->flags |= PEGASUS_RUNNING;
 
@@ -758,15 +773,18 @@
 {
 	pegasus_t	*pegasus = net->priv;
 
-	stop_net( pegasus );
 	pegasus->flags &= ~PEGASUS_RUNNING;
-	netif_stop_queue(net);
+	netif_stop_queue( net );
+	if ( !(pegasus->flags & PEGASUS_UNPLUG) )
+		disable_net_traffic( 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 );
 
+	MOD_DEC_USE_COUNT;
+
 	return 0;
 }
 
@@ -855,7 +873,7 @@
 {
 	set_register( pegasus, Reg1d, 0 );
 	set_register( pegasus, Reg7b, 2 );
-	if ( pegasus->features & HAS_HOME_PNA )
+	if ( pegasus->features & HAS_HOME_PNA  && mii_mode )
 		set_register( pegasus, Reg81, 6 );
 	else
 		set_register( pegasus, Reg81, 2 );
@@ -881,37 +899,33 @@
 		err("out of memory allocating device structure");
 		return NULL;
 	}
+
+	usb_inc_dev_use( dev );
 	memset(pegasus, 0, sizeof(struct pegasus));
+	init_MUTEX( &pegasus-> ctrl_sem );
+	init_waitqueue_head( &pegasus->ctrl_wait );
 
-	net = init_etherdev(0, 0);
+	net = init_etherdev( NULL, 0 );
+	if ( !net ) {
+		kfree( pegasus );
+		return	NULL;
+	}
+	
+	pegasus->usb = dev;
+	pegasus->net = net;
 	net->priv = pegasus;
 	net->open = pegasus_open;
 	net->stop = pegasus_close;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,48)
 	net->watchdog_timeo = PEGASUS_TX_TIMEOUT;
 	net->tx_timeout = pegasus_tx_timeout;
+#endif
 	net->do_ioctl = pegasus_ioctl;
 	net->hard_start_xmit = pegasus_start_xmit;
 	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, 
-			read_bulk_callback, pegasus );
-	FILL_BULK_URB( &pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2),
-			pegasus->tx_buff, PEGASUS_MAX_MTU, 
-			write_bulk_callback, pegasus );
-	FILL_INT_URB( &pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3),
-			pegasus->intr_buff, 8, intr_callback,
-			pegasus, INTR_IVAL );
-
 	pegasus->features = usb_dev_id[dev_indx].private;
 	if ( reset_mac(pegasus) ) {
 		err("can't reset MAC");
@@ -921,23 +935,21 @@
 		return NULL;
 	}
 
+	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 ) {
 		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;
-
 	return pegasus;
 }
 
@@ -951,16 +963,11 @@
 		return;
 	}
 
-	stop_net( pegasus );
-	pegasus->flags &= ~PEGASUS_RUNNING;
-	netif_stop_queue( pegasus->net );
+	pegasus->flags |= PEGASUS_UNPLUG;
 	unregister_netdev( pegasus->net );
-
-	usb_dec_dev_use (pegasus->usb);
+	usb_dec_dev_use( dev );
 	kfree( pegasus );
 	pegasus = NULL;
-
-	MOD_DEC_USE_COUNT;
 }
 
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)