Commit 964f3281 authored by Petko Manolov's avatar Petko Manolov Committed by Greg Kroah-Hartman

[PATCH] various pegasus and rtl8150 fixes and improvements

USB pegasus and rtl8150 fixes and improvements

pegasus:
	- using preallocated skb thus avoiding memcpy in the receive path;
	- tasklet used to handle failed skb allocations and Rx urb submission;
	- Lindent run on the result.

rtl8150:
	- better tasklet handling and a few races fixed;
	- introducing new flag for Rx urb resubmission;
	- GFP_KERNEL to GFP_ATOMIC flag change in Tx path.
parent 59985585
......@@ -41,7 +41,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/init.h>
......@@ -59,7 +58,7 @@
/*
* Version Information
*/
#define DRIVER_VERSION "v0.5.2 (2002/03/21)"
#define DRIVER_VERSION "v0.5.4 (2002/04/11)"
#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
......@@ -85,105 +84,103 @@ static struct usb_device_id pegasus_ids[] = {
{match_flags: USB_DEVICE_ID_MATCH_DEVICE, idVendor:vid, idProduct:pid},
#include "pegasus.h"
#undef PEGASUS_DEV
{ }
{}
};
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_PARM(loopback, "i");
MODULE_PARM(mii_mode, "i");
MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)");
MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0");
MODULE_DEVICE_TABLE (usb, pegasus_ids);
MODULE_DEVICE_TABLE(usb, pegasus_ids);
static int update_eth_regs_async( pegasus_t * );
static int update_eth_regs_async(pegasus_t *);
/* Aargh!!! I _really_ hate such tweaks */
static void ctrl_callback( struct urb *urb )
static void ctrl_callback(struct urb *urb)
{
pegasus_t *pegasus = urb->context;
pegasus_t *pegasus = urb->context;
if ( !pegasus )
if (!pegasus)
return;
switch ( urb->status ) {
case 0:
if ( pegasus->flags & ETH_REGS_CHANGE ) {
pegasus->flags &= ~ETH_REGS_CHANGE;
pegasus->flags |= ETH_REGS_CHANGED;
update_eth_regs_async( pegasus );
return;
}
break;
case -EINPROGRESS:
switch (urb->status) {
case 0:
if (pegasus->flags & ETH_REGS_CHANGE) {
pegasus->flags &= ~ETH_REGS_CHANGE;
pegasus->flags |= ETH_REGS_CHANGED;
update_eth_regs_async(pegasus);
return;
case -ENOENT:
break;
default:
warn("%s: status %d", __FUNCTION__, urb->status);
}
break;
case -EINPROGRESS:
return;
case -ENOENT:
break;
default:
warn("%s: status %d", __FUNCTION__, urb->status);
}
pegasus->flags &= ~ETH_REGS_CHANGED;
wake_up(&pegasus->ctrl_wait );
wake_up(&pegasus->ctrl_wait);
}
static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
void *data)
{
int ret;
int ret;
unsigned char *buffer;
DECLARE_WAITQUEUE(wait, current);
buffer = kmalloc(size,GFP_KERNEL);
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer) {
err("unable to allocate memory for configuration descriptors");
return 0;
}
memcpy(buffer,data,size);
memcpy(buffer, data, size);
add_wait_queue(&pegasus->ctrl_wait, &wait);
set_current_state(TASK_UNINTERRUPTIBLE);
while ( pegasus->flags & ETH_REGS_CHANGED )
while (pegasus->flags & ETH_REGS_CHANGED)
schedule();
remove_wait_queue(&pegasus->ctrl_wait, &wait);
set_current_state(TASK_RUNNING);
pegasus->dr.bRequestType = PEGASUS_REQT_READ;
pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS;
pegasus->dr.wValue = cpu_to_le16 (0);
pegasus->dr.wValue = cpu_to_le16(0);
pegasus->dr.wIndex = cpu_to_le16p(&indx);
pegasus->dr.wLength = cpu_to_le16p(&size);
pegasus->ctrl_urb->transfer_buffer_length = size;
FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb,
usb_rcvctrlpipe(pegasus->usb,0),
(char *)&pegasus->dr,
buffer, size, ctrl_callback, pegasus );
FILL_CONTROL_URB(pegasus->ctrl_urb, pegasus->usb,
usb_rcvctrlpipe(pegasus->usb, 0),
(char *) &pegasus->dr,
buffer, size, ctrl_callback, pegasus);
add_wait_queue( &pegasus->ctrl_wait, &wait );
set_current_state( TASK_UNINTERRUPTIBLE );
add_wait_queue(&pegasus->ctrl_wait, &wait);
set_current_state(TASK_UNINTERRUPTIBLE);
/* using ATOMIC, we'd never wake up if we slept */
if ( (ret = usb_submit_urb( pegasus->ctrl_urb, GFP_ATOMIC )) ) {
if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
err("%s: BAD CTRLs %d", __FUNCTION__, ret);
goto out;
}
schedule();
out:
remove_wait_queue( &pegasus->ctrl_wait, &wait );
memcpy(data,buffer,size);
remove_wait_queue(&pegasus->ctrl_wait, &wait);
memcpy(data, buffer, size);
kfree(buffer);
return ret;
}
static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
void *data)
{
int ret;
int ret;
unsigned char *buffer;
DECLARE_WAITQUEUE(wait, current);
......@@ -196,47 +193,46 @@ static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
add_wait_queue(&pegasus->ctrl_wait, &wait);
set_current_state(TASK_UNINTERRUPTIBLE);
while ( pegasus->flags & ETH_REGS_CHANGED )
while (pegasus->flags & ETH_REGS_CHANGED)
schedule();
remove_wait_queue(&pegasus->ctrl_wait, &wait);
set_current_state(TASK_RUNNING);
pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS;
pegasus->dr.wValue = cpu_to_le16 (0);
pegasus->dr.wIndex = cpu_to_le16p( &indx );
pegasus->dr.wLength = cpu_to_le16p( &size );
pegasus->dr.wValue = cpu_to_le16(0);
pegasus->dr.wIndex = cpu_to_le16p(&indx);
pegasus->dr.wLength = cpu_to_le16p(&size);
pegasus->ctrl_urb->transfer_buffer_length = size;
FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb,
usb_sndctrlpipe(pegasus->usb,0),
(char *)&pegasus->dr,
buffer, size, ctrl_callback, pegasus );
add_wait_queue( &pegasus->ctrl_wait, &wait );
set_current_state( TASK_UNINTERRUPTIBLE );
FILL_CONTROL_URB(pegasus->ctrl_urb, pegasus->usb,
usb_sndctrlpipe(pegasus->usb, 0),
(char *) &pegasus->dr,
buffer, size, ctrl_callback, pegasus);
if ( (ret = usb_submit_urb( pegasus->ctrl_urb, GFP_ATOMIC )) ) {
add_wait_queue(&pegasus->ctrl_wait, &wait);
set_current_state(TASK_UNINTERRUPTIBLE);
if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
err("%s: BAD CTRL %d", __FUNCTION__, ret);
goto out;
}
schedule();
out:
remove_wait_queue( &pegasus->ctrl_wait, &wait );
remove_wait_queue(&pegasus->ctrl_wait, &wait);
kfree(buffer);
return ret;
}
static int set_register( pegasus_t *pegasus, __u16 indx, __u8 data )
static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data)
{
int ret;
int ret;
unsigned char *buffer;
__u16 dat = data;
DECLARE_WAITQUEUE(wait, current);
buffer = kmalloc(1, GFP_KERNEL);
if (!buffer) {
err("unable to allocate memory for configuration descriptors");
......@@ -246,129 +242,126 @@ static int set_register( pegasus_t *pegasus, __u16 indx, __u8 data )
add_wait_queue(&pegasus->ctrl_wait, &wait);
set_current_state(TASK_UNINTERRUPTIBLE);
while ( pegasus->flags & ETH_REGS_CHANGED )
while (pegasus->flags & ETH_REGS_CHANGED)
schedule();
remove_wait_queue(&pegasus->ctrl_wait, &wait);
set_current_state(TASK_RUNNING);
pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
pegasus->dr.bRequest = PEGASUS_REQ_SET_REG;
pegasus->dr.wValue = cpu_to_le16p( &dat);
pegasus->dr.wIndex = cpu_to_le16p( &indx );
pegasus->dr.wLength = cpu_to_le16( 1 );
pegasus->dr.wValue = cpu_to_le16p(&dat);
pegasus->dr.wIndex = cpu_to_le16p(&indx);
pegasus->dr.wLength = cpu_to_le16(1);
pegasus->ctrl_urb->transfer_buffer_length = 1;
FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb,
usb_sndctrlpipe(pegasus->usb,0),
(char *)&pegasus->dr,
buffer, 1, ctrl_callback, pegasus );
FILL_CONTROL_URB(pegasus->ctrl_urb, pegasus->usb,
usb_sndctrlpipe(pegasus->usb, 0),
(char *) &pegasus->dr,
buffer, 1, ctrl_callback, pegasus);
add_wait_queue( &pegasus->ctrl_wait, &wait );
set_current_state( TASK_UNINTERRUPTIBLE );
add_wait_queue(&pegasus->ctrl_wait, &wait);
set_current_state(TASK_UNINTERRUPTIBLE);
if ( (ret = usb_submit_urb( pegasus->ctrl_urb, GFP_ATOMIC )) ) {
if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
err("%s: BAD CTRL %d", __FUNCTION__, ret);
goto out;
}
schedule();
out:
remove_wait_queue( &pegasus->ctrl_wait, &wait );
remove_wait_queue(&pegasus->ctrl_wait, &wait);
kfree(buffer);
return ret;
}
static int update_eth_regs_async( pegasus_t *pegasus )
static int update_eth_regs_async(pegasus_t * pegasus)
{
int ret;
int ret;
pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS;
pegasus->dr.wValue = 0;
pegasus->dr.wIndex = cpu_to_le16(EthCtrl0);
pegasus->dr.wIndex = cpu_to_le16(EthCtrl0);
pegasus->dr.wLength = cpu_to_le16(3);
pegasus->ctrl_urb->transfer_buffer_length = 3;
FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb,
usb_sndctrlpipe(pegasus->usb,0),
(char *)&pegasus->dr,
pegasus->eth_regs, 3, ctrl_callback, pegasus );
FILL_CONTROL_URB(pegasus->ctrl_urb, pegasus->usb,
usb_sndctrlpipe(pegasus->usb, 0),
(char *) &pegasus->dr,
pegasus->eth_regs, 3, ctrl_callback, pegasus);
if ( (ret = usb_submit_urb( pegasus->ctrl_urb, GFP_ATOMIC )) )
err("%s: BAD CTRL %d, flgs %x",__FUNCTION__,ret,pegasus->flags);
if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC)))
err("%s: BAD CTRL %d, flgs %x", __FUNCTION__, ret,
pegasus->flags);
return ret;
return ret;
}
static int read_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd )
static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd)
{
int i;
__u8 data[4] = { phy, 0, 0, indx };
__u16 regdi;
set_register( pegasus, PhyCtrl, 0 );
set_registers( pegasus, PhyAddr, sizeof(data), data );
set_register( pegasus, PhyCtrl, (indx | PHY_READ) );
int i;
__u8 data[4] = { phy, 0, 0, indx };
__u16 regdi;
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 )
if (data[0] & PHY_DONE)
break;
}
if ( i < REG_TIMEOUT ) {
get_registers( pegasus, PhyData, 2, &regdi );
if (i < REG_TIMEOUT) {
get_registers(pegasus, PhyData, 2, &regdi);
*regd = le16_to_cpu(regdi);
return 0;
return 0;
}
warn("%s: failed", __FUNCTION__);
return 1;
}
static int write_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd )
static int write_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 regd)
{
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) );
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 )
if (data[0] & PHY_DONE)
break;
}
if ( i < REG_TIMEOUT )
return 0;
if (i < REG_TIMEOUT)
return 0;
warn("%s: failed", __FUNCTION__);
return 1;
}
static int read_eprom_word( pegasus_t *pegasus, __u8 index, __u16 *retdata )
static int read_eprom_word(pegasus_t * pegasus, __u8 index, __u16 * retdata)
{
int i;
int i;
__u8 tmp;
__u16 retdatai;
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 )
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;
}
if ( i < REG_TIMEOUT ) {
get_registers( pegasus, EpromData, 2, &retdatai );
*retdata = le16_to_cpu (retdatai);
return 0;
if (i < REG_TIMEOUT) {
get_registers(pegasus, EpromData, 2, &retdatai);
*retdata = le16_to_cpu(retdatai);
return 0;
}
warn("%s: failed", __FUNCTION__);
......@@ -376,355 +369,372 @@ static int read_eprom_word( pegasus_t *pegasus, __u8 index, __u16 *retdata )
}
#ifdef PEGASUS_WRITE_EEPROM
static inline void enable_eprom_write( pegasus_t *pegasus )
static inline void enable_eprom_write(pegasus_t * pegasus)
{
__u8 tmp;
__u8 tmp;
get_registers( pegasus, EthCtrl2, 1, &tmp );
set_register( pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE );
get_registers(pegasus, EthCtrl2, 1, &tmp);
set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE);
}
static inline void disable_eprom_write( pegasus_t *pegasus )
static inline void disable_eprom_write(pegasus_t * pegasus)
{
__u8 tmp;
__u8 tmp;
get_registers( pegasus, EthCtrl2, 1, &tmp );
set_register( pegasus, EpromCtrl, 0 );
set_register( pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE );
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 )
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 )
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;
disable_eprom_write(pegasus);
if (i < REG_TIMEOUT)
return 0;
warn("%s: failed", __FUNCTION__);
return -1;
return -1;
}
#endif /* PEGASUS_WRITE_EEPROM */
#endif /* PEGASUS_WRITE_EEPROM */
static inline void get_node_id( pegasus_t *pegasus, __u8 *id )
static inline void get_node_id(pegasus_t * pegasus, __u8 * id)
{
int i;
int i;
__u16 w16;
for (i = 0; i < 3; i++) {
read_eprom_word( pegasus, i, &w16);
((__u16 *) id)[i] = cpu_to_le16p (&w16);
read_eprom_word(pegasus, i, &w16);
((__u16 *) id)[i] = cpu_to_le16p(&w16);
}
}
static void set_ethernet_addr( pegasus_t *pegasus )
static void set_ethernet_addr(pegasus_t * pegasus)
{
__u8 node_id[6];
__u8 node_id[6];
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) );
set_registers(pegasus, EthID, sizeof(node_id), node_id);
memcpy(pegasus->net->dev_addr, node_id, sizeof(node_id));
}
static inline int reset_mac( pegasus_t *pegasus )
static inline int reset_mac(pegasus_t * pegasus)
{
__u8 data = 0x8;
int i;
__u8 data = 0x8;
int i;
set_register(pegasus, EthCtrl1, data);
for (i = 0; i < REG_TIMEOUT; i++) {
get_registers(pegasus, EthCtrl1, 1, &data);
if (~data & 0x08) {
if (loopback & 1)
if (loopback & 1)
break;
if ( mii_mode && (pegasus->features & HAS_HOME_PNA) )
set_register( pegasus, Gpio1, 0x34 );
if (mii_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 );
set_register(pegasus, Gpio1, 0x26);
set_register(pegasus, Gpio0, pegasus->features);
set_register(pegasus, Gpio0, DEFAULT_GPIO_SET);
break;
}
}
if ( i == REG_TIMEOUT )
if (i == REG_TIMEOUT)
return 1;
if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
__u16 auxmode;
read_mii_word(pegasus, 1, MII_TPISTATUS, &auxmode);
write_mii_word(pegasus, 1, MII_TPISTATUS, auxmode | 4);
set_register(pegasus, Gpio0, 0x24);
set_register(pegasus, Gpio0, 0x26);
}
if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) {
__u16 auxmode;
__u16 auxmode;
read_mii_word(pegasus, 3, 0x1b, &auxmode);
write_mii_word(pegasus, 3, 0x1b, auxmode | 4);
}
return 0;
return 0;
}
static int enable_net_traffic( 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 data[4];
__u16 linkpart, bmsr;
__u8 data[4];
pegasus_t *pegasus = dev->priv;
read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr);
read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr);
if ( !(bmsr & 4) && !loopback )
warn( "%s: link NOT established (%04x) - check the cable.",
dev->name, bmsr );
if ( read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart) )
if (!(bmsr & BMSR_LSTATUS) && !loopback)
warn("%s: link NOT established (%04x) - check the cable.",
dev->name, bmsr);
if (read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart))
return 2;
if ( !(linkpart & 1) )
warn( "link partner stat %x", linkpart );
if (!(linkpart & 1))
warn("link partner stat %x", linkpart);
data[0] = 0xc9;
data[1] = 0;
if ( linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL) )
data[1] |= 0x20; /* set full duplex */
if ( linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF) )
data[1] |= 0x10; /* set 100 Mbps */
if ( mii_mode )
if (linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL))
data[1] |= 0x20; /* set full duplex */
if (linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF))
data[1] |= 0x10; /* set 100 Mbps */
if (mii_mode)
data[1] = 0;
data[2] = (loopback & 1) ? 0x09 : 0x01;
memcpy( pegasus->eth_regs, data, sizeof(data) );
memcpy(pegasus->eth_regs, data, sizeof(data));
set_registers(pegasus, EthCtrl0, 3, data);
set_registers( pegasus, EthCtrl0, 3, data );
if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
u16 auxmode;
read_mii_word(pegasus, 0, 0x1b, &auxmode);
write_mii_word(pegasus, 0, 0x1b, auxmode | 4);
}
return 0;
}
static void read_bulk_callback( struct urb *urb )
static void read_bulk_callback(struct urb *urb)
{
pegasus_t *pegasus = urb->context;
struct net_device *net;
int count = urb->actual_length, res;
int count = urb->actual_length;
int rx_status;
struct sk_buff *skb;
struct sk_buff *skb;
__u16 pkt_len;
if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) )
if (!pegasus || !(pegasus->flags & PEGASUS_RUNNING))
return;
net = pegasus->net;
if ( !netif_device_present(net) )
if (!netif_device_present(net))
return;
if ( pegasus->flags & PEGASUS_RX_BUSY ) {
pegasus->stats.rx_errors++;
dbg("pegasus Rx busy");
return;
}
pegasus->flags |= PEGASUS_RX_BUSY;
switch ( urb->status ) {
case 0:
break;
case -ETIMEDOUT:
dbg( "reset MAC" );
pegasus->flags &= ~PEGASUS_RX_BUSY;
break;
default:
dbg( "%s: RX status %d", net->name, urb->status );
goto goon;
switch (urb->status) {
case 0:
break;
case -ETIMEDOUT:
dbg("reset MAC");
pegasus->flags &= ~PEGASUS_RX_BUSY;
break;
default:
dbg("%s: RX status %d", net->name, urb->status);
goto goon;
}
if ( !count )
if (!count)
goto goon;
rx_status = le32_to_cpu(*(int *)(pegasus->rx_buff + count - 4));
if ( rx_status & 0x000e0000 ) {
rx_status = le32_to_cpu(*(int *)(urb->transfer_buffer + count - 4));
if (rx_status & 0x000e0000) {
dbg("%s: RX packet error %x", net->name, rx_status & 0xe0000);
pegasus->stats.rx_errors++;
if ( rx_status & 0x060000 )
if (rx_status & 0x060000)
pegasus->stats.rx_length_errors++;
if ( rx_status & 0x080000 )
if (rx_status & 0x080000)
pegasus->stats.rx_crc_errors++;
if ( rx_status & 0x100000 )
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)) )
goto goon;
tasklet_schedule(&pegasus->rx_tl);
if (!pegasus->rx_skb)
return;
skb_put(pegasus->rx_skb, pkt_len);
pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net);
netif_rx(pegasus->rx_skb);
if (!(skb = dev_alloc_skb(PEGASUS_MTU + 2))) {
pegasus->rx_skb = NULL;
return;
}
skb->dev = net;
skb_reserve(skb, 2);
eth_copy_and_sum(skb, pegasus->rx_buff, pkt_len, 0);
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, net);
netif_rx(skb);
pegasus->rx_skb = skb;
pegasus->stats.rx_packets++;
pegasus->stats.rx_bytes += pkt_len;
goon:
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, GFP_ATOMIC)) )
warn("%s: failed submint rx_urb %d", __FUNCTION__, res);
pegasus->flags &= ~PEGASUS_RX_BUSY;
FILL_BULK_URB(pegasus->rx_urb, pegasus->usb,
usb_rcvbulkpipe(pegasus->usb, 1),
pegasus->rx_skb->data, PEGASUS_MTU + 8,
read_bulk_callback, pegasus);
if (usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC))
pegasus->flags |= PEGASUS_RX_URB_FAIL;
else
pegasus->flags &= ~PEGASUS_RX_URB_FAIL;
}
static void rx_fixup(unsigned long data)
{
pegasus_t *pegasus;
static void write_bulk_callback( struct urb *urb )
pegasus = (pegasus_t *)data;
if (pegasus->flags & PEGASUS_RX_URB_FAIL) {
goto try_again;
}
if (pegasus->rx_skb)
return;
if (!(pegasus->rx_skb = dev_alloc_skb(PEGASUS_MTU + 2))) {
tasklet_schedule(&pegasus->rx_tl);
return;
}
FILL_BULK_URB(pegasus->rx_urb, pegasus->usb,
usb_rcvbulkpipe(pegasus->usb, 1),
pegasus->rx_skb->data, PEGASUS_MTU + 8,
read_bulk_callback, pegasus);
try_again:
if (usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC)) {
pegasus->flags |= PEGASUS_RX_URB_FAIL;
tasklet_schedule(&pegasus->rx_tl);
} else {
pegasus->flags &= ~PEGASUS_RX_URB_FAIL;
}
}
static void write_bulk_callback(struct urb *urb)
{
pegasus_t *pegasus = urb->context;
if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) )
if (!pegasus || !(pegasus->flags & PEGASUS_RUNNING))
return;
if ( !netif_device_present(pegasus->net) )
if (!netif_device_present(pegasus->net))
return;
if ( urb->status )
if (urb->status)
info("%s: TX status %d", pegasus->net->name, urb->status);
pegasus->net->trans_start = jiffies;
netif_wake_queue( pegasus->net );
netif_wake_queue(pegasus->net);
}
#ifdef PEGASUS_USE_INTR
static void intr_callback( struct urb *urb )
static void intr_callback(struct urb *urb)
{
pegasus_t *pegasus = urb->context;
struct net_device *net;
__u8 *d;
__u8 *d;
if ( !pegasus )
if (!pegasus)
return;
switch ( urb->status ) {
case 0:
break;
case -ENOENT:
return;
default:
info("intr status %d", urb->status);
switch (urb->status) {
case 0:
break;
case -ENOENT:
return;
default:
info("intr status %d", urb->status);
}
d = urb->transfer_buffer;
net = pegasus->net;
if ( d[0] & 0xfc ) {
if (d[0] & 0xfc) {
pegasus->stats.tx_errors++;
if ( d[0] & TX_UNDERRUN )
if (d[0] & TX_UNDERRUN)
pegasus->stats.tx_fifo_errors++;
if ( d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT) )
if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT))
pegasus->stats.tx_aborted_errors++;
if ( d[0] & LATE_COL )
if (d[0] & LATE_COL)
pegasus->stats.tx_window_errors++;
if ( d[0] & (NO_CARRIER | LOSS_CARRIER) )
if (d[0] & (NO_CARRIER | LOSS_CARRIER))
pegasus->stats.tx_carrier_errors++;
}
}
#endif
static void pegasus_tx_timeout( struct net_device *net )
static void pegasus_tx_timeout(struct net_device *net)
{
pegasus_t *pegasus = net->priv;
if ( !pegasus )
if (!pegasus)
return;
warn("%s: Tx timed out.", net->name);
pegasus->tx_urb->transfer_flags |= USB_ASYNC_UNLINK;
usb_unlink_urb( pegasus->tx_urb );
usb_unlink_urb(pegasus->tx_urb);
pegasus->stats.tx_errors++;
}
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)
{
pegasus_t *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;
__u16 l16 = skb->len;
netif_stop_queue( net );
((__u16 *)pegasus->tx_buff)[0] = cpu_to_le16( l16 );
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;
netif_stop_queue(net);
((__u16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16);
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, count,
write_bulk_callback, pegasus);
if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) {
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;
net->trans_start = jiffies;
}
dev_kfree_skb(skb);
return 0;
}
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 &((pegasus_t *)dev->priv)->stats;
return &((pegasus_t *) dev->priv)->stats;
}
static inline void disable_net_traffic( pegasus_t *pegasus )
static inline void disable_net_traffic(pegasus_t * pegasus)
{
int tmp=0;
int tmp = 0;
set_registers( pegasus, EthCtrl0, 2, &tmp );
set_registers(pegasus, EthCtrl0, 2, &tmp);
}
static inline void get_interrupt_interval( pegasus_t *pegasus )
static inline void get_interrupt_interval(pegasus_t * pegasus)
{
__u8 data[2];
__u8 data[2];
read_eprom_word( pegasus, 4, (__u16 *)data );
if ( data[1] < 0x80 ) {
info( "intr interval will be changed from %ums to %ums",
data[1], 0x80 );
read_eprom_word(pegasus, 4, (__u16 *) data);
if (data[1] < 0x80) {
info("intr interval will be changed from %ums to %ums",
data[1], 0x80);
data[1] = 0x80;
#ifdef PEGASUS_WRITE_EEPROM
write_eprom_word( pegasus, 4, *(__u16 *)data );
write_eprom_word(pegasus, 4, *(__u16 *) data);
#endif
}
pegasus->intr_interval = data[1];
}
static void set_carrier(struct net_device *net)
{
pegasus_t *pegasus;
short tmp;
pegasus_t *pegasus;
short tmp;
pegasus = net->priv;
read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp);
......@@ -732,33 +742,37 @@ static void set_carrier(struct net_device *net)
netif_carrier_on(net);
else
netif_carrier_off(net);
}
}
static int pegasus_open(struct net_device *net)
{
pegasus_t *pegasus = (pegasus_t *)net->priv;
int res;
pegasus_t *pegasus = (pegasus_t *) net->priv;
int res;
if (!(pegasus->rx_skb = dev_alloc_skb(PEGASUS_MTU + 2)))
return -ENOMEM;
pegasus->rx_skb->dev = net;
skb_reserve(pegasus->rx_skb, 2);
down(&pegasus->sem);
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, GFP_KERNEL)) )
FILL_BULK_URB(pegasus->rx_urb, pegasus->usb,
usb_rcvbulkpipe(pegasus->usb, 1),
pegasus->rx_skb->data, PEGASUS_MTU + 8,
read_bulk_callback, pegasus);
if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL)))
warn("%s: failed rx_urb %d", __FUNCTION__, res);
#ifdef PEGASUS_USE_INTR
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, GFP_KERNEL)) )
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, GFP_KERNEL)))
warn("%s: failed intr_urb %d", __FUNCTION__, res);
#endif
netif_start_queue( net );
netif_start_queue(net);
pegasus->flags |= PEGASUS_RUNNING;
if ( (res = enable_net_traffic(net, pegasus->usb)) ) {
if ((res = enable_net_traffic(net, pegasus->usb))) {
err("can't enable_net_traffic() - %d", res);
res = -EIO;
goto exit;
......@@ -771,122 +785,122 @@ static int pegasus_open(struct net_device *net)
return res;
}
static int pegasus_close( struct net_device *net )
static int pegasus_close(struct net_device *net)
{
pegasus_t *pegasus = net->priv;
pegasus_t *pegasus = net->priv;
down(&pegasus->sem);
pegasus->flags &= ~PEGASUS_RUNNING;
netif_stop_queue( net );
if ( !(pegasus->flags & PEGASUS_UNPLUG) )
disable_net_traffic( pegasus );
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->rx_urb);
usb_unlink_urb(pegasus->tx_urb);
usb_unlink_urb(pegasus->ctrl_urb);
#ifdef PEGASUS_USE_INTR
usb_unlink_urb( pegasus->intr_urb );
usb_unlink_urb(pegasus->intr_urb);
#endif
up(&pegasus->sem);
return 0;
}
static int pegasus_ethtool_ioctl(struct net_device *net, void *uaddr)
{
pegasus_t *pegasus;
int cmd;
pegasus_t *pegasus;
int cmd;
char tmp[128];
pegasus = net->priv;
if (get_user(cmd, (int *)uaddr))
if (get_user(cmd, (int *) uaddr))
return -EFAULT;
switch (cmd) {
case ETHTOOL_GDRVINFO: {
struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
strncpy(info.driver, DRIVER_DESC, ETHTOOL_BUSINFO_LEN);
strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN);
usb_make_path (pegasus->usb, info.bus_info, sizeof info.bus_info);
if (copy_to_user(uaddr, &info, sizeof(info)))
return -EFAULT;
return 0;
}
case ETHTOOL_GSET: {
struct ethtool_cmd ecmd;
short lpa, bmcr;
if (copy_from_user(&ecmd, uaddr, sizeof(ecmd)))
return -EFAULT;
ecmd.supported = (SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half |
SUPPORTED_100baseT_Full |
SUPPORTED_Autoneg |
SUPPORTED_TP |
SUPPORTED_MII);
ecmd.port = PORT_TP;
ecmd.transceiver = XCVR_INTERNAL;
ecmd.phy_address = pegasus->phy;
read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr);
read_mii_word(pegasus, pegasus->phy, MII_LPA, &lpa);
if (bmcr & BMCR_ANENABLE) {
ecmd.autoneg = AUTONEG_ENABLE;
ecmd.speed = lpa & (LPA_100HALF|LPA_100FULL) ?
SPEED_100 : SPEED_10;
if (ecmd.speed == SPEED_100)
ecmd.duplex = lpa & LPA_100FULL ?
DUPLEX_FULL : DUPLEX_HALF;
else
ecmd.duplex = lpa & LPA_10FULL ?
DUPLEX_FULL : DUPLEX_HALF;
} else {
ecmd.autoneg = AUTONEG_DISABLE;
ecmd.speed = bmcr & BMCR_SPEED100 ?
SPEED_100 : SPEED_10;
ecmd.duplex = bmcr & BMCR_FULLDPLX ?
DUPLEX_FULL : DUPLEX_HALF;
case ETHTOOL_GDRVINFO:{
struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
strncpy(info.driver, DRIVER_DESC, ETHTOOL_BUSINFO_LEN);
strncpy(info.version, DRIVER_VERSION,
ETHTOOL_BUSINFO_LEN);
sprintf(tmp, "usb%d:%d", pegasus->usb->bus->busnum,
pegasus->usb->devnum);
strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN);
if (copy_to_user(uaddr, &info, sizeof(info)))
return -EFAULT;
return 0;
}
case ETHTOOL_GSET:{
struct ethtool_cmd ecmd;
short lpa, bmcr;
if (copy_from_user(&ecmd, uaddr, sizeof(ecmd)))
return -EFAULT;
ecmd.supported = (SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half |
SUPPORTED_100baseT_Full |
SUPPORTED_Autoneg |
SUPPORTED_TP | SUPPORTED_MII);
ecmd.port = PORT_TP;
ecmd.transceiver = XCVR_INTERNAL;
ecmd.phy_address = pegasus->phy;
read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr);
read_mii_word(pegasus, pegasus->phy, MII_LPA, &lpa);
if (bmcr & BMCR_ANENABLE) {
ecmd.autoneg = AUTONEG_ENABLE;
ecmd.speed = lpa & (LPA_100HALF | LPA_100FULL) ?
SPEED_100 : SPEED_10;
if (ecmd.speed == SPEED_100)
ecmd.duplex = lpa & LPA_100FULL ?
DUPLEX_FULL : DUPLEX_HALF;
else
ecmd.duplex = lpa & LPA_10FULL ?
DUPLEX_FULL : DUPLEX_HALF;
} else {
ecmd.autoneg = AUTONEG_DISABLE;
ecmd.speed = bmcr & BMCR_SPEED100 ?
SPEED_100 : SPEED_10;
ecmd.duplex = bmcr & BMCR_FULLDPLX ?
DUPLEX_FULL : DUPLEX_HALF;
}
if (copy_to_user(uaddr, &ecmd, sizeof(ecmd)))
return -EFAULT;
return 0;
}
case ETHTOOL_SSET:{
return -EOPNOTSUPP;
}
case ETHTOOL_GLINK:{
struct ethtool_value edata = { ETHTOOL_GLINK };
edata.data = netif_carrier_ok(net);
if (copy_to_user(uaddr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
if (copy_to_user(uaddr, &ecmd, sizeof(ecmd)))
return -EFAULT;
return 0;
}
case ETHTOOL_SSET: {
return -EOPNOTSUPP;
}
case ETHTOOL_GLINK: {
struct ethtool_value edata = {ETHTOOL_GLINK};
edata.data = netif_carrier_ok(net);
if (copy_to_user(uaddr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
default:
return -EOPNOTSUPP;
}
}
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;
pegasus_t *pegasus = net->priv;
int res;
__u16 *data = (__u16 *) & rq->ifr_data;
pegasus_t *pegasus = net->priv;
int res;
down(&pegasus->sem);
switch(cmd) {
switch (cmd) {
case SIOCETHTOOL:
res = pegasus_ethtool_ioctl(net, rq->ifr_data);
break;
case SIOCDEVPRIVATE:
data[0] = pegasus->phy;
case SIOCDEVPRIVATE+1:
read_mii_word(pegasus, data[0], data[1]&0x1f, &data[3]);
case SIOCDEVPRIVATE + 1:
read_mii_word(pegasus, data[0], data[1] & 0x1f, &data[3]);
res = 0;
break;
case SIOCDEVPRIVATE+2:
if ( !capable(CAP_NET_ADMIN) ) {
case SIOCDEVPRIVATE + 2:
if (!capable(CAP_NET_ADMIN)) {
up(&pegasus->sem);
return -EPERM;
}
......@@ -900,8 +914,7 @@ static int pegasus_ioctl( struct net_device *net, struct ifreq *rq, int cmd )
return res;
}
static void pegasus_set_multicast( struct net_device *net )
static void pegasus_set_multicast(struct net_device *net)
{
pegasus_t *pegasus = net->priv;
......@@ -911,7 +924,7 @@ static void pegasus_set_multicast( struct net_device *net )
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)) {
(net->flags & IFF_ALLMULTI)) {
pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST;
pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
info("%s set allmulti", net->name);
......@@ -921,98 +934,97 @@ static void pegasus_set_multicast( struct net_device *net )
}
pegasus->flags |= ETH_REGS_CHANGE;
ctrl_callback( pegasus->ctrl_urb );
ctrl_callback(pegasus->ctrl_urb);
netif_wake_queue(net);
}
static __u8 mii_phy_probe( pegasus_t *pegasus )
static __u8 mii_phy_probe(pegasus_t * pegasus)
{
int i;
__u16 tmp;
int i;
__u16 tmp;
for ( i=0; i < 32; i++ ) {
read_mii_word( pegasus, i, MII_BMSR, &tmp );
if ( tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0 )
for (i = 0; i < 32; i++) {
read_mii_word(pegasus, i, MII_BMSR, &tmp);
if (tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0)
continue;
else
return i;
return i;
}
return 0xff;
return 0xff;
}
static inline void setup_pegasus_II( pegasus_t *pegasus )
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 && mii_mode )
set_register( pegasus, Reg81, 6 );
set_register(pegasus, Reg1d, 0);
set_register(pegasus, Reg7b, 2);
if (pegasus->features & HAS_HOME_PNA && mii_mode)
set_register(pegasus, Reg81, 6);
else
set_register( pegasus, Reg81, 2 );
set_register(pegasus, Reg81, 2);
}
static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id)
static void *pegasus_probe(struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id)
{
struct net_device *net;
pegasus_t *pegasus;
int dev_index = id - pegasus_ids;
struct net_device *net;
pegasus_t *pegasus;
int dev_index = id - pegasus_ids;
if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
err("usb_set_configuration() failed");
return NULL;
}
if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) {
if (!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) {
err("out of memory allocating device structure");
return NULL;
}
usb_inc_dev_use( dev );
usb_inc_dev_use(dev);
memset(pegasus, 0, sizeof(struct pegasus));
pegasus->dev_index = dev_index;
init_waitqueue_head( &pegasus->ctrl_wait );
init_waitqueue_head(&pegasus->ctrl_wait);
pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!pegasus->ctrl_urb) {
kfree (pegasus);
kfree(pegasus);
return NULL;
}
pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!pegasus->rx_urb) {
usb_free_urb (pegasus->ctrl_urb);
kfree (pegasus);
usb_free_urb(pegasus->ctrl_urb);
kfree(pegasus);
return NULL;
}
pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!pegasus->tx_urb) {
usb_free_urb (pegasus->rx_urb);
usb_free_urb (pegasus->ctrl_urb);
kfree (pegasus);
usb_free_urb(pegasus->rx_urb);
usb_free_urb(pegasus->ctrl_urb);
kfree(pegasus);
return NULL;
}
pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!pegasus->intr_urb) {
usb_free_urb (pegasus->tx_urb);
usb_free_urb (pegasus->rx_urb);
usb_free_urb (pegasus->ctrl_urb);
kfree (pegasus);
usb_free_urb(pegasus->tx_urb);
usb_free_urb(pegasus->rx_urb);
usb_free_urb(pegasus->ctrl_urb);
kfree(pegasus);
return NULL;
}
net = init_etherdev( NULL, 0 );
if ( !net ) {
usb_free_urb (pegasus->tx_urb);
usb_free_urb (pegasus->rx_urb);
usb_free_urb (pegasus->ctrl_urb);
kfree( pegasus );
net = init_etherdev(NULL, 0);
if (!net) {
usb_free_urb(pegasus->tx_urb);
usb_free_urb(pegasus->rx_urb);
usb_free_urb(pegasus->ctrl_urb);
kfree(pegasus);
return NULL;
}
init_MUTEX(&pegasus->sem);
tasklet_init(&pegasus->rx_tl, rx_fixup, (unsigned long)pegasus);
down(&pegasus->sem);
pegasus->usb = dev;
pegasus->net = net;
......@@ -1030,32 +1042,32 @@ static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum,
pegasus->features = usb_dev_id[dev_index].private;
#ifdef PEGASUS_USE_INTR
get_interrupt_interval( pegasus );
get_interrupt_interval(pegasus);
#endif
if ( reset_mac(pegasus) ) {
if (reset_mac(pegasus)) {
err("can't reset MAC");
unregister_netdev( pegasus->net );
usb_free_urb (pegasus->tx_urb);
usb_free_urb (pegasus->rx_urb);
usb_free_urb (pegasus->ctrl_urb);
unregister_netdev(pegasus->net);
usb_free_urb(pegasus->tx_urb);
usb_free_urb(pegasus->rx_urb);
usb_free_urb(pegasus->ctrl_urb);
kfree(pegasus->net);
kfree(pegasus);
pegasus = NULL;
goto exit;
}
info( "%s: %s", net->name, usb_dev_id[dev_index].name );
info("%s: %s", net->name, usb_dev_id[dev_index].name);
set_ethernet_addr( pegasus );
set_ethernet_addr(pegasus);
if ( pegasus->features & PEGASUS_II ) {
info( "setup Pegasus II specific registers" );
setup_pegasus_II( 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 == 0xff ) {
warn( "can't locate MII phy, using default" );
pegasus->phy = mii_phy_probe(pegasus);
if (pegasus->phy == 0xff) {
warn("can't locate MII phy, using default");
pegasus->phy = 1;
}
exit:
......@@ -1063,19 +1075,18 @@ static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum,
return pegasus;
}
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;
}
pegasus->flags |= PEGASUS_UNPLUG;
unregister_netdev( pegasus->net );
usb_dec_dev_use( dev );
unregister_netdev(pegasus->net);
usb_dec_dev_use(dev);
usb_unlink_urb(pegasus->intr_urb);
usb_unlink_urb(pegasus->tx_urb);
usb_unlink_urb(pegasus->rx_urb);
......@@ -1084,12 +1095,13 @@ static void pegasus_disconnect( struct usb_device *dev, void *ptr )
usb_free_urb(pegasus->tx_urb);
usb_free_urb(pegasus->rx_urb);
usb_free_urb(pegasus->ctrl_urb);
kfree( pegasus->net );
kfree( pegasus );
if (pegasus->rx_skb)
dev_kfree_skb(pegasus->rx_skb);
kfree(pegasus->net);
kfree(pegasus);
pegasus = NULL;
}
static struct usb_driver pegasus_driver = {
name: "pegasus",
probe: pegasus_probe,
......@@ -1100,13 +1112,13 @@ static struct usb_driver pegasus_driver = {
int __init pegasus_init(void)
{
info(DRIVER_VERSION ":" DRIVER_DESC);
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);
......@@ -22,8 +22,7 @@
#define PEGASUS_II 0x80000000
#define HAS_HOME_PNA 0x40000000
#define PEGASUS_MTU 1500
#define PEGASUS_MAX_MTU 1536
#define PEGASUS_MTU 1536
#define EPROM_WRITE 0x01
#define EPROM_READ 0x02
......@@ -45,6 +44,7 @@
#define CTRL_URB_RUNNING 0x00000010
#define CTRL_URB_SLEEP 0x00000020
#define PEGASUS_UNPLUG 0x00000040
#define PEGASUS_RX_URB_FAIL 0x00000080
#define ETH_REGS_CHANGE 0x40000000
#define ETH_REGS_CHANGED 0x80000000
......@@ -98,13 +98,14 @@ typedef struct pegasus {
unsigned features;
int dev_index;
int intr_interval;
struct tasklet_struct rx_tl;
struct urb *ctrl_urb, *rx_urb, *tx_urb, *intr_urb;
struct sk_buff *rx_skb;
struct usb_ctrlrequest dr;
wait_queue_head_t ctrl_wait;
struct semaphore sem;
unsigned char rx_buff[PEGASUS_MAX_MTU];
unsigned char tx_buff[PEGASUS_MAX_MTU];
unsigned char intr_buff[8];
__u8 tx_buff[PEGASUS_MTU];
__u8 eth_regs[4];
__u8 phy;
__u8 gpio_res;
......@@ -236,7 +237,7 @@ PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2204,
LINKSYS_GPIO_RESET | HAS_HOME_PNA )
PEGASUS_DEV( "Linksys USB Ethernet Adapter", VENDOR_LINKSYS, 0x2206,
LINKSYS_GPIO_RESET )
PEGASUS_DEV( "Linksys USB USB10TX", VENDOR_LINKSYS, 0x400b,
PEGASUS_DEV( "Linksys USB USB100TX", VENDOR_LINKSYS, 0x400b,
LINKSYS_GPIO_RESET | PEGASUS_II )
PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x200c,
LINKSYS_GPIO_RESET | PEGASUS_II )
......
......@@ -22,7 +22,7 @@
#include <asm/uaccess.h>
/* Version Information */
#define DRIVER_VERSION "v0.5.3 (2002/04/08)"
#define DRIVER_VERSION "v0.5.4 (2002/04/11)"
#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
#define DRIVER_DESC "rtl8150 based usb-ethernet driver"
......@@ -65,6 +65,7 @@
#define RTL8150_HW_CRC 0
#define RX_REG_SET 1
#define RTL8150_UNPLUG 2
#define RX_URB_FAIL 3
/* Define these values to match your device */
#define VENDOR_ID_REALTEK 0x0bda
......@@ -296,20 +297,19 @@ static void read_bulk_callback(struct urb *urb)
u16 rx_stat;
dev = urb->context;
if (!dev) {
warn("!dev");
if (!dev)
return;
if (test_bit(RTL8150_UNPLUG, &dev->flags))
return;
}
netdev = dev->netdev;
if (!netif_device_present(netdev)) {
warn("the network device is not present");
if (!netif_device_present(netdev))
return;
}
switch (urb->status) {
case 0:
break;
case -ENOENT:
return;
return; /* urb's in unlink state */
case -ETIMEDOUT:
warn("reset needed may be?..");
goto goon;
......@@ -318,6 +318,8 @@ static void read_bulk_callback(struct urb *urb)
goto goon;
}
tasklet_schedule(&dev->tl);
if (!dev->rx_skb) {
/* lost packets++ */
return;
......@@ -333,8 +335,6 @@ static void read_bulk_callback(struct urb *urb)
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
tasklet_schedule(&dev->tl);
skb = pull_skb(dev);
if (!skb) {
dev->rx_skb = NULL;
......@@ -347,8 +347,10 @@ static void read_bulk_callback(struct urb *urb)
goon:
FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
if ((res = usb_submit_urb(dev->rx_urb, GFP_ATOMIC)))
warn("%s: Rx urb submission failed %d", netdev->name, res);
if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC))
set_bit(RX_URB_FAIL, &dev->flags);
else
clear_bit(RX_URB_FAIL, &dev->flags);
}
static void rx_fixup(unsigned long data)
......@@ -357,18 +359,25 @@ static void rx_fixup(unsigned long data)
struct sk_buff *skb;
dev = (rtl8150_t *)data;
fill_skb_pool(dev);
skb = pull_skb(dev);
if (!skb) {
if (test_bit(RX_URB_FAIL, &dev->flags))
goto try_again;
if (dev->rx_skb)
return;
if (!(skb = pull_skb(dev))) {
tasklet_schedule(&dev->tl);
return;
}
if (dev->rx_skb)
return;
dev->rx_skb = skb;
FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
usb_submit_urb(dev->rx_urb, GFP_ATOMIC);
try_again:
if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC)) {
set_bit(RX_URB_FAIL, &dev->flags);
tasklet_schedule(&dev->tl);
} else
clear_bit(RX_URB_FAIL, &dev->flags);
}
static void write_bulk_callback(struct urb *urb)
......@@ -544,7 +553,7 @@ static int rtl8150_start_xmit(struct sk_buff *skb, struct net_device *netdev)
dev->tx_skb = skb;
FILL_BULK_URB(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2),
skb->data, count, write_bulk_callback, dev);
if ((res = usb_submit_urb(dev->tx_urb, GFP_KERNEL))) {
if ((res = usb_submit_urb(dev->tx_urb, GFP_ATOMIC))) {
warn("failed tx_urb %d\n", res);
dev->stats.tx_errors++;
netif_start_queue(netdev);
......@@ -598,10 +607,10 @@ static int rtl8150_close(struct net_device *netdev)
return -ENODEV;
down(&dev->sem);
netif_stop_queue(netdev);
if (!test_bit(RTL8150_UNPLUG, &dev->flags))
disable_net_traffic(dev);
unlink_all_urbs(dev);
netif_stop_queue(netdev);
up(&dev->sem);
return res;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment