162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) 2002 Petko Manolov (petkan@users.sourceforge.net)
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/signal.h>
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/netdevice.h>
1062306a36Sopenharmony_ci#include <linux/etherdevice.h>
1162306a36Sopenharmony_ci#include <linux/mii.h>
1262306a36Sopenharmony_ci#include <linux/ethtool.h>
1362306a36Sopenharmony_ci#include <linux/usb.h>
1462306a36Sopenharmony_ci#include <linux/uaccess.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* Version Information */
1762306a36Sopenharmony_ci#define DRIVER_VERSION "v0.6.2 (2004/08/27)"
1862306a36Sopenharmony_ci#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
1962306a36Sopenharmony_ci#define DRIVER_DESC "rtl8150 based usb-ethernet driver"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define	IDR			0x0120
2262306a36Sopenharmony_ci#define	MAR			0x0126
2362306a36Sopenharmony_ci#define	CR			0x012e
2462306a36Sopenharmony_ci#define	TCR			0x012f
2562306a36Sopenharmony_ci#define	RCR			0x0130
2662306a36Sopenharmony_ci#define	TSR			0x0132
2762306a36Sopenharmony_ci#define	RSR			0x0133
2862306a36Sopenharmony_ci#define	CON0			0x0135
2962306a36Sopenharmony_ci#define	CON1			0x0136
3062306a36Sopenharmony_ci#define	MSR			0x0137
3162306a36Sopenharmony_ci#define	PHYADD			0x0138
3262306a36Sopenharmony_ci#define	PHYDAT			0x0139
3362306a36Sopenharmony_ci#define	PHYCNT			0x013b
3462306a36Sopenharmony_ci#define	GPPC			0x013d
3562306a36Sopenharmony_ci#define	BMCR			0x0140
3662306a36Sopenharmony_ci#define	BMSR			0x0142
3762306a36Sopenharmony_ci#define	ANAR			0x0144
3862306a36Sopenharmony_ci#define	ANLP			0x0146
3962306a36Sopenharmony_ci#define	AER			0x0148
4062306a36Sopenharmony_ci#define CSCR			0x014C  /* This one has the link status */
4162306a36Sopenharmony_ci#define CSCR_LINK_STATUS	(1 << 3)
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define	IDR_EEPROM		0x1202
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define	PHY_READ		0
4662306a36Sopenharmony_ci#define	PHY_WRITE		0x20
4762306a36Sopenharmony_ci#define	PHY_GO			0x40
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define	MII_TIMEOUT		10
5062306a36Sopenharmony_ci#define	INTBUFSIZE		8
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define	RTL8150_REQT_READ	0xc0
5362306a36Sopenharmony_ci#define	RTL8150_REQT_WRITE	0x40
5462306a36Sopenharmony_ci#define	RTL8150_REQ_GET_REGS	0x05
5562306a36Sopenharmony_ci#define	RTL8150_REQ_SET_REGS	0x05
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Transmit status register errors */
5962306a36Sopenharmony_ci#define TSR_ECOL		(1<<5)
6062306a36Sopenharmony_ci#define TSR_LCOL		(1<<4)
6162306a36Sopenharmony_ci#define TSR_LOSS_CRS		(1<<3)
6262306a36Sopenharmony_ci#define TSR_JBR			(1<<2)
6362306a36Sopenharmony_ci#define TSR_ERRORS		(TSR_ECOL | TSR_LCOL | TSR_LOSS_CRS | TSR_JBR)
6462306a36Sopenharmony_ci/* Receive status register errors */
6562306a36Sopenharmony_ci#define RSR_CRC			(1<<2)
6662306a36Sopenharmony_ci#define RSR_FAE			(1<<1)
6762306a36Sopenharmony_ci#define RSR_ERRORS		(RSR_CRC | RSR_FAE)
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* Media status register definitions */
7062306a36Sopenharmony_ci#define MSR_DUPLEX		(1<<4)
7162306a36Sopenharmony_ci#define MSR_SPEED		(1<<3)
7262306a36Sopenharmony_ci#define MSR_LINK		(1<<2)
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* Interrupt pipe data */
7562306a36Sopenharmony_ci#define INT_TSR			0x00
7662306a36Sopenharmony_ci#define INT_RSR			0x01
7762306a36Sopenharmony_ci#define INT_MSR			0x02
7862306a36Sopenharmony_ci#define INT_WAKSR		0x03
7962306a36Sopenharmony_ci#define INT_TXOK_CNT		0x04
8062306a36Sopenharmony_ci#define INT_RXLOST_CNT		0x05
8162306a36Sopenharmony_ci#define INT_CRERR_CNT		0x06
8262306a36Sopenharmony_ci#define INT_COL_CNT		0x07
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#define	RTL8150_MTU		1540
8662306a36Sopenharmony_ci#define	RTL8150_TX_TIMEOUT	(HZ)
8762306a36Sopenharmony_ci#define	RX_SKB_POOL_SIZE	4
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/* rtl8150 flags */
9062306a36Sopenharmony_ci#define	RTL8150_HW_CRC		0
9162306a36Sopenharmony_ci#define	RX_REG_SET		1
9262306a36Sopenharmony_ci#define	RTL8150_UNPLUG		2
9362306a36Sopenharmony_ci#define	RX_URB_FAIL		3
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/* Define these values to match your device */
9662306a36Sopenharmony_ci#define	VENDOR_ID_REALTEK		0x0bda
9762306a36Sopenharmony_ci#define	VENDOR_ID_MELCO			0x0411
9862306a36Sopenharmony_ci#define	VENDOR_ID_MICRONET		0x3980
9962306a36Sopenharmony_ci#define	VENDOR_ID_LONGSHINE		0x07b8
10062306a36Sopenharmony_ci#define	VENDOR_ID_OQO			0x1557
10162306a36Sopenharmony_ci#define	VENDOR_ID_ZYXEL			0x0586
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define PRODUCT_ID_RTL8150		0x8150
10462306a36Sopenharmony_ci#define	PRODUCT_ID_LUAKTX		0x0012
10562306a36Sopenharmony_ci#define	PRODUCT_ID_LCS8138TX		0x401a
10662306a36Sopenharmony_ci#define PRODUCT_ID_SP128AR		0x0003
10762306a36Sopenharmony_ci#define	PRODUCT_ID_PRESTIGE		0x401a
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#undef	EEPROM_WRITE
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/* table of devices that work with this driver */
11262306a36Sopenharmony_cistatic const struct usb_device_id rtl8150_table[] = {
11362306a36Sopenharmony_ci	{USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8150)},
11462306a36Sopenharmony_ci	{USB_DEVICE(VENDOR_ID_MELCO, PRODUCT_ID_LUAKTX)},
11562306a36Sopenharmony_ci	{USB_DEVICE(VENDOR_ID_MICRONET, PRODUCT_ID_SP128AR)},
11662306a36Sopenharmony_ci	{USB_DEVICE(VENDOR_ID_LONGSHINE, PRODUCT_ID_LCS8138TX)},
11762306a36Sopenharmony_ci	{USB_DEVICE(VENDOR_ID_OQO, PRODUCT_ID_RTL8150)},
11862306a36Sopenharmony_ci	{USB_DEVICE(VENDOR_ID_ZYXEL, PRODUCT_ID_PRESTIGE)},
11962306a36Sopenharmony_ci	{}
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, rtl8150_table);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistruct rtl8150 {
12562306a36Sopenharmony_ci	unsigned long flags;
12662306a36Sopenharmony_ci	struct usb_device *udev;
12762306a36Sopenharmony_ci	struct tasklet_struct tl;
12862306a36Sopenharmony_ci	struct net_device *netdev;
12962306a36Sopenharmony_ci	struct urb *rx_urb, *tx_urb, *intr_urb;
13062306a36Sopenharmony_ci	struct sk_buff *tx_skb, *rx_skb;
13162306a36Sopenharmony_ci	struct sk_buff *rx_skb_pool[RX_SKB_POOL_SIZE];
13262306a36Sopenharmony_ci	spinlock_t rx_pool_lock;
13362306a36Sopenharmony_ci	struct usb_ctrlrequest dr;
13462306a36Sopenharmony_ci	int intr_interval;
13562306a36Sopenharmony_ci	u8 *intr_buff;
13662306a36Sopenharmony_ci	u8 phy;
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_citypedef struct rtl8150 rtl8150_t;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistruct async_req {
14262306a36Sopenharmony_ci	struct usb_ctrlrequest dr;
14362306a36Sopenharmony_ci	u16 rx_creg;
14462306a36Sopenharmony_ci};
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic const char driver_name [] = "rtl8150";
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/*
14962306a36Sopenharmony_ci**
15062306a36Sopenharmony_ci**	device related part of the code
15162306a36Sopenharmony_ci**
15262306a36Sopenharmony_ci*/
15362306a36Sopenharmony_cistatic int get_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	return usb_control_msg_recv(dev->udev, 0, RTL8150_REQ_GET_REGS,
15662306a36Sopenharmony_ci				    RTL8150_REQT_READ, indx, 0, data, size,
15762306a36Sopenharmony_ci				    1000, GFP_NOIO);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic int set_registers(rtl8150_t * dev, u16 indx, u16 size, const void *data)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	return usb_control_msg_send(dev->udev, 0, RTL8150_REQ_SET_REGS,
16362306a36Sopenharmony_ci				    RTL8150_REQT_WRITE, indx, 0, data, size,
16462306a36Sopenharmony_ci				    1000, GFP_NOIO);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic void async_set_reg_cb(struct urb *urb)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct async_req *req = (struct async_req *)urb->context;
17062306a36Sopenharmony_ci	int status = urb->status;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (status < 0)
17362306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s failed with %d", __func__, status);
17462306a36Sopenharmony_ci	kfree(req);
17562306a36Sopenharmony_ci	usb_free_urb(urb);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic int async_set_registers(rtl8150_t *dev, u16 indx, u16 size, u16 reg)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	int res = -ENOMEM;
18162306a36Sopenharmony_ci	struct urb *async_urb;
18262306a36Sopenharmony_ci	struct async_req *req;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	req = kmalloc(sizeof(struct async_req), GFP_ATOMIC);
18562306a36Sopenharmony_ci	if (req == NULL)
18662306a36Sopenharmony_ci		return res;
18762306a36Sopenharmony_ci	async_urb = usb_alloc_urb(0, GFP_ATOMIC);
18862306a36Sopenharmony_ci	if (async_urb == NULL) {
18962306a36Sopenharmony_ci		kfree(req);
19062306a36Sopenharmony_ci		return res;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci	req->rx_creg = cpu_to_le16(reg);
19362306a36Sopenharmony_ci	req->dr.bRequestType = RTL8150_REQT_WRITE;
19462306a36Sopenharmony_ci	req->dr.bRequest = RTL8150_REQ_SET_REGS;
19562306a36Sopenharmony_ci	req->dr.wIndex = 0;
19662306a36Sopenharmony_ci	req->dr.wValue = cpu_to_le16(indx);
19762306a36Sopenharmony_ci	req->dr.wLength = cpu_to_le16(size);
19862306a36Sopenharmony_ci	usb_fill_control_urb(async_urb, dev->udev,
19962306a36Sopenharmony_ci	                     usb_sndctrlpipe(dev->udev, 0), (void *)&req->dr,
20062306a36Sopenharmony_ci			     &req->rx_creg, size, async_set_reg_cb, req);
20162306a36Sopenharmony_ci	res = usb_submit_urb(async_urb, GFP_ATOMIC);
20262306a36Sopenharmony_ci	if (res) {
20362306a36Sopenharmony_ci		if (res == -ENODEV)
20462306a36Sopenharmony_ci			netif_device_detach(dev->netdev);
20562306a36Sopenharmony_ci		dev_err(&dev->udev->dev, "%s failed with %d\n", __func__, res);
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci	return res;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic int read_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 * reg)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	int i;
21362306a36Sopenharmony_ci	u8 data[3], tmp;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	data[0] = phy;
21662306a36Sopenharmony_ci	data[1] = data[2] = 0;
21762306a36Sopenharmony_ci	tmp = indx | PHY_READ | PHY_GO;
21862306a36Sopenharmony_ci	i = 0;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	set_registers(dev, PHYADD, sizeof(data), data);
22162306a36Sopenharmony_ci	set_registers(dev, PHYCNT, 1, &tmp);
22262306a36Sopenharmony_ci	do {
22362306a36Sopenharmony_ci		get_registers(dev, PHYCNT, 1, data);
22462306a36Sopenharmony_ci	} while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT));
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (i <= MII_TIMEOUT) {
22762306a36Sopenharmony_ci		get_registers(dev, PHYDAT, 2, data);
22862306a36Sopenharmony_ci		*reg = data[0] | (data[1] << 8);
22962306a36Sopenharmony_ci		return 0;
23062306a36Sopenharmony_ci	} else
23162306a36Sopenharmony_ci		return 1;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic int write_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 reg)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	int i;
23762306a36Sopenharmony_ci	u8 data[3], tmp;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	data[0] = phy;
24062306a36Sopenharmony_ci	data[1] = reg & 0xff;
24162306a36Sopenharmony_ci	data[2] = (reg >> 8) & 0xff;
24262306a36Sopenharmony_ci	tmp = indx | PHY_WRITE | PHY_GO;
24362306a36Sopenharmony_ci	i = 0;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	set_registers(dev, PHYADD, sizeof(data), data);
24662306a36Sopenharmony_ci	set_registers(dev, PHYCNT, 1, &tmp);
24762306a36Sopenharmony_ci	do {
24862306a36Sopenharmony_ci		get_registers(dev, PHYCNT, 1, data);
24962306a36Sopenharmony_ci	} while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT));
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (i <= MII_TIMEOUT)
25262306a36Sopenharmony_ci		return 0;
25362306a36Sopenharmony_ci	else
25462306a36Sopenharmony_ci		return 1;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void set_ethernet_addr(rtl8150_t *dev)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	u8 node_id[ETH_ALEN];
26062306a36Sopenharmony_ci	int ret;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	ret = get_registers(dev, IDR, sizeof(node_id), node_id);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (!ret) {
26562306a36Sopenharmony_ci		eth_hw_addr_set(dev->netdev, node_id);
26662306a36Sopenharmony_ci	} else {
26762306a36Sopenharmony_ci		eth_hw_addr_random(dev->netdev);
26862306a36Sopenharmony_ci		netdev_notice(dev->netdev, "Assigned a random MAC address: %pM\n",
26962306a36Sopenharmony_ci			      dev->netdev->dev_addr);
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int rtl8150_set_mac_address(struct net_device *netdev, void *p)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct sockaddr *addr = p;
27662306a36Sopenharmony_ci	rtl8150_t *dev = netdev_priv(netdev);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (netif_running(netdev))
27962306a36Sopenharmony_ci		return -EBUSY;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	eth_hw_addr_set(netdev, addr->sa_data);
28262306a36Sopenharmony_ci	netdev_dbg(netdev, "Setting MAC address to %pM\n", netdev->dev_addr);
28362306a36Sopenharmony_ci	/* Set the IDR registers. */
28462306a36Sopenharmony_ci	set_registers(dev, IDR, netdev->addr_len, netdev->dev_addr);
28562306a36Sopenharmony_ci#ifdef EEPROM_WRITE
28662306a36Sopenharmony_ci	{
28762306a36Sopenharmony_ci	int i;
28862306a36Sopenharmony_ci	u8 cr;
28962306a36Sopenharmony_ci	/* Get the CR contents. */
29062306a36Sopenharmony_ci	get_registers(dev, CR, 1, &cr);
29162306a36Sopenharmony_ci	/* Set the WEPROM bit (eeprom write enable). */
29262306a36Sopenharmony_ci	cr |= 0x20;
29362306a36Sopenharmony_ci	set_registers(dev, CR, 1, &cr);
29462306a36Sopenharmony_ci	/* Write the MAC address into eeprom. Eeprom writes must be word-sized,
29562306a36Sopenharmony_ci	   so we need to split them up. */
29662306a36Sopenharmony_ci	for (i = 0; i * 2 < netdev->addr_len; i++) {
29762306a36Sopenharmony_ci		set_registers(dev, IDR_EEPROM + (i * 2), 2,
29862306a36Sopenharmony_ci		netdev->dev_addr + (i * 2));
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci	/* Clear the WEPROM bit (preventing accidental eeprom writes). */
30162306a36Sopenharmony_ci	cr &= 0xdf;
30262306a36Sopenharmony_ci	set_registers(dev, CR, 1, &cr);
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci#endif
30562306a36Sopenharmony_ci	return 0;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic int rtl8150_reset(rtl8150_t * dev)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	u8 data = 0x10;
31162306a36Sopenharmony_ci	int i = HZ;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	set_registers(dev, CR, 1, &data);
31462306a36Sopenharmony_ci	do {
31562306a36Sopenharmony_ci		get_registers(dev, CR, 1, &data);
31662306a36Sopenharmony_ci	} while ((data & 0x10) && --i);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return (i > 0) ? 1 : 0;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int alloc_all_urbs(rtl8150_t * dev)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
32462306a36Sopenharmony_ci	if (!dev->rx_urb)
32562306a36Sopenharmony_ci		return 0;
32662306a36Sopenharmony_ci	dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
32762306a36Sopenharmony_ci	if (!dev->tx_urb) {
32862306a36Sopenharmony_ci		usb_free_urb(dev->rx_urb);
32962306a36Sopenharmony_ci		return 0;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci	dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
33262306a36Sopenharmony_ci	if (!dev->intr_urb) {
33362306a36Sopenharmony_ci		usb_free_urb(dev->rx_urb);
33462306a36Sopenharmony_ci		usb_free_urb(dev->tx_urb);
33562306a36Sopenharmony_ci		return 0;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return 1;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic void free_all_urbs(rtl8150_t * dev)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	usb_free_urb(dev->rx_urb);
34462306a36Sopenharmony_ci	usb_free_urb(dev->tx_urb);
34562306a36Sopenharmony_ci	usb_free_urb(dev->intr_urb);
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic void unlink_all_urbs(rtl8150_t * dev)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	usb_kill_urb(dev->rx_urb);
35162306a36Sopenharmony_ci	usb_kill_urb(dev->tx_urb);
35262306a36Sopenharmony_ci	usb_kill_urb(dev->intr_urb);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic inline struct sk_buff *pull_skb(rtl8150_t *dev)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct sk_buff *skb;
35862306a36Sopenharmony_ci	int i;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	for (i = 0; i < RX_SKB_POOL_SIZE; i++) {
36162306a36Sopenharmony_ci		if (dev->rx_skb_pool[i]) {
36262306a36Sopenharmony_ci			skb = dev->rx_skb_pool[i];
36362306a36Sopenharmony_ci			dev->rx_skb_pool[i] = NULL;
36462306a36Sopenharmony_ci			return skb;
36562306a36Sopenharmony_ci		}
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci	return NULL;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void read_bulk_callback(struct urb *urb)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	rtl8150_t *dev;
37362306a36Sopenharmony_ci	unsigned pkt_len, res;
37462306a36Sopenharmony_ci	struct sk_buff *skb;
37562306a36Sopenharmony_ci	struct net_device *netdev;
37662306a36Sopenharmony_ci	int status = urb->status;
37762306a36Sopenharmony_ci	int result;
37862306a36Sopenharmony_ci	unsigned long flags;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	dev = urb->context;
38162306a36Sopenharmony_ci	if (!dev)
38262306a36Sopenharmony_ci		return;
38362306a36Sopenharmony_ci	if (test_bit(RTL8150_UNPLUG, &dev->flags))
38462306a36Sopenharmony_ci		return;
38562306a36Sopenharmony_ci	netdev = dev->netdev;
38662306a36Sopenharmony_ci	if (!netif_device_present(netdev))
38762306a36Sopenharmony_ci		return;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	switch (status) {
39062306a36Sopenharmony_ci	case 0:
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	case -ENOENT:
39362306a36Sopenharmony_ci		return;	/* the urb is in unlink state */
39462306a36Sopenharmony_ci	case -ETIME:
39562306a36Sopenharmony_ci		if (printk_ratelimit())
39662306a36Sopenharmony_ci			dev_warn(&urb->dev->dev, "may be reset is needed?..\n");
39762306a36Sopenharmony_ci		goto goon;
39862306a36Sopenharmony_ci	default:
39962306a36Sopenharmony_ci		if (printk_ratelimit())
40062306a36Sopenharmony_ci			dev_warn(&urb->dev->dev, "Rx status %d\n", status);
40162306a36Sopenharmony_ci		goto goon;
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (!dev->rx_skb)
40562306a36Sopenharmony_ci		goto resched;
40662306a36Sopenharmony_ci	/* protect against short packets (tell me why we got some?!?) */
40762306a36Sopenharmony_ci	if (urb->actual_length < 4)
40862306a36Sopenharmony_ci		goto goon;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	res = urb->actual_length;
41162306a36Sopenharmony_ci	pkt_len = res - 4;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	skb_put(dev->rx_skb, pkt_len);
41462306a36Sopenharmony_ci	dev->rx_skb->protocol = eth_type_trans(dev->rx_skb, netdev);
41562306a36Sopenharmony_ci	netif_rx(dev->rx_skb);
41662306a36Sopenharmony_ci	netdev->stats.rx_packets++;
41762306a36Sopenharmony_ci	netdev->stats.rx_bytes += pkt_len;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	spin_lock_irqsave(&dev->rx_pool_lock, flags);
42062306a36Sopenharmony_ci	skb = pull_skb(dev);
42162306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->rx_pool_lock, flags);
42262306a36Sopenharmony_ci	if (!skb)
42362306a36Sopenharmony_ci		goto resched;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	dev->rx_skb = skb;
42662306a36Sopenharmony_cigoon:
42762306a36Sopenharmony_ci	usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
42862306a36Sopenharmony_ci		      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
42962306a36Sopenharmony_ci	result = usb_submit_urb(dev->rx_urb, GFP_ATOMIC);
43062306a36Sopenharmony_ci	if (result == -ENODEV)
43162306a36Sopenharmony_ci		netif_device_detach(dev->netdev);
43262306a36Sopenharmony_ci	else if (result) {
43362306a36Sopenharmony_ci		set_bit(RX_URB_FAIL, &dev->flags);
43462306a36Sopenharmony_ci		goto resched;
43562306a36Sopenharmony_ci	} else {
43662306a36Sopenharmony_ci		clear_bit(RX_URB_FAIL, &dev->flags);
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	return;
44062306a36Sopenharmony_ciresched:
44162306a36Sopenharmony_ci	tasklet_schedule(&dev->tl);
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic void write_bulk_callback(struct urb *urb)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	rtl8150_t *dev;
44762306a36Sopenharmony_ci	int status = urb->status;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	dev = urb->context;
45062306a36Sopenharmony_ci	if (!dev)
45162306a36Sopenharmony_ci		return;
45262306a36Sopenharmony_ci	dev_kfree_skb_irq(dev->tx_skb);
45362306a36Sopenharmony_ci	if (!netif_device_present(dev->netdev))
45462306a36Sopenharmony_ci		return;
45562306a36Sopenharmony_ci	if (status)
45662306a36Sopenharmony_ci		dev_info(&urb->dev->dev, "%s: Tx status %d\n",
45762306a36Sopenharmony_ci			 dev->netdev->name, status);
45862306a36Sopenharmony_ci	netif_trans_update(dev->netdev);
45962306a36Sopenharmony_ci	netif_wake_queue(dev->netdev);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic void intr_callback(struct urb *urb)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	rtl8150_t *dev;
46562306a36Sopenharmony_ci	__u8 *d;
46662306a36Sopenharmony_ci	int status = urb->status;
46762306a36Sopenharmony_ci	int res;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	dev = urb->context;
47062306a36Sopenharmony_ci	if (!dev)
47162306a36Sopenharmony_ci		return;
47262306a36Sopenharmony_ci	switch (status) {
47362306a36Sopenharmony_ci	case 0:			/* success */
47462306a36Sopenharmony_ci		break;
47562306a36Sopenharmony_ci	case -ECONNRESET:	/* unlink */
47662306a36Sopenharmony_ci	case -ENOENT:
47762306a36Sopenharmony_ci	case -ESHUTDOWN:
47862306a36Sopenharmony_ci		return;
47962306a36Sopenharmony_ci	/* -EPIPE:  should clear the halt */
48062306a36Sopenharmony_ci	default:
48162306a36Sopenharmony_ci		dev_info(&urb->dev->dev, "%s: intr status %d\n",
48262306a36Sopenharmony_ci			 dev->netdev->name, status);
48362306a36Sopenharmony_ci		goto resubmit;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	d = urb->transfer_buffer;
48762306a36Sopenharmony_ci	if (d[0] & TSR_ERRORS) {
48862306a36Sopenharmony_ci		dev->netdev->stats.tx_errors++;
48962306a36Sopenharmony_ci		if (d[INT_TSR] & (TSR_ECOL | TSR_JBR))
49062306a36Sopenharmony_ci			dev->netdev->stats.tx_aborted_errors++;
49162306a36Sopenharmony_ci		if (d[INT_TSR] & TSR_LCOL)
49262306a36Sopenharmony_ci			dev->netdev->stats.tx_window_errors++;
49362306a36Sopenharmony_ci		if (d[INT_TSR] & TSR_LOSS_CRS)
49462306a36Sopenharmony_ci			dev->netdev->stats.tx_carrier_errors++;
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci	/* Report link status changes to the network stack */
49762306a36Sopenharmony_ci	if ((d[INT_MSR] & MSR_LINK) == 0) {
49862306a36Sopenharmony_ci		if (netif_carrier_ok(dev->netdev)) {
49962306a36Sopenharmony_ci			netif_carrier_off(dev->netdev);
50062306a36Sopenharmony_ci			netdev_dbg(dev->netdev, "%s: LINK LOST\n", __func__);
50162306a36Sopenharmony_ci		}
50262306a36Sopenharmony_ci	} else {
50362306a36Sopenharmony_ci		if (!netif_carrier_ok(dev->netdev)) {
50462306a36Sopenharmony_ci			netif_carrier_on(dev->netdev);
50562306a36Sopenharmony_ci			netdev_dbg(dev->netdev, "%s: LINK CAME BACK\n", __func__);
50662306a36Sopenharmony_ci		}
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ciresubmit:
51062306a36Sopenharmony_ci	res = usb_submit_urb (urb, GFP_ATOMIC);
51162306a36Sopenharmony_ci	if (res == -ENODEV)
51262306a36Sopenharmony_ci		netif_device_detach(dev->netdev);
51362306a36Sopenharmony_ci	else if (res)
51462306a36Sopenharmony_ci		dev_err(&dev->udev->dev,
51562306a36Sopenharmony_ci			"can't resubmit intr, %s-%s/input0, status %d\n",
51662306a36Sopenharmony_ci			dev->udev->bus->bus_name, dev->udev->devpath, res);
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic int rtl8150_suspend(struct usb_interface *intf, pm_message_t message)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	rtl8150_t *dev = usb_get_intfdata(intf);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	netif_device_detach(dev->netdev);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (netif_running(dev->netdev)) {
52662306a36Sopenharmony_ci		usb_kill_urb(dev->rx_urb);
52762306a36Sopenharmony_ci		usb_kill_urb(dev->intr_urb);
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci	return 0;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic int rtl8150_resume(struct usb_interface *intf)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	rtl8150_t *dev = usb_get_intfdata(intf);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	netif_device_attach(dev->netdev);
53762306a36Sopenharmony_ci	if (netif_running(dev->netdev)) {
53862306a36Sopenharmony_ci		dev->rx_urb->status = 0;
53962306a36Sopenharmony_ci		dev->rx_urb->actual_length = 0;
54062306a36Sopenharmony_ci		read_bulk_callback(dev->rx_urb);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		dev->intr_urb->status = 0;
54362306a36Sopenharmony_ci		dev->intr_urb->actual_length = 0;
54462306a36Sopenharmony_ci		intr_callback(dev->intr_urb);
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci	return 0;
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci/*
55062306a36Sopenharmony_ci**
55162306a36Sopenharmony_ci**	network related part of the code
55262306a36Sopenharmony_ci**
55362306a36Sopenharmony_ci*/
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic void fill_skb_pool(rtl8150_t *dev)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct sk_buff *skb;
55862306a36Sopenharmony_ci	int i;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	for (i = 0; i < RX_SKB_POOL_SIZE; i++) {
56162306a36Sopenharmony_ci		if (dev->rx_skb_pool[i])
56262306a36Sopenharmony_ci			continue;
56362306a36Sopenharmony_ci		skb = dev_alloc_skb(RTL8150_MTU + 2);
56462306a36Sopenharmony_ci		if (!skb) {
56562306a36Sopenharmony_ci			return;
56662306a36Sopenharmony_ci		}
56762306a36Sopenharmony_ci		skb_reserve(skb, 2);
56862306a36Sopenharmony_ci		dev->rx_skb_pool[i] = skb;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic void free_skb_pool(rtl8150_t *dev)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	int i;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	for (i = 0; i < RX_SKB_POOL_SIZE; i++)
57762306a36Sopenharmony_ci		dev_kfree_skb(dev->rx_skb_pool[i]);
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic void rx_fixup(struct tasklet_struct *t)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct rtl8150 *dev = from_tasklet(dev, t, tl);
58362306a36Sopenharmony_ci	struct sk_buff *skb;
58462306a36Sopenharmony_ci	int status;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	spin_lock_irq(&dev->rx_pool_lock);
58762306a36Sopenharmony_ci	fill_skb_pool(dev);
58862306a36Sopenharmony_ci	spin_unlock_irq(&dev->rx_pool_lock);
58962306a36Sopenharmony_ci	if (test_bit(RX_URB_FAIL, &dev->flags))
59062306a36Sopenharmony_ci		if (dev->rx_skb)
59162306a36Sopenharmony_ci			goto try_again;
59262306a36Sopenharmony_ci	spin_lock_irq(&dev->rx_pool_lock);
59362306a36Sopenharmony_ci	skb = pull_skb(dev);
59462306a36Sopenharmony_ci	spin_unlock_irq(&dev->rx_pool_lock);
59562306a36Sopenharmony_ci	if (skb == NULL)
59662306a36Sopenharmony_ci		goto tlsched;
59762306a36Sopenharmony_ci	dev->rx_skb = skb;
59862306a36Sopenharmony_ci	usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
59962306a36Sopenharmony_ci		      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
60062306a36Sopenharmony_citry_again:
60162306a36Sopenharmony_ci	status = usb_submit_urb(dev->rx_urb, GFP_ATOMIC);
60262306a36Sopenharmony_ci	if (status == -ENODEV) {
60362306a36Sopenharmony_ci		netif_device_detach(dev->netdev);
60462306a36Sopenharmony_ci	} else if (status) {
60562306a36Sopenharmony_ci		set_bit(RX_URB_FAIL, &dev->flags);
60662306a36Sopenharmony_ci		goto tlsched;
60762306a36Sopenharmony_ci	} else {
60862306a36Sopenharmony_ci		clear_bit(RX_URB_FAIL, &dev->flags);
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	return;
61262306a36Sopenharmony_citlsched:
61362306a36Sopenharmony_ci	tasklet_schedule(&dev->tl);
61462306a36Sopenharmony_ci}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cistatic int enable_net_traffic(rtl8150_t * dev)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	u8 cr, tcr, rcr, msr;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (!rtl8150_reset(dev)) {
62162306a36Sopenharmony_ci		dev_warn(&dev->udev->dev, "device reset failed\n");
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci	/* RCR bit7=1 attach Rx info at the end;  =0 HW CRC (which is broken) */
62462306a36Sopenharmony_ci	rcr = 0x9e;
62562306a36Sopenharmony_ci	tcr = 0xd8;
62662306a36Sopenharmony_ci	cr = 0x0c;
62762306a36Sopenharmony_ci	if (!(rcr & 0x80))
62862306a36Sopenharmony_ci		set_bit(RTL8150_HW_CRC, &dev->flags);
62962306a36Sopenharmony_ci	set_registers(dev, RCR, 1, &rcr);
63062306a36Sopenharmony_ci	set_registers(dev, TCR, 1, &tcr);
63162306a36Sopenharmony_ci	set_registers(dev, CR, 1, &cr);
63262306a36Sopenharmony_ci	get_registers(dev, MSR, 1, &msr);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	return 0;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic void disable_net_traffic(rtl8150_t * dev)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	u8 cr;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	get_registers(dev, CR, 1, &cr);
64262306a36Sopenharmony_ci	cr &= 0xf3;
64362306a36Sopenharmony_ci	set_registers(dev, CR, 1, &cr);
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic void rtl8150_tx_timeout(struct net_device *netdev, unsigned int txqueue)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	rtl8150_t *dev = netdev_priv(netdev);
64962306a36Sopenharmony_ci	dev_warn(&netdev->dev, "Tx timeout.\n");
65062306a36Sopenharmony_ci	usb_unlink_urb(dev->tx_urb);
65162306a36Sopenharmony_ci	netdev->stats.tx_errors++;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic void rtl8150_set_multicast(struct net_device *netdev)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	rtl8150_t *dev = netdev_priv(netdev);
65762306a36Sopenharmony_ci	u16 rx_creg = 0x9e;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	netif_stop_queue(netdev);
66062306a36Sopenharmony_ci	if (netdev->flags & IFF_PROMISC) {
66162306a36Sopenharmony_ci		rx_creg |= 0x0001;
66262306a36Sopenharmony_ci		dev_info(&netdev->dev, "%s: promiscuous mode\n", netdev->name);
66362306a36Sopenharmony_ci	} else if (!netdev_mc_empty(netdev) ||
66462306a36Sopenharmony_ci		   (netdev->flags & IFF_ALLMULTI)) {
66562306a36Sopenharmony_ci		rx_creg &= 0xfffe;
66662306a36Sopenharmony_ci		rx_creg |= 0x0002;
66762306a36Sopenharmony_ci		dev_dbg(&netdev->dev, "%s: allmulti set\n", netdev->name);
66862306a36Sopenharmony_ci	} else {
66962306a36Sopenharmony_ci		/* ~RX_MULTICAST, ~RX_PROMISCUOUS */
67062306a36Sopenharmony_ci		rx_creg &= 0x00fc;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci	async_set_registers(dev, RCR, sizeof(rx_creg), rx_creg);
67362306a36Sopenharmony_ci	netif_wake_queue(netdev);
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb,
67762306a36Sopenharmony_ci					    struct net_device *netdev)
67862306a36Sopenharmony_ci{
67962306a36Sopenharmony_ci	rtl8150_t *dev = netdev_priv(netdev);
68062306a36Sopenharmony_ci	int count, res;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	netif_stop_queue(netdev);
68362306a36Sopenharmony_ci	count = (skb->len < 60) ? 60 : skb->len;
68462306a36Sopenharmony_ci	count = (count & 0x3f) ? count : count + 1;
68562306a36Sopenharmony_ci	dev->tx_skb = skb;
68662306a36Sopenharmony_ci	usb_fill_bulk_urb(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2),
68762306a36Sopenharmony_ci		      skb->data, count, write_bulk_callback, dev);
68862306a36Sopenharmony_ci	if ((res = usb_submit_urb(dev->tx_urb, GFP_ATOMIC))) {
68962306a36Sopenharmony_ci		/* Can we get/handle EPIPE here? */
69062306a36Sopenharmony_ci		if (res == -ENODEV)
69162306a36Sopenharmony_ci			netif_device_detach(dev->netdev);
69262306a36Sopenharmony_ci		else {
69362306a36Sopenharmony_ci			dev_warn(&netdev->dev, "failed tx_urb %d\n", res);
69462306a36Sopenharmony_ci			netdev->stats.tx_errors++;
69562306a36Sopenharmony_ci			netif_start_queue(netdev);
69662306a36Sopenharmony_ci		}
69762306a36Sopenharmony_ci	} else {
69862306a36Sopenharmony_ci		netdev->stats.tx_packets++;
69962306a36Sopenharmony_ci		netdev->stats.tx_bytes += skb->len;
70062306a36Sopenharmony_ci		netif_trans_update(netdev);
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return NETDEV_TX_OK;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic void set_carrier(struct net_device *netdev)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	rtl8150_t *dev = netdev_priv(netdev);
71062306a36Sopenharmony_ci	short tmp;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	get_registers(dev, CSCR, 2, &tmp);
71362306a36Sopenharmony_ci	if (tmp & CSCR_LINK_STATUS)
71462306a36Sopenharmony_ci		netif_carrier_on(netdev);
71562306a36Sopenharmony_ci	else
71662306a36Sopenharmony_ci		netif_carrier_off(netdev);
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic int rtl8150_open(struct net_device *netdev)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	rtl8150_t *dev = netdev_priv(netdev);
72262306a36Sopenharmony_ci	int res;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (dev->rx_skb == NULL)
72562306a36Sopenharmony_ci		dev->rx_skb = pull_skb(dev);
72662306a36Sopenharmony_ci	if (!dev->rx_skb)
72762306a36Sopenharmony_ci		return -ENOMEM;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	set_registers(dev, IDR, 6, netdev->dev_addr);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
73262306a36Sopenharmony_ci		      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
73362306a36Sopenharmony_ci	if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL))) {
73462306a36Sopenharmony_ci		if (res == -ENODEV)
73562306a36Sopenharmony_ci			netif_device_detach(dev->netdev);
73662306a36Sopenharmony_ci		dev_warn(&netdev->dev, "rx_urb submit failed: %d\n", res);
73762306a36Sopenharmony_ci		return res;
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci	usb_fill_int_urb(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev, 3),
74062306a36Sopenharmony_ci		     dev->intr_buff, INTBUFSIZE, intr_callback,
74162306a36Sopenharmony_ci		     dev, dev->intr_interval);
74262306a36Sopenharmony_ci	if ((res = usb_submit_urb(dev->intr_urb, GFP_KERNEL))) {
74362306a36Sopenharmony_ci		if (res == -ENODEV)
74462306a36Sopenharmony_ci			netif_device_detach(dev->netdev);
74562306a36Sopenharmony_ci		dev_warn(&netdev->dev, "intr_urb submit failed: %d\n", res);
74662306a36Sopenharmony_ci		usb_kill_urb(dev->rx_urb);
74762306a36Sopenharmony_ci		return res;
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci	enable_net_traffic(dev);
75062306a36Sopenharmony_ci	set_carrier(netdev);
75162306a36Sopenharmony_ci	netif_start_queue(netdev);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	return res;
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic int rtl8150_close(struct net_device *netdev)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	rtl8150_t *dev = netdev_priv(netdev);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	netif_stop_queue(netdev);
76162306a36Sopenharmony_ci	if (!test_bit(RTL8150_UNPLUG, &dev->flags))
76262306a36Sopenharmony_ci		disable_net_traffic(dev);
76362306a36Sopenharmony_ci	unlink_all_urbs(dev);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	return 0;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic void rtl8150_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	rtl8150_t *dev = netdev_priv(netdev);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	strscpy(info->driver, driver_name, sizeof(info->driver));
77362306a36Sopenharmony_ci	strscpy(info->version, DRIVER_VERSION, sizeof(info->version));
77462306a36Sopenharmony_ci	usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info));
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cistatic int rtl8150_get_link_ksettings(struct net_device *netdev,
77862306a36Sopenharmony_ci				      struct ethtool_link_ksettings *ecmd)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	rtl8150_t *dev = netdev_priv(netdev);
78162306a36Sopenharmony_ci	short lpa, bmcr;
78262306a36Sopenharmony_ci	u32 supported;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	supported = (SUPPORTED_10baseT_Half |
78562306a36Sopenharmony_ci			  SUPPORTED_10baseT_Full |
78662306a36Sopenharmony_ci			  SUPPORTED_100baseT_Half |
78762306a36Sopenharmony_ci			  SUPPORTED_100baseT_Full |
78862306a36Sopenharmony_ci			  SUPPORTED_Autoneg |
78962306a36Sopenharmony_ci			  SUPPORTED_TP | SUPPORTED_MII);
79062306a36Sopenharmony_ci	ecmd->base.port = PORT_TP;
79162306a36Sopenharmony_ci	ecmd->base.phy_address = dev->phy;
79262306a36Sopenharmony_ci	get_registers(dev, BMCR, 2, &bmcr);
79362306a36Sopenharmony_ci	get_registers(dev, ANLP, 2, &lpa);
79462306a36Sopenharmony_ci	if (bmcr & BMCR_ANENABLE) {
79562306a36Sopenharmony_ci		u32 speed = ((lpa & (LPA_100HALF | LPA_100FULL)) ?
79662306a36Sopenharmony_ci			     SPEED_100 : SPEED_10);
79762306a36Sopenharmony_ci		ecmd->base.speed = speed;
79862306a36Sopenharmony_ci		ecmd->base.autoneg = AUTONEG_ENABLE;
79962306a36Sopenharmony_ci		if (speed == SPEED_100)
80062306a36Sopenharmony_ci			ecmd->base.duplex = (lpa & LPA_100FULL) ?
80162306a36Sopenharmony_ci			    DUPLEX_FULL : DUPLEX_HALF;
80262306a36Sopenharmony_ci		else
80362306a36Sopenharmony_ci			ecmd->base.duplex = (lpa & LPA_10FULL) ?
80462306a36Sopenharmony_ci			    DUPLEX_FULL : DUPLEX_HALF;
80562306a36Sopenharmony_ci	} else {
80662306a36Sopenharmony_ci		ecmd->base.autoneg = AUTONEG_DISABLE;
80762306a36Sopenharmony_ci		ecmd->base.speed = ((bmcr & BMCR_SPEED100) ?
80862306a36Sopenharmony_ci					     SPEED_100 : SPEED_10);
80962306a36Sopenharmony_ci		ecmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
81062306a36Sopenharmony_ci		    DUPLEX_FULL : DUPLEX_HALF;
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported,
81462306a36Sopenharmony_ci						supported);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	return 0;
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic const struct ethtool_ops ops = {
82062306a36Sopenharmony_ci	.get_drvinfo = rtl8150_get_drvinfo,
82162306a36Sopenharmony_ci	.get_link = ethtool_op_get_link,
82262306a36Sopenharmony_ci	.get_link_ksettings = rtl8150_get_link_ksettings,
82362306a36Sopenharmony_ci};
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic int rtl8150_siocdevprivate(struct net_device *netdev, struct ifreq *rq,
82662306a36Sopenharmony_ci				  void __user *udata, int cmd)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	rtl8150_t *dev = netdev_priv(netdev);
82962306a36Sopenharmony_ci	u16 *data = (u16 *) & rq->ifr_ifru;
83062306a36Sopenharmony_ci	int res = 0;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	switch (cmd) {
83362306a36Sopenharmony_ci	case SIOCDEVPRIVATE:
83462306a36Sopenharmony_ci		data[0] = dev->phy;
83562306a36Sopenharmony_ci		fallthrough;
83662306a36Sopenharmony_ci	case SIOCDEVPRIVATE + 1:
83762306a36Sopenharmony_ci		read_mii_word(dev, dev->phy, (data[1] & 0x1f), &data[3]);
83862306a36Sopenharmony_ci		break;
83962306a36Sopenharmony_ci	case SIOCDEVPRIVATE + 2:
84062306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
84162306a36Sopenharmony_ci			return -EPERM;
84262306a36Sopenharmony_ci		write_mii_word(dev, dev->phy, (data[1] & 0x1f), data[2]);
84362306a36Sopenharmony_ci		break;
84462306a36Sopenharmony_ci	default:
84562306a36Sopenharmony_ci		res = -EOPNOTSUPP;
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	return res;
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic const struct net_device_ops rtl8150_netdev_ops = {
85262306a36Sopenharmony_ci	.ndo_open		= rtl8150_open,
85362306a36Sopenharmony_ci	.ndo_stop		= rtl8150_close,
85462306a36Sopenharmony_ci	.ndo_siocdevprivate	= rtl8150_siocdevprivate,
85562306a36Sopenharmony_ci	.ndo_start_xmit		= rtl8150_start_xmit,
85662306a36Sopenharmony_ci	.ndo_tx_timeout		= rtl8150_tx_timeout,
85762306a36Sopenharmony_ci	.ndo_set_rx_mode	= rtl8150_set_multicast,
85862306a36Sopenharmony_ci	.ndo_set_mac_address	= rtl8150_set_mac_address,
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
86162306a36Sopenharmony_ci};
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_cistatic int rtl8150_probe(struct usb_interface *intf,
86462306a36Sopenharmony_ci			 const struct usb_device_id *id)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
86762306a36Sopenharmony_ci	rtl8150_t *dev;
86862306a36Sopenharmony_ci	struct net_device *netdev;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	netdev = alloc_etherdev(sizeof(rtl8150_t));
87162306a36Sopenharmony_ci	if (!netdev)
87262306a36Sopenharmony_ci		return -ENOMEM;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	dev = netdev_priv(netdev);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	dev->intr_buff = kmalloc(INTBUFSIZE, GFP_KERNEL);
87762306a36Sopenharmony_ci	if (!dev->intr_buff) {
87862306a36Sopenharmony_ci		free_netdev(netdev);
87962306a36Sopenharmony_ci		return -ENOMEM;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	tasklet_setup(&dev->tl, rx_fixup);
88362306a36Sopenharmony_ci	spin_lock_init(&dev->rx_pool_lock);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	dev->udev = udev;
88662306a36Sopenharmony_ci	dev->netdev = netdev;
88762306a36Sopenharmony_ci	netdev->netdev_ops = &rtl8150_netdev_ops;
88862306a36Sopenharmony_ci	netdev->watchdog_timeo = RTL8150_TX_TIMEOUT;
88962306a36Sopenharmony_ci	netdev->ethtool_ops = &ops;
89062306a36Sopenharmony_ci	dev->intr_interval = 100;	/* 100ms */
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (!alloc_all_urbs(dev)) {
89362306a36Sopenharmony_ci		dev_err(&intf->dev, "out of memory\n");
89462306a36Sopenharmony_ci		goto out;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci	if (!rtl8150_reset(dev)) {
89762306a36Sopenharmony_ci		dev_err(&intf->dev, "couldn't reset the device\n");
89862306a36Sopenharmony_ci		goto out1;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci	fill_skb_pool(dev);
90162306a36Sopenharmony_ci	set_ethernet_addr(dev);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	usb_set_intfdata(intf, dev);
90462306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, &intf->dev);
90562306a36Sopenharmony_ci	if (register_netdev(netdev) != 0) {
90662306a36Sopenharmony_ci		dev_err(&intf->dev, "couldn't register the device\n");
90762306a36Sopenharmony_ci		goto out2;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	dev_info(&intf->dev, "%s: rtl8150 is detected\n", netdev->name);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	return 0;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ciout2:
91562306a36Sopenharmony_ci	usb_set_intfdata(intf, NULL);
91662306a36Sopenharmony_ci	free_skb_pool(dev);
91762306a36Sopenharmony_ciout1:
91862306a36Sopenharmony_ci	free_all_urbs(dev);
91962306a36Sopenharmony_ciout:
92062306a36Sopenharmony_ci	kfree(dev->intr_buff);
92162306a36Sopenharmony_ci	free_netdev(netdev);
92262306a36Sopenharmony_ci	return -EIO;
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic void rtl8150_disconnect(struct usb_interface *intf)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	rtl8150_t *dev = usb_get_intfdata(intf);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	usb_set_intfdata(intf, NULL);
93062306a36Sopenharmony_ci	if (dev) {
93162306a36Sopenharmony_ci		set_bit(RTL8150_UNPLUG, &dev->flags);
93262306a36Sopenharmony_ci		tasklet_kill(&dev->tl);
93362306a36Sopenharmony_ci		unregister_netdev(dev->netdev);
93462306a36Sopenharmony_ci		unlink_all_urbs(dev);
93562306a36Sopenharmony_ci		free_all_urbs(dev);
93662306a36Sopenharmony_ci		free_skb_pool(dev);
93762306a36Sopenharmony_ci		dev_kfree_skb(dev->rx_skb);
93862306a36Sopenharmony_ci		kfree(dev->intr_buff);
93962306a36Sopenharmony_ci		free_netdev(dev->netdev);
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic struct usb_driver rtl8150_driver = {
94462306a36Sopenharmony_ci	.name		= driver_name,
94562306a36Sopenharmony_ci	.probe		= rtl8150_probe,
94662306a36Sopenharmony_ci	.disconnect	= rtl8150_disconnect,
94762306a36Sopenharmony_ci	.id_table	= rtl8150_table,
94862306a36Sopenharmony_ci	.suspend	= rtl8150_suspend,
94962306a36Sopenharmony_ci	.resume		= rtl8150_resume,
95062306a36Sopenharmony_ci	.disable_hub_initiated_lpm = 1,
95162306a36Sopenharmony_ci};
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_cimodule_usb_driver(rtl8150_driver);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
95662306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
95762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
958