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