aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/uwb/wlp/wlp-lc.c
diff options
context:
space:
mode:
authorReinette Chatre <reinette.chatre@intel.com>2008-09-17 11:34:16 -0400
committerDavid Vrabel <dv02@dv02pc01.europe.root.pri>2008-09-17 11:54:27 -0400
commitf51448543f8e4871f0539435fce42a14044f5652 (patch)
tree3e6f211771e2ac746029ee9bc9b21634f9457446 /drivers/uwb/wlp/wlp-lc.c
parentde520b8bd5525d33e6a6f36b297836125736bd2a (diff)
uwb: add the WiMedia LLC Protocol stack
Add the generic code for the WiMedia Logical Link Control Protocol (WLP). This has been split into several patches for easier review. core (this patch): - everything else messages: - WLP message construction/decode wss: - Wireless Service Set support build-system: - Kconfig and Kbuild files Signed-off-by: David Vrabel <david.vrabel@csr.com>
Diffstat (limited to 'drivers/uwb/wlp/wlp-lc.c')
-rw-r--r--drivers/uwb/wlp/wlp-lc.c585
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
31static
32void 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 */
42int __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 */
61static
62void __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 */
79int __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 */
106void 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 */
137static
138int 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 }
169error_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 */
177static
178void __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 */
195static
196void __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
206static
207void 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 */
233static
234int 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 }
319error_resp_parse:
320 kfree_skb(skb);
321error_session:
322 wlp->session = NULL;
323out:
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 */
333int 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);
370out:
371 return result;
372error:
373 wlp_wss_reset(wss);
374 return result;
375}
376
377/**
378 * Discover WSS information of neighbor's active WSS
379 */
380static
381int 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 */
401static
402int 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
421static 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 */
447ssize_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 }
470error_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 */
485static
486void 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
529int 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}
553EXPORT_SYMBOL_GPL(wlp_setup);
554
555void 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}
572EXPORT_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 */
581void wlp_reset_all(struct wlp *wlp)
582{
583 uwb_rc_reset_all(wlp->rc);
584}
585EXPORT_SYMBOL_GPL(wlp_reset_all);