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