Path: blob/main/sys/netgraph/bluetooth/hci/ng_hci_ulpi.c
34814 views
/*1* ng_hci_ulpi.c2*/34/*-5* SPDX-License-Identifier: BSD-2-Clause6*7* Copyright (c) Maksim Yevmenkin <[email protected]>8* All rights reserved.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18*19* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND20* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE21* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE22* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE23* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL24* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS25* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT27* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY28* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF29* SUCH DAMAGE.30*31* $Id: ng_hci_ulpi.c,v 1.7 2003/09/08 18:57:51 max Exp $32*/3334#include <sys/param.h>35#include <sys/systm.h>36#include <sys/kernel.h>37#include <sys/endian.h>38#include <sys/malloc.h>39#include <sys/mbuf.h>40#include <sys/queue.h>41#include <netgraph/ng_message.h>42#include <netgraph/netgraph.h>43#include <netgraph/bluetooth/include/ng_bluetooth.h>44#include <netgraph/bluetooth/include/ng_hci.h>45#include <netgraph/bluetooth/hci/ng_hci_var.h>46#include <netgraph/bluetooth/hci/ng_hci_cmds.h>47#include <netgraph/bluetooth/hci/ng_hci_evnt.h>48#include <netgraph/bluetooth/hci/ng_hci_ulpi.h>49#include <netgraph/bluetooth/hci/ng_hci_misc.h>5051/******************************************************************************52******************************************************************************53** Upper Layer Protocol Interface module54******************************************************************************55******************************************************************************/5657static int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p);58static int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p);59static int ng_hci_lp_le_con_req (ng_hci_unit_p, item_p, hook_p, int);6061/*62* Process LP_ConnectReq event from the upper layer protocol63*/6465int66ng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)67{68int link_type;6970if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {71NG_HCI_WARN(72"%s: %s - unit is not ready, state=%#x\n",73__func__, NG_NODE_NAME(unit->node), unit->state);7475NG_FREE_ITEM(item);7677return (ENXIO);78}7980if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) {81NG_HCI_ALERT(82"%s: %s - invalid LP_ConnectReq message size=%d\n",83__func__, NG_NODE_NAME(unit->node),84NGI_MSG(item)->header.arglen);8586NG_FREE_ITEM(item);8788return (EMSGSIZE);89}90link_type = ((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type;91switch(link_type){92case NG_HCI_LINK_ACL:93return (ng_hci_lp_acl_con_req(unit, item, hook));94case NG_HCI_LINK_SCO:95if (hook != unit->sco ) {96NG_HCI_WARN(97"%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n",98__func__, NG_NODE_NAME(unit->node), hook);99100NG_FREE_ITEM(item);101102return (EINVAL);103}104105return (ng_hci_lp_sco_con_req(unit, item, hook));106case NG_HCI_LINK_LE_PUBLIC:107case NG_HCI_LINK_LE_RANDOM:108return (ng_hci_lp_le_con_req(unit, item, hook, link_type));109default:110panic("%s: link_type invalid.", __func__);111}112113return (EINVAL);114} /* ng_hci_lp_con_req */115116/*117* Request to create new ACL connection118*/119120static int121ng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)122{123struct acl_con_req {124ng_hci_cmd_pkt_t hdr;125ng_hci_create_con_cp cp;126} __attribute__ ((packed)) *req = NULL;127ng_hci_lp_con_req_ep *ep = NULL;128ng_hci_unit_con_p con = NULL;129ng_hci_neighbor_t *n = NULL;130struct mbuf *m = NULL;131int error = 0;132133ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);134135/*136* Only one ACL connection can exist between each pair of units.137* So try to find ACL connection descriptor (in any state) that138* has requested remote BD_ADDR.139*140* Two cases:141*142* 1) We do not have connection to the remote unit. This is simple.143* Just create new connection descriptor and send HCI command to144* create new connection.145*146* 2) We do have connection descriptor. We need to check connection147* state:148*149* 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of150* accepting connection from the remote unit. This is a race151* condition. We will ignore this message.152*153* 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already154* requested connection or we just accepted it. In any case155* all we need to do here is set appropriate notification bit156* and wait.157*158* 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back159* and let upper layer know that we have connection already.160*/161162con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);163if (con != NULL) {164switch (con->state) {165case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */166error = EALREADY;167break;168169case NG_HCI_CON_W4_CONN_COMPLETE:170if (hook == unit->acl)171con->flags |= NG_HCI_CON_NOTIFY_ACL;172else173con->flags |= NG_HCI_CON_NOTIFY_SCO;174break;175176case NG_HCI_CON_OPEN: {177struct ng_mesg *msg = NULL;178ng_hci_lp_con_cfm_ep *cfm = NULL;179180if (hook != NULL && NG_HOOK_IS_VALID(hook)) {181NGI_GET_MSG(item, msg);182NG_FREE_MSG(msg);183184NG_MKMESSAGE(msg, NGM_HCI_COOKIE,185NGM_HCI_LP_CON_CFM, sizeof(*cfm),186M_NOWAIT);187if (msg != NULL) {188cfm = (ng_hci_lp_con_cfm_ep *)msg->data;189cfm->status = 0;190cfm->link_type = con->link_type;191cfm->con_handle = con->con_handle;192bcopy(&con->bdaddr, &cfm->bdaddr,193sizeof(cfm->bdaddr));194195/*196* This will forward item back to197* sender and set item to NULL198*/199200_NGI_MSG(item) = msg;201NG_FWD_ITEM_HOOK(error, item, hook);202} else203error = ENOMEM;204} else205NG_HCI_INFO(206"%s: %s - Source hook is not valid, hook=%p\n",207__func__, NG_NODE_NAME(unit->node),208hook);209} break;210211default:212panic(213"%s: %s - Invalid connection state=%d\n",214__func__, NG_NODE_NAME(unit->node), con->state);215break;216}217218goto out;219}220221/*222* If we got here then we need to create new ACL connection descriptor223* and submit HCI command. First create new connection desriptor, set224* bdaddr and notification flags.225*/226227con = ng_hci_new_con(unit, NG_HCI_LINK_ACL);228if (con == NULL) {229error = ENOMEM;230goto out;231}232233bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));234235/*236* Create HCI command237*/238239MGETHDR(m, M_NOWAIT, MT_DATA);240if (m == NULL) {241ng_hci_free_con(con);242error = ENOBUFS;243goto out;244}245246m->m_pkthdr.len = m->m_len = sizeof(*req);247req = mtod(m, struct acl_con_req *);248req->hdr.type = NG_HCI_CMD_PKT;249req->hdr.length = sizeof(req->cp);250req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,251NG_HCI_OCF_CREATE_CON));252253bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr));254255req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);256if (unit->features[0] & NG_HCI_LMP_3SLOT)257req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3);258if (unit->features[0] & NG_HCI_LMP_5SLOT)259req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5);260261req->cp.pkt_type &= unit->packet_mask;262if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1|263NG_HCI_PKT_DM3|NG_HCI_PKT_DH3|264NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0)265req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);266267req->cp.pkt_type = htole16(req->cp.pkt_type);268269if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch)270req->cp.accept_role_switch = 1;271else272req->cp.accept_role_switch = 0;273274/*275* We may speed up connect by specifying valid parameters.276* So check the neighbor cache.277*/278279n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL);280if (n == NULL) {281req->cp.page_scan_rep_mode = 0;282req->cp.page_scan_mode = 0;283req->cp.clock_offset = 0;284} else {285req->cp.page_scan_rep_mode = n->page_scan_rep_mode;286req->cp.page_scan_mode = n->page_scan_mode;287req->cp.clock_offset = htole16(n->clock_offset);288}289290/*291* Adust connection state292*/293294if (hook == unit->acl)295con->flags |= NG_HCI_CON_NOTIFY_ACL;296else297con->flags |= NG_HCI_CON_NOTIFY_SCO;298299con->state = NG_HCI_CON_W4_CONN_COMPLETE;300ng_hci_con_timeout(con);301302/*303* Queue and send HCI command304*/305306NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);307if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))308error = ng_hci_send_command(unit);309out:310if (item != NULL)311NG_FREE_ITEM(item);312313return (error);314} /* ng_hci_lp_acl_con_req */315316/*317* Request to create new SCO connection318*/319320static int321ng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)322{323struct sco_con_req {324ng_hci_cmd_pkt_t hdr;325ng_hci_add_sco_con_cp cp;326} __attribute__ ((packed)) *req = NULL;327ng_hci_lp_con_req_ep *ep = NULL;328ng_hci_unit_con_p acl_con = NULL, sco_con = NULL;329struct mbuf *m = NULL;330int error = 0;331332ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);333334/*335* SCO connection without ACL link336*337* If upper layer requests SCO connection and there is no open ACL338* connection to the desired remote unit, we will reject the request.339*/340341LIST_FOREACH(acl_con, &unit->con_list, next)342if (acl_con->link_type == NG_HCI_LINK_ACL &&343acl_con->state == NG_HCI_CON_OPEN &&344bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)345break;346347if (acl_con == NULL) {348NG_HCI_INFO(349"%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n",350__func__, NG_NODE_NAME(unit->node),351ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],352ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);353354error = ENOENT;355goto out;356}357358/*359* Multiple SCO connections can exist between the same pair of units.360* We assume that multiple SCO connections have to be opened one after361* another.362*363* Try to find SCO connection descriptor that matches the following:364*365* 1) sco_con->link_type == NG_HCI_LINK_SCO366*367* 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||368* sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE369*370* 3) sco_con->bdaddr == ep->bdaddr371*372* Two cases:373*374* 1) We do not have connection descriptor. This is simple. Just375* create new connection and submit Add_SCO_Connection command.376*377* 2) We do have connection descriptor. We need to check the state.378*379* 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting380* connection from the remote unit. This is a race condition and381* we will ignore the request.382*383* 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested384* connection or we just accepted it.385*/386387LIST_FOREACH(sco_con, &unit->con_list, next)388if (sco_con->link_type == NG_HCI_LINK_SCO &&389(sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||390sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&391bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)392break;393394if (sco_con != NULL) {395switch (sco_con->state) {396case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */397error = EALREADY;398break;399400case NG_HCI_CON_W4_CONN_COMPLETE:401sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;402break;403404default:405panic(406"%s: %s - Invalid connection state=%d\n",407__func__, NG_NODE_NAME(unit->node),408sco_con->state);409break;410}411412goto out;413}414415/*416* If we got here then we need to create new SCO connection descriptor417* and submit HCI command.418*/419420sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO);421if (sco_con == NULL) {422error = ENOMEM;423goto out;424}425426bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr));427428/*429* Create HCI command430*/431432MGETHDR(m, M_NOWAIT, MT_DATA);433if (m == NULL) {434ng_hci_free_con(sco_con);435error = ENOBUFS;436goto out;437}438439m->m_pkthdr.len = m->m_len = sizeof(*req);440req = mtod(m, struct sco_con_req *);441req->hdr.type = NG_HCI_CMD_PKT;442req->hdr.length = sizeof(req->cp);443req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,444NG_HCI_OCF_ADD_SCO_CON));445446req->cp.con_handle = htole16(acl_con->con_handle);447448req->cp.pkt_type = NG_HCI_PKT_HV1;449if (unit->features[1] & NG_HCI_LMP_HV2_PKT)450req->cp.pkt_type |= NG_HCI_PKT_HV2;451if (unit->features[1] & NG_HCI_LMP_HV3_PKT)452req->cp.pkt_type |= NG_HCI_PKT_HV3;453454req->cp.pkt_type &= unit->packet_mask;455if ((req->cp.pkt_type & (NG_HCI_PKT_HV1|456NG_HCI_PKT_HV2|457NG_HCI_PKT_HV3)) == 0)458req->cp.pkt_type = NG_HCI_PKT_HV1;459460req->cp.pkt_type = htole16(req->cp.pkt_type);461462/*463* Adust connection state464*/465466sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;467468sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE;469ng_hci_con_timeout(sco_con);470471/*472* Queue and send HCI command473*/474475NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);476if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))477error = ng_hci_send_command(unit);478out:479NG_FREE_ITEM(item);480481return (error);482} /* ng_hci_lp_sco_con_req */483484static int485ng_hci_lp_le_con_req(ng_hci_unit_p unit, item_p item, hook_p hook, int link_type)486{487struct acl_con_req {488ng_hci_cmd_pkt_t hdr;489ng_hci_le_create_connection_cp cp;490} __attribute__ ((packed)) *req = NULL;491ng_hci_lp_con_req_ep *ep = NULL;492ng_hci_unit_con_p con = NULL;493struct mbuf *m = NULL;494int error = 0;495496ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);497if((link_type != NG_HCI_LINK_LE_PUBLIC)&&498(link_type != NG_HCI_LINK_LE_RANDOM)){499printf("%s: Link type %d Cannot be here \n", __func__,500link_type);501}502/*503* Only one ACL connection can exist between each pair of units.504* So try to find ACL connection descriptor (in any state) that505* has requested remote BD_ADDR.506*507* Two cases:508*509* 1) We do not have connection to the remote unit. This is simple.510* Just create new connection descriptor and send HCI command to511* create new connection.512*513* 2) We do have connection descriptor. We need to check connection514* state:515*516* 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of517* accepting connection from the remote unit. This is a race518* condition. We will ignore this message.519*520* 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already521* requested connection or we just accepted it. In any case522* all we need to do here is set appropriate notification bit523* and wait.524*525* 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back526* and let upper layer know that we have connection already.527*/528529con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, link_type);530if (con != NULL) {531switch (con->state) {532case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */533error = EALREADY;534break;535536case NG_HCI_CON_W4_CONN_COMPLETE:537if (hook != unit->sco)538con->flags |= NG_HCI_CON_NOTIFY_ACL;539else540con->flags |= NG_HCI_CON_NOTIFY_SCO;541break;542543case NG_HCI_CON_OPEN: {544struct ng_mesg *msg = NULL;545ng_hci_lp_con_cfm_ep *cfm = NULL;546547if (hook != NULL && NG_HOOK_IS_VALID(hook)) {548NGI_GET_MSG(item, msg);549NG_FREE_MSG(msg);550551NG_MKMESSAGE(msg, NGM_HCI_COOKIE,552NGM_HCI_LP_CON_CFM, sizeof(*cfm),553M_NOWAIT);554if (msg != NULL) {555cfm = (ng_hci_lp_con_cfm_ep *)msg->data;556cfm->status = 0;557cfm->link_type = con->link_type;558cfm->con_handle = con->con_handle;559bcopy(&con->bdaddr, &cfm->bdaddr,560sizeof(cfm->bdaddr));561562/*563* This will forward item back to564* sender and set item to NULL565*/566567_NGI_MSG(item) = msg;568NG_FWD_ITEM_HOOK(error, item, hook);569} else570error = ENOMEM;571} else572NG_HCI_INFO(573"%s: %s - Source hook is not valid, hook=%p\n",574__func__, NG_NODE_NAME(unit->node),575hook);576} break;577578default:579panic(580"%s: %s - Invalid connection state=%d\n",581__func__, NG_NODE_NAME(unit->node), con->state);582break;583}584585goto out;586}587588/*589* If we got here then we need to create new ACL connection descriptor590* and submit HCI command. First create new connection desriptor, set591* bdaddr and notification flags.592*/593594con = ng_hci_new_con(unit, link_type);595if (con == NULL) {596error = ENOMEM;597goto out;598}599600bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));601602/*603* Create HCI command604*/605606MGETHDR(m, M_NOWAIT, MT_DATA);607if (m == NULL) {608ng_hci_free_con(con);609error = ENOBUFS;610goto out;611}612613m->m_pkthdr.len = m->m_len = sizeof(*req);614req = mtod(m, struct acl_con_req *);615req->hdr.type = NG_HCI_CMD_PKT;616req->hdr.length = sizeof(req->cp);617req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LE,618NG_HCI_OCF_LE_CREATE_CONNECTION));619620bcopy(&ep->bdaddr, &req->cp.peer_addr, sizeof(req->cp.peer_addr));621req->cp.own_address_type = 0;622req->cp.peer_addr_type = (link_type == NG_HCI_LINK_LE_RANDOM)? 1:0;623req->cp.scan_interval = htole16(4);624req->cp.scan_window = htole16(4);625req->cp.filter_policy = 0;626req->cp.conn_interval_min = htole16(0xf);627req->cp.conn_interval_max = htole16(0xf);628req->cp.conn_latency = htole16(0);629req->cp.supervision_timeout = htole16(0xc80);630req->cp.min_ce_length = htole16(1);631req->cp.max_ce_length = htole16(1);632/*633* Adust connection state634*/635636if (hook != unit->sco)637con->flags |= NG_HCI_CON_NOTIFY_ACL;638else639con->flags |= NG_HCI_CON_NOTIFY_SCO;640641con->state = NG_HCI_CON_W4_CONN_COMPLETE;642ng_hci_con_timeout(con);643644/*645* Queue and send HCI command646*/647648NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);649if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))650error = ng_hci_send_command(unit);651out:652if (item != NULL)653NG_FREE_ITEM(item);654655return (error);656} /* ng_hci_lp_acl_con_req */657658/*659* Process LP_DisconnectReq event from the upper layer protocol660*/661662int663ng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook)664{665struct discon_req {666ng_hci_cmd_pkt_t hdr;667ng_hci_discon_cp cp;668} __attribute__ ((packed)) *req = NULL;669ng_hci_lp_discon_req_ep *ep = NULL;670ng_hci_unit_con_p con = NULL;671struct mbuf *m = NULL;672int error = 0;673674/* Check if unit is ready */675if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {676NG_HCI_WARN(677"%s: %s - unit is not ready, state=%#x\n",678__func__, NG_NODE_NAME(unit->node), unit->state);679680error = ENXIO;681goto out;682}683684if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {685NG_HCI_ALERT(686"%s: %s - invalid LP_DisconnectReq message size=%d\n",687__func__, NG_NODE_NAME(unit->node),688NGI_MSG(item)->header.arglen);689690error = EMSGSIZE;691goto out;692}693694ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data);695696con = ng_hci_con_by_handle(unit, ep->con_handle);697if (con == NULL) {698NG_HCI_ERR(699"%s: %s - invalid connection handle=%d\n",700__func__, NG_NODE_NAME(unit->node), ep->con_handle);701702error = ENOENT;703goto out;704}705706if (con->state != NG_HCI_CON_OPEN) {707NG_HCI_ERR(708"%s: %s - invalid connection state=%d, handle=%d\n",709__func__, NG_NODE_NAME(unit->node), con->state,710ep->con_handle);711712error = EINVAL;713goto out;714}715716/*717* Create HCI command718*/719720MGETHDR(m, M_NOWAIT, MT_DATA);721if (m == NULL) {722error = ENOBUFS;723goto out;724}725726m->m_pkthdr.len = m->m_len = sizeof(*req);727req = mtod(m, struct discon_req *);728req->hdr.type = NG_HCI_CMD_PKT;729req->hdr.length = sizeof(req->cp);730req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,731NG_HCI_OCF_DISCON));732733req->cp.con_handle = htole16(ep->con_handle);734req->cp.reason = ep->reason;735736/*737* Queue and send HCI command738*/739740NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);741if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))742error = ng_hci_send_command(unit);743out:744NG_FREE_ITEM(item);745746return (error);747} /* ng_hci_lp_discon_req */748749/*750* Send LP_ConnectCfm event to the upper layer protocol751*/752753int754ng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status)755{756ng_hci_unit_p unit = con->unit;757struct ng_mesg *msg = NULL;758ng_hci_lp_con_cfm_ep *ep = NULL;759int error;760761/*762* Check who wants to be notified. For ACL links both ACL and SCO763* upstream hooks will be notified (if required). For SCO links764* only SCO upstream hook will receive notification765*/766767if (con->link_type != NG_HCI_LINK_SCO &&768con->flags & NG_HCI_CON_NOTIFY_ACL) {769if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {770NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,771sizeof(*ep), M_NOWAIT);772if (msg != NULL) {773ep = (ng_hci_lp_con_cfm_ep *) msg->data;774ep->status = status;775ep->link_type = con->link_type;776ep->con_handle = con->con_handle;777bcopy(&con->bdaddr, &ep->bdaddr,778sizeof(ep->bdaddr));779780NG_SEND_MSG_HOOK(error, unit->node, msg,781unit->acl, 0);782}783} else784NG_HCI_INFO(785"%s: %s - ACL hook not valid, hook=%p\n",786__func__, NG_NODE_NAME(unit->node), unit->acl);787788con->flags &= ~NG_HCI_CON_NOTIFY_ACL;789}790791if (con->flags & NG_HCI_CON_NOTIFY_SCO) {792if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {793NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,794sizeof(*ep), M_NOWAIT);795if (msg != NULL) {796ep = (ng_hci_lp_con_cfm_ep *) msg->data;797ep->status = status;798ep->link_type = con->link_type;799ep->con_handle = con->con_handle;800bcopy(&con->bdaddr, &ep->bdaddr,801sizeof(ep->bdaddr));802803NG_SEND_MSG_HOOK(error, unit->node, msg,804unit->sco, 0);805}806} else807NG_HCI_INFO(808"%s: %s - SCO hook not valid, hook=%p\n",809__func__, NG_NODE_NAME(unit->node), unit->acl);810811con->flags &= ~NG_HCI_CON_NOTIFY_SCO;812}813814return (0);815} /* ng_hci_lp_con_cfm */816817int818ng_hci_lp_enc_change(ng_hci_unit_con_p con, int status)819{820ng_hci_unit_p unit = con->unit;821struct ng_mesg *msg = NULL;822ng_hci_lp_enc_change_ep *ep = NULL;823int error;824825if (con->link_type != NG_HCI_LINK_SCO) {826if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {827NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_ENC_CHG,828sizeof(*ep), M_NOWAIT);829if (msg != NULL) {830ep = (ng_hci_lp_enc_change_ep *) msg->data;831ep->status = status;832ep->link_type = con->link_type;833ep->con_handle = con->con_handle;834835NG_SEND_MSG_HOOK(error, unit->node, msg,836unit->acl, 0);837}838} else839NG_HCI_INFO(840"%s: %s - ACL hook not valid, hook=%p\n",841__func__, NG_NODE_NAME(unit->node), unit->acl);842}843return (0);844} /* ng_hci_lp_con_cfm */845846/*847* Send LP_ConnectInd event to the upper layer protocol848*/849850int851ng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass)852{853ng_hci_unit_p unit = con->unit;854struct ng_mesg *msg = NULL;855ng_hci_lp_con_ind_ep *ep = NULL;856hook_p hook = NULL;857int error = 0;858859/*860* Connection_Request event is generated for specific link type.861* Use link_type to select upstream hook.862*/863864if (con->link_type != NG_HCI_LINK_SCO)865hook = unit->acl;866else867hook = unit->sco;868869if (hook != NULL && NG_HOOK_IS_VALID(hook)) {870NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND,871sizeof(*ep), M_NOWAIT);872if (msg == NULL)873return (ENOMEM);874875ep = (ng_hci_lp_con_ind_ep *)(msg->data);876ep->link_type = con->link_type;877bcopy(uclass, ep->uclass, sizeof(ep->uclass));878bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));879880NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0);881} else {882NG_HCI_WARN(883"%s: %s - Upstream hook is not connected or not valid, hook=%p\n",884__func__, NG_NODE_NAME(unit->node), hook);885886error = ENOTCONN;887}888889return (error);890} /* ng_hci_lp_con_ind */891892/*893* Process LP_ConnectRsp event from the upper layer protocol894*/895896int897ng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook)898{899struct con_rsp_req {900ng_hci_cmd_pkt_t hdr;901union {902ng_hci_accept_con_cp acc;903ng_hci_reject_con_cp rej;904} __attribute__ ((packed)) cp;905} __attribute__ ((packed)) *req = NULL;906ng_hci_lp_con_rsp_ep *ep = NULL;907ng_hci_unit_con_p con = NULL;908struct mbuf *m = NULL;909int error = 0;910911/* Check if unit is ready */912if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {913NG_HCI_WARN(914"%s: %s - unit is not ready, state=%#x\n",915__func__, NG_NODE_NAME(unit->node), unit->state);916917error = ENXIO;918goto out;919}920921if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {922NG_HCI_ALERT(923"%s: %s - invalid LP_ConnectRsp message size=%d\n",924__func__, NG_NODE_NAME(unit->node),925NGI_MSG(item)->header.arglen);926927error = EMSGSIZE;928goto out;929}930931ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data);932933/*934* Here we have to deal with race. Upper layers might send conflicting935* requests. One might send Accept and other Reject. We will not try936* to solve all the problems, so first request will always win.937*938* Try to find connection that matches the following:939*940* 1) con->link_type == ep->link_type941*942* 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||943* con->state == NG_HCI_CON_W4_CONN_COMPLETE944*945* 3) con->bdaddr == ep->bdaddr946*947* Two cases:948*949* 1) We do not have connection descriptor. Could be bogus request or950* we have rejected connection already.951*952* 2) We do have connection descriptor. Then we need to check state:953*954* 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested955* connection and it is a first response from the upper layer.956* if "status == 0" (Accept) then we will send Accept_Connection957* command and change connection state to W4_CONN_COMPLETE, else958* send reject and delete connection.959*960* 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted961* connection. If "status == 0" we just need to link request962* and wait, else ignore Reject request.963*/964965LIST_FOREACH(con, &unit->con_list, next)966if (con->link_type == ep->link_type &&967(con->state == NG_HCI_CON_W4_LP_CON_RSP ||968con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&969bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)970break;971972if (con == NULL) {973/* Reject for non-existing connection is fine */974error = (ep->status == 0)? ENOENT : 0;975goto out;976}977978/*979* Remove connection timeout and check connection state.980* Note: if ng_hci_con_untimeout() fails (returns non-zero value) then981* timeout already happened and event went into node's queue.982*/983984if ((error = ng_hci_con_untimeout(con)) != 0)985goto out;986987switch (con->state) {988case NG_HCI_CON_W4_LP_CON_RSP:989990/*991* Create HCI command992*/993994MGETHDR(m, M_NOWAIT, MT_DATA);995if (m == NULL) {996error = ENOBUFS;997goto out;998}9991000req = mtod(m, struct con_rsp_req *);1001req->hdr.type = NG_HCI_CMD_PKT;10021003if (ep->status == 0) {1004req->hdr.length = sizeof(req->cp.acc);1005req->hdr.opcode = htole16(NG_HCI_OPCODE(1006NG_HCI_OGF_LINK_CONTROL,1007NG_HCI_OCF_ACCEPT_CON));10081009bcopy(&ep->bdaddr, &req->cp.acc.bdaddr,1010sizeof(req->cp.acc.bdaddr));10111012/*1013* We are accepting connection, so if we support role1014* switch and role switch was enabled then set role to1015* NG_HCI_ROLE_MASTER and let LM perform role switch.1016* Otherwise we remain slave. In this case LM WILL NOT1017* perform role switch.1018*/10191020if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&1021unit->role_switch)1022req->cp.acc.role = NG_HCI_ROLE_MASTER;1023else1024req->cp.acc.role = NG_HCI_ROLE_SLAVE;10251026/*1027* Adjust connection state1028*/10291030if (hook == unit->acl)1031con->flags |= NG_HCI_CON_NOTIFY_ACL;1032else1033con->flags |= NG_HCI_CON_NOTIFY_SCO;10341035con->state = NG_HCI_CON_W4_CONN_COMPLETE;1036ng_hci_con_timeout(con);1037} else {1038req->hdr.length = sizeof(req->cp.rej);1039req->hdr.opcode = htole16(NG_HCI_OPCODE(1040NG_HCI_OGF_LINK_CONTROL,1041NG_HCI_OCF_REJECT_CON));10421043bcopy(&ep->bdaddr, &req->cp.rej.bdaddr,1044sizeof(req->cp.rej.bdaddr));10451046req->cp.rej.reason = ep->status;10471048/*1049* Free connection descritor1050* Item will be deleted just before return.1051*/10521053ng_hci_free_con(con);1054}10551056m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length;10571058/* Queue and send HCI command */1059NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);1060if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))1061error = ng_hci_send_command(unit);1062break;10631064case NG_HCI_CON_W4_CONN_COMPLETE:1065if (ep->status == 0) {1066if (hook == unit->acl)1067con->flags |= NG_HCI_CON_NOTIFY_ACL;1068else1069con->flags |= NG_HCI_CON_NOTIFY_SCO;1070} else1071error = EPERM;1072break;10731074default:1075panic(1076"%s: %s - Invalid connection state=%d\n",1077__func__, NG_NODE_NAME(unit->node), con->state);1078break;1079}1080out:1081NG_FREE_ITEM(item);10821083return (error);1084} /* ng_hci_lp_con_rsp */10851086/*1087* Send LP_DisconnectInd to the upper layer protocol1088*/10891090int1091ng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason)1092{1093ng_hci_unit_p unit = con->unit;1094struct ng_mesg *msg = NULL;1095ng_hci_lp_discon_ind_ep *ep = NULL;1096int error = 0;10971098/*1099* Disconnect_Complete event is generated for specific connection1100* handle. For ACL connection handles both ACL and SCO upstream1101* hooks will receive notification. For SCO connection handles1102* only SCO upstream hook will receive notification.1103*/11041105if (con->link_type != NG_HCI_LINK_SCO) {1106if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {1107NG_MKMESSAGE(msg, NGM_HCI_COOKIE,1108NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT);1109if (msg == NULL)1110return (ENOMEM);11111112ep = (ng_hci_lp_discon_ind_ep *) msg->data;1113ep->reason = reason;1114ep->link_type = con->link_type;1115ep->con_handle = con->con_handle;11161117NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,0);1118} else1119NG_HCI_INFO(1120"%s: %s - ACL hook is not connected or not valid, hook=%p\n",1121__func__, NG_NODE_NAME(unit->node), unit->acl);1122}11231124if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {1125NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND,1126sizeof(*ep), M_NOWAIT);1127if (msg == NULL)1128return (ENOMEM);11291130ep = (ng_hci_lp_discon_ind_ep *) msg->data;1131ep->reason = reason;1132ep->link_type = con->link_type;1133ep->con_handle = con->con_handle;11341135NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);1136} else1137NG_HCI_INFO(1138"%s: %s - SCO hook is not connected or not valid, hook=%p\n",1139__func__, NG_NODE_NAME(unit->node), unit->sco);11401141return (0);1142} /* ng_hci_lp_discon_ind */11431144/*1145* Process LP_QoSReq action from the upper layer protocol1146*/11471148int1149ng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook)1150{1151struct qos_setup_req {1152ng_hci_cmd_pkt_t hdr;1153ng_hci_qos_setup_cp cp;1154} __attribute__ ((packed)) *req = NULL;1155ng_hci_lp_qos_req_ep *ep = NULL;1156ng_hci_unit_con_p con = NULL;1157struct mbuf *m = NULL;1158int error = 0;11591160/* Check if unit is ready */1161if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {1162NG_HCI_WARN(1163"%s: %s - unit is not ready, state=%#x\n",1164__func__, NG_NODE_NAME(unit->node), unit->state);11651166error = ENXIO;1167goto out;1168}11691170if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {1171NG_HCI_ALERT(1172"%s: %s - invalid LP_QoSSetupReq message size=%d\n",1173__func__, NG_NODE_NAME(unit->node),1174NGI_MSG(item)->header.arglen);11751176error = EMSGSIZE;1177goto out;1178}11791180ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data);11811182con = ng_hci_con_by_handle(unit, ep->con_handle);1183if (con == NULL) {1184NG_HCI_ERR(1185"%s: %s - invalid connection handle=%d\n",1186__func__, NG_NODE_NAME(unit->node), ep->con_handle);11871188error = EINVAL;1189goto out;1190}11911192if (con->link_type != NG_HCI_LINK_ACL) {1193NG_HCI_ERR("%s: %s - invalid link type=%d\n",1194__func__, NG_NODE_NAME(unit->node), con->link_type);11951196error = EINVAL;1197goto out;1198}11991200if (con->state != NG_HCI_CON_OPEN) {1201NG_HCI_ERR(1202"%s: %s - invalid connection state=%d, handle=%d\n",1203__func__, NG_NODE_NAME(unit->node), con->state,1204con->con_handle);12051206error = EINVAL;1207goto out;1208}12091210/*1211* Create HCI command1212*/12131214MGETHDR(m, M_NOWAIT, MT_DATA);1215if (m == NULL) {1216error = ENOBUFS;1217goto out;1218}12191220m->m_pkthdr.len = m->m_len = sizeof(*req);1221req = mtod(m, struct qos_setup_req *);1222req->hdr.type = NG_HCI_CMD_PKT;1223req->hdr.length = sizeof(req->cp);1224req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,1225NG_HCI_OCF_QOS_SETUP));12261227req->cp.con_handle = htole16(ep->con_handle);1228req->cp.flags = ep->flags;1229req->cp.service_type = ep->service_type;1230req->cp.token_rate = htole32(ep->token_rate);1231req->cp.peak_bandwidth = htole32(ep->peak_bandwidth);1232req->cp.latency = htole32(ep->latency);1233req->cp.delay_variation = htole32(ep->delay_variation);12341235/*1236* Adjust connection state1237*/12381239if (hook == unit->acl)1240con->flags |= NG_HCI_CON_NOTIFY_ACL;1241else1242con->flags |= NG_HCI_CON_NOTIFY_SCO;12431244/*1245* Queue and send HCI command1246*/12471248NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);1249if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))1250error = ng_hci_send_command(unit);1251out:1252NG_FREE_ITEM(item);12531254return (error);1255} /* ng_hci_lp_qos_req */12561257/*1258* Send LP_QoSCfm event to the upper layer protocol1259*/12601261int1262ng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status)1263{1264ng_hci_unit_p unit = con->unit;1265struct ng_mesg *msg = NULL;1266ng_hci_lp_qos_cfm_ep *ep = NULL;1267int error;12681269if (con->flags & NG_HCI_CON_NOTIFY_ACL) {1270if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {1271NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,1272sizeof(*ep), M_NOWAIT);1273if (msg != NULL) {1274ep = (ng_hci_lp_qos_cfm_ep *) msg->data;1275ep->status = status;1276ep->con_handle = con->con_handle;12771278NG_SEND_MSG_HOOK(error, unit->node, msg,1279unit->acl, 0);1280}1281} else1282NG_HCI_INFO(1283"%s: %s - ACL hook not valid, hook=%p\n",1284__func__, NG_NODE_NAME(unit->node), unit->acl);12851286con->flags &= ~NG_HCI_CON_NOTIFY_ACL;1287}12881289if (con->flags & NG_HCI_CON_NOTIFY_SCO) {1290if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {1291NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,1292sizeof(*ep), M_NOWAIT);1293if (msg != NULL) {1294ep = (ng_hci_lp_qos_cfm_ep *) msg->data;1295ep->status = status;1296ep->con_handle = con->con_handle;12971298NG_SEND_MSG_HOOK(error, unit->node, msg,1299unit->sco, 0);1300}1301} else1302NG_HCI_INFO(1303"%s: %s - SCO hook not valid, hook=%p\n",1304__func__, NG_NODE_NAME(unit->node), unit->sco);13051306con->flags &= ~NG_HCI_CON_NOTIFY_SCO;1307}13081309return (0);1310} /* ng_hci_lp_qos_cfm */13111312/*1313* Send LP_QoSViolationInd event to the upper layer protocol1314*/13151316int1317ng_hci_lp_qos_ind(ng_hci_unit_con_p con)1318{1319ng_hci_unit_p unit = con->unit;1320struct ng_mesg *msg = NULL;1321ng_hci_lp_qos_ind_ep *ep = NULL;1322int error;13231324/*1325* QoS Violation can only be generated for ACL connection handles.1326* Both ACL and SCO upstream hooks will receive notification.1327*/13281329if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {1330NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,1331sizeof(*ep), M_NOWAIT);1332if (msg == NULL)1333return (ENOMEM);13341335ep = (ng_hci_lp_qos_ind_ep *) msg->data;1336ep->con_handle = con->con_handle;13371338NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0);1339} else1340NG_HCI_INFO(1341"%s: %s - ACL hook is not connected or not valid, hook=%p\n",1342__func__, NG_NODE_NAME(unit->node), unit->acl);13431344if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {1345NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,1346sizeof(*ep), M_NOWAIT);1347if (msg == NULL)1348return (ENOMEM);13491350ep = (ng_hci_lp_qos_ind_ep *) msg->data;1351ep->con_handle = con->con_handle;13521353NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);1354} else1355NG_HCI_INFO(1356"%s: %s - SCO hook is not connected or not valid, hook=%p\n",1357__func__, NG_NODE_NAME(unit->node), unit->sco);13581359return (0);1360} /* ng_hci_lp_qos_ind */13611362/*1363* Process connection timeout1364*/13651366void1367ng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int con_handle)1368{1369ng_hci_unit_p unit = NULL;1370ng_hci_unit_con_p con = NULL;13711372if (NG_NODE_NOT_VALID(node)) {1373printf("%s: Netgraph node is not valid\n", __func__);1374return;1375}13761377unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);1378con = ng_hci_con_by_handle(unit, con_handle);13791380if (con == NULL) {1381NG_HCI_ALERT(1382"%s: %s - could not find connection, handle=%d\n",1383__func__, NG_NODE_NAME(node), con_handle);1384return;1385}13861387if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) {1388NG_HCI_ALERT(1389"%s: %s - no pending connection timeout, handle=%d, state=%d, flags=%#x\n",1390__func__, NG_NODE_NAME(node), con_handle, con->state,1391con->flags);1392return;1393}13941395con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;13961397/*1398* We expect to receive connection timeout in one of the following1399* states:1400*1401* 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded1402* to our LP_CON_IND. Do nothing and destroy connection. Remote peer1403* most likely already gave up on us.1404*1405* 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection1406* (or we in the process of accepting it) and baseband has timedout1407* on us. Inform upper layers and send LP_CON_CFM.1408*/14091410switch (con->state) {1411case NG_HCI_CON_W4_LP_CON_RSP:1412break;14131414case NG_HCI_CON_W4_CONN_COMPLETE:1415ng_hci_lp_con_cfm(con, 0xee);1416break;14171418default:1419panic(1420"%s: %s - Invalid connection state=%d\n",1421__func__, NG_NODE_NAME(node), con->state);1422break;1423}14241425ng_hci_free_con(con);1426} /* ng_hci_process_con_timeout */142714281429