xref: /freebsd/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.c (revision 2ff63af9b88c7413b7d71715b5532625752a248e)
1878ed226SJulian Elischer /*
2878ed226SJulian Elischer  * ng_l2cap_llpi.c
3c398230bSWarner Losh  */
4c398230bSWarner Losh 
5c398230bSWarner Losh /*-
6*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
7fe267a55SPedro F. Giffuni  *
8878ed226SJulian Elischer  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
9878ed226SJulian Elischer  * All rights reserved.
10878ed226SJulian Elischer  *
11878ed226SJulian Elischer  * Redistribution and use in source and binary forms, with or without
12878ed226SJulian Elischer  * modification, are permitted provided that the following conditions
13878ed226SJulian Elischer  * are met:
14878ed226SJulian Elischer  * 1. Redistributions of source code must retain the above copyright
15878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer.
16878ed226SJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
17878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
18878ed226SJulian Elischer  *    documentation and/or other materials provided with the distribution.
19878ed226SJulian Elischer  *
20878ed226SJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21878ed226SJulian Elischer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22878ed226SJulian Elischer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23878ed226SJulian Elischer  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24878ed226SJulian Elischer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25878ed226SJulian Elischer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26878ed226SJulian Elischer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27878ed226SJulian Elischer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28878ed226SJulian Elischer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29878ed226SJulian Elischer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30878ed226SJulian Elischer  * SUCH DAMAGE.
31878ed226SJulian Elischer  *
320986ab12SMaksim Yevmenkin  * $Id: ng_l2cap_llpi.c,v 1.5 2003/09/08 19:11:45 max Exp $
33878ed226SJulian Elischer  */
34878ed226SJulian Elischer 
35878ed226SJulian Elischer #include <sys/param.h>
36878ed226SJulian Elischer #include <sys/systm.h>
37878ed226SJulian Elischer #include <sys/kernel.h>
38878ed226SJulian Elischer #include <sys/endian.h>
39878ed226SJulian Elischer #include <sys/malloc.h>
40878ed226SJulian Elischer #include <sys/mbuf.h>
41878ed226SJulian Elischer #include <sys/queue.h>
42878ed226SJulian Elischer #include <netgraph/ng_message.h>
43878ed226SJulian Elischer #include <netgraph/netgraph.h>
44b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_bluetooth.h>
45b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_hci.h>
46b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_l2cap.h>
47b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
48b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
49b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
50b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
51b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
52b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>
53878ed226SJulian Elischer 
54878ed226SJulian Elischer /******************************************************************************
55878ed226SJulian Elischer  ******************************************************************************
56878ed226SJulian Elischer  **                 Lower Layer Protocol (HCI) Interface module
57878ed226SJulian Elischer  ******************************************************************************
58878ed226SJulian Elischer  ******************************************************************************/
59878ed226SJulian Elischer 
60878ed226SJulian Elischer /*
61878ed226SJulian Elischer  * Send LP_ConnectReq event to the lower layer protocol. Create new connection
62878ed226SJulian Elischer  * descriptor and initialize it. Create LP_ConnectReq event and send it to the
63878ed226SJulian Elischer  * lower layer, then adjust connection state and start timer. The function WILL
64878ed226SJulian Elischer  * FAIL if connection to the remote unit already exists.
65878ed226SJulian Elischer  */
66878ed226SJulian Elischer 
67878ed226SJulian Elischer int
ng_l2cap_lp_con_req(ng_l2cap_p l2cap,bdaddr_p bdaddr,int type)68fbc48c2bSTakanori Watanabe ng_l2cap_lp_con_req(ng_l2cap_p l2cap, bdaddr_p bdaddr, int type)
69878ed226SJulian Elischer {
70878ed226SJulian Elischer 	struct ng_mesg		*msg = NULL;
71878ed226SJulian Elischer 	ng_hci_lp_con_req_ep	*ep = NULL;
72878ed226SJulian Elischer 	ng_l2cap_con_p		 con = NULL;
73878ed226SJulian Elischer 	int			 error = 0;
74878ed226SJulian Elischer 
75878ed226SJulian Elischer 	/* Verify that we DO NOT have connection to the remote unit */
76fbc48c2bSTakanori Watanabe 	con = ng_l2cap_con_by_addr(l2cap, bdaddr, type);
77878ed226SJulian Elischer 	if (con != NULL) {
78878ed226SJulian Elischer 		NG_L2CAP_ALERT(
79878ed226SJulian Elischer "%s: %s - unexpected LP_ConnectReq event. " \
80878ed226SJulian Elischer "Connection already exists, state=%d, con_handle=%d\n",
81878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), con->state,
82878ed226SJulian Elischer 			con->con_handle);
83878ed226SJulian Elischer 
84878ed226SJulian Elischer 		return (EEXIST);
85878ed226SJulian Elischer 	}
86878ed226SJulian Elischer 
87878ed226SJulian Elischer 	/* Check if lower layer protocol is still connected */
88878ed226SJulian Elischer 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
89878ed226SJulian Elischer 		NG_L2CAP_ERR(
90878ed226SJulian Elischer "%s: %s - hook \"%s\" is not connected or valid\n",
91878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
92878ed226SJulian Elischer 
93878ed226SJulian Elischer 		return (ENOTCONN);
94878ed226SJulian Elischer 	}
95878ed226SJulian Elischer 
96878ed226SJulian Elischer 	/* Create and intialize new connection descriptor */
97fbc48c2bSTakanori Watanabe 	con = ng_l2cap_new_con(l2cap, bdaddr, type);
98878ed226SJulian Elischer 	if (con == NULL)
99878ed226SJulian Elischer 		return (ENOMEM);
100878ed226SJulian Elischer 
101878ed226SJulian Elischer 	/* Create and send LP_ConnectReq event */
102878ed226SJulian Elischer 	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_REQ,
103878ed226SJulian Elischer 		sizeof(*ep), M_NOWAIT);
104878ed226SJulian Elischer 	if (msg == NULL) {
105878ed226SJulian Elischer 		ng_l2cap_free_con(con);
106878ed226SJulian Elischer 
107878ed226SJulian Elischer 		return (ENOMEM);
108878ed226SJulian Elischer 	}
109878ed226SJulian Elischer 
110878ed226SJulian Elischer 	ep = (ng_hci_lp_con_req_ep *) (msg->data);
111878ed226SJulian Elischer 	bcopy(bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
112fbc48c2bSTakanori Watanabe 	ep->link_type = type;
113878ed226SJulian Elischer 
114f2bb1caeSJulian Elischer 	con->flags |= NG_L2CAP_CON_OUTGOING;
115878ed226SJulian Elischer 	con->state = NG_L2CAP_W4_LP_CON_CFM;
116878ed226SJulian Elischer 	ng_l2cap_lp_timeout(con);
117878ed226SJulian Elischer 
1184ae439a3SMaksim Yevmenkin 	NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
119f2bb1caeSJulian Elischer 	if (error != 0) {
12090326507SMaksim Yevmenkin 		if (ng_l2cap_lp_untimeout(con) == 0)
121f2bb1caeSJulian Elischer 			ng_l2cap_free_con(con);
12290326507SMaksim Yevmenkin 
12390326507SMaksim Yevmenkin 		/*
12490326507SMaksim Yevmenkin 		 * Do not free connection if ng_l2cap_lp_untimeout() failed
12590326507SMaksim Yevmenkin 		 * let timeout handler deal with it. Always return error to
12690326507SMaksim Yevmenkin 		 * the caller.
12790326507SMaksim Yevmenkin 		 */
128f2bb1caeSJulian Elischer 	}
129878ed226SJulian Elischer 
130878ed226SJulian Elischer 	return (error);
131878ed226SJulian Elischer } /* ng_l2cap_lp_con_req */
132878ed226SJulian Elischer 
133878ed226SJulian Elischer /*
134878ed226SJulian Elischer  * Process LP_ConnectCfm event from the lower layer protocol. It could be
135878ed226SJulian Elischer  * positive or negative. Verify remote unit address then stop the timer and
136878ed226SJulian Elischer  * process event.
137878ed226SJulian Elischer  */
138878ed226SJulian Elischer 
139878ed226SJulian Elischer int
ng_l2cap_lp_con_cfm(ng_l2cap_p l2cap,struct ng_mesg * msg)140878ed226SJulian Elischer ng_l2cap_lp_con_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg)
141878ed226SJulian Elischer {
142878ed226SJulian Elischer 	ng_hci_lp_con_cfm_ep	*ep = NULL;
143878ed226SJulian Elischer 	ng_l2cap_con_p		 con = NULL;
144878ed226SJulian Elischer 	int			 error = 0;
145878ed226SJulian Elischer 
146878ed226SJulian Elischer 	/* Check message */
147878ed226SJulian Elischer 	if (msg->header.arglen != sizeof(*ep)) {
148878ed226SJulian Elischer 		NG_L2CAP_ALERT(
149878ed226SJulian Elischer "%s: %s - invalid LP_ConnectCfm[Neg] message size\n",
150878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node));
151878ed226SJulian Elischer 		error = EMSGSIZE;
152878ed226SJulian Elischer 		goto out;
153878ed226SJulian Elischer 	}
154878ed226SJulian Elischer 
155878ed226SJulian Elischer 	ep = (ng_hci_lp_con_cfm_ep *) (msg->data);
156878ed226SJulian Elischer 	/* Check if we have requested/accepted this connection */
157fbc48c2bSTakanori Watanabe 	con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr, ep->link_type);
158878ed226SJulian Elischer 	if (con == NULL) {
159878ed226SJulian Elischer 		NG_L2CAP_ERR(
160878ed226SJulian Elischer "%s: %s - unexpected LP_ConnectCfm event. Connection does not exist\n",
161878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node));
162878ed226SJulian Elischer 		error = ENOENT;
163878ed226SJulian Elischer 		goto out;
164878ed226SJulian Elischer 	}
165878ed226SJulian Elischer 
166878ed226SJulian Elischer 	/* Check connection state */
167878ed226SJulian Elischer 	if (con->state != NG_L2CAP_W4_LP_CON_CFM) {
168878ed226SJulian Elischer 		NG_L2CAP_ALERT(
169878ed226SJulian Elischer "%s: %s - unexpected LP_ConnectCfm event. " \
170878ed226SJulian Elischer "Invalid connection state, state=%d, con_handle=%d\n",
171878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), con->state,
172878ed226SJulian Elischer 			con->con_handle);
173878ed226SJulian Elischer 		error = EINVAL;
174878ed226SJulian Elischer 		goto out;
175878ed226SJulian Elischer 	}
176878ed226SJulian Elischer 
177878ed226SJulian Elischer 	/*
178878ed226SJulian Elischer 	 * Looks like it is our confirmation. It is safe now to cancel
1790986ab12SMaksim Yevmenkin 	 * connection timer and notify upper layer. If timeout already
1800986ab12SMaksim Yevmenkin 	 * happened then ignore connection confirmation and let timeout
1810986ab12SMaksim Yevmenkin 	 * handle that.
182878ed226SJulian Elischer  	 */
183878ed226SJulian Elischer 
1840986ab12SMaksim Yevmenkin 	if ((error = ng_l2cap_lp_untimeout(con)) != 0)
1850986ab12SMaksim Yevmenkin 		goto out;
186878ed226SJulian Elischer 
187878ed226SJulian Elischer 	if (ep->status == 0) {
188878ed226SJulian Elischer 		con->state = NG_L2CAP_CON_OPEN;
189878ed226SJulian Elischer 		con->con_handle = ep->con_handle;
190878ed226SJulian Elischer 		ng_l2cap_lp_deliver(con);
191f2bb1caeSJulian Elischer 	} else /* Negative confirmation - remove connection descriptor */
192878ed226SJulian Elischer 		ng_l2cap_con_fail(con, ep->status);
193878ed226SJulian Elischer out:
194878ed226SJulian Elischer 	return (error);
195878ed226SJulian Elischer } /* ng_l2cap_lp_con_cfm */
196878ed226SJulian Elischer 
197878ed226SJulian Elischer /*
198878ed226SJulian Elischer  * Process LP_ConnectInd event from the lower layer protocol. This is a good
199878ed226SJulian Elischer  * place to put some extra check on remote unit address and/or class. We could
200878ed226SJulian Elischer  * even forward this information to control hook (or check against internal
201878ed226SJulian Elischer  * black list) and thus implement some kind of firewall. But for now be simple
202878ed226SJulian Elischer  * and create new connection descriptor, start timer and send LP_ConnectRsp
203878ed226SJulian Elischer  * event (i.e. accept connection).
204878ed226SJulian Elischer  */
205878ed226SJulian Elischer 
206878ed226SJulian Elischer int
ng_l2cap_lp_con_ind(ng_l2cap_p l2cap,struct ng_mesg * msg)207878ed226SJulian Elischer ng_l2cap_lp_con_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
208878ed226SJulian Elischer {
209878ed226SJulian Elischer 	ng_hci_lp_con_ind_ep	*ep = NULL;
210878ed226SJulian Elischer 	ng_hci_lp_con_rsp_ep	*rp = NULL;
211878ed226SJulian Elischer 	struct ng_mesg		*rsp = NULL;
212878ed226SJulian Elischer 	ng_l2cap_con_p		 con = NULL;
213878ed226SJulian Elischer 	int			 error = 0;
214878ed226SJulian Elischer 
215878ed226SJulian Elischer 	/* Check message */
216878ed226SJulian Elischer 	if (msg->header.arglen != sizeof(*ep)) {
217878ed226SJulian Elischer 		NG_L2CAP_ALERT(
218878ed226SJulian Elischer "%s: %s - invalid LP_ConnectInd message size\n",
219878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node));
22090326507SMaksim Yevmenkin 
22190326507SMaksim Yevmenkin 		return (EMSGSIZE);
222878ed226SJulian Elischer 	}
223878ed226SJulian Elischer 
224878ed226SJulian Elischer  	ep = (ng_hci_lp_con_ind_ep *) (msg->data);
225878ed226SJulian Elischer 
226878ed226SJulian Elischer 	/* Make sure we have only one connection to the remote unit */
227fbc48c2bSTakanori Watanabe 	con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr, ep->link_type);
228878ed226SJulian Elischer 	if (con != NULL) {
229878ed226SJulian Elischer 		NG_L2CAP_ALERT(
230878ed226SJulian Elischer "%s: %s - unexpected LP_ConnectInd event. " \
231878ed226SJulian Elischer "Connection already exists, state=%d, con_handle=%d\n",
232878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), con->state,
233878ed226SJulian Elischer 			con->con_handle);
23490326507SMaksim Yevmenkin 
23590326507SMaksim Yevmenkin 		return (EEXIST);
236878ed226SJulian Elischer 	}
237878ed226SJulian Elischer 
238878ed226SJulian Elischer 	/* Check if lower layer protocol is still connected */
239878ed226SJulian Elischer 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
240878ed226SJulian Elischer 		NG_L2CAP_ERR(
241878ed226SJulian Elischer "%s: %s - hook \"%s\" is not connected or valid",
242878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
24390326507SMaksim Yevmenkin 
24490326507SMaksim Yevmenkin 		return (ENOTCONN);
245878ed226SJulian Elischer 	}
246878ed226SJulian Elischer 
247878ed226SJulian Elischer 	/* Create and intialize new connection descriptor */
248fbc48c2bSTakanori Watanabe 	con = ng_l2cap_new_con(l2cap, &ep->bdaddr, ep->link_type);
24990326507SMaksim Yevmenkin 	if (con == NULL)
25090326507SMaksim Yevmenkin 		return (ENOMEM);
251878ed226SJulian Elischer 
252878ed226SJulian Elischer 	/* Create and send LP_ConnectRsp event */
253878ed226SJulian Elischer 	NG_MKMESSAGE(rsp, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP,
254878ed226SJulian Elischer 		sizeof(*rp), M_NOWAIT);
25589a4a8b5SMaksim Yevmenkin 	if (rsp == NULL) {
256878ed226SJulian Elischer 		ng_l2cap_free_con(con);
25790326507SMaksim Yevmenkin 
25890326507SMaksim Yevmenkin 		return (ENOMEM);
259878ed226SJulian Elischer 	}
260878ed226SJulian Elischer 
261878ed226SJulian Elischer 	rp = (ng_hci_lp_con_rsp_ep *)(rsp->data);
262878ed226SJulian Elischer 	rp->status = 0x00; /* accept connection */
263878ed226SJulian Elischer 	rp->link_type = NG_HCI_LINK_ACL;
264878ed226SJulian Elischer 	bcopy(&ep->bdaddr, &rp->bdaddr, sizeof(rp->bdaddr));
265878ed226SJulian Elischer 
266878ed226SJulian Elischer 	con->state = NG_L2CAP_W4_LP_CON_CFM;
267878ed226SJulian Elischer 	ng_l2cap_lp_timeout(con);
268878ed226SJulian Elischer 
2694ae439a3SMaksim Yevmenkin 	NG_SEND_MSG_HOOK(error, l2cap->node, rsp, l2cap->hci, 0);
270f2bb1caeSJulian Elischer 	if (error != 0) {
27190326507SMaksim Yevmenkin 		if (ng_l2cap_lp_untimeout(con) == 0)
272f2bb1caeSJulian Elischer 			ng_l2cap_free_con(con);
27390326507SMaksim Yevmenkin 
27490326507SMaksim Yevmenkin 		/*
27590326507SMaksim Yevmenkin 		 * Do not free connection if ng_l2cap_lp_untimeout() failed
27690326507SMaksim Yevmenkin 		 * let timeout handler deal with it. Always return error to
27790326507SMaksim Yevmenkin 		 * the caller.
27890326507SMaksim Yevmenkin 		 */
279f2bb1caeSJulian Elischer 	}
28090326507SMaksim Yevmenkin 
281878ed226SJulian Elischer 	return (error);
28290326507SMaksim Yevmenkin } /* ng_l2cap_lp_con_ind */
283878ed226SJulian Elischer 
284878ed226SJulian Elischer /*
285878ed226SJulian Elischer  * Process LP_DisconnectInd event from the lower layer protocol. We have been
286878ed226SJulian Elischer  * disconnected from the remote unit. So notify the upper layer protocol.
287878ed226SJulian Elischer  */
288878ed226SJulian Elischer 
289878ed226SJulian Elischer int
ng_l2cap_lp_discon_ind(ng_l2cap_p l2cap,struct ng_mesg * msg)290878ed226SJulian Elischer ng_l2cap_lp_discon_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
291878ed226SJulian Elischer {
292878ed226SJulian Elischer 	ng_hci_lp_discon_ind_ep	*ep = NULL;
293878ed226SJulian Elischer 	ng_l2cap_con_p		 con = NULL;
294878ed226SJulian Elischer 	int			 error = 0;
295878ed226SJulian Elischer 
296878ed226SJulian Elischer 	/* Check message */
297878ed226SJulian Elischer 	if (msg->header.arglen != sizeof(*ep)) {
298878ed226SJulian Elischer 		NG_L2CAP_ALERT(
299878ed226SJulian Elischer "%s: %s - invalid LP_DisconnectInd message size\n",
300878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node));
301878ed226SJulian Elischer 		error = EMSGSIZE;
302878ed226SJulian Elischer 		goto out;
303878ed226SJulian Elischer 	}
304878ed226SJulian Elischer 
305878ed226SJulian Elischer 	ep = (ng_hci_lp_discon_ind_ep *) (msg->data);
306878ed226SJulian Elischer 
307878ed226SJulian Elischer 	/* Check if we have this connection */
308878ed226SJulian Elischer 	con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
309878ed226SJulian Elischer 	if (con == NULL) {
310878ed226SJulian Elischer 		NG_L2CAP_ERR(
311878ed226SJulian Elischer "%s: %s - unexpected LP_DisconnectInd event. " \
312878ed226SJulian Elischer "Connection does not exist, con_handle=%d\n",
313878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
314878ed226SJulian Elischer 		error = ENOENT;
315878ed226SJulian Elischer 		goto out;
316878ed226SJulian Elischer 	}
317878ed226SJulian Elischer 
318878ed226SJulian Elischer 	/* XXX Verify connection state -- do we need to check this? */
319878ed226SJulian Elischer 	if (con->state != NG_L2CAP_CON_OPEN) {
320878ed226SJulian Elischer 		NG_L2CAP_ERR(
321878ed226SJulian Elischer "%s: %s - unexpected LP_DisconnectInd event. " \
322878ed226SJulian Elischer "Invalid connection state, state=%d, con_handle=%d\n",
323878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), con->state,
324878ed226SJulian Elischer 			con->con_handle);
325878ed226SJulian Elischer 		error = EINVAL;
326878ed226SJulian Elischer 		goto out;
327878ed226SJulian Elischer 	}
328878ed226SJulian Elischer 
3290986ab12SMaksim Yevmenkin 	/*
3300986ab12SMaksim Yevmenkin 	 * Notify upper layer and remove connection
3310986ab12SMaksim Yevmenkin 	 * Note: The connection could have auto disconnect timeout set. Try
3320986ab12SMaksim Yevmenkin 	 * to remove it. If auto disconnect timeout happened then ignore
3330986ab12SMaksim Yevmenkin 	 * disconnect indication and let timeout handle that.
3340986ab12SMaksim Yevmenkin 	 */
3350986ab12SMaksim Yevmenkin 
336f2bb1caeSJulian Elischer 	if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)
3370986ab12SMaksim Yevmenkin 		if ((error = ng_l2cap_discon_untimeout(con)) != 0)
3380986ab12SMaksim Yevmenkin 			return (error);
339f2bb1caeSJulian Elischer 
340878ed226SJulian Elischer 	ng_l2cap_con_fail(con, ep->reason);
341878ed226SJulian Elischer out:
342878ed226SJulian Elischer 	return (error);
343878ed226SJulian Elischer } /* ng_l2cap_lp_discon_ind */
344878ed226SJulian Elischer 
345878ed226SJulian Elischer /*
346878ed226SJulian Elischer  * Send LP_QoSSetupReq event to the lower layer protocol
347878ed226SJulian Elischer  */
348878ed226SJulian Elischer 
349878ed226SJulian Elischer int
ng_l2cap_lp_qos_req(ng_l2cap_p l2cap,u_int16_t con_handle,ng_l2cap_flow_p flow)350878ed226SJulian Elischer ng_l2cap_lp_qos_req(ng_l2cap_p l2cap, u_int16_t con_handle,
351878ed226SJulian Elischer 		ng_l2cap_flow_p flow)
352878ed226SJulian Elischer {
353878ed226SJulian Elischer 	struct ng_mesg		*msg = NULL;
354878ed226SJulian Elischer 	ng_hci_lp_qos_req_ep	*ep = NULL;
355878ed226SJulian Elischer 	ng_l2cap_con_p		 con = NULL;
356878ed226SJulian Elischer 	int			 error = 0;
357878ed226SJulian Elischer 
358878ed226SJulian Elischer 	/* Verify that we have this connection */
359878ed226SJulian Elischer 	con = ng_l2cap_con_by_handle(l2cap, con_handle);
360878ed226SJulian Elischer 	if (con == NULL) {
361878ed226SJulian Elischer 		NG_L2CAP_ERR(
362878ed226SJulian Elischer "%s: %s - unexpected LP_QoSSetupReq event. " \
363878ed226SJulian Elischer "Connection does not exist, con_handle=%d\n",
364878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), con_handle);
365878ed226SJulian Elischer 
366878ed226SJulian Elischer 		return (ENOENT);
367878ed226SJulian Elischer 	}
368878ed226SJulian Elischer 
369878ed226SJulian Elischer 	/* Verify connection state */
370878ed226SJulian Elischer 	if (con->state != NG_L2CAP_CON_OPEN) {
371878ed226SJulian Elischer 		NG_L2CAP_ERR(
372878ed226SJulian Elischer "%s: %s - unexpected LP_QoSSetupReq event. " \
373878ed226SJulian Elischer "Invalid connection state, state=%d, con_handle=%d\n",
374878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), con->state,
375878ed226SJulian Elischer 			con->con_handle);
376878ed226SJulian Elischer 
377878ed226SJulian Elischer 		return (EINVAL);
378878ed226SJulian Elischer 	}
379878ed226SJulian Elischer 
380878ed226SJulian Elischer 	/* Check if lower layer protocol is still connected */
381878ed226SJulian Elischer 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
382878ed226SJulian Elischer 		NG_L2CAP_ERR(
383878ed226SJulian Elischer "%s: %s - hook \"%s\" is not connected or valid",
384878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
385878ed226SJulian Elischer 
386878ed226SJulian Elischer 		return (ENOTCONN);
387878ed226SJulian Elischer 	}
388878ed226SJulian Elischer 
389878ed226SJulian Elischer 	/* Create and send LP_QoSSetupReq event */
390878ed226SJulian Elischer 	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_REQ,
391878ed226SJulian Elischer 		sizeof(*ep), M_NOWAIT);
392878ed226SJulian Elischer 	if (msg == NULL)
393878ed226SJulian Elischer 		return (ENOMEM);
394878ed226SJulian Elischer 
395878ed226SJulian Elischer 	ep = (ng_hci_lp_qos_req_ep *) (msg->data);
396878ed226SJulian Elischer 	ep->con_handle = con_handle;
397878ed226SJulian Elischer 	ep->flags = flow->flags;
398878ed226SJulian Elischer 	ep->service_type = flow->service_type;
399878ed226SJulian Elischer 	ep->token_rate = flow->token_rate;
400878ed226SJulian Elischer 	ep->peak_bandwidth = flow->peak_bandwidth;
401878ed226SJulian Elischer 	ep->latency = flow->latency;
402878ed226SJulian Elischer 	ep->delay_variation = flow->delay_variation;
403878ed226SJulian Elischer 
4044ae439a3SMaksim Yevmenkin 	NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
405878ed226SJulian Elischer 
406878ed226SJulian Elischer 	return (error);
407878ed226SJulian Elischer } /* ng_l2cap_lp_con_req */
408878ed226SJulian Elischer 
409878ed226SJulian Elischer /*
410878ed226SJulian Elischer  * Process LP_QoSSetupCfm from the lower layer protocol
411878ed226SJulian Elischer  */
412878ed226SJulian Elischer 
413878ed226SJulian Elischer int
ng_l2cap_lp_qos_cfm(ng_l2cap_p l2cap,struct ng_mesg * msg)414878ed226SJulian Elischer ng_l2cap_lp_qos_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg)
415878ed226SJulian Elischer {
416878ed226SJulian Elischer 	ng_hci_lp_qos_cfm_ep	*ep = NULL;
417878ed226SJulian Elischer 	int			 error = 0;
418878ed226SJulian Elischer 
419878ed226SJulian Elischer 	/* Check message */
420878ed226SJulian Elischer 	if (msg->header.arglen != sizeof(*ep)) {
421878ed226SJulian Elischer 		NG_L2CAP_ALERT(
422878ed226SJulian Elischer "%s: %s - invalid LP_QoSSetupCfm[Neg] message size\n",
423878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node));
424878ed226SJulian Elischer 		error = EMSGSIZE;
425878ed226SJulian Elischer 		goto out;
426878ed226SJulian Elischer 	}
427878ed226SJulian Elischer 
428878ed226SJulian Elischer 	ep = (ng_hci_lp_qos_cfm_ep *) (msg->data);
429878ed226SJulian Elischer 	/* XXX FIXME do something */
430878ed226SJulian Elischer out:
431878ed226SJulian Elischer 	return (error);
432878ed226SJulian Elischer } /* ng_l2cap_lp_qos_cfm */
433878ed226SJulian Elischer 
434878ed226SJulian Elischer /*
435878ed226SJulian Elischer  * Process LP_QoSViolationInd event from the lower layer protocol. Lower
436878ed226SJulian Elischer  * layer protocol has detected QoS Violation, so we MUST notify the
437878ed226SJulian Elischer  * upper layer.
438878ed226SJulian Elischer  */
439878ed226SJulian Elischer 
440878ed226SJulian Elischer int
ng_l2cap_lp_qos_ind(ng_l2cap_p l2cap,struct ng_mesg * msg)441878ed226SJulian Elischer ng_l2cap_lp_qos_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
442878ed226SJulian Elischer {
443878ed226SJulian Elischer 	ng_hci_lp_qos_ind_ep	*ep = NULL;
444878ed226SJulian Elischer 	ng_l2cap_con_p		 con = NULL;
445878ed226SJulian Elischer 	int			 error = 0;
446878ed226SJulian Elischer 
447878ed226SJulian Elischer 	/* Check message */
448878ed226SJulian Elischer 	if (msg->header.arglen != sizeof(*ep)) {
449878ed226SJulian Elischer 		NG_L2CAP_ALERT(
450878ed226SJulian Elischer "%s: %s - invalid LP_QoSViolation message size\n",
451878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node));
452878ed226SJulian Elischer 		error = EMSGSIZE;
453878ed226SJulian Elischer 		goto out;
454878ed226SJulian Elischer 	}
455878ed226SJulian Elischer 
456878ed226SJulian Elischer 	ep = (ng_hci_lp_qos_ind_ep *) (msg->data);
457878ed226SJulian Elischer 
458878ed226SJulian Elischer 	/* Check if we have this connection */
459878ed226SJulian Elischer 	con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
460878ed226SJulian Elischer 	if (con == NULL) {
461878ed226SJulian Elischer 		NG_L2CAP_ERR(
462878ed226SJulian Elischer "%s: %s - unexpected LP_QoSViolationInd event. " \
463878ed226SJulian Elischer "Connection does not exist, con_handle=%d\n",
464878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
465878ed226SJulian Elischer 		error = ENOENT;
466878ed226SJulian Elischer 		goto out;
467878ed226SJulian Elischer 	}
468878ed226SJulian Elischer 
469878ed226SJulian Elischer 	/* Verify connection state */
470878ed226SJulian Elischer 	if (con->state != NG_L2CAP_CON_OPEN) {
471878ed226SJulian Elischer 		NG_L2CAP_ERR(
472878ed226SJulian Elischer "%s: %s - unexpected LP_QoSViolationInd event. " \
473878ed226SJulian Elischer "Invalid connection state, state=%d, con_handle=%d\n",
474878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), con->state,
475878ed226SJulian Elischer 			con->con_handle);
476878ed226SJulian Elischer 		error = EINVAL;
477878ed226SJulian Elischer 		goto out;
478878ed226SJulian Elischer 	}
479878ed226SJulian Elischer 
480878ed226SJulian Elischer 	/* XXX FIXME Notify upper layer and terminate channels if required */
481878ed226SJulian Elischer out:
482878ed226SJulian Elischer 	return (error);
483878ed226SJulian Elischer } /* ng_l2cap_qos_ind */
484878ed226SJulian Elischer 
4853a601a23STakanori Watanabe int
ng_l2cap_lp_enc_change(ng_l2cap_p l2cap,struct ng_mesg * msg)4863a601a23STakanori Watanabe ng_l2cap_lp_enc_change(ng_l2cap_p l2cap, struct ng_mesg *msg)
4873a601a23STakanori Watanabe {
4883a601a23STakanori Watanabe 	ng_hci_lp_enc_change_ep	*ep = NULL;
4893a601a23STakanori Watanabe 	ng_l2cap_con_p		 con = NULL;
4903a601a23STakanori Watanabe 	int			 error = 0;
4913a601a23STakanori Watanabe 	ng_l2cap_chan_p 	 ch = NULL;
4923a601a23STakanori Watanabe 	/* Check message */
4933a601a23STakanori Watanabe 	if (msg->header.arglen != sizeof(*ep)) {
4943a601a23STakanori Watanabe 		NG_L2CAP_ALERT(
4953a601a23STakanori Watanabe "%s: %s - invalid LP_ENCChange message size\n",
4963a601a23STakanori Watanabe 			__func__, NG_NODE_NAME(l2cap->node));
4973a601a23STakanori Watanabe 		error = EMSGSIZE;
4983a601a23STakanori Watanabe 		goto out;
4993a601a23STakanori Watanabe 	}
5003a601a23STakanori Watanabe 
5013a601a23STakanori Watanabe 	ep = (ng_hci_lp_enc_change_ep *) (msg->data);
5023a601a23STakanori Watanabe 
5033a601a23STakanori Watanabe 	/* Check if we have this connection */
5043a601a23STakanori Watanabe 	con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
5053a601a23STakanori Watanabe 	if (con == NULL) {
5063a601a23STakanori Watanabe 		NG_L2CAP_ERR(
5073a601a23STakanori Watanabe "%s: %s - unexpected LP_Enc Change Event. " \
5083a601a23STakanori Watanabe "Connection does not exist, con_handle=%d\n",
5093a601a23STakanori Watanabe 			__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
5103a601a23STakanori Watanabe 		error = ENOENT;
5113a601a23STakanori Watanabe 		goto out;
5123a601a23STakanori Watanabe 	}
5133a601a23STakanori Watanabe 
5143a601a23STakanori Watanabe 	/* Verify connection state */
5153a601a23STakanori Watanabe 	if (con->state != NG_L2CAP_CON_OPEN) {
5163a601a23STakanori Watanabe 		NG_L2CAP_ERR(
5173a601a23STakanori Watanabe "%s: %s - unexpected ENC_CHANGE event. " \
5183a601a23STakanori Watanabe "Invalid connection state, state=%d, con_handle=%d\n",
5193a601a23STakanori Watanabe 			__func__, NG_NODE_NAME(l2cap->node), con->state,
5203a601a23STakanori Watanabe 			con->con_handle);
5213a601a23STakanori Watanabe 		error = EINVAL;
5223a601a23STakanori Watanabe 		goto out;
5233a601a23STakanori Watanabe 	}
5243a601a23STakanori Watanabe 
5253a601a23STakanori Watanabe 	con->encryption = ep->status;
5263a601a23STakanori Watanabe 
5273a601a23STakanori Watanabe 	LIST_FOREACH(ch, &l2cap->chan_list, next){
5283a601a23STakanori Watanabe 		if((ch->con->con_handle == ep->con_handle) &&
5293a601a23STakanori Watanabe 		   (ch->con->linktype == ep->link_type))
5303a601a23STakanori Watanabe 			ng_l2cap_l2ca_encryption_change(ch, ep->status);
5313a601a23STakanori Watanabe 	}
5323a601a23STakanori Watanabe 
5333a601a23STakanori Watanabe out:
5343a601a23STakanori Watanabe 	return (error);
5353a601a23STakanori Watanabe } /* ng_l2cap_enc_change */
5363a601a23STakanori Watanabe 
537878ed226SJulian Elischer /*
538878ed226SJulian Elischer  * Prepare L2CAP packet. Prepend packet with L2CAP packet header and then
539878ed226SJulian Elischer  * segment it according to HCI MTU.
540878ed226SJulian Elischer  */
541878ed226SJulian Elischer 
542878ed226SJulian Elischer int
ng_l2cap_lp_send(ng_l2cap_con_p con,u_int16_t dcid,struct mbuf * m0)543878ed226SJulian Elischer ng_l2cap_lp_send(ng_l2cap_con_p con, u_int16_t dcid, struct mbuf *m0)
544878ed226SJulian Elischer {
545878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
546878ed226SJulian Elischer 	ng_l2cap_hdr_t		*l2cap_hdr = NULL;
547878ed226SJulian Elischer         ng_hci_acldata_pkt_t	*acl_hdr = NULL;
548878ed226SJulian Elischer         struct mbuf		*m_last = NULL, *m = NULL;
549b8c46d56STakanori Watanabe         int			 len, flag = (con->linktype == NG_HCI_LINK_ACL) ? NG_HCI_PACKET_START : NG_HCI_LE_PACKET_START;
550878ed226SJulian Elischer 
551878ed226SJulian Elischer 	KASSERT((con->tx_pkt == NULL),
552878ed226SJulian Elischer ("%s: %s - another packet pending?!\n", __func__, NG_NODE_NAME(l2cap->node)));
553878ed226SJulian Elischer 	KASSERT((l2cap->pkt_size > 0),
554878ed226SJulian Elischer ("%s: %s - invalid l2cap->pkt_size?!\n", __func__, NG_NODE_NAME(l2cap->node)));
555878ed226SJulian Elischer 
556878ed226SJulian Elischer 	/* Prepend mbuf with L2CAP header */
557878ed226SJulian Elischer 	m0 = ng_l2cap_prepend(m0, sizeof(*l2cap_hdr));
558878ed226SJulian Elischer 	if (m0 == NULL) {
559878ed226SJulian Elischer 		NG_L2CAP_ALERT(
5604ae439a3SMaksim Yevmenkin "%s: %s - ng_l2cap_prepend(%zd) failed\n",
561878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node),
562878ed226SJulian Elischer 			sizeof(*l2cap_hdr));
563878ed226SJulian Elischer 
564878ed226SJulian Elischer 		goto fail;
565878ed226SJulian Elischer 	}
566878ed226SJulian Elischer 
567878ed226SJulian Elischer 	l2cap_hdr = mtod(m0, ng_l2cap_hdr_t *);
568878ed226SJulian Elischer 	l2cap_hdr->length = htole16(m0->m_pkthdr.len - sizeof(*l2cap_hdr));
569878ed226SJulian Elischer 	l2cap_hdr->dcid = htole16(dcid);
570878ed226SJulian Elischer 
571878ed226SJulian Elischer 	/*
572878ed226SJulian Elischer 	 * Segment single L2CAP packet according to the HCI layer MTU. Convert
573878ed226SJulian Elischer 	 * each segment into ACL data packet and prepend it with ACL data packet
574878ed226SJulian Elischer 	 * header. Link all segments together via m_nextpkt link.
575878ed226SJulian Elischer  	 *
576878ed226SJulian Elischer 	 * XXX BC (Broadcast flag) will always be 0 (zero).
577878ed226SJulian Elischer 	 */
578878ed226SJulian Elischer 
579878ed226SJulian Elischer 	while (m0 != NULL) {
580878ed226SJulian Elischer 		/* Check length of the packet against HCI MTU */
581878ed226SJulian Elischer 		len = m0->m_pkthdr.len;
582878ed226SJulian Elischer 		if (len > l2cap->pkt_size) {
583eb1b1807SGleb Smirnoff 			m = m_split(m0, l2cap->pkt_size, M_NOWAIT);
584878ed226SJulian Elischer 			if (m == NULL) {
585878ed226SJulian Elischer 				NG_L2CAP_ALERT(
586878ed226SJulian Elischer "%s: %s - m_split(%d) failed\n",	__func__, NG_NODE_NAME(l2cap->node),
587878ed226SJulian Elischer 					l2cap->pkt_size);
588878ed226SJulian Elischer 				goto fail;
589878ed226SJulian Elischer 			}
590878ed226SJulian Elischer 
591878ed226SJulian Elischer 			len = l2cap->pkt_size;
592878ed226SJulian Elischer 		}
593878ed226SJulian Elischer 
594878ed226SJulian Elischer 		/* Convert packet fragment into ACL data packet */
595878ed226SJulian Elischer 		m0 = ng_l2cap_prepend(m0, sizeof(*acl_hdr));
596878ed226SJulian Elischer 		if (m0 == NULL) {
597878ed226SJulian Elischer 			NG_L2CAP_ALERT(
5984ae439a3SMaksim Yevmenkin "%s: %s - ng_l2cap_prepend(%zd) failed\n",
599878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node),
600878ed226SJulian Elischer 				sizeof(*acl_hdr));
601878ed226SJulian Elischer 			goto fail;
602878ed226SJulian Elischer 		}
603878ed226SJulian Elischer 
604878ed226SJulian Elischer 		acl_hdr = mtod(m0, ng_hci_acldata_pkt_t *);
605878ed226SJulian Elischer 		acl_hdr->type = NG_HCI_ACL_DATA_PKT;
606878ed226SJulian Elischer 		acl_hdr->length = htole16(len);
607878ed226SJulian Elischer 		acl_hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE(
608878ed226SJulian Elischer 					con->con_handle, flag, 0));
609878ed226SJulian Elischer 
610878ed226SJulian Elischer 		/* Add fragment to the chain */
611878ed226SJulian Elischer 		m0->m_nextpkt = NULL;
612878ed226SJulian Elischer 
613878ed226SJulian Elischer 		if (con->tx_pkt == NULL)
614878ed226SJulian Elischer 			con->tx_pkt = m_last = m0;
615878ed226SJulian Elischer 		else {
616878ed226SJulian Elischer 			m_last->m_nextpkt = m0;
617878ed226SJulian Elischer 			m_last = m0;
618878ed226SJulian Elischer 		}
619878ed226SJulian Elischer 
620878ed226SJulian Elischer 		NG_L2CAP_INFO(
621878ed226SJulian Elischer "%s: %s - attaching ACL packet, con_handle=%d, PB=%#x, length=%d\n",
622878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), con->con_handle,
623878ed226SJulian Elischer 			flag, len);
624878ed226SJulian Elischer 
625878ed226SJulian Elischer 		m0 = m;
626878ed226SJulian Elischer 		m = NULL;
627878ed226SJulian Elischer 		flag = NG_HCI_PACKET_FRAGMENT;
628878ed226SJulian Elischer 	}
629878ed226SJulian Elischer 
630878ed226SJulian Elischer 	return (0);
631878ed226SJulian Elischer fail:
632878ed226SJulian Elischer 	NG_FREE_M(m0);
633878ed226SJulian Elischer 	NG_FREE_M(m);
634878ed226SJulian Elischer 
635878ed226SJulian Elischer 	while (con->tx_pkt != NULL) {
636878ed226SJulian Elischer 		m = con->tx_pkt->m_nextpkt;
637878ed226SJulian Elischer 		m_freem(con->tx_pkt);
638878ed226SJulian Elischer 		con->tx_pkt = m;
639878ed226SJulian Elischer 	}
640878ed226SJulian Elischer 
641878ed226SJulian Elischer 	return (ENOBUFS);
642878ed226SJulian Elischer } /* ng_l2cap_lp_send */
643878ed226SJulian Elischer 
644878ed226SJulian Elischer /*
645878ed226SJulian Elischer  * Receive ACL data packet from the HCI layer. First strip ACL packet header
646878ed226SJulian Elischer  * and get connection handle, PB (Packet Boundary) flag and payload length.
647878ed226SJulian Elischer  * Then find connection descriptor and verify its state. Then process ACL
648878ed226SJulian Elischer  * packet as follows.
649878ed226SJulian Elischer  *
650878ed226SJulian Elischer  * 1) If we got first segment (pb == NG_HCI_PACKET_START) then extract L2CAP
651878ed226SJulian Elischer  *    header and get total length of the L2CAP packet. Then start new L2CAP
652878ed226SJulian Elischer  *    packet.
653878ed226SJulian Elischer  *
654878ed226SJulian Elischer  * 2) If we got other (then first :) segment (pb == NG_HCI_PACKET_FRAGMENT)
655878ed226SJulian Elischer  *    then add segment to the packet.
656878ed226SJulian Elischer  */
657878ed226SJulian Elischer 
658878ed226SJulian Elischer int
ng_l2cap_lp_receive(ng_l2cap_p l2cap,struct mbuf * m)659878ed226SJulian Elischer ng_l2cap_lp_receive(ng_l2cap_p l2cap, struct mbuf *m)
660878ed226SJulian Elischer {
661878ed226SJulian Elischer 	ng_hci_acldata_pkt_t	*acl_hdr = NULL;
662878ed226SJulian Elischer 	ng_l2cap_hdr_t		*l2cap_hdr = NULL;
663878ed226SJulian Elischer 	ng_l2cap_con_p		 con = NULL;
664878ed226SJulian Elischer 	u_int16_t		 con_handle, length, pb;
665878ed226SJulian Elischer 	int			 error = 0;
666878ed226SJulian Elischer 
667878ed226SJulian Elischer 	/* Check ACL data packet */
668878ed226SJulian Elischer 	if (m->m_pkthdr.len < sizeof(*acl_hdr)) {
669878ed226SJulian Elischer 		NG_L2CAP_ERR(
670878ed226SJulian Elischer "%s: %s - invalid ACL data packet. Packet too small, length=%d\n",
671878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), m->m_pkthdr.len);
672878ed226SJulian Elischer 		error = EMSGSIZE;
673878ed226SJulian Elischer 		goto drop;
674878ed226SJulian Elischer 	}
675878ed226SJulian Elischer 
676878ed226SJulian Elischer 	/* Strip ACL data packet header */
677878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(m, sizeof(*acl_hdr));
678878ed226SJulian Elischer 	if (m == NULL)
679878ed226SJulian Elischer 		return (ENOBUFS);
680878ed226SJulian Elischer 
681878ed226SJulian Elischer 	acl_hdr = mtod(m, ng_hci_acldata_pkt_t *);
682878ed226SJulian Elischer 	m_adj(m, sizeof(*acl_hdr));
683878ed226SJulian Elischer 
684878ed226SJulian Elischer 	/* Get ACL connection handle, PB flag and payload length */
685878ed226SJulian Elischer 	acl_hdr->con_handle = le16toh(acl_hdr->con_handle);
686878ed226SJulian Elischer 	con_handle = NG_HCI_CON_HANDLE(acl_hdr->con_handle);
687878ed226SJulian Elischer 	pb = NG_HCI_PB_FLAG(acl_hdr->con_handle);
688878ed226SJulian Elischer 	length = le16toh(acl_hdr->length);
689878ed226SJulian Elischer 
690878ed226SJulian Elischer 	NG_L2CAP_INFO(
691878ed226SJulian Elischer "%s: %s - got ACL data packet, con_handle=%d, PB=%#x, length=%d\n",
692878ed226SJulian Elischer 		__func__, NG_NODE_NAME(l2cap->node), con_handle, pb, length);
693878ed226SJulian Elischer 
694878ed226SJulian Elischer 	/* Get connection descriptor */
695878ed226SJulian Elischer 	con = ng_l2cap_con_by_handle(l2cap, con_handle);
696878ed226SJulian Elischer 	if (con == NULL) {
697878ed226SJulian Elischer 		NG_L2CAP_ERR(
698878ed226SJulian Elischer "%s: %s - unexpected ACL data packet. " \
699878ed226SJulian Elischer "Connection does not exist, con_handle=%d\n",
700878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), con_handle);
701878ed226SJulian Elischer 		error = ENOENT;
702878ed226SJulian Elischer 		goto drop;
703878ed226SJulian Elischer 	}
704878ed226SJulian Elischer 
705878ed226SJulian Elischer 	/* Verify connection state */
706878ed226SJulian Elischer 	if (con->state != NG_L2CAP_CON_OPEN) {
707878ed226SJulian Elischer 		NG_L2CAP_ERR(
708878ed226SJulian Elischer "%s: %s - unexpected ACL data packet. Invalid connection state=%d\n",
709878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), con->state);
710878ed226SJulian Elischer 		error = EHOSTDOWN;
711878ed226SJulian Elischer 		goto drop;
712878ed226SJulian Elischer 	}
713878ed226SJulian Elischer 
714878ed226SJulian Elischer 	/* Process packet */
715b8c46d56STakanori Watanabe 	if ((pb == NG_HCI_PACKET_START) || (pb == NG_HCI_LE_PACKET_START))
716b8c46d56STakanori Watanabe 	  {
717878ed226SJulian Elischer 		if (con->rx_pkt != NULL) {
718878ed226SJulian Elischer 			NG_L2CAP_ERR(
719878ed226SJulian Elischer "%s: %s - dropping incomplete L2CAP packet, got %d bytes, want %d bytes\n",
720878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node),
721878ed226SJulian Elischer 				con->rx_pkt->m_pkthdr.len, con->rx_pkt_len);
722878ed226SJulian Elischer 			NG_FREE_M(con->rx_pkt);
723878ed226SJulian Elischer 			con->rx_pkt_len = 0;
724878ed226SJulian Elischer 		}
725878ed226SJulian Elischer 
726878ed226SJulian Elischer 		/* Get L2CAP header */
727878ed226SJulian Elischer 		if (m->m_pkthdr.len < sizeof(*l2cap_hdr)) {
728878ed226SJulian Elischer 			NG_L2CAP_ERR(
729878ed226SJulian Elischer "%s: %s - invalid L2CAP packet start fragment. Packet too small, length=%d\n",
730878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node),
731878ed226SJulian Elischer 				m->m_pkthdr.len);
732878ed226SJulian Elischer 			error = EMSGSIZE;
733878ed226SJulian Elischer 			goto drop;
734878ed226SJulian Elischer 		}
735878ed226SJulian Elischer 
736878ed226SJulian Elischer 		NG_L2CAP_M_PULLUP(m, sizeof(*l2cap_hdr));
737878ed226SJulian Elischer 		if (m == NULL)
738878ed226SJulian Elischer 			return (ENOBUFS);
739878ed226SJulian Elischer 
740878ed226SJulian Elischer 		l2cap_hdr = mtod(m, ng_l2cap_hdr_t *);
741878ed226SJulian Elischer 
742878ed226SJulian Elischer 		NG_L2CAP_INFO(
743878ed226SJulian Elischer "%s: %s - staring new L2CAP packet, con_handle=%d, length=%d\n",
744878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), con_handle,
745878ed226SJulian Elischer 			le16toh(l2cap_hdr->length));
746878ed226SJulian Elischer 
747878ed226SJulian Elischer 		/* Start new L2CAP packet */
748878ed226SJulian Elischer 		con->rx_pkt = m;
749878ed226SJulian Elischer 		con->rx_pkt_len = le16toh(l2cap_hdr->length)+sizeof(*l2cap_hdr);
750878ed226SJulian Elischer 	} else if (pb == NG_HCI_PACKET_FRAGMENT) {
751878ed226SJulian Elischer 		if (con->rx_pkt == NULL) {
752878ed226SJulian Elischer 			NG_L2CAP_ERR(
753878ed226SJulian Elischer "%s: %s - unexpected ACL data packet fragment, con_handle=%d\n",
754878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node),
755878ed226SJulian Elischer 				con->con_handle);
756878ed226SJulian Elischer 			goto drop;
757878ed226SJulian Elischer 		}
758878ed226SJulian Elischer 
759878ed226SJulian Elischer 		/* Add fragment to the L2CAP packet */
760878ed226SJulian Elischer 		m_cat(con->rx_pkt, m);
761878ed226SJulian Elischer 		con->rx_pkt->m_pkthdr.len += length;
762878ed226SJulian Elischer 	} else {
763878ed226SJulian Elischer 		NG_L2CAP_ERR(
764878ed226SJulian Elischer "%s: %s - invalid ACL data packet. Invalid PB flag=%#x\n",
765878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), pb);
766878ed226SJulian Elischer 		error = EINVAL;
767878ed226SJulian Elischer 		goto drop;
768878ed226SJulian Elischer 	}
769878ed226SJulian Elischer 
770878ed226SJulian Elischer 	con->rx_pkt_len -= length;
771878ed226SJulian Elischer 	if (con->rx_pkt_len < 0) {
772878ed226SJulian Elischer 		NG_L2CAP_ALERT(
773878ed226SJulian Elischer "%s: %s - packet length mismatch. Got %d bytes, offset %d bytes\n",
774878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node),
775878ed226SJulian Elischer 			con->rx_pkt->m_pkthdr.len, con->rx_pkt_len);
776878ed226SJulian Elischer 		NG_FREE_M(con->rx_pkt);
777878ed226SJulian Elischer 		con->rx_pkt_len = 0;
778878ed226SJulian Elischer 	} else if (con->rx_pkt_len == 0) {
779878ed226SJulian Elischer 		/* OK, we have got complete L2CAP packet, so process it */
780878ed226SJulian Elischer 		error = ng_l2cap_receive(con);
781878ed226SJulian Elischer 		con->rx_pkt = NULL;
782878ed226SJulian Elischer 		con->rx_pkt_len = 0;
783878ed226SJulian Elischer 	}
784878ed226SJulian Elischer 
785878ed226SJulian Elischer 	return (error);
786878ed226SJulian Elischer 
787878ed226SJulian Elischer drop:
788878ed226SJulian Elischer 	NG_FREE_M(m);
789878ed226SJulian Elischer 
790878ed226SJulian Elischer 	return (error);
791878ed226SJulian Elischer } /* ng_l2cap_lp_receive */
792878ed226SJulian Elischer 
793878ed226SJulian Elischer /*
794878ed226SJulian Elischer  * Send queued ACL packets to the HCI layer
795878ed226SJulian Elischer  */
796878ed226SJulian Elischer 
797878ed226SJulian Elischer void
ng_l2cap_lp_deliver(ng_l2cap_con_p con)798878ed226SJulian Elischer ng_l2cap_lp_deliver(ng_l2cap_con_p con)
799878ed226SJulian Elischer {
800878ed226SJulian Elischer 	ng_l2cap_p	 l2cap = con->l2cap;
801878ed226SJulian Elischer 	struct mbuf	*m = NULL;
802878ed226SJulian Elischer 	int		 error;
803878ed226SJulian Elischer 
804878ed226SJulian Elischer 	/* Check connection */
805878ed226SJulian Elischer 	if (con->state != NG_L2CAP_CON_OPEN)
806878ed226SJulian Elischer 		return;
807878ed226SJulian Elischer 
808878ed226SJulian Elischer 	if (con->tx_pkt == NULL)
809878ed226SJulian Elischer 		ng_l2cap_con_wakeup(con);
810878ed226SJulian Elischer 
811878ed226SJulian Elischer 	if (con->tx_pkt == NULL)
812878ed226SJulian Elischer 		return;
813878ed226SJulian Elischer 
814878ed226SJulian Elischer 	/* Check if lower layer protocol is still connected */
815878ed226SJulian Elischer 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
816878ed226SJulian Elischer 		NG_L2CAP_ERR(
817878ed226SJulian Elischer "%s: %s - hook \"%s\" is not connected or valid",
818878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
819878ed226SJulian Elischer 
820878ed226SJulian Elischer 		goto drop; /* XXX what to do with "pending"? */
821878ed226SJulian Elischer 	}
822878ed226SJulian Elischer 
823878ed226SJulian Elischer 	/* Send ACL data packets */
824878ed226SJulian Elischer 	while (con->pending < con->l2cap->num_pkts && con->tx_pkt != NULL) {
825878ed226SJulian Elischer 		m = con->tx_pkt;
826878ed226SJulian Elischer 		con->tx_pkt = con->tx_pkt->m_nextpkt;
827878ed226SJulian Elischer 		m->m_nextpkt = NULL;
828878ed226SJulian Elischer 
829fbc48c2bSTakanori Watanabe 		if(m->m_flags &M_PROTO2){
830fbc48c2bSTakanori Watanabe 			ng_l2cap_lp_receive(con->l2cap, m);
831fbc48c2bSTakanori Watanabe 			continue;
832fbc48c2bSTakanori Watanabe 		}
833878ed226SJulian Elischer 		NG_L2CAP_INFO(
834878ed226SJulian Elischer "%s: %s - sending ACL packet, con_handle=%d, len=%d\n",
835878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), con->con_handle,
836878ed226SJulian Elischer 			m->m_pkthdr.len);
837878ed226SJulian Elischer 
838878ed226SJulian Elischer 		NG_SEND_DATA_ONLY(error, l2cap->hci, m);
839878ed226SJulian Elischer 		if (error != 0) {
840878ed226SJulian Elischer 			NG_L2CAP_ERR(
841878ed226SJulian Elischer "%s: %s - could not send ACL data packet, con_handle=%d, error=%d\n",
842878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node),
843878ed226SJulian Elischer 				con->con_handle, error);
844878ed226SJulian Elischer 
845878ed226SJulian Elischer 			goto drop; /* XXX what to do with "pending"? */
846878ed226SJulian Elischer 		}
847878ed226SJulian Elischer 
848878ed226SJulian Elischer 		con->pending ++;
849878ed226SJulian Elischer 	}
850878ed226SJulian Elischer 
851878ed226SJulian Elischer 	NG_L2CAP_INFO(
852878ed226SJulian Elischer "%s: %s - %d ACL packets have been sent, con_handle=%d\n",
853878ed226SJulian Elischer 		__func__, NG_NODE_NAME(l2cap->node), con->pending,
854878ed226SJulian Elischer 		con->con_handle);
855878ed226SJulian Elischer 
856878ed226SJulian Elischer 	return;
857878ed226SJulian Elischer 
858878ed226SJulian Elischer drop:
859878ed226SJulian Elischer 	while (con->tx_pkt != NULL) {
860878ed226SJulian Elischer 		m = con->tx_pkt->m_nextpkt;
861878ed226SJulian Elischer 		m_freem(con->tx_pkt);
862878ed226SJulian Elischer 		con->tx_pkt = m;
863878ed226SJulian Elischer 	}
864878ed226SJulian Elischer } /* ng_l2cap_lp_deliver */
865878ed226SJulian Elischer 
866878ed226SJulian Elischer /*
867878ed226SJulian Elischer  * Process connection timeout. Remove connection from the list. If there
868878ed226SJulian Elischer  * are any channels that wait for the connection then notify them. Free
869878ed226SJulian Elischer  * connection descriptor.
870878ed226SJulian Elischer  */
871878ed226SJulian Elischer 
872878ed226SJulian Elischer void
ng_l2cap_process_lp_timeout(node_p node,hook_p hook,void * arg1,int con_handle)8730986ab12SMaksim Yevmenkin ng_l2cap_process_lp_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
874878ed226SJulian Elischer {
8750986ab12SMaksim Yevmenkin 	ng_l2cap_p	l2cap = NULL;
8760986ab12SMaksim Yevmenkin 	ng_l2cap_con_p	con = NULL;
877878ed226SJulian Elischer 
8780986ab12SMaksim Yevmenkin 	if (NG_NODE_NOT_VALID(node)) {
8790986ab12SMaksim Yevmenkin 		printf("%s: Netgraph node is not valid\n", __func__);
8800986ab12SMaksim Yevmenkin 		return;
8810986ab12SMaksim Yevmenkin 	}
8820986ab12SMaksim Yevmenkin 
8830986ab12SMaksim Yevmenkin 	l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
8840986ab12SMaksim Yevmenkin 	con = ng_l2cap_con_by_handle(l2cap, con_handle);
8850986ab12SMaksim Yevmenkin 
8860986ab12SMaksim Yevmenkin 	if (con == NULL) {
8870986ab12SMaksim Yevmenkin 		NG_L2CAP_ALERT(
8880986ab12SMaksim Yevmenkin "%s: %s - could not find connection, con_handle=%d\n",
8890986ab12SMaksim Yevmenkin 			__func__, NG_NODE_NAME(node), con_handle);
8900986ab12SMaksim Yevmenkin 		return;
8910986ab12SMaksim Yevmenkin 	}
8920986ab12SMaksim Yevmenkin 
8930986ab12SMaksim Yevmenkin 	if (!(con->flags & NG_L2CAP_CON_LP_TIMO)) {
8940986ab12SMaksim Yevmenkin 		NG_L2CAP_ALERT(
8950986ab12SMaksim Yevmenkin "%s: %s - no pending LP timeout, con_handle=%d, state=%d, flags=%#x\n",
8960986ab12SMaksim Yevmenkin 			__func__, NG_NODE_NAME(node), con_handle, con->state,
8970986ab12SMaksim Yevmenkin 			con->flags);
8980986ab12SMaksim Yevmenkin 		return;
8990986ab12SMaksim Yevmenkin 	}
900878ed226SJulian Elischer 
901878ed226SJulian Elischer 	/*
902878ed226SJulian Elischer 	 * Notify channels that connection has timed out. This will remove
903878ed226SJulian Elischer 	 * connection, channels and pending commands.
904878ed226SJulian Elischer 	 */
905878ed226SJulian Elischer 
906f2bb1caeSJulian Elischer 	con->flags &= ~NG_L2CAP_CON_LP_TIMO;
907878ed226SJulian Elischer 	ng_l2cap_con_fail(con, NG_L2CAP_TIMEOUT);
908878ed226SJulian Elischer } /* ng_l2cap_process_lp_timeout */
909878ed226SJulian Elischer 
910f2bb1caeSJulian Elischer /*
911f2bb1caeSJulian Elischer  * Process auto disconnect timeout and send LP_DisconReq event to the
912f2bb1caeSJulian Elischer  * lower layer protocol
913f2bb1caeSJulian Elischer  */
914f2bb1caeSJulian Elischer 
915f2bb1caeSJulian Elischer void
ng_l2cap_process_discon_timeout(node_p node,hook_p hook,void * arg1,int con_handle)9160986ab12SMaksim Yevmenkin ng_l2cap_process_discon_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
917f2bb1caeSJulian Elischer {
9180986ab12SMaksim Yevmenkin 	ng_l2cap_p		 l2cap = NULL;
9190986ab12SMaksim Yevmenkin 	ng_l2cap_con_p		 con = NULL;
920f2bb1caeSJulian Elischer 	struct ng_mesg		*msg = NULL;
921f2bb1caeSJulian Elischer 	ng_hci_lp_discon_req_ep	*ep = NULL;
922f2bb1caeSJulian Elischer 	int			 error;
923f2bb1caeSJulian Elischer 
9240986ab12SMaksim Yevmenkin 	if (NG_NODE_NOT_VALID(node)) {
9250986ab12SMaksim Yevmenkin 		printf("%s: Netgraph node is not valid\n", __func__);
9260986ab12SMaksim Yevmenkin 		return;
9270986ab12SMaksim Yevmenkin 	}
9280986ab12SMaksim Yevmenkin 
9290986ab12SMaksim Yevmenkin 	l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
9300986ab12SMaksim Yevmenkin 	con = ng_l2cap_con_by_handle(l2cap, con_handle);
9310986ab12SMaksim Yevmenkin 
9320986ab12SMaksim Yevmenkin 	if (con == NULL) {
9330986ab12SMaksim Yevmenkin 		NG_L2CAP_ALERT(
9340986ab12SMaksim Yevmenkin "%s: %s - could not find connection, con_handle=%d\n",
9350986ab12SMaksim Yevmenkin 			__func__, NG_NODE_NAME(node), con_handle);
9360986ab12SMaksim Yevmenkin 		return;
9370986ab12SMaksim Yevmenkin 	}
9380986ab12SMaksim Yevmenkin 
9390986ab12SMaksim Yevmenkin 	if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)) {
9400986ab12SMaksim Yevmenkin 		NG_L2CAP_ALERT(
9410986ab12SMaksim Yevmenkin "%s: %s - no pending disconnect timeout, con_handle=%d, state=%d, flags=%#x\n",
9420986ab12SMaksim Yevmenkin 			__func__, NG_NODE_NAME(node), con_handle, con->state,
9430986ab12SMaksim Yevmenkin 			con->flags);
9440986ab12SMaksim Yevmenkin 		return;
9450986ab12SMaksim Yevmenkin 	}
9460986ab12SMaksim Yevmenkin 
947f2bb1caeSJulian Elischer 	con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO;
948f2bb1caeSJulian Elischer 
949f2bb1caeSJulian Elischer 	/* Check if lower layer protocol is still connected */
950f2bb1caeSJulian Elischer 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
951f2bb1caeSJulian Elischer 		NG_L2CAP_ERR(
952f2bb1caeSJulian Elischer "%s: %s - hook \"%s\" is not connected or valid\n",
953f2bb1caeSJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
954f2bb1caeSJulian Elischer 		return;
955f2bb1caeSJulian Elischer 	}
956f2bb1caeSJulian Elischer 
957f2bb1caeSJulian Elischer 	/* Create and send LP_DisconReq event */
958f2bb1caeSJulian Elischer 	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_REQ,
959f2bb1caeSJulian Elischer 		sizeof(*ep), M_NOWAIT);
960f2bb1caeSJulian Elischer 	if (msg == NULL)
961f2bb1caeSJulian Elischer 		return;
962f2bb1caeSJulian Elischer 
963f2bb1caeSJulian Elischer 	ep = (ng_hci_lp_discon_req_ep *) (msg->data);
964f2bb1caeSJulian Elischer 	ep->con_handle = con->con_handle;
965f2bb1caeSJulian Elischer 	ep->reason = 0x13; /* User Ended Connection */
966f2bb1caeSJulian Elischer 
9674ae439a3SMaksim Yevmenkin 	NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
968f2bb1caeSJulian Elischer } /* ng_l2cap_process_discon_timeout */
969