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