Path: blob/main/sys/netgraph/bluetooth/hci/ng_hci_evnt.c
34814 views
/*1* ng_hci_evnt.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_evnt.c,v 1.6 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** HCI event processing module54******************************************************************************55******************************************************************************/5657/*58* Event processing routines59*/6061static int inquiry_result (ng_hci_unit_p, struct mbuf *);62static int con_compl (ng_hci_unit_p, struct mbuf *);63static int con_req (ng_hci_unit_p, struct mbuf *);64static int discon_compl (ng_hci_unit_p, struct mbuf *);65static int encryption_change (ng_hci_unit_p, struct mbuf *);66static int read_remote_features_compl (ng_hci_unit_p, struct mbuf *);67static int qos_setup_compl (ng_hci_unit_p, struct mbuf *);68static int hardware_error (ng_hci_unit_p, struct mbuf *);69static int role_change (ng_hci_unit_p, struct mbuf *);70static int num_compl_pkts (ng_hci_unit_p, struct mbuf *);71static int mode_change (ng_hci_unit_p, struct mbuf *);72static int data_buffer_overflow (ng_hci_unit_p, struct mbuf *);73static int read_clock_offset_compl (ng_hci_unit_p, struct mbuf *);74static int qos_violation (ng_hci_unit_p, struct mbuf *);75static int page_scan_mode_change (ng_hci_unit_p, struct mbuf *);76static int page_scan_rep_mode_change (ng_hci_unit_p, struct mbuf *);77static int sync_con_queue (ng_hci_unit_p, ng_hci_unit_con_p, int);78static int send_data_packets (ng_hci_unit_p, int, int);79static int le_event (ng_hci_unit_p, struct mbuf *);8081/*82* Process HCI event packet83*/8485int86ng_hci_process_event(ng_hci_unit_p unit, struct mbuf *event)87{88ng_hci_event_pkt_t *hdr = NULL;89int error = 0;9091/* Get event packet header */92NG_HCI_M_PULLUP(event, sizeof(*hdr));93if (event == NULL)94return (ENOBUFS);9596hdr = mtod(event, ng_hci_event_pkt_t *);9798NG_HCI_INFO(99"%s: %s - got HCI event=%#x, length=%d\n",100__func__, NG_NODE_NAME(unit->node), hdr->event, hdr->length);101102/* Get rid of event header and process event */103m_adj(event, sizeof(*hdr));104105switch (hdr->event) {106case NG_HCI_EVENT_INQUIRY_COMPL:107case NG_HCI_EVENT_RETURN_LINK_KEYS:108case NG_HCI_EVENT_PIN_CODE_REQ:109case NG_HCI_EVENT_LINK_KEY_REQ:110case NG_HCI_EVENT_LINK_KEY_NOTIFICATION:111case NG_HCI_EVENT_LOOPBACK_COMMAND:112case NG_HCI_EVENT_AUTH_COMPL:113case NG_HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL:114case NG_HCI_EVENT_MASTER_LINK_KEY_COMPL:115case NG_HCI_EVENT_FLUSH_OCCUR: /* XXX Do we have to handle it? */116case NG_HCI_EVENT_MAX_SLOT_CHANGE:117case NG_HCI_EVENT_CON_PKT_TYPE_CHANGED:118case NG_HCI_EVENT_BT_LOGO:119case NG_HCI_EVENT_VENDOR:120case NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL:121case NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL:122case NG_HCI_EVENT_IO_CAPABILITY_REQUEST:123case NG_HCI_EVENT_SIMPLE_PAIRING_COMPLETE:124/* These do not need post processing */125NG_FREE_M(event);126break;127case NG_HCI_EVENT_LE:128error = le_event(unit, event);129break;130131case NG_HCI_EVENT_INQUIRY_RESULT:132error = inquiry_result(unit, event);133break;134135case NG_HCI_EVENT_CON_COMPL:136error = con_compl(unit, event);137break;138139case NG_HCI_EVENT_CON_REQ:140error = con_req(unit, event);141break;142143case NG_HCI_EVENT_DISCON_COMPL:144error = discon_compl(unit, event);145break;146147case NG_HCI_EVENT_ENCRYPTION_CHANGE:148error = encryption_change(unit, event);149break;150151case NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL:152error = read_remote_features_compl(unit, event);153break;154155case NG_HCI_EVENT_QOS_SETUP_COMPL:156error = qos_setup_compl(unit, event);157break;158159case NG_HCI_EVENT_COMMAND_COMPL:160error = ng_hci_process_command_complete(unit, event);161break;162163case NG_HCI_EVENT_COMMAND_STATUS:164error = ng_hci_process_command_status(unit, event);165break;166167case NG_HCI_EVENT_HARDWARE_ERROR:168error = hardware_error(unit, event);169break;170171case NG_HCI_EVENT_ROLE_CHANGE:172error = role_change(unit, event);173break;174175case NG_HCI_EVENT_NUM_COMPL_PKTS:176error = num_compl_pkts(unit, event);177break;178179case NG_HCI_EVENT_MODE_CHANGE:180error = mode_change(unit, event);181break;182183case NG_HCI_EVENT_DATA_BUFFER_OVERFLOW:184error = data_buffer_overflow(unit, event);185break;186187case NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL:188error = read_clock_offset_compl(unit, event);189break;190191case NG_HCI_EVENT_QOS_VIOLATION:192error = qos_violation(unit, event);193break;194195case NG_HCI_EVENT_PAGE_SCAN_MODE_CHANGE:196error = page_scan_mode_change(unit, event);197break;198199case NG_HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE:200error = page_scan_rep_mode_change(unit, event);201break;202203default:204NG_FREE_M(event);205error = EINVAL;206break;207}208209return (error);210} /* ng_hci_process_event */211212/*213* Send ACL and/or SCO data to the unit driver214*/215216void217ng_hci_send_data(ng_hci_unit_p unit)218{219int count;220221/* Send ACL data */222NG_HCI_BUFF_ACL_AVAIL(unit->buffer, count);223224NG_HCI_INFO(225"%s: %s - sending ACL data packets, count=%d\n",226__func__, NG_NODE_NAME(unit->node), count);227228if (count > 0) {229count = send_data_packets(unit, NG_HCI_LINK_ACL, count);230NG_HCI_STAT_ACL_SENT(unit->stat, count);231NG_HCI_BUFF_ACL_USE(unit->buffer, count);232}233234/* Send SCO data */235NG_HCI_BUFF_SCO_AVAIL(unit->buffer, count);236237NG_HCI_INFO(238"%s: %s - sending SCO data packets, count=%d\n",239__func__, NG_NODE_NAME(unit->node), count);240241if (count > 0) {242count = send_data_packets(unit, NG_HCI_LINK_SCO, count);243NG_HCI_STAT_SCO_SENT(unit->stat, count);244NG_HCI_BUFF_SCO_USE(unit->buffer, count);245}246} /* ng_hci_send_data */247248/*249* Send data packets to the lower layer.250*/251252static int253send_data_packets(ng_hci_unit_p unit, int link_type, int limit)254{255ng_hci_unit_con_p con = NULL, winner = NULL;256int reallink_type;257item_p item = NULL;258int min_pending, total_sent, sent, error, v;259260for (total_sent = 0; limit > 0; ) {261min_pending = 0x0fffffff;262winner = NULL;263264/*265* Find the connection that has has data to send266* and the smallest number of pending packets267*/268269LIST_FOREACH(con, &unit->con_list, next) {270reallink_type = (con->link_type == NG_HCI_LINK_SCO)?271NG_HCI_LINK_SCO: NG_HCI_LINK_ACL;272if (reallink_type != link_type){273continue;274}275if (NG_BT_ITEMQ_LEN(&con->conq) == 0)276continue;277278if (con->pending < min_pending) {279winner = con;280min_pending = con->pending;281}282}283284if (winner == NULL)285break;286287/*288* OK, we have a winner now send as much packets as we can289* Count the number of packets we have sent and then sync290* winner connection queue.291*/292293for (sent = 0; limit > 0; limit --, total_sent ++, sent ++) {294NG_BT_ITEMQ_DEQUEUE(&winner->conq, item);295if (item == NULL)296break;297298NG_HCI_INFO(299"%s: %s - sending data packet, handle=%d, len=%d\n",300__func__, NG_NODE_NAME(unit->node),301winner->con_handle, NGI_M(item)->m_pkthdr.len);302303/* Check if driver hook still there */304v = (unit->drv != NULL && NG_HOOK_IS_VALID(unit->drv));305if (!v || (unit->state & NG_HCI_UNIT_READY) !=306NG_HCI_UNIT_READY) {307NG_HCI_ERR(308"%s: %s - could not send data. Hook \"%s\" is %svalid, state=%#x\n",309__func__, NG_NODE_NAME(unit->node),310NG_HCI_HOOK_DRV, ((v)? "" : "not "),311unit->state);312313NG_FREE_ITEM(item);314error = ENOTCONN;315} else {316v = NGI_M(item)->m_pkthdr.len;317318/* Give packet to raw hook */319ng_hci_mtap(unit, NGI_M(item));320321/* ... and forward item to the driver */322NG_FWD_ITEM_HOOK(error, item, unit->drv);323}324325if (error != 0) {326NG_HCI_ERR(327"%s: %s - could not send data packet, handle=%d, error=%d\n",328__func__, NG_NODE_NAME(unit->node),329winner->con_handle, error);330break;331}332333winner->pending ++;334NG_HCI_STAT_BYTES_SENT(unit->stat, v);335}336337/*338* Sync connection queue for the winner339*/340sync_con_queue(unit, winner, sent);341}342343return (total_sent);344} /* send_data_packets */345346/*347* Send flow control messages to the upper layer348*/349350static int351sync_con_queue(ng_hci_unit_p unit, ng_hci_unit_con_p con, int completed)352{353hook_p hook = NULL;354struct ng_mesg *msg = NULL;355ng_hci_sync_con_queue_ep *state = NULL;356int error;357358hook = (con->link_type != NG_HCI_LINK_SCO)? unit->acl : unit->sco;359if (hook == NULL || NG_HOOK_NOT_VALID(hook))360return (ENOTCONN);361362NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_SYNC_CON_QUEUE,363sizeof(*state), M_NOWAIT);364if (msg == NULL)365return (ENOMEM);366367state = (ng_hci_sync_con_queue_ep *)(msg->data);368state->con_handle = con->con_handle;369state->completed = completed;370371NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0);372373return (error);374} /* sync_con_queue */375/* le meta event */376/* Inquiry result event */377static int378le_advertizing_report(ng_hci_unit_p unit, struct mbuf *event)379{380ng_hci_le_advertising_report_ep *ep = NULL;381ng_hci_neighbor_p n = NULL;382bdaddr_t bdaddr;383int error = 0;384int num_reports = 0;385u_int8_t addr_type;386387NG_HCI_M_PULLUP(event, sizeof(*ep));388if (event == NULL)389return (ENOBUFS);390391ep = mtod(event, ng_hci_le_advertising_report_ep *);392num_reports = ep->num_reports;393m_adj(event, sizeof(*ep));394ep = NULL;395396for (; num_reports > 0; num_reports --) {397/* event_type */398m_adj(event, sizeof(u_int8_t));399400/* Get remote unit address */401NG_HCI_M_PULLUP(event, sizeof(u_int8_t));402if (event == NULL) {403error = ENOBUFS;404goto out;405}406addr_type = *mtod(event, u_int8_t *);407m_adj(event, sizeof(u_int8_t));408409m_copydata(event, 0, sizeof(bdaddr), (caddr_t) &bdaddr);410m_adj(event, sizeof(bdaddr));411412/* Lookup entry in the cache */413n = ng_hci_get_neighbor(unit, &bdaddr, (addr_type) ? NG_HCI_LINK_LE_RANDOM:NG_HCI_LINK_LE_PUBLIC);414if (n == NULL) {415/* Create new entry */416n = ng_hci_new_neighbor(unit);417if (n == NULL) {418error = ENOMEM;419break;420}421bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr));422n->addrtype = (addr_type)? NG_HCI_LINK_LE_RANDOM :423NG_HCI_LINK_LE_PUBLIC;424425} else426getmicrotime(&n->updated);427428{429/*430* TODO: Make these information431* Available from userland.432*/433u_int8_t length_data;434435event = m_pullup(event, sizeof(u_int8_t));436if(event == NULL){437NG_HCI_WARN("%s: Event datasize Pullup Failed\n", __func__);438goto out;439}440length_data = *mtod(event, u_int8_t *);441m_adj(event, sizeof(u_int8_t));442n->extinq_size = (length_data < NG_HCI_EXTINQ_MAX)?443length_data : NG_HCI_EXTINQ_MAX;444445/*Advertizement data*/446event = m_pullup(event, n->extinq_size);447if(event == NULL){448NG_HCI_WARN("%s: Event data pullup Failed\n", __func__);449goto out;450}451m_copydata(event, 0, n->extinq_size, n->extinq_data);452m_adj(event, n->extinq_size);453event = m_pullup(event, sizeof(char ));454/*Get RSSI*/455if(event == NULL){456NG_HCI_WARN("%s: Event rssi pull up Failed\n", __func__);457458goto out;459}460n->page_scan_mode = *mtod(event, char *);461m_adj(event, sizeof(u_int8_t));462}463}464out:465NG_FREE_M(event);466467return (error);468} /* inquiry_result */469470static int le_connection_complete(ng_hci_unit_p unit, struct mbuf *event)471{472int error = 0;473474ng_hci_le_connection_complete_ep *ep = NULL;475ng_hci_unit_con_p con = NULL;476int link_type;477uint8_t uclass[3] = {0,0,0};//dummy uclass478479NG_HCI_M_PULLUP(event, sizeof(*ep));480if (event == NULL)481return (ENOBUFS);482483ep = mtod(event, ng_hci_le_connection_complete_ep *);484link_type = (ep->address_type)? NG_HCI_LINK_LE_RANDOM :485NG_HCI_LINK_LE_PUBLIC;486/*487* Find the first connection descriptor that matches the following:488*489* 1) con->link_type == link_type490* 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE491* 3) con->bdaddr == ep->address492*/493LIST_FOREACH(con, &unit->con_list, next)494if (con->link_type == link_type &&495con->state == NG_HCI_CON_W4_CONN_COMPLETE &&496bcmp(&con->bdaddr, &ep->address, sizeof(bdaddr_t)) == 0)497break;498499/*500* Two possible cases:501*502* 1) We have found connection descriptor. That means upper layer has503* requested this connection via LP_CON_REQ message. In this case504* connection must have timeout set. If ng_hci_con_untimeout() fails505* then timeout message already went into node's queue. In this case506* ignore Connection_Complete event and let timeout deal with it.507*508* 2) We do not have connection descriptor. That means upper layer509* nas not requested this connection , (less likely) we gave up510* on this connection (timeout) or as node act as slave role.511* The most likely scenario is that512* we have received LE_Create_Connection command513* from the RAW hook514*/515516if (con == NULL) {517if (ep->status != 0)518goto out;519520con = ng_hci_new_con(unit, link_type);521if (con == NULL) {522error = ENOMEM;523goto out;524}525526con->state = NG_HCI_CON_W4_LP_CON_RSP;527ng_hci_con_timeout(con);528529bcopy(&ep->address, &con->bdaddr, sizeof(con->bdaddr));530error = ng_hci_lp_con_ind(con, uclass);531if (error != 0) {532ng_hci_con_untimeout(con);533ng_hci_free_con(con);534goto out;535}536537} else if ((error = ng_hci_con_untimeout(con)) != 0)538goto out;539540/*541* Update connection descriptor and send notification542* to the upper layers.543*/544545con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->handle));546con->encryption_mode = NG_HCI_ENCRYPTION_MODE_NONE;547548ng_hci_lp_con_cfm(con, ep->status);549550/* Adjust connection state */551if (ep->status != 0)552ng_hci_free_con(con);553else {554con->state = NG_HCI_CON_OPEN;555556/*557* Change link policy for the ACL connections. Enable all558* supported link modes. Enable Role switch as well if559* device supports it.560*/561}562563out:564NG_FREE_M(event);565566return (error);567568}569570static int le_connection_update(ng_hci_unit_p unit, struct mbuf *event)571{572int error = 0;573/*TBD*/574575NG_FREE_M(event);576return error;577578}579static int580le_event(ng_hci_unit_p unit, struct mbuf *event)581{582int error = 0;583ng_hci_le_ep *lep;584585NG_HCI_M_PULLUP(event, sizeof(*lep));586if(event ==NULL){587return ENOBUFS;588}589lep = mtod(event, ng_hci_le_ep *);590m_adj(event, sizeof(*lep));591switch(lep->subevent_code){592case NG_HCI_LEEV_CON_COMPL:593le_connection_complete(unit, event);594break;595case NG_HCI_LEEV_ADVREP:596le_advertizing_report(unit, event);597break;598case NG_HCI_LEEV_CON_UPDATE_COMPL:599le_connection_update(unit, event);600break;601case NG_HCI_LEEV_READ_REMOTE_FEATURES_COMPL:602//TBD603/*FALLTHROUGH*/604case NG_HCI_LEEV_LONG_TERM_KEY_REQUEST:605//TBD606/*FALLTHROUGH*/607default:608NG_FREE_M(event);609}610return error;611}612613/* Inquiry result event */614static int615inquiry_result(ng_hci_unit_p unit, struct mbuf *event)616{617ng_hci_inquiry_result_ep *ep = NULL;618ng_hci_neighbor_p n = NULL;619bdaddr_t bdaddr;620int error = 0;621622NG_HCI_M_PULLUP(event, sizeof(*ep));623if (event == NULL)624return (ENOBUFS);625626ep = mtod(event, ng_hci_inquiry_result_ep *);627m_adj(event, sizeof(*ep));628629for (; ep->num_responses > 0; ep->num_responses --) {630/* Get remote unit address */631m_copydata(event, 0, sizeof(bdaddr), (caddr_t) &bdaddr);632m_adj(event, sizeof(bdaddr));633634/* Lookup entry in the cache */635n = ng_hci_get_neighbor(unit, &bdaddr, NG_HCI_LINK_ACL);636if (n == NULL) {637/* Create new entry */638n = ng_hci_new_neighbor(unit);639if (n == NULL) {640error = ENOMEM;641break;642}643} else644getmicrotime(&n->updated);645646bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr));647n->addrtype = NG_HCI_LINK_ACL;648649/* XXX call m_pullup here? */650651n->page_scan_rep_mode = *mtod(event, u_int8_t *);652m_adj(event, sizeof(u_int8_t));653654/* page_scan_period_mode */655m_adj(event, sizeof(u_int8_t));656657n->page_scan_mode = *mtod(event, u_int8_t *);658m_adj(event, sizeof(u_int8_t));659660/* class */661m_adj(event, NG_HCI_CLASS_SIZE);662663/* clock offset */664m_copydata(event, 0, sizeof(n->clock_offset),665(caddr_t) &n->clock_offset);666n->clock_offset = le16toh(n->clock_offset);667}668669NG_FREE_M(event);670671return (error);672} /* inquiry_result */673674/* Connection complete event */675static int676con_compl(ng_hci_unit_p unit, struct mbuf *event)677{678ng_hci_con_compl_ep *ep = NULL;679ng_hci_unit_con_p con = NULL;680int error = 0;681682NG_HCI_M_PULLUP(event, sizeof(*ep));683if (event == NULL)684return (ENOBUFS);685686ep = mtod(event, ng_hci_con_compl_ep *);687688/*689* Find the first connection descriptor that matches the following:690*691* 1) con->link_type == ep->link_type692* 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE693* 3) con->bdaddr == ep->bdaddr694*/695696LIST_FOREACH(con, &unit->con_list, next)697if (con->link_type == ep->link_type &&698con->state == NG_HCI_CON_W4_CONN_COMPLETE &&699bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)700break;701702/*703* Two possible cases:704*705* 1) We have found connection descriptor. That means upper layer has706* requested this connection via LP_CON_REQ message. In this case707* connection must have timeout set. If ng_hci_con_untimeout() fails708* then timeout message already went into node's queue. In this case709* ignore Connection_Complete event and let timeout deal with it.710*711* 2) We do not have connection descriptor. That means upper layer712* nas not requested this connection or (less likely) we gave up713* on this connection (timeout). The most likely scenario is that714* we have received Create_Connection/Add_SCO_Connection command715* from the RAW hook716*/717718if (con == NULL) {719if (ep->status != 0)720goto out;721722con = ng_hci_new_con(unit, ep->link_type);723if (con == NULL) {724error = ENOMEM;725goto out;726}727728bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));729} else if ((error = ng_hci_con_untimeout(con)) != 0)730goto out;731732/*733* Update connection descriptor and send notification734* to the upper layers.735*/736737con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));738con->encryption_mode = ep->encryption_mode;739740ng_hci_lp_con_cfm(con, ep->status);741742/* Adjust connection state */743if (ep->status != 0)744ng_hci_free_con(con);745else {746con->state = NG_HCI_CON_OPEN;747748/*749* Change link policy for the ACL connections. Enable all750* supported link modes. Enable Role switch as well if751* device supports it.752*/753754if (ep->link_type == NG_HCI_LINK_ACL) {755struct __link_policy {756ng_hci_cmd_pkt_t hdr;757ng_hci_write_link_policy_settings_cp cp;758} __attribute__ ((packed)) *lp;759struct mbuf *m;760761MGETHDR(m, M_NOWAIT, MT_DATA);762if (m != NULL) {763m->m_pkthdr.len = m->m_len = sizeof(*lp);764lp = mtod(m, struct __link_policy *);765766lp->hdr.type = NG_HCI_CMD_PKT;767lp->hdr.opcode = htole16(NG_HCI_OPCODE(768NG_HCI_OGF_LINK_POLICY,769NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS));770lp->hdr.length = sizeof(lp->cp);771772lp->cp.con_handle = ep->con_handle;773774lp->cp.settings = 0;775if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&776unit->role_switch)777lp->cp.settings |= 0x1;778if (unit->features[0] & NG_HCI_LMP_HOLD_MODE)779lp->cp.settings |= 0x2;780if (unit->features[0] & NG_HCI_LMP_SNIFF_MODE)781lp->cp.settings |= 0x4;782if (unit->features[1] & NG_HCI_LMP_PARK_MODE)783lp->cp.settings |= 0x8;784785lp->cp.settings &= unit->link_policy_mask;786lp->cp.settings = htole16(lp->cp.settings);787788NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);789if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))790ng_hci_send_command(unit);791}792}793}794out:795NG_FREE_M(event);796797return (error);798} /* con_compl */799800/* Connection request event */801static int802con_req(ng_hci_unit_p unit, struct mbuf *event)803{804ng_hci_con_req_ep *ep = NULL;805ng_hci_unit_con_p con = NULL;806int error = 0;807808NG_HCI_M_PULLUP(event, sizeof(*ep));809if (event == NULL)810return (ENOBUFS);811812ep = mtod(event, ng_hci_con_req_ep *);813814/*815* Find the first connection descriptor that matches the following:816*817* 1) con->link_type == ep->link_type818*819* 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||820* con->state == NG_HCI_CON_W4_CONN_COMPL821*822* 3) con->bdaddr == ep->bdaddr823*824* Possible cases:825*826* 1) We do not have connection descriptor. This is simple. Create827* new fresh connection descriptor and send notification to the828* appropriate upstream hook (based on link_type).829*830* 2) We found connection handle. This is more complicated.831*832* 2.1) ACL links833*834* Since only one ACL link can exist between each pair of835* units then we have a race. Our upper layer has requested836* an ACL connection to the remote unit, but we did not send837* command yet. At the same time the remote unit has requested838* an ACL connection from us. In this case we will ignore839* Connection_Request event. This probably will cause connect840* failure on both units.841*842* 2.2) SCO links843*844* The spec on page 45 says :845*846* "The master can support up to three SCO links to the same847* slave or to different slaves. A slave can support up to848* three SCO links from the same master, or two SCO links if849* the links originate from different masters."850*851* The only problem is how to handle multiple SCO links between852* matster and slave. For now we will assume that multiple SCO853* links MUST be opened one after another.854*/855856LIST_FOREACH(con, &unit->con_list, next)857if (con->link_type == ep->link_type &&858(con->state == NG_HCI_CON_W4_LP_CON_RSP ||859con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&860bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)861break;862863if (con == NULL) {864con = ng_hci_new_con(unit, ep->link_type);865if (con != NULL) {866bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));867868con->state = NG_HCI_CON_W4_LP_CON_RSP;869ng_hci_con_timeout(con);870871error = ng_hci_lp_con_ind(con, ep->uclass);872if (error != 0) {873ng_hci_con_untimeout(con);874ng_hci_free_con(con);875}876} else877error = ENOMEM;878}879880NG_FREE_M(event);881882return (error);883} /* con_req */884885/* Disconnect complete event */886static int887discon_compl(ng_hci_unit_p unit, struct mbuf *event)888{889ng_hci_discon_compl_ep *ep = NULL;890ng_hci_unit_con_p con = NULL;891int error = 0;892u_int16_t h;893894NG_HCI_M_PULLUP(event, sizeof(*ep));895if (event == NULL)896return (ENOBUFS);897898ep = mtod(event, ng_hci_discon_compl_ep *);899900/*901* XXX902* Do we have to send notification if ep->status != 0?903* For now we will send notification for both ACL and SCO connections904* ONLY if ep->status == 0.905*/906907if (ep->status == 0) {908h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));909con = ng_hci_con_by_handle(unit, h);910if (con != NULL) {911error = ng_hci_lp_discon_ind(con, ep->reason);912913/* Remove all timeouts (if any) */914if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)915ng_hci_con_untimeout(con);916917ng_hci_free_con(con);918} else {919NG_HCI_ALERT(920"%s: %s - invalid connection handle=%d\n",921__func__, NG_NODE_NAME(unit->node), h);922error = ENOENT;923}924}925926NG_FREE_M(event);927928return (error);929} /* discon_compl */930931/* Encryption change event */932static int933encryption_change(ng_hci_unit_p unit, struct mbuf *event)934{935ng_hci_encryption_change_ep *ep = NULL;936ng_hci_unit_con_p con = NULL;937int error = 0;938u_int16_t h;939940NG_HCI_M_PULLUP(event, sizeof(*ep));941if (event == NULL)942return (ENOBUFS);943944ep = mtod(event, ng_hci_encryption_change_ep *);945h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));946con = ng_hci_con_by_handle(unit, h);947948if (ep->status == 0) {949if (con == NULL) {950NG_HCI_ALERT(951"%s: %s - invalid connection handle=%d\n",952__func__, NG_NODE_NAME(unit->node), h);953error = ENOENT;954} else if (con->link_type == NG_HCI_LINK_SCO) {955NG_HCI_ALERT(956"%s: %s - invalid link type=%d\n",957__func__, NG_NODE_NAME(unit->node),958con->link_type);959error = EINVAL;960} else if (ep->encryption_enable)961/* XXX is that true? */962con->encryption_mode = NG_HCI_ENCRYPTION_MODE_P2P;963else964con->encryption_mode = NG_HCI_ENCRYPTION_MODE_NONE;965} else966NG_HCI_ERR(967"%s: %s - failed to change encryption mode, status=%d\n",968__func__, NG_NODE_NAME(unit->node), ep->status);969970/*Anyway, propagete encryption status to upper layer*/971ng_hci_lp_enc_change(con, con->encryption_mode);972973NG_FREE_M(event);974975return (error);976} /* encryption_change */977978/* Read remote feature complete event */979static int980read_remote_features_compl(ng_hci_unit_p unit, struct mbuf *event)981{982ng_hci_read_remote_features_compl_ep *ep = NULL;983ng_hci_unit_con_p con = NULL;984ng_hci_neighbor_p n = NULL;985u_int16_t h;986int error = 0;987988NG_HCI_M_PULLUP(event, sizeof(*ep));989if (event == NULL)990return (ENOBUFS);991992ep = mtod(event, ng_hci_read_remote_features_compl_ep *);993994if (ep->status == 0) {995/* Check if we have this connection handle */996h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));997con = ng_hci_con_by_handle(unit, h);998if (con == NULL) {999NG_HCI_ALERT(1000"%s: %s - invalid connection handle=%d\n",1001__func__, NG_NODE_NAME(unit->node), h);1002error = ENOENT;1003goto out;1004}10051006/* Update cache entry */1007n = ng_hci_get_neighbor(unit, &con->bdaddr, NG_HCI_LINK_ACL);1008if (n == NULL) {1009n = ng_hci_new_neighbor(unit);1010if (n == NULL) {1011error = ENOMEM;1012goto out;1013}10141015bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));1016n->addrtype = NG_HCI_LINK_ACL;1017} else1018getmicrotime(&n->updated);10191020bcopy(ep->features, n->features, sizeof(n->features));1021} else1022NG_HCI_ERR(1023"%s: %s - failed to read remote unit features, status=%d\n",1024__func__, NG_NODE_NAME(unit->node), ep->status);1025out:1026NG_FREE_M(event);10271028return (error);1029} /* read_remote_features_compl */10301031/* QoS setup complete event */1032static int1033qos_setup_compl(ng_hci_unit_p unit, struct mbuf *event)1034{1035ng_hci_qos_setup_compl_ep *ep = NULL;1036ng_hci_unit_con_p con = NULL;1037u_int16_t h;1038int error = 0;10391040NG_HCI_M_PULLUP(event, sizeof(*ep));1041if (event == NULL)1042return (ENOBUFS);10431044ep = mtod(event, ng_hci_qos_setup_compl_ep *);10451046/* Check if we have this connection handle */1047h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));1048con = ng_hci_con_by_handle(unit, h);1049if (con == NULL) {1050NG_HCI_ALERT(1051"%s: %s - invalid connection handle=%d\n",1052__func__, NG_NODE_NAME(unit->node), h);1053error = ENOENT;1054} else if (con->link_type != NG_HCI_LINK_ACL) {1055NG_HCI_ALERT(1056"%s: %s - invalid link type=%d, handle=%d\n",1057__func__, NG_NODE_NAME(unit->node), con->link_type, h);1058error = EINVAL;1059} else if (con->state != NG_HCI_CON_OPEN) {1060NG_HCI_ALERT(1061"%s: %s - invalid connection state=%d, handle=%d\n",1062__func__, NG_NODE_NAME(unit->node),1063con->state, h);1064error = EINVAL;1065} else /* Notify upper layer */1066error = ng_hci_lp_qos_cfm(con, ep->status);10671068NG_FREE_M(event);10691070return (error);1071} /* qos_setup_compl */10721073/* Hardware error event */1074static int1075hardware_error(ng_hci_unit_p unit, struct mbuf *event)1076{1077NG_HCI_ALERT(1078"%s: %s - hardware error %#x\n",1079__func__, NG_NODE_NAME(unit->node), *mtod(event, u_int8_t *));10801081NG_FREE_M(event);10821083return (0);1084} /* hardware_error */10851086/* Role change event */1087static int1088role_change(ng_hci_unit_p unit, struct mbuf *event)1089{1090ng_hci_role_change_ep *ep = NULL;1091ng_hci_unit_con_p con = NULL;10921093NG_HCI_M_PULLUP(event, sizeof(*ep));1094if (event == NULL)1095return (ENOBUFS);10961097ep = mtod(event, ng_hci_role_change_ep *);10981099if (ep->status == 0) {1100/* XXX shoud we also change "role" for SCO connections? */1101con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);1102if (con != NULL)1103con->role = ep->role;1104else1105NG_HCI_ALERT(1106"%s: %s - ACL connection does not exist, bdaddr=%x:%x:%x:%x:%x:%x\n",1107__func__, NG_NODE_NAME(unit->node),1108ep->bdaddr.b[5], ep->bdaddr.b[4],1109ep->bdaddr.b[3], ep->bdaddr.b[2],1110ep->bdaddr.b[1], ep->bdaddr.b[0]);1111} else1112NG_HCI_ERR(1113"%s: %s - failed to change role, status=%d, bdaddr=%x:%x:%x:%x:%x:%x\n",1114__func__, NG_NODE_NAME(unit->node), ep->status,1115ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],1116ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);11171118NG_FREE_M(event);11191120return (0);1121} /* role_change */11221123/* Number of completed packets event */1124static int1125num_compl_pkts(ng_hci_unit_p unit, struct mbuf *event)1126{1127ng_hci_num_compl_pkts_ep *ep = NULL;1128ng_hci_unit_con_p con = NULL;1129u_int16_t h, p;11301131NG_HCI_M_PULLUP(event, sizeof(*ep));1132if (event == NULL)1133return (ENOBUFS);11341135ep = mtod(event, ng_hci_num_compl_pkts_ep *);1136m_adj(event, sizeof(*ep));11371138for (; ep->num_con_handles > 0; ep->num_con_handles --) {1139/* Get connection handle */1140m_copydata(event, 0, sizeof(h), (caddr_t) &h);1141m_adj(event, sizeof(h));1142h = NG_HCI_CON_HANDLE(le16toh(h));11431144/* Get number of completed packets */1145m_copydata(event, 0, sizeof(p), (caddr_t) &p);1146m_adj(event, sizeof(p));1147p = le16toh(p);11481149/* Check if we have this connection handle */1150con = ng_hci_con_by_handle(unit, h);1151if (con != NULL) {1152con->pending -= p;1153if (con->pending < 0) {1154NG_HCI_WARN(1155"%s: %s - pending packet counter is out of sync! " \1156"handle=%d, pending=%d, ncp=%d\n", __func__, NG_NODE_NAME(unit->node),1157con->con_handle, con->pending, p);11581159con->pending = 0;1160}11611162/* Update buffer descriptor */1163if (con->link_type != NG_HCI_LINK_SCO)1164NG_HCI_BUFF_ACL_FREE(unit->buffer, p);1165else1166NG_HCI_BUFF_SCO_FREE(unit->buffer, p);1167} else1168NG_HCI_ALERT(1169"%s: %s - invalid connection handle=%d\n",1170__func__, NG_NODE_NAME(unit->node), h);1171}11721173NG_FREE_M(event);11741175/* Send more data */1176ng_hci_send_data(unit);11771178return (0);1179} /* num_compl_pkts */11801181/* Mode change event */1182static int1183mode_change(ng_hci_unit_p unit, struct mbuf *event)1184{1185ng_hci_mode_change_ep *ep = NULL;1186ng_hci_unit_con_p con = NULL;1187int error = 0;11881189NG_HCI_M_PULLUP(event, sizeof(*ep));1190if (event == NULL)1191return (ENOBUFS);11921193ep = mtod(event, ng_hci_mode_change_ep *);11941195if (ep->status == 0) {1196u_int16_t h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));11971198con = ng_hci_con_by_handle(unit, h);1199if (con == NULL) {1200NG_HCI_ALERT(1201"%s: %s - invalid connection handle=%d\n",1202__func__, NG_NODE_NAME(unit->node), h);1203error = ENOENT;1204} else if (con->link_type != NG_HCI_LINK_ACL) {1205NG_HCI_ALERT(1206"%s: %s - invalid link type=%d\n",1207__func__, NG_NODE_NAME(unit->node),1208con->link_type);1209error = EINVAL;1210} else1211con->mode = ep->unit_mode;1212} else1213NG_HCI_ERR(1214"%s: %s - failed to change mode, status=%d\n",1215__func__, NG_NODE_NAME(unit->node), ep->status);12161217NG_FREE_M(event);12181219return (error);1220} /* mode_change */12211222/* Data buffer overflow event */1223static int1224data_buffer_overflow(ng_hci_unit_p unit, struct mbuf *event)1225{1226NG_HCI_ALERT(1227"%s: %s - %s data buffer overflow\n",1228__func__, NG_NODE_NAME(unit->node),1229(*mtod(event, u_int8_t *) == NG_HCI_LINK_ACL)? "ACL" : "SCO");12301231NG_FREE_M(event);12321233return (0);1234} /* data_buffer_overflow */12351236/* Read clock offset complete event */1237static int1238read_clock_offset_compl(ng_hci_unit_p unit, struct mbuf *event)1239{1240ng_hci_read_clock_offset_compl_ep *ep = NULL;1241ng_hci_unit_con_p con = NULL;1242ng_hci_neighbor_p n = NULL;1243int error = 0;12441245NG_HCI_M_PULLUP(event, sizeof(*ep));1246if (event == NULL)1247return (ENOBUFS);12481249ep = mtod(event, ng_hci_read_clock_offset_compl_ep *);12501251if (ep->status == 0) {1252u_int16_t h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));12531254con = ng_hci_con_by_handle(unit, h);1255if (con == NULL) {1256NG_HCI_ALERT(1257"%s: %s - invalid connection handle=%d\n",1258__func__, NG_NODE_NAME(unit->node), h);1259error = ENOENT;1260goto out;1261}12621263/* Update cache entry */1264n = ng_hci_get_neighbor(unit, &con->bdaddr, NG_HCI_LINK_ACL);1265if (n == NULL) {1266n = ng_hci_new_neighbor(unit);1267if (n == NULL) {1268error = ENOMEM;1269goto out;1270}12711272bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));1273n->addrtype = NG_HCI_LINK_ACL;1274} else1275getmicrotime(&n->updated);12761277n->clock_offset = le16toh(ep->clock_offset);1278} else1279NG_HCI_ERR(1280"%s: %s - failed to Read Remote Clock Offset, status=%d\n",1281__func__, NG_NODE_NAME(unit->node), ep->status);1282out:1283NG_FREE_M(event);12841285return (error);1286} /* read_clock_offset_compl */12871288/* QoS violation event */1289static int1290qos_violation(ng_hci_unit_p unit, struct mbuf *event)1291{1292ng_hci_qos_violation_ep *ep = NULL;1293ng_hci_unit_con_p con = NULL;1294u_int16_t h;1295int error = 0;12961297NG_HCI_M_PULLUP(event, sizeof(*ep));1298if (event == NULL)1299return (ENOBUFS);13001301ep = mtod(event, ng_hci_qos_violation_ep *);13021303/* Check if we have this connection handle */1304h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));1305con = ng_hci_con_by_handle(unit, h);1306if (con == NULL) {1307NG_HCI_ALERT(1308"%s: %s - invalid connection handle=%d\n",1309__func__, NG_NODE_NAME(unit->node), h);1310error = ENOENT;1311} else if (con->link_type != NG_HCI_LINK_ACL) {1312NG_HCI_ALERT(1313"%s: %s - invalid link type=%d\n",1314__func__, NG_NODE_NAME(unit->node), con->link_type);1315error = EINVAL;1316} else if (con->state != NG_HCI_CON_OPEN) {1317NG_HCI_ALERT(1318"%s: %s - invalid connection state=%d, handle=%d\n",1319__func__, NG_NODE_NAME(unit->node), con->state, h);1320error = EINVAL;1321} else /* Notify upper layer */1322error = ng_hci_lp_qos_ind(con);13231324NG_FREE_M(event);13251326return (error);1327} /* qos_violation */13281329/* Page scan mode change event */1330static int1331page_scan_mode_change(ng_hci_unit_p unit, struct mbuf *event)1332{1333ng_hci_page_scan_mode_change_ep *ep = NULL;1334ng_hci_neighbor_p n = NULL;1335int error = 0;13361337NG_HCI_M_PULLUP(event, sizeof(*ep));1338if (event == NULL)1339return (ENOBUFS);13401341ep = mtod(event, ng_hci_page_scan_mode_change_ep *);13421343/* Update cache entry */1344n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL);1345if (n == NULL) {1346n = ng_hci_new_neighbor(unit);1347if (n == NULL) {1348error = ENOMEM;1349goto out;1350}13511352bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));1353n->addrtype = NG_HCI_LINK_ACL;1354} else1355getmicrotime(&n->updated);13561357n->page_scan_mode = ep->page_scan_mode;1358out:1359NG_FREE_M(event);13601361return (error);1362} /* page_scan_mode_change */13631364/* Page scan repetition mode change event */1365static int1366page_scan_rep_mode_change(ng_hci_unit_p unit, struct mbuf *event)1367{1368ng_hci_page_scan_rep_mode_change_ep *ep = NULL;1369ng_hci_neighbor_p n = NULL;1370int error = 0;13711372NG_HCI_M_PULLUP(event, sizeof(*ep));1373if (event == NULL)1374return (ENOBUFS);13751376ep = mtod(event, ng_hci_page_scan_rep_mode_change_ep *);13771378/* Update cache entry */1379n = ng_hci_get_neighbor(unit, &ep->bdaddr, NG_HCI_LINK_ACL);1380if (n == NULL) {1381n = ng_hci_new_neighbor(unit);1382if (n == NULL) {1383error = ENOMEM;1384goto out;1385}13861387bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));1388n->addrtype = NG_HCI_LINK_ACL;1389} else1390getmicrotime(&n->updated);13911392n->page_scan_rep_mode = ep->page_scan_rep_mode;1393out:1394NG_FREE_M(event);13951396return (error);1397} /* page_scan_rep_mode_change */139813991400