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