1107120Sjulian/* 2107120Sjulian * ng_hci_ulpi.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 * 30121054Semax * $Id: ng_hci_ulpi.c,v 1.7 2003/09/08 18:57:51 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> 43128688Semax#include <netgraph/bluetooth/include/ng_bluetooth.h> 44128688Semax#include <netgraph/bluetooth/include/ng_hci.h> 45128688Semax#include <netgraph/bluetooth/hci/ng_hci_var.h> 46128688Semax#include <netgraph/bluetooth/hci/ng_hci_cmds.h> 47128688Semax#include <netgraph/bluetooth/hci/ng_hci_evnt.h> 48128688Semax#include <netgraph/bluetooth/hci/ng_hci_ulpi.h> 49128688Semax#include <netgraph/bluetooth/hci/ng_hci_misc.h> 50107120Sjulian 51107120Sjulian/****************************************************************************** 52107120Sjulian ****************************************************************************** 53107120Sjulian ** Upper Layer Protocol Interface module 54107120Sjulian ****************************************************************************** 55107120Sjulian ******************************************************************************/ 56107120Sjulian 57107120Sjulianstatic int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p); 58107120Sjulianstatic int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p); 59107120Sjulian 60107120Sjulian/* 61107120Sjulian * Process LP_ConnectReq event from the upper layer protocol 62107120Sjulian */ 63107120Sjulian 64107120Sjulianint 65107120Sjulianng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 66107120Sjulian{ 67107120Sjulian if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 68107120Sjulian NG_HCI_WARN( 69107120Sjulian"%s: %s - unit is not ready, state=%#x\n", 70107120Sjulian __func__, NG_NODE_NAME(unit->node), unit->state); 71107120Sjulian 72107120Sjulian NG_FREE_ITEM(item); 73107120Sjulian 74107120Sjulian return (ENXIO); 75107120Sjulian } 76107120Sjulian 77107120Sjulian if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) { 78107120Sjulian NG_HCI_ALERT( 79107120Sjulian"%s: %s - invalid LP_ConnectReq message size=%d\n", 80107120Sjulian __func__, NG_NODE_NAME(unit->node), 81107120Sjulian NGI_MSG(item)->header.arglen); 82107120Sjulian 83107120Sjulian NG_FREE_ITEM(item); 84107120Sjulian 85107120Sjulian return (EMSGSIZE); 86107120Sjulian } 87107120Sjulian 88107120Sjulian if (((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type == NG_HCI_LINK_ACL) 89107120Sjulian return (ng_hci_lp_acl_con_req(unit, item, hook)); 90107120Sjulian 91107120Sjulian if (hook != unit->sco) { 92107120Sjulian NG_HCI_WARN( 93107120Sjulian"%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n", 94107120Sjulian __func__, NG_NODE_NAME(unit->node), hook); 95107120Sjulian 96107120Sjulian NG_FREE_ITEM(item); 97107120Sjulian 98107120Sjulian return (EINVAL); 99107120Sjulian } 100107120Sjulian 101107120Sjulian return (ng_hci_lp_sco_con_req(unit, item, hook)); 102107120Sjulian} /* ng_hci_lp_con_req */ 103107120Sjulian 104107120Sjulian/* 105107120Sjulian * Request to create new ACL connection 106107120Sjulian */ 107107120Sjulian 108107120Sjulianstatic int 109107120Sjulianng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 110107120Sjulian{ 111107120Sjulian struct acl_con_req { 112107120Sjulian ng_hci_cmd_pkt_t hdr; 113107120Sjulian ng_hci_create_con_cp cp; 114107120Sjulian } __attribute__ ((packed)) *req = NULL; 115107120Sjulian ng_hci_lp_con_req_ep *ep = NULL; 116107120Sjulian ng_hci_unit_con_p con = NULL; 117107120Sjulian ng_hci_neighbor_t *n = NULL; 118107120Sjulian struct mbuf *m = NULL; 119107120Sjulian int error = 0; 120107120Sjulian 121107120Sjulian ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); 122107120Sjulian 123107120Sjulian /* 124107120Sjulian * Only one ACL connection can exist between each pair of units. 125107120Sjulian * So try to find ACL connection descriptor (in any state) that 126107120Sjulian * has requested remote BD_ADDR. 127107120Sjulian * 128107120Sjulian * Two cases: 129107120Sjulian * 130107120Sjulian * 1) We do not have connection to the remote unit. This is simple. 131107120Sjulian * Just create new connection descriptor and send HCI command to 132107120Sjulian * create new connection. 133107120Sjulian * 134107120Sjulian * 2) We do have connection descriptor. We need to check connection 135107120Sjulian * state: 136107120Sjulian * 137114878Sjulian * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of 138107120Sjulian * accepting connection from the remote unit. This is a race 139107120Sjulian * condition. We will ignore this message. 140107120Sjulian * 141114878Sjulian * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already 142107120Sjulian * requested connection or we just accepted it. In any case 143107120Sjulian * all we need to do here is set appropriate notification bit 144107120Sjulian * and wait. 145107120Sjulian * 146114878Sjulian * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back 147107120Sjulian * and let upper layer know that we have connection already. 148107120Sjulian */ 149107120Sjulian 150107120Sjulian con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL); 151107120Sjulian if (con != NULL) { 152107120Sjulian switch (con->state) { 153107120Sjulian case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */ 154107120Sjulian error = EALREADY; 155107120Sjulian break; 156107120Sjulian 157107120Sjulian case NG_HCI_CON_W4_CONN_COMPLETE: 158107120Sjulian if (hook == unit->acl) 159107120Sjulian con->flags |= NG_HCI_CON_NOTIFY_ACL; 160107120Sjulian else 161107120Sjulian con->flags |= NG_HCI_CON_NOTIFY_SCO; 162107120Sjulian break; 163107120Sjulian 164107120Sjulian case NG_HCI_CON_OPEN: { 165107120Sjulian struct ng_mesg *msg = NULL; 166107120Sjulian ng_hci_lp_con_cfm_ep *cfm = NULL; 167107120Sjulian 168107120Sjulian if (hook != NULL && NG_HOOK_IS_VALID(hook)) { 169107120Sjulian NGI_GET_MSG(item, msg); 170107120Sjulian NG_FREE_MSG(msg); 171107120Sjulian 172107120Sjulian NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 173107120Sjulian NGM_HCI_LP_CON_CFM, sizeof(*cfm), 174107120Sjulian M_NOWAIT); 175107120Sjulian if (msg != NULL) { 176107120Sjulian cfm = (ng_hci_lp_con_cfm_ep *)msg->data; 177107120Sjulian cfm->status = 0; 178107120Sjulian cfm->link_type = con->link_type; 179107120Sjulian cfm->con_handle = con->con_handle; 180107120Sjulian bcopy(&con->bdaddr, &cfm->bdaddr, 181107120Sjulian sizeof(cfm->bdaddr)); 182107120Sjulian 183107120Sjulian /* 184107120Sjulian * This will forward item back to 185107120Sjulian * sender and set item to NULL 186107120Sjulian */ 187107120Sjulian 188107120Sjulian _NGI_MSG(item) = msg; 189107120Sjulian NG_FWD_ITEM_HOOK(error, item, hook); 190107120Sjulian } else 191107120Sjulian error = ENOMEM; 192107120Sjulian } else 193107120Sjulian NG_HCI_INFO( 194107120Sjulian"%s: %s - Source hook is not valid, hook=%p\n", 195107120Sjulian __func__, NG_NODE_NAME(unit->node), 196107120Sjulian hook); 197107120Sjulian } break; 198107120Sjulian 199107120Sjulian default: 200121054Semax panic( 201121054Semax"%s: %s - Invalid connection state=%d\n", 202121054Semax __func__, NG_NODE_NAME(unit->node), con->state); 203107120Sjulian break; 204107120Sjulian } 205107120Sjulian 206107120Sjulian goto out; 207107120Sjulian } 208107120Sjulian 209107120Sjulian /* 210107120Sjulian * If we got here then we need to create new ACL connection descriptor 211107120Sjulian * and submit HCI command. First create new connection desriptor, set 212107120Sjulian * bdaddr and notification flags. 213107120Sjulian */ 214107120Sjulian 215107120Sjulian con = ng_hci_new_con(unit, NG_HCI_LINK_ACL); 216107120Sjulian if (con == NULL) { 217107120Sjulian error = ENOMEM; 218107120Sjulian goto out; 219107120Sjulian } 220107120Sjulian 221107120Sjulian bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); 222107120Sjulian 223107120Sjulian /* 224107120Sjulian * Create HCI command 225107120Sjulian */ 226107120Sjulian 227243882Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 228107120Sjulian if (m == NULL) { 229107120Sjulian ng_hci_free_con(con); 230107120Sjulian error = ENOBUFS; 231107120Sjulian goto out; 232107120Sjulian } 233107120Sjulian 234107120Sjulian m->m_pkthdr.len = m->m_len = sizeof(*req); 235107120Sjulian req = mtod(m, struct acl_con_req *); 236107120Sjulian req->hdr.type = NG_HCI_CMD_PKT; 237107120Sjulian req->hdr.length = sizeof(req->cp); 238107120Sjulian req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 239107120Sjulian NG_HCI_OCF_CREATE_CON)); 240107120Sjulian 241107120Sjulian bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr)); 242107120Sjulian 243107120Sjulian req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1); 244107120Sjulian if (unit->features[0] & NG_HCI_LMP_3SLOT) 245107120Sjulian req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3); 246107120Sjulian if (unit->features[0] & NG_HCI_LMP_5SLOT) 247107120Sjulian req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5); 248107120Sjulian 249107120Sjulian req->cp.pkt_type &= unit->packet_mask; 250114878Sjulian if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1| 251114878Sjulian NG_HCI_PKT_DM3|NG_HCI_PKT_DH3| 252114878Sjulian NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0) 253107120Sjulian req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1); 254107120Sjulian 255107120Sjulian req->cp.pkt_type = htole16(req->cp.pkt_type); 256107120Sjulian 257114878Sjulian if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch) 258107120Sjulian req->cp.accept_role_switch = 1; 259107120Sjulian else 260107120Sjulian req->cp.accept_role_switch = 0; 261107120Sjulian 262107120Sjulian /* 263107120Sjulian * We may speed up connect by specifying valid parameters. 264107120Sjulian * So check the neighbor cache. 265107120Sjulian */ 266107120Sjulian 267107120Sjulian n = ng_hci_get_neighbor(unit, &ep->bdaddr); 268107120Sjulian if (n == NULL) { 269107120Sjulian req->cp.page_scan_rep_mode = 0; 270107120Sjulian req->cp.page_scan_mode = 0; 271107120Sjulian req->cp.clock_offset = 0; 272107120Sjulian } else { 273107120Sjulian req->cp.page_scan_rep_mode = n->page_scan_rep_mode; 274107120Sjulian req->cp.page_scan_mode = n->page_scan_mode; 275107120Sjulian req->cp.clock_offset = htole16(n->clock_offset); 276107120Sjulian } 277107120Sjulian 278107120Sjulian /* 279107120Sjulian * Adust connection state 280107120Sjulian */ 281107120Sjulian 282107120Sjulian if (hook == unit->acl) 283107120Sjulian con->flags |= NG_HCI_CON_NOTIFY_ACL; 284107120Sjulian else 285107120Sjulian con->flags |= NG_HCI_CON_NOTIFY_SCO; 286107120Sjulian 287107120Sjulian con->state = NG_HCI_CON_W4_CONN_COMPLETE; 288107120Sjulian ng_hci_con_timeout(con); 289107120Sjulian 290107120Sjulian /* 291107120Sjulian * Queue and send HCI command 292107120Sjulian */ 293107120Sjulian 294107120Sjulian NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 295107120Sjulian if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 296107120Sjulian error = ng_hci_send_command(unit); 297107120Sjulianout: 298107120Sjulian if (item != NULL) 299107120Sjulian NG_FREE_ITEM(item); 300107120Sjulian 301107120Sjulian return (error); 302107120Sjulian} /* ng_hci_lp_acl_con_req */ 303107120Sjulian 304107120Sjulian/* 305107120Sjulian * Request to create new SCO connection 306107120Sjulian */ 307107120Sjulian 308107120Sjulianstatic int 309107120Sjulianng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 310107120Sjulian{ 311107120Sjulian struct sco_con_req { 312107120Sjulian ng_hci_cmd_pkt_t hdr; 313107120Sjulian ng_hci_add_sco_con_cp cp; 314107120Sjulian } __attribute__ ((packed)) *req = NULL; 315107120Sjulian ng_hci_lp_con_req_ep *ep = NULL; 316107120Sjulian ng_hci_unit_con_p acl_con = NULL, sco_con = NULL; 317107120Sjulian struct mbuf *m = NULL; 318107120Sjulian int error = 0; 319107120Sjulian 320107120Sjulian ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); 321107120Sjulian 322107120Sjulian /* 323107120Sjulian * SCO connection without ACL link 324107120Sjulian * 325107120Sjulian * If upper layer requests SCO connection and there is no open ACL 326107120Sjulian * connection to the desired remote unit, we will reject the request. 327107120Sjulian */ 328107120Sjulian 329107120Sjulian LIST_FOREACH(acl_con, &unit->con_list, next) 330107120Sjulian if (acl_con->link_type == NG_HCI_LINK_ACL && 331107120Sjulian acl_con->state == NG_HCI_CON_OPEN && 332107120Sjulian bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 333107120Sjulian break; 334107120Sjulian 335107120Sjulian if (acl_con == NULL) { 336107120Sjulian NG_HCI_INFO( 337107120Sjulian"%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n", 338107120Sjulian __func__, NG_NODE_NAME(unit->node), 339107120Sjulian ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], 340107120Sjulian ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); 341107120Sjulian 342107120Sjulian error = ENOENT; 343107120Sjulian goto out; 344107120Sjulian } 345107120Sjulian 346107120Sjulian /* 347107120Sjulian * Multiple SCO connections can exist between the same pair of units. 348107120Sjulian * We assume that multiple SCO connections have to be opened one after 349107120Sjulian * another. 350107120Sjulian * 351107120Sjulian * Try to find SCO connection descriptor that matches the following: 352107120Sjulian * 353107120Sjulian * 1) sco_con->link_type == NG_HCI_LINK_SCO 354107120Sjulian * 355107120Sjulian * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP || 356107120Sjulian * sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE 357107120Sjulian * 358107120Sjulian * 3) sco_con->bdaddr == ep->bdaddr 359107120Sjulian * 360107120Sjulian * Two cases: 361107120Sjulian * 362107120Sjulian * 1) We do not have connection descriptor. This is simple. Just 363107120Sjulian * create new connection and submit Add_SCO_Connection command. 364107120Sjulian * 365107120Sjulian * 2) We do have connection descriptor. We need to check the state. 366107120Sjulian * 367107120Sjulian * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting 368107120Sjulian * connection from the remote unit. This is a race condition and 369107120Sjulian * we will ignore the request. 370107120Sjulian * 371107120Sjulian * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested 372107120Sjulian * connection or we just accepted it. 373107120Sjulian */ 374107120Sjulian 375107120Sjulian LIST_FOREACH(sco_con, &unit->con_list, next) 376107120Sjulian if (sco_con->link_type == NG_HCI_LINK_SCO && 377107120Sjulian (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP || 378107120Sjulian sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) && 379107120Sjulian bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 380107120Sjulian break; 381107120Sjulian 382107120Sjulian if (sco_con != NULL) { 383107120Sjulian switch (sco_con->state) { 384107120Sjulian case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */ 385107120Sjulian error = EALREADY; 386107120Sjulian break; 387107120Sjulian 388107120Sjulian case NG_HCI_CON_W4_CONN_COMPLETE: 389107120Sjulian sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; 390107120Sjulian break; 391107120Sjulian 392107120Sjulian default: 393121054Semax panic( 394250576Seadler"%s: %s - Invalid connection state=%d\n", 395107120Sjulian __func__, NG_NODE_NAME(unit->node), 396121054Semax sco_con->state); 397107120Sjulian break; 398107120Sjulian } 399107120Sjulian 400107120Sjulian goto out; 401107120Sjulian } 402107120Sjulian 403107120Sjulian /* 404107120Sjulian * If we got here then we need to create new SCO connection descriptor 405107120Sjulian * and submit HCI command. 406107120Sjulian */ 407107120Sjulian 408107120Sjulian sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO); 409107120Sjulian if (sco_con == NULL) { 410107120Sjulian error = ENOMEM; 411107120Sjulian goto out; 412107120Sjulian } 413107120Sjulian 414107120Sjulian bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr)); 415107120Sjulian 416107120Sjulian /* 417107120Sjulian * Create HCI command 418107120Sjulian */ 419107120Sjulian 420243882Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 421107120Sjulian if (m == NULL) { 422107120Sjulian ng_hci_free_con(sco_con); 423107120Sjulian error = ENOBUFS; 424107120Sjulian goto out; 425107120Sjulian } 426107120Sjulian 427107120Sjulian m->m_pkthdr.len = m->m_len = sizeof(*req); 428107120Sjulian req = mtod(m, struct sco_con_req *); 429107120Sjulian req->hdr.type = NG_HCI_CMD_PKT; 430107120Sjulian req->hdr.length = sizeof(req->cp); 431107120Sjulian req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 432107120Sjulian NG_HCI_OCF_ADD_SCO_CON)); 433107120Sjulian 434107120Sjulian req->cp.con_handle = htole16(acl_con->con_handle); 435107120Sjulian 436107120Sjulian req->cp.pkt_type = NG_HCI_PKT_HV1; 437107120Sjulian if (unit->features[1] & NG_HCI_LMP_HV2_PKT) 438107120Sjulian req->cp.pkt_type |= NG_HCI_PKT_HV2; 439107120Sjulian if (unit->features[1] & NG_HCI_LMP_HV3_PKT) 440107120Sjulian req->cp.pkt_type |= NG_HCI_PKT_HV3; 441107120Sjulian 442107120Sjulian req->cp.pkt_type &= unit->packet_mask; 443114878Sjulian if ((req->cp.pkt_type & (NG_HCI_PKT_HV1| 444114878Sjulian NG_HCI_PKT_HV2| 445114878Sjulian NG_HCI_PKT_HV3)) == 0) 446107120Sjulian req->cp.pkt_type = NG_HCI_PKT_HV1; 447107120Sjulian 448107120Sjulian req->cp.pkt_type = htole16(req->cp.pkt_type); 449107120Sjulian 450107120Sjulian /* 451107120Sjulian * Adust connection state 452107120Sjulian */ 453107120Sjulian 454107120Sjulian sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; 455107120Sjulian 456107120Sjulian sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE; 457107120Sjulian ng_hci_con_timeout(sco_con); 458107120Sjulian 459107120Sjulian /* 460107120Sjulian * Queue and send HCI command 461107120Sjulian */ 462107120Sjulian 463107120Sjulian NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 464107120Sjulian if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 465107120Sjulian error = ng_hci_send_command(unit); 466107120Sjulianout: 467107120Sjulian NG_FREE_ITEM(item); 468107120Sjulian 469107120Sjulian return (error); 470107120Sjulian} /* ng_hci_lp_sco_con_req */ 471107120Sjulian 472107120Sjulian/* 473107120Sjulian * Process LP_DisconnectReq event from the upper layer protocol 474107120Sjulian */ 475107120Sjulian 476107120Sjulianint 477107120Sjulianng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook) 478107120Sjulian{ 479107120Sjulian struct discon_req { 480107120Sjulian ng_hci_cmd_pkt_t hdr; 481107120Sjulian ng_hci_discon_cp cp; 482107120Sjulian } __attribute__ ((packed)) *req = NULL; 483107120Sjulian ng_hci_lp_discon_req_ep *ep = NULL; 484107120Sjulian ng_hci_unit_con_p con = NULL; 485107120Sjulian struct mbuf *m = NULL; 486107120Sjulian int error = 0; 487107120Sjulian 488107120Sjulian /* Check if unit is ready */ 489107120Sjulian if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 490107120Sjulian NG_HCI_WARN( 491107120Sjulian"%s: %s - unit is not ready, state=%#x\n", 492107120Sjulian __func__, NG_NODE_NAME(unit->node), unit->state); 493107120Sjulian 494107120Sjulian error = ENXIO; 495107120Sjulian goto out; 496107120Sjulian } 497107120Sjulian 498107120Sjulian if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 499107120Sjulian NG_HCI_ALERT( 500107120Sjulian"%s: %s - invalid LP_DisconnectReq message size=%d\n", 501107120Sjulian __func__, NG_NODE_NAME(unit->node), 502107120Sjulian NGI_MSG(item)->header.arglen); 503107120Sjulian 504107120Sjulian error = EMSGSIZE; 505107120Sjulian goto out; 506107120Sjulian } 507107120Sjulian 508107120Sjulian ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data); 509107120Sjulian 510107120Sjulian con = ng_hci_con_by_handle(unit, ep->con_handle); 511107120Sjulian if (con == NULL) { 512107120Sjulian NG_HCI_ERR( 513107120Sjulian"%s: %s - invalid connection handle=%d\n", 514107120Sjulian __func__, NG_NODE_NAME(unit->node), ep->con_handle); 515107120Sjulian 516107120Sjulian error = ENOENT; 517107120Sjulian goto out; 518107120Sjulian } 519107120Sjulian 520107120Sjulian if (con->state != NG_HCI_CON_OPEN) { 521107120Sjulian NG_HCI_ERR( 522107120Sjulian"%s: %s - invalid connection state=%d, handle=%d\n", 523107120Sjulian __func__, NG_NODE_NAME(unit->node), con->state, 524107120Sjulian ep->con_handle); 525107120Sjulian 526107120Sjulian error = EINVAL; 527107120Sjulian goto out; 528107120Sjulian } 529107120Sjulian 530107120Sjulian /* 531107120Sjulian * Create HCI command 532107120Sjulian */ 533107120Sjulian 534243882Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 535107120Sjulian if (m == NULL) { 536107120Sjulian error = ENOBUFS; 537107120Sjulian goto out; 538107120Sjulian } 539107120Sjulian 540107120Sjulian m->m_pkthdr.len = m->m_len = sizeof(*req); 541107120Sjulian req = mtod(m, struct discon_req *); 542107120Sjulian req->hdr.type = NG_HCI_CMD_PKT; 543107120Sjulian req->hdr.length = sizeof(req->cp); 544107120Sjulian req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 545107120Sjulian NG_HCI_OCF_DISCON)); 546107120Sjulian 547107120Sjulian req->cp.con_handle = htole16(ep->con_handle); 548107120Sjulian req->cp.reason = ep->reason; 549107120Sjulian 550107120Sjulian /* 551107120Sjulian * Queue and send HCI command 552107120Sjulian */ 553107120Sjulian 554107120Sjulian NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 555107120Sjulian if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 556107120Sjulian error = ng_hci_send_command(unit); 557107120Sjulianout: 558107120Sjulian NG_FREE_ITEM(item); 559107120Sjulian 560107120Sjulian return (error); 561107120Sjulian} /* ng_hci_lp_discon_req */ 562107120Sjulian 563107120Sjulian/* 564107120Sjulian * Send LP_ConnectCfm event to the upper layer protocol 565107120Sjulian */ 566107120Sjulian 567107120Sjulianint 568107120Sjulianng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status) 569107120Sjulian{ 570107120Sjulian ng_hci_unit_p unit = con->unit; 571107120Sjulian struct ng_mesg *msg = NULL; 572107120Sjulian ng_hci_lp_con_cfm_ep *ep = NULL; 573107120Sjulian int error; 574107120Sjulian 575107120Sjulian /* 576107120Sjulian * Check who wants to be notified. For ACL links both ACL and SCO 577107120Sjulian * upstream hooks will be notified (if required). For SCO links 578107120Sjulian * only SCO upstream hook will receive notification 579107120Sjulian */ 580107120Sjulian 581107120Sjulian if (con->link_type == NG_HCI_LINK_ACL && 582107120Sjulian con->flags & NG_HCI_CON_NOTIFY_ACL) { 583107120Sjulian if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 584107120Sjulian NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, 585107120Sjulian sizeof(*ep), M_NOWAIT); 586107120Sjulian if (msg != NULL) { 587107120Sjulian ep = (ng_hci_lp_con_cfm_ep *) msg->data; 588107120Sjulian ep->status = status; 589107120Sjulian ep->link_type = con->link_type; 590107120Sjulian ep->con_handle = con->con_handle; 591107120Sjulian bcopy(&con->bdaddr, &ep->bdaddr, 592107120Sjulian sizeof(ep->bdaddr)); 593107120Sjulian 594107120Sjulian NG_SEND_MSG_HOOK(error, unit->node, msg, 595128076Semax unit->acl, 0); 596107120Sjulian } 597107120Sjulian } else 598107120Sjulian NG_HCI_INFO( 599107120Sjulian"%s: %s - ACL hook not valid, hook=%p\n", 600107120Sjulian __func__, NG_NODE_NAME(unit->node), unit->acl); 601107120Sjulian 602107120Sjulian con->flags &= ~NG_HCI_CON_NOTIFY_ACL; 603107120Sjulian } 604107120Sjulian 605107120Sjulian if (con->flags & NG_HCI_CON_NOTIFY_SCO) { 606107120Sjulian if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 607107120Sjulian NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, 608107120Sjulian sizeof(*ep), M_NOWAIT); 609107120Sjulian if (msg != NULL) { 610107120Sjulian ep = (ng_hci_lp_con_cfm_ep *) msg->data; 611107120Sjulian ep->status = status; 612107120Sjulian ep->link_type = con->link_type; 613107120Sjulian ep->con_handle = con->con_handle; 614107120Sjulian bcopy(&con->bdaddr, &ep->bdaddr, 615107120Sjulian sizeof(ep->bdaddr)); 616107120Sjulian 617107120Sjulian NG_SEND_MSG_HOOK(error, unit->node, msg, 618128076Semax unit->sco, 0); 619107120Sjulian } 620107120Sjulian } else 621107120Sjulian NG_HCI_INFO( 622107120Sjulian"%s: %s - SCO hook not valid, hook=%p\n", 623107120Sjulian __func__, NG_NODE_NAME(unit->node), unit->acl); 624107120Sjulian 625107120Sjulian con->flags &= ~NG_HCI_CON_NOTIFY_SCO; 626107120Sjulian } 627107120Sjulian 628107120Sjulian return (0); 629107120Sjulian} /* ng_hci_lp_con_cfm */ 630107120Sjulian 631107120Sjulian/* 632107120Sjulian * Send LP_ConnectInd event to the upper layer protocol 633107120Sjulian */ 634107120Sjulian 635107120Sjulianint 636107120Sjulianng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass) 637107120Sjulian{ 638107120Sjulian ng_hci_unit_p unit = con->unit; 639107120Sjulian struct ng_mesg *msg = NULL; 640107120Sjulian ng_hci_lp_con_ind_ep *ep = NULL; 641107120Sjulian hook_p hook = NULL; 642107120Sjulian int error = 0; 643107120Sjulian 644107120Sjulian /* 645107120Sjulian * Connection_Request event is generated for specific link type. 646107120Sjulian * Use link_type to select upstream hook. 647107120Sjulian */ 648107120Sjulian 649107120Sjulian if (con->link_type == NG_HCI_LINK_ACL) 650107120Sjulian hook = unit->acl; 651107120Sjulian else 652107120Sjulian hook = unit->sco; 653107120Sjulian 654107120Sjulian if (hook != NULL && NG_HOOK_IS_VALID(hook)) { 655107120Sjulian NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND, 656107120Sjulian sizeof(*ep), M_NOWAIT); 657107120Sjulian if (msg == NULL) 658107120Sjulian return (ENOMEM); 659107120Sjulian 660107120Sjulian ep = (ng_hci_lp_con_ind_ep *)(msg->data); 661107120Sjulian ep->link_type = con->link_type; 662107120Sjulian bcopy(uclass, ep->uclass, sizeof(ep->uclass)); 663107120Sjulian bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); 664107120Sjulian 665128076Semax NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0); 666107120Sjulian } else { 667107120Sjulian NG_HCI_WARN( 668107120Sjulian"%s: %s - Upstream hook is not connected or not valid, hook=%p\n", 669107120Sjulian __func__, NG_NODE_NAME(unit->node), hook); 670107120Sjulian 671107120Sjulian error = ENOTCONN; 672107120Sjulian } 673107120Sjulian 674107120Sjulian return (error); 675107120Sjulian} /* ng_hci_lp_con_ind */ 676107120Sjulian 677107120Sjulian/* 678107120Sjulian * Process LP_ConnectRsp event from the upper layer protocol 679107120Sjulian */ 680107120Sjulian 681107120Sjulianint 682107120Sjulianng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook) 683107120Sjulian{ 684107120Sjulian struct con_rsp_req { 685107120Sjulian ng_hci_cmd_pkt_t hdr; 686107120Sjulian union { 687107120Sjulian ng_hci_accept_con_cp acc; 688107120Sjulian ng_hci_reject_con_cp rej; 689107120Sjulian } __attribute__ ((packed)) cp; 690107120Sjulian } __attribute__ ((packed)) *req = NULL; 691107120Sjulian ng_hci_lp_con_rsp_ep *ep = NULL; 692107120Sjulian ng_hci_unit_con_p con = NULL; 693107120Sjulian struct mbuf *m = NULL; 694107120Sjulian int error = 0; 695107120Sjulian 696107120Sjulian /* Check if unit is ready */ 697107120Sjulian if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 698107120Sjulian NG_HCI_WARN( 699107120Sjulian"%s: %s - unit is not ready, state=%#x\n", 700107120Sjulian __func__, NG_NODE_NAME(unit->node), unit->state); 701107120Sjulian 702107120Sjulian error = ENXIO; 703107120Sjulian goto out; 704107120Sjulian } 705107120Sjulian 706107120Sjulian if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 707107120Sjulian NG_HCI_ALERT( 708107120Sjulian"%s: %s - invalid LP_ConnectRsp message size=%d\n", 709107120Sjulian __func__, NG_NODE_NAME(unit->node), 710107120Sjulian NGI_MSG(item)->header.arglen); 711107120Sjulian 712107120Sjulian error = EMSGSIZE; 713107120Sjulian goto out; 714107120Sjulian } 715107120Sjulian 716107120Sjulian ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data); 717107120Sjulian 718107120Sjulian /* 719107120Sjulian * Here we have to deal with race. Upper layers might send conflicting 720107120Sjulian * requests. One might send Accept and other Reject. We will not try 721107120Sjulian * to solve all the problems, so first request will always win. 722107120Sjulian * 723107120Sjulian * Try to find connection that matches the following: 724107120Sjulian * 725107120Sjulian * 1) con->link_type == ep->link_type 726107120Sjulian * 727107120Sjulian * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP || 728107120Sjulian * con->state == NG_HCI_CON_W4_CONN_COMPLETE 729107120Sjulian * 730107120Sjulian * 3) con->bdaddr == ep->bdaddr 731107120Sjulian * 732107120Sjulian * Two cases: 733107120Sjulian * 734107120Sjulian * 1) We do not have connection descriptor. Could be bogus request or 735107120Sjulian * we have rejected connection already. 736107120Sjulian * 737107120Sjulian * 2) We do have connection descriptor. Then we need to check state: 738107120Sjulian * 739107120Sjulian * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested 740107120Sjulian * connection and it is a first response from the upper layer. 741107120Sjulian * if "status == 0" (Accept) then we will send Accept_Connection 742107120Sjulian * command and change connection state to W4_CONN_COMPLETE, else 743107120Sjulian * send reject and delete connection. 744107120Sjulian * 745107120Sjulian * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted 746107120Sjulian * connection. If "status == 0" we just need to link request 747107120Sjulian * and wait, else ignore Reject request. 748107120Sjulian */ 749107120Sjulian 750107120Sjulian LIST_FOREACH(con, &unit->con_list, next) 751107120Sjulian if (con->link_type == ep->link_type && 752107120Sjulian (con->state == NG_HCI_CON_W4_LP_CON_RSP || 753107120Sjulian con->state == NG_HCI_CON_W4_CONN_COMPLETE) && 754107120Sjulian bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 755107120Sjulian break; 756107120Sjulian 757107120Sjulian if (con == NULL) { 758107120Sjulian /* Reject for non-existing connection is fine */ 759107120Sjulian error = (ep->status == 0)? ENOENT : 0; 760107120Sjulian goto out; 761107120Sjulian } 762107120Sjulian 763107120Sjulian /* 764121054Semax * Remove connection timeout and check connection state. 765121054Semax * Note: if ng_hci_con_untimeout() fails (returns non-zero value) then 766121054Semax * timeout already happened and event went into node's queue. 767107120Sjulian */ 768107120Sjulian 769121054Semax if ((error = ng_hci_con_untimeout(con)) != 0) 770121054Semax goto out; 771107120Sjulian 772107120Sjulian switch (con->state) { 773107120Sjulian case NG_HCI_CON_W4_LP_CON_RSP: 774107120Sjulian 775107120Sjulian /* 776107120Sjulian * Create HCI command 777107120Sjulian */ 778107120Sjulian 779243882Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 780107120Sjulian if (m == NULL) { 781107120Sjulian error = ENOBUFS; 782107120Sjulian goto out; 783107120Sjulian } 784107120Sjulian 785107120Sjulian req = mtod(m, struct con_rsp_req *); 786107120Sjulian req->hdr.type = NG_HCI_CMD_PKT; 787107120Sjulian 788107120Sjulian if (ep->status == 0) { 789107120Sjulian req->hdr.length = sizeof(req->cp.acc); 790107120Sjulian req->hdr.opcode = htole16(NG_HCI_OPCODE( 791107120Sjulian NG_HCI_OGF_LINK_CONTROL, 792107120Sjulian NG_HCI_OCF_ACCEPT_CON)); 793107120Sjulian 794107120Sjulian bcopy(&ep->bdaddr, &req->cp.acc.bdaddr, 795107120Sjulian sizeof(req->cp.acc.bdaddr)); 796107120Sjulian 797107120Sjulian /* 798107120Sjulian * We are accepting connection, so if we support role 799114878Sjulian * switch and role switch was enabled then set role to 800114878Sjulian * NG_HCI_ROLE_MASTER and let LM peform role switch. 801114878Sjulian * Otherwise we remain slave. In this case LM WILL NOT 802114878Sjulian * perform role switch. 803107120Sjulian */ 804107120Sjulian 805114878Sjulian if ((unit->features[0] & NG_HCI_LMP_SWITCH) && 806114878Sjulian unit->role_switch) 807107120Sjulian req->cp.acc.role = NG_HCI_ROLE_MASTER; 808107120Sjulian else 809107120Sjulian req->cp.acc.role = NG_HCI_ROLE_SLAVE; 810107120Sjulian 811107120Sjulian /* 812107120Sjulian * Adjust connection state 813107120Sjulian */ 814107120Sjulian 815107120Sjulian if (hook == unit->acl) 816107120Sjulian con->flags |= NG_HCI_CON_NOTIFY_ACL; 817107120Sjulian else 818107120Sjulian con->flags |= NG_HCI_CON_NOTIFY_SCO; 819107120Sjulian 820107120Sjulian con->state = NG_HCI_CON_W4_CONN_COMPLETE; 821107120Sjulian ng_hci_con_timeout(con); 822107120Sjulian } else { 823107120Sjulian req->hdr.length = sizeof(req->cp.rej); 824107120Sjulian req->hdr.opcode = htole16(NG_HCI_OPCODE( 825107120Sjulian NG_HCI_OGF_LINK_CONTROL, 826107120Sjulian NG_HCI_OCF_REJECT_CON)); 827107120Sjulian 828107120Sjulian bcopy(&ep->bdaddr, &req->cp.rej.bdaddr, 829107120Sjulian sizeof(req->cp.rej.bdaddr)); 830107120Sjulian 831107120Sjulian req->cp.rej.reason = ep->status; 832107120Sjulian 833107120Sjulian /* 834107120Sjulian * Free connection descritor 835107120Sjulian * Item will be deleted just before return. 836107120Sjulian */ 837107120Sjulian 838107120Sjulian ng_hci_free_con(con); 839107120Sjulian } 840107120Sjulian 841107120Sjulian m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length; 842107120Sjulian 843107120Sjulian /* Queue and send HCI command */ 844107120Sjulian NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 845107120Sjulian if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 846107120Sjulian error = ng_hci_send_command(unit); 847107120Sjulian break; 848107120Sjulian 849107120Sjulian case NG_HCI_CON_W4_CONN_COMPLETE: 850107120Sjulian if (ep->status == 0) { 851107120Sjulian if (hook == unit->acl) 852107120Sjulian con->flags |= NG_HCI_CON_NOTIFY_ACL; 853107120Sjulian else 854107120Sjulian con->flags |= NG_HCI_CON_NOTIFY_SCO; 855107120Sjulian } else 856107120Sjulian error = EPERM; 857107120Sjulian break; 858107120Sjulian 859107120Sjulian default: 860121054Semax panic( 861121054Semax"%s: %s - Invalid connection state=%d\n", 862121054Semax __func__, NG_NODE_NAME(unit->node), con->state); 863107120Sjulian break; 864107120Sjulian } 865107120Sjulianout: 866107120Sjulian NG_FREE_ITEM(item); 867107120Sjulian 868107120Sjulian return (error); 869107120Sjulian} /* ng_hci_lp_con_rsp */ 870107120Sjulian 871107120Sjulian/* 872107120Sjulian * Send LP_DisconnectInd to the upper layer protocol 873107120Sjulian */ 874107120Sjulian 875107120Sjulianint 876107120Sjulianng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason) 877107120Sjulian{ 878107120Sjulian ng_hci_unit_p unit = con->unit; 879107120Sjulian struct ng_mesg *msg = NULL; 880107120Sjulian ng_hci_lp_discon_ind_ep *ep = NULL; 881107120Sjulian int error = 0; 882107120Sjulian 883107120Sjulian /* 884107120Sjulian * Disconnect_Complete event is generated for specific connection 885107120Sjulian * handle. For ACL connection handles both ACL and SCO upstream 886107120Sjulian * hooks will receive notification. For SCO connection handles 887107120Sjulian * only SCO upstream hook will receive notification. 888107120Sjulian */ 889107120Sjulian 890107120Sjulian if (con->link_type == NG_HCI_LINK_ACL) { 891107120Sjulian if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 892107120Sjulian NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 893107120Sjulian NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT); 894107120Sjulian if (msg == NULL) 895107120Sjulian return (ENOMEM); 896107120Sjulian 897107120Sjulian ep = (ng_hci_lp_discon_ind_ep *) msg->data; 898107120Sjulian ep->reason = reason; 899107120Sjulian ep->link_type = con->link_type; 900107120Sjulian ep->con_handle = con->con_handle; 901107120Sjulian 902128076Semax NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,0); 903107120Sjulian } else 904107120Sjulian NG_HCI_INFO( 905107120Sjulian"%s: %s - ACL hook is not connected or not valid, hook=%p\n", 906107120Sjulian __func__, NG_NODE_NAME(unit->node), unit->acl); 907107120Sjulian } 908107120Sjulian 909107120Sjulian if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 910107120Sjulian NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND, 911107120Sjulian sizeof(*ep), M_NOWAIT); 912107120Sjulian if (msg == NULL) 913107120Sjulian return (ENOMEM); 914107120Sjulian 915107120Sjulian ep = (ng_hci_lp_discon_ind_ep *) msg->data; 916107120Sjulian ep->reason = reason; 917107120Sjulian ep->link_type = con->link_type; 918107120Sjulian ep->con_handle = con->con_handle; 919107120Sjulian 920128076Semax NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0); 921107120Sjulian } else 922107120Sjulian NG_HCI_INFO( 923107120Sjulian"%s: %s - SCO hook is not connected or not valid, hook=%p\n", 924107120Sjulian __func__, NG_NODE_NAME(unit->node), unit->sco); 925107120Sjulian 926107120Sjulian return (0); 927107120Sjulian} /* ng_hci_lp_discon_ind */ 928107120Sjulian 929107120Sjulian/* 930107120Sjulian * Process LP_QoSReq action from the upper layer protocol 931107120Sjulian */ 932107120Sjulian 933107120Sjulianint 934107120Sjulianng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook) 935107120Sjulian{ 936107120Sjulian struct qos_setup_req { 937107120Sjulian ng_hci_cmd_pkt_t hdr; 938107120Sjulian ng_hci_qos_setup_cp cp; 939107120Sjulian } __attribute__ ((packed)) *req = NULL; 940107120Sjulian ng_hci_lp_qos_req_ep *ep = NULL; 941107120Sjulian ng_hci_unit_con_p con = NULL; 942107120Sjulian struct mbuf *m = NULL; 943107120Sjulian int error = 0; 944107120Sjulian 945107120Sjulian /* Check if unit is ready */ 946107120Sjulian if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 947107120Sjulian NG_HCI_WARN( 948107120Sjulian"%s: %s - unit is not ready, state=%#x\n", 949107120Sjulian __func__, NG_NODE_NAME(unit->node), unit->state); 950107120Sjulian 951107120Sjulian error = ENXIO; 952107120Sjulian goto out; 953107120Sjulian } 954107120Sjulian 955107120Sjulian if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 956107120Sjulian NG_HCI_ALERT( 957107120Sjulian"%s: %s - invalid LP_QoSSetupReq message size=%d\n", 958107120Sjulian __func__, NG_NODE_NAME(unit->node), 959107120Sjulian NGI_MSG(item)->header.arglen); 960107120Sjulian 961107120Sjulian error = EMSGSIZE; 962107120Sjulian goto out; 963107120Sjulian } 964107120Sjulian 965107120Sjulian ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data); 966107120Sjulian 967107120Sjulian con = ng_hci_con_by_handle(unit, ep->con_handle); 968107120Sjulian if (con == NULL) { 969107120Sjulian NG_HCI_ERR( 970107120Sjulian"%s: %s - invalid connection handle=%d\n", 971107120Sjulian __func__, NG_NODE_NAME(unit->node), ep->con_handle); 972107120Sjulian 973107120Sjulian error = EINVAL; 974107120Sjulian goto out; 975107120Sjulian } 976107120Sjulian 977107120Sjulian if (con->link_type != NG_HCI_LINK_ACL) { 978107120Sjulian NG_HCI_ERR("%s: %s - invalid link type=%d\n", 979107120Sjulian __func__, NG_NODE_NAME(unit->node), con->link_type); 980107120Sjulian 981107120Sjulian error = EINVAL; 982107120Sjulian goto out; 983107120Sjulian } 984107120Sjulian 985107120Sjulian if (con->state != NG_HCI_CON_OPEN) { 986107120Sjulian NG_HCI_ERR( 987107120Sjulian"%s: %s - invalid connection state=%d, handle=%d\n", 988107120Sjulian __func__, NG_NODE_NAME(unit->node), con->state, 989107120Sjulian con->con_handle); 990107120Sjulian 991107120Sjulian error = EINVAL; 992107120Sjulian goto out; 993107120Sjulian } 994107120Sjulian 995107120Sjulian /* 996107120Sjulian * Create HCI command 997107120Sjulian */ 998107120Sjulian 999243882Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 1000107120Sjulian if (m == NULL) { 1001107120Sjulian error = ENOBUFS; 1002107120Sjulian goto out; 1003107120Sjulian } 1004107120Sjulian 1005107120Sjulian m->m_pkthdr.len = m->m_len = sizeof(*req); 1006107120Sjulian req = mtod(m, struct qos_setup_req *); 1007107120Sjulian req->hdr.type = NG_HCI_CMD_PKT; 1008107120Sjulian req->hdr.length = sizeof(req->cp); 1009107120Sjulian req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, 1010107120Sjulian NG_HCI_OCF_QOS_SETUP)); 1011107120Sjulian 1012107120Sjulian req->cp.con_handle = htole16(ep->con_handle); 1013107120Sjulian req->cp.flags = ep->flags; 1014107120Sjulian req->cp.service_type = ep->service_type; 1015107120Sjulian req->cp.token_rate = htole32(ep->token_rate); 1016107120Sjulian req->cp.peak_bandwidth = htole32(ep->peak_bandwidth); 1017107120Sjulian req->cp.latency = htole32(ep->latency); 1018107120Sjulian req->cp.delay_variation = htole32(ep->delay_variation); 1019107120Sjulian 1020107120Sjulian /* 1021107120Sjulian * Adjust connection state 1022107120Sjulian */ 1023107120Sjulian 1024107120Sjulian if (hook == unit->acl) 1025107120Sjulian con->flags |= NG_HCI_CON_NOTIFY_ACL; 1026107120Sjulian else 1027107120Sjulian con->flags |= NG_HCI_CON_NOTIFY_SCO; 1028107120Sjulian 1029107120Sjulian /* 1030107120Sjulian * Queue and send HCI command 1031107120Sjulian */ 1032107120Sjulian 1033107120Sjulian NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 1034107120Sjulian if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 1035107120Sjulian error = ng_hci_send_command(unit); 1036107120Sjulianout: 1037107120Sjulian NG_FREE_ITEM(item); 1038107120Sjulian 1039107120Sjulian return (error); 1040107120Sjulian} /* ng_hci_lp_qos_req */ 1041107120Sjulian 1042107120Sjulian/* 1043107120Sjulian * Send LP_QoSCfm event to the upper layer protocol 1044107120Sjulian */ 1045107120Sjulian 1046107120Sjulianint 1047107120Sjulianng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status) 1048107120Sjulian{ 1049107120Sjulian ng_hci_unit_p unit = con->unit; 1050107120Sjulian struct ng_mesg *msg = NULL; 1051107120Sjulian ng_hci_lp_qos_cfm_ep *ep = NULL; 1052107120Sjulian int error; 1053107120Sjulian 1054107120Sjulian if (con->flags & NG_HCI_CON_NOTIFY_ACL) { 1055107120Sjulian if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 1056107120Sjulian NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, 1057107120Sjulian sizeof(*ep), M_NOWAIT); 1058107120Sjulian if (msg != NULL) { 1059107120Sjulian ep = (ng_hci_lp_qos_cfm_ep *) msg->data; 1060107120Sjulian ep->status = status; 1061107120Sjulian ep->con_handle = con->con_handle; 1062107120Sjulian 1063107120Sjulian NG_SEND_MSG_HOOK(error, unit->node, msg, 1064128076Semax unit->acl, 0); 1065107120Sjulian } 1066107120Sjulian } else 1067107120Sjulian NG_HCI_INFO( 1068107120Sjulian"%s: %s - ACL hook not valid, hook=%p\n", 1069107120Sjulian __func__, NG_NODE_NAME(unit->node), unit->acl); 1070107120Sjulian 1071107120Sjulian con->flags &= ~NG_HCI_CON_NOTIFY_ACL; 1072107120Sjulian } 1073107120Sjulian 1074107120Sjulian if (con->flags & NG_HCI_CON_NOTIFY_SCO) { 1075107120Sjulian if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 1076107120Sjulian NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, 1077107120Sjulian sizeof(*ep), M_NOWAIT); 1078107120Sjulian if (msg != NULL) { 1079107120Sjulian ep = (ng_hci_lp_qos_cfm_ep *) msg->data; 1080107120Sjulian ep->status = status; 1081107120Sjulian ep->con_handle = con->con_handle; 1082107120Sjulian 1083107120Sjulian NG_SEND_MSG_HOOK(error, unit->node, msg, 1084128076Semax unit->sco, 0); 1085107120Sjulian } 1086107120Sjulian } else 1087107120Sjulian NG_HCI_INFO( 1088107120Sjulian"%s: %s - SCO hook not valid, hook=%p\n", 1089107120Sjulian __func__, NG_NODE_NAME(unit->node), unit->sco); 1090107120Sjulian 1091107120Sjulian con->flags &= ~NG_HCI_CON_NOTIFY_SCO; 1092107120Sjulian } 1093107120Sjulian 1094107120Sjulian return (0); 1095107120Sjulian} /* ng_hci_lp_qos_cfm */ 1096107120Sjulian 1097107120Sjulian/* 1098107120Sjulian * Send LP_QoSViolationInd event to the upper layer protocol 1099107120Sjulian */ 1100107120Sjulian 1101107120Sjulianint 1102107120Sjulianng_hci_lp_qos_ind(ng_hci_unit_con_p con) 1103107120Sjulian{ 1104107120Sjulian ng_hci_unit_p unit = con->unit; 1105107120Sjulian struct ng_mesg *msg = NULL; 1106107120Sjulian ng_hci_lp_qos_ind_ep *ep = NULL; 1107107120Sjulian int error; 1108107120Sjulian 1109107120Sjulian /* 1110107120Sjulian * QoS Violation can only be generated for ACL connection handles. 1111107120Sjulian * Both ACL and SCO upstream hooks will receive notification. 1112107120Sjulian */ 1113107120Sjulian 1114107120Sjulian if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 1115107120Sjulian NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, 1116107120Sjulian sizeof(*ep), M_NOWAIT); 1117107120Sjulian if (msg == NULL) 1118107120Sjulian return (ENOMEM); 1119107120Sjulian 1120107120Sjulian ep = (ng_hci_lp_qos_ind_ep *) msg->data; 1121107120Sjulian ep->con_handle = con->con_handle; 1122107120Sjulian 1123128076Semax NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0); 1124107120Sjulian } else 1125107120Sjulian NG_HCI_INFO( 1126107120Sjulian"%s: %s - ACL hook is not connected or not valid, hook=%p\n", 1127107120Sjulian __func__, NG_NODE_NAME(unit->node), unit->acl); 1128107120Sjulian 1129107120Sjulian if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 1130107120Sjulian NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, 1131107120Sjulian sizeof(*ep), M_NOWAIT); 1132107120Sjulian if (msg == NULL) 1133107120Sjulian return (ENOMEM); 1134107120Sjulian 1135107120Sjulian ep = (ng_hci_lp_qos_ind_ep *) msg->data; 1136107120Sjulian ep->con_handle = con->con_handle; 1137107120Sjulian 1138128076Semax NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0); 1139107120Sjulian } else 1140107120Sjulian NG_HCI_INFO( 1141107120Sjulian"%s: %s - SCO hook is not connected or not valid, hook=%p\n", 1142107120Sjulian __func__, NG_NODE_NAME(unit->node), unit->sco); 1143107120Sjulian 1144107120Sjulian return (0); 1145107120Sjulian} /* ng_hci_lp_qos_ind */ 1146107120Sjulian 1147107120Sjulian/* 1148107120Sjulian * Process connection timeout 1149107120Sjulian */ 1150107120Sjulian 1151107120Sjulianvoid 1152121054Semaxng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int con_handle) 1153107120Sjulian{ 1154121054Semax ng_hci_unit_p unit = NULL; 1155121054Semax ng_hci_unit_con_p con = NULL; 1156107120Sjulian 1157121054Semax if (NG_NODE_NOT_VALID(node)) { 1158121054Semax printf("%s: Netgraph node is not valid\n", __func__); 1159121054Semax return; 1160121054Semax } 1161107120Sjulian 1162121054Semax unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 1163121054Semax con = ng_hci_con_by_handle(unit, con_handle); 1164121054Semax 1165121054Semax if (con == NULL) { 1166121054Semax NG_HCI_ALERT( 1167121054Semax"%s: %s - could not find connection, handle=%d\n", 1168121054Semax __func__, NG_NODE_NAME(node), con_handle); 1169121054Semax return; 1170121054Semax } 1171121054Semax 1172121054Semax if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) { 1173121054Semax NG_HCI_ALERT( 1174121054Semax"%s: %s - no pending connection timeout, handle=%d, state=%d, flags=%#x\n", 1175121054Semax __func__, NG_NODE_NAME(node), con_handle, con->state, 1176121054Semax con->flags); 1177121054Semax return; 1178121054Semax } 1179121054Semax 1180107120Sjulian con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING; 1181107120Sjulian 1182107120Sjulian /* 1183107120Sjulian * We expect to receive connection timeout in one of the following 1184107120Sjulian * states: 1185107120Sjulian * 1186114878Sjulian * 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded 1187107120Sjulian * to our LP_CON_IND. Do nothing and destroy connection. Remote peer 1188107120Sjulian * most likely already gave up on us. 1189107120Sjulian * 1190114878Sjulian * 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection 1191107120Sjulian * (or we in the process of accepting it) and baseband has timedout 1192107120Sjulian * on us. Inform upper layers and send LP_CON_CFM. 1193107120Sjulian */ 1194107120Sjulian 1195107120Sjulian switch (con->state) { 1196107120Sjulian case NG_HCI_CON_W4_LP_CON_RSP: 1197107120Sjulian break; 1198107120Sjulian 1199107120Sjulian case NG_HCI_CON_W4_CONN_COMPLETE: 1200107120Sjulian ng_hci_lp_con_cfm(con, 0xee); 1201107120Sjulian break; 1202107120Sjulian 1203107120Sjulian default: 1204121054Semax panic( 1205121054Semax"%s: %s - Invalid connection state=%d\n", 1206121054Semax __func__, NG_NODE_NAME(node), con->state); 1207107120Sjulian break; 1208107120Sjulian } 1209107120Sjulian 1210107120Sjulian ng_hci_free_con(con); 1211107120Sjulian} /* ng_hci_process_con_timeout */ 1212107120Sjulian 1213