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