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 *
  99wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
 100{
 101        static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
 102        struct cfg802154_registered_device *rdev;
 103        size_t alloc_size;
 104
 105        alloc_size = sizeof(*rdev) + priv_size;
 106        rdev = kzalloc(alloc_size, GFP_KERNEL);
 107        if (!rdev)
 108                return NULL;
 109
 110        rdev->ops = ops;
 111
 112        rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter);
 113
 114        if (unlikely(rdev->wpan_phy_idx < 0)) {
 115                /* ugh, wrapped! */
 116                atomic_dec(&wpan_phy_counter);
 117                kfree(rdev);
 118                return NULL;
 119        }
 120
 121        /* atomic_inc_return makes it start at 1, make it start at 0 */
 122        rdev->wpan_phy_idx--;
 123
 124        INIT_LIST_HEAD(&rdev->wpan_dev_list);
 125        device_initialize(&rdev->wpan_phy.dev);
 126        dev_set_name(&rdev->wpan_phy.dev, PHY_NAME "%d", rdev->wpan_phy_idx);
 127
 128        rdev->wpan_phy.dev.class = &wpan_phy_class;
 129        rdev->wpan_phy.dev.platform_data = rdev;
 130
 131        init_waitqueue_head(&rdev->dev_wait);
 132
 133        return &rdev->wpan_phy;
 134}
 135EXPORT_SYMBOL(wpan_phy_new);
 136
 137int wpan_phy_register(struct wpan_phy *phy)
 138{
 139        struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
 140        int ret;
 141
 142        rtnl_lock();
 143        ret = device_add(&phy->dev);
 144        if (ret) {
 145                rtnl_unlock();
 146                return ret;
 147        }
 148
 149        list_add_rcu(&rdev->list, &cfg802154_rdev_list);
 150        cfg802154_rdev_list_generation++;
 151
 152        /* TODO phy registered lock */
 153        rtnl_unlock();
 154
 155        /* TODO nl802154 phy notify */
 156
 157        return 0;
 158}
 159EXPORT_SYMBOL(wpan_phy_register);
 160
 161void wpan_phy_unregister(struct wpan_phy *phy)
 162{
 163        struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
 164
 165        wait_event(rdev->dev_wait, ({
 166                int __count;
 167                rtnl_lock();
 168                __count = rdev->opencount;
 169                rtnl_unlock();
 170                __count == 0; }));
 171
 172        rtnl_lock();
 173        /* TODO nl802154 phy notify */
 174        /* TODO phy registered lock */
 175
 176        WARN_ON(!list_empty(&rdev->wpan_dev_list));
 177
 178        /* First remove the hardware from everywhere, this makes
 179         * it impossible to find from userspace.
 180         */
 181        list_del_rcu(&rdev->list);
 182        synchronize_rcu();
 183
 184        cfg802154_rdev_list_generation++;
 185
 186        device_del(&phy->dev);
 187
 188        rtnl_unlock();
 189}
 190EXPORT_SYMBOL(wpan_phy_unregister);
 191
 192void wpan_phy_free(struct wpan_phy *phy)
 193{
 194        put_device(&phy->dev);
 195}
 196EXPORT_SYMBOL(wpan_phy_free);
 197
 198void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
 199{
 200        kfree(rdev);
 201}
 202
 203static void
 204cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
 205                           int iftype, int num)
 206{
 207        ASSERT_RTNL();
 208
 209        rdev->num_running_ifaces += num;
 210}
 211
 212static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
 213                                          unsigned long state, void *ptr)
 214{
 215        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 216        struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
 217        struct cfg802154_registered_device *rdev;
 218
 219        if (!wpan_dev)
 220                return NOTIFY_DONE;
 221
 222        rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
 223
 224        /* TODO WARN_ON unspec type */
 225
 226        switch (state) {
 227                /* TODO NETDEV_DEVTYPE */
 228        case NETDEV_REGISTER:
 229                dev->features |= NETIF_F_NETNS_LOCAL;
 230                wpan_dev->identifier = ++rdev->wpan_dev_id;
 231                list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
 232                rdev->devlist_generation++;
 233
 234                wpan_dev->netdev = dev;
 235                break;
 236        case NETDEV_DOWN:
 237                cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
 238
 239                rdev->opencount--;
 240                wake_up(&rdev->dev_wait);
 241                break;
 242        case NETDEV_UP:
 243                cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
 244
 245                rdev->opencount++;
 246                break;
 247        case NETDEV_UNREGISTER:
 248                /* It is possible to get NETDEV_UNREGISTER
 249                 * multiple times. To detect that, check
 250                 * that the interface is still on the list
 251                 * of registered interfaces, and only then
 252                 * remove and clean it up.
 253                 */
 254                if (!list_empty(&wpan_dev->list)) {
 255                        list_del_rcu(&wpan_dev->list);
 256                        rdev->devlist_generation++;
 257                }
 258                /* synchronize (so that we won't find this netdev
 259                 * from other code any more) and then clear the list
 260                 * head so that the above code can safely check for
 261                 * !list_empty() to avoid double-cleanup.
 262                 */
 263                synchronize_rcu();
 264                INIT_LIST_HEAD(&wpan_dev->list);
 265                break;
 266        default:
 267                return NOTIFY_DONE;
 268        }
 269
 270        return NOTIFY_OK;
 271}
 272
 273static struct notifier_block cfg802154_netdev_notifier = {
 274        .notifier_call = cfg802154_netdev_notifier_call,
 275};
 276
 277static int __init wpan_phy_class_init(void)
 278{
 279        int rc;
 280
 281        rc = wpan_phy_sysfs_init();
 282        if (rc)
 283                goto err;
 284
 285        rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
 286        if (rc)
 287                goto err_nl;
 288
 289        rc = ieee802154_nl_init();
 290        if (rc)
 291                goto err_notifier;
 292
 293        rc = nl802154_init();
 294        if (rc)
 295                goto err_ieee802154_nl;
 296
 297        return 0;
 298
 299err_ieee802154_nl:
 300        ieee802154_nl_exit();
 301
 302err_notifier:
 303        unregister_netdevice_notifier(&cfg802154_netdev_notifier);
 304err_nl:
 305        wpan_phy_sysfs_exit();
 306err:
 307        return rc;
 308}
 309subsys_initcall(wpan_phy_class_init);
 310
 311static void __exit wpan_phy_class_exit(void)
 312{
 313        nl802154_exit();
 314        ieee802154_nl_exit();
 315        unregister_netdevice_notifier(&cfg802154_netdev_notifier);
 316        wpan_phy_sysfs_exit();
 317}
 318module_exit(wpan_phy_class_exit);
 319
 320MODULE_LICENSE("GPL v2");
 321MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
 322MODULE_AUTHOR("Dmitry Eremin-Solenikov");
 323
 324