Path: blob/main/sys/netgraph/bluetooth/hci/ng_hci_main.c
34814 views
/*1* ng_hci_main.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_main.c,v 1.2 2003/03/18 00:09:36 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/ng_parse.h>44#include <netgraph/bluetooth/include/ng_bluetooth.h>45#include <netgraph/bluetooth/include/ng_hci.h>46#include <netgraph/bluetooth/hci/ng_hci_var.h>47#include <netgraph/bluetooth/hci/ng_hci_prse.h>48#include <netgraph/bluetooth/hci/ng_hci_cmds.h>49#include <netgraph/bluetooth/hci/ng_hci_evnt.h>50#include <netgraph/bluetooth/hci/ng_hci_ulpi.h>51#include <netgraph/bluetooth/hci/ng_hci_misc.h>5253/******************************************************************************54******************************************************************************55** This node implements Bluetooth Host Controller Interface (HCI)56******************************************************************************57******************************************************************************/5859/* MALLOC define */60#ifdef NG_SEPARATE_MALLOC61MALLOC_DEFINE(M_NETGRAPH_HCI, "netgraph_hci", "Netgraph Bluetooth HCI node");62#else63#define M_NETGRAPH_HCI M_NETGRAPH64#endif /* NG_SEPARATE_MALLOC */6566/* Netgraph node methods */67static ng_constructor_t ng_hci_constructor;68static ng_shutdown_t ng_hci_shutdown;69static ng_newhook_t ng_hci_newhook;70static ng_connect_t ng_hci_connect;71static ng_disconnect_t ng_hci_disconnect;72static ng_rcvmsg_t ng_hci_default_rcvmsg;73static ng_rcvmsg_t ng_hci_upper_rcvmsg;74static ng_rcvdata_t ng_hci_drv_rcvdata;75static ng_rcvdata_t ng_hci_acl_rcvdata;76static ng_rcvdata_t ng_hci_sco_rcvdata;77static ng_rcvdata_t ng_hci_raw_rcvdata;7879/* Netgraph node type descriptor */80static struct ng_type typestruct = {81.version = NG_ABI_VERSION,82.name = NG_HCI_NODE_TYPE,83.constructor = ng_hci_constructor,84.rcvmsg = ng_hci_default_rcvmsg,85.shutdown = ng_hci_shutdown,86.newhook = ng_hci_newhook,87.connect = ng_hci_connect,88.rcvdata = ng_hci_drv_rcvdata,89.disconnect = ng_hci_disconnect,90.cmdlist = ng_hci_cmdlist,91};92NETGRAPH_INIT(hci, &typestruct);93MODULE_VERSION(ng_hci, NG_BLUETOOTH_VERSION);94MODULE_DEPEND(ng_hci, ng_bluetooth, NG_BLUETOOTH_VERSION,95NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);96static int ng_hci_linktype_to_addrtype(int linktype);9798static int ng_hci_linktype_to_addrtype(int linktype)99{100switch(linktype){101case NG_HCI_LINK_LE_PUBLIC:102return BDADDR_LE_PUBLIC;103case NG_HCI_LINK_LE_RANDOM:104return BDADDR_LE_RANDOM;105case NG_HCI_LINK_ACL:106/*FALLTHROUGH*/107default:108return BDADDR_BREDR;109}110return BDADDR_BREDR;111}112/*****************************************************************************113*****************************************************************************114** Netgraph methods implementation115*****************************************************************************116*****************************************************************************/117118/*119* Create new instance of HCI node (new unit)120*/121122static int123ng_hci_constructor(node_p node)124{125ng_hci_unit_p unit = NULL;126127unit = malloc(sizeof(*unit), M_NETGRAPH_HCI, M_WAITOK | M_ZERO);128129unit->node = node;130unit->debug = NG_HCI_WARN_LEVEL;131132unit->link_policy_mask = 0xffff; /* Enable all supported modes */133unit->packet_mask = 0xffff; /* Enable all packet types */134unit->role_switch = 1; /* Enable role switch (if device supports it) */135136/*137* Set default buffer info138*139* One HCI command140* One ACL packet with max. size of 17 bytes (1 DM1 packet)141* One SCO packet with max. size of 10 bytes (1 HV1 packet)142*/143144NG_HCI_BUFF_CMD_SET(unit->buffer, 1);145NG_HCI_BUFF_ACL_SET(unit->buffer, 1, 17, 1);146NG_HCI_BUFF_SCO_SET(unit->buffer, 1, 10, 1);147148/* Init command queue & command timeout handler */149ng_callout_init(&unit->cmd_timo);150NG_BT_MBUFQ_INIT(&unit->cmdq, NG_HCI_CMD_QUEUE_LEN);151152/* Init lists */153LIST_INIT(&unit->con_list);154LIST_INIT(&unit->neighbors);155156/*157* This node has to be a WRITER because both data and messages158* can change node state.159*/160161NG_NODE_FORCE_WRITER(node);162NG_NODE_SET_PRIVATE(node, unit);163164return (0);165} /* ng_hci_constructor */166167/*168* Destroy the node169*/170171static int172ng_hci_shutdown(node_p node)173{174ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);175176NG_NODE_SET_PRIVATE(node, NULL);177NG_NODE_UNREF(node);178179unit->node = NULL;180ng_hci_unit_clean(unit, 0x16 /* Connection terminated by local host */);181182NG_BT_MBUFQ_DESTROY(&unit->cmdq);183184bzero(unit, sizeof(*unit));185free(unit, M_NETGRAPH_HCI);186187return (0);188} /* ng_hci_shutdown */189190/*191* Give our OK for a hook to be added. Unit driver is connected to the driver192* (NG_HCI_HOOK_DRV) hook. Upper layer protocols are connected to appropriate193* (NG_HCI_HOOK_ACL or NG_HCI_HOOK_SCO) hooks.194*/195196static int197ng_hci_newhook(node_p node, hook_p hook, char const *name)198{199ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);200hook_p *h = NULL;201202if (strcmp(name, NG_HCI_HOOK_DRV) == 0)203h = &unit->drv;204else if (strcmp(name, NG_HCI_HOOK_ACL) == 0)205h = &unit->acl;206else if (strcmp(name, NG_HCI_HOOK_SCO) == 0)207h = &unit->sco;208else if (strcmp(name, NG_HCI_HOOK_RAW) == 0)209h = &unit->raw;210else211return (EINVAL);212213if (*h != NULL)214return (EISCONN);215216*h = hook;217218return (0);219} /* ng_hci_newhook */220221/*222* Give our final OK to connect hook223*/224225static int226ng_hci_connect(hook_p hook)227{228ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));229230if (hook != unit->drv) {231if (hook == unit->acl) {232NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg);233NG_HOOK_SET_RCVDATA(hook, ng_hci_acl_rcvdata);234} else if (hook == unit->sco) {235NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg);236NG_HOOK_SET_RCVDATA(hook, ng_hci_sco_rcvdata);237} else238NG_HOOK_SET_RCVDATA(hook, ng_hci_raw_rcvdata);239240/* Send delayed notification to the upper layers */241if (hook != unit->raw)242ng_send_fn(unit->node, hook, ng_hci_node_is_up, NULL,0);243} else244unit->state |= NG_HCI_UNIT_CONNECTED;245246return (0);247} /* ng_hci_connect */248249/*250* Disconnect the hook251*/252253static int254ng_hci_disconnect(hook_p hook)255{256ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));257258if (hook == unit->acl)259unit->acl = NULL;260else if (hook == unit->sco)261unit->sco = NULL;262else if (hook == unit->raw)263unit->raw = NULL;264else if (hook == unit->drv) {265unit->drv = NULL;266267/* Connection terminated by local host */268ng_hci_unit_clean(unit, 0x16);269unit->state &= ~(NG_HCI_UNIT_CONNECTED|NG_HCI_UNIT_INITED);270} else271return (EINVAL);272273/* Shutdown when all hooks are disconnected */274if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&275(NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))276ng_rmnode_self(NG_HOOK_NODE(hook));277278return (0);279} /* ng_hci_disconnect */280281/*282* Default control message processing routine. Control message could be:283*284* 1) GENERIC Netgraph messages285*286* 2) Control message directed to the node itself.287*/288289static int290ng_hci_default_rcvmsg(node_p node, item_p item, hook_p lasthook)291{292ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);293struct ng_mesg *msg = NULL, *rsp = NULL;294int error = 0;295296NGI_GET_MSG(item, msg);297298switch (msg->header.typecookie) {299case NGM_GENERIC_COOKIE:300switch (msg->header.cmd) {301case NGM_TEXT_STATUS: {302int cmd_avail,303acl_total, acl_avail, acl_size,304sco_total, sco_avail, sco_size;305306NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);307if (rsp == NULL) {308error = ENOMEM;309break;310}311312NG_HCI_BUFF_CMD_GET(unit->buffer, cmd_avail);313314NG_HCI_BUFF_ACL_AVAIL(unit->buffer, acl_avail);315NG_HCI_BUFF_ACL_TOTAL(unit->buffer, acl_total);316NG_HCI_BUFF_ACL_SIZE(unit->buffer, acl_size);317318NG_HCI_BUFF_SCO_AVAIL(unit->buffer, sco_avail);319NG_HCI_BUFF_SCO_TOTAL(unit->buffer, sco_total);320NG_HCI_BUFF_SCO_SIZE(unit->buffer, sco_size);321322snprintf(rsp->data, NG_TEXTRESPONSE,323"bdaddr %x:%x:%x:%x:%x:%x\n" \324"Hooks %s %s %s %s\n" \325"State %#x\n" \326"Queue cmd:%d\n" \327"Buffer cmd:%d,acl:%d,%d,%d,sco:%d,%d,%d",328unit->bdaddr.b[5], unit->bdaddr.b[4],329unit->bdaddr.b[3], unit->bdaddr.b[2],330unit->bdaddr.b[1], unit->bdaddr.b[0],331(unit->drv != NULL)? NG_HCI_HOOK_DRV : "",332(unit->acl != NULL)? NG_HCI_HOOK_ACL : "",333(unit->sco != NULL)? NG_HCI_HOOK_SCO : "",334(unit->raw != NULL)? NG_HCI_HOOK_RAW : "",335unit->state,336NG_BT_MBUFQ_LEN(&unit->cmdq),337cmd_avail,338acl_avail, acl_total, acl_size,339sco_avail, sco_total, sco_size);340} break;341342default:343error = EINVAL;344break;345}346break;347348case NGM_HCI_COOKIE:349switch (msg->header.cmd) {350/* Get current node state */351case NGM_HCI_NODE_GET_STATE:352NG_MKRESPONSE(rsp, msg, sizeof(unit->state), M_NOWAIT);353if (rsp == NULL) {354error = ENOMEM;355break;356}357358*((ng_hci_node_state_ep *)(rsp->data)) = unit->state;359break;360361/* Turn INITED bit - node initialized */362case NGM_HCI_NODE_INIT:363if (bcmp(&unit->bdaddr, NG_HCI_BDADDR_ANY,364sizeof(bdaddr_t)) == 0) {365error = ENXIO;366break;367}368369unit->state |= NG_HCI_UNIT_INITED;370371ng_hci_node_is_up(unit->node, unit->acl, NULL, 0);372ng_hci_node_is_up(unit->node, unit->sco, NULL, 0);373break;374375/* Get node debug level */376case NGM_HCI_NODE_GET_DEBUG:377NG_MKRESPONSE(rsp, msg, sizeof(unit->debug), M_NOWAIT);378if (rsp == NULL) {379error = ENOMEM;380break;381}382383*((ng_hci_node_debug_ep *)(rsp->data)) = unit->debug;384break;385386/* Set node debug level */387case NGM_HCI_NODE_SET_DEBUG:388if (msg->header.arglen != sizeof(ng_hci_node_debug_ep)){389error = EMSGSIZE;390break;391}392393unit->debug = *((ng_hci_node_debug_ep *)(msg->data));394break;395396/* Get buffer info */397case NGM_HCI_NODE_GET_BUFFER: {398ng_hci_node_buffer_ep *ep = NULL;399400NG_MKRESPONSE(rsp, msg, sizeof(ng_hci_node_buffer_ep),401M_NOWAIT);402if (rsp == NULL) {403error = ENOMEM;404break;405}406407ep = (ng_hci_node_buffer_ep *)(rsp->data);408409NG_HCI_BUFF_CMD_GET(unit->buffer, ep->cmd_free);410NG_HCI_BUFF_ACL_AVAIL(unit->buffer, ep->acl_free);411NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->acl_pkts);412NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->acl_size);413NG_HCI_BUFF_SCO_AVAIL(unit->buffer, ep->sco_free);414NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->sco_pkts);415NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->sco_size);416} break;417418/* Get BDADDR */419case NGM_HCI_NODE_GET_BDADDR:420NG_MKRESPONSE(rsp, msg, sizeof(bdaddr_t), M_NOWAIT);421if (rsp == NULL) {422error = ENOMEM;423break;424}425426bcopy(&unit->bdaddr, rsp->data, sizeof(bdaddr_t));427break;428429/* Get features */430case NGM_HCI_NODE_GET_FEATURES:431NG_MKRESPONSE(rsp,msg,sizeof(unit->features),M_NOWAIT);432if (rsp == NULL) {433error = ENOMEM;434break;435}436437bcopy(&unit->features,rsp->data,sizeof(unit->features));438break;439440/* Get stat */441case NGM_HCI_NODE_GET_STAT:442NG_MKRESPONSE(rsp, msg, sizeof(unit->stat), M_NOWAIT);443if (rsp == NULL) {444error = ENOMEM;445break;446}447448bcopy(&unit->stat, rsp->data, sizeof(unit->stat));449break;450451/* Reset stat */452case NGM_HCI_NODE_RESET_STAT:453NG_HCI_STAT_RESET(unit->stat);454break;455456/* Clean up neighbors list */457case NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE:458ng_hci_flush_neighbor_cache(unit);459break;460461/* Get neighbor cache entries */462case NGM_HCI_NODE_GET_NEIGHBOR_CACHE: {463ng_hci_neighbor_p n = NULL;464ng_hci_node_get_neighbor_cache_ep *e1 = NULL;465ng_hci_node_neighbor_cache_entry_ep *e2 = NULL;466int s = 0;467468/* Look for the fresh entries in the cache */469for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) {470ng_hci_neighbor_p nn = LIST_NEXT(n, next);471472if (ng_hci_neighbor_stale(n))473ng_hci_free_neighbor(n);474else475s ++;476477n = nn;478}479if (s > NG_HCI_MAX_NEIGHBOR_NUM)480s = NG_HCI_MAX_NEIGHBOR_NUM;481482/* Prepare response */483NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2),484M_NOWAIT);485if (rsp == NULL) {486error = ENOMEM;487break;488}489490e1 = (ng_hci_node_get_neighbor_cache_ep *)(rsp->data);491e2 = (ng_hci_node_neighbor_cache_entry_ep *)(e1 + 1);492493e1->num_entries = s;494495LIST_FOREACH(n, &unit->neighbors, next) {496e2->page_scan_rep_mode = n->page_scan_rep_mode;497e2->page_scan_mode = n->page_scan_mode;498e2->clock_offset = n->clock_offset;499e2->addrtype =500ng_hci_linktype_to_addrtype(n->addrtype);501e2->extinq_size = n->extinq_size;502bcopy(&n->bdaddr, &e2->bdaddr,503sizeof(e2->bdaddr));504bcopy(&n->features, &e2->features,505sizeof(e2->features));506bcopy(&n->extinq_data, &e2->extinq_data,507n->extinq_size);508e2 ++;509if (--s <= 0)510break;511}512} break;513514/* Get connection list */515case NGM_HCI_NODE_GET_CON_LIST: {516ng_hci_unit_con_p c = NULL;517ng_hci_node_con_list_ep *e1 = NULL;518ng_hci_node_con_ep *e2 = NULL;519int s = 0;520521/* Count number of connections in the list */522LIST_FOREACH(c, &unit->con_list, next)523s ++;524if (s > NG_HCI_MAX_CON_NUM)525s = NG_HCI_MAX_CON_NUM;526527/* Prepare response */528NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2),529M_NOWAIT);530if (rsp == NULL) {531error = ENOMEM;532break;533}534535e1 = (ng_hci_node_con_list_ep *)(rsp->data);536e2 = (ng_hci_node_con_ep *)(e1 + 1);537538e1->num_connections = s;539540LIST_FOREACH(c, &unit->con_list, next) {541e2->link_type = c->link_type;542e2->encryption_mode= c->encryption_mode;543e2->mode = c->mode;544e2->role = c->role;545546e2->state = c->state;547548e2->pending = c->pending;549e2->queue_len = NG_BT_ITEMQ_LEN(&c->conq);550551e2->con_handle = c->con_handle;552bcopy(&c->bdaddr, &e2->bdaddr,553sizeof(e2->bdaddr));554555e2 ++;556if (--s <= 0)557break;558}559} break;560561/* Get link policy settings mask */562case NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK:563NG_MKRESPONSE(rsp, msg, sizeof(unit->link_policy_mask),564M_NOWAIT);565if (rsp == NULL) {566error = ENOMEM;567break;568}569570*((ng_hci_node_link_policy_mask_ep *)(rsp->data)) =571unit->link_policy_mask;572break;573574/* Set link policy settings mask */575case NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK:576if (msg->header.arglen !=577sizeof(ng_hci_node_link_policy_mask_ep)) {578error = EMSGSIZE;579break;580}581582unit->link_policy_mask =583*((ng_hci_node_link_policy_mask_ep *)584(msg->data));585break;586587/* Get packet mask */588case NGM_HCI_NODE_GET_PACKET_MASK:589NG_MKRESPONSE(rsp, msg, sizeof(unit->packet_mask),590M_NOWAIT);591if (rsp == NULL) {592error = ENOMEM;593break;594}595596*((ng_hci_node_packet_mask_ep *)(rsp->data)) =597unit->packet_mask;598break;599600/* Set packet mask */601case NGM_HCI_NODE_SET_PACKET_MASK:602if (msg->header.arglen !=603sizeof(ng_hci_node_packet_mask_ep)) {604error = EMSGSIZE;605break;606}607608unit->packet_mask =609*((ng_hci_node_packet_mask_ep *)(msg->data));610break;611612/* Get role switch */613case NGM_HCI_NODE_GET_ROLE_SWITCH:614NG_MKRESPONSE(rsp, msg, sizeof(unit->role_switch),615M_NOWAIT);616if (rsp == NULL) {617error = ENOMEM;618break;619}620621*((ng_hci_node_role_switch_ep *)(rsp->data)) =622unit->role_switch;623break;624625/* Set role switch */626case NGM_HCI_NODE_SET_ROLE_SWITCH:627if (msg->header.arglen !=628sizeof(ng_hci_node_role_switch_ep)) {629error = EMSGSIZE;630break;631}632633unit->role_switch =634*((ng_hci_node_role_switch_ep *)(msg->data));635break;636637default:638error = EINVAL;639break;640}641break;642643default:644error = EINVAL;645break;646}647648/* NG_RESPOND_MSG should take care of "item" and "rsp" */649NG_RESPOND_MSG(error, node, item, rsp);650NG_FREE_MSG(msg);651652return (error);653} /* ng_hci_default_rcvmsg */654655/*656* Process control message from upstream hooks (ACL and SCO).657* Handle LP_xxx messages here, give everything else to default routine.658*/659660static int661ng_hci_upper_rcvmsg(node_p node, item_p item, hook_p lasthook)662{663ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);664int error = 0;665666switch (NGI_MSG(item)->header.typecookie) {667case NGM_HCI_COOKIE:668switch (NGI_MSG(item)->header.cmd) {669case NGM_HCI_LP_CON_REQ:670error = ng_hci_lp_con_req(unit, item, lasthook);671break;672673case NGM_HCI_LP_DISCON_REQ: /* XXX not defined by specs */674error = ng_hci_lp_discon_req(unit, item, lasthook);675break;676677case NGM_HCI_LP_CON_RSP:678error = ng_hci_lp_con_rsp(unit, item, lasthook);679break;680681case NGM_HCI_LP_QOS_REQ:682error = ng_hci_lp_qos_req(unit, item, lasthook);683break;684685default:686error = ng_hci_default_rcvmsg(node, item, lasthook);687break;688}689break;690691default:692error = ng_hci_default_rcvmsg(node, item, lasthook);693break;694}695696return (error);697} /* ng_hci_upper_rcvmsg */698699/*700* Process data packet from the driver hook.701* We expect HCI events, ACL or SCO data packets.702*/703704static int705ng_hci_drv_rcvdata(hook_p hook, item_p item)706{707ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));708struct mbuf *m = NULL;709int error = 0;710711/* Process packet */712m = NGI_M(item); /* item still has mbuf, just peeking */713m->m_flags |= M_PROTO1; /* mark as incoming packet */714715NG_HCI_STAT_BYTES_RECV(unit->stat, m->m_pkthdr.len);716717/* Give copy packet to RAW hook */718ng_hci_mtap(unit, m);719720/*721* XXX XXX XXX722* Lower layer drivers MUST NOT send mbuf chain with empty mbuf at723* the beginning of the chain. HCI layer WILL NOT call m_pullup() here.724*/725726switch (*mtod(m, u_int8_t *)) {727case NG_HCI_ACL_DATA_PKT:728NG_HCI_STAT_ACL_RECV(unit->stat);729730if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY ||731unit->acl == NULL || NG_HOOK_NOT_VALID(unit->acl)) {732NG_HCI_WARN(733"%s: %s - could not forward HCI ACL data packet, state=%#x, hook=%p\n",734__func__, NG_NODE_NAME(unit->node),735unit->state, unit->acl);736737NG_FREE_ITEM(item);738} else739NG_FWD_ITEM_HOOK(error, item, unit->acl);740break;741742case NG_HCI_SCO_DATA_PKT:743NG_HCI_STAT_SCO_RECV(unit->stat);744745if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY ||746unit->sco == NULL || NG_HOOK_NOT_VALID(unit->sco)) {747NG_HCI_INFO(748"%s: %s - could not forward HCI SCO data packet, state=%#x, hook=%p\n",749__func__, NG_NODE_NAME(unit->node),750unit->state, unit->sco);751752NG_FREE_ITEM(item);753} else754NG_FWD_ITEM_HOOK(error, item, unit->sco);755break;756757case NG_HCI_EVENT_PKT:758NG_HCI_STAT_EVNT_RECV(unit->stat);759760/* Detach mbuf, discard item and process event */761NGI_GET_M(item, m);762NG_FREE_ITEM(item);763764error = ng_hci_process_event(unit, m);765break;766767default:768NG_HCI_ALERT(769"%s: %s - got unknown HCI packet type=%#x\n",770__func__, NG_NODE_NAME(unit->node),771*mtod(m, u_int8_t *));772773NG_FREE_ITEM(item);774775error = EINVAL;776break;777}778779return (error);780} /* ng_hci_drv_rcvdata */781782/*783* Process data packet from ACL upstream hook.784* We expect valid HCI ACL data packets.785*/786787static int788ng_hci_acl_rcvdata(hook_p hook, item_p item)789{790ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));791struct mbuf *m = NULL;792ng_hci_unit_con_p con = NULL;793u_int16_t con_handle;794int size, error = 0;795796NG_HCI_BUFF_ACL_SIZE(unit->buffer, size);797/* Check packet */798NGI_GET_M(item, m);799800if (*mtod(m, u_int8_t *) != NG_HCI_ACL_DATA_PKT) {801NG_HCI_ALERT(802"%s: %s - invalid HCI data packet type=%#x\n",803__func__, NG_NODE_NAME(unit->node),804*mtod(m, u_int8_t *));805806error = EINVAL;807goto drop;808}809if (m->m_pkthdr.len < sizeof(ng_hci_acldata_pkt_t) ||810m->m_pkthdr.len > sizeof(ng_hci_acldata_pkt_t) + size) {811NG_HCI_ALERT(812"%s: %s - invalid HCI ACL data packet, len=%d, mtu=%d\n",813__func__, NG_NODE_NAME(unit->node),814m->m_pkthdr.len, size);815816error = EMSGSIZE;817goto drop;818}819820NG_HCI_M_PULLUP(m, sizeof(ng_hci_acldata_pkt_t));821if (m == NULL) {822error = ENOBUFS;823goto drop;824}825826con_handle = NG_HCI_CON_HANDLE(le16toh(827mtod(m, ng_hci_acldata_pkt_t *)->con_handle));828size = le16toh(mtod(m, ng_hci_acldata_pkt_t *)->length);829830if (m->m_pkthdr.len != sizeof(ng_hci_acldata_pkt_t) + size) {831NG_HCI_ALERT(832"%s: %s - invalid HCI ACL data packet size, len=%d, length=%d\n",833__func__, NG_NODE_NAME(unit->node),834m->m_pkthdr.len, size);835836error = EMSGSIZE;837goto drop;838}839840/* Queue packet */841con = ng_hci_con_by_handle(unit, con_handle);842if (con == NULL) {843NG_HCI_ERR(844"%s: %s - unexpected HCI ACL data packet. Connection does not exists, " \845"con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), con_handle);846847error = ENOENT;848goto drop;849}850851if (con->link_type == NG_HCI_LINK_SCO) {852NG_HCI_ERR(853"%s: %s - unexpected HCI ACL data packet. Not ACL link, con_handle=%d, " \854"link_type=%d\n", __func__, NG_NODE_NAME(unit->node),855con_handle, con->link_type);856857error = EINVAL;858goto drop;859}860861if (con->state != NG_HCI_CON_OPEN) {862NG_HCI_ERR(863"%s: %s - unexpected HCI ACL data packet. Invalid connection state=%d, " \864"con_handle=%d\n", __func__, NG_NODE_NAME(unit->node),865con->state, con_handle);866867error = EHOSTDOWN;868goto drop;869}870871if (NG_BT_ITEMQ_FULL(&con->conq)) {872NG_HCI_ALERT(873"%s: %s - dropping HCI ACL data packet, con_handle=%d, len=%d, queue_len=%d\n",874__func__, NG_NODE_NAME(unit->node), con_handle,875m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq));876877NG_BT_ITEMQ_DROP(&con->conq);878879error = ENOBUFS;880goto drop;881}882883/* Queue item and schedule data transfer */884NGI_M(item) = m;885NG_BT_ITEMQ_ENQUEUE(&con->conq, item);886item = NULL;887m = NULL;888889ng_hci_send_data(unit);890drop:891if (item != NULL)892NG_FREE_ITEM(item);893894NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */895896return (error);897} /* ng_hci_acl_rcvdata */898899/*900* Process data packet from SCO upstream hook.901* We expect valid HCI SCO data packets902*/903904static int905ng_hci_sco_rcvdata(hook_p hook, item_p item)906{907ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));908struct mbuf *m = NULL;909ng_hci_unit_con_p con = NULL;910u_int16_t con_handle;911int size, error = 0;912913NG_HCI_BUFF_SCO_SIZE(unit->buffer, size);914915/* Check packet */916NGI_GET_M(item, m);917918if (*mtod(m, u_int8_t *) != NG_HCI_SCO_DATA_PKT) {919NG_HCI_ALERT(920"%s: %s - invalid HCI data packet type=%#x\n",921__func__, NG_NODE_NAME(unit->node),922*mtod(m, u_int8_t *));923924error = EINVAL;925goto drop;926}927928if (m->m_pkthdr.len < sizeof(ng_hci_scodata_pkt_t) ||929m->m_pkthdr.len > sizeof(ng_hci_scodata_pkt_t) + size) {930NG_HCI_ALERT(931"%s: %s - invalid HCI SCO data packet, len=%d, mtu=%d\n",932__func__, NG_NODE_NAME(unit->node),933m->m_pkthdr.len, size);934935error = EMSGSIZE;936goto drop;937}938939NG_HCI_M_PULLUP(m, sizeof(ng_hci_scodata_pkt_t));940if (m == NULL) {941error = ENOBUFS;942goto drop;943}944945con_handle = NG_HCI_CON_HANDLE(le16toh(946mtod(m, ng_hci_scodata_pkt_t *)->con_handle));947size = mtod(m, ng_hci_scodata_pkt_t *)->length;948949if (m->m_pkthdr.len != sizeof(ng_hci_scodata_pkt_t) + size) {950NG_HCI_ALERT(951"%s: %s - invalid HCI SCO data packet size, len=%d, length=%d\n",952__func__, NG_NODE_NAME(unit->node),953m->m_pkthdr.len, size);954955error = EMSGSIZE;956goto drop;957}958959/* Queue packet */960con = ng_hci_con_by_handle(unit, con_handle);961if (con == NULL) {962NG_HCI_ERR(963"%s: %s - unexpected HCI SCO data packet. Connection does not exists, " \964"con_handle=%d\n", __func__, NG_NODE_NAME(unit->node), con_handle);965966error = ENOENT;967goto drop;968}969970if (con->link_type != NG_HCI_LINK_SCO) {971NG_HCI_ERR(972"%s: %s - unexpected HCI SCO data packet. Not SCO link, con_handle=%d, " \973"link_type=%d\n", __func__, NG_NODE_NAME(unit->node),974con_handle, con->link_type);975976error = EINVAL;977goto drop;978}979980if (con->state != NG_HCI_CON_OPEN) {981NG_HCI_ERR(982"%s: %s - unexpected HCI SCO data packet. Invalid connection state=%d, " \983"con_handle=%d\n", __func__, NG_NODE_NAME(unit->node),984con->state, con_handle);985986error = EHOSTDOWN;987goto drop;988}989990if (NG_BT_ITEMQ_FULL(&con->conq)) {991NG_HCI_ALERT(992"%s: %s - dropping HCI SCO data packet, con_handle=%d, len=%d, queue_len=%d\n",993__func__, NG_NODE_NAME(unit->node), con_handle,994m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq));995996NG_BT_ITEMQ_DROP(&con->conq);997998error = ENOBUFS;999goto drop;1000}10011002/* Queue item and schedule data transfer */1003NGI_M(item) = m;1004NG_BT_ITEMQ_ENQUEUE(&con->conq, item);1005item = NULL;1006m = NULL;10071008ng_hci_send_data(unit);1009drop:1010if (item != NULL)1011NG_FREE_ITEM(item);10121013NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */10141015return (error);1016} /* ng_hci_sco_rcvdata */10171018/*1019* Process data packet from uptream RAW hook.1020* We expect valid HCI command packets.1021*/10221023static int1024ng_hci_raw_rcvdata(hook_p hook, item_p item)1025{1026ng_hci_unit_p unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));1027struct mbuf *m = NULL;1028int error = 0;10291030NGI_GET_M(item, m);1031NG_FREE_ITEM(item);10321033/* Check packet */1034if (*mtod(m, u_int8_t *) != NG_HCI_CMD_PKT) {1035NG_HCI_ALERT(1036"%s: %s - invalid HCI command packet type=%#x\n",1037__func__, NG_NODE_NAME(unit->node),1038*mtod(m, u_int8_t *));10391040error = EINVAL;1041goto drop;1042}10431044if (m->m_pkthdr.len < sizeof(ng_hci_cmd_pkt_t)) {1045NG_HCI_ALERT(1046"%s: %s - invalid HCI command packet len=%d\n",1047__func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len);10481049error = EMSGSIZE;1050goto drop;1051}10521053NG_HCI_M_PULLUP(m, sizeof(ng_hci_cmd_pkt_t));1054if (m == NULL) {1055error = ENOBUFS;1056goto drop;1057}10581059if (m->m_pkthdr.len !=1060mtod(m, ng_hci_cmd_pkt_t *)->length + sizeof(ng_hci_cmd_pkt_t)) {1061NG_HCI_ALERT(1062"%s: %s - invalid HCI command packet size, len=%d, length=%d\n",1063__func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len,1064mtod(m, ng_hci_cmd_pkt_t *)->length);10651066error = EMSGSIZE;1067goto drop;1068}10691070if (mtod(m, ng_hci_cmd_pkt_t *)->opcode == 0) {1071NG_HCI_ALERT(1072"%s: %s - invalid HCI command opcode\n",1073__func__, NG_NODE_NAME(unit->node));10741075error = EINVAL;1076goto drop;1077}10781079if (NG_BT_MBUFQ_FULL(&unit->cmdq)) {1080NG_HCI_ALERT(1081"%s: %s - dropping HCI command packet, len=%d, queue_len=%d\n",1082__func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len,1083NG_BT_MBUFQ_LEN(&unit->cmdq));10841085NG_BT_MBUFQ_DROP(&unit->cmdq);10861087error = ENOBUFS;1088goto drop;1089}10901091/* Queue and send command */1092NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);1093m = NULL;10941095if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))1096error = ng_hci_send_command(unit);1097drop:1098NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */10991100return (error);1101} /* ng_hci_raw_rcvdata */110211031104