1 /* 2 * ng_hci_main.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_main.c,v 1.2 2003/03/18 00:09:36 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/ng_parse.h> 45 #include <netgraph/bluetooth/include/ng_bluetooth.h> 46 #include <netgraph/bluetooth/include/ng_hci.h> 47 #include <netgraph/bluetooth/hci/ng_hci_var.h> 48 #include <netgraph/bluetooth/hci/ng_hci_prse.h> 49 #include <netgraph/bluetooth/hci/ng_hci_cmds.h> 50 #include <netgraph/bluetooth/hci/ng_hci_evnt.h> 51 #include <netgraph/bluetooth/hci/ng_hci_ulpi.h> 52 #include <netgraph/bluetooth/hci/ng_hci_misc.h> 53 54 /****************************************************************************** 55 ****************************************************************************** 56 ** This node implements Bluetooth Host Controller Interface (HCI) 57 ****************************************************************************** 58 ******************************************************************************/ 59 60 /* MALLOC define */ 61 #ifdef NG_SEPARATE_MALLOC 62 MALLOC_DEFINE(M_NETGRAPH_HCI, "netgraph_hci", "Netgraph Bluetooth HCI node"); 63 #else 64 #define M_NETGRAPH_HCI M_NETGRAPH 65 #endif /* NG_SEPARATE_MALLOC */ 66 67 /* Netgraph node methods */ 68 static ng_constructor_t ng_hci_constructor; 69 static ng_shutdown_t ng_hci_shutdown; 70 static ng_newhook_t ng_hci_newhook; 71 static ng_connect_t ng_hci_connect; 72 static ng_disconnect_t ng_hci_disconnect; 73 static ng_rcvmsg_t ng_hci_default_rcvmsg; 74 static ng_rcvmsg_t ng_hci_upper_rcvmsg; 75 static ng_rcvdata_t ng_hci_drv_rcvdata; 76 static ng_rcvdata_t ng_hci_acl_rcvdata; 77 static ng_rcvdata_t ng_hci_sco_rcvdata; 78 static ng_rcvdata_t ng_hci_raw_rcvdata; 79 80 /* Netgraph node type descriptor */ 81 static struct ng_type typestruct = { 82 .version = NG_ABI_VERSION, 83 .name = NG_HCI_NODE_TYPE, 84 .constructor = ng_hci_constructor, 85 .rcvmsg = ng_hci_default_rcvmsg, 86 .shutdown = ng_hci_shutdown, 87 .newhook = ng_hci_newhook, 88 .connect = ng_hci_connect, 89 .rcvdata = ng_hci_drv_rcvdata, 90 .disconnect = ng_hci_disconnect, 91 .cmdlist = ng_hci_cmdlist, 92 }; 93 NETGRAPH_INIT(hci, &typestruct); 94 MODULE_VERSION(ng_hci, NG_BLUETOOTH_VERSION); 95 MODULE_DEPEND(ng_hci, ng_bluetooth, NG_BLUETOOTH_VERSION, 96 NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); 97 static int ng_hci_linktype_to_addrtype(int linktype); 98 99 static int ng_hci_linktype_to_addrtype(int linktype) 100 { 101 switch(linktype){ 102 case NG_HCI_LINK_LE_PUBLIC: 103 return BDADDR_LE_PUBLIC; 104 case NG_HCI_LINK_LE_RANDOM: 105 return BDADDR_LE_RANDOM; 106 case NG_HCI_LINK_ACL: 107 /*FALLTHROUGH*/ 108 default: 109 return BDADDR_BREDR; 110 } 111 return BDADDR_BREDR; 112 } 113 /***************************************************************************** 114 ***************************************************************************** 115 ** Netgraph methods implementation 116 ***************************************************************************** 117 *****************************************************************************/ 118 119 /* 120 * Create new instance of HCI node (new unit) 121 */ 122 123 static int 124 ng_hci_constructor(node_p node) 125 { 126 ng_hci_unit_p unit = NULL; 127 128 unit = malloc(sizeof(*unit), M_NETGRAPH_HCI, M_WAITOK | M_ZERO); 129 130 unit->node = node; 131 unit->debug = NG_HCI_WARN_LEVEL; 132 133 unit->link_policy_mask = 0xffff; /* Enable all supported modes */ 134 unit->packet_mask = 0xffff; /* Enable all packet types */ 135 unit->role_switch = 1; /* Enable role switch (if device supports it) */ 136 137 /* 138 * Set default buffer info 139 * 140 * One HCI command 141 * One ACL packet with max. size of 17 bytes (1 DM1 packet) 142 * One SCO packet with max. size of 10 bytes (1 HV1 packet) 143 */ 144 145 NG_HCI_BUFF_CMD_SET(unit->buffer, 1); 146 NG_HCI_BUFF_ACL_SET(unit->buffer, 1, 17, 1); 147 NG_HCI_BUFF_SCO_SET(unit->buffer, 1, 10, 1); 148 149 /* Init command queue & command timeout handler */ 150 ng_callout_init(&unit->cmd_timo); 151 NG_BT_MBUFQ_INIT(&unit->cmdq, NG_HCI_CMD_QUEUE_LEN); 152 153 /* Init lists */ 154 LIST_INIT(&unit->con_list); 155 LIST_INIT(&unit->neighbors); 156 157 /* 158 * This node has to be a WRITER because both data and messages 159 * can change node state. 160 */ 161 162 NG_NODE_FORCE_WRITER(node); 163 NG_NODE_SET_PRIVATE(node, unit); 164 165 return (0); 166 } /* ng_hci_constructor */ 167 168 /* 169 * Destroy the node 170 */ 171 172 static int 173 ng_hci_shutdown(node_p node) 174 { 175 ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 176 177 NG_NODE_SET_PRIVATE(node, NULL); 178 NG_NODE_UNREF(node); 179 180 unit->node = NULL; 181 ng_hci_unit_clean(unit, 0x16 /* Connection terminated by local host */); 182 183 NG_BT_MBUFQ_DESTROY(&unit->cmdq); 184 185 bzero(unit, sizeof(*unit)); 186 free(unit, M_NETGRAPH_HCI); 187 188 return (0); 189 } /* ng_hci_shutdown */ 190 191 /* 192 * Give our OK for a hook to be added. Unit driver is connected to the driver 193 * (NG_HCI_HOOK_DRV) hook. Upper layer protocols are connected to appropriate 194 * (NG_HCI_HOOK_ACL or NG_HCI_HOOK_SCO) hooks. 195 */ 196 197 static int 198 ng_hci_newhook(node_p node, hook_p hook, char const *name) 199 { 200 ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 201 hook_p *h = NULL; 202 203 if (strcmp(name, NG_HCI_HOOK_DRV) == 0) 204 h = &unit->drv; 205 else if (strcmp(name, NG_HCI_HOOK_ACL) == 0) 206 h = &unit->acl; 207 else if (strcmp(name, NG_HCI_HOOK_SCO) == 0) 208 h = &unit->sco; 209 else if (strcmp(name, NG_HCI_HOOK_RAW) == 0) 210 h = &unit->raw; 211 else 212 return (EINVAL); 213 214 if (*h != NULL) 215 return (EISCONN); 216 217 *h = hook; 218 219 return (0); 220 } /* ng_hci_newhook */ 221 222 /* 223 * Give our final OK to connect hook 224 */ 225 226 static int 227 ng_hci_connect(hook_p hook) 228 { 229 ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 230 231 if (hook != unit->drv) { 232 if (hook == unit->acl) { 233 NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg); 234 NG_HOOK_SET_RCVDATA(hook, ng_hci_acl_rcvdata); 235 } else if (hook == unit->sco) { 236 NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg); 237 NG_HOOK_SET_RCVDATA(hook, ng_hci_sco_rcvdata); 238 } else 239 NG_HOOK_SET_RCVDATA(hook, ng_hci_raw_rcvdata); 240 241 /* Send delayed notification to the upper layers */ 242 if (hook != unit->raw) 243 ng_send_fn(unit->node, hook, ng_hci_node_is_up, NULL,0); 244 } else 245 unit->state |= NG_HCI_UNIT_CONNECTED; 246 247 return (0); 248 } /* ng_hci_connect */ 249 250 /* 251 * Disconnect the hook 252 */ 253 254 static int 255 ng_hci_disconnect(hook_p hook) 256 { 257 ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 258 259 if (hook == unit->acl) 260 unit->acl = NULL; 261 else if (hook == unit->sco) 262 unit->sco = NULL; 263 else if (hook == unit->raw) 264 unit->raw = NULL; 265 else if (hook == unit->drv) { 266 unit->drv = NULL; 267 268 /* Connection terminated by local host */ 269 ng_hci_unit_clean(unit, 0x16); 270 unit->state &= ~(NG_HCI_UNIT_CONNECTED|NG_HCI_UNIT_INITED); 271 } else 272 return (EINVAL); 273 274 /* Shutdown when all hooks are disconnected */ 275 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && 276 (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 277 ng_rmnode_self(NG_HOOK_NODE(hook)); 278 279 return (0); 280 } /* ng_hci_disconnect */ 281 282 /* 283 * Default control message processing routine. Control message could be: 284 * 285 * 1) GENERIC Netgraph messages 286 * 287 * 2) Control message directed to the node itself. 288 */ 289 290 static int 291 ng_hci_default_rcvmsg(node_p node, item_p item, hook_p lasthook) 292 { 293 ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 294 struct ng_mesg *msg = NULL, *rsp = NULL; 295 int error = 0; 296 297 NGI_GET_MSG(item, msg); 298 299 switch (msg->header.typecookie) { 300 case NGM_GENERIC_COOKIE: 301 switch (msg->header.cmd) { 302 case NGM_TEXT_STATUS: { 303 int cmd_avail, 304 acl_total, acl_avail, acl_size, 305 sco_total, sco_avail, sco_size; 306 307 NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); 308 if (rsp == NULL) { 309 error = ENOMEM; 310 break; 311 } 312 313 NG_HCI_BUFF_CMD_GET(unit->buffer, cmd_avail); 314 315 NG_HCI_BUFF_ACL_AVAIL(unit->buffer, acl_avail); 316 NG_HCI_BUFF_ACL_TOTAL(unit->buffer, acl_total); 317 NG_HCI_BUFF_ACL_SIZE(unit->buffer, acl_size); 318 319 NG_HCI_BUFF_SCO_AVAIL(unit->buffer, sco_avail); 320 NG_HCI_BUFF_SCO_TOTAL(unit->buffer, sco_total); 321 NG_HCI_BUFF_SCO_SIZE(unit->buffer, sco_size); 322 323 snprintf(rsp->data, NG_TEXTRESPONSE, 324 "bdaddr %x:%x:%x:%x:%x:%x\n" \ 325 "Hooks %s %s %s %s\n" \ 326 "State %#x\n" \ 327 "Queue cmd:%d\n" \ 328 "Buffer cmd:%d,acl:%d,%d,%d,sco:%d,%d,%d", 329 unit->bdaddr.b[5], unit->bdaddr.b[4], 330 unit->bdaddr.b[3], unit->bdaddr.b[2], 331 unit->bdaddr.b[1], unit->bdaddr.b[0], 332 (unit->drv != NULL)? NG_HCI_HOOK_DRV : "", 333 (unit->acl != NULL)? NG_HCI_HOOK_ACL : "", 334 (unit->sco != NULL)? NG_HCI_HOOK_SCO : "", 335 (unit->raw != NULL)? NG_HCI_HOOK_RAW : "", 336 unit->state, 337 NG_BT_MBUFQ_LEN(&unit->cmdq), 338 cmd_avail, 339 acl_avail, acl_total, acl_size, 340 sco_avail, sco_total, sco_size); 341 } break; 342 343 default: 344 error = EINVAL; 345 break; 346 } 347 break; 348 349 case NGM_HCI_COOKIE: 350 switch (msg->header.cmd) { 351 /* Get current node state */ 352 case NGM_HCI_NODE_GET_STATE: 353 NG_MKRESPONSE(rsp, msg, sizeof(unit->state), M_NOWAIT); 354 if (rsp == NULL) { 355 error = ENOMEM; 356 break; 357 } 358 359 *((ng_hci_node_state_ep *)(rsp->data)) = unit->state; 360 break; 361 362 /* Turn INITED bit - node initialized */ 363 case NGM_HCI_NODE_INIT: 364 if (bcmp(&unit->bdaddr, NG_HCI_BDADDR_ANY, 365 sizeof(bdaddr_t)) == 0) { 366 error = ENXIO; 367 break; 368 } 369 370 unit->state |= NG_HCI_UNIT_INITED; 371 372 ng_hci_node_is_up(unit->node, unit->acl, NULL, 0); 373 ng_hci_node_is_up(unit->node, unit->sco, NULL, 0); 374 break; 375 376 /* Get node debug level */ 377 case NGM_HCI_NODE_GET_DEBUG: 378 NG_MKRESPONSE(rsp, msg, sizeof(unit->debug), M_NOWAIT); 379 if (rsp == NULL) { 380 error = ENOMEM; 381 break; 382 } 383 384 *((ng_hci_node_debug_ep *)(rsp->data)) = unit->debug; 385 break; 386 387 /* Set node debug level */ 388 case NGM_HCI_NODE_SET_DEBUG: 389 if (msg->header.arglen != sizeof(ng_hci_node_debug_ep)){ 390 error = EMSGSIZE; 391 break; 392 } 393 394 unit->debug = *((ng_hci_node_debug_ep *)(msg->data)); 395 break; 396 397 /* Get buffer info */ 398 case NGM_HCI_NODE_GET_BUFFER: { 399 ng_hci_node_buffer_ep *ep = NULL; 400 401 NG_MKRESPONSE(rsp, msg, sizeof(ng_hci_node_buffer_ep), 402 M_NOWAIT); 403 if (rsp == NULL) { 404 error = ENOMEM; 405 break; 406 } 407 408 ep = (ng_hci_node_buffer_ep *)(rsp->data); 409 410 NG_HCI_BUFF_CMD_GET(unit->buffer, ep->cmd_free); 411 NG_HCI_BUFF_ACL_AVAIL(unit->buffer, ep->acl_free); 412 NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->acl_pkts); 413 NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->acl_size); 414 NG_HCI_BUFF_SCO_AVAIL(unit->buffer, ep->sco_free); 415 NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->sco_pkts); 416 NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->sco_size); 417 } break; 418 419 /* Get BDADDR */ 420 case NGM_HCI_NODE_GET_BDADDR: 421 NG_MKRESPONSE(rsp, msg, sizeof(bdaddr_t), M_NOWAIT); 422 if (rsp == NULL) { 423 error = ENOMEM; 424 break; 425 } 426 427 bcopy(&unit->bdaddr, rsp->data, sizeof(bdaddr_t)); 428 break; 429 430 /* Get features */ 431 case NGM_HCI_NODE_GET_FEATURES: 432 NG_MKRESPONSE(rsp,msg,sizeof(unit->features),M_NOWAIT); 433 if (rsp == NULL) { 434 error = ENOMEM; 435 break; 436 } 437 438 bcopy(&unit->features,rsp->data,sizeof(unit->features)); 439 break; 440 441 /* Get stat */ 442 case NGM_HCI_NODE_GET_STAT: 443 NG_MKRESPONSE(rsp, msg, sizeof(unit->stat), M_NOWAIT); 444 if (rsp == NULL) { 445 error = ENOMEM; 446 break; 447 } 448 449 bcopy(&unit->stat, rsp->data, sizeof(unit->stat)); 450 break; 451 452 /* Reset stat */ 453 case NGM_HCI_NODE_RESET_STAT: 454 NG_HCI_STAT_RESET(unit->stat); 455 break; 456 457 /* Clean up neighbors list */ 458 case NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE: 459 ng_hci_flush_neighbor_cache(unit); 460 break; 461 462 /* Get neighbor cache entries */ 463 case NGM_HCI_NODE_GET_NEIGHBOR_CACHE: { 464 ng_hci_neighbor_p n = NULL; 465 ng_hci_node_get_neighbor_cache_ep *e1 = NULL; 466 ng_hci_node_neighbor_cache_entry_ep *e2 = NULL; 467 int s = 0; 468 469 /* Look for the fresh entries in the cache */ 470 for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) { 471 ng_hci_neighbor_p nn = LIST_NEXT(n, next); 472 473 if (ng_hci_neighbor_stale(n)) 474 ng_hci_free_neighbor(n); 475 else 476 s ++; 477 478 n = nn; 479 } 480 if (s > NG_HCI_MAX_NEIGHBOR_NUM) 481 s = NG_HCI_MAX_NEIGHBOR_NUM; 482 483 /* Prepare response */ 484 NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2), 485 M_NOWAIT); 486 if (rsp == NULL) { 487 error = ENOMEM; 488 break; 489 } 490 491 e1 = (ng_hci_node_get_neighbor_cache_ep *)(rsp->data); 492 e2 = (ng_hci_node_neighbor_cache_entry_ep *)(e1 + 1); 493 494 e1->num_entries = s; 495 496 LIST_FOREACH(n, &unit->neighbors, next) { 497 e2->page_scan_rep_mode = n->page_scan_rep_mode; 498 e2->page_scan_mode = n->page_scan_mode; 499 e2->clock_offset = n->clock_offset; 500 e2->addrtype = 501 ng_hci_linktype_to_addrtype(n->addrtype); 502 e2->extinq_size = n->extinq_size; 503 bcopy(&n->bdaddr, &e2->bdaddr, 504 sizeof(e2->bdaddr)); 505 bcopy(&n->features, &e2->features, 506 sizeof(e2->features)); 507 bcopy(&n->extinq_data, &e2->extinq_data, 508 n->extinq_size); 509 e2 ++; 510 if (--s <= 0) 511 break; 512 } 513 } break; 514 515 /* Get connection list */ 516 case NGM_HCI_NODE_GET_CON_LIST: { 517 ng_hci_unit_con_p c = NULL; 518 ng_hci_node_con_list_ep *e1 = NULL; 519 ng_hci_node_con_ep *e2 = NULL; 520 int s = 0; 521 522 /* Count number of connections in the list */ 523 LIST_FOREACH(c, &unit->con_list, next) 524 s ++; 525 if (s > NG_HCI_MAX_CON_NUM) 526 s = NG_HCI_MAX_CON_NUM; 527 528 /* Prepare response */ 529 NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2), 530 M_NOWAIT); 531 if (rsp == NULL) { 532 error = ENOMEM; 533 break; 534 } 535 536 e1 = (ng_hci_node_con_list_ep *)(rsp->data); 537 e2 = (ng_hci_node_con_ep *)(e1 + 1); 538 539 e1->num_connections = s; 540 541 LIST_FOREACH(c, &unit->con_list, next) { 542 e2->link_type = c->link_type; 543 e2->encryption_mode= c->encryption_mode; 544 e2->mode = c->mode; 545 e2->role = c->role; 546 547 e2->state = c->state; 548 549 e2->pending = c->pending; 550 e2->queue_len = NG_BT_ITEMQ_LEN(&c->conq); 551 552 e2->con_handle = c->con_handle; 553 bcopy(&c->bdaddr, &e2->bdaddr, 554 sizeof(e2->bdaddr)); 555 556 e2 ++; 557 if (--s <= 0) 558 break; 559 } 560 } break; 561 562 /* Get link policy settings mask */ 563 case NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK: 564 NG_MKRESPONSE(rsp, msg, sizeof(unit->link_policy_mask), 565 M_NOWAIT); 566 if (rsp == NULL) { 567 error = ENOMEM; 568 break; 569 } 570 571 *((ng_hci_node_link_policy_mask_ep *)(rsp->data)) = 572 unit->link_policy_mask; 573 break; 574 575 /* Set link policy settings mask */ 576 case NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK: 577 if (msg->header.arglen != 578 sizeof(ng_hci_node_link_policy_mask_ep)) { 579 error = EMSGSIZE; 580 break; 581 } 582 583 unit->link_policy_mask = 584 *((ng_hci_node_link_policy_mask_ep *) 585 (msg->data)); 586 break; 587 588 /* Get packet mask */ 589 case NGM_HCI_NODE_GET_PACKET_MASK: 590 NG_MKRESPONSE(rsp, msg, sizeof(unit->packet_mask), 591 M_NOWAIT); 592 if (rsp == NULL) { 593 error = ENOMEM; 594 break; 595 } 596 597 *((ng_hci_node_packet_mask_ep *)(rsp->data)) = 598 unit->packet_mask; 599 break; 600 601 /* Set packet mask */ 602 case NGM_HCI_NODE_SET_PACKET_MASK: 603 if (msg->header.arglen != 604 sizeof(ng_hci_node_packet_mask_ep)) { 605 error = EMSGSIZE; 606 break; 607 } 608 609 unit->packet_mask = 610 *((ng_hci_node_packet_mask_ep *)(msg->data)); 611 break; 612 613 /* Get role switch */ 614 case NGM_HCI_NODE_GET_ROLE_SWITCH: 615 NG_MKRESPONSE(rsp, msg, sizeof(unit->role_switch), 616 M_NOWAIT); 617 if (rsp == NULL) { 618 error = ENOMEM; 619 break; 620 } 621 622 *((ng_hci_node_role_switch_ep *)(rsp->data)) = 623 unit->role_switch; 624 break; 625 626 /* Set role switch */ 627 case NGM_HCI_NODE_SET_ROLE_SWITCH: 628 if (msg->header.arglen != 629 sizeof(ng_hci_node_role_switch_ep)) { 630 error = EMSGSIZE; 631 break; 632 } 633 634 unit->role_switch = 635 *((ng_hci_node_role_switch_ep *)(msg->data)); 636 break; 637 638 default: 639 error = EINVAL; 640 break; 641 } 642 break; 643 644 default: 645 error = EINVAL; 646 break; 647 } 648 649 /* NG_RESPOND_MSG should take care of "item" and "rsp" */ 650 NG_RESPOND_MSG(error, node, item, rsp); 651 NG_FREE_MSG(msg); 652 653 return (error); 654 } /* ng_hci_default_rcvmsg */ 655 656 /* 657 * Process control message from upstream hooks (ACL and SCO). 658 * Handle LP_xxx messages here, give everything else to default routine. 659 */ 660 661 static int 662 ng_hci_upper_rcvmsg(node_p node, item_p item, hook_p lasthook) 663 { 664 ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 665 int error = 0; 666 667 switch (NGI_MSG(item)->header.typecookie) { 668 case NGM_HCI_COOKIE: 669 switch (NGI_MSG(item)->header.cmd) { 670 case NGM_HCI_LP_CON_REQ: 671 error = ng_hci_lp_con_req(unit, item, lasthook); 672 break; 673 674 case NGM_HCI_LP_DISCON_REQ: /* XXX not defined by specs */ 675 error = ng_hci_lp_discon_req(unit, item, lasthook); 676 break; 677 678 case NGM_HCI_LP_CON_RSP: 679 error = ng_hci_lp_con_rsp(unit, item, lasthook); 680 break; 681 682 case NGM_HCI_LP_QOS_REQ: 683 error = ng_hci_lp_qos_req(unit, item, lasthook); 684 break; 685 686 default: 687 error = ng_hci_default_rcvmsg(node, item, lasthook); 688 break; 689 } 690 break; 691 692 default: 693 error = ng_hci_default_rcvmsg(node, item, lasthook); 694 break; 695 } 696 697 return (error); 698 } /* ng_hci_upper_rcvmsg */ 699 700 /* 701 * Process data packet from the driver hook. 702 * We expect HCI events, ACL or SCO data packets. 703 */ 704 705 static int 706 ng_hci_drv_rcvdata(hook_p hook, item_p item) 707 { 708 ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 709 struct mbuf *m = NULL; 710 int error = 0; 711 712 /* Process packet */ 713 m = NGI_M(item); /* item still has mbuf, just peeking */ 714 m->m_flags |= M_PROTO1; /* mark as incoming packet */ 715 716 NG_HCI_STAT_BYTES_RECV(unit->stat, m->m_pkthdr.len); 717 718 /* Give copy packet to RAW hook */ 719 ng_hci_mtap(unit, m); 720 721 /* 722 * XXX XXX XXX 723 * Lower layer drivers MUST NOT send mbuf chain with empty mbuf at 724 * the beginning of the chain. HCI layer WILL NOT call m_pullup() here. 725 */ 726 727 switch (*mtod(m, u_int8_t *)) { 728 case NG_HCI_ACL_DATA_PKT: 729 NG_HCI_STAT_ACL_RECV(unit->stat); 730 731 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY || 732 unit->acl == NULL || NG_HOOK_NOT_VALID(unit->acl)) { 733 NG_HCI_WARN( 734 "%s: %s - could not forward HCI ACL data packet, state=%#x, hook=%p\n", 735 __func__, NG_NODE_NAME(unit->node), 736 unit->state, unit->acl); 737 738 NG_FREE_ITEM(item); 739 } else 740 NG_FWD_ITEM_HOOK(error, item, unit->acl); 741 break; 742 743 case NG_HCI_SCO_DATA_PKT: 744 NG_HCI_STAT_SCO_RECV(unit->stat); 745 746 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY || 747 unit->sco == NULL || NG_HOOK_NOT_VALID(unit->sco)) { 748 NG_HCI_INFO( 749 "%s: %s - could not forward HCI SCO data packet, state=%#x, hook=%p\n", 750 __func__, NG_NODE_NAME(unit->node), 751 unit->state, unit->sco); 752 753 NG_FREE_ITEM(item); 754 } else 755 NG_FWD_ITEM_HOOK(error, item, unit->sco); 756 break; 757 758 case NG_HCI_EVENT_PKT: 759 NG_HCI_STAT_EVNT_RECV(unit->stat); 760 761 /* Detach mbuf, discard item and process event */ 762 NGI_GET_M(item, m); 763 NG_FREE_ITEM(item); 764 765 error = ng_hci_process_event(unit, m); 766 break; 767 768 default: 769 NG_HCI_ALERT( 770 "%s: %s - got unknown HCI packet type=%#x\n", 771 __func__, NG_NODE_NAME(unit->node), 772 *mtod(m, u_int8_t *)); 773 774 NG_FREE_ITEM(item); 775 776 error = EINVAL; 777 break; 778 } 779 780 return (error); 781 } /* ng_hci_drv_rcvdata */ 782 783 /* 784 * Process data packet from ACL upstream hook. 785 * We expect valid HCI ACL data packets. 786 */ 787 788 static int 789 ng_hci_acl_rcvdata(hook_p hook, item_p item) 790 { 791 ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 792 struct mbuf *m = NULL; 793 ng_hci_unit_con_p con = NULL; 794 u_int16_t con_handle; 795 int size, error = 0; 796 797 NG_HCI_BUFF_ACL_SIZE(unit->buffer, size); 798 /* Check packet */ 799 NGI_GET_M(item, m); 800 801 if (*mtod(m, u_int8_t *) != NG_HCI_ACL_DATA_PKT) { 802 NG_HCI_ALERT( 803 "%s: %s - invalid HCI data packet type=%#x\n", 804 __func__, NG_NODE_NAME(unit->node), 805 *mtod(m, u_int8_t *)); 806 807 error = EINVAL; 808 goto drop; 809 } 810 if (m->m_pkthdr.len < sizeof(ng_hci_acldata_pkt_t) || 811 m->m_pkthdr.len > sizeof(ng_hci_acldata_pkt_t) + size) { 812 NG_HCI_ALERT( 813 "%s: %s - invalid HCI ACL data packet, len=%d, mtu=%d\n", 814 __func__, NG_NODE_NAME(unit->node), 815 m->m_pkthdr.len, size); 816 817 error = EMSGSIZE; 818 goto drop; 819 } 820 821 NG_HCI_M_PULLUP(m, sizeof(ng_hci_acldata_pkt_t)); 822 if (m == NULL) { 823 error = ENOBUFS; 824 goto drop; 825 } 826 827 con_handle = NG_HCI_CON_HANDLE(le16toh( 828 mtod(m, ng_hci_acldata_pkt_t *)->con_handle)); 829 size = le16toh(mtod(m, ng_hci_acldata_pkt_t *)->length); 830 831 if (m->m_pkthdr.len != sizeof(ng_hci_acldata_pkt_t) + size) { 832 NG_HCI_ALERT( 833 "%s: %s - invalid HCI ACL data packet size, len=%d, length=%d\n", 834 __func__, NG_NODE_NAME(unit->node), 835 m->m_pkthdr.len, size); 836 837 error = EMSGSIZE; 838 goto drop; 839 } 840 841 /* Queue packet */ 842 con = ng_hci_con_by_handle(unit, con_handle); 843 if (con == NULL) { 844 NG_HCI_ERR( 845 "%s: %s - unexpected HCI ACL data packet. Connection does not exists, " \ 846 "con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), con_handle); 847 848 error = ENOENT; 849 goto drop; 850 } 851 852 if (con->link_type == NG_HCI_LINK_SCO) { 853 NG_HCI_ERR( 854 "%s: %s - unexpected HCI ACL data packet. Not ACL link, con_handle=%d, " \ 855 "link_type=%d\n", __func__, NG_NODE_NAME(unit->node), 856 con_handle, con->link_type); 857 858 error = EINVAL; 859 goto drop; 860 } 861 862 if (con->state != NG_HCI_CON_OPEN) { 863 NG_HCI_ERR( 864 "%s: %s - unexpected HCI ACL data packet. Invalid connection state=%d, " \ 865 "con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), 866 con->state, con_handle); 867 868 error = EHOSTDOWN; 869 goto drop; 870 } 871 872 if (NG_BT_ITEMQ_FULL(&con->conq)) { 873 NG_HCI_ALERT( 874 "%s: %s - dropping HCI ACL data packet, con_handle=%d, len=%d, queue_len=%d\n", 875 __func__, NG_NODE_NAME(unit->node), con_handle, 876 m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq)); 877 878 NG_BT_ITEMQ_DROP(&con->conq); 879 880 error = ENOBUFS; 881 goto drop; 882 } 883 884 /* Queue item and schedule data transfer */ 885 NGI_M(item) = m; 886 NG_BT_ITEMQ_ENQUEUE(&con->conq, item); 887 item = NULL; 888 m = NULL; 889 890 ng_hci_send_data(unit); 891 drop: 892 if (item != NULL) 893 NG_FREE_ITEM(item); 894 895 NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */ 896 897 return (error); 898 } /* ng_hci_acl_rcvdata */ 899 900 /* 901 * Process data packet from SCO upstream hook. 902 * We expect valid HCI SCO data packets 903 */ 904 905 static int 906 ng_hci_sco_rcvdata(hook_p hook, item_p item) 907 { 908 ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 909 struct mbuf *m = NULL; 910 ng_hci_unit_con_p con = NULL; 911 u_int16_t con_handle; 912 int size, error = 0; 913 914 NG_HCI_BUFF_SCO_SIZE(unit->buffer, size); 915 916 /* Check packet */ 917 NGI_GET_M(item, m); 918 919 if (*mtod(m, u_int8_t *) != NG_HCI_SCO_DATA_PKT) { 920 NG_HCI_ALERT( 921 "%s: %s - invalid HCI data packet type=%#x\n", 922 __func__, NG_NODE_NAME(unit->node), 923 *mtod(m, u_int8_t *)); 924 925 error = EINVAL; 926 goto drop; 927 } 928 929 if (m->m_pkthdr.len < sizeof(ng_hci_scodata_pkt_t) || 930 m->m_pkthdr.len > sizeof(ng_hci_scodata_pkt_t) + size) { 931 NG_HCI_ALERT( 932 "%s: %s - invalid HCI SCO data packet, len=%d, mtu=%d\n", 933 __func__, NG_NODE_NAME(unit->node), 934 m->m_pkthdr.len, size); 935 936 error = EMSGSIZE; 937 goto drop; 938 } 939 940 NG_HCI_M_PULLUP(m, sizeof(ng_hci_scodata_pkt_t)); 941 if (m == NULL) { 942 error = ENOBUFS; 943 goto drop; 944 } 945 946 con_handle = NG_HCI_CON_HANDLE(le16toh( 947 mtod(m, ng_hci_scodata_pkt_t *)->con_handle)); 948 size = mtod(m, ng_hci_scodata_pkt_t *)->length; 949 950 if (m->m_pkthdr.len != sizeof(ng_hci_scodata_pkt_t) + size) { 951 NG_HCI_ALERT( 952 "%s: %s - invalid HCI SCO data packet size, len=%d, length=%d\n", 953 __func__, NG_NODE_NAME(unit->node), 954 m->m_pkthdr.len, size); 955 956 error = EMSGSIZE; 957 goto drop; 958 } 959 960 /* Queue packet */ 961 con = ng_hci_con_by_handle(unit, con_handle); 962 if (con == NULL) { 963 NG_HCI_ERR( 964 "%s: %s - unexpected HCI SCO data packet. Connection does not exists, " \ 965 "con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), con_handle); 966 967 error = ENOENT; 968 goto drop; 969 } 970 971 if (con->link_type != NG_HCI_LINK_SCO) { 972 NG_HCI_ERR( 973 "%s: %s - unexpected HCI SCO data packet. Not SCO link, con_handle=%d, " \ 974 "link_type=%d\n", __func__, NG_NODE_NAME(unit->node), 975 con_handle, con->link_type); 976 977 error = EINVAL; 978 goto drop; 979 } 980 981 if (con->state != NG_HCI_CON_OPEN) { 982 NG_HCI_ERR( 983 "%s: %s - unexpected HCI SCO data packet. Invalid connection state=%d, " \ 984 "con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), 985 con->state, con_handle); 986 987 error = EHOSTDOWN; 988 goto drop; 989 } 990 991 if (NG_BT_ITEMQ_FULL(&con->conq)) { 992 NG_HCI_ALERT( 993 "%s: %s - dropping HCI SCO data packet, con_handle=%d, len=%d, queue_len=%d\n", 994 __func__, NG_NODE_NAME(unit->node), con_handle, 995 m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq)); 996 997 NG_BT_ITEMQ_DROP(&con->conq); 998 999 error = ENOBUFS; 1000 goto drop; 1001 } 1002 1003 /* Queue item and schedule data transfer */ 1004 NGI_M(item) = m; 1005 NG_BT_ITEMQ_ENQUEUE(&con->conq, item); 1006 item = NULL; 1007 m = NULL; 1008 1009 ng_hci_send_data(unit); 1010 drop: 1011 if (item != NULL) 1012 NG_FREE_ITEM(item); 1013 1014 NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */ 1015 1016 return (error); 1017 } /* ng_hci_sco_rcvdata */ 1018 1019 /* 1020 * Process data packet from uptream RAW hook. 1021 * We expect valid HCI command packets. 1022 */ 1023 1024 static int 1025 ng_hci_raw_rcvdata(hook_p hook, item_p item) 1026 { 1027 ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1028 struct mbuf *m = NULL; 1029 int error = 0; 1030 1031 NGI_GET_M(item, m); 1032 NG_FREE_ITEM(item); 1033 1034 /* Check packet */ 1035 if (*mtod(m, u_int8_t *) != NG_HCI_CMD_PKT) { 1036 NG_HCI_ALERT( 1037 "%s: %s - invalid HCI command packet type=%#x\n", 1038 __func__, NG_NODE_NAME(unit->node), 1039 *mtod(m, u_int8_t *)); 1040 1041 error = EINVAL; 1042 goto drop; 1043 } 1044 1045 if (m->m_pkthdr.len < sizeof(ng_hci_cmd_pkt_t)) { 1046 NG_HCI_ALERT( 1047 "%s: %s - invalid HCI command packet len=%d\n", 1048 __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len); 1049 1050 error = EMSGSIZE; 1051 goto drop; 1052 } 1053 1054 NG_HCI_M_PULLUP(m, sizeof(ng_hci_cmd_pkt_t)); 1055 if (m == NULL) { 1056 error = ENOBUFS; 1057 goto drop; 1058 } 1059 1060 if (m->m_pkthdr.len != 1061 mtod(m, ng_hci_cmd_pkt_t *)->length + sizeof(ng_hci_cmd_pkt_t)) { 1062 NG_HCI_ALERT( 1063 "%s: %s - invalid HCI command packet size, len=%d, length=%d\n", 1064 __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len, 1065 mtod(m, ng_hci_cmd_pkt_t *)->length); 1066 1067 error = EMSGSIZE; 1068 goto drop; 1069 } 1070 1071 if (mtod(m, ng_hci_cmd_pkt_t *)->opcode == 0) { 1072 NG_HCI_ALERT( 1073 "%s: %s - invalid HCI command opcode\n", 1074 __func__, NG_NODE_NAME(unit->node)); 1075 1076 error = EINVAL; 1077 goto drop; 1078 } 1079 1080 if (NG_BT_MBUFQ_FULL(&unit->cmdq)) { 1081 NG_HCI_ALERT( 1082 "%s: %s - dropping HCI command packet, len=%d, queue_len=%d\n", 1083 __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len, 1084 NG_BT_MBUFQ_LEN(&unit->cmdq)); 1085 1086 NG_BT_MBUFQ_DROP(&unit->cmdq); 1087 1088 error = ENOBUFS; 1089 goto drop; 1090 } 1091 1092 /* Queue and send command */ 1093 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 1094 m = NULL; 1095 1096 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 1097 error = ng_hci_send_command(unit); 1098 drop: 1099 NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */ 1100 1101 return (error); 1102 } /* ng_hci_raw_rcvdata */ 1103