1878ed226SJulian Elischer /* 2878ed226SJulian Elischer * ng_hci_ulpi.c 3c398230bSWarner Losh */ 4c398230bSWarner Losh 5c398230bSWarner Losh /*- 6fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 7fe267a55SPedro F. Giffuni * 8878ed226SJulian Elischer * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com> 9878ed226SJulian Elischer * All rights reserved. 10878ed226SJulian Elischer * 11878ed226SJulian Elischer * Redistribution and use in source and binary forms, with or without 12878ed226SJulian Elischer * modification, are permitted provided that the following conditions 13878ed226SJulian Elischer * are met: 14878ed226SJulian Elischer * 1. Redistributions of source code must retain the above copyright 15878ed226SJulian Elischer * notice, this list of conditions and the following disclaimer. 16878ed226SJulian Elischer * 2. Redistributions in binary form must reproduce the above copyright 17878ed226SJulian Elischer * notice, this list of conditions and the following disclaimer in the 18878ed226SJulian Elischer * documentation and/or other materials provided with the distribution. 19878ed226SJulian Elischer * 20878ed226SJulian Elischer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21878ed226SJulian Elischer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22878ed226SJulian Elischer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23878ed226SJulian Elischer * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24878ed226SJulian Elischer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25878ed226SJulian Elischer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26878ed226SJulian Elischer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27878ed226SJulian Elischer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28878ed226SJulian Elischer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29878ed226SJulian Elischer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30878ed226SJulian Elischer * SUCH DAMAGE. 31878ed226SJulian Elischer * 320986ab12SMaksim Yevmenkin * $Id: ng_hci_ulpi.c,v 1.7 2003/09/08 18:57:51 max Exp $ 33878ed226SJulian Elischer * $FreeBSD$ 34878ed226SJulian Elischer */ 35878ed226SJulian Elischer 36878ed226SJulian Elischer #include <sys/param.h> 37878ed226SJulian Elischer #include <sys/systm.h> 38878ed226SJulian Elischer #include <sys/kernel.h> 39878ed226SJulian Elischer #include <sys/endian.h> 40878ed226SJulian Elischer #include <sys/malloc.h> 41878ed226SJulian Elischer #include <sys/mbuf.h> 42878ed226SJulian Elischer #include <sys/queue.h> 43878ed226SJulian Elischer #include <netgraph/ng_message.h> 44878ed226SJulian Elischer #include <netgraph/netgraph.h> 45b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_bluetooth.h> 46b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_hci.h> 47b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/hci/ng_hci_var.h> 48b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/hci/ng_hci_cmds.h> 49b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/hci/ng_hci_evnt.h> 50b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/hci/ng_hci_ulpi.h> 51b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/hci/ng_hci_misc.h> 52878ed226SJulian Elischer 53878ed226SJulian Elischer /****************************************************************************** 54878ed226SJulian Elischer ****************************************************************************** 55878ed226SJulian Elischer ** Upper Layer Protocol Interface module 56878ed226SJulian Elischer ****************************************************************************** 57878ed226SJulian Elischer ******************************************************************************/ 58878ed226SJulian Elischer 59878ed226SJulian Elischer static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p); 60878ed226SJulian Elischer static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p); 61fbc48c2bSTakanori Watanabe static int ng_hci_lp_le_con_req (ng_hci_unit_p, item_p, hook_p, int); 62878ed226SJulian Elischer 63878ed226SJulian Elischer /* 64878ed226SJulian Elischer * Process LP_ConnectReq event from the upper layer protocol 65878ed226SJulian Elischer */ 66878ed226SJulian Elischer 67878ed226SJulian Elischer int 68878ed226SJulian Elischer ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 69878ed226SJulian Elischer { 70fbc48c2bSTakanori Watanabe int link_type; 71fbc48c2bSTakanori Watanabe 72878ed226SJulian Elischer if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 73878ed226SJulian Elischer NG_HCI_WARN( 74878ed226SJulian Elischer "%s: %s - unit is not ready, state=%#x\n", 75878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), unit->state); 76878ed226SJulian Elischer 77878ed226SJulian Elischer NG_FREE_ITEM(item); 78878ed226SJulian Elischer 79878ed226SJulian Elischer return (ENXIO); 80878ed226SJulian Elischer } 81878ed226SJulian Elischer 82878ed226SJulian Elischer if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) { 83878ed226SJulian Elischer NG_HCI_ALERT( 84878ed226SJulian Elischer "%s: %s - invalid LP_ConnectReq message size=%d\n", 85878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), 86878ed226SJulian Elischer NGI_MSG(item)->header.arglen); 87878ed226SJulian Elischer 88878ed226SJulian Elischer NG_FREE_ITEM(item); 89878ed226SJulian Elischer 90878ed226SJulian Elischer return (EMSGSIZE); 91878ed226SJulian Elischer } 92fbc48c2bSTakanori Watanabe link_type = ((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type; 93fbc48c2bSTakanori Watanabe switch(link_type){ 94fbc48c2bSTakanori Watanabe case NG_HCI_LINK_ACL: 95878ed226SJulian Elischer return (ng_hci_lp_acl_con_req(unit, item, hook)); 96fbc48c2bSTakanori Watanabe case NG_HCI_LINK_SCO: 97878ed226SJulian Elischer if (hook != unit->sco ) { 98878ed226SJulian Elischer NG_HCI_WARN( 99878ed226SJulian Elischer "%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n", 100878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), hook); 101878ed226SJulian Elischer 102878ed226SJulian Elischer NG_FREE_ITEM(item); 103878ed226SJulian Elischer 104878ed226SJulian Elischer return (EINVAL); 105878ed226SJulian Elischer } 106878ed226SJulian Elischer 107878ed226SJulian Elischer return (ng_hci_lp_sco_con_req(unit, item, hook)); 108fbc48c2bSTakanori Watanabe case NG_HCI_LINK_LE_PUBLIC: 109fbc48c2bSTakanori Watanabe case NG_HCI_LINK_LE_RANDOM: 110fbc48c2bSTakanori Watanabe return (ng_hci_lp_le_con_req(unit, item, hook, link_type)); 111fbc48c2bSTakanori Watanabe default: 112fbc48c2bSTakanori Watanabe panic("%s: link_type invalid.", __func__); 113fbc48c2bSTakanori Watanabe } 114fbc48c2bSTakanori Watanabe 115fbc48c2bSTakanori Watanabe return (EINVAL); 116878ed226SJulian Elischer } /* ng_hci_lp_con_req */ 117878ed226SJulian Elischer 118878ed226SJulian Elischer /* 119878ed226SJulian Elischer * Request to create new ACL connection 120878ed226SJulian Elischer */ 121878ed226SJulian Elischer 122878ed226SJulian Elischer static int 123878ed226SJulian Elischer ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 124878ed226SJulian Elischer { 125878ed226SJulian Elischer struct acl_con_req { 126878ed226SJulian Elischer ng_hci_cmd_pkt_t hdr; 127878ed226SJulian Elischer ng_hci_create_con_cp cp; 128878ed226SJulian Elischer } __attribute__ ((packed)) *req = NULL; 129878ed226SJulian Elischer ng_hci_lp_con_req_ep *ep = NULL; 130878ed226SJulian Elischer ng_hci_unit_con_p con = NULL; 131878ed226SJulian Elischer ng_hci_neighbor_t *n = NULL; 132878ed226SJulian Elischer struct mbuf *m = NULL; 133878ed226SJulian Elischer int error = 0; 134878ed226SJulian Elischer 135878ed226SJulian Elischer ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); 136878ed226SJulian Elischer 137878ed226SJulian Elischer /* 138878ed226SJulian Elischer * Only one ACL connection can exist between each pair of units. 139878ed226SJulian Elischer * So try to find ACL connection descriptor (in any state) that 140878ed226SJulian Elischer * has requested remote BD_ADDR. 141878ed226SJulian Elischer * 142878ed226SJulian Elischer * Two cases: 143878ed226SJulian Elischer * 144878ed226SJulian Elischer * 1) We do not have connection to the remote unit. This is simple. 145878ed226SJulian Elischer * Just create new connection descriptor and send HCI command to 146878ed226SJulian Elischer * create new connection. 147878ed226SJulian Elischer * 148878ed226SJulian Elischer * 2) We do have connection descriptor. We need to check connection 149878ed226SJulian Elischer * state: 150878ed226SJulian Elischer * 151f2bb1caeSJulian Elischer * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of 152878ed226SJulian Elischer * accepting connection from the remote unit. This is a race 153878ed226SJulian Elischer * condition. We will ignore this message. 154878ed226SJulian Elischer * 155f2bb1caeSJulian Elischer * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already 156878ed226SJulian Elischer * requested connection or we just accepted it. In any case 157878ed226SJulian Elischer * all we need to do here is set appropriate notification bit 158878ed226SJulian Elischer * and wait. 159878ed226SJulian Elischer * 160f2bb1caeSJulian Elischer * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back 161878ed226SJulian Elischer * and let upper layer know that we have connection already. 162878ed226SJulian Elischer */ 163878ed226SJulian Elischer 164878ed226SJulian Elischer con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL); 165878ed226SJulian Elischer if (con != NULL) { 166878ed226SJulian Elischer switch (con->state) { 167878ed226SJulian Elischer case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */ 168878ed226SJulian Elischer error = EALREADY; 169878ed226SJulian Elischer break; 170878ed226SJulian Elischer 171878ed226SJulian Elischer case NG_HCI_CON_W4_CONN_COMPLETE: 172878ed226SJulian Elischer if (hook == unit->acl) 173878ed226SJulian Elischer con->flags |= NG_HCI_CON_NOTIFY_ACL; 174878ed226SJulian Elischer else 175878ed226SJulian Elischer con->flags |= NG_HCI_CON_NOTIFY_SCO; 176878ed226SJulian Elischer break; 177878ed226SJulian Elischer 178878ed226SJulian Elischer case NG_HCI_CON_OPEN: { 179878ed226SJulian Elischer struct ng_mesg *msg = NULL; 180878ed226SJulian Elischer ng_hci_lp_con_cfm_ep *cfm = NULL; 181878ed226SJulian Elischer 182878ed226SJulian Elischer if (hook != NULL && NG_HOOK_IS_VALID(hook)) { 183878ed226SJulian Elischer NGI_GET_MSG(item, msg); 184878ed226SJulian Elischer NG_FREE_MSG(msg); 185878ed226SJulian Elischer 186878ed226SJulian Elischer NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 187878ed226SJulian Elischer NGM_HCI_LP_CON_CFM, sizeof(*cfm), 188878ed226SJulian Elischer M_NOWAIT); 189878ed226SJulian Elischer if (msg != NULL) { 190878ed226SJulian Elischer cfm = (ng_hci_lp_con_cfm_ep *)msg->data; 191878ed226SJulian Elischer cfm->status = 0; 192878ed226SJulian Elischer cfm->link_type = con->link_type; 193878ed226SJulian Elischer cfm->con_handle = con->con_handle; 194878ed226SJulian Elischer bcopy(&con->bdaddr, &cfm->bdaddr, 195878ed226SJulian Elischer sizeof(cfm->bdaddr)); 196878ed226SJulian Elischer 197878ed226SJulian Elischer /* 198878ed226SJulian Elischer * This will forward item back to 199878ed226SJulian Elischer * sender and set item to NULL 200878ed226SJulian Elischer */ 201878ed226SJulian Elischer 202878ed226SJulian Elischer _NGI_MSG(item) = msg; 203878ed226SJulian Elischer NG_FWD_ITEM_HOOK(error, item, hook); 204878ed226SJulian Elischer } else 205878ed226SJulian Elischer error = ENOMEM; 206878ed226SJulian Elischer } else 207878ed226SJulian Elischer NG_HCI_INFO( 208878ed226SJulian Elischer "%s: %s - Source hook is not valid, hook=%p\n", 209878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), 210878ed226SJulian Elischer hook); 211878ed226SJulian Elischer } break; 212878ed226SJulian Elischer 213878ed226SJulian Elischer default: 2140986ab12SMaksim Yevmenkin panic( 2150986ab12SMaksim Yevmenkin "%s: %s - Invalid connection state=%d\n", 2160986ab12SMaksim Yevmenkin __func__, NG_NODE_NAME(unit->node), con->state); 217878ed226SJulian Elischer break; 218878ed226SJulian Elischer } 219878ed226SJulian Elischer 220878ed226SJulian Elischer goto out; 221878ed226SJulian Elischer } 222878ed226SJulian Elischer 223878ed226SJulian Elischer /* 224878ed226SJulian Elischer * If we got here then we need to create new ACL connection descriptor 225878ed226SJulian Elischer * and submit HCI command. First create new connection desriptor, set 226878ed226SJulian Elischer * bdaddr and notification flags. 227878ed226SJulian Elischer */ 228878ed226SJulian Elischer 229878ed226SJulian Elischer con = ng_hci_new_con(unit, NG_HCI_LINK_ACL); 230878ed226SJulian Elischer if (con == NULL) { 231878ed226SJulian Elischer error = ENOMEM; 232878ed226SJulian Elischer goto out; 233878ed226SJulian Elischer } 234878ed226SJulian Elischer 235878ed226SJulian Elischer bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); 236878ed226SJulian Elischer 237878ed226SJulian Elischer /* 238878ed226SJulian Elischer * Create HCI command 239878ed226SJulian Elischer */ 240878ed226SJulian Elischer 241eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 242878ed226SJulian Elischer if (m == NULL) { 243878ed226SJulian Elischer ng_hci_free_con(con); 244878ed226SJulian Elischer error = ENOBUFS; 245878ed226SJulian Elischer goto out; 246878ed226SJulian Elischer } 247878ed226SJulian Elischer 248878ed226SJulian Elischer m->m_pkthdr.len = m->m_len = sizeof(*req); 249878ed226SJulian Elischer req = mtod(m, struct acl_con_req *); 250878ed226SJulian Elischer req->hdr.type = NG_HCI_CMD_PKT; 251878ed226SJulian Elischer req->hdr.length = sizeof(req->cp); 252878ed226SJulian Elischer req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 253878ed226SJulian Elischer NG_HCI_OCF_CREATE_CON)); 254878ed226SJulian Elischer 255878ed226SJulian Elischer bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr)); 256878ed226SJulian Elischer 257878ed226SJulian Elischer req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1); 258878ed226SJulian Elischer if (unit->features[0] & NG_HCI_LMP_3SLOT) 259878ed226SJulian Elischer req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3); 260878ed226SJulian Elischer if (unit->features[0] & NG_HCI_LMP_5SLOT) 261878ed226SJulian Elischer req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5); 262878ed226SJulian Elischer 263878ed226SJulian Elischer req->cp.pkt_type &= unit->packet_mask; 264f2bb1caeSJulian Elischer if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1| 265f2bb1caeSJulian Elischer NG_HCI_PKT_DM3|NG_HCI_PKT_DH3| 266f2bb1caeSJulian Elischer NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0) 267878ed226SJulian Elischer req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1); 268878ed226SJulian Elischer 269878ed226SJulian Elischer req->cp.pkt_type = htole16(req->cp.pkt_type); 270878ed226SJulian Elischer 271f2bb1caeSJulian Elischer if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch) 272878ed226SJulian Elischer req->cp.accept_role_switch = 1; 273878ed226SJulian Elischer else 274878ed226SJulian Elischer req->cp.accept_role_switch = 0; 275878ed226SJulian Elischer 276878ed226SJulian Elischer /* 277878ed226SJulian Elischer * We may speed up connect by specifying valid parameters. 278878ed226SJulian Elischer * So check the neighbor cache. 279878ed226SJulian Elischer */ 280878ed226SJulian Elischer 281fbc48c2bSTakanori Watanabe n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL); 282878ed226SJulian Elischer if (n == NULL) { 283878ed226SJulian Elischer req->cp.page_scan_rep_mode = 0; 284878ed226SJulian Elischer req->cp.page_scan_mode = 0; 285878ed226SJulian Elischer req->cp.clock_offset = 0; 286878ed226SJulian Elischer } else { 287878ed226SJulian Elischer req->cp.page_scan_rep_mode = n->page_scan_rep_mode; 288878ed226SJulian Elischer req->cp.page_scan_mode = n->page_scan_mode; 289878ed226SJulian Elischer req->cp.clock_offset = htole16(n->clock_offset); 290878ed226SJulian Elischer } 291878ed226SJulian Elischer 292878ed226SJulian Elischer /* 293878ed226SJulian Elischer * Adust connection state 294878ed226SJulian Elischer */ 295878ed226SJulian Elischer 296878ed226SJulian Elischer if (hook == unit->acl) 297878ed226SJulian Elischer con->flags |= NG_HCI_CON_NOTIFY_ACL; 298878ed226SJulian Elischer else 299878ed226SJulian Elischer con->flags |= NG_HCI_CON_NOTIFY_SCO; 300878ed226SJulian Elischer 301878ed226SJulian Elischer con->state = NG_HCI_CON_W4_CONN_COMPLETE; 302878ed226SJulian Elischer ng_hci_con_timeout(con); 303878ed226SJulian Elischer 304878ed226SJulian Elischer /* 305878ed226SJulian Elischer * Queue and send HCI command 306878ed226SJulian Elischer */ 307878ed226SJulian Elischer 308878ed226SJulian Elischer NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 309878ed226SJulian Elischer if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 310878ed226SJulian Elischer error = ng_hci_send_command(unit); 311878ed226SJulian Elischer out: 312878ed226SJulian Elischer if (item != NULL) 313878ed226SJulian Elischer NG_FREE_ITEM(item); 314878ed226SJulian Elischer 315878ed226SJulian Elischer return (error); 316878ed226SJulian Elischer } /* ng_hci_lp_acl_con_req */ 317878ed226SJulian Elischer 318878ed226SJulian Elischer /* 319878ed226SJulian Elischer * Request to create new SCO connection 320878ed226SJulian Elischer */ 321878ed226SJulian Elischer 322878ed226SJulian Elischer static int 323878ed226SJulian Elischer ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook) 324878ed226SJulian Elischer { 325878ed226SJulian Elischer struct sco_con_req { 326878ed226SJulian Elischer ng_hci_cmd_pkt_t hdr; 327878ed226SJulian Elischer ng_hci_add_sco_con_cp cp; 328878ed226SJulian Elischer } __attribute__ ((packed)) *req = NULL; 329878ed226SJulian Elischer ng_hci_lp_con_req_ep *ep = NULL; 330878ed226SJulian Elischer ng_hci_unit_con_p acl_con = NULL, sco_con = NULL; 331878ed226SJulian Elischer struct mbuf *m = NULL; 332878ed226SJulian Elischer int error = 0; 333878ed226SJulian Elischer 334878ed226SJulian Elischer ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); 335878ed226SJulian Elischer 336878ed226SJulian Elischer /* 337878ed226SJulian Elischer * SCO connection without ACL link 338878ed226SJulian Elischer * 339878ed226SJulian Elischer * If upper layer requests SCO connection and there is no open ACL 340878ed226SJulian Elischer * connection to the desired remote unit, we will reject the request. 341878ed226SJulian Elischer */ 342878ed226SJulian Elischer 343878ed226SJulian Elischer LIST_FOREACH(acl_con, &unit->con_list, next) 344878ed226SJulian Elischer if (acl_con->link_type == NG_HCI_LINK_ACL && 345878ed226SJulian Elischer acl_con->state == NG_HCI_CON_OPEN && 346878ed226SJulian Elischer bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 347878ed226SJulian Elischer break; 348878ed226SJulian Elischer 349878ed226SJulian Elischer if (acl_con == NULL) { 350878ed226SJulian Elischer NG_HCI_INFO( 351878ed226SJulian Elischer "%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n", 352878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), 353878ed226SJulian Elischer ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], 354878ed226SJulian Elischer ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); 355878ed226SJulian Elischer 356878ed226SJulian Elischer error = ENOENT; 357878ed226SJulian Elischer goto out; 358878ed226SJulian Elischer } 359878ed226SJulian Elischer 360878ed226SJulian Elischer /* 361878ed226SJulian Elischer * Multiple SCO connections can exist between the same pair of units. 362878ed226SJulian Elischer * We assume that multiple SCO connections have to be opened one after 363878ed226SJulian Elischer * another. 364878ed226SJulian Elischer * 365878ed226SJulian Elischer * Try to find SCO connection descriptor that matches the following: 366878ed226SJulian Elischer * 367878ed226SJulian Elischer * 1) sco_con->link_type == NG_HCI_LINK_SCO 368878ed226SJulian Elischer * 369878ed226SJulian Elischer * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP || 370878ed226SJulian Elischer * sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE 371878ed226SJulian Elischer * 372878ed226SJulian Elischer * 3) sco_con->bdaddr == ep->bdaddr 373878ed226SJulian Elischer * 374878ed226SJulian Elischer * Two cases: 375878ed226SJulian Elischer * 376878ed226SJulian Elischer * 1) We do not have connection descriptor. This is simple. Just 377878ed226SJulian Elischer * create new connection and submit Add_SCO_Connection command. 378878ed226SJulian Elischer * 379878ed226SJulian Elischer * 2) We do have connection descriptor. We need to check the state. 380878ed226SJulian Elischer * 381878ed226SJulian Elischer * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting 382878ed226SJulian Elischer * connection from the remote unit. This is a race condition and 383878ed226SJulian Elischer * we will ignore the request. 384878ed226SJulian Elischer * 385878ed226SJulian Elischer * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested 386878ed226SJulian Elischer * connection or we just accepted it. 387878ed226SJulian Elischer */ 388878ed226SJulian Elischer 389878ed226SJulian Elischer LIST_FOREACH(sco_con, &unit->con_list, next) 390878ed226SJulian Elischer if (sco_con->link_type == NG_HCI_LINK_SCO && 391878ed226SJulian Elischer (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP || 392878ed226SJulian Elischer sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) && 393878ed226SJulian Elischer bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 394878ed226SJulian Elischer break; 395878ed226SJulian Elischer 396878ed226SJulian Elischer if (sco_con != NULL) { 397878ed226SJulian Elischer switch (sco_con->state) { 398878ed226SJulian Elischer case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */ 399878ed226SJulian Elischer error = EALREADY; 400878ed226SJulian Elischer break; 401878ed226SJulian Elischer 402878ed226SJulian Elischer case NG_HCI_CON_W4_CONN_COMPLETE: 403878ed226SJulian Elischer sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; 404878ed226SJulian Elischer break; 405878ed226SJulian Elischer 406878ed226SJulian Elischer default: 4070986ab12SMaksim Yevmenkin panic( 408a164074fSEitan Adler "%s: %s - Invalid connection state=%d\n", 409878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), 4100986ab12SMaksim Yevmenkin sco_con->state); 411878ed226SJulian Elischer break; 412878ed226SJulian Elischer } 413878ed226SJulian Elischer 414878ed226SJulian Elischer goto out; 415878ed226SJulian Elischer } 416878ed226SJulian Elischer 417878ed226SJulian Elischer /* 418878ed226SJulian Elischer * If we got here then we need to create new SCO connection descriptor 419878ed226SJulian Elischer * and submit HCI command. 420878ed226SJulian Elischer */ 421878ed226SJulian Elischer 422878ed226SJulian Elischer sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO); 423878ed226SJulian Elischer if (sco_con == NULL) { 424878ed226SJulian Elischer error = ENOMEM; 425878ed226SJulian Elischer goto out; 426878ed226SJulian Elischer } 427878ed226SJulian Elischer 428878ed226SJulian Elischer bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr)); 429878ed226SJulian Elischer 430878ed226SJulian Elischer /* 431878ed226SJulian Elischer * Create HCI command 432878ed226SJulian Elischer */ 433878ed226SJulian Elischer 434eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 435878ed226SJulian Elischer if (m == NULL) { 436878ed226SJulian Elischer ng_hci_free_con(sco_con); 437878ed226SJulian Elischer error = ENOBUFS; 438878ed226SJulian Elischer goto out; 439878ed226SJulian Elischer } 440878ed226SJulian Elischer 441878ed226SJulian Elischer m->m_pkthdr.len = m->m_len = sizeof(*req); 442878ed226SJulian Elischer req = mtod(m, struct sco_con_req *); 443878ed226SJulian Elischer req->hdr.type = NG_HCI_CMD_PKT; 444878ed226SJulian Elischer req->hdr.length = sizeof(req->cp); 445878ed226SJulian Elischer req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 446878ed226SJulian Elischer NG_HCI_OCF_ADD_SCO_CON)); 447878ed226SJulian Elischer 448878ed226SJulian Elischer req->cp.con_handle = htole16(acl_con->con_handle); 449878ed226SJulian Elischer 450878ed226SJulian Elischer req->cp.pkt_type = NG_HCI_PKT_HV1; 451878ed226SJulian Elischer if (unit->features[1] & NG_HCI_LMP_HV2_PKT) 452878ed226SJulian Elischer req->cp.pkt_type |= NG_HCI_PKT_HV2; 453878ed226SJulian Elischer if (unit->features[1] & NG_HCI_LMP_HV3_PKT) 454878ed226SJulian Elischer req->cp.pkt_type |= NG_HCI_PKT_HV3; 455878ed226SJulian Elischer 456878ed226SJulian Elischer req->cp.pkt_type &= unit->packet_mask; 457f2bb1caeSJulian Elischer if ((req->cp.pkt_type & (NG_HCI_PKT_HV1| 458f2bb1caeSJulian Elischer NG_HCI_PKT_HV2| 459f2bb1caeSJulian Elischer NG_HCI_PKT_HV3)) == 0) 460878ed226SJulian Elischer req->cp.pkt_type = NG_HCI_PKT_HV1; 461878ed226SJulian Elischer 462878ed226SJulian Elischer req->cp.pkt_type = htole16(req->cp.pkt_type); 463878ed226SJulian Elischer 464878ed226SJulian Elischer /* 465878ed226SJulian Elischer * Adust connection state 466878ed226SJulian Elischer */ 467878ed226SJulian Elischer 468878ed226SJulian Elischer sco_con->flags |= NG_HCI_CON_NOTIFY_SCO; 469878ed226SJulian Elischer 470878ed226SJulian Elischer sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE; 471878ed226SJulian Elischer ng_hci_con_timeout(sco_con); 472878ed226SJulian Elischer 473878ed226SJulian Elischer /* 474878ed226SJulian Elischer * Queue and send HCI command 475878ed226SJulian Elischer */ 476878ed226SJulian Elischer 477878ed226SJulian Elischer NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 478878ed226SJulian Elischer if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 479878ed226SJulian Elischer error = ng_hci_send_command(unit); 480878ed226SJulian Elischer out: 481878ed226SJulian Elischer NG_FREE_ITEM(item); 482878ed226SJulian Elischer 483878ed226SJulian Elischer return (error); 484878ed226SJulian Elischer } /* ng_hci_lp_sco_con_req */ 485878ed226SJulian Elischer 486fbc48c2bSTakanori Watanabe static int 487fbc48c2bSTakanori Watanabe ng_hci_lp_le_con_req(ng_hci_unit_p unit, item_p item, hook_p hook, int link_type) 488fbc48c2bSTakanori Watanabe { 489fbc48c2bSTakanori Watanabe struct acl_con_req { 490fbc48c2bSTakanori Watanabe ng_hci_cmd_pkt_t hdr; 491fbc48c2bSTakanori Watanabe ng_hci_le_create_connection_cp cp; 492fbc48c2bSTakanori Watanabe } __attribute__ ((packed)) *req = NULL; 493fbc48c2bSTakanori Watanabe ng_hci_lp_con_req_ep *ep = NULL; 494fbc48c2bSTakanori Watanabe ng_hci_unit_con_p con = NULL; 495fbc48c2bSTakanori Watanabe struct mbuf *m = NULL; 496fbc48c2bSTakanori Watanabe int error = 0; 497fbc48c2bSTakanori Watanabe 498fbc48c2bSTakanori Watanabe ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data); 499fbc48c2bSTakanori Watanabe if((link_type != NG_HCI_LINK_LE_PUBLIC)&& 500fbc48c2bSTakanori Watanabe (link_type != NG_HCI_LINK_LE_RANDOM)){ 501fbc48c2bSTakanori Watanabe printf("%s: Link type %d Cannot be here \n", __func__, 502fbc48c2bSTakanori Watanabe link_type); 503fbc48c2bSTakanori Watanabe } 504fbc48c2bSTakanori Watanabe /* 505fbc48c2bSTakanori Watanabe * Only one ACL connection can exist between each pair of units. 506fbc48c2bSTakanori Watanabe * So try to find ACL connection descriptor (in any state) that 507fbc48c2bSTakanori Watanabe * has requested remote BD_ADDR. 508fbc48c2bSTakanori Watanabe * 509fbc48c2bSTakanori Watanabe * Two cases: 510fbc48c2bSTakanori Watanabe * 511fbc48c2bSTakanori Watanabe * 1) We do not have connection to the remote unit. This is simple. 512fbc48c2bSTakanori Watanabe * Just create new connection descriptor and send HCI command to 513fbc48c2bSTakanori Watanabe * create new connection. 514fbc48c2bSTakanori Watanabe * 515fbc48c2bSTakanori Watanabe * 2) We do have connection descriptor. We need to check connection 516fbc48c2bSTakanori Watanabe * state: 517fbc48c2bSTakanori Watanabe * 518fbc48c2bSTakanori Watanabe * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of 519fbc48c2bSTakanori Watanabe * accepting connection from the remote unit. This is a race 520fbc48c2bSTakanori Watanabe * condition. We will ignore this message. 521fbc48c2bSTakanori Watanabe * 522fbc48c2bSTakanori Watanabe * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already 523fbc48c2bSTakanori Watanabe * requested connection or we just accepted it. In any case 524fbc48c2bSTakanori Watanabe * all we need to do here is set appropriate notification bit 525fbc48c2bSTakanori Watanabe * and wait. 526fbc48c2bSTakanori Watanabe * 527fbc48c2bSTakanori Watanabe * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back 528fbc48c2bSTakanori Watanabe * and let upper layer know that we have connection already. 529fbc48c2bSTakanori Watanabe */ 530fbc48c2bSTakanori Watanabe 531fbc48c2bSTakanori Watanabe con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, link_type); 532fbc48c2bSTakanori Watanabe if (con != NULL) { 533fbc48c2bSTakanori Watanabe switch (con->state) { 534fbc48c2bSTakanori Watanabe case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */ 535fbc48c2bSTakanori Watanabe error = EALREADY; 536fbc48c2bSTakanori Watanabe break; 537fbc48c2bSTakanori Watanabe 538fbc48c2bSTakanori Watanabe case NG_HCI_CON_W4_CONN_COMPLETE: 539fbc48c2bSTakanori Watanabe if (hook != unit->sco) 540fbc48c2bSTakanori Watanabe con->flags |= NG_HCI_CON_NOTIFY_ACL; 541fbc48c2bSTakanori Watanabe else 542fbc48c2bSTakanori Watanabe con->flags |= NG_HCI_CON_NOTIFY_SCO; 543fbc48c2bSTakanori Watanabe break; 544fbc48c2bSTakanori Watanabe 545fbc48c2bSTakanori Watanabe case NG_HCI_CON_OPEN: { 546fbc48c2bSTakanori Watanabe struct ng_mesg *msg = NULL; 547fbc48c2bSTakanori Watanabe ng_hci_lp_con_cfm_ep *cfm = NULL; 548fbc48c2bSTakanori Watanabe 549fbc48c2bSTakanori Watanabe if (hook != NULL && NG_HOOK_IS_VALID(hook)) { 550fbc48c2bSTakanori Watanabe NGI_GET_MSG(item, msg); 551fbc48c2bSTakanori Watanabe NG_FREE_MSG(msg); 552fbc48c2bSTakanori Watanabe 553fbc48c2bSTakanori Watanabe NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 554fbc48c2bSTakanori Watanabe NGM_HCI_LP_CON_CFM, sizeof(*cfm), 555fbc48c2bSTakanori Watanabe M_NOWAIT); 556fbc48c2bSTakanori Watanabe if (msg != NULL) { 557fbc48c2bSTakanori Watanabe cfm = (ng_hci_lp_con_cfm_ep *)msg->data; 558fbc48c2bSTakanori Watanabe cfm->status = 0; 559fbc48c2bSTakanori Watanabe cfm->link_type = con->link_type; 560fbc48c2bSTakanori Watanabe cfm->con_handle = con->con_handle; 561fbc48c2bSTakanori Watanabe bcopy(&con->bdaddr, &cfm->bdaddr, 562fbc48c2bSTakanori Watanabe sizeof(cfm->bdaddr)); 563fbc48c2bSTakanori Watanabe 564fbc48c2bSTakanori Watanabe /* 565fbc48c2bSTakanori Watanabe * This will forward item back to 566fbc48c2bSTakanori Watanabe * sender and set item to NULL 567fbc48c2bSTakanori Watanabe */ 568fbc48c2bSTakanori Watanabe 569fbc48c2bSTakanori Watanabe _NGI_MSG(item) = msg; 570fbc48c2bSTakanori Watanabe NG_FWD_ITEM_HOOK(error, item, hook); 571fbc48c2bSTakanori Watanabe } else 572fbc48c2bSTakanori Watanabe error = ENOMEM; 573fbc48c2bSTakanori Watanabe } else 574fbc48c2bSTakanori Watanabe NG_HCI_INFO( 575fbc48c2bSTakanori Watanabe "%s: %s - Source hook is not valid, hook=%p\n", 576fbc48c2bSTakanori Watanabe __func__, NG_NODE_NAME(unit->node), 577fbc48c2bSTakanori Watanabe hook); 578fbc48c2bSTakanori Watanabe } break; 579fbc48c2bSTakanori Watanabe 580fbc48c2bSTakanori Watanabe default: 581fbc48c2bSTakanori Watanabe panic( 582fbc48c2bSTakanori Watanabe "%s: %s - Invalid connection state=%d\n", 583fbc48c2bSTakanori Watanabe __func__, NG_NODE_NAME(unit->node), con->state); 584fbc48c2bSTakanori Watanabe break; 585fbc48c2bSTakanori Watanabe } 586fbc48c2bSTakanori Watanabe 587fbc48c2bSTakanori Watanabe goto out; 588fbc48c2bSTakanori Watanabe } 589fbc48c2bSTakanori Watanabe 590fbc48c2bSTakanori Watanabe /* 591fbc48c2bSTakanori Watanabe * If we got here then we need to create new ACL connection descriptor 592fbc48c2bSTakanori Watanabe * and submit HCI command. First create new connection desriptor, set 593fbc48c2bSTakanori Watanabe * bdaddr and notification flags. 594fbc48c2bSTakanori Watanabe */ 595fbc48c2bSTakanori Watanabe 596fbc48c2bSTakanori Watanabe con = ng_hci_new_con(unit, link_type); 597fbc48c2bSTakanori Watanabe if (con == NULL) { 598fbc48c2bSTakanori Watanabe error = ENOMEM; 599fbc48c2bSTakanori Watanabe goto out; 600fbc48c2bSTakanori Watanabe } 601fbc48c2bSTakanori Watanabe 602fbc48c2bSTakanori Watanabe bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr)); 603fbc48c2bSTakanori Watanabe 604fbc48c2bSTakanori Watanabe /* 605fbc48c2bSTakanori Watanabe * Create HCI command 606fbc48c2bSTakanori Watanabe */ 607fbc48c2bSTakanori Watanabe 608fbc48c2bSTakanori Watanabe MGETHDR(m, M_NOWAIT, MT_DATA); 609fbc48c2bSTakanori Watanabe if (m == NULL) { 610fbc48c2bSTakanori Watanabe ng_hci_free_con(con); 611fbc48c2bSTakanori Watanabe error = ENOBUFS; 612fbc48c2bSTakanori Watanabe goto out; 613fbc48c2bSTakanori Watanabe } 614fbc48c2bSTakanori Watanabe 615fbc48c2bSTakanori Watanabe m->m_pkthdr.len = m->m_len = sizeof(*req); 616fbc48c2bSTakanori Watanabe req = mtod(m, struct acl_con_req *); 617fbc48c2bSTakanori Watanabe req->hdr.type = NG_HCI_CMD_PKT; 618fbc48c2bSTakanori Watanabe req->hdr.length = sizeof(req->cp); 619fbc48c2bSTakanori Watanabe req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LE, 620fbc48c2bSTakanori Watanabe NG_HCI_OCF_LE_CREATE_CONNECTION)); 621fbc48c2bSTakanori Watanabe 622fbc48c2bSTakanori Watanabe bcopy(&ep->bdaddr, &req->cp.peer_addr, sizeof(req->cp.peer_addr)); 623fbc48c2bSTakanori Watanabe req->cp.own_address_type = 0; 624fbc48c2bSTakanori Watanabe req->cp.peer_addr_type = (link_type == NG_HCI_LINK_LE_RANDOM)? 1:0; 625fbc48c2bSTakanori Watanabe req->cp.scan_interval = htole16(4); 626fbc48c2bSTakanori Watanabe req->cp.scan_window = htole16(4); 627fbc48c2bSTakanori Watanabe req->cp.filter_policy = 0; 628fbc48c2bSTakanori Watanabe req->cp.conn_interval_min = htole16(0xf); 629fbc48c2bSTakanori Watanabe req->cp.conn_interval_max = htole16(0xf); 630fbc48c2bSTakanori Watanabe req->cp.conn_latency = htole16(0); 631fbc48c2bSTakanori Watanabe req->cp.supervision_timeout = htole16(0xc80); 632fbc48c2bSTakanori Watanabe req->cp.min_ce_length = htole16(1); 633fbc48c2bSTakanori Watanabe req->cp.max_ce_length = htole16(1); 634fbc48c2bSTakanori Watanabe /* 635fbc48c2bSTakanori Watanabe * Adust connection state 636fbc48c2bSTakanori Watanabe */ 637fbc48c2bSTakanori Watanabe 638fbc48c2bSTakanori Watanabe if (hook != unit->sco) 639fbc48c2bSTakanori Watanabe con->flags |= NG_HCI_CON_NOTIFY_ACL; 640fbc48c2bSTakanori Watanabe else 641fbc48c2bSTakanori Watanabe con->flags |= NG_HCI_CON_NOTIFY_SCO; 642fbc48c2bSTakanori Watanabe 643fbc48c2bSTakanori Watanabe con->state = NG_HCI_CON_W4_CONN_COMPLETE; 644fbc48c2bSTakanori Watanabe ng_hci_con_timeout(con); 645fbc48c2bSTakanori Watanabe 646fbc48c2bSTakanori Watanabe /* 647fbc48c2bSTakanori Watanabe * Queue and send HCI command 648fbc48c2bSTakanori Watanabe */ 649fbc48c2bSTakanori Watanabe 650fbc48c2bSTakanori Watanabe NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 651fbc48c2bSTakanori Watanabe if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 652fbc48c2bSTakanori Watanabe error = ng_hci_send_command(unit); 653fbc48c2bSTakanori Watanabe out: 654fbc48c2bSTakanori Watanabe if (item != NULL) 655fbc48c2bSTakanori Watanabe NG_FREE_ITEM(item); 656fbc48c2bSTakanori Watanabe 657fbc48c2bSTakanori Watanabe return (error); 658fbc48c2bSTakanori Watanabe } /* ng_hci_lp_acl_con_req */ 659fbc48c2bSTakanori Watanabe 660878ed226SJulian Elischer /* 661878ed226SJulian Elischer * Process LP_DisconnectReq event from the upper layer protocol 662878ed226SJulian Elischer */ 663878ed226SJulian Elischer 664878ed226SJulian Elischer int 665878ed226SJulian Elischer ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook) 666878ed226SJulian Elischer { 667878ed226SJulian Elischer struct discon_req { 668878ed226SJulian Elischer ng_hci_cmd_pkt_t hdr; 669878ed226SJulian Elischer ng_hci_discon_cp cp; 670878ed226SJulian Elischer } __attribute__ ((packed)) *req = NULL; 671878ed226SJulian Elischer ng_hci_lp_discon_req_ep *ep = NULL; 672878ed226SJulian Elischer ng_hci_unit_con_p con = NULL; 673878ed226SJulian Elischer struct mbuf *m = NULL; 674878ed226SJulian Elischer int error = 0; 675878ed226SJulian Elischer 676878ed226SJulian Elischer /* Check if unit is ready */ 677878ed226SJulian Elischer if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 678878ed226SJulian Elischer NG_HCI_WARN( 679878ed226SJulian Elischer "%s: %s - unit is not ready, state=%#x\n", 680878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), unit->state); 681878ed226SJulian Elischer 682878ed226SJulian Elischer error = ENXIO; 683878ed226SJulian Elischer goto out; 684878ed226SJulian Elischer } 685878ed226SJulian Elischer 686878ed226SJulian Elischer if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 687878ed226SJulian Elischer NG_HCI_ALERT( 688878ed226SJulian Elischer "%s: %s - invalid LP_DisconnectReq message size=%d\n", 689878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), 690878ed226SJulian Elischer NGI_MSG(item)->header.arglen); 691878ed226SJulian Elischer 692878ed226SJulian Elischer error = EMSGSIZE; 693878ed226SJulian Elischer goto out; 694878ed226SJulian Elischer } 695878ed226SJulian Elischer 696878ed226SJulian Elischer ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data); 697878ed226SJulian Elischer 698878ed226SJulian Elischer con = ng_hci_con_by_handle(unit, ep->con_handle); 699878ed226SJulian Elischer if (con == NULL) { 700878ed226SJulian Elischer NG_HCI_ERR( 701878ed226SJulian Elischer "%s: %s - invalid connection handle=%d\n", 702878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), ep->con_handle); 703878ed226SJulian Elischer 704878ed226SJulian Elischer error = ENOENT; 705878ed226SJulian Elischer goto out; 706878ed226SJulian Elischer } 707878ed226SJulian Elischer 708878ed226SJulian Elischer if (con->state != NG_HCI_CON_OPEN) { 709878ed226SJulian Elischer NG_HCI_ERR( 710878ed226SJulian Elischer "%s: %s - invalid connection state=%d, handle=%d\n", 711878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), con->state, 712878ed226SJulian Elischer ep->con_handle); 713878ed226SJulian Elischer 714878ed226SJulian Elischer error = EINVAL; 715878ed226SJulian Elischer goto out; 716878ed226SJulian Elischer } 717878ed226SJulian Elischer 718878ed226SJulian Elischer /* 719878ed226SJulian Elischer * Create HCI command 720878ed226SJulian Elischer */ 721878ed226SJulian Elischer 722eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 723878ed226SJulian Elischer if (m == NULL) { 724878ed226SJulian Elischer error = ENOBUFS; 725878ed226SJulian Elischer goto out; 726878ed226SJulian Elischer } 727878ed226SJulian Elischer 728878ed226SJulian Elischer m->m_pkthdr.len = m->m_len = sizeof(*req); 729878ed226SJulian Elischer req = mtod(m, struct discon_req *); 730878ed226SJulian Elischer req->hdr.type = NG_HCI_CMD_PKT; 731878ed226SJulian Elischer req->hdr.length = sizeof(req->cp); 732878ed226SJulian Elischer req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL, 733878ed226SJulian Elischer NG_HCI_OCF_DISCON)); 734878ed226SJulian Elischer 735878ed226SJulian Elischer req->cp.con_handle = htole16(ep->con_handle); 736878ed226SJulian Elischer req->cp.reason = ep->reason; 737878ed226SJulian Elischer 738878ed226SJulian Elischer /* 739878ed226SJulian Elischer * Queue and send HCI command 740878ed226SJulian Elischer */ 741878ed226SJulian Elischer 742878ed226SJulian Elischer NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 743878ed226SJulian Elischer if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 744878ed226SJulian Elischer error = ng_hci_send_command(unit); 745878ed226SJulian Elischer out: 746878ed226SJulian Elischer NG_FREE_ITEM(item); 747878ed226SJulian Elischer 748878ed226SJulian Elischer return (error); 749878ed226SJulian Elischer } /* ng_hci_lp_discon_req */ 750878ed226SJulian Elischer 751878ed226SJulian Elischer /* 752878ed226SJulian Elischer * Send LP_ConnectCfm event to the upper layer protocol 753878ed226SJulian Elischer */ 754878ed226SJulian Elischer 755878ed226SJulian Elischer int 756878ed226SJulian Elischer ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status) 757878ed226SJulian Elischer { 758878ed226SJulian Elischer ng_hci_unit_p unit = con->unit; 759878ed226SJulian Elischer struct ng_mesg *msg = NULL; 760878ed226SJulian Elischer ng_hci_lp_con_cfm_ep *ep = NULL; 761878ed226SJulian Elischer int error; 762878ed226SJulian Elischer 763878ed226SJulian Elischer /* 764878ed226SJulian Elischer * Check who wants to be notified. For ACL links both ACL and SCO 765878ed226SJulian Elischer * upstream hooks will be notified (if required). For SCO links 766878ed226SJulian Elischer * only SCO upstream hook will receive notification 767878ed226SJulian Elischer */ 768878ed226SJulian Elischer 769fbc48c2bSTakanori Watanabe if (con->link_type != NG_HCI_LINK_SCO && 770878ed226SJulian Elischer con->flags & NG_HCI_CON_NOTIFY_ACL) { 771878ed226SJulian Elischer if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 772878ed226SJulian Elischer NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, 773878ed226SJulian Elischer sizeof(*ep), M_NOWAIT); 774878ed226SJulian Elischer if (msg != NULL) { 775878ed226SJulian Elischer ep = (ng_hci_lp_con_cfm_ep *) msg->data; 776878ed226SJulian Elischer ep->status = status; 777878ed226SJulian Elischer ep->link_type = con->link_type; 778878ed226SJulian Elischer ep->con_handle = con->con_handle; 779878ed226SJulian Elischer bcopy(&con->bdaddr, &ep->bdaddr, 780878ed226SJulian Elischer sizeof(ep->bdaddr)); 781878ed226SJulian Elischer 782878ed226SJulian Elischer NG_SEND_MSG_HOOK(error, unit->node, msg, 7834ae439a3SMaksim Yevmenkin unit->acl, 0); 784878ed226SJulian Elischer } 785878ed226SJulian Elischer } else 786878ed226SJulian Elischer NG_HCI_INFO( 787878ed226SJulian Elischer "%s: %s - ACL hook not valid, hook=%p\n", 788878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), unit->acl); 789878ed226SJulian Elischer 790878ed226SJulian Elischer con->flags &= ~NG_HCI_CON_NOTIFY_ACL; 791878ed226SJulian Elischer } 792878ed226SJulian Elischer 793878ed226SJulian Elischer if (con->flags & NG_HCI_CON_NOTIFY_SCO) { 794878ed226SJulian Elischer if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 795878ed226SJulian Elischer NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM, 796878ed226SJulian Elischer sizeof(*ep), M_NOWAIT); 797878ed226SJulian Elischer if (msg != NULL) { 798878ed226SJulian Elischer ep = (ng_hci_lp_con_cfm_ep *) msg->data; 799878ed226SJulian Elischer ep->status = status; 800878ed226SJulian Elischer ep->link_type = con->link_type; 801878ed226SJulian Elischer ep->con_handle = con->con_handle; 802878ed226SJulian Elischer bcopy(&con->bdaddr, &ep->bdaddr, 803878ed226SJulian Elischer sizeof(ep->bdaddr)); 804878ed226SJulian Elischer 805878ed226SJulian Elischer NG_SEND_MSG_HOOK(error, unit->node, msg, 8064ae439a3SMaksim Yevmenkin unit->sco, 0); 807878ed226SJulian Elischer } 808878ed226SJulian Elischer } else 809878ed226SJulian Elischer NG_HCI_INFO( 810878ed226SJulian Elischer "%s: %s - SCO hook not valid, hook=%p\n", 811878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), unit->acl); 812878ed226SJulian Elischer 813878ed226SJulian Elischer con->flags &= ~NG_HCI_CON_NOTIFY_SCO; 814878ed226SJulian Elischer } 815878ed226SJulian Elischer 816878ed226SJulian Elischer return (0); 817878ed226SJulian Elischer } /* ng_hci_lp_con_cfm */ 818878ed226SJulian Elischer 8193a601a23STakanori Watanabe int 8203a601a23STakanori Watanabe ng_hci_lp_enc_change(ng_hci_unit_con_p con, int status) 8213a601a23STakanori Watanabe { 8223a601a23STakanori Watanabe ng_hci_unit_p unit = con->unit; 8233a601a23STakanori Watanabe struct ng_mesg *msg = NULL; 8243a601a23STakanori Watanabe ng_hci_lp_enc_change_ep *ep = NULL; 8253a601a23STakanori Watanabe int error; 8263a601a23STakanori Watanabe 8273a601a23STakanori Watanabe if (con->link_type != NG_HCI_LINK_SCO) { 8283a601a23STakanori Watanabe if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 8293a601a23STakanori Watanabe NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_ENC_CHG, 8303a601a23STakanori Watanabe sizeof(*ep), M_NOWAIT); 8313a601a23STakanori Watanabe if (msg != NULL) { 8323a601a23STakanori Watanabe ep = (ng_hci_lp_enc_change_ep *) msg->data; 8333a601a23STakanori Watanabe ep->status = status; 8343a601a23STakanori Watanabe ep->link_type = con->link_type; 8353a601a23STakanori Watanabe ep->con_handle = con->con_handle; 8363a601a23STakanori Watanabe 8373a601a23STakanori Watanabe NG_SEND_MSG_HOOK(error, unit->node, msg, 8383a601a23STakanori Watanabe unit->acl, 0); 8393a601a23STakanori Watanabe } 8403a601a23STakanori Watanabe } else 8413a601a23STakanori Watanabe NG_HCI_INFO( 8423a601a23STakanori Watanabe "%s: %s - ACL hook not valid, hook=%p\n", 8433a601a23STakanori Watanabe __func__, NG_NODE_NAME(unit->node), unit->acl); 8443a601a23STakanori Watanabe } 8453a601a23STakanori Watanabe return (0); 8463a601a23STakanori Watanabe } /* ng_hci_lp_con_cfm */ 8473a601a23STakanori Watanabe 848878ed226SJulian Elischer /* 849878ed226SJulian Elischer * Send LP_ConnectInd event to the upper layer protocol 850878ed226SJulian Elischer */ 851878ed226SJulian Elischer 852878ed226SJulian Elischer int 853878ed226SJulian Elischer ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass) 854878ed226SJulian Elischer { 855878ed226SJulian Elischer ng_hci_unit_p unit = con->unit; 856878ed226SJulian Elischer struct ng_mesg *msg = NULL; 857878ed226SJulian Elischer ng_hci_lp_con_ind_ep *ep = NULL; 858878ed226SJulian Elischer hook_p hook = NULL; 859878ed226SJulian Elischer int error = 0; 860878ed226SJulian Elischer 861878ed226SJulian Elischer /* 862878ed226SJulian Elischer * Connection_Request event is generated for specific link type. 863878ed226SJulian Elischer * Use link_type to select upstream hook. 864878ed226SJulian Elischer */ 865878ed226SJulian Elischer 866fbc48c2bSTakanori Watanabe if (con->link_type != NG_HCI_LINK_SCO) 867878ed226SJulian Elischer hook = unit->acl; 868878ed226SJulian Elischer else 869878ed226SJulian Elischer hook = unit->sco; 870878ed226SJulian Elischer 871878ed226SJulian Elischer if (hook != NULL && NG_HOOK_IS_VALID(hook)) { 872878ed226SJulian Elischer NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND, 873878ed226SJulian Elischer sizeof(*ep), M_NOWAIT); 874878ed226SJulian Elischer if (msg == NULL) 875878ed226SJulian Elischer return (ENOMEM); 876878ed226SJulian Elischer 877878ed226SJulian Elischer ep = (ng_hci_lp_con_ind_ep *)(msg->data); 878878ed226SJulian Elischer ep->link_type = con->link_type; 879878ed226SJulian Elischer bcopy(uclass, ep->uclass, sizeof(ep->uclass)); 880878ed226SJulian Elischer bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); 881878ed226SJulian Elischer 8824ae439a3SMaksim Yevmenkin NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0); 883878ed226SJulian Elischer } else { 884878ed226SJulian Elischer NG_HCI_WARN( 885878ed226SJulian Elischer "%s: %s - Upstream hook is not connected or not valid, hook=%p\n", 886878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), hook); 887878ed226SJulian Elischer 888878ed226SJulian Elischer error = ENOTCONN; 889878ed226SJulian Elischer } 890878ed226SJulian Elischer 891878ed226SJulian Elischer return (error); 892878ed226SJulian Elischer } /* ng_hci_lp_con_ind */ 893878ed226SJulian Elischer 894878ed226SJulian Elischer /* 895878ed226SJulian Elischer * Process LP_ConnectRsp event from the upper layer protocol 896878ed226SJulian Elischer */ 897878ed226SJulian Elischer 898878ed226SJulian Elischer int 899878ed226SJulian Elischer ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook) 900878ed226SJulian Elischer { 901878ed226SJulian Elischer struct con_rsp_req { 902878ed226SJulian Elischer ng_hci_cmd_pkt_t hdr; 903878ed226SJulian Elischer union { 904878ed226SJulian Elischer ng_hci_accept_con_cp acc; 905878ed226SJulian Elischer ng_hci_reject_con_cp rej; 906878ed226SJulian Elischer } __attribute__ ((packed)) cp; 907878ed226SJulian Elischer } __attribute__ ((packed)) *req = NULL; 908878ed226SJulian Elischer ng_hci_lp_con_rsp_ep *ep = NULL; 909878ed226SJulian Elischer ng_hci_unit_con_p con = NULL; 910878ed226SJulian Elischer struct mbuf *m = NULL; 911878ed226SJulian Elischer int error = 0; 912878ed226SJulian Elischer 913878ed226SJulian Elischer /* Check if unit is ready */ 914878ed226SJulian Elischer if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 915878ed226SJulian Elischer NG_HCI_WARN( 916878ed226SJulian Elischer "%s: %s - unit is not ready, state=%#x\n", 917878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), unit->state); 918878ed226SJulian Elischer 919878ed226SJulian Elischer error = ENXIO; 920878ed226SJulian Elischer goto out; 921878ed226SJulian Elischer } 922878ed226SJulian Elischer 923878ed226SJulian Elischer if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 924878ed226SJulian Elischer NG_HCI_ALERT( 925878ed226SJulian Elischer "%s: %s - invalid LP_ConnectRsp message size=%d\n", 926878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), 927878ed226SJulian Elischer NGI_MSG(item)->header.arglen); 928878ed226SJulian Elischer 929878ed226SJulian Elischer error = EMSGSIZE; 930878ed226SJulian Elischer goto out; 931878ed226SJulian Elischer } 932878ed226SJulian Elischer 933878ed226SJulian Elischer ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data); 934878ed226SJulian Elischer 935878ed226SJulian Elischer /* 936878ed226SJulian Elischer * Here we have to deal with race. Upper layers might send conflicting 937878ed226SJulian Elischer * requests. One might send Accept and other Reject. We will not try 938878ed226SJulian Elischer * to solve all the problems, so first request will always win. 939878ed226SJulian Elischer * 940878ed226SJulian Elischer * Try to find connection that matches the following: 941878ed226SJulian Elischer * 942878ed226SJulian Elischer * 1) con->link_type == ep->link_type 943878ed226SJulian Elischer * 944878ed226SJulian Elischer * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP || 945878ed226SJulian Elischer * con->state == NG_HCI_CON_W4_CONN_COMPLETE 946878ed226SJulian Elischer * 947878ed226SJulian Elischer * 3) con->bdaddr == ep->bdaddr 948878ed226SJulian Elischer * 949878ed226SJulian Elischer * Two cases: 950878ed226SJulian Elischer * 951878ed226SJulian Elischer * 1) We do not have connection descriptor. Could be bogus request or 952878ed226SJulian Elischer * we have rejected connection already. 953878ed226SJulian Elischer * 954878ed226SJulian Elischer * 2) We do have connection descriptor. Then we need to check state: 955878ed226SJulian Elischer * 956878ed226SJulian Elischer * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested 957878ed226SJulian Elischer * connection and it is a first response from the upper layer. 958878ed226SJulian Elischer * if "status == 0" (Accept) then we will send Accept_Connection 959878ed226SJulian Elischer * command and change connection state to W4_CONN_COMPLETE, else 960878ed226SJulian Elischer * send reject and delete connection. 961878ed226SJulian Elischer * 962878ed226SJulian Elischer * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted 963878ed226SJulian Elischer * connection. If "status == 0" we just need to link request 964878ed226SJulian Elischer * and wait, else ignore Reject request. 965878ed226SJulian Elischer */ 966878ed226SJulian Elischer 967878ed226SJulian Elischer LIST_FOREACH(con, &unit->con_list, next) 968878ed226SJulian Elischer if (con->link_type == ep->link_type && 969878ed226SJulian Elischer (con->state == NG_HCI_CON_W4_LP_CON_RSP || 970878ed226SJulian Elischer con->state == NG_HCI_CON_W4_CONN_COMPLETE) && 971878ed226SJulian Elischer bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0) 972878ed226SJulian Elischer break; 973878ed226SJulian Elischer 974878ed226SJulian Elischer if (con == NULL) { 975878ed226SJulian Elischer /* Reject for non-existing connection is fine */ 976878ed226SJulian Elischer error = (ep->status == 0)? ENOENT : 0; 977878ed226SJulian Elischer goto out; 978878ed226SJulian Elischer } 979878ed226SJulian Elischer 980878ed226SJulian Elischer /* 9810986ab12SMaksim Yevmenkin * Remove connection timeout and check connection state. 9820986ab12SMaksim Yevmenkin * Note: if ng_hci_con_untimeout() fails (returns non-zero value) then 9830986ab12SMaksim Yevmenkin * timeout already happened and event went into node's queue. 984878ed226SJulian Elischer */ 985878ed226SJulian Elischer 9860986ab12SMaksim Yevmenkin if ((error = ng_hci_con_untimeout(con)) != 0) 9870986ab12SMaksim Yevmenkin goto out; 988878ed226SJulian Elischer 989878ed226SJulian Elischer switch (con->state) { 990878ed226SJulian Elischer case NG_HCI_CON_W4_LP_CON_RSP: 991878ed226SJulian Elischer 992878ed226SJulian Elischer /* 993878ed226SJulian Elischer * Create HCI command 994878ed226SJulian Elischer */ 995878ed226SJulian Elischer 996eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 997878ed226SJulian Elischer if (m == NULL) { 998878ed226SJulian Elischer error = ENOBUFS; 999878ed226SJulian Elischer goto out; 1000878ed226SJulian Elischer } 1001878ed226SJulian Elischer 1002878ed226SJulian Elischer req = mtod(m, struct con_rsp_req *); 1003878ed226SJulian Elischer req->hdr.type = NG_HCI_CMD_PKT; 1004878ed226SJulian Elischer 1005878ed226SJulian Elischer if (ep->status == 0) { 1006878ed226SJulian Elischer req->hdr.length = sizeof(req->cp.acc); 1007878ed226SJulian Elischer req->hdr.opcode = htole16(NG_HCI_OPCODE( 1008878ed226SJulian Elischer NG_HCI_OGF_LINK_CONTROL, 1009878ed226SJulian Elischer NG_HCI_OCF_ACCEPT_CON)); 1010878ed226SJulian Elischer 1011878ed226SJulian Elischer bcopy(&ep->bdaddr, &req->cp.acc.bdaddr, 1012878ed226SJulian Elischer sizeof(req->cp.acc.bdaddr)); 1013878ed226SJulian Elischer 1014878ed226SJulian Elischer /* 1015878ed226SJulian Elischer * We are accepting connection, so if we support role 1016f2bb1caeSJulian Elischer * switch and role switch was enabled then set role to 1017*77a44875SGordon Bergling * NG_HCI_ROLE_MASTER and let LM perform role switch. 1018f2bb1caeSJulian Elischer * Otherwise we remain slave. In this case LM WILL NOT 1019f2bb1caeSJulian Elischer * perform role switch. 1020878ed226SJulian Elischer */ 1021878ed226SJulian Elischer 1022f2bb1caeSJulian Elischer if ((unit->features[0] & NG_HCI_LMP_SWITCH) && 1023f2bb1caeSJulian Elischer unit->role_switch) 1024878ed226SJulian Elischer req->cp.acc.role = NG_HCI_ROLE_MASTER; 1025878ed226SJulian Elischer else 1026878ed226SJulian Elischer req->cp.acc.role = NG_HCI_ROLE_SLAVE; 1027878ed226SJulian Elischer 1028878ed226SJulian Elischer /* 1029878ed226SJulian Elischer * Adjust connection state 1030878ed226SJulian Elischer */ 1031878ed226SJulian Elischer 1032878ed226SJulian Elischer if (hook == unit->acl) 1033878ed226SJulian Elischer con->flags |= NG_HCI_CON_NOTIFY_ACL; 1034878ed226SJulian Elischer else 1035878ed226SJulian Elischer con->flags |= NG_HCI_CON_NOTIFY_SCO; 1036878ed226SJulian Elischer 1037878ed226SJulian Elischer con->state = NG_HCI_CON_W4_CONN_COMPLETE; 1038878ed226SJulian Elischer ng_hci_con_timeout(con); 1039878ed226SJulian Elischer } else { 1040878ed226SJulian Elischer req->hdr.length = sizeof(req->cp.rej); 1041878ed226SJulian Elischer req->hdr.opcode = htole16(NG_HCI_OPCODE( 1042878ed226SJulian Elischer NG_HCI_OGF_LINK_CONTROL, 1043878ed226SJulian Elischer NG_HCI_OCF_REJECT_CON)); 1044878ed226SJulian Elischer 1045878ed226SJulian Elischer bcopy(&ep->bdaddr, &req->cp.rej.bdaddr, 1046878ed226SJulian Elischer sizeof(req->cp.rej.bdaddr)); 1047878ed226SJulian Elischer 1048878ed226SJulian Elischer req->cp.rej.reason = ep->status; 1049878ed226SJulian Elischer 1050878ed226SJulian Elischer /* 1051878ed226SJulian Elischer * Free connection descritor 1052878ed226SJulian Elischer * Item will be deleted just before return. 1053878ed226SJulian Elischer */ 1054878ed226SJulian Elischer 1055878ed226SJulian Elischer ng_hci_free_con(con); 1056878ed226SJulian Elischer } 1057878ed226SJulian Elischer 1058878ed226SJulian Elischer m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length; 1059878ed226SJulian Elischer 1060878ed226SJulian Elischer /* Queue and send HCI command */ 1061878ed226SJulian Elischer NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 1062878ed226SJulian Elischer if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 1063878ed226SJulian Elischer error = ng_hci_send_command(unit); 1064878ed226SJulian Elischer break; 1065878ed226SJulian Elischer 1066878ed226SJulian Elischer case NG_HCI_CON_W4_CONN_COMPLETE: 1067878ed226SJulian Elischer if (ep->status == 0) { 1068878ed226SJulian Elischer if (hook == unit->acl) 1069878ed226SJulian Elischer con->flags |= NG_HCI_CON_NOTIFY_ACL; 1070878ed226SJulian Elischer else 1071878ed226SJulian Elischer con->flags |= NG_HCI_CON_NOTIFY_SCO; 1072878ed226SJulian Elischer } else 1073878ed226SJulian Elischer error = EPERM; 1074878ed226SJulian Elischer break; 1075878ed226SJulian Elischer 1076878ed226SJulian Elischer default: 10770986ab12SMaksim Yevmenkin panic( 10780986ab12SMaksim Yevmenkin "%s: %s - Invalid connection state=%d\n", 10790986ab12SMaksim Yevmenkin __func__, NG_NODE_NAME(unit->node), con->state); 1080878ed226SJulian Elischer break; 1081878ed226SJulian Elischer } 1082878ed226SJulian Elischer out: 1083878ed226SJulian Elischer NG_FREE_ITEM(item); 1084878ed226SJulian Elischer 1085878ed226SJulian Elischer return (error); 1086878ed226SJulian Elischer } /* ng_hci_lp_con_rsp */ 1087878ed226SJulian Elischer 1088878ed226SJulian Elischer /* 1089878ed226SJulian Elischer * Send LP_DisconnectInd to the upper layer protocol 1090878ed226SJulian Elischer */ 1091878ed226SJulian Elischer 1092878ed226SJulian Elischer int 1093878ed226SJulian Elischer ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason) 1094878ed226SJulian Elischer { 1095878ed226SJulian Elischer ng_hci_unit_p unit = con->unit; 1096878ed226SJulian Elischer struct ng_mesg *msg = NULL; 1097878ed226SJulian Elischer ng_hci_lp_discon_ind_ep *ep = NULL; 1098878ed226SJulian Elischer int error = 0; 1099878ed226SJulian Elischer 1100878ed226SJulian Elischer /* 1101878ed226SJulian Elischer * Disconnect_Complete event is generated for specific connection 1102878ed226SJulian Elischer * handle. For ACL connection handles both ACL and SCO upstream 1103878ed226SJulian Elischer * hooks will receive notification. For SCO connection handles 1104878ed226SJulian Elischer * only SCO upstream hook will receive notification. 1105878ed226SJulian Elischer */ 1106878ed226SJulian Elischer 1107fbc48c2bSTakanori Watanabe if (con->link_type != NG_HCI_LINK_SCO) { 1108878ed226SJulian Elischer if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 1109878ed226SJulian Elischer NG_MKMESSAGE(msg, NGM_HCI_COOKIE, 1110878ed226SJulian Elischer NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT); 1111878ed226SJulian Elischer if (msg == NULL) 1112878ed226SJulian Elischer return (ENOMEM); 1113878ed226SJulian Elischer 1114878ed226SJulian Elischer ep = (ng_hci_lp_discon_ind_ep *) msg->data; 1115878ed226SJulian Elischer ep->reason = reason; 1116878ed226SJulian Elischer ep->link_type = con->link_type; 1117878ed226SJulian Elischer ep->con_handle = con->con_handle; 1118878ed226SJulian Elischer 11194ae439a3SMaksim Yevmenkin NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,0); 1120878ed226SJulian Elischer } else 1121878ed226SJulian Elischer NG_HCI_INFO( 1122878ed226SJulian Elischer "%s: %s - ACL hook is not connected or not valid, hook=%p\n", 1123878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), unit->acl); 1124878ed226SJulian Elischer } 1125878ed226SJulian Elischer 1126878ed226SJulian Elischer if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 1127878ed226SJulian Elischer NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND, 1128878ed226SJulian Elischer sizeof(*ep), M_NOWAIT); 1129878ed226SJulian Elischer if (msg == NULL) 1130878ed226SJulian Elischer return (ENOMEM); 1131878ed226SJulian Elischer 1132878ed226SJulian Elischer ep = (ng_hci_lp_discon_ind_ep *) msg->data; 1133878ed226SJulian Elischer ep->reason = reason; 1134878ed226SJulian Elischer ep->link_type = con->link_type; 1135878ed226SJulian Elischer ep->con_handle = con->con_handle; 1136878ed226SJulian Elischer 11374ae439a3SMaksim Yevmenkin NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0); 1138878ed226SJulian Elischer } else 1139878ed226SJulian Elischer NG_HCI_INFO( 1140878ed226SJulian Elischer "%s: %s - SCO hook is not connected or not valid, hook=%p\n", 1141878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), unit->sco); 1142878ed226SJulian Elischer 1143878ed226SJulian Elischer return (0); 1144878ed226SJulian Elischer } /* ng_hci_lp_discon_ind */ 1145878ed226SJulian Elischer 1146878ed226SJulian Elischer /* 1147878ed226SJulian Elischer * Process LP_QoSReq action from the upper layer protocol 1148878ed226SJulian Elischer */ 1149878ed226SJulian Elischer 1150878ed226SJulian Elischer int 1151878ed226SJulian Elischer ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook) 1152878ed226SJulian Elischer { 1153878ed226SJulian Elischer struct qos_setup_req { 1154878ed226SJulian Elischer ng_hci_cmd_pkt_t hdr; 1155878ed226SJulian Elischer ng_hci_qos_setup_cp cp; 1156878ed226SJulian Elischer } __attribute__ ((packed)) *req = NULL; 1157878ed226SJulian Elischer ng_hci_lp_qos_req_ep *ep = NULL; 1158878ed226SJulian Elischer ng_hci_unit_con_p con = NULL; 1159878ed226SJulian Elischer struct mbuf *m = NULL; 1160878ed226SJulian Elischer int error = 0; 1161878ed226SJulian Elischer 1162878ed226SJulian Elischer /* Check if unit is ready */ 1163878ed226SJulian Elischer if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) { 1164878ed226SJulian Elischer NG_HCI_WARN( 1165878ed226SJulian Elischer "%s: %s - unit is not ready, state=%#x\n", 1166878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), unit->state); 1167878ed226SJulian Elischer 1168878ed226SJulian Elischer error = ENXIO; 1169878ed226SJulian Elischer goto out; 1170878ed226SJulian Elischer } 1171878ed226SJulian Elischer 1172878ed226SJulian Elischer if (NGI_MSG(item)->header.arglen != sizeof(*ep)) { 1173878ed226SJulian Elischer NG_HCI_ALERT( 1174878ed226SJulian Elischer "%s: %s - invalid LP_QoSSetupReq message size=%d\n", 1175878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), 1176878ed226SJulian Elischer NGI_MSG(item)->header.arglen); 1177878ed226SJulian Elischer 1178878ed226SJulian Elischer error = EMSGSIZE; 1179878ed226SJulian Elischer goto out; 1180878ed226SJulian Elischer } 1181878ed226SJulian Elischer 1182878ed226SJulian Elischer ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data); 1183878ed226SJulian Elischer 1184878ed226SJulian Elischer con = ng_hci_con_by_handle(unit, ep->con_handle); 1185878ed226SJulian Elischer if (con == NULL) { 1186878ed226SJulian Elischer NG_HCI_ERR( 1187878ed226SJulian Elischer "%s: %s - invalid connection handle=%d\n", 1188878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), ep->con_handle); 1189878ed226SJulian Elischer 1190878ed226SJulian Elischer error = EINVAL; 1191878ed226SJulian Elischer goto out; 1192878ed226SJulian Elischer } 1193878ed226SJulian Elischer 1194878ed226SJulian Elischer if (con->link_type != NG_HCI_LINK_ACL) { 1195878ed226SJulian Elischer NG_HCI_ERR("%s: %s - invalid link type=%d\n", 1196878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), con->link_type); 1197878ed226SJulian Elischer 1198878ed226SJulian Elischer error = EINVAL; 1199878ed226SJulian Elischer goto out; 1200878ed226SJulian Elischer } 1201878ed226SJulian Elischer 1202878ed226SJulian Elischer if (con->state != NG_HCI_CON_OPEN) { 1203878ed226SJulian Elischer NG_HCI_ERR( 1204878ed226SJulian Elischer "%s: %s - invalid connection state=%d, handle=%d\n", 1205878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), con->state, 1206878ed226SJulian Elischer con->con_handle); 1207878ed226SJulian Elischer 1208878ed226SJulian Elischer error = EINVAL; 1209878ed226SJulian Elischer goto out; 1210878ed226SJulian Elischer } 1211878ed226SJulian Elischer 1212878ed226SJulian Elischer /* 1213878ed226SJulian Elischer * Create HCI command 1214878ed226SJulian Elischer */ 1215878ed226SJulian Elischer 1216eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 1217878ed226SJulian Elischer if (m == NULL) { 1218878ed226SJulian Elischer error = ENOBUFS; 1219878ed226SJulian Elischer goto out; 1220878ed226SJulian Elischer } 1221878ed226SJulian Elischer 1222878ed226SJulian Elischer m->m_pkthdr.len = m->m_len = sizeof(*req); 1223878ed226SJulian Elischer req = mtod(m, struct qos_setup_req *); 1224878ed226SJulian Elischer req->hdr.type = NG_HCI_CMD_PKT; 1225878ed226SJulian Elischer req->hdr.length = sizeof(req->cp); 1226878ed226SJulian Elischer req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY, 1227878ed226SJulian Elischer NG_HCI_OCF_QOS_SETUP)); 1228878ed226SJulian Elischer 1229878ed226SJulian Elischer req->cp.con_handle = htole16(ep->con_handle); 1230878ed226SJulian Elischer req->cp.flags = ep->flags; 1231878ed226SJulian Elischer req->cp.service_type = ep->service_type; 1232878ed226SJulian Elischer req->cp.token_rate = htole32(ep->token_rate); 1233878ed226SJulian Elischer req->cp.peak_bandwidth = htole32(ep->peak_bandwidth); 1234878ed226SJulian Elischer req->cp.latency = htole32(ep->latency); 1235878ed226SJulian Elischer req->cp.delay_variation = htole32(ep->delay_variation); 1236878ed226SJulian Elischer 1237878ed226SJulian Elischer /* 1238878ed226SJulian Elischer * Adjust connection state 1239878ed226SJulian Elischer */ 1240878ed226SJulian Elischer 1241878ed226SJulian Elischer if (hook == unit->acl) 1242878ed226SJulian Elischer con->flags |= NG_HCI_CON_NOTIFY_ACL; 1243878ed226SJulian Elischer else 1244878ed226SJulian Elischer con->flags |= NG_HCI_CON_NOTIFY_SCO; 1245878ed226SJulian Elischer 1246878ed226SJulian Elischer /* 1247878ed226SJulian Elischer * Queue and send HCI command 1248878ed226SJulian Elischer */ 1249878ed226SJulian Elischer 1250878ed226SJulian Elischer NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m); 1251878ed226SJulian Elischer if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) 1252878ed226SJulian Elischer error = ng_hci_send_command(unit); 1253878ed226SJulian Elischer out: 1254878ed226SJulian Elischer NG_FREE_ITEM(item); 1255878ed226SJulian Elischer 1256878ed226SJulian Elischer return (error); 1257878ed226SJulian Elischer } /* ng_hci_lp_qos_req */ 1258878ed226SJulian Elischer 1259878ed226SJulian Elischer /* 1260878ed226SJulian Elischer * Send LP_QoSCfm event to the upper layer protocol 1261878ed226SJulian Elischer */ 1262878ed226SJulian Elischer 1263878ed226SJulian Elischer int 1264878ed226SJulian Elischer ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status) 1265878ed226SJulian Elischer { 1266878ed226SJulian Elischer ng_hci_unit_p unit = con->unit; 1267878ed226SJulian Elischer struct ng_mesg *msg = NULL; 1268878ed226SJulian Elischer ng_hci_lp_qos_cfm_ep *ep = NULL; 1269878ed226SJulian Elischer int error; 1270878ed226SJulian Elischer 1271878ed226SJulian Elischer if (con->flags & NG_HCI_CON_NOTIFY_ACL) { 1272878ed226SJulian Elischer if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 1273878ed226SJulian Elischer NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, 1274878ed226SJulian Elischer sizeof(*ep), M_NOWAIT); 1275878ed226SJulian Elischer if (msg != NULL) { 1276878ed226SJulian Elischer ep = (ng_hci_lp_qos_cfm_ep *) msg->data; 1277878ed226SJulian Elischer ep->status = status; 1278878ed226SJulian Elischer ep->con_handle = con->con_handle; 1279878ed226SJulian Elischer 1280878ed226SJulian Elischer NG_SEND_MSG_HOOK(error, unit->node, msg, 12814ae439a3SMaksim Yevmenkin unit->acl, 0); 1282878ed226SJulian Elischer } 1283878ed226SJulian Elischer } else 1284878ed226SJulian Elischer NG_HCI_INFO( 1285878ed226SJulian Elischer "%s: %s - ACL hook not valid, hook=%p\n", 1286878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), unit->acl); 1287878ed226SJulian Elischer 1288878ed226SJulian Elischer con->flags &= ~NG_HCI_CON_NOTIFY_ACL; 1289878ed226SJulian Elischer } 1290878ed226SJulian Elischer 1291878ed226SJulian Elischer if (con->flags & NG_HCI_CON_NOTIFY_SCO) { 1292878ed226SJulian Elischer if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 1293878ed226SJulian Elischer NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM, 1294878ed226SJulian Elischer sizeof(*ep), M_NOWAIT); 1295878ed226SJulian Elischer if (msg != NULL) { 1296878ed226SJulian Elischer ep = (ng_hci_lp_qos_cfm_ep *) msg->data; 1297878ed226SJulian Elischer ep->status = status; 1298878ed226SJulian Elischer ep->con_handle = con->con_handle; 1299878ed226SJulian Elischer 1300878ed226SJulian Elischer NG_SEND_MSG_HOOK(error, unit->node, msg, 13014ae439a3SMaksim Yevmenkin unit->sco, 0); 1302878ed226SJulian Elischer } 1303878ed226SJulian Elischer } else 1304878ed226SJulian Elischer NG_HCI_INFO( 1305878ed226SJulian Elischer "%s: %s - SCO hook not valid, hook=%p\n", 1306878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), unit->sco); 1307878ed226SJulian Elischer 1308878ed226SJulian Elischer con->flags &= ~NG_HCI_CON_NOTIFY_SCO; 1309878ed226SJulian Elischer } 1310878ed226SJulian Elischer 1311878ed226SJulian Elischer return (0); 1312878ed226SJulian Elischer } /* ng_hci_lp_qos_cfm */ 1313878ed226SJulian Elischer 1314878ed226SJulian Elischer /* 1315878ed226SJulian Elischer * Send LP_QoSViolationInd event to the upper layer protocol 1316878ed226SJulian Elischer */ 1317878ed226SJulian Elischer 1318878ed226SJulian Elischer int 1319878ed226SJulian Elischer ng_hci_lp_qos_ind(ng_hci_unit_con_p con) 1320878ed226SJulian Elischer { 1321878ed226SJulian Elischer ng_hci_unit_p unit = con->unit; 1322878ed226SJulian Elischer struct ng_mesg *msg = NULL; 1323878ed226SJulian Elischer ng_hci_lp_qos_ind_ep *ep = NULL; 1324878ed226SJulian Elischer int error; 1325878ed226SJulian Elischer 1326878ed226SJulian Elischer /* 1327878ed226SJulian Elischer * QoS Violation can only be generated for ACL connection handles. 1328878ed226SJulian Elischer * Both ACL and SCO upstream hooks will receive notification. 1329878ed226SJulian Elischer */ 1330878ed226SJulian Elischer 1331878ed226SJulian Elischer if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) { 1332878ed226SJulian Elischer NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, 1333878ed226SJulian Elischer sizeof(*ep), M_NOWAIT); 1334878ed226SJulian Elischer if (msg == NULL) 1335878ed226SJulian Elischer return (ENOMEM); 1336878ed226SJulian Elischer 1337878ed226SJulian Elischer ep = (ng_hci_lp_qos_ind_ep *) msg->data; 1338878ed226SJulian Elischer ep->con_handle = con->con_handle; 1339878ed226SJulian Elischer 13404ae439a3SMaksim Yevmenkin NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0); 1341878ed226SJulian Elischer } else 1342878ed226SJulian Elischer NG_HCI_INFO( 1343878ed226SJulian Elischer "%s: %s - ACL hook is not connected or not valid, hook=%p\n", 1344878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), unit->acl); 1345878ed226SJulian Elischer 1346878ed226SJulian Elischer if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) { 1347878ed226SJulian Elischer NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND, 1348878ed226SJulian Elischer sizeof(*ep), M_NOWAIT); 1349878ed226SJulian Elischer if (msg == NULL) 1350878ed226SJulian Elischer return (ENOMEM); 1351878ed226SJulian Elischer 1352878ed226SJulian Elischer ep = (ng_hci_lp_qos_ind_ep *) msg->data; 1353878ed226SJulian Elischer ep->con_handle = con->con_handle; 1354878ed226SJulian Elischer 13554ae439a3SMaksim Yevmenkin NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0); 1356878ed226SJulian Elischer } else 1357878ed226SJulian Elischer NG_HCI_INFO( 1358878ed226SJulian Elischer "%s: %s - SCO hook is not connected or not valid, hook=%p\n", 1359878ed226SJulian Elischer __func__, NG_NODE_NAME(unit->node), unit->sco); 1360878ed226SJulian Elischer 1361878ed226SJulian Elischer return (0); 1362878ed226SJulian Elischer } /* ng_hci_lp_qos_ind */ 1363878ed226SJulian Elischer 1364878ed226SJulian Elischer /* 1365878ed226SJulian Elischer * Process connection timeout 1366878ed226SJulian Elischer */ 1367878ed226SJulian Elischer 1368878ed226SJulian Elischer void 13690986ab12SMaksim Yevmenkin ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int con_handle) 1370878ed226SJulian Elischer { 13710986ab12SMaksim Yevmenkin ng_hci_unit_p unit = NULL; 13720986ab12SMaksim Yevmenkin ng_hci_unit_con_p con = NULL; 1373878ed226SJulian Elischer 13740986ab12SMaksim Yevmenkin if (NG_NODE_NOT_VALID(node)) { 13750986ab12SMaksim Yevmenkin printf("%s: Netgraph node is not valid\n", __func__); 13760986ab12SMaksim Yevmenkin return; 13770986ab12SMaksim Yevmenkin } 13780986ab12SMaksim Yevmenkin 13790986ab12SMaksim Yevmenkin unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); 13800986ab12SMaksim Yevmenkin con = ng_hci_con_by_handle(unit, con_handle); 13810986ab12SMaksim Yevmenkin 13820986ab12SMaksim Yevmenkin if (con == NULL) { 13830986ab12SMaksim Yevmenkin NG_HCI_ALERT( 13840986ab12SMaksim Yevmenkin "%s: %s - could not find connection, handle=%d\n", 13850986ab12SMaksim Yevmenkin __func__, NG_NODE_NAME(node), con_handle); 13860986ab12SMaksim Yevmenkin return; 13870986ab12SMaksim Yevmenkin } 13880986ab12SMaksim Yevmenkin 13890986ab12SMaksim Yevmenkin if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) { 13900986ab12SMaksim Yevmenkin NG_HCI_ALERT( 13910986ab12SMaksim Yevmenkin "%s: %s - no pending connection timeout, handle=%d, state=%d, flags=%#x\n", 13920986ab12SMaksim Yevmenkin __func__, NG_NODE_NAME(node), con_handle, con->state, 13930986ab12SMaksim Yevmenkin con->flags); 13940986ab12SMaksim Yevmenkin return; 13950986ab12SMaksim Yevmenkin } 1396878ed226SJulian Elischer 1397878ed226SJulian Elischer con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING; 1398878ed226SJulian Elischer 1399878ed226SJulian Elischer /* 1400878ed226SJulian Elischer * We expect to receive connection timeout in one of the following 1401878ed226SJulian Elischer * states: 1402878ed226SJulian Elischer * 1403f2bb1caeSJulian Elischer * 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded 1404878ed226SJulian Elischer * to our LP_CON_IND. Do nothing and destroy connection. Remote peer 1405878ed226SJulian Elischer * most likely already gave up on us. 1406878ed226SJulian Elischer * 1407f2bb1caeSJulian Elischer * 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection 1408878ed226SJulian Elischer * (or we in the process of accepting it) and baseband has timedout 1409878ed226SJulian Elischer * on us. Inform upper layers and send LP_CON_CFM. 1410878ed226SJulian Elischer */ 1411878ed226SJulian Elischer 1412878ed226SJulian Elischer switch (con->state) { 1413878ed226SJulian Elischer case NG_HCI_CON_W4_LP_CON_RSP: 1414878ed226SJulian Elischer break; 1415878ed226SJulian Elischer 1416878ed226SJulian Elischer case NG_HCI_CON_W4_CONN_COMPLETE: 1417878ed226SJulian Elischer ng_hci_lp_con_cfm(con, 0xee); 1418878ed226SJulian Elischer break; 1419878ed226SJulian Elischer 1420878ed226SJulian Elischer default: 14210986ab12SMaksim Yevmenkin panic( 14220986ab12SMaksim Yevmenkin "%s: %s - Invalid connection state=%d\n", 14230986ab12SMaksim Yevmenkin __func__, NG_NODE_NAME(node), con->state); 1424878ed226SJulian Elischer break; 1425878ed226SJulian Elischer } 1426878ed226SJulian Elischer 1427878ed226SJulian Elischer ng_hci_free_con(con); 1428878ed226SJulian Elischer } /* ng_hci_process_con_timeout */ 1429