xref: /freebsd/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c (revision 2ff63af9b88c7413b7d71715b5532625752a248e)
1 /*
2  * ng_hci_ulpi.c
3  */
4 
5 /*-
6  * SPDX-License-Identifier: BSD-2-Clause
7  *
8  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: ng_hci_ulpi.c,v 1.7 2003/09/08 18:57:51 max Exp $
33  */
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/endian.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/queue.h>
42 #include <netgraph/ng_message.h>
43 #include <netgraph/netgraph.h>
44 #include <netgraph/bluetooth/include/ng_bluetooth.h>
45 #include <netgraph/bluetooth/include/ng_hci.h>
46 #include <netgraph/bluetooth/hci/ng_hci_var.h>
47 #include <netgraph/bluetooth/hci/ng_hci_cmds.h>
48 #include <netgraph/bluetooth/hci/ng_hci_evnt.h>
49 #include <netgraph/bluetooth/hci/ng_hci_ulpi.h>
50 #include <netgraph/bluetooth/hci/ng_hci_misc.h>
51 
52 /******************************************************************************
53  ******************************************************************************
54  **                 Upper Layer Protocol Interface module
55  ******************************************************************************
56  ******************************************************************************/
57 
58 static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p);
59 static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p);
60 static int ng_hci_lp_le_con_req (ng_hci_unit_p, item_p, hook_p, int);
61 
62 /*
63  * Process LP_ConnectReq event from the upper layer protocol
64  */
65 
66 int
ng_hci_lp_con_req(ng_hci_unit_p unit,item_p item,hook_p hook)67 ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
68 {
69 	int link_type;
70 
71 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
72 		NG_HCI_WARN(
73 "%s: %s - unit is not ready, state=%#x\n",
74 			__func__, NG_NODE_NAME(unit->node), unit->state);
75 
76 		NG_FREE_ITEM(item);
77 
78 		return (ENXIO);
79 	}
80 
81 	if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) {
82 		NG_HCI_ALERT(
83 "%s: %s - invalid LP_ConnectReq message size=%d\n",
84 			__func__, NG_NODE_NAME(unit->node),
85 			NGI_MSG(item)->header.arglen);
86 
87 		NG_FREE_ITEM(item);
88 
89 		return (EMSGSIZE);
90 	}
91 	link_type = ((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type;
92 	switch(link_type){
93 	case NG_HCI_LINK_ACL:
94 		return (ng_hci_lp_acl_con_req(unit, item, hook));
95 	case NG_HCI_LINK_SCO:
96 		if (hook != unit->sco ) {
97 			NG_HCI_WARN(
98 				"%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n",
99 				__func__, NG_NODE_NAME(unit->node), hook);
100 
101 			NG_FREE_ITEM(item);
102 
103 			return (EINVAL);
104 		}
105 
106 		return (ng_hci_lp_sco_con_req(unit, item, hook));
107 	case NG_HCI_LINK_LE_PUBLIC:
108 	case NG_HCI_LINK_LE_RANDOM:
109 		return (ng_hci_lp_le_con_req(unit, item, hook, link_type));
110 	default:
111 		panic("%s: link_type invalid.", __func__);
112 	}
113 
114 	return (EINVAL);
115 } /* ng_hci_lp_con_req */
116 
117 /*
118  * Request to create new ACL connection
119  */
120 
121 static int
ng_hci_lp_acl_con_req(ng_hci_unit_p unit,item_p item,hook_p hook)122 ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
123 {
124 	struct acl_con_req {
125 		ng_hci_cmd_pkt_t	 hdr;
126 		ng_hci_create_con_cp	 cp;
127 	} __attribute__ ((packed))	*req = NULL;
128 	ng_hci_lp_con_req_ep		*ep = NULL;
129 	ng_hci_unit_con_p		 con = NULL;
130 	ng_hci_neighbor_t		*n = NULL;
131 	struct mbuf			*m = NULL;
132 	int				 error = 0;
133 
134 	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
135 
136 	/*
137 	 * Only one ACL connection can exist between each pair of units.
138 	 * So try to find ACL connection descriptor (in any state) that
139 	 * has requested remote BD_ADDR.
140 	 *
141 	 * Two cases:
142 	 *
143 	 * 1) We do not have connection to the remote unit. This is simple.
144 	 *    Just create new connection descriptor and send HCI command to
145 	 *    create new connection.
146 	 *
147 	 * 2) We do have connection descriptor. We need to check connection
148 	 *    state:
149 	 *
150 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
151 	 *      accepting connection from the remote unit. This is a race
152 	 *      condition. We will ignore this message.
153 	 *
154 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
155 	 *      requested connection or we just accepted it. In any case
156 	 *      all we need to do here is set appropriate notification bit
157 	 *      and wait.
158 	 *
159 	 * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back
160 	 *      and let upper layer know that we have connection already.
161 	 */
162 
163 	con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
164 	if (con != NULL) {
165 		switch (con->state) {
166 		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
167 			error = EALREADY;
168 			break;
169 
170 		case NG_HCI_CON_W4_CONN_COMPLETE:
171 			if (hook == unit->acl)
172 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
173 			else
174 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
175 			break;
176 
177 		case NG_HCI_CON_OPEN: {
178 			struct ng_mesg		*msg = NULL;
179 			ng_hci_lp_con_cfm_ep	*cfm = NULL;
180 
181 			if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
182 				NGI_GET_MSG(item, msg);
183 				NG_FREE_MSG(msg);
184 
185 				NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
186 					NGM_HCI_LP_CON_CFM, sizeof(*cfm),
187 					M_NOWAIT);
188 				if (msg != NULL) {
189 					cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
190 					cfm->status = 0;
191 					cfm->link_type = con->link_type;
192 					cfm->con_handle = con->con_handle;
193 					bcopy(&con->bdaddr, &cfm->bdaddr,
194 						sizeof(cfm->bdaddr));
195 
196 					/*
197 					 * This will forward item back to
198 					 * sender and set item to NULL
199 					 */
200 
201 					_NGI_MSG(item) = msg;
202 					NG_FWD_ITEM_HOOK(error, item, hook);
203 				} else
204 					error = ENOMEM;
205 			} else
206 				NG_HCI_INFO(
207 "%s: %s - Source hook is not valid, hook=%p\n",
208 					__func__, NG_NODE_NAME(unit->node),
209 					hook);
210 			} break;
211 
212 		default:
213 			panic(
214 "%s: %s - Invalid connection state=%d\n",
215 				__func__, NG_NODE_NAME(unit->node), con->state);
216 			break;
217 		}
218 
219 		goto out;
220 	}
221 
222 	/*
223 	 * If we got here then we need to create new ACL connection descriptor
224 	 * and submit HCI command. First create new connection desriptor, set
225 	 * bdaddr and notification flags.
226 	 */
227 
228 	con = ng_hci_new_con(unit, NG_HCI_LINK_ACL);
229 	if (con == NULL) {
230 		error = ENOMEM;
231 		goto out;
232 	}
233 
234 	bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
235 
236 	/*
237 	 * Create HCI command
238 	 */
239 
240 	MGETHDR(m, M_NOWAIT, MT_DATA);
241 	if (m == NULL) {
242 		ng_hci_free_con(con);
243 		error = ENOBUFS;
244 		goto out;
245 	}
246 
247 	m->m_pkthdr.len = m->m_len = sizeof(*req);
248 	req = mtod(m, struct acl_con_req *);
249 	req->hdr.type = NG_HCI_CMD_PKT;
250 	req->hdr.length = sizeof(req->cp);
251 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
252 					NG_HCI_OCF_CREATE_CON));
253 
254 	bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr));
255 
256 	req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
257 	if (unit->features[0] & NG_HCI_LMP_3SLOT)
258 		req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3);
259 	if (unit->features[0] & NG_HCI_LMP_5SLOT)
260 		req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5);
261 
262 	req->cp.pkt_type &= unit->packet_mask;
263 	if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1|
264 				 NG_HCI_PKT_DM3|NG_HCI_PKT_DH3|
265 				 NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0)
266 		req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
267 
268 	req->cp.pkt_type = htole16(req->cp.pkt_type);
269 
270 	if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch)
271 		req->cp.accept_role_switch = 1;
272 	else
273 		req->cp.accept_role_switch = 0;
274 
275 	/*
276 	 * We may speed up connect by specifying valid parameters.
277 	 * So check the neighbor cache.
278 	 */
279 
280 	n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
281 	if (n == NULL) {
282 		req->cp.page_scan_rep_mode = 0;
283 		req->cp.page_scan_mode = 0;
284 		req->cp.clock_offset = 0;
285 	} else {
286 		req->cp.page_scan_rep_mode = n->page_scan_rep_mode;
287 		req->cp.page_scan_mode = n->page_scan_mode;
288 		req->cp.clock_offset = htole16(n->clock_offset);
289 	}
290 
291 	/*
292 	 * Adust connection state
293 	 */
294 
295 	if (hook == unit->acl)
296 		con->flags |= NG_HCI_CON_NOTIFY_ACL;
297 	else
298 		con->flags |= NG_HCI_CON_NOTIFY_SCO;
299 
300 	con->state = NG_HCI_CON_W4_CONN_COMPLETE;
301 	ng_hci_con_timeout(con);
302 
303 	/*
304 	 * Queue and send HCI command
305 	 */
306 
307 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
308 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
309 		error = ng_hci_send_command(unit);
310 out:
311 	if (item != NULL)
312 		NG_FREE_ITEM(item);
313 
314 	return (error);
315 } /* ng_hci_lp_acl_con_req */
316 
317 /*
318  * Request to create new SCO connection
319  */
320 
321 static int
ng_hci_lp_sco_con_req(ng_hci_unit_p unit,item_p item,hook_p hook)322 ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
323 {
324 	struct sco_con_req {
325 		ng_hci_cmd_pkt_t	 hdr;
326 		ng_hci_add_sco_con_cp	 cp;
327 	} __attribute__ ((packed))	*req = NULL;
328 	ng_hci_lp_con_req_ep		*ep = NULL;
329 	ng_hci_unit_con_p		 acl_con = NULL, sco_con = NULL;
330 	struct mbuf			*m = NULL;
331 	int				 error = 0;
332 
333 	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
334 
335 	/*
336 	 * SCO connection without ACL link
337 	 *
338 	 * If upper layer requests SCO connection and there is no open ACL
339 	 * connection to the desired remote unit, we will reject the request.
340 	 */
341 
342 	LIST_FOREACH(acl_con, &unit->con_list, next)
343 		if (acl_con->link_type == NG_HCI_LINK_ACL &&
344 		    acl_con->state == NG_HCI_CON_OPEN &&
345 		    bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
346 			break;
347 
348 	if (acl_con == NULL) {
349 		NG_HCI_INFO(
350 "%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n",
351 			__func__, NG_NODE_NAME(unit->node),
352 			ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
353 			ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
354 
355 		error = ENOENT;
356 		goto out;
357 	}
358 
359 	/*
360 	 * Multiple SCO connections can exist between the same pair of units.
361 	 * We assume that multiple SCO connections have to be opened one after
362 	 * another.
363 	 *
364 	 * Try to find SCO connection descriptor that matches the following:
365 	 *
366 	 * 1) sco_con->link_type == NG_HCI_LINK_SCO
367 	 *
368 	 * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
369 	 *    sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE
370 	 *
371 	 * 3) sco_con->bdaddr == ep->bdaddr
372 	 *
373 	 * Two cases:
374 	 *
375 	 * 1) We do not have connection descriptor. This is simple. Just
376 	 *    create new connection and submit Add_SCO_Connection command.
377 	 *
378 	 * 2) We do have connection descriptor. We need to check the state.
379 	 *
380 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting
381 	 *      connection from the remote unit. This is a race condition and
382 	 *      we will ignore the request.
383 	 *
384 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested
385 	 *      connection or we just accepted it.
386 	 */
387 
388 	LIST_FOREACH(sco_con, &unit->con_list, next)
389 		if (sco_con->link_type == NG_HCI_LINK_SCO &&
390 		    (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
391 		     sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
392 		    bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
393 			break;
394 
395 	if (sco_con != NULL) {
396 		switch (sco_con->state) {
397 		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
398 			error = EALREADY;
399 			break;
400 
401 		case NG_HCI_CON_W4_CONN_COMPLETE:
402 			sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
403 			break;
404 
405 		default:
406 			panic(
407 "%s: %s - Invalid connection state=%d\n",
408 				__func__, NG_NODE_NAME(unit->node),
409 				sco_con->state);
410 			break;
411 		}
412 
413 		goto out;
414 	}
415 
416 	/*
417 	 * If we got here then we need to create new SCO connection descriptor
418 	 * and submit HCI command.
419 	 */
420 
421 	sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO);
422 	if (sco_con == NULL) {
423 		error = ENOMEM;
424 		goto out;
425 	}
426 
427 	bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr));
428 
429 	/*
430 	 * Create HCI command
431 	 */
432 
433 	MGETHDR(m, M_NOWAIT, MT_DATA);
434 	if (m == NULL) {
435 		ng_hci_free_con(sco_con);
436 		error = ENOBUFS;
437 		goto out;
438 	}
439 
440 	m->m_pkthdr.len = m->m_len = sizeof(*req);
441 	req = mtod(m, struct sco_con_req *);
442 	req->hdr.type = NG_HCI_CMD_PKT;
443 	req->hdr.length = sizeof(req->cp);
444 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
445 					NG_HCI_OCF_ADD_SCO_CON));
446 
447 	req->cp.con_handle = htole16(acl_con->con_handle);
448 
449 	req->cp.pkt_type = NG_HCI_PKT_HV1;
450 	if (unit->features[1] & NG_HCI_LMP_HV2_PKT)
451 		req->cp.pkt_type |= NG_HCI_PKT_HV2;
452 	if (unit->features[1] & NG_HCI_LMP_HV3_PKT)
453 		req->cp.pkt_type |= NG_HCI_PKT_HV3;
454 
455 	req->cp.pkt_type &= unit->packet_mask;
456 	if ((req->cp.pkt_type & (NG_HCI_PKT_HV1|
457 				 NG_HCI_PKT_HV2|
458 				 NG_HCI_PKT_HV3)) == 0)
459 		req->cp.pkt_type = NG_HCI_PKT_HV1;
460 
461 	req->cp.pkt_type = htole16(req->cp.pkt_type);
462 
463 	/*
464 	 * Adust connection state
465 	 */
466 
467 	sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
468 
469 	sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE;
470 	ng_hci_con_timeout(sco_con);
471 
472 	/*
473 	 * Queue and send HCI command
474 	 */
475 
476 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
477 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
478 		error = ng_hci_send_command(unit);
479 out:
480 	NG_FREE_ITEM(item);
481 
482 	return (error);
483 } /* ng_hci_lp_sco_con_req */
484 
485 static int
ng_hci_lp_le_con_req(ng_hci_unit_p unit,item_p item,hook_p hook,int link_type)486 ng_hci_lp_le_con_req(ng_hci_unit_p unit, item_p item, hook_p hook, int link_type)
487 {
488 	struct acl_con_req {
489 		ng_hci_cmd_pkt_t	 hdr;
490 		ng_hci_le_create_connection_cp	 cp;
491 	} __attribute__ ((packed))	*req = NULL;
492 	ng_hci_lp_con_req_ep		*ep = NULL;
493 	ng_hci_unit_con_p		 con = NULL;
494 	struct mbuf			*m = NULL;
495 	int				 error = 0;
496 
497 	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
498 	if((link_type != NG_HCI_LINK_LE_PUBLIC)&&
499 	   (link_type != NG_HCI_LINK_LE_RANDOM)){
500 		printf("%s: Link type %d Cannot be here \n", __func__,
501 		       link_type);
502 	}
503 	/*
504 	 * Only one ACL connection can exist between each pair of units.
505 	 * So try to find ACL connection descriptor (in any state) that
506 	 * has requested remote BD_ADDR.
507 	 *
508 	 * Two cases:
509 	 *
510 	 * 1) We do not have connection to the remote unit. This is simple.
511 	 *    Just create new connection descriptor and send HCI command to
512 	 *    create new connection.
513 	 *
514 	 * 2) We do have connection descriptor. We need to check connection
515 	 *    state:
516 	 *
517 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
518 	 *      accepting connection from the remote unit. This is a race
519 	 *      condition. We will ignore this message.
520 	 *
521 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
522 	 *      requested connection or we just accepted it. In any case
523 	 *      all we need to do here is set appropriate notification bit
524 	 *      and wait.
525 	 *
526 	 * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back
527 	 *      and let upper layer know that we have connection already.
528 	 */
529 
530 	con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, link_type);
531 	if (con != NULL) {
532 		switch (con->state) {
533 		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
534 			error = EALREADY;
535 			break;
536 
537 		case NG_HCI_CON_W4_CONN_COMPLETE:
538 			if (hook != unit->sco)
539 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
540 			else
541 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
542 			break;
543 
544 		case NG_HCI_CON_OPEN: {
545 			struct ng_mesg		*msg = NULL;
546 			ng_hci_lp_con_cfm_ep	*cfm = NULL;
547 
548 			if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
549 				NGI_GET_MSG(item, msg);
550 				NG_FREE_MSG(msg);
551 
552 				NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
553 					NGM_HCI_LP_CON_CFM, sizeof(*cfm),
554 					M_NOWAIT);
555 				if (msg != NULL) {
556 					cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
557 					cfm->status = 0;
558 					cfm->link_type = con->link_type;
559 					cfm->con_handle = con->con_handle;
560 					bcopy(&con->bdaddr, &cfm->bdaddr,
561 						sizeof(cfm->bdaddr));
562 
563 					/*
564 					 * This will forward item back to
565 					 * sender and set item to NULL
566 					 */
567 
568 					_NGI_MSG(item) = msg;
569 					NG_FWD_ITEM_HOOK(error, item, hook);
570 				} else
571 					error = ENOMEM;
572 			} else
573 				NG_HCI_INFO(
574 "%s: %s - Source hook is not valid, hook=%p\n",
575 					__func__, NG_NODE_NAME(unit->node),
576 					hook);
577 			} break;
578 
579 		default:
580 			panic(
581 "%s: %s - Invalid connection state=%d\n",
582 				__func__, NG_NODE_NAME(unit->node), con->state);
583 			break;
584 		}
585 
586 		goto out;
587 	}
588 
589 	/*
590 	 * If we got here then we need to create new ACL connection descriptor
591 	 * and submit HCI command. First create new connection desriptor, set
592 	 * bdaddr and notification flags.
593 	 */
594 
595 	con = ng_hci_new_con(unit, link_type);
596 	if (con == NULL) {
597 		error = ENOMEM;
598 		goto out;
599 	}
600 
601 	bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
602 
603 	/*
604 	 * Create HCI command
605 	 */
606 
607 	MGETHDR(m, M_NOWAIT, MT_DATA);
608 	if (m == NULL) {
609 		ng_hci_free_con(con);
610 		error = ENOBUFS;
611 		goto out;
612 	}
613 
614 	m->m_pkthdr.len = m->m_len = sizeof(*req);
615 	req = mtod(m, struct acl_con_req *);
616 	req->hdr.type = NG_HCI_CMD_PKT;
617 	req->hdr.length = sizeof(req->cp);
618 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LE,
619 					NG_HCI_OCF_LE_CREATE_CONNECTION));
620 
621 	bcopy(&ep->bdaddr, &req->cp.peer_addr, sizeof(req->cp.peer_addr));
622 	req->cp.own_address_type = 0;
623 	req->cp.peer_addr_type = (link_type == NG_HCI_LINK_LE_RANDOM)? 1:0;
624 	req->cp.scan_interval = htole16(4);
625 	req->cp.scan_window = htole16(4);
626 	req->cp.filter_policy = 0;
627 	req->cp.conn_interval_min = htole16(0xf);
628 	req->cp.conn_interval_max = htole16(0xf);
629 	req->cp.conn_latency = htole16(0);
630 	req->cp.supervision_timeout = htole16(0xc80);
631 	req->cp.min_ce_length = htole16(1);
632 	req->cp.max_ce_length = htole16(1);
633 	/*
634 	 * Adust connection state
635 	 */
636 
637 	if (hook != unit->sco)
638 		con->flags |= NG_HCI_CON_NOTIFY_ACL;
639 	else
640 		con->flags |= NG_HCI_CON_NOTIFY_SCO;
641 
642 	con->state = NG_HCI_CON_W4_CONN_COMPLETE;
643 	ng_hci_con_timeout(con);
644 
645 	/*
646 	 * Queue and send HCI command
647 	 */
648 
649 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
650 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
651 		error = ng_hci_send_command(unit);
652 out:
653 	if (item != NULL)
654 		NG_FREE_ITEM(item);
655 
656 	return (error);
657 } /* ng_hci_lp_acl_con_req */
658 
659 /*
660  * Process LP_DisconnectReq event from the upper layer protocol
661  */
662 
663 int
ng_hci_lp_discon_req(ng_hci_unit_p unit,item_p item,hook_p hook)664 ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook)
665 {
666 	struct discon_req {
667 		ng_hci_cmd_pkt_t	 hdr;
668 		ng_hci_discon_cp	 cp;
669 	} __attribute__ ((packed))	*req = NULL;
670 	ng_hci_lp_discon_req_ep		*ep = NULL;
671 	ng_hci_unit_con_p		 con = NULL;
672 	struct mbuf			*m = NULL;
673 	int				 error = 0;
674 
675 	/* Check if unit is ready */
676 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
677 		NG_HCI_WARN(
678 "%s: %s - unit is not ready, state=%#x\n",
679 			__func__, NG_NODE_NAME(unit->node), unit->state);
680 
681 		error = ENXIO;
682 		goto out;
683 	}
684 
685 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
686 		NG_HCI_ALERT(
687 "%s: %s - invalid LP_DisconnectReq message size=%d\n",
688 			__func__, NG_NODE_NAME(unit->node),
689 			NGI_MSG(item)->header.arglen);
690 
691 		error = EMSGSIZE;
692 		goto out;
693 	}
694 
695 	ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data);
696 
697 	con = ng_hci_con_by_handle(unit, ep->con_handle);
698 	if (con == NULL) {
699 		NG_HCI_ERR(
700 "%s: %s - invalid connection handle=%d\n",
701 			__func__, NG_NODE_NAME(unit->node), ep->con_handle);
702 
703 		error = ENOENT;
704 		goto out;
705 	}
706 
707 	if (con->state != NG_HCI_CON_OPEN) {
708 		NG_HCI_ERR(
709 "%s: %s - invalid connection state=%d, handle=%d\n",
710 			__func__, NG_NODE_NAME(unit->node), con->state,
711 			ep->con_handle);
712 
713 		error = EINVAL;
714 		goto out;
715 	}
716 
717 	/*
718 	 * Create HCI command
719 	 */
720 
721 	MGETHDR(m, M_NOWAIT, MT_DATA);
722 	if (m == NULL) {
723 		error = ENOBUFS;
724 		goto out;
725 	}
726 
727 	m->m_pkthdr.len = m->m_len = sizeof(*req);
728 	req = mtod(m, struct discon_req *);
729 	req->hdr.type = NG_HCI_CMD_PKT;
730 	req->hdr.length = sizeof(req->cp);
731 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
732 							NG_HCI_OCF_DISCON));
733 
734 	req->cp.con_handle = htole16(ep->con_handle);
735 	req->cp.reason = ep->reason;
736 
737 	/*
738 	 * Queue and send HCI command
739 	 */
740 
741 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
742 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
743 		error = ng_hci_send_command(unit);
744 out:
745 	NG_FREE_ITEM(item);
746 
747 	return (error);
748 } /* ng_hci_lp_discon_req */
749 
750 /*
751  * Send LP_ConnectCfm event to the upper layer protocol
752  */
753 
754 int
ng_hci_lp_con_cfm(ng_hci_unit_con_p con,int status)755 ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status)
756 {
757 	ng_hci_unit_p		 unit = con->unit;
758 	struct ng_mesg		*msg = NULL;
759 	ng_hci_lp_con_cfm_ep	*ep = NULL;
760 	int			 error;
761 
762 	/*
763 	 * Check who wants to be notified. For ACL links both ACL and SCO
764 	 * upstream hooks will be notified (if required). For SCO links
765 	 * only SCO upstream hook will receive notification
766 	 */
767 
768 	if (con->link_type != NG_HCI_LINK_SCO &&
769 	    con->flags & NG_HCI_CON_NOTIFY_ACL) {
770 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
771 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
772 				sizeof(*ep), M_NOWAIT);
773 			if (msg != NULL) {
774 				ep = (ng_hci_lp_con_cfm_ep *) msg->data;
775 				ep->status = status;
776 				ep->link_type = con->link_type;
777 				ep->con_handle = con->con_handle;
778 				bcopy(&con->bdaddr, &ep->bdaddr,
779 					sizeof(ep->bdaddr));
780 
781 				NG_SEND_MSG_HOOK(error, unit->node, msg,
782 					unit->acl, 0);
783 			}
784 		} else
785 			NG_HCI_INFO(
786 "%s: %s - ACL hook not valid, hook=%p\n",
787 				__func__, NG_NODE_NAME(unit->node), unit->acl);
788 
789 		con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
790 	}
791 
792 	if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
793 		if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
794 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
795 				sizeof(*ep), M_NOWAIT);
796 			if (msg != NULL) {
797 				ep = (ng_hci_lp_con_cfm_ep *) msg->data;
798 				ep->status = status;
799 				ep->link_type = con->link_type;
800 				ep->con_handle = con->con_handle;
801 				bcopy(&con->bdaddr, &ep->bdaddr,
802 					sizeof(ep->bdaddr));
803 
804 				NG_SEND_MSG_HOOK(error, unit->node, msg,
805 					unit->sco, 0);
806 			}
807 		} else
808 			NG_HCI_INFO(
809 "%s: %s - SCO hook not valid, hook=%p\n",
810 				__func__, NG_NODE_NAME(unit->node), unit->acl);
811 
812 		con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
813 	}
814 
815 	return (0);
816 } /* ng_hci_lp_con_cfm */
817 
818 int
ng_hci_lp_enc_change(ng_hci_unit_con_p con,int status)819 ng_hci_lp_enc_change(ng_hci_unit_con_p con, int status)
820 {
821 	ng_hci_unit_p		 unit = con->unit;
822 	struct ng_mesg		*msg = NULL;
823 	ng_hci_lp_enc_change_ep	*ep = NULL;
824 	int			 error;
825 
826 	if (con->link_type != NG_HCI_LINK_SCO) {
827 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
828 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_ENC_CHG,
829 				sizeof(*ep), M_NOWAIT);
830 			if (msg != NULL) {
831 				ep = (ng_hci_lp_enc_change_ep *) msg->data;
832 				ep->status = status;
833 				ep->link_type = con->link_type;
834 				ep->con_handle = con->con_handle;
835 
836 				NG_SEND_MSG_HOOK(error, unit->node, msg,
837 					unit->acl, 0);
838 			}
839 		} else
840 			NG_HCI_INFO(
841 "%s: %s - ACL hook not valid, hook=%p\n",
842 				__func__, NG_NODE_NAME(unit->node), unit->acl);
843 	}
844 	return (0);
845 } /* ng_hci_lp_con_cfm */
846 
847 /*
848  * Send LP_ConnectInd event to the upper layer protocol
849  */
850 
851 int
ng_hci_lp_con_ind(ng_hci_unit_con_p con,u_int8_t * uclass)852 ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass)
853 {
854 	ng_hci_unit_p		 unit = con->unit;
855 	struct ng_mesg		*msg = NULL;
856 	ng_hci_lp_con_ind_ep	*ep = NULL;
857 	hook_p			 hook = NULL;
858 	int			 error = 0;
859 
860 	/*
861 	 * Connection_Request event is generated for specific link type.
862 	 * Use link_type to select upstream hook.
863 	 */
864 
865 	if (con->link_type != NG_HCI_LINK_SCO)
866 		hook = unit->acl;
867 	else
868 		hook = unit->sco;
869 
870 	if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
871 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND,
872 			sizeof(*ep), M_NOWAIT);
873 		if (msg == NULL)
874 			return (ENOMEM);
875 
876 		ep = (ng_hci_lp_con_ind_ep *)(msg->data);
877 		ep->link_type = con->link_type;
878 		bcopy(uclass, ep->uclass, sizeof(ep->uclass));
879 		bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
880 
881 		NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0);
882 	} else {
883 		NG_HCI_WARN(
884 "%s: %s - Upstream hook is not connected or not valid, hook=%p\n",
885 			__func__, NG_NODE_NAME(unit->node), hook);
886 
887 		error = ENOTCONN;
888 	}
889 
890 	return (error);
891 } /* ng_hci_lp_con_ind */
892 
893 /*
894  * Process LP_ConnectRsp event from the upper layer protocol
895  */
896 
897 int
ng_hci_lp_con_rsp(ng_hci_unit_p unit,item_p item,hook_p hook)898 ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook)
899 {
900 	struct con_rsp_req {
901 		ng_hci_cmd_pkt_t		 hdr;
902 		union {
903 			ng_hci_accept_con_cp	 acc;
904 			ng_hci_reject_con_cp	 rej;
905 		} __attribute__ ((packed))	 cp;
906 	} __attribute__ ((packed))		*req = NULL;
907 	ng_hci_lp_con_rsp_ep			*ep = NULL;
908 	ng_hci_unit_con_p			 con = NULL;
909 	struct mbuf				*m = NULL;
910 	int					 error = 0;
911 
912 	/* Check if unit is ready */
913 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
914 		NG_HCI_WARN(
915 "%s: %s - unit is not ready, state=%#x\n",
916 			__func__, NG_NODE_NAME(unit->node), unit->state);
917 
918 		error = ENXIO;
919 		goto out;
920 	}
921 
922 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
923 		NG_HCI_ALERT(
924 "%s: %s - invalid LP_ConnectRsp message size=%d\n",
925 			__func__, NG_NODE_NAME(unit->node),
926 			NGI_MSG(item)->header.arglen);
927 
928 		error = EMSGSIZE;
929 		goto out;
930 	}
931 
932 	ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data);
933 
934 	/*
935 	 * Here we have to deal with race. Upper layers might send conflicting
936 	 * requests. One might send Accept and other Reject. We will not try
937 	 * to solve all the problems, so first request will always win.
938 	 *
939 	 * Try to find connection that matches the following:
940 	 *
941 	 * 1) con->link_type == ep->link_type
942 	 *
943 	 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
944 	 *    con->state == NG_HCI_CON_W4_CONN_COMPLETE
945 	 *
946 	 * 3) con->bdaddr == ep->bdaddr
947 	 *
948 	 * Two cases:
949 	 *
950 	 * 1) We do not have connection descriptor. Could be bogus request or
951 	 *    we have rejected connection already.
952 	 *
953 	 * 2) We do have connection descriptor. Then we need to check state:
954 	 *
955 	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested
956 	 *      connection and it is a first response from the upper layer.
957 	 *      if "status == 0" (Accept) then we will send Accept_Connection
958 	 *      command and change connection state to W4_CONN_COMPLETE, else
959 	 *      send reject and delete connection.
960 	 *
961 	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted
962 	 *      connection. If "status == 0" we just need to link request
963 	 *      and wait, else ignore Reject request.
964 	 */
965 
966 	LIST_FOREACH(con, &unit->con_list, next)
967 		if (con->link_type == ep->link_type &&
968 		    (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
969 		     con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
970 		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
971 			break;
972 
973 	if (con == NULL) {
974 		/* Reject for non-existing connection is fine */
975 		error = (ep->status == 0)? ENOENT : 0;
976 		goto out;
977 	}
978 
979 	/*
980 	 * Remove connection timeout and check connection state.
981 	 * Note: if ng_hci_con_untimeout() fails (returns non-zero value) then
982 	 * timeout already happened and event went into node's queue.
983 	 */
984 
985 	if ((error = ng_hci_con_untimeout(con)) != 0)
986 		goto out;
987 
988 	switch (con->state) {
989 	case NG_HCI_CON_W4_LP_CON_RSP:
990 
991 		/*
992 		 * Create HCI command
993 		 */
994 
995 		MGETHDR(m, M_NOWAIT, MT_DATA);
996 		if (m == NULL) {
997 			error = ENOBUFS;
998 			goto out;
999 		}
1000 
1001 		req = mtod(m, struct con_rsp_req *);
1002 		req->hdr.type = NG_HCI_CMD_PKT;
1003 
1004 		if (ep->status == 0) {
1005 			req->hdr.length = sizeof(req->cp.acc);
1006 			req->hdr.opcode = htole16(NG_HCI_OPCODE(
1007 							NG_HCI_OGF_LINK_CONTROL,
1008 							NG_HCI_OCF_ACCEPT_CON));
1009 
1010 			bcopy(&ep->bdaddr, &req->cp.acc.bdaddr,
1011 				sizeof(req->cp.acc.bdaddr));
1012 
1013 			/*
1014 			 * We are accepting connection, so if we support role
1015 			 * switch and role switch was enabled then set role to
1016 			 * NG_HCI_ROLE_MASTER and let LM perform role switch.
1017 			 * Otherwise we remain slave. In this case LM WILL NOT
1018 			 * perform role switch.
1019 			 */
1020 
1021 			if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&
1022 			    unit->role_switch)
1023 				req->cp.acc.role = NG_HCI_ROLE_MASTER;
1024 			else
1025 				req->cp.acc.role = NG_HCI_ROLE_SLAVE;
1026 
1027 			/*
1028 			 * Adjust connection state
1029 			 */
1030 
1031 			if (hook == unit->acl)
1032 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
1033 			else
1034 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
1035 
1036 			con->state = NG_HCI_CON_W4_CONN_COMPLETE;
1037 			ng_hci_con_timeout(con);
1038 		} else {
1039 			req->hdr.length = sizeof(req->cp.rej);
1040 			req->hdr.opcode = htole16(NG_HCI_OPCODE(
1041 							NG_HCI_OGF_LINK_CONTROL,
1042 							NG_HCI_OCF_REJECT_CON));
1043 
1044 			bcopy(&ep->bdaddr, &req->cp.rej.bdaddr,
1045 				sizeof(req->cp.rej.bdaddr));
1046 
1047 			req->cp.rej.reason = ep->status;
1048 
1049 			/*
1050 			 * Free connection descritor
1051 			 * Item will be deleted just before return.
1052 			 */
1053 
1054 			ng_hci_free_con(con);
1055 		}
1056 
1057 		m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length;
1058 
1059 		/* Queue and send HCI command */
1060 		NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1061 		if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1062 			error = ng_hci_send_command(unit);
1063 		break;
1064 
1065 	case NG_HCI_CON_W4_CONN_COMPLETE:
1066 		if (ep->status == 0) {
1067 			if (hook == unit->acl)
1068 				con->flags |= NG_HCI_CON_NOTIFY_ACL;
1069 			else
1070 				con->flags |= NG_HCI_CON_NOTIFY_SCO;
1071 		} else
1072 			error = EPERM;
1073 		break;
1074 
1075 	default:
1076 		panic(
1077 "%s: %s - Invalid connection state=%d\n",
1078 			__func__, NG_NODE_NAME(unit->node), con->state);
1079 		break;
1080 	}
1081 out:
1082 	NG_FREE_ITEM(item);
1083 
1084 	return (error);
1085 } /* ng_hci_lp_con_rsp */
1086 
1087 /*
1088  * Send LP_DisconnectInd to the upper layer protocol
1089  */
1090 
1091 int
ng_hci_lp_discon_ind(ng_hci_unit_con_p con,int reason)1092 ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason)
1093 {
1094 	ng_hci_unit_p		 unit = con->unit;
1095 	struct ng_mesg		*msg = NULL;
1096 	ng_hci_lp_discon_ind_ep	*ep = NULL;
1097 	int			 error = 0;
1098 
1099 	/*
1100 	 * Disconnect_Complete event is generated for specific connection
1101 	 * handle. For ACL connection handles both ACL and SCO upstream
1102 	 * hooks will receive notification. For SCO connection handles
1103 	 * only SCO upstream hook will receive notification.
1104 	 */
1105 
1106 	if (con->link_type != NG_HCI_LINK_SCO) {
1107 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1108 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
1109 				NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT);
1110 			if (msg == NULL)
1111 				return (ENOMEM);
1112 
1113 			ep = (ng_hci_lp_discon_ind_ep *) msg->data;
1114 			ep->reason = reason;
1115 			ep->link_type = con->link_type;
1116 			ep->con_handle = con->con_handle;
1117 
1118 			NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,0);
1119 		} else
1120 			NG_HCI_INFO(
1121 "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
1122 				__func__, NG_NODE_NAME(unit->node), unit->acl);
1123 	}
1124 
1125 	if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1126 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND,
1127 			sizeof(*ep), M_NOWAIT);
1128 		if (msg == NULL)
1129 			return (ENOMEM);
1130 
1131 		ep = (ng_hci_lp_discon_ind_ep *) msg->data;
1132 		ep->reason = reason;
1133 		ep->link_type = con->link_type;
1134 		ep->con_handle = con->con_handle;
1135 
1136 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);
1137 	} else
1138 		NG_HCI_INFO(
1139 "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
1140 			__func__, NG_NODE_NAME(unit->node), unit->sco);
1141 
1142 	return (0);
1143 } /* ng_hci_lp_discon_ind */
1144 
1145 /*
1146  * Process LP_QoSReq action from the upper layer protocol
1147  */
1148 
1149 int
ng_hci_lp_qos_req(ng_hci_unit_p unit,item_p item,hook_p hook)1150 ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook)
1151 {
1152 	struct qos_setup_req {
1153 		ng_hci_cmd_pkt_t	 hdr;
1154 		ng_hci_qos_setup_cp	 cp;
1155 	} __attribute__ ((packed))	*req = NULL;
1156 	ng_hci_lp_qos_req_ep		*ep = NULL;
1157 	ng_hci_unit_con_p		 con = NULL;
1158 	struct mbuf			*m = NULL;
1159 	int				 error = 0;
1160 
1161 	/* Check if unit is ready */
1162 	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
1163 		NG_HCI_WARN(
1164 "%s: %s - unit is not ready, state=%#x\n",
1165 			__func__, NG_NODE_NAME(unit->node), unit->state);
1166 
1167 		error = ENXIO;
1168 		goto out;
1169 	}
1170 
1171 	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
1172 		NG_HCI_ALERT(
1173 "%s: %s - invalid LP_QoSSetupReq message size=%d\n",
1174 			__func__, NG_NODE_NAME(unit->node),
1175 			NGI_MSG(item)->header.arglen);
1176 
1177 		error = EMSGSIZE;
1178 		goto out;
1179 	}
1180 
1181 	ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data);
1182 
1183 	con = ng_hci_con_by_handle(unit, ep->con_handle);
1184 	if (con == NULL) {
1185 		NG_HCI_ERR(
1186 "%s: %s - invalid connection handle=%d\n",
1187 			__func__, NG_NODE_NAME(unit->node), ep->con_handle);
1188 
1189 		error = EINVAL;
1190 		goto out;
1191 	}
1192 
1193 	if (con->link_type != NG_HCI_LINK_ACL) {
1194 		NG_HCI_ERR("%s: %s - invalid link type=%d\n",
1195 			__func__, NG_NODE_NAME(unit->node), con->link_type);
1196 
1197 		error = EINVAL;
1198 		goto out;
1199 	}
1200 
1201 	if (con->state != NG_HCI_CON_OPEN) {
1202 		NG_HCI_ERR(
1203 "%s: %s - invalid connection state=%d, handle=%d\n",
1204 			__func__, NG_NODE_NAME(unit->node), con->state,
1205 			con->con_handle);
1206 
1207 		error = EINVAL;
1208 		goto out;
1209 	}
1210 
1211 	/*
1212 	 * Create HCI command
1213 	 */
1214 
1215 	MGETHDR(m, M_NOWAIT, MT_DATA);
1216 	if (m == NULL) {
1217 		error = ENOBUFS;
1218 		goto out;
1219 	}
1220 
1221 	m->m_pkthdr.len = m->m_len = sizeof(*req);
1222 	req = mtod(m, struct qos_setup_req *);
1223 	req->hdr.type = NG_HCI_CMD_PKT;
1224 	req->hdr.length = sizeof(req->cp);
1225 	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
1226 			NG_HCI_OCF_QOS_SETUP));
1227 
1228 	req->cp.con_handle = htole16(ep->con_handle);
1229 	req->cp.flags = ep->flags;
1230 	req->cp.service_type = ep->service_type;
1231 	req->cp.token_rate = htole32(ep->token_rate);
1232 	req->cp.peak_bandwidth = htole32(ep->peak_bandwidth);
1233 	req->cp.latency = htole32(ep->latency);
1234 	req->cp.delay_variation = htole32(ep->delay_variation);
1235 
1236 	/*
1237 	 * Adjust connection state
1238  	 */
1239 
1240 	if (hook == unit->acl)
1241 		con->flags |= NG_HCI_CON_NOTIFY_ACL;
1242 	else
1243 		con->flags |= NG_HCI_CON_NOTIFY_SCO;
1244 
1245 	/*
1246 	 * Queue and send HCI command
1247 	 */
1248 
1249 	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1250 	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1251 		error = ng_hci_send_command(unit);
1252 out:
1253 	NG_FREE_ITEM(item);
1254 
1255 	return (error);
1256 } /* ng_hci_lp_qos_req */
1257 
1258 /*
1259  * Send LP_QoSCfm event to the upper layer protocol
1260  */
1261 
1262 int
ng_hci_lp_qos_cfm(ng_hci_unit_con_p con,int status)1263 ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status)
1264 {
1265 	ng_hci_unit_p		 unit = con->unit;
1266 	struct ng_mesg		*msg = NULL;
1267 	ng_hci_lp_qos_cfm_ep	*ep = NULL;
1268 	int			 error;
1269 
1270 	if (con->flags & NG_HCI_CON_NOTIFY_ACL) {
1271 		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1272 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1273 				sizeof(*ep), M_NOWAIT);
1274 			if (msg != NULL) {
1275 				ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1276 				ep->status = status;
1277 				ep->con_handle = con->con_handle;
1278 
1279 				NG_SEND_MSG_HOOK(error, unit->node, msg,
1280 					unit->acl, 0);
1281 			}
1282 		} else
1283 			NG_HCI_INFO(
1284 "%s: %s - ACL hook not valid, hook=%p\n",
1285 				__func__, NG_NODE_NAME(unit->node), unit->acl);
1286 
1287 		con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
1288 	}
1289 
1290 	if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
1291 		if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1292 			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1293 				sizeof(*ep), M_NOWAIT);
1294 			if (msg != NULL) {
1295 				ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1296 				ep->status = status;
1297 				ep->con_handle = con->con_handle;
1298 
1299 				NG_SEND_MSG_HOOK(error, unit->node, msg,
1300 					unit->sco, 0);
1301 			}
1302 		} else
1303 			NG_HCI_INFO(
1304 "%s: %s - SCO hook not valid, hook=%p\n",
1305 				 __func__, NG_NODE_NAME(unit->node), unit->sco);
1306 
1307 		con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
1308 	}
1309 
1310 	return (0);
1311 } /* ng_hci_lp_qos_cfm */
1312 
1313 /*
1314  * Send LP_QoSViolationInd event to the upper layer protocol
1315  */
1316 
1317 int
ng_hci_lp_qos_ind(ng_hci_unit_con_p con)1318 ng_hci_lp_qos_ind(ng_hci_unit_con_p con)
1319 {
1320 	ng_hci_unit_p		 unit = con->unit;
1321 	struct ng_mesg		*msg = NULL;
1322 	ng_hci_lp_qos_ind_ep	*ep = NULL;
1323 	int			 error;
1324 
1325 	/*
1326 	 * QoS Violation can only be generated for ACL connection handles.
1327 	 * Both ACL and SCO upstream hooks will receive notification.
1328 	 */
1329 
1330 	if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1331 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1332 			sizeof(*ep), M_NOWAIT);
1333 		if (msg == NULL)
1334 			return (ENOMEM);
1335 
1336 		ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1337 		ep->con_handle = con->con_handle;
1338 
1339 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0);
1340 	} else
1341 		NG_HCI_INFO(
1342 "%s: %s - ACL hook is not connected or not valid, hook=%p\n",
1343 			__func__, NG_NODE_NAME(unit->node), unit->acl);
1344 
1345 	if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1346 		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1347 			sizeof(*ep), M_NOWAIT);
1348 		if (msg == NULL)
1349 			return (ENOMEM);
1350 
1351 		ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1352 		ep->con_handle = con->con_handle;
1353 
1354 		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);
1355 	} else
1356 		NG_HCI_INFO(
1357 "%s: %s - SCO hook is not connected or not valid, hook=%p\n",
1358 			__func__, NG_NODE_NAME(unit->node), unit->sco);
1359 
1360 	return (0);
1361 } /* ng_hci_lp_qos_ind */
1362 
1363 /*
1364  * Process connection timeout
1365  */
1366 
1367 void
ng_hci_process_con_timeout(node_p node,hook_p hook,void * arg1,int con_handle)1368 ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
1369 {
1370 	ng_hci_unit_p		unit = NULL;
1371 	ng_hci_unit_con_p	con = NULL;
1372 
1373 	if (NG_NODE_NOT_VALID(node)) {
1374 		printf("%s: Netgraph node is not valid\n", __func__);
1375 		return;
1376 	}
1377 
1378 	unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
1379 	con = ng_hci_con_by_handle(unit, con_handle);
1380 
1381 	if (con == NULL) {
1382 		NG_HCI_ALERT(
1383 "%s: %s - could not find connection, handle=%d\n",
1384 			__func__, NG_NODE_NAME(node), con_handle);
1385 		return;
1386 	}
1387 
1388 	if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) {
1389 		NG_HCI_ALERT(
1390 "%s: %s - no pending connection timeout, handle=%d, state=%d, flags=%#x\n",
1391 			__func__, NG_NODE_NAME(node), con_handle, con->state,
1392 			con->flags);
1393 		return;
1394 	}
1395 
1396 	con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
1397 
1398 	/*
1399 	 * We expect to receive connection timeout in one of the following
1400 	 * states:
1401 	 *
1402 	 * 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded
1403 	 *    to our LP_CON_IND. Do nothing and destroy connection. Remote peer
1404 	 *    most likely already gave up on us.
1405 	 *
1406 	 * 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection
1407 	 *    (or we in the process of accepting it) and baseband has timedout
1408 	 *    on us. Inform upper layers and send LP_CON_CFM.
1409 	 */
1410 
1411 	switch (con->state) {
1412 	case NG_HCI_CON_W4_LP_CON_RSP:
1413 		break;
1414 
1415 	case NG_HCI_CON_W4_CONN_COMPLETE:
1416 		ng_hci_lp_con_cfm(con, 0xee);
1417 		break;
1418 
1419 	default:
1420 		panic(
1421 "%s: %s - Invalid connection state=%d\n",
1422 			__func__, NG_NODE_NAME(node), con->state);
1423 		break;
1424 	}
1425 
1426 	ng_hci_free_con(con);
1427 } /* ng_hci_process_con_timeout */
1428