diff options
author | David Vrabel <david.vrabel@csr.com> | 2008-09-17 11:34:09 -0400 |
---|---|---|
committer | David Vrabel <dv02@dv02pc01.europe.root.pri> | 2008-09-17 11:54:24 -0400 |
commit | 8cc13a09474bb30d15dbf449767bb6d0198a8bf8 (patch) | |
tree | 2423388e0a7e9bca0c6e6f7edd5f492887d38160 /drivers/uwb/drp.c | |
parent | 22d203ecef9b0cc1fa8d8f64c935b451ca7d1022 (diff) |
uwb: add the UWB stack (reservation manager)
DRP and reservation management.
Signed-off-by: David Vrabel <david.vrabel@csr.com>
Diffstat (limited to 'drivers/uwb/drp.c')
-rw-r--r-- | drivers/uwb/drp.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/drivers/uwb/drp.c b/drivers/uwb/drp.c new file mode 100644 index 000000000000..c0b1e5e2bd08 --- /dev/null +++ b/drivers/uwb/drp.c | |||
@@ -0,0 +1,461 @@ | |||
1 | /* | ||
2 | * Ultra Wide Band | ||
3 | * Dynamic Reservation Protocol handling | ||
4 | * | ||
5 | * Copyright (C) 2005-2006 Intel Corporation | ||
6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
7 | * Copyright (C) 2008 Cambridge Silicon Radio Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License version | ||
11 | * 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
20 | */ | ||
21 | #include <linux/kthread.h> | ||
22 | #include <linux/freezer.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include "uwb-internal.h" | ||
25 | |||
26 | /** | ||
27 | * Construct and send the SET DRP IE | ||
28 | * | ||
29 | * @rc: UWB Host controller | ||
30 | * @returns: >= 0 number of bytes still available in the beacon | ||
31 | * < 0 errno code on error. | ||
32 | * | ||
33 | * See WUSB[8.6.2.7]: The host must set all the DRP IEs that it wants the | ||
34 | * device to include in its beacon at the same time. We thus have to | ||
35 | * traverse all reservations and include the DRP IEs of all PENDING | ||
36 | * and NEGOTIATED reservations in a SET DRP command for transmission. | ||
37 | * | ||
38 | * A DRP Availability IE is appended. | ||
39 | * | ||
40 | * rc->uwb_dev.mutex is held | ||
41 | * | ||
42 | * FIXME We currently ignore the returned value indicating the remaining space | ||
43 | * in beacon. This could be used to deny reservation requests earlier if | ||
44 | * determined that they would cause the beacon space to be exceeded. | ||
45 | */ | ||
46 | static | ||
47 | int uwb_rc_gen_send_drp_ie(struct uwb_rc *rc) | ||
48 | { | ||
49 | int result; | ||
50 | struct device *dev = &rc->uwb_dev.dev; | ||
51 | struct uwb_rc_cmd_set_drp_ie *cmd; | ||
52 | struct uwb_rc_evt_set_drp_ie reply; | ||
53 | struct uwb_rsv *rsv; | ||
54 | int num_bytes = 0; | ||
55 | u8 *IEDataptr; | ||
56 | |||
57 | result = -ENOMEM; | ||
58 | /* First traverse all reservations to determine memory needed. */ | ||
59 | list_for_each_entry(rsv, &rc->reservations, rc_node) { | ||
60 | if (rsv->drp_ie != NULL) | ||
61 | num_bytes += rsv->drp_ie->hdr.length + 2; | ||
62 | } | ||
63 | num_bytes += sizeof(rc->drp_avail.ie); | ||
64 | cmd = kzalloc(sizeof(*cmd) + num_bytes, GFP_KERNEL); | ||
65 | if (cmd == NULL) | ||
66 | goto error; | ||
67 | cmd->rccb.bCommandType = UWB_RC_CET_GENERAL; | ||
68 | cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_SET_DRP_IE); | ||
69 | cmd->wIELength = num_bytes; | ||
70 | IEDataptr = (u8 *)&cmd->IEData[0]; | ||
71 | |||
72 | /* Next traverse all reservations to place IEs in allocated memory. */ | ||
73 | list_for_each_entry(rsv, &rc->reservations, rc_node) { | ||
74 | if (rsv->drp_ie != NULL) { | ||
75 | memcpy(IEDataptr, rsv->drp_ie, | ||
76 | rsv->drp_ie->hdr.length + 2); | ||
77 | IEDataptr += rsv->drp_ie->hdr.length + 2; | ||
78 | } | ||
79 | } | ||
80 | memcpy(IEDataptr, &rc->drp_avail.ie, sizeof(rc->drp_avail.ie)); | ||
81 | |||
82 | reply.rceb.bEventType = UWB_RC_CET_GENERAL; | ||
83 | reply.rceb.wEvent = UWB_RC_CMD_SET_DRP_IE; | ||
84 | result = uwb_rc_cmd(rc, "SET-DRP-IE", &cmd->rccb, | ||
85 | sizeof(*cmd) + num_bytes, &reply.rceb, | ||
86 | sizeof(reply)); | ||
87 | if (result < 0) | ||
88 | goto error_cmd; | ||
89 | result = le16_to_cpu(reply.wRemainingSpace); | ||
90 | if (reply.bResultCode != UWB_RC_RES_SUCCESS) { | ||
91 | dev_err(&rc->uwb_dev.dev, "SET-DRP-IE: command execution " | ||
92 | "failed: %s (%d). RemainingSpace in beacon " | ||
93 | "= %d\n", uwb_rc_strerror(reply.bResultCode), | ||
94 | reply.bResultCode, result); | ||
95 | result = -EIO; | ||
96 | } else { | ||
97 | dev_dbg(dev, "SET-DRP-IE sent. RemainingSpace in beacon " | ||
98 | "= %d.\n", result); | ||
99 | result = 0; | ||
100 | } | ||
101 | error_cmd: | ||
102 | kfree(cmd); | ||
103 | error: | ||
104 | return result; | ||
105 | |||
106 | } | ||
107 | /** | ||
108 | * Send all DRP IEs associated with this host | ||
109 | * | ||
110 | * @returns: >= 0 number of bytes still available in the beacon | ||
111 | * < 0 errno code on error. | ||
112 | * | ||
113 | * As per the protocol we obtain the host controller device lock to access | ||
114 | * bandwidth structures. | ||
115 | */ | ||
116 | int uwb_rc_send_all_drp_ie(struct uwb_rc *rc) | ||
117 | { | ||
118 | int result; | ||
119 | |||
120 | mutex_lock(&rc->uwb_dev.mutex); | ||
121 | result = uwb_rc_gen_send_drp_ie(rc); | ||
122 | mutex_unlock(&rc->uwb_dev.mutex); | ||
123 | return result; | ||
124 | } | ||
125 | |||
126 | void uwb_drp_handle_timeout(struct uwb_rsv *rsv) | ||
127 | { | ||
128 | struct device *dev = &rsv->rc->uwb_dev.dev; | ||
129 | |||
130 | dev_dbg(dev, "reservation timeout in state %s (%d)\n", | ||
131 | uwb_rsv_state_str(rsv->state), rsv->state); | ||
132 | |||
133 | switch (rsv->state) { | ||
134 | case UWB_RSV_STATE_O_INITIATED: | ||
135 | if (rsv->is_multicast) { | ||
136 | uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); | ||
137 | return; | ||
138 | } | ||
139 | break; | ||
140 | case UWB_RSV_STATE_O_ESTABLISHED: | ||
141 | if (rsv->is_multicast) | ||
142 | return; | ||
143 | break; | ||
144 | default: | ||
145 | break; | ||
146 | } | ||
147 | uwb_rsv_remove(rsv); | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * Based on the DRP IE, transition a target reservation to a new | ||
152 | * state. | ||
153 | */ | ||
154 | static void uwb_drp_process_target(struct uwb_rc *rc, struct uwb_rsv *rsv, | ||
155 | struct uwb_ie_drp *drp_ie) | ||
156 | { | ||
157 | struct device *dev = &rc->uwb_dev.dev; | ||
158 | int status; | ||
159 | enum uwb_drp_reason reason_code; | ||
160 | |||
161 | status = uwb_ie_drp_status(drp_ie); | ||
162 | reason_code = uwb_ie_drp_reason_code(drp_ie); | ||
163 | |||
164 | if (status) { | ||
165 | switch (reason_code) { | ||
166 | case UWB_DRP_REASON_ACCEPTED: | ||
167 | uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED); | ||
168 | break; | ||
169 | case UWB_DRP_REASON_MODIFIED: | ||
170 | dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n", | ||
171 | reason_code, status); | ||
172 | break; | ||
173 | default: | ||
174 | dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", | ||
175 | reason_code, status); | ||
176 | } | ||
177 | } else { | ||
178 | switch (reason_code) { | ||
179 | case UWB_DRP_REASON_ACCEPTED: | ||
180 | /* New reservations are handled in uwb_rsv_find(). */ | ||
181 | break; | ||
182 | case UWB_DRP_REASON_DENIED: | ||
183 | uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); | ||
184 | break; | ||
185 | case UWB_DRP_REASON_CONFLICT: | ||
186 | case UWB_DRP_REASON_MODIFIED: | ||
187 | dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n", | ||
188 | reason_code, status); | ||
189 | break; | ||
190 | default: | ||
191 | dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", | ||
192 | reason_code, status); | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * Based on the DRP IE, transition an owner reservation to a new | ||
199 | * state. | ||
200 | */ | ||
201 | static void uwb_drp_process_owner(struct uwb_rc *rc, struct uwb_rsv *rsv, | ||
202 | struct uwb_ie_drp *drp_ie) | ||
203 | { | ||
204 | struct device *dev = &rc->uwb_dev.dev; | ||
205 | int status; | ||
206 | enum uwb_drp_reason reason_code; | ||
207 | |||
208 | status = uwb_ie_drp_status(drp_ie); | ||
209 | reason_code = uwb_ie_drp_reason_code(drp_ie); | ||
210 | |||
211 | if (status) { | ||
212 | switch (reason_code) { | ||
213 | case UWB_DRP_REASON_ACCEPTED: | ||
214 | uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); | ||
215 | break; | ||
216 | case UWB_DRP_REASON_MODIFIED: | ||
217 | dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n", | ||
218 | reason_code, status); | ||
219 | break; | ||
220 | default: | ||
221 | dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", | ||
222 | reason_code, status); | ||
223 | } | ||
224 | } else { | ||
225 | switch (reason_code) { | ||
226 | case UWB_DRP_REASON_PENDING: | ||
227 | uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_PENDING); | ||
228 | break; | ||
229 | case UWB_DRP_REASON_DENIED: | ||
230 | uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); | ||
231 | break; | ||
232 | case UWB_DRP_REASON_CONFLICT: | ||
233 | case UWB_DRP_REASON_MODIFIED: | ||
234 | dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n", | ||
235 | reason_code, status); | ||
236 | break; | ||
237 | default: | ||
238 | dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", | ||
239 | reason_code, status); | ||
240 | } | ||
241 | } | ||
242 | } | ||
243 | |||
244 | /* | ||
245 | * Process a received DRP IE, it's either for a reservation owned by | ||
246 | * the RC or targeted at it (or it's for a WUSB cluster reservation). | ||
247 | */ | ||
248 | static void uwb_drp_process(struct uwb_rc *rc, struct uwb_dev *src, | ||
249 | struct uwb_ie_drp *drp_ie) | ||
250 | { | ||
251 | struct uwb_rsv *rsv; | ||
252 | |||
253 | rsv = uwb_rsv_find(rc, src, drp_ie); | ||
254 | if (!rsv) { | ||
255 | /* | ||
256 | * No reservation? It's either for a recently | ||
257 | * terminated reservation; or the DRP IE couldn't be | ||
258 | * processed (e.g., an invalid IE or out of memory). | ||
259 | */ | ||
260 | return; | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * Do nothing with DRP IEs for reservations that have been | ||
265 | * terminated. | ||
266 | */ | ||
267 | if (rsv->state == UWB_RSV_STATE_NONE) { | ||
268 | uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); | ||
269 | return; | ||
270 | } | ||
271 | |||
272 | if (uwb_ie_drp_owner(drp_ie)) | ||
273 | uwb_drp_process_target(rc, rsv, drp_ie); | ||
274 | else | ||
275 | uwb_drp_process_owner(rc, rsv, drp_ie); | ||
276 | } | ||
277 | |||
278 | |||
279 | /* | ||
280 | * Process all the DRP IEs (both DRP IEs and the DRP Availability IE) | ||
281 | * from a device. | ||
282 | */ | ||
283 | static | ||
284 | void uwb_drp_process_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt, | ||
285 | size_t ielen, struct uwb_dev *src_dev) | ||
286 | { | ||
287 | struct device *dev = &rc->uwb_dev.dev; | ||
288 | struct uwb_ie_hdr *ie_hdr; | ||
289 | void *ptr; | ||
290 | |||
291 | ptr = drp_evt->ie_data; | ||
292 | for (;;) { | ||
293 | ie_hdr = uwb_ie_next(&ptr, &ielen); | ||
294 | if (!ie_hdr) | ||
295 | break; | ||
296 | |||
297 | switch (ie_hdr->element_id) { | ||
298 | case UWB_IE_DRP_AVAILABILITY: | ||
299 | /* FIXME: does something need to be done with this? */ | ||
300 | break; | ||
301 | case UWB_IE_DRP: | ||
302 | uwb_drp_process(rc, src_dev, (struct uwb_ie_drp *)ie_hdr); | ||
303 | break; | ||
304 | default: | ||
305 | dev_warn(dev, "unexpected IE in DRP notification\n"); | ||
306 | break; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | if (ielen > 0) | ||
311 | dev_warn(dev, "%d octets remaining in DRP notification\n", | ||
312 | (int)ielen); | ||
313 | } | ||
314 | |||
315 | |||
316 | /* | ||
317 | * Go through all the DRP IEs and find the ones that conflict with our | ||
318 | * reservations. | ||
319 | * | ||
320 | * FIXME: must resolve the conflict according the the rules in | ||
321 | * [ECMA-368]. | ||
322 | */ | ||
323 | static | ||
324 | void uwb_drp_process_conflict_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt, | ||
325 | size_t ielen, struct uwb_dev *src_dev) | ||
326 | { | ||
327 | struct device *dev = &rc->uwb_dev.dev; | ||
328 | struct uwb_ie_hdr *ie_hdr; | ||
329 | struct uwb_ie_drp *drp_ie; | ||
330 | void *ptr; | ||
331 | |||
332 | ptr = drp_evt->ie_data; | ||
333 | for (;;) { | ||
334 | ie_hdr = uwb_ie_next(&ptr, &ielen); | ||
335 | if (!ie_hdr) | ||
336 | break; | ||
337 | |||
338 | drp_ie = container_of(ie_hdr, struct uwb_ie_drp, hdr); | ||
339 | |||
340 | /* FIXME: check if this DRP IE conflicts. */ | ||
341 | } | ||
342 | |||
343 | if (ielen > 0) | ||
344 | dev_warn(dev, "%d octets remaining in DRP notification\n", | ||
345 | (int)ielen); | ||
346 | } | ||
347 | |||
348 | |||
349 | /* | ||
350 | * Terminate all reservations owned by, or targeted at, 'uwb_dev'. | ||
351 | */ | ||
352 | static void uwb_drp_terminate_all(struct uwb_rc *rc, struct uwb_dev *uwb_dev) | ||
353 | { | ||
354 | struct uwb_rsv *rsv; | ||
355 | |||
356 | list_for_each_entry(rsv, &rc->reservations, rc_node) { | ||
357 | if (rsv->owner == uwb_dev | ||
358 | || (rsv->target.type == UWB_RSV_TARGET_DEV && rsv->target.dev == uwb_dev)) | ||
359 | uwb_rsv_remove(rsv); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | |||
364 | /** | ||
365 | * uwbd_evt_handle_rc_drp - handle a DRP_IE event | ||
366 | * @evt: the DRP_IE event from the radio controller | ||
367 | * | ||
368 | * This processes DRP notifications from the radio controller, either | ||
369 | * initiating a new reservation or transitioning an existing | ||
370 | * reservation into a different state. | ||
371 | * | ||
372 | * DRP notifications can occur for three different reasons: | ||
373 | * | ||
374 | * - UWB_DRP_NOTIF_DRP_IE_RECVD: one or more DRP IEs with the RC as | ||
375 | * the target or source have been recieved. | ||
376 | * | ||
377 | * These DRP IEs could be new or for an existing reservation. | ||
378 | * | ||
379 | * If the DRP IE for an existing reservation ceases to be to | ||
380 | * recieved for at least mMaxLostBeacons, the reservation should be | ||
381 | * considered to be terminated. Note that the TERMINATE reason (see | ||
382 | * below) may not always be signalled (e.g., the remote device has | ||
383 | * two or more reservations established with the RC). | ||
384 | * | ||
385 | * - UWB_DRP_NOTIF_CONFLICT: DRP IEs from any device in the beacon | ||
386 | * group conflict with the RC's reservations. | ||
387 | * | ||
388 | * - UWB_DRP_NOTIF_TERMINATE: DRP IEs are no longer being received | ||
389 | * from a device (i.e., it's terminated all reservations). | ||
390 | * | ||
391 | * Only the software state of the reservations is changed; the setting | ||
392 | * of the radio controller's DRP IEs is done after all the events in | ||
393 | * an event buffer are processed. This saves waiting multiple times | ||
394 | * for the SET_DRP_IE command to complete. | ||
395 | */ | ||
396 | int uwbd_evt_handle_rc_drp(struct uwb_event *evt) | ||
397 | { | ||
398 | struct device *dev = &evt->rc->uwb_dev.dev; | ||
399 | struct uwb_rc *rc = evt->rc; | ||
400 | struct uwb_rc_evt_drp *drp_evt; | ||
401 | size_t ielength, bytes_left; | ||
402 | struct uwb_dev_addr src_addr; | ||
403 | struct uwb_dev *src_dev; | ||
404 | int reason; | ||
405 | |||
406 | /* Is there enough data to decode the event (and any IEs in | ||
407 | its payload)? */ | ||
408 | if (evt->notif.size < sizeof(*drp_evt)) { | ||
409 | dev_err(dev, "DRP event: Not enough data to decode event " | ||
410 | "[%zu bytes left, %zu needed]\n", | ||
411 | evt->notif.size, sizeof(*drp_evt)); | ||
412 | return 0; | ||
413 | } | ||
414 | bytes_left = evt->notif.size - sizeof(*drp_evt); | ||
415 | drp_evt = container_of(evt->notif.rceb, struct uwb_rc_evt_drp, rceb); | ||
416 | ielength = le16_to_cpu(drp_evt->ie_length); | ||
417 | if (bytes_left != ielength) { | ||
418 | dev_err(dev, "DRP event: Not enough data in payload [%zu" | ||
419 | "bytes left, %zu declared in the event]\n", | ||
420 | bytes_left, ielength); | ||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | memcpy(src_addr.data, &drp_evt->src_addr, sizeof(src_addr)); | ||
425 | src_dev = uwb_dev_get_by_devaddr(rc, &src_addr); | ||
426 | if (!src_dev) { | ||
427 | /* | ||
428 | * A DRP notification from an unrecognized device. | ||
429 | * | ||
430 | * This is probably from a WUSB device that doesn't | ||
431 | * have an EUI-48 and therefore doesn't show up in the | ||
432 | * UWB device database. It's safe to simply ignore | ||
433 | * these. | ||
434 | */ | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | mutex_lock(&rc->rsvs_mutex); | ||
439 | |||
440 | reason = uwb_rc_evt_drp_reason(drp_evt); | ||
441 | |||
442 | switch (reason) { | ||
443 | case UWB_DRP_NOTIF_DRP_IE_RCVD: | ||
444 | uwb_drp_process_all(rc, drp_evt, ielength, src_dev); | ||
445 | break; | ||
446 | case UWB_DRP_NOTIF_CONFLICT: | ||
447 | uwb_drp_process_conflict_all(rc, drp_evt, ielength, src_dev); | ||
448 | break; | ||
449 | case UWB_DRP_NOTIF_TERMINATE: | ||
450 | uwb_drp_terminate_all(rc, src_dev); | ||
451 | break; | ||
452 | default: | ||
453 | dev_warn(dev, "ignored DRP event with reason code: %d\n", reason); | ||
454 | break; | ||
455 | } | ||
456 | |||
457 | mutex_unlock(&rc->rsvs_mutex); | ||
458 | |||
459 | uwb_dev_put(src_dev); | ||
460 | return 0; | ||
461 | } | ||