| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * Ultra Wide Band | 
|  | 3 | * Life cycle of devices | 
|  | 4 | * | 
|  | 5 | * Copyright (C) 2005-2006 Intel Corporation | 
|  | 6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | 
|  | 7 | * | 
|  | 8 | * This program is free software; you can redistribute it and/or | 
|  | 9 | * modify it under the terms of the GNU General Public License version | 
|  | 10 | * 2 as published by the Free Software Foundation. | 
|  | 11 | * | 
|  | 12 | * This program is distributed in the hope that it will be useful, | 
|  | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 15 | * GNU General Public License for more details. | 
|  | 16 | * | 
|  | 17 | * You should have received a copy of the GNU General Public License | 
|  | 18 | * along with this program; if not, write to the Free Software | 
|  | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 
|  | 20 | * 02110-1301, USA. | 
|  | 21 | * | 
|  | 22 | * | 
|  | 23 | * FIXME: docs | 
|  | 24 | */ | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 25 | #include <linux/kernel.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 26 | #include <linux/slab.h> | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 27 | #include <linux/device.h> | 
|  | 28 | #include <linux/err.h> | 
|  | 29 | #include <linux/kdev_t.h> | 
|  | 30 | #include <linux/random.h> | 
|  | 31 | #include "uwb-internal.h" | 
|  | 32 |  | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 33 | /* We initialize addresses to 0xff (invalid, as it is bcast) */ | 
|  | 34 | static inline void uwb_dev_addr_init(struct uwb_dev_addr *addr) | 
|  | 35 | { | 
|  | 36 | memset(&addr->data, 0xff, sizeof(addr->data)); | 
|  | 37 | } | 
|  | 38 |  | 
|  | 39 | static inline void uwb_mac_addr_init(struct uwb_mac_addr *addr) | 
|  | 40 | { | 
|  | 41 | memset(&addr->data, 0xff, sizeof(addr->data)); | 
|  | 42 | } | 
|  | 43 |  | 
|  | 44 | /* @returns !0 if a device @addr is a broadcast address */ | 
|  | 45 | static inline int uwb_dev_addr_bcast(const struct uwb_dev_addr *addr) | 
|  | 46 | { | 
|  | 47 | static const struct uwb_dev_addr bcast = { .data = { 0xff, 0xff } }; | 
|  | 48 | return !uwb_dev_addr_cmp(addr, &bcast); | 
|  | 49 | } | 
|  | 50 |  | 
|  | 51 | /* | 
|  | 52 | * Add callback @new to be called when an event occurs in @rc. | 
|  | 53 | */ | 
|  | 54 | int uwb_notifs_register(struct uwb_rc *rc, struct uwb_notifs_handler *new) | 
|  | 55 | { | 
|  | 56 | if (mutex_lock_interruptible(&rc->notifs_chain.mutex)) | 
|  | 57 | return -ERESTARTSYS; | 
|  | 58 | list_add(&new->list_node, &rc->notifs_chain.list); | 
|  | 59 | mutex_unlock(&rc->notifs_chain.mutex); | 
|  | 60 | return 0; | 
|  | 61 | } | 
|  | 62 | EXPORT_SYMBOL_GPL(uwb_notifs_register); | 
|  | 63 |  | 
|  | 64 | /* | 
|  | 65 | * Remove event handler (callback) | 
|  | 66 | */ | 
|  | 67 | int uwb_notifs_deregister(struct uwb_rc *rc, struct uwb_notifs_handler *entry) | 
|  | 68 | { | 
|  | 69 | if (mutex_lock_interruptible(&rc->notifs_chain.mutex)) | 
|  | 70 | return -ERESTARTSYS; | 
|  | 71 | list_del(&entry->list_node); | 
|  | 72 | mutex_unlock(&rc->notifs_chain.mutex); | 
|  | 73 | return 0; | 
|  | 74 | } | 
|  | 75 | EXPORT_SYMBOL_GPL(uwb_notifs_deregister); | 
|  | 76 |  | 
|  | 77 | /* | 
|  | 78 | * Notify all event handlers of a given event on @rc | 
|  | 79 | * | 
|  | 80 | * We are called with a valid reference to the device, or NULL if the | 
|  | 81 | * event is not for a particular event (e.g., a BG join event). | 
|  | 82 | */ | 
|  | 83 | void uwb_notify(struct uwb_rc *rc, struct uwb_dev *uwb_dev, enum uwb_notifs event) | 
|  | 84 | { | 
|  | 85 | struct uwb_notifs_handler *handler; | 
|  | 86 | if (mutex_lock_interruptible(&rc->notifs_chain.mutex)) | 
|  | 87 | return; | 
|  | 88 | if (!list_empty(&rc->notifs_chain.list)) { | 
|  | 89 | list_for_each_entry(handler, &rc->notifs_chain.list, list_node) { | 
|  | 90 | handler->cb(handler->data, uwb_dev, event); | 
|  | 91 | } | 
|  | 92 | } | 
|  | 93 | mutex_unlock(&rc->notifs_chain.mutex); | 
|  | 94 | } | 
|  | 95 |  | 
|  | 96 | /* | 
|  | 97 | * Release the backing device of a uwb_dev that has been dynamically allocated. | 
|  | 98 | */ | 
|  | 99 | static void uwb_dev_sys_release(struct device *dev) | 
|  | 100 | { | 
|  | 101 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | 
|  | 102 |  | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 103 | uwb_bce_put(uwb_dev->bce); | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 104 | memset(uwb_dev, 0x69, sizeof(*uwb_dev)); | 
|  | 105 | kfree(uwb_dev); | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 106 | } | 
|  | 107 |  | 
|  | 108 | /* | 
|  | 109 | * Initialize a UWB device instance | 
|  | 110 | * | 
|  | 111 | * Alloc, zero and call this function. | 
|  | 112 | */ | 
|  | 113 | void uwb_dev_init(struct uwb_dev *uwb_dev) | 
|  | 114 | { | 
|  | 115 | mutex_init(&uwb_dev->mutex); | 
|  | 116 | device_initialize(&uwb_dev->dev); | 
|  | 117 | uwb_dev->dev.release = uwb_dev_sys_release; | 
|  | 118 | uwb_dev_addr_init(&uwb_dev->dev_addr); | 
|  | 119 | uwb_mac_addr_init(&uwb_dev->mac_addr); | 
|  | 120 | bitmap_fill(uwb_dev->streams, UWB_NUM_GLOBAL_STREAMS); | 
|  | 121 | } | 
|  | 122 |  | 
|  | 123 | static ssize_t uwb_dev_EUI_48_show(struct device *dev, | 
|  | 124 | struct device_attribute *attr, char *buf) | 
|  | 125 | { | 
|  | 126 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | 
|  | 127 | char addr[UWB_ADDR_STRSIZE]; | 
|  | 128 |  | 
|  | 129 | uwb_mac_addr_print(addr, sizeof(addr), &uwb_dev->mac_addr); | 
|  | 130 | return sprintf(buf, "%s\n", addr); | 
|  | 131 | } | 
|  | 132 | static DEVICE_ATTR(EUI_48, S_IRUGO, uwb_dev_EUI_48_show, NULL); | 
|  | 133 |  | 
|  | 134 | static ssize_t uwb_dev_DevAddr_show(struct device *dev, | 
|  | 135 | struct device_attribute *attr, char *buf) | 
|  | 136 | { | 
|  | 137 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | 
|  | 138 | char addr[UWB_ADDR_STRSIZE]; | 
|  | 139 |  | 
|  | 140 | uwb_dev_addr_print(addr, sizeof(addr), &uwb_dev->dev_addr); | 
|  | 141 | return sprintf(buf, "%s\n", addr); | 
|  | 142 | } | 
|  | 143 | static DEVICE_ATTR(DevAddr, S_IRUGO, uwb_dev_DevAddr_show, NULL); | 
|  | 144 |  | 
|  | 145 | /* | 
|  | 146 | * Show the BPST of this device. | 
|  | 147 | * | 
|  | 148 | * Calculated from the receive time of the device's beacon and it's | 
|  | 149 | * slot number. | 
|  | 150 | */ | 
|  | 151 | static ssize_t uwb_dev_BPST_show(struct device *dev, | 
|  | 152 | struct device_attribute *attr, char *buf) | 
|  | 153 | { | 
|  | 154 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | 
|  | 155 | struct uwb_beca_e *bce; | 
|  | 156 | struct uwb_beacon_frame *bf; | 
|  | 157 | u16 bpst; | 
|  | 158 |  | 
|  | 159 | bce = uwb_dev->bce; | 
|  | 160 | mutex_lock(&bce->mutex); | 
|  | 161 | bf = (struct uwb_beacon_frame *)bce->be->BeaconInfo; | 
|  | 162 | bpst = bce->be->wBPSTOffset | 
|  | 163 | - (u16)(bf->Beacon_Slot_Number * UWB_BEACON_SLOT_LENGTH_US); | 
|  | 164 | mutex_unlock(&bce->mutex); | 
|  | 165 |  | 
|  | 166 | return sprintf(buf, "%d\n", bpst); | 
|  | 167 | } | 
|  | 168 | static DEVICE_ATTR(BPST, S_IRUGO, uwb_dev_BPST_show, NULL); | 
|  | 169 |  | 
|  | 170 | /* | 
|  | 171 | * Show the IEs a device is beaconing | 
|  | 172 | * | 
|  | 173 | * We need to access the beacon cache, so we just lock it really | 
|  | 174 | * quick, print the IEs and unlock. | 
|  | 175 | * | 
|  | 176 | * We have a reference on the cache entry, so that should be | 
|  | 177 | * quite safe. | 
|  | 178 | */ | 
|  | 179 | static ssize_t uwb_dev_IEs_show(struct device *dev, | 
|  | 180 | struct device_attribute *attr, char *buf) | 
|  | 181 | { | 
|  | 182 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | 
|  | 183 |  | 
|  | 184 | return uwb_bce_print_IEs(uwb_dev, uwb_dev->bce, buf, PAGE_SIZE); | 
|  | 185 | } | 
|  | 186 | static DEVICE_ATTR(IEs, S_IRUGO | S_IWUSR, uwb_dev_IEs_show, NULL); | 
|  | 187 |  | 
|  | 188 | static ssize_t uwb_dev_LQE_show(struct device *dev, | 
|  | 189 | struct device_attribute *attr, char *buf) | 
|  | 190 | { | 
|  | 191 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | 
|  | 192 | struct uwb_beca_e *bce = uwb_dev->bce; | 
|  | 193 | size_t result; | 
|  | 194 |  | 
|  | 195 | mutex_lock(&bce->mutex); | 
|  | 196 | result = stats_show(&uwb_dev->bce->lqe_stats, buf); | 
|  | 197 | mutex_unlock(&bce->mutex); | 
|  | 198 | return result; | 
|  | 199 | } | 
|  | 200 |  | 
|  | 201 | static ssize_t uwb_dev_LQE_store(struct device *dev, | 
|  | 202 | struct device_attribute *attr, | 
|  | 203 | const char *buf, size_t size) | 
|  | 204 | { | 
|  | 205 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | 
|  | 206 | struct uwb_beca_e *bce = uwb_dev->bce; | 
|  | 207 | ssize_t result; | 
|  | 208 |  | 
|  | 209 | mutex_lock(&bce->mutex); | 
|  | 210 | result = stats_store(&uwb_dev->bce->lqe_stats, buf, size); | 
|  | 211 | mutex_unlock(&bce->mutex); | 
|  | 212 | return result; | 
|  | 213 | } | 
|  | 214 | static DEVICE_ATTR(LQE, S_IRUGO | S_IWUSR, uwb_dev_LQE_show, uwb_dev_LQE_store); | 
|  | 215 |  | 
|  | 216 | static ssize_t uwb_dev_RSSI_show(struct device *dev, | 
|  | 217 | struct device_attribute *attr, char *buf) | 
|  | 218 | { | 
|  | 219 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | 
|  | 220 | struct uwb_beca_e *bce = uwb_dev->bce; | 
|  | 221 | size_t result; | 
|  | 222 |  | 
|  | 223 | mutex_lock(&bce->mutex); | 
|  | 224 | result = stats_show(&uwb_dev->bce->rssi_stats, buf); | 
|  | 225 | mutex_unlock(&bce->mutex); | 
|  | 226 | return result; | 
|  | 227 | } | 
|  | 228 |  | 
|  | 229 | static ssize_t uwb_dev_RSSI_store(struct device *dev, | 
|  | 230 | struct device_attribute *attr, | 
|  | 231 | const char *buf, size_t size) | 
|  | 232 | { | 
|  | 233 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | 
|  | 234 | struct uwb_beca_e *bce = uwb_dev->bce; | 
|  | 235 | ssize_t result; | 
|  | 236 |  | 
|  | 237 | mutex_lock(&bce->mutex); | 
|  | 238 | result = stats_store(&uwb_dev->bce->rssi_stats, buf, size); | 
|  | 239 | mutex_unlock(&bce->mutex); | 
|  | 240 | return result; | 
|  | 241 | } | 
|  | 242 | static DEVICE_ATTR(RSSI, S_IRUGO | S_IWUSR, uwb_dev_RSSI_show, uwb_dev_RSSI_store); | 
|  | 243 |  | 
|  | 244 |  | 
|  | 245 | static struct attribute *dev_attrs[] = { | 
|  | 246 | &dev_attr_EUI_48.attr, | 
|  | 247 | &dev_attr_DevAddr.attr, | 
|  | 248 | &dev_attr_BPST.attr, | 
|  | 249 | &dev_attr_IEs.attr, | 
|  | 250 | &dev_attr_LQE.attr, | 
|  | 251 | &dev_attr_RSSI.attr, | 
|  | 252 | NULL, | 
|  | 253 | }; | 
|  | 254 |  | 
|  | 255 | static struct attribute_group dev_attr_group = { | 
|  | 256 | .attrs = dev_attrs, | 
|  | 257 | }; | 
|  | 258 |  | 
| David Brownell | a4dbd67 | 2009-06-24 10:06:31 -0700 | [diff] [blame] | 259 | static const struct attribute_group *groups[] = { | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 260 | &dev_attr_group, | 
|  | 261 | NULL, | 
|  | 262 | }; | 
|  | 263 |  | 
|  | 264 | /** | 
|  | 265 | * Device SYSFS registration | 
|  | 266 | * | 
|  | 267 | * | 
|  | 268 | */ | 
|  | 269 | static int __uwb_dev_sys_add(struct uwb_dev *uwb_dev, struct device *parent_dev) | 
|  | 270 | { | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 271 | struct device *dev; | 
|  | 272 |  | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 273 | dev = &uwb_dev->dev; | 
|  | 274 | /* Device sysfs files are only useful for neighbor devices not | 
|  | 275 | local radio controllers. */ | 
|  | 276 | if (&uwb_dev->rc->uwb_dev != uwb_dev) | 
|  | 277 | dev->groups = groups; | 
|  | 278 | dev->parent = parent_dev; | 
|  | 279 | dev_set_drvdata(dev, uwb_dev); | 
|  | 280 |  | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 281 | return device_add(dev); | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 282 | } | 
|  | 283 |  | 
|  | 284 |  | 
|  | 285 | static void __uwb_dev_sys_rm(struct uwb_dev *uwb_dev) | 
|  | 286 | { | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 287 | dev_set_drvdata(&uwb_dev->dev, NULL); | 
|  | 288 | device_del(&uwb_dev->dev); | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 289 | } | 
|  | 290 |  | 
|  | 291 |  | 
|  | 292 | /** | 
|  | 293 | * Register and initialize a new UWB device | 
|  | 294 | * | 
|  | 295 | * Did you call uwb_dev_init() on it? | 
|  | 296 | * | 
|  | 297 | * @parent_rc: is the parent radio controller who has the link to the | 
|  | 298 | *             device. When registering the UWB device that is a UWB | 
|  | 299 | *             Radio Controller, we point back to it. | 
|  | 300 | * | 
|  | 301 | * If registering the device that is part of a radio, caller has set | 
|  | 302 | * rc->uwb_dev->dev. Otherwise it is to be left NULL--a new one will | 
|  | 303 | * be allocated. | 
|  | 304 | */ | 
|  | 305 | int uwb_dev_add(struct uwb_dev *uwb_dev, struct device *parent_dev, | 
|  | 306 | struct uwb_rc *parent_rc) | 
|  | 307 | { | 
|  | 308 | int result; | 
|  | 309 | struct device *dev; | 
|  | 310 |  | 
|  | 311 | BUG_ON(uwb_dev == NULL); | 
|  | 312 | BUG_ON(parent_dev == NULL); | 
|  | 313 | BUG_ON(parent_rc == NULL); | 
|  | 314 |  | 
|  | 315 | mutex_lock(&uwb_dev->mutex); | 
|  | 316 | dev = &uwb_dev->dev; | 
|  | 317 | uwb_dev->rc = parent_rc; | 
|  | 318 | result = __uwb_dev_sys_add(uwb_dev, parent_dev); | 
|  | 319 | if (result < 0) | 
|  | 320 | printk(KERN_ERR "UWB: unable to register dev %s with sysfs: %d\n", | 
|  | 321 | dev_name(dev), result); | 
|  | 322 | mutex_unlock(&uwb_dev->mutex); | 
|  | 323 | return result; | 
|  | 324 | } | 
|  | 325 |  | 
|  | 326 |  | 
|  | 327 | void uwb_dev_rm(struct uwb_dev *uwb_dev) | 
|  | 328 | { | 
|  | 329 | mutex_lock(&uwb_dev->mutex); | 
|  | 330 | __uwb_dev_sys_rm(uwb_dev); | 
|  | 331 | mutex_unlock(&uwb_dev->mutex); | 
|  | 332 | } | 
|  | 333 |  | 
|  | 334 |  | 
|  | 335 | static | 
|  | 336 | int __uwb_dev_try_get(struct device *dev, void *__target_uwb_dev) | 
|  | 337 | { | 
|  | 338 | struct uwb_dev *target_uwb_dev = __target_uwb_dev; | 
|  | 339 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | 
|  | 340 | if (uwb_dev == target_uwb_dev) { | 
|  | 341 | uwb_dev_get(uwb_dev); | 
|  | 342 | return 1; | 
|  | 343 | } else | 
|  | 344 | return 0; | 
|  | 345 | } | 
|  | 346 |  | 
|  | 347 |  | 
|  | 348 | /** | 
|  | 349 | * Given a UWB device descriptor, validate and refcount it | 
|  | 350 | * | 
|  | 351 | * @returns NULL if the device does not exist or is quiescing; the ptr to | 
|  | 352 | *               it otherwise. | 
|  | 353 | */ | 
|  | 354 | struct uwb_dev *uwb_dev_try_get(struct uwb_rc *rc, struct uwb_dev *uwb_dev) | 
|  | 355 | { | 
|  | 356 | if (uwb_dev_for_each(rc, __uwb_dev_try_get, uwb_dev)) | 
|  | 357 | return uwb_dev; | 
|  | 358 | else | 
|  | 359 | return NULL; | 
|  | 360 | } | 
|  | 361 | EXPORT_SYMBOL_GPL(uwb_dev_try_get); | 
|  | 362 |  | 
|  | 363 |  | 
|  | 364 | /** | 
|  | 365 | * Remove a device from the system [grunt for other functions] | 
|  | 366 | */ | 
|  | 367 | int __uwb_dev_offair(struct uwb_dev *uwb_dev, struct uwb_rc *rc) | 
|  | 368 | { | 
|  | 369 | struct device *dev = &uwb_dev->dev; | 
|  | 370 | char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE]; | 
|  | 371 |  | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 372 | uwb_mac_addr_print(macbuf, sizeof(macbuf), &uwb_dev->mac_addr); | 
|  | 373 | uwb_dev_addr_print(devbuf, sizeof(devbuf), &uwb_dev->dev_addr); | 
|  | 374 | dev_info(dev, "uwb device (mac %s dev %s) disconnected from %s %s\n", | 
|  | 375 | macbuf, devbuf, | 
|  | 376 | rc ? rc->uwb_dev.dev.parent->bus->name : "n/a", | 
|  | 377 | rc ? dev_name(rc->uwb_dev.dev.parent) : ""); | 
|  | 378 | uwb_dev_rm(uwb_dev); | 
| Stefano Panella | 3af3730 | 2008-12-23 12:31:09 +0000 | [diff] [blame] | 379 | list_del(&uwb_dev->bce->node); | 
|  | 380 | uwb_bce_put(uwb_dev->bce); | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 381 | uwb_dev_put(uwb_dev);	/* for the creation in _onair() */ | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 382 |  | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 383 | return 0; | 
|  | 384 | } | 
|  | 385 |  | 
|  | 386 |  | 
|  | 387 | /** | 
|  | 388 | * A device went off the air, clean up after it! | 
|  | 389 | * | 
|  | 390 | * This is called by the UWB Daemon (through the beacon purge function | 
|  | 391 | * uwb_bcn_cache_purge) when it is detected that a device has been in | 
|  | 392 | * radio silence for a while. | 
|  | 393 | * | 
|  | 394 | * If this device is actually a local radio controller we don't need | 
|  | 395 | * to go through the offair process, as it is not registered as that. | 
|  | 396 | * | 
|  | 397 | * NOTE: uwb_bcn_cache.mutex is held! | 
|  | 398 | */ | 
|  | 399 | void uwbd_dev_offair(struct uwb_beca_e *bce) | 
|  | 400 | { | 
|  | 401 | struct uwb_dev *uwb_dev; | 
|  | 402 |  | 
|  | 403 | uwb_dev = bce->uwb_dev; | 
|  | 404 | if (uwb_dev) { | 
|  | 405 | uwb_notify(uwb_dev->rc, uwb_dev, UWB_NOTIF_OFFAIR); | 
|  | 406 | __uwb_dev_offair(uwb_dev, uwb_dev->rc); | 
|  | 407 | } | 
|  | 408 | } | 
|  | 409 |  | 
|  | 410 |  | 
|  | 411 | /** | 
|  | 412 | * A device went on the air, start it up! | 
|  | 413 | * | 
|  | 414 | * This is called by the UWB Daemon when it is detected that a device | 
|  | 415 | * has popped up in the radio range of the radio controller. | 
|  | 416 | * | 
|  | 417 | * It will just create the freaking device, register the beacon and | 
|  | 418 | * stuff and yatla, done. | 
|  | 419 | * | 
|  | 420 | * | 
|  | 421 | * NOTE: uwb_beca.mutex is held, bce->mutex is held | 
|  | 422 | */ | 
|  | 423 | void uwbd_dev_onair(struct uwb_rc *rc, struct uwb_beca_e *bce) | 
|  | 424 | { | 
|  | 425 | int result; | 
|  | 426 | struct device *dev = &rc->uwb_dev.dev; | 
|  | 427 | struct uwb_dev *uwb_dev; | 
|  | 428 | char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE]; | 
|  | 429 |  | 
|  | 430 | uwb_mac_addr_print(macbuf, sizeof(macbuf), bce->mac_addr); | 
|  | 431 | uwb_dev_addr_print(devbuf, sizeof(devbuf), &bce->dev_addr); | 
| David Vrabel | 92c4d9b | 2008-10-15 14:50:10 +0100 | [diff] [blame] | 432 | uwb_dev = kzalloc(sizeof(struct uwb_dev), GFP_KERNEL); | 
| Inaky Perez-Gonzalez | 183b9b5 | 2008-09-17 16:34:06 +0100 | [diff] [blame] | 433 | if (uwb_dev == NULL) { | 
|  | 434 | dev_err(dev, "new device %s: Cannot allocate memory\n", | 
|  | 435 | macbuf); | 
|  | 436 | return; | 
|  | 437 | } | 
|  | 438 | uwb_dev_init(uwb_dev);		/* This sets refcnt to one, we own it */ | 
|  | 439 | uwb_dev->mac_addr = *bce->mac_addr; | 
|  | 440 | uwb_dev->dev_addr = bce->dev_addr; | 
|  | 441 | dev_set_name(&uwb_dev->dev, macbuf); | 
|  | 442 | result = uwb_dev_add(uwb_dev, &rc->uwb_dev.dev, rc); | 
|  | 443 | if (result < 0) { | 
|  | 444 | dev_err(dev, "new device %s: cannot instantiate device\n", | 
|  | 445 | macbuf); | 
|  | 446 | goto error_dev_add; | 
|  | 447 | } | 
|  | 448 | /* plug the beacon cache */ | 
|  | 449 | bce->uwb_dev = uwb_dev; | 
|  | 450 | uwb_dev->bce = bce; | 
|  | 451 | uwb_bce_get(bce);		/* released in uwb_dev_sys_release() */ | 
|  | 452 | dev_info(dev, "uwb device (mac %s dev %s) connected to %s %s\n", | 
|  | 453 | macbuf, devbuf, rc->uwb_dev.dev.parent->bus->name, | 
|  | 454 | dev_name(rc->uwb_dev.dev.parent)); | 
|  | 455 | uwb_notify(rc, uwb_dev, UWB_NOTIF_ONAIR); | 
|  | 456 | return; | 
|  | 457 |  | 
|  | 458 | error_dev_add: | 
|  | 459 | kfree(uwb_dev); | 
|  | 460 | return; | 
|  | 461 | } | 
|  | 462 |  | 
|  | 463 | /** | 
|  | 464 | * Iterate over the list of UWB devices, calling a @function on each | 
|  | 465 | * | 
|  | 466 | * See docs for bus_for_each().... | 
|  | 467 | * | 
|  | 468 | * @rc:       radio controller for the devices. | 
|  | 469 | * @function: function to call. | 
|  | 470 | * @priv:     data to pass to @function. | 
|  | 471 | * @returns:  0 if no invocation of function() returned a value | 
|  | 472 | *            different to zero. That value otherwise. | 
|  | 473 | */ | 
|  | 474 | int uwb_dev_for_each(struct uwb_rc *rc, uwb_dev_for_each_f function, void *priv) | 
|  | 475 | { | 
|  | 476 | return device_for_each_child(&rc->uwb_dev.dev, priv, function); | 
|  | 477 | } | 
|  | 478 | EXPORT_SYMBOL_GPL(uwb_dev_for_each); |