Commit | Line | Data |
---|---|---|
b06ebda0 MD |
1 | /* |
2 | * ng_l2cap_misc.c | |
3 | */ | |
4 | ||
5 | /*- | |
6 | * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com> | |
7 | * All rights reserved. | |
8 | * | |
9 | * Redistribution and use in source and binary forms, with or without | |
10 | * modification, are permitted provided that the following conditions | |
11 | * are met: | |
12 | * 1. Redistributions of source code must retain the above copyright | |
13 | * notice, this list of conditions and the following disclaimer. | |
14 | * 2. Redistributions in binary form must reproduce the above copyright | |
15 | * notice, this list of conditions and the following disclaimer in the | |
16 | * documentation and/or other materials provided with the distribution. | |
17 | * | |
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
28 | * SUCH DAMAGE. | |
29 | * | |
30 | * $Id: ng_l2cap_misc.c,v 1.5 2003/09/08 19:11:45 max Exp $ | |
31 | * $FreeBSD: src/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.c,v 1.12 2005/08/31 18:13:23 emax Exp $ | |
32 | */ | |
33 | ||
34 | #include <sys/param.h> | |
35 | #include <sys/systm.h> | |
36 | #include <sys/kernel.h> | |
37 | #include <sys/malloc.h> | |
38 | #include <sys/mbuf.h> | |
39 | #include <sys/queue.h> | |
e85b99ab SW |
40 | #include <netgraph7/ng_message.h> |
41 | #include <netgraph7/netgraph.h> | |
42 | #include <netgraph7/bluetooth/include/ng_bluetooth.h> | |
43 | #include <netgraph7/bluetooth/include/ng_hci.h> | |
44 | #include <netgraph7/bluetooth/include/ng_l2cap.h> | |
45 | #include <netgraph7/bluetooth/l2cap/ng_l2cap_var.h> | |
46 | #include <netgraph7/bluetooth/l2cap/ng_l2cap_cmds.h> | |
47 | #include <netgraph7/bluetooth/l2cap/ng_l2cap_evnt.h> | |
48 | #include <netgraph7/bluetooth/l2cap/ng_l2cap_llpi.h> | |
49 | #include <netgraph7/bluetooth/l2cap/ng_l2cap_ulpi.h> | |
50 | #include <netgraph7/bluetooth/l2cap/ng_l2cap_misc.h> | |
b06ebda0 MD |
51 | |
52 | static u_int16_t ng_l2cap_get_cid (ng_l2cap_p); | |
53 | ||
54 | /****************************************************************************** | |
55 | ****************************************************************************** | |
56 | ** Utility routines | |
57 | ****************************************************************************** | |
58 | ******************************************************************************/ | |
59 | ||
60 | /* | |
61 | * Send hook information to the upper layer | |
62 | */ | |
63 | ||
64 | void | |
65 | ng_l2cap_send_hook_info(node_p node, hook_p hook, void *arg1, int arg2) | |
66 | { | |
67 | ng_l2cap_p l2cap = NULL; | |
68 | struct ng_mesg *msg = NULL; | |
69 | int error = 0; | |
70 | ||
71 | if (node == NULL || NG_NODE_NOT_VALID(node) || | |
72 | hook == NULL || NG_HOOK_NOT_VALID(hook)) | |
73 | return; | |
74 | ||
75 | l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node); | |
76 | if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci) || | |
77 | bcmp(&l2cap->bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2cap->bdaddr)) == 0) | |
78 | return; | |
79 | ||
80 | NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_HOOK_INFO, | |
5a975a3d | 81 | sizeof(bdaddr_t), M_WAITOK | M_NULLOK); |
b06ebda0 MD |
82 | if (msg != NULL) { |
83 | bcopy(&l2cap->bdaddr, msg->data, sizeof(bdaddr_t)); | |
84 | NG_SEND_MSG_HOOK(error, node, msg, hook, 0); | |
85 | } else | |
86 | error = ENOMEM; | |
87 | ||
88 | if (error != 0) | |
89 | NG_L2CAP_INFO( | |
90 | "%s: %s - failed to send HOOK_INFO message to hook \"%s\", error=%d\n", | |
91 | __func__, NG_NODE_NAME(l2cap->node), NG_HOOK_NAME(hook), | |
92 | error); | |
93 | } /* ng_l2cap_send_hook_info */ | |
94 | ||
95 | /* | |
96 | * Create new connection descriptor for the "remote" unit. | |
97 | * Will link connection descriptor to the l2cap node. | |
98 | */ | |
99 | ||
100 | ng_l2cap_con_p | |
101 | ng_l2cap_new_con(ng_l2cap_p l2cap, bdaddr_p bdaddr) | |
102 | { | |
103 | static int fake_con_handle = 0x0f00; | |
104 | ng_l2cap_con_p con = NULL; | |
105 | ||
106 | /* Create new connection descriptor */ | |
fc025606 SW |
107 | con = kmalloc(sizeof(*con), M_NETGRAPH_L2CAP, |
108 | M_WAITOK | M_NULLOK | M_ZERO); | |
b06ebda0 MD |
109 | if (con == NULL) |
110 | return (NULL); | |
111 | ||
112 | con->l2cap = l2cap; | |
113 | con->state = NG_L2CAP_CON_CLOSED; | |
114 | ||
115 | /* | |
116 | * XXX | |
117 | * | |
118 | * Assign fake connection handle to the connection descriptor. | |
119 | * Bluetooth specification marks 0x0f00 - 0x0fff connection | |
120 | * handles as reserved. We need this fake connection handles | |
121 | * for timeouts. Connection handle will be passed as argument | |
122 | * to timeout so when timeout happens we can find the right | |
123 | * connection descriptor. We can not pass pointers, because | |
124 | * timeouts are external (to Netgraph) events and there might | |
125 | * be a race when node/hook goes down and timeout event already | |
126 | * went into node's queue | |
127 | */ | |
128 | ||
129 | con->con_handle = fake_con_handle ++; | |
130 | if (fake_con_handle > 0x0fff) | |
131 | fake_con_handle = 0x0f00; | |
132 | ||
133 | bcopy(bdaddr, &con->remote, sizeof(con->remote)); | |
134 | ng_callout_init(&con->con_timo); | |
135 | ||
136 | con->ident = NG_L2CAP_FIRST_IDENT - 1; | |
137 | TAILQ_INIT(&con->cmd_list); | |
138 | ||
139 | /* Link connection */ | |
140 | LIST_INSERT_HEAD(&l2cap->con_list, con, next); | |
141 | ||
142 | return (con); | |
143 | } /* ng_l2cap_new_con */ | |
144 | ||
145 | /* | |
146 | * Add reference to the connection descriptor | |
147 | */ | |
148 | ||
149 | void | |
150 | ng_l2cap_con_ref(ng_l2cap_con_p con) | |
151 | { | |
152 | con->refcnt ++; | |
153 | ||
154 | if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO) { | |
155 | if ((con->state != NG_L2CAP_CON_OPEN) || | |
156 | (con->flags & NG_L2CAP_CON_OUTGOING) == 0) | |
157 | panic( | |
158 | "%s: %s - bad auto disconnect timeout, state=%d, flags=%#x\n", | |
159 | __func__, NG_NODE_NAME(con->l2cap->node), | |
160 | con->state, con->flags); | |
161 | ||
162 | ng_l2cap_discon_untimeout(con); | |
163 | } | |
164 | } /* ng_l2cap_con_ref */ | |
165 | ||
166 | /* | |
167 | * Remove reference from the connection descriptor | |
168 | */ | |
169 | ||
170 | void | |
171 | ng_l2cap_con_unref(ng_l2cap_con_p con) | |
172 | { | |
173 | con->refcnt --; | |
174 | ||
175 | if (con->refcnt < 0) | |
176 | panic( | |
177 | "%s: %s - con->refcnt < 0\n", __func__, NG_NODE_NAME(con->l2cap->node)); | |
178 | ||
179 | /* | |
180 | * Set auto disconnect timer only if the following conditions are met: | |
181 | * 1) we have no reference on the connection | |
182 | * 2) connection is in OPEN state | |
183 | * 3) it is an outgoing connection | |
184 | * 4) disconnect timeout > 0 | |
185 | * 5) connection is not dying | |
186 | */ | |
187 | ||
188 | if ((con->refcnt == 0) && | |
189 | (con->state == NG_L2CAP_CON_OPEN) && | |
190 | (con->flags & NG_L2CAP_CON_OUTGOING) && | |
191 | (con->l2cap->discon_timo > 0) && | |
192 | ((con->flags & NG_L2CAP_CON_DYING) == 0)) | |
193 | ng_l2cap_discon_timeout(con); | |
194 | } /* ng_l2cap_con_unref */ | |
195 | ||
196 | /* | |
197 | * Set auto disconnect timeout | |
198 | * XXX FIXME: check return code from ng_callout | |
199 | */ | |
200 | ||
201 | int | |
202 | ng_l2cap_discon_timeout(ng_l2cap_con_p con) | |
203 | { | |
204 | if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO)) | |
205 | panic( | |
206 | "%s: %s - invalid timeout, state=%d, flags=%#x\n", | |
207 | __func__, NG_NODE_NAME(con->l2cap->node), | |
208 | con->state, con->flags); | |
209 | ||
210 | con->flags |= NG_L2CAP_CON_AUTO_DISCON_TIMO; | |
211 | ng_callout(&con->con_timo, con->l2cap->node, NULL, | |
212 | con->l2cap->discon_timo * hz, | |
213 | ng_l2cap_process_discon_timeout, NULL, | |
214 | con->con_handle); | |
215 | ||
216 | return (0); | |
217 | } /* ng_l2cap_discon_timeout */ | |
218 | ||
219 | /* | |
220 | * Unset auto disconnect timeout | |
221 | */ | |
222 | ||
223 | int | |
224 | ng_l2cap_discon_untimeout(ng_l2cap_con_p con) | |
225 | { | |
226 | if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)) | |
227 | panic( | |
228 | "%s: %s - no disconnect timeout, state=%d, flags=%#x\n", | |
229 | __func__, NG_NODE_NAME(con->l2cap->node), | |
230 | con->state, con->flags); | |
231 | ||
232 | if (ng_uncallout(&con->con_timo, con->l2cap->node) == 0) | |
233 | return (ETIMEDOUT); | |
234 | ||
235 | con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO; | |
236 | ||
237 | return (0); | |
238 | } /* ng_l2cap_discon_untimeout */ | |
239 | ||
240 | /* | |
241 | * Free connection descriptor. Will unlink connection and free everything. | |
242 | */ | |
243 | ||
244 | void | |
245 | ng_l2cap_free_con(ng_l2cap_con_p con) | |
246 | { | |
247 | ng_l2cap_chan_p f = NULL, n = NULL; | |
248 | ||
249 | con->state = NG_L2CAP_CON_CLOSED; | |
250 | ||
251 | while (con->tx_pkt != NULL) { | |
252 | struct mbuf *m = con->tx_pkt->m_nextpkt; | |
253 | ||
254 | m_freem(con->tx_pkt); | |
255 | con->tx_pkt = m; | |
256 | } | |
257 | ||
258 | NG_FREE_M(con->rx_pkt); | |
259 | ||
260 | for (f = LIST_FIRST(&con->l2cap->chan_list); f != NULL; ) { | |
261 | n = LIST_NEXT(f, next); | |
262 | ||
263 | if (f->con == con) | |
264 | ng_l2cap_free_chan(f); | |
265 | ||
266 | f = n; | |
267 | } | |
268 | ||
269 | while (!TAILQ_EMPTY(&con->cmd_list)) { | |
270 | ng_l2cap_cmd_p cmd = TAILQ_FIRST(&con->cmd_list); | |
271 | ||
272 | ng_l2cap_unlink_cmd(cmd); | |
273 | if (cmd->flags & NG_L2CAP_CMD_PENDING) | |
274 | ng_l2cap_command_untimeout(cmd); | |
275 | ng_l2cap_free_cmd(cmd); | |
276 | } | |
277 | ||
278 | if (con->flags & (NG_L2CAP_CON_AUTO_DISCON_TIMO|NG_L2CAP_CON_LP_TIMO)) | |
279 | panic( | |
280 | "%s: %s - timeout pending! state=%d, flags=%#x\n", | |
281 | __func__, NG_NODE_NAME(con->l2cap->node), | |
282 | con->state, con->flags); | |
283 | ||
284 | LIST_REMOVE(con, next); | |
285 | ||
286 | bzero(con, sizeof(*con)); | |
fc025606 | 287 | kfree(con, M_NETGRAPH_L2CAP); |
b06ebda0 MD |
288 | } /* ng_l2cap_free_con */ |
289 | ||
290 | /* | |
291 | * Get connection by "remote" address | |
292 | */ | |
293 | ||
294 | ng_l2cap_con_p | |
295 | ng_l2cap_con_by_addr(ng_l2cap_p l2cap, bdaddr_p bdaddr) | |
296 | { | |
297 | ng_l2cap_con_p con = NULL; | |
298 | ||
299 | LIST_FOREACH(con, &l2cap->con_list, next) | |
300 | if (bcmp(bdaddr, &con->remote, sizeof(con->remote)) == 0) | |
301 | break; | |
302 | ||
303 | return (con); | |
304 | } /* ng_l2cap_con_by_addr */ | |
305 | ||
306 | /* | |
307 | * Get connection by "handle" | |
308 | */ | |
309 | ||
310 | ng_l2cap_con_p | |
311 | ng_l2cap_con_by_handle(ng_l2cap_p l2cap, u_int16_t con_handle) | |
312 | { | |
313 | ng_l2cap_con_p con = NULL; | |
314 | ||
315 | LIST_FOREACH(con, &l2cap->con_list, next) | |
316 | if (con->con_handle == con_handle) | |
317 | break; | |
318 | ||
319 | return (con); | |
320 | } /* ng_l2cap_con_by_handle */ | |
321 | ||
322 | /* | |
323 | * Allocate new L2CAP channel descriptor on "con" conection with "psm". | |
324 | * Will link the channel to the l2cap node | |
325 | */ | |
326 | ||
327 | ng_l2cap_chan_p | |
328 | ng_l2cap_new_chan(ng_l2cap_p l2cap, ng_l2cap_con_p con, u_int16_t psm) | |
329 | { | |
330 | ng_l2cap_chan_p ch = NULL; | |
331 | ||
fc025606 SW |
332 | ch = kmalloc(sizeof(*ch), M_NETGRAPH_L2CAP, |
333 | M_WAITOK | M_NULLOK | M_ZERO); | |
b06ebda0 MD |
334 | if (ch == NULL) |
335 | return (NULL); | |
336 | ||
337 | ch->scid = ng_l2cap_get_cid(l2cap); | |
338 | ||
339 | if (ch->scid != NG_L2CAP_NULL_CID) { | |
340 | /* Initialize channel */ | |
341 | ch->psm = psm; | |
342 | ch->con = con; | |
343 | ch->state = NG_L2CAP_CLOSED; | |
344 | ||
345 | /* Set MTU and flow control settings to defaults */ | |
346 | ch->imtu = NG_L2CAP_MTU_DEFAULT; | |
347 | bcopy(ng_l2cap_default_flow(), &ch->iflow, sizeof(ch->iflow)); | |
348 | ||
349 | ch->omtu = NG_L2CAP_MTU_DEFAULT; | |
350 | bcopy(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow)); | |
351 | ||
352 | ch->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT; | |
353 | ch->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT; | |
354 | ||
355 | LIST_INSERT_HEAD(&l2cap->chan_list, ch, next); | |
356 | ||
357 | ng_l2cap_con_ref(con); | |
358 | } else { | |
359 | bzero(ch, sizeof(*ch)); | |
fc025606 | 360 | kfree(ch, M_NETGRAPH_L2CAP); |
b06ebda0 MD |
361 | ch = NULL; |
362 | } | |
363 | ||
364 | return (ch); | |
365 | } /* ng_l2cap_new_chan */ | |
366 | ||
367 | /* | |
368 | * Get channel by source (local) channel ID | |
369 | */ | |
370 | ||
371 | ng_l2cap_chan_p | |
372 | ng_l2cap_chan_by_scid(ng_l2cap_p l2cap, u_int16_t scid) | |
373 | { | |
374 | ng_l2cap_chan_p ch = NULL; | |
375 | ||
376 | LIST_FOREACH(ch, &l2cap->chan_list, next) | |
377 | if (ch->scid == scid) | |
378 | break; | |
379 | ||
380 | return (ch); | |
381 | } /* ng_l2cap_chan_by_scid */ | |
382 | ||
383 | /* | |
384 | * Free channel descriptor. | |
385 | */ | |
386 | ||
387 | void | |
388 | ng_l2cap_free_chan(ng_l2cap_chan_p ch) | |
389 | { | |
390 | ng_l2cap_cmd_p f = NULL, n = NULL; | |
391 | ||
392 | f = TAILQ_FIRST(&ch->con->cmd_list); | |
393 | while (f != NULL) { | |
394 | n = TAILQ_NEXT(f, next); | |
395 | ||
396 | if (f->ch == ch) { | |
397 | ng_l2cap_unlink_cmd(f); | |
398 | if (f->flags & NG_L2CAP_CMD_PENDING) | |
399 | ng_l2cap_command_untimeout(f); | |
400 | ng_l2cap_free_cmd(f); | |
401 | } | |
402 | ||
403 | f = n; | |
404 | } | |
405 | ||
406 | LIST_REMOVE(ch, next); | |
407 | ||
408 | ng_l2cap_con_unref(ch->con); | |
409 | ||
410 | bzero(ch, sizeof(*ch)); | |
fc025606 | 411 | kfree(ch, M_NETGRAPH_L2CAP); |
b06ebda0 MD |
412 | } /* ng_l2cap_free_chan */ |
413 | ||
414 | /* | |
415 | * Create new L2CAP command descriptor. WILL NOT add command to the queue. | |
416 | */ | |
417 | ||
418 | ng_l2cap_cmd_p | |
419 | ng_l2cap_new_cmd(ng_l2cap_con_p con, ng_l2cap_chan_p ch, u_int8_t ident, | |
420 | u_int8_t code, u_int32_t token) | |
421 | { | |
422 | ng_l2cap_cmd_p cmd = NULL; | |
423 | ||
424 | KASSERT((ch == NULL || ch->con == con), | |
425 | ("%s: %s - invalid channel pointer!\n", | |
426 | __func__, NG_NODE_NAME(con->l2cap->node))); | |
427 | ||
fc025606 SW |
428 | cmd = kmalloc(sizeof(*cmd), M_NETGRAPH_L2CAP, |
429 | M_WAITOK | M_NULLOK | M_ZERO); | |
b06ebda0 MD |
430 | if (cmd == NULL) |
431 | return (NULL); | |
432 | ||
433 | cmd->con = con; | |
434 | cmd->ch = ch; | |
435 | cmd->ident = ident; | |
436 | cmd->code = code; | |
437 | cmd->token = token; | |
438 | ng_callout_init(&cmd->timo); | |
439 | ||
440 | return (cmd); | |
441 | } /* ng_l2cap_new_cmd */ | |
442 | ||
443 | /* | |
444 | * Get pending (i.e. initiated by local side) L2CAP command descriptor by ident | |
445 | */ | |
446 | ||
447 | ng_l2cap_cmd_p | |
448 | ng_l2cap_cmd_by_ident(ng_l2cap_con_p con, u_int8_t ident) | |
449 | { | |
450 | ng_l2cap_cmd_p cmd = NULL; | |
451 | ||
452 | TAILQ_FOREACH(cmd, &con->cmd_list, next) { | |
453 | if ((cmd->flags & NG_L2CAP_CMD_PENDING) && cmd->ident == ident) { | |
454 | KASSERT((cmd->con == con), | |
455 | ("%s: %s - invalid connection pointer!\n", | |
456 | __func__, NG_NODE_NAME(con->l2cap->node))); | |
457 | ||
458 | break; | |
459 | } | |
460 | } | |
461 | ||
462 | return (cmd); | |
463 | } /* ng_l2cap_cmd_by_ident */ | |
464 | ||
465 | /* | |
466 | * Set LP timeout | |
467 | * XXX FIXME: check return code from ng_callout | |
468 | */ | |
469 | ||
470 | int | |
471 | ng_l2cap_lp_timeout(ng_l2cap_con_p con) | |
472 | { | |
473 | if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO)) | |
474 | panic( | |
475 | "%s: %s - invalid timeout, state=%d, flags=%#x\n", | |
476 | __func__, NG_NODE_NAME(con->l2cap->node), | |
477 | con->state, con->flags); | |
478 | ||
479 | con->flags |= NG_L2CAP_CON_LP_TIMO; | |
480 | ng_callout(&con->con_timo, con->l2cap->node, NULL, | |
481 | bluetooth_hci_connect_timeout(), | |
482 | ng_l2cap_process_lp_timeout, NULL, | |
483 | con->con_handle); | |
484 | ||
485 | return (0); | |
486 | } /* ng_l2cap_lp_timeout */ | |
487 | ||
488 | /* | |
489 | * Unset LP timeout | |
490 | */ | |
491 | ||
492 | int | |
493 | ng_l2cap_lp_untimeout(ng_l2cap_con_p con) | |
494 | { | |
495 | if (!(con->flags & NG_L2CAP_CON_LP_TIMO)) | |
496 | panic( | |
497 | "%s: %s - no LP connection timeout, state=%d, flags=%#x\n", | |
498 | __func__, NG_NODE_NAME(con->l2cap->node), | |
499 | con->state, con->flags); | |
500 | ||
501 | if (ng_uncallout(&con->con_timo, con->l2cap->node) == 0) | |
502 | return (ETIMEDOUT); | |
503 | ||
504 | con->flags &= ~NG_L2CAP_CON_LP_TIMO; | |
505 | ||
506 | return (0); | |
507 | } /* ng_l2cap_lp_untimeout */ | |
508 | ||
509 | /* | |
510 | * Set L2CAP command timeout | |
511 | * XXX FIXME: check return code from ng_callout | |
512 | */ | |
513 | ||
514 | int | |
515 | ng_l2cap_command_timeout(ng_l2cap_cmd_p cmd, int timo) | |
516 | { | |
517 | int arg; | |
518 | ||
519 | if (cmd->flags & NG_L2CAP_CMD_PENDING) | |
520 | panic( | |
521 | "%s: %s - duplicated command timeout, code=%#x, flags=%#x\n", | |
522 | __func__, NG_NODE_NAME(cmd->con->l2cap->node), | |
523 | cmd->code, cmd->flags); | |
524 | ||
525 | arg = ((cmd->ident << 16) | cmd->con->con_handle); | |
526 | cmd->flags |= NG_L2CAP_CMD_PENDING; | |
527 | ng_callout(&cmd->timo, cmd->con->l2cap->node, NULL, timo, | |
528 | ng_l2cap_process_command_timeout, NULL, arg); | |
529 | ||
530 | return (0); | |
531 | } /* ng_l2cap_command_timeout */ | |
532 | ||
533 | /* | |
534 | * Unset L2CAP command timeout | |
535 | */ | |
536 | ||
537 | int | |
538 | ng_l2cap_command_untimeout(ng_l2cap_cmd_p cmd) | |
539 | { | |
540 | if (!(cmd->flags & NG_L2CAP_CMD_PENDING)) | |
541 | panic( | |
542 | "%s: %s - no command timeout, code=%#x, flags=%#x\n", | |
543 | __func__, NG_NODE_NAME(cmd->con->l2cap->node), | |
544 | cmd->code, cmd->flags); | |
545 | ||
546 | if (ng_uncallout(&cmd->timo, cmd->con->l2cap->node) == 0) | |
547 | return (ETIMEDOUT); | |
548 | ||
549 | cmd->flags &= ~NG_L2CAP_CMD_PENDING; | |
550 | ||
551 | return (0); | |
552 | } /* ng_l2cap_command_untimeout */ | |
553 | ||
554 | /* | |
555 | * Prepend "m"buf with "size" bytes | |
556 | */ | |
557 | ||
558 | struct mbuf * | |
559 | ng_l2cap_prepend(struct mbuf *m, int size) | |
560 | { | |
b5523eac | 561 | M_PREPEND(m, size, M_NOWAIT); |
b06ebda0 MD |
562 | if (m == NULL || (m->m_len < size && (m = m_pullup(m, size)) == NULL)) |
563 | return (NULL); | |
564 | ||
565 | return (m); | |
566 | } /* ng_l2cap_prepend */ | |
567 | ||
568 | /* | |
569 | * Default flow settings | |
570 | */ | |
571 | ||
572 | ng_l2cap_flow_p | |
573 | ng_l2cap_default_flow(void) | |
574 | { | |
575 | static ng_l2cap_flow_t default_flow = { | |
576 | /* flags */ 0x0, | |
577 | /* service_type */ NG_HCI_SERVICE_TYPE_BEST_EFFORT, | |
578 | /* token_rate */ 0xffffffff, /* maximum */ | |
579 | /* token_bucket_size */ 0xffffffff, /* maximum */ | |
580 | /* peak_bandwidth */ 0x00000000, /* maximum */ | |
581 | /* latency */ 0xffffffff, /* don't care */ | |
582 | /* delay_variation */ 0xffffffff /* don't care */ | |
583 | }; | |
584 | ||
585 | return (&default_flow); | |
586 | } /* ng_l2cap_default_flow */ | |
587 | ||
588 | /* | |
589 | * Get next available channel ID | |
590 | * XXX FIXME this is *UGLY* but will do for now | |
591 | */ | |
592 | ||
593 | static u_int16_t | |
594 | ng_l2cap_get_cid(ng_l2cap_p l2cap) | |
595 | { | |
596 | u_int16_t cid = l2cap->cid + 1; | |
597 | ||
598 | if (cid < NG_L2CAP_FIRST_CID) | |
599 | cid = NG_L2CAP_FIRST_CID; | |
600 | ||
601 | while (cid != l2cap->cid) { | |
602 | if (ng_l2cap_chan_by_scid(l2cap, cid) == NULL) { | |
603 | l2cap->cid = cid; | |
604 | ||
605 | return (cid); | |
606 | } | |
607 | ||
608 | cid ++; | |
609 | if (cid < NG_L2CAP_FIRST_CID) | |
610 | cid = NG_L2CAP_FIRST_CID; | |
611 | } | |
612 | ||
613 | return (NG_L2CAP_NULL_CID); | |
614 | } /* ng_l2cap_get_cid */ | |
615 | ||
616 | /* | |
617 | * Get next available command ident | |
618 | * XXX FIXME this is *UGLY* but will do for now | |
619 | */ | |
620 | ||
621 | u_int8_t | |
622 | ng_l2cap_get_ident(ng_l2cap_con_p con) | |
623 | { | |
624 | u_int8_t ident = con->ident + 1; | |
625 | ||
626 | if (ident < NG_L2CAP_FIRST_IDENT) | |
627 | ident = NG_L2CAP_FIRST_IDENT; | |
628 | ||
629 | while (ident != con->ident) { | |
630 | if (ng_l2cap_cmd_by_ident(con, ident) == NULL) { | |
631 | con->ident = ident; | |
632 | ||
633 | return (ident); | |
634 | } | |
635 | ||
636 | ident ++; | |
637 | if (ident < NG_L2CAP_FIRST_IDENT) | |
638 | ident = NG_L2CAP_FIRST_IDENT; | |
639 | } | |
640 | ||
641 | return (NG_L2CAP_NULL_IDENT); | |
642 | } /* ng_l2cap_get_ident */ | |
643 |