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