summaryrefslogtreecommitdiffstats
path: root/drivers/usb/pegasus.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/pegasus.c')
-rw-r--r--drivers/usb/pegasus.c269
1 files changed, 212 insertions, 57 deletions
diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c
index 80b82bba2..8c588f79e 100644
--- a/drivers/usb/pegasus.c
+++ b/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.14 2000/06/09 (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 @@ static const char *version = __FILE__ ": v0.3.14 2000/06/09 (C) 1999-2000 Petko
#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,7 +112,7 @@ static struct usb_eth_dev usb_dev_id[] = {
{"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-10", 0x07b8, 0xabc1, 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},
@@ -78,23 +126,120 @@ static struct usb_eth_dev usb_dev_id[] = {
};
-#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_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata)
+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_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;
@@ -107,14 +252,14 @@ static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regd
}
-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);
@@ -125,51 +270,51 @@ static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regd
}
-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;
}
}
@@ -183,22 +328,22 @@ static int pegasus_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;
- 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) {
@@ -210,7 +355,7 @@ static int pegasus_start_net(struct net_device *dev, struct usb_device *usb)
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;
}
@@ -281,24 +426,26 @@ static void pegasus_write_bulk(struct urb *urb)
{
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);
}
@@ -372,6 +519,8 @@ static int pegasus_close(struct net_device *net)
netif_stop_queue(net);
+ 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 )
@@ -394,12 +543,12 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
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;
@@ -410,19 +559,23 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
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);
@@ -464,12 +617,6 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
}
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;
@@ -485,6 +632,7 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
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,
@@ -495,7 +643,12 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
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;
@@ -515,7 +668,9 @@ static void pegasus_disconnect(struct usb_device *dev, void *ptr)
dev_close(pegasus->net);
unregister_netdev(pegasus->net);
-
+
+ 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 )