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.6 2003/04/26 22:35:21 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 "ng_bluetooth.h" 42 #include "ng_hci.h" 43 #include "ng_hci_var.h" 44 #include "ng_hci_cmds.h" 45 #include "ng_hci_evnt.h" 46 #include "ng_hci_ulpi.h" 47 #include "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 KASSERT(0, 199 ("%s: %s - Invalid connection state=%d\n", 200 __func__, NG_NODE_NAME(unit->node),con->state)); 201 202 error = EINVAL; 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_DONTWAIT, 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 KASSERT(0, 394 ("%s: %s - Inavalid connection state=%d\n", 395 __func__, NG_NODE_NAME(unit->node), 396 sco_con->state)); 397 398 error = EINVAL; 399 break; 400 } 401 402 goto out; 403 } 404 405 /* 406 * If we got here then we need to create new SCO connection descriptor 407 * and submit HCI command. 408 */ 409 410 sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO); 411 if (sco_con == NULL) { 412 error = ENOMEM; 413 goto out; 414 } 415 416 bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr)); 417 418 /* 419 * Create HCI command 420 */ 421 422 MGETHDR(m, M_DONTWAIT, MT_DATA); 423 if (m == NULL) { 424 ng_hci_free_con(sco_con); 425 error = ENOBUFS; 426 goto out; 427 } 428 429 m->m_pkthdr.len = m->m_len = sizeof(*req); 430 req = mtod(m, struct sco_con_req *); 431 req->hdr.type = NG_HCI_CMD_PKT; 432 req->hdr.length = sizeof(req->cp); 433 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 434 NG_HCI_OCF_ADD_SCO_CON)); 435 436 req->cp.con_handle = htole16(acl_con->con_handle); 437 438 req->cp.pkt_type = NG_HCI_PKT_HV1; 439 if (unit->features[1] & NG_HCI_LMP_HV2_PKT) 440 req->cp.pkt_type |= NG_HCI_PKT_HV2; 441 if (unit->features[1] & NG_HCI_LMP_HV3_PKT) 442 req->cp.pkt_type |= NG_HCI_PKT_HV3; 443 444 req->cp.pkt_type &= unit->packet_mask; 445 if ((req->cp.pkt_type & (NG_HCI_PKT_HV1| 446 NG_HCI_PKT_HV2| 447 NG_HCI_PKT_HV3)) == 0) 448 req->cp.pkt_type = NG_HCI_PKT_HV1; 449 450 req->cp.pkt_type = htole16(req->cp.pkt_type); 451 452 /* 453 * Adust connection state 454 */ 455 456 sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; 457 458 sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE; 459 ng_hci_con_timeout(sco_con); 460 461 /* 462 * Queue and send HCI command 463 */ 464 465 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 466 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 467 error = ng_hci_send_command(unit); 468 out: 469 NG_FREE_ITEM(item); 470 471 return (error); 472 } /* ng_hci_lp_sco_con_req */ 473 474 /* 475 * Process LP_DisconnectReq event from the upper layer protocol 476 */ 477 478 int 479 ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook) 480 { 481 struct discon_req { 482 ng_hci_cmd_pkt_t hdr; 483 ng_hci_discon_cp cp; 484 } __attribute__ ((packed)) *req = NULL; 485 ng_hci_lp_discon_req_ep *ep = NULL; 486 ng_hci_unit_con_p con = NULL; 487 struct mbuf *m = NULL; 488 int error = 0; 489 490 /* Check if unit is ready */ 491 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 492 NG_HCI_WARN( 493 "%s: %s - unit is not ready, state=%#x\n", 494 __func__, NG_NODE_NAME(unit->node), unit->state); 495 496 error = ENXIO; 497 goto out; 498 } 499 500 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 501 NG_HCI_ALERT( 502 "%s: %s - invalid LP_DisconnectReq message size=%d\n", 503 __func__, NG_NODE_NAME(unit->node), 504 NGI_MSG(item)->header.arglen); 505 506 error = EMSGSIZE; 507 goto out; 508 } 509 510 ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data); 511 512 con = ng_hci_con_by_handle(unit, ep->con_handle); 513 if (con == NULL) { 514 NG_HCI_ERR( 515 "%s: %s - invalid connection handle=%d\n", 516 __func__, NG_NODE_NAME(unit->node), ep->con_handle); 517 518 error = ENOENT; 519 goto out; 520 } 521 522 if (con->state != NG_HCI_CON_OPEN) { 523 NG_HCI_ERR( 524 "%s: %s - invalid connection state=%d, handle=%d\n", 525 __func__, NG_NODE_NAME(unit->node), con->state, 526 ep->con_handle); 527 528 error = EINVAL; 529 goto out; 530 } 531 532 /* 533 * Create HCI command 534 */ 535 536 MGETHDR(m, M_DONTWAIT, MT_DATA); 537 if (m == NULL) { 538 error = ENOBUFS; 539 goto out; 540 } 541 542 m->m_pkthdr.len = m->m_len = sizeof(*req); 543 req = mtod(m, struct discon_req *); 544 req->hdr.type = NG_HCI_CMD_PKT; 545 req->hdr.length = sizeof(req->cp); 546 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 547 NG_HCI_OCF_DISCON)); 548 549 req->cp.con_handle = htole16(ep->con_handle); 550 req->cp.reason = ep->reason; 551 552 /* 553 * Queue and send HCI command 554 */ 555 556 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 557 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 558 error = ng_hci_send_command(unit); 559 out: 560 NG_FREE_ITEM(item); 561 562 return (error); 563 } /* ng_hci_lp_discon_req */ 564 565 /* 566 * Send LP_ConnectCfm event to the upper layer protocol 567 */ 568 569 int 570 ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status) 571 { 572 ng_hci_unit_p unit = con->unit; 573 struct ng_mesg *msg = NULL; 574 ng_hci_lp_con_cfm_ep *ep = NULL; 575 int error; 576 577 /* 578 * Check who wants to be notified. For ACL links both ACL and SCO 579 * upstream hooks will be notified (if required). For SCO links 580 * only SCO upstream hook will receive notification 581 */ 582 583 if (con->link_type == NG_HCI_LINK_ACL && 584 con->flags & NG_HCI_CON_NOTIFY_ACL) { 585 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 586 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, 587 sizeof(*ep), M_NOWAIT); 588 if (msg != NULL) { 589 ep = (ng_hci_lp_con_cfm_ep *) msg->data; 590 ep->status = status; 591 ep->link_type = con->link_type; 592 ep->con_handle = con->con_handle; 593 bcopy(&con->bdaddr, &ep->bdaddr, 594 sizeof(ep->bdaddr)); 595 596 NG_SEND_MSG_HOOK(error, unit->node, msg, 597 unit->acl, NULL); 598 } 599 } else 600 NG_HCI_INFO( 601 "%s: %s - ACL hook not valid, hook=%p\n", 602 __func__, NG_NODE_NAME(unit->node), unit->acl); 603 604 con->flags &= ~NG_HCI_CON_NOTIFY_ACL; 605 } 606 607 if (con->flags & NG_HCI_CON_NOTIFY_SCO) { 608 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 609 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, 610 sizeof(*ep), M_NOWAIT); 611 if (msg != NULL) { 612 ep = (ng_hci_lp_con_cfm_ep *) msg->data; 613 ep->status = status; 614 ep->link_type = con->link_type; 615 ep->con_handle = con->con_handle; 616 bcopy(&con->bdaddr, &ep->bdaddr, 617 sizeof(ep->bdaddr)); 618 619 NG_SEND_MSG_HOOK(error, unit->node, msg, 620 unit->sco, NULL); 621 } 622 } else 623 NG_HCI_INFO( 624 "%s: %s - SCO hook not valid, hook=%p\n", 625 __func__, NG_NODE_NAME(unit->node), unit->acl); 626 627 con->flags &= ~NG_HCI_CON_NOTIFY_SCO; 628 } 629 630 return (0); 631 } /* ng_hci_lp_con_cfm */ 632 633 /* 634 * Send LP_ConnectInd event to the upper layer protocol 635 */ 636 637 int 638 ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass) 639 { 640 ng_hci_unit_p unit = con->unit; 641 struct ng_mesg *msg = NULL; 642 ng_hci_lp_con_ind_ep *ep = NULL; 643 hook_p hook = NULL; 644 int error = 0; 645 646 /* 647 * Connection_Request event is generated for specific link type. 648 * Use link_type to select upstream hook. 649 */ 650 651 if (con->link_type == NG_HCI_LINK_ACL) 652 hook = unit->acl; 653 else 654 hook = unit->sco; 655 656 if (hook != NULL && NG_HOOK_IS_VALID(hook)) { 657 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND, 658 sizeof(*ep), M_NOWAIT); 659 if (msg == NULL) 660 return (ENOMEM); 661 662 ep = (ng_hci_lp_con_ind_ep *)(msg->data); 663 ep->link_type = con->link_type; 664 bcopy(uclass, ep->uclass, sizeof(ep->uclass)); 665 bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); 666 667 NG_SEND_MSG_HOOK(error, unit->node, msg, hook, NULL); 668 } else { 669 NG_HCI_WARN( 670 "%s: %s - Upstream hook is not connected or not valid, hook=%p\n", 671 __func__, NG_NODE_NAME(unit->node), hook); 672 673 error = ENOTCONN; 674 } 675 676 return (error); 677 } /* ng_hci_lp_con_ind */ 678 679 /* 680 * Process LP_ConnectRsp event from the upper layer protocol 681 */ 682 683 int 684 ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook) 685 { 686 struct con_rsp_req { 687 ng_hci_cmd_pkt_t hdr; 688 union { 689 ng_hci_accept_con_cp acc; 690 ng_hci_reject_con_cp rej; 691 } __attribute__ ((packed)) cp; 692 } __attribute__ ((packed)) *req = NULL; 693 ng_hci_lp_con_rsp_ep *ep = NULL; 694 ng_hci_unit_con_p con = NULL; 695 struct mbuf *m = NULL; 696 int error = 0; 697 698 /* Check if unit is ready */ 699 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 700 NG_HCI_WARN( 701 "%s: %s - unit is not ready, state=%#x\n", 702 __func__, NG_NODE_NAME(unit->node), unit->state); 703 704 error = ENXIO; 705 goto out; 706 } 707 708 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 709 NG_HCI_ALERT( 710 "%s: %s - invalid LP_ConnectRsp message size=%d\n", 711 __func__, NG_NODE_NAME(unit->node), 712 NGI_MSG(item)->header.arglen); 713 714 error = EMSGSIZE; 715 goto out; 716 } 717 718 ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data); 719 720 /* 721 * Here we have to deal with race. Upper layers might send conflicting 722 * requests. One might send Accept and other Reject. We will not try 723 * to solve all the problems, so first request will always win. 724 * 725 * Try to find connection that matches the following: 726 * 727 * 1) con->link_type == ep->link_type 728 * 729 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP || 730 * con->state == NG_HCI_CON_W4_CONN_COMPLETE 731 * 732 * 3) con->bdaddr == ep->bdaddr 733 * 734 * Two cases: 735 * 736 * 1) We do not have connection descriptor. Could be bogus request or 737 * we have rejected connection already. 738 * 739 * 2) We do have connection descriptor. Then we need to check state: 740 * 741 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested 742 * connection and it is a first response from the upper layer. 743 * if "status == 0" (Accept) then we will send Accept_Connection 744 * command and change connection state to W4_CONN_COMPLETE, else 745 * send reject and delete connection. 746 * 747 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted 748 * connection. If "status == 0" we just need to link request 749 * and wait, else ignore Reject request. 750 */ 751 752 LIST_FOREACH(con, &unit->con_list, next) 753 if (con->link_type == ep->link_type && 754 (con->state == NG_HCI_CON_W4_LP_CON_RSP || 755 con->state == NG_HCI_CON_W4_CONN_COMPLETE) && 756 bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 757 break; 758 759 if (con == NULL) { 760 /* Reject for non-existing connection is fine */ 761 error = (ep->status == 0)? ENOENT : 0; 762 goto out; 763 } 764 765 /* 766 * Remove connection timeout and check connection state 767 */ 768 769 ng_hci_con_untimeout(con); 770 771 switch (con->state) { 772 case NG_HCI_CON_W4_LP_CON_RSP: 773 774 /* 775 * Create HCI command 776 */ 777 778 MGETHDR(m, M_DONTWAIT, MT_DATA); 779 if (m == NULL) { 780 error = ENOBUFS; 781 goto out; 782 } 783 784 req = mtod(m, struct con_rsp_req *); 785 req->hdr.type = NG_HCI_CMD_PKT; 786 787 if (ep->status == 0) { 788 req->hdr.length = sizeof(req->cp.acc); 789 req->hdr.opcode = htole16(NG_HCI_OPCODE( 790 NG_HCI_OGF_LINK_CONTROL, 791 NG_HCI_OCF_ACCEPT_CON)); 792 793 bcopy(&ep->bdaddr, &req->cp.acc.bdaddr, 794 sizeof(req->cp.acc.bdaddr)); 795 796 /* 797 * We are accepting connection, so if we support role 798 * switch and role switch was enabled then set role to 799 * NG_HCI_ROLE_MASTER and let LM peform role switch. 800 * Otherwise we remain slave. In this case LM WILL NOT 801 * perform role switch. 802 */ 803 804 if ((unit->features[0] & NG_HCI_LMP_SWITCH) && 805 unit->role_switch) 806 req->cp.acc.role = NG_HCI_ROLE_MASTER; 807 else 808 req->cp.acc.role = NG_HCI_ROLE_SLAVE; 809 810 /* 811 * Adjust connection state 812 */ 813 814 if (hook == unit->acl) 815 con->flags |= NG_HCI_CON_NOTIFY_ACL; 816 else 817 con->flags |= NG_HCI_CON_NOTIFY_SCO; 818 819 con->state = NG_HCI_CON_W4_CONN_COMPLETE; 820 ng_hci_con_timeout(con); 821 } else { 822 req->hdr.length = sizeof(req->cp.rej); 823 req->hdr.opcode = htole16(NG_HCI_OPCODE( 824 NG_HCI_OGF_LINK_CONTROL, 825 NG_HCI_OCF_REJECT_CON)); 826 827 bcopy(&ep->bdaddr, &req->cp.rej.bdaddr, 828 sizeof(req->cp.rej.bdaddr)); 829 830 req->cp.rej.reason = ep->status; 831 832 /* 833 * Free connection descritor 834 * Item will be deleted just before return. 835 */ 836 837 ng_hci_free_con(con); 838 } 839 840 m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length; 841 842 /* Queue and send HCI command */ 843 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 844 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 845 error = ng_hci_send_command(unit); 846 break; 847 848 case NG_HCI_CON_W4_CONN_COMPLETE: 849 if (ep->status == 0) { 850 if (hook == unit->acl) 851 con->flags |= NG_HCI_CON_NOTIFY_ACL; 852 else 853 con->flags |= NG_HCI_CON_NOTIFY_SCO; 854 } else 855 error = EPERM; 856 break; 857 858 default: 859 KASSERT(0, 860 ("%s: %s - Invalid connection state=%d\n", 861 __func__, NG_NODE_NAME(unit->node), con->state)); 862 863 error = EINVAL; 864 break; 865 } 866 out: 867 NG_FREE_ITEM(item); 868 869 return (error); 870 } /* ng_hci_lp_con_rsp */ 871 872 /* 873 * Send LP_DisconnectInd to the upper layer protocol 874 */ 875 876 int 877 ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason) 878 { 879 ng_hci_unit_p unit = con->unit; 880 struct ng_mesg *msg = NULL; 881 ng_hci_lp_discon_ind_ep *ep = NULL; 882 int error = 0; 883 884 /* 885 * Disconnect_Complete event is generated for specific connection 886 * handle. For ACL connection handles both ACL and SCO upstream 887 * hooks will receive notification. For SCO connection handles 888 * only SCO upstream hook will receive notification. 889 */ 890 891 if (con->link_type == NG_HCI_LINK_ACL) { 892 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 893 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 894 NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT); 895 if (msg == NULL) 896 return (ENOMEM); 897 898 ep = (ng_hci_lp_discon_ind_ep *) msg->data; 899 ep->reason = reason; 900 ep->link_type = con->link_type; 901 ep->con_handle = con->con_handle; 902 903 NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,NULL); 904 } else 905 NG_HCI_INFO( 906 "%s: %s - ACL hook is not connected or not valid, hook=%p\n", 907 __func__, NG_NODE_NAME(unit->node), unit->acl); 908 } 909 910 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 911 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND, 912 sizeof(*ep), M_NOWAIT); 913 if (msg == NULL) 914 return (ENOMEM); 915 916 ep = (ng_hci_lp_discon_ind_ep *) msg->data; 917 ep->reason = reason; 918 ep->link_type = con->link_type; 919 ep->con_handle = con->con_handle; 920 921 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, NULL); 922 } else 923 NG_HCI_INFO( 924 "%s: %s - SCO hook is not connected or not valid, hook=%p\n", 925 __func__, NG_NODE_NAME(unit->node), unit->sco); 926 927 return (0); 928 } /* ng_hci_lp_discon_ind */ 929 930 /* 931 * Process LP_QoSReq action from the upper layer protocol 932 */ 933 934 int 935 ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook) 936 { 937 struct qos_setup_req { 938 ng_hci_cmd_pkt_t hdr; 939 ng_hci_qos_setup_cp cp; 940 } __attribute__ ((packed)) *req = NULL; 941 ng_hci_lp_qos_req_ep *ep = NULL; 942 ng_hci_unit_con_p con = NULL; 943 struct mbuf *m = NULL; 944 int error = 0; 945 946 /* Check if unit is ready */ 947 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 948 NG_HCI_WARN( 949 "%s: %s - unit is not ready, state=%#x\n", 950 __func__, NG_NODE_NAME(unit->node), unit->state); 951 952 error = ENXIO; 953 goto out; 954 } 955 956 if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 957 NG_HCI_ALERT( 958 "%s: %s - invalid LP_QoSSetupReq message size=%d\n", 959 __func__, NG_NODE_NAME(unit->node), 960 NGI_MSG(item)->header.arglen); 961 962 error = EMSGSIZE; 963 goto out; 964 } 965 966 ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data); 967 968 con = ng_hci_con_by_handle(unit, ep->con_handle); 969 if (con == NULL) { 970 NG_HCI_ERR( 971 "%s: %s - invalid connection handle=%d\n", 972 __func__, NG_NODE_NAME(unit->node), ep->con_handle); 973 974 error = EINVAL; 975 goto out; 976 } 977 978 if (con->link_type != NG_HCI_LINK_ACL) { 979 NG_HCI_ERR("%s: %s - invalid link type=%d\n", 980 __func__, NG_NODE_NAME(unit->node), con->link_type); 981 982 error = EINVAL; 983 goto out; 984 } 985 986 if (con->state != NG_HCI_CON_OPEN) { 987 NG_HCI_ERR( 988 "%s: %s - invalid connection state=%d, handle=%d\n", 989 __func__, NG_NODE_NAME(unit->node), con->state, 990 con->con_handle); 991 992 error = EINVAL; 993 goto out; 994 } 995 996 /* 997 * Create HCI command 998 */ 999 1000 MGETHDR(m, M_DONTWAIT, MT_DATA); 1001 if (m == NULL) { 1002 error = ENOBUFS; 1003 goto out; 1004 } 1005 1006 m->m_pkthdr.len = m->m_len = sizeof(*req); 1007 req = mtod(m, struct qos_setup_req *); 1008 req->hdr.type = NG_HCI_CMD_PKT; 1009 req->hdr.length = sizeof(req->cp); 1010 req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, 1011 NG_HCI_OCF_QOS_SETUP)); 1012 1013 req->cp.con_handle = htole16(ep->con_handle); 1014 req->cp.flags = ep->flags; 1015 req->cp.service_type = ep->service_type; 1016 req->cp.token_rate = htole32(ep->token_rate); 1017 req->cp.peak_bandwidth = htole32(ep->peak_bandwidth); 1018 req->cp.latency = htole32(ep->latency); 1019 req->cp.delay_variation = htole32(ep->delay_variation); 1020 1021 /* 1022 * Adjust connection state 1023 */ 1024 1025 if (hook == unit->acl) 1026 con->flags |= NG_HCI_CON_NOTIFY_ACL; 1027 else 1028 con->flags |= NG_HCI_CON_NOTIFY_SCO; 1029 1030 /* 1031 * Queue and send HCI command 1032 */ 1033 1034 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 1035 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 1036 error = ng_hci_send_command(unit); 1037 out: 1038 NG_FREE_ITEM(item); 1039 1040 return (error); 1041 } /* ng_hci_lp_qos_req */ 1042 1043 /* 1044 * Send LP_QoSCfm event to the upper layer protocol 1045 */ 1046 1047 int 1048 ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status) 1049 { 1050 ng_hci_unit_p unit = con->unit; 1051 struct ng_mesg *msg = NULL; 1052 ng_hci_lp_qos_cfm_ep *ep = NULL; 1053 int error; 1054 1055 if (con->flags & NG_HCI_CON_NOTIFY_ACL) { 1056 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 1057 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, 1058 sizeof(*ep), M_NOWAIT); 1059 if (msg != NULL) { 1060 ep = (ng_hci_lp_qos_cfm_ep *) msg->data; 1061 ep->status = status; 1062 ep->con_handle = con->con_handle; 1063 1064 NG_SEND_MSG_HOOK(error, unit->node, msg, 1065 unit->acl, NULL); 1066 } 1067 } else 1068 NG_HCI_INFO( 1069 "%s: %s - ACL hook not valid, hook=%p\n", 1070 __func__, NG_NODE_NAME(unit->node), unit->acl); 1071 1072 con->flags &= ~NG_HCI_CON_NOTIFY_ACL; 1073 } 1074 1075 if (con->flags & NG_HCI_CON_NOTIFY_SCO) { 1076 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 1077 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, 1078 sizeof(*ep), M_NOWAIT); 1079 if (msg != NULL) { 1080 ep = (ng_hci_lp_qos_cfm_ep *) msg->data; 1081 ep->status = status; 1082 ep->con_handle = con->con_handle; 1083 1084 NG_SEND_MSG_HOOK(error, unit->node, msg, 1085 unit->sco, NULL); 1086 } 1087 } else 1088 NG_HCI_INFO( 1089 "%s: %s - SCO hook not valid, hook=%p\n", 1090 __func__, NG_NODE_NAME(unit->node), unit->sco); 1091 1092 con->flags &= ~NG_HCI_CON_NOTIFY_SCO; 1093 } 1094 1095 return (0); 1096 } /* ng_hci_lp_qos_cfm */ 1097 1098 /* 1099 * Send LP_QoSViolationInd event to the upper layer protocol 1100 */ 1101 1102 int 1103 ng_hci_lp_qos_ind(ng_hci_unit_con_p con) 1104 { 1105 ng_hci_unit_p unit = con->unit; 1106 struct ng_mesg *msg = NULL; 1107 ng_hci_lp_qos_ind_ep *ep = NULL; 1108 int error; 1109 1110 /* 1111 * QoS Violation can only be generated for ACL connection handles. 1112 * Both ACL and SCO upstream hooks will receive notification. 1113 */ 1114 1115 if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 1116 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, 1117 sizeof(*ep), M_NOWAIT); 1118 if (msg == NULL) 1119 return (ENOMEM); 1120 1121 ep = (ng_hci_lp_qos_ind_ep *) msg->data; 1122 ep->con_handle = con->con_handle; 1123 1124 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, NULL); 1125 } else 1126 NG_HCI_INFO( 1127 "%s: %s - ACL hook is not connected or not valid, hook=%p\n", 1128 __func__, NG_NODE_NAME(unit->node), unit->acl); 1129 1130 if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 1131 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, 1132 sizeof(*ep), M_NOWAIT); 1133 if (msg == NULL) 1134 return (ENOMEM); 1135 1136 ep = (ng_hci_lp_qos_ind_ep *) msg->data; 1137 ep->con_handle = con->con_handle; 1138 1139 NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, NULL); 1140 } else 1141 NG_HCI_INFO( 1142 "%s: %s - SCO hook is not connected or not valid, hook=%p\n", 1143 __func__, NG_NODE_NAME(unit->node), unit->sco); 1144 1145 return (0); 1146 } /* ng_hci_lp_qos_ind */ 1147 1148 /* 1149 * Process connection timeout 1150 */ 1151 1152 void 1153 ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int arg2) 1154 { 1155 ng_hci_unit_con_p con = (ng_hci_unit_con_p) arg1; 1156 1157 KASSERT((con->flags & NG_HCI_CON_TIMEOUT_PENDING), 1158 ("%s: %s - No connection timeout!\n", __func__, NG_NODE_NAME(node))); 1159 1160 con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING; 1161 1162 /* 1163 * We expect to receive connection timeout in one of the following 1164 * states: 1165 * 1166 * 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded 1167 * to our LP_CON_IND. Do nothing and destroy connection. Remote peer 1168 * most likely already gave up on us. 1169 * 1170 * 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection 1171 * (or we in the process of accepting it) and baseband has timedout 1172 * on us. Inform upper layers and send LP_CON_CFM. 1173 */ 1174 1175 switch (con->state) { 1176 case NG_HCI_CON_W4_LP_CON_RSP: 1177 break; 1178 1179 case NG_HCI_CON_W4_CONN_COMPLETE: 1180 ng_hci_lp_con_cfm(con, 0xee); 1181 break; 1182 1183 default: 1184 KASSERT(0, 1185 ("%s: %s - Invalid connection state=%d\n", 1186 __func__, NG_NODE_NAME(node), con->state)); 1187 break; 1188 } 1189 1190 ng_hci_free_con(con); 1191 } /* ng_hci_process_con_timeout */ 1192 1193