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