summaryrefslogtreecommitdiff
path: root/net/ieee802154/core.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-11-22 00:39:45 +0300
committerDavid S. Miller <davem@davemloft.net>2014-11-22 00:39:45 +0300
commit53b15ef3c2a6bac8e3d9bb58c5689d731ed9593b (patch)
tree6b3194af697d91ac4c2ab1c716bd30c972cf4949 /net/ieee802154/core.c
parentb48c5ec53ef9f0fe617aafa94a752f528fdad149 (diff)
parent9e6f3f472c8f95021ad048acc7cd3e40a827f8ce (diff)
downloadlinux-53b15ef3c2a6bac8e3d9bb58c5689d731ed9593b.tar.xz
Merge tag 'master-2014-11-20' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next
John W. Linville says: ==================== pull request: wireless-next 2014-11-21 Please pull this batch of updates intended for the 3.19 stream... For the mac80211 bits, Johannes says: "It has been a while since my last pull request, so we accumulated another relatively large set of changes: * TDLS off-channel support set from Arik/Liad, with some support patches I did * custom regulatory fixes from Arik * minstrel VHT fix (and a small optimisation) from Felix * add back radiotap vendor namespace support (myself) * random MAC address scanning for cfg80211/mac80211/hwsim (myself) * CSA improvements (Luca) * WoWLAN Net Detect (wake on network found) support (Luca) * and lots of other smaller changes from many people" For the Bluetooth bits, Johan says: "Here's another set of patches for 3.19. Most of it is again fixes and cleanups to ieee802154 related code from Alexander Aring. We've also got better handling of hardware error events along with a proper API for HCI drivers to notify the HCI core of such situations. There's also a minor fix for mgmt events as well as a sparse warning fix. The code for sending HCI commands synchronously also gets a fix where we might loose the completion event in the case of very fast HW (particularly easily reproducible with an emulated HCI device)." And... "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" For the Atheros bits, Kalle says: "One ath6kl patch and rest for ath10k, but nothing really major which stands out. Most notable: o fix resume (Bartosz) o firmware restart is now faster and more reliable (Michal) o it's now possible to test hardware restart functionality without crashing the firmware using hw-restart parameter with simulate_fw_crash debugfs file (Michal)" On top of that...both ath9k and mwifiex get their usual level of updates. Of note is the ath9k spectral scan work from Oleksij Rempel. I also pulled from the wireless tree in order to avoid some merge issues. Please let me know if there are problems! ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ieee802154/core.c')
-rw-r--r--net/ieee802154/core.c220
1 files changed, 190 insertions, 30 deletions
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c
index 620abc2ba5fc..18bc7e738507 100644
--- a/net/ieee802154/core.c
+++ b/net/ieee802154/core.c
@@ -18,12 +18,16 @@
#include <linux/device.h>
#include <net/cfg802154.h>
+#include <net/rtnetlink.h>
#include "ieee802154.h"
+#include "nl802154.h"
#include "sysfs.h"
+#include "core.h"
-static DEFINE_MUTEX(wpan_phy_mutex);
-static int wpan_phy_idx;
+/* 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)
{
@@ -71,54 +75,116 @@ int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
}
EXPORT_SYMBOL(wpan_phy_for_each);
-static int wpan_phy_idx_valid(int idx)
+struct cfg802154_registered_device *
+cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
{
- return idx >= 0;
+ 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(size_t priv_size)
+struct wpan_phy *
+wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
{
- struct wpan_phy *phy = kzalloc(sizeof(*phy) + priv_size,
- GFP_KERNEL);
-
- if (!phy)
- goto out;
- mutex_lock(&wpan_phy_mutex);
- phy->idx = wpan_phy_idx++;
- if (unlikely(!wpan_phy_idx_valid(phy->idx))) {
- wpan_phy_idx--;
- mutex_unlock(&wpan_phy_mutex);
- kfree(phy);
- goto out;
+ static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
+ struct cfg802154_registered_device *rdev;
+ size_t alloc_size;
+
+ alloc_size = sizeof(*rdev) + priv_size;
+ rdev = kzalloc(alloc_size, GFP_KERNEL);
+ if (!rdev)
+ return NULL;
+
+ rdev->ops = ops;
+
+ rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter);
+
+ if (unlikely(rdev->wpan_phy_idx < 0)) {
+ /* ugh, wrapped! */
+ atomic_dec(&wpan_phy_counter);
+ kfree(rdev);
+ return NULL;
}
- mutex_unlock(&wpan_phy_mutex);
- mutex_init(&phy->pib_lock);
+ /* atomic_inc_return makes it start at 1, make it start at 0 */
+ rdev->wpan_phy_idx--;
- device_initialize(&phy->dev);
- dev_set_name(&phy->dev, "wpan-phy%d", phy->idx);
+ mutex_init(&rdev->wpan_phy.pib_lock);
- phy->dev.class = &wpan_phy_class;
+ 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);
- phy->current_channel = -1; /* not initialised */
- phy->current_page = 0; /* for compatibility */
+ rdev->wpan_phy.dev.class = &wpan_phy_class;
+ rdev->wpan_phy.dev.platform_data = rdev;
- return phy;
+ init_waitqueue_head(&rdev->dev_wait);
-out:
- return NULL;
+ 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);
@@ -128,6 +194,84 @@ void wpan_phy_free(struct wpan_phy *phy)
}
EXPORT_SYMBOL(wpan_phy_free);
+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;
@@ -136,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:
@@ -150,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);