1107120Sjulian/* 2107120Sjulian * ng_hci_main.c 3139823Simp */ 4139823Simp 5139823Simp/*- 6107120Sjulian * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com> 7107120Sjulian * All rights reserved. 8107120Sjulian * 9107120Sjulian * Redistribution and use in source and binary forms, with or without 10107120Sjulian * modification, are permitted provided that the following conditions 11107120Sjulian * are met: 12107120Sjulian * 1. Redistributions of source code must retain the above copyright 13107120Sjulian * notice, this list of conditions and the following disclaimer. 14107120Sjulian * 2. Redistributions in binary form must reproduce the above copyright 15107120Sjulian * notice, this list of conditions and the following disclaimer in the 16107120Sjulian * documentation and/or other materials provided with the distribution. 17107120Sjulian * 18107120Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19107120Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20107120Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21107120Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22107120Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23107120Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24107120Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25107120Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26107120Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27107120Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28107120Sjulian * SUCH DAMAGE. 29107120Sjulian * 30114878Sjulian * $Id: ng_hci_main.c,v 1.2 2003/03/18 00:09:36 max Exp $ 31107120Sjulian * $FreeBSD$ 32107120Sjulian */ 33107120Sjulian 34107120Sjulian#include <sys/param.h> 35107120Sjulian#include <sys/systm.h> 36107120Sjulian#include <sys/kernel.h> 37107120Sjulian#include <sys/endian.h> 38107120Sjulian#include <sys/malloc.h> 39107120Sjulian#include <sys/mbuf.h> 40107120Sjulian#include <sys/queue.h> 41107120Sjulian#include <netgraph/ng_message.h> 42107120Sjulian#include <netgraph/netgraph.h> 43107120Sjulian#include <netgraph/ng_parse.h> 44128688Semax#include <netgraph/bluetooth/include/ng_bluetooth.h> 45128688Semax#include <netgraph/bluetooth/include/ng_hci.h> 46128688Semax#include <netgraph/bluetooth/hci/ng_hci_var.h> 47128688Semax#include <netgraph/bluetooth/hci/ng_hci_prse.h> 48128688Semax#include <netgraph/bluetooth/hci/ng_hci_cmds.h> 49128688Semax#include <netgraph/bluetooth/hci/ng_hci_evnt.h> 50128688Semax#include <netgraph/bluetooth/hci/ng_hci_ulpi.h> 51128688Semax#include <netgraph/bluetooth/hci/ng_hci_misc.h> 52107120Sjulian 53107120Sjulian/****************************************************************************** 54107120Sjulian ****************************************************************************** 55107120Sjulian ** This node implements Bluetooth Host Controller Interface (HCI) 56107120Sjulian ****************************************************************************** 57107120Sjulian ******************************************************************************/ 58107120Sjulian 59107120Sjulian/* MALLOC define */ 60107120Sjulian#ifdef NG_SEPARATE_MALLOC 61107120SjulianMALLOC_DEFINE(M_NETGRAPH_HCI, "netgraph_hci", "Netgraph Bluetooth HCI node"); 62107120Sjulian#else 63107120Sjulian#define M_NETGRAPH_HCI M_NETGRAPH 64107120Sjulian#endif /* NG_SEPARATE_MALLOC */ 65107120Sjulian 66107120Sjulian/* Netgraph node methods */ 67107120Sjulianstatic ng_constructor_t ng_hci_constructor; 68107120Sjulianstatic ng_shutdown_t ng_hci_shutdown; 69107120Sjulianstatic ng_newhook_t ng_hci_newhook; 70107120Sjulianstatic ng_connect_t ng_hci_connect; 71107120Sjulianstatic ng_disconnect_t ng_hci_disconnect; 72107120Sjulianstatic ng_rcvmsg_t ng_hci_default_rcvmsg; 73107120Sjulianstatic ng_rcvmsg_t ng_hci_upper_rcvmsg; 74107120Sjulianstatic ng_rcvdata_t ng_hci_drv_rcvdata; 75107120Sjulianstatic ng_rcvdata_t ng_hci_acl_rcvdata; 76107120Sjulianstatic ng_rcvdata_t ng_hci_sco_rcvdata; 77107120Sjulianstatic ng_rcvdata_t ng_hci_raw_rcvdata; 78107120Sjulian 79107120Sjulian/* Netgraph node type descriptor */ 80107120Sjulianstatic struct ng_type typestruct = { 81129823Sjulian .version = NG_ABI_VERSION, 82129823Sjulian .name = NG_HCI_NODE_TYPE, 83129823Sjulian .constructor = ng_hci_constructor, 84129823Sjulian .rcvmsg = ng_hci_default_rcvmsg, 85129823Sjulian .shutdown = ng_hci_shutdown, 86129823Sjulian .newhook = ng_hci_newhook, 87129823Sjulian .connect = ng_hci_connect, 88129823Sjulian .rcvdata = ng_hci_drv_rcvdata, 89129823Sjulian .disconnect = ng_hci_disconnect, 90129823Sjulian .cmdlist = ng_hci_cmdlist, 91107120Sjulian}; 92107120SjulianNETGRAPH_INIT(hci, &typestruct); 93107120SjulianMODULE_VERSION(ng_hci, NG_BLUETOOTH_VERSION); 94107120SjulianMODULE_DEPEND(ng_hci, ng_bluetooth, NG_BLUETOOTH_VERSION, 95107120Sjulian NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); 96107120Sjulian 97107120Sjulian/***************************************************************************** 98107120Sjulian ***************************************************************************** 99107120Sjulian ** Netgraph methods implementation 100107120Sjulian ***************************************************************************** 101107120Sjulian *****************************************************************************/ 102107120Sjulian 103107120Sjulian/* 104107120Sjulian * Create new instance of HCI node (new unit) 105107120Sjulian */ 106107120Sjulian 107107120Sjulianstatic int 108107120Sjulianng_hci_constructor(node_p node) 109107120Sjulian{ 110107120Sjulian ng_hci_unit_p unit = NULL; 111107120Sjulian 112220768Sglebius unit = malloc(sizeof(*unit), M_NETGRAPH_HCI, M_WAITOK | M_ZERO); 113107120Sjulian 114107120Sjulian unit->node = node; 115107120Sjulian unit->debug = NG_HCI_WARN_LEVEL; 116107120Sjulian 117107120Sjulian unit->link_policy_mask = 0xffff; /* Enable all supported modes */ 118107120Sjulian unit->packet_mask = 0xffff; /* Enable all packet types */ 119114878Sjulian unit->role_switch = 1; /* Enable role switch (if device supports it) */ 120107120Sjulian 121107120Sjulian /* 122107120Sjulian * Set default buffer info 123107120Sjulian * 124107120Sjulian * One HCI command 125107120Sjulian * One ACL packet with max. size of 17 bytes (1 DM1 packet) 126107120Sjulian * One SCO packet with max. size of 10 bytes (1 HV1 packet) 127107120Sjulian */ 128107120Sjulian 129107120Sjulian NG_HCI_BUFF_CMD_SET(unit->buffer, 1); 130107120Sjulian NG_HCI_BUFF_ACL_SET(unit->buffer, 1, 17, 1); 131107120Sjulian NG_HCI_BUFF_SCO_SET(unit->buffer, 1, 10, 1); 132107120Sjulian 133107120Sjulian /* Init command queue & command timeout handler */ 134137163Semax ng_callout_init(&unit->cmd_timo); 135107120Sjulian NG_BT_MBUFQ_INIT(&unit->cmdq, NG_HCI_CMD_QUEUE_LEN); 136107120Sjulian 137107120Sjulian /* Init lists */ 138107120Sjulian LIST_INIT(&unit->con_list); 139107120Sjulian LIST_INIT(&unit->neighbors); 140107120Sjulian 141107120Sjulian /* 142107120Sjulian * This node has to be a WRITER because both data and messages 143107120Sjulian * can change node state. 144107120Sjulian */ 145107120Sjulian 146107120Sjulian NG_NODE_FORCE_WRITER(node); 147107120Sjulian NG_NODE_SET_PRIVATE(node, unit); 148107120Sjulian 149107120Sjulian return (0); 150107120Sjulian} /* ng_hci_constructor */ 151107120Sjulian 152107120Sjulian/* 153107120Sjulian * Destroy the node 154107120Sjulian */ 155107120Sjulian 156107120Sjulianstatic int 157107120Sjulianng_hci_shutdown(node_p node) 158107120Sjulian{ 159107120Sjulian ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 160107120Sjulian 161107120Sjulian NG_NODE_SET_PRIVATE(node, NULL); 162107120Sjulian NG_NODE_UNREF(node); 163107120Sjulian 164107120Sjulian unit->node = NULL; 165107120Sjulian ng_hci_unit_clean(unit, 0x16 /* Connection terminated by local host */); 166107120Sjulian 167107120Sjulian NG_BT_MBUFQ_DESTROY(&unit->cmdq); 168107120Sjulian 169107120Sjulian bzero(unit, sizeof(*unit)); 170184205Sdes free(unit, M_NETGRAPH_HCI); 171107120Sjulian 172107120Sjulian return (0); 173107120Sjulian} /* ng_hci_shutdown */ 174107120Sjulian 175107120Sjulian/* 176107120Sjulian * Give our OK for a hook to be added. Unit driver is connected to the driver 177107120Sjulian * (NG_HCI_HOOK_DRV) hook. Upper layer protocols are connected to appropriate 178107120Sjulian * (NG_HCI_HOOK_ACL or NG_HCI_HOOK_SCO) hooks. 179107120Sjulian */ 180107120Sjulian 181107120Sjulianstatic int 182107120Sjulianng_hci_newhook(node_p node, hook_p hook, char const *name) 183107120Sjulian{ 184107120Sjulian ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 185107120Sjulian hook_p *h = NULL; 186107120Sjulian 187107120Sjulian if (strcmp(name, NG_HCI_HOOK_DRV) == 0) 188107120Sjulian h = &unit->drv; 189107120Sjulian else if (strcmp(name, NG_HCI_HOOK_ACL) == 0) 190107120Sjulian h = &unit->acl; 191107120Sjulian else if (strcmp(name, NG_HCI_HOOK_SCO) == 0) 192107120Sjulian h = &unit->sco; 193107120Sjulian else if (strcmp(name, NG_HCI_HOOK_RAW) == 0) 194107120Sjulian h = &unit->raw; 195107120Sjulian else 196107120Sjulian return (EINVAL); 197107120Sjulian 198107120Sjulian if (*h != NULL) 199107120Sjulian return (EISCONN); 200107120Sjulian 201107120Sjulian *h = hook; 202107120Sjulian 203107120Sjulian return (0); 204107120Sjulian} /* ng_hci_newhook */ 205107120Sjulian 206107120Sjulian/* 207107120Sjulian * Give our final OK to connect hook 208107120Sjulian */ 209107120Sjulian 210107120Sjulianstatic int 211107120Sjulianng_hci_connect(hook_p hook) 212107120Sjulian{ 213107120Sjulian ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 214107120Sjulian 215107120Sjulian if (hook != unit->drv) { 216107120Sjulian if (hook == unit->acl) { 217107120Sjulian NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg); 218107120Sjulian NG_HOOK_SET_RCVDATA(hook, ng_hci_acl_rcvdata); 219107120Sjulian } else if (hook == unit->sco) { 220107120Sjulian NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg); 221107120Sjulian NG_HOOK_SET_RCVDATA(hook, ng_hci_sco_rcvdata); 222107120Sjulian } else 223107120Sjulian NG_HOOK_SET_RCVDATA(hook, ng_hci_raw_rcvdata); 224107120Sjulian 225107120Sjulian /* Send delayed notification to the upper layers */ 226107120Sjulian if (hook != unit->raw) 227107120Sjulian ng_send_fn(unit->node, hook, ng_hci_node_is_up, NULL,0); 228107120Sjulian } else 229107120Sjulian unit->state |= NG_HCI_UNIT_CONNECTED; 230107120Sjulian 231107120Sjulian return (0); 232107120Sjulian} /* ng_hci_connect */ 233107120Sjulian 234107120Sjulian/* 235107120Sjulian * Disconnect the hook 236107120Sjulian */ 237107120Sjulian 238107120Sjulianstatic int 239107120Sjulianng_hci_disconnect(hook_p hook) 240107120Sjulian{ 241107120Sjulian ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 242107120Sjulian 243107120Sjulian if (hook == unit->acl) 244107120Sjulian unit->acl = NULL; 245107120Sjulian else if (hook == unit->sco) 246107120Sjulian unit->sco = NULL; 247107120Sjulian else if (hook == unit->raw) 248107120Sjulian unit->raw = NULL; 249107120Sjulian else if (hook == unit->drv) { 250107120Sjulian unit->drv = NULL; 251107120Sjulian 252107120Sjulian /* Connection terminated by local host */ 253107120Sjulian ng_hci_unit_clean(unit, 0x16); 254107120Sjulian unit->state &= ~(NG_HCI_UNIT_CONNECTED|NG_HCI_UNIT_INITED); 255107120Sjulian } else 256107120Sjulian return (EINVAL); 257107120Sjulian 258107120Sjulian /* Shutdown when all hooks are disconnected */ 259107120Sjulian if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && 260107120Sjulian (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 261107120Sjulian ng_rmnode_self(NG_HOOK_NODE(hook)); 262107120Sjulian 263107120Sjulian return (0); 264107120Sjulian} /* ng_hci_disconnect */ 265107120Sjulian 266107120Sjulian/* 267107120Sjulian * Default control message processing routine. Control message could be: 268107120Sjulian * 269107120Sjulian * 1) GENERIC Netgraph messages 270107120Sjulian * 271107120Sjulian * 2) Control message directed to the node itself. 272107120Sjulian */ 273107120Sjulian 274107120Sjulianstatic int 275107120Sjulianng_hci_default_rcvmsg(node_p node, item_p item, hook_p lasthook) 276107120Sjulian{ 277107120Sjulian ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 278107120Sjulian struct ng_mesg *msg = NULL, *rsp = NULL; 279107120Sjulian int error = 0; 280107120Sjulian 281107120Sjulian NGI_GET_MSG(item, msg); 282107120Sjulian 283107120Sjulian switch (msg->header.typecookie) { 284107120Sjulian case NGM_GENERIC_COOKIE: 285107120Sjulian switch (msg->header.cmd) { 286107120Sjulian case NGM_TEXT_STATUS: { 287107120Sjulian int cmd_avail, 288107120Sjulian acl_total, acl_avail, acl_size, 289107120Sjulian sco_total, sco_avail, sco_size; 290107120Sjulian 291107120Sjulian NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); 292107120Sjulian if (rsp == NULL) { 293107120Sjulian error = ENOMEM; 294107120Sjulian break; 295107120Sjulian } 296107120Sjulian 297107120Sjulian NG_HCI_BUFF_CMD_GET(unit->buffer, cmd_avail); 298107120Sjulian 299107120Sjulian NG_HCI_BUFF_ACL_AVAIL(unit->buffer, acl_avail); 300107120Sjulian NG_HCI_BUFF_ACL_TOTAL(unit->buffer, acl_total); 301107120Sjulian NG_HCI_BUFF_ACL_SIZE(unit->buffer, acl_size); 302107120Sjulian 303107120Sjulian NG_HCI_BUFF_SCO_AVAIL(unit->buffer, sco_avail); 304107120Sjulian NG_HCI_BUFF_SCO_TOTAL(unit->buffer, sco_total); 305107120Sjulian NG_HCI_BUFF_SCO_SIZE(unit->buffer, sco_size); 306107120Sjulian 307107120Sjulian snprintf(rsp->data, NG_TEXTRESPONSE, 308107120Sjulian "bdaddr %x:%x:%x:%x:%x:%x\n" \ 309107120Sjulian "Hooks %s %s %s %s\n" \ 310107120Sjulian "State %#x\n" \ 311107120Sjulian "Queue cmd:%d\n" \ 312107120Sjulian "Buffer cmd:%d,acl:%d,%d,%d,sco:%d,%d,%d", 313107120Sjulian unit->bdaddr.b[5], unit->bdaddr.b[4], 314107120Sjulian unit->bdaddr.b[3], unit->bdaddr.b[2], 315107120Sjulian unit->bdaddr.b[1], unit->bdaddr.b[0], 316107120Sjulian (unit->drv != NULL)? NG_HCI_HOOK_DRV : "", 317107120Sjulian (unit->acl != NULL)? NG_HCI_HOOK_ACL : "", 318107120Sjulian (unit->sco != NULL)? NG_HCI_HOOK_SCO : "", 319107120Sjulian (unit->raw != NULL)? NG_HCI_HOOK_RAW : "", 320107120Sjulian unit->state, 321107120Sjulian NG_BT_MBUFQ_LEN(&unit->cmdq), 322107120Sjulian cmd_avail, 323107120Sjulian acl_avail, acl_total, acl_size, 324107120Sjulian sco_avail, sco_total, sco_size); 325107120Sjulian } break; 326107120Sjulian 327107120Sjulian default: 328107120Sjulian error = EINVAL; 329107120Sjulian break; 330107120Sjulian } 331107120Sjulian break; 332107120Sjulian 333107120Sjulian case NGM_HCI_COOKIE: 334107120Sjulian switch (msg->header.cmd) { 335107120Sjulian /* Get current node state */ 336107120Sjulian case NGM_HCI_NODE_GET_STATE: 337107120Sjulian NG_MKRESPONSE(rsp, msg, sizeof(unit->state), M_NOWAIT); 338107120Sjulian if (rsp == NULL) { 339107120Sjulian error = ENOMEM; 340107120Sjulian break; 341107120Sjulian } 342107120Sjulian 343107120Sjulian *((ng_hci_node_state_ep *)(rsp->data)) = unit->state; 344107120Sjulian break; 345107120Sjulian 346107120Sjulian /* Turn INITED bit - node initialized */ 347107120Sjulian case NGM_HCI_NODE_INIT: 348107120Sjulian if (bcmp(&unit->bdaddr, NG_HCI_BDADDR_ANY, 349107120Sjulian sizeof(bdaddr_t)) == 0) { 350107120Sjulian error = ENXIO; 351107120Sjulian break; 352107120Sjulian } 353107120Sjulian 354107120Sjulian unit->state |= NG_HCI_UNIT_INITED; 355107120Sjulian 356107120Sjulian ng_hci_node_is_up(unit->node, unit->acl, NULL, 0); 357107120Sjulian ng_hci_node_is_up(unit->node, unit->sco, NULL, 0); 358107120Sjulian break; 359107120Sjulian 360107120Sjulian /* Get node debug level */ 361107120Sjulian case NGM_HCI_NODE_GET_DEBUG: 362107120Sjulian NG_MKRESPONSE(rsp, msg, sizeof(unit->debug), M_NOWAIT); 363107120Sjulian if (rsp == NULL) { 364107120Sjulian error = ENOMEM; 365107120Sjulian break; 366107120Sjulian } 367107120Sjulian 368107120Sjulian *((ng_hci_node_debug_ep *)(rsp->data)) = unit->debug; 369107120Sjulian break; 370107120Sjulian 371107120Sjulian /* Set node debug level */ 372107120Sjulian case NGM_HCI_NODE_SET_DEBUG: 373107120Sjulian if (msg->header.arglen != sizeof(ng_hci_node_debug_ep)){ 374107120Sjulian error = EMSGSIZE; 375107120Sjulian break; 376107120Sjulian } 377107120Sjulian 378107120Sjulian unit->debug = *((ng_hci_node_debug_ep *)(msg->data)); 379107120Sjulian break; 380107120Sjulian 381107120Sjulian /* Get buffer info */ 382107120Sjulian case NGM_HCI_NODE_GET_BUFFER: { 383107120Sjulian ng_hci_node_buffer_ep *ep = NULL; 384107120Sjulian 385107120Sjulian NG_MKRESPONSE(rsp, msg, sizeof(ng_hci_node_buffer_ep), 386107120Sjulian M_NOWAIT); 387107120Sjulian if (rsp == NULL) { 388107120Sjulian error = ENOMEM; 389107120Sjulian break; 390107120Sjulian } 391107120Sjulian 392107120Sjulian ep = (ng_hci_node_buffer_ep *)(rsp->data); 393107120Sjulian 394107120Sjulian NG_HCI_BUFF_CMD_GET(unit->buffer, ep->cmd_free); 395107120Sjulian NG_HCI_BUFF_ACL_AVAIL(unit->buffer, ep->acl_free); 396107120Sjulian NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->acl_pkts); 397107120Sjulian NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->acl_size); 398107120Sjulian NG_HCI_BUFF_SCO_AVAIL(unit->buffer, ep->sco_free); 399107120Sjulian NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->sco_pkts); 400107120Sjulian NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->sco_size); 401107120Sjulian } break; 402107120Sjulian 403107120Sjulian /* Get BDADDR */ 404107120Sjulian case NGM_HCI_NODE_GET_BDADDR: 405107120Sjulian NG_MKRESPONSE(rsp, msg, sizeof(bdaddr_t), M_NOWAIT); 406107120Sjulian if (rsp == NULL) { 407107120Sjulian error = ENOMEM; 408107120Sjulian break; 409107120Sjulian } 410107120Sjulian 411107120Sjulian bcopy(&unit->bdaddr, rsp->data, sizeof(bdaddr_t)); 412107120Sjulian break; 413107120Sjulian 414107120Sjulian /* Get features */ 415107120Sjulian case NGM_HCI_NODE_GET_FEATURES: 416107120Sjulian NG_MKRESPONSE(rsp,msg,sizeof(unit->features),M_NOWAIT); 417107120Sjulian if (rsp == NULL) { 418107120Sjulian error = ENOMEM; 419107120Sjulian break; 420107120Sjulian } 421107120Sjulian 422107120Sjulian bcopy(&unit->features,rsp->data,sizeof(unit->features)); 423107120Sjulian break; 424107120Sjulian 425107120Sjulian /* Get stat */ 426107120Sjulian case NGM_HCI_NODE_GET_STAT: 427107120Sjulian NG_MKRESPONSE(rsp, msg, sizeof(unit->stat), M_NOWAIT); 428107120Sjulian if (rsp == NULL) { 429107120Sjulian error = ENOMEM; 430107120Sjulian break; 431107120Sjulian } 432107120Sjulian 433107120Sjulian bcopy(&unit->stat, rsp->data, sizeof(unit->stat)); 434107120Sjulian break; 435107120Sjulian 436107120Sjulian /* Reset stat */ 437107120Sjulian case NGM_HCI_NODE_RESET_STAT: 438107120Sjulian NG_HCI_STAT_RESET(unit->stat); 439107120Sjulian break; 440107120Sjulian 441107120Sjulian /* Clean up neighbors list */ 442107120Sjulian case NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE: 443107120Sjulian ng_hci_flush_neighbor_cache(unit); 444107120Sjulian break; 445107120Sjulian 446107120Sjulian /* Get neighbor cache entries */ 447107120Sjulian case NGM_HCI_NODE_GET_NEIGHBOR_CACHE: { 448107120Sjulian ng_hci_neighbor_p n = NULL; 449107120Sjulian ng_hci_node_get_neighbor_cache_ep *e1 = NULL; 450107120Sjulian ng_hci_node_neighbor_cache_entry_ep *e2 = NULL; 451107120Sjulian int s = 0; 452107120Sjulian 453107120Sjulian /* Look for the fresh entries in the cache */ 454107120Sjulian for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) { 455107120Sjulian ng_hci_neighbor_p nn = LIST_NEXT(n, next); 456107120Sjulian 457107120Sjulian if (ng_hci_neighbor_stale(n)) 458107120Sjulian ng_hci_free_neighbor(n); 459107120Sjulian else 460107120Sjulian s ++; 461107120Sjulian 462107120Sjulian n = nn; 463107120Sjulian } 464107120Sjulian if (s > NG_HCI_MAX_NEIGHBOR_NUM) 465107120Sjulian s = NG_HCI_MAX_NEIGHBOR_NUM; 466107120Sjulian 467107120Sjulian /* Prepare response */ 468107120Sjulian NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2), 469107120Sjulian M_NOWAIT); 470107120Sjulian if (rsp == NULL) { 471107120Sjulian error = ENOMEM; 472107120Sjulian break; 473107120Sjulian } 474107120Sjulian 475107120Sjulian e1 = (ng_hci_node_get_neighbor_cache_ep *)(rsp->data); 476107120Sjulian e2 = (ng_hci_node_neighbor_cache_entry_ep *)(e1 + 1); 477107120Sjulian 478107120Sjulian e1->num_entries = s; 479107120Sjulian 480107120Sjulian LIST_FOREACH(n, &unit->neighbors, next) { 481107120Sjulian e2->page_scan_rep_mode = n->page_scan_rep_mode; 482107120Sjulian e2->page_scan_mode = n->page_scan_mode; 483107120Sjulian e2->clock_offset = n->clock_offset; 484107120Sjulian bcopy(&n->bdaddr, &e2->bdaddr, 485107120Sjulian sizeof(e2->bdaddr)); 486107120Sjulian bcopy(&n->features, &e2->features, 487107120Sjulian sizeof(e2->features)); 488107120Sjulian 489107120Sjulian e2 ++; 490107120Sjulian if (--s <= 0) 491107120Sjulian break; 492107120Sjulian } 493107120Sjulian } break; 494107120Sjulian 495107120Sjulian /* Get connection list */ 496107120Sjulian case NGM_HCI_NODE_GET_CON_LIST: { 497107120Sjulian ng_hci_unit_con_p c = NULL; 498107120Sjulian ng_hci_node_con_list_ep *e1 = NULL; 499107120Sjulian ng_hci_node_con_ep *e2 = NULL; 500107120Sjulian int s = 0; 501107120Sjulian 502107120Sjulian /* Count number of connections in the list */ 503107120Sjulian LIST_FOREACH(c, &unit->con_list, next) 504107120Sjulian s ++; 505107120Sjulian if (s > NG_HCI_MAX_CON_NUM) 506107120Sjulian s = NG_HCI_MAX_CON_NUM; 507107120Sjulian 508107120Sjulian /* Prepare response */ 509107120Sjulian NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2), 510107120Sjulian M_NOWAIT); 511107120Sjulian if (rsp == NULL) { 512107120Sjulian error = ENOMEM; 513107120Sjulian break; 514107120Sjulian } 515107120Sjulian 516107120Sjulian e1 = (ng_hci_node_con_list_ep *)(rsp->data); 517107120Sjulian e2 = (ng_hci_node_con_ep *)(e1 + 1); 518107120Sjulian 519107120Sjulian e1->num_connections = s; 520107120Sjulian 521107120Sjulian LIST_FOREACH(c, &unit->con_list, next) { 522107120Sjulian e2->link_type = c->link_type; 523107120Sjulian e2->encryption_mode= c->encryption_mode; 524107120Sjulian e2->mode = c->mode; 525107120Sjulian e2->role = c->role; 526107120Sjulian 527107120Sjulian e2->state = c->state; 528107120Sjulian 529107120Sjulian e2->pending = c->pending; 530107120Sjulian e2->queue_len = NG_BT_ITEMQ_LEN(&c->conq); 531107120Sjulian 532107120Sjulian e2->con_handle = c->con_handle; 533107120Sjulian bcopy(&c->bdaddr, &e2->bdaddr, 534107120Sjulian sizeof(e2->bdaddr)); 535107120Sjulian 536107120Sjulian e2 ++; 537107120Sjulian if (--s <= 0) 538107120Sjulian break; 539107120Sjulian } 540107120Sjulian } break; 541107120Sjulian 542107120Sjulian /* Get link policy settings mask */ 543107120Sjulian case NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK: 544107120Sjulian NG_MKRESPONSE(rsp, msg, sizeof(unit->link_policy_mask), 545107120Sjulian M_NOWAIT); 546107120Sjulian if (rsp == NULL) { 547107120Sjulian error = ENOMEM; 548107120Sjulian break; 549107120Sjulian } 550107120Sjulian 551107120Sjulian *((ng_hci_node_link_policy_mask_ep *)(rsp->data)) = 552107120Sjulian unit->link_policy_mask; 553107120Sjulian break; 554107120Sjulian 555107120Sjulian /* Set link policy settings mask */ 556107120Sjulian case NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK: 557107120Sjulian if (msg->header.arglen != 558107120Sjulian sizeof(ng_hci_node_link_policy_mask_ep)) { 559107120Sjulian error = EMSGSIZE; 560107120Sjulian break; 561107120Sjulian } 562107120Sjulian 563107120Sjulian unit->link_policy_mask = 564107120Sjulian *((ng_hci_node_link_policy_mask_ep *) 565107120Sjulian (msg->data)); 566107120Sjulian break; 567107120Sjulian 568107120Sjulian /* Get packet mask */ 569107120Sjulian case NGM_HCI_NODE_GET_PACKET_MASK: 570107120Sjulian NG_MKRESPONSE(rsp, msg, sizeof(unit->packet_mask), 571107120Sjulian M_NOWAIT); 572107120Sjulian if (rsp == NULL) { 573107120Sjulian error = ENOMEM; 574107120Sjulian break; 575107120Sjulian } 576107120Sjulian 577107120Sjulian *((ng_hci_node_packet_mask_ep *)(rsp->data)) = 578107120Sjulian unit->packet_mask; 579107120Sjulian break; 580107120Sjulian 581107120Sjulian /* Set packet mask */ 582107120Sjulian case NGM_HCI_NODE_SET_PACKET_MASK: 583107120Sjulian if (msg->header.arglen != 584107120Sjulian sizeof(ng_hci_node_packet_mask_ep)) { 585107120Sjulian error = EMSGSIZE; 586107120Sjulian break; 587107120Sjulian } 588107120Sjulian 589107120Sjulian unit->packet_mask = 590107120Sjulian *((ng_hci_node_packet_mask_ep *)(msg->data)); 591107120Sjulian break; 592107120Sjulian 593114878Sjulian /* Get role switch */ 594114878Sjulian case NGM_HCI_NODE_GET_ROLE_SWITCH: 595114878Sjulian NG_MKRESPONSE(rsp, msg, sizeof(unit->role_switch), 596114878Sjulian M_NOWAIT); 597114878Sjulian if (rsp == NULL) { 598114878Sjulian error = ENOMEM; 599114878Sjulian break; 600114878Sjulian } 601114878Sjulian 602114878Sjulian *((ng_hci_node_role_switch_ep *)(rsp->data)) = 603114878Sjulian unit->role_switch; 604114878Sjulian break; 605114878Sjulian 606114878Sjulian /* Set role switch */ 607114878Sjulian case NGM_HCI_NODE_SET_ROLE_SWITCH: 608114878Sjulian if (msg->header.arglen != 609114878Sjulian sizeof(ng_hci_node_role_switch_ep)) { 610114878Sjulian error = EMSGSIZE; 611114878Sjulian break; 612114878Sjulian } 613114878Sjulian 614114878Sjulian unit->role_switch = 615114878Sjulian *((ng_hci_node_role_switch_ep *)(msg->data)); 616114878Sjulian break; 617114878Sjulian 618107120Sjulian default: 619107120Sjulian error = EINVAL; 620107120Sjulian break; 621107120Sjulian } 622107120Sjulian break; 623107120Sjulian 624107120Sjulian default: 625107120Sjulian error = EINVAL; 626107120Sjulian break; 627107120Sjulian } 628107120Sjulian 629107120Sjulian /* NG_RESPOND_MSG should take care of "item" and "rsp" */ 630107120Sjulian NG_RESPOND_MSG(error, node, item, rsp); 631107120Sjulian NG_FREE_MSG(msg); 632107120Sjulian 633107120Sjulian return (error); 634107120Sjulian} /* ng_hci_default_rcvmsg */ 635107120Sjulian 636107120Sjulian/* 637107120Sjulian * Process control message from upstream hooks (ACL and SCO). 638107120Sjulian * Handle LP_xxx messages here, give everything else to default routine. 639107120Sjulian */ 640107120Sjulian 641107120Sjulianstatic int 642107120Sjulianng_hci_upper_rcvmsg(node_p node, item_p item, hook_p lasthook) 643107120Sjulian{ 644107120Sjulian ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 645107120Sjulian int error = 0; 646107120Sjulian 647107120Sjulian switch (NGI_MSG(item)->header.typecookie) { 648107120Sjulian case NGM_HCI_COOKIE: 649107120Sjulian switch (NGI_MSG(item)->header.cmd) { 650107120Sjulian case NGM_HCI_LP_CON_REQ: 651107120Sjulian error = ng_hci_lp_con_req(unit, item, lasthook); 652107120Sjulian break; 653107120Sjulian 654107120Sjulian case NGM_HCI_LP_DISCON_REQ: /* XXX not defined by specs */ 655107120Sjulian error = ng_hci_lp_discon_req(unit, item, lasthook); 656107120Sjulian break; 657107120Sjulian 658107120Sjulian case NGM_HCI_LP_CON_RSP: 659107120Sjulian error = ng_hci_lp_con_rsp(unit, item, lasthook); 660107120Sjulian break; 661107120Sjulian 662107120Sjulian case NGM_HCI_LP_QOS_REQ: 663107120Sjulian error = ng_hci_lp_qos_req(unit, item, lasthook); 664107120Sjulian break; 665107120Sjulian 666107120Sjulian default: 667107120Sjulian error = ng_hci_default_rcvmsg(node, item, lasthook); 668107120Sjulian break; 669107120Sjulian } 670107120Sjulian break; 671107120Sjulian 672107120Sjulian default: 673107120Sjulian error = ng_hci_default_rcvmsg(node, item, lasthook); 674107120Sjulian break; 675107120Sjulian } 676107120Sjulian 677107120Sjulian return (error); 678107120Sjulian} /* ng_hci_upper_rcvmsg */ 679107120Sjulian 680107120Sjulian/* 681107120Sjulian * Process data packet from the driver hook. 682107120Sjulian * We expect HCI events, ACL or SCO data packets. 683107120Sjulian */ 684107120Sjulian 685107120Sjulianstatic int 686107120Sjulianng_hci_drv_rcvdata(hook_p hook, item_p item) 687107120Sjulian{ 688107120Sjulian ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 689107120Sjulian struct mbuf *m = NULL; 690107120Sjulian int error = 0; 691107120Sjulian 692107120Sjulian /* Process packet */ 693107120Sjulian m = NGI_M(item); /* item still has mbuf, just peeking */ 694107120Sjulian m->m_flags |= M_PROTO1; /* mark as incoming packet */ 695107120Sjulian 696107120Sjulian NG_HCI_STAT_BYTES_RECV(unit->stat, m->m_pkthdr.len); 697107120Sjulian 698107120Sjulian /* Give copy packet to RAW hook */ 699107120Sjulian ng_hci_mtap(unit, m); 700107120Sjulian 701107120Sjulian /* 702107120Sjulian * XXX XXX XXX 703107120Sjulian * Lower layer drivers MUST NOT send mbuf chain with empty mbuf at 704107120Sjulian * the beginning of the chain. HCI layer WILL NOT call m_pullup() here. 705107120Sjulian */ 706107120Sjulian 707107120Sjulian switch (*mtod(m, u_int8_t *)) { 708107120Sjulian case NG_HCI_ACL_DATA_PKT: 709107120Sjulian NG_HCI_STAT_ACL_RECV(unit->stat); 710107120Sjulian 711107120Sjulian if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY || 712107120Sjulian unit->acl == NULL || NG_HOOK_NOT_VALID(unit->acl)) { 713107120Sjulian NG_HCI_WARN( 714107120Sjulian"%s: %s - could not forward HCI ACL data packet, state=%#x, hook=%p\n", 715107120Sjulian __func__, NG_NODE_NAME(unit->node), 716107120Sjulian unit->state, unit->acl); 717107120Sjulian 718107120Sjulian NG_FREE_ITEM(item); 719107120Sjulian } else 720107120Sjulian NG_FWD_ITEM_HOOK(error, item, unit->acl); 721107120Sjulian break; 722107120Sjulian 723107120Sjulian case NG_HCI_SCO_DATA_PKT: 724107120Sjulian NG_HCI_STAT_SCO_RECV(unit->stat); 725107120Sjulian 726107120Sjulian if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY || 727107120Sjulian unit->sco == NULL || NG_HOOK_NOT_VALID(unit->sco)) { 728186466Semax NG_HCI_INFO( 729107120Sjulian"%s: %s - could not forward HCI SCO data packet, state=%#x, hook=%p\n", 730107120Sjulian __func__, NG_NODE_NAME(unit->node), 731107120Sjulian unit->state, unit->sco); 732107120Sjulian 733107120Sjulian NG_FREE_ITEM(item); 734107120Sjulian } else 735107120Sjulian NG_FWD_ITEM_HOOK(error, item, unit->sco); 736107120Sjulian break; 737107120Sjulian 738107120Sjulian case NG_HCI_EVENT_PKT: 739107120Sjulian NG_HCI_STAT_EVNT_RECV(unit->stat); 740107120Sjulian 741107120Sjulian /* Detach mbuf, discard item and process event */ 742107120Sjulian NGI_GET_M(item, m); 743107120Sjulian NG_FREE_ITEM(item); 744107120Sjulian 745107120Sjulian error = ng_hci_process_event(unit, m); 746107120Sjulian break; 747107120Sjulian 748107120Sjulian default: 749107120Sjulian NG_HCI_ALERT( 750107120Sjulian"%s: %s - got unknown HCI packet type=%#x\n", 751107120Sjulian __func__, NG_NODE_NAME(unit->node), 752107120Sjulian *mtod(m, u_int8_t *)); 753107120Sjulian 754107120Sjulian NG_FREE_ITEM(item); 755107120Sjulian 756107120Sjulian error = EINVAL; 757107120Sjulian break; 758107120Sjulian } 759107120Sjulian 760107120Sjulian return (error); 761107120Sjulian} /* ng_hci_drv_rcvdata */ 762107120Sjulian 763107120Sjulian/* 764107120Sjulian * Process data packet from ACL upstream hook. 765107120Sjulian * We expect valid HCI ACL data packets. 766107120Sjulian */ 767107120Sjulian 768107120Sjulianstatic int 769107120Sjulianng_hci_acl_rcvdata(hook_p hook, item_p item) 770107120Sjulian{ 771107120Sjulian ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 772107120Sjulian struct mbuf *m = NULL; 773107120Sjulian ng_hci_unit_con_p con = NULL; 774107120Sjulian u_int16_t con_handle; 775107120Sjulian int size, error = 0; 776107120Sjulian 777107120Sjulian NG_HCI_BUFF_ACL_SIZE(unit->buffer, size); 778107120Sjulian 779107120Sjulian /* Check packet */ 780107120Sjulian NGI_GET_M(item, m); 781107120Sjulian 782107120Sjulian if (*mtod(m, u_int8_t *) != NG_HCI_ACL_DATA_PKT) { 783107120Sjulian NG_HCI_ALERT( 784107120Sjulian"%s: %s - invalid HCI data packet type=%#x\n", 785107120Sjulian __func__, NG_NODE_NAME(unit->node), 786107120Sjulian *mtod(m, u_int8_t *)); 787107120Sjulian 788107120Sjulian error = EINVAL; 789107120Sjulian goto drop; 790107120Sjulian } 791107120Sjulian 792107120Sjulian if (m->m_pkthdr.len < sizeof(ng_hci_acldata_pkt_t) || 793107120Sjulian m->m_pkthdr.len > sizeof(ng_hci_acldata_pkt_t) + size) { 794107120Sjulian NG_HCI_ALERT( 795107120Sjulian"%s: %s - invalid HCI ACL data packet, len=%d, mtu=%d\n", 796107120Sjulian __func__, NG_NODE_NAME(unit->node), 797107120Sjulian m->m_pkthdr.len, size); 798107120Sjulian 799107120Sjulian error = EMSGSIZE; 800107120Sjulian goto drop; 801107120Sjulian } 802107120Sjulian 803107120Sjulian NG_HCI_M_PULLUP(m, sizeof(ng_hci_acldata_pkt_t)); 804107120Sjulian if (m == NULL) { 805107120Sjulian error = ENOBUFS; 806107120Sjulian goto drop; 807107120Sjulian } 808107120Sjulian 809107120Sjulian con_handle = NG_HCI_CON_HANDLE(le16toh( 810107120Sjulian mtod(m, ng_hci_acldata_pkt_t *)->con_handle)); 811107120Sjulian size = le16toh(mtod(m, ng_hci_acldata_pkt_t *)->length); 812107120Sjulian 813107120Sjulian if (m->m_pkthdr.len != sizeof(ng_hci_acldata_pkt_t) + size) { 814107120Sjulian NG_HCI_ALERT( 815107120Sjulian"%s: %s - invalid HCI ACL data packet size, len=%d, length=%d\n", 816107120Sjulian __func__, NG_NODE_NAME(unit->node), 817107120Sjulian m->m_pkthdr.len, size); 818107120Sjulian 819107120Sjulian error = EMSGSIZE; 820107120Sjulian goto drop; 821107120Sjulian } 822107120Sjulian 823107120Sjulian /* Queue packet */ 824107120Sjulian con = ng_hci_con_by_handle(unit, con_handle); 825107120Sjulian if (con == NULL) { 826107120Sjulian NG_HCI_ERR( 827107120Sjulian"%s: %s - unexpected HCI ACL data packet. Connection does not exists, " \ 828107120Sjulian"con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), con_handle); 829107120Sjulian 830107120Sjulian error = ENOENT; 831107120Sjulian goto drop; 832107120Sjulian } 833107120Sjulian 834107120Sjulian if (con->link_type != NG_HCI_LINK_ACL) { 835107120Sjulian NG_HCI_ERR( 836107120Sjulian"%s: %s - unexpected HCI ACL data packet. Not ACL link, con_handle=%d, " \ 837107120Sjulian"link_type=%d\n", __func__, NG_NODE_NAME(unit->node), 838107120Sjulian con_handle, con->link_type); 839107120Sjulian 840107120Sjulian error = EINVAL; 841107120Sjulian goto drop; 842107120Sjulian } 843107120Sjulian 844107120Sjulian if (con->state != NG_HCI_CON_OPEN) { 845107120Sjulian NG_HCI_ERR( 846107120Sjulian"%s: %s - unexpected HCI ACL data packet. Invalid connection state=%d, " \ 847107120Sjulian"con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), 848107120Sjulian con->state, con_handle); 849107120Sjulian 850107120Sjulian error = EHOSTDOWN; 851107120Sjulian goto drop; 852107120Sjulian } 853107120Sjulian 854107120Sjulian if (NG_BT_ITEMQ_FULL(&con->conq)) { 855107120Sjulian NG_HCI_ALERT( 856107120Sjulian"%s: %s - dropping HCI ACL data packet, con_handle=%d, len=%d, queue_len=%d\n", 857107120Sjulian __func__, NG_NODE_NAME(unit->node), con_handle, 858107120Sjulian m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq)); 859107120Sjulian 860107120Sjulian NG_BT_ITEMQ_DROP(&con->conq); 861107120Sjulian 862107120Sjulian error = ENOBUFS; 863107120Sjulian goto drop; 864107120Sjulian } 865107120Sjulian 866107120Sjulian /* Queue item and schedule data transfer */ 867107120Sjulian NGI_M(item) = m; 868107120Sjulian NG_BT_ITEMQ_ENQUEUE(&con->conq, item); 869107120Sjulian item = NULL; 870107120Sjulian m = NULL; 871107120Sjulian 872107120Sjulian ng_hci_send_data(unit); 873107120Sjuliandrop: 874107120Sjulian if (item != NULL) 875107120Sjulian NG_FREE_ITEM(item); 876107120Sjulian 877107120Sjulian NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */ 878107120Sjulian 879107120Sjulian return (error); 880107120Sjulian} /* ng_hci_acl_rcvdata */ 881107120Sjulian 882107120Sjulian/* 883107120Sjulian * Process data packet from SCO upstream hook. 884107120Sjulian * We expect valid HCI SCO data packets 885107120Sjulian */ 886107120Sjulian 887107120Sjulianstatic int 888107120Sjulianng_hci_sco_rcvdata(hook_p hook, item_p item) 889107120Sjulian{ 890107120Sjulian ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 891107120Sjulian struct mbuf *m = NULL; 892107120Sjulian ng_hci_unit_con_p con = NULL; 893107120Sjulian u_int16_t con_handle; 894107120Sjulian int size, error = 0; 895107120Sjulian 896107120Sjulian NG_HCI_BUFF_SCO_SIZE(unit->buffer, size); 897107120Sjulian 898107120Sjulian /* Check packet */ 899107120Sjulian NGI_GET_M(item, m); 900107120Sjulian 901107120Sjulian if (*mtod(m, u_int8_t *) != NG_HCI_SCO_DATA_PKT) { 902107120Sjulian NG_HCI_ALERT( 903107120Sjulian"%s: %s - invalid HCI data packet type=%#x\n", 904107120Sjulian __func__, NG_NODE_NAME(unit->node), 905107120Sjulian *mtod(m, u_int8_t *)); 906107120Sjulian 907107120Sjulian error = EINVAL; 908107120Sjulian goto drop; 909107120Sjulian } 910107120Sjulian 911107120Sjulian if (m->m_pkthdr.len < sizeof(ng_hci_scodata_pkt_t) || 912107120Sjulian m->m_pkthdr.len > sizeof(ng_hci_scodata_pkt_t) + size) { 913107120Sjulian NG_HCI_ALERT( 914107120Sjulian"%s: %s - invalid HCI SCO data packet, len=%d, mtu=%d\n", 915107120Sjulian __func__, NG_NODE_NAME(unit->node), 916107120Sjulian m->m_pkthdr.len, size); 917107120Sjulian 918107120Sjulian error = EMSGSIZE; 919107120Sjulian goto drop; 920107120Sjulian } 921107120Sjulian 922107120Sjulian NG_HCI_M_PULLUP(m, sizeof(ng_hci_scodata_pkt_t)); 923107120Sjulian if (m == NULL) { 924107120Sjulian error = ENOBUFS; 925107120Sjulian goto drop; 926107120Sjulian } 927107120Sjulian 928107120Sjulian con_handle = NG_HCI_CON_HANDLE(le16toh( 929107120Sjulian mtod(m, ng_hci_scodata_pkt_t *)->con_handle)); 930107120Sjulian size = mtod(m, ng_hci_scodata_pkt_t *)->length; 931107120Sjulian 932107120Sjulian if (m->m_pkthdr.len != sizeof(ng_hci_scodata_pkt_t) + size) { 933107120Sjulian NG_HCI_ALERT( 934107120Sjulian"%s: %s - invalid HCI SCO data packet size, len=%d, length=%d\n", 935107120Sjulian __func__, NG_NODE_NAME(unit->node), 936107120Sjulian m->m_pkthdr.len, size); 937107120Sjulian 938107120Sjulian error = EMSGSIZE; 939107120Sjulian goto drop; 940107120Sjulian } 941107120Sjulian 942107120Sjulian /* Queue packet */ 943107120Sjulian con = ng_hci_con_by_handle(unit, con_handle); 944107120Sjulian if (con == NULL) { 945107120Sjulian NG_HCI_ERR( 946107120Sjulian"%s: %s - unexpected HCI SCO data packet. Connection does not exists, " \ 947107120Sjulian"con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), con_handle); 948107120Sjulian 949107120Sjulian error = ENOENT; 950107120Sjulian goto drop; 951107120Sjulian } 952107120Sjulian 953107120Sjulian if (con->link_type != NG_HCI_LINK_SCO) { 954107120Sjulian NG_HCI_ERR( 955107120Sjulian"%s: %s - unexpected HCI SCO data packet. Not SCO link, con_handle=%d, " \ 956107120Sjulian"link_type=%d\n", __func__, NG_NODE_NAME(unit->node), 957107120Sjulian con_handle, con->link_type); 958107120Sjulian 959107120Sjulian error = EINVAL; 960107120Sjulian goto drop; 961107120Sjulian } 962107120Sjulian 963107120Sjulian if (con->state != NG_HCI_CON_OPEN) { 964107120Sjulian NG_HCI_ERR( 965107120Sjulian"%s: %s - unexpected HCI SCO data packet. Invalid connection state=%d, " \ 966107120Sjulian"con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), 967107120Sjulian con->state, con_handle); 968107120Sjulian 969107120Sjulian error = EHOSTDOWN; 970107120Sjulian goto drop; 971107120Sjulian } 972107120Sjulian 973107120Sjulian if (NG_BT_ITEMQ_FULL(&con->conq)) { 974107120Sjulian NG_HCI_ALERT( 975107120Sjulian"%s: %s - dropping HCI SCO data packet, con_handle=%d, len=%d, queue_len=%d\n", 976107120Sjulian __func__, NG_NODE_NAME(unit->node), con_handle, 977107120Sjulian m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq)); 978107120Sjulian 979107120Sjulian NG_BT_ITEMQ_DROP(&con->conq); 980107120Sjulian 981107120Sjulian error = ENOBUFS; 982107120Sjulian goto drop; 983107120Sjulian } 984107120Sjulian 985107120Sjulian /* Queue item and schedule data transfer */ 986107120Sjulian NGI_M(item) = m; 987107120Sjulian NG_BT_ITEMQ_ENQUEUE(&con->conq, item); 988107120Sjulian item = NULL; 989107120Sjulian m = NULL; 990107120Sjulian 991107120Sjulian ng_hci_send_data(unit); 992107120Sjuliandrop: 993107120Sjulian if (item != NULL) 994107120Sjulian NG_FREE_ITEM(item); 995107120Sjulian 996107120Sjulian NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */ 997107120Sjulian 998107120Sjulian return (error); 999107120Sjulian} /* ng_hci_sco_rcvdata */ 1000107120Sjulian 1001107120Sjulian/* 1002107120Sjulian * Process data packet from uptream RAW hook. 1003107120Sjulian * We expect valid HCI command packets. 1004107120Sjulian */ 1005107120Sjulian 1006107120Sjulianstatic int 1007107120Sjulianng_hci_raw_rcvdata(hook_p hook, item_p item) 1008107120Sjulian{ 1009107120Sjulian ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1010107120Sjulian struct mbuf *m = NULL; 1011107120Sjulian int error = 0; 1012107120Sjulian 1013107120Sjulian NGI_GET_M(item, m); 1014107120Sjulian NG_FREE_ITEM(item); 1015107120Sjulian 1016107120Sjulian /* Check packet */ 1017107120Sjulian if (*mtod(m, u_int8_t *) != NG_HCI_CMD_PKT) { 1018107120Sjulian NG_HCI_ALERT( 1019107120Sjulian"%s: %s - invalid HCI command packet type=%#x\n", 1020107120Sjulian __func__, NG_NODE_NAME(unit->node), 1021107120Sjulian *mtod(m, u_int8_t *)); 1022107120Sjulian 1023107120Sjulian error = EINVAL; 1024107120Sjulian goto drop; 1025107120Sjulian } 1026107120Sjulian 1027107120Sjulian if (m->m_pkthdr.len < sizeof(ng_hci_cmd_pkt_t)) { 1028107120Sjulian NG_HCI_ALERT( 1029107120Sjulian"%s: %s - invalid HCI command packet len=%d\n", 1030107120Sjulian __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len); 1031107120Sjulian 1032107120Sjulian error = EMSGSIZE; 1033107120Sjulian goto drop; 1034107120Sjulian } 1035107120Sjulian 1036107120Sjulian NG_HCI_M_PULLUP(m, sizeof(ng_hci_cmd_pkt_t)); 1037107120Sjulian if (m == NULL) { 1038107120Sjulian error = ENOBUFS; 1039107120Sjulian goto drop; 1040107120Sjulian } 1041107120Sjulian 1042107120Sjulian if (m->m_pkthdr.len != 1043107120Sjulian mtod(m, ng_hci_cmd_pkt_t *)->length + sizeof(ng_hci_cmd_pkt_t)) { 1044107120Sjulian NG_HCI_ALERT( 1045107120Sjulian"%s: %s - invalid HCI command packet size, len=%d, length=%d\n", 1046107120Sjulian __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len, 1047107120Sjulian mtod(m, ng_hci_cmd_pkt_t *)->length); 1048107120Sjulian 1049107120Sjulian error = EMSGSIZE; 1050107120Sjulian goto drop; 1051107120Sjulian } 1052107120Sjulian 1053107120Sjulian if (mtod(m, ng_hci_cmd_pkt_t *)->opcode == 0) { 1054107120Sjulian NG_HCI_ALERT( 1055107120Sjulian"%s: %s - invalid HCI command opcode\n", 1056107120Sjulian __func__, NG_NODE_NAME(unit->node)); 1057107120Sjulian 1058107120Sjulian error = EINVAL; 1059107120Sjulian goto drop; 1060107120Sjulian } 1061107120Sjulian 1062107120Sjulian if (NG_BT_MBUFQ_FULL(&unit->cmdq)) { 1063107120Sjulian NG_HCI_ALERT( 1064107120Sjulian"%s: %s - dropping HCI command packet, len=%d, queue_len=%d\n", 1065107120Sjulian __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len, 1066107120Sjulian NG_BT_MBUFQ_LEN(&unit->cmdq)); 1067107120Sjulian 1068107120Sjulian NG_BT_MBUFQ_DROP(&unit->cmdq); 1069107120Sjulian 1070107120Sjulian error = ENOBUFS; 1071107120Sjulian goto drop; 1072107120Sjulian } 1073107120Sjulian 1074107120Sjulian /* Queue and send command */ 1075107120Sjulian NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 1076107120Sjulian m = NULL; 1077107120Sjulian 1078107120Sjulian if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 1079107120Sjulian error = ng_hci_send_command(unit); 1080107120Sjuliandrop: 1081107120Sjulian NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */ 1082107120Sjulian 1083107120Sjulian return (error); 1084107120Sjulian} /* ng_hci_raw_rcvdata */ 1085107120Sjulian 1086