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