xref: /openbmc/linux/drivers/net/usb/cx82310_eth.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cc28a20eSOndrej Zary /*
3cc28a20eSOndrej Zary  * Driver for USB ethernet port of Conexant CX82310-based ADSL routers
4cc28a20eSOndrej Zary  * Copyright (C) 2010 by Ondrej Zary
5cc28a20eSOndrej Zary  * some parts inspired by the cxacru driver
6cc28a20eSOndrej Zary  */
7cc28a20eSOndrej Zary 
8cc28a20eSOndrej Zary #include <linux/module.h>
9cc28a20eSOndrej Zary #include <linux/netdevice.h>
10cc28a20eSOndrej Zary #include <linux/etherdevice.h>
11cc28a20eSOndrej Zary #include <linux/ethtool.h>
12cc28a20eSOndrej Zary #include <linux/workqueue.h>
13cc28a20eSOndrej Zary #include <linux/mii.h>
14cc28a20eSOndrej Zary #include <linux/usb.h>
15cc28a20eSOndrej Zary #include <linux/usb/usbnet.h>
16cc28a20eSOndrej Zary 
17cc28a20eSOndrej Zary enum cx82310_cmd {
18cc28a20eSOndrej Zary 	CMD_START		= 0x84,	/* no effect? */
19cc28a20eSOndrej Zary 	CMD_STOP		= 0x85,	/* no effect? */
20cc28a20eSOndrej Zary 	CMD_GET_STATUS		= 0x90,	/* returns nothing? */
21cc28a20eSOndrej Zary 	CMD_GET_MAC_ADDR	= 0x91,	/* read MAC address */
22cc28a20eSOndrej Zary 	CMD_GET_LINK_STATUS	= 0x92,	/* not useful, link is always up */
23cc28a20eSOndrej Zary 	CMD_ETHERNET_MODE	= 0x99,	/* unknown, needed during init */
24cc28a20eSOndrej Zary };
25cc28a20eSOndrej Zary 
26cc28a20eSOndrej Zary enum cx82310_status {
27cc28a20eSOndrej Zary 	STATUS_UNDEFINED,
28cc28a20eSOndrej Zary 	STATUS_SUCCESS,
29cc28a20eSOndrej Zary 	STATUS_ERROR,
30cc28a20eSOndrej Zary 	STATUS_UNSUPPORTED,
31cc28a20eSOndrej Zary 	STATUS_UNIMPLEMENTED,
32cc28a20eSOndrej Zary 	STATUS_PARAMETER_ERROR,
33cc28a20eSOndrej Zary 	STATUS_DBG_LOOPBACK,
34cc28a20eSOndrej Zary };
35cc28a20eSOndrej Zary 
36cc28a20eSOndrej Zary #define CMD_PACKET_SIZE	64
37f40bff42SOndrej Zary #define CMD_TIMEOUT	100
38cc28a20eSOndrej Zary #define CMD_REPLY_RETRY 5
39cc28a20eSOndrej Zary 
40cc28a20eSOndrej Zary #define CX82310_MTU	1514
41cc28a20eSOndrej Zary #define CMD_EP		0x01
42cc28a20eSOndrej Zary 
43ca139d76SOndrej Zary struct cx82310_priv {
44ca139d76SOndrej Zary 	struct work_struct reenable_work;
45ca139d76SOndrej Zary 	struct usbnet *dev;
46ca139d76SOndrej Zary };
47ca139d76SOndrej Zary 
48cc28a20eSOndrej Zary /*
49cc28a20eSOndrej Zary  * execute control command
50cc28a20eSOndrej Zary  *  - optionally send some data (command parameters)
51cc28a20eSOndrej Zary  *  - optionally wait for the reply
52cc28a20eSOndrej Zary  *  - optionally read some data from the reply
53cc28a20eSOndrej Zary  */
cx82310_cmd(struct usbnet * dev,enum cx82310_cmd cmd,bool reply,u8 * wdata,int wlen,u8 * rdata,int rlen)54cc28a20eSOndrej Zary static int cx82310_cmd(struct usbnet *dev, enum cx82310_cmd cmd, bool reply,
55cc28a20eSOndrej Zary 		       u8 *wdata, int wlen, u8 *rdata, int rlen)
56cc28a20eSOndrej Zary {
57cc28a20eSOndrej Zary 	int actual_len, retries, ret;
58cc28a20eSOndrej Zary 	struct usb_device *udev = dev->udev;
59cc28a20eSOndrej Zary 	u8 *buf = kzalloc(CMD_PACKET_SIZE, GFP_KERNEL);
60cc28a20eSOndrej Zary 
61cc28a20eSOndrej Zary 	if (!buf)
62cc28a20eSOndrej Zary 		return -ENOMEM;
63cc28a20eSOndrej Zary 
64cc28a20eSOndrej Zary 	/* create command packet */
65cc28a20eSOndrej Zary 	buf[0] = cmd;
66cc28a20eSOndrej Zary 	if (wdata)
67cc28a20eSOndrej Zary 		memcpy(buf + 4, wdata, min_t(int, wlen, CMD_PACKET_SIZE - 4));
68cc28a20eSOndrej Zary 
69cc28a20eSOndrej Zary 	/* send command packet */
70cc28a20eSOndrej Zary 	ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, CMD_EP), buf,
71cc28a20eSOndrej Zary 			   CMD_PACKET_SIZE, &actual_len, CMD_TIMEOUT);
72cc28a20eSOndrej Zary 	if (ret < 0) {
73f40bff42SOndrej Zary 		if (cmd != CMD_GET_LINK_STATUS)
7415f5e48fSOndrej Zary 			netdev_err(dev->net, "send command %#x: error %d\n",
75cc28a20eSOndrej Zary 				   cmd, ret);
76cc28a20eSOndrej Zary 		goto end;
77cc28a20eSOndrej Zary 	}
78cc28a20eSOndrej Zary 
79cc28a20eSOndrej Zary 	if (reply) {
80cc28a20eSOndrej Zary 		/* wait for reply, retry if it's empty */
81cc28a20eSOndrej Zary 		for (retries = 0; retries < CMD_REPLY_RETRY; retries++) {
82cc28a20eSOndrej Zary 			ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, CMD_EP),
83cc28a20eSOndrej Zary 					   buf, CMD_PACKET_SIZE, &actual_len,
84cc28a20eSOndrej Zary 					   CMD_TIMEOUT);
85cc28a20eSOndrej Zary 			if (ret < 0) {
86f40bff42SOndrej Zary 				if (cmd != CMD_GET_LINK_STATUS)
8715f5e48fSOndrej Zary 					netdev_err(dev->net, "reply receive error %d\n",
88f40bff42SOndrej Zary 						   ret);
89cc28a20eSOndrej Zary 				goto end;
90cc28a20eSOndrej Zary 			}
91cc28a20eSOndrej Zary 			if (actual_len > 0)
92cc28a20eSOndrej Zary 				break;
93cc28a20eSOndrej Zary 		}
94cc28a20eSOndrej Zary 		if (actual_len == 0) {
9515f5e48fSOndrej Zary 			netdev_err(dev->net, "no reply to command %#x\n", cmd);
96cc28a20eSOndrej Zary 			ret = -EIO;
97cc28a20eSOndrej Zary 			goto end;
98cc28a20eSOndrej Zary 		}
99cc28a20eSOndrej Zary 		if (buf[0] != cmd) {
10015f5e48fSOndrej Zary 			netdev_err(dev->net, "got reply to command %#x, expected: %#x\n",
101cc28a20eSOndrej Zary 				   buf[0], cmd);
102cc28a20eSOndrej Zary 			ret = -EIO;
103cc28a20eSOndrej Zary 			goto end;
104cc28a20eSOndrej Zary 		}
105cc28a20eSOndrej Zary 		if (buf[1] != STATUS_SUCCESS) {
10615f5e48fSOndrej Zary 			netdev_err(dev->net, "command %#x failed: %#x\n", cmd,
10715f5e48fSOndrej Zary 				   buf[1]);
108cc28a20eSOndrej Zary 			ret = -EIO;
109cc28a20eSOndrej Zary 			goto end;
110cc28a20eSOndrej Zary 		}
111cc28a20eSOndrej Zary 		if (rdata)
112cc28a20eSOndrej Zary 			memcpy(rdata, buf + 4,
113cc28a20eSOndrej Zary 			       min_t(int, rlen, CMD_PACKET_SIZE - 4));
114cc28a20eSOndrej Zary 	}
115cc28a20eSOndrej Zary end:
116cc28a20eSOndrej Zary 	kfree(buf);
117cc28a20eSOndrej Zary 	return ret;
118cc28a20eSOndrej Zary }
119cc28a20eSOndrej Zary 
cx82310_enable_ethernet(struct usbnet * dev)120ca139d76SOndrej Zary static int cx82310_enable_ethernet(struct usbnet *dev)
121ca139d76SOndrej Zary {
122ca139d76SOndrej Zary 	int ret = cx82310_cmd(dev, CMD_ETHERNET_MODE, true, "\x01", 1, NULL, 0);
123ca139d76SOndrej Zary 
124ca139d76SOndrej Zary 	if (ret)
125ca139d76SOndrej Zary 		netdev_err(dev->net, "unable to enable ethernet mode: %d\n",
126ca139d76SOndrej Zary 			   ret);
127ca139d76SOndrej Zary 	return ret;
128ca139d76SOndrej Zary }
129ca139d76SOndrej Zary 
cx82310_reenable_work(struct work_struct * work)130ca139d76SOndrej Zary static void cx82310_reenable_work(struct work_struct *work)
131ca139d76SOndrej Zary {
132ca139d76SOndrej Zary 	struct cx82310_priv *priv = container_of(work, struct cx82310_priv,
133ca139d76SOndrej Zary 						 reenable_work);
134ca139d76SOndrej Zary 	cx82310_enable_ethernet(priv->dev);
135ca139d76SOndrej Zary }
136ca139d76SOndrej Zary 
137cc28a20eSOndrej Zary #define partial_len	data[0]		/* length of partial packet data */
138cc28a20eSOndrej Zary #define partial_rem	data[1]		/* remaining (missing) data length */
139cc28a20eSOndrej Zary #define partial_data	data[2]		/* partial packet data */
140cc28a20eSOndrej Zary 
cx82310_bind(struct usbnet * dev,struct usb_interface * intf)141cc28a20eSOndrej Zary static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf)
142cc28a20eSOndrej Zary {
143cc28a20eSOndrej Zary 	int ret;
144cc28a20eSOndrej Zary 	char buf[15];
145cc28a20eSOndrej Zary 	struct usb_device *udev = dev->udev;
146f40bff42SOndrej Zary 	u8 link[3];
147f40bff42SOndrej Zary 	int timeout = 50;
148ca139d76SOndrej Zary 	struct cx82310_priv *priv;
149*2674e7eaSJakub Kicinski 	u8 addr[ETH_ALEN];
150cc28a20eSOndrej Zary 
151cc28a20eSOndrej Zary 	/* avoid ADSL modems - continue only if iProduct is "USB NET CARD" */
1527dbfdc23SOndrej Zary 	if (usb_string(udev, udev->descriptor.iProduct, buf, sizeof(buf)) > 0
1537dbfdc23SOndrej Zary 	    && strcmp(buf, "USB NET CARD")) {
1547dbfdc23SOndrej Zary 		dev_info(&udev->dev, "ignoring: probably an ADSL modem\n");
155cc28a20eSOndrej Zary 		return -ENODEV;
156cc28a20eSOndrej Zary 	}
157cc28a20eSOndrej Zary 
158cc28a20eSOndrej Zary 	ret = usbnet_get_endpoints(dev, intf);
159cc28a20eSOndrej Zary 	if (ret)
160cc28a20eSOndrej Zary 		return ret;
161cc28a20eSOndrej Zary 
162cc28a20eSOndrej Zary 	/*
163cc28a20eSOndrej Zary 	 * this must not include ethernet header as the device can send partial
164441993daSOndrej Zary 	 * packets with no header (and sometimes even empty URBs)
165cc28a20eSOndrej Zary 	 */
166441993daSOndrej Zary 	dev->net->hard_header_len = 0;
167cc28a20eSOndrej Zary 	/* we can send at most 1514 bytes of data (+ 2-byte header) per URB */
168441993daSOndrej Zary 	dev->hard_mtu = CX82310_MTU + 2;
169cc28a20eSOndrej Zary 	/* we can receive URBs up to 4KB from the device */
170cc28a20eSOndrej Zary 	dev->rx_urb_size = 4096;
171cc28a20eSOndrej Zary 
172cc28a20eSOndrej Zary 	dev->partial_data = (unsigned long) kmalloc(dev->hard_mtu, GFP_KERNEL);
173cc28a20eSOndrej Zary 	if (!dev->partial_data)
174cc28a20eSOndrej Zary 		return -ENOMEM;
175cc28a20eSOndrej Zary 
176ca139d76SOndrej Zary 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
177ca139d76SOndrej Zary 	if (!priv) {
178ca139d76SOndrej Zary 		ret = -ENOMEM;
179ca139d76SOndrej Zary 		goto err_partial;
180ca139d76SOndrej Zary 	}
181ca139d76SOndrej Zary 	dev->driver_priv = priv;
182ca139d76SOndrej Zary 	INIT_WORK(&priv->reenable_work, cx82310_reenable_work);
183ca139d76SOndrej Zary 	priv->dev = dev;
184ca139d76SOndrej Zary 
185f40bff42SOndrej Zary 	/* wait for firmware to become ready (indicated by the link being up) */
186f40bff42SOndrej Zary 	while (--timeout) {
187f40bff42SOndrej Zary 		ret = cx82310_cmd(dev, CMD_GET_LINK_STATUS, true, NULL, 0,
188f40bff42SOndrej Zary 				  link, sizeof(link));
189f40bff42SOndrej Zary 		/* the command can time out during boot - it's not an error */
190f40bff42SOndrej Zary 		if (!ret && link[0] == 1 && link[2] == 1)
191f40bff42SOndrej Zary 			break;
192f40bff42SOndrej Zary 		msleep(500);
1938263d57eSWu Fengguang 	}
194f40bff42SOndrej Zary 	if (!timeout) {
19515f5e48fSOndrej Zary 		netdev_err(dev->net, "firmware not ready in time\n");
1961eca92eeSWenwen Wang 		ret = -ETIMEDOUT;
1971eca92eeSWenwen Wang 		goto err;
198f40bff42SOndrej Zary 	}
199f40bff42SOndrej Zary 
200cc28a20eSOndrej Zary 	/* enable ethernet mode (?) */
201cfbaa8b3SZhang Changzhong 	ret = cx82310_enable_ethernet(dev);
202cfbaa8b3SZhang Changzhong 	if (ret)
203cc28a20eSOndrej Zary 		goto err;
204cc28a20eSOndrej Zary 
205cc28a20eSOndrej Zary 	/* get the MAC address */
206*2674e7eaSJakub Kicinski 	ret = cx82310_cmd(dev, CMD_GET_MAC_ADDR, true, NULL, 0, addr, ETH_ALEN);
207cc28a20eSOndrej Zary 	if (ret) {
20815f5e48fSOndrej Zary 		netdev_err(dev->net, "unable to read MAC address: %d\n", ret);
209cc28a20eSOndrej Zary 		goto err;
210cc28a20eSOndrej Zary 	}
211*2674e7eaSJakub Kicinski 	eth_hw_addr_set(dev->net, addr);
212cc28a20eSOndrej Zary 
213cc28a20eSOndrej Zary 	/* start (does not seem to have any effect?) */
214cc28a20eSOndrej Zary 	ret = cx82310_cmd(dev, CMD_START, false, NULL, 0, NULL, 0);
215cc28a20eSOndrej Zary 	if (ret)
216cc28a20eSOndrej Zary 		goto err;
217cc28a20eSOndrej Zary 
218cc28a20eSOndrej Zary 	return 0;
219cc28a20eSOndrej Zary err:
220ca139d76SOndrej Zary 	kfree(dev->driver_priv);
221ca139d76SOndrej Zary err_partial:
222cc28a20eSOndrej Zary 	kfree((void *)dev->partial_data);
223cc28a20eSOndrej Zary 	return ret;
224cc28a20eSOndrej Zary }
225cc28a20eSOndrej Zary 
cx82310_unbind(struct usbnet * dev,struct usb_interface * intf)226cc28a20eSOndrej Zary static void cx82310_unbind(struct usbnet *dev, struct usb_interface *intf)
227cc28a20eSOndrej Zary {
228ca139d76SOndrej Zary 	struct cx82310_priv *priv = dev->driver_priv;
229ca139d76SOndrej Zary 
230cc28a20eSOndrej Zary 	kfree((void *)dev->partial_data);
231ca139d76SOndrej Zary 	cancel_work_sync(&priv->reenable_work);
232ca139d76SOndrej Zary 	kfree(dev->driver_priv);
233cc28a20eSOndrej Zary }
234cc28a20eSOndrej Zary 
235cc28a20eSOndrej Zary /*
236cc28a20eSOndrej Zary  * RX is NOT easy - we can receive multiple packets per skb, each having 2-byte
237cc28a20eSOndrej Zary  * packet length at the beginning.
238cc28a20eSOndrej Zary  * The last packet might be incomplete (when it crosses the 4KB URB size),
239cc28a20eSOndrej Zary  * continuing in the next skb (without any headers).
240cc28a20eSOndrej Zary  * If a packet has odd length, there is one extra byte at the end (before next
241cc28a20eSOndrej Zary  * packet or at the end of the URB).
242cc28a20eSOndrej Zary  */
cx82310_rx_fixup(struct usbnet * dev,struct sk_buff * skb)243cc28a20eSOndrej Zary static int cx82310_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
244cc28a20eSOndrej Zary {
245cc28a20eSOndrej Zary 	int len;
246cc28a20eSOndrej Zary 	struct sk_buff *skb2;
247ca139d76SOndrej Zary 	struct cx82310_priv *priv = dev->driver_priv;
248cc28a20eSOndrej Zary 
249cc28a20eSOndrej Zary 	/*
250cc28a20eSOndrej Zary 	 * If the last skb ended with an incomplete packet, this skb contains
251cc28a20eSOndrej Zary 	 * end of that packet at the beginning.
252cc28a20eSOndrej Zary 	 */
253cc28a20eSOndrej Zary 	if (dev->partial_rem) {
254cc28a20eSOndrej Zary 		len = dev->partial_len + dev->partial_rem;
255cc28a20eSOndrej Zary 		skb2 = alloc_skb(len, GFP_ATOMIC);
256cc28a20eSOndrej Zary 		if (!skb2)
257cc28a20eSOndrej Zary 			return 0;
258cc28a20eSOndrej Zary 		skb_put(skb2, len);
259cc28a20eSOndrej Zary 		memcpy(skb2->data, (void *)dev->partial_data,
260cc28a20eSOndrej Zary 		       dev->partial_len);
261cc28a20eSOndrej Zary 		memcpy(skb2->data + dev->partial_len, skb->data,
262cc28a20eSOndrej Zary 		       dev->partial_rem);
263cc28a20eSOndrej Zary 		usbnet_skb_return(dev, skb2);
264cc28a20eSOndrej Zary 		skb_pull(skb, (dev->partial_rem + 1) & ~1);
265cc28a20eSOndrej Zary 		dev->partial_rem = 0;
266cc28a20eSOndrej Zary 		if (skb->len < 2)
267cc28a20eSOndrej Zary 			return 1;
268cc28a20eSOndrej Zary 	}
269cc28a20eSOndrej Zary 
270cc28a20eSOndrej Zary 	/* a skb can contain multiple packets */
271cc28a20eSOndrej Zary 	while (skb->len > 1) {
272cc28a20eSOndrej Zary 		/* first two bytes are packet length */
273cc28a20eSOndrej Zary 		len = skb->data[0] | (skb->data[1] << 8);
274cc28a20eSOndrej Zary 		skb_pull(skb, 2);
275cc28a20eSOndrej Zary 
276cc28a20eSOndrej Zary 		/* if last packet in the skb, let usbnet to process it */
277cc28a20eSOndrej Zary 		if (len == skb->len || len + 1 == skb->len) {
278cc28a20eSOndrej Zary 			skb_trim(skb, len);
279cc28a20eSOndrej Zary 			break;
280cc28a20eSOndrej Zary 		}
281cc28a20eSOndrej Zary 
282ca139d76SOndrej Zary 		if (len == 0xffff) {
283ca139d76SOndrej Zary 			netdev_info(dev->net, "router was rebooted, re-enabling ethernet mode");
284ca139d76SOndrej Zary 			schedule_work(&priv->reenable_work);
285ca139d76SOndrej Zary 		} else if (len > CX82310_MTU) {
28615f5e48fSOndrej Zary 			netdev_err(dev->net, "RX packet too long: %d B\n", len);
287cc28a20eSOndrej Zary 			return 0;
288cc28a20eSOndrej Zary 		}
289cc28a20eSOndrej Zary 
290cc28a20eSOndrej Zary 		/* incomplete packet, save it for the next skb */
291cc28a20eSOndrej Zary 		if (len > skb->len) {
292cc28a20eSOndrej Zary 			dev->partial_len = skb->len;
293cc28a20eSOndrej Zary 			dev->partial_rem = len - skb->len;
294cc28a20eSOndrej Zary 			memcpy((void *)dev->partial_data, skb->data,
295cc28a20eSOndrej Zary 			       dev->partial_len);
296cc28a20eSOndrej Zary 			skb_pull(skb, skb->len);
297cc28a20eSOndrej Zary 			break;
298cc28a20eSOndrej Zary 		}
299cc28a20eSOndrej Zary 
300cc28a20eSOndrej Zary 		skb2 = alloc_skb(len, GFP_ATOMIC);
301cc28a20eSOndrej Zary 		if (!skb2)
302cc28a20eSOndrej Zary 			return 0;
303cc28a20eSOndrej Zary 		skb_put(skb2, len);
304cc28a20eSOndrej Zary 		memcpy(skb2->data, skb->data, len);
305cc28a20eSOndrej Zary 		/* process the packet */
306cc28a20eSOndrej Zary 		usbnet_skb_return(dev, skb2);
307cc28a20eSOndrej Zary 
308cc28a20eSOndrej Zary 		skb_pull(skb, (len + 1) & ~1);
309cc28a20eSOndrej Zary 	}
310cc28a20eSOndrej Zary 
311cc28a20eSOndrej Zary 	/* let usbnet process the last packet */
312cc28a20eSOndrej Zary 	return 1;
313cc28a20eSOndrej Zary }
314cc28a20eSOndrej Zary 
315cc28a20eSOndrej Zary /* TX is easy, just add 2 bytes of length at the beginning */
cx82310_tx_fixup(struct usbnet * dev,struct sk_buff * skb,gfp_t flags)316cc28a20eSOndrej Zary static struct sk_buff *cx82310_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
317cc28a20eSOndrej Zary 				       gfp_t flags)
318cc28a20eSOndrej Zary {
319cc28a20eSOndrej Zary 	int len = skb->len;
320cc28a20eSOndrej Zary 
321a9e840a2SEric Dumazet 	if (skb_cow_head(skb, 2)) {
322cc28a20eSOndrej Zary 		dev_kfree_skb_any(skb);
323cc28a20eSOndrej Zary 		return NULL;
324cc28a20eSOndrej Zary 	}
325cc28a20eSOndrej Zary 	skb_push(skb, 2);
326cc28a20eSOndrej Zary 
327cc28a20eSOndrej Zary 	skb->data[0] = len;
328cc28a20eSOndrej Zary 	skb->data[1] = len >> 8;
329cc28a20eSOndrej Zary 
330cc28a20eSOndrej Zary 	return skb;
331cc28a20eSOndrej Zary }
332cc28a20eSOndrej Zary 
333cc28a20eSOndrej Zary 
334cc28a20eSOndrej Zary static const struct driver_info	cx82310_info = {
335cc28a20eSOndrej Zary 	.description	= "Conexant CX82310 USB ethernet",
336cc28a20eSOndrej Zary 	.flags		= FLAG_ETHER,
337cc28a20eSOndrej Zary 	.bind		= cx82310_bind,
338cc28a20eSOndrej Zary 	.unbind		= cx82310_unbind,
339cc28a20eSOndrej Zary 	.rx_fixup	= cx82310_rx_fixup,
340cc28a20eSOndrej Zary 	.tx_fixup	= cx82310_tx_fixup,
341cc28a20eSOndrej Zary };
342cc28a20eSOndrej Zary 
3438d006e01SOndrej Zary #define USB_DEVICE_CLASS(vend, prod, cl, sc, pr) \
3448d006e01SOndrej Zary 	.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
3458d006e01SOndrej Zary 		       USB_DEVICE_ID_MATCH_DEV_INFO, \
3468d006e01SOndrej Zary 	.idVendor = (vend), \
3478d006e01SOndrej Zary 	.idProduct = (prod), \
3488d006e01SOndrej Zary 	.bDeviceClass = (cl), \
3498d006e01SOndrej Zary 	.bDeviceSubClass = (sc), \
3508d006e01SOndrej Zary 	.bDeviceProtocol = (pr)
3518d006e01SOndrej Zary 
352cc28a20eSOndrej Zary static const struct usb_device_id products[] = {
353cc28a20eSOndrej Zary 	{
3548d006e01SOndrej Zary 		USB_DEVICE_CLASS(0x0572, 0xcb01, 0xff, 0, 0),
355cc28a20eSOndrej Zary 		.driver_info = (unsigned long) &cx82310_info
356cc28a20eSOndrej Zary 	},
357cc28a20eSOndrej Zary 	{ },
358cc28a20eSOndrej Zary };
359cc28a20eSOndrej Zary MODULE_DEVICE_TABLE(usb, products);
360cc28a20eSOndrej Zary 
361cc28a20eSOndrej Zary static struct usb_driver cx82310_driver = {
362cc28a20eSOndrej Zary 	.name		= "cx82310_eth",
363cc28a20eSOndrej Zary 	.id_table	= products,
364cc28a20eSOndrej Zary 	.probe		= usbnet_probe,
365cc28a20eSOndrej Zary 	.disconnect	= usbnet_disconnect,
366cc28a20eSOndrej Zary 	.suspend	= usbnet_suspend,
367cc28a20eSOndrej Zary 	.resume		= usbnet_resume,
368e1f12eb6SSarah Sharp 	.disable_hub_initiated_lpm = 1,
369cc28a20eSOndrej Zary };
370cc28a20eSOndrej Zary 
371d632eb1bSGreg Kroah-Hartman module_usb_driver(cx82310_driver);
372cc28a20eSOndrej Zary 
373cc28a20eSOndrej Zary MODULE_AUTHOR("Ondrej Zary");
374cc28a20eSOndrej Zary MODULE_DESCRIPTION("Conexant CX82310-based ADSL router USB ethernet driver");
375cc28a20eSOndrej Zary MODULE_LICENSE("GPL");
376