xref: /dflybsd-src/sys/netgraph7/bluetooth/l2cap/ng_l2cap_llpi.c (revision 5a975a3dcb66c383a644f58dd3a9a43c6ba43ac9)
1b06ebda0SMatthew Dillon /*
2b06ebda0SMatthew Dillon  * ng_l2cap_llpi.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_llpi.c,v 1.5 2003/09/08 19:11:45 max Exp $
31b06ebda0SMatthew Dillon  * $FreeBSD: src/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.c,v 1.9 2005/07/29 14:44:17 emax Exp $
32*5a975a3dSMatthew Dillon  * $DragonFly: src/sys/netgraph7/bluetooth/l2cap/ng_l2cap_llpi.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/endian.h>
39b06ebda0SMatthew Dillon #include <sys/malloc.h>
40b06ebda0SMatthew Dillon #include <sys/mbuf.h>
41b06ebda0SMatthew Dillon #include <sys/queue.h>
42*5a975a3dSMatthew Dillon #include "ng_message.h"
43*5a975a3dSMatthew Dillon #include "netgraph.h"
44*5a975a3dSMatthew Dillon #include "bluetooth/include/ng_bluetooth.h"
45*5a975a3dSMatthew Dillon #include "bluetooth/include/ng_hci.h"
46*5a975a3dSMatthew Dillon #include "bluetooth/include/ng_l2cap.h"
47*5a975a3dSMatthew Dillon #include "bluetooth/l2cap/ng_l2cap_var.h"
48*5a975a3dSMatthew Dillon #include "bluetooth/l2cap/ng_l2cap_cmds.h"
49*5a975a3dSMatthew Dillon #include "bluetooth/l2cap/ng_l2cap_evnt.h"
50*5a975a3dSMatthew Dillon #include "bluetooth/l2cap/ng_l2cap_llpi.h"
51*5a975a3dSMatthew Dillon #include "bluetooth/l2cap/ng_l2cap_ulpi.h"
52*5a975a3dSMatthew Dillon #include "bluetooth/l2cap/ng_l2cap_misc.h"
53b06ebda0SMatthew Dillon 
54b06ebda0SMatthew Dillon /******************************************************************************
55b06ebda0SMatthew Dillon  ******************************************************************************
56b06ebda0SMatthew Dillon  **                 Lower Layer Protocol (HCI) Interface module
57b06ebda0SMatthew Dillon  ******************************************************************************
58b06ebda0SMatthew Dillon  ******************************************************************************/
59b06ebda0SMatthew Dillon 
60b06ebda0SMatthew Dillon /*
61b06ebda0SMatthew Dillon  * Send LP_ConnectReq event to the lower layer protocol. Create new connection
62b06ebda0SMatthew Dillon  * descriptor and initialize it. Create LP_ConnectReq event and send it to the
63b06ebda0SMatthew Dillon  * lower layer, then adjust connection state and start timer. The function WILL
64b06ebda0SMatthew Dillon  * FAIL if connection to the remote unit already exists.
65b06ebda0SMatthew Dillon  */
66b06ebda0SMatthew Dillon 
67b06ebda0SMatthew Dillon int
68b06ebda0SMatthew Dillon ng_l2cap_lp_con_req(ng_l2cap_p l2cap, bdaddr_p bdaddr)
69b06ebda0SMatthew Dillon {
70b06ebda0SMatthew Dillon 	struct ng_mesg		*msg = NULL;
71b06ebda0SMatthew Dillon 	ng_hci_lp_con_req_ep	*ep = NULL;
72b06ebda0SMatthew Dillon 	ng_l2cap_con_p		 con = NULL;
73b06ebda0SMatthew Dillon 	int			 error = 0;
74b06ebda0SMatthew Dillon 
75b06ebda0SMatthew Dillon 	/* Verify that we DO NOT have connection to the remote unit */
76b06ebda0SMatthew Dillon 	con = ng_l2cap_con_by_addr(l2cap, bdaddr);
77b06ebda0SMatthew Dillon 	if (con != NULL) {
78b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
79b06ebda0SMatthew Dillon "%s: %s - unexpected LP_ConnectReq event. " \
80b06ebda0SMatthew Dillon "Connection already exists, state=%d, con_handle=%d\n",
81b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), con->state,
82b06ebda0SMatthew Dillon 			con->con_handle);
83b06ebda0SMatthew Dillon 
84b06ebda0SMatthew Dillon 		return (EEXIST);
85b06ebda0SMatthew Dillon 	}
86b06ebda0SMatthew Dillon 
87b06ebda0SMatthew Dillon 	/* Check if lower layer protocol is still connected */
88b06ebda0SMatthew Dillon 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
89b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
90b06ebda0SMatthew Dillon "%s: %s - hook \"%s\" is not connected or valid\n",
91b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
92b06ebda0SMatthew Dillon 
93b06ebda0SMatthew Dillon 		return (ENOTCONN);
94b06ebda0SMatthew Dillon 	}
95b06ebda0SMatthew Dillon 
96b06ebda0SMatthew Dillon 	/* Create and intialize new connection descriptor */
97b06ebda0SMatthew Dillon 	con = ng_l2cap_new_con(l2cap, bdaddr);
98b06ebda0SMatthew Dillon 	if (con == NULL)
99b06ebda0SMatthew Dillon 		return (ENOMEM);
100b06ebda0SMatthew Dillon 
101b06ebda0SMatthew Dillon 	/* Create and send LP_ConnectReq event */
102b06ebda0SMatthew Dillon 	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_REQ,
103*5a975a3dSMatthew Dillon 		sizeof(*ep), M_WAITOK | M_NULLOK);
104b06ebda0SMatthew Dillon 	if (msg == NULL) {
105b06ebda0SMatthew Dillon 		ng_l2cap_free_con(con);
106b06ebda0SMatthew Dillon 
107b06ebda0SMatthew Dillon 		return (ENOMEM);
108b06ebda0SMatthew Dillon 	}
109b06ebda0SMatthew Dillon 
110b06ebda0SMatthew Dillon 	ep = (ng_hci_lp_con_req_ep *) (msg->data);
111b06ebda0SMatthew Dillon 	bcopy(bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
112b06ebda0SMatthew Dillon 	ep->link_type = NG_HCI_LINK_ACL;
113b06ebda0SMatthew Dillon 
114b06ebda0SMatthew Dillon 	con->flags |= NG_L2CAP_CON_OUTGOING;
115b06ebda0SMatthew Dillon 	con->state = NG_L2CAP_W4_LP_CON_CFM;
116b06ebda0SMatthew Dillon 	ng_l2cap_lp_timeout(con);
117b06ebda0SMatthew Dillon 
118b06ebda0SMatthew Dillon 	NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
119b06ebda0SMatthew Dillon 	if (error != 0) {
120b06ebda0SMatthew Dillon 		if ((error = ng_l2cap_lp_untimeout(con)) != 0)
121b06ebda0SMatthew Dillon 			return (error);
122b06ebda0SMatthew Dillon 
123b06ebda0SMatthew Dillon 		ng_l2cap_free_con(con);
124b06ebda0SMatthew Dillon 	}
125b06ebda0SMatthew Dillon 
126b06ebda0SMatthew Dillon 	return (error);
127b06ebda0SMatthew Dillon } /* ng_l2cap_lp_con_req */
128b06ebda0SMatthew Dillon 
129b06ebda0SMatthew Dillon /*
130b06ebda0SMatthew Dillon  * Process LP_ConnectCfm event from the lower layer protocol. It could be
131b06ebda0SMatthew Dillon  * positive or negative. Verify remote unit address then stop the timer and
132b06ebda0SMatthew Dillon  * process event.
133b06ebda0SMatthew Dillon  */
134b06ebda0SMatthew Dillon 
135b06ebda0SMatthew Dillon int
136b06ebda0SMatthew Dillon ng_l2cap_lp_con_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg)
137b06ebda0SMatthew Dillon {
138b06ebda0SMatthew Dillon 	ng_hci_lp_con_cfm_ep	*ep = NULL;
139b06ebda0SMatthew Dillon 	ng_l2cap_con_p		 con = NULL;
140b06ebda0SMatthew Dillon 	int			 error = 0;
141b06ebda0SMatthew Dillon 
142b06ebda0SMatthew Dillon 	/* Check message */
143b06ebda0SMatthew Dillon 	if (msg->header.arglen != sizeof(*ep)) {
144b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
145b06ebda0SMatthew Dillon "%s: %s - invalid LP_ConnectCfm[Neg] message size\n",
146b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node));
147b06ebda0SMatthew Dillon 		error = EMSGSIZE;
148b06ebda0SMatthew Dillon 		goto out;
149b06ebda0SMatthew Dillon 	}
150b06ebda0SMatthew Dillon 
151b06ebda0SMatthew Dillon 	ep = (ng_hci_lp_con_cfm_ep *) (msg->data);
152b06ebda0SMatthew Dillon 
153b06ebda0SMatthew Dillon 	/* Check if we have requested/accepted this connection */
154b06ebda0SMatthew Dillon 	con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr);
155b06ebda0SMatthew Dillon 	if (con == NULL) {
156b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
157b06ebda0SMatthew Dillon "%s: %s - unexpected LP_ConnectCfm event. Connection does not exist\n",
158b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node));
159b06ebda0SMatthew Dillon 		error = ENOENT;
160b06ebda0SMatthew Dillon 		goto out;
161b06ebda0SMatthew Dillon 	}
162b06ebda0SMatthew Dillon 
163b06ebda0SMatthew Dillon 	/* Check connection state */
164b06ebda0SMatthew Dillon 	if (con->state != NG_L2CAP_W4_LP_CON_CFM) {
165b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
166b06ebda0SMatthew Dillon "%s: %s - unexpected LP_ConnectCfm event. " \
167b06ebda0SMatthew Dillon "Invalid connection state, state=%d, con_handle=%d\n",
168b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), con->state,
169b06ebda0SMatthew Dillon 			con->con_handle);
170b06ebda0SMatthew Dillon 		error = EINVAL;
171b06ebda0SMatthew Dillon 		goto out;
172b06ebda0SMatthew Dillon 	}
173b06ebda0SMatthew Dillon 
174b06ebda0SMatthew Dillon 	/*
175b06ebda0SMatthew Dillon 	 * Looks like it is our confirmation. It is safe now to cancel
176b06ebda0SMatthew Dillon 	 * connection timer and notify upper layer. If timeout already
177b06ebda0SMatthew Dillon 	 * happened then ignore connection confirmation and let timeout
178b06ebda0SMatthew Dillon 	 * handle that.
179b06ebda0SMatthew Dillon  	 */
180b06ebda0SMatthew Dillon 
181b06ebda0SMatthew Dillon 	if ((error = ng_l2cap_lp_untimeout(con)) != 0)
182b06ebda0SMatthew Dillon 		goto out;
183b06ebda0SMatthew Dillon 
184b06ebda0SMatthew Dillon 	if (ep->status == 0) {
185b06ebda0SMatthew Dillon 		con->state = NG_L2CAP_CON_OPEN;
186b06ebda0SMatthew Dillon 		con->con_handle = ep->con_handle;
187b06ebda0SMatthew Dillon 		ng_l2cap_lp_deliver(con);
188b06ebda0SMatthew Dillon 	} else /* Negative confirmation - remove connection descriptor */
189b06ebda0SMatthew Dillon 		ng_l2cap_con_fail(con, ep->status);
190b06ebda0SMatthew Dillon out:
191b06ebda0SMatthew Dillon 	return (error);
192b06ebda0SMatthew Dillon } /* ng_l2cap_lp_con_cfm */
193b06ebda0SMatthew Dillon 
194b06ebda0SMatthew Dillon /*
195b06ebda0SMatthew Dillon  * Process LP_ConnectInd event from the lower layer protocol. This is a good
196b06ebda0SMatthew Dillon  * place to put some extra check on remote unit address and/or class. We could
197b06ebda0SMatthew Dillon  * even forward this information to control hook (or check against internal
198b06ebda0SMatthew Dillon  * black list) and thus implement some kind of firewall. But for now be simple
199b06ebda0SMatthew Dillon  * and create new connection descriptor, start timer and send LP_ConnectRsp
200b06ebda0SMatthew Dillon  * event (i.e. accept connection).
201b06ebda0SMatthew Dillon  */
202b06ebda0SMatthew Dillon 
203b06ebda0SMatthew Dillon int
204b06ebda0SMatthew Dillon ng_l2cap_lp_con_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
205b06ebda0SMatthew Dillon {
206b06ebda0SMatthew Dillon 	ng_hci_lp_con_ind_ep	*ep = NULL;
207b06ebda0SMatthew Dillon 	ng_hci_lp_con_rsp_ep	*rp = NULL;
208b06ebda0SMatthew Dillon 	struct ng_mesg		*rsp = NULL;
209b06ebda0SMatthew Dillon 	ng_l2cap_con_p		 con = NULL;
210b06ebda0SMatthew Dillon 	int			 error = 0;
211b06ebda0SMatthew Dillon 
212b06ebda0SMatthew Dillon 	/* Check message */
213b06ebda0SMatthew Dillon 	if (msg->header.arglen != sizeof(*ep)) {
214b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
215b06ebda0SMatthew Dillon "%s: %s - invalid LP_ConnectInd message size\n",
216b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node));
217b06ebda0SMatthew Dillon 		error = EMSGSIZE;
218b06ebda0SMatthew Dillon 		goto out;
219b06ebda0SMatthew Dillon 	}
220b06ebda0SMatthew Dillon 
221b06ebda0SMatthew Dillon  	ep = (ng_hci_lp_con_ind_ep *) (msg->data);
222b06ebda0SMatthew Dillon 
223b06ebda0SMatthew Dillon 	/* Make sure we have only one connection to the remote unit */
224b06ebda0SMatthew Dillon 	con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr);
225b06ebda0SMatthew Dillon 	if (con != NULL) {
226b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
227b06ebda0SMatthew Dillon "%s: %s - unexpected LP_ConnectInd event. " \
228b06ebda0SMatthew Dillon "Connection already exists, state=%d, con_handle=%d\n",
229b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), con->state,
230b06ebda0SMatthew Dillon 			con->con_handle);
231b06ebda0SMatthew Dillon 		error = EEXIST;
232b06ebda0SMatthew Dillon 		goto out;
233b06ebda0SMatthew Dillon 	}
234b06ebda0SMatthew Dillon 
235b06ebda0SMatthew Dillon 	/* Check if lower layer protocol is still connected */
236b06ebda0SMatthew Dillon 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
237b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
238b06ebda0SMatthew Dillon "%s: %s - hook \"%s\" is not connected or valid",
239b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
240b06ebda0SMatthew Dillon 		error = ENOTCONN;
241b06ebda0SMatthew Dillon 		goto out;
242b06ebda0SMatthew Dillon 	}
243b06ebda0SMatthew Dillon 
244b06ebda0SMatthew Dillon 	/* Create and intialize new connection descriptor */
245b06ebda0SMatthew Dillon 	con = ng_l2cap_new_con(l2cap, &ep->bdaddr);
246b06ebda0SMatthew Dillon 	if (con == NULL) {
247b06ebda0SMatthew Dillon 		error = ENOMEM;
248b06ebda0SMatthew Dillon 		goto out;
249b06ebda0SMatthew Dillon 	}
250b06ebda0SMatthew Dillon 
251b06ebda0SMatthew Dillon 	/* Create and send LP_ConnectRsp event */
252b06ebda0SMatthew Dillon 	NG_MKMESSAGE(rsp, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP,
253*5a975a3dSMatthew Dillon 		sizeof(*rp), M_WAITOK | M_NULLOK);
254b06ebda0SMatthew Dillon 	if (rsp == NULL) {
255b06ebda0SMatthew Dillon 		ng_l2cap_free_con(con);
256b06ebda0SMatthew Dillon 		error = ENOMEM;
257b06ebda0SMatthew Dillon 		goto out;
258b06ebda0SMatthew Dillon 	}
259b06ebda0SMatthew Dillon 
260b06ebda0SMatthew Dillon 	rp = (ng_hci_lp_con_rsp_ep *)(rsp->data);
261b06ebda0SMatthew Dillon 	rp->status = 0x00; /* accept connection */
262b06ebda0SMatthew Dillon 	rp->link_type = NG_HCI_LINK_ACL;
263b06ebda0SMatthew Dillon 	bcopy(&ep->bdaddr, &rp->bdaddr, sizeof(rp->bdaddr));
264b06ebda0SMatthew Dillon 
265b06ebda0SMatthew Dillon 	con->state = NG_L2CAP_W4_LP_CON_CFM;
266b06ebda0SMatthew Dillon 	ng_l2cap_lp_timeout(con);
267b06ebda0SMatthew Dillon 
268b06ebda0SMatthew Dillon 	NG_SEND_MSG_HOOK(error, l2cap->node, rsp, l2cap->hci, 0);
269b06ebda0SMatthew Dillon 	if (error != 0) {
270b06ebda0SMatthew Dillon 		if ((error = ng_l2cap_lp_untimeout(con)) != 0)
271b06ebda0SMatthew Dillon 			goto out;
272b06ebda0SMatthew Dillon 
273b06ebda0SMatthew Dillon 		ng_l2cap_free_con(con);
274b06ebda0SMatthew Dillon 	}
275b06ebda0SMatthew Dillon out:
276b06ebda0SMatthew Dillon 	return (error);
277b06ebda0SMatthew Dillon } /* ng_hci_lp_con_ind */
278b06ebda0SMatthew Dillon 
279b06ebda0SMatthew Dillon /*
280b06ebda0SMatthew Dillon  * Process LP_DisconnectInd event from the lower layer protocol. We have been
281b06ebda0SMatthew Dillon  * disconnected from the remote unit. So notify the upper layer protocol.
282b06ebda0SMatthew Dillon  */
283b06ebda0SMatthew Dillon 
284b06ebda0SMatthew Dillon int
285b06ebda0SMatthew Dillon ng_l2cap_lp_discon_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
286b06ebda0SMatthew Dillon {
287b06ebda0SMatthew Dillon 	ng_hci_lp_discon_ind_ep	*ep = NULL;
288b06ebda0SMatthew Dillon 	ng_l2cap_con_p		 con = NULL;
289b06ebda0SMatthew Dillon 	int			 error = 0;
290b06ebda0SMatthew Dillon 
291b06ebda0SMatthew Dillon 	/* Check message */
292b06ebda0SMatthew Dillon 	if (msg->header.arglen != sizeof(*ep)) {
293b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
294b06ebda0SMatthew Dillon "%s: %s - invalid LP_DisconnectInd message size\n",
295b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node));
296b06ebda0SMatthew Dillon 		error = EMSGSIZE;
297b06ebda0SMatthew Dillon 		goto out;
298b06ebda0SMatthew Dillon 	}
299b06ebda0SMatthew Dillon 
300b06ebda0SMatthew Dillon 	ep = (ng_hci_lp_discon_ind_ep *) (msg->data);
301b06ebda0SMatthew Dillon 
302b06ebda0SMatthew Dillon 	/* Check if we have this connection */
303b06ebda0SMatthew Dillon 	con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
304b06ebda0SMatthew Dillon 	if (con == NULL) {
305b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
306b06ebda0SMatthew Dillon "%s: %s - unexpected LP_DisconnectInd event. " \
307b06ebda0SMatthew Dillon "Connection does not exist, con_handle=%d\n",
308b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
309b06ebda0SMatthew Dillon 		error = ENOENT;
310b06ebda0SMatthew Dillon 		goto out;
311b06ebda0SMatthew Dillon 	}
312b06ebda0SMatthew Dillon 
313b06ebda0SMatthew Dillon 	/* XXX Verify connection state -- do we need to check this? */
314b06ebda0SMatthew Dillon 	if (con->state != NG_L2CAP_CON_OPEN) {
315b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
316b06ebda0SMatthew Dillon "%s: %s - unexpected LP_DisconnectInd event. " \
317b06ebda0SMatthew Dillon "Invalid connection state, state=%d, con_handle=%d\n",
318b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), con->state,
319b06ebda0SMatthew Dillon 			con->con_handle);
320b06ebda0SMatthew Dillon 		error = EINVAL;
321b06ebda0SMatthew Dillon 		goto out;
322b06ebda0SMatthew Dillon 	}
323b06ebda0SMatthew Dillon 
324b06ebda0SMatthew Dillon 	/*
325b06ebda0SMatthew Dillon 	 * Notify upper layer and remove connection
326b06ebda0SMatthew Dillon 	 * Note: The connection could have auto disconnect timeout set. Try
327b06ebda0SMatthew Dillon 	 * to remove it. If auto disconnect timeout happened then ignore
328b06ebda0SMatthew Dillon 	 * disconnect indication and let timeout handle that.
329b06ebda0SMatthew Dillon 	 */
330b06ebda0SMatthew Dillon 
331b06ebda0SMatthew Dillon 	if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)
332b06ebda0SMatthew Dillon 		if ((error = ng_l2cap_discon_untimeout(con)) != 0)
333b06ebda0SMatthew Dillon 			return (error);
334b06ebda0SMatthew Dillon 
335b06ebda0SMatthew Dillon 	ng_l2cap_con_fail(con, ep->reason);
336b06ebda0SMatthew Dillon out:
337b06ebda0SMatthew Dillon 	return (error);
338b06ebda0SMatthew Dillon } /* ng_l2cap_lp_discon_ind */
339b06ebda0SMatthew Dillon 
340b06ebda0SMatthew Dillon /*
341b06ebda0SMatthew Dillon  * Send LP_QoSSetupReq event to the lower layer protocol
342b06ebda0SMatthew Dillon  */
343b06ebda0SMatthew Dillon 
344b06ebda0SMatthew Dillon int
345b06ebda0SMatthew Dillon ng_l2cap_lp_qos_req(ng_l2cap_p l2cap, u_int16_t con_handle,
346b06ebda0SMatthew Dillon 		ng_l2cap_flow_p flow)
347b06ebda0SMatthew Dillon {
348b06ebda0SMatthew Dillon 	struct ng_mesg		*msg = NULL;
349b06ebda0SMatthew Dillon 	ng_hci_lp_qos_req_ep	*ep = NULL;
350b06ebda0SMatthew Dillon 	ng_l2cap_con_p		 con = NULL;
351b06ebda0SMatthew Dillon 	int			 error = 0;
352b06ebda0SMatthew Dillon 
353b06ebda0SMatthew Dillon 	/* Verify that we have this connection */
354b06ebda0SMatthew Dillon 	con = ng_l2cap_con_by_handle(l2cap, con_handle);
355b06ebda0SMatthew Dillon 	if (con == NULL) {
356b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
357b06ebda0SMatthew Dillon "%s: %s - unexpected LP_QoSSetupReq event. " \
358b06ebda0SMatthew Dillon "Connection does not exist, con_handle=%d\n",
359b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), con_handle);
360b06ebda0SMatthew Dillon 
361b06ebda0SMatthew Dillon 		return (ENOENT);
362b06ebda0SMatthew Dillon 	}
363b06ebda0SMatthew Dillon 
364b06ebda0SMatthew Dillon 	/* Verify connection state */
365b06ebda0SMatthew Dillon 	if (con->state != NG_L2CAP_CON_OPEN) {
366b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
367b06ebda0SMatthew Dillon "%s: %s - unexpected LP_QoSSetupReq event. " \
368b06ebda0SMatthew Dillon "Invalid connection state, state=%d, con_handle=%d\n",
369b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), con->state,
370b06ebda0SMatthew Dillon 			con->con_handle);
371b06ebda0SMatthew Dillon 
372b06ebda0SMatthew Dillon 		return (EINVAL);
373b06ebda0SMatthew Dillon 	}
374b06ebda0SMatthew Dillon 
375b06ebda0SMatthew Dillon 	/* Check if lower layer protocol is still connected */
376b06ebda0SMatthew Dillon 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
377b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
378b06ebda0SMatthew Dillon "%s: %s - hook \"%s\" is not connected or valid",
379b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
380b06ebda0SMatthew Dillon 
381b06ebda0SMatthew Dillon 		return (ENOTCONN);
382b06ebda0SMatthew Dillon 	}
383b06ebda0SMatthew Dillon 
384b06ebda0SMatthew Dillon 	/* Create and send LP_QoSSetupReq event */
385b06ebda0SMatthew Dillon 	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_REQ,
386*5a975a3dSMatthew Dillon 		sizeof(*ep), M_WAITOK | M_NULLOK);
387b06ebda0SMatthew Dillon 	if (msg == NULL)
388b06ebda0SMatthew Dillon 		return (ENOMEM);
389b06ebda0SMatthew Dillon 
390b06ebda0SMatthew Dillon 	ep = (ng_hci_lp_qos_req_ep *) (msg->data);
391b06ebda0SMatthew Dillon 	ep->con_handle = con_handle;
392b06ebda0SMatthew Dillon 	ep->flags = flow->flags;
393b06ebda0SMatthew Dillon 	ep->service_type = flow->service_type;
394b06ebda0SMatthew Dillon 	ep->token_rate = flow->token_rate;
395b06ebda0SMatthew Dillon 	ep->peak_bandwidth = flow->peak_bandwidth;
396b06ebda0SMatthew Dillon 	ep->latency = flow->latency;
397b06ebda0SMatthew Dillon 	ep->delay_variation = flow->delay_variation;
398b06ebda0SMatthew Dillon 
399b06ebda0SMatthew Dillon 	NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
400b06ebda0SMatthew Dillon 
401b06ebda0SMatthew Dillon 	return (error);
402b06ebda0SMatthew Dillon } /* ng_l2cap_lp_con_req */
403b06ebda0SMatthew Dillon 
404b06ebda0SMatthew Dillon /*
405b06ebda0SMatthew Dillon  * Process LP_QoSSetupCfm from the lower layer protocol
406b06ebda0SMatthew Dillon  */
407b06ebda0SMatthew Dillon 
408b06ebda0SMatthew Dillon int
409b06ebda0SMatthew Dillon ng_l2cap_lp_qos_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg)
410b06ebda0SMatthew Dillon {
411b06ebda0SMatthew Dillon 	ng_hci_lp_qos_cfm_ep	*ep = NULL;
412b06ebda0SMatthew Dillon 	int			 error = 0;
413b06ebda0SMatthew Dillon 
414b06ebda0SMatthew Dillon 	/* Check message */
415b06ebda0SMatthew Dillon 	if (msg->header.arglen != sizeof(*ep)) {
416b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
417b06ebda0SMatthew Dillon "%s: %s - invalid LP_QoSSetupCfm[Neg] message size\n",
418b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node));
419b06ebda0SMatthew Dillon 		error = EMSGSIZE;
420b06ebda0SMatthew Dillon 		goto out;
421b06ebda0SMatthew Dillon 	}
422b06ebda0SMatthew Dillon 
423b06ebda0SMatthew Dillon 	ep = (ng_hci_lp_qos_cfm_ep *) (msg->data);
424b06ebda0SMatthew Dillon 	/* XXX FIXME do something */
425b06ebda0SMatthew Dillon out:
426b06ebda0SMatthew Dillon 	return (error);
427b06ebda0SMatthew Dillon } /* ng_l2cap_lp_qos_cfm */
428b06ebda0SMatthew Dillon 
429b06ebda0SMatthew Dillon /*
430b06ebda0SMatthew Dillon  * Process LP_QoSViolationInd event from the lower layer protocol. Lower
431b06ebda0SMatthew Dillon  * layer protocol has detected QoS Violation, so we MUST notify the
432b06ebda0SMatthew Dillon  * upper layer.
433b06ebda0SMatthew Dillon  */
434b06ebda0SMatthew Dillon 
435b06ebda0SMatthew Dillon int
436b06ebda0SMatthew Dillon ng_l2cap_lp_qos_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
437b06ebda0SMatthew Dillon {
438b06ebda0SMatthew Dillon 	ng_hci_lp_qos_ind_ep	*ep = NULL;
439b06ebda0SMatthew Dillon 	ng_l2cap_con_p		 con = NULL;
440b06ebda0SMatthew Dillon 	int			 error = 0;
441b06ebda0SMatthew Dillon 
442b06ebda0SMatthew Dillon 	/* Check message */
443b06ebda0SMatthew Dillon 	if (msg->header.arglen != sizeof(*ep)) {
444b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
445b06ebda0SMatthew Dillon "%s: %s - invalid LP_QoSViolation message size\n",
446b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node));
447b06ebda0SMatthew Dillon 		error = EMSGSIZE;
448b06ebda0SMatthew Dillon 		goto out;
449b06ebda0SMatthew Dillon 	}
450b06ebda0SMatthew Dillon 
451b06ebda0SMatthew Dillon 	ep = (ng_hci_lp_qos_ind_ep *) (msg->data);
452b06ebda0SMatthew Dillon 
453b06ebda0SMatthew Dillon 	/* Check if we have this connection */
454b06ebda0SMatthew Dillon 	con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
455b06ebda0SMatthew Dillon 	if (con == NULL) {
456b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
457b06ebda0SMatthew Dillon "%s: %s - unexpected LP_QoSViolationInd event. " \
458b06ebda0SMatthew Dillon "Connection does not exist, con_handle=%d\n",
459b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
460b06ebda0SMatthew Dillon 		error = ENOENT;
461b06ebda0SMatthew Dillon 		goto out;
462b06ebda0SMatthew Dillon 	}
463b06ebda0SMatthew Dillon 
464b06ebda0SMatthew Dillon 	/* Verify connection state */
465b06ebda0SMatthew Dillon 	if (con->state != NG_L2CAP_CON_OPEN) {
466b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
467b06ebda0SMatthew Dillon "%s: %s - unexpected LP_QoSViolationInd event. " \
468b06ebda0SMatthew Dillon "Invalid connection state, state=%d, con_handle=%d\n",
469b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), con->state,
470b06ebda0SMatthew Dillon 			con->con_handle);
471b06ebda0SMatthew Dillon 		error = EINVAL;
472b06ebda0SMatthew Dillon 		goto out;
473b06ebda0SMatthew Dillon 	}
474b06ebda0SMatthew Dillon 
475b06ebda0SMatthew Dillon 	/* XXX FIXME Notify upper layer and terminate channels if required */
476b06ebda0SMatthew Dillon out:
477b06ebda0SMatthew Dillon 	return (error);
478b06ebda0SMatthew Dillon } /* ng_l2cap_qos_ind */
479b06ebda0SMatthew Dillon 
480b06ebda0SMatthew Dillon /*
481b06ebda0SMatthew Dillon  * Prepare L2CAP packet. Prepend packet with L2CAP packet header and then
482b06ebda0SMatthew Dillon  * segment it according to HCI MTU.
483b06ebda0SMatthew Dillon  */
484b06ebda0SMatthew Dillon 
485b06ebda0SMatthew Dillon int
486b06ebda0SMatthew Dillon ng_l2cap_lp_send(ng_l2cap_con_p con, u_int16_t dcid, struct mbuf *m0)
487b06ebda0SMatthew Dillon {
488b06ebda0SMatthew Dillon 	ng_l2cap_p		 l2cap = con->l2cap;
489b06ebda0SMatthew Dillon 	ng_l2cap_hdr_t		*l2cap_hdr = NULL;
490b06ebda0SMatthew Dillon         ng_hci_acldata_pkt_t	*acl_hdr = NULL;
491b06ebda0SMatthew Dillon         struct mbuf		*m_last = NULL, *m = NULL;
492b06ebda0SMatthew Dillon         int			 len, flag = NG_HCI_PACKET_START;
493b06ebda0SMatthew Dillon 
494b06ebda0SMatthew Dillon 	KASSERT((con->tx_pkt == NULL),
495b06ebda0SMatthew Dillon ("%s: %s - another packet pending?!\n", __func__, NG_NODE_NAME(l2cap->node)));
496b06ebda0SMatthew Dillon 	KASSERT((l2cap->pkt_size > 0),
497b06ebda0SMatthew Dillon ("%s: %s - invalid l2cap->pkt_size?!\n", __func__, NG_NODE_NAME(l2cap->node)));
498b06ebda0SMatthew Dillon 
499b06ebda0SMatthew Dillon 	/* Prepend mbuf with L2CAP header */
500b06ebda0SMatthew Dillon 	m0 = ng_l2cap_prepend(m0, sizeof(*l2cap_hdr));
501b06ebda0SMatthew Dillon 	if (m0 == NULL) {
502b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
503b06ebda0SMatthew Dillon "%s: %s - ng_l2cap_prepend(%zd) failed\n",
504b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node),
505b06ebda0SMatthew Dillon 			sizeof(*l2cap_hdr));
506b06ebda0SMatthew Dillon 
507b06ebda0SMatthew Dillon 		goto fail;
508b06ebda0SMatthew Dillon 	}
509b06ebda0SMatthew Dillon 
510b06ebda0SMatthew Dillon 	l2cap_hdr = mtod(m0, ng_l2cap_hdr_t *);
511b06ebda0SMatthew Dillon 	l2cap_hdr->length = htole16(m0->m_pkthdr.len - sizeof(*l2cap_hdr));
512b06ebda0SMatthew Dillon 	l2cap_hdr->dcid = htole16(dcid);
513b06ebda0SMatthew Dillon 
514b06ebda0SMatthew Dillon 	/*
515b06ebda0SMatthew Dillon 	 * Segment single L2CAP packet according to the HCI layer MTU. Convert
516b06ebda0SMatthew Dillon 	 * each segment into ACL data packet and prepend it with ACL data packet
517b06ebda0SMatthew Dillon 	 * header. Link all segments together via m_nextpkt link.
518b06ebda0SMatthew Dillon  	 *
519b06ebda0SMatthew Dillon 	 * XXX BC (Broadcast flag) will always be 0 (zero).
520b06ebda0SMatthew Dillon 	 */
521b06ebda0SMatthew Dillon 
522b06ebda0SMatthew Dillon 	while (m0 != NULL) {
523b06ebda0SMatthew Dillon 		/* Check length of the packet against HCI MTU */
524b06ebda0SMatthew Dillon 		len = m0->m_pkthdr.len;
525b06ebda0SMatthew Dillon 		if (len > l2cap->pkt_size) {
526*5a975a3dSMatthew Dillon 			m = m_split(m0, l2cap->pkt_size, MB_DONTWAIT);
527b06ebda0SMatthew Dillon 			if (m == NULL) {
528b06ebda0SMatthew Dillon 				NG_L2CAP_ALERT(
529b06ebda0SMatthew Dillon "%s: %s - m_split(%d) failed\n",	__func__, NG_NODE_NAME(l2cap->node),
530b06ebda0SMatthew Dillon 					l2cap->pkt_size);
531b06ebda0SMatthew Dillon 				goto fail;
532b06ebda0SMatthew Dillon 			}
533b06ebda0SMatthew Dillon 
534b06ebda0SMatthew Dillon 			len = l2cap->pkt_size;
535b06ebda0SMatthew Dillon 		}
536b06ebda0SMatthew Dillon 
537b06ebda0SMatthew Dillon 		/* Convert packet fragment into ACL data packet */
538b06ebda0SMatthew Dillon 		m0 = ng_l2cap_prepend(m0, sizeof(*acl_hdr));
539b06ebda0SMatthew Dillon 		if (m0 == NULL) {
540b06ebda0SMatthew Dillon 			NG_L2CAP_ALERT(
541b06ebda0SMatthew Dillon "%s: %s - ng_l2cap_prepend(%zd) failed\n",
542b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(l2cap->node),
543b06ebda0SMatthew Dillon 				sizeof(*acl_hdr));
544b06ebda0SMatthew Dillon 			goto fail;
545b06ebda0SMatthew Dillon 		}
546b06ebda0SMatthew Dillon 
547b06ebda0SMatthew Dillon 		acl_hdr = mtod(m0, ng_hci_acldata_pkt_t *);
548b06ebda0SMatthew Dillon 		acl_hdr->type = NG_HCI_ACL_DATA_PKT;
549b06ebda0SMatthew Dillon 		acl_hdr->length = htole16(len);
550b06ebda0SMatthew Dillon 		acl_hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE(
551b06ebda0SMatthew Dillon 					con->con_handle, flag, 0));
552b06ebda0SMatthew Dillon 
553b06ebda0SMatthew Dillon 		/* Add fragment to the chain */
554b06ebda0SMatthew Dillon 		m0->m_nextpkt = NULL;
555b06ebda0SMatthew Dillon 
556b06ebda0SMatthew Dillon 		if (con->tx_pkt == NULL)
557b06ebda0SMatthew Dillon 			con->tx_pkt = m_last = m0;
558b06ebda0SMatthew Dillon 		else {
559b06ebda0SMatthew Dillon 			m_last->m_nextpkt = m0;
560b06ebda0SMatthew Dillon 			m_last = m0;
561b06ebda0SMatthew Dillon 		}
562b06ebda0SMatthew Dillon 
563b06ebda0SMatthew Dillon 		NG_L2CAP_INFO(
564b06ebda0SMatthew Dillon "%s: %s - attaching ACL packet, con_handle=%d, PB=%#x, length=%d\n",
565b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), con->con_handle,
566b06ebda0SMatthew Dillon 			flag, len);
567b06ebda0SMatthew Dillon 
568b06ebda0SMatthew Dillon 		m0 = m;
569b06ebda0SMatthew Dillon 		m = NULL;
570b06ebda0SMatthew Dillon 		flag = NG_HCI_PACKET_FRAGMENT;
571b06ebda0SMatthew Dillon 	}
572b06ebda0SMatthew Dillon 
573b06ebda0SMatthew Dillon 	return (0);
574b06ebda0SMatthew Dillon fail:
575b06ebda0SMatthew Dillon 	NG_FREE_M(m0);
576b06ebda0SMatthew Dillon 	NG_FREE_M(m);
577b06ebda0SMatthew Dillon 
578b06ebda0SMatthew Dillon 	while (con->tx_pkt != NULL) {
579b06ebda0SMatthew Dillon 		m = con->tx_pkt->m_nextpkt;
580b06ebda0SMatthew Dillon 		m_freem(con->tx_pkt);
581b06ebda0SMatthew Dillon 		con->tx_pkt = m;
582b06ebda0SMatthew Dillon 	}
583b06ebda0SMatthew Dillon 
584b06ebda0SMatthew Dillon 	return (ENOBUFS);
585b06ebda0SMatthew Dillon } /* ng_l2cap_lp_send */
586b06ebda0SMatthew Dillon 
587b06ebda0SMatthew Dillon /*
588b06ebda0SMatthew Dillon  * Receive ACL data packet from the HCI layer. First strip ACL packet header
589b06ebda0SMatthew Dillon  * and get connection handle, PB (Packet Boundary) flag and payload length.
590b06ebda0SMatthew Dillon  * Then find connection descriptor and verify its state. Then process ACL
591b06ebda0SMatthew Dillon  * packet as follows.
592b06ebda0SMatthew Dillon  *
593b06ebda0SMatthew Dillon  * 1) If we got first segment (pb == NG_HCI_PACKET_START) then extract L2CAP
594b06ebda0SMatthew Dillon  *    header and get total length of the L2CAP packet. Then start new L2CAP
595b06ebda0SMatthew Dillon  *    packet.
596b06ebda0SMatthew Dillon  *
597b06ebda0SMatthew Dillon  * 2) If we got other (then first :) segment (pb == NG_HCI_PACKET_FRAGMENT)
598b06ebda0SMatthew Dillon  *    then add segment to the packet.
599b06ebda0SMatthew Dillon  */
600b06ebda0SMatthew Dillon 
601b06ebda0SMatthew Dillon int
602b06ebda0SMatthew Dillon ng_l2cap_lp_receive(ng_l2cap_p l2cap, struct mbuf *m)
603b06ebda0SMatthew Dillon {
604b06ebda0SMatthew Dillon 	ng_hci_acldata_pkt_t	*acl_hdr = NULL;
605b06ebda0SMatthew Dillon 	ng_l2cap_hdr_t		*l2cap_hdr = NULL;
606b06ebda0SMatthew Dillon 	ng_l2cap_con_p		 con = NULL;
607b06ebda0SMatthew Dillon 	u_int16_t		 con_handle, length, pb;
608b06ebda0SMatthew Dillon 	int			 error = 0;
609b06ebda0SMatthew Dillon 
610b06ebda0SMatthew Dillon 	/* Check ACL data packet */
611b06ebda0SMatthew Dillon 	if (m->m_pkthdr.len < sizeof(*acl_hdr)) {
612b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
613b06ebda0SMatthew Dillon "%s: %s - invalid ACL data packet. Packet too small, length=%d\n",
614b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), m->m_pkthdr.len);
615b06ebda0SMatthew Dillon 		error = EMSGSIZE;
616b06ebda0SMatthew Dillon 		goto drop;
617b06ebda0SMatthew Dillon 	}
618b06ebda0SMatthew Dillon 
619b06ebda0SMatthew Dillon 	/* Strip ACL data packet header */
620b06ebda0SMatthew Dillon 	NG_L2CAP_M_PULLUP(m, sizeof(*acl_hdr));
621b06ebda0SMatthew Dillon 	if (m == NULL)
622b06ebda0SMatthew Dillon 		return (ENOBUFS);
623b06ebda0SMatthew Dillon 
624b06ebda0SMatthew Dillon 	acl_hdr = mtod(m, ng_hci_acldata_pkt_t *);
625b06ebda0SMatthew Dillon 	m_adj(m, sizeof(*acl_hdr));
626b06ebda0SMatthew Dillon 
627b06ebda0SMatthew Dillon 	/* Get ACL connection handle, PB flag and payload length */
628b06ebda0SMatthew Dillon 	acl_hdr->con_handle = le16toh(acl_hdr->con_handle);
629b06ebda0SMatthew Dillon 	con_handle = NG_HCI_CON_HANDLE(acl_hdr->con_handle);
630b06ebda0SMatthew Dillon 	pb = NG_HCI_PB_FLAG(acl_hdr->con_handle);
631b06ebda0SMatthew Dillon 	length = le16toh(acl_hdr->length);
632b06ebda0SMatthew Dillon 
633b06ebda0SMatthew Dillon 	NG_L2CAP_INFO(
634b06ebda0SMatthew Dillon "%s: %s - got ACL data packet, con_handle=%d, PB=%#x, length=%d\n",
635b06ebda0SMatthew Dillon 		__func__, NG_NODE_NAME(l2cap->node), con_handle, pb, length);
636b06ebda0SMatthew Dillon 
637b06ebda0SMatthew Dillon 	/* Get connection descriptor */
638b06ebda0SMatthew Dillon 	con = ng_l2cap_con_by_handle(l2cap, con_handle);
639b06ebda0SMatthew Dillon 	if (con == NULL) {
640b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
641b06ebda0SMatthew Dillon "%s: %s - unexpected ACL data packet. " \
642b06ebda0SMatthew Dillon "Connection does not exist, con_handle=%d\n",
643b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), con_handle);
644b06ebda0SMatthew Dillon 		error = ENOENT;
645b06ebda0SMatthew Dillon 		goto drop;
646b06ebda0SMatthew Dillon 	}
647b06ebda0SMatthew Dillon 
648b06ebda0SMatthew Dillon 	/* Verify connection state */
649b06ebda0SMatthew Dillon 	if (con->state != NG_L2CAP_CON_OPEN) {
650b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
651b06ebda0SMatthew Dillon "%s: %s - unexpected ACL data packet. Invalid connection state=%d\n",
652b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), con->state);
653b06ebda0SMatthew Dillon 		error = EHOSTDOWN;
654b06ebda0SMatthew Dillon 		goto drop;
655b06ebda0SMatthew Dillon 	}
656b06ebda0SMatthew Dillon 
657b06ebda0SMatthew Dillon 	/* Process packet */
658b06ebda0SMatthew Dillon 	if (pb == NG_HCI_PACKET_START) {
659b06ebda0SMatthew Dillon 		if (con->rx_pkt != NULL) {
660b06ebda0SMatthew Dillon 			NG_L2CAP_ERR(
661b06ebda0SMatthew Dillon "%s: %s - dropping incomplete L2CAP packet, got %d bytes, want %d bytes\n",
662b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(l2cap->node),
663b06ebda0SMatthew Dillon 				con->rx_pkt->m_pkthdr.len, con->rx_pkt_len);
664b06ebda0SMatthew Dillon 			NG_FREE_M(con->rx_pkt);
665b06ebda0SMatthew Dillon 			con->rx_pkt_len = 0;
666b06ebda0SMatthew Dillon 		}
667b06ebda0SMatthew Dillon 
668b06ebda0SMatthew Dillon 		/* Get L2CAP header */
669b06ebda0SMatthew Dillon 		if (m->m_pkthdr.len < sizeof(*l2cap_hdr)) {
670b06ebda0SMatthew Dillon 			NG_L2CAP_ERR(
671b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP packet start fragment. Packet too small, length=%d\n",
672b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(l2cap->node),
673b06ebda0SMatthew Dillon 				m->m_pkthdr.len);
674b06ebda0SMatthew Dillon 			error = EMSGSIZE;
675b06ebda0SMatthew Dillon 			goto drop;
676b06ebda0SMatthew Dillon 		}
677b06ebda0SMatthew Dillon 
678b06ebda0SMatthew Dillon 		NG_L2CAP_M_PULLUP(m, sizeof(*l2cap_hdr));
679b06ebda0SMatthew Dillon 		if (m == NULL)
680b06ebda0SMatthew Dillon 			return (ENOBUFS);
681b06ebda0SMatthew Dillon 
682b06ebda0SMatthew Dillon 		l2cap_hdr = mtod(m, ng_l2cap_hdr_t *);
683b06ebda0SMatthew Dillon 
684b06ebda0SMatthew Dillon 		NG_L2CAP_INFO(
685b06ebda0SMatthew Dillon "%s: %s - staring new L2CAP packet, con_handle=%d, length=%d\n",
686b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), con_handle,
687b06ebda0SMatthew Dillon 			le16toh(l2cap_hdr->length));
688b06ebda0SMatthew Dillon 
689b06ebda0SMatthew Dillon 		/* Start new L2CAP packet */
690b06ebda0SMatthew Dillon 		con->rx_pkt = m;
691b06ebda0SMatthew Dillon 		con->rx_pkt_len = le16toh(l2cap_hdr->length)+sizeof(*l2cap_hdr);
692b06ebda0SMatthew Dillon 	} else if (pb == NG_HCI_PACKET_FRAGMENT) {
693b06ebda0SMatthew Dillon 		if (con->rx_pkt == NULL) {
694b06ebda0SMatthew Dillon 			NG_L2CAP_ERR(
695b06ebda0SMatthew Dillon "%s: %s - unexpected ACL data packet fragment, con_handle=%d\n",
696b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(l2cap->node),
697b06ebda0SMatthew Dillon 				con->con_handle);
698b06ebda0SMatthew Dillon 			goto drop;
699b06ebda0SMatthew Dillon 		}
700b06ebda0SMatthew Dillon 
701b06ebda0SMatthew Dillon 		/* Add fragment to the L2CAP packet */
702b06ebda0SMatthew Dillon 		m_cat(con->rx_pkt, m);
703b06ebda0SMatthew Dillon 		con->rx_pkt->m_pkthdr.len += length;
704b06ebda0SMatthew Dillon 	} else {
705b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
706b06ebda0SMatthew Dillon "%s: %s - invalid ACL data packet. Invalid PB flag=%#x\n",
707b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), pb);
708b06ebda0SMatthew Dillon 		error = EINVAL;
709b06ebda0SMatthew Dillon 		goto drop;
710b06ebda0SMatthew Dillon 	}
711b06ebda0SMatthew Dillon 
712b06ebda0SMatthew Dillon 	con->rx_pkt_len -= length;
713b06ebda0SMatthew Dillon 	if (con->rx_pkt_len < 0) {
714b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
715b06ebda0SMatthew Dillon "%s: %s - packet length mismatch. Got %d bytes, offset %d bytes\n",
716b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node),
717b06ebda0SMatthew Dillon 			con->rx_pkt->m_pkthdr.len, con->rx_pkt_len);
718b06ebda0SMatthew Dillon 		NG_FREE_M(con->rx_pkt);
719b06ebda0SMatthew Dillon 		con->rx_pkt_len = 0;
720b06ebda0SMatthew Dillon 	} else if (con->rx_pkt_len == 0) {
721b06ebda0SMatthew Dillon 		/* OK, we have got complete L2CAP packet, so process it */
722b06ebda0SMatthew Dillon 		error = ng_l2cap_receive(con);
723b06ebda0SMatthew Dillon 		con->rx_pkt = NULL;
724b06ebda0SMatthew Dillon 		con->rx_pkt_len = 0;
725b06ebda0SMatthew Dillon 	}
726b06ebda0SMatthew Dillon 
727b06ebda0SMatthew Dillon 	return (error);
728b06ebda0SMatthew Dillon 
729b06ebda0SMatthew Dillon drop:
730b06ebda0SMatthew Dillon 	NG_FREE_M(m);
731b06ebda0SMatthew Dillon 
732b06ebda0SMatthew Dillon 	return (error);
733b06ebda0SMatthew Dillon } /* ng_l2cap_lp_receive */
734b06ebda0SMatthew Dillon 
735b06ebda0SMatthew Dillon /*
736b06ebda0SMatthew Dillon  * Send queued ACL packets to the HCI layer
737b06ebda0SMatthew Dillon  */
738b06ebda0SMatthew Dillon 
739b06ebda0SMatthew Dillon void
740b06ebda0SMatthew Dillon ng_l2cap_lp_deliver(ng_l2cap_con_p con)
741b06ebda0SMatthew Dillon {
742b06ebda0SMatthew Dillon 	ng_l2cap_p	 l2cap = con->l2cap;
743b06ebda0SMatthew Dillon 	struct mbuf	*m = NULL;
744b06ebda0SMatthew Dillon 	int		 error;
745b06ebda0SMatthew Dillon 
746b06ebda0SMatthew Dillon 	/* Check connection */
747b06ebda0SMatthew Dillon 	if (con->state != NG_L2CAP_CON_OPEN)
748b06ebda0SMatthew Dillon 		return;
749b06ebda0SMatthew Dillon 
750b06ebda0SMatthew Dillon 	if (con->tx_pkt == NULL)
751b06ebda0SMatthew Dillon 		ng_l2cap_con_wakeup(con);
752b06ebda0SMatthew Dillon 
753b06ebda0SMatthew Dillon 	if (con->tx_pkt == NULL)
754b06ebda0SMatthew Dillon 		return;
755b06ebda0SMatthew Dillon 
756b06ebda0SMatthew Dillon 	/* Check if lower layer protocol is still connected */
757b06ebda0SMatthew Dillon 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
758b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
759b06ebda0SMatthew Dillon "%s: %s - hook \"%s\" is not connected or valid",
760b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
761b06ebda0SMatthew Dillon 
762b06ebda0SMatthew Dillon 		goto drop; /* XXX what to do with "pending"? */
763b06ebda0SMatthew Dillon 	}
764b06ebda0SMatthew Dillon 
765b06ebda0SMatthew Dillon 	/* Send ACL data packets */
766b06ebda0SMatthew Dillon 	while (con->pending < con->l2cap->num_pkts && con->tx_pkt != NULL) {
767b06ebda0SMatthew Dillon 		m = con->tx_pkt;
768b06ebda0SMatthew Dillon 		con->tx_pkt = con->tx_pkt->m_nextpkt;
769b06ebda0SMatthew Dillon 		m->m_nextpkt = NULL;
770b06ebda0SMatthew Dillon 
771b06ebda0SMatthew Dillon 		NG_L2CAP_INFO(
772b06ebda0SMatthew Dillon "%s: %s - sending ACL packet, con_handle=%d, len=%d\n",
773b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), con->con_handle,
774b06ebda0SMatthew Dillon 			m->m_pkthdr.len);
775b06ebda0SMatthew Dillon 
776b06ebda0SMatthew Dillon 		NG_SEND_DATA_ONLY(error, l2cap->hci, m);
777b06ebda0SMatthew Dillon 		if (error != 0) {
778b06ebda0SMatthew Dillon 			NG_L2CAP_ERR(
779b06ebda0SMatthew Dillon "%s: %s - could not send ACL data packet, con_handle=%d, error=%d\n",
780b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(l2cap->node),
781b06ebda0SMatthew Dillon 				con->con_handle, error);
782b06ebda0SMatthew Dillon 
783b06ebda0SMatthew Dillon 			goto drop; /* XXX what to do with "pending"? */
784b06ebda0SMatthew Dillon 		}
785b06ebda0SMatthew Dillon 
786b06ebda0SMatthew Dillon 		con->pending ++;
787b06ebda0SMatthew Dillon 	}
788b06ebda0SMatthew Dillon 
789b06ebda0SMatthew Dillon 	NG_L2CAP_INFO(
790b06ebda0SMatthew Dillon "%s: %s - %d ACL packets have been sent, con_handle=%d\n",
791b06ebda0SMatthew Dillon 		__func__, NG_NODE_NAME(l2cap->node), con->pending,
792b06ebda0SMatthew Dillon 		con->con_handle);
793b06ebda0SMatthew Dillon 
794b06ebda0SMatthew Dillon 	return;
795b06ebda0SMatthew Dillon 
796b06ebda0SMatthew Dillon drop:
797b06ebda0SMatthew Dillon 	while (con->tx_pkt != NULL) {
798b06ebda0SMatthew Dillon 		m = con->tx_pkt->m_nextpkt;
799b06ebda0SMatthew Dillon 		m_freem(con->tx_pkt);
800b06ebda0SMatthew Dillon 		con->tx_pkt = m;
801b06ebda0SMatthew Dillon 	}
802b06ebda0SMatthew Dillon } /* ng_l2cap_lp_deliver */
803b06ebda0SMatthew Dillon 
804b06ebda0SMatthew Dillon /*
805b06ebda0SMatthew Dillon  * Process connection timeout. Remove connection from the list. If there
806b06ebda0SMatthew Dillon  * are any channels that wait for the connection then notify them. Free
807b06ebda0SMatthew Dillon  * connection descriptor.
808b06ebda0SMatthew Dillon  */
809b06ebda0SMatthew Dillon 
810b06ebda0SMatthew Dillon void
811b06ebda0SMatthew Dillon ng_l2cap_process_lp_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
812b06ebda0SMatthew Dillon {
813b06ebda0SMatthew Dillon 	ng_l2cap_p	l2cap = NULL;
814b06ebda0SMatthew Dillon 	ng_l2cap_con_p	con = NULL;
815b06ebda0SMatthew Dillon 
816b06ebda0SMatthew Dillon 	if (NG_NODE_NOT_VALID(node)) {
817b06ebda0SMatthew Dillon 		printf("%s: Netgraph node is not valid\n", __func__);
818b06ebda0SMatthew Dillon 		return;
819b06ebda0SMatthew Dillon 	}
820b06ebda0SMatthew Dillon 
821b06ebda0SMatthew Dillon 	l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
822b06ebda0SMatthew Dillon 	con = ng_l2cap_con_by_handle(l2cap, con_handle);
823b06ebda0SMatthew Dillon 
824b06ebda0SMatthew Dillon 	if (con == NULL) {
825b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
826b06ebda0SMatthew Dillon "%s: %s - could not find connection, con_handle=%d\n",
827b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(node), con_handle);
828b06ebda0SMatthew Dillon 		return;
829b06ebda0SMatthew Dillon 	}
830b06ebda0SMatthew Dillon 
831b06ebda0SMatthew Dillon 	if (!(con->flags & NG_L2CAP_CON_LP_TIMO)) {
832b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
833b06ebda0SMatthew Dillon "%s: %s - no pending LP timeout, con_handle=%d, state=%d, flags=%#x\n",
834b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(node), con_handle, con->state,
835b06ebda0SMatthew Dillon 			con->flags);
836b06ebda0SMatthew Dillon 		return;
837b06ebda0SMatthew Dillon 	}
838b06ebda0SMatthew Dillon 
839b06ebda0SMatthew Dillon 	/*
840b06ebda0SMatthew Dillon 	 * Notify channels that connection has timed out. This will remove
841b06ebda0SMatthew Dillon 	 * connection, channels and pending commands.
842b06ebda0SMatthew Dillon 	 */
843b06ebda0SMatthew Dillon 
844b06ebda0SMatthew Dillon 	con->flags &= ~NG_L2CAP_CON_LP_TIMO;
845b06ebda0SMatthew Dillon 	ng_l2cap_con_fail(con, NG_L2CAP_TIMEOUT);
846b06ebda0SMatthew Dillon } /* ng_l2cap_process_lp_timeout */
847b06ebda0SMatthew Dillon 
848b06ebda0SMatthew Dillon /*
849b06ebda0SMatthew Dillon  * Process auto disconnect timeout and send LP_DisconReq event to the
850b06ebda0SMatthew Dillon  * lower layer protocol
851b06ebda0SMatthew Dillon  */
852b06ebda0SMatthew Dillon 
853b06ebda0SMatthew Dillon void
854b06ebda0SMatthew Dillon ng_l2cap_process_discon_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
855b06ebda0SMatthew Dillon {
856b06ebda0SMatthew Dillon 	ng_l2cap_p		 l2cap = NULL;
857b06ebda0SMatthew Dillon 	ng_l2cap_con_p		 con = NULL;
858b06ebda0SMatthew Dillon 	struct ng_mesg		*msg = NULL;
859b06ebda0SMatthew Dillon 	ng_hci_lp_discon_req_ep	*ep = NULL;
860b06ebda0SMatthew Dillon 	int			 error;
861b06ebda0SMatthew Dillon 
862b06ebda0SMatthew Dillon 	if (NG_NODE_NOT_VALID(node)) {
863b06ebda0SMatthew Dillon 		printf("%s: Netgraph node is not valid\n", __func__);
864b06ebda0SMatthew Dillon 		return;
865b06ebda0SMatthew Dillon 	}
866b06ebda0SMatthew Dillon 
867b06ebda0SMatthew Dillon 	l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
868b06ebda0SMatthew Dillon 	con = ng_l2cap_con_by_handle(l2cap, con_handle);
869b06ebda0SMatthew Dillon 
870b06ebda0SMatthew Dillon 	if (con == NULL) {
871b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
872b06ebda0SMatthew Dillon "%s: %s - could not find connection, con_handle=%d\n",
873b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(node), con_handle);
874b06ebda0SMatthew Dillon 		return;
875b06ebda0SMatthew Dillon 	}
876b06ebda0SMatthew Dillon 
877b06ebda0SMatthew Dillon 	if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)) {
878b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
879b06ebda0SMatthew Dillon "%s: %s - no pending disconnect timeout, con_handle=%d, state=%d, flags=%#x\n",
880b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(node), con_handle, con->state,
881b06ebda0SMatthew Dillon 			con->flags);
882b06ebda0SMatthew Dillon 		return;
883b06ebda0SMatthew Dillon 	}
884b06ebda0SMatthew Dillon 
885b06ebda0SMatthew Dillon 	con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO;
886b06ebda0SMatthew Dillon 
887b06ebda0SMatthew Dillon 	/* Check if lower layer protocol is still connected */
888b06ebda0SMatthew Dillon 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
889b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
890b06ebda0SMatthew Dillon "%s: %s - hook \"%s\" is not connected or valid\n",
891b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
892b06ebda0SMatthew Dillon 		return;
893b06ebda0SMatthew Dillon 	}
894b06ebda0SMatthew Dillon 
895b06ebda0SMatthew Dillon 	/* Create and send LP_DisconReq event */
896b06ebda0SMatthew Dillon 	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_REQ,
897*5a975a3dSMatthew Dillon 		sizeof(*ep), M_WAITOK | M_NULLOK);
898b06ebda0SMatthew Dillon 	if (msg == NULL)
899b06ebda0SMatthew Dillon 		return;
900b06ebda0SMatthew Dillon 
901b06ebda0SMatthew Dillon 	ep = (ng_hci_lp_discon_req_ep *) (msg->data);
902b06ebda0SMatthew Dillon 	ep->con_handle = con->con_handle;
903b06ebda0SMatthew Dillon 	ep->reason = 0x13; /* User Ended Connection */
904b06ebda0SMatthew Dillon 
905b06ebda0SMatthew Dillon 	NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
906b06ebda0SMatthew Dillon } /* ng_l2cap_process_discon_timeout */
907b06ebda0SMatthew Dillon 
908