Commit 02ffc244 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 5f5624cf 323b3496
/* /*
* 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 * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -26,6 +26,9 @@ ...@@ -26,6 +26,9 @@
* v0.5.1 ethtool support added * v0.5.1 ethtool support added
* v0.5.5 rx socket buffers are in a pool and the their allocation * v0.5.5 rx socket buffers are in a pool and the their allocation
* is out of the interrupt routine. * 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> #include <linux/sched.h>
...@@ -45,8 +48,8 @@ ...@@ -45,8 +48,8 @@
/* /*
* Version Information * Version Information
*/ */
#define DRIVER_VERSION "v0.6.14 (2006/09/27)" #define DRIVER_VERSION "v0.9.3 (2013/04/25)"
#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>" #define DRIVER_AUTHOR "Petko Manolov <petkan@nucleusys.com>"
#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
static const char driver_name[] = "pegasus"; static const char driver_name[] = "pegasus";
...@@ -108,253 +111,139 @@ MODULE_PARM_DESC(msg_level, "Override default message level"); ...@@ -108,253 +111,139 @@ MODULE_PARM_DESC(msg_level, "Override default message level");
MODULE_DEVICE_TABLE(usb, pegasus_ids); MODULE_DEVICE_TABLE(usb, pegasus_ids);
static const struct net_device_ops pegasus_netdev_ops; 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; int status = urb->status;
if (!pegasus) if (status < 0)
return; dev_dbg(&urb->dev->dev, "%s failed with %d", __func__, status);
kfree(req);
switch (status) { usb_free_urb(urb);
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);
} }
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;
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; 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;
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; 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;
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; return ret;
} }
static int update_eth_regs_async(pegasus_t *pegasus) 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; req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS; if (req == NULL)
pegasus->dr.wValue = cpu_to_le16(0); return ret;
pegasus->dr.wIndex = cpu_to_le16(EthCtrl0);
pegasus->dr.wLength = cpu_to_le16(3);
pegasus->ctrl_urb->transfer_buffer_length = 3;
usb_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))) { 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);
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) if (ret == -ENODEV)
netif_device_detach(pegasus->net); netif_device_detach(pegasus->net);
netif_err(pegasus, drv, pegasus->net, netif_err(pegasus, drv, pegasus->net,
"%s, status %d\n", __func__, ret); "%s returned %d\n", __func__, ret);
} }
return ret; return ret;
} }
/* Returns 0 on success, error on failure */ static int __mii_op(pegasus_t *p, __u8 phy, __u8 indx, __u16 *regd, __u8 cmd)
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 };
__le16 regdi; __le16 regdi;
int ret; int ret = -ETIMEDOUT;
set_register(pegasus, PhyCtrl, 0); if (cmd & PHY_WRITE) {
set_registers(pegasus, PhyAddr, sizeof(data), data); __le16 *t = (__le16 *) & data[1];
set_register(pegasus, PhyCtrl, (indx | PHY_READ)); *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++) { for (i = 0; i < REG_TIMEOUT; i++) {
ret = get_registers(pegasus, PhyCtrl, 1, data); ret = get_registers(p, PhyCtrl, 1, data);
if (ret == -ESHUTDOWN) if (ret < 0)
goto fail; goto fail;
if (data[0] & PHY_DONE) if (data[0] & PHY_DONE)
break; break;
} }
if (i >= REG_TIMEOUT) if (i >= REG_TIMEOUT)
goto fail; goto fail;
if (cmd & PHY_READ) {
ret = get_registers(pegasus, PhyData, 2, &regdi); ret = get_registers(p, PhyData, 2, &regdi);
*regd = le16_to_cpu(regdi); *regd = le16_to_cpu(regdi);
return ret; return ret;
}
return 0;
fail: fail:
netif_warn(pegasus, drv, pegasus->net, "%s failed\n", __func__); netif_dbg(p, drv, p->net, "%s failed\n", __func__);
return ret; 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) static int mdio_read(struct net_device *dev, int phy_id, int loc)
{ {
pegasus_t *pegasus = netdev_priv(dev); pegasus_t *pegasus = netdev_priv(dev);
...@@ -364,40 +253,11 @@ static int mdio_read(struct net_device *dev, int phy_id, int loc) ...@@ -364,40 +253,11 @@ static int mdio_read(struct net_device *dev, int phy_id, int loc)
return (int)res; 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) static void mdio_write(struct net_device *dev, int phy_id, int loc, int val)
{ {
pegasus_t *pegasus = netdev_priv(dev); 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) static int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata)
...@@ -434,7 +294,6 @@ static int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata) ...@@ -434,7 +294,6 @@ static int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata)
static inline void enable_eprom_write(pegasus_t *pegasus) static inline void enable_eprom_write(pegasus_t *pegasus)
{ {
__u8 tmp; __u8 tmp;
int ret;
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);
...@@ -443,7 +302,6 @@ static inline void enable_eprom_write(pegasus_t *pegasus) ...@@ -443,7 +302,6 @@ static inline void enable_eprom_write(pegasus_t *pegasus)
static inline void disable_eprom_write(pegasus_t *pegasus) static inline void disable_eprom_write(pegasus_t *pegasus)
{ {
__u8 tmp; __u8 tmp;
int ret;
get_registers(pegasus, EthCtrl2, 1, &tmp); get_registers(pegasus, EthCtrl2, 1, &tmp);
set_register(pegasus, EpromCtrl, 0); set_register(pegasus, EpromCtrl, 0);
...@@ -537,7 +395,8 @@ static inline int reset_mac(pegasus_t *pegasus) ...@@ -537,7 +395,8 @@ static inline int reset_mac(pegasus_t *pegasus)
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); auxmode |= 4;
write_mii_word(pegasus, 3, 0x1b, &auxmode);
} }
return 0; return 0;
...@@ -569,57 +428,13 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb) ...@@ -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) { usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
u16 auxmode; u16 auxmode;
read_mii_word(pegasus, 0, 0x1b, &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; 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) static void read_bulk_callback(struct urb *urb)
{ {
pegasus_t *pegasus = urb->context; pegasus_t *pegasus = urb->context;
...@@ -704,9 +519,8 @@ static void read_bulk_callback(struct urb *urb) ...@@ -704,9 +519,8 @@ static void read_bulk_callback(struct urb *urb)
if (pegasus->flags & PEGASUS_UNPLUG) if (pegasus->flags & PEGASUS_UNPLUG)
return; return;
spin_lock(&pegasus->rx_pool_lock); pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, PEGASUS_MTU,
pegasus->rx_skb = pull_skb(pegasus); GFP_ATOMIC);
spin_unlock(&pegasus->rx_pool_lock);
if (pegasus->rx_skb == NULL) if (pegasus->rx_skb == NULL)
goto tl_sched; goto tl_sched;
...@@ -734,24 +548,23 @@ static void read_bulk_callback(struct urb *urb) ...@@ -734,24 +548,23 @@ static void read_bulk_callback(struct urb *urb)
static void rx_fixup(unsigned long data) static void rx_fixup(unsigned long data)
{ {
pegasus_t *pegasus; pegasus_t *pegasus;
unsigned long flags;
int status; int status;
pegasus = (pegasus_t *) data; pegasus = (pegasus_t *) data;
if (pegasus->flags & PEGASUS_UNPLUG) if (pegasus->flags & PEGASUS_UNPLUG)
return; return;
spin_lock_irqsave(&pegasus->rx_pool_lock, flags);
fill_skb_pool(pegasus);
if (pegasus->flags & PEGASUS_RX_URB_FAIL) if (pegasus->flags & PEGASUS_RX_URB_FAIL)
if (pegasus->rx_skb) if (pegasus->rx_skb)
goto try_again; goto try_again;
if (pegasus->rx_skb == NULL) 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) { if (pegasus->rx_skb == NULL) {
netif_warn(pegasus, rx_err, pegasus->net, "low on memory\n"); netif_warn(pegasus, rx_err, pegasus->net, "low on memory\n");
tasklet_schedule(&pegasus->rx_tl); tasklet_schedule(&pegasus->rx_tl);
goto done; return;
} }
usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,
usb_rcvbulkpipe(pegasus->usb, 1), usb_rcvbulkpipe(pegasus->usb, 1),
...@@ -767,8 +580,6 @@ static void rx_fixup(unsigned long data) ...@@ -767,8 +580,6 @@ static void rx_fixup(unsigned long data)
} else { } else {
pegasus->flags &= ~PEGASUS_RX_URB_FAIL; pegasus->flags &= ~PEGASUS_RX_URB_FAIL;
} }
done:
spin_unlock_irqrestore(&pegasus->rx_pool_lock, flags);
} }
static void write_bulk_callback(struct urb *urb) static void write_bulk_callback(struct urb *urb)
...@@ -963,7 +774,6 @@ static void free_all_urbs(pegasus_t *pegasus) ...@@ -963,7 +774,6 @@ static void free_all_urbs(pegasus_t *pegasus)
usb_free_urb(pegasus->intr_urb); usb_free_urb(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);
} }
static void unlink_all_urbs(pegasus_t *pegasus) static void unlink_all_urbs(pegasus_t *pegasus)
...@@ -971,48 +781,42 @@ 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->intr_urb);
usb_kill_urb(pegasus->tx_urb); usb_kill_urb(pegasus->tx_urb);
usb_kill_urb(pegasus->rx_urb); usb_kill_urb(pegasus->rx_urb);
usb_kill_urb(pegasus->ctrl_urb);
} }
static int alloc_urbs(pegasus_t *pegasus) static int alloc_urbs(pegasus_t *pegasus)
{ {
pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); int res = -ENOMEM;
if (!pegasus->ctrl_urb)
return 0;
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); return res;
return 0;
} }
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); return res;
return 0;
} }
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); return res;
return 0;
} }
return 1; return 0;
} }
static int pegasus_open(struct net_device *net) static int pegasus_open(struct net_device *net)
{ {
pegasus_t *pegasus = netdev_priv(net); pegasus_t *pegasus = netdev_priv(net);
int res; int res=-ENOMEM;
if (pegasus->rx_skb == NULL) if (pegasus->rx_skb == NULL)
pegasus->rx_skb = pull_skb(pegasus); pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net,
/* PEGASUS_MTU,
** Note: no point to free the pool. it is empty :-) GFP_KERNEL);
*/
if (!pegasus->rx_skb) if (!pegasus->rx_skb)
return -ENOMEM; goto exit;
res = set_registers(pegasus, EthID, 6, net->dev_addr); res = set_registers(pegasus, EthID, 6, net->dev_addr);
...@@ -1038,13 +842,13 @@ static int pegasus_open(struct net_device *net) ...@@ -1038,13 +842,13 @@ static int pegasus_open(struct net_device *net)
usb_kill_urb(pegasus->rx_urb); usb_kill_urb(pegasus->rx_urb);
goto exit; 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, netif_dbg(pegasus, ifup, net,
"can't enable_net_traffic() - %d\n", res); "can't enable_net_traffic() - %d\n", res);
res = -EIO; res = -EIO;
usb_kill_urb(pegasus->rx_urb); usb_kill_urb(pegasus->rx_urb);
usb_kill_urb(pegasus->intr_urb); usb_kill_urb(pegasus->intr_urb);
free_skb_pool(pegasus);
goto exit; goto exit;
} }
set_carrier(net); set_carrier(net);
...@@ -1195,7 +999,7 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) ...@@ -1195,7 +999,7 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
case SIOCDEVPRIVATE + 2: case SIOCDEVPRIVATE + 2:
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
return -EPERM; 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; res = 0;
break; break;
default: default:
...@@ -1219,11 +1023,7 @@ static void pegasus_set_multicast(struct net_device *net) ...@@ -1219,11 +1023,7 @@ static void pegasus_set_multicast(struct net_device *net)
pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST; pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST;
pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS; pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
} }
update_eth_regs_async(pegasus);
pegasus->ctrl_urb->status = 0;
pegasus->flags |= ETH_REGS_CHANGE;
ctrl_callback(pegasus->ctrl_urb);
} }
static __u8 mii_phy_probe(pegasus_t *pegasus) static __u8 mii_phy_probe(pegasus_t *pegasus)
...@@ -1340,9 +1140,9 @@ static int pegasus_probe(struct usb_interface *intf, ...@@ -1340,9 +1140,9 @@ static int pegasus_probe(struct usb_interface *intf,
pegasus = netdev_priv(net); pegasus = netdev_priv(net);
pegasus->dev_index = dev_index; 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"); dev_err(&intf->dev, "can't allocate %s\n", "urbs");
goto out1; goto out1;
} }
...@@ -1364,7 +1164,6 @@ static int pegasus_probe(struct usb_interface *intf, ...@@ -1364,7 +1164,6 @@ static int pegasus_probe(struct usb_interface *intf,
pegasus->mii.mdio_write = mdio_write; pegasus->mii.mdio_write = mdio_write;
pegasus->mii.phy_id_mask = 0x1f; pegasus->mii.phy_id_mask = 0x1f;
pegasus->mii.reg_num_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 pegasus->msg_enable = netif_msg_init(msg_level, NETIF_MSG_DRV
| NETIF_MSG_PROBE | NETIF_MSG_LINK); | NETIF_MSG_PROBE | NETIF_MSG_LINK);
...@@ -1376,7 +1175,6 @@ static int pegasus_probe(struct usb_interface *intf, ...@@ -1376,7 +1175,6 @@ static int pegasus_probe(struct usb_interface *intf,
goto out2; goto out2;
} }
set_ethernet_addr(pegasus); set_ethernet_addr(pegasus);
fill_skb_pool(pegasus);
if (pegasus->features & PEGASUS_II) { if (pegasus->features & PEGASUS_II) {
dev_info(&intf->dev, "setup Pegasus II specific registers\n"); dev_info(&intf->dev, "setup Pegasus II specific registers\n");
setup_pegasus_II(pegasus); setup_pegasus_II(pegasus);
...@@ -1395,16 +1193,12 @@ static int pegasus_probe(struct usb_interface *intf, ...@@ -1395,16 +1193,12 @@ static int pegasus_probe(struct usb_interface *intf,
goto out3; goto out3;
queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check, queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check,
CARRIER_CHECK_DELAY); CARRIER_CHECK_DELAY);
dev_info(&intf->dev, "%s, %s, %pM\n", net->name,
dev_info(&intf->dev, "%s, %s, %pM\n", usb_dev_id[dev_index].name, net->dev_addr);
net->name,
usb_dev_id[dev_index].name,
net->dev_addr);
return 0; return 0;
out3: out3:
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
free_skb_pool(pegasus);
out2: out2:
free_all_urbs(pegasus); free_all_urbs(pegasus);
out1: out1:
...@@ -1429,7 +1223,6 @@ static void pegasus_disconnect(struct usb_interface *intf) ...@@ -1429,7 +1223,6 @@ static void pegasus_disconnect(struct usb_interface *intf)
unregister_netdev(pegasus->net); unregister_netdev(pegasus->net);
unlink_all_urbs(pegasus); unlink_all_urbs(pegasus);
free_all_urbs(pegasus); free_all_urbs(pegasus);
free_skb_pool(pegasus);
if (pegasus->rx_skb != NULL) { if (pegasus->rx_skb != NULL) {
dev_kfree_skb(pegasus->rx_skb); dev_kfree_skb(pegasus->rx_skb);
pegasus->rx_skb = NULL; pegasus->rx_skb = NULL;
......
/* /*
* 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 * 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 * it under the terms of the GNU General Public License version 2 as published
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#define HAS_HOME_PNA 0x40000000 #define HAS_HOME_PNA 0x40000000
#define PEGASUS_MTU 1536 #define PEGASUS_MTU 1536
#define RX_SKBS 4
#define EPROM_WRITE 0x01 #define EPROM_WRITE 0x01
#define EPROM_READ 0x02 #define EPROM_READ 0x02
...@@ -34,8 +33,6 @@ ...@@ -34,8 +33,6 @@
#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 PEGASUS_RX_URB_FAIL 0x00000080
#define ETH_REGS_CHANGE 0x40000000
#define ETH_REGS_CHANGED 0x80000000
#define RX_MULTICAST 2 #define RX_MULTICAST 2
#define RX_PROMISCUOUS 4 #define RX_PROMISCUOUS 4
...@@ -96,12 +93,8 @@ typedef struct pegasus { ...@@ -96,12 +93,8 @@ typedef struct pegasus {
int intr_interval; int intr_interval;
struct tasklet_struct rx_tl; struct tasklet_struct rx_tl;
struct delayed_work carrier_check; struct delayed_work carrier_check;
struct urb *ctrl_urb, *rx_urb, *tx_urb, *intr_urb; struct urb *rx_urb, *tx_urb, *intr_urb;
struct sk_buff *rx_pool[RX_SKBS];
struct sk_buff *rx_skb; struct sk_buff *rx_skb;
struct usb_ctrlrequest dr;
wait_queue_head_t ctrl_wait;
spinlock_t rx_pool_lock;
int chip; int chip;
unsigned char intr_buff[8]; unsigned char intr_buff[8];
__u8 tx_buff[PEGASUS_MTU]; __u8 tx_buff[PEGASUS_MTU];
......
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