xref: /freebsd/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
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.5 2003/09/08 19:11:45 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 <netgraph/bluetooth/include/ng_bluetooth.h>
42 #include <netgraph/bluetooth/include/ng_hci.h>
43 #include <netgraph/bluetooth/include/ng_l2cap.h>
44 #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
45 #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
46 #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
47 #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
48 #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
49 #include <netgraph/bluetooth/l2cap/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, 0);
116 	if (error != 0) {
117 		if ((error = ng_l2cap_lp_untimeout(con)) != 0)
118 			return (error);
119 
120 		ng_l2cap_free_con(con);
121 	}
122 
123 	return (error);
124 } /* ng_l2cap_lp_con_req */
125 
126 /*
127  * Process LP_ConnectCfm event from the lower layer protocol. It could be
128  * positive or negative. Verify remote unit address then stop the timer and
129  * process event.
130  */
131 
132 int
133 ng_l2cap_lp_con_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg)
134 {
135 	ng_hci_lp_con_cfm_ep	*ep = NULL;
136 	ng_l2cap_con_p		 con = NULL;
137 	int			 error = 0;
138 
139 	/* Check message */
140 	if (msg->header.arglen != sizeof(*ep)) {
141 		NG_L2CAP_ALERT(
142 "%s: %s - invalid LP_ConnectCfm[Neg] message size\n",
143 			__func__, NG_NODE_NAME(l2cap->node));
144 		error = EMSGSIZE;
145 		goto out;
146 	}
147 
148 	ep = (ng_hci_lp_con_cfm_ep *) (msg->data);
149 
150 	/* Check if we have requested/accepted this connection */
151 	con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr);
152 	if (con == NULL) {
153 		NG_L2CAP_ERR(
154 "%s: %s - unexpected LP_ConnectCfm event. Connection does not exist\n",
155 			__func__, NG_NODE_NAME(l2cap->node));
156 		error = ENOENT;
157 		goto out;
158 	}
159 
160 	/* Check connection state */
161 	if (con->state != NG_L2CAP_W4_LP_CON_CFM) {
162 		NG_L2CAP_ALERT(
163 "%s: %s - unexpected LP_ConnectCfm event. " \
164 "Invalid connection state, state=%d, con_handle=%d\n",
165 			__func__, NG_NODE_NAME(l2cap->node), con->state,
166 			con->con_handle);
167 		error = EINVAL;
168 		goto out;
169 	}
170 
171 	/*
172 	 * Looks like it is our confirmation. It is safe now to cancel
173 	 * connection timer and notify upper layer. If timeout already
174 	 * happened then ignore connection confirmation and let timeout
175 	 * handle that.
176  	 */
177 
178 	if ((error = ng_l2cap_lp_untimeout(con)) != 0)
179 		goto out;
180 
181 	if (ep->status == 0) {
182 		con->state = NG_L2CAP_CON_OPEN;
183 		con->con_handle = ep->con_handle;
184 		ng_l2cap_lp_deliver(con);
185 	} else /* Negative confirmation - remove connection descriptor */
186 		ng_l2cap_con_fail(con, ep->status);
187 out:
188 	return (error);
189 } /* ng_l2cap_lp_con_cfm */
190 
191 /*
192  * Process LP_ConnectInd event from the lower layer protocol. This is a good
193  * place to put some extra check on remote unit address and/or class. We could
194  * even forward this information to control hook (or check against internal
195  * black list) and thus implement some kind of firewall. But for now be simple
196  * and create new connection descriptor, start timer and send LP_ConnectRsp
197  * event (i.e. accept connection).
198  */
199 
200 int
201 ng_l2cap_lp_con_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
202 {
203 	ng_hci_lp_con_ind_ep	*ep = NULL;
204 	ng_hci_lp_con_rsp_ep	*rp = NULL;
205 	struct ng_mesg		*rsp = NULL;
206 	ng_l2cap_con_p		 con = NULL;
207 	int			 error = 0;
208 
209 	/* Check message */
210 	if (msg->header.arglen != sizeof(*ep)) {
211 		NG_L2CAP_ALERT(
212 "%s: %s - invalid LP_ConnectInd message size\n",
213 			__func__, NG_NODE_NAME(l2cap->node));
214 		error = EMSGSIZE;
215 		goto out;
216 	}
217 
218  	ep = (ng_hci_lp_con_ind_ep *) (msg->data);
219 
220 	/* Make sure we have only one connection to the remote unit */
221 	con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr);
222 	if (con != NULL) {
223 		NG_L2CAP_ALERT(
224 "%s: %s - unexpected LP_ConnectInd event. " \
225 "Connection already exists, state=%d, con_handle=%d\n",
226 			__func__, NG_NODE_NAME(l2cap->node), con->state,
227 			con->con_handle);
228 		error = EEXIST;
229 		goto out;
230 	}
231 
232 	/* Check if lower layer protocol is still connected */
233 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
234 		NG_L2CAP_ERR(
235 "%s: %s - hook \"%s\" is not connected or valid",
236 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
237 		error = ENOTCONN;
238 		goto out;
239 	}
240 
241 	/* Create and intialize new connection descriptor */
242 	con = ng_l2cap_new_con(l2cap, &ep->bdaddr);
243 	if (con == NULL) {
244 		error = ENOMEM;
245 		goto out;
246 	}
247 
248 	/* Create and send LP_ConnectRsp event */
249 	NG_MKMESSAGE(rsp, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP,
250 		sizeof(*rp), M_NOWAIT);
251 	if (msg == NULL) {
252 		ng_l2cap_free_con(con);
253 		error = ENOMEM;
254 		goto out;
255 	}
256 
257 	rp = (ng_hci_lp_con_rsp_ep *)(rsp->data);
258 	rp->status = 0x00; /* accept connection */
259 	rp->link_type = NG_HCI_LINK_ACL;
260 	bcopy(&ep->bdaddr, &rp->bdaddr, sizeof(rp->bdaddr));
261 
262 	con->state = NG_L2CAP_W4_LP_CON_CFM;
263 	ng_l2cap_lp_timeout(con);
264 
265 	NG_SEND_MSG_HOOK(error, l2cap->node, rsp, l2cap->hci, 0);
266 	if (error != 0) {
267 		if ((error = ng_l2cap_lp_untimeout(con)) != 0)
268 			goto out;
269 
270 		ng_l2cap_free_con(con);
271 	}
272 out:
273 	return (error);
274 } /* ng_hci_lp_con_ind */
275 
276 /*
277  * Process LP_DisconnectInd event from the lower layer protocol. We have been
278  * disconnected from the remote unit. So notify the upper layer protocol.
279  */
280 
281 int
282 ng_l2cap_lp_discon_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
283 {
284 	ng_hci_lp_discon_ind_ep	*ep = NULL;
285 	ng_l2cap_con_p		 con = NULL;
286 	int			 error = 0;
287 
288 	/* Check message */
289 	if (msg->header.arglen != sizeof(*ep)) {
290 		NG_L2CAP_ALERT(
291 "%s: %s - invalid LP_DisconnectInd message size\n",
292 			__func__, NG_NODE_NAME(l2cap->node));
293 		error = EMSGSIZE;
294 		goto out;
295 	}
296 
297 	ep = (ng_hci_lp_discon_ind_ep *) (msg->data);
298 
299 	/* Check if we have this connection */
300 	con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
301 	if (con == NULL) {
302 		NG_L2CAP_ERR(
303 "%s: %s - unexpected LP_DisconnectInd event. " \
304 "Connection does not exist, con_handle=%d\n",
305 			__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
306 		error = ENOENT;
307 		goto out;
308 	}
309 
310 	/* XXX Verify connection state -- do we need to check this? */
311 	if (con->state != NG_L2CAP_CON_OPEN) {
312 		NG_L2CAP_ERR(
313 "%s: %s - unexpected LP_DisconnectInd event. " \
314 "Invalid connection state, state=%d, con_handle=%d\n",
315 			__func__, NG_NODE_NAME(l2cap->node), con->state,
316 			con->con_handle);
317 		error = EINVAL;
318 		goto out;
319 	}
320 
321 	/*
322 	 * Notify upper layer and remove connection
323 	 * Note: The connection could have auto disconnect timeout set. Try
324 	 * to remove it. If auto disconnect timeout happened then ignore
325 	 * disconnect indication and let timeout handle that.
326 	 */
327 
328 	if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)
329 		if ((error = ng_l2cap_discon_untimeout(con)) != 0)
330 			return (error);
331 
332 	ng_l2cap_con_fail(con, ep->reason);
333 out:
334 	return (error);
335 } /* ng_l2cap_lp_discon_ind */
336 
337 /*
338  * Send LP_QoSSetupReq event to the lower layer protocol
339  */
340 
341 int
342 ng_l2cap_lp_qos_req(ng_l2cap_p l2cap, u_int16_t con_handle,
343 		ng_l2cap_flow_p flow)
344 {
345 	struct ng_mesg		*msg = NULL;
346 	ng_hci_lp_qos_req_ep	*ep = NULL;
347 	ng_l2cap_con_p		 con = NULL;
348 	int			 error = 0;
349 
350 	/* Verify that we have this connection */
351 	con = ng_l2cap_con_by_handle(l2cap, con_handle);
352 	if (con == NULL) {
353 		NG_L2CAP_ERR(
354 "%s: %s - unexpected LP_QoSSetupReq event. " \
355 "Connection does not exist, con_handle=%d\n",
356 			__func__, NG_NODE_NAME(l2cap->node), con_handle);
357 
358 		return (ENOENT);
359 	}
360 
361 	/* Verify connection state */
362 	if (con->state != NG_L2CAP_CON_OPEN) {
363 		NG_L2CAP_ERR(
364 "%s: %s - unexpected LP_QoSSetupReq event. " \
365 "Invalid connection state, state=%d, con_handle=%d\n",
366 			__func__, NG_NODE_NAME(l2cap->node), con->state,
367 			con->con_handle);
368 
369 		return (EINVAL);
370 	}
371 
372 	/* Check if lower layer protocol is still connected */
373 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
374 		NG_L2CAP_ERR(
375 "%s: %s - hook \"%s\" is not connected or valid",
376 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
377 
378 		return (ENOTCONN);
379 	}
380 
381 	/* Create and send LP_QoSSetupReq event */
382 	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_REQ,
383 		sizeof(*ep), M_NOWAIT);
384 	if (msg == NULL)
385 		return (ENOMEM);
386 
387 	ep = (ng_hci_lp_qos_req_ep *) (msg->data);
388 	ep->con_handle = con_handle;
389 	ep->flags = flow->flags;
390 	ep->service_type = flow->service_type;
391 	ep->token_rate = flow->token_rate;
392 	ep->peak_bandwidth = flow->peak_bandwidth;
393 	ep->latency = flow->latency;
394 	ep->delay_variation = flow->delay_variation;
395 
396 	NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
397 
398 	return (error);
399 } /* ng_l2cap_lp_con_req */
400 
401 /*
402  * Process LP_QoSSetupCfm from the lower layer protocol
403  */
404 
405 int
406 ng_l2cap_lp_qos_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg)
407 {
408 	ng_hci_lp_qos_cfm_ep	*ep = NULL;
409 	int			 error = 0;
410 
411 	/* Check message */
412 	if (msg->header.arglen != sizeof(*ep)) {
413 		NG_L2CAP_ALERT(
414 "%s: %s - invalid LP_QoSSetupCfm[Neg] message size\n",
415 			__func__, NG_NODE_NAME(l2cap->node));
416 		error = EMSGSIZE;
417 		goto out;
418 	}
419 
420 	ep = (ng_hci_lp_qos_cfm_ep *) (msg->data);
421 	/* XXX FIXME do something */
422 out:
423 	return (error);
424 } /* ng_l2cap_lp_qos_cfm */
425 
426 /*
427  * Process LP_QoSViolationInd event from the lower layer protocol. Lower
428  * layer protocol has detected QoS Violation, so we MUST notify the
429  * upper layer.
430  */
431 
432 int
433 ng_l2cap_lp_qos_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
434 {
435 	ng_hci_lp_qos_ind_ep	*ep = NULL;
436 	ng_l2cap_con_p		 con = NULL;
437 	int			 error = 0;
438 
439 	/* Check message */
440 	if (msg->header.arglen != sizeof(*ep)) {
441 		NG_L2CAP_ALERT(
442 "%s: %s - invalid LP_QoSViolation message size\n",
443 			__func__, NG_NODE_NAME(l2cap->node));
444 		error = EMSGSIZE;
445 		goto out;
446 	}
447 
448 	ep = (ng_hci_lp_qos_ind_ep *) (msg->data);
449 
450 	/* Check if we have this connection */
451 	con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
452 	if (con == NULL) {
453 		NG_L2CAP_ERR(
454 "%s: %s - unexpected LP_QoSViolationInd event. " \
455 "Connection does not exist, con_handle=%d\n",
456 			__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
457 		error = ENOENT;
458 		goto out;
459 	}
460 
461 	/* Verify connection state */
462 	if (con->state != NG_L2CAP_CON_OPEN) {
463 		NG_L2CAP_ERR(
464 "%s: %s - unexpected LP_QoSViolationInd event. " \
465 "Invalid connection state, state=%d, con_handle=%d\n",
466 			__func__, NG_NODE_NAME(l2cap->node), con->state,
467 			con->con_handle);
468 		error = EINVAL;
469 		goto out;
470 	}
471 
472 	/* XXX FIXME Notify upper layer and terminate channels if required */
473 out:
474 	return (error);
475 } /* ng_l2cap_qos_ind */
476 
477 /*
478  * Prepare L2CAP packet. Prepend packet with L2CAP packet header and then
479  * segment it according to HCI MTU.
480  */
481 
482 int
483 ng_l2cap_lp_send(ng_l2cap_con_p con, u_int16_t dcid, struct mbuf *m0)
484 {
485 	ng_l2cap_p		 l2cap = con->l2cap;
486 	ng_l2cap_hdr_t		*l2cap_hdr = NULL;
487         ng_hci_acldata_pkt_t	*acl_hdr = NULL;
488         struct mbuf		*m_last = NULL, *m = NULL;
489         int			 len, flag = NG_HCI_PACKET_START;
490 
491 	KASSERT((con->tx_pkt == NULL),
492 ("%s: %s - another packet pending?!\n", __func__, NG_NODE_NAME(l2cap->node)));
493 	KASSERT((l2cap->pkt_size > 0),
494 ("%s: %s - invalid l2cap->pkt_size?!\n", __func__, NG_NODE_NAME(l2cap->node)));
495 
496 	/* Prepend mbuf with L2CAP header */
497 	m0 = ng_l2cap_prepend(m0, sizeof(*l2cap_hdr));
498 	if (m0 == NULL) {
499 		NG_L2CAP_ALERT(
500 "%s: %s - ng_l2cap_prepend(%zd) failed\n",
501 			__func__, NG_NODE_NAME(l2cap->node),
502 			sizeof(*l2cap_hdr));
503 
504 		goto fail;
505 	}
506 
507 	l2cap_hdr = mtod(m0, ng_l2cap_hdr_t *);
508 	l2cap_hdr->length = htole16(m0->m_pkthdr.len - sizeof(*l2cap_hdr));
509 	l2cap_hdr->dcid = htole16(dcid);
510 
511 	/*
512 	 * Segment single L2CAP packet according to the HCI layer MTU. Convert
513 	 * each segment into ACL data packet and prepend it with ACL data packet
514 	 * header. Link all segments together via m_nextpkt link.
515  	 *
516 	 * XXX BC (Broadcast flag) will always be 0 (zero).
517 	 */
518 
519 	while (m0 != NULL) {
520 		/* Check length of the packet against HCI MTU */
521 		len = m0->m_pkthdr.len;
522 		if (len > l2cap->pkt_size) {
523 			m = m_split(m0, l2cap->pkt_size, M_DONTWAIT);
524 			if (m == NULL) {
525 				NG_L2CAP_ALERT(
526 "%s: %s - m_split(%d) failed\n",	__func__, NG_NODE_NAME(l2cap->node),
527 					l2cap->pkt_size);
528 				goto fail;
529 			}
530 
531 			len = l2cap->pkt_size;
532 		}
533 
534 		/* Convert packet fragment into ACL data packet */
535 		m0 = ng_l2cap_prepend(m0, sizeof(*acl_hdr));
536 		if (m0 == NULL) {
537 			NG_L2CAP_ALERT(
538 "%s: %s - ng_l2cap_prepend(%zd) failed\n",
539 				__func__, NG_NODE_NAME(l2cap->node),
540 				sizeof(*acl_hdr));
541 			goto fail;
542 		}
543 
544 		acl_hdr = mtod(m0, ng_hci_acldata_pkt_t *);
545 		acl_hdr->type = NG_HCI_ACL_DATA_PKT;
546 		acl_hdr->length = htole16(len);
547 		acl_hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE(
548 					con->con_handle, flag, 0));
549 
550 		/* Add fragment to the chain */
551 		m0->m_nextpkt = NULL;
552 
553 		if (con->tx_pkt == NULL)
554 			con->tx_pkt = m_last = m0;
555 		else {
556 			m_last->m_nextpkt = m0;
557 			m_last = m0;
558 		}
559 
560 		NG_L2CAP_INFO(
561 "%s: %s - attaching ACL packet, con_handle=%d, PB=%#x, length=%d\n",
562 			__func__, NG_NODE_NAME(l2cap->node), con->con_handle,
563 			flag, len);
564 
565 		m0 = m;
566 		m = NULL;
567 		flag = NG_HCI_PACKET_FRAGMENT;
568 	}
569 
570 	return (0);
571 fail:
572 	NG_FREE_M(m0);
573 	NG_FREE_M(m);
574 
575 	while (con->tx_pkt != NULL) {
576 		m = con->tx_pkt->m_nextpkt;
577 		m_freem(con->tx_pkt);
578 		con->tx_pkt = m;
579 	}
580 
581 	return (ENOBUFS);
582 } /* ng_l2cap_lp_send */
583 
584 /*
585  * Receive ACL data packet from the HCI layer. First strip ACL packet header
586  * and get connection handle, PB (Packet Boundary) flag and payload length.
587  * Then find connection descriptor and verify its state. Then process ACL
588  * packet as follows.
589  *
590  * 1) If we got first segment (pb == NG_HCI_PACKET_START) then extract L2CAP
591  *    header and get total length of the L2CAP packet. Then start new L2CAP
592  *    packet.
593  *
594  * 2) If we got other (then first :) segment (pb == NG_HCI_PACKET_FRAGMENT)
595  *    then add segment to the packet.
596  */
597 
598 int
599 ng_l2cap_lp_receive(ng_l2cap_p l2cap, struct mbuf *m)
600 {
601 	ng_hci_acldata_pkt_t	*acl_hdr = NULL;
602 	ng_l2cap_hdr_t		*l2cap_hdr = NULL;
603 	ng_l2cap_con_p		 con = NULL;
604 	u_int16_t		 con_handle, length, pb;
605 	int			 error = 0;
606 
607 	/* Check ACL data packet */
608 	if (m->m_pkthdr.len < sizeof(*acl_hdr)) {
609 		NG_L2CAP_ERR(
610 "%s: %s - invalid ACL data packet. Packet too small, length=%d\n",
611 			__func__, NG_NODE_NAME(l2cap->node), m->m_pkthdr.len);
612 		error = EMSGSIZE;
613 		goto drop;
614 	}
615 
616 	/* Strip ACL data packet header */
617 	NG_L2CAP_M_PULLUP(m, sizeof(*acl_hdr));
618 	if (m == NULL)
619 		return (ENOBUFS);
620 
621 	acl_hdr = mtod(m, ng_hci_acldata_pkt_t *);
622 	m_adj(m, sizeof(*acl_hdr));
623 
624 	/* Get ACL connection handle, PB flag and payload length */
625 	acl_hdr->con_handle = le16toh(acl_hdr->con_handle);
626 	con_handle = NG_HCI_CON_HANDLE(acl_hdr->con_handle);
627 	pb = NG_HCI_PB_FLAG(acl_hdr->con_handle);
628 	length = le16toh(acl_hdr->length);
629 
630 	NG_L2CAP_INFO(
631 "%s: %s - got ACL data packet, con_handle=%d, PB=%#x, length=%d\n",
632 		__func__, NG_NODE_NAME(l2cap->node), con_handle, pb, length);
633 
634 	/* Get connection descriptor */
635 	con = ng_l2cap_con_by_handle(l2cap, con_handle);
636 	if (con == NULL) {
637 		NG_L2CAP_ERR(
638 "%s: %s - unexpected ACL data packet. " \
639 "Connection does not exist, con_handle=%d\n",
640 			__func__, NG_NODE_NAME(l2cap->node), con_handle);
641 		error = ENOENT;
642 		goto drop;
643 	}
644 
645 	/* Verify connection state */
646 	if (con->state != NG_L2CAP_CON_OPEN) {
647 		NG_L2CAP_ERR(
648 "%s: %s - unexpected ACL data packet. Invalid connection state=%d\n",
649 			__func__, NG_NODE_NAME(l2cap->node), con->state);
650 		error = EHOSTDOWN;
651 		goto drop;
652 	}
653 
654 	/* Process packet */
655 	if (pb == NG_HCI_PACKET_START) {
656 		if (con->rx_pkt != NULL) {
657 			NG_L2CAP_ERR(
658 "%s: %s - dropping incomplete L2CAP packet, got %d bytes, want %d bytes\n",
659 				__func__, NG_NODE_NAME(l2cap->node),
660 				con->rx_pkt->m_pkthdr.len, con->rx_pkt_len);
661 			NG_FREE_M(con->rx_pkt);
662 			con->rx_pkt_len = 0;
663 		}
664 
665 		/* Get L2CAP header */
666 		if (m->m_pkthdr.len < sizeof(*l2cap_hdr)) {
667 			NG_L2CAP_ERR(
668 "%s: %s - invalid L2CAP packet start fragment. Packet too small, length=%d\n",
669 				__func__, NG_NODE_NAME(l2cap->node),
670 				m->m_pkthdr.len);
671 			error = EMSGSIZE;
672 			goto drop;
673 		}
674 
675 		NG_L2CAP_M_PULLUP(m, sizeof(*l2cap_hdr));
676 		if (m == NULL)
677 			return (ENOBUFS);
678 
679 		l2cap_hdr = mtod(m, ng_l2cap_hdr_t *);
680 
681 		NG_L2CAP_INFO(
682 "%s: %s - staring new L2CAP packet, con_handle=%d, length=%d\n",
683 			__func__, NG_NODE_NAME(l2cap->node), con_handle,
684 			le16toh(l2cap_hdr->length));
685 
686 		/* Start new L2CAP packet */
687 		con->rx_pkt = m;
688 		con->rx_pkt_len = le16toh(l2cap_hdr->length)+sizeof(*l2cap_hdr);
689 	} else if (pb == NG_HCI_PACKET_FRAGMENT) {
690 		if (con->rx_pkt == NULL) {
691 			NG_L2CAP_ERR(
692 "%s: %s - unexpected ACL data packet fragment, con_handle=%d\n",
693 				__func__, NG_NODE_NAME(l2cap->node),
694 				con->con_handle);
695 			goto drop;
696 		}
697 
698 		/* Add fragment to the L2CAP packet */
699 		m_cat(con->rx_pkt, m);
700 		con->rx_pkt->m_pkthdr.len += length;
701 	} else {
702 		NG_L2CAP_ERR(
703 "%s: %s - invalid ACL data packet. Invalid PB flag=%#x\n",
704 			__func__, NG_NODE_NAME(l2cap->node), pb);
705 		error = EINVAL;
706 		goto drop;
707 	}
708 
709 	con->rx_pkt_len -= length;
710 	if (con->rx_pkt_len < 0) {
711 		NG_L2CAP_ALERT(
712 "%s: %s - packet length mismatch. Got %d bytes, offset %d bytes\n",
713 			__func__, NG_NODE_NAME(l2cap->node),
714 			con->rx_pkt->m_pkthdr.len, con->rx_pkt_len);
715 		NG_FREE_M(con->rx_pkt);
716 		con->rx_pkt_len = 0;
717 	} else if (con->rx_pkt_len == 0) {
718 		/* OK, we have got complete L2CAP packet, so process it */
719 		error = ng_l2cap_receive(con);
720 		con->rx_pkt = NULL;
721 		con->rx_pkt_len = 0;
722 	}
723 
724 	return (error);
725 
726 drop:
727 	NG_FREE_M(m);
728 
729 	return (error);
730 } /* ng_l2cap_lp_receive */
731 
732 /*
733  * Send queued ACL packets to the HCI layer
734  */
735 
736 void
737 ng_l2cap_lp_deliver(ng_l2cap_con_p con)
738 {
739 	ng_l2cap_p	 l2cap = con->l2cap;
740 	struct mbuf	*m = NULL;
741 	int		 error;
742 
743 	/* Check connection */
744 	if (con->state != NG_L2CAP_CON_OPEN)
745 		return;
746 
747 	if (con->tx_pkt == NULL)
748 		ng_l2cap_con_wakeup(con);
749 
750 	if (con->tx_pkt == NULL)
751 		return;
752 
753 	/* Check if lower layer protocol is still connected */
754 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
755 		NG_L2CAP_ERR(
756 "%s: %s - hook \"%s\" is not connected or valid",
757 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
758 
759 		goto drop; /* XXX what to do with "pending"? */
760 	}
761 
762 	/* Send ACL data packets */
763 	while (con->pending < con->l2cap->num_pkts && con->tx_pkt != NULL) {
764 		m = con->tx_pkt;
765 		con->tx_pkt = con->tx_pkt->m_nextpkt;
766 		m->m_nextpkt = NULL;
767 
768 		NG_L2CAP_INFO(
769 "%s: %s - sending ACL packet, con_handle=%d, len=%d\n",
770 			__func__, NG_NODE_NAME(l2cap->node), con->con_handle,
771 			m->m_pkthdr.len);
772 
773 		NG_SEND_DATA_ONLY(error, l2cap->hci, m);
774 		if (error != 0) {
775 			NG_L2CAP_ERR(
776 "%s: %s - could not send ACL data packet, con_handle=%d, error=%d\n",
777 				__func__, NG_NODE_NAME(l2cap->node),
778 				con->con_handle, error);
779 
780 			goto drop; /* XXX what to do with "pending"? */
781 		}
782 
783 		con->pending ++;
784 	}
785 
786 	NG_L2CAP_INFO(
787 "%s: %s - %d ACL packets have been sent, con_handle=%d\n",
788 		__func__, NG_NODE_NAME(l2cap->node), con->pending,
789 		con->con_handle);
790 
791 	return;
792 
793 drop:
794 	while (con->tx_pkt != NULL) {
795 		m = con->tx_pkt->m_nextpkt;
796 		m_freem(con->tx_pkt);
797 		con->tx_pkt = m;
798 	}
799 } /* ng_l2cap_lp_deliver */
800 
801 /*
802  * Process connection timeout. Remove connection from the list. If there
803  * are any channels that wait for the connection then notify them. Free
804  * connection descriptor.
805  */
806 
807 void
808 ng_l2cap_process_lp_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
809 {
810 	ng_l2cap_p	l2cap = NULL;
811 	ng_l2cap_con_p	con = NULL;
812 
813 	if (NG_NODE_NOT_VALID(node)) {
814 		printf("%s: Netgraph node is not valid\n", __func__);
815 		return;
816 	}
817 
818 	l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
819 	con = ng_l2cap_con_by_handle(l2cap, con_handle);
820 
821 	if (con == NULL) {
822 		NG_L2CAP_ALERT(
823 "%s: %s - could not find connection, con_handle=%d\n",
824 			__func__, NG_NODE_NAME(node), con_handle);
825 		return;
826 	}
827 
828 	if (!(con->flags & NG_L2CAP_CON_LP_TIMO)) {
829 		NG_L2CAP_ALERT(
830 "%s: %s - no pending LP timeout, con_handle=%d, state=%d, flags=%#x\n",
831 			__func__, NG_NODE_NAME(node), con_handle, con->state,
832 			con->flags);
833 		return;
834 	}
835 
836 	/*
837 	 * Notify channels that connection has timed out. This will remove
838 	 * connection, channels and pending commands.
839 	 */
840 
841 	con->flags &= ~NG_L2CAP_CON_LP_TIMO;
842 	ng_l2cap_con_fail(con, NG_L2CAP_TIMEOUT);
843 } /* ng_l2cap_process_lp_timeout */
844 
845 /*
846  * Process auto disconnect timeout and send LP_DisconReq event to the
847  * lower layer protocol
848  */
849 
850 void
851 ng_l2cap_process_discon_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
852 {
853 	ng_l2cap_p		 l2cap = NULL;
854 	ng_l2cap_con_p		 con = NULL;
855 	struct ng_mesg		*msg = NULL;
856 	ng_hci_lp_discon_req_ep	*ep = NULL;
857 	int			 error;
858 
859 	if (NG_NODE_NOT_VALID(node)) {
860 		printf("%s: Netgraph node is not valid\n", __func__);
861 		return;
862 	}
863 
864 	l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
865 	con = ng_l2cap_con_by_handle(l2cap, con_handle);
866 
867 	if (con == NULL) {
868 		NG_L2CAP_ALERT(
869 "%s: %s - could not find connection, con_handle=%d\n",
870 			__func__, NG_NODE_NAME(node), con_handle);
871 		return;
872 	}
873 
874 	if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)) {
875 		NG_L2CAP_ALERT(
876 "%s: %s - no pending disconnect timeout, con_handle=%d, state=%d, flags=%#x\n",
877 			__func__, NG_NODE_NAME(node), con_handle, con->state,
878 			con->flags);
879 		return;
880 	}
881 
882 	con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO;
883 
884 	/* Check if lower layer protocol is still connected */
885 	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
886 		NG_L2CAP_ERR(
887 "%s: %s - hook \"%s\" is not connected or valid\n",
888 			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
889 		return;
890 	}
891 
892 	/* Create and send LP_DisconReq event */
893 	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_REQ,
894 		sizeof(*ep), M_NOWAIT);
895 	if (msg == NULL)
896 		return;
897 
898 	ep = (ng_hci_lp_discon_req_ep *) (msg->data);
899 	ep->con_handle = con->con_handle;
900 	ep->reason = 0x13; /* User Ended Connection */
901 
902 	NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
903 } /* ng_l2cap_process_discon_timeout */
904 
905