xref: /freebsd/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
1 /*
2  * ng_hci_ulpi.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_hci_ulpi.c,v 1.6 2003/04/26 22:35:21 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_hci_var.h"
44 #include "ng_hci_cmds.h"
45 #include "ng_hci_evnt.h"
46 #include "ng_hci_ulpi.h"
47 #include "ng_hci_misc.h"
48 
49 /******************************************************************************
50  ******************************************************************************
51  **                 Upper Layer Protocol Interface module
52  ******************************************************************************
53  ******************************************************************************/
54 
55 static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p);
56 static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p);
57 
58 /*
59  * Process LP_ConnectReq event from the upper layer protocol
60  */
61 
62 int
63 ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
64 {
65 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
66 		NG_HCI_WARN(
67 "%s: %s - unit is not ready, state=%#x\n",
68 			__func__, NG_NODE_NAME(unit->node), unit->state);
69 
70 		NG_FREE_ITEM(item);
71 
72 		return (ENXIO);
73 	}
74 
75 	if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) {
76 		NG_HCI_ALERT(
77 "%s: %s - invalid LP_ConnectReq message size=%d\n",
78 			__func__, NG_NODE_NAME(unit->node),
79 			NGI_MSG(item)->header.arglen);
80 
81 		NG_FREE_ITEM(item);
82 
83 		return (EMSGSIZE);
84 	}
85 
86 	if (((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type == NG_HCI_LINK_ACL)
87 		return (ng_hci_lp_acl_con_req(unit, item, hook));
88 
89 	if (hook != unit->sco) {
90 		NG_HCI_WARN(
91 "%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n",
92 			__func__, NG_NODE_NAME(unit->node), hook);
93 
94 		NG_FREE_ITEM(item);
95 
96 		return (EINVAL);
97 	}
98 
99 	return (ng_hci_lp_sco_con_req(unit, item, hook));
100 } /* ng_hci_lp_con_req */
101 
102 /*
103  * Request to create new ACL connection
104  */
105 
106 static int
107 ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
108 {
109 	struct acl_con_req {
110 		ng_hci_cmd_pkt_t	 hdr;
111 		ng_hci_create_con_cp	 cp;
112 	} __attribute__ ((packed))	*req = NULL;
113 	ng_hci_lp_con_req_ep		*ep = NULL;
114 	ng_hci_unit_con_p		 con = NULL;
115 	ng_hci_neighbor_t		*n = NULL;
116 	struct mbuf			*m = NULL;
117 	int				 error = 0;
118 
119 	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
120 
121 	/*
122 	 * Only one ACL connection can exist between each pair of units.
123 	 * So try to find ACL connection descriptor (in any state) that
124 	 * has requested remote BD_ADDR.
125 	 *
126 	 * Two cases:
127 	 *
128 	 * 1) We do not have connection to the remote unit. This is simple.
129 	 *    Just create new connection descriptor and send HCI command to
130 	 *    create new connection.
131 	 *
132 	 * 2) We do have connection descriptor. We need to check connection
133 	 *    state:
134 	 *
135 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
136 	 *      accepting connection from the remote unit. This is a race
137 	 *      condition. We will ignore this message.
138 	 *
139 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
140 	 *      requested connection or we just accepted it. In any case
141 	 *      all we need to do here is set appropriate notification bit
142 	 *      and wait.
143 	 *
144 	 * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back
145 	 *      and let upper layer know that we have connection already.
146 	 */
147 
148 	con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
149 	if (con != NULL) {
150 		switch (con->state) {
151 		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
152 			error = EALREADY;
153 			break;
154 
155 		case NG_HCI_CON_W4_CONN_COMPLETE:
156 			if (hook == unit->acl)
157 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
158 			else
159 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
160 			break;
161 
162 		case NG_HCI_CON_OPEN: {
163 			struct ng_mesg		*msg = NULL;
164 			ng_hci_lp_con_cfm_ep	*cfm = NULL;
165 
166 			if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
167 				NGI_GET_MSG(item, msg);
168 				NG_FREE_MSG(msg);
169 
170 				NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
171 					NGM_HCI_LP_CON_CFM, sizeof(*cfm),
172 					M_NOWAIT);
173 				if (msg != NULL) {
174 					cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
175 					cfm->status = 0;
176 					cfm->link_type = con->link_type;
177 					cfm->con_handle = con->con_handle;
178 					bcopy(&con->bdaddr, &cfm->bdaddr,
179 						sizeof(cfm->bdaddr));
180 
181 					/*
182 					 * This will forward item back to
183 					 * sender and set item to NULL
184 					 */
185 
186 					_NGI_MSG(item) = msg;
187 					NG_FWD_ITEM_HOOK(error, item, hook);
188 				} else
189 					error = ENOMEM;
190 			} else
191 				NG_HCI_INFO(
192 "%s: %s - Source hook is not valid, hook=%p\n",
193 					__func__, NG_NODE_NAME(unit->node),
194 					hook);
195 			} break;
196 
197 		default:
198 			KASSERT(0,
199 ("%s: %s - Invalid connection state=%d\n",
200 				__func__, NG_NODE_NAME(unit->node),con->state));
201 
202 			error = EINVAL;
203 			break;
204 		}
205 
206 		goto out;
207 	}
208 
209 	/*
210 	 * If we got here then we need to create new ACL connection descriptor
211 	 * and submit HCI command. First create new connection desriptor, set
212 	 * bdaddr and notification flags.
213 	 */
214 
215 	con = ng_hci_new_con(unit, NG_HCI_LINK_ACL);
216 	if (con == NULL) {
217 		error = ENOMEM;
218 		goto out;
219 	}
220 
221 	bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
222 
223 	/*
224 	 * Create HCI command
225 	 */
226 
227 	MGETHDR(m, M_DONTWAIT, MT_DATA);
228 	if (m == NULL) {
229 		ng_hci_free_con(con);
230 		error = ENOBUFS;
231 		goto out;
232 	}
233 
234 	m->m_pkthdr.len = m->m_len = sizeof(*req);
235 	req = mtod(m, struct acl_con_req *);
236 	req->hdr.type = NG_HCI_CMD_PKT;
237 	req->hdr.length = sizeof(req->cp);
238 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
239 					NG_HCI_OCF_CREATE_CON));
240 
241 	bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr));
242 
243 	req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
244 	if (unit->features[0] & NG_HCI_LMP_3SLOT)
245 		req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3);
246 	if (unit->features[0] & NG_HCI_LMP_5SLOT)
247 		req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5);
248 
249 	req->cp.pkt_type &= unit->packet_mask;
250 	if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1|
251 				 NG_HCI_PKT_DM3|NG_HCI_PKT_DH3|
252 				 NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0)
253 		req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
254 
255 	req->cp.pkt_type = htole16(req->cp.pkt_type);
256 
257 	if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch)
258 		req->cp.accept_role_switch = 1;
259 	else
260 		req->cp.accept_role_switch = 0;
261 
262 	/*
263 	 * We may speed up connect by specifying valid parameters.
264 	 * So check the neighbor cache.
265 	 */
266 
267 	n = ng_hci_get_neighbor(unit, &ep->bdaddr);
268 	if (n == NULL) {
269 		req->cp.page_scan_rep_mode = 0;
270 		req->cp.page_scan_mode = 0;
271 		req->cp.clock_offset = 0;
272 	} else {
273 		req->cp.page_scan_rep_mode = n->page_scan_rep_mode;
274 		req->cp.page_scan_mode = n->page_scan_mode;
275 		req->cp.clock_offset = htole16(n->clock_offset);
276 	}
277 
278 	/*
279 	 * Adust connection state
280 	 */
281 
282 	if (hook == unit->acl)
283 		con->flags |= NG_HCI_CON_NOTIFY_ACL;
284 	else
285 		con->flags |= NG_HCI_CON_NOTIFY_SCO;
286 
287 	con->state = NG_HCI_CON_W4_CONN_COMPLETE;
288 	ng_hci_con_timeout(con);
289 
290 	/*
291 	 * Queue and send HCI command
292 	 */
293 
294 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
295 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
296 		error = ng_hci_send_command(unit);
297 out:
298 	if (item != NULL)
299 		NG_FREE_ITEM(item);
300 
301 	return (error);
302 } /* ng_hci_lp_acl_con_req */
303 
304 /*
305  * Request to create new SCO connection
306  */
307 
308 static int
309 ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
310 {
311 	struct sco_con_req {
312 		ng_hci_cmd_pkt_t	 hdr;
313 		ng_hci_add_sco_con_cp	 cp;
314 	} __attribute__ ((packed))	*req = NULL;
315 	ng_hci_lp_con_req_ep		*ep = NULL;
316 	ng_hci_unit_con_p		 acl_con = NULL, sco_con = NULL;
317 	struct mbuf			*m = NULL;
318 	int				 error = 0;
319 
320 	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
321 
322 	/*
323 	 * SCO connection without ACL link
324 	 *
325 	 * If upper layer requests SCO connection and there is no open ACL
326 	 * connection to the desired remote unit, we will reject the request.
327 	 */
328 
329 	LIST_FOREACH(acl_con, &unit->con_list, next)
330 		if (acl_con->link_type == NG_HCI_LINK_ACL &&
331 		    acl_con->state == NG_HCI_CON_OPEN &&
332 		    bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
333 			break;
334 
335 	if (acl_con == NULL) {
336 		NG_HCI_INFO(
337 "%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n",
338 			__func__, NG_NODE_NAME(unit->node),
339 			ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
340 			ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
341 
342 		error = ENOENT;
343 		goto out;
344 	}
345 
346 	/*
347 	 * Multiple SCO connections can exist between the same pair of units.
348 	 * We assume that multiple SCO connections have to be opened one after
349 	 * another.
350 	 *
351 	 * Try to find SCO connection descriptor that matches the following:
352 	 *
353 	 * 1) sco_con->link_type == NG_HCI_LINK_SCO
354 	 *
355 	 * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
356 	 *    sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE
357 	 *
358 	 * 3) sco_con->bdaddr == ep->bdaddr
359 	 *
360 	 * Two cases:
361 	 *
362 	 * 1) We do not have connection descriptor. This is simple. Just
363 	 *    create new connection and submit Add_SCO_Connection command.
364 	 *
365 	 * 2) We do have connection descriptor. We need to check the state.
366 	 *
367 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting
368 	 *      connection from the remote unit. This is a race condition and
369 	 *      we will ignore the request.
370 	 *
371 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested
372 	 *      connection or we just accepted it.
373 	 */
374 
375 	LIST_FOREACH(sco_con, &unit->con_list, next)
376 		if (sco_con->link_type == NG_HCI_LINK_SCO &&
377 		    (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
378 		     sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
379 		    bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
380 			break;
381 
382 	if (sco_con != NULL) {
383 		switch (sco_con->state) {
384 		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
385 			error = EALREADY;
386 			break;
387 
388 		case NG_HCI_CON_W4_CONN_COMPLETE:
389 			sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
390 			break;
391 
392 		default:
393 			KASSERT(0,
394 ("%s: %s - Inavalid connection state=%d\n",
395 				__func__, NG_NODE_NAME(unit->node),
396 				sco_con->state));
397 
398 			error = EINVAL;
399 			break;
400 		}
401 
402 		goto out;
403 	}
404 
405 	/*
406 	 * If we got here then we need to create new SCO connection descriptor
407 	 * and submit HCI command.
408 	 */
409 
410 	sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO);
411 	if (sco_con == NULL) {
412 		error = ENOMEM;
413 		goto out;
414 	}
415 
416 	bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr));
417 
418 	/*
419 	 * Create HCI command
420 	 */
421 
422 	MGETHDR(m, M_DONTWAIT, MT_DATA);
423 	if (m == NULL) {
424 		ng_hci_free_con(sco_con);
425 		error = ENOBUFS;
426 		goto out;
427 	}
428 
429 	m->m_pkthdr.len = m->m_len = sizeof(*req);
430 	req = mtod(m, struct sco_con_req *);
431 	req->hdr.type = NG_HCI_CMD_PKT;
432 	req->hdr.length = sizeof(req->cp);
433 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
434 					NG_HCI_OCF_ADD_SCO_CON));
435 
436 	req->cp.con_handle = htole16(acl_con->con_handle);
437 
438 	req->cp.pkt_type = NG_HCI_PKT_HV1;
439 	if (unit->features[1] & NG_HCI_LMP_HV2_PKT)
440 		req->cp.pkt_type |= NG_HCI_PKT_HV2;
441 	if (unit->features[1] & NG_HCI_LMP_HV3_PKT)
442 		req->cp.pkt_type |= NG_HCI_PKT_HV3;
443 
444 	req->cp.pkt_type &= unit->packet_mask;
445 	if ((req->cp.pkt_type & (NG_HCI_PKT_HV1|
446 				 NG_HCI_PKT_HV2|
447 				 NG_HCI_PKT_HV3)) == 0)
448 		req->cp.pkt_type = NG_HCI_PKT_HV1;
449 
450 	req->cp.pkt_type = htole16(req->cp.pkt_type);
451 
452 	/*
453 	 * Adust connection state
454 	 */
455 
456 	sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
457 
458 	sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE;
459 	ng_hci_con_timeout(sco_con);
460 
461 	/*
462 	 * Queue and send HCI command
463 	 */
464 
465 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
466 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
467 		error = ng_hci_send_command(unit);
468 out:
469 	NG_FREE_ITEM(item);
470 
471 	return (error);
472 } /* ng_hci_lp_sco_con_req */
473 
474 /*
475  * Process LP_DisconnectReq event from the upper layer protocol
476  */
477 
478 int
479 ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook)
480 {
481 	struct discon_req {
482 		ng_hci_cmd_pkt_t	 hdr;
483 		ng_hci_discon_cp	 cp;
484 	} __attribute__ ((packed))	*req = NULL;
485 	ng_hci_lp_discon_req_ep		*ep = NULL;
486 	ng_hci_unit_con_p		 con = NULL;
487 	struct mbuf			*m = NULL;
488 	int				 error = 0;
489 
490 	/* Check if unit is ready */
491 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
492 		NG_HCI_WARN(
493 "%s: %s - unit is not ready, state=%#x\n",
494 			__func__, NG_NODE_NAME(unit->node), unit->state);
495 
496 		error = ENXIO;
497 		goto out;
498 	}
499 
500 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
501 		NG_HCI_ALERT(
502 "%s: %s - invalid LP_DisconnectReq message size=%d\n",
503 			__func__, NG_NODE_NAME(unit->node),
504 			NGI_MSG(item)->header.arglen);
505 
506 		error = EMSGSIZE;
507 		goto out;
508 	}
509 
510 	ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data);
511 
512 	con = ng_hci_con_by_handle(unit, ep->con_handle);
513 	if (con == NULL) {
514 		NG_HCI_ERR(
515 "%s: %s - invalid connection handle=%d\n",
516 			__func__, NG_NODE_NAME(unit->node), ep->con_handle);
517 
518 		error = ENOENT;
519 		goto out;
520 	}
521 
522 	if (con->state != NG_HCI_CON_OPEN) {
523 		NG_HCI_ERR(
524 "%s: %s - invalid connection state=%d, handle=%d\n",
525 			__func__, NG_NODE_NAME(unit->node), con->state,
526 			ep->con_handle);
527 
528 		error = EINVAL;
529 		goto out;
530 	}
531 
532 	/*
533 	 * Create HCI command
534 	 */
535 
536 	MGETHDR(m, M_DONTWAIT, MT_DATA);
537 	if (m == NULL) {
538 		error = ENOBUFS;
539 		goto out;
540 	}
541 
542 	m->m_pkthdr.len = m->m_len = sizeof(*req);
543 	req = mtod(m, struct discon_req *);
544 	req->hdr.type = NG_HCI_CMD_PKT;
545 	req->hdr.length = sizeof(req->cp);
546 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
547 							NG_HCI_OCF_DISCON));
548 
549 	req->cp.con_handle = htole16(ep->con_handle);
550 	req->cp.reason = ep->reason;
551 
552 	/*
553 	 * Queue and send HCI command
554 	 */
555 
556 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
557 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
558 		error = ng_hci_send_command(unit);
559 out:
560 	NG_FREE_ITEM(item);
561 
562 	return (error);
563 } /* ng_hci_lp_discon_req */
564 
565 /*
566  * Send LP_ConnectCfm event to the upper layer protocol
567  */
568 
569 int
570 ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status)
571 {
572 	ng_hci_unit_p		 unit = con->unit;
573 	struct ng_mesg		*msg = NULL;
574 	ng_hci_lp_con_cfm_ep	*ep = NULL;
575 	int			 error;
576 
577 	/*
578 	 * Check who wants to be notified. For ACL links both ACL and SCO
579 	 * upstream hooks will be notified (if required). For SCO links
580 	 * only SCO upstream hook will receive notification
581 	 */
582 
583 	if (con->link_type == NG_HCI_LINK_ACL &&
584 	    con->flags & NG_HCI_CON_NOTIFY_ACL) {
585 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
586 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
587 				sizeof(*ep), M_NOWAIT);
588 			if (msg != NULL) {
589 				ep = (ng_hci_lp_con_cfm_ep *) msg->data;
590 				ep->status = status;
591 				ep->link_type = con->link_type;
592 				ep->con_handle = con->con_handle;
593 				bcopy(&con->bdaddr, &ep->bdaddr,
594 					sizeof(ep->bdaddr));
595 
596 				NG_SEND_MSG_HOOK(error, unit->node, msg,
597 					unit->acl, NULL);
598 			}
599 		} else
600 			NG_HCI_INFO(
601 "%s: %s - ACL hook not valid, hook=%p\n",
602 				__func__, NG_NODE_NAME(unit->node), unit->acl);
603 
604 		con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
605 	}
606 
607 	if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
608 		if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
609 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
610 				sizeof(*ep), M_NOWAIT);
611 			if (msg != NULL) {
612 				ep = (ng_hci_lp_con_cfm_ep *) msg->data;
613 				ep->status = status;
614 				ep->link_type = con->link_type;
615 				ep->con_handle = con->con_handle;
616 				bcopy(&con->bdaddr, &ep->bdaddr,
617 					sizeof(ep->bdaddr));
618 
619 				NG_SEND_MSG_HOOK(error, unit->node, msg,
620 					unit->sco, NULL);
621 			}
622 		} else
623 			NG_HCI_INFO(
624 "%s: %s - SCO hook not valid, hook=%p\n",
625 				__func__, NG_NODE_NAME(unit->node), unit->acl);
626 
627 		con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
628 	}
629 
630 	return (0);
631 } /* ng_hci_lp_con_cfm */
632 
633 /*
634  * Send LP_ConnectInd event to the upper layer protocol
635  */
636 
637 int
638 ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass)
639 {
640 	ng_hci_unit_p		 unit = con->unit;
641 	struct ng_mesg		*msg = NULL;
642 	ng_hci_lp_con_ind_ep	*ep = NULL;
643 	hook_p			 hook = NULL;
644 	int			 error = 0;
645 
646 	/*
647 	 * Connection_Request event is generated for specific link type.
648 	 * Use link_type to select upstream hook.
649 	 */
650 
651 	if (con->link_type == NG_HCI_LINK_ACL)
652 		hook = unit->acl;
653 	else
654 		hook = unit->sco;
655 
656 	if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
657 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND,
658 			sizeof(*ep), M_NOWAIT);
659 		if (msg == NULL)
660 			return (ENOMEM);
661 
662 		ep = (ng_hci_lp_con_ind_ep *)(msg->data);
663 		ep->link_type = con->link_type;
664 		bcopy(uclass, ep->uclass, sizeof(ep->uclass));
665 		bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
666 
667 		NG_SEND_MSG_HOOK(error, unit->node, msg, hook, NULL);
668 	} else {
669 		NG_HCI_WARN(
670 "%s: %s - Upstream hook is not connected or not valid, hook=%p\n",
671 			__func__, NG_NODE_NAME(unit->node), hook);
672 
673 		error = ENOTCONN;
674 	}
675 
676 	return (error);
677 } /* ng_hci_lp_con_ind */
678 
679 /*
680  * Process LP_ConnectRsp event from the upper layer protocol
681  */
682 
683 int
684 ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook)
685 {
686 	struct con_rsp_req {
687 		ng_hci_cmd_pkt_t		 hdr;
688 		union {
689 			ng_hci_accept_con_cp	 acc;
690 			ng_hci_reject_con_cp	 rej;
691 		} __attribute__ ((packed))	 cp;
692 	} __attribute__ ((packed))		*req = NULL;
693 	ng_hci_lp_con_rsp_ep			*ep = NULL;
694 	ng_hci_unit_con_p			 con = NULL;
695 	struct mbuf				*m = NULL;
696 	int					 error = 0;
697 
698 	/* Check if unit is ready */
699 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
700 		NG_HCI_WARN(
701 "%s: %s - unit is not ready, state=%#x\n",
702 			__func__, NG_NODE_NAME(unit->node), unit->state);
703 
704 		error = ENXIO;
705 		goto out;
706 	}
707 
708 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
709 		NG_HCI_ALERT(
710 "%s: %s - invalid LP_ConnectRsp message size=%d\n",
711 			__func__, NG_NODE_NAME(unit->node),
712 			NGI_MSG(item)->header.arglen);
713 
714 		error = EMSGSIZE;
715 		goto out;
716 	}
717 
718 	ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data);
719 
720 	/*
721 	 * Here we have to deal with race. Upper layers might send conflicting
722 	 * requests. One might send Accept and other Reject. We will not try
723 	 * to solve all the problems, so first request will always win.
724 	 *
725 	 * Try to find connection that matches the following:
726 	 *
727 	 * 1) con->link_type == ep->link_type
728 	 *
729 	 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
730 	 *    con->state == NG_HCI_CON_W4_CONN_COMPLETE
731 	 *
732 	 * 3) con->bdaddr == ep->bdaddr
733 	 *
734 	 * Two cases:
735 	 *
736 	 * 1) We do not have connection descriptor. Could be bogus request or
737 	 *    we have rejected connection already.
738 	 *
739 	 * 2) We do have connection descriptor. Then we need to check state:
740 	 *
741 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested
742 	 *      connection and it is a first response from the upper layer.
743 	 *      if "status == 0" (Accept) then we will send Accept_Connection
744 	 *      command and change connection state to W4_CONN_COMPLETE, else
745 	 *      send reject and delete connection.
746 	 *
747 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted
748 	 *      connection. If "status == 0" we just need to link request
749 	 *      and wait, else ignore Reject request.
750 	 */
751 
752 	LIST_FOREACH(con, &unit->con_list, next)
753 		if (con->link_type == ep->link_type &&
754 		    (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
755 		     con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
756 		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
757 			break;
758 
759 	if (con == NULL) {
760 		/* Reject for non-existing connection is fine */
761 		error = (ep->status == 0)? ENOENT : 0;
762 		goto out;
763 	}
764 
765 	/*
766 	 * Remove connection timeout and check connection state
767 	 */
768 
769 	ng_hci_con_untimeout(con);
770 
771 	switch (con->state) {
772 	case NG_HCI_CON_W4_LP_CON_RSP:
773 
774 		/*
775 		 * Create HCI command
776 		 */
777 
778 		MGETHDR(m, M_DONTWAIT, MT_DATA);
779 		if (m == NULL) {
780 			error = ENOBUFS;
781 			goto out;
782 		}
783 
784 		req = mtod(m, struct con_rsp_req *);
785 		req->hdr.type = NG_HCI_CMD_PKT;
786 
787 		if (ep->status == 0) {
788 			req->hdr.length = sizeof(req->cp.acc);
789 			req->hdr.opcode = htole16(NG_HCI_OPCODE(
790 							NG_HCI_OGF_LINK_CONTROL,
791 							NG_HCI_OCF_ACCEPT_CON));
792 
793 			bcopy(&ep->bdaddr, &req->cp.acc.bdaddr,
794 				sizeof(req->cp.acc.bdaddr));
795 
796 			/*
797 			 * We are accepting connection, so if we support role
798 			 * switch and role switch was enabled then set role to
799 			 * NG_HCI_ROLE_MASTER and let LM peform role switch.
800 			 * Otherwise we remain slave. In this case LM WILL NOT
801 			 * perform role switch.
802 			 */
803 
804 			if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&
805 			    unit->role_switch)
806 				req->cp.acc.role = NG_HCI_ROLE_MASTER;
807 			else
808 				req->cp.acc.role = NG_HCI_ROLE_SLAVE;
809 
810 			/*
811 			 * Adjust connection state
812 			 */
813 
814 			if (hook == unit->acl)
815 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
816 			else
817 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
818 
819 			con->state = NG_HCI_CON_W4_CONN_COMPLETE;
820 			ng_hci_con_timeout(con);
821 		} else {
822 			req->hdr.length = sizeof(req->cp.rej);
823 			req->hdr.opcode = htole16(NG_HCI_OPCODE(
824 							NG_HCI_OGF_LINK_CONTROL,
825 							NG_HCI_OCF_REJECT_CON));
826 
827 			bcopy(&ep->bdaddr, &req->cp.rej.bdaddr,
828 				sizeof(req->cp.rej.bdaddr));
829 
830 			req->cp.rej.reason = ep->status;
831 
832 			/*
833 			 * Free connection descritor
834 			 * Item will be deleted just before return.
835 			 */
836 
837 			ng_hci_free_con(con);
838 		}
839 
840 		m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length;
841 
842 		/* Queue and send HCI command */
843 		NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
844 		if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
845 			error = ng_hci_send_command(unit);
846 		break;
847 
848 	case NG_HCI_CON_W4_CONN_COMPLETE:
849 		if (ep->status == 0) {
850 			if (hook == unit->acl)
851 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
852 			else
853 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
854 		} else
855 			error = EPERM;
856 		break;
857 
858 	default:
859 		KASSERT(0,
860 ("%s: %s - Invalid connection state=%d\n",
861 			__func__, NG_NODE_NAME(unit->node), con->state));
862 
863 		error = EINVAL;
864 		break;
865 	}
866 out:
867 	NG_FREE_ITEM(item);
868 
869 	return (error);
870 } /* ng_hci_lp_con_rsp */
871 
872 /*
873  * Send LP_DisconnectInd to the upper layer protocol
874  */
875 
876 int
877 ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason)
878 {
879 	ng_hci_unit_p		 unit = con->unit;
880 	struct ng_mesg		*msg = NULL;
881 	ng_hci_lp_discon_ind_ep	*ep = NULL;
882 	int			 error = 0;
883 
884 	/*
885 	 * Disconnect_Complete event is generated for specific connection
886 	 * handle. For ACL connection handles both ACL and SCO upstream
887 	 * hooks will receive notification. For SCO connection handles
888 	 * only SCO upstream hook will receive notification.
889 	 */
890 
891 	if (con->link_type == NG_HCI_LINK_ACL) {
892 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
893 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
894 				NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT);
895 			if (msg == NULL)
896 				return (ENOMEM);
897 
898 			ep = (ng_hci_lp_discon_ind_ep *) msg->data;
899 			ep->reason = reason;
900 			ep->link_type = con->link_type;
901 			ep->con_handle = con->con_handle;
902 
903 			NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,NULL);
904 		} else
905 			NG_HCI_INFO(
906 "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
907 				__func__, NG_NODE_NAME(unit->node), unit->acl);
908 	}
909 
910 	if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
911 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND,
912 			sizeof(*ep), M_NOWAIT);
913 		if (msg == NULL)
914 			return (ENOMEM);
915 
916 		ep = (ng_hci_lp_discon_ind_ep *) msg->data;
917 		ep->reason = reason;
918 		ep->link_type = con->link_type;
919 		ep->con_handle = con->con_handle;
920 
921 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, NULL);
922 	} else
923 		NG_HCI_INFO(
924 "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
925 			__func__, NG_NODE_NAME(unit->node), unit->sco);
926 
927 	return (0);
928 } /* ng_hci_lp_discon_ind */
929 
930 /*
931  * Process LP_QoSReq action from the upper layer protocol
932  */
933 
934 int
935 ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook)
936 {
937 	struct qos_setup_req {
938 		ng_hci_cmd_pkt_t	 hdr;
939 		ng_hci_qos_setup_cp	 cp;
940 	} __attribute__ ((packed))	*req = NULL;
941 	ng_hci_lp_qos_req_ep		*ep = NULL;
942 	ng_hci_unit_con_p		 con = NULL;
943 	struct mbuf			*m = NULL;
944 	int				 error = 0;
945 
946 	/* Check if unit is ready */
947 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
948 		NG_HCI_WARN(
949 "%s: %s - unit is not ready, state=%#x\n",
950 			__func__, NG_NODE_NAME(unit->node), unit->state);
951 
952 		error = ENXIO;
953 		goto out;
954 	}
955 
956 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
957 		NG_HCI_ALERT(
958 "%s: %s - invalid LP_QoSSetupReq message size=%d\n",
959 			__func__, NG_NODE_NAME(unit->node),
960 			NGI_MSG(item)->header.arglen);
961 
962 		error = EMSGSIZE;
963 		goto out;
964 	}
965 
966 	ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data);
967 
968 	con = ng_hci_con_by_handle(unit, ep->con_handle);
969 	if (con == NULL) {
970 		NG_HCI_ERR(
971 "%s: %s - invalid connection handle=%d\n",
972 			__func__, NG_NODE_NAME(unit->node), ep->con_handle);
973 
974 		error = EINVAL;
975 		goto out;
976 	}
977 
978 	if (con->link_type != NG_HCI_LINK_ACL) {
979 		NG_HCI_ERR("%s: %s - invalid link type=%d\n",
980 			__func__, NG_NODE_NAME(unit->node), con->link_type);
981 
982 		error = EINVAL;
983 		goto out;
984 	}
985 
986 	if (con->state != NG_HCI_CON_OPEN) {
987 		NG_HCI_ERR(
988 "%s: %s - invalid connection state=%d, handle=%d\n",
989 			__func__, NG_NODE_NAME(unit->node), con->state,
990 			con->con_handle);
991 
992 		error = EINVAL;
993 		goto out;
994 	}
995 
996 	/*
997 	 * Create HCI command
998 	 */
999 
1000 	MGETHDR(m, M_DONTWAIT, MT_DATA);
1001 	if (m == NULL) {
1002 		error = ENOBUFS;
1003 		goto out;
1004 	}
1005 
1006 	m->m_pkthdr.len = m->m_len = sizeof(*req);
1007 	req = mtod(m, struct qos_setup_req *);
1008 	req->hdr.type = NG_HCI_CMD_PKT;
1009 	req->hdr.length = sizeof(req->cp);
1010 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
1011 			NG_HCI_OCF_QOS_SETUP));
1012 
1013 	req->cp.con_handle = htole16(ep->con_handle);
1014 	req->cp.flags = ep->flags;
1015 	req->cp.service_type = ep->service_type;
1016 	req->cp.token_rate = htole32(ep->token_rate);
1017 	req->cp.peak_bandwidth = htole32(ep->peak_bandwidth);
1018 	req->cp.latency = htole32(ep->latency);
1019 	req->cp.delay_variation = htole32(ep->delay_variation);
1020 
1021 	/*
1022 	 * Adjust connection state
1023  	 */
1024 
1025 	if (hook == unit->acl)
1026 		con->flags |= NG_HCI_CON_NOTIFY_ACL;
1027 	else
1028 		con->flags |= NG_HCI_CON_NOTIFY_SCO;
1029 
1030 	/*
1031 	 * Queue and send HCI command
1032 	 */
1033 
1034 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1035 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1036 		error = ng_hci_send_command(unit);
1037 out:
1038 	NG_FREE_ITEM(item);
1039 
1040 	return (error);
1041 } /* ng_hci_lp_qos_req */
1042 
1043 /*
1044  * Send LP_QoSCfm event to the upper layer protocol
1045  */
1046 
1047 int
1048 ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status)
1049 {
1050 	ng_hci_unit_p		 unit = con->unit;
1051 	struct ng_mesg		*msg = NULL;
1052 	ng_hci_lp_qos_cfm_ep	*ep = NULL;
1053 	int			 error;
1054 
1055 	if (con->flags & NG_HCI_CON_NOTIFY_ACL) {
1056 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1057 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1058 				sizeof(*ep), M_NOWAIT);
1059 			if (msg != NULL) {
1060 				ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1061 				ep->status = status;
1062 				ep->con_handle = con->con_handle;
1063 
1064 				NG_SEND_MSG_HOOK(error, unit->node, msg,
1065 					unit->acl, NULL);
1066 			}
1067 		} else
1068 			NG_HCI_INFO(
1069 "%s: %s - ACL hook not valid, hook=%p\n",
1070 				__func__, NG_NODE_NAME(unit->node), unit->acl);
1071 
1072 		con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
1073 	}
1074 
1075 	if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
1076 		if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1077 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1078 				sizeof(*ep), M_NOWAIT);
1079 			if (msg != NULL) {
1080 				ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1081 				ep->status = status;
1082 				ep->con_handle = con->con_handle;
1083 
1084 				NG_SEND_MSG_HOOK(error, unit->node, msg,
1085 					unit->sco, NULL);
1086 			}
1087 		} else
1088 			NG_HCI_INFO(
1089 "%s: %s - SCO hook not valid, hook=%p\n",
1090 				 __func__, NG_NODE_NAME(unit->node), unit->sco);
1091 
1092 		con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
1093 	}
1094 
1095 	return (0);
1096 } /* ng_hci_lp_qos_cfm */
1097 
1098 /*
1099  * Send LP_QoSViolationInd event to the upper layer protocol
1100  */
1101 
1102 int
1103 ng_hci_lp_qos_ind(ng_hci_unit_con_p con)
1104 {
1105 	ng_hci_unit_p		 unit = con->unit;
1106 	struct ng_mesg		*msg = NULL;
1107 	ng_hci_lp_qos_ind_ep	*ep = NULL;
1108 	int			 error;
1109 
1110 	/*
1111 	 * QoS Violation can only be generated for ACL connection handles.
1112 	 * Both ACL and SCO upstream hooks will receive notification.
1113 	 */
1114 
1115 	if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1116 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1117 			sizeof(*ep), M_NOWAIT);
1118 		if (msg == NULL)
1119 			return (ENOMEM);
1120 
1121 		ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1122 		ep->con_handle = con->con_handle;
1123 
1124 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, NULL);
1125 	} else
1126 		NG_HCI_INFO(
1127 "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
1128 			__func__, NG_NODE_NAME(unit->node), unit->acl);
1129 
1130 	if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1131 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1132 			sizeof(*ep), M_NOWAIT);
1133 		if (msg == NULL)
1134 			return (ENOMEM);
1135 
1136 		ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1137 		ep->con_handle = con->con_handle;
1138 
1139 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, NULL);
1140 	} else
1141 		NG_HCI_INFO(
1142 "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
1143 			__func__, NG_NODE_NAME(unit->node), unit->sco);
1144 
1145 	return (0);
1146 } /* ng_hci_lp_qos_ind */
1147 
1148 /*
1149  * Process connection timeout
1150  */
1151 
1152 void
1153 ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1154 {
1155 	ng_hci_unit_con_p	con = (ng_hci_unit_con_p) arg1;
1156 
1157 	KASSERT((con->flags & NG_HCI_CON_TIMEOUT_PENDING),
1158 ("%s: %s - No connection timeout!\n", __func__, NG_NODE_NAME(node)));
1159 
1160 	con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
1161 
1162 	/*
1163 	 * We expect to receive connection timeout in one of the following
1164 	 * states:
1165 	 *
1166 	 * 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded
1167 	 *    to our LP_CON_IND. Do nothing and destroy connection. Remote peer
1168 	 *    most likely already gave up on us.
1169 	 *
1170 	 * 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection
1171 	 *    (or we in the process of accepting it) and baseband has timedout
1172 	 *    on us. Inform upper layers and send LP_CON_CFM.
1173 	 */
1174 
1175 	switch (con->state) {
1176 	case NG_HCI_CON_W4_LP_CON_RSP:
1177 		break;
1178 
1179 	case NG_HCI_CON_W4_CONN_COMPLETE:
1180 		ng_hci_lp_con_cfm(con, 0xee);
1181 		break;
1182 
1183 	default:
1184 		KASSERT(0,
1185 ("%s: %s - Invalid connection state=%d\n",
1186 			__func__, NG_NODE_NAME(node), con->state));
1187 		break;
1188 	}
1189 
1190 	ng_hci_free_con(con);
1191 } /* ng_hci_process_con_timeout */
1192 
1193