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        wpan_phy_net_set(&rdev->wpan_phy, &init_net);
 144
 145        init_waitqueue_head(&rdev->dev_wait);
 146
 147        return &rdev->wpan_phy;
 148}
 149EXPORT_SYMBOL(wpan_phy_new);
 150
 151int wpan_phy_register(struct wpan_phy *phy)
 152{
 153        struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
 154        int ret;
 155
 156        rtnl_lock();
 157        ret = device_add(&phy->dev);
 158        if (ret) {
 159                rtnl_unlock();
 160                return ret;
 161        }
 162
 163        list_add_rcu(&rdev->list, &cfg802154_rdev_list);
 164        cfg802154_rdev_list_generation++;
 165
 166        /* TODO phy registered lock */
 167        rtnl_unlock();
 168
 169        /* TODO nl802154 phy notify */
 170
 171        return 0;
 172}
 173EXPORT_SYMBOL(wpan_phy_register);
 174
 175void wpan_phy_unregister(struct wpan_phy *phy)
 176{
 177        struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
 178
 179        wait_event(rdev->dev_wait, ({
 180                int __count;
 181                rtnl_lock();
 182                __count = rdev->opencount;
 183                rtnl_unlock();
 184                __count == 0; }));
 185
 186        rtnl_lock();
 187        /* TODO nl802154 phy notify */
 188        /* TODO phy registered lock */
 189
 190        WARN_ON(!list_empty(&rdev->wpan_dev_list));
 191
 192        /* First remove the hardware from everywhere, this makes
 193         * it impossible to find from userspace.
 194         */
 195        list_del_rcu(&rdev->list);
 196        synchronize_rcu();
 197
 198        cfg802154_rdev_list_generation++;
 199
 200        device_del(&phy->dev);
 201
 202        rtnl_unlock();
 203}
 204EXPORT_SYMBOL(wpan_phy_unregister);
 205
 206void wpan_phy_free(struct wpan_phy *phy)
 207{
 208        put_device(&phy->dev);
 209}
 210EXPORT_SYMBOL(wpan_phy_free);
 211
 212int cfg802154_switch_netns(struct cfg802154_registered_device *rdev,
 213                           struct net *net)
 214{
 215        struct wpan_dev *wpan_dev;
 216        int err = 0;
 217
 218        list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
 219                if (!wpan_dev->netdev)
 220                        continue;
 221                wpan_dev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
 222                err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d");
 223                if (err)
 224                        break;
 225                wpan_dev->netdev->features |= NETIF_F_NETNS_LOCAL;
 226        }
 227
 228        if (err) {
 229                /* failed -- clean up to old netns */
 230                net = wpan_phy_net(&rdev->wpan_phy);
 231
 232                list_for_each_entry_continue_reverse(wpan_dev,
 233                                                     &rdev->wpan_dev_list,
 234                                                     list) {
 235                        if (!wpan_dev->netdev)
 236                                continue;
 237                        wpan_dev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
 238                        err = dev_change_net_namespace(wpan_dev->netdev, net,
 239                                                       "wpan%d");
 240                        WARN_ON(err);
 241                        wpan_dev->netdev->features |= NETIF_F_NETNS_LOCAL;
 242                }
 243
 244                return err;
 245        }
 246
 247        wpan_phy_net_set(&rdev->wpan_phy, net);
 248
 249        err = device_rename(&rdev->wpan_phy.dev, dev_name(&rdev->wpan_phy.dev));
 250        WARN_ON(err);
 251
 252        return 0;
 253}
 254
 255void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
 256{
 257        kfree(rdev);
 258}
 259
 260static void
 261cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
 262                           int iftype, int num)
 263{
 264        ASSERT_RTNL();
 265
 266        rdev->num_running_ifaces += num;
 267}
 268
 269static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
 270                                          unsigned long state, void *ptr)
 271{
 272        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 273        struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
 274        struct cfg802154_registered_device *rdev;
 275
 276        if (!wpan_dev)
 277                return NOTIFY_DONE;
 278
 279        rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
 280
 281        /* TODO WARN_ON unspec type */
 282
 283        switch (state) {
 284                /* TODO NETDEV_DEVTYPE */
 285        case NETDEV_REGISTER:
 286                dev->features |= NETIF_F_NETNS_LOCAL;
 287                wpan_dev->identifier = ++rdev->wpan_dev_id;
 288                list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
 289                rdev->devlist_generation++;
 290
 291                wpan_dev->netdev = dev;
 292                break;
 293        case NETDEV_DOWN:
 294                cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
 295
 296                rdev->opencount--;
 297                wake_up(&rdev->dev_wait);
 298                break;
 299        case NETDEV_UP:
 300                cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
 301
 302                rdev->opencount++;
 303                break;
 304        case NETDEV_UNREGISTER:
 305                /* It is possible to get NETDEV_UNREGISTER
 306                 * multiple times. To detect that, check
 307                 * that the interface is still on the list
 308                 * of registered interfaces, and only then
 309                 * remove and clean it up.
 310                 */
 311                if (!list_empty(&wpan_dev->list)) {
 312                        list_del_rcu(&wpan_dev->list);
 313                        rdev->devlist_generation++;
 314                }
 315                /* synchronize (so that we won't find this netdev
 316                 * from other code any more) and then clear the list
 317                 * head so that the above code can safely check for
 318                 * !list_empty() to avoid double-cleanup.
 319                 */
 320                synchronize_rcu();
 321                INIT_LIST_HEAD(&wpan_dev->list);
 322                break;
 323        default:
 324                return NOTIFY_DONE;
 325        }
 326
 327        return NOTIFY_OK;
 328}
 329
 330static struct notifier_block cfg802154_netdev_notifier = {
 331        .notifier_call = cfg802154_netdev_notifier_call,
 332};
 333
 334static void __net_exit cfg802154_pernet_exit(struct net *net)
 335{
 336        struct cfg802154_registered_device *rdev;
 337
 338        rtnl_lock();
 339        list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
 340                if (net_eq(wpan_phy_net(&rdev->wpan_phy), net))
 341                        WARN_ON(cfg802154_switch_netns(rdev, &init_net));
 342        }
 343        rtnl_unlock();
 344}
 345
 346static struct pernet_operations cfg802154_pernet_ops = {
 347        .exit = cfg802154_pernet_exit,
 348};
 349
 350static int __init wpan_phy_class_init(void)
 351{
 352        int rc;
 353
 354        rc = register_pernet_device(&cfg802154_pernet_ops);
 355        if (rc)
 356                goto err;
 357
 358        rc = wpan_phy_sysfs_init();
 359        if (rc)
 360                goto err_sysfs;
 361
 362        rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
 363        if (rc)
 364                goto err_nl;
 365
 366        rc = ieee802154_nl_init();
 367        if (rc)
 368                goto err_notifier;
 369
 370        rc = nl802154_init();
 371        if (rc)
 372                goto err_ieee802154_nl;
 373
 374        return 0;
 375
 376err_ieee802154_nl:
 377        ieee802154_nl_exit();
 378
 379err_notifier:
 380        unregister_netdevice_notifier(&cfg802154_netdev_notifier);
 381err_nl:
 382        wpan_phy_sysfs_exit();
 383err_sysfs:
 384        unregister_pernet_device(&cfg802154_pernet_ops);
 385err:
 386        return rc;
 387}
 388subsys_initcall(wpan_phy_class_init);
 389
 390static void __exit wpan_phy_class_exit(void)
 391{
 392        nl802154_exit();
 393        ieee802154_nl_exit();
 394        unregister_netdevice_notifier(&cfg802154_netdev_notifier);
 395        wpan_phy_sysfs_exit();
 396        unregister_pernet_device(&cfg802154_pernet_ops);
 397}
 398module_exit(wpan_phy_class_exit);
 399
 400MODULE_LICENSE("GPL v2");
 401MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
 402MODULE_AUTHOR("Dmitry Eremin-Solenikov");
 403
 404