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