diff options
Diffstat (limited to 'drivers/net/phy/phy_device.c')
-rw-r--r-- | drivers/net/phy/phy_device.c | 62 |
1 files changed, 48 insertions, 14 deletions
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index c0f211127274..f761288abe66 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c | |||
@@ -384,6 +384,24 @@ int phy_device_register(struct phy_device *phydev) | |||
384 | EXPORT_SYMBOL(phy_device_register); | 384 | EXPORT_SYMBOL(phy_device_register); |
385 | 385 | ||
386 | /** | 386 | /** |
387 | * phy_device_remove - Remove a previously registered phy device from the MDIO bus | ||
388 | * @phydev: phy_device structure to remove | ||
389 | * | ||
390 | * This doesn't free the phy_device itself, it merely reverses the effects | ||
391 | * of phy_device_register(). Use phy_device_free() to free the device | ||
392 | * after calling this function. | ||
393 | */ | ||
394 | void phy_device_remove(struct phy_device *phydev) | ||
395 | { | ||
396 | struct mii_bus *bus = phydev->bus; | ||
397 | int addr = phydev->addr; | ||
398 | |||
399 | device_del(&phydev->dev); | ||
400 | bus->phy_map[addr] = NULL; | ||
401 | } | ||
402 | EXPORT_SYMBOL(phy_device_remove); | ||
403 | |||
404 | /** | ||
387 | * phy_find_first - finds the first PHY device on the bus | 405 | * phy_find_first - finds the first PHY device on the bus |
388 | * @bus: the target MII bus | 406 | * @bus: the target MII bus |
389 | */ | 407 | */ |
@@ -578,14 +596,22 @@ EXPORT_SYMBOL(phy_init_hw); | |||
578 | * generic driver is used. The phy_device is given a ptr to | 596 | * generic driver is used. The phy_device is given a ptr to |
579 | * the attaching device, and given a callback for link status | 597 | * the attaching device, and given a callback for link status |
580 | * change. The phy_device is returned to the attaching driver. | 598 | * change. The phy_device is returned to the attaching driver. |
599 | * This function takes a reference on the phy device. | ||
581 | */ | 600 | */ |
582 | int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, | 601 | int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, |
583 | u32 flags, phy_interface_t interface) | 602 | u32 flags, phy_interface_t interface) |
584 | { | 603 | { |
604 | struct mii_bus *bus = phydev->bus; | ||
585 | struct device *d = &phydev->dev; | 605 | struct device *d = &phydev->dev; |
586 | struct module *bus_module; | ||
587 | int err; | 606 | int err; |
588 | 607 | ||
608 | if (!try_module_get(bus->owner)) { | ||
609 | dev_err(&dev->dev, "failed to get the bus module\n"); | ||
610 | return -EIO; | ||
611 | } | ||
612 | |||
613 | get_device(d); | ||
614 | |||
589 | /* Assume that if there is no driver, that it doesn't | 615 | /* Assume that if there is no driver, that it doesn't |
590 | * exist, and we should use the genphy driver. | 616 | * exist, and we should use the genphy driver. |
591 | */ | 617 | */ |
@@ -600,20 +626,13 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, | |||
600 | err = device_bind_driver(d); | 626 | err = device_bind_driver(d); |
601 | 627 | ||
602 | if (err) | 628 | if (err) |
603 | return err; | 629 | goto error; |
604 | } | 630 | } |
605 | 631 | ||
606 | if (phydev->attached_dev) { | 632 | if (phydev->attached_dev) { |
607 | dev_err(&dev->dev, "PHY already attached\n"); | 633 | dev_err(&dev->dev, "PHY already attached\n"); |
608 | return -EBUSY; | 634 | err = -EBUSY; |
609 | } | 635 | goto error; |
610 | |||
611 | /* Increment the bus module reference count */ | ||
612 | bus_module = phydev->bus->dev.driver ? | ||
613 | phydev->bus->dev.driver->owner : NULL; | ||
614 | if (!try_module_get(bus_module)) { | ||
615 | dev_err(&dev->dev, "failed to get the bus module\n"); | ||
616 | return -EIO; | ||
617 | } | 636 | } |
618 | 637 | ||
619 | phydev->attached_dev = dev; | 638 | phydev->attached_dev = dev; |
@@ -636,6 +655,11 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, | |||
636 | phy_resume(phydev); | 655 | phy_resume(phydev); |
637 | 656 | ||
638 | return err; | 657 | return err; |
658 | |||
659 | error: | ||
660 | put_device(d); | ||
661 | module_put(bus->owner); | ||
662 | return err; | ||
639 | } | 663 | } |
640 | EXPORT_SYMBOL(phy_attach_direct); | 664 | EXPORT_SYMBOL(phy_attach_direct); |
641 | 665 | ||
@@ -677,14 +701,15 @@ EXPORT_SYMBOL(phy_attach); | |||
677 | /** | 701 | /** |
678 | * phy_detach - detach a PHY device from its network device | 702 | * phy_detach - detach a PHY device from its network device |
679 | * @phydev: target phy_device struct | 703 | * @phydev: target phy_device struct |
704 | * | ||
705 | * This detaches the phy device from its network device and the phy | ||
706 | * driver, and drops the reference count taken in phy_attach_direct(). | ||
680 | */ | 707 | */ |
681 | void phy_detach(struct phy_device *phydev) | 708 | void phy_detach(struct phy_device *phydev) |
682 | { | 709 | { |
710 | struct mii_bus *bus; | ||
683 | int i; | 711 | int i; |
684 | 712 | ||
685 | if (phydev->bus->dev.driver) | ||
686 | module_put(phydev->bus->dev.driver->owner); | ||
687 | |||
688 | phydev->attached_dev->phydev = NULL; | 713 | phydev->attached_dev->phydev = NULL; |
689 | phydev->attached_dev = NULL; | 714 | phydev->attached_dev = NULL; |
690 | phy_suspend(phydev); | 715 | phy_suspend(phydev); |
@@ -700,6 +725,15 @@ void phy_detach(struct phy_device *phydev) | |||
700 | break; | 725 | break; |
701 | } | 726 | } |
702 | } | 727 | } |
728 | |||
729 | /* | ||
730 | * The phydev might go away on the put_device() below, so avoid | ||
731 | * a use-after-free bug by reading the underlying bus first. | ||
732 | */ | ||
733 | bus = phydev->bus; | ||
734 | |||
735 | put_device(&phydev->dev); | ||
736 | module_put(bus->owner); | ||
703 | } | 737 | } |
704 | EXPORT_SYMBOL(phy_detach); | 738 | EXPORT_SYMBOL(phy_detach); |
705 | 739 | ||