ng_hci_misc.c revision 138268
1193579Sraj/* 2193579Sraj * ng_hci_misc.c 3193579Sraj * 4193579Sraj * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com> 5193579Sraj * All rights reserved. 6193579Sraj * 7193579Sraj * Redistribution and use in source and binary forms, with or without 8193579Sraj * modification, are permitted provided that the following conditions 9193579Sraj * are met: 10193579Sraj * 1. Redistributions of source code must retain the above copyright 11193579Sraj * notice, this list of conditions and the following disclaimer. 12193579Sraj * 2. Redistributions in binary form must reproduce the above copyright 13193579Sraj * notice, this list of conditions and the following disclaimer in the 14193579Sraj * documentation and/or other materials provided with the distribution. 15193579Sraj * 16193579Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17193579Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18193579Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19193579Sraj * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20193579Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21193579Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22193579Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23193579Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24193579Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25193579Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26193579Sraj * SUCH DAMAGE. 27193579Sraj * 28193579Sraj * $Id: ng_hci_misc.c,v 1.5 2003/09/08 18:57:51 max Exp $ 29193579Sraj * $FreeBSD: head/sys/netgraph/bluetooth/hci/ng_hci_misc.c 138268 2004-12-01 11:56:32Z glebius $ 30193579Sraj */ 31193579Sraj 32193579Sraj#include <sys/param.h> 33193579Sraj#include <sys/systm.h> 34193579Sraj#include <sys/kernel.h> 35193579Sraj#include <sys/malloc.h> 36193579Sraj#include <sys/mbuf.h> 37193579Sraj#include <sys/queue.h> 38193579Sraj#include <netgraph/ng_message.h> 39193579Sraj#include <netgraph/netgraph.h> 40193579Sraj#include <netgraph/bluetooth/include/ng_bluetooth.h> 41193579Sraj#include <netgraph/bluetooth/include/ng_hci.h> 42193579Sraj#include <netgraph/bluetooth/hci/ng_hci_var.h> 43193579Sraj#include <netgraph/bluetooth/hci/ng_hci_cmds.h> 44193579Sraj#include <netgraph/bluetooth/hci/ng_hci_evnt.h> 45193579Sraj#include <netgraph/bluetooth/hci/ng_hci_ulpi.h> 46193579Sraj#include <netgraph/bluetooth/hci/ng_hci_misc.h> 47193579Sraj 48193579Sraj/****************************************************************************** 49193579Sraj ****************************************************************************** 50193579Sraj ** Utility routines 51193579Sraj ****************************************************************************** 52193579Sraj ******************************************************************************/ 53193579Sraj 54193579Sraj/* 55193579Sraj * Give packet to RAW hook 56193579Sraj * Assumes input mbuf is read only. 57193579Sraj */ 58193579Sraj 59193579Srajvoid 60193579Srajng_hci_mtap(ng_hci_unit_p unit, struct mbuf *m0) 61193579Sraj{ 62193579Sraj struct mbuf *m = NULL; 63193579Sraj int error = 0; 64193579Sraj 65193579Sraj if (unit->raw != NULL && NG_HOOK_IS_VALID(unit->raw)) { 66193579Sraj m = m_dup(m0, M_DONTWAIT); 67193579Sraj if (m != NULL) 68193579Sraj NG_SEND_DATA_ONLY(error, unit->raw, m); 69193579Sraj 70193579Sraj if (error != 0) 71193579Sraj NG_HCI_INFO( 72193579Sraj"%s: %s - Could not forward packet, error=%d\n", 73193579Sraj __func__, NG_NODE_NAME(unit->node), error); 74193579Sraj } 75193579Sraj} /* ng_hci_mtap */ 76193579Sraj 77193579Sraj/* 78193579Sraj * Send notification to the upper layer's 79193579Sraj */ 80193579Sraj 81193579Srajvoid 82193579Srajng_hci_node_is_up(node_p node, hook_p hook, void *arg1, int arg2) 83193579Sraj{ 84193579Sraj ng_hci_unit_p unit = NULL; 85193579Sraj struct ng_mesg *msg = NULL; 86193579Sraj ng_hci_node_up_ep *ep = NULL; 87193579Sraj int error; 88193579Sraj 89193579Sraj if (node == NULL || NG_NODE_NOT_VALID(node) || 90193579Sraj hook == NULL || NG_HOOK_NOT_VALID(hook)) 91193579Sraj return; 92193579Sraj 93193579Sraj unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 94193579Sraj if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) 95193579Sraj return; 96193579Sraj 97193579Sraj if (hook != unit->acl && hook != unit->sco) 98193579Sraj return; 99193579Sraj 100193579Sraj NG_MKMESSAGE(msg,NGM_HCI_COOKIE,NGM_HCI_NODE_UP,sizeof(*ep),M_NOWAIT); 101193579Sraj if (msg != NULL) { 102193579Sraj ep = (ng_hci_node_up_ep *)(msg->data); 103193579Sraj 104193579Sraj if (hook == unit->acl) { 105193579Sraj NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->pkt_size); 106193579Sraj NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->num_pkts); 107193579Sraj } else { 108193579Sraj NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->pkt_size); 109193579Sraj NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->num_pkts); 110193579Sraj } 111193579Sraj 112193579Sraj bcopy(&unit->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); 113193579Sraj 114193579Sraj NG_SEND_MSG_HOOK(error, node, msg, hook, 0); 115193579Sraj } else 116193579Sraj error = ENOMEM; 117193579Sraj 118193579Sraj if (error != 0) 119193579Sraj NG_HCI_INFO( 120193579Sraj"%s: %s - failed to send NODE_UP message to hook \"%s\", error=%d\n", 121193579Sraj __func__, NG_NODE_NAME(unit->node), 122193579Sraj NG_HOOK_NAME(hook), error); 123193579Sraj} /* ng_hci_node_is_up */ 124193579Sraj 125193579Sraj/* 126193579Sraj * Clean unit (helper) 127193579Sraj */ 128193579Sraj 129193579Srajvoid 130193579Srajng_hci_unit_clean(ng_hci_unit_p unit, int reason) 131193579Sraj{ 132193579Sraj int size; 133193579Sraj 134193579Sraj /* Drain command queue */ 135193579Sraj if (unit->state & NG_HCI_UNIT_COMMAND_PENDING) 136193579Sraj ng_hci_command_untimeout(unit); 137193579Sraj 138193579Sraj NG_BT_MBUFQ_DRAIN(&unit->cmdq); 139193579Sraj NG_HCI_BUFF_CMD_SET(unit->buffer, 1); 140193579Sraj 141193579Sraj /* Clean up connection list */ 142193579Sraj while (!LIST_EMPTY(&unit->con_list)) { 143193579Sraj ng_hci_unit_con_p con = LIST_FIRST(&unit->con_list); 144193579Sraj 145193579Sraj /* Remove all timeouts (if any) */ 146193579Sraj if (con->flags & NG_HCI_CON_TIMEOUT_PENDING) 147193579Sraj ng_hci_con_untimeout(con); 148193579Sraj 149193579Sraj /* 150193579Sraj * Notify upper layer protocol and destroy connection 151193579Sraj * descriptor. Do not really care about the result. 152193579Sraj */ 153193579Sraj 154193579Sraj ng_hci_lp_discon_ind(con, reason); 155193579Sraj ng_hci_free_con(con); 156193579Sraj } 157193579Sraj 158193579Sraj NG_HCI_BUFF_ACL_TOTAL(unit->buffer, size); 159193579Sraj NG_HCI_BUFF_ACL_FREE(unit->buffer, size); 160193579Sraj 161193579Sraj NG_HCI_BUFF_SCO_TOTAL(unit->buffer, size); 162193579Sraj NG_HCI_BUFF_SCO_FREE(unit->buffer, size); 163193579Sraj 164193579Sraj /* Clean up neighbors list */ 165193579Sraj ng_hci_flush_neighbor_cache(unit); 166193579Sraj} /* ng_hci_unit_clean */ 167193579Sraj 168193579Sraj/* 169193579Sraj * Allocate and link new unit neighbor cache entry 170193579Sraj */ 171193579Sraj 172193579Srajng_hci_neighbor_p 173193579Srajng_hci_new_neighbor(ng_hci_unit_p unit) 174193579Sraj{ 175193579Sraj ng_hci_neighbor_p n = NULL; 176193579Sraj 177193579Sraj MALLOC(n, ng_hci_neighbor_p, sizeof(*n), M_NETGRAPH_HCI, 178193579Sraj M_NOWAIT | M_ZERO); 179193579Sraj if (n != NULL) { 180193579Sraj getmicrotime(&n->updated); 181193579Sraj LIST_INSERT_HEAD(&unit->neighbors, n, next); 182193579Sraj } 183193579Sraj 184193579Sraj return (n); 185193579Sraj} /* ng_hci_new_neighbor */ 186193579Sraj 187193579Sraj/* 188193579Sraj * Free unit neighbor cache entry 189193579Sraj */ 190193579Sraj 191193579Srajvoid 192193579Srajng_hci_free_neighbor(ng_hci_neighbor_p n) 193193579Sraj{ 194193579Sraj LIST_REMOVE(n, next); 195193579Sraj bzero(n, sizeof(*n)); 196193579Sraj FREE(n, M_NETGRAPH_HCI); 197193579Sraj} /* ng_hci_free_neighbor */ 198193579Sraj 199193579Sraj/* 200193579Sraj * Flush neighbor cache 201193579Sraj */ 202193579Sraj 203193579Srajvoid 204193579Srajng_hci_flush_neighbor_cache(ng_hci_unit_p unit) 205193579Sraj{ 206193579Sraj while (!LIST_EMPTY(&unit->neighbors)) 207193579Sraj ng_hci_free_neighbor(LIST_FIRST(&unit->neighbors)); 208193579Sraj} /* ng_hci_flush_neighbor_cache */ 209193579Sraj 210193579Sraj/* 211193579Sraj * Lookup unit in neighbor cache 212193579Sraj */ 213193579Sraj 214193579Srajng_hci_neighbor_p 215193579Srajng_hci_get_neighbor(ng_hci_unit_p unit, bdaddr_p bdaddr) 216193579Sraj{ 217193579Sraj ng_hci_neighbor_p n = NULL; 218193579Sraj 219193579Sraj for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) { 220193579Sraj ng_hci_neighbor_p nn = LIST_NEXT(n, next); 221193579Sraj 222193579Sraj if (!ng_hci_neighbor_stale(n)) { 223193579Sraj if (bcmp(&n->bdaddr, bdaddr, sizeof(*bdaddr)) == 0) 224193579Sraj break; 225193579Sraj } else 226193579Sraj ng_hci_free_neighbor(n); /* remove old entry */ 227193579Sraj 228193579Sraj n = nn; 229193579Sraj } 230193579Sraj 231193579Sraj return (n); 232193579Sraj} /* ng_hci_get_neighbor */ 233193579Sraj 234193579Sraj/* 235193579Sraj * Check if neighbor entry is stale 236193579Sraj */ 237193579Sraj 238193579Srajint 239193579Srajng_hci_neighbor_stale(ng_hci_neighbor_p n) 240193579Sraj{ 241193579Sraj struct timeval now; 242193579Sraj 243193579Sraj getmicrotime(&now); 244193579Sraj 245193579Sraj return (now.tv_sec - n->updated.tv_sec > bluetooth_hci_max_neighbor_age()); 246193579Sraj} /* ng_hci_neighbor_stale */ 247193579Sraj 248193579Sraj/* 249193579Sraj * Allocate and link new connection descriptor 250193579Sraj */ 251193579Sraj 252193579Srajng_hci_unit_con_p 253193579Srajng_hci_new_con(ng_hci_unit_p unit, int link_type) 254193579Sraj{ 255193579Sraj ng_hci_unit_con_p con = NULL; 256193579Sraj int num_pkts; 257193579Sraj static int fake_con_handle = 0x0f00; 258193579Sraj 259193579Sraj MALLOC(con, ng_hci_unit_con_p, sizeof(*con), M_NETGRAPH_HCI, 260193579Sraj M_NOWAIT | M_ZERO); 261193579Sraj if (con != NULL) { 262193579Sraj con->unit = unit; 263193579Sraj con->state = NG_HCI_CON_CLOSED; 264193579Sraj 265193579Sraj /* 266193579Sraj * XXX 267193579Sraj * 268193579Sraj * Assign fake connection handle to the connection descriptor. 269193579Sraj * Bluetooth specification marks 0x0f00 - 0x0fff connection 270193579Sraj * handles as reserved. We need this fake connection handles 271193579Sraj * for timeouts. Connection handle will be passed as argument 272193579Sraj * to timeout so when timeout happens we can find the right 273193579Sraj * connection descriptor. We can not pass pointers, because 274193579Sraj * timeouts are external (to Netgraph) events and there might 275193579Sraj * be a race when node/hook goes down and timeout event already 276193579Sraj * went into node's queue 277193579Sraj */ 278193579Sraj 279193579Sraj con->con_handle = fake_con_handle ++; 280193579Sraj if (fake_con_handle > 0x0fff) 281193579Sraj fake_con_handle = 0x0f00; 282193579Sraj 283193579Sraj con->link_type = link_type; 284193579Sraj 285193579Sraj if (con->link_type == NG_HCI_LINK_ACL) 286193579Sraj NG_HCI_BUFF_ACL_TOTAL(unit->buffer, num_pkts); 287193579Sraj else 288193579Sraj NG_HCI_BUFF_SCO_TOTAL(unit->buffer, num_pkts); 289193579Sraj 290193579Sraj NG_BT_ITEMQ_INIT(&con->conq, num_pkts); 291193579Sraj 292193579Sraj ng_callout_init(&con->con_timo); 293193579Sraj 294193579Sraj LIST_INSERT_HEAD(&unit->con_list, con, next); 295193579Sraj } 296193579Sraj 297193579Sraj return (con); 298193579Sraj} /* ng_hci_new_con */ 299193579Sraj 300193579Sraj/* 301193579Sraj * Free connection descriptor 302193579Sraj */ 303193579Sraj 304193579Srajvoid 305193579Srajng_hci_free_con(ng_hci_unit_con_p con) 306193579Sraj{ 307193579Sraj LIST_REMOVE(con, next); 308193579Sraj 309193579Sraj /* 310193579Sraj * If we have pending packets then assume that Host Controller has 311193579Sraj * flushed these packets and we can free them too 312193579Sraj */ 313193579Sraj 314193579Sraj if (con->link_type == NG_HCI_LINK_ACL) 315193579Sraj NG_HCI_BUFF_ACL_FREE(con->unit->buffer, con->pending); 316193579Sraj else 317193579Sraj NG_HCI_BUFF_SCO_FREE(con->unit->buffer, con->pending); 318193579Sraj 319193579Sraj NG_BT_ITEMQ_DESTROY(&con->conq); 320193579Sraj 321193579Sraj bzero(con, sizeof(*con)); 322193579Sraj FREE(con, M_NETGRAPH_HCI); 323193579Sraj} /* ng_hci_free_con */ 324193579Sraj 325193579Sraj/* 326193579Sraj * Lookup connection for given unit and connection handle. 327193579Sraj */ 328193579Sraj 329193579Srajng_hci_unit_con_p 330193579Srajng_hci_con_by_handle(ng_hci_unit_p unit, int con_handle) 331193579Sraj{ 332193579Sraj ng_hci_unit_con_p con = NULL; 333193579Sraj 334193579Sraj LIST_FOREACH(con, &unit->con_list, next) 335193579Sraj if (con->con_handle == con_handle) 336193579Sraj break; 337193579Sraj 338193579Sraj return (con); 339193579Sraj} /* ng_hci_con_by_handle */ 340193579Sraj 341193579Sraj/* 342193579Sraj * Lookup connection for given unit, link type and remove unit address 343193579Sraj */ 344193579Sraj 345193579Srajng_hci_unit_con_p 346193579Srajng_hci_con_by_bdaddr(ng_hci_unit_p unit, bdaddr_p bdaddr, int link_type) 347193579Sraj{ 348193579Sraj ng_hci_unit_con_p con = NULL; 349193579Sraj 350193579Sraj LIST_FOREACH(con, &unit->con_list, next) 351193579Sraj if (con->link_type == link_type && 352193579Sraj bcmp(&con->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0) 353193579Sraj break; 354193579Sraj 355193579Sraj return (con); 356193579Sraj} /* ng_hci_con_by_bdaddr */ 357193579Sraj 358193579Sraj/* 359193579Sraj * Set HCI command timeout 360193579Sraj * XXX FIXME: check return code from ng_callout 361193579Sraj */ 362193579Sraj 363193579Srajint 364193579Srajng_hci_command_timeout(ng_hci_unit_p unit) 365193579Sraj{ 366193579Sraj if (unit->state & NG_HCI_UNIT_COMMAND_PENDING) 367193579Sraj panic( 368193579Sraj"%s: %s - Duplicated command timeout!\n", __func__, NG_NODE_NAME(unit->node)); 369193579Sraj 370193579Sraj unit->state |= NG_HCI_UNIT_COMMAND_PENDING; 371193579Sraj ng_callout(&unit->cmd_timo, unit->node, NULL, 372193579Sraj bluetooth_hci_command_timeout(), 373193579Sraj ng_hci_process_command_timeout, NULL, 0); 374193579Sraj 375193579Sraj return (0); 376193579Sraj} /* ng_hci_command_timeout */ 377193579Sraj 378193579Sraj/* 379193579Sraj * Unset HCI command timeout 380193579Sraj */ 381193579Sraj 382193579Srajint 383193579Srajng_hci_command_untimeout(ng_hci_unit_p unit) 384193579Sraj{ 385193579Sraj if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 386193579Sraj panic( 387193579Sraj"%s: %s - No command timeout!\n", __func__, NG_NODE_NAME(unit->node)); 388193579Sraj 389193579Sraj if (ng_uncallout(&unit->cmd_timo, unit->node) == 0) 390193579Sraj return (ETIMEDOUT); 391193579Sraj 392193579Sraj unit->state &= ~NG_HCI_UNIT_COMMAND_PENDING; 393193579Sraj 394193579Sraj return (0); 395193579Sraj} /* ng_hci_command_untimeout */ 396193579Sraj 397193579Sraj/* 398193579Sraj * Set HCI connection timeout 399193579Sraj * XXX FIXME: check return code from ng_callout 400193579Sraj */ 401193579Sraj 402193579Srajint 403193579Srajng_hci_con_timeout(ng_hci_unit_con_p con) 404193579Sraj{ 405193579Sraj if (con->flags & NG_HCI_CON_TIMEOUT_PENDING) 406193579Sraj panic( 407193579Sraj"%s: %s - Duplicated connection timeout!\n", 408193579Sraj __func__, NG_NODE_NAME(con->unit->node)); 409193579Sraj 410193579Sraj con->flags |= NG_HCI_CON_TIMEOUT_PENDING; 411193579Sraj ng_callout(&con->con_timo, con->unit->node, NULL, 412193579Sraj bluetooth_hci_connect_timeout(), 413193579Sraj ng_hci_process_con_timeout, NULL, 414193579Sraj con->con_handle); 415193579Sraj 416193579Sraj return (0); 417193579Sraj} /* ng_hci_con_timeout */ 418193579Sraj 419193579Sraj/* 420193579Sraj * Unset HCI connection timeout 421193579Sraj */ 422193579Sraj 423193579Srajint 424193579Srajng_hci_con_untimeout(ng_hci_unit_con_p con) 425193579Sraj{ 426193579Sraj if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) 427 panic( 428"%s: %s - No connection timeout!\n", __func__, NG_NODE_NAME(con->unit->node)); 429 430 if (ng_uncallout(&con->con_timo, con->unit->node) == 0) 431 return (ETIMEDOUT); 432 433 con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING; 434 435 return (0); 436} /* ng_hci_con_untimeout */ 437 438#if 0 439/* 440 * Convert numeric error code/reason to a string 441 */ 442 443char const * const 444ng_hci_str_error(u_int16_t code) 445{ 446#define LAST_ERROR_CODE ((sizeof(s)/sizeof(s[0]))-1) 447 static char const * const s[] = { 448 /* 0x00 */ "No error", 449 /* 0x01 */ "Unknown HCI command", 450 /* 0x02 */ "No connection", 451 /* 0x03 */ "Hardware failure", 452 /* 0x04 */ "Page timeout", 453 /* 0x05 */ "Authentication failure", 454 /* 0x06 */ "Key missing", 455 /* 0x07 */ "Memory full", 456 /* 0x08 */ "Connection timeout", 457 /* 0x09 */ "Max number of connections", 458 /* 0x0a */ "Max number of SCO connections to a unit", 459 /* 0x0b */ "ACL connection already exists", 460 /* 0x0c */ "Command disallowed", 461 /* 0x0d */ "Host rejected due to limited resources", 462 /* 0x0e */ "Host rejected due to securiity reasons", 463 /* 0x0f */ "Host rejected due to remote unit is a personal unit", 464 /* 0x10 */ "Host timeout", 465 /* 0x11 */ "Unsupported feature or parameter value", 466 /* 0x12 */ "Invalid HCI command parameter", 467 /* 0x13 */ "Other end terminated connection: User ended connection", 468 /* 0x14 */ "Other end terminated connection: Low resources", 469 /* 0x15 */ "Other end terminated connection: About to power off", 470 /* 0x16 */ "Connection terminated by local host", 471 /* 0x17 */ "Repeated attempts", 472 /* 0x18 */ "Pairing not allowed", 473 /* 0x19 */ "Unknown LMP PDU", 474 /* 0x1a */ "Unsupported remote feature", 475 /* 0x1b */ "SCO offset rejected", 476 /* 0x1c */ "SCO interval rejected", 477 /* 0x1d */ "SCO air mode rejected", 478 /* 0x1e */ "Invalid LMP parameters", 479 /* 0x1f */ "Unspecified error", 480 /* 0x20 */ "Unsupported LMP parameter value", 481 /* 0x21 */ "Role change not allowed", 482 /* 0x22 */ "LMP response timeout", 483 /* 0x23 */ "LMP error transaction collision", 484 /* 0x24 */ "LMP PSU not allowed", 485 /* 0x25 */ "Encryption mode not acceptable", 486 /* 0x26 */ "Unit key used", 487 /* 0x27 */ "QoS is not supported", 488 /* 0x28 */ "Instant passed", 489 /* 0x29 */ "Paring with unit key not supported", 490 /* SHOULD ALWAYS BE LAST */ "Unknown error" 491 }; 492 493 return ((code >= LAST_ERROR_CODE)? s[LAST_ERROR_CODE] : s[code]); 494} /* ng_hci_str_error */ 495#endif 496 497