1107120Sjulian/*
2107120Sjulian * ng_l2cap_misc.c
3139823Simp */
4139823Simp
5139823Simp/*-
6107120Sjulian * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
7107120Sjulian * All rights reserved.
8107120Sjulian *
9107120Sjulian * Redistribution and use in source and binary forms, with or without
10107120Sjulian * modification, are permitted provided that the following conditions
11107120Sjulian * are met:
12107120Sjulian * 1. Redistributions of source code must retain the above copyright
13107120Sjulian *    notice, this list of conditions and the following disclaimer.
14107120Sjulian * 2. Redistributions in binary form must reproduce the above copyright
15107120Sjulian *    notice, this list of conditions and the following disclaimer in the
16107120Sjulian *    documentation and/or other materials provided with the distribution.
17107120Sjulian *
18107120Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19107120Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20107120Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21107120Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22107120Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23107120Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24107120Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25107120Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26107120Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27107120Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28107120Sjulian * SUCH DAMAGE.
29107120Sjulian *
30121054Semax * $Id: ng_l2cap_misc.c,v 1.5 2003/09/08 19:11:45 max Exp $
31107120Sjulian * $FreeBSD$
32107120Sjulian */
33107120Sjulian
34107120Sjulian#include <sys/param.h>
35107120Sjulian#include <sys/systm.h>
36107120Sjulian#include <sys/kernel.h>
37107120Sjulian#include <sys/malloc.h>
38107120Sjulian#include <sys/mbuf.h>
39107120Sjulian#include <sys/queue.h>
40107120Sjulian#include <netgraph/ng_message.h>
41107120Sjulian#include <netgraph/netgraph.h>
42128688Semax#include <netgraph/bluetooth/include/ng_bluetooth.h>
43128688Semax#include <netgraph/bluetooth/include/ng_hci.h>
44128688Semax#include <netgraph/bluetooth/include/ng_l2cap.h>
45128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
46128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
47128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
48128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
49128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
50128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>
51107120Sjulian
52121054Semaxstatic u_int16_t	ng_l2cap_get_cid	(ng_l2cap_p);
53107120Sjulian
54107120Sjulian/******************************************************************************
55107120Sjulian ******************************************************************************
56107120Sjulian **                              Utility routines
57107120Sjulian ******************************************************************************
58107120Sjulian ******************************************************************************/
59107120Sjulian
60107120Sjulian/*
61107120Sjulian * Send hook information to the upper layer
62107120Sjulian */
63107120Sjulian
64107120Sjulianvoid
65107120Sjulianng_l2cap_send_hook_info(node_p node, hook_p hook, void *arg1, int arg2)
66107120Sjulian{
67107120Sjulian	ng_l2cap_p	 l2cap = NULL;
68107120Sjulian	struct ng_mesg	*msg = NULL;
69107120Sjulian	int		 error = 0;
70107120Sjulian
71107120Sjulian	if (node == NULL || NG_NODE_NOT_VALID(node) ||
72107120Sjulian	    hook == NULL || NG_HOOK_NOT_VALID(hook))
73107120Sjulian		return;
74107120Sjulian
75107120Sjulian	l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
76107120Sjulian	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci) ||
77107120Sjulian	    bcmp(&l2cap->bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2cap->bdaddr)) == 0)
78107120Sjulian		return;
79107120Sjulian
80107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_HOOK_INFO,
81107120Sjulian		sizeof(bdaddr_t), M_NOWAIT);
82107120Sjulian	if (msg != NULL) {
83107120Sjulian		bcopy(&l2cap->bdaddr, msg->data, sizeof(bdaddr_t));
84128076Semax		NG_SEND_MSG_HOOK(error, node, msg, hook, 0);
85107120Sjulian	} else
86107120Sjulian		error = ENOMEM;
87107120Sjulian
88107120Sjulian	if (error != 0)
89107120Sjulian		NG_L2CAP_INFO(
90107120Sjulian"%s: %s - failed to send HOOK_INFO message to hook \"%s\", error=%d\n",
91107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), NG_HOOK_NAME(hook),
92107120Sjulian			error);
93107120Sjulian} /* ng_l2cap_send_hook_info */
94107120Sjulian
95107120Sjulian/*
96114878Sjulian * Create new connection descriptor for the "remote" unit.
97114878Sjulian * Will link connection descriptor to the l2cap node.
98107120Sjulian */
99107120Sjulian
100107120Sjulianng_l2cap_con_p
101107120Sjulianng_l2cap_new_con(ng_l2cap_p l2cap, bdaddr_p bdaddr)
102107120Sjulian{
103121054Semax	static int	fake_con_handle = 0x0f00;
104107120Sjulian	ng_l2cap_con_p	con = NULL;
105107120Sjulian
106107120Sjulian	/* Create new connection descriptor */
107184205Sdes	con = malloc(sizeof(*con), M_NETGRAPH_L2CAP,
108107120Sjulian		M_NOWAIT|M_ZERO);
109107120Sjulian	if (con == NULL)
110107120Sjulian		return (NULL);
111107120Sjulian
112107120Sjulian	con->l2cap = l2cap;
113107120Sjulian	con->state = NG_L2CAP_CON_CLOSED;
114107120Sjulian
115121054Semax	/*
116121054Semax	 * XXX
117121054Semax	 *
118121054Semax	 * Assign fake connection handle to the connection descriptor.
119121054Semax	 * Bluetooth specification marks 0x0f00 - 0x0fff connection
120121054Semax	 * handles as reserved. We need this fake connection handles
121121054Semax	 * for timeouts. Connection handle will be passed as argument
122121054Semax	 * to timeout so when timeout happens we can find the right
123121054Semax	 * connection descriptor. We can not pass pointers, because
124121054Semax	 * timeouts are external (to Netgraph) events and there might
125121054Semax	 * be a race when node/hook goes down and timeout event already
126121054Semax	 * went into node's queue
127121054Semax	 */
128121054Semax
129121054Semax	con->con_handle = fake_con_handle ++;
130121054Semax	if (fake_con_handle > 0x0fff)
131121054Semax		fake_con_handle = 0x0f00;
132121054Semax
133107120Sjulian	bcopy(bdaddr, &con->remote, sizeof(con->remote));
134137163Semax	ng_callout_init(&con->con_timo);
135107120Sjulian
136107120Sjulian	con->ident = NG_L2CAP_FIRST_IDENT - 1;
137107120Sjulian	TAILQ_INIT(&con->cmd_list);
138107120Sjulian
139107120Sjulian	/* Link connection */
140107120Sjulian	LIST_INSERT_HEAD(&l2cap->con_list, con, next);
141107120Sjulian
142107120Sjulian	return (con);
143107120Sjulian} /* ng_l2cap_new_con */
144107120Sjulian
145107120Sjulian/*
146114878Sjulian * Add reference to the connection descriptor
147114878Sjulian */
148114878Sjulian
149114878Sjulianvoid
150114878Sjulianng_l2cap_con_ref(ng_l2cap_con_p con)
151114878Sjulian{
152114878Sjulian	con->refcnt ++;
153114878Sjulian
154114878Sjulian	if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO) {
155114878Sjulian		if ((con->state != NG_L2CAP_CON_OPEN) ||
156114878Sjulian		    (con->flags & NG_L2CAP_CON_OUTGOING) == 0)
157121054Semax			panic(
158121054Semax"%s: %s - bad auto disconnect timeout, state=%d, flags=%#x\n",
159121054Semax				__func__, NG_NODE_NAME(con->l2cap->node),
160121054Semax				con->state, con->flags);
161114878Sjulian
162114878Sjulian		ng_l2cap_discon_untimeout(con);
163114878Sjulian	}
164114878Sjulian} /* ng_l2cap_con_ref */
165114878Sjulian
166114878Sjulian/*
167114878Sjulian * Remove reference from the connection descriptor
168114878Sjulian */
169114878Sjulian
170114878Sjulianvoid
171114878Sjulianng_l2cap_con_unref(ng_l2cap_con_p con)
172114878Sjulian{
173114878Sjulian	con->refcnt --;
174114878Sjulian
175114878Sjulian	if (con->refcnt < 0)
176121054Semax		panic(
177121054Semax"%s: %s - con->refcnt < 0\n", __func__, NG_NODE_NAME(con->l2cap->node));
178114878Sjulian
179114878Sjulian	/*
180114878Sjulian	 * Set auto disconnect timer only if the following conditions are met:
181114878Sjulian	 * 1) we have no reference on the connection
182114878Sjulian	 * 2) connection is in OPEN state
183114878Sjulian	 * 3) it is an outgoing connection
184114878Sjulian	 * 4) disconnect timeout > 0
185149679Semax	 * 5) connection is not dying
186114878Sjulian	 */
187114878Sjulian
188114878Sjulian	if ((con->refcnt == 0) &&
189114878Sjulian	    (con->state == NG_L2CAP_CON_OPEN) &&
190114878Sjulian	    (con->flags & NG_L2CAP_CON_OUTGOING) &&
191149679Semax	    (con->l2cap->discon_timo > 0) &&
192149679Semax	    ((con->flags & NG_L2CAP_CON_DYING) == 0))
193114878Sjulian		ng_l2cap_discon_timeout(con);
194114878Sjulian} /* ng_l2cap_con_unref */
195114878Sjulian
196114878Sjulian/*
197114878Sjulian * Set auto disconnect timeout
198138268Sglebius * XXX FIXME: check return code from ng_callout
199114878Sjulian */
200114878Sjulian
201121054Semaxint
202114878Sjulianng_l2cap_discon_timeout(ng_l2cap_con_p con)
203114878Sjulian{
204114878Sjulian	if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO))
205121054Semax		panic(
206121054Semax"%s: %s - invalid timeout, state=%d, flags=%#x\n",
207114878Sjulian			__func__, NG_NODE_NAME(con->l2cap->node),
208114878Sjulian			con->state, con->flags);
209114878Sjulian
210114878Sjulian	con->flags |= NG_L2CAP_CON_AUTO_DISCON_TIMO;
211138268Sglebius	ng_callout(&con->con_timo, con->l2cap->node, NULL,
212121054Semax				con->l2cap->discon_timo * hz,
213121054Semax				ng_l2cap_process_discon_timeout, NULL,
214121054Semax				con->con_handle);
215121054Semax
216121054Semax	return (0);
217114878Sjulian} /* ng_l2cap_discon_timeout */
218114878Sjulian
219114878Sjulian/*
220114878Sjulian * Unset auto disconnect timeout
221114878Sjulian */
222114878Sjulian
223121054Semaxint
224114878Sjulianng_l2cap_discon_untimeout(ng_l2cap_con_p con)
225114878Sjulian{
226121054Semax	if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO))
227121054Semax		panic(
228121054Semax"%s: %s - no disconnect timeout, state=%d, flags=%#x\n",
229121054Semax			__func__,  NG_NODE_NAME(con->l2cap->node),
230121054Semax			con->state, con->flags);
231121054Semax
232138268Sglebius	if (ng_uncallout(&con->con_timo, con->l2cap->node) == 0)
233121054Semax		return (ETIMEDOUT);
234121054Semax
235114878Sjulian	con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO;
236121054Semax
237121054Semax	return (0);
238114878Sjulian} /* ng_l2cap_discon_untimeout */
239114878Sjulian
240114878Sjulian/*
241107120Sjulian * Free connection descriptor. Will unlink connection and free everything.
242107120Sjulian */
243107120Sjulian
244107120Sjulianvoid
245107120Sjulianng_l2cap_free_con(ng_l2cap_con_p con)
246107120Sjulian{
247107120Sjulian	ng_l2cap_chan_p f = NULL, n = NULL;
248107120Sjulian
249114878Sjulian	con->state = NG_L2CAP_CON_CLOSED;
250114878Sjulian
251121054Semax	while (con->tx_pkt != NULL) {
252121054Semax		struct mbuf	*m = con->tx_pkt->m_nextpkt;
253107120Sjulian
254121054Semax		m_freem(con->tx_pkt);
255121054Semax		con->tx_pkt = m;
256107120Sjulian	}
257107120Sjulian
258107120Sjulian	NG_FREE_M(con->rx_pkt);
259107120Sjulian
260107120Sjulian	for (f = LIST_FIRST(&con->l2cap->chan_list); f != NULL; ) {
261107120Sjulian		n = LIST_NEXT(f, next);
262107120Sjulian
263107120Sjulian		if (f->con == con)
264107120Sjulian			ng_l2cap_free_chan(f);
265107120Sjulian
266107120Sjulian		f = n;
267107120Sjulian	}
268107120Sjulian
269107120Sjulian	while (!TAILQ_EMPTY(&con->cmd_list)) {
270107120Sjulian		ng_l2cap_cmd_p	cmd = TAILQ_FIRST(&con->cmd_list);
271107120Sjulian
272107120Sjulian		ng_l2cap_unlink_cmd(cmd);
273121054Semax		if (cmd->flags & NG_L2CAP_CMD_PENDING)
274121054Semax			ng_l2cap_command_untimeout(cmd);
275107120Sjulian		ng_l2cap_free_cmd(cmd);
276107120Sjulian	}
277107120Sjulian
278149679Semax	if (con->flags & (NG_L2CAP_CON_AUTO_DISCON_TIMO|NG_L2CAP_CON_LP_TIMO))
279149679Semax		panic(
280149679Semax"%s: %s - timeout pending! state=%d, flags=%#x\n",
281149679Semax			__func__,  NG_NODE_NAME(con->l2cap->node),
282149679Semax			con->state, con->flags);
283149679Semax
284107120Sjulian	LIST_REMOVE(con, next);
285149679Semax
286107120Sjulian	bzero(con, sizeof(*con));
287184205Sdes	free(con, M_NETGRAPH_L2CAP);
288107120Sjulian} /* ng_l2cap_free_con */
289107120Sjulian
290107120Sjulian/*
291107120Sjulian * Get connection by "remote" address
292107120Sjulian */
293107120Sjulian
294107120Sjulianng_l2cap_con_p
295107120Sjulianng_l2cap_con_by_addr(ng_l2cap_p l2cap, bdaddr_p bdaddr)
296107120Sjulian{
297107120Sjulian	ng_l2cap_con_p	con = NULL;
298107120Sjulian
299107120Sjulian	LIST_FOREACH(con, &l2cap->con_list, next)
300107120Sjulian		if (bcmp(bdaddr, &con->remote, sizeof(con->remote)) == 0)
301107120Sjulian			break;
302107120Sjulian
303107120Sjulian	return (con);
304107120Sjulian} /* ng_l2cap_con_by_addr */
305107120Sjulian
306107120Sjulian/*
307107120Sjulian * Get connection by "handle"
308107120Sjulian */
309107120Sjulian
310107120Sjulianng_l2cap_con_p
311107120Sjulianng_l2cap_con_by_handle(ng_l2cap_p l2cap, u_int16_t con_handle)
312107120Sjulian{
313107120Sjulian	ng_l2cap_con_p	con = NULL;
314107120Sjulian
315107120Sjulian	LIST_FOREACH(con, &l2cap->con_list, next)
316107120Sjulian		if (con->con_handle == con_handle)
317107120Sjulian			break;
318107120Sjulian
319107120Sjulian	return (con);
320107120Sjulian} /* ng_l2cap_con_by_handle */
321107120Sjulian
322107120Sjulian/*
323107120Sjulian * Allocate new L2CAP channel descriptor on "con" conection with "psm".
324107120Sjulian * Will link the channel to the l2cap node
325107120Sjulian */
326107120Sjulian
327107120Sjulianng_l2cap_chan_p
328107120Sjulianng_l2cap_new_chan(ng_l2cap_p l2cap, ng_l2cap_con_p con, u_int16_t psm)
329107120Sjulian{
330107120Sjulian	ng_l2cap_chan_p	ch = NULL;
331107120Sjulian
332184205Sdes	ch = malloc(sizeof(*ch), M_NETGRAPH_L2CAP,
333107120Sjulian		M_NOWAIT|M_ZERO);
334107120Sjulian	if (ch == NULL)
335107120Sjulian		return (NULL);
336107120Sjulian
337107120Sjulian	ch->scid = ng_l2cap_get_cid(l2cap);
338107120Sjulian
339107120Sjulian	if (ch->scid != NG_L2CAP_NULL_CID) {
340107120Sjulian		/* Initialize channel */
341107120Sjulian		ch->psm = psm;
342107120Sjulian		ch->con = con;
343107120Sjulian		ch->state = NG_L2CAP_CLOSED;
344107120Sjulian
345107120Sjulian		/* Set MTU and flow control settings to defaults */
346107120Sjulian		ch->imtu = NG_L2CAP_MTU_DEFAULT;
347107120Sjulian		bcopy(ng_l2cap_default_flow(), &ch->iflow, sizeof(ch->iflow));
348107120Sjulian
349107120Sjulian		ch->omtu = NG_L2CAP_MTU_DEFAULT;
350107120Sjulian		bcopy(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow));
351107120Sjulian
352107120Sjulian		ch->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT;
353107120Sjulian		ch->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT;
354107120Sjulian
355107120Sjulian		LIST_INSERT_HEAD(&l2cap->chan_list, ch, next);
356114878Sjulian
357114878Sjulian		ng_l2cap_con_ref(con);
358107120Sjulian	} else {
359107120Sjulian		bzero(ch, sizeof(*ch));
360184205Sdes		free(ch, M_NETGRAPH_L2CAP);
361107120Sjulian		ch = NULL;
362107120Sjulian	}
363107120Sjulian
364107120Sjulian	return (ch);
365107120Sjulian} /* ng_l2cap_new_chan */
366107120Sjulian
367107120Sjulian/*
368107120Sjulian * Get channel by source (local) channel ID
369107120Sjulian */
370107120Sjulian
371107120Sjulianng_l2cap_chan_p
372107120Sjulianng_l2cap_chan_by_scid(ng_l2cap_p l2cap, u_int16_t scid)
373107120Sjulian{
374107120Sjulian	ng_l2cap_chan_p	ch = NULL;
375107120Sjulian
376107120Sjulian	LIST_FOREACH(ch, &l2cap->chan_list, next)
377107120Sjulian		if (ch->scid == scid)
378107120Sjulian			break;
379107120Sjulian
380107120Sjulian	return (ch);
381107120Sjulian} /* ng_l2cap_chan_by_scid */
382107120Sjulian
383107120Sjulian/*
384107120Sjulian * Free channel descriptor.
385107120Sjulian */
386107120Sjulian
387107120Sjulianvoid
388107120Sjulianng_l2cap_free_chan(ng_l2cap_chan_p ch)
389107120Sjulian{
390107120Sjulian	ng_l2cap_cmd_p	f = NULL, n = NULL;
391107120Sjulian
392107120Sjulian	f = TAILQ_FIRST(&ch->con->cmd_list);
393107120Sjulian	while (f != NULL) {
394107120Sjulian		n = TAILQ_NEXT(f, next);
395107120Sjulian
396107120Sjulian		if (f->ch == ch) {
397107120Sjulian			ng_l2cap_unlink_cmd(f);
398121054Semax			if (f->flags & NG_L2CAP_CMD_PENDING)
399121054Semax				ng_l2cap_command_untimeout(f);
400107120Sjulian			ng_l2cap_free_cmd(f);
401107120Sjulian		}
402107120Sjulian
403107120Sjulian		f = n;
404107120Sjulian	}
405107120Sjulian
406107120Sjulian	LIST_REMOVE(ch, next);
407114878Sjulian
408114878Sjulian	ng_l2cap_con_unref(ch->con);
409114878Sjulian
410107120Sjulian	bzero(ch, sizeof(*ch));
411184205Sdes	free(ch, M_NETGRAPH_L2CAP);
412107120Sjulian} /* ng_l2cap_free_chan */
413107120Sjulian
414107120Sjulian/*
415107120Sjulian * Create new L2CAP command descriptor. WILL NOT add command to the queue.
416107120Sjulian */
417107120Sjulian
418107120Sjulianng_l2cap_cmd_p
419107120Sjulianng_l2cap_new_cmd(ng_l2cap_con_p con, ng_l2cap_chan_p ch, u_int8_t ident,
420107120Sjulian		u_int8_t code, u_int32_t token)
421107120Sjulian{
422107120Sjulian	ng_l2cap_cmd_p	cmd = NULL;
423107120Sjulian
424107120Sjulian	KASSERT((ch == NULL || ch->con == con),
425107120Sjulian("%s: %s - invalid channel pointer!\n",
426107120Sjulian		__func__, NG_NODE_NAME(con->l2cap->node)));
427107120Sjulian
428184205Sdes	cmd = malloc(sizeof(*cmd), M_NETGRAPH_L2CAP,
429107120Sjulian		M_NOWAIT|M_ZERO);
430107120Sjulian	if (cmd == NULL)
431107120Sjulian		return (NULL);
432107120Sjulian
433107120Sjulian	cmd->con = con;
434107120Sjulian	cmd->ch = ch;
435107120Sjulian	cmd->ident = ident;
436107120Sjulian	cmd->code = code;
437107120Sjulian	cmd->token = token;
438137163Semax	ng_callout_init(&cmd->timo);
439107120Sjulian
440107120Sjulian	return (cmd);
441107120Sjulian} /* ng_l2cap_new_cmd */
442107120Sjulian
443107120Sjulian/*
444121054Semax * Get pending (i.e. initiated by local side) L2CAP command descriptor by ident
445107120Sjulian */
446107120Sjulian
447107120Sjulianng_l2cap_cmd_p
448107120Sjulianng_l2cap_cmd_by_ident(ng_l2cap_con_p con, u_int8_t ident)
449107120Sjulian{
450107120Sjulian	ng_l2cap_cmd_p	cmd = NULL;
451107120Sjulian
452121054Semax	TAILQ_FOREACH(cmd, &con->cmd_list, next) {
453121054Semax		if ((cmd->flags & NG_L2CAP_CMD_PENDING) && cmd->ident == ident) {
454121054Semax			KASSERT((cmd->con == con),
455121054Semax("%s: %s - invalid connection pointer!\n",
456121054Semax				__func__, NG_NODE_NAME(con->l2cap->node)));
457121054Semax
458107120Sjulian			break;
459121054Semax		}
460121054Semax	}
461107120Sjulian
462107120Sjulian	return (cmd);
463107120Sjulian} /* ng_l2cap_cmd_by_ident */
464107120Sjulian
465107120Sjulian/*
466107120Sjulian * Set LP timeout
467138268Sglebius * XXX FIXME: check return code from ng_callout
468107120Sjulian */
469107120Sjulian
470121054Semaxint
471107120Sjulianng_l2cap_lp_timeout(ng_l2cap_con_p con)
472107120Sjulian{
473114878Sjulian	if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO))
474121054Semax		panic(
475121054Semax"%s: %s - invalid timeout, state=%d, flags=%#x\n",
476114878Sjulian			__func__, NG_NODE_NAME(con->l2cap->node),
477114878Sjulian			con->state, con->flags);
478114878Sjulian
479114878Sjulian	con->flags |= NG_L2CAP_CON_LP_TIMO;
480138268Sglebius	ng_callout(&con->con_timo, con->l2cap->node, NULL,
481121054Semax				bluetooth_hci_connect_timeout(),
482121054Semax				ng_l2cap_process_lp_timeout, NULL,
483121054Semax				con->con_handle);
484121054Semax
485121054Semax	return (0);
486107120Sjulian} /* ng_l2cap_lp_timeout */
487107120Sjulian
488107120Sjulian/*
489107120Sjulian * Unset LP timeout
490107120Sjulian */
491107120Sjulian
492121054Semaxint
493107120Sjulianng_l2cap_lp_untimeout(ng_l2cap_con_p con)
494107120Sjulian{
495121054Semax	if (!(con->flags & NG_L2CAP_CON_LP_TIMO))
496121054Semax		panic(
497121054Semax"%s: %s - no LP connection timeout, state=%d, flags=%#x\n",
498121054Semax			__func__,  NG_NODE_NAME(con->l2cap->node),
499121054Semax			con->state, con->flags);
500121054Semax
501138268Sglebius	if (ng_uncallout(&con->con_timo, con->l2cap->node) == 0)
502121054Semax		return (ETIMEDOUT);
503121054Semax
504114878Sjulian	con->flags &= ~NG_L2CAP_CON_LP_TIMO;
505121054Semax
506121054Semax	return (0);
507107120Sjulian} /* ng_l2cap_lp_untimeout */
508107120Sjulian
509107120Sjulian/*
510121054Semax * Set L2CAP command timeout
511138268Sglebius * XXX FIXME: check return code from ng_callout
512107120Sjulian */
513107120Sjulian
514121054Semaxint
515121054Semaxng_l2cap_command_timeout(ng_l2cap_cmd_p cmd, int timo)
516107120Sjulian{
517121054Semax	int	arg;
518107120Sjulian
519121054Semax	if (cmd->flags & NG_L2CAP_CMD_PENDING)
520121054Semax		panic(
521121054Semax"%s: %s - duplicated command timeout, code=%#x, flags=%#x\n",
522121054Semax			__func__, NG_NODE_NAME(cmd->con->l2cap->node),
523121054Semax			cmd->code, cmd->flags);
524107120Sjulian
525121054Semax	arg = ((cmd->ident << 16) | cmd->con->con_handle);
526121054Semax	cmd->flags |= NG_L2CAP_CMD_PENDING;
527138268Sglebius	ng_callout(&cmd->timo, cmd->con->l2cap->node, NULL, timo,
528121054Semax				ng_l2cap_process_command_timeout, NULL, arg);
529107120Sjulian
530121054Semax	return (0);
531107120Sjulian} /* ng_l2cap_command_timeout */
532107120Sjulian
533107120Sjulian/*
534107120Sjulian * Unset L2CAP command timeout
535107120Sjulian */
536107120Sjulian
537121054Semaxint
538107120Sjulianng_l2cap_command_untimeout(ng_l2cap_cmd_p cmd)
539107120Sjulian{
540121054Semax	if (!(cmd->flags & NG_L2CAP_CMD_PENDING))
541121054Semax		panic(
542121054Semax"%s: %s - no command timeout, code=%#x, flags=%#x\n",
543121054Semax			__func__, NG_NODE_NAME(cmd->con->l2cap->node),
544121054Semax			cmd->code, cmd->flags);
545107120Sjulian
546138268Sglebius	if (ng_uncallout(&cmd->timo, cmd->con->l2cap->node) == 0)
547121054Semax		return (ETIMEDOUT);
548107120Sjulian
549121054Semax	cmd->flags &= ~NG_L2CAP_CMD_PENDING;
550107120Sjulian
551121054Semax	return (0);
552121054Semax} /* ng_l2cap_command_untimeout */
553107120Sjulian
554107120Sjulian/*
555107120Sjulian * Prepend "m"buf with "size" bytes
556107120Sjulian */
557107120Sjulian
558107120Sjulianstruct mbuf *
559107120Sjulianng_l2cap_prepend(struct mbuf *m, int size)
560107120Sjulian{
561243882Sglebius	M_PREPEND(m, size, M_NOWAIT);
562107120Sjulian	if (m == NULL || (m->m_len < size && (m = m_pullup(m, size)) == NULL))
563107120Sjulian		return (NULL);
564107120Sjulian
565107120Sjulian	return (m);
566107120Sjulian} /* ng_l2cap_prepend */
567107120Sjulian
568107120Sjulian/*
569107120Sjulian * Default flow settings
570107120Sjulian */
571107120Sjulian
572107120Sjulianng_l2cap_flow_p
573107120Sjulianng_l2cap_default_flow(void)
574107120Sjulian{
575107120Sjulian	static ng_l2cap_flow_t	default_flow = {
576107120Sjulian		/* flags */		0x0,
577107120Sjulian		/* service_type */	NG_HCI_SERVICE_TYPE_BEST_EFFORT,
578107120Sjulian		/* token_rate */	0xffffffff, /* maximum */
579107120Sjulian		/* token_bucket_size */	0xffffffff, /* maximum */
580107120Sjulian		/* peak_bandwidth */	0x00000000, /* maximum */
581107120Sjulian		/* latency */		0xffffffff, /* don't care */
582107120Sjulian		/* delay_variation */	0xffffffff  /* don't care */
583107120Sjulian	};
584107120Sjulian
585107120Sjulian	return (&default_flow);
586107120Sjulian} /* ng_l2cap_default_flow */
587107120Sjulian
588107120Sjulian/*
589107120Sjulian * Get next available channel ID
590107120Sjulian * XXX FIXME this is *UGLY* but will do for now
591107120Sjulian */
592107120Sjulian
593107120Sjulianstatic u_int16_t
594107120Sjulianng_l2cap_get_cid(ng_l2cap_p l2cap)
595107120Sjulian{
596107120Sjulian	u_int16_t	cid = l2cap->cid + 1;
597107120Sjulian
598107120Sjulian	if (cid < NG_L2CAP_FIRST_CID)
599107120Sjulian		cid = NG_L2CAP_FIRST_CID;
600107120Sjulian
601107120Sjulian	while (cid != l2cap->cid) {
602107120Sjulian		if (ng_l2cap_chan_by_scid(l2cap, cid) == NULL) {
603107120Sjulian			l2cap->cid = cid;
604107120Sjulian
605107120Sjulian			return (cid);
606107120Sjulian		}
607107120Sjulian
608107120Sjulian		cid ++;
609107120Sjulian		if (cid < NG_L2CAP_FIRST_CID)
610107120Sjulian			cid = NG_L2CAP_FIRST_CID;
611107120Sjulian	}
612107120Sjulian
613107120Sjulian	return (NG_L2CAP_NULL_CID);
614107120Sjulian} /* ng_l2cap_get_cid */
615107120Sjulian
616107120Sjulian/*
617107120Sjulian * Get next available command ident
618107120Sjulian * XXX FIXME this is *UGLY* but will do for now
619107120Sjulian */
620107120Sjulian
621107120Sjulianu_int8_t
622107120Sjulianng_l2cap_get_ident(ng_l2cap_con_p con)
623107120Sjulian{
624107120Sjulian	u_int8_t	ident = con->ident + 1;
625107120Sjulian
626107120Sjulian	if (ident < NG_L2CAP_FIRST_IDENT)
627107120Sjulian		ident = NG_L2CAP_FIRST_IDENT;
628107120Sjulian
629107120Sjulian	while (ident != con->ident) {
630107120Sjulian		if (ng_l2cap_cmd_by_ident(con, ident) == NULL) {
631107120Sjulian			con->ident = ident;
632107120Sjulian
633107120Sjulian			return (ident);
634107120Sjulian		}
635107120Sjulian
636107120Sjulian		ident ++;
637107120Sjulian		if (ident < NG_L2CAP_FIRST_IDENT)
638107120Sjulian			ident = NG_L2CAP_FIRST_IDENT;
639107120Sjulian	}
640107120Sjulian
641107120Sjulian	return (NG_L2CAP_NULL_IDENT);
642107120Sjulian} /* ng_l2cap_get_ident */
643107120Sjulian
644