Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+120 −327
Original line number Diff line number Diff line
/*
 *  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,296 +111,153 @@ 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, &regdi);
	if (cmd & PHY_READ) {
		ret = get_registers(p, PhyData, 2, &regdi);
		*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;
}

static int mdio_read(struct net_device *dev, int phy_id, int loc)
/* Returns non-negative int on success, error on failure */
static int read_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd)
{
	pegasus_t *pegasus = netdev_priv(dev);
	u16 res;

	read_mii_word(pegasus, phy_id, loc, &res);
	return (int)res;
	return __mii_op(pegasus, phy, indx, regd, PHY_READ);
}

static int write_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd)
/* Returns zero on success, error on failure */
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;
	return __mii_op(pegasus, phy, indx, regd, PHY_WRITE);
}

	if (i >= REG_TIMEOUT)
		goto fail;

	return ret;
static int mdio_read(struct net_device *dev, int phy_id, int loc)
{
	pegasus_t *pegasus = netdev_priv(dev);
	u16 res;

fail:
	netif_warn(pegasus, drv, pegasus->net, "%s failed\n", __func__);
	return -ETIMEDOUT;
	read_mii_word(pegasus, phy_id, loc, &res);
	return (int)res;
}

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 @@ static int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata)
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 @@ static void read_bulk_callback(struct urb *urb)
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 @@ static void rx_fixup(unsigned long data)
	} 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);
@@ -1395,16 +1193,12 @@ static int pegasus_probe(struct usb_interface *intf,
		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);
	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;
+2 −9
Original line number Diff line number Diff line
/*
 * 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];