diff options
author | John W. Linville <linville@tuxdriver.com> | 2014-11-18 15:13:26 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-11-18 15:13:26 -0500 |
commit | f48ecb19bc0443a4272dd0934e7bee4480610df9 (patch) | |
tree | eb29c8f7491e0fbf93e0c722a4b3feb575fc93c3 /net/ieee802154/core.c | |
parent | 47b6308b643302e642ca2a5cb6470926f7e1c428 (diff) | |
parent | a86c02ea38c53b695209b1181f9e2e18d73eb4e8 (diff) |
Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg <johan.hedberg@gmail.com> says:
"Here's another bluetooth-next pull request for 3.19. We've got:
- Various fixes, cleanups and improvements to ieee802154/mac802154
- Support for a Broadcom BCM20702A1 variant
- Lots of lockdep fixes
- Fixed handling of LE CoC errors that should trigger SMP"
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/ieee802154/core.c')
-rw-r--r-- | net/ieee802154/core.c | 166 |
1 files changed, 162 insertions, 4 deletions
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c index d1cd0edfb149..18bc7e738507 100644 --- a/net/ieee802154/core.c +++ b/net/ieee802154/core.c @@ -18,11 +18,17 @@ #include <linux/device.h> #include <net/cfg802154.h> +#include <net/rtnetlink.h> #include "ieee802154.h" +#include "nl802154.h" #include "sysfs.h" #include "core.h" +/* RCU-protected (and RTNL for writers) */ +LIST_HEAD(cfg802154_rdev_list); +int cfg802154_rdev_list_generation; + static int wpan_phy_match(struct device *dev, const void *data) { return !strcmp(dev_name(dev), (const char *)data); @@ -69,8 +75,25 @@ int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data), } EXPORT_SYMBOL(wpan_phy_for_each); +struct cfg802154_registered_device * +cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx) +{ + struct cfg802154_registered_device *result = NULL, *rdev; + + ASSERT_RTNL(); + + list_for_each_entry(rdev, &cfg802154_rdev_list, list) { + if (rdev->wpan_phy_idx == wpan_phy_idx) { + result = rdev; + break; + } + } + + return result; +} + struct wpan_phy * -wpan_phy_alloc(const struct cfg802154_ops *ops, size_t priv_size) +wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size) { static atomic_t wpan_phy_counter = ATOMIC_INIT(0); struct cfg802154_registered_device *rdev; @@ -97,25 +120,71 @@ wpan_phy_alloc(const struct cfg802154_ops *ops, size_t priv_size) mutex_init(&rdev->wpan_phy.pib_lock); + INIT_LIST_HEAD(&rdev->wpan_dev_list); device_initialize(&rdev->wpan_phy.dev); dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx); rdev->wpan_phy.dev.class = &wpan_phy_class; rdev->wpan_phy.dev.platform_data = rdev; + init_waitqueue_head(&rdev->dev_wait); + return &rdev->wpan_phy; } -EXPORT_SYMBOL(wpan_phy_alloc); +EXPORT_SYMBOL(wpan_phy_new); int wpan_phy_register(struct wpan_phy *phy) { - return device_add(&phy->dev); + struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy); + int ret; + + rtnl_lock(); + ret = device_add(&phy->dev); + if (ret) { + rtnl_unlock(); + return ret; + } + + list_add_rcu(&rdev->list, &cfg802154_rdev_list); + cfg802154_rdev_list_generation++; + + /* TODO phy registered lock */ + rtnl_unlock(); + + /* TODO nl802154 phy notify */ + + return 0; } EXPORT_SYMBOL(wpan_phy_register); void wpan_phy_unregister(struct wpan_phy *phy) { + struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy); + + wait_event(rdev->dev_wait, ({ + int __count; + rtnl_lock(); + __count = rdev->opencount; + rtnl_unlock(); + __count == 0; })); + + rtnl_lock(); + /* TODO nl802154 phy notify */ + /* TODO phy registered lock */ + + WARN_ON(!list_empty(&rdev->wpan_dev_list)); + + /* First remove the hardware from everywhere, this makes + * it impossible to find from userspace. + */ + list_del_rcu(&rdev->list); + synchronize_rcu(); + + cfg802154_rdev_list_generation++; + device_del(&phy->dev); + + rtnl_unlock(); } EXPORT_SYMBOL(wpan_phy_unregister); @@ -130,6 +199,79 @@ void cfg802154_dev_free(struct cfg802154_registered_device *rdev) kfree(rdev); } +static void +cfg802154_update_iface_num(struct cfg802154_registered_device *rdev, + int iftype, int num) +{ + ASSERT_RTNL(); + + rdev->num_running_ifaces += num; +} + +static int cfg802154_netdev_notifier_call(struct notifier_block *nb, + unsigned long state, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct cfg802154_registered_device *rdev; + + if (!wpan_dev) + return NOTIFY_DONE; + + rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy); + + /* TODO WARN_ON unspec type */ + + switch (state) { + /* TODO NETDEV_DEVTYPE */ + case NETDEV_REGISTER: + wpan_dev->identifier = ++rdev->wpan_dev_id; + list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list); + rdev->devlist_generation++; + + wpan_dev->netdev = dev; + break; + case NETDEV_DOWN: + cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1); + + rdev->opencount--; + wake_up(&rdev->dev_wait); + break; + case NETDEV_UP: + cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1); + + rdev->opencount++; + break; + case NETDEV_UNREGISTER: + /* It is possible to get NETDEV_UNREGISTER + * multiple times. To detect that, check + * that the interface is still on the list + * of registered interfaces, and only then + * remove and clean it up. + */ + if (!list_empty(&wpan_dev->list)) { + list_del_rcu(&wpan_dev->list); + rdev->devlist_generation++; + } + /* synchronize (so that we won't find this netdev + * from other code any more) and then clear the list + * head so that the above code can safely check for + * !list_empty() to avoid double-cleanup. + */ + synchronize_rcu(); + INIT_LIST_HEAD(&wpan_dev->list); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static struct notifier_block cfg802154_netdev_notifier = { + .notifier_call = cfg802154_netdev_notifier_call, +}; + static int __init wpan_phy_class_init(void) { int rc; @@ -138,11 +280,25 @@ static int __init wpan_phy_class_init(void) if (rc) goto err; - rc = ieee802154_nl_init(); + rc = register_netdevice_notifier(&cfg802154_netdev_notifier); if (rc) goto err_nl; + rc = ieee802154_nl_init(); + if (rc) + goto err_notifier; + + rc = nl802154_init(); + if (rc) + goto err_ieee802154_nl; + return 0; + +err_ieee802154_nl: + ieee802154_nl_exit(); + +err_notifier: + unregister_netdevice_notifier(&cfg802154_netdev_notifier); err_nl: wpan_phy_sysfs_exit(); err: @@ -152,7 +308,9 @@ subsys_initcall(wpan_phy_class_init); static void __exit wpan_phy_class_exit(void) { + nl802154_exit(); ieee802154_nl_exit(); + unregister_netdevice_notifier(&cfg802154_netdev_notifier); wpan_phy_sysfs_exit(); } module_exit(wpan_phy_class_exit); |