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