diff options
Diffstat (limited to 'drivers/uwb/wlp/wlp-lc.c')
-rw-r--r-- | drivers/uwb/wlp/wlp-lc.c | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/drivers/uwb/wlp/wlp-lc.c b/drivers/uwb/wlp/wlp-lc.c new file mode 100644 index 000000000000..0799402e73fb --- /dev/null +++ b/drivers/uwb/wlp/wlp-lc.c | |||
@@ -0,0 +1,585 @@ | |||
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); | ||
546 | result = uwb_pal_register(rc, &wlp->pal); | ||
547 | if (result < 0) | ||
548 | uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); | ||
549 | |||
550 | d_fnend(6, dev, "wlp %p, result = %d\n", wlp, result); | ||
551 | return result; | ||
552 | } | ||
553 | EXPORT_SYMBOL_GPL(wlp_setup); | ||
554 | |||
555 | void wlp_remove(struct wlp *wlp) | ||
556 | { | ||
557 | struct device *dev = &wlp->rc->uwb_dev.dev; | ||
558 | d_fnstart(6, dev, "wlp %p\n", wlp); | ||
559 | wlp_neighbors_release(wlp); | ||
560 | uwb_pal_unregister(wlp->rc, &wlp->pal); | ||
561 | uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler); | ||
562 | wlp_eda_release(&wlp->eda); | ||
563 | mutex_lock(&wlp->mutex); | ||
564 | if (wlp->dev_info != NULL) | ||
565 | kfree(wlp->dev_info); | ||
566 | mutex_unlock(&wlp->mutex); | ||
567 | wlp->rc = NULL; | ||
568 | /* We have to use NULL here because this function can be called | ||
569 | * when the device disappeared. */ | ||
570 | d_fnend(6, NULL, "wlp %p\n", wlp); | ||
571 | } | ||
572 | EXPORT_SYMBOL_GPL(wlp_remove); | ||
573 | |||
574 | /** | ||
575 | * wlp_reset_all - reset the WLP hardware | ||
576 | * @wlp: the WLP device to reset. | ||
577 | * | ||
578 | * This schedules a full hardware reset of the WLP device. The radio | ||
579 | * controller and any other PALs will also be reset. | ||
580 | */ | ||
581 | void wlp_reset_all(struct wlp *wlp) | ||
582 | { | ||
583 | uwb_rc_reset_all(wlp->rc); | ||
584 | } | ||
585 | EXPORT_SYMBOL_GPL(wlp_reset_all); | ||