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 /* RCU-protected (and RTNL for writers) */ 29 LIST_HEAD(cfg802154_rdev_list); 30 int cfg802154_rdev_list_generation; 31 32 static int wpan_phy_match(struct device *dev, const void *data) 33 { 34 return !strcmp(dev_name(dev), (const char *)data); 35 } 36 37 struct wpan_phy *wpan_phy_find(const char *str) 38 { 39 struct device *dev; 40 41 if (WARN_ON(!str)) 42 return NULL; 43 44 dev = class_find_device(&wpan_phy_class, NULL, str, wpan_phy_match); 45 if (!dev) 46 return NULL; 47 48 return container_of(dev, struct wpan_phy, dev); 49 } 50 EXPORT_SYMBOL(wpan_phy_find); 51 52 struct wpan_phy_iter_data { 53 int (*fn)(struct wpan_phy *phy, void *data); 54 void *data; 55 }; 56 57 static int wpan_phy_iter(struct device *dev, void *_data) 58 { 59 struct wpan_phy_iter_data *wpid = _data; 60 struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); 61 62 return wpid->fn(phy, wpid->data); 63 } 64 65 int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data), 66 void *data) 67 { 68 struct wpan_phy_iter_data wpid = { 69 .fn = fn, 70 .data = data, 71 }; 72 73 return class_for_each_device(&wpan_phy_class, NULL, 74 &wpid, wpan_phy_iter); 75 } 76 EXPORT_SYMBOL(wpan_phy_for_each); 77 78 struct cfg802154_registered_device * 79 cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx) 80 { 81 struct cfg802154_registered_device *result = NULL, *rdev; 82 83 ASSERT_RTNL(); 84 85 list_for_each_entry(rdev, &cfg802154_rdev_list, list) { 86 if (rdev->wpan_phy_idx == wpan_phy_idx) { 87 result = rdev; 88 break; 89 } 90 } 91 92 return result; 93 } 94 95 struct wpan_phy * 96 wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size) 97 { 98 static atomic_t wpan_phy_counter = ATOMIC_INIT(0); 99 struct cfg802154_registered_device *rdev; 100 size_t alloc_size; 101 102 alloc_size = sizeof(*rdev) + priv_size; 103 rdev = kzalloc(alloc_size, GFP_KERNEL); 104 if (!rdev) 105 return NULL; 106 107 rdev->ops = ops; 108 109 rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter); 110 111 if (unlikely(rdev->wpan_phy_idx < 0)) { 112 /* ugh, wrapped! */ 113 atomic_dec(&wpan_phy_counter); 114 kfree(rdev); 115 return NULL; 116 } 117 118 /* atomic_inc_return makes it start at 1, make it start at 0 */ 119 rdev->wpan_phy_idx--; 120 121 mutex_init(&rdev->wpan_phy.pib_lock); 122 123 INIT_LIST_HEAD(&rdev->wpan_dev_list); 124 device_initialize(&rdev->wpan_phy.dev); 125 dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx); 126 127 rdev->wpan_phy.dev.class = &wpan_phy_class; 128 rdev->wpan_phy.dev.platform_data = rdev; 129 130 init_waitqueue_head(&rdev->dev_wait); 131 132 return &rdev->wpan_phy; 133 } 134 EXPORT_SYMBOL(wpan_phy_new); 135 136 int wpan_phy_register(struct wpan_phy *phy) 137 { 138 struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy); 139 int ret; 140 141 rtnl_lock(); 142 ret = device_add(&phy->dev); 143 if (ret) { 144 rtnl_unlock(); 145 return ret; 146 } 147 148 list_add_rcu(&rdev->list, &cfg802154_rdev_list); 149 cfg802154_rdev_list_generation++; 150 151 /* TODO phy registered lock */ 152 rtnl_unlock(); 153 154 /* TODO nl802154 phy notify */ 155 156 return 0; 157 } 158 EXPORT_SYMBOL(wpan_phy_register); 159 160 void wpan_phy_unregister(struct wpan_phy *phy) 161 { 162 struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy); 163 164 wait_event(rdev->dev_wait, ({ 165 int __count; 166 rtnl_lock(); 167 __count = rdev->opencount; 168 rtnl_unlock(); 169 __count == 0; })); 170 171 rtnl_lock(); 172 /* TODO nl802154 phy notify */ 173 /* TODO phy registered lock */ 174 175 WARN_ON(!list_empty(&rdev->wpan_dev_list)); 176 177 /* First remove the hardware from everywhere, this makes 178 * it impossible to find from userspace. 179 */ 180 list_del_rcu(&rdev->list); 181 synchronize_rcu(); 182 183 cfg802154_rdev_list_generation++; 184 185 device_del(&phy->dev); 186 187 rtnl_unlock(); 188 } 189 EXPORT_SYMBOL(wpan_phy_unregister); 190 191 void wpan_phy_free(struct wpan_phy *phy) 192 { 193 put_device(&phy->dev); 194 } 195 EXPORT_SYMBOL(wpan_phy_free); 196 197 void cfg802154_dev_free(struct cfg802154_registered_device *rdev) 198 { 199 kfree(rdev); 200 } 201 202 static void 203 cfg802154_update_iface_num(struct cfg802154_registered_device *rdev, 204 int iftype, int num) 205 { 206 ASSERT_RTNL(); 207 208 rdev->num_running_ifaces += num; 209 } 210 211 static int cfg802154_netdev_notifier_call(struct notifier_block *nb, 212 unsigned long state, void *ptr) 213 { 214 struct net_device *dev = netdev_notifier_info_to_dev(ptr); 215 struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 216 struct cfg802154_registered_device *rdev; 217 218 if (!wpan_dev) 219 return NOTIFY_DONE; 220 221 rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy); 222 223 /* TODO WARN_ON unspec type */ 224 225 switch (state) { 226 /* TODO NETDEV_DEVTYPE */ 227 case NETDEV_REGISTER: 228 wpan_dev->identifier = ++rdev->wpan_dev_id; 229 list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list); 230 rdev->devlist_generation++; 231 232 wpan_dev->netdev = dev; 233 break; 234 case NETDEV_DOWN: 235 cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1); 236 237 rdev->opencount--; 238 wake_up(&rdev->dev_wait); 239 break; 240 case NETDEV_UP: 241 cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1); 242 243 rdev->opencount++; 244 break; 245 case NETDEV_UNREGISTER: 246 /* It is possible to get NETDEV_UNREGISTER 247 * multiple times. To detect that, check 248 * that the interface is still on the list 249 * of registered interfaces, and only then 250 * remove and clean it up. 251 */ 252 if (!list_empty(&wpan_dev->list)) { 253 list_del_rcu(&wpan_dev->list); 254 rdev->devlist_generation++; 255 } 256 /* synchronize (so that we won't find this netdev 257 * from other code any more) and then clear the list 258 * head so that the above code can safely check for 259 * !list_empty() to avoid double-cleanup. 260 */ 261 synchronize_rcu(); 262 INIT_LIST_HEAD(&wpan_dev->list); 263 break; 264 default: 265 return NOTIFY_DONE; 266 } 267 268 return NOTIFY_OK; 269 } 270 271 static struct notifier_block cfg802154_netdev_notifier = { 272 .notifier_call = cfg802154_netdev_notifier_call, 273 }; 274 275 static int __init wpan_phy_class_init(void) 276 { 277 int rc; 278 279 rc = wpan_phy_sysfs_init(); 280 if (rc) 281 goto err; 282 283 rc = register_netdevice_notifier(&cfg802154_netdev_notifier); 284 if (rc) 285 goto err_nl; 286 287 rc = ieee802154_nl_init(); 288 if (rc) 289 goto err_notifier; 290 291 rc = nl802154_init(); 292 if (rc) 293 goto err_ieee802154_nl; 294 295 return 0; 296 297 err_ieee802154_nl: 298 ieee802154_nl_exit(); 299 300 err_notifier: 301 unregister_netdevice_notifier(&cfg802154_netdev_notifier); 302 err_nl: 303 wpan_phy_sysfs_exit(); 304 err: 305 return rc; 306 } 307 subsys_initcall(wpan_phy_class_init); 308 309 static void __exit wpan_phy_class_exit(void) 310 { 311 nl802154_exit(); 312 ieee802154_nl_exit(); 313 unregister_netdevice_notifier(&cfg802154_netdev_notifier); 314 wpan_phy_sysfs_exit(); 315 } 316 module_exit(wpan_phy_class_exit); 317 318 MODULE_LICENSE("GPL v2"); 319 MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface"); 320 MODULE_AUTHOR("Dmitry Eremin-Solenikov"); 321 322