summaryrefslogtreecommitdiffstats
path: root/net/ieee802154
diff options
context:
space:
mode:
authorAlexander Aring <alex.aring@gmail.com>2014-11-09 08:36:50 +0100
committerMarcel Holtmann <marcel@holtmann.org>2014-11-09 19:50:28 +0100
commitfcf39e6e88e9492f6688ec8ba4e1be622b904232 (patch)
treeae87d1f093f7b8baa69cc438af36d14ba1866abe /net/ieee802154
parentieee802154: add iftype to wpan_dev (diff)
downloadlinux-fcf39e6e88e9492f6688ec8ba4e1be622b904232.tar.xz
linux-fcf39e6e88e9492f6688ec8ba4e1be622b904232.zip
ieee802154: add wpan_dev_list
This patch adds a wpan_dev_list list into cfg802154_registered_device struct. Also adding new wpan_dev into this list while cfg802154_netdev_notifier_call. This behaviour is mostly grab from wireless core.c implementation and is needed for preparing nl802154 framework. Signed-off-by: Alexander Aring <alex.aring@gmail.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/ieee802154')
-rw-r--r--net/ieee802154/core.c95
-rw-r--r--net/ieee802154/core.h11
2 files changed, 103 insertions, 3 deletions
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c
index 11a1d2ed5b26..3ee00bf0e514 100644
--- a/net/ieee802154/core.c
+++ b/net/ieee802154/core.c
@@ -102,12 +102,15 @@ wpan_phy_new(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_new);
@@ -140,13 +143,18 @@ void wpan_phy_unregister(struct wpan_phy *phy)
{
struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
- /* TODO open count */
+ 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 */
- /* TODO WARN_ON wpan_dev_list */
+ WARN_ON(!list_empty(&rdev->wpan_dev_list));
/* First remove the hardware from everywhere, this makes
* it impossible to find from userspace.
@@ -173,6 +181,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;
@@ -181,11 +262,18 @@ 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;
+
return 0;
+
+err_notifier:
+ unregister_netdevice_notifier(&cfg802154_netdev_notifier);
err_nl:
wpan_phy_sysfs_exit();
err:
@@ -196,6 +284,7 @@ subsys_initcall(wpan_phy_class_init);
static void __exit wpan_phy_class_exit(void)
{
ieee802154_nl_exit();
+ unregister_netdevice_notifier(&cfg802154_netdev_notifier);
wpan_phy_sysfs_exit();
}
module_exit(wpan_phy_class_exit);
diff --git a/net/ieee802154/core.h b/net/ieee802154/core.h
index 38887cb2eaf4..e708d9d5878b 100644
--- a/net/ieee802154/core.h
+++ b/net/ieee802154/core.h
@@ -10,6 +10,17 @@ struct cfg802154_registered_device {
/* wpan_phy index, internal only */
int wpan_phy_idx;
+ /* also protected by devlist_mtx */
+ int opencount;
+ wait_queue_head_t dev_wait;
+
+ /* protected by RTNL only */
+ int num_running_ifaces;
+
+ /* associated wpan interfaces, protected by rtnl or RCU */
+ struct list_head wpan_dev_list;
+ int devlist_generation, wpan_dev_id;
+
/* must be last because of the way we do wpan_phy_priv(),
* and it should at least be aligned to NETDEV_ALIGN
*/