diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /drivers/net/usb/mcs7830.c | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'drivers/net/usb/mcs7830.c')
-rw-r--r-- | drivers/net/usb/mcs7830.c | 259 |
1 files changed, 178 insertions, 81 deletions
diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index 10873d96b2da..9f24e3f871e1 100644 --- a/drivers/net/usb/mcs7830.c +++ b/drivers/net/usb/mcs7830.c | |||
@@ -1,13 +1,27 @@ | |||
1 | /* | 1 | /* |
2 | * MosChips MCS7830 based USB 2.0 Ethernet Devices | 2 | * MOSCHIP MCS7830 based USB 2.0 Ethernet Devices |
3 | * | 3 | * |
4 | * based on usbnet.c, asix.c and the vendor provided mcs7830 driver | 4 | * based on usbnet.c, asix.c and the vendor provided mcs7830 driver |
5 | * | 5 | * |
6 | * Copyright (C) 2010 Andreas Mohr <andi@lisas.de> | ||
6 | * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de> | 7 | * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de> |
7 | * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com> | 8 | * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com> |
8 | * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> | 9 | * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net> |
9 | * Copyright (c) 2002-2003 TiVo Inc. | 10 | * Copyright (c) 2002-2003 TiVo Inc. |
10 | * | 11 | * |
12 | * Definitions gathered from MOSCHIP, Data Sheet_7830DA.pdf (thanks!). | ||
13 | * | ||
14 | * TODO: | ||
15 | * - support HIF_REG_CONFIG_SLEEPMODE/HIF_REG_CONFIG_TXENABLE (via autopm?) | ||
16 | * - implement ethtool_ops get_pauseparam/set_pauseparam | ||
17 | * via HIF_REG_PAUSE_THRESHOLD (>= revision C only!) | ||
18 | * - implement get_eeprom/[set_eeprom] | ||
19 | * - switch PHY on/off on ifup/ifdown (perhaps in usbnet.c, via MII) | ||
20 | * - mcs7830_get_regs() handling is weird: for rev 2 we return 32 regs, | ||
21 | * can access only ~ 24, remaining user buffer is uninitialized garbage | ||
22 | * - anything else? | ||
23 | * | ||
24 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | 25 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License as published by | 26 | * it under the terms of the GNU General Public License as published by |
13 | * the Free Software Foundation; either version 2 of the License, or | 27 | * the Free Software Foundation; either version 2 of the License, or |
@@ -30,6 +44,7 @@ | |||
30 | #include <linux/mii.h> | 44 | #include <linux/mii.h> |
31 | #include <linux/module.h> | 45 | #include <linux/module.h> |
32 | #include <linux/netdevice.h> | 46 | #include <linux/netdevice.h> |
47 | #include <linux/slab.h> | ||
33 | #include <linux/usb.h> | 48 | #include <linux/usb.h> |
34 | #include <linux/usb/usbnet.h> | 49 | #include <linux/usb/usbnet.h> |
35 | 50 | ||
@@ -55,7 +70,7 @@ | |||
55 | ADVERTISE_100HALF | ADVERTISE_10FULL | \ | 70 | ADVERTISE_100HALF | ADVERTISE_10FULL | \ |
56 | ADVERTISE_10HALF | ADVERTISE_CSMA) | 71 | ADVERTISE_10HALF | ADVERTISE_CSMA) |
57 | 72 | ||
58 | /* HIF_REG_XX coressponding index value */ | 73 | /* HIF_REG_XX corresponding index value */ |
59 | enum { | 74 | enum { |
60 | HIF_REG_MULTICAST_HASH = 0x00, | 75 | HIF_REG_MULTICAST_HASH = 0x00, |
61 | HIF_REG_PACKET_GAP1 = 0x08, | 76 | HIF_REG_PACKET_GAP1 = 0x08, |
@@ -69,6 +84,7 @@ enum { | |||
69 | HIF_REG_PHY_CMD2_PEND_FLAG_BIT = 0x80, | 84 | HIF_REG_PHY_CMD2_PEND_FLAG_BIT = 0x80, |
70 | HIF_REG_PHY_CMD2_READY_FLAG_BIT = 0x40, | 85 | HIF_REG_PHY_CMD2_READY_FLAG_BIT = 0x40, |
71 | HIF_REG_CONFIG = 0x0e, | 86 | HIF_REG_CONFIG = 0x0e, |
87 | /* hmm, spec sez: "R/W", "Except bit 3" (likely TXENABLE). */ | ||
72 | HIF_REG_CONFIG_CFG = 0x80, | 88 | HIF_REG_CONFIG_CFG = 0x80, |
73 | HIF_REG_CONFIG_SPEED100 = 0x40, | 89 | HIF_REG_CONFIG_SPEED100 = 0x40, |
74 | HIF_REG_CONFIG_FULLDUPLEX_ENABLE = 0x20, | 90 | HIF_REG_CONFIG_FULLDUPLEX_ENABLE = 0x20, |
@@ -76,13 +92,24 @@ enum { | |||
76 | HIF_REG_CONFIG_TXENABLE = 0x08, | 92 | HIF_REG_CONFIG_TXENABLE = 0x08, |
77 | HIF_REG_CONFIG_SLEEPMODE = 0x04, | 93 | HIF_REG_CONFIG_SLEEPMODE = 0x04, |
78 | HIF_REG_CONFIG_ALLMULTICAST = 0x02, | 94 | HIF_REG_CONFIG_ALLMULTICAST = 0x02, |
79 | HIF_REG_CONFIG_PROMISCIOUS = 0x01, | 95 | HIF_REG_CONFIG_PROMISCUOUS = 0x01, |
80 | HIF_REG_ETHERNET_ADDR = 0x0f, | 96 | HIF_REG_ETHERNET_ADDR = 0x0f, |
81 | HIF_REG_22 = 0x15, | 97 | HIF_REG_FRAME_DROP_COUNTER = 0x15, /* 0..ff; reset: 0 */ |
82 | HIF_REG_PAUSE_THRESHOLD = 0x16, | 98 | HIF_REG_PAUSE_THRESHOLD = 0x16, |
83 | HIF_REG_PAUSE_THRESHOLD_DEFAULT = 0, | 99 | HIF_REG_PAUSE_THRESHOLD_DEFAULT = 0, |
84 | }; | 100 | }; |
85 | 101 | ||
102 | /* Trailing status byte in Ethernet Rx frame */ | ||
103 | enum { | ||
104 | MCS7830_RX_SHORT_FRAME = 0x01, /* < 64 bytes */ | ||
105 | MCS7830_RX_LENGTH_ERROR = 0x02, /* framelen != Ethernet length field */ | ||
106 | MCS7830_RX_ALIGNMENT_ERROR = 0x04, /* non-even number of nibbles */ | ||
107 | MCS7830_RX_CRC_ERROR = 0x08, | ||
108 | MCS7830_RX_LARGE_FRAME = 0x10, /* > 1518 bytes */ | ||
109 | MCS7830_RX_FRAME_CORRECT = 0x20, /* frame is correct */ | ||
110 | /* [7:6] reserved */ | ||
111 | }; | ||
112 | |||
86 | struct mcs7830_data { | 113 | struct mcs7830_data { |
87 | u8 multi_filter[8]; | 114 | u8 multi_filter[8]; |
88 | u8 config; | 115 | u8 config; |
@@ -109,7 +136,7 @@ static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data) | |||
109 | return ret; | 136 | return ret; |
110 | } | 137 | } |
111 | 138 | ||
112 | static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, void *data) | 139 | static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, const void *data) |
113 | { | 140 | { |
114 | struct usb_device *xdev = dev->udev; | 141 | struct usb_device *xdev = dev->udev; |
115 | int ret; | 142 | int ret; |
@@ -183,13 +210,43 @@ out: | |||
183 | usb_free_urb(urb); | 210 | usb_free_urb(urb); |
184 | } | 211 | } |
185 | 212 | ||
186 | static int mcs7830_get_address(struct usbnet *dev) | 213 | static int mcs7830_hif_get_mac_address(struct usbnet *dev, unsigned char *addr) |
214 | { | ||
215 | int ret = mcs7830_get_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr); | ||
216 | if (ret < 0) | ||
217 | return ret; | ||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static int mcs7830_hif_set_mac_address(struct usbnet *dev, unsigned char *addr) | ||
222 | { | ||
223 | int ret = mcs7830_set_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr); | ||
224 | |||
225 | if (ret < 0) | ||
226 | return ret; | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static int mcs7830_set_mac_address(struct net_device *netdev, void *p) | ||
187 | { | 231 | { |
188 | int ret; | 232 | int ret; |
189 | ret = mcs7830_get_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, | 233 | struct usbnet *dev = netdev_priv(netdev); |
190 | dev->net->dev_addr); | 234 | struct sockaddr *addr = p; |
235 | |||
236 | if (netif_running(netdev)) | ||
237 | return -EBUSY; | ||
238 | |||
239 | if (!is_valid_ether_addr(addr->sa_data)) | ||
240 | return -EINVAL; | ||
241 | |||
242 | ret = mcs7830_hif_set_mac_address(dev, addr->sa_data); | ||
243 | |||
191 | if (ret < 0) | 244 | if (ret < 0) |
192 | return ret; | 245 | return ret; |
246 | |||
247 | /* it worked --> adopt it on netdev side */ | ||
248 | memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); | ||
249 | |||
193 | return 0; | 250 | return 0; |
194 | } | 251 | } |
195 | 252 | ||
@@ -307,7 +364,7 @@ static int mcs7830_get_rev(struct usbnet *dev) | |||
307 | { | 364 | { |
308 | u8 dummy[2]; | 365 | u8 dummy[2]; |
309 | int ret; | 366 | int ret; |
310 | ret = mcs7830_get_reg(dev, HIF_REG_22, 2, dummy); | 367 | ret = mcs7830_get_reg(dev, HIF_REG_FRAME_DROP_COUNTER, 2, dummy); |
311 | if (ret > 0) | 368 | if (ret > 0) |
312 | return 2; /* Rev C or later */ | 369 | return 2; /* Rev C or later */ |
313 | return 1; /* earlier revision */ | 370 | return 1; /* earlier revision */ |
@@ -331,33 +388,6 @@ static void mcs7830_rev_C_fixup(struct usbnet *dev) | |||
331 | } | 388 | } |
332 | } | 389 | } |
333 | 390 | ||
334 | static int mcs7830_init_dev(struct usbnet *dev) | ||
335 | { | ||
336 | int ret; | ||
337 | int retry; | ||
338 | |||
339 | /* Read MAC address from EEPROM */ | ||
340 | ret = -EINVAL; | ||
341 | for (retry = 0; retry < 5 && ret; retry++) | ||
342 | ret = mcs7830_get_address(dev); | ||
343 | if (ret) { | ||
344 | dev_warn(&dev->udev->dev, "Cannot read MAC address\n"); | ||
345 | goto out; | ||
346 | } | ||
347 | |||
348 | /* Set up PHY */ | ||
349 | ret = mcs7830_set_autoneg(dev, 0); | ||
350 | if (ret) { | ||
351 | dev_info(&dev->udev->dev, "Cannot set autoneg\n"); | ||
352 | goto out; | ||
353 | } | ||
354 | |||
355 | mcs7830_rev_C_fixup(dev); | ||
356 | ret = 0; | ||
357 | out: | ||
358 | return ret; | ||
359 | } | ||
360 | |||
361 | static int mcs7830_mdio_read(struct net_device *netdev, int phy_id, | 391 | static int mcs7830_mdio_read(struct net_device *netdev, int phy_id, |
362 | int location) | 392 | int location) |
363 | { | 393 | { |
@@ -378,11 +408,33 @@ static int mcs7830_ioctl(struct net_device *net, struct ifreq *rq, int cmd) | |||
378 | return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); | 408 | return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); |
379 | } | 409 | } |
380 | 410 | ||
381 | /* credits go to asix_set_multicast */ | 411 | static inline struct mcs7830_data *mcs7830_get_data(struct usbnet *dev) |
382 | static void mcs7830_set_multicast(struct net_device *net) | 412 | { |
413 | return (struct mcs7830_data *)&dev->data; | ||
414 | } | ||
415 | |||
416 | static void mcs7830_hif_update_multicast_hash(struct usbnet *dev) | ||
417 | { | ||
418 | struct mcs7830_data *data = mcs7830_get_data(dev); | ||
419 | mcs7830_set_reg_async(dev, HIF_REG_MULTICAST_HASH, | ||
420 | sizeof data->multi_filter, | ||
421 | data->multi_filter); | ||
422 | } | ||
423 | |||
424 | static void mcs7830_hif_update_config(struct usbnet *dev) | ||
425 | { | ||
426 | /* implementation specific to data->config | ||
427 | (argument needs to be heap-based anyway - USB DMA!) */ | ||
428 | struct mcs7830_data *data = mcs7830_get_data(dev); | ||
429 | mcs7830_set_reg_async(dev, HIF_REG_CONFIG, 1, &data->config); | ||
430 | } | ||
431 | |||
432 | static void mcs7830_data_set_multicast(struct net_device *net) | ||
383 | { | 433 | { |
384 | struct usbnet *dev = netdev_priv(net); | 434 | struct usbnet *dev = netdev_priv(net); |
385 | struct mcs7830_data *data = (struct mcs7830_data *)&dev->data; | 435 | struct mcs7830_data *data = mcs7830_get_data(dev); |
436 | |||
437 | memset(data->multi_filter, 0, sizeof data->multi_filter); | ||
386 | 438 | ||
387 | data->config = HIF_REG_CONFIG_TXENABLE; | 439 | data->config = HIF_REG_CONFIG_TXENABLE; |
388 | 440 | ||
@@ -390,36 +442,64 @@ static void mcs7830_set_multicast(struct net_device *net) | |||
390 | data->config |= HIF_REG_CONFIG_ALLMULTICAST; | 442 | data->config |= HIF_REG_CONFIG_ALLMULTICAST; |
391 | 443 | ||
392 | if (net->flags & IFF_PROMISC) { | 444 | if (net->flags & IFF_PROMISC) { |
393 | data->config |= HIF_REG_CONFIG_PROMISCIOUS; | 445 | data->config |= HIF_REG_CONFIG_PROMISCUOUS; |
394 | } else if (net->flags & IFF_ALLMULTI | 446 | } else if (net->flags & IFF_ALLMULTI || |
395 | || net->mc_count > MCS7830_MAX_MCAST) { | 447 | netdev_mc_count(net) > MCS7830_MAX_MCAST) { |
396 | data->config |= HIF_REG_CONFIG_ALLMULTICAST; | 448 | data->config |= HIF_REG_CONFIG_ALLMULTICAST; |
397 | } else if (net->mc_count == 0) { | 449 | } else if (netdev_mc_empty(net)) { |
398 | /* just broadcast and directed */ | 450 | /* just broadcast and directed */ |
399 | } else { | 451 | } else { |
400 | /* We use the 20 byte dev->data | 452 | /* We use the 20 byte dev->data |
401 | * for our 8 byte filter buffer | 453 | * for our 8 byte filter buffer |
402 | * to avoid allocating memory that | 454 | * to avoid allocating memory that |
403 | * is tricky to free later */ | 455 | * is tricky to free later */ |
404 | struct dev_mc_list *mc_list = net->mc_list; | 456 | struct dev_mc_list *mc_list; |
405 | u32 crc_bits; | 457 | u32 crc_bits; |
406 | int i; | ||
407 | |||
408 | memset(data->multi_filter, 0, sizeof data->multi_filter); | ||
409 | 458 | ||
410 | /* Build the multicast hash filter. */ | 459 | /* Build the multicast hash filter. */ |
411 | for (i = 0; i < net->mc_count; i++) { | 460 | netdev_for_each_mc_addr(mc_list, net) { |
412 | crc_bits = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26; | 461 | crc_bits = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26; |
413 | data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7); | 462 | data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7); |
414 | mc_list = mc_list->next; | ||
415 | } | 463 | } |
464 | } | ||
465 | } | ||
416 | 466 | ||
417 | mcs7830_set_reg_async(dev, HIF_REG_MULTICAST_HASH, | 467 | static int mcs7830_apply_base_config(struct usbnet *dev) |
418 | sizeof data->multi_filter, | 468 | { |
419 | data->multi_filter); | 469 | int ret; |
470 | |||
471 | /* re-configure known MAC (suspend case etc.) */ | ||
472 | ret = mcs7830_hif_set_mac_address(dev, dev->net->dev_addr); | ||
473 | if (ret) { | ||
474 | dev_info(&dev->udev->dev, "Cannot set MAC address\n"); | ||
475 | goto out; | ||
420 | } | 476 | } |
421 | 477 | ||
422 | mcs7830_set_reg_async(dev, HIF_REG_CONFIG, 1, &data->config); | 478 | /* Set up PHY */ |
479 | ret = mcs7830_set_autoneg(dev, 0); | ||
480 | if (ret) { | ||
481 | dev_info(&dev->udev->dev, "Cannot set autoneg\n"); | ||
482 | goto out; | ||
483 | } | ||
484 | |||
485 | mcs7830_hif_update_multicast_hash(dev); | ||
486 | mcs7830_hif_update_config(dev); | ||
487 | |||
488 | mcs7830_rev_C_fixup(dev); | ||
489 | ret = 0; | ||
490 | out: | ||
491 | return ret; | ||
492 | } | ||
493 | |||
494 | /* credits go to asix_set_multicast */ | ||
495 | static void mcs7830_set_multicast(struct net_device *net) | ||
496 | { | ||
497 | struct usbnet *dev = netdev_priv(net); | ||
498 | |||
499 | mcs7830_data_set_multicast(net); | ||
500 | |||
501 | mcs7830_hif_update_multicast_hash(dev); | ||
502 | mcs7830_hif_update_config(dev); | ||
423 | } | 503 | } |
424 | 504 | ||
425 | static int mcs7830_get_regs_len(struct net_device *net) | 505 | static int mcs7830_get_regs_len(struct net_device *net) |
@@ -463,29 +543,6 @@ static const struct ethtool_ops mcs7830_ethtool_ops = { | |||
463 | .nway_reset = usbnet_nway_reset, | 543 | .nway_reset = usbnet_nway_reset, |
464 | }; | 544 | }; |
465 | 545 | ||
466 | static int mcs7830_set_mac_address(struct net_device *netdev, void *p) | ||
467 | { | ||
468 | int ret; | ||
469 | struct usbnet *dev = netdev_priv(netdev); | ||
470 | struct sockaddr *addr = p; | ||
471 | |||
472 | if (netif_running(netdev)) | ||
473 | return -EBUSY; | ||
474 | |||
475 | if (!is_valid_ether_addr(addr->sa_data)) | ||
476 | return -EINVAL; | ||
477 | |||
478 | memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); | ||
479 | |||
480 | ret = mcs7830_set_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, | ||
481 | netdev->dev_addr); | ||
482 | |||
483 | if (ret < 0) | ||
484 | return ret; | ||
485 | |||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | static const struct net_device_ops mcs7830_netdev_ops = { | 546 | static const struct net_device_ops mcs7830_netdev_ops = { |
490 | .ndo_open = usbnet_open, | 547 | .ndo_open = usbnet_open, |
491 | .ndo_stop = usbnet_stop, | 548 | .ndo_stop = usbnet_stop, |
@@ -495,21 +552,32 @@ static const struct net_device_ops mcs7830_netdev_ops = { | |||
495 | .ndo_validate_addr = eth_validate_addr, | 552 | .ndo_validate_addr = eth_validate_addr, |
496 | .ndo_do_ioctl = mcs7830_ioctl, | 553 | .ndo_do_ioctl = mcs7830_ioctl, |
497 | .ndo_set_multicast_list = mcs7830_set_multicast, | 554 | .ndo_set_multicast_list = mcs7830_set_multicast, |
498 | .ndo_set_mac_address = mcs7830_set_mac_address, | 555 | .ndo_set_mac_address = mcs7830_set_mac_address, |
499 | }; | 556 | }; |
500 | 557 | ||
501 | static int mcs7830_bind(struct usbnet *dev, struct usb_interface *udev) | 558 | static int mcs7830_bind(struct usbnet *dev, struct usb_interface *udev) |
502 | { | 559 | { |
503 | struct net_device *net = dev->net; | 560 | struct net_device *net = dev->net; |
504 | int ret; | 561 | int ret; |
562 | int retry; | ||
505 | 563 | ||
506 | ret = mcs7830_init_dev(dev); | 564 | /* Initial startup: Gather MAC address setting from EEPROM */ |
565 | ret = -EINVAL; | ||
566 | for (retry = 0; retry < 5 && ret; retry++) | ||
567 | ret = mcs7830_hif_get_mac_address(dev, net->dev_addr); | ||
568 | if (ret) { | ||
569 | dev_warn(&dev->udev->dev, "Cannot read MAC address\n"); | ||
570 | goto out; | ||
571 | } | ||
572 | |||
573 | mcs7830_data_set_multicast(net); | ||
574 | |||
575 | ret = mcs7830_apply_base_config(dev); | ||
507 | if (ret) | 576 | if (ret) |
508 | goto out; | 577 | goto out; |
509 | 578 | ||
510 | net->ethtool_ops = &mcs7830_ethtool_ops; | 579 | net->ethtool_ops = &mcs7830_ethtool_ops; |
511 | net->netdev_ops = &mcs7830_netdev_ops; | 580 | net->netdev_ops = &mcs7830_netdev_ops; |
512 | mcs7830_set_multicast(net); | ||
513 | 581 | ||
514 | /* reserve space for the status byte on rx */ | 582 | /* reserve space for the status byte on rx */ |
515 | dev->rx_urb_size = ETH_FRAME_LEN + 1; | 583 | dev->rx_urb_size = ETH_FRAME_LEN + 1; |
@@ -526,7 +594,7 @@ out: | |||
526 | return ret; | 594 | return ret; |
527 | } | 595 | } |
528 | 596 | ||
529 | /* The chip always appends a status bytes that we need to strip */ | 597 | /* The chip always appends a status byte that we need to strip */ |
530 | static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb) | 598 | static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb) |
531 | { | 599 | { |
532 | u8 status; | 600 | u8 status; |
@@ -539,9 +607,23 @@ static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb) | |||
539 | skb_trim(skb, skb->len - 1); | 607 | skb_trim(skb, skb->len - 1); |
540 | status = skb->data[skb->len]; | 608 | status = skb->data[skb->len]; |
541 | 609 | ||
542 | if (status != 0x20) | 610 | if (status != MCS7830_RX_FRAME_CORRECT) { |
543 | dev_dbg(&dev->udev->dev, "rx fixup status %x\n", status); | 611 | dev_dbg(&dev->udev->dev, "rx fixup status %x\n", status); |
544 | 612 | ||
613 | /* hmm, perhaps usbnet.c already sees a globally visible | ||
614 | frame error and increments rx_errors on its own already? */ | ||
615 | dev->net->stats.rx_errors++; | ||
616 | |||
617 | if (status & (MCS7830_RX_SHORT_FRAME | ||
618 | |MCS7830_RX_LENGTH_ERROR | ||
619 | |MCS7830_RX_LARGE_FRAME)) | ||
620 | dev->net->stats.rx_length_errors++; | ||
621 | if (status & MCS7830_RX_ALIGNMENT_ERROR) | ||
622 | dev->net->stats.rx_frame_errors++; | ||
623 | if (status & MCS7830_RX_CRC_ERROR) | ||
624 | dev->net->stats.rx_crc_errors++; | ||
625 | } | ||
626 | |||
545 | return skb->len > 0; | 627 | return skb->len > 0; |
546 | } | 628 | } |
547 | 629 | ||
@@ -580,6 +662,20 @@ static const struct usb_device_id products[] = { | |||
580 | }; | 662 | }; |
581 | MODULE_DEVICE_TABLE(usb, products); | 663 | MODULE_DEVICE_TABLE(usb, products); |
582 | 664 | ||
665 | static int mcs7830_reset_resume (struct usb_interface *intf) | ||
666 | { | ||
667 | /* YES, this function is successful enough that ethtool -d | ||
668 | does show same output pre-/post-suspend */ | ||
669 | |||
670 | struct usbnet *dev = usb_get_intfdata(intf); | ||
671 | |||
672 | mcs7830_apply_base_config(dev); | ||
673 | |||
674 | usbnet_resume(intf); | ||
675 | |||
676 | return 0; | ||
677 | } | ||
678 | |||
583 | static struct usb_driver mcs7830_driver = { | 679 | static struct usb_driver mcs7830_driver = { |
584 | .name = driver_name, | 680 | .name = driver_name, |
585 | .id_table = products, | 681 | .id_table = products, |
@@ -587,6 +683,7 @@ static struct usb_driver mcs7830_driver = { | |||
587 | .disconnect = usbnet_disconnect, | 683 | .disconnect = usbnet_disconnect, |
588 | .suspend = usbnet_suspend, | 684 | .suspend = usbnet_suspend, |
589 | .resume = usbnet_resume, | 685 | .resume = usbnet_resume, |
686 | .reset_resume = mcs7830_reset_resume, | ||
590 | }; | 687 | }; |
591 | 688 | ||
592 | static int __init mcs7830_init(void) | 689 | static int __init mcs7830_init(void) |