xref: /freebsd/sys/netgraph/bluetooth/l2cap/ng_l2cap_misc.c (revision f2bb1cae36283a8eb5a0f19d8612c6abc5148e8f)
1878ed226SJulian Elischer /*
2878ed226SJulian Elischer  * ng_l2cap_misc.c
3878ed226SJulian Elischer  *
4878ed226SJulian Elischer  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
5878ed226SJulian Elischer  * All rights reserved.
6878ed226SJulian Elischer  *
7878ed226SJulian Elischer  * Redistribution and use in source and binary forms, with or without
8878ed226SJulian Elischer  * modification, are permitted provided that the following conditions
9878ed226SJulian Elischer  * are met:
10878ed226SJulian Elischer  * 1. Redistributions of source code must retain the above copyright
11878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer.
12878ed226SJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
13878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
14878ed226SJulian Elischer  *    documentation and/or other materials provided with the distribution.
15878ed226SJulian Elischer  *
16878ed226SJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17878ed226SJulian Elischer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18878ed226SJulian Elischer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19878ed226SJulian Elischer  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20878ed226SJulian Elischer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21878ed226SJulian Elischer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22878ed226SJulian Elischer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23878ed226SJulian Elischer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24878ed226SJulian Elischer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25878ed226SJulian Elischer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26878ed226SJulian Elischer  * SUCH DAMAGE.
27878ed226SJulian Elischer  *
28f2bb1caeSJulian Elischer  * $Id: ng_l2cap_misc.c,v 1.4 2003/04/28 21:44:59 max Exp $
29878ed226SJulian Elischer  * $FreeBSD$
30878ed226SJulian Elischer  */
31878ed226SJulian Elischer 
32878ed226SJulian Elischer #include <sys/param.h>
33878ed226SJulian Elischer #include <sys/systm.h>
34878ed226SJulian Elischer #include <sys/kernel.h>
35878ed226SJulian Elischer #include <sys/malloc.h>
36878ed226SJulian Elischer #include <sys/mbuf.h>
37878ed226SJulian Elischer #include <sys/queue.h>
38878ed226SJulian Elischer #include <netgraph/ng_message.h>
39878ed226SJulian Elischer #include <netgraph/netgraph.h>
40878ed226SJulian Elischer #include "ng_bluetooth.h"
41878ed226SJulian Elischer #include "ng_hci.h"
42878ed226SJulian Elischer #include "ng_l2cap.h"
43878ed226SJulian Elischer #include "ng_l2cap_var.h"
44878ed226SJulian Elischer #include "ng_l2cap_cmds.h"
45878ed226SJulian Elischer #include "ng_l2cap_evnt.h"
46878ed226SJulian Elischer #include "ng_l2cap_llpi.h"
47878ed226SJulian Elischer #include "ng_l2cap_ulpi.h"
48878ed226SJulian Elischer #include "ng_l2cap_misc.h"
49878ed226SJulian Elischer 
50878ed226SJulian Elischer static u_int16_t	ng_l2cap_get_cid		(ng_l2cap_p);
51f2bb1caeSJulian Elischer static void		ng_l2cap_queue_discon_timeout	(void *);
52878ed226SJulian Elischer static void		ng_l2cap_queue_lp_timeout	(void *);
53878ed226SJulian Elischer static void		ng_l2cap_queue_command_timeout	(void *);
54878ed226SJulian Elischer 
55878ed226SJulian Elischer /******************************************************************************
56878ed226SJulian Elischer  ******************************************************************************
57878ed226SJulian Elischer  **                              Utility routines
58878ed226SJulian Elischer  ******************************************************************************
59878ed226SJulian Elischer  ******************************************************************************/
60878ed226SJulian Elischer 
61878ed226SJulian Elischer /*
62878ed226SJulian Elischer  * Send hook information to the upper layer
63878ed226SJulian Elischer  */
64878ed226SJulian Elischer 
65878ed226SJulian Elischer void
66878ed226SJulian Elischer ng_l2cap_send_hook_info(node_p node, hook_p hook, void *arg1, int arg2)
67878ed226SJulian Elischer {
68878ed226SJulian Elischer 	ng_l2cap_p	 l2cap = NULL;
69878ed226SJulian Elischer 	struct ng_mesg	*msg = NULL;
70878ed226SJulian Elischer 	int		 error = 0;
71878ed226SJulian Elischer 
72878ed226SJulian Elischer 	if (node == NULL || NG_NODE_NOT_VALID(node) ||
73878ed226SJulian Elischer 	    hook == NULL || NG_HOOK_NOT_VALID(hook))
74878ed226SJulian Elischer 		return;
75878ed226SJulian Elischer 
76878ed226SJulian Elischer 	l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
77878ed226SJulian Elischer 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci) ||
78878ed226SJulian Elischer 	    bcmp(&l2cap->bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2cap->bdaddr)) == 0)
79878ed226SJulian Elischer 		return;
80878ed226SJulian Elischer 
81878ed226SJulian Elischer 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_HOOK_INFO,
82878ed226SJulian Elischer 		sizeof(bdaddr_t), M_NOWAIT);
83878ed226SJulian Elischer 	if (msg != NULL) {
84878ed226SJulian Elischer 		bcopy(&l2cap->bdaddr, msg->data, sizeof(bdaddr_t));
85878ed226SJulian Elischer 		NG_SEND_MSG_HOOK(error, node, msg, hook, NULL);
86878ed226SJulian Elischer 	} else
87878ed226SJulian Elischer 		error = ENOMEM;
88878ed226SJulian Elischer 
89878ed226SJulian Elischer 	if (error != 0)
90878ed226SJulian Elischer 		NG_L2CAP_INFO(
91878ed226SJulian Elischer "%s: %s - failed to send HOOK_INFO message to hook \"%s\", error=%d\n",
92878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), NG_HOOK_NAME(hook),
93878ed226SJulian Elischer 			error);
94878ed226SJulian Elischer } /* ng_l2cap_send_hook_info */
95878ed226SJulian Elischer 
96878ed226SJulian Elischer /*
97f2bb1caeSJulian Elischer  * Create new connection descriptor for the "remote" unit.
98f2bb1caeSJulian Elischer  * Will link connection descriptor to the l2cap node.
99878ed226SJulian Elischer  */
100878ed226SJulian Elischer 
101878ed226SJulian Elischer ng_l2cap_con_p
102878ed226SJulian Elischer ng_l2cap_new_con(ng_l2cap_p l2cap, bdaddr_p bdaddr)
103878ed226SJulian Elischer {
104878ed226SJulian Elischer 	ng_l2cap_con_p	con = NULL;
105878ed226SJulian Elischer 
106878ed226SJulian Elischer 	/* Create new connection descriptor */
107878ed226SJulian Elischer 	MALLOC(con, ng_l2cap_con_p, sizeof(*con), M_NETGRAPH_L2CAP,
108878ed226SJulian Elischer 		M_NOWAIT|M_ZERO);
109878ed226SJulian Elischer 	if (con == NULL)
110878ed226SJulian Elischer 		return (NULL);
111878ed226SJulian Elischer 
112878ed226SJulian Elischer 	con->l2cap = l2cap;
113878ed226SJulian Elischer 	con->state = NG_L2CAP_CON_CLOSED;
114878ed226SJulian Elischer 
115878ed226SJulian Elischer 	bcopy(bdaddr, &con->remote, sizeof(con->remote));
116878ed226SJulian Elischer 	callout_handle_init(&con->con_timo);
117878ed226SJulian Elischer 
118878ed226SJulian Elischer 	con->ident = NG_L2CAP_FIRST_IDENT - 1;
119878ed226SJulian Elischer 	TAILQ_INIT(&con->cmd_list);
120878ed226SJulian Elischer 
121878ed226SJulian Elischer 	/* Link connection */
122878ed226SJulian Elischer 	LIST_INSERT_HEAD(&l2cap->con_list, con, next);
123878ed226SJulian Elischer 
124878ed226SJulian Elischer 	return (con);
125878ed226SJulian Elischer } /* ng_l2cap_new_con */
126878ed226SJulian Elischer 
127878ed226SJulian Elischer /*
128f2bb1caeSJulian Elischer  * Add reference to the connection descriptor
129f2bb1caeSJulian Elischer  */
130f2bb1caeSJulian Elischer 
131f2bb1caeSJulian Elischer void
132f2bb1caeSJulian Elischer ng_l2cap_con_ref(ng_l2cap_con_p con)
133f2bb1caeSJulian Elischer {
134f2bb1caeSJulian Elischer 	con->refcnt ++;
135f2bb1caeSJulian Elischer 
136f2bb1caeSJulian Elischer 	if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO) {
137f2bb1caeSJulian Elischer 		if ((con->state != NG_L2CAP_CON_OPEN) ||
138f2bb1caeSJulian Elischer 		    (con->flags & NG_L2CAP_CON_OUTGOING) == 0)
139f2bb1caeSJulian Elischer 			panic("%s: %s - bad auto disconnect timeout\n",
140f2bb1caeSJulian Elischer 				__func__, NG_NODE_NAME(con->l2cap->node));
141f2bb1caeSJulian Elischer 
142f2bb1caeSJulian Elischer 		ng_l2cap_discon_untimeout(con);
143f2bb1caeSJulian Elischer 	}
144f2bb1caeSJulian Elischer } /* ng_l2cap_con_ref */
145f2bb1caeSJulian Elischer 
146f2bb1caeSJulian Elischer /*
147f2bb1caeSJulian Elischer  * Remove reference from the connection descriptor
148f2bb1caeSJulian Elischer  */
149f2bb1caeSJulian Elischer 
150f2bb1caeSJulian Elischer void
151f2bb1caeSJulian Elischer ng_l2cap_con_unref(ng_l2cap_con_p con)
152f2bb1caeSJulian Elischer {
153f2bb1caeSJulian Elischer 	con->refcnt --;
154f2bb1caeSJulian Elischer 
155f2bb1caeSJulian Elischer 	if (con->refcnt < 0)
156f2bb1caeSJulian Elischer 		panic("%s: %s - con->refcnt < 0\n",
157f2bb1caeSJulian Elischer 			__func__, NG_NODE_NAME(con->l2cap->node));
158f2bb1caeSJulian Elischer 
159f2bb1caeSJulian Elischer 	/*
160f2bb1caeSJulian Elischer 	 * Set auto disconnect timer only if the following conditions are met:
161f2bb1caeSJulian Elischer 	 * 1) we have no reference on the connection
162f2bb1caeSJulian Elischer 	 * 2) connection is in OPEN state
163f2bb1caeSJulian Elischer 	 * 3) it is an outgoing connection
164f2bb1caeSJulian Elischer 	 * 4) disconnect timeout > 0
165f2bb1caeSJulian Elischer 	 */
166f2bb1caeSJulian Elischer 
167f2bb1caeSJulian Elischer 	if ((con->refcnt == 0) &&
168f2bb1caeSJulian Elischer 	    (con->state == NG_L2CAP_CON_OPEN) &&
169f2bb1caeSJulian Elischer 	    (con->flags & NG_L2CAP_CON_OUTGOING) &&
170f2bb1caeSJulian Elischer 	    (con->l2cap->discon_timo > 0)) {
171f2bb1caeSJulian Elischer 		if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)
172f2bb1caeSJulian Elischer 			panic("%s: %s - duplicated auto disconnect timeout\n",
173f2bb1caeSJulian Elischer 				__func__, NG_NODE_NAME(con->l2cap->node));
174f2bb1caeSJulian Elischer 
175f2bb1caeSJulian Elischer 		ng_l2cap_discon_timeout(con);
176f2bb1caeSJulian Elischer 	}
177f2bb1caeSJulian Elischer } /* ng_l2cap_con_unref */
178f2bb1caeSJulian Elischer 
179f2bb1caeSJulian Elischer /*
180f2bb1caeSJulian Elischer  * Set auto disconnect timeout
181f2bb1caeSJulian Elischer  */
182f2bb1caeSJulian Elischer 
183f2bb1caeSJulian Elischer void
184f2bb1caeSJulian Elischer ng_l2cap_discon_timeout(ng_l2cap_con_p con)
185f2bb1caeSJulian Elischer {
186f2bb1caeSJulian Elischer 	if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO))
187f2bb1caeSJulian Elischer 		panic("%s: %s - invalid timeout, state=%d, flags=%#x\n",
188f2bb1caeSJulian Elischer 			__func__, NG_NODE_NAME(con->l2cap->node),
189f2bb1caeSJulian Elischer 			con->state, con->flags);
190f2bb1caeSJulian Elischer 
191f2bb1caeSJulian Elischer 	NG_NODE_REF(con->l2cap->node);
192f2bb1caeSJulian Elischer 	con->flags |= NG_L2CAP_CON_AUTO_DISCON_TIMO;
193f2bb1caeSJulian Elischer 	con->con_timo = timeout(ng_l2cap_queue_discon_timeout, con,
194f2bb1caeSJulian Elischer 					con->l2cap->discon_timo * hz);
195f2bb1caeSJulian Elischer } /* ng_l2cap_discon_timeout */
196f2bb1caeSJulian Elischer 
197f2bb1caeSJulian Elischer /*
198f2bb1caeSJulian Elischer  * Unset auto disconnect timeout
199f2bb1caeSJulian Elischer  */
200f2bb1caeSJulian Elischer 
201f2bb1caeSJulian Elischer void
202f2bb1caeSJulian Elischer ng_l2cap_discon_untimeout(ng_l2cap_con_p con)
203f2bb1caeSJulian Elischer {
204f2bb1caeSJulian Elischer 	untimeout(ng_l2cap_queue_discon_timeout, con, con->con_timo);
205f2bb1caeSJulian Elischer 	con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO;
206f2bb1caeSJulian Elischer 	NG_NODE_UNREF(con->l2cap->node);
207f2bb1caeSJulian Elischer } /* ng_l2cap_discon_untimeout */
208f2bb1caeSJulian Elischer 
209f2bb1caeSJulian Elischer /*
210f2bb1caeSJulian Elischer  *  Queue auto disconnect timeout
211f2bb1caeSJulian Elischer  */
212f2bb1caeSJulian Elischer 
213f2bb1caeSJulian Elischer static void
214f2bb1caeSJulian Elischer ng_l2cap_queue_discon_timeout(void *context)
215f2bb1caeSJulian Elischer {
216f2bb1caeSJulian Elischer 	ng_l2cap_con_p	con = (ng_l2cap_con_p) context;
217f2bb1caeSJulian Elischer 	node_p		node = con->l2cap->node;
218f2bb1caeSJulian Elischer 
219f2bb1caeSJulian Elischer 	if (NG_NODE_IS_VALID(node))
220f2bb1caeSJulian Elischer 		ng_send_fn(node,NULL,&ng_l2cap_process_discon_timeout,con,0);
221f2bb1caeSJulian Elischer 
222f2bb1caeSJulian Elischer 	NG_NODE_UNREF(node);
223f2bb1caeSJulian Elischer } /* ng_l2cap_queue_discon_timeout */
224f2bb1caeSJulian Elischer 
225f2bb1caeSJulian Elischer /*
226878ed226SJulian Elischer  * Free connection descriptor. Will unlink connection and free everything.
227878ed226SJulian Elischer  */
228878ed226SJulian Elischer 
229878ed226SJulian Elischer void
230878ed226SJulian Elischer ng_l2cap_free_con(ng_l2cap_con_p con)
231878ed226SJulian Elischer {
232878ed226SJulian Elischer 	ng_l2cap_chan_p f = NULL, n = NULL;
233878ed226SJulian Elischer 
234f2bb1caeSJulian Elischer 	if (con->flags & NG_L2CAP_CON_LP_TIMO)
235878ed226SJulian Elischer 		ng_l2cap_lp_untimeout(con);
236f2bb1caeSJulian Elischer 	else if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)
237f2bb1caeSJulian Elischer 		ng_l2cap_discon_untimeout(con);
238f2bb1caeSJulian Elischer 
239f2bb1caeSJulian Elischer 	con->state = NG_L2CAP_CON_CLOSED;
240878ed226SJulian Elischer 
241878ed226SJulian Elischer 	if (con->tx_pkt != NULL) {
242878ed226SJulian Elischer 		while (con->tx_pkt != NULL) {
243878ed226SJulian Elischer 			struct mbuf	*m = con->tx_pkt->m_nextpkt;
244878ed226SJulian Elischer 
245878ed226SJulian Elischer 			m_freem(con->tx_pkt);
246878ed226SJulian Elischer 			con->tx_pkt = m;
247878ed226SJulian Elischer 		}
248878ed226SJulian Elischer 	}
249878ed226SJulian Elischer 
250878ed226SJulian Elischer 	NG_FREE_M(con->rx_pkt);
251878ed226SJulian Elischer 
252878ed226SJulian Elischer 	for (f = LIST_FIRST(&con->l2cap->chan_list); f != NULL; ) {
253878ed226SJulian Elischer 		n = LIST_NEXT(f, next);
254878ed226SJulian Elischer 
255878ed226SJulian Elischer 		if (f->con == con)
256878ed226SJulian Elischer 			ng_l2cap_free_chan(f);
257878ed226SJulian Elischer 
258878ed226SJulian Elischer 		f = n;
259878ed226SJulian Elischer 	}
260878ed226SJulian Elischer 
261878ed226SJulian Elischer 	while (!TAILQ_EMPTY(&con->cmd_list)) {
262878ed226SJulian Elischer 		ng_l2cap_cmd_p	cmd = TAILQ_FIRST(&con->cmd_list);
263878ed226SJulian Elischer 
264878ed226SJulian Elischer 		ng_l2cap_unlink_cmd(cmd);
265878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
266878ed226SJulian Elischer 	}
267878ed226SJulian Elischer 
268878ed226SJulian Elischer 	LIST_REMOVE(con, next);
269878ed226SJulian Elischer 	bzero(con, sizeof(*con));
270878ed226SJulian Elischer 	FREE(con, M_NETGRAPH_L2CAP);
271878ed226SJulian Elischer } /* ng_l2cap_free_con */
272878ed226SJulian Elischer 
273878ed226SJulian Elischer /*
274878ed226SJulian Elischer  * Get connection by "remote" address
275878ed226SJulian Elischer  */
276878ed226SJulian Elischer 
277878ed226SJulian Elischer ng_l2cap_con_p
278878ed226SJulian Elischer ng_l2cap_con_by_addr(ng_l2cap_p l2cap, bdaddr_p bdaddr)
279878ed226SJulian Elischer {
280878ed226SJulian Elischer 	ng_l2cap_con_p	con = NULL;
281878ed226SJulian Elischer 
282878ed226SJulian Elischer 	LIST_FOREACH(con, &l2cap->con_list, next)
283878ed226SJulian Elischer 		if (bcmp(bdaddr, &con->remote, sizeof(con->remote)) == 0)
284878ed226SJulian Elischer 			break;
285878ed226SJulian Elischer 
286878ed226SJulian Elischer 	return (con);
287878ed226SJulian Elischer } /* ng_l2cap_con_by_addr */
288878ed226SJulian Elischer 
289878ed226SJulian Elischer /*
290878ed226SJulian Elischer  * Get connection by "handle"
291878ed226SJulian Elischer  */
292878ed226SJulian Elischer 
293878ed226SJulian Elischer ng_l2cap_con_p
294878ed226SJulian Elischer ng_l2cap_con_by_handle(ng_l2cap_p l2cap, u_int16_t con_handle)
295878ed226SJulian Elischer {
296878ed226SJulian Elischer 	ng_l2cap_con_p	con = NULL;
297878ed226SJulian Elischer 
298878ed226SJulian Elischer 	LIST_FOREACH(con, &l2cap->con_list, next)
299878ed226SJulian Elischer 		if (con->con_handle == con_handle)
300878ed226SJulian Elischer 			break;
301878ed226SJulian Elischer 
302878ed226SJulian Elischer 	return (con);
303878ed226SJulian Elischer } /* ng_l2cap_con_by_handle */
304878ed226SJulian Elischer 
305878ed226SJulian Elischer /*
306878ed226SJulian Elischer  * Allocate new L2CAP channel descriptor on "con" conection with "psm".
307878ed226SJulian Elischer  * Will link the channel to the l2cap node
308878ed226SJulian Elischer  */
309878ed226SJulian Elischer 
310878ed226SJulian Elischer ng_l2cap_chan_p
311878ed226SJulian Elischer ng_l2cap_new_chan(ng_l2cap_p l2cap, ng_l2cap_con_p con, u_int16_t psm)
312878ed226SJulian Elischer {
313878ed226SJulian Elischer 	ng_l2cap_chan_p	ch = NULL;
314878ed226SJulian Elischer 
315878ed226SJulian Elischer 	MALLOC(ch, ng_l2cap_chan_p, sizeof(*ch), M_NETGRAPH_L2CAP,
316878ed226SJulian Elischer 		M_NOWAIT|M_ZERO);
317878ed226SJulian Elischer 	if (ch == NULL)
318878ed226SJulian Elischer 		return (NULL);
319878ed226SJulian Elischer 
320878ed226SJulian Elischer 	ch->scid = ng_l2cap_get_cid(l2cap);
321878ed226SJulian Elischer 
322878ed226SJulian Elischer 	if (ch->scid != NG_L2CAP_NULL_CID) {
323878ed226SJulian Elischer 		/* Initialize channel */
324878ed226SJulian Elischer 		ch->psm = psm;
325878ed226SJulian Elischer 		ch->con = con;
326878ed226SJulian Elischer 		ch->state = NG_L2CAP_CLOSED;
327878ed226SJulian Elischer 
328878ed226SJulian Elischer 		/* Set MTU and flow control settings to defaults */
329878ed226SJulian Elischer 		ch->imtu = NG_L2CAP_MTU_DEFAULT;
330878ed226SJulian Elischer 		bcopy(ng_l2cap_default_flow(), &ch->iflow, sizeof(ch->iflow));
331878ed226SJulian Elischer 
332878ed226SJulian Elischer 		ch->omtu = NG_L2CAP_MTU_DEFAULT;
333878ed226SJulian Elischer 		bcopy(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow));
334878ed226SJulian Elischer 
335878ed226SJulian Elischer 		ch->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT;
336878ed226SJulian Elischer 		ch->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT;
337878ed226SJulian Elischer 
338878ed226SJulian Elischer 		LIST_INSERT_HEAD(&l2cap->chan_list, ch, next);
339f2bb1caeSJulian Elischer 
340f2bb1caeSJulian Elischer 		ng_l2cap_con_ref(con);
341878ed226SJulian Elischer 	} else {
342878ed226SJulian Elischer 		bzero(ch, sizeof(*ch));
343878ed226SJulian Elischer 		FREE(ch, M_NETGRAPH_L2CAP);
344878ed226SJulian Elischer 		ch = NULL;
345878ed226SJulian Elischer 	}
346878ed226SJulian Elischer 
347878ed226SJulian Elischer 	return (ch);
348878ed226SJulian Elischer } /* ng_l2cap_new_chan */
349878ed226SJulian Elischer 
350878ed226SJulian Elischer /*
351878ed226SJulian Elischer  * Get channel by source (local) channel ID
352878ed226SJulian Elischer  */
353878ed226SJulian Elischer 
354878ed226SJulian Elischer ng_l2cap_chan_p
355878ed226SJulian Elischer ng_l2cap_chan_by_scid(ng_l2cap_p l2cap, u_int16_t scid)
356878ed226SJulian Elischer {
357878ed226SJulian Elischer 	ng_l2cap_chan_p	ch = NULL;
358878ed226SJulian Elischer 
359878ed226SJulian Elischer 	LIST_FOREACH(ch, &l2cap->chan_list, next)
360878ed226SJulian Elischer 		if (ch->scid == scid)
361878ed226SJulian Elischer 			break;
362878ed226SJulian Elischer 
363878ed226SJulian Elischer 	return (ch);
364878ed226SJulian Elischer } /* ng_l2cap_chan_by_scid */
365878ed226SJulian Elischer 
366878ed226SJulian Elischer /*
367878ed226SJulian Elischer  * Free channel descriptor.
368878ed226SJulian Elischer  */
369878ed226SJulian Elischer 
370878ed226SJulian Elischer void
371878ed226SJulian Elischer ng_l2cap_free_chan(ng_l2cap_chan_p ch)
372878ed226SJulian Elischer {
373878ed226SJulian Elischer 	ng_l2cap_cmd_p	f = NULL, n = NULL;
374878ed226SJulian Elischer 
375878ed226SJulian Elischer 	f = TAILQ_FIRST(&ch->con->cmd_list);
376878ed226SJulian Elischer 	while (f != NULL) {
377878ed226SJulian Elischer 		n = TAILQ_NEXT(f, next);
378878ed226SJulian Elischer 
379878ed226SJulian Elischer 		if (f->ch == ch) {
380878ed226SJulian Elischer 			ng_l2cap_unlink_cmd(f);
381878ed226SJulian Elischer 			ng_l2cap_free_cmd(f);
382878ed226SJulian Elischer 		}
383878ed226SJulian Elischer 
384878ed226SJulian Elischer 		f = n;
385878ed226SJulian Elischer 	}
386878ed226SJulian Elischer 
387878ed226SJulian Elischer 	LIST_REMOVE(ch, next);
388f2bb1caeSJulian Elischer 
389f2bb1caeSJulian Elischer 	ng_l2cap_con_unref(ch->con);
390f2bb1caeSJulian Elischer 
391878ed226SJulian Elischer 	bzero(ch, sizeof(*ch));
392878ed226SJulian Elischer 	FREE(ch, M_NETGRAPH_L2CAP);
393878ed226SJulian Elischer } /* ng_l2cap_free_chan */
394878ed226SJulian Elischer 
395878ed226SJulian Elischer /*
396878ed226SJulian Elischer  * Create new L2CAP command descriptor. WILL NOT add command to the queue.
397878ed226SJulian Elischer  */
398878ed226SJulian Elischer 
399878ed226SJulian Elischer ng_l2cap_cmd_p
400878ed226SJulian Elischer ng_l2cap_new_cmd(ng_l2cap_con_p con, ng_l2cap_chan_p ch, u_int8_t ident,
401878ed226SJulian Elischer 		u_int8_t code, u_int32_t token)
402878ed226SJulian Elischer {
403878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = NULL;
404878ed226SJulian Elischer 
405878ed226SJulian Elischer 	KASSERT((ch == NULL || ch->con == con),
406878ed226SJulian Elischer ("%s: %s - invalid channel pointer!\n",
407878ed226SJulian Elischer 		__func__, NG_NODE_NAME(con->l2cap->node)));
408878ed226SJulian Elischer 
409878ed226SJulian Elischer 	MALLOC(cmd, ng_l2cap_cmd_p, sizeof(*cmd), M_NETGRAPH_L2CAP,
410878ed226SJulian Elischer 		M_NOWAIT|M_ZERO);
411878ed226SJulian Elischer 	if (cmd == NULL)
412878ed226SJulian Elischer 		return (NULL);
413878ed226SJulian Elischer 
414878ed226SJulian Elischer 	cmd->con = con;
415878ed226SJulian Elischer 	cmd->ch = ch;
416878ed226SJulian Elischer 	cmd->ident = ident;
417878ed226SJulian Elischer 	cmd->code = code;
418878ed226SJulian Elischer 	cmd->token = token;
419878ed226SJulian Elischer 	callout_handle_init(&cmd->timo);
420878ed226SJulian Elischer 
421878ed226SJulian Elischer 	return (cmd);
422878ed226SJulian Elischer } /* ng_l2cap_new_cmd */
423878ed226SJulian Elischer 
424878ed226SJulian Elischer /*
425878ed226SJulian Elischer  * Get L2CAP command descriptor by ident
426878ed226SJulian Elischer  */
427878ed226SJulian Elischer 
428878ed226SJulian Elischer ng_l2cap_cmd_p
429878ed226SJulian Elischer ng_l2cap_cmd_by_ident(ng_l2cap_con_p con, u_int8_t ident)
430878ed226SJulian Elischer {
431878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = NULL;
432878ed226SJulian Elischer 
433878ed226SJulian Elischer 	TAILQ_FOREACH(cmd, &con->cmd_list, next)
434878ed226SJulian Elischer 		if (cmd->ident == ident)
435878ed226SJulian Elischer 			break;
436878ed226SJulian Elischer 
437878ed226SJulian Elischer 	return (cmd);
438878ed226SJulian Elischer } /* ng_l2cap_cmd_by_ident */
439878ed226SJulian Elischer 
440878ed226SJulian Elischer /*
441878ed226SJulian Elischer  * Set LP timeout
442878ed226SJulian Elischer  */
443878ed226SJulian Elischer 
444878ed226SJulian Elischer void
445878ed226SJulian Elischer ng_l2cap_lp_timeout(ng_l2cap_con_p con)
446878ed226SJulian Elischer {
447f2bb1caeSJulian Elischer 	if (con->flags & (NG_L2CAP_CON_LP_TIMO|NG_L2CAP_CON_AUTO_DISCON_TIMO))
448f2bb1caeSJulian Elischer 		panic("%s: %s - invalid timeout, state=%d, flags=%#x\n",
449f2bb1caeSJulian Elischer 			__func__, NG_NODE_NAME(con->l2cap->node),
450f2bb1caeSJulian Elischer 			con->state, con->flags);
451f2bb1caeSJulian Elischer 
452878ed226SJulian Elischer 	NG_NODE_REF(con->l2cap->node);
453f2bb1caeSJulian Elischer 	con->flags |= NG_L2CAP_CON_LP_TIMO;
454878ed226SJulian Elischer 	con->con_timo = timeout(ng_l2cap_queue_lp_timeout, con,
455878ed226SJulian Elischer 				bluetooth_hci_connect_timeout());
456878ed226SJulian Elischer } /* ng_l2cap_lp_timeout */
457878ed226SJulian Elischer 
458878ed226SJulian Elischer /*
459878ed226SJulian Elischer  * Unset LP timeout
460878ed226SJulian Elischer  */
461878ed226SJulian Elischer 
462878ed226SJulian Elischer void
463878ed226SJulian Elischer ng_l2cap_lp_untimeout(ng_l2cap_con_p con)
464878ed226SJulian Elischer {
465878ed226SJulian Elischer 	untimeout(ng_l2cap_queue_lp_timeout, con, con->con_timo);
466f2bb1caeSJulian Elischer 	con->flags &= ~NG_L2CAP_CON_LP_TIMO;
467878ed226SJulian Elischer 	NG_NODE_UNREF(con->l2cap->node);
468878ed226SJulian Elischer } /* ng_l2cap_lp_untimeout */
469878ed226SJulian Elischer 
470878ed226SJulian Elischer /*
471878ed226SJulian Elischer  * OK, timeout has happend so queue LP timeout processing function
472878ed226SJulian Elischer  */
473878ed226SJulian Elischer 
474878ed226SJulian Elischer static void
475878ed226SJulian Elischer ng_l2cap_queue_lp_timeout(void *context)
476878ed226SJulian Elischer {
477878ed226SJulian Elischer 	ng_l2cap_con_p	con = (ng_l2cap_con_p) context;
478878ed226SJulian Elischer 	node_p		node = con->l2cap->node;
479878ed226SJulian Elischer 
480878ed226SJulian Elischer 	/*
481878ed226SJulian Elischer 	 * We need to save node pointer here, because ng_send_fn()
482878ed226SJulian Elischer 	 * can execute ng_l2cap_process_lp_timeout() without putting
483878ed226SJulian Elischer 	 * item into node's queue (if node can be locked). Once
484878ed226SJulian Elischer 	 * ng_l2cap_process_lp_timeout() executed the con pointer
485878ed226SJulian Elischer 	 * is no longer valid.
486878ed226SJulian Elischer 	 */
487878ed226SJulian Elischer 
488878ed226SJulian Elischer 	if (NG_NODE_IS_VALID(node))
489878ed226SJulian Elischer 		ng_send_fn(node, NULL, &ng_l2cap_process_lp_timeout, con, 0);
490878ed226SJulian Elischer 
491878ed226SJulian Elischer 	NG_NODE_UNREF(node);
492878ed226SJulian Elischer } /* ng_l2cap_queue_lp_timeout */
493878ed226SJulian Elischer 
494878ed226SJulian Elischer /*
495878ed226SJulian Elischer  * Set L2CAP command timeout
496878ed226SJulian Elischer  */
497878ed226SJulian Elischer 
498878ed226SJulian Elischer void
499878ed226SJulian Elischer ng_l2cap_command_timeout(ng_l2cap_cmd_p cmd, int timo)
500878ed226SJulian Elischer {
501878ed226SJulian Elischer 	NG_NODE_REF(cmd->con->l2cap->node);
502878ed226SJulian Elischer 	cmd->flags |= NG_L2CAP_CMD_PENDING;
503878ed226SJulian Elischer 	cmd->timo = timeout(ng_l2cap_queue_command_timeout, cmd, timo);
504878ed226SJulian Elischer } /* ng_l2cap_command_timeout */
505878ed226SJulian Elischer 
506878ed226SJulian Elischer /*
507878ed226SJulian Elischer  * Unset L2CAP command timeout
508878ed226SJulian Elischer  */
509878ed226SJulian Elischer 
510878ed226SJulian Elischer void
511878ed226SJulian Elischer ng_l2cap_command_untimeout(ng_l2cap_cmd_p cmd)
512878ed226SJulian Elischer {
513878ed226SJulian Elischer 	cmd->flags &= ~NG_L2CAP_CMD_PENDING;
514878ed226SJulian Elischer 	untimeout(ng_l2cap_queue_command_timeout, cmd, cmd->timo);
515878ed226SJulian Elischer 	NG_NODE_UNREF(cmd->con->l2cap->node);
516878ed226SJulian Elischer } /* ng_l2cap_command_untimeout */
517878ed226SJulian Elischer 
518878ed226SJulian Elischer /*
519878ed226SJulian Elischer  * OK, timeout has happend so queue L2CAP command timeout processing function
520878ed226SJulian Elischer  */
521878ed226SJulian Elischer 
522878ed226SJulian Elischer static void
523878ed226SJulian Elischer ng_l2cap_queue_command_timeout(void *context)
524878ed226SJulian Elischer {
525878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = (ng_l2cap_cmd_p) context;
526878ed226SJulian Elischer 	node_p		node = cmd->con->l2cap->node;
527878ed226SJulian Elischer 
528878ed226SJulian Elischer 	/*
529878ed226SJulian Elischer 	 * We need to save node pointer here, because ng_send_fn()
530878ed226SJulian Elischer 	 * can execute ng_l2cap_process_command_timeout() without
531878ed226SJulian Elischer 	 * putting item into node's queue (if node can be locked).
532878ed226SJulian Elischer 	 * Once ng_l2cap_process_command_timeout() executed the
533878ed226SJulian Elischer 	 * cmd pointer is no longer valid.
534878ed226SJulian Elischer 	 */
535878ed226SJulian Elischer 
536878ed226SJulian Elischer 	if (NG_NODE_IS_VALID(node))
537878ed226SJulian Elischer 		ng_send_fn(node,NULL,&ng_l2cap_process_command_timeout,cmd,0);
538878ed226SJulian Elischer 
539878ed226SJulian Elischer 	NG_NODE_UNREF(node);
540878ed226SJulian Elischer } /* ng_l2cap_queue_command_timeout */
541878ed226SJulian Elischer 
542878ed226SJulian Elischer /*
543878ed226SJulian Elischer  * Prepend "m"buf with "size" bytes
544878ed226SJulian Elischer  */
545878ed226SJulian Elischer 
546878ed226SJulian Elischer struct mbuf *
547878ed226SJulian Elischer ng_l2cap_prepend(struct mbuf *m, int size)
548878ed226SJulian Elischer {
549a163d034SWarner Losh 	M_PREPEND(m, size, M_DONTWAIT);
550878ed226SJulian Elischer 	if (m == NULL || (m->m_len < size && (m = m_pullup(m, size)) == NULL))
551878ed226SJulian Elischer 		return (NULL);
552878ed226SJulian Elischer 
553878ed226SJulian Elischer 	return (m);
554878ed226SJulian Elischer } /* ng_l2cap_prepend */
555878ed226SJulian Elischer 
556878ed226SJulian Elischer /*
557878ed226SJulian Elischer  * Default flow settings
558878ed226SJulian Elischer  */
559878ed226SJulian Elischer 
560878ed226SJulian Elischer ng_l2cap_flow_p
561878ed226SJulian Elischer ng_l2cap_default_flow(void)
562878ed226SJulian Elischer {
563878ed226SJulian Elischer 	static ng_l2cap_flow_t	default_flow = {
564878ed226SJulian Elischer 		/* flags */		0x0,
565878ed226SJulian Elischer 		/* service_type */	NG_HCI_SERVICE_TYPE_BEST_EFFORT,
566878ed226SJulian Elischer 		/* token_rate */	0xffffffff, /* maximum */
567878ed226SJulian Elischer 		/* token_bucket_size */	0xffffffff, /* maximum */
568878ed226SJulian Elischer 		/* peak_bandwidth */	0x00000000, /* maximum */
569878ed226SJulian Elischer 		/* latency */		0xffffffff, /* don't care */
570878ed226SJulian Elischer 		/* delay_variation */	0xffffffff  /* don't care */
571878ed226SJulian Elischer 	};
572878ed226SJulian Elischer 
573878ed226SJulian Elischer 	return (&default_flow);
574878ed226SJulian Elischer } /* ng_l2cap_default_flow */
575878ed226SJulian Elischer 
576878ed226SJulian Elischer /*
577878ed226SJulian Elischer  * Get next available channel ID
578878ed226SJulian Elischer  * XXX FIXME this is *UGLY* but will do for now
579878ed226SJulian Elischer  */
580878ed226SJulian Elischer 
581878ed226SJulian Elischer static u_int16_t
582878ed226SJulian Elischer ng_l2cap_get_cid(ng_l2cap_p l2cap)
583878ed226SJulian Elischer {
584878ed226SJulian Elischer 	u_int16_t	cid = l2cap->cid + 1;
585878ed226SJulian Elischer 
586878ed226SJulian Elischer 	if (cid < NG_L2CAP_FIRST_CID)
587878ed226SJulian Elischer 		cid = NG_L2CAP_FIRST_CID;
588878ed226SJulian Elischer 
589878ed226SJulian Elischer 	while (cid != l2cap->cid) {
590878ed226SJulian Elischer 		if (ng_l2cap_chan_by_scid(l2cap, cid) == NULL) {
591878ed226SJulian Elischer 			l2cap->cid = cid;
592878ed226SJulian Elischer 
593878ed226SJulian Elischer 			return (cid);
594878ed226SJulian Elischer 		}
595878ed226SJulian Elischer 
596878ed226SJulian Elischer 		cid ++;
597878ed226SJulian Elischer 		if (cid < NG_L2CAP_FIRST_CID)
598878ed226SJulian Elischer 			cid = NG_L2CAP_FIRST_CID;
599878ed226SJulian Elischer 	}
600878ed226SJulian Elischer 
601878ed226SJulian Elischer 	return (NG_L2CAP_NULL_CID);
602878ed226SJulian Elischer } /* ng_l2cap_get_cid */
603878ed226SJulian Elischer 
604878ed226SJulian Elischer /*
605878ed226SJulian Elischer  * Get next available command ident
606878ed226SJulian Elischer  * XXX FIXME this is *UGLY* but will do for now
607878ed226SJulian Elischer  */
608878ed226SJulian Elischer 
609878ed226SJulian Elischer u_int8_t
610878ed226SJulian Elischer ng_l2cap_get_ident(ng_l2cap_con_p con)
611878ed226SJulian Elischer {
612878ed226SJulian Elischer 	u_int8_t	ident = con->ident + 1;
613878ed226SJulian Elischer 
614878ed226SJulian Elischer 	if (ident < NG_L2CAP_FIRST_IDENT)
615878ed226SJulian Elischer 		ident = NG_L2CAP_FIRST_IDENT;
616878ed226SJulian Elischer 
617878ed226SJulian Elischer 	while (ident != con->ident) {
618878ed226SJulian Elischer 		if (ng_l2cap_cmd_by_ident(con, ident) == NULL) {
619878ed226SJulian Elischer 			con->ident = ident;
620878ed226SJulian Elischer 
621878ed226SJulian Elischer 			return (ident);
622878ed226SJulian Elischer 		}
623878ed226SJulian Elischer 
624878ed226SJulian Elischer 		ident ++;
625878ed226SJulian Elischer 		if (ident < NG_L2CAP_FIRST_IDENT)
626878ed226SJulian Elischer 			ident = NG_L2CAP_FIRST_IDENT;
627878ed226SJulian Elischer 	}
628878ed226SJulian Elischer 
629878ed226SJulian Elischer 	return (NG_L2CAP_NULL_IDENT);
630878ed226SJulian Elischer } /* ng_l2cap_get_ident */
631878ed226SJulian Elischer 
632