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