Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2007, 2008, 2009 Siemens AG
0004  */
0005 
0006 #include <linux/slab.h>
0007 #include <linux/kernel.h>
0008 #include <linux/module.h>
0009 #include <linux/device.h>
0010 
0011 #include <net/cfg802154.h>
0012 #include <net/rtnetlink.h>
0013 
0014 #include "ieee802154.h"
0015 #include "nl802154.h"
0016 #include "sysfs.h"
0017 #include "core.h"
0018 
0019 /* name for sysfs, %d is appended */
0020 #define PHY_NAME "phy"
0021 
0022 /* RCU-protected (and RTNL for writers) */
0023 LIST_HEAD(cfg802154_rdev_list);
0024 int cfg802154_rdev_list_generation;
0025 
0026 struct wpan_phy *wpan_phy_find(const char *str)
0027 {
0028     struct device *dev;
0029 
0030     if (WARN_ON(!str))
0031         return NULL;
0032 
0033     dev = class_find_device_by_name(&wpan_phy_class, str);
0034     if (!dev)
0035         return NULL;
0036 
0037     return container_of(dev, struct wpan_phy, dev);
0038 }
0039 EXPORT_SYMBOL(wpan_phy_find);
0040 
0041 struct wpan_phy_iter_data {
0042     int (*fn)(struct wpan_phy *phy, void *data);
0043     void *data;
0044 };
0045 
0046 static int wpan_phy_iter(struct device *dev, void *_data)
0047 {
0048     struct wpan_phy_iter_data *wpid = _data;
0049     struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
0050 
0051     return wpid->fn(phy, wpid->data);
0052 }
0053 
0054 int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
0055               void *data)
0056 {
0057     struct wpan_phy_iter_data wpid = {
0058         .fn = fn,
0059         .data = data,
0060     };
0061 
0062     return class_for_each_device(&wpan_phy_class, NULL,
0063             &wpid, wpan_phy_iter);
0064 }
0065 EXPORT_SYMBOL(wpan_phy_for_each);
0066 
0067 struct cfg802154_registered_device *
0068 cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
0069 {
0070     struct cfg802154_registered_device *result = NULL, *rdev;
0071 
0072     ASSERT_RTNL();
0073 
0074     list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
0075         if (rdev->wpan_phy_idx == wpan_phy_idx) {
0076             result = rdev;
0077             break;
0078         }
0079     }
0080 
0081     return result;
0082 }
0083 
0084 struct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx)
0085 {
0086     struct cfg802154_registered_device *rdev;
0087 
0088     ASSERT_RTNL();
0089 
0090     rdev = cfg802154_rdev_by_wpan_phy_idx(wpan_phy_idx);
0091     if (!rdev)
0092         return NULL;
0093     return &rdev->wpan_phy;
0094 }
0095 
0096 struct wpan_phy *
0097 wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
0098 {
0099     static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
0100     struct cfg802154_registered_device *rdev;
0101     size_t alloc_size;
0102 
0103     alloc_size = sizeof(*rdev) + priv_size;
0104     rdev = kzalloc(alloc_size, GFP_KERNEL);
0105     if (!rdev)
0106         return NULL;
0107 
0108     rdev->ops = ops;
0109 
0110     rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter);
0111 
0112     if (unlikely(rdev->wpan_phy_idx < 0)) {
0113         /* ugh, wrapped! */
0114         atomic_dec(&wpan_phy_counter);
0115         kfree(rdev);
0116         return NULL;
0117     }
0118 
0119     /* atomic_inc_return makes it start at 1, make it start at 0 */
0120     rdev->wpan_phy_idx--;
0121 
0122     INIT_LIST_HEAD(&rdev->wpan_dev_list);
0123     device_initialize(&rdev->wpan_phy.dev);
0124     dev_set_name(&rdev->wpan_phy.dev, PHY_NAME "%d", rdev->wpan_phy_idx);
0125 
0126     rdev->wpan_phy.dev.class = &wpan_phy_class;
0127     rdev->wpan_phy.dev.platform_data = rdev;
0128 
0129     wpan_phy_net_set(&rdev->wpan_phy, &init_net);
0130 
0131     init_waitqueue_head(&rdev->dev_wait);
0132 
0133     return &rdev->wpan_phy;
0134 }
0135 EXPORT_SYMBOL(wpan_phy_new);
0136 
0137 int wpan_phy_register(struct wpan_phy *phy)
0138 {
0139     struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
0140     int ret;
0141 
0142     rtnl_lock();
0143     ret = device_add(&phy->dev);
0144     if (ret) {
0145         rtnl_unlock();
0146         return ret;
0147     }
0148 
0149     list_add_rcu(&rdev->list, &cfg802154_rdev_list);
0150     cfg802154_rdev_list_generation++;
0151 
0152     /* TODO phy registered lock */
0153     rtnl_unlock();
0154 
0155     /* TODO nl802154 phy notify */
0156 
0157     return 0;
0158 }
0159 EXPORT_SYMBOL(wpan_phy_register);
0160 
0161 void wpan_phy_unregister(struct wpan_phy *phy)
0162 {
0163     struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
0164 
0165     wait_event(rdev->dev_wait, ({
0166         int __count;
0167         rtnl_lock();
0168         __count = rdev->opencount;
0169         rtnl_unlock();
0170         __count == 0; }));
0171 
0172     rtnl_lock();
0173     /* TODO nl802154 phy notify */
0174     /* TODO phy registered lock */
0175 
0176     WARN_ON(!list_empty(&rdev->wpan_dev_list));
0177 
0178     /* First remove the hardware from everywhere, this makes
0179      * it impossible to find from userspace.
0180      */
0181     list_del_rcu(&rdev->list);
0182     synchronize_rcu();
0183 
0184     cfg802154_rdev_list_generation++;
0185 
0186     device_del(&phy->dev);
0187 
0188     rtnl_unlock();
0189 }
0190 EXPORT_SYMBOL(wpan_phy_unregister);
0191 
0192 void wpan_phy_free(struct wpan_phy *phy)
0193 {
0194     put_device(&phy->dev);
0195 }
0196 EXPORT_SYMBOL(wpan_phy_free);
0197 
0198 int cfg802154_switch_netns(struct cfg802154_registered_device *rdev,
0199                struct net *net)
0200 {
0201     struct wpan_dev *wpan_dev;
0202     int err = 0;
0203 
0204     list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
0205         if (!wpan_dev->netdev)
0206             continue;
0207         wpan_dev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
0208         err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d");
0209         if (err)
0210             break;
0211         wpan_dev->netdev->features |= NETIF_F_NETNS_LOCAL;
0212     }
0213 
0214     if (err) {
0215         /* failed -- clean up to old netns */
0216         net = wpan_phy_net(&rdev->wpan_phy);
0217 
0218         list_for_each_entry_continue_reverse(wpan_dev,
0219                              &rdev->wpan_dev_list,
0220                              list) {
0221             if (!wpan_dev->netdev)
0222                 continue;
0223             wpan_dev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
0224             err = dev_change_net_namespace(wpan_dev->netdev, net,
0225                                "wpan%d");
0226             WARN_ON(err);
0227             wpan_dev->netdev->features |= NETIF_F_NETNS_LOCAL;
0228         }
0229 
0230         return err;
0231     }
0232 
0233     wpan_phy_net_set(&rdev->wpan_phy, net);
0234 
0235     err = device_rename(&rdev->wpan_phy.dev, dev_name(&rdev->wpan_phy.dev));
0236     WARN_ON(err);
0237 
0238     return 0;
0239 }
0240 
0241 void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
0242 {
0243     kfree(rdev);
0244 }
0245 
0246 static void
0247 cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
0248                int iftype, int num)
0249 {
0250     ASSERT_RTNL();
0251 
0252     rdev->num_running_ifaces += num;
0253 }
0254 
0255 static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
0256                       unsigned long state, void *ptr)
0257 {
0258     struct net_device *dev = netdev_notifier_info_to_dev(ptr);
0259     struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
0260     struct cfg802154_registered_device *rdev;
0261 
0262     if (!wpan_dev)
0263         return NOTIFY_DONE;
0264 
0265     rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
0266 
0267     /* TODO WARN_ON unspec type */
0268 
0269     switch (state) {
0270         /* TODO NETDEV_DEVTYPE */
0271     case NETDEV_REGISTER:
0272         dev->features |= NETIF_F_NETNS_LOCAL;
0273         wpan_dev->identifier = ++rdev->wpan_dev_id;
0274         list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
0275         rdev->devlist_generation++;
0276 
0277         wpan_dev->netdev = dev;
0278         break;
0279     case NETDEV_DOWN:
0280         cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
0281 
0282         rdev->opencount--;
0283         wake_up(&rdev->dev_wait);
0284         break;
0285     case NETDEV_UP:
0286         cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
0287 
0288         rdev->opencount++;
0289         break;
0290     case NETDEV_UNREGISTER:
0291         /* It is possible to get NETDEV_UNREGISTER
0292          * multiple times. To detect that, check
0293          * that the interface is still on the list
0294          * of registered interfaces, and only then
0295          * remove and clean it up.
0296          */
0297         if (!list_empty(&wpan_dev->list)) {
0298             list_del_rcu(&wpan_dev->list);
0299             rdev->devlist_generation++;
0300         }
0301         /* synchronize (so that we won't find this netdev
0302          * from other code any more) and then clear the list
0303          * head so that the above code can safely check for
0304          * !list_empty() to avoid double-cleanup.
0305          */
0306         synchronize_rcu();
0307         INIT_LIST_HEAD(&wpan_dev->list);
0308         break;
0309     default:
0310         return NOTIFY_DONE;
0311     }
0312 
0313     return NOTIFY_OK;
0314 }
0315 
0316 static struct notifier_block cfg802154_netdev_notifier = {
0317     .notifier_call = cfg802154_netdev_notifier_call,
0318 };
0319 
0320 static void __net_exit cfg802154_pernet_exit(struct net *net)
0321 {
0322     struct cfg802154_registered_device *rdev;
0323 
0324     rtnl_lock();
0325     list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
0326         if (net_eq(wpan_phy_net(&rdev->wpan_phy), net))
0327             WARN_ON(cfg802154_switch_netns(rdev, &init_net));
0328     }
0329     rtnl_unlock();
0330 }
0331 
0332 static struct pernet_operations cfg802154_pernet_ops = {
0333     .exit = cfg802154_pernet_exit,
0334 };
0335 
0336 static int __init wpan_phy_class_init(void)
0337 {
0338     int rc;
0339 
0340     rc = register_pernet_device(&cfg802154_pernet_ops);
0341     if (rc)
0342         goto err;
0343 
0344     rc = wpan_phy_sysfs_init();
0345     if (rc)
0346         goto err_sysfs;
0347 
0348     rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
0349     if (rc)
0350         goto err_nl;
0351 
0352     rc = ieee802154_nl_init();
0353     if (rc)
0354         goto err_notifier;
0355 
0356     rc = nl802154_init();
0357     if (rc)
0358         goto err_ieee802154_nl;
0359 
0360     return 0;
0361 
0362 err_ieee802154_nl:
0363     ieee802154_nl_exit();
0364 
0365 err_notifier:
0366     unregister_netdevice_notifier(&cfg802154_netdev_notifier);
0367 err_nl:
0368     wpan_phy_sysfs_exit();
0369 err_sysfs:
0370     unregister_pernet_device(&cfg802154_pernet_ops);
0371 err:
0372     return rc;
0373 }
0374 subsys_initcall(wpan_phy_class_init);
0375 
0376 static void __exit wpan_phy_class_exit(void)
0377 {
0378     nl802154_exit();
0379     ieee802154_nl_exit();
0380     unregister_netdevice_notifier(&cfg802154_netdev_notifier);
0381     wpan_phy_sysfs_exit();
0382     unregister_pernet_device(&cfg802154_pernet_ops);
0383 }
0384 module_exit(wpan_phy_class_exit);
0385 
0386 MODULE_LICENSE("GPL v2");
0387 MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
0388 MODULE_AUTHOR("Dmitry Eremin-Solenikov");