1 /* 2 * ng_hci_ulpi.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_ulpi.c,v 1.7 2003/09/08 18:57:51 max Exp $ 31 * $FreeBSD$ 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 <netgraph/ng_message.h> 42 #include <netgraph/netgraph.h> 43 #include <netgraph/bluetooth/include/ng_bluetooth.h> 44 #include <netgraph/bluetooth/include/ng_hci.h> 45 #include <netgraph/bluetooth/hci/ng_hci_var.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 ** Upper Layer Protocol Interface module 54 ****************************************************************************** 55 ******************************************************************************/ 56 57 static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p); 58 static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p); 59 60 /* 61 * Process LP_ConnectReq event from the upper layer protocol 62 */ 63 64 int 65 ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 66 { 67 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 68 NG_HCI_WARN( 69 "%s: %s - unit is not ready, state=%#x\n", 70 __func__, NG_NODE_NAME(unit->node), unit->state); 71 72 NG_FREE_ITEM(item); 73 74 return (ENXIO); 75 } 76 77 if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) { 78 NG_HCI_ALERT( 79 "%s: %s - invalid LP_ConnectReq message size=%d\n", 80 __func__, NG_NODE_NAME(unit->node), 81 NGI_MSG(item)->header.arglen); 82 83 NG_FREE_ITEM(item); 84 85 return (EMSGSIZE); 86 } 87 88 if (((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type == NG_HCI_LINK_ACL) 89 return (ng_hci_lp_acl_con_req(unit, item, hook)); 90 91 if (hook != unit->sco) { 92 NG_HCI_WARN( 93 "%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n", 94 __func__, NG_NODE_NAME(unit->node), hook); 95 96 NG_FREE_ITEM(item); 97 98 return (EINVAL); 99 } 100 101 return (ng_hci_lp_sco_con_req(unit, item, hook)); 102 } /* ng_hci_lp_con_req */ 103 104 /* 105 * Request to create new ACL connection 106 */ 107 108 static int 109 ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 110 { 111 struct acl_con_req { 112 ng_hci_cmd_pkt_t hdr; 113 ng_hci_create_con_cp cp; 114 } __attribute__ ((packed)) *req = NULL; 115 ng_hci_lp_con_req_ep *ep = NULL; 116 ng_hci_unit_con_p con = NULL; 117 ng_hci_neighbor_t *n = NULL; 118 struct mbuf *m = NULL; 119 int error = 0; 120 121 ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); 122 123 /* 124 * Only one ACL connection can exist between each pair of units. 125 * So try to find ACL connection descriptor (in any state) that 126 * has requested remote BD_ADDR. 127 * 128 * Two cases: 129 * 130 * 1) We do not have connection to the remote unit. This is simple. 131 * Just create new connection descriptor and send HCI command to 132 * create new connection. 133 * 134 * 2) We do have connection descriptor. We need to check connection 135 * state: 136 * 137 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of 138 * accepting connection from the remote unit. This is a race 139 * condition. We will ignore this message. 140 * 141 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already 142 * requested connection or we just accepted it. In any case 143 * all we need to do here is set appropriate notification bit 144 * and wait. 145 * 146 * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back 147 * and let upper layer know that we have connection already. 148 */ 149 150 con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL); 151 if (con != NULL) { 152 switch (con->state) { 153 case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */ 154 error = EALREADY; 155 break; 156 157 case NG_HCI_CON_W4_CONN_COMPLETE: 158 if (hook == unit->acl) 159 con->flags |= NG_HCI_CON_NOTIFY_ACL; 160 else 161 con->flags |= NG_HCI_CON_NOTIFY_SCO; 162 break; 163 164 case NG_HCI_CON_OPEN: { 165 struct ng_mesg *msg = NULL; 166 ng_hci_lp_con_cfm_ep *cfm = NULL; 167 168 if (hook != NULL && NG_HOOK_IS_VALID(hook)) { 169 NGI_GET_MSG(item, msg); 170 NG_FREE_MSG(msg); 171 172 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 173 NGM_HCI_LP_CON_CFM, sizeof(*cfm), 174 M_NOWAIT); 175 if (msg != NULL) { 176 cfm = (ng_hci_lp_con_cfm_ep *)msg->data; 177 cfm->status = 0; 178 cfm->link_type = con->link_type; 179 cfm->con_handle = con->con_handle; 180 bcopy(&con->bdaddr, &cfm->bdaddr, 181 sizeof(cfm->bdaddr)); 182 183 /* 184 * This will forward item back to 185 * sender and set item to NULL 186 */ 187 188 _NGI_MSG(item) = msg; 189 NG_FWD_ITEM_HOOK(error, item, hook); 190 } else 191 error = ENOMEM; 192 } else 193 NG_HCI_INFO( 194 "%s: %s - Source hook is not valid, hook=%p\n", 195 __func__, NG_NODE_NAME(unit->node), 196 hook); 197 } break; 198 199 default: 200 panic( 201 "%s: %s - Invalid connection state=%d\n", 202 __func__, NG_NODE_NAME(unit->node), con->state); 203 break; 204 } 205 206 goto out; 207 } 208 209 /* 210 * If we got here then we need to create new ACL connection descriptor 211 * and submit HCI command. First create new connection desriptor, set 212 * bdaddr and notification flags. 213 */ 214 215 con = ng_hci_new_con(unit, NG_HCI_LINK_ACL); 216 if (con == NULL) { 217 error = ENOMEM; 218 goto out; 219 } 220 221 bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); 222 223 /* 224 * Create HCI command 225 */ 226 227 MGETHDR(m, M_NOWAIT, MT_DATA); 228 if (m == NULL) { 229 ng_hci_free_con(con); 230 error = ENOBUFS; 231 goto out; 232 } 233 234 m->m_pkthdr.len = m->m_len = sizeof(*req); 235 req = mtod(m, struct acl_con_req *); 236 req->hdr.type = NG_HCI_CMD_PKT; 237 req->hdr.length = sizeof(req->cp); 238 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 239 NG_HCI_OCF_CREATE_CON)); 240 241 bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr)); 242 243 req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1); 244 if (unit->features[0] & NG_HCI_LMP_3SLOT) 245 req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3); 246 if (unit->features[0] & NG_HCI_LMP_5SLOT) 247 req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5); 248 249 req->cp.pkt_type &= unit->packet_mask; 250 if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1| 251 NG_HCI_PKT_DM3|NG_HCI_PKT_DH3| 252 NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0) 253 req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1); 254 255 req->cp.pkt_type = htole16(req->cp.pkt_type); 256 257 if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch) 258 req->cp.accept_role_switch = 1; 259 else 260 req->cp.accept_role_switch = 0; 261 262 /* 263 * We may speed up connect by specifying valid parameters. 264 * So check the neighbor cache. 265 */ 266 267 n = ng_hci_get_neighbor(unit, &ep->bdaddr); 268 if (n == NULL) { 269 req->cp.page_scan_rep_mode = 0; 270 req->cp.page_scan_mode = 0; 271 req->cp.clock_offset = 0; 272 } else { 273 req->cp.page_scan_rep_mode = n->page_scan_rep_mode; 274 req->cp.page_scan_mode = n->page_scan_mode; 275 req->cp.clock_offset = htole16(n->clock_offset); 276 } 277 278 /* 279 * Adust connection state 280 */ 281 282 if (hook == unit->acl) 283 con->flags |= NG_HCI_CON_NOTIFY_ACL; 284 else 285 con->flags |= NG_HCI_CON_NOTIFY_SCO; 286 287 con->state = NG_HCI_CON_W4_CONN_COMPLETE; 288 ng_hci_con_timeout(con); 289 290 /* 291 * Queue and send HCI command 292 */ 293 294 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 295 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 296 error = ng_hci_send_command(unit); 297 out: 298 if (item != NULL) 299 NG_FREE_ITEM(item); 300 301 return (error); 302 } /* ng_hci_lp_acl_con_req */ 303 304 /* 305 * Request to create new SCO connection 306 */ 307 308 static int 309 ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 310 { 311 struct sco_con_req { 312 ng_hci_cmd_pkt_t hdr; 313 ng_hci_add_sco_con_cp cp; 314 } __attribute__ ((packed)) *req = NULL; 315 ng_hci_lp_con_req_ep *ep = NULL; 316 ng_hci_unit_con_p acl_con = NULL, sco_con = NULL; 317 struct mbuf *m = NULL; 318 int error = 0; 319 320 ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); 321 322 /* 323 * SCO connection without ACL link 324 * 325 * If upper layer requests SCO connection and there is no open ACL 326 * connection to the desired remote unit, we will reject the request. 327 */ 328 329 LIST_FOREACH(acl_con, &unit->con_list, next) 330 if (acl_con->link_type == NG_HCI_LINK_ACL && 331 acl_con->state == NG_HCI_CON_OPEN && 332 bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 333 break; 334 335 if (acl_con == NULL) { 336 NG_HCI_INFO( 337 "%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n", 338 __func__, NG_NODE_NAME(unit->node), 339 ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], 340 ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); 341 342 error = ENOENT; 343 goto out; 344 } 345 346 /* 347 * Multiple SCO connections can exist between the same pair of units. 348 * We assume that multiple SCO connections have to be opened one after 349 * another. 350 * 351 * Try to find SCO connection descriptor that matches the following: 352 * 353 * 1) sco_con->link_type == NG_HCI_LINK_SCO 354 * 355 * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP || 356 * sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE 357 * 358 * 3) sco_con->bdaddr == ep->bdaddr 359 * 360 * Two cases: 361 * 362 * 1) We do not have connection descriptor. This is simple. Just 363 * create new connection and submit Add_SCO_Connection command. 364 * 365 * 2) We do have connection descriptor. We need to check the state. 366 * 367 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting 368 * connection from the remote unit. This is a race condition and 369 * we will ignore the request. 370 * 371 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested 372 * connection or we just accepted it. 373 */ 374 375 LIST_FOREACH(sco_con, &unit->con_list, next) 376 if (sco_con->link_type == NG_HCI_LINK_SCO && 377 (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP || 378 sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) && 379 bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 380 break; 381 382 if (sco_con != NULL) { 383 switch (sco_con->state) { 384 case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */ 385 error = EALREADY; 386 break; 387 388 case NG_HCI_CON_W4_CONN_COMPLETE: 389 sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; 390 break; 391 392 default: 393 panic( 394 "%s: %s - Invalid connection state=%d\n", 395 __func__, NG_NODE_NAME(unit->node), 396 sco_con->state); 397 break; 398 } 399 400 goto out; 401 } 402 403 /* 404 * If we got here then we need to create new SCO connection descriptor 405 * and submit HCI command. 406 */ 407 408 sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO); 409 if (sco_con == NULL) { 410 error = ENOMEM; 411 goto out; 412 } 413 414 bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr)); 415 416 /* 417 * Create HCI command 418 */ 419 420 MGETHDR(m, M_NOWAIT, MT_DATA); 421 if (m == NULL) { 422 ng_hci_free_con(sco_con); 423 error = ENOBUFS; 424 goto out; 425 } 426 427 m->m_pkthdr.len = m->m_len = sizeof(*req); 428 req = mtod(m, struct sco_con_req *); 429 req->hdr.type = NG_HCI_CMD_PKT; 430 req->hdr.length = sizeof(req->cp); 431 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 432 NG_HCI_OCF_ADD_SCO_CON)); 433 434 req->cp.con_handle = htole16(acl_con->con_handle); 435 436 req->cp.pkt_type = NG_HCI_PKT_HV1; 437 if (unit->features[1] & NG_HCI_LMP_HV2_PKT) 438 req->cp.pkt_type |= NG_HCI_PKT_HV2; 439 if (unit->features[1] & NG_HCI_LMP_HV3_PKT) 440 req->cp.pkt_type |= NG_HCI_PKT_HV3; 441 442 req->cp.pkt_type &= unit->packet_mask; 443 if ((req->cp.pkt_type & (NG_HCI_PKT_HV1| 444 NG_HCI_PKT_HV2| 445 NG_HCI_PKT_HV3)) == 0) 446 req->cp.pkt_type = NG_HCI_PKT_HV1; 447 448 req->cp.pkt_type = htole16(req->cp.pkt_type); 449 450 /* 451 * Adust connection state 452 */ 453 454 sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; 455 456 sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE; 457 ng_hci_con_timeout(sco_con); 458 459 /* 460 * Queue and send HCI command 461 */ 462 463 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 464 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 465 error = ng_hci_send_command(unit); 466 out: 467 NG_FREE_ITEM(item); 468 469 return (error); 470 } /* ng_hci_lp_sco_con_req */ 471 472 /* 473 * Process LP_DisconnectReq event from the upper layer protocol 474 */ 475 476 int 477 ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook) 478 { 479 struct discon_req { 480 ng_hci_cmd_pkt_t hdr; 481 ng_hci_discon_cp cp; 482 } __attribute__ ((packed)) *req = NULL; 483 ng_hci_lp_discon_req_ep *ep = NULL; 484 ng_hci_unit_con_p con = NULL; 485 struct mbuf *m = NULL; 486 int error = 0; 487 488 /* Check if unit is ready */ 489 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 490 NG_HCI_WARN( 491 "%s: %s - unit is not ready, state=%#x\n", 492 __func__, NG_NODE_NAME(unit->node), unit->state); 493 494 error = ENXIO; 495 goto out; 496 } 497 498 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 499 NG_HCI_ALERT( 500 "%s: %s - invalid LP_DisconnectReq message size=%d\n", 501 __func__, NG_NODE_NAME(unit->node), 502 NGI_MSG(item)->header.arglen); 503 504 error = EMSGSIZE; 505 goto out; 506 } 507 508 ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data); 509 510 con = ng_hci_con_by_handle(unit, ep->con_handle); 511 if (con == NULL) { 512 NG_HCI_ERR( 513 "%s: %s - invalid connection handle=%d\n", 514 __func__, NG_NODE_NAME(unit->node), ep->con_handle); 515 516 error = ENOENT; 517 goto out; 518 } 519 520 if (con->state != NG_HCI_CON_OPEN) { 521 NG_HCI_ERR( 522 "%s: %s - invalid connection state=%d, handle=%d\n", 523 __func__, NG_NODE_NAME(unit->node), con->state, 524 ep->con_handle); 525 526 error = EINVAL; 527 goto out; 528 } 529 530 /* 531 * Create HCI command 532 */ 533 534 MGETHDR(m, M_NOWAIT, MT_DATA); 535 if (m == NULL) { 536 error = ENOBUFS; 537 goto out; 538 } 539 540 m->m_pkthdr.len = m->m_len = sizeof(*req); 541 req = mtod(m, struct discon_req *); 542 req->hdr.type = NG_HCI_CMD_PKT; 543 req->hdr.length = sizeof(req->cp); 544 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 545 NG_HCI_OCF_DISCON)); 546 547 req->cp.con_handle = htole16(ep->con_handle); 548 req->cp.reason = ep->reason; 549 550 /* 551 * Queue and send HCI command 552 */ 553 554 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 555 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 556 error = ng_hci_send_command(unit); 557 out: 558 NG_FREE_ITEM(item); 559 560 return (error); 561 } /* ng_hci_lp_discon_req */ 562 563 /* 564 * Send LP_ConnectCfm event to the upper layer protocol 565 */ 566 567 int 568 ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status) 569 { 570 ng_hci_unit_p unit = con->unit; 571 struct ng_mesg *msg = NULL; 572 ng_hci_lp_con_cfm_ep *ep = NULL; 573 int error; 574 575 /* 576 * Check who wants to be notified. For ACL links both ACL and SCO 577 * upstream hooks will be notified (if required). For SCO links 578 * only SCO upstream hook will receive notification 579 */ 580 581 if (con->link_type == NG_HCI_LINK_ACL && 582 con->flags & NG_HCI_CON_NOTIFY_ACL) { 583 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 584 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, 585 sizeof(*ep), M_NOWAIT); 586 if (msg != NULL) { 587 ep = (ng_hci_lp_con_cfm_ep *) msg->data; 588 ep->status = status; 589 ep->link_type = con->link_type; 590 ep->con_handle = con->con_handle; 591 bcopy(&con->bdaddr, &ep->bdaddr, 592 sizeof(ep->bdaddr)); 593 594 NG_SEND_MSG_HOOK(error, unit->node, msg, 595 unit->acl, 0); 596 } 597 } else 598 NG_HCI_INFO( 599 "%s: %s - ACL hook not valid, hook=%p\n", 600 __func__, NG_NODE_NAME(unit->node), unit->acl); 601 602 con->flags &= ~NG_HCI_CON_NOTIFY_ACL; 603 } 604 605 if (con->flags & NG_HCI_CON_NOTIFY_SCO) { 606 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 607 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, 608 sizeof(*ep), M_NOWAIT); 609 if (msg != NULL) { 610 ep = (ng_hci_lp_con_cfm_ep *) msg->data; 611 ep->status = status; 612 ep->link_type = con->link_type; 613 ep->con_handle = con->con_handle; 614 bcopy(&con->bdaddr, &ep->bdaddr, 615 sizeof(ep->bdaddr)); 616 617 NG_SEND_MSG_HOOK(error, unit->node, msg, 618 unit->sco, 0); 619 } 620 } else 621 NG_HCI_INFO( 622 "%s: %s - SCO hook not valid, hook=%p\n", 623 __func__, NG_NODE_NAME(unit->node), unit->acl); 624 625 con->flags &= ~NG_HCI_CON_NOTIFY_SCO; 626 } 627 628 return (0); 629 } /* ng_hci_lp_con_cfm */ 630 631 /* 632 * Send LP_ConnectInd event to the upper layer protocol 633 */ 634 635 int 636 ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass) 637 { 638 ng_hci_unit_p unit = con->unit; 639 struct ng_mesg *msg = NULL; 640 ng_hci_lp_con_ind_ep *ep = NULL; 641 hook_p hook = NULL; 642 int error = 0; 643 644 /* 645 * Connection_Request event is generated for specific link type. 646 * Use link_type to select upstream hook. 647 */ 648 649 if (con->link_type == NG_HCI_LINK_ACL) 650 hook = unit->acl; 651 else 652 hook = unit->sco; 653 654 if (hook != NULL && NG_HOOK_IS_VALID(hook)) { 655 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND, 656 sizeof(*ep), M_NOWAIT); 657 if (msg == NULL) 658 return (ENOMEM); 659 660 ep = (ng_hci_lp_con_ind_ep *)(msg->data); 661 ep->link_type = con->link_type; 662 bcopy(uclass, ep->uclass, sizeof(ep->uclass)); 663 bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); 664 665 NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0); 666 } else { 667 NG_HCI_WARN( 668 "%s: %s - Upstream hook is not connected or not valid, hook=%p\n", 669 __func__, NG_NODE_NAME(unit->node), hook); 670 671 error = ENOTCONN; 672 } 673 674 return (error); 675 } /* ng_hci_lp_con_ind */ 676 677 /* 678 * Process LP_ConnectRsp event from the upper layer protocol 679 */ 680 681 int 682 ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook) 683 { 684 struct con_rsp_req { 685 ng_hci_cmd_pkt_t hdr; 686 union { 687 ng_hci_accept_con_cp acc; 688 ng_hci_reject_con_cp rej; 689 } __attribute__ ((packed)) cp; 690 } __attribute__ ((packed)) *req = NULL; 691 ng_hci_lp_con_rsp_ep *ep = NULL; 692 ng_hci_unit_con_p con = NULL; 693 struct mbuf *m = NULL; 694 int error = 0; 695 696 /* Check if unit is ready */ 697 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 698 NG_HCI_WARN( 699 "%s: %s - unit is not ready, state=%#x\n", 700 __func__, NG_NODE_NAME(unit->node), unit->state); 701 702 error = ENXIO; 703 goto out; 704 } 705 706 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 707 NG_HCI_ALERT( 708 "%s: %s - invalid LP_ConnectRsp message size=%d\n", 709 __func__, NG_NODE_NAME(unit->node), 710 NGI_MSG(item)->header.arglen); 711 712 error = EMSGSIZE; 713 goto out; 714 } 715 716 ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data); 717 718 /* 719 * Here we have to deal with race. Upper layers might send conflicting 720 * requests. One might send Accept and other Reject. We will not try 721 * to solve all the problems, so first request will always win. 722 * 723 * Try to find connection that matches the following: 724 * 725 * 1) con->link_type == ep->link_type 726 * 727 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP || 728 * con->state == NG_HCI_CON_W4_CONN_COMPLETE 729 * 730 * 3) con->bdaddr == ep->bdaddr 731 * 732 * Two cases: 733 * 734 * 1) We do not have connection descriptor. Could be bogus request or 735 * we have rejected connection already. 736 * 737 * 2) We do have connection descriptor. Then we need to check state: 738 * 739 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested 740 * connection and it is a first response from the upper layer. 741 * if "status == 0" (Accept) then we will send Accept_Connection 742 * command and change connection state to W4_CONN_COMPLETE, else 743 * send reject and delete connection. 744 * 745 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted 746 * connection. If "status == 0" we just need to link request 747 * and wait, else ignore Reject request. 748 */ 749 750 LIST_FOREACH(con, &unit->con_list, next) 751 if (con->link_type == ep->link_type && 752 (con->state == NG_HCI_CON_W4_LP_CON_RSP || 753 con->state == NG_HCI_CON_W4_CONN_COMPLETE) && 754 bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 755 break; 756 757 if (con == NULL) { 758 /* Reject for non-existing connection is fine */ 759 error = (ep->status == 0)? ENOENT : 0; 760 goto out; 761 } 762 763 /* 764 * Remove connection timeout and check connection state. 765 * Note: if ng_hci_con_untimeout() fails (returns non-zero value) then 766 * timeout already happened and event went into node's queue. 767 */ 768 769 if ((error = ng_hci_con_untimeout(con)) != 0) 770 goto out; 771 772 switch (con->state) { 773 case NG_HCI_CON_W4_LP_CON_RSP: 774 775 /* 776 * Create HCI command 777 */ 778 779 MGETHDR(m, M_NOWAIT, MT_DATA); 780 if (m == NULL) { 781 error = ENOBUFS; 782 goto out; 783 } 784 785 req = mtod(m, struct con_rsp_req *); 786 req->hdr.type = NG_HCI_CMD_PKT; 787 788 if (ep->status == 0) { 789 req->hdr.length = sizeof(req->cp.acc); 790 req->hdr.opcode = htole16(NG_HCI_OPCODE( 791 NG_HCI_OGF_LINK_CONTROL, 792 NG_HCI_OCF_ACCEPT_CON)); 793 794 bcopy(&ep->bdaddr, &req->cp.acc.bdaddr, 795 sizeof(req->cp.acc.bdaddr)); 796 797 /* 798 * We are accepting connection, so if we support role 799 * switch and role switch was enabled then set role to 800 * NG_HCI_ROLE_MASTER and let LM peform role switch. 801 * Otherwise we remain slave. In this case LM WILL NOT 802 * perform role switch. 803 */ 804 805 if ((unit->features[0] & NG_HCI_LMP_SWITCH) && 806 unit->role_switch) 807 req->cp.acc.role = NG_HCI_ROLE_MASTER; 808 else 809 req->cp.acc.role = NG_HCI_ROLE_SLAVE; 810 811 /* 812 * Adjust connection state 813 */ 814 815 if (hook == unit->acl) 816 con->flags |= NG_HCI_CON_NOTIFY_ACL; 817 else 818 con->flags |= NG_HCI_CON_NOTIFY_SCO; 819 820 con->state = NG_HCI_CON_W4_CONN_COMPLETE; 821 ng_hci_con_timeout(con); 822 } else { 823 req->hdr.length = sizeof(req->cp.rej); 824 req->hdr.opcode = htole16(NG_HCI_OPCODE( 825 NG_HCI_OGF_LINK_CONTROL, 826 NG_HCI_OCF_REJECT_CON)); 827 828 bcopy(&ep->bdaddr, &req->cp.rej.bdaddr, 829 sizeof(req->cp.rej.bdaddr)); 830 831 req->cp.rej.reason = ep->status; 832 833 /* 834 * Free connection descritor 835 * Item will be deleted just before return. 836 */ 837 838 ng_hci_free_con(con); 839 } 840 841 m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length; 842 843 /* Queue and send HCI command */ 844 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 845 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 846 error = ng_hci_send_command(unit); 847 break; 848 849 case NG_HCI_CON_W4_CONN_COMPLETE: 850 if (ep->status == 0) { 851 if (hook == unit->acl) 852 con->flags |= NG_HCI_CON_NOTIFY_ACL; 853 else 854 con->flags |= NG_HCI_CON_NOTIFY_SCO; 855 } else 856 error = EPERM; 857 break; 858 859 default: 860 panic( 861 "%s: %s - Invalid connection state=%d\n", 862 __func__, NG_NODE_NAME(unit->node), con->state); 863 break; 864 } 865 out: 866 NG_FREE_ITEM(item); 867 868 return (error); 869 } /* ng_hci_lp_con_rsp */ 870 871 /* 872 * Send LP_DisconnectInd to the upper layer protocol 873 */ 874 875 int 876 ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason) 877 { 878 ng_hci_unit_p unit = con->unit; 879 struct ng_mesg *msg = NULL; 880 ng_hci_lp_discon_ind_ep *ep = NULL; 881 int error = 0; 882 883 /* 884 * Disconnect_Complete event is generated for specific connection 885 * handle. For ACL connection handles both ACL and SCO upstream 886 * hooks will receive notification. For SCO connection handles 887 * only SCO upstream hook will receive notification. 888 */ 889 890 if (con->link_type == NG_HCI_LINK_ACL) { 891 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 892 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 893 NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT); 894 if (msg == NULL) 895 return (ENOMEM); 896 897 ep = (ng_hci_lp_discon_ind_ep *) msg->data; 898 ep->reason = reason; 899 ep->link_type = con->link_type; 900 ep->con_handle = con->con_handle; 901 902 NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,0); 903 } else 904 NG_HCI_INFO( 905 "%s: %s - ACL hook is not connected or not valid, hook=%p\n", 906 __func__, NG_NODE_NAME(unit->node), unit->acl); 907 } 908 909 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 910 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND, 911 sizeof(*ep), M_NOWAIT); 912 if (msg == NULL) 913 return (ENOMEM); 914 915 ep = (ng_hci_lp_discon_ind_ep *) msg->data; 916 ep->reason = reason; 917 ep->link_type = con->link_type; 918 ep->con_handle = con->con_handle; 919 920 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0); 921 } else 922 NG_HCI_INFO( 923 "%s: %s - SCO hook is not connected or not valid, hook=%p\n", 924 __func__, NG_NODE_NAME(unit->node), unit->sco); 925 926 return (0); 927 } /* ng_hci_lp_discon_ind */ 928 929 /* 930 * Process LP_QoSReq action from the upper layer protocol 931 */ 932 933 int 934 ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook) 935 { 936 struct qos_setup_req { 937 ng_hci_cmd_pkt_t hdr; 938 ng_hci_qos_setup_cp cp; 939 } __attribute__ ((packed)) *req = NULL; 940 ng_hci_lp_qos_req_ep *ep = NULL; 941 ng_hci_unit_con_p con = NULL; 942 struct mbuf *m = NULL; 943 int error = 0; 944 945 /* Check if unit is ready */ 946 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 947 NG_HCI_WARN( 948 "%s: %s - unit is not ready, state=%#x\n", 949 __func__, NG_NODE_NAME(unit->node), unit->state); 950 951 error = ENXIO; 952 goto out; 953 } 954 955 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 956 NG_HCI_ALERT( 957 "%s: %s - invalid LP_QoSSetupReq message size=%d\n", 958 __func__, NG_NODE_NAME(unit->node), 959 NGI_MSG(item)->header.arglen); 960 961 error = EMSGSIZE; 962 goto out; 963 } 964 965 ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data); 966 967 con = ng_hci_con_by_handle(unit, ep->con_handle); 968 if (con == NULL) { 969 NG_HCI_ERR( 970 "%s: %s - invalid connection handle=%d\n", 971 __func__, NG_NODE_NAME(unit->node), ep->con_handle); 972 973 error = EINVAL; 974 goto out; 975 } 976 977 if (con->link_type != NG_HCI_LINK_ACL) { 978 NG_HCI_ERR("%s: %s - invalid link type=%d\n", 979 __func__, NG_NODE_NAME(unit->node), con->link_type); 980 981 error = EINVAL; 982 goto out; 983 } 984 985 if (con->state != NG_HCI_CON_OPEN) { 986 NG_HCI_ERR( 987 "%s: %s - invalid connection state=%d, handle=%d\n", 988 __func__, NG_NODE_NAME(unit->node), con->state, 989 con->con_handle); 990 991 error = EINVAL; 992 goto out; 993 } 994 995 /* 996 * Create HCI command 997 */ 998 999 MGETHDR(m, M_NOWAIT, MT_DATA); 1000 if (m == NULL) { 1001 error = ENOBUFS; 1002 goto out; 1003 } 1004 1005 m->m_pkthdr.len = m->m_len = sizeof(*req); 1006 req = mtod(m, struct qos_setup_req *); 1007 req->hdr.type = NG_HCI_CMD_PKT; 1008 req->hdr.length = sizeof(req->cp); 1009 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, 1010 NG_HCI_OCF_QOS_SETUP)); 1011 1012 req->cp.con_handle = htole16(ep->con_handle); 1013 req->cp.flags = ep->flags; 1014 req->cp.service_type = ep->service_type; 1015 req->cp.token_rate = htole32(ep->token_rate); 1016 req->cp.peak_bandwidth = htole32(ep->peak_bandwidth); 1017 req->cp.latency = htole32(ep->latency); 1018 req->cp.delay_variation = htole32(ep->delay_variation); 1019 1020 /* 1021 * Adjust connection state 1022 */ 1023 1024 if (hook == unit->acl) 1025 con->flags |= NG_HCI_CON_NOTIFY_ACL; 1026 else 1027 con->flags |= NG_HCI_CON_NOTIFY_SCO; 1028 1029 /* 1030 * Queue and send HCI command 1031 */ 1032 1033 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 1034 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 1035 error = ng_hci_send_command(unit); 1036 out: 1037 NG_FREE_ITEM(item); 1038 1039 return (error); 1040 } /* ng_hci_lp_qos_req */ 1041 1042 /* 1043 * Send LP_QoSCfm event to the upper layer protocol 1044 */ 1045 1046 int 1047 ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status) 1048 { 1049 ng_hci_unit_p unit = con->unit; 1050 struct ng_mesg *msg = NULL; 1051 ng_hci_lp_qos_cfm_ep *ep = NULL; 1052 int error; 1053 1054 if (con->flags & NG_HCI_CON_NOTIFY_ACL) { 1055 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 1056 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, 1057 sizeof(*ep), M_NOWAIT); 1058 if (msg != NULL) { 1059 ep = (ng_hci_lp_qos_cfm_ep *) msg->data; 1060 ep->status = status; 1061 ep->con_handle = con->con_handle; 1062 1063 NG_SEND_MSG_HOOK(error, unit->node, msg, 1064 unit->acl, 0); 1065 } 1066 } else 1067 NG_HCI_INFO( 1068 "%s: %s - ACL hook not valid, hook=%p\n", 1069 __func__, NG_NODE_NAME(unit->node), unit->acl); 1070 1071 con->flags &= ~NG_HCI_CON_NOTIFY_ACL; 1072 } 1073 1074 if (con->flags & NG_HCI_CON_NOTIFY_SCO) { 1075 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 1076 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, 1077 sizeof(*ep), M_NOWAIT); 1078 if (msg != NULL) { 1079 ep = (ng_hci_lp_qos_cfm_ep *) msg->data; 1080 ep->status = status; 1081 ep->con_handle = con->con_handle; 1082 1083 NG_SEND_MSG_HOOK(error, unit->node, msg, 1084 unit->sco, 0); 1085 } 1086 } else 1087 NG_HCI_INFO( 1088 "%s: %s - SCO hook not valid, hook=%p\n", 1089 __func__, NG_NODE_NAME(unit->node), unit->sco); 1090 1091 con->flags &= ~NG_HCI_CON_NOTIFY_SCO; 1092 } 1093 1094 return (0); 1095 } /* ng_hci_lp_qos_cfm */ 1096 1097 /* 1098 * Send LP_QoSViolationInd event to the upper layer protocol 1099 */ 1100 1101 int 1102 ng_hci_lp_qos_ind(ng_hci_unit_con_p con) 1103 { 1104 ng_hci_unit_p unit = con->unit; 1105 struct ng_mesg *msg = NULL; 1106 ng_hci_lp_qos_ind_ep *ep = NULL; 1107 int error; 1108 1109 /* 1110 * QoS Violation can only be generated for ACL connection handles. 1111 * Both ACL and SCO upstream hooks will receive notification. 1112 */ 1113 1114 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 1115 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, 1116 sizeof(*ep), M_NOWAIT); 1117 if (msg == NULL) 1118 return (ENOMEM); 1119 1120 ep = (ng_hci_lp_qos_ind_ep *) msg->data; 1121 ep->con_handle = con->con_handle; 1122 1123 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0); 1124 } else 1125 NG_HCI_INFO( 1126 "%s: %s - ACL hook is not connected or not valid, hook=%p\n", 1127 __func__, NG_NODE_NAME(unit->node), unit->acl); 1128 1129 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 1130 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, 1131 sizeof(*ep), M_NOWAIT); 1132 if (msg == NULL) 1133 return (ENOMEM); 1134 1135 ep = (ng_hci_lp_qos_ind_ep *) msg->data; 1136 ep->con_handle = con->con_handle; 1137 1138 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0); 1139 } else 1140 NG_HCI_INFO( 1141 "%s: %s - SCO hook is not connected or not valid, hook=%p\n", 1142 __func__, NG_NODE_NAME(unit->node), unit->sco); 1143 1144 return (0); 1145 } /* ng_hci_lp_qos_ind */ 1146 1147 /* 1148 * Process connection timeout 1149 */ 1150 1151 void 1152 ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int con_handle) 1153 { 1154 ng_hci_unit_p unit = NULL; 1155 ng_hci_unit_con_p con = NULL; 1156 1157 if (NG_NODE_NOT_VALID(node)) { 1158 printf("%s: Netgraph node is not valid\n", __func__); 1159 return; 1160 } 1161 1162 unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 1163 con = ng_hci_con_by_handle(unit, con_handle); 1164 1165 if (con == NULL) { 1166 NG_HCI_ALERT( 1167 "%s: %s - could not find connection, handle=%d\n", 1168 __func__, NG_NODE_NAME(node), con_handle); 1169 return; 1170 } 1171 1172 if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) { 1173 NG_HCI_ALERT( 1174 "%s: %s - no pending connection timeout, handle=%d, state=%d, flags=%#x\n", 1175 __func__, NG_NODE_NAME(node), con_handle, con->state, 1176 con->flags); 1177 return; 1178 } 1179 1180 con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING; 1181 1182 /* 1183 * We expect to receive connection timeout in one of the following 1184 * states: 1185 * 1186 * 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded 1187 * to our LP_CON_IND. Do nothing and destroy connection. Remote peer 1188 * most likely already gave up on us. 1189 * 1190 * 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection 1191 * (or we in the process of accepting it) and baseband has timedout 1192 * on us. Inform upper layers and send LP_CON_CFM. 1193 */ 1194 1195 switch (con->state) { 1196 case NG_HCI_CON_W4_LP_CON_RSP: 1197 break; 1198 1199 case NG_HCI_CON_W4_CONN_COMPLETE: 1200 ng_hci_lp_con_cfm(con, 0xee); 1201 break; 1202 1203 default: 1204 panic( 1205 "%s: %s - Invalid connection state=%d\n", 1206 __func__, NG_NODE_NAME(node), con->state); 1207 break; 1208 } 1209 1210 ng_hci_free_con(con); 1211 } /* ng_hci_process_con_timeout */ 1212 1213