linux/net/ieee802154/core.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2007, 2008, 2009 Siemens AG
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2
   6 * as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 *
  13 */
  14
  15#include <linux/slab.h>
  16#include <linux/kernel.h>
  17#include <linux/module.h>
  18#include <linux/device.h>
  19
  20#include <net/cfg802154.h>
  21#include <net/rtnetlink.h>
  22
  23#include "ieee802154.h"
  24#include "nl802154.h"
  25#include "sysfs.h"
  26#include "core.h"
  27
  28/* name for sysfs, %d is appended */
  29#define PHY_NAME "phy"
  30
  31/* RCU-protected (and RTNL for writers) */
  32LIST_HEAD(cfg802154_rdev_list);
  33int cfg802154_rdev_list_generation;
  34
  35static int wpan_phy_match(struct device *dev, const void *data)
  36{
  37        return !strcmp(dev_name(dev), (const char *)data);
  38}
  39
  40struct wpan_phy *wpan_phy_find(const char *str)
  41{
  42        struct device *dev;
  43
  44        if (WARN_ON(!str))
  45                return NULL;
  46
  47        dev = class_find_device(&wpan_phy_class, NULL, str, wpan_phy_match);
  48        if (!dev)
  49                return NULL;
  50
  51        return container_of(dev, struct wpan_phy, dev);
  52}
  53EXPORT_SYMBOL(wpan_phy_find);
  54
  55struct wpan_phy_iter_data {
  56        int (*fn)(struct wpan_phy *phy, void *data);
  57        void *data;
  58};
  59
  60static int wpan_phy_iter(struct device *dev, void *_data)
  61{
  62        struct wpan_phy_iter_data *wpid = _data;
  63        struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
  64
  65        return wpid->fn(phy, wpid->data);
  66}
  67
  68int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
  69                      void *data)
  70{
  71        struct wpan_phy_iter_data wpid = {
  72                .fn = fn,
  73                .data = data,
  74        };
  75
  76        return class_for_each_device(&wpan_phy_class, NULL,
  77                        &wpid, wpan_phy_iter);
  78}
  79EXPORT_SYMBOL(wpan_phy_for_each);
  80
  81struct cfg802154_registered_device *
  82cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
  83{
  84        struct cfg802154_registered_device *result = NULL, *rdev;
  85
  86        ASSERT_RTNL();
  87
  88        list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
  89                if (rdev->wpan_phy_idx == wpan_phy_idx) {
  90                        result = rdev;
  91                        break;
  92                }
  93        }
  94
  95        return result;
  96}
  97
  98struct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx)
  99{
 100        struct cfg802154_registered_device *rdev;
 101
 102        ASSERT_RTNL();
 103
 104        rdev = cfg802154_rdev_by_wpan_phy_idx(wpan_phy_idx);
 105        if (!rdev)
 106                return NULL;
 107        return &rdev->wpan_phy;
 108}
 109
 110struct wpan_phy *
 111wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
 112{
 113        static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
 114        struct cfg802154_registered_device *rdev;
 115        size_t alloc_size;
 116
 117        alloc_size = sizeof(*rdev) + priv_size;
 118        rdev = kzalloc(alloc_size, GFP_KERNEL);
 119        if (!rdev)
 120                return NULL;
 121
 122        rdev->ops = ops;
 123
 124        rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter);
 125
 126        if (unlikely(rdev->wpan_phy_idx < 0)) {
 127                /* ugh, wrapped! */
 128                atomic_dec(&wpan_phy_counter);
 129                kfree(rdev);
 130                return NULL;
 131        }
 132
 133        /* atomic_inc_return makes it start at 1, make it start at 0 */
 134        rdev->wpan_phy_idx--;
 135
 136        INIT_LIST_HEAD(&rdev->wpan_dev_list);
 137        device_initialize(&rdev->wpan_phy.dev);
 138        dev_set_name(&rdev->wpan_phy.dev, PHY_NAME "%d", rdev->wpan_phy_idx);
 139
 140        rdev->wpan_phy.dev.class = &wpan_phy_class;
 141        rdev->wpan_phy.dev.platform_data = rdev;
 142
 143        init_waitqueue_head(&rdev->dev_wait);
 144
 145        return &rdev->wpan_phy;
 146}
 147EXPORT_SYMBOL(wpan_phy_new);
 148
 149int wpan_phy_register(struct wpan_phy *phy)
 150{
 151        struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
 152        int ret;
 153
 154        rtnl_lock();
 155        ret = device_add(&phy->dev);
 156        if (ret) {
 157                rtnl_unlock();
 158                return ret;
 159        }
 160
 161        list_add_rcu(&rdev->list, &cfg802154_rdev_list);
 162        cfg802154_rdev_list_generation++;
 163
 164        /* TODO phy registered lock */
 165        rtnl_unlock();
 166
 167        /* TODO nl802154 phy notify */
 168
 169        return 0;
 170}
 171EXPORT_SYMBOL(wpan_phy_register);
 172
 173void wpan_phy_unregister(struct wpan_phy *phy)
 174{
 175        struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
 176
 177        wait_event(rdev->dev_wait, ({
 178                int __count;
 179                rtnl_lock();
 180                __count = rdev->opencount;
 181                rtnl_unlock();
 182                __count == 0; }));
 183
 184        rtnl_lock();
 185        /* TODO nl802154 phy notify */
 186        /* TODO phy registered lock */
 187
 188        WARN_ON(!list_empty(&rdev->wpan_dev_list));
 189
 190        /* First remove the hardware from everywhere, this makes
 191         * it impossible to find from userspace.
 192         */
 193        list_del_rcu(&rdev->list);
 194        synchronize_rcu();
 195
 196        cfg802154_rdev_list_generation++;
 197
 198        device_del(&phy->dev);
 199
 200        rtnl_unlock();
 201}
 202EXPORT_SYMBOL(wpan_phy_unregister);
 203
 204void wpan_phy_free(struct wpan_phy *phy)
 205{
 206        put_device(&phy->dev);
 207}
 208EXPORT_SYMBOL(wpan_phy_free);
 209
 210void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
 211{
 212        kfree(rdev);
 213}
 214
 215static void
 216cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
 217                           int iftype, int num)
 218{
 219        ASSERT_RTNL();
 220
 221        rdev->num_running_ifaces += num;
 222}
 223
 224static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
 225                                          unsigned long state, void *ptr)
 226{
 227        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 228        struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
 229        struct cfg802154_registered_device *rdev;
 230
 231        if (!wpan_dev)
 232                return NOTIFY_DONE;
 233
 234        rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
 235
 236        /* TODO WARN_ON unspec type */
 237
 238        switch (state) {
 239                /* TODO NETDEV_DEVTYPE */
 240        case NETDEV_REGISTER:
 241                dev->features |= NETIF_F_NETNS_LOCAL;
 242                wpan_dev->identifier = ++rdev->wpan_dev_id;
 243                list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
 244                rdev->devlist_generation++;
 245
 246                wpan_dev->netdev = dev;
 247                break;
 248        case NETDEV_DOWN:
 249                cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
 250
 251                rdev->opencount--;
 252                wake_up(&rdev->dev_wait);
 253                break;
 254        case NETDEV_UP:
 255                cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
 256
 257                rdev->opencount++;
 258                break;
 259        case NETDEV_UNREGISTER:
 260                /* It is possible to get NETDEV_UNREGISTER
 261                 * multiple times. To detect that, check
 262                 * that the interface is still on the list
 263                 * of registered interfaces, and only then
 264                 * remove and clean it up.
 265                 */
 266                if (!list_empty(&wpan_dev->list)) {
 267                        list_del_rcu(&wpan_dev->list);
 268                        rdev->devlist_generation++;
 269                }
 270                /* synchronize (so that we won't find this netdev
 271                 * from other code any more) and then clear the list
 272                 * head so that the above code can safely check for
 273                 * !list_empty() to avoid double-cleanup.
 274                 */
 275                synchronize_rcu();
 276                INIT_LIST_HEAD(&wpan_dev->list);
 277                break;
 278        default:
 279                return NOTIFY_DONE;
 280        }
 281
 282        return NOTIFY_OK;
 283}
 284
 285static struct notifier_block cfg802154_netdev_notifier = {
 286        .notifier_call = cfg802154_netdev_notifier_call,
 287};
 288
 289static int __init wpan_phy_class_init(void)
 290{
 291        int rc;
 292
 293        rc = wpan_phy_sysfs_init();
 294        if (rc)
 295                goto err;
 296
 297        rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
 298        if (rc)
 299                goto err_nl;
 300
 301        rc = ieee802154_nl_init();
 302        if (rc)
 303                goto err_notifier;
 304
 305        rc = nl802154_init();
 306        if (rc)
 307                goto err_ieee802154_nl;
 308
 309        return 0;
 310
 311err_ieee802154_nl:
 312        ieee802154_nl_exit();
 313
 314err_notifier:
 315        unregister_netdevice_notifier(&cfg802154_netdev_notifier);
 316err_nl:
 317        wpan_phy_sysfs_exit();
 318err:
 319        return rc;
 320}
 321subsys_initcall(wpan_phy_class_init);
 322
 323static void __exit wpan_phy_class_exit(void)
 324{
 325        nl802154_exit();
 326        ieee802154_nl_exit();
 327        unregister_netdevice_notifier(&cfg802154_netdev_notifier);
 328        wpan_phy_sysfs_exit();
 329}
 330module_exit(wpan_phy_class_exit);
 331
 332MODULE_LICENSE("GPL v2");
 333MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
 334MODULE_AUTHOR("Dmitry Eremin-Solenikov");
 335
 336