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

Merge branch 'pegasus-errors'



Petko Manolov says:

====================
net: usb: pegasus: better error checking and DRIVER_VERSION removal

v3:

Pavel Skripkin again: make sure -ETIMEDOUT is returned by __mii_op() on timeout
condition;

v2:

Special thanks to Pavel Skripkin for the review and who caught a few bugs.
setup_pegasus_II() would not print an erroneous message on the success path.

v1:

Add error checking for get_registers() and derivatives.  If the usb transfer
fail then just don't use the buffer where the legal data should have been
returned.

Remove DRIVER_VERSION per Greg KH request.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 13a9c4ac bc65bacf
Loading
Loading
Loading
Loading
+77 −61
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright (c) 1999-2013 Petko Manolov (petkan@nucleusys.com)
 *  Copyright (c) 1999-2021 Petko Manolov (petkan@nucleusys.com)
 *
 *	ChangeLog:
 *		....	Most of the time spent on reading sources & docs.
 *		v0.2.x	First official release for the Linux kernel.
 *		v0.3.0	Beutified and structured, some bugs fixed.
 *		v0.3.x	URBifying bulk requests and bugfixing. First relatively
 *			stable release. Still can touch device's registers only
 *			from top-halves.
 *		v0.4.0	Control messages remained unurbified are now URBs.
 *			Now we can touch the HW at any time.
 *		v0.4.9	Control urbs again use process context to wait. Argh...
 *			Some long standing bugs (enable_net_traffic) fixed.
 *			Also nasty trick about resubmiting control urb from
 *			interrupt context used. Please let me know how it
 *			behaves. Pegasus II support added since this version.
 *			TODO: suppressing HCD warnings spewage on disconnect.
 *		v0.4.13	Ethernet address is now set at probe(), not at open()
 *			time as this seems to break dhcpd.
 *		v0.5.0	branch to 2.5.x kernels
 *		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,7 +21,6 @@
/*
 * Version Information
 */
#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"

@@ -132,9 +107,15 @@ static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size,
			 const void *data)
{
	return usb_control_msg_send(pegasus->usb, 0, PEGASUS_REQ_SET_REGS,
	int ret;

	ret = usb_control_msg_send(pegasus->usb, 0, PEGASUS_REQ_SET_REGS,
				    PEGASUS_REQT_WRITE, 0, indx, data, size,
				    1000, GFP_NOIO);
	if (ret < 0)
		netif_dbg(pegasus, drv, pegasus->net, "%s failed with %d\n", __func__, ret);

	return ret;
}

/*
@@ -145,10 +126,15 @@ static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size,
static int set_register(pegasus_t *pegasus, __u16 indx, __u8 data)
{
	void *buf = &data;
	int ret;

	return usb_control_msg_send(pegasus->usb, 0, PEGASUS_REQ_SET_REG,
	ret = usb_control_msg_send(pegasus->usb, 0, PEGASUS_REQ_SET_REG,
				    PEGASUS_REQT_WRITE, data, indx, buf, 1,
				    1000, GFP_NOIO);
	if (ret < 0)
		netif_dbg(pegasus, drv, pegasus->net, "%s failed with %d\n", __func__, ret);

	return ret;
}

static int update_eth_regs_async(pegasus_t *pegasus)
@@ -188,10 +174,9 @@ static int update_eth_regs_async(pegasus_t *pegasus)

static int __mii_op(pegasus_t *p, __u8 phy, __u8 indx, __u16 *regd, __u8 cmd)
{
	int i;
	__u8 data[4] = { phy, 0, 0, indx };
	int i, ret;
	__le16 regdi;
	int ret = -ETIMEDOUT;
	__u8 data[4] = { phy, 0, 0, indx };

	if (cmd & PHY_WRITE) {
		__le16 *t = (__le16 *) & data[1];
@@ -207,12 +192,15 @@ static int __mii_op(pegasus_t *p, __u8 phy, __u8 indx, __u16 *regd, __u8 cmd)
		if (data[0] & PHY_DONE)
			break;
	}
	if (i >= REG_TIMEOUT)
	if (i >= REG_TIMEOUT) {
		ret = -ETIMEDOUT;
		goto fail;
	}
	if (cmd & PHY_READ) {
		ret = get_registers(p, PhyData, 2, &regdi);
		if (ret < 0)
			goto fail;
		*regd = le16_to_cpu(regdi);
		return ret;
	}
	return 0;
fail:
@@ -235,9 +223,13 @@ static int write_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd)
static int mdio_read(struct net_device *dev, int phy_id, int loc)
{
	pegasus_t *pegasus = netdev_priv(dev);
	int ret;
	u16 res;

	read_mii_word(pegasus, phy_id, loc, &res);
	ret = read_mii_word(pegasus, phy_id, loc, &res);
	if (ret < 0)
		return ret;

	return (int)res;
}

@@ -251,10 +243,9 @@ static void mdio_write(struct net_device *dev, int phy_id, int loc, int val)

static int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata)
{
	int i;
	__u8 tmp = 0;
	int ret, i;
	__le16 retdatai;
	int ret;
	__u8 tmp = 0;

	set_register(pegasus, EpromCtrl, 0);
	set_register(pegasus, EpromOffset, index);
@@ -262,21 +253,25 @@ static int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata)

	for (i = 0; i < REG_TIMEOUT; i++) {
		ret = get_registers(pegasus, EpromCtrl, 1, &tmp);
		if (ret < 0)
			goto fail;
		if (tmp & EPROM_DONE)
			break;
		if (ret == -ESHUTDOWN)
			goto fail;
	}
	if (i >= REG_TIMEOUT)
	if (i >= REG_TIMEOUT) {
		ret = -ETIMEDOUT;
		goto fail;
	}

	ret = get_registers(pegasus, EpromData, 2, &retdatai);
	if (ret < 0)
		goto fail;
	*retdata = le16_to_cpu(retdatai);
	return ret;

fail:
	netif_warn(pegasus, drv, pegasus->net, "%s failed\n", __func__);
	return -ETIMEDOUT;
	netif_dbg(pegasus, drv, pegasus->net, "%s failed\n", __func__);
	return ret;
}

#ifdef	PEGASUS_WRITE_EEPROM
@@ -324,7 +319,7 @@ static int write_eprom_word(pegasus_t *pegasus, __u8 index, __u16 data)
	return ret;

fail:
	netif_warn(pegasus, drv, pegasus->net, "%s failed\n", __func__);
	netif_dbg(pegasus, drv, pegasus->net, "%s failed\n", __func__);
	return -ETIMEDOUT;
}
#endif	/* PEGASUS_WRITE_EEPROM */
@@ -367,19 +362,21 @@ static void set_ethernet_addr(pegasus_t *pegasus)
	return;
err:
	eth_hw_addr_random(pegasus->net);
	dev_info(&pegasus->intf->dev, "software assigned MAC address.\n");
	netif_dbg(pegasus, drv, pegasus->net, "software assigned MAC address.\n");

	return;
}

static inline int reset_mac(pegasus_t *pegasus)
{
	int ret, i;
	__u8 data = 0x8;
	int i;

	set_register(pegasus, EthCtrl1, data);
	for (i = 0; i < REG_TIMEOUT; i++) {
		get_registers(pegasus, EthCtrl1, 1, &data);
		ret = get_registers(pegasus, EthCtrl1, 1, &data);
		if (ret < 0)
			goto fail;
		if (~data & 0x08) {
			if (loopback)
				break;
@@ -402,22 +399,29 @@ 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);
		ret = read_mii_word(pegasus, 3, 0x1b, &auxmode);
		if (ret < 0)
			goto fail;
		auxmode |= 4;
		write_mii_word(pegasus, 3, 0x1b, &auxmode);
	}

	return 0;
fail:
	netif_dbg(pegasus, drv, pegasus->net, "%s failed\n", __func__);
	return ret;
}

static int enable_net_traffic(struct net_device *dev, struct usb_device *usb)
{
	__u16 linkpart;
	__u8 data[4];
	pegasus_t *pegasus = netdev_priv(dev);
	int ret;
	__u16 linkpart;
	__u8 data[4];

	read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart);
	ret = read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart);
	if (ret < 0)
		goto fail;
	data[0] = 0xc8; /* TX & RX enable, append status, no CRC */
	data[1] = 0;
	if (linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL))
@@ -435,11 +439,16 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb)
	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS2 ||
	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
		u16 auxmode;
		read_mii_word(pegasus, 0, 0x1b, &auxmode);
		ret = read_mii_word(pegasus, 0, 0x1b, &auxmode);
		if (ret < 0)
			goto fail;
		auxmode |= 4;
		write_mii_word(pegasus, 0, 0x1b, &auxmode);
	}

	return 0;
fail:
	netif_dbg(pegasus, drv, pegasus->net, "%s failed\n", __func__);
	return ret;
}

@@ -447,9 +456,9 @@ static void read_bulk_callback(struct urb *urb)
{
	pegasus_t *pegasus = urb->context;
	struct net_device *net;
	u8 *buf = urb->transfer_buffer;
	int rx_status, count = urb->actual_length;
	int status = urb->status;
	u8 *buf = urb->transfer_buffer;
	__u16 pkt_len;

	if (!pegasus)
@@ -880,7 +889,6 @@ static void pegasus_get_drvinfo(struct net_device *dev,
	pegasus_t *pegasus = netdev_priv(dev);

	strlcpy(info->driver, driver_name, sizeof(info->driver));
	strlcpy(info->version, DRIVER_VERSION, sizeof(info->version));
	usb_make_path(pegasus->usb, info->bus_info, sizeof(info->bus_info));
}

@@ -998,8 +1006,7 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
		data[0] = pegasus->phy;
		fallthrough;
	case SIOCDEVPRIVATE + 1:
		read_mii_word(pegasus, data[0], data[1] & 0x1f, &data[3]);
		res = 0;
		res = read_mii_word(pegasus, data[0], data[1] & 0x1f, &data[3]);
		break;
	case SIOCDEVPRIVATE + 2:
		if (!capable(CAP_NET_ADMIN))
@@ -1033,22 +1040,25 @@ static void pegasus_set_multicast(struct net_device *net)

static __u8 mii_phy_probe(pegasus_t *pegasus)
{
	int i;
	int i, ret;
	__u16 tmp;

	for (i = 0; i < 32; i++) {
		read_mii_word(pegasus, i, MII_BMSR, &tmp);
		ret = read_mii_word(pegasus, i, MII_BMSR, &tmp);
		if (ret < 0)
			goto fail;
		if (tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0)
			continue;
		else
			return i;
	}

fail:
	return 0xff;
}

static inline void setup_pegasus_II(pegasus_t *pegasus)
{
	int ret;
	__u8 data = 0xa5;

	set_register(pegasus, Reg1d, 0);
@@ -1060,7 +1070,9 @@ static inline void setup_pegasus_II(pegasus_t *pegasus)
		set_register(pegasus, Reg7b, 2);

	set_register(pegasus, 0x83, data);
	get_registers(pegasus, 0x83, 1, &data);
	ret = get_registers(pegasus, 0x83, 1, &data);
	if (ret < 0)
		goto fail;

	if (data == 0xa5)
		pegasus->chip = 0x8513;
@@ -1075,6 +1087,10 @@ static inline void setup_pegasus_II(pegasus_t *pegasus)
		set_register(pegasus, Reg81, 6);
	else
		set_register(pegasus, Reg81, 2);

	return;
fail:
	netif_dbg(pegasus, drv, pegasus->net, "%s failed\n", __func__);
}

static void check_carrier(struct work_struct *work)
@@ -1296,7 +1312,7 @@ static void __init parse_id(char *id)

static int __init pegasus_init(void)
{
	pr_info("%s: %s, " DRIVER_DESC "\n", driver_name, DRIVER_VERSION);
	pr_info("%s: " DRIVER_DESC "\n", driver_name);
	if (devid)
		parse_id(devid);
	return usb_register(&pegasus_driver);