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