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