18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2007, 2008, 2009 Siemens AG 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <net/cfg802154.h> 128c2ecf20Sopenharmony_ci#include <net/rtnetlink.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "ieee802154.h" 158c2ecf20Sopenharmony_ci#include "nl802154.h" 168c2ecf20Sopenharmony_ci#include "sysfs.h" 178c2ecf20Sopenharmony_ci#include "core.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* name for sysfs, %d is appended */ 208c2ecf20Sopenharmony_ci#define PHY_NAME "phy" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* RCU-protected (and RTNL for writers) */ 238c2ecf20Sopenharmony_ciLIST_HEAD(cfg802154_rdev_list); 248c2ecf20Sopenharmony_ciint cfg802154_rdev_list_generation; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct wpan_phy *wpan_phy_find(const char *str) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct device *dev; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (WARN_ON(!str)) 318c2ecf20Sopenharmony_ci return NULL; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci dev = class_find_device_by_name(&wpan_phy_class, str); 348c2ecf20Sopenharmony_ci if (!dev) 358c2ecf20Sopenharmony_ci return NULL; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return container_of(dev, struct wpan_phy, dev); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wpan_phy_find); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct wpan_phy_iter_data { 428c2ecf20Sopenharmony_ci int (*fn)(struct wpan_phy *phy, void *data); 438c2ecf20Sopenharmony_ci void *data; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int wpan_phy_iter(struct device *dev, void *_data) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct wpan_phy_iter_data *wpid = _data; 498c2ecf20Sopenharmony_ci struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return wpid->fn(phy, wpid->data); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ciint wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data), 558c2ecf20Sopenharmony_ci void *data) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct wpan_phy_iter_data wpid = { 588c2ecf20Sopenharmony_ci .fn = fn, 598c2ecf20Sopenharmony_ci .data = data, 608c2ecf20Sopenharmony_ci }; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return class_for_each_device(&wpan_phy_class, NULL, 638c2ecf20Sopenharmony_ci &wpid, wpan_phy_iter); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wpan_phy_for_each); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistruct cfg802154_registered_device * 688c2ecf20Sopenharmony_cicfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct cfg802154_registered_device *result = NULL, *rdev; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci ASSERT_RTNL(); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci list_for_each_entry(rdev, &cfg802154_rdev_list, list) { 758c2ecf20Sopenharmony_ci if (rdev->wpan_phy_idx == wpan_phy_idx) { 768c2ecf20Sopenharmony_ci result = rdev; 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return result; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistruct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct cfg802154_registered_device *rdev; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci ASSERT_RTNL(); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci rdev = cfg802154_rdev_by_wpan_phy_idx(wpan_phy_idx); 918c2ecf20Sopenharmony_ci if (!rdev) 928c2ecf20Sopenharmony_ci return NULL; 938c2ecf20Sopenharmony_ci return &rdev->wpan_phy; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistruct wpan_phy * 978c2ecf20Sopenharmony_ciwpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci static atomic_t wpan_phy_counter = ATOMIC_INIT(0); 1008c2ecf20Sopenharmony_ci struct cfg802154_registered_device *rdev; 1018c2ecf20Sopenharmony_ci size_t alloc_size; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci alloc_size = sizeof(*rdev) + priv_size; 1048c2ecf20Sopenharmony_ci rdev = kzalloc(alloc_size, GFP_KERNEL); 1058c2ecf20Sopenharmony_ci if (!rdev) 1068c2ecf20Sopenharmony_ci return NULL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci rdev->ops = ops; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (unlikely(rdev->wpan_phy_idx < 0)) { 1138c2ecf20Sopenharmony_ci /* ugh, wrapped! */ 1148c2ecf20Sopenharmony_ci atomic_dec(&wpan_phy_counter); 1158c2ecf20Sopenharmony_ci kfree(rdev); 1168c2ecf20Sopenharmony_ci return NULL; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* atomic_inc_return makes it start at 1, make it start at 0 */ 1208c2ecf20Sopenharmony_ci rdev->wpan_phy_idx--; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rdev->wpan_dev_list); 1238c2ecf20Sopenharmony_ci device_initialize(&rdev->wpan_phy.dev); 1248c2ecf20Sopenharmony_ci dev_set_name(&rdev->wpan_phy.dev, PHY_NAME "%d", rdev->wpan_phy_idx); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci rdev->wpan_phy.dev.class = &wpan_phy_class; 1278c2ecf20Sopenharmony_ci rdev->wpan_phy.dev.platform_data = rdev; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci wpan_phy_net_set(&rdev->wpan_phy, &init_net); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci init_waitqueue_head(&rdev->dev_wait); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return &rdev->wpan_phy; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wpan_phy_new); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ciint wpan_phy_register(struct wpan_phy *phy) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy); 1408c2ecf20Sopenharmony_ci int ret; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci rtnl_lock(); 1438c2ecf20Sopenharmony_ci ret = device_add(&phy->dev); 1448c2ecf20Sopenharmony_ci if (ret) { 1458c2ecf20Sopenharmony_ci rtnl_unlock(); 1468c2ecf20Sopenharmony_ci return ret; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci list_add_rcu(&rdev->list, &cfg802154_rdev_list); 1508c2ecf20Sopenharmony_ci cfg802154_rdev_list_generation++; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* TODO phy registered lock */ 1538c2ecf20Sopenharmony_ci rtnl_unlock(); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* TODO nl802154 phy notify */ 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wpan_phy_register); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_civoid wpan_phy_unregister(struct wpan_phy *phy) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci wait_event(rdev->dev_wait, ({ 1668c2ecf20Sopenharmony_ci int __count; 1678c2ecf20Sopenharmony_ci rtnl_lock(); 1688c2ecf20Sopenharmony_ci __count = rdev->opencount; 1698c2ecf20Sopenharmony_ci rtnl_unlock(); 1708c2ecf20Sopenharmony_ci __count == 0; })); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci rtnl_lock(); 1738c2ecf20Sopenharmony_ci /* TODO nl802154 phy notify */ 1748c2ecf20Sopenharmony_ci /* TODO phy registered lock */ 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&rdev->wpan_dev_list)); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* First remove the hardware from everywhere, this makes 1798c2ecf20Sopenharmony_ci * it impossible to find from userspace. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci list_del_rcu(&rdev->list); 1828c2ecf20Sopenharmony_ci synchronize_rcu(); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci cfg802154_rdev_list_generation++; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci device_del(&phy->dev); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci rtnl_unlock(); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wpan_phy_unregister); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_civoid wpan_phy_free(struct wpan_phy *phy) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci put_device(&phy->dev); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wpan_phy_free); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ciint cfg802154_switch_netns(struct cfg802154_registered_device *rdev, 1998c2ecf20Sopenharmony_ci struct net *net) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct wpan_dev *wpan_dev; 2028c2ecf20Sopenharmony_ci int err = 0; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) { 2058c2ecf20Sopenharmony_ci if (!wpan_dev->netdev) 2068c2ecf20Sopenharmony_ci continue; 2078c2ecf20Sopenharmony_ci wpan_dev->netdev->features &= ~NETIF_F_NETNS_LOCAL; 2088c2ecf20Sopenharmony_ci err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d"); 2098c2ecf20Sopenharmony_ci if (err) 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci wpan_dev->netdev->features |= NETIF_F_NETNS_LOCAL; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (err) { 2158c2ecf20Sopenharmony_ci /* failed -- clean up to old netns */ 2168c2ecf20Sopenharmony_ci net = wpan_phy_net(&rdev->wpan_phy); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(wpan_dev, 2198c2ecf20Sopenharmony_ci &rdev->wpan_dev_list, 2208c2ecf20Sopenharmony_ci list) { 2218c2ecf20Sopenharmony_ci if (!wpan_dev->netdev) 2228c2ecf20Sopenharmony_ci continue; 2238c2ecf20Sopenharmony_ci wpan_dev->netdev->features &= ~NETIF_F_NETNS_LOCAL; 2248c2ecf20Sopenharmony_ci err = dev_change_net_namespace(wpan_dev->netdev, net, 2258c2ecf20Sopenharmony_ci "wpan%d"); 2268c2ecf20Sopenharmony_ci WARN_ON(err); 2278c2ecf20Sopenharmony_ci wpan_dev->netdev->features |= NETIF_F_NETNS_LOCAL; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return err; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci wpan_phy_net_set(&rdev->wpan_phy, net); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci err = device_rename(&rdev->wpan_phy.dev, dev_name(&rdev->wpan_phy.dev)); 2368c2ecf20Sopenharmony_ci WARN_ON(err); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_civoid cfg802154_dev_free(struct cfg802154_registered_device *rdev) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci kfree(rdev); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic void 2478c2ecf20Sopenharmony_cicfg802154_update_iface_num(struct cfg802154_registered_device *rdev, 2488c2ecf20Sopenharmony_ci int iftype, int num) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci ASSERT_RTNL(); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci rdev->num_running_ifaces += num; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int cfg802154_netdev_notifier_call(struct notifier_block *nb, 2568c2ecf20Sopenharmony_ci unsigned long state, void *ptr) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 2598c2ecf20Sopenharmony_ci struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 2608c2ecf20Sopenharmony_ci struct cfg802154_registered_device *rdev; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (!wpan_dev) 2638c2ecf20Sopenharmony_ci return NOTIFY_DONE; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* TODO WARN_ON unspec type */ 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci switch (state) { 2708c2ecf20Sopenharmony_ci /* TODO NETDEV_DEVTYPE */ 2718c2ecf20Sopenharmony_ci case NETDEV_REGISTER: 2728c2ecf20Sopenharmony_ci dev->features |= NETIF_F_NETNS_LOCAL; 2738c2ecf20Sopenharmony_ci wpan_dev->identifier = ++rdev->wpan_dev_id; 2748c2ecf20Sopenharmony_ci list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list); 2758c2ecf20Sopenharmony_ci rdev->devlist_generation++; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci wpan_dev->netdev = dev; 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci case NETDEV_DOWN: 2808c2ecf20Sopenharmony_ci cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci rdev->opencount--; 2838c2ecf20Sopenharmony_ci wake_up(&rdev->dev_wait); 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci case NETDEV_UP: 2868c2ecf20Sopenharmony_ci cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci rdev->opencount++; 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 2918c2ecf20Sopenharmony_ci /* It is possible to get NETDEV_UNREGISTER 2928c2ecf20Sopenharmony_ci * multiple times. To detect that, check 2938c2ecf20Sopenharmony_ci * that the interface is still on the list 2948c2ecf20Sopenharmony_ci * of registered interfaces, and only then 2958c2ecf20Sopenharmony_ci * remove and clean it up. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ci if (!list_empty(&wpan_dev->list)) { 2988c2ecf20Sopenharmony_ci list_del_rcu(&wpan_dev->list); 2998c2ecf20Sopenharmony_ci rdev->devlist_generation++; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci /* synchronize (so that we won't find this netdev 3028c2ecf20Sopenharmony_ci * from other code any more) and then clear the list 3038c2ecf20Sopenharmony_ci * head so that the above code can safely check for 3048c2ecf20Sopenharmony_ci * !list_empty() to avoid double-cleanup. 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_ci synchronize_rcu(); 3078c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&wpan_dev->list); 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci default: 3108c2ecf20Sopenharmony_ci return NOTIFY_DONE; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return NOTIFY_OK; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic struct notifier_block cfg802154_netdev_notifier = { 3178c2ecf20Sopenharmony_ci .notifier_call = cfg802154_netdev_notifier_call, 3188c2ecf20Sopenharmony_ci}; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic void __net_exit cfg802154_pernet_exit(struct net *net) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct cfg802154_registered_device *rdev; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci rtnl_lock(); 3258c2ecf20Sopenharmony_ci list_for_each_entry(rdev, &cfg802154_rdev_list, list) { 3268c2ecf20Sopenharmony_ci if (net_eq(wpan_phy_net(&rdev->wpan_phy), net)) 3278c2ecf20Sopenharmony_ci WARN_ON(cfg802154_switch_netns(rdev, &init_net)); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci rtnl_unlock(); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic struct pernet_operations cfg802154_pernet_ops = { 3338c2ecf20Sopenharmony_ci .exit = cfg802154_pernet_exit, 3348c2ecf20Sopenharmony_ci}; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int __init wpan_phy_class_init(void) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci int rc; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci rc = register_pernet_device(&cfg802154_pernet_ops); 3418c2ecf20Sopenharmony_ci if (rc) 3428c2ecf20Sopenharmony_ci goto err; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci rc = wpan_phy_sysfs_init(); 3458c2ecf20Sopenharmony_ci if (rc) 3468c2ecf20Sopenharmony_ci goto err_sysfs; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci rc = register_netdevice_notifier(&cfg802154_netdev_notifier); 3498c2ecf20Sopenharmony_ci if (rc) 3508c2ecf20Sopenharmony_ci goto err_nl; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci rc = ieee802154_nl_init(); 3538c2ecf20Sopenharmony_ci if (rc) 3548c2ecf20Sopenharmony_ci goto err_notifier; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci rc = nl802154_init(); 3578c2ecf20Sopenharmony_ci if (rc) 3588c2ecf20Sopenharmony_ci goto err_ieee802154_nl; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return 0; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cierr_ieee802154_nl: 3638c2ecf20Sopenharmony_ci ieee802154_nl_exit(); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cierr_notifier: 3668c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&cfg802154_netdev_notifier); 3678c2ecf20Sopenharmony_cierr_nl: 3688c2ecf20Sopenharmony_ci wpan_phy_sysfs_exit(); 3698c2ecf20Sopenharmony_cierr_sysfs: 3708c2ecf20Sopenharmony_ci unregister_pernet_device(&cfg802154_pernet_ops); 3718c2ecf20Sopenharmony_cierr: 3728c2ecf20Sopenharmony_ci return rc; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_cisubsys_initcall(wpan_phy_class_init); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic void __exit wpan_phy_class_exit(void) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci nl802154_exit(); 3798c2ecf20Sopenharmony_ci ieee802154_nl_exit(); 3808c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&cfg802154_netdev_notifier); 3818c2ecf20Sopenharmony_ci wpan_phy_sysfs_exit(); 3828c2ecf20Sopenharmony_ci unregister_pernet_device(&cfg802154_pernet_ops); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_cimodule_exit(wpan_phy_class_exit); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IEEE 802.15.4 configuration interface"); 3888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dmitry Eremin-Solenikov"); 389