Merge branch 'pegasus'
Petko Manolov says: ==================== This series of patches is fixing a bug related to multiple control URB submissions (noted by Sarah Sharp), optimizes read and write_mii_word routines and removes socket buffer pool used in the receive path. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
02ffc244f2
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1999-2005 Petko Manolov (petkan@users.sourceforge.net)
|
||||
* Copyright (c) 1999-2013 Petko Manolov (petkan@nucleusys.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
|
@ -26,6 +26,9 @@
|
|||
* v0.5.1 ethtool support added
|
||||
* v0.5.5 rx socket buffers are in a pool and the their allocation
|
||||
* is out of the interrupt routine.
|
||||
* ...
|
||||
* v0.9.3 simplified [get|set]_register(s), async update registers
|
||||
* logic revisited, receive skb_pool removed.
|
||||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
|
@ -45,8 +48,8 @@
|
|||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v0.6.14 (2006/09/27)"
|
||||
#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
|
||||
#define DRIVER_VERSION "v0.9.3 (2013/04/25)"
|
||||
#define DRIVER_AUTHOR "Petko Manolov <petkan@nucleusys.com>"
|
||||
#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
|
||||
|
||||
static const char driver_name[] = "pegasus";
|
||||
|
@ -108,253 +111,139 @@ MODULE_PARM_DESC(msg_level, "Override default message level");
|
|||
MODULE_DEVICE_TABLE(usb, pegasus_ids);
|
||||
static const struct net_device_ops pegasus_netdev_ops;
|
||||
|
||||
static int update_eth_regs_async(pegasus_t *);
|
||||
/* Aargh!!! I _really_ hate such tweaks */
|
||||
static void ctrl_callback(struct urb *urb)
|
||||
/*****/
|
||||
|
||||
static void async_ctrl_callback(struct urb *urb)
|
||||
{
|
||||
pegasus_t *pegasus = urb->context;
|
||||
struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
|
||||
int status = urb->status;
|
||||
|
||||
if (!pegasus)
|
||||
return;
|
||||
|
||||
switch (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:
|
||||
return;
|
||||
case -ENOENT:
|
||||
break;
|
||||
default:
|
||||
if (net_ratelimit())
|
||||
netif_dbg(pegasus, drv, pegasus->net,
|
||||
"%s, status %d\n", __func__, status);
|
||||
break;
|
||||
}
|
||||
pegasus->flags &= ~ETH_REGS_CHANGED;
|
||||
wake_up(&pegasus->ctrl_wait);
|
||||
if (status < 0)
|
||||
dev_dbg(&urb->dev->dev, "%s failed with %d", __func__, status);
|
||||
kfree(req);
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
|
||||
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;
|
||||
char *buffer;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
buffer = kmalloc(size, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
add_wait_queue(&pegasus->ctrl_wait, &wait);
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
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.wIndex = cpu_to_le16(indx);
|
||||
pegasus->dr.wLength = cpu_to_le16(size);
|
||||
pegasus->ctrl_urb->transfer_buffer_length = size;
|
||||
|
||||
usb_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);
|
||||
|
||||
/* using ATOMIC, we'd never wake up if we slept */
|
||||
if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
|
||||
set_current_state(TASK_RUNNING);
|
||||
if (ret == -ENODEV)
|
||||
netif_device_detach(pegasus->net);
|
||||
if (net_ratelimit())
|
||||
netif_err(pegasus, drv, pegasus->net,
|
||||
"%s, status %d\n", __func__, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
schedule();
|
||||
out:
|
||||
remove_wait_queue(&pegasus->ctrl_wait, &wait);
|
||||
memcpy(data, buffer, size);
|
||||
kfree(buffer);
|
||||
|
||||
ret = usb_control_msg(pegasus->usb, usb_rcvctrlpipe(pegasus->usb, 0),
|
||||
PEGASUS_REQ_GET_REGS, PEGASUS_REQT_READ, 0,
|
||||
indx, data, size, 1000);
|
||||
if (ret < 0)
|
||||
netif_dbg(pegasus, drv, pegasus->net,
|
||||
"%s returned %d\n", __func__, ret);
|
||||
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;
|
||||
char *buffer;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
buffer = kmemdup(data, size, GFP_KERNEL);
|
||||
if (!buffer) {
|
||||
netif_warn(pegasus, drv, pegasus->net,
|
||||
"out of memory in %s\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
add_wait_queue(&pegasus->ctrl_wait, &wait);
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
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_le16(indx);
|
||||
pegasus->dr.wLength = cpu_to_le16(size);
|
||||
pegasus->ctrl_urb->transfer_buffer_length = size;
|
||||
|
||||
usb_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);
|
||||
|
||||
if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
|
||||
if (ret == -ENODEV)
|
||||
netif_device_detach(pegasus->net);
|
||||
netif_err(pegasus, drv, pegasus->net,
|
||||
"%s, status %d\n", __func__, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
schedule();
|
||||
out:
|
||||
remove_wait_queue(&pegasus->ctrl_wait, &wait);
|
||||
kfree(buffer);
|
||||
|
||||
ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0),
|
||||
PEGASUS_REQ_SET_REGS, PEGASUS_REQT_WRITE, 0,
|
||||
indx, data, size, 100);
|
||||
if (ret < 0)
|
||||
netif_dbg(pegasus, drv, pegasus->net,
|
||||
"%s returned %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_register(pegasus_t *pegasus, __u16 indx, __u8 data)
|
||||
{
|
||||
int ret;
|
||||
char *tmp;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
tmp = kmemdup(&data, 1, GFP_KERNEL);
|
||||
if (!tmp) {
|
||||
netif_warn(pegasus, drv, pegasus->net,
|
||||
"out of memory in %s\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
add_wait_queue(&pegasus->ctrl_wait, &wait);
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
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_le16(data);
|
||||
pegasus->dr.wIndex = cpu_to_le16(indx);
|
||||
pegasus->dr.wLength = cpu_to_le16(1);
|
||||
pegasus->ctrl_urb->transfer_buffer_length = 1;
|
||||
|
||||
usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
|
||||
usb_sndctrlpipe(pegasus->usb, 0),
|
||||
(char *) &pegasus->dr,
|
||||
tmp, 1, 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))) {
|
||||
if (ret == -ENODEV)
|
||||
netif_device_detach(pegasus->net);
|
||||
if (net_ratelimit())
|
||||
netif_err(pegasus, drv, pegasus->net,
|
||||
"%s, status %d\n", __func__, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
schedule();
|
||||
out:
|
||||
remove_wait_queue(&pegasus->ctrl_wait, &wait);
|
||||
kfree(tmp);
|
||||
|
||||
ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0),
|
||||
PEGASUS_REQ_SET_REG, PEGASUS_REQT_WRITE, data,
|
||||
indx, &data, 1, 1000);
|
||||
if (ret < 0)
|
||||
netif_dbg(pegasus, drv, pegasus->net,
|
||||
"%s returned %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int update_eth_regs_async(pegasus_t *pegasus)
|
||||
{
|
||||
int ret;
|
||||
int ret = -ENOMEM;
|
||||
struct urb *async_urb;
|
||||
struct usb_ctrlrequest *req;
|
||||
|
||||
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_le16(EthCtrl0);
|
||||
pegasus->dr.wLength = cpu_to_le16(3);
|
||||
pegasus->ctrl_urb->transfer_buffer_length = 3;
|
||||
req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
|
||||
if (req == NULL)
|
||||
return ret;
|
||||
|
||||
usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
|
||||
usb_sndctrlpipe(pegasus->usb, 0),
|
||||
(char *) &pegasus->dr,
|
||||
pegasus->eth_regs, 3, ctrl_callback, pegasus);
|
||||
async_urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||
if (async_urb == NULL) {
|
||||
kfree(req);
|
||||
return ret;
|
||||
}
|
||||
req->bRequestType = PEGASUS_REQT_WRITE;
|
||||
req->bRequest = PEGASUS_REQ_SET_REGS;
|
||||
req->wValue = cpu_to_le16(0);
|
||||
req->wIndex = cpu_to_le16(EthCtrl0);
|
||||
req->wLength = cpu_to_le16(3);
|
||||
|
||||
if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
|
||||
usb_fill_control_urb(async_urb, pegasus->usb,
|
||||
usb_sndctrlpipe(pegasus->usb, 0), (void *)req,
|
||||
pegasus->eth_regs, 3, async_ctrl_callback, req);
|
||||
|
||||
ret = usb_submit_urb(async_urb, GFP_ATOMIC);
|
||||
if (ret) {
|
||||
if (ret == -ENODEV)
|
||||
netif_device_detach(pegasus->net);
|
||||
netif_err(pegasus, drv, pegasus->net,
|
||||
"%s, status %d\n", __func__, ret);
|
||||
"%s returned %d\n", __func__, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Returns 0 on success, error on failure */
|
||||
static int read_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd)
|
||||
static int __mii_op(pegasus_t *p, __u8 phy, __u8 indx, __u16 *regd, __u8 cmd)
|
||||
{
|
||||
int i;
|
||||
__u8 data[4] = { phy, 0, 0, indx };
|
||||
__le16 regdi;
|
||||
int ret;
|
||||
int ret = -ETIMEDOUT;
|
||||
|
||||
set_register(pegasus, PhyCtrl, 0);
|
||||
set_registers(pegasus, PhyAddr, sizeof(data), data);
|
||||
set_register(pegasus, PhyCtrl, (indx | PHY_READ));
|
||||
if (cmd & PHY_WRITE) {
|
||||
__le16 *t = (__le16 *) & data[1];
|
||||
*t = cpu_to_le16(*regd);
|
||||
}
|
||||
set_register(p, PhyCtrl, 0);
|
||||
set_registers(p, PhyAddr, sizeof(data), data);
|
||||
set_register(p, PhyCtrl, (indx | cmd));
|
||||
for (i = 0; i < REG_TIMEOUT; i++) {
|
||||
ret = get_registers(pegasus, PhyCtrl, 1, data);
|
||||
if (ret == -ESHUTDOWN)
|
||||
ret = get_registers(p, PhyCtrl, 1, data);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
if (data[0] & PHY_DONE)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= REG_TIMEOUT)
|
||||
goto fail;
|
||||
|
||||
ret = get_registers(pegasus, PhyData, 2, ®di);
|
||||
*regd = le16_to_cpu(regdi);
|
||||
return ret;
|
||||
|
||||
if (cmd & PHY_READ) {
|
||||
ret = get_registers(p, PhyData, 2, ®di);
|
||||
*regd = le16_to_cpu(regdi);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
fail:
|
||||
netif_warn(pegasus, drv, pegasus->net, "%s failed\n", __func__);
|
||||
|
||||
netif_dbg(p, drv, p->net, "%s failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Returns non-negative int on success, error on failure */
|
||||
static int read_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd)
|
||||
{
|
||||
return __mii_op(pegasus, phy, indx, regd, PHY_READ);
|
||||
}
|
||||
|
||||
/* Returns zero on success, error on failure */
|
||||
static int write_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd)
|
||||
{
|
||||
return __mii_op(pegasus, phy, indx, regd, PHY_WRITE);
|
||||
}
|
||||
|
||||
static int mdio_read(struct net_device *dev, int phy_id, int loc)
|
||||
{
|
||||
pegasus_t *pegasus = netdev_priv(dev);
|
||||
|
@ -364,40 +253,11 @@ static int mdio_read(struct net_device *dev, int phy_id, int loc)
|
|||
return (int)res;
|
||||
}
|
||||
|
||||
static int write_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd)
|
||||
{
|
||||
int i;
|
||||
__u8 data[4] = { phy, 0, 0, indx };
|
||||
int ret;
|
||||
|
||||
data[1] = (u8) regd;
|
||||
data[2] = (u8) (regd >> 8);
|
||||
set_register(pegasus, PhyCtrl, 0);
|
||||
set_registers(pegasus, PhyAddr, sizeof(data), data);
|
||||
set_register(pegasus, PhyCtrl, (indx | PHY_WRITE));
|
||||
for (i = 0; i < REG_TIMEOUT; i++) {
|
||||
ret = get_registers(pegasus, PhyCtrl, 1, data);
|
||||
if (ret == -ESHUTDOWN)
|
||||
goto fail;
|
||||
if (data[0] & PHY_DONE)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= REG_TIMEOUT)
|
||||
goto fail;
|
||||
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
netif_warn(pegasus, drv, pegasus->net, "%s failed\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void mdio_write(struct net_device *dev, int phy_id, int loc, int val)
|
||||
{
|
||||
pegasus_t *pegasus = netdev_priv(dev);
|
||||
|
||||
write_mii_word(pegasus, phy_id, loc, val);
|
||||
write_mii_word(pegasus, phy_id, loc, (__u16 *)&val);
|
||||
}
|
||||
|
||||
static int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata)
|
||||
|
@ -434,7 +294,6 @@ fail:
|
|||
static inline void enable_eprom_write(pegasus_t *pegasus)
|
||||
{
|
||||
__u8 tmp;
|
||||
int ret;
|
||||
|
||||
get_registers(pegasus, EthCtrl2, 1, &tmp);
|
||||
set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE);
|
||||
|
@ -443,7 +302,6 @@ static inline void enable_eprom_write(pegasus_t *pegasus)
|
|||
static inline void disable_eprom_write(pegasus_t *pegasus)
|
||||
{
|
||||
__u8 tmp;
|
||||
int ret;
|
||||
|
||||
get_registers(pegasus, EthCtrl2, 1, &tmp);
|
||||
set_register(pegasus, EpromCtrl, 0);
|
||||
|
@ -537,7 +395,8 @@ static inline int reset_mac(pegasus_t *pegasus)
|
|||
if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) {
|
||||
__u16 auxmode;
|
||||
read_mii_word(pegasus, 3, 0x1b, &auxmode);
|
||||
write_mii_word(pegasus, 3, 0x1b, auxmode | 4);
|
||||
auxmode |= 4;
|
||||
write_mii_word(pegasus, 3, 0x1b, &auxmode);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -569,57 +428,13 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb)
|
|||
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);
|
||||
auxmode |= 4;
|
||||
write_mii_word(pegasus, 0, 0x1b, &auxmode);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fill_skb_pool(pegasus_t *pegasus)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RX_SKBS; i++) {
|
||||
if (pegasus->rx_pool[i])
|
||||
continue;
|
||||
pegasus->rx_pool[i] = dev_alloc_skb(PEGASUS_MTU + 2);
|
||||
/*
|
||||
** we give up if the allocation fail. the tasklet will be
|
||||
** rescheduled again anyway...
|
||||
*/
|
||||
if (pegasus->rx_pool[i] == NULL)
|
||||
return;
|
||||
skb_reserve(pegasus->rx_pool[i], 2);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_skb_pool(pegasus_t *pegasus)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RX_SKBS; i++) {
|
||||
if (pegasus->rx_pool[i]) {
|
||||
dev_kfree_skb(pegasus->rx_pool[i]);
|
||||
pegasus->rx_pool[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct sk_buff *pull_skb(pegasus_t * pegasus)
|
||||
{
|
||||
int i;
|
||||
struct sk_buff *skb;
|
||||
|
||||
for (i = 0; i < RX_SKBS; i++) {
|
||||
if (likely(pegasus->rx_pool[i] != NULL)) {
|
||||
skb = pegasus->rx_pool[i];
|
||||
pegasus->rx_pool[i] = NULL;
|
||||
return skb;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void read_bulk_callback(struct urb *urb)
|
||||
{
|
||||
pegasus_t *pegasus = urb->context;
|
||||
|
@ -704,9 +519,8 @@ static void read_bulk_callback(struct urb *urb)
|
|||
if (pegasus->flags & PEGASUS_UNPLUG)
|
||||
return;
|
||||
|
||||
spin_lock(&pegasus->rx_pool_lock);
|
||||
pegasus->rx_skb = pull_skb(pegasus);
|
||||
spin_unlock(&pegasus->rx_pool_lock);
|
||||
pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, PEGASUS_MTU,
|
||||
GFP_ATOMIC);
|
||||
|
||||
if (pegasus->rx_skb == NULL)
|
||||
goto tl_sched;
|
||||
|
@ -734,24 +548,23 @@ tl_sched:
|
|||
static void rx_fixup(unsigned long data)
|
||||
{
|
||||
pegasus_t *pegasus;
|
||||
unsigned long flags;
|
||||
int status;
|
||||
|
||||
pegasus = (pegasus_t *) data;
|
||||
if (pegasus->flags & PEGASUS_UNPLUG)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&pegasus->rx_pool_lock, flags);
|
||||
fill_skb_pool(pegasus);
|
||||
if (pegasus->flags & PEGASUS_RX_URB_FAIL)
|
||||
if (pegasus->rx_skb)
|
||||
goto try_again;
|
||||
if (pegasus->rx_skb == NULL)
|
||||
pegasus->rx_skb = pull_skb(pegasus);
|
||||
pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net,
|
||||
PEGASUS_MTU,
|
||||
GFP_ATOMIC);
|
||||
if (pegasus->rx_skb == NULL) {
|
||||
netif_warn(pegasus, rx_err, pegasus->net, "low on memory\n");
|
||||
tasklet_schedule(&pegasus->rx_tl);
|
||||
goto done;
|
||||
return;
|
||||
}
|
||||
usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,
|
||||
usb_rcvbulkpipe(pegasus->usb, 1),
|
||||
|
@ -767,8 +580,6 @@ try_again:
|
|||
} else {
|
||||
pegasus->flags &= ~PEGASUS_RX_URB_FAIL;
|
||||
}
|
||||
done:
|
||||
spin_unlock_irqrestore(&pegasus->rx_pool_lock, flags);
|
||||
}
|
||||
|
||||
static void write_bulk_callback(struct urb *urb)
|
||||
|
@ -963,7 +774,6 @@ static void free_all_urbs(pegasus_t *pegasus)
|
|||
usb_free_urb(pegasus->intr_urb);
|
||||
usb_free_urb(pegasus->tx_urb);
|
||||
usb_free_urb(pegasus->rx_urb);
|
||||
usb_free_urb(pegasus->ctrl_urb);
|
||||
}
|
||||
|
||||
static void unlink_all_urbs(pegasus_t *pegasus)
|
||||
|
@ -971,48 +781,42 @@ static void unlink_all_urbs(pegasus_t *pegasus)
|
|||
usb_kill_urb(pegasus->intr_urb);
|
||||
usb_kill_urb(pegasus->tx_urb);
|
||||
usb_kill_urb(pegasus->rx_urb);
|
||||
usb_kill_urb(pegasus->ctrl_urb);
|
||||
}
|
||||
|
||||
static int alloc_urbs(pegasus_t *pegasus)
|
||||
{
|
||||
pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!pegasus->ctrl_urb)
|
||||
return 0;
|
||||
int res = -ENOMEM;
|
||||
|
||||
pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!pegasus->rx_urb) {
|
||||
usb_free_urb(pegasus->ctrl_urb);
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
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);
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
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);
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pegasus_open(struct net_device *net)
|
||||
{
|
||||
pegasus_t *pegasus = netdev_priv(net);
|
||||
int res;
|
||||
int res=-ENOMEM;
|
||||
|
||||
if (pegasus->rx_skb == NULL)
|
||||
pegasus->rx_skb = pull_skb(pegasus);
|
||||
/*
|
||||
** Note: no point to free the pool. it is empty :-)
|
||||
*/
|
||||
pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net,
|
||||
PEGASUS_MTU,
|
||||
GFP_KERNEL);
|
||||
if (!pegasus->rx_skb)
|
||||
return -ENOMEM;
|
||||
goto exit;
|
||||
|
||||
res = set_registers(pegasus, EthID, 6, net->dev_addr);
|
||||
|
||||
|
@ -1038,13 +842,13 @@ static int pegasus_open(struct net_device *net)
|
|||
usb_kill_urb(pegasus->rx_urb);
|
||||
goto exit;
|
||||
}
|
||||
if ((res = enable_net_traffic(net, pegasus->usb))) {
|
||||
res = enable_net_traffic(net, pegasus->usb);
|
||||
if (res < 0) {
|
||||
netif_dbg(pegasus, ifup, net,
|
||||
"can't enable_net_traffic() - %d\n", res);
|
||||
res = -EIO;
|
||||
usb_kill_urb(pegasus->rx_urb);
|
||||
usb_kill_urb(pegasus->intr_urb);
|
||||
free_skb_pool(pegasus);
|
||||
goto exit;
|
||||
}
|
||||
set_carrier(net);
|
||||
|
@ -1195,7 +999,7 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
|
|||
case SIOCDEVPRIVATE + 2:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]);
|
||||
write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, &data[2]);
|
||||
res = 0;
|
||||
break;
|
||||
default:
|
||||
|
@ -1219,11 +1023,7 @@ static void pegasus_set_multicast(struct net_device *net)
|
|||
pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST;
|
||||
pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
|
||||
}
|
||||
|
||||
pegasus->ctrl_urb->status = 0;
|
||||
|
||||
pegasus->flags |= ETH_REGS_CHANGE;
|
||||
ctrl_callback(pegasus->ctrl_urb);
|
||||
update_eth_regs_async(pegasus);
|
||||
}
|
||||
|
||||
static __u8 mii_phy_probe(pegasus_t *pegasus)
|
||||
|
@ -1340,9 +1140,9 @@ static int pegasus_probe(struct usb_interface *intf,
|
|||
|
||||
pegasus = netdev_priv(net);
|
||||
pegasus->dev_index = dev_index;
|
||||
init_waitqueue_head(&pegasus->ctrl_wait);
|
||||
|
||||
if (!alloc_urbs(pegasus)) {
|
||||
res = alloc_urbs(pegasus);
|
||||
if (res < 0) {
|
||||
dev_err(&intf->dev, "can't allocate %s\n", "urbs");
|
||||
goto out1;
|
||||
}
|
||||
|
@ -1364,7 +1164,6 @@ static int pegasus_probe(struct usb_interface *intf,
|
|||
pegasus->mii.mdio_write = mdio_write;
|
||||
pegasus->mii.phy_id_mask = 0x1f;
|
||||
pegasus->mii.reg_num_mask = 0x1f;
|
||||
spin_lock_init(&pegasus->rx_pool_lock);
|
||||
pegasus->msg_enable = netif_msg_init(msg_level, NETIF_MSG_DRV
|
||||
| NETIF_MSG_PROBE | NETIF_MSG_LINK);
|
||||
|
||||
|
@ -1376,7 +1175,6 @@ static int pegasus_probe(struct usb_interface *intf,
|
|||
goto out2;
|
||||
}
|
||||
set_ethernet_addr(pegasus);
|
||||
fill_skb_pool(pegasus);
|
||||
if (pegasus->features & PEGASUS_II) {
|
||||
dev_info(&intf->dev, "setup Pegasus II specific registers\n");
|
||||
setup_pegasus_II(pegasus);
|
||||
|
@ -1394,17 +1192,13 @@ static int pegasus_probe(struct usb_interface *intf,
|
|||
if (res)
|
||||
goto out3;
|
||||
queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check,
|
||||
CARRIER_CHECK_DELAY);
|
||||
|
||||
dev_info(&intf->dev, "%s, %s, %pM\n",
|
||||
net->name,
|
||||
usb_dev_id[dev_index].name,
|
||||
net->dev_addr);
|
||||
CARRIER_CHECK_DELAY);
|
||||
dev_info(&intf->dev, "%s, %s, %pM\n", net->name,
|
||||
usb_dev_id[dev_index].name, net->dev_addr);
|
||||
return 0;
|
||||
|
||||
out3:
|
||||
usb_set_intfdata(intf, NULL);
|
||||
free_skb_pool(pegasus);
|
||||
out2:
|
||||
free_all_urbs(pegasus);
|
||||
out1:
|
||||
|
@ -1429,7 +1223,6 @@ static void pegasus_disconnect(struct usb_interface *intf)
|
|||
unregister_netdev(pegasus->net);
|
||||
unlink_all_urbs(pegasus);
|
||||
free_all_urbs(pegasus);
|
||||
free_skb_pool(pegasus);
|
||||
if (pegasus->rx_skb != NULL) {
|
||||
dev_kfree_skb(pegasus->rx_skb);
|
||||
pegasus->rx_skb = NULL;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1999-2003 Petko Manolov - Petkan (petkan@users.sourceforge.net)
|
||||
* Copyright (c) 1999-2013 Petko Manolov (petkan@nucleusys.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as published
|
||||
|
@ -13,7 +13,6 @@
|
|||
#define HAS_HOME_PNA 0x40000000
|
||||
|
||||
#define PEGASUS_MTU 1536
|
||||
#define RX_SKBS 4
|
||||
|
||||
#define EPROM_WRITE 0x01
|
||||
#define EPROM_READ 0x02
|
||||
|
@ -34,8 +33,6 @@
|
|||
#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
|
||||
|
||||
#define RX_MULTICAST 2
|
||||
#define RX_PROMISCUOUS 4
|
||||
|
@ -96,12 +93,8 @@ typedef struct pegasus {
|
|||
int intr_interval;
|
||||
struct tasklet_struct rx_tl;
|
||||
struct delayed_work carrier_check;
|
||||
struct urb *ctrl_urb, *rx_urb, *tx_urb, *intr_urb;
|
||||
struct sk_buff *rx_pool[RX_SKBS];
|
||||
struct urb *rx_urb, *tx_urb, *intr_urb;
|
||||
struct sk_buff *rx_skb;
|
||||
struct usb_ctrlrequest dr;
|
||||
wait_queue_head_t ctrl_wait;
|
||||
spinlock_t rx_pool_lock;
|
||||
int chip;
|
||||
unsigned char intr_buff[8];
|
||||
__u8 tx_buff[PEGASUS_MTU];
|
||||
|
|
Loading…
Reference in New Issue