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