]>
Commit | Line | Data |
---|---|---|
f5144854 RC |
1 | /* |
2 | * WiMedia Logical Link Control Protocol (WLP) | |
3 | * | |
4 | * Copyright (C) 2005-2006 Intel Corporation | |
5 | * Reinette Chatre <reinette.chatre@intel.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License version | |
9 | * 2 as published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
19 | * 02110-1301, USA. | |
20 | * | |
21 | * | |
22 | * FIXME: docs | |
23 | */ | |
24 | ||
25 | #include <linux/wlp.h> | |
26 | #define D_LOCAL 6 | |
27 | #include <linux/uwb/debug.h> | |
28 | #include "wlp-internal.h" | |
29 | ||
30 | ||
31 | static | |
32 | void wlp_neighbor_init(struct wlp_neighbor_e *neighbor) | |
33 | { | |
34 | INIT_LIST_HEAD(&neighbor->wssid); | |
35 | } | |
36 | ||
37 | /** | |
38 | * Create area for device information storage | |
39 | * | |
40 | * wlp->mutex must be held | |
41 | */ | |
42 | int __wlp_alloc_device_info(struct wlp *wlp) | |
43 | { | |
44 | struct device *dev = &wlp->rc->uwb_dev.dev; | |
45 | BUG_ON(wlp->dev_info != NULL); | |
46 | wlp->dev_info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL); | |
47 | if (wlp->dev_info == NULL) { | |
48 | dev_err(dev, "WLP: Unable to allocate memory for " | |
49 | "device information.\n"); | |
50 | return -ENOMEM; | |
51 | } | |
52 | return 0; | |
53 | } | |
54 | ||
55 | ||
56 | /** | |
57 | * Fill in device information using function provided by driver | |
58 | * | |
59 | * wlp->mutex must be held | |
60 | */ | |
61 | static | |
62 | void __wlp_fill_device_info(struct wlp *wlp) | |
63 | { | |
64 | struct device *dev = &wlp->rc->uwb_dev.dev; | |
65 | ||
66 | BUG_ON(wlp->fill_device_info == NULL); | |
67 | d_printf(6, dev, "Retrieving device information " | |
68 | "from device driver.\n"); | |
69 | wlp->fill_device_info(wlp, wlp->dev_info); | |
70 | } | |
71 | ||
72 | /** | |
73 | * Setup device information | |
74 | * | |
75 | * Allocate area for device information and populate it. | |
76 | * | |
77 | * wlp->mutex must be held | |
78 | */ | |
79 | int __wlp_setup_device_info(struct wlp *wlp) | |
80 | { | |
81 | int result; | |
82 | struct device *dev = &wlp->rc->uwb_dev.dev; | |
83 | ||
84 | result = __wlp_alloc_device_info(wlp); | |
85 | if (result < 0) { | |
86 | dev_err(dev, "WLP: Unable to allocate area for " | |
87 | "device information.\n"); | |
88 | return result; | |
89 | } | |
90 | __wlp_fill_device_info(wlp); | |
91 | return 0; | |
92 | } | |
93 | ||
94 | /** | |
95 | * Remove information about neighbor stored temporarily | |
96 | * | |
97 | * Information learned during discovey should only be stored when the | |
98 | * device enrolls in the neighbor's WSS. We do need to store this | |
99 | * information temporarily in order to present it to the user. | |
100 | * | |
101 | * We are only interested in keeping neighbor WSS information if that | |
102 | * neighbor is accepting enrollment. | |
103 | * | |
104 | * should be called with wlp->nbmutex held | |
105 | */ | |
106 | void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *neighbor) | |
107 | { | |
108 | struct wlp_wssid_e *wssid_e, *next; | |
109 | u8 keep; | |
110 | if (!list_empty(&neighbor->wssid)) { | |
111 | list_for_each_entry_safe(wssid_e, next, &neighbor->wssid, | |
112 | node) { | |
113 | if (wssid_e->info != NULL) { | |
114 | keep = wssid_e->info->accept_enroll; | |
115 | kfree(wssid_e->info); | |
116 | wssid_e->info = NULL; | |
117 | if (!keep) { | |
118 | list_del(&wssid_e->node); | |
119 | kfree(wssid_e); | |
120 | } | |
121 | } | |
122 | } | |
123 | } | |
124 | if (neighbor->info != NULL) { | |
125 | kfree(neighbor->info); | |
126 | neighbor->info = NULL; | |
127 | } | |
128 | } | |
129 | ||
130 | /** | |
131 | * Populate WLP neighborhood cache with neighbor information | |
132 | * | |
133 | * A new neighbor is found. If it is discoverable then we add it to the | |
134 | * neighborhood cache. | |
135 | * | |
136 | */ | |
137 | static | |
138 | int wlp_add_neighbor(struct wlp *wlp, struct uwb_dev *dev) | |
139 | { | |
140 | int result = 0; | |
141 | int discoverable; | |
142 | struct wlp_neighbor_e *neighbor; | |
143 | ||
144 | d_fnstart(6, &dev->dev, "uwb %p \n", dev); | |
145 | d_printf(6, &dev->dev, "Found neighbor device %02x:%02x \n", | |
146 | dev->dev_addr.data[1], dev->dev_addr.data[0]); | |
147 | /** | |
148 | * FIXME: | |
149 | * Use contents of WLP IE found in beacon cache to determine if | |
150 | * neighbor is discoverable. | |
151 | * The device does not support WLP IE yet so this still needs to be | |
152 | * done. Until then we assume all devices are discoverable. | |
153 | */ | |
154 | discoverable = 1; /* will be changed when FIXME disappears */ | |
155 | if (discoverable) { | |
156 | /* Add neighbor to cache for discovery */ | |
157 | neighbor = kzalloc(sizeof(*neighbor), GFP_KERNEL); | |
158 | if (neighbor == NULL) { | |
159 | dev_err(&dev->dev, "Unable to create memory for " | |
160 | "new neighbor. \n"); | |
161 | result = -ENOMEM; | |
162 | goto error_no_mem; | |
163 | } | |
164 | wlp_neighbor_init(neighbor); | |
165 | uwb_dev_get(dev); | |
166 | neighbor->uwb_dev = dev; | |
167 | list_add(&neighbor->node, &wlp->neighbors); | |
168 | } | |
169 | error_no_mem: | |
170 | d_fnend(6, &dev->dev, "uwb %p, result = %d \n", dev, result); | |
171 | return result; | |
172 | } | |
173 | ||
174 | /** | |
175 | * Remove one neighbor from cache | |
176 | */ | |
177 | static | |
178 | void __wlp_neighbor_release(struct wlp_neighbor_e *neighbor) | |
179 | { | |
180 | struct wlp_wssid_e *wssid_e, *next_wssid_e; | |
181 | ||
182 | list_for_each_entry_safe(wssid_e, next_wssid_e, | |
183 | &neighbor->wssid, node) { | |
184 | list_del(&wssid_e->node); | |
185 | kfree(wssid_e); | |
186 | } | |
187 | uwb_dev_put(neighbor->uwb_dev); | |
188 | list_del(&neighbor->node); | |
189 | kfree(neighbor); | |
190 | } | |
191 | ||
192 | /** | |
193 | * Clear entire neighborhood cache. | |
194 | */ | |
195 | static | |
196 | void __wlp_neighbors_release(struct wlp *wlp) | |
197 | { | |
198 | struct wlp_neighbor_e *neighbor, *next; | |
199 | if (list_empty(&wlp->neighbors)) | |
200 | return; | |
201 | list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) { | |
202 | __wlp_neighbor_release(neighbor); | |
203 | } | |
204 | } | |
205 | ||
206 | static | |
207 | void wlp_neighbors_release(struct wlp *wlp) | |
208 | { | |
209 | mutex_lock(&wlp->nbmutex); | |
210 | __wlp_neighbors_release(wlp); | |
211 | mutex_unlock(&wlp->nbmutex); | |
212 | } | |
213 | ||
214 | ||
215 | ||
216 | /** | |
217 | * Send D1 message to neighbor, receive D2 message | |
218 | * | |
219 | * @neighbor: neighbor to which D1 message will be sent | |
220 | * @wss: if not NULL, it is an enrollment request for this WSS | |
221 | * @wssid: if wss not NULL, this is the wssid of the WSS in which we | |
222 | * want to enroll | |
223 | * | |
224 | * A D1/D2 exchange is done for one of two reasons: discovery or | |
225 | * enrollment. If done for discovery the D1 message is sent to the neighbor | |
226 | * and the contents of the D2 response is stored in a temporary cache. | |
227 | * If done for enrollment the @wss and @wssid are provided also. In this | |
228 | * case the D1 message is sent to the neighbor, the D2 response is parsed | |
229 | * for enrollment of the WSS with wssid. | |
230 | * | |
231 | * &wss->mutex is held | |
232 | */ | |
233 | static | |
234 | int wlp_d1d2_exchange(struct wlp *wlp, struct wlp_neighbor_e *neighbor, | |
235 | struct wlp_wss *wss, struct wlp_uuid *wssid) | |
236 | { | |
237 | int result; | |
238 | struct device *dev = &wlp->rc->uwb_dev.dev; | |
239 | DECLARE_COMPLETION_ONSTACK(completion); | |
240 | struct wlp_session session; | |
241 | struct sk_buff *skb; | |
242 | struct wlp_frame_assoc *resp; | |
243 | struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr; | |
244 | ||
245 | mutex_lock(&wlp->mutex); | |
246 | if (!wlp_uuid_is_set(&wlp->uuid)) { | |
247 | dev_err(dev, "WLP: UUID is not set. Set via sysfs to " | |
248 | "proceed.\n"); | |
249 | result = -ENXIO; | |
250 | goto out; | |
251 | } | |
252 | /* Send D1 association frame */ | |
253 | result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_D1); | |
254 | if (result < 0) { | |
255 | dev_err(dev, "Unable to send D1 frame to neighbor " | |
256 | "%02x:%02x (%d)\n", dev_addr->data[1], | |
257 | dev_addr->data[0], result); | |
258 | d_printf(6, dev, "Add placeholders into buffer next to " | |
259 | "neighbor information we have (dev address).\n"); | |
260 | goto out; | |
261 | } | |
262 | /* Create session, wait for response */ | |
263 | session.exp_message = WLP_ASSOC_D2; | |
264 | session.cb = wlp_session_cb; | |
265 | session.cb_priv = &completion; | |
266 | session.neighbor_addr = *dev_addr; | |
267 | BUG_ON(wlp->session != NULL); | |
268 | wlp->session = &session; | |
269 | /* Wait for D2/F0 frame */ | |
270 | result = wait_for_completion_interruptible_timeout(&completion, | |
271 | WLP_PER_MSG_TIMEOUT * HZ); | |
272 | if (result == 0) { | |
273 | result = -ETIMEDOUT; | |
274 | dev_err(dev, "Timeout while sending D1 to neighbor " | |
275 | "%02x:%02x.\n", dev_addr->data[1], | |
276 | dev_addr->data[0]); | |
277 | goto error_session; | |
278 | } | |
279 | if (result < 0) { | |
280 | dev_err(dev, "Unable to discover/enroll neighbor %02x:%02x.\n", | |
281 | dev_addr->data[1], dev_addr->data[0]); | |
282 | goto error_session; | |
283 | } | |
284 | /* Parse message in session->data: it will be either D2 or F0 */ | |
285 | skb = session.data; | |
286 | resp = (void *) skb->data; | |
287 | d_printf(6, dev, "Received response to D1 frame. \n"); | |
288 | d_dump(6, dev, skb->data, skb->len > 72 ? 72 : skb->len); | |
289 | ||
290 | if (resp->type == WLP_ASSOC_F0) { | |
291 | result = wlp_parse_f0(wlp, skb); | |
292 | if (result < 0) | |
293 | dev_err(dev, "WLP: Unable to parse F0 from neighbor " | |
294 | "%02x:%02x.\n", dev_addr->data[1], | |
295 | dev_addr->data[0]); | |
296 | result = -EINVAL; | |
297 | goto error_resp_parse; | |
298 | } | |
299 | if (wss == NULL) { | |
300 | /* Discovery */ | |
301 | result = wlp_parse_d2_frame_to_cache(wlp, skb, neighbor); | |
302 | if (result < 0) { | |
303 | dev_err(dev, "WLP: Unable to parse D2 message from " | |
304 | "neighbor %02x:%02x for discovery.\n", | |
305 | dev_addr->data[1], dev_addr->data[0]); | |
306 | goto error_resp_parse; | |
307 | } | |
308 | } else { | |
309 | /* Enrollment */ | |
310 | result = wlp_parse_d2_frame_to_enroll(wss, skb, neighbor, | |
311 | wssid); | |
312 | if (result < 0) { | |
313 | dev_err(dev, "WLP: Unable to parse D2 message from " | |
314 | "neighbor %02x:%02x for enrollment.\n", | |
315 | dev_addr->data[1], dev_addr->data[0]); | |
316 | goto error_resp_parse; | |
317 | } | |
318 | } | |
319 | error_resp_parse: | |
320 | kfree_skb(skb); | |
321 | error_session: | |
322 | wlp->session = NULL; | |
323 | out: | |
324 | mutex_unlock(&wlp->mutex); | |
325 | return result; | |
326 | } | |
327 | ||
328 | /** | |
329 | * Enroll into WSS of provided WSSID by using neighbor as registrar | |
330 | * | |
331 | * &wss->mutex is held | |
332 | */ | |
333 | int wlp_enroll_neighbor(struct wlp *wlp, struct wlp_neighbor_e *neighbor, | |
334 | struct wlp_wss *wss, struct wlp_uuid *wssid) | |
335 | { | |
336 | int result = 0; | |
337 | struct device *dev = &wlp->rc->uwb_dev.dev; | |
338 | char buf[WLP_WSS_UUID_STRSIZE]; | |
339 | struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr; | |
340 | wlp_wss_uuid_print(buf, sizeof(buf), wssid); | |
341 | d_fnstart(6, dev, "wlp %p, neighbor %p, wss %p, wssid %p (%s)\n", | |
342 | wlp, neighbor, wss, wssid, buf); | |
343 | d_printf(6, dev, "Complete me.\n"); | |
344 | result = wlp_d1d2_exchange(wlp, neighbor, wss, wssid); | |
345 | if (result < 0) { | |
346 | dev_err(dev, "WLP: D1/D2 message exchange for enrollment " | |
347 | "failed. result = %d \n", result); | |
348 | goto out; | |
349 | } | |
350 | if (wss->state != WLP_WSS_STATE_PART_ENROLLED) { | |
351 | dev_err(dev, "WLP: Unable to enroll into WSS %s using " | |
352 | "neighbor %02x:%02x. \n", buf, | |
353 | dev_addr->data[1], dev_addr->data[0]); | |
354 | result = -EINVAL; | |
355 | goto out; | |
356 | } | |
357 | if (wss->secure_status == WLP_WSS_SECURE) { | |
358 | dev_err(dev, "FIXME: need to complete secure enrollment.\n"); | |
359 | result = -EINVAL; | |
360 | goto error; | |
361 | } else { | |
362 | wss->state = WLP_WSS_STATE_ENROLLED; | |
363 | d_printf(2, dev, "WLP: Success Enrollment into unsecure WSS " | |
364 | "%s using neighbor %02x:%02x. \n", buf, | |
365 | dev_addr->data[1], dev_addr->data[0]); | |
366 | } | |
367 | ||
368 | d_fnend(6, dev, "wlp %p, neighbor %p, wss %p, wssid %p (%s)\n", | |
369 | wlp, neighbor, wss, wssid, buf); | |
370 | out: | |
371 | return result; | |
372 | error: | |
373 | wlp_wss_reset(wss); | |
374 | return result; | |
375 | } | |
376 | ||
377 | /** | |
378 | * Discover WSS information of neighbor's active WSS | |
379 | */ | |
380 | static | |
381 | int wlp_discover_neighbor(struct wlp *wlp, | |
382 | struct wlp_neighbor_e *neighbor) | |
383 | { | |
384 | return wlp_d1d2_exchange(wlp, neighbor, NULL, NULL); | |
385 | } | |
386 | ||
387 | ||
388 | /** | |
389 | * Each neighbor in the neighborhood cache is discoverable. Discover it. | |
390 | * | |
391 | * Discovery is done through sending of D1 association frame and parsing | |
392 | * the D2 association frame response. Only wssid from D2 will be included | |
393 | * in neighbor cache, rest is just displayed to user and forgotten. | |
394 | * | |
395 | * The discovery is not done in parallel. This is simple and enables us to | |
396 | * maintain only one association context. | |
397 | * | |
398 | * The discovery of one neighbor does not affect the other, but if the | |
399 | * discovery of a neighbor fails it is removed from the neighborhood cache. | |
400 | */ | |
401 | static | |
402 | int wlp_discover_all_neighbors(struct wlp *wlp) | |
403 | { | |
404 | int result = 0; | |
405 | struct device *dev = &wlp->rc->uwb_dev.dev; | |
406 | struct wlp_neighbor_e *neighbor, *next; | |
407 | ||
408 | list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) { | |
409 | result = wlp_discover_neighbor(wlp, neighbor); | |
410 | if (result < 0) { | |
411 | dev_err(dev, "WLP: Unable to discover neighbor " | |
412 | "%02x:%02x, removing from neighborhood. \n", | |
413 | neighbor->uwb_dev->dev_addr.data[1], | |
414 | neighbor->uwb_dev->dev_addr.data[0]); | |
415 | __wlp_neighbor_release(neighbor); | |
416 | } | |
417 | } | |
418 | return result; | |
419 | } | |
420 | ||
421 | static int wlp_add_neighbor_helper(struct device *dev, void *priv) | |
422 | { | |
423 | struct wlp *wlp = priv; | |
424 | struct uwb_dev *uwb_dev = to_uwb_dev(dev); | |
425 | ||
426 | return wlp_add_neighbor(wlp, uwb_dev); | |
427 | } | |
428 | ||
429 | /** | |
430 | * Discover WLP neighborhood | |
431 | * | |
432 | * Will send D1 association frame to all devices in beacon group that have | |
433 | * discoverable bit set in WLP IE. D2 frames will be received, information | |
434 | * displayed to user in @buf. Partial information (from D2 association | |
435 | * frame) will be cached to assist with future association | |
436 | * requests. | |
437 | * | |
438 | * The discovery of the WLP neighborhood is triggered by the user. This | |
439 | * should occur infrequently and we thus free current cache and re-allocate | |
440 | * memory if needed. | |
441 | * | |
442 | * If one neighbor fails during initial discovery (determining if it is a | |
443 | * neighbor or not), we fail all - note that interaction with neighbor has | |
444 | * not occured at this point so if a failure occurs we know something went wrong | |
445 | * locally. We thus undo everything. | |
446 | */ | |
447 | ssize_t wlp_discover(struct wlp *wlp) | |
448 | { | |
449 | int result = 0; | |
450 | struct device *dev = &wlp->rc->uwb_dev.dev; | |
451 | ||
452 | d_fnstart(6, dev, "wlp %p \n", wlp); | |
453 | mutex_lock(&wlp->nbmutex); | |
454 | /* Clear current neighborhood cache. */ | |
455 | __wlp_neighbors_release(wlp); | |
456 | /* Determine which devices in neighborhood. Repopulate cache. */ | |
457 | result = uwb_dev_for_each(wlp->rc, wlp_add_neighbor_helper, wlp); | |
458 | if (result < 0) { | |
459 | /* May have partial neighbor information, release all. */ | |
460 | __wlp_neighbors_release(wlp); | |
461 | goto error_dev_for_each; | |
462 | } | |
463 | /* Discover the properties of devices in neighborhood. */ | |
464 | result = wlp_discover_all_neighbors(wlp); | |
465 | /* In case of failure we still print our partial results. */ | |
466 | if (result < 0) { | |
467 | dev_err(dev, "Unable to fully discover neighborhood. \n"); | |
468 | result = 0; | |
469 | } | |
470 | error_dev_for_each: | |
471 | mutex_unlock(&wlp->nbmutex); | |
472 | d_fnend(6, dev, "wlp %p \n", wlp); | |
473 | return result; | |
474 | } | |
475 | ||
476 | /** | |
477 | * Handle events from UWB stack | |
478 | * | |
479 | * We handle events conservatively. If a neighbor goes off the air we | |
480 | * remove it from the neighborhood. If an association process is in | |
481 | * progress this function will block waiting for the nbmutex to become | |
482 | * free. The association process will thus be allowed to complete before it | |
483 | * is removed. | |
484 | */ | |
485 | static | |
486 | void wlp_uwb_notifs_cb(void *_wlp, struct uwb_dev *uwb_dev, | |
487 | enum uwb_notifs event) | |
488 | { | |
489 | struct wlp *wlp = _wlp; | |
490 | struct device *dev = &wlp->rc->uwb_dev.dev; | |
491 | struct wlp_neighbor_e *neighbor, *next; | |
492 | int result; | |
493 | switch (event) { | |
494 | case UWB_NOTIF_ONAIR: | |
495 | d_printf(6, dev, "UWB device %02x:%02x is onair\n", | |
496 | uwb_dev->dev_addr.data[1], | |
497 | uwb_dev->dev_addr.data[0]); | |
498 | result = wlp_eda_create_node(&wlp->eda, | |
499 | uwb_dev->mac_addr.data, | |
500 | &uwb_dev->dev_addr); | |
501 | if (result < 0) | |
502 | dev_err(dev, "WLP: Unable to add new neighbor " | |
503 | "%02x:%02x to EDA cache.\n", | |
504 | uwb_dev->dev_addr.data[1], | |
505 | uwb_dev->dev_addr.data[0]); | |
506 | break; | |
507 | case UWB_NOTIF_OFFAIR: | |
508 | d_printf(6, dev, "UWB device %02x:%02x is offair\n", | |
509 | uwb_dev->dev_addr.data[1], | |
510 | uwb_dev->dev_addr.data[0]); | |
511 | wlp_eda_rm_node(&wlp->eda, &uwb_dev->dev_addr); | |
512 | mutex_lock(&wlp->nbmutex); | |
513 | list_for_each_entry_safe(neighbor, next, &wlp->neighbors, | |
514 | node) { | |
515 | if (neighbor->uwb_dev == uwb_dev) { | |
516 | d_printf(6, dev, "Removing device from " | |
517 | "neighborhood.\n"); | |
518 | __wlp_neighbor_release(neighbor); | |
519 | } | |
520 | } | |
521 | mutex_unlock(&wlp->nbmutex); | |
522 | break; | |
523 | default: | |
524 | dev_err(dev, "don't know how to handle event %d from uwb\n", | |
525 | event); | |
526 | } | |
527 | } | |
528 | ||
529 | int wlp_setup(struct wlp *wlp, struct uwb_rc *rc) | |
530 | { | |
531 | struct device *dev = &rc->uwb_dev.dev; | |
532 | int result; | |
533 | ||
534 | d_fnstart(6, dev, "wlp %p\n", wlp); | |
535 | BUG_ON(wlp->fill_device_info == NULL); | |
536 | BUG_ON(wlp->xmit_frame == NULL); | |
537 | BUG_ON(wlp->stop_queue == NULL); | |
538 | BUG_ON(wlp->start_queue == NULL); | |
539 | wlp->rc = rc; | |
540 | wlp_eda_init(&wlp->eda);/* Set up address cache */ | |
541 | wlp->uwb_notifs_handler.cb = wlp_uwb_notifs_cb; | |
542 | wlp->uwb_notifs_handler.data = wlp; | |
543 | uwb_notifs_register(rc, &wlp->uwb_notifs_handler); | |
544 | ||
545 | uwb_pal_init(&wlp->pal); | |
6fae35f9 DV |
546 | wlp->pal.rc = rc; |
547 | result = uwb_pal_register(&wlp->pal); | |
f5144854 RC |
548 | if (result < 0) |
549 | uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); | |
550 | ||
551 | d_fnend(6, dev, "wlp %p, result = %d\n", wlp, result); | |
552 | return result; | |
553 | } | |
554 | EXPORT_SYMBOL_GPL(wlp_setup); | |
555 | ||
556 | void wlp_remove(struct wlp *wlp) | |
557 | { | |
558 | struct device *dev = &wlp->rc->uwb_dev.dev; | |
559 | d_fnstart(6, dev, "wlp %p\n", wlp); | |
560 | wlp_neighbors_release(wlp); | |
6fae35f9 | 561 | uwb_pal_unregister(&wlp->pal); |
f5144854 RC |
562 | uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); |
563 | wlp_eda_release(&wlp->eda); | |
564 | mutex_lock(&wlp->mutex); | |
565 | if (wlp->dev_info != NULL) | |
566 | kfree(wlp->dev_info); | |
567 | mutex_unlock(&wlp->mutex); | |
568 | wlp->rc = NULL; | |
569 | /* We have to use NULL here because this function can be called | |
570 | * when the device disappeared. */ | |
571 | d_fnend(6, NULL, "wlp %p\n", wlp); | |
572 | } | |
573 | EXPORT_SYMBOL_GPL(wlp_remove); | |
574 | ||
575 | /** | |
576 | * wlp_reset_all - reset the WLP hardware | |
577 | * @wlp: the WLP device to reset. | |
578 | * | |
579 | * This schedules a full hardware reset of the WLP device. The radio | |
580 | * controller and any other PALs will also be reset. | |
581 | */ | |
582 | void wlp_reset_all(struct wlp *wlp) | |
583 | { | |
584 | uwb_rc_reset_all(wlp->rc); | |
585 | } | |
586 | EXPORT_SYMBOL_GPL(wlp_reset_all); |