]> Git Repo - linux.git/blob - drivers/net/usb/mcs7830.c
Merge tag 'net-5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[linux.git] / drivers / net / usb / mcs7830.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * MOSCHIP MCS7830 based (7730/7830/7832) USB 2.0 Ethernet Devices
4  *
5  * based on usbnet.c, asix.c and the vendor provided mcs7830 driver
6  *
7  * Copyright (C) 2010 Andreas Mohr <[email protected]>
8  * Copyright (C) 2006 Arnd Bergmann <[email protected]>
9  * Copyright (C) 2003-2005 David Hollis <[email protected]>
10  * Copyright (C) 2005 Phil Chang <[email protected]>
11  * Copyright (c) 2002-2003 TiVo Inc.
12  *
13  * Definitions gathered from MOSCHIP, Data Sheet_7830DA.pdf (thanks!).
14  *
15  * 2010-12-19: add 7832 USB PID ("functionality same as MCS7830"),
16  *             per active notification by manufacturer
17  *
18  * TODO:
19  * - support HIF_REG_CONFIG_SLEEPMODE/HIF_REG_CONFIG_TXENABLE (via autopm?)
20  * - implement ethtool_ops get_pauseparam/set_pauseparam
21  *   via HIF_REG_PAUSE_THRESHOLD (>= revision C only!)
22  * - implement get_eeprom/[set_eeprom]
23  * - switch PHY on/off on ifup/ifdown (perhaps in usbnet.c, via MII)
24  * - mcs7830_get_regs() handling is weird: for rev 2 we return 32 regs,
25  *   can access only ~ 24, remaining user buffer is uninitialized garbage
26  * - anything else?
27  */
28
29 #include <linux/crc32.h>
30 #include <linux/etherdevice.h>
31 #include <linux/ethtool.h>
32 #include <linux/mii.h>
33 #include <linux/module.h>
34 #include <linux/netdevice.h>
35 #include <linux/slab.h>
36 #include <linux/usb.h>
37 #include <linux/usb/usbnet.h>
38
39 /* requests */
40 #define MCS7830_RD_BMREQ        (USB_DIR_IN  | USB_TYPE_VENDOR | \
41                                  USB_RECIP_DEVICE)
42 #define MCS7830_WR_BMREQ        (USB_DIR_OUT | USB_TYPE_VENDOR | \
43                                  USB_RECIP_DEVICE)
44 #define MCS7830_RD_BREQ         0x0E
45 #define MCS7830_WR_BREQ         0x0D
46
47 #define MCS7830_CTRL_TIMEOUT    1000
48 #define MCS7830_MAX_MCAST       64
49
50 #define MCS7830_VENDOR_ID       0x9710
51 #define MCS7832_PRODUCT_ID      0x7832
52 #define MCS7830_PRODUCT_ID      0x7830
53 #define MCS7730_PRODUCT_ID      0x7730
54
55 #define SITECOM_VENDOR_ID       0x0DF6
56 #define LN_030_PRODUCT_ID       0x0021
57
58 #define MCS7830_MII_ADVERTISE   (ADVERTISE_PAUSE_CAP | ADVERTISE_100FULL | \
59                                  ADVERTISE_100HALF | ADVERTISE_10FULL | \
60                                  ADVERTISE_10HALF | ADVERTISE_CSMA)
61
62 /* HIF_REG_XX corresponding index value */
63 enum {
64         HIF_REG_MULTICAST_HASH                  = 0x00,
65         HIF_REG_PACKET_GAP1                     = 0x08,
66         HIF_REG_PACKET_GAP2                     = 0x09,
67         HIF_REG_PHY_DATA                        = 0x0a,
68         HIF_REG_PHY_CMD1                        = 0x0c,
69            HIF_REG_PHY_CMD1_READ                = 0x40,
70            HIF_REG_PHY_CMD1_WRITE               = 0x20,
71            HIF_REG_PHY_CMD1_PHYADDR             = 0x01,
72         HIF_REG_PHY_CMD2                        = 0x0d,
73            HIF_REG_PHY_CMD2_PEND_FLAG_BIT       = 0x80,
74            HIF_REG_PHY_CMD2_READY_FLAG_BIT      = 0x40,
75         HIF_REG_CONFIG                          = 0x0e,
76         /* hmm, spec sez: "R/W", "Except bit 3" (likely TXENABLE). */
77            HIF_REG_CONFIG_CFG                   = 0x80,
78            HIF_REG_CONFIG_SPEED100              = 0x40,
79            HIF_REG_CONFIG_FULLDUPLEX_ENABLE     = 0x20,
80            HIF_REG_CONFIG_RXENABLE              = 0x10,
81            HIF_REG_CONFIG_TXENABLE              = 0x08,
82            HIF_REG_CONFIG_SLEEPMODE             = 0x04,
83            HIF_REG_CONFIG_ALLMULTICAST          = 0x02,
84            HIF_REG_CONFIG_PROMISCUOUS           = 0x01,
85         HIF_REG_ETHERNET_ADDR                   = 0x0f,
86         HIF_REG_FRAME_DROP_COUNTER              = 0x15, /* 0..ff; reset: 0 */
87         HIF_REG_PAUSE_THRESHOLD                 = 0x16,
88            HIF_REG_PAUSE_THRESHOLD_DEFAULT      = 0,
89 };
90
91 /* Trailing status byte in Ethernet Rx frame */
92 enum {
93         MCS7830_RX_SHORT_FRAME          = 0x01, /* < 64 bytes */
94         MCS7830_RX_LENGTH_ERROR         = 0x02, /* framelen != Ethernet length field */
95         MCS7830_RX_ALIGNMENT_ERROR      = 0x04, /* non-even number of nibbles */
96         MCS7830_RX_CRC_ERROR            = 0x08,
97         MCS7830_RX_LARGE_FRAME          = 0x10, /* > 1518 bytes */
98         MCS7830_RX_FRAME_CORRECT        = 0x20, /* frame is correct */
99         /* [7:6] reserved */
100 };
101
102 struct mcs7830_data {
103         u8 multi_filter[8];
104         u8 config;
105 };
106
107 static const char driver_name[] = "MOSCHIP usb-ethernet driver";
108
109 static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data)
110 {
111         return usbnet_read_cmd(dev, MCS7830_RD_BREQ, MCS7830_RD_BMREQ,
112                                 0x0000, index, data, size);
113 }
114
115 static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, const void *data)
116 {
117         return usbnet_write_cmd(dev, MCS7830_WR_BREQ, MCS7830_WR_BMREQ,
118                                 0x0000, index, data, size);
119 }
120
121 static void mcs7830_set_reg_async(struct usbnet *dev, u16 index, u16 size, void *data)
122 {
123         usbnet_write_cmd_async(dev, MCS7830_WR_BREQ, MCS7830_WR_BMREQ,
124                                 0x0000, index, data, size);
125 }
126
127 static int mcs7830_hif_get_mac_address(struct usbnet *dev, unsigned char *addr)
128 {
129         int ret = mcs7830_get_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr);
130         if (ret < 0)
131                 return ret;
132         return 0;
133 }
134
135 static int mcs7830_hif_set_mac_address(struct usbnet *dev,
136                                        const unsigned char *addr)
137 {
138         int ret = mcs7830_set_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr);
139
140         if (ret < 0)
141                 return ret;
142         return 0;
143 }
144
145 static int mcs7830_set_mac_address(struct net_device *netdev, void *p)
146 {
147         int ret;
148         struct usbnet *dev = netdev_priv(netdev);
149         struct sockaddr *addr = p;
150
151         if (netif_running(netdev))
152                 return -EBUSY;
153
154         if (!is_valid_ether_addr(addr->sa_data))
155                 return -EADDRNOTAVAIL;
156
157         ret = mcs7830_hif_set_mac_address(dev, addr->sa_data);
158
159         if (ret < 0)
160                 return ret;
161
162         /* it worked --> adopt it on netdev side */
163         eth_hw_addr_set(netdev, addr->sa_data);
164
165         return 0;
166 }
167
168 static int mcs7830_read_phy(struct usbnet *dev, u8 index)
169 {
170         int ret;
171         int i;
172         __le16 val;
173
174         u8 cmd[2] = {
175                 HIF_REG_PHY_CMD1_READ | HIF_REG_PHY_CMD1_PHYADDR,
176                 HIF_REG_PHY_CMD2_PEND_FLAG_BIT | index,
177         };
178
179         mutex_lock(&dev->phy_mutex);
180         /* write the MII command */
181         ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
182         if (ret < 0)
183                 goto out;
184
185         /* wait for the data to become valid, should be within < 1ms */
186         for (i = 0; i < 10; i++) {
187                 ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
188                 if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT))
189                         break;
190                 ret = -EIO;
191                 msleep(1);
192         }
193         if (ret < 0)
194                 goto out;
195
196         /* read actual register contents */
197         ret = mcs7830_get_reg(dev, HIF_REG_PHY_DATA, 2, &val);
198         if (ret < 0)
199                 goto out;
200         ret = le16_to_cpu(val);
201         dev_dbg(&dev->udev->dev, "read PHY reg %02x: %04x (%d tries)\n",
202                 index, val, i);
203 out:
204         mutex_unlock(&dev->phy_mutex);
205         return ret;
206 }
207
208 static int mcs7830_write_phy(struct usbnet *dev, u8 index, u16 val)
209 {
210         int ret;
211         int i;
212         __le16 le_val;
213
214         u8 cmd[2] = {
215                 HIF_REG_PHY_CMD1_WRITE | HIF_REG_PHY_CMD1_PHYADDR,
216                 HIF_REG_PHY_CMD2_PEND_FLAG_BIT | (index & 0x1F),
217         };
218
219         mutex_lock(&dev->phy_mutex);
220
221         /* write the new register contents */
222         le_val = cpu_to_le16(val);
223         ret = mcs7830_set_reg(dev, HIF_REG_PHY_DATA, 2, &le_val);
224         if (ret < 0)
225                 goto out;
226
227         /* write the MII command */
228         ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
229         if (ret < 0)
230                 goto out;
231
232         /* wait for the command to be accepted by the PHY */
233         for (i = 0; i < 10; i++) {
234                 ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd);
235                 if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT))
236                         break;
237                 ret = -EIO;
238                 msleep(1);
239         }
240         if (ret < 0)
241                 goto out;
242
243         ret = 0;
244         dev_dbg(&dev->udev->dev, "write PHY reg %02x: %04x (%d tries)\n",
245                 index, val, i);
246 out:
247         mutex_unlock(&dev->phy_mutex);
248         return ret;
249 }
250
251 /*
252  * This algorithm comes from the original mcs7830 version 1.4 driver,
253  * not sure if it is needed.
254  */
255 static int mcs7830_set_autoneg(struct usbnet *dev, int ptrUserPhyMode)
256 {
257         int ret;
258         /* Enable all media types */
259         ret = mcs7830_write_phy(dev, MII_ADVERTISE, MCS7830_MII_ADVERTISE);
260
261         /* First reset BMCR */
262         if (!ret)
263                 ret = mcs7830_write_phy(dev, MII_BMCR, 0x0000);
264         /* Enable Auto Neg */
265         if (!ret)
266                 ret = mcs7830_write_phy(dev, MII_BMCR, BMCR_ANENABLE);
267         /* Restart Auto Neg (Keep the Enable Auto Neg Bit Set) */
268         if (!ret)
269                 ret = mcs7830_write_phy(dev, MII_BMCR,
270                                 BMCR_ANENABLE | BMCR_ANRESTART  );
271         return ret;
272 }
273
274
275 /*
276  * if we can read register 22, the chip revision is C or higher
277  */
278 static int mcs7830_get_rev(struct usbnet *dev)
279 {
280         u8 dummy[2];
281         int ret;
282         ret = mcs7830_get_reg(dev, HIF_REG_FRAME_DROP_COUNTER, 2, dummy);
283         if (ret > 0)
284                 return 2; /* Rev C or later */
285         return 1; /* earlier revision */
286 }
287
288 /*
289  * On rev. C we need to set the pause threshold
290  */
291 static void mcs7830_rev_C_fixup(struct usbnet *dev)
292 {
293         u8 pause_threshold = HIF_REG_PAUSE_THRESHOLD_DEFAULT;
294         int retry;
295
296         for (retry = 0; retry < 2; retry++) {
297                 if (mcs7830_get_rev(dev) == 2) {
298                         dev_info(&dev->udev->dev, "applying rev.C fixup\n");
299                         mcs7830_set_reg(dev, HIF_REG_PAUSE_THRESHOLD,
300                                         1, &pause_threshold);
301                 }
302                 msleep(1);
303         }
304 }
305
306 static int mcs7830_mdio_read(struct net_device *netdev, int phy_id,
307                              int location)
308 {
309         struct usbnet *dev = netdev_priv(netdev);
310         return mcs7830_read_phy(dev, location);
311 }
312
313 static void mcs7830_mdio_write(struct net_device *netdev, int phy_id,
314                                 int location, int val)
315 {
316         struct usbnet *dev = netdev_priv(netdev);
317         mcs7830_write_phy(dev, location, val);
318 }
319
320 static int mcs7830_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
321 {
322         struct usbnet *dev = netdev_priv(net);
323         return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
324 }
325
326 static inline struct mcs7830_data *mcs7830_get_data(struct usbnet *dev)
327 {
328         return (struct mcs7830_data *)&dev->data;
329 }
330
331 static void mcs7830_hif_update_multicast_hash(struct usbnet *dev)
332 {
333         struct mcs7830_data *data = mcs7830_get_data(dev);
334         mcs7830_set_reg_async(dev, HIF_REG_MULTICAST_HASH,
335                                 sizeof data->multi_filter,
336                                 data->multi_filter);
337 }
338
339 static void mcs7830_hif_update_config(struct usbnet *dev)
340 {
341         /* implementation specific to data->config
342            (argument needs to be heap-based anyway - USB DMA!) */
343         struct mcs7830_data *data = mcs7830_get_data(dev);
344         mcs7830_set_reg_async(dev, HIF_REG_CONFIG, 1, &data->config);
345 }
346
347 static void mcs7830_data_set_multicast(struct net_device *net)
348 {
349         struct usbnet *dev = netdev_priv(net);
350         struct mcs7830_data *data = mcs7830_get_data(dev);
351
352         memset(data->multi_filter, 0, sizeof data->multi_filter);
353
354         data->config = HIF_REG_CONFIG_TXENABLE;
355
356         /* this should not be needed, but it doesn't work otherwise */
357         data->config |= HIF_REG_CONFIG_ALLMULTICAST;
358
359         if (net->flags & IFF_PROMISC) {
360                 data->config |= HIF_REG_CONFIG_PROMISCUOUS;
361         } else if (net->flags & IFF_ALLMULTI ||
362                    netdev_mc_count(net) > MCS7830_MAX_MCAST) {
363                 data->config |= HIF_REG_CONFIG_ALLMULTICAST;
364         } else if (netdev_mc_empty(net)) {
365                 /* just broadcast and directed */
366         } else {
367                 /* We use the 20 byte dev->data
368                  * for our 8 byte filter buffer
369                  * to avoid allocating memory that
370                  * is tricky to free later */
371                 struct netdev_hw_addr *ha;
372                 u32 crc_bits;
373
374                 /* Build the multicast hash filter. */
375                 netdev_for_each_mc_addr(ha, net) {
376                         crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
377                         data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7);
378                 }
379         }
380 }
381
382 static int mcs7830_apply_base_config(struct usbnet *dev)
383 {
384         int ret;
385
386         /* re-configure known MAC (suspend case etc.) */
387         ret = mcs7830_hif_set_mac_address(dev, dev->net->dev_addr);
388         if (ret) {
389                 dev_info(&dev->udev->dev, "Cannot set MAC address\n");
390                 goto out;
391         }
392
393         /* Set up PHY */
394         ret = mcs7830_set_autoneg(dev, 0);
395         if (ret) {
396                 dev_info(&dev->udev->dev, "Cannot set autoneg\n");
397                 goto out;
398         }
399
400         mcs7830_hif_update_multicast_hash(dev);
401         mcs7830_hif_update_config(dev);
402
403         mcs7830_rev_C_fixup(dev);
404         ret = 0;
405 out:
406         return ret;
407 }
408
409 /* credits go to asix_set_multicast */
410 static void mcs7830_set_multicast(struct net_device *net)
411 {
412         struct usbnet *dev = netdev_priv(net);
413
414         mcs7830_data_set_multicast(net);
415
416         mcs7830_hif_update_multicast_hash(dev);
417         mcs7830_hif_update_config(dev);
418 }
419
420 static int mcs7830_get_regs_len(struct net_device *net)
421 {
422         struct usbnet *dev = netdev_priv(net);
423
424         switch (mcs7830_get_rev(dev)) {
425         case 1:
426                 return 21;
427         case 2:
428                 return 32;
429         }
430         return 0;
431 }
432
433 static void mcs7830_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *drvinfo)
434 {
435         usbnet_get_drvinfo(net, drvinfo);
436 }
437
438 static void mcs7830_get_regs(struct net_device *net, struct ethtool_regs *regs, void *data)
439 {
440         struct usbnet *dev = netdev_priv(net);
441
442         regs->version = mcs7830_get_rev(dev);
443         mcs7830_get_reg(dev, 0, regs->len, data);
444 }
445
446 static const struct ethtool_ops mcs7830_ethtool_ops = {
447         .get_drvinfo            = mcs7830_get_drvinfo,
448         .get_regs_len           = mcs7830_get_regs_len,
449         .get_regs               = mcs7830_get_regs,
450
451         /* common usbnet calls */
452         .get_link               = usbnet_get_link,
453         .get_msglevel           = usbnet_get_msglevel,
454         .set_msglevel           = usbnet_set_msglevel,
455         .nway_reset             = usbnet_nway_reset,
456         .get_link_ksettings     = usbnet_get_link_ksettings_mii,
457         .set_link_ksettings     = usbnet_set_link_ksettings_mii,
458 };
459
460 static const struct net_device_ops mcs7830_netdev_ops = {
461         .ndo_open               = usbnet_open,
462         .ndo_stop               = usbnet_stop,
463         .ndo_start_xmit         = usbnet_start_xmit,
464         .ndo_tx_timeout         = usbnet_tx_timeout,
465         .ndo_change_mtu         = usbnet_change_mtu,
466         .ndo_get_stats64        = dev_get_tstats64,
467         .ndo_validate_addr      = eth_validate_addr,
468         .ndo_eth_ioctl          = mcs7830_ioctl,
469         .ndo_set_rx_mode        = mcs7830_set_multicast,
470         .ndo_set_mac_address    = mcs7830_set_mac_address,
471 };
472
473 static int mcs7830_bind(struct usbnet *dev, struct usb_interface *udev)
474 {
475         struct net_device *net = dev->net;
476         u8 addr[ETH_ALEN];
477         int ret;
478         int retry;
479
480         /* Initial startup: Gather MAC address setting from EEPROM */
481         ret = -EINVAL;
482         for (retry = 0; retry < 5 && ret; retry++)
483                 ret = mcs7830_hif_get_mac_address(dev, addr);
484         if (ret) {
485                 dev_warn(&dev->udev->dev, "Cannot read MAC address\n");
486                 goto out;
487         }
488         eth_hw_addr_set(net, addr);
489
490         mcs7830_data_set_multicast(net);
491
492         ret = mcs7830_apply_base_config(dev);
493         if (ret)
494                 goto out;
495
496         net->ethtool_ops = &mcs7830_ethtool_ops;
497         net->netdev_ops = &mcs7830_netdev_ops;
498
499         /* reserve space for the status byte on rx */
500         dev->rx_urb_size = ETH_FRAME_LEN + 1;
501
502         dev->mii.mdio_read = mcs7830_mdio_read;
503         dev->mii.mdio_write = mcs7830_mdio_write;
504         dev->mii.dev = net;
505         dev->mii.phy_id_mask = 0x3f;
506         dev->mii.reg_num_mask = 0x1f;
507         dev->mii.phy_id = *((u8 *) net->dev_addr + 1);
508
509         ret = usbnet_get_endpoints(dev, udev);
510 out:
511         return ret;
512 }
513
514 /* The chip always appends a status byte that we need to strip */
515 static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
516 {
517         u8 status;
518
519         /* This check is no longer done by usbnet */
520         if (skb->len < dev->net->hard_header_len) {
521                 dev_err(&dev->udev->dev, "unexpected tiny rx frame\n");
522                 return 0;
523         }
524
525         skb_trim(skb, skb->len - 1);
526         status = skb->data[skb->len];
527
528         if (status != MCS7830_RX_FRAME_CORRECT) {
529                 dev_dbg(&dev->udev->dev, "rx fixup status %x\n", status);
530
531                 /* hmm, perhaps usbnet.c already sees a globally visible
532                    frame error and increments rx_errors on its own already? */
533                 dev->net->stats.rx_errors++;
534
535                 if (status &    (MCS7830_RX_SHORT_FRAME
536                                 |MCS7830_RX_LENGTH_ERROR
537                                 |MCS7830_RX_LARGE_FRAME))
538                         dev->net->stats.rx_length_errors++;
539                 if (status & MCS7830_RX_ALIGNMENT_ERROR)
540                         dev->net->stats.rx_frame_errors++;
541                 if (status & MCS7830_RX_CRC_ERROR)
542                         dev->net->stats.rx_crc_errors++;
543         }
544
545         return skb->len > 0;
546 }
547
548 static void mcs7830_status(struct usbnet *dev, struct urb *urb)
549 {
550         u8 *buf = urb->transfer_buffer;
551         bool link, link_changed;
552
553         if (urb->actual_length < 16)
554                 return;
555
556         link = !(buf[1] == 0x20);
557         link_changed = netif_carrier_ok(dev->net) != link;
558         if (link_changed) {
559                 usbnet_link_change(dev, link, 0);
560                 netdev_dbg(dev->net, "Link Status is: %d\n", link);
561         }
562 }
563
564 static const struct driver_info moschip_info = {
565         .description    = "MOSCHIP 7830/7832/7730 usb-NET adapter",
566         .bind           = mcs7830_bind,
567         .rx_fixup       = mcs7830_rx_fixup,
568         .flags          = FLAG_ETHER | FLAG_LINK_INTR,
569         .status         = mcs7830_status,
570         .in             = 1,
571         .out            = 2,
572 };
573
574 static const struct driver_info sitecom_info = {
575         .description    = "Sitecom LN-30 usb-NET adapter",
576         .bind           = mcs7830_bind,
577         .rx_fixup       = mcs7830_rx_fixup,
578         .flags          = FLAG_ETHER | FLAG_LINK_INTR,
579         .status         = mcs7830_status,
580         .in             = 1,
581         .out            = 2,
582 };
583
584 static const struct usb_device_id products[] = {
585         {
586                 USB_DEVICE(MCS7830_VENDOR_ID, MCS7832_PRODUCT_ID),
587                 .driver_info = (unsigned long) &moschip_info,
588         },
589         {
590                 USB_DEVICE(MCS7830_VENDOR_ID, MCS7830_PRODUCT_ID),
591                 .driver_info = (unsigned long) &moschip_info,
592         },
593         {
594                 USB_DEVICE(MCS7830_VENDOR_ID, MCS7730_PRODUCT_ID),
595                 .driver_info = (unsigned long) &moschip_info,
596         },
597         {
598                 USB_DEVICE(SITECOM_VENDOR_ID, LN_030_PRODUCT_ID),
599                 .driver_info = (unsigned long) &sitecom_info,
600         },
601         {},
602 };
603 MODULE_DEVICE_TABLE(usb, products);
604
605 static int mcs7830_reset_resume (struct usb_interface *intf)
606 {
607         /* YES, this function is successful enough that ethtool -d
608            does show same output pre-/post-suspend */
609
610         struct usbnet           *dev = usb_get_intfdata(intf);
611
612         mcs7830_apply_base_config(dev);
613
614         usbnet_resume(intf);
615
616         return 0;
617 }
618
619 static struct usb_driver mcs7830_driver = {
620         .name = driver_name,
621         .id_table = products,
622         .probe = usbnet_probe,
623         .disconnect = usbnet_disconnect,
624         .suspend = usbnet_suspend,
625         .resume = usbnet_resume,
626         .reset_resume = mcs7830_reset_resume,
627         .disable_hub_initiated_lpm = 1,
628 };
629
630 module_usb_driver(mcs7830_driver);
631
632 MODULE_DESCRIPTION("USB to network adapter MCS7830)");
633 MODULE_LICENSE("GPL");
This page took 0.071715 seconds and 4 git commands to generate.