1 /* 2 * ng_hci_evnt.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_evnt.c,v 1.6 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 ** HCI event processing module 54 ****************************************************************************** 55 ******************************************************************************/ 56 57 /* 58 * Event processing routines 59 */ 60 61 static int inquiry_result (ng_hci_unit_p, struct mbuf *); 62 static int con_compl (ng_hci_unit_p, struct mbuf *); 63 static int con_req (ng_hci_unit_p, struct mbuf *); 64 static int discon_compl (ng_hci_unit_p, struct mbuf *); 65 static int encryption_change (ng_hci_unit_p, struct mbuf *); 66 static int read_remote_features_compl (ng_hci_unit_p, struct mbuf *); 67 static int qos_setup_compl (ng_hci_unit_p, struct mbuf *); 68 static int hardware_error (ng_hci_unit_p, struct mbuf *); 69 static int role_change (ng_hci_unit_p, struct mbuf *); 70 static int num_compl_pkts (ng_hci_unit_p, struct mbuf *); 71 static int mode_change (ng_hci_unit_p, struct mbuf *); 72 static int data_buffer_overflow (ng_hci_unit_p, struct mbuf *); 73 static int read_clock_offset_compl (ng_hci_unit_p, struct mbuf *); 74 static int qos_violation (ng_hci_unit_p, struct mbuf *); 75 static int page_scan_mode_change (ng_hci_unit_p, struct mbuf *); 76 static int page_scan_rep_mode_change (ng_hci_unit_p, struct mbuf *); 77 static int sync_con_queue (ng_hci_unit_p, ng_hci_unit_con_p, int); 78 static int send_data_packets (ng_hci_unit_p, int, int); 79 80 /* 81 * Process HCI event packet 82 */ 83 84 int 85 ng_hci_process_event(ng_hci_unit_p unit, struct mbuf *event) 86 { 87 ng_hci_event_pkt_t *hdr = NULL; 88 int error = 0; 89 90 /* Get event packet header */ 91 NG_HCI_M_PULLUP(event, sizeof(*hdr)); 92 if (event == NULL) 93 return (ENOBUFS); 94 95 hdr = mtod(event, ng_hci_event_pkt_t *); 96 97 NG_HCI_INFO( 98 "%s: %s - got HCI event=%#x, length=%d\n", 99 __func__, NG_NODE_NAME(unit->node), hdr->event, hdr->length); 100 101 /* Get rid of event header and process event */ 102 m_adj(event, sizeof(*hdr)); 103 104 switch (hdr->event) { 105 case NG_HCI_EVENT_INQUIRY_COMPL: 106 case NG_HCI_EVENT_RETURN_LINK_KEYS: 107 case NG_HCI_EVENT_PIN_CODE_REQ: 108 case NG_HCI_EVENT_LINK_KEY_REQ: 109 case NG_HCI_EVENT_LINK_KEY_NOTIFICATION: 110 case NG_HCI_EVENT_LOOPBACK_COMMAND: 111 case NG_HCI_EVENT_AUTH_COMPL: 112 case NG_HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL: 113 case NG_HCI_EVENT_MASTER_LINK_KEY_COMPL: 114 case NG_HCI_EVENT_FLUSH_OCCUR: /* XXX Do we have to handle it? */ 115 case NG_HCI_EVENT_MAX_SLOT_CHANGE: 116 case NG_HCI_EVENT_CON_PKT_TYPE_CHANGED: 117 case NG_HCI_EVENT_BT_LOGO: 118 case NG_HCI_EVENT_VENDOR: 119 case NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL: 120 case NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL: 121 /* These do not need post processing */ 122 NG_FREE_M(event); 123 break; 124 125 case NG_HCI_EVENT_INQUIRY_RESULT: 126 error = inquiry_result(unit, event); 127 break; 128 129 case NG_HCI_EVENT_CON_COMPL: 130 error = con_compl(unit, event); 131 break; 132 133 case NG_HCI_EVENT_CON_REQ: 134 error = con_req(unit, event); 135 break; 136 137 case NG_HCI_EVENT_DISCON_COMPL: 138 error = discon_compl(unit, event); 139 break; 140 141 case NG_HCI_EVENT_ENCRYPTION_CHANGE: 142 error = encryption_change(unit, event); 143 break; 144 145 case NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL: 146 error = read_remote_features_compl(unit, event); 147 break; 148 149 case NG_HCI_EVENT_QOS_SETUP_COMPL: 150 error = qos_setup_compl(unit, event); 151 break; 152 153 case NG_HCI_EVENT_COMMAND_COMPL: 154 error = ng_hci_process_command_complete(unit, event); 155 break; 156 157 case NG_HCI_EVENT_COMMAND_STATUS: 158 error = ng_hci_process_command_status(unit, event); 159 break; 160 161 case NG_HCI_EVENT_HARDWARE_ERROR: 162 error = hardware_error(unit, event); 163 break; 164 165 case NG_HCI_EVENT_ROLE_CHANGE: 166 error = role_change(unit, event); 167 break; 168 169 case NG_HCI_EVENT_NUM_COMPL_PKTS: 170 error = num_compl_pkts(unit, event); 171 break; 172 173 case NG_HCI_EVENT_MODE_CHANGE: 174 error = mode_change(unit, event); 175 break; 176 177 case NG_HCI_EVENT_DATA_BUFFER_OVERFLOW: 178 error = data_buffer_overflow(unit, event); 179 break; 180 181 case NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL: 182 error = read_clock_offset_compl(unit, event); 183 break; 184 185 case NG_HCI_EVENT_QOS_VIOLATION: 186 error = qos_violation(unit, event); 187 break; 188 189 case NG_HCI_EVENT_PAGE_SCAN_MODE_CHANGE: 190 error = page_scan_mode_change(unit, event); 191 break; 192 193 case NG_HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE: 194 error = page_scan_rep_mode_change(unit, event); 195 break; 196 197 default: 198 NG_FREE_M(event); 199 error = EINVAL; 200 break; 201 } 202 203 return (error); 204 } /* ng_hci_process_event */ 205 206 /* 207 * Send ACL and/or SCO data to the unit driver 208 */ 209 210 void 211 ng_hci_send_data(ng_hci_unit_p unit) 212 { 213 int count; 214 215 /* Send ACL data */ 216 NG_HCI_BUFF_ACL_AVAIL(unit->buffer, count); 217 218 NG_HCI_INFO( 219 "%s: %s - sending ACL data packets, count=%d\n", 220 __func__, NG_NODE_NAME(unit->node), count); 221 222 if (count > 0) { 223 count = send_data_packets(unit, NG_HCI_LINK_ACL, count); 224 NG_HCI_STAT_ACL_SENT(unit->stat, count); 225 NG_HCI_BUFF_ACL_USE(unit->buffer, count); 226 } 227 228 /* Send SCO data */ 229 NG_HCI_BUFF_SCO_AVAIL(unit->buffer, count); 230 231 NG_HCI_INFO( 232 "%s: %s - sending SCO data packets, count=%d\n", 233 __func__, NG_NODE_NAME(unit->node), count); 234 235 if (count > 0) { 236 count = send_data_packets(unit, NG_HCI_LINK_SCO, count); 237 NG_HCI_STAT_SCO_SENT(unit->stat, count); 238 NG_HCI_BUFF_SCO_USE(unit->buffer, count); 239 } 240 } /* ng_hci_send_data */ 241 242 /* 243 * Send data packets to the lower layer. 244 */ 245 246 static int 247 send_data_packets(ng_hci_unit_p unit, int link_type, int limit) 248 { 249 ng_hci_unit_con_p con = NULL, winner = NULL; 250 item_p item = NULL; 251 int min_pending, total_sent, sent, error, v; 252 253 for (total_sent = 0; limit > 0; ) { 254 min_pending = 0x0fffffff; 255 winner = NULL; 256 257 /* 258 * Find the connection that has has data to send 259 * and the smallest number of pending packets 260 */ 261 262 LIST_FOREACH(con, &unit->con_list, next) { 263 if (con->link_type != link_type) 264 continue; 265 if (NG_BT_ITEMQ_LEN(&con->conq) == 0) 266 continue; 267 268 if (con->pending < min_pending) { 269 winner = con; 270 min_pending = con->pending; 271 } 272 } 273 274 if (winner == NULL) 275 break; 276 277 /* 278 * OK, we have a winner now send as much packets as we can 279 * Count the number of packets we have sent and then sync 280 * winner connection queue. 281 */ 282 283 for (sent = 0; limit > 0; limit --, total_sent ++, sent ++) { 284 NG_BT_ITEMQ_DEQUEUE(&winner->conq, item); 285 if (item == NULL) 286 break; 287 288 NG_HCI_INFO( 289 "%s: %s - sending data packet, handle=%d, len=%d\n", 290 __func__, NG_NODE_NAME(unit->node), 291 winner->con_handle, NGI_M(item)->m_pkthdr.len); 292 293 /* Check if driver hook still there */ 294 v = (unit->drv != NULL && NG_HOOK_IS_VALID(unit->drv)); 295 if (!v || (unit->state & NG_HCI_UNIT_READY) != 296 NG_HCI_UNIT_READY) { 297 NG_HCI_ERR( 298 "%s: %s - could not send data. Hook \"%s\" is %svalid, state=%#x\n", 299 __func__, NG_NODE_NAME(unit->node), 300 NG_HCI_HOOK_DRV, ((v)? "" : "not "), 301 unit->state); 302 303 NG_FREE_ITEM(item); 304 error = ENOTCONN; 305 } else { 306 v = NGI_M(item)->m_pkthdr.len; 307 308 /* Give packet to raw hook */ 309 ng_hci_mtap(unit, NGI_M(item)); 310 311 /* ... and forward item to the driver */ 312 NG_FWD_ITEM_HOOK(error, item, unit->drv); 313 } 314 315 if (error != 0) { 316 NG_HCI_ERR( 317 "%s: %s - could not send data packet, handle=%d, error=%d\n", 318 __func__, NG_NODE_NAME(unit->node), 319 winner->con_handle, error); 320 break; 321 } 322 323 winner->pending ++; 324 NG_HCI_STAT_BYTES_SENT(unit->stat, v); 325 } 326 327 /* 328 * Sync connection queue for the winner 329 */ 330 331 sync_con_queue(unit, winner, sent); 332 } 333 334 return (total_sent); 335 } /* send_data_packets */ 336 337 /* 338 * Send flow control messages to the upper layer 339 */ 340 341 static int 342 sync_con_queue(ng_hci_unit_p unit, ng_hci_unit_con_p con, int completed) 343 { 344 hook_p hook = NULL; 345 struct ng_mesg *msg = NULL; 346 ng_hci_sync_con_queue_ep *state = NULL; 347 int error; 348 349 hook = (con->link_type == NG_HCI_LINK_ACL)? unit->acl : unit->sco; 350 if (hook == NULL || NG_HOOK_NOT_VALID(hook)) 351 return (ENOTCONN); 352 353 NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_SYNC_CON_QUEUE, 354 sizeof(*state), M_NOWAIT); 355 if (msg == NULL) 356 return (ENOMEM); 357 358 state = (ng_hci_sync_con_queue_ep *)(msg->data); 359 state->con_handle = con->con_handle; 360 state->completed = completed; 361 362 NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0); 363 364 return (error); 365 } /* sync_con_queue */ 366 367 /* Inquiry result event */ 368 static int 369 inquiry_result(ng_hci_unit_p unit, struct mbuf *event) 370 { 371 ng_hci_inquiry_result_ep *ep = NULL; 372 ng_hci_neighbor_p n = NULL; 373 bdaddr_t bdaddr; 374 int error = 0; 375 376 NG_HCI_M_PULLUP(event, sizeof(*ep)); 377 if (event == NULL) 378 return (ENOBUFS); 379 380 ep = mtod(event, ng_hci_inquiry_result_ep *); 381 m_adj(event, sizeof(*ep)); 382 383 for (; ep->num_responses > 0; ep->num_responses --) { 384 /* Get remote unit address */ 385 m_copydata(event, 0, sizeof(bdaddr), (caddr_t) &bdaddr); 386 m_adj(event, sizeof(bdaddr)); 387 388 /* Lookup entry in the cache */ 389 n = ng_hci_get_neighbor(unit, &bdaddr); 390 if (n == NULL) { 391 /* Create new entry */ 392 n = ng_hci_new_neighbor(unit); 393 if (n == NULL) { 394 error = ENOMEM; 395 break; 396 } 397 } else 398 getmicrotime(&n->updated); 399 400 bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr)); 401 402 /* XXX call m_pullup here? */ 403 404 n->page_scan_rep_mode = *mtod(event, u_int8_t *); 405 m_adj(event, sizeof(u_int8_t)); 406 407 /* page_scan_period_mode */ 408 m_adj(event, sizeof(u_int8_t)); 409 410 n->page_scan_mode = *mtod(event, u_int8_t *); 411 m_adj(event, sizeof(u_int8_t)); 412 413 /* class */ 414 m_adj(event, NG_HCI_CLASS_SIZE); 415 416 /* clock offset */ 417 m_copydata(event, 0, sizeof(n->clock_offset), 418 (caddr_t) &n->clock_offset); 419 n->clock_offset = le16toh(n->clock_offset); 420 } 421 422 NG_FREE_M(event); 423 424 return (error); 425 } /* inquiry_result */ 426 427 /* Connection complete event */ 428 static int 429 con_compl(ng_hci_unit_p unit, struct mbuf *event) 430 { 431 ng_hci_con_compl_ep *ep = NULL; 432 ng_hci_unit_con_p con = NULL; 433 int error = 0; 434 435 NG_HCI_M_PULLUP(event, sizeof(*ep)); 436 if (event == NULL) 437 return (ENOBUFS); 438 439 ep = mtod(event, ng_hci_con_compl_ep *); 440 441 /* 442 * Find the first connection descriptor that matches the following: 443 * 444 * 1) con->link_type == ep->link_type 445 * 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE 446 * 3) con->bdaddr == ep->bdaddr 447 */ 448 449 LIST_FOREACH(con, &unit->con_list, next) 450 if (con->link_type == ep->link_type && 451 con->state == NG_HCI_CON_W4_CONN_COMPLETE && 452 bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 453 break; 454 455 /* 456 * Two possible cases: 457 * 458 * 1) We have found connection descriptor. That means upper layer has 459 * requested this connection via LP_CON_REQ message. In this case 460 * connection must have timeout set. If ng_hci_con_untimeout() fails 461 * then timeout message already went into node's queue. In this case 462 * ignore Connection_Complete event and let timeout deal with it. 463 * 464 * 2) We do not have connection descriptor. That means upper layer 465 * nas not requested this connection or (less likely) we gave up 466 * on this connection (timeout). The most likely scenario is that 467 * we have received Create_Connection/Add_SCO_Connection command 468 * from the RAW hook 469 */ 470 471 if (con == NULL) { 472 if (ep->status != 0) 473 goto out; 474 475 con = ng_hci_new_con(unit, ep->link_type); 476 if (con == NULL) { 477 error = ENOMEM; 478 goto out; 479 } 480 481 bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); 482 } else if ((error = ng_hci_con_untimeout(con)) != 0) 483 goto out; 484 485 /* 486 * Update connection descriptor and send notification 487 * to the upper layers. 488 */ 489 490 con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 491 con->encryption_mode = ep->encryption_mode; 492 493 ng_hci_lp_con_cfm(con, ep->status); 494 495 /* Adjust connection state */ 496 if (ep->status != 0) 497 ng_hci_free_con(con); 498 else { 499 con->state = NG_HCI_CON_OPEN; 500 501 /* 502 * Change link policy for the ACL connections. Enable all 503 * supported link modes. Enable Role switch as well if 504 * device supports it. 505 */ 506 507 if (ep->link_type == NG_HCI_LINK_ACL) { 508 struct __link_policy { 509 ng_hci_cmd_pkt_t hdr; 510 ng_hci_write_link_policy_settings_cp cp; 511 } __attribute__ ((packed)) *lp; 512 struct mbuf *m; 513 514 MGETHDR(m, M_NOWAIT, MT_DATA); 515 if (m != NULL) { 516 m->m_pkthdr.len = m->m_len = sizeof(*lp); 517 lp = mtod(m, struct __link_policy *); 518 519 lp->hdr.type = NG_HCI_CMD_PKT; 520 lp->hdr.opcode = htole16(NG_HCI_OPCODE( 521 NG_HCI_OGF_LINK_POLICY, 522 NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS)); 523 lp->hdr.length = sizeof(lp->cp); 524 525 lp->cp.con_handle = ep->con_handle; 526 527 lp->cp.settings = 0; 528 if ((unit->features[0] & NG_HCI_LMP_SWITCH) && 529 unit->role_switch) 530 lp->cp.settings |= 0x1; 531 if (unit->features[0] & NG_HCI_LMP_HOLD_MODE) 532 lp->cp.settings |= 0x2; 533 if (unit->features[0] & NG_HCI_LMP_SNIFF_MODE) 534 lp->cp.settings |= 0x4; 535 if (unit->features[1] & NG_HCI_LMP_PARK_MODE) 536 lp->cp.settings |= 0x8; 537 538 lp->cp.settings &= unit->link_policy_mask; 539 lp->cp.settings = htole16(lp->cp.settings); 540 541 NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 542 if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 543 ng_hci_send_command(unit); 544 } 545 } 546 } 547 out: 548 NG_FREE_M(event); 549 550 return (error); 551 } /* con_compl */ 552 553 /* Connection request event */ 554 static int 555 con_req(ng_hci_unit_p unit, struct mbuf *event) 556 { 557 ng_hci_con_req_ep *ep = NULL; 558 ng_hci_unit_con_p con = NULL; 559 int error = 0; 560 561 NG_HCI_M_PULLUP(event, sizeof(*ep)); 562 if (event == NULL) 563 return (ENOBUFS); 564 565 ep = mtod(event, ng_hci_con_req_ep *); 566 567 /* 568 * Find the first connection descriptor that matches the following: 569 * 570 * 1) con->link_type == ep->link_type 571 * 572 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP || 573 * con->state == NG_HCI_CON_W4_CONN_COMPL 574 * 575 * 3) con->bdaddr == ep->bdaddr 576 * 577 * Possible cases: 578 * 579 * 1) We do not have connection descriptor. This is simple. Create 580 * new fresh connection descriptor and send notification to the 581 * appropriate upstream hook (based on link_type). 582 * 583 * 2) We found connection handle. This is more complicated. 584 * 585 * 2.1) ACL links 586 * 587 * Since only one ACL link can exist between each pair of 588 * units then we have a race. Our upper layer has requested 589 * an ACL connection to the remote unit, but we did not send 590 * command yet. At the same time the remote unit has requested 591 * an ACL connection from us. In this case we will ignore 592 * Connection_Request event. This probably will cause connect 593 * failure on both units. 594 * 595 * 2.2) SCO links 596 * 597 * The spec on page 45 says : 598 * 599 * "The master can support up to three SCO links to the same 600 * slave or to different slaves. A slave can support up to 601 * three SCO links from the same master, or two SCO links if 602 * the links originate from different masters." 603 * 604 * The only problem is how to handle multiple SCO links between 605 * matster and slave. For now we will assume that multiple SCO 606 * links MUST be opened one after another. 607 */ 608 609 LIST_FOREACH(con, &unit->con_list, next) 610 if (con->link_type == ep->link_type && 611 (con->state == NG_HCI_CON_W4_LP_CON_RSP || 612 con->state == NG_HCI_CON_W4_CONN_COMPLETE) && 613 bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 614 break; 615 616 if (con == NULL) { 617 con = ng_hci_new_con(unit, ep->link_type); 618 if (con != NULL) { 619 bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); 620 621 con->state = NG_HCI_CON_W4_LP_CON_RSP; 622 ng_hci_con_timeout(con); 623 624 error = ng_hci_lp_con_ind(con, ep->uclass); 625 if (error != 0) { 626 ng_hci_con_untimeout(con); 627 ng_hci_free_con(con); 628 } 629 } else 630 error = ENOMEM; 631 } 632 633 NG_FREE_M(event); 634 635 return (error); 636 } /* con_req */ 637 638 /* Disconnect complete event */ 639 static int 640 discon_compl(ng_hci_unit_p unit, struct mbuf *event) 641 { 642 ng_hci_discon_compl_ep *ep = NULL; 643 ng_hci_unit_con_p con = NULL; 644 int error = 0; 645 u_int16_t h; 646 647 NG_HCI_M_PULLUP(event, sizeof(*ep)); 648 if (event == NULL) 649 return (ENOBUFS); 650 651 ep = mtod(event, ng_hci_discon_compl_ep *); 652 653 /* 654 * XXX 655 * Do we have to send notification if ep->status != 0? 656 * For now we will send notification for both ACL and SCO connections 657 * ONLY if ep->status == 0. 658 */ 659 660 if (ep->status == 0) { 661 h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 662 con = ng_hci_con_by_handle(unit, h); 663 if (con != NULL) { 664 error = ng_hci_lp_discon_ind(con, ep->reason); 665 666 /* Remove all timeouts (if any) */ 667 if (con->flags & NG_HCI_CON_TIMEOUT_PENDING) 668 ng_hci_con_untimeout(con); 669 670 ng_hci_free_con(con); 671 } else { 672 NG_HCI_ALERT( 673 "%s: %s - invalid connection handle=%d\n", 674 __func__, NG_NODE_NAME(unit->node), h); 675 error = ENOENT; 676 } 677 } 678 679 NG_FREE_M(event); 680 681 return (error); 682 } /* discon_compl */ 683 684 /* Encryption change event */ 685 static int 686 encryption_change(ng_hci_unit_p unit, struct mbuf *event) 687 { 688 ng_hci_encryption_change_ep *ep = NULL; 689 ng_hci_unit_con_p con = NULL; 690 int error = 0; 691 692 NG_HCI_M_PULLUP(event, sizeof(*ep)); 693 if (event == NULL) 694 return (ENOBUFS); 695 696 ep = mtod(event, ng_hci_encryption_change_ep *); 697 698 if (ep->status == 0) { 699 u_int16_t h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 700 701 con = ng_hci_con_by_handle(unit, h); 702 if (con == NULL) { 703 NG_HCI_ALERT( 704 "%s: %s - invalid connection handle=%d\n", 705 __func__, NG_NODE_NAME(unit->node), h); 706 error = ENOENT; 707 } else if (con->link_type != NG_HCI_LINK_ACL) { 708 NG_HCI_ALERT( 709 "%s: %s - invalid link type=%d\n", 710 __func__, NG_NODE_NAME(unit->node), 711 con->link_type); 712 error = EINVAL; 713 } else if (ep->encryption_enable) 714 /* XXX is that true? */ 715 con->encryption_mode = NG_HCI_ENCRYPTION_MODE_P2P; 716 else 717 con->encryption_mode = NG_HCI_ENCRYPTION_MODE_NONE; 718 } else 719 NG_HCI_ERR( 720 "%s: %s - failed to change encryption mode, status=%d\n", 721 __func__, NG_NODE_NAME(unit->node), ep->status); 722 723 NG_FREE_M(event); 724 725 return (error); 726 } /* encryption_change */ 727 728 /* Read remote feature complete event */ 729 static int 730 read_remote_features_compl(ng_hci_unit_p unit, struct mbuf *event) 731 { 732 ng_hci_read_remote_features_compl_ep *ep = NULL; 733 ng_hci_unit_con_p con = NULL; 734 ng_hci_neighbor_p n = NULL; 735 u_int16_t h; 736 int error = 0; 737 738 NG_HCI_M_PULLUP(event, sizeof(*ep)); 739 if (event == NULL) 740 return (ENOBUFS); 741 742 ep = mtod(event, ng_hci_read_remote_features_compl_ep *); 743 744 if (ep->status == 0) { 745 /* Check if we have this connection handle */ 746 h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 747 con = ng_hci_con_by_handle(unit, h); 748 if (con == NULL) { 749 NG_HCI_ALERT( 750 "%s: %s - invalid connection handle=%d\n", 751 __func__, NG_NODE_NAME(unit->node), h); 752 error = ENOENT; 753 goto out; 754 } 755 756 /* Update cache entry */ 757 n = ng_hci_get_neighbor(unit, &con->bdaddr); 758 if (n == NULL) { 759 n = ng_hci_new_neighbor(unit); 760 if (n == NULL) { 761 error = ENOMEM; 762 goto out; 763 } 764 765 bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr)); 766 } else 767 getmicrotime(&n->updated); 768 769 bcopy(ep->features, n->features, sizeof(n->features)); 770 } else 771 NG_HCI_ERR( 772 "%s: %s - failed to read remote unit features, status=%d\n", 773 __func__, NG_NODE_NAME(unit->node), ep->status); 774 out: 775 NG_FREE_M(event); 776 777 return (error); 778 } /* read_remote_features_compl */ 779 780 /* QoS setup complete event */ 781 static int 782 qos_setup_compl(ng_hci_unit_p unit, struct mbuf *event) 783 { 784 ng_hci_qos_setup_compl_ep *ep = NULL; 785 ng_hci_unit_con_p con = NULL; 786 u_int16_t h; 787 int error = 0; 788 789 NG_HCI_M_PULLUP(event, sizeof(*ep)); 790 if (event == NULL) 791 return (ENOBUFS); 792 793 ep = mtod(event, ng_hci_qos_setup_compl_ep *); 794 795 /* Check if we have this connection handle */ 796 h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 797 con = ng_hci_con_by_handle(unit, h); 798 if (con == NULL) { 799 NG_HCI_ALERT( 800 "%s: %s - invalid connection handle=%d\n", 801 __func__, NG_NODE_NAME(unit->node), h); 802 error = ENOENT; 803 } else if (con->link_type != NG_HCI_LINK_ACL) { 804 NG_HCI_ALERT( 805 "%s: %s - invalid link type=%d, handle=%d\n", 806 __func__, NG_NODE_NAME(unit->node), con->link_type, h); 807 error = EINVAL; 808 } else if (con->state != NG_HCI_CON_OPEN) { 809 NG_HCI_ALERT( 810 "%s: %s - invalid connection state=%d, handle=%d\n", 811 __func__, NG_NODE_NAME(unit->node), 812 con->state, h); 813 error = EINVAL; 814 } else /* Notify upper layer */ 815 error = ng_hci_lp_qos_cfm(con, ep->status); 816 817 NG_FREE_M(event); 818 819 return (error); 820 } /* qos_setup_compl */ 821 822 /* Hardware error event */ 823 static int 824 hardware_error(ng_hci_unit_p unit, struct mbuf *event) 825 { 826 NG_HCI_ALERT( 827 "%s: %s - hardware error %#x\n", 828 __func__, NG_NODE_NAME(unit->node), *mtod(event, u_int8_t *)); 829 830 NG_FREE_M(event); 831 832 return (0); 833 } /* hardware_error */ 834 835 /* Role change event */ 836 static int 837 role_change(ng_hci_unit_p unit, struct mbuf *event) 838 { 839 ng_hci_role_change_ep *ep = NULL; 840 ng_hci_unit_con_p con = NULL; 841 842 NG_HCI_M_PULLUP(event, sizeof(*ep)); 843 if (event == NULL) 844 return (ENOBUFS); 845 846 ep = mtod(event, ng_hci_role_change_ep *); 847 848 if (ep->status == 0) { 849 /* XXX shoud we also change "role" for SCO connections? */ 850 con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL); 851 if (con != NULL) 852 con->role = ep->role; 853 else 854 NG_HCI_ALERT( 855 "%s: %s - ACL connection does not exist, bdaddr=%x:%x:%x:%x:%x:%x\n", 856 __func__, NG_NODE_NAME(unit->node), 857 ep->bdaddr.b[5], ep->bdaddr.b[4], 858 ep->bdaddr.b[3], ep->bdaddr.b[2], 859 ep->bdaddr.b[1], ep->bdaddr.b[0]); 860 } else 861 NG_HCI_ERR( 862 "%s: %s - failed to change role, status=%d, bdaddr=%x:%x:%x:%x:%x:%x\n", 863 __func__, NG_NODE_NAME(unit->node), ep->status, 864 ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], 865 ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); 866 867 NG_FREE_M(event); 868 869 return (0); 870 } /* role_change */ 871 872 /* Number of completed packets event */ 873 static int 874 num_compl_pkts(ng_hci_unit_p unit, struct mbuf *event) 875 { 876 ng_hci_num_compl_pkts_ep *ep = NULL; 877 ng_hci_unit_con_p con = NULL; 878 u_int16_t h, p; 879 880 NG_HCI_M_PULLUP(event, sizeof(*ep)); 881 if (event == NULL) 882 return (ENOBUFS); 883 884 ep = mtod(event, ng_hci_num_compl_pkts_ep *); 885 m_adj(event, sizeof(*ep)); 886 887 for (; ep->num_con_handles > 0; ep->num_con_handles --) { 888 /* Get connection handle */ 889 m_copydata(event, 0, sizeof(h), (caddr_t) &h); 890 m_adj(event, sizeof(h)); 891 h = NG_HCI_CON_HANDLE(le16toh(h)); 892 893 /* Get number of completed packets */ 894 m_copydata(event, 0, sizeof(p), (caddr_t) &p); 895 m_adj(event, sizeof(p)); 896 p = le16toh(p); 897 898 /* Check if we have this connection handle */ 899 con = ng_hci_con_by_handle(unit, h); 900 if (con != NULL) { 901 con->pending -= p; 902 if (con->pending < 0) { 903 NG_HCI_WARN( 904 "%s: %s - pending packet counter is out of sync! " \ 905 "handle=%d, pending=%d, ncp=%d\n", __func__, NG_NODE_NAME(unit->node), 906 con->con_handle, con->pending, p); 907 908 con->pending = 0; 909 } 910 911 /* Update buffer descriptor */ 912 if (con->link_type == NG_HCI_LINK_ACL) 913 NG_HCI_BUFF_ACL_FREE(unit->buffer, p); 914 else 915 NG_HCI_BUFF_SCO_FREE(unit->buffer, p); 916 } else 917 NG_HCI_ALERT( 918 "%s: %s - invalid connection handle=%d\n", 919 __func__, NG_NODE_NAME(unit->node), h); 920 } 921 922 NG_FREE_M(event); 923 924 /* Send more data */ 925 ng_hci_send_data(unit); 926 927 return (0); 928 } /* num_compl_pkts */ 929 930 /* Mode change event */ 931 static int 932 mode_change(ng_hci_unit_p unit, struct mbuf *event) 933 { 934 ng_hci_mode_change_ep *ep = NULL; 935 ng_hci_unit_con_p con = NULL; 936 int error = 0; 937 938 NG_HCI_M_PULLUP(event, sizeof(*ep)); 939 if (event == NULL) 940 return (ENOBUFS); 941 942 ep = mtod(event, ng_hci_mode_change_ep *); 943 944 if (ep->status == 0) { 945 u_int16_t h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 946 947 con = ng_hci_con_by_handle(unit, h); 948 if (con == NULL) { 949 NG_HCI_ALERT( 950 "%s: %s - invalid connection handle=%d\n", 951 __func__, NG_NODE_NAME(unit->node), h); 952 error = ENOENT; 953 } else if (con->link_type != NG_HCI_LINK_ACL) { 954 NG_HCI_ALERT( 955 "%s: %s - invalid link type=%d\n", 956 __func__, NG_NODE_NAME(unit->node), 957 con->link_type); 958 error = EINVAL; 959 } else 960 con->mode = ep->unit_mode; 961 } else 962 NG_HCI_ERR( 963 "%s: %s - failed to change mode, status=%d\n", 964 __func__, NG_NODE_NAME(unit->node), ep->status); 965 966 NG_FREE_M(event); 967 968 return (error); 969 } /* mode_change */ 970 971 /* Data buffer overflow event */ 972 static int 973 data_buffer_overflow(ng_hci_unit_p unit, struct mbuf *event) 974 { 975 NG_HCI_ALERT( 976 "%s: %s - %s data buffer overflow\n", 977 __func__, NG_NODE_NAME(unit->node), 978 (*mtod(event, u_int8_t *) == NG_HCI_LINK_ACL)? "ACL" : "SCO"); 979 980 NG_FREE_M(event); 981 982 return (0); 983 } /* data_buffer_overflow */ 984 985 /* Read clock offset complete event */ 986 static int 987 read_clock_offset_compl(ng_hci_unit_p unit, struct mbuf *event) 988 { 989 ng_hci_read_clock_offset_compl_ep *ep = NULL; 990 ng_hci_unit_con_p con = NULL; 991 ng_hci_neighbor_p n = NULL; 992 int error = 0; 993 994 NG_HCI_M_PULLUP(event, sizeof(*ep)); 995 if (event == NULL) 996 return (ENOBUFS); 997 998 ep = mtod(event, ng_hci_read_clock_offset_compl_ep *); 999 1000 if (ep->status == 0) { 1001 u_int16_t h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 1002 1003 con = ng_hci_con_by_handle(unit, h); 1004 if (con == NULL) { 1005 NG_HCI_ALERT( 1006 "%s: %s - invalid connection handle=%d\n", 1007 __func__, NG_NODE_NAME(unit->node), h); 1008 error = ENOENT; 1009 goto out; 1010 } 1011 1012 /* Update cache entry */ 1013 n = ng_hci_get_neighbor(unit, &con->bdaddr); 1014 if (n == NULL) { 1015 n = ng_hci_new_neighbor(unit); 1016 if (n == NULL) { 1017 error = ENOMEM; 1018 goto out; 1019 } 1020 1021 bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr)); 1022 } else 1023 getmicrotime(&n->updated); 1024 1025 n->clock_offset = le16toh(ep->clock_offset); 1026 } else 1027 NG_HCI_ERR( 1028 "%s: %s - failed to Read Remote Clock Offset, status=%d\n", 1029 __func__, NG_NODE_NAME(unit->node), ep->status); 1030 out: 1031 NG_FREE_M(event); 1032 1033 return (error); 1034 } /* read_clock_offset_compl */ 1035 1036 /* QoS violation event */ 1037 static int 1038 qos_violation(ng_hci_unit_p unit, struct mbuf *event) 1039 { 1040 ng_hci_qos_violation_ep *ep = NULL; 1041 ng_hci_unit_con_p con = NULL; 1042 u_int16_t h; 1043 int error = 0; 1044 1045 NG_HCI_M_PULLUP(event, sizeof(*ep)); 1046 if (event == NULL) 1047 return (ENOBUFS); 1048 1049 ep = mtod(event, ng_hci_qos_violation_ep *); 1050 1051 /* Check if we have this connection handle */ 1052 h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle)); 1053 con = ng_hci_con_by_handle(unit, h); 1054 if (con == NULL) { 1055 NG_HCI_ALERT( 1056 "%s: %s - invalid connection handle=%d\n", 1057 __func__, NG_NODE_NAME(unit->node), h); 1058 error = ENOENT; 1059 } else if (con->link_type != NG_HCI_LINK_ACL) { 1060 NG_HCI_ALERT( 1061 "%s: %s - invalid link type=%d\n", 1062 __func__, NG_NODE_NAME(unit->node), con->link_type); 1063 error = EINVAL; 1064 } else if (con->state != NG_HCI_CON_OPEN) { 1065 NG_HCI_ALERT( 1066 "%s: %s - invalid connection state=%d, handle=%d\n", 1067 __func__, NG_NODE_NAME(unit->node), con->state, h); 1068 error = EINVAL; 1069 } else /* Notify upper layer */ 1070 error = ng_hci_lp_qos_ind(con); 1071 1072 NG_FREE_M(event); 1073 1074 return (error); 1075 } /* qos_violation */ 1076 1077 /* Page scan mode change event */ 1078 static int 1079 page_scan_mode_change(ng_hci_unit_p unit, struct mbuf *event) 1080 { 1081 ng_hci_page_scan_mode_change_ep *ep = NULL; 1082 ng_hci_neighbor_p n = NULL; 1083 int error = 0; 1084 1085 NG_HCI_M_PULLUP(event, sizeof(*ep)); 1086 if (event == NULL) 1087 return (ENOBUFS); 1088 1089 ep = mtod(event, ng_hci_page_scan_mode_change_ep *); 1090 1091 /* Update cache entry */ 1092 n = ng_hci_get_neighbor(unit, &ep->bdaddr); 1093 if (n == NULL) { 1094 n = ng_hci_new_neighbor(unit); 1095 if (n == NULL) { 1096 error = ENOMEM; 1097 goto out; 1098 } 1099 1100 bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr)); 1101 } else 1102 getmicrotime(&n->updated); 1103 1104 n->page_scan_mode = ep->page_scan_mode; 1105 out: 1106 NG_FREE_M(event); 1107 1108 return (error); 1109 } /* page_scan_mode_change */ 1110 1111 /* Page scan repetition mode change event */ 1112 static int 1113 page_scan_rep_mode_change(ng_hci_unit_p unit, struct mbuf *event) 1114 { 1115 ng_hci_page_scan_rep_mode_change_ep *ep = NULL; 1116 ng_hci_neighbor_p n = NULL; 1117 int error = 0; 1118 1119 NG_HCI_M_PULLUP(event, sizeof(*ep)); 1120 if (event == NULL) 1121 return (ENOBUFS); 1122 1123 ep = mtod(event, ng_hci_page_scan_rep_mode_change_ep *); 1124 1125 /* Update cache entry */ 1126 n = ng_hci_get_neighbor(unit, &ep->bdaddr); 1127 if (n == NULL) { 1128 n = ng_hci_new_neighbor(unit); 1129 if (n == NULL) { 1130 error = ENOMEM; 1131 goto out; 1132 } 1133 1134 bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr)); 1135 } else 1136 getmicrotime(&n->updated); 1137 1138 n->page_scan_rep_mode = ep->page_scan_rep_mode; 1139 out: 1140 NG_FREE_M(event); 1141 1142 return (error); 1143 } /* page_scan_rep_mode_change */ 1144 1145