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

Next file: linux/drivers/usb/printer.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-test1/linux/drivers/usb/pegasus.c linux/drivers/usb/pegasus.c
@@ -1,11 +1,37 @@
 /*
 **	Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller
 **
-**	Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net)
+**	Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@spct.net)
+**	
 **
-**	Distribute under GPL version 2 or later.
+**	ChangeLog:
+**		....	Most of the time spend reading sources & docs.
+**		v0.2.x	First official release for the Linux kernel.
+**		v0.3.0	Beutified and structured, some bugs fixed.
+**		v0.3.x	URBifying bulk requests and bugfixing. First relatively
+**			stable release. Still can touch device's registers only
+**			from top-halves.
+**		v0.4.0	Control messages remained unurbified are now URBs.
+**			Now we can touch the HW at any time.
 */
 
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/malloc.h>
@@ -16,7 +42,7 @@
 #include <linux/usb.h>
 
 
-static const char *version = __FILE__ ": v0.3.12 2000/05/22 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n";
+static const char *version = __FILE__ ": v0.4.0 2000/06/15 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n";
 
 
 #define	PEGASUS_MTU		1500
@@ -24,17 +50,39 @@
 #define	SROM_WRITE		0x01
 #define	SROM_READ		0x02
 #define	PEGASUS_TX_TIMEOUT	(HZ*5)
+#define	PEGASUS_CTRL_TIMEOUT	1000
 #define	PEGASUS_RESET		1
 #define	ALIGN(x)		x __attribute__((aligned(L1_CACHE_BYTES)))
 
 
+enum pegasus_registers {
+	EthCtrl0 = 0,
+	EthCtrl1 = 1,
+	EthCtrl2 = 2,
+	EthID = 0x10,
+	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,
+	Gpio0 = 0x7e,
+	Gpio1 = 0x7f,
+};
+
+
 struct pegasus {
 	struct usb_device	*usb;
 	struct net_device	*net;
 	struct net_device_stats	stats;
 	int			flags;
-	spinlock_t		pegasus_lock;
-	struct urb		rx_urb, tx_urb, intr_urb;
+	spinlock_t		pegasus_lock, ctrl_lock;
+	struct urb		rx_urb, tx_urb, intr_urb, ctrl_urb;
+	devrequest		dr;
 	unsigned char		ALIGN(rx_buff[PEGASUS_MAX_MTU]); 
 	unsigned char		ALIGN(tx_buff[PEGASUS_MAX_MTU]); 
 	unsigned char		ALIGN(intr_buff[8]);
@@ -64,8 +112,11 @@
 	{"D-Link DSB-650TX", 0x2001, 0x4001, NULL},
 	{"D-Link DSB-650TX", 0x2001, 0x4002, NULL},
 	{"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, NULL},
+	{"D-Link DU-E10", 0x07b8, 0xabc1, NULL},
+	{"D-Link DU-E100", 0x07b8, 0x4002, 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},
@@ -75,23 +126,120 @@
 };
 
 
-#define pegasus_get_registers(dev, indx, size, data)\
-	usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ);
-#define pegasus_set_registers(dev, indx, size, data)\
-	usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ);
-#define pegasus_set_register(dev, indx, value)	\
-	{ __u8	data = value;			\
-	usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, data, indx, &data, 1, HZ);}
+
+static void pegasus_ctrl_end( urb_t *urb )
+{
+	if ( urb->status )
+		warn("ctrl_urb end status %d", urb->status);
+}
+
+
+static int pegasus_ctrl_timeout( urb_t *ctrl_urb )
+{
+	int	timeout=0;
+	
+	while ( ctrl_urb->status == -EINPROGRESS ) {
+		if ( timeout++ < PEGASUS_CTRL_TIMEOUT ) {
+			udelay(100);
+			continue;
+		}
+		err("ctrl urb busy %d", ctrl_urb->status);
+		usb_unlink_urb( ctrl_urb );
+		return	ctrl_urb->status;
+	}
+	return	0;
+}
+
+
+static int pegasus_get_registers( struct pegasus *pegasus, __u16 indx, __u16 size, void *data )
+{
+	int	ret;
+
+
+	spin_lock( &pegasus->ctrl_lock );
+	pegasus->dr.requesttype = 0xc0;
+	pegasus->dr.request = 0xf0;
+	pegasus->dr.value = 0x0;
+	pegasus->dr.index = indx;
+	pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = size;
+
+	FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,
+			  usb_rcvctrlpipe(pegasus->usb,0), (char *)&pegasus->dr,
+			  data, size, pegasus_ctrl_end, pegasus );
+
+	if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) 
+		err("BAD CTRLs %d", ret);
+	else
+		ret = pegasus_ctrl_timeout( &pegasus->ctrl_urb );
+
+	spin_unlock( &pegasus->ctrl_lock );
+
+	return	ret;
+}
+
+
+static int pegasus_set_registers( struct pegasus *pegasus, __u16 indx, __u16 size, void *data )
+{
+	int	ret;
+
+
+	spin_lock( &pegasus->ctrl_lock );
+	pegasus->dr.requesttype = 0x40;
+	pegasus->dr.request = 0xf1;
+	pegasus->dr.value = 0x0;
+	pegasus->dr.index = indx;
+	pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = size;
+
+	FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,
+			  usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr,
+			  data, size, pegasus_ctrl_end, pegasus );
+
+	if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) )
+		err("BAD CTRL %d", ret);
+	else
+		ret = pegasus_ctrl_timeout( &pegasus->ctrl_urb );
+
+	spin_unlock( &pegasus->ctrl_lock );
+
+	return	ret;
+}
 
 
-static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata)
+static int pegasus_set_register( struct pegasus *pegasus, __u16 indx,__u8 data )
+{
+	int	ret;
+
+
+	spin_lock( &pegasus->ctrl_lock );
+	pegasus->dr.requesttype = 0x40;
+	pegasus->dr.request = 0xf1;
+	pegasus->dr.value = data;
+	pegasus->dr.index = indx;
+	pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = 1;
+
+	FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,
+			  usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr,
+			  &data, 1, pegasus_ctrl_end, pegasus );
+			  
+	if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) )
+		err("BAD CTRL %d", ret);
+	else
+		ret = pegasus_ctrl_timeout( &pegasus->ctrl_urb );
+
+	spin_unlock( &pegasus->ctrl_lock );
+
+	return	ret;
+}
+
+
+static int pegasus_read_phy_word(struct pegasus *pegasus, __u8 index, __u16 *regdata)
 {
 	int i;
 	__u8 data[4] = { 1, 0, 0, 0x40 + index };
 
-	pegasus_set_registers(dev, 0x25, 4, data);
+	pegasus_set_registers(pegasus, PhyAddr, 4, data);
 	for (i = 0; i < 100; i++) {
-		pegasus_get_registers(dev, 0x26, 3, data);
+		pegasus_get_registers(pegasus, PhyData, 3, data);
 		if (data[2] & 0x80) {
 			*regdata = *(__u16 *)(data);
 			return 0;
@@ -104,14 +252,14 @@
 }
 
 
-static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata)
+static int pegasus_write_phy_word(struct pegasus *pegasus, __u8 index, __u16 regdata)
 {
 	int i;
 	__u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index };
 
-	pegasus_set_registers(dev, 0x25, 4, data);
+	pegasus_set_registers(pegasus, PhyAddr, 4, data);
 	for (i = 0; i < 100; i++) {
-		pegasus_get_registers(dev, 0x28, 1, data);
+		pegasus_get_registers(pegasus, PhyCtrl, 1, data);
 		if (data[0] & 0x80)
 			return 0;
 		udelay(100);
@@ -122,51 +270,51 @@
 }
 
 
-static int pegasus_rw_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata, __u8 direction)
+static int pegasus_rw_eprom_word(struct pegasus *pegasus, __u8 index, __u16 *retdata, __u8 direction)
 {
 	int i;
 	__u8 data[4] = { index, 0, 0, direction };
 
-	pegasus_set_registers(dev, 0x20, 4, data);
+	pegasus_set_registers(pegasus, EpromOffset, 4, data);
 	for (i = 0; i < 100; i++) {
-		pegasus_get_registers(dev, 0x23, 1, data);
+		pegasus_get_registers(pegasus, EpromCtrl, 1, data);
 		if (data[0] & 4) {
-			pegasus_get_registers(dev, 0x21, 2, data);
+			pegasus_get_registers(pegasus, EpromData, 2, data);
 			*retdata = *(__u16 *)data;
 			return 0;
 		}
 	}
 
-	warn("pegasus_rw_srom_word() failed");
+	warn("pegasus_rw_eprom_word() failed");
 	return 1;
 }
 
 
-static int pegasus_get_node_id(struct usb_device *dev, __u8 *id)
+static int pegasus_get_node_id(struct pegasus *pegasus, __u8 *id)
 {
 	int i;
 	for (i = 0; i < 3; i++)
-		if (pegasus_rw_srom_word(dev,i,(__u16 *)&id[i * 2],SROM_READ))
+		if (pegasus_rw_eprom_word(pegasus,i,(__u16 *)&id[i*2],SROM_READ))
 			return 1;
 	return 0;
 }
 
 
-static int pegasus_reset_mac(struct usb_device *dev)
+static int pegasus_reset_mac(struct pegasus *pegasus)
 {
 	__u8 data = 0x8;
 	int i;
 
-	pegasus_set_register(dev, 1, data);
+	pegasus_set_register(pegasus, EthCtrl1, data);
 	for (i = 0; i < 100; i++) {
-		pegasus_get_registers(dev, 1, 1, &data);
+		pegasus_get_registers(pegasus, EthCtrl1, 1, &data);
 		if (~data & 0x08) {
 			if (loopback & 1) 
 				return 0;
 			if (loopback & 2) 
-				pegasus_write_phy_word(dev, 0, 0x4000);
-			pegasus_set_register(dev, 0x7e, 0x24);
-			pegasus_set_register(dev, 0x7e, 0x27);
+				pegasus_write_phy_word(pegasus, 0, 0x4000);
+			pegasus_set_register(pegasus, Gpio0, 0x24);
+			pegasus_set_register(pegasus, Gpio0, 0x27);
 			return 0;
 		}
 	}
@@ -180,22 +328,22 @@
 	__u16 partmedia, temp;
 	__u8 node_id[6];
 	__u8 data[4];
+	struct pegasus *pegasus = dev->priv;
 
-	if (pegasus_get_node_id(usb, node_id)) 
+	if (pegasus_get_node_id(pegasus, node_id)) 
 		return 1;
 
-	pegasus_set_registers(usb, 0x10, 6, node_id);
+	pegasus_set_registers(pegasus, EthID, 6, node_id);
 	memcpy(dev->dev_addr, node_id, 6);
-	if (pegasus_read_phy_word(usb, 1, &temp)) 
+	if (pegasus_read_phy_word(pegasus, 1, &temp)) 
 		return 2;
 
 	if ((~temp & 4) && !loopback) {
-		warn("%s: link NOT established (0x%x), check the cable.",
+		warn("%s: link NOT established (0x%x) - check the cable.",
 			dev->name, temp);
-		/* return 3; FIXME */
 	}
 
-	if (pegasus_read_phy_word(usb, 5, &partmedia))
+	if (pegasus_read_phy_word(pegasus, 5, &partmedia))
 		return 4;
 
 	if ((partmedia & 0x1f) != 1) {
@@ -207,7 +355,7 @@
 	data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0);
 	data[2] = (loopback & 1) ? 0x09 : 0x01;
 
-	pegasus_set_registers(usb, 0, 3, data);
+	pegasus_set_registers(pegasus, EthCtrl0, 3, data);
 
 	return 0;
 }
@@ -223,7 +371,7 @@
 	__u16 pkt_len;
 
 	if (urb->status) {
-		info("%s: RX status %d", net->name, urb->status);
+		dbg("%s: RX status %d", net->name, urb->status);
 		goto goon;
 	}
 
@@ -278,24 +426,26 @@
 {
 	struct pegasus *pegasus = urb->context;
 
-	spin_lock(&pegasus->pegasus_lock);
 
 	if (urb->status)
 		info("%s: TX status %d", pegasus->net->name, urb->status);
+#if 1	/* Should be fixed */
+	if (urb->status == -ETIMEDOUT)
+		pegasus_reset_mac(pegasus);
+#endif
 	netif_wake_queue(pegasus->net);
-
-	spin_unlock(&pegasus->pegasus_lock);
 }
 
 static void pegasus_tx_timeout(struct net_device *net)
 {
 	struct pegasus *pegasus = net->priv;
 
-	warn("%s: Tx timed out. Reseting...", net->name);
+	
 	usb_unlink_urb(&pegasus->tx_urb);
+	warn("%s: Tx timed out. Reseting...", net->name);
+	pegasus_reset_mac( pegasus );
 	pegasus->stats.tx_errors++;
 	net->trans_start = jiffies;
-	pegasus->flags |= PEGASUS_RESET;
 
 	netif_wake_queue(net);
 }
@@ -369,9 +519,14 @@
 
 	netif_stop_queue(net);
 
-	usb_unlink_urb(&pegasus->rx_urb);
-	usb_unlink_urb(&pegasus->tx_urb);
-	usb_unlink_urb(&pegasus->intr_urb);
+	if ( pegasus->ctrl_urb.status == -EINPROGRESS )
+		usb_unlink_urb(&pegasus->ctrl_urb);
+	if ( pegasus->rx_urb.status == -EINPROGRESS )
+		usb_unlink_urb(&pegasus->rx_urb);
+	if ( pegasus->tx_urb.status == -EINPROGRESS )	
+		usb_unlink_urb(&pegasus->tx_urb);
+	if ( pegasus->intr_urb.status == -EINPROGRESS )
+		usb_unlink_urb(&pegasus->intr_urb);
 
 	MOD_DEC_USE_COUNT;
 
@@ -388,12 +543,12 @@
 		case SIOCDEVPRIVATE:
 			data[0] = 1;
 		case SIOCDEVPRIVATE+1:
-			pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]);
+			pegasus_read_phy_word(pegasus, data[1] & 0x1f, &data[3]);
 			return 0;
 		case SIOCDEVPRIVATE+2:
 			if (!capable(CAP_NET_ADMIN))
 				return -EPERM;
-			pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]);
+			pegasus_write_phy_word(pegasus, data[1] & 0x1f, data[2]);
 			return 0;
 		default:
 			return -EOPNOTSUPP;
@@ -404,19 +559,23 @@
 static void pegasus_set_rx_mode(struct net_device *net)
 {
 	struct pegasus *pegasus = net->priv;
+	__u8	tmp;
 
 	netif_stop_queue(net);
 
 	if (net->flags & IFF_PROMISC) {
 		info("%s: Promiscuous mode enabled", net->name);
-/*		pegasus_set_register(pegasus->usb, 2, 0x04); FIXME */
+		pegasus_get_registers(pegasus, EthCtrl2, 1, &tmp);
+		pegasus_set_register(pegasus, EthCtrl2, tmp | 4);
 	} else if ((net->mc_count > multicast_filter_limit) ||
 			(net->flags & IFF_ALLMULTI)) {
-		pegasus_set_register(pegasus->usb, 0, 0xfa);
-		pegasus_set_register(pegasus->usb, 2, 0);
+		pegasus_set_register(pegasus, EthCtrl0, 0xfa);
+		pegasus_set_register(pegasus, EthCtrl2, 0);
 		info("%s set allmulti", net->name);
 	} else {
 		info("%s: set Rx mode", net->name);
+		pegasus_get_registers(pegasus, EthCtrl2, 1, &tmp);
+		pegasus_set_register(pegasus, EthCtrl2, tmp & ~4);	
 	}
 
 	netif_wake_queue(net);
@@ -458,12 +617,6 @@
 	}
 	memset(pegasus, 0, sizeof(struct pegasus));
 
-	if (pegasus_reset_mac(dev)) {
-		err("can't reset MAC");
-		kfree(pegasus);
-		return NULL;
-	}
-	
 	net = init_etherdev(0, 0);
 	net->priv = pegasus;
 	net->open = pegasus_open;
@@ -479,6 +632,7 @@
 	pegasus->usb = dev;
 	pegasus->net = net;
 	pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED;
+	pegasus->ctrl_lock = SPIN_LOCK_UNLOCKED;
 
 	FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1),
 			pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk, 
@@ -489,7 +643,12 @@
 	FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3),
 			pegasus->intr_buff, 8, pegasus_irq, pegasus, 500);
 
-
+	if (pegasus_reset_mac(pegasus)) {
+		err("can't reset MAC");
+		kfree(pegasus);
+		return NULL;
+	}
+	
 	printk(KERN_INFO "%s: %s\n", net->name, usb_dev_id[dev_indx].name);
 
 	return pegasus;
@@ -509,10 +668,15 @@
 		dev_close(pegasus->net);
 
 	unregister_netdev(pegasus->net);
-
-	usb_unlink_urb(&pegasus->rx_urb);
-	usb_unlink_urb(&pegasus->tx_urb);
-	usb_unlink_urb(&pegasus->intr_urb);
+	
+	if ( pegasus->ctrl_urb.status == -EINPROGRESS )
+		usb_unlink_urb(&pegasus->ctrl_urb);
+	if ( pegasus->rx_urb.status == -EINPROGRESS )
+		usb_unlink_urb(&pegasus->rx_urb);
+	if ( pegasus->tx_urb.status == -EINPROGRESS )
+		usb_unlink_urb(&pegasus->tx_urb);
+	if ( pegasus->intr_urb.status == -EINPROGRESS )
+		usb_unlink_urb(&pegasus->intr_urb);
 
 	kfree(pegasus);
 }

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