aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Mohr <andi@lisas.de>2010-01-30 21:58:42 -0500
committerDavid S. Miller <davem@davemloft.net>2010-02-03 21:28:58 -0500
commitace2a4d0fbf868c00625a8fd91c44ad9fabe7012 (patch)
tree64c11f1a46591b13c74a5f8b25858e40fa6c257c
parentc774651a5ffc0250f82d72730753f196c86884c5 (diff)
MCS7830 USB-Ether: resume _with_ working link, via .reset_resume support
ChangeLog: Implement .reset_resume support to retain a live network connection during suspend despite USB power loss. - rework operation to reference cached data in mcs7830_data and netdev->dev_addr - update netdev->dev_addr only in case new MAC was set successfully . Tests done: . ethtool -d pre-/post-suspend: register values match . running ssh session suspend, resume: works . ifdown device, suspend, resume: works . ifup, suspend, unplug, resume: WORKS (eth1 is removed, re-ifup of eth1 after card replug works) . verified identical MAC in ifconfig post-resume (ok, should be verified on network side to be fully certain...) Keywords: suspend resume network connection dead interface down Signed-off-by: Andreas Mohr <andi@lisas.de> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/usb/mcs7830.c190
1 files changed, 123 insertions, 67 deletions
diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c
index 36a029218e3..6fc098fe9ff 100644
--- a/drivers/net/usb/mcs7830.c
+++ b/drivers/net/usb/mcs7830.c
@@ -12,8 +12,6 @@
12 * Definitions gathered from MOSCHIP, Data Sheet_7830DA.pdf (thanks!). 12 * Definitions gathered from MOSCHIP, Data Sheet_7830DA.pdf (thanks!).
13 * 13 *
14 * TODO: 14 * TODO:
15 * - add .reset_resume support (iface is _gone_ after resume w/ power loss)
16 * - verify that mcs7830_get_regs() does have same output pre-/post-suspend
17 * - support HIF_REG_CONFIG_SLEEPMODE/HIF_REG_CONFIG_TXENABLE (via autopm?) 15 * - support HIF_REG_CONFIG_SLEEPMODE/HIF_REG_CONFIG_TXENABLE (via autopm?)
18 * - implement ethtool_ops get_pauseparam/set_pauseparam 16 * - implement ethtool_ops get_pauseparam/set_pauseparam
19 * via HIF_REG_PAUSE_THRESHOLD (>= revision C only!) 17 * via HIF_REG_PAUSE_THRESHOLD (>= revision C only!)
@@ -137,7 +135,7 @@ static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data)
137 return ret; 135 return ret;
138} 136}
139 137
140static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, void *data) 138static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, const void *data)
141{ 139{
142 struct usb_device *xdev = dev->udev; 140 struct usb_device *xdev = dev->udev;
143 int ret; 141 int ret;
@@ -211,13 +209,43 @@ out:
211 usb_free_urb(urb); 209 usb_free_urb(urb);
212} 210}
213 211
214static int mcs7830_get_address(struct usbnet *dev) 212static int mcs7830_hif_get_mac_address(struct usbnet *dev, unsigned char *addr)
213{
214 int ret = mcs7830_get_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr);
215 if (ret < 0)
216 return ret;
217 return 0;
218}
219
220static int mcs7830_hif_set_mac_address(struct usbnet *dev, unsigned char *addr)
221{
222 int ret = mcs7830_set_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr);
223
224 if (ret < 0)
225 return ret;
226 return 0;
227}
228
229static int mcs7830_set_mac_address(struct net_device *netdev, void *p)
215{ 230{
216 int ret; 231 int ret;
217 ret = mcs7830_get_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, 232 struct usbnet *dev = netdev_priv(netdev);
218 dev->net->dev_addr); 233 struct sockaddr *addr = p;
234
235 if (netif_running(netdev))
236 return -EBUSY;
237
238 if (!is_valid_ether_addr(addr->sa_data))
239 return -EINVAL;
240
241 ret = mcs7830_hif_set_mac_address(dev, addr->sa_data);
242
219 if (ret < 0) 243 if (ret < 0)
220 return ret; 244 return ret;
245
246 /* it worked --> adopt it on netdev side */
247 memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
248
221 return 0; 249 return 0;
222} 250}
223 251
@@ -359,33 +387,6 @@ static void mcs7830_rev_C_fixup(struct usbnet *dev)
359 } 387 }
360} 388}
361 389
362static int mcs7830_init_dev(struct usbnet *dev)
363{
364 int ret;
365 int retry;
366
367 /* Read MAC address from EEPROM */
368 ret = -EINVAL;
369 for (retry = 0; retry < 5 && ret; retry++)
370 ret = mcs7830_get_address(dev);
371 if (ret) {
372 dev_warn(&dev->udev->dev, "Cannot read MAC address\n");
373 goto out;
374 }
375
376 /* Set up PHY */
377 ret = mcs7830_set_autoneg(dev, 0);
378 if (ret) {
379 dev_info(&dev->udev->dev, "Cannot set autoneg\n");
380 goto out;
381 }
382
383 mcs7830_rev_C_fixup(dev);
384 ret = 0;
385out:
386 return ret;
387}
388
389static int mcs7830_mdio_read(struct net_device *netdev, int phy_id, 390static int mcs7830_mdio_read(struct net_device *netdev, int phy_id,
390 int location) 391 int location)
391{ 392{
@@ -406,11 +407,33 @@ static int mcs7830_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
406 return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); 407 return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
407} 408}
408 409
409/* credits go to asix_set_multicast */ 410static inline struct mcs7830_data *mcs7830_get_data(struct usbnet *dev)
410static void mcs7830_set_multicast(struct net_device *net) 411{
412 return (struct mcs7830_data *)&dev->data;
413}
414
415static void mcs7830_hif_update_multicast_hash(struct usbnet *dev)
416{
417 struct mcs7830_data *data = mcs7830_get_data(dev);
418 mcs7830_set_reg_async(dev, HIF_REG_MULTICAST_HASH,
419 sizeof data->multi_filter,
420 data->multi_filter);
421}
422
423static void mcs7830_hif_update_config(struct usbnet *dev)
424{
425 /* implementation specific to data->config
426 (argument needs to be heap-based anyway - USB DMA!) */
427 struct mcs7830_data *data = mcs7830_get_data(dev);
428 mcs7830_set_reg_async(dev, HIF_REG_CONFIG, 1, &data->config);
429}
430
431static void mcs7830_data_set_multicast(struct net_device *net)
411{ 432{
412 struct usbnet *dev = netdev_priv(net); 433 struct usbnet *dev = netdev_priv(net);
413 struct mcs7830_data *data = (struct mcs7830_data *)&dev->data; 434 struct mcs7830_data *data = mcs7830_get_data(dev);
435
436 memset(data->multi_filter, 0, sizeof data->multi_filter);
414 437
415 data->config = HIF_REG_CONFIG_TXENABLE; 438 data->config = HIF_REG_CONFIG_TXENABLE;
416 439
@@ -433,21 +456,51 @@ static void mcs7830_set_multicast(struct net_device *net)
433 u32 crc_bits; 456 u32 crc_bits;
434 int i; 457 int i;
435 458
436 memset(data->multi_filter, 0, sizeof data->multi_filter);
437
438 /* Build the multicast hash filter. */ 459 /* Build the multicast hash filter. */
439 for (i = 0; i < net->mc_count; i++) { 460 for (i = 0; i < net->mc_count; i++) {
440 crc_bits = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26; 461 crc_bits = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26;
441 data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7); 462 data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7);
442 mc_list = mc_list->next; 463 mc_list = mc_list->next;
443 } 464 }
465 }
466}
444 467
445 mcs7830_set_reg_async(dev, HIF_REG_MULTICAST_HASH, 468static int mcs7830_apply_base_config(struct usbnet *dev)
446 sizeof data->multi_filter, 469{
447 data->multi_filter); 470 int ret;
471
472 /* re-configure known MAC (suspend case etc.) */
473 ret = mcs7830_hif_set_mac_address(dev, dev->net->dev_addr);
474 if (ret) {
475 dev_info(&dev->udev->dev, "Cannot set MAC address\n");
476 goto out;
448 } 477 }
449 478
450 mcs7830_set_reg_async(dev, HIF_REG_CONFIG, 1, &data->config); 479 /* Set up PHY */
480 ret = mcs7830_set_autoneg(dev, 0);
481 if (ret) {
482 dev_info(&dev->udev->dev, "Cannot set autoneg\n");
483 goto out;
484 }
485
486 mcs7830_hif_update_multicast_hash(dev);
487 mcs7830_hif_update_config(dev);
488
489 mcs7830_rev_C_fixup(dev);
490 ret = 0;
491out:
492 return ret;
493}
494
495/* credits go to asix_set_multicast */
496static void mcs7830_set_multicast(struct net_device *net)
497{
498 struct usbnet *dev = netdev_priv(net);
499
500 mcs7830_data_set_multicast(net);
501
502 mcs7830_hif_update_multicast_hash(dev);
503 mcs7830_hif_update_config(dev);
451} 504}
452 505
453static int mcs7830_get_regs_len(struct net_device *net) 506static int mcs7830_get_regs_len(struct net_device *net)
@@ -491,29 +544,6 @@ static const struct ethtool_ops mcs7830_ethtool_ops = {
491 .nway_reset = usbnet_nway_reset, 544 .nway_reset = usbnet_nway_reset,
492}; 545};
493 546
494static int mcs7830_set_mac_address(struct net_device *netdev, void *p)
495{
496 int ret;
497 struct usbnet *dev = netdev_priv(netdev);
498 struct sockaddr *addr = p;
499
500 if (netif_running(netdev))
501 return -EBUSY;
502
503 if (!is_valid_ether_addr(addr->sa_data))
504 return -EINVAL;
505
506 memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
507
508 ret = mcs7830_set_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN,
509 netdev->dev_addr);
510
511 if (ret < 0)
512 return ret;
513
514 return 0;
515}
516
517static const struct net_device_ops mcs7830_netdev_ops = { 547static const struct net_device_ops mcs7830_netdev_ops = {
518 .ndo_open = usbnet_open, 548 .ndo_open = usbnet_open,
519 .ndo_stop = usbnet_stop, 549 .ndo_stop = usbnet_stop,
@@ -530,14 +560,25 @@ static int mcs7830_bind(struct usbnet *dev, struct usb_interface *udev)
530{ 560{
531 struct net_device *net = dev->net; 561 struct net_device *net = dev->net;
532 int ret; 562 int ret;
563 int retry;
533 564
534 ret = mcs7830_init_dev(dev); 565 /* Initial startup: Gather MAC address setting from EEPROM */
566 ret = -EINVAL;
567 for (retry = 0; retry < 5 && ret; retry++)
568 ret = mcs7830_hif_get_mac_address(dev, net->dev_addr);
569 if (ret) {
570 dev_warn(&dev->udev->dev, "Cannot read MAC address\n");
571 goto out;
572 }
573
574 mcs7830_data_set_multicast(net);
575
576 ret = mcs7830_apply_base_config(dev);
535 if (ret) 577 if (ret)
536 goto out; 578 goto out;
537 579
538 net->ethtool_ops = &mcs7830_ethtool_ops; 580 net->ethtool_ops = &mcs7830_ethtool_ops;
539 net->netdev_ops = &mcs7830_netdev_ops; 581 net->netdev_ops = &mcs7830_netdev_ops;
540 mcs7830_set_multicast(net);
541 582
542 /* reserve space for the status byte on rx */ 583 /* reserve space for the status byte on rx */
543 dev->rx_urb_size = ETH_FRAME_LEN + 1; 584 dev->rx_urb_size = ETH_FRAME_LEN + 1;
@@ -622,6 +663,20 @@ static const struct usb_device_id products[] = {
622}; 663};
623MODULE_DEVICE_TABLE(usb, products); 664MODULE_DEVICE_TABLE(usb, products);
624 665
666static int mcs7830_reset_resume (struct usb_interface *intf)
667{
668 /* YES, this function is successful enough that ethtool -d
669 does show same output pre-/post-suspend */
670
671 struct usbnet *dev = usb_get_intfdata(intf);
672
673 mcs7830_apply_base_config(dev);
674
675 usbnet_resume(intf);
676
677 return 0;
678}
679
625static struct usb_driver mcs7830_driver = { 680static struct usb_driver mcs7830_driver = {
626 .name = driver_name, 681 .name = driver_name,
627 .id_table = products, 682 .id_table = products,
@@ -629,6 +684,7 @@ static struct usb_driver mcs7830_driver = {
629 .disconnect = usbnet_disconnect, 684 .disconnect = usbnet_disconnect,
630 .suspend = usbnet_suspend, 685 .suspend = usbnet_suspend,
631 .resume = usbnet_resume, 686 .resume = usbnet_resume,
687 .reset_resume = mcs7830_reset_resume,
632}; 688};
633 689
634static int __init mcs7830_init(void) 690static int __init mcs7830_init(void)