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