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