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