Path: blob/main/sys/netgraph/bluetooth/socket/ng_btsocket_sco.c
34677 views
/*1* ng_btsocket_sco.c2*/34/*-5* SPDX-License-Identifier: BSD-2-Clause6*7* Copyright (c) 2001-2002 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_btsocket_sco.c,v 1.2 2005/10/31 18:08:51 max Exp $32*/3334#include <sys/param.h>35#include <sys/systm.h>36#include <sys/bitstring.h>37#include <sys/domain.h>38#include <sys/endian.h>39#include <sys/errno.h>40#include <sys/filedesc.h>41#include <sys/ioccom.h>42#include <sys/kernel.h>43#include <sys/lock.h>44#include <sys/malloc.h>45#include <sys/mbuf.h>46#include <sys/mutex.h>47#include <sys/protosw.h>48#include <sys/queue.h>49#include <sys/socket.h>50#include <sys/socketvar.h>51#include <sys/sysctl.h>52#include <sys/taskqueue.h>5354#include <net/vnet.h>5556#include <netgraph/ng_message.h>57#include <netgraph/netgraph.h>58#include <netgraph/bluetooth/include/ng_bluetooth.h>59#include <netgraph/bluetooth/include/ng_hci.h>60#include <netgraph/bluetooth/include/ng_l2cap.h>61#include <netgraph/bluetooth/include/ng_btsocket.h>62#include <netgraph/bluetooth/include/ng_btsocket_sco.h>6364/* MALLOC define */65#ifdef NG_SEPARATE_MALLOC66static MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_SCO, "netgraph_btsocks_sco",67"Netgraph Bluetooth SCO sockets");68#else69#define M_NETGRAPH_BTSOCKET_SCO M_NETGRAPH70#endif /* NG_SEPARATE_MALLOC */7172/* Netgraph node methods */73static ng_constructor_t ng_btsocket_sco_node_constructor;74static ng_rcvmsg_t ng_btsocket_sco_node_rcvmsg;75static ng_shutdown_t ng_btsocket_sco_node_shutdown;76static ng_newhook_t ng_btsocket_sco_node_newhook;77static ng_connect_t ng_btsocket_sco_node_connect;78static ng_rcvdata_t ng_btsocket_sco_node_rcvdata;79static ng_disconnect_t ng_btsocket_sco_node_disconnect;8081static void ng_btsocket_sco_input (void *, int);82static void ng_btsocket_sco_rtclean (void *, int);8384/* Netgraph type descriptor */85static struct ng_type typestruct = {86.version = NG_ABI_VERSION,87.name = NG_BTSOCKET_SCO_NODE_TYPE,88.constructor = ng_btsocket_sco_node_constructor,89.rcvmsg = ng_btsocket_sco_node_rcvmsg,90.shutdown = ng_btsocket_sco_node_shutdown,91.newhook = ng_btsocket_sco_node_newhook,92.connect = ng_btsocket_sco_node_connect,93.rcvdata = ng_btsocket_sco_node_rcvdata,94.disconnect = ng_btsocket_sco_node_disconnect,95};9697/* Globals */98static u_int32_t ng_btsocket_sco_debug_level;99static node_p ng_btsocket_sco_node;100static struct ng_bt_itemq ng_btsocket_sco_queue;101static struct mtx ng_btsocket_sco_queue_mtx;102static struct task ng_btsocket_sco_queue_task;103static struct mtx ng_btsocket_sco_sockets_mtx;104static LIST_HEAD(, ng_btsocket_sco_pcb) ng_btsocket_sco_sockets;105static LIST_HEAD(, ng_btsocket_sco_rtentry) ng_btsocket_sco_rt;106static struct mtx ng_btsocket_sco_rt_mtx;107static struct task ng_btsocket_sco_rt_task;108static struct timeval ng_btsocket_sco_lasttime;109static int ng_btsocket_sco_curpps;110111/* Sysctl tree */112SYSCTL_DECL(_net_bluetooth_sco_sockets);113static SYSCTL_NODE(_net_bluetooth_sco_sockets, OID_AUTO, seq,114CTLFLAG_RW | CTLFLAG_MPSAFE, 0,115"Bluetooth SEQPACKET SCO sockets family");116SYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, debug_level,117CTLFLAG_RW,118&ng_btsocket_sco_debug_level, NG_BTSOCKET_WARN_LEVEL,119"Bluetooth SEQPACKET SCO sockets debug level");120SYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, queue_len,121CTLFLAG_RD,122&ng_btsocket_sco_queue.len, 0,123"Bluetooth SEQPACKET SCO sockets input queue length");124SYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, queue_maxlen,125CTLFLAG_RD,126&ng_btsocket_sco_queue.maxlen, 0,127"Bluetooth SEQPACKET SCO sockets input queue max. length");128SYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, queue_drops,129CTLFLAG_RD,130&ng_btsocket_sco_queue.drops, 0,131"Bluetooth SEQPACKET SCO sockets input queue drops");132133/* Debug */134#define NG_BTSOCKET_SCO_INFO \135if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_INFO_LEVEL && \136ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \137printf138139#define NG_BTSOCKET_SCO_WARN \140if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_WARN_LEVEL && \141ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \142printf143144#define NG_BTSOCKET_SCO_ERR \145if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_ERR_LEVEL && \146ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \147printf148149#define NG_BTSOCKET_SCO_ALERT \150if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_ALERT_LEVEL && \151ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \152printf153154/*155* Netgraph message processing routines156*/157158static int ng_btsocket_sco_process_lp_con_cfm159(struct ng_mesg *, ng_btsocket_sco_rtentry_p);160static int ng_btsocket_sco_process_lp_con_ind161(struct ng_mesg *, ng_btsocket_sco_rtentry_p);162static int ng_btsocket_sco_process_lp_discon_ind163(struct ng_mesg *, ng_btsocket_sco_rtentry_p);164165/*166* Send LP messages to the lower layer167*/168169static int ng_btsocket_sco_send_lp_con_req170(ng_btsocket_sco_pcb_p);171static int ng_btsocket_sco_send_lp_con_rsp172(ng_btsocket_sco_rtentry_p, bdaddr_p, int);173static int ng_btsocket_sco_send_lp_discon_req174(ng_btsocket_sco_pcb_p);175176static int ng_btsocket_sco_send2177(ng_btsocket_sco_pcb_p);178179/*180* Timeout processing routines181*/182183static void ng_btsocket_sco_timeout (ng_btsocket_sco_pcb_p);184static void ng_btsocket_sco_untimeout (ng_btsocket_sco_pcb_p);185static void ng_btsocket_sco_process_timeout (void *);186187/*188* Other stuff189*/190191static ng_btsocket_sco_pcb_p ng_btsocket_sco_pcb_by_addr(bdaddr_p);192static ng_btsocket_sco_pcb_p ng_btsocket_sco_pcb_by_handle(bdaddr_p, int);193static ng_btsocket_sco_pcb_p ng_btsocket_sco_pcb_by_addrs(bdaddr_p, bdaddr_p);194195#define ng_btsocket_sco_wakeup_input_task() \196taskqueue_enqueue(taskqueue_swi, &ng_btsocket_sco_queue_task)197198#define ng_btsocket_sco_wakeup_route_task() \199taskqueue_enqueue(taskqueue_swi, &ng_btsocket_sco_rt_task)200201/*****************************************************************************202*****************************************************************************203** Netgraph node interface204*****************************************************************************205*****************************************************************************/206207/*208* Netgraph node constructor. Do not allow to create node of this type.209*/210211static int212ng_btsocket_sco_node_constructor(node_p node)213{214return (EINVAL);215} /* ng_btsocket_sco_node_constructor */216217/*218* Do local shutdown processing. Let old node go and create new fresh one.219*/220221static int222ng_btsocket_sco_node_shutdown(node_p node)223{224int error = 0;225226NG_NODE_UNREF(node);227228/* Create new node */229error = ng_make_node_common(&typestruct, &ng_btsocket_sco_node);230if (error != 0) {231NG_BTSOCKET_SCO_ALERT(232"%s: Could not create Netgraph node, error=%d\n", __func__, error);233234ng_btsocket_sco_node = NULL;235236return (error);237}238239error = ng_name_node(ng_btsocket_sco_node,240NG_BTSOCKET_SCO_NODE_TYPE);241if (error != 0) {242NG_BTSOCKET_SCO_ALERT(243"%s: Could not name Netgraph node, error=%d\n", __func__, error);244245NG_NODE_UNREF(ng_btsocket_sco_node);246ng_btsocket_sco_node = NULL;247248return (error);249}250251return (0);252} /* ng_btsocket_sco_node_shutdown */253254/*255* We allow any hook to be connected to the node.256*/257258static int259ng_btsocket_sco_node_newhook(node_p node, hook_p hook, char const *name)260{261return (0);262} /* ng_btsocket_sco_node_newhook */263264/*265* Just say "YEP, that's OK by me!"266*/267268static int269ng_btsocket_sco_node_connect(hook_p hook)270{271NG_HOOK_SET_PRIVATE(hook, NULL);272NG_HOOK_REF(hook); /* Keep extra reference to the hook */273274#if 0275NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));276NG_HOOK_FORCE_QUEUE(hook);277#endif278279return (0);280} /* ng_btsocket_sco_node_connect */281282/*283* Hook disconnection. Schedule route cleanup task284*/285286static int287ng_btsocket_sco_node_disconnect(hook_p hook)288{289/*290* If hook has private information than we must have this hook in291* the routing table and must schedule cleaning for the routing table.292* Otherwise hook was connected but we never got "hook_info" message,293* so we have never added this hook to the routing table and it save294* to just delete it.295*/296297if (NG_HOOK_PRIVATE(hook) != NULL)298return (ng_btsocket_sco_wakeup_route_task());299300NG_HOOK_UNREF(hook); /* Remove extra reference */301302return (0);303} /* ng_btsocket_sco_node_disconnect */304305/*306* Process incoming messages307*/308309static int310ng_btsocket_sco_node_rcvmsg(node_p node, item_p item, hook_p hook)311{312struct ng_mesg *msg = NGI_MSG(item); /* item still has message */313int error = 0;314315if (msg != NULL && msg->header.typecookie == NGM_HCI_COOKIE) {316mtx_lock(&ng_btsocket_sco_queue_mtx);317if (NG_BT_ITEMQ_FULL(&ng_btsocket_sco_queue)) {318NG_BTSOCKET_SCO_ERR(319"%s: Input queue is full (msg)\n", __func__);320321NG_BT_ITEMQ_DROP(&ng_btsocket_sco_queue);322NG_FREE_ITEM(item);323error = ENOBUFS;324} else {325if (hook != NULL) {326NG_HOOK_REF(hook);327NGI_SET_HOOK(item, hook);328}329330NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_sco_queue, item);331error = ng_btsocket_sco_wakeup_input_task();332}333mtx_unlock(&ng_btsocket_sco_queue_mtx);334} else {335NG_FREE_ITEM(item);336error = EINVAL;337}338339return (error);340} /* ng_btsocket_sco_node_rcvmsg */341342/*343* Receive data on a hook344*/345346static int347ng_btsocket_sco_node_rcvdata(hook_p hook, item_p item)348{349int error = 0;350351mtx_lock(&ng_btsocket_sco_queue_mtx);352if (NG_BT_ITEMQ_FULL(&ng_btsocket_sco_queue)) {353NG_BTSOCKET_SCO_ERR(354"%s: Input queue is full (data)\n", __func__);355356NG_BT_ITEMQ_DROP(&ng_btsocket_sco_queue);357NG_FREE_ITEM(item);358error = ENOBUFS;359} else {360NG_HOOK_REF(hook);361NGI_SET_HOOK(item, hook);362363NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_sco_queue, item);364error = ng_btsocket_sco_wakeup_input_task();365}366mtx_unlock(&ng_btsocket_sco_queue_mtx);367368return (error);369} /* ng_btsocket_sco_node_rcvdata */370371/*372* Process LP_ConnectCfm event from the lower layer protocol373*/374375static int376ng_btsocket_sco_process_lp_con_cfm(struct ng_mesg *msg,377ng_btsocket_sco_rtentry_p rt)378{379ng_hci_lp_con_cfm_ep *ep = NULL;380ng_btsocket_sco_pcb_t *pcb = NULL;381int error = 0;382383if (msg->header.arglen != sizeof(*ep))384return (EMSGSIZE);385386ep = (ng_hci_lp_con_cfm_ep *)(msg->data);387388mtx_lock(&ng_btsocket_sco_sockets_mtx);389390/* Look for the socket with the token */391pcb = ng_btsocket_sco_pcb_by_addrs(&rt->src, &ep->bdaddr);392if (pcb == NULL) {393mtx_unlock(&ng_btsocket_sco_sockets_mtx);394return (ENOENT);395}396397/* pcb is locked */398399NG_BTSOCKET_SCO_INFO(400"%s: Got LP_ConnectCfm response, src bdaddr=%x:%x:%x:%x:%x:%x, " \401"dst bdaddr=%x:%x:%x:%x:%x:%x, status=%d, handle=%d, state=%d\n",402__func__,403pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],404pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],405pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],406pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],407ep->status, ep->con_handle, pcb->state);408409if (pcb->state != NG_BTSOCKET_SCO_CONNECTING) {410mtx_unlock(&pcb->pcb_mtx);411mtx_unlock(&ng_btsocket_sco_sockets_mtx);412413return (ENOENT);414}415416ng_btsocket_sco_untimeout(pcb);417418if (ep->status == 0) {419/*420* Connection is open. Update connection handle and421* socket state422*/423424pcb->con_handle = ep->con_handle;425pcb->state = NG_BTSOCKET_SCO_OPEN;426soisconnected(pcb->so);427} else {428/*429* We have failed to open connection, so disconnect the socket430*/431432pcb->so->so_error = ECONNREFUSED; /* XXX convert status ??? */433pcb->state = NG_BTSOCKET_SCO_CLOSED;434soisdisconnected(pcb->so);435}436437mtx_unlock(&pcb->pcb_mtx);438mtx_unlock(&ng_btsocket_sco_sockets_mtx);439440return (error);441} /* ng_btsocket_sco_process_lp_con_cfm */442443/*444* Process LP_ConnectInd indicator. Find socket that listens on address.445* Find exact or closest match.446*/447448static int449ng_btsocket_sco_process_lp_con_ind(struct ng_mesg *msg,450ng_btsocket_sco_rtentry_p rt)451{452ng_hci_lp_con_ind_ep *ep = NULL;453ng_btsocket_sco_pcb_t *pcb = NULL, *pcb1 = NULL;454int error = 0;455u_int16_t status = 0;456457if (msg->header.arglen != sizeof(*ep))458return (EMSGSIZE);459460ep = (ng_hci_lp_con_ind_ep *)(msg->data);461462NG_BTSOCKET_SCO_INFO(463"%s: Got LP_ConnectInd indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \464"dst bdaddr=%x:%x:%x:%x:%x:%x\n",465__func__,466rt->src.b[5], rt->src.b[4], rt->src.b[3],467rt->src.b[2], rt->src.b[1], rt->src.b[0],468ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],469ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);470471mtx_lock(&ng_btsocket_sco_sockets_mtx);472473pcb = ng_btsocket_sco_pcb_by_addr(&rt->src);474if (pcb != NULL) {475struct socket *so1;476477/* pcb is locked */478479CURVNET_SET(pcb->so->so_vnet);480so1 = sonewconn(pcb->so, 0);481CURVNET_RESTORE();482483if (so1 == NULL) {484status = 0x0d; /* Rejected due to limited resources */485goto respond;486}487488/*489* If we got here than we have created new socket. So complete490* connection. If we we listening on specific address then copy491* source address from listening socket, otherwise copy source492* address from hook's routing information.493*/494495pcb1 = so2sco_pcb(so1);496KASSERT((pcb1 != NULL),497("%s: pcb1 == NULL\n", __func__));498499mtx_lock(&pcb1->pcb_mtx);500501if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src)) != 0)502bcopy(&pcb->src, &pcb1->src, sizeof(pcb1->src));503else504bcopy(&rt->src, &pcb1->src, sizeof(pcb1->src));505506pcb1->flags &= ~NG_BTSOCKET_SCO_CLIENT;507508bcopy(&ep->bdaddr, &pcb1->dst, sizeof(pcb1->dst));509pcb1->rt = rt;510} else511/* Nobody listens on requested BDADDR */512status = 0x1f; /* Unspecified Error */513514respond:515error = ng_btsocket_sco_send_lp_con_rsp(rt, &ep->bdaddr, status);516if (pcb1 != NULL) {517if (error != 0) {518pcb1->so->so_error = error;519pcb1->state = NG_BTSOCKET_SCO_CLOSED;520soisdisconnected(pcb1->so);521} else {522pcb1->state = NG_BTSOCKET_SCO_CONNECTING;523soisconnecting(pcb1->so);524525ng_btsocket_sco_timeout(pcb1);526}527528mtx_unlock(&pcb1->pcb_mtx);529}530531if (pcb != NULL)532mtx_unlock(&pcb->pcb_mtx);533534mtx_unlock(&ng_btsocket_sco_sockets_mtx);535536return (error);537} /* ng_btsocket_sco_process_lp_con_ind */538539/*540* Process LP_DisconnectInd indicator541*/542543static int544ng_btsocket_sco_process_lp_discon_ind(struct ng_mesg *msg,545ng_btsocket_sco_rtentry_p rt)546{547ng_hci_lp_discon_ind_ep *ep = NULL;548ng_btsocket_sco_pcb_t *pcb = NULL;549550/* Check message */551if (msg->header.arglen != sizeof(*ep))552return (EMSGSIZE);553554ep = (ng_hci_lp_discon_ind_ep *)(msg->data);555556mtx_lock(&ng_btsocket_sco_sockets_mtx);557558/* Look for the socket with given channel ID */559pcb = ng_btsocket_sco_pcb_by_handle(&rt->src, ep->con_handle);560if (pcb == NULL) {561mtx_unlock(&ng_btsocket_sco_sockets_mtx);562return (0);563}564565/*566* Disconnect the socket. If there was any pending request we can567* not do anything here anyway.568*/569570/* pcb is locked */571572NG_BTSOCKET_SCO_INFO(573"%s: Got LP_DisconnectInd indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \574"dst bdaddr=%x:%x:%x:%x:%x:%x, handle=%d, state=%d\n",575__func__,576pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],577pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],578pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],579pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],580pcb->con_handle, pcb->state);581582if (pcb->flags & NG_BTSOCKET_SCO_TIMO)583ng_btsocket_sco_untimeout(pcb);584585pcb->state = NG_BTSOCKET_SCO_CLOSED;586soisdisconnected(pcb->so);587588mtx_unlock(&pcb->pcb_mtx);589mtx_unlock(&ng_btsocket_sco_sockets_mtx);590591return (0);592} /* ng_btsocket_sco_process_lp_discon_ind */593594/*595* Send LP_ConnectReq request596*/597598static int599ng_btsocket_sco_send_lp_con_req(ng_btsocket_sco_pcb_p pcb)600{601struct ng_mesg *msg = NULL;602ng_hci_lp_con_req_ep *ep = NULL;603int error = 0;604605mtx_assert(&pcb->pcb_mtx, MA_OWNED);606607if (pcb->rt == NULL ||608pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))609return (ENETDOWN);610611NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_REQ,612sizeof(*ep), M_NOWAIT);613if (msg == NULL)614return (ENOMEM);615616ep = (ng_hci_lp_con_req_ep *)(msg->data);617ep->link_type = NG_HCI_LINK_SCO;618bcopy(&pcb->dst, &ep->bdaddr, sizeof(ep->bdaddr));619620NG_SEND_MSG_HOOK(error, ng_btsocket_sco_node, msg, pcb->rt->hook, 0);621622return (error);623} /* ng_btsocket_sco_send_lp_con_req */624625/*626* Send LP_ConnectRsp response627*/628629static int630ng_btsocket_sco_send_lp_con_rsp(ng_btsocket_sco_rtentry_p rt, bdaddr_p dst, int status)631{632struct ng_mesg *msg = NULL;633ng_hci_lp_con_rsp_ep *ep = NULL;634int error = 0;635636if (rt == NULL || rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))637return (ENETDOWN);638639NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP,640sizeof(*ep), M_NOWAIT);641if (msg == NULL)642return (ENOMEM);643644ep = (ng_hci_lp_con_rsp_ep *)(msg->data);645ep->status = status;646ep->link_type = NG_HCI_LINK_SCO;647bcopy(dst, &ep->bdaddr, sizeof(ep->bdaddr));648649NG_SEND_MSG_HOOK(error, ng_btsocket_sco_node, msg, rt->hook, 0);650651return (error);652} /* ng_btsocket_sco_send_lp_con_rsp */653654/*655* Send LP_DisconReq request656*/657658static int659ng_btsocket_sco_send_lp_discon_req(ng_btsocket_sco_pcb_p pcb)660{661struct ng_mesg *msg = NULL;662ng_hci_lp_discon_req_ep *ep = NULL;663int error = 0;664665mtx_assert(&pcb->pcb_mtx, MA_OWNED);666667if (pcb->rt == NULL ||668pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))669return (ENETDOWN);670671NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_REQ,672sizeof(*ep), M_NOWAIT);673if (msg == NULL)674return (ENOMEM);675676ep = (ng_hci_lp_discon_req_ep *)(msg->data);677ep->con_handle = pcb->con_handle;678ep->reason = 0x13; /* User Ended Connection */679680NG_SEND_MSG_HOOK(error, ng_btsocket_sco_node, msg, pcb->rt->hook, 0);681682return (error);683} /* ng_btsocket_sco_send_lp_discon_req */684685/*****************************************************************************686*****************************************************************************687** Socket interface688*****************************************************************************689*****************************************************************************/690691/*692* SCO sockets data input routine693*/694695static void696ng_btsocket_sco_data_input(struct mbuf *m, hook_p hook)697{698ng_hci_scodata_pkt_t *hdr = NULL;699ng_btsocket_sco_pcb_t *pcb = NULL;700ng_btsocket_sco_rtentry_t *rt = NULL;701u_int16_t con_handle;702703if (hook == NULL) {704NG_BTSOCKET_SCO_ALERT(705"%s: Invalid source hook for SCO data packet\n", __func__);706goto drop;707}708709rt = (ng_btsocket_sco_rtentry_t *) NG_HOOK_PRIVATE(hook);710if (rt == NULL) {711NG_BTSOCKET_SCO_ALERT(712"%s: Could not find out source bdaddr for SCO data packet\n", __func__);713goto drop;714}715716/* Make sure we can access header */717if (m->m_pkthdr.len < sizeof(*hdr)) {718NG_BTSOCKET_SCO_ERR(719"%s: SCO data packet too small, len=%d\n", __func__, m->m_pkthdr.len);720goto drop;721}722723if (m->m_len < sizeof(*hdr)) {724m = m_pullup(m, sizeof(*hdr));725if (m == NULL)726goto drop;727}728729/* Strip SCO packet header and verify packet length */730hdr = mtod(m, ng_hci_scodata_pkt_t *);731m_adj(m, sizeof(*hdr));732733if (hdr->length != m->m_pkthdr.len) {734NG_BTSOCKET_SCO_ERR(735"%s: Bad SCO data packet length, len=%d, length=%d\n",736__func__, m->m_pkthdr.len, hdr->length);737goto drop;738}739740/*741* Now process packet742*/743744con_handle = NG_HCI_CON_HANDLE(le16toh(hdr->con_handle));745746NG_BTSOCKET_SCO_INFO(747"%s: Received SCO data packet: src bdaddr=%x:%x:%x:%x:%x:%x, handle=%d, " \748"length=%d\n", __func__,749rt->src.b[5], rt->src.b[4], rt->src.b[3],750rt->src.b[2], rt->src.b[1], rt->src.b[0],751con_handle, hdr->length);752753mtx_lock(&ng_btsocket_sco_sockets_mtx);754755/* Find socket */756pcb = ng_btsocket_sco_pcb_by_handle(&rt->src, con_handle);757if (pcb == NULL) {758mtx_unlock(&ng_btsocket_sco_sockets_mtx);759goto drop;760}761762/* pcb is locked */763764if (pcb->state != NG_BTSOCKET_SCO_OPEN) {765NG_BTSOCKET_SCO_ERR(766"%s: No connected socket found, src bdaddr=%x:%x:%x:%x:%x:%x, state=%d\n",767__func__,768rt->src.b[5], rt->src.b[4], rt->src.b[3],769rt->src.b[2], rt->src.b[1], rt->src.b[0],770pcb->state);771772mtx_unlock(&pcb->pcb_mtx);773mtx_unlock(&ng_btsocket_sco_sockets_mtx);774goto drop;775}776777/* Check if we have enough space in socket receive queue */778if (m->m_pkthdr.len > sbspace(&pcb->so->so_rcv)) {779NG_BTSOCKET_SCO_ERR(780"%s: Not enough space in socket receive queue. Dropping SCO data packet, " \781"src bdaddr=%x:%x:%x:%x:%x:%x, len=%d, space=%ld\n",782__func__,783rt->src.b[5], rt->src.b[4], rt->src.b[3],784rt->src.b[2], rt->src.b[1], rt->src.b[0],785m->m_pkthdr.len,786sbspace(&pcb->so->so_rcv));787788mtx_unlock(&pcb->pcb_mtx);789mtx_unlock(&ng_btsocket_sco_sockets_mtx);790goto drop;791}792793/* Append packet to the socket receive queue and wakeup */794sbappendrecord(&pcb->so->so_rcv, m);795m = NULL;796797sorwakeup(pcb->so);798799mtx_unlock(&pcb->pcb_mtx);800mtx_unlock(&ng_btsocket_sco_sockets_mtx);801drop:802NG_FREE_M(m); /* checks for m != NULL */803} /* ng_btsocket_sco_data_input */804805/*806* SCO sockets default message input routine807*/808809static void810ng_btsocket_sco_default_msg_input(struct ng_mesg *msg, hook_p hook)811{812ng_btsocket_sco_rtentry_t *rt = NULL;813814if (hook == NULL || NG_HOOK_NOT_VALID(hook))815return;816817rt = (ng_btsocket_sco_rtentry_t *) NG_HOOK_PRIVATE(hook);818819switch (msg->header.cmd) {820case NGM_HCI_NODE_UP: {821ng_hci_node_up_ep *ep = NULL;822823if (msg->header.arglen != sizeof(*ep))824break;825826ep = (ng_hci_node_up_ep *)(msg->data);827if (bcmp(&ep->bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)828break;829830if (rt == NULL) {831rt = malloc(sizeof(*rt),832M_NETGRAPH_BTSOCKET_SCO, M_NOWAIT|M_ZERO);833if (rt == NULL)834break;835836NG_HOOK_SET_PRIVATE(hook, rt);837838mtx_lock(&ng_btsocket_sco_rt_mtx);839840LIST_INSERT_HEAD(&ng_btsocket_sco_rt, rt, next);841} else842mtx_lock(&ng_btsocket_sco_rt_mtx);843844bcopy(&ep->bdaddr, &rt->src, sizeof(rt->src));845rt->pkt_size = (ep->pkt_size == 0)? 60 : ep->pkt_size;846rt->num_pkts = ep->num_pkts;847rt->hook = hook;848849mtx_unlock(&ng_btsocket_sco_rt_mtx);850851NG_BTSOCKET_SCO_INFO(852"%s: Updating hook \"%s\", src bdaddr=%x:%x:%x:%x:%x:%x, pkt_size=%d, " \853"num_pkts=%d\n", __func__, NG_HOOK_NAME(hook),854rt->src.b[5], rt->src.b[4], rt->src.b[3],855rt->src.b[2], rt->src.b[1], rt->src.b[0],856rt->pkt_size, rt->num_pkts);857} break;858859case NGM_HCI_SYNC_CON_QUEUE: {860ng_hci_sync_con_queue_ep *ep = NULL;861ng_btsocket_sco_pcb_t *pcb = NULL;862863if (rt == NULL || msg->header.arglen != sizeof(*ep))864break;865866ep = (ng_hci_sync_con_queue_ep *)(msg->data);867868rt->pending -= ep->completed;869if (rt->pending < 0) {870NG_BTSOCKET_SCO_WARN(871"%s: Pending packet counter is out of sync! bdaddr=%x:%x:%x:%x:%x:%x, " \872"handle=%d, pending=%d, completed=%d\n",873__func__,874rt->src.b[5], rt->src.b[4], rt->src.b[3],875rt->src.b[2], rt->src.b[1], rt->src.b[0],876ep->con_handle, rt->pending,877ep->completed);878879rt->pending = 0;880}881882mtx_lock(&ng_btsocket_sco_sockets_mtx);883884/* Find socket */885pcb = ng_btsocket_sco_pcb_by_handle(&rt->src, ep->con_handle);886if (pcb == NULL) {887mtx_unlock(&ng_btsocket_sco_sockets_mtx);888break;889}890891/* pcb is locked */892893/* Check state */894if (pcb->state == NG_BTSOCKET_SCO_OPEN) {895/* Remove timeout */896ng_btsocket_sco_untimeout(pcb);897898/* Drop completed packets from the send queue */899for (; ep->completed > 0; ep->completed --)900sbdroprecord(&pcb->so->so_snd);901902/* Send more if we have any */903if (sbavail(&pcb->so->so_snd) > 0)904if (ng_btsocket_sco_send2(pcb) == 0)905ng_btsocket_sco_timeout(pcb);906907/* Wake up writers */908sowwakeup(pcb->so);909}910911mtx_unlock(&pcb->pcb_mtx);912mtx_unlock(&ng_btsocket_sco_sockets_mtx);913} break;914915default:916NG_BTSOCKET_SCO_WARN(917"%s: Unknown message, cmd=%d\n", __func__, msg->header.cmd);918break;919}920921NG_FREE_MSG(msg); /* Checks for msg != NULL */922} /* ng_btsocket_sco_default_msg_input */923924/*925* SCO sockets LP message input routine926*/927928static void929ng_btsocket_sco_lp_msg_input(struct ng_mesg *msg, hook_p hook)930{931ng_btsocket_sco_rtentry_p rt = NULL;932933if (hook == NULL) {934NG_BTSOCKET_SCO_ALERT(935"%s: Invalid source hook for LP message\n", __func__);936goto drop;937}938939rt = (ng_btsocket_sco_rtentry_p) NG_HOOK_PRIVATE(hook);940if (rt == NULL) {941NG_BTSOCKET_SCO_ALERT(942"%s: Could not find out source bdaddr for LP message\n", __func__);943goto drop;944}945946switch (msg->header.cmd) {947case NGM_HCI_LP_CON_CFM: /* Connection Confirmation Event */948ng_btsocket_sco_process_lp_con_cfm(msg, rt);949break;950951case NGM_HCI_LP_CON_IND: /* Connection Indication Event */952ng_btsocket_sco_process_lp_con_ind(msg, rt);953break;954955case NGM_HCI_LP_DISCON_IND: /* Disconnection Indication Event */956ng_btsocket_sco_process_lp_discon_ind(msg, rt);957break;958959/* XXX FIXME add other LP messages */960961default:962NG_BTSOCKET_SCO_WARN(963"%s: Unknown LP message, cmd=%d\n", __func__, msg->header.cmd);964break;965}966drop:967NG_FREE_MSG(msg);968} /* ng_btsocket_sco_lp_msg_input */969970/*971* SCO sockets input routine972*/973974static void975ng_btsocket_sco_input(void *context, int pending)976{977item_p item = NULL;978hook_p hook = NULL;979980for (;;) {981mtx_lock(&ng_btsocket_sco_queue_mtx);982NG_BT_ITEMQ_DEQUEUE(&ng_btsocket_sco_queue, item);983mtx_unlock(&ng_btsocket_sco_queue_mtx);984985if (item == NULL)986break;987988NGI_GET_HOOK(item, hook);989if (hook != NULL && NG_HOOK_NOT_VALID(hook))990goto drop;991992switch(item->el_flags & NGQF_TYPE) {993case NGQF_DATA: {994struct mbuf *m = NULL;995996NGI_GET_M(item, m);997ng_btsocket_sco_data_input(m, hook);998} break;9991000case NGQF_MESG: {1001struct ng_mesg *msg = NULL;10021003NGI_GET_MSG(item, msg);10041005switch (msg->header.cmd) {1006case NGM_HCI_LP_CON_CFM:1007case NGM_HCI_LP_CON_IND:1008case NGM_HCI_LP_DISCON_IND:1009/* XXX FIXME add other LP messages */1010ng_btsocket_sco_lp_msg_input(msg, hook);1011break;10121013default:1014ng_btsocket_sco_default_msg_input(msg, hook);1015break;1016}1017} break;10181019default:1020KASSERT(0,1021("%s: invalid item type=%ld\n", __func__, (item->el_flags & NGQF_TYPE)));1022break;1023}1024drop:1025if (hook != NULL)1026NG_HOOK_UNREF(hook);10271028NG_FREE_ITEM(item);1029}1030} /* ng_btsocket_sco_input */10311032/*1033* Route cleanup task. Gets scheduled when hook is disconnected. Here we1034* will find all sockets that use "invalid" hook and disconnect them.1035*/10361037static void1038ng_btsocket_sco_rtclean(void *context, int pending)1039{1040ng_btsocket_sco_pcb_p pcb = NULL, pcb_next = NULL;1041ng_btsocket_sco_rtentry_p rt = NULL;10421043/*1044* First disconnect all sockets that use "invalid" hook1045*/10461047mtx_lock(&ng_btsocket_sco_sockets_mtx);10481049for(pcb = LIST_FIRST(&ng_btsocket_sco_sockets); pcb != NULL; ) {1050mtx_lock(&pcb->pcb_mtx);1051pcb_next = LIST_NEXT(pcb, next);10521053if (pcb->rt != NULL &&1054pcb->rt->hook != NULL && NG_HOOK_NOT_VALID(pcb->rt->hook)) {1055if (pcb->flags & NG_BTSOCKET_SCO_TIMO)1056ng_btsocket_sco_untimeout(pcb);10571058pcb->rt = NULL;1059pcb->so->so_error = ENETDOWN;1060pcb->state = NG_BTSOCKET_SCO_CLOSED;1061soisdisconnected(pcb->so);1062}10631064mtx_unlock(&pcb->pcb_mtx);1065pcb = pcb_next;1066}10671068mtx_unlock(&ng_btsocket_sco_sockets_mtx);10691070/*1071* Now cleanup routing table1072*/10731074mtx_lock(&ng_btsocket_sco_rt_mtx);10751076for (rt = LIST_FIRST(&ng_btsocket_sco_rt); rt != NULL; ) {1077ng_btsocket_sco_rtentry_p rt_next = LIST_NEXT(rt, next);10781079if (rt->hook != NULL && NG_HOOK_NOT_VALID(rt->hook)) {1080LIST_REMOVE(rt, next);10811082NG_HOOK_SET_PRIVATE(rt->hook, NULL);1083NG_HOOK_UNREF(rt->hook); /* Remove extra reference */10841085bzero(rt, sizeof(*rt));1086free(rt, M_NETGRAPH_BTSOCKET_SCO);1087}10881089rt = rt_next;1090}10911092mtx_unlock(&ng_btsocket_sco_rt_mtx);1093} /* ng_btsocket_sco_rtclean */10941095/*1096* Initialize everything1097*/10981099static void1100ng_btsocket_sco_init(void *arg __unused)1101{1102int error = 0;11031104ng_btsocket_sco_node = NULL;1105ng_btsocket_sco_debug_level = NG_BTSOCKET_WARN_LEVEL;11061107/* Register Netgraph node type */1108error = ng_newtype(&typestruct);1109if (error != 0) {1110NG_BTSOCKET_SCO_ALERT(1111"%s: Could not register Netgraph node type, error=%d\n", __func__, error);11121113return;1114}11151116/* Create Netgrapg node */1117error = ng_make_node_common(&typestruct, &ng_btsocket_sco_node);1118if (error != 0) {1119NG_BTSOCKET_SCO_ALERT(1120"%s: Could not create Netgraph node, error=%d\n", __func__, error);11211122ng_btsocket_sco_node = NULL;11231124return;1125}11261127error = ng_name_node(ng_btsocket_sco_node, NG_BTSOCKET_SCO_NODE_TYPE);1128if (error != 0) {1129NG_BTSOCKET_SCO_ALERT(1130"%s: Could not name Netgraph node, error=%d\n", __func__, error);11311132NG_NODE_UNREF(ng_btsocket_sco_node);1133ng_btsocket_sco_node = NULL;11341135return;1136}11371138/* Create input queue */1139NG_BT_ITEMQ_INIT(&ng_btsocket_sco_queue, 300);1140mtx_init(&ng_btsocket_sco_queue_mtx,1141"btsocks_sco_queue_mtx", NULL, MTX_DEF);1142TASK_INIT(&ng_btsocket_sco_queue_task, 0,1143ng_btsocket_sco_input, NULL);11441145/* Create list of sockets */1146LIST_INIT(&ng_btsocket_sco_sockets);1147mtx_init(&ng_btsocket_sco_sockets_mtx,1148"btsocks_sco_sockets_mtx", NULL, MTX_DEF);11491150/* Routing table */1151LIST_INIT(&ng_btsocket_sco_rt);1152mtx_init(&ng_btsocket_sco_rt_mtx,1153"btsocks_sco_rt_mtx", NULL, MTX_DEF);1154TASK_INIT(&ng_btsocket_sco_rt_task, 0,1155ng_btsocket_sco_rtclean, NULL);1156} /* ng_btsocket_sco_init */1157SYSINIT(ng_btsocket_sco_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD,1158ng_btsocket_sco_init, NULL);11591160/*1161* Abort connection on socket1162*/11631164void1165ng_btsocket_sco_abort(struct socket *so)1166{1167so->so_error = ECONNABORTED;11681169(void) ng_btsocket_sco_disconnect(so);1170} /* ng_btsocket_sco_abort */11711172void1173ng_btsocket_sco_close(struct socket *so)1174{1175(void) ng_btsocket_sco_disconnect(so);1176} /* ng_btsocket_sco_close */11771178/*1179* Create and attach new socket1180*/11811182int1183ng_btsocket_sco_attach(struct socket *so, int proto, struct thread *td)1184{1185ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so);1186int error;11871188/* Check socket and protocol */1189if (ng_btsocket_sco_node == NULL)1190return (EPROTONOSUPPORT);1191if (so->so_type != SOCK_SEQPACKET)1192return (ESOCKTNOSUPPORT);11931194#if 0 /* XXX sonewconn() calls pr_attach() with proto == 0 */1195if (proto != 0)1196if (proto != BLUETOOTH_PROTO_SCO)1197return (EPROTONOSUPPORT);1198#endif /* XXX */11991200if (pcb != NULL)1201return (EISCONN);12021203/* Reserve send and receive space if it is not reserved yet */1204if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) {1205error = soreserve(so, NG_BTSOCKET_SCO_SENDSPACE,1206NG_BTSOCKET_SCO_RECVSPACE);1207if (error != 0)1208return (error);1209}12101211/* Allocate the PCB */1212pcb = malloc(sizeof(*pcb),1213M_NETGRAPH_BTSOCKET_SCO, M_NOWAIT | M_ZERO);1214if (pcb == NULL)1215return (ENOMEM);12161217/* Link the PCB and the socket */1218so->so_pcb = (caddr_t) pcb;1219pcb->so = so;1220pcb->state = NG_BTSOCKET_SCO_CLOSED;12211222callout_init(&pcb->timo, 1);12231224/*1225* Mark PCB mutex as DUPOK to prevent "duplicated lock of1226* the same type" message. When accepting new SCO connection1227* ng_btsocket_sco_process_lp_con_ind() holds both PCB mutexes1228* for "old" (accepting) PCB and "new" (created) PCB.1229*/12301231mtx_init(&pcb->pcb_mtx, "btsocks_sco_pcb_mtx", NULL,1232MTX_DEF|MTX_DUPOK);12331234/*1235* Add the PCB to the list1236*1237* XXX FIXME VERY IMPORTANT!1238*1239* This is totally FUBAR. We could get here in two cases:1240*1241* 1) When user calls socket()1242* 2) When we need to accept new incoming connection and call1243* sonewconn()1244*1245* In the first case we must acquire ng_btsocket_sco_sockets_mtx.1246* In the second case we hold ng_btsocket_sco_sockets_mtx already.1247* So we now need to distinguish between these cases. From reading1248* /sys/kern/uipc_socket2.c we can find out that sonewconn() calls1249* pr_attach() with proto == 0 and td == NULL. For now use this fact1250* to figure out if we were called from socket() or from sonewconn().1251*/12521253if (td != NULL)1254mtx_lock(&ng_btsocket_sco_sockets_mtx);1255else1256mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED);12571258LIST_INSERT_HEAD(&ng_btsocket_sco_sockets, pcb, next);12591260if (td != NULL)1261mtx_unlock(&ng_btsocket_sco_sockets_mtx);12621263return (0);1264} /* ng_btsocket_sco_attach */12651266/*1267* Bind socket1268*/12691270int1271ng_btsocket_sco_bind(struct socket *so, struct sockaddr *nam,1272struct thread *td)1273{1274ng_btsocket_sco_pcb_t *pcb = NULL;1275struct sockaddr_sco *sa = (struct sockaddr_sco *) nam;12761277if (ng_btsocket_sco_node == NULL)1278return (EINVAL);12791280/* Verify address */1281if (sa == NULL)1282return (EINVAL);1283if (sa->sco_family != AF_BLUETOOTH)1284return (EAFNOSUPPORT);1285if (sa->sco_len != sizeof(*sa))1286return (EINVAL);12871288mtx_lock(&ng_btsocket_sco_sockets_mtx);12891290/*1291* Check if other socket has this address already (look for exact1292* match in bdaddr) and assign socket address if it's available.1293*/12941295if (bcmp(&sa->sco_bdaddr, NG_HCI_BDADDR_ANY, sizeof(sa->sco_bdaddr)) != 0) {1296LIST_FOREACH(pcb, &ng_btsocket_sco_sockets, next) {1297mtx_lock(&pcb->pcb_mtx);12981299if (bcmp(&pcb->src, &sa->sco_bdaddr, sizeof(bdaddr_t)) == 0) {1300mtx_unlock(&pcb->pcb_mtx);1301mtx_unlock(&ng_btsocket_sco_sockets_mtx);13021303return (EADDRINUSE);1304}13051306mtx_unlock(&pcb->pcb_mtx);1307}1308}13091310pcb = so2sco_pcb(so);1311if (pcb == NULL) {1312mtx_unlock(&ng_btsocket_sco_sockets_mtx);1313return (EINVAL);1314}13151316mtx_lock(&pcb->pcb_mtx);1317bcopy(&sa->sco_bdaddr, &pcb->src, sizeof(pcb->src));1318mtx_unlock(&pcb->pcb_mtx);13191320mtx_unlock(&ng_btsocket_sco_sockets_mtx);13211322return (0);1323} /* ng_btsocket_sco_bind */13241325/*1326* Connect socket1327*/13281329int1330ng_btsocket_sco_connect(struct socket *so, struct sockaddr *nam,1331struct thread *td)1332{1333ng_btsocket_sco_pcb_t *pcb = so2sco_pcb(so);1334struct sockaddr_sco *sa = (struct sockaddr_sco *) nam;1335ng_btsocket_sco_rtentry_t *rt = NULL;1336int have_src, error = 0;13371338/* Check socket */1339if (pcb == NULL)1340return (EINVAL);1341if (ng_btsocket_sco_node == NULL)1342return (EINVAL);13431344/* Verify address */1345if (sa == NULL)1346return (EINVAL);1347if (sa->sco_family != AF_BLUETOOTH)1348return (EAFNOSUPPORT);1349if (sa->sco_len != sizeof(*sa))1350return (EINVAL);1351if (bcmp(&sa->sco_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)1352return (EDESTADDRREQ);13531354/*1355* Routing. Socket should be bound to some source address. The source1356* address can be ANY. Destination address must be set and it must not1357* be ANY. If source address is ANY then find first rtentry that has1358* src != dst.1359*/13601361mtx_lock(&ng_btsocket_sco_rt_mtx);1362mtx_lock(&pcb->pcb_mtx);13631364if (pcb->state == NG_BTSOCKET_SCO_CONNECTING) {1365mtx_unlock(&pcb->pcb_mtx);1366mtx_unlock(&ng_btsocket_sco_rt_mtx);13671368return (EINPROGRESS);1369}13701371if (bcmp(&sa->sco_bdaddr, &pcb->src, sizeof(pcb->src)) == 0) {1372mtx_unlock(&pcb->pcb_mtx);1373mtx_unlock(&ng_btsocket_sco_rt_mtx);13741375return (EINVAL);1376}13771378/* Send destination address and PSM */1379bcopy(&sa->sco_bdaddr, &pcb->dst, sizeof(pcb->dst));13801381pcb->rt = NULL;1382have_src = bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src));13831384LIST_FOREACH(rt, &ng_btsocket_sco_rt, next) {1385if (rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))1386continue;13871388/* Match src and dst */1389if (have_src) {1390if (bcmp(&pcb->src, &rt->src, sizeof(rt->src)) == 0)1391break;1392} else {1393if (bcmp(&pcb->dst, &rt->src, sizeof(rt->src)) != 0)1394break;1395}1396}13971398if (rt != NULL) {1399pcb->rt = rt;14001401if (!have_src)1402bcopy(&rt->src, &pcb->src, sizeof(pcb->src));1403} else1404error = EHOSTUNREACH;14051406/*1407* Send LP_Connect request1408*/14091410if (error == 0) {1411error = ng_btsocket_sco_send_lp_con_req(pcb);1412if (error == 0) {1413pcb->flags |= NG_BTSOCKET_SCO_CLIENT;1414pcb->state = NG_BTSOCKET_SCO_CONNECTING;1415soisconnecting(pcb->so);14161417ng_btsocket_sco_timeout(pcb);1418}1419}14201421mtx_unlock(&pcb->pcb_mtx);1422mtx_unlock(&ng_btsocket_sco_rt_mtx);14231424return (error);1425} /* ng_btsocket_sco_connect */14261427/*1428* Process ioctl's calls on socket1429*/14301431int1432ng_btsocket_sco_control(struct socket *so, u_long cmd, void *data,1433struct ifnet *ifp, struct thread *td)1434{1435return (EINVAL);1436} /* ng_btsocket_sco_control */14371438/*1439* Process getsockopt/setsockopt system calls1440*/14411442int1443ng_btsocket_sco_ctloutput(struct socket *so, struct sockopt *sopt)1444{1445ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so);1446int error, tmp;14471448if (ng_btsocket_sco_node == NULL)1449return (EINVAL);1450if (pcb == NULL)1451return (EINVAL);14521453if (sopt->sopt_level != SOL_SCO)1454return (0);14551456mtx_lock(&pcb->pcb_mtx);14571458switch (sopt->sopt_dir) {1459case SOPT_GET:1460if (pcb->state != NG_BTSOCKET_SCO_OPEN) {1461error = ENOTCONN;1462break;1463}14641465switch (sopt->sopt_name) {1466case SO_SCO_MTU:1467tmp = pcb->rt->pkt_size;1468error = sooptcopyout(sopt, &tmp, sizeof(tmp));1469break;14701471case SO_SCO_CONNINFO:1472tmp = pcb->con_handle;1473error = sooptcopyout(sopt, &tmp, sizeof(tmp));1474break;14751476default:1477error = EINVAL;1478break;1479}1480break;14811482case SOPT_SET:1483error = ENOPROTOOPT;1484break;14851486default:1487error = EINVAL;1488break;1489}14901491mtx_unlock(&pcb->pcb_mtx);14921493return (error);1494} /* ng_btsocket_sco_ctloutput */14951496/*1497* Detach and destroy socket1498*/14991500void1501ng_btsocket_sco_detach(struct socket *so)1502{1503ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so);15041505KASSERT(pcb != NULL, ("ng_btsocket_sco_detach: pcb == NULL"));15061507if (ng_btsocket_sco_node == NULL)1508return;15091510mtx_lock(&ng_btsocket_sco_sockets_mtx);1511mtx_lock(&pcb->pcb_mtx);15121513if (pcb->flags & NG_BTSOCKET_SCO_TIMO)1514ng_btsocket_sco_untimeout(pcb);15151516if (pcb->state == NG_BTSOCKET_SCO_OPEN)1517ng_btsocket_sco_send_lp_discon_req(pcb);15181519pcb->state = NG_BTSOCKET_SCO_CLOSED;15201521LIST_REMOVE(pcb, next);15221523mtx_unlock(&pcb->pcb_mtx);1524mtx_unlock(&ng_btsocket_sco_sockets_mtx);15251526mtx_destroy(&pcb->pcb_mtx);1527bzero(pcb, sizeof(*pcb));1528free(pcb, M_NETGRAPH_BTSOCKET_SCO);15291530soisdisconnected(so);1531so->so_pcb = NULL;1532} /* ng_btsocket_sco_detach */15331534/*1535* Disconnect socket1536*/15371538int1539ng_btsocket_sco_disconnect(struct socket *so)1540{1541ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so);15421543if (pcb == NULL)1544return (EINVAL);1545if (ng_btsocket_sco_node == NULL)1546return (EINVAL);15471548mtx_lock(&pcb->pcb_mtx);15491550if (pcb->state == NG_BTSOCKET_SCO_DISCONNECTING) {1551mtx_unlock(&pcb->pcb_mtx);15521553return (EINPROGRESS);1554}15551556if (pcb->flags & NG_BTSOCKET_SCO_TIMO)1557ng_btsocket_sco_untimeout(pcb);15581559if (pcb->state == NG_BTSOCKET_SCO_OPEN) {1560ng_btsocket_sco_send_lp_discon_req(pcb);15611562pcb->state = NG_BTSOCKET_SCO_DISCONNECTING;1563soisdisconnecting(so);15641565ng_btsocket_sco_timeout(pcb);1566} else {1567pcb->state = NG_BTSOCKET_SCO_CLOSED;1568soisdisconnected(so);1569}15701571mtx_unlock(&pcb->pcb_mtx);15721573return (0);1574} /* ng_btsocket_sco_disconnect */15751576/*1577* Listen on socket1578*/15791580int1581ng_btsocket_sco_listen(struct socket *so, int backlog, struct thread *td)1582{1583ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so);1584int error;15851586if (pcb == NULL)1587return (EINVAL);1588if (ng_btsocket_sco_node == NULL)1589return (EINVAL);15901591SOCK_LOCK(so);1592mtx_lock(&pcb->pcb_mtx);15931594error = solisten_proto_check(so);1595if (error != 0)1596goto out;1597#if 01598if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0) {1599error = EDESTADDRREQ;1600goto out;1601}1602#endif1603solisten_proto(so, backlog);1604out:1605mtx_unlock(&pcb->pcb_mtx);1606SOCK_UNLOCK(so);16071608return (error);1609} /* ng_btsocket_listen */16101611/*1612* Return peer address for getpeername(2) or for accept(2). For the latter1613* case no extra work to do here, socket must be connected and ready.1614*/1615int1616ng_btsocket_sco_peeraddr(struct socket *so, struct sockaddr *sa)1617{1618ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so);1619struct sockaddr_sco *sco = (struct sockaddr_sco *)sa;16201621if (pcb == NULL)1622return (EINVAL);1623if (ng_btsocket_sco_node == NULL)1624return (EINVAL);16251626*sco = (struct sockaddr_sco ){1627.sco_len = sizeof(struct sockaddr_sco),1628.sco_family = AF_BLUETOOTH,1629};1630mtx_lock(&pcb->pcb_mtx);1631bcopy(&pcb->dst, &sco->sco_bdaddr, sizeof(sco->sco_bdaddr));1632mtx_unlock(&pcb->pcb_mtx);16331634return (0);1635}16361637/*1638* Send data to socket1639*/16401641int1642ng_btsocket_sco_send(struct socket *so, int flags, struct mbuf *m,1643struct sockaddr *nam, struct mbuf *control, struct thread *td)1644{1645ng_btsocket_sco_pcb_t *pcb = so2sco_pcb(so);1646int error = 0;16471648if (ng_btsocket_sco_node == NULL) {1649error = ENETDOWN;1650goto drop;1651}16521653/* Check socket and input */1654if (pcb == NULL || m == NULL || control != NULL) {1655error = EINVAL;1656goto drop;1657}16581659mtx_lock(&pcb->pcb_mtx);16601661/* Make sure socket is connected */1662if (pcb->state != NG_BTSOCKET_SCO_OPEN) {1663mtx_unlock(&pcb->pcb_mtx);1664error = ENOTCONN;1665goto drop;1666}16671668/* Check route */1669if (pcb->rt == NULL ||1670pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) {1671mtx_unlock(&pcb->pcb_mtx);1672error = ENETDOWN;1673goto drop;1674}16751676/* Check packet size */1677if (m->m_pkthdr.len > pcb->rt->pkt_size) {1678NG_BTSOCKET_SCO_ERR(1679"%s: Packet too big, len=%d, pkt_size=%d\n",1680__func__, m->m_pkthdr.len, pcb->rt->pkt_size);16811682mtx_unlock(&pcb->pcb_mtx);1683error = EMSGSIZE;1684goto drop;1685}16861687/*1688* First put packet on socket send queue. Then check if we have1689* pending timeout. If we do not have timeout then we must send1690* packet and schedule timeout. Otherwise do nothing and wait for1691* NGM_HCI_SYNC_CON_QUEUE message.1692*/16931694sbappendrecord(&pcb->so->so_snd, m);1695m = NULL;16961697if (!(pcb->flags & NG_BTSOCKET_SCO_TIMO)) {1698error = ng_btsocket_sco_send2(pcb);1699if (error == 0)1700ng_btsocket_sco_timeout(pcb);1701else1702sbdroprecord(&pcb->so->so_snd); /* XXX */1703}17041705mtx_unlock(&pcb->pcb_mtx);1706drop:1707NG_FREE_M(m); /* checks for != NULL */1708NG_FREE_M(control);17091710return (error);1711} /* ng_btsocket_sco_send */17121713/*1714* Send first packet in the socket queue to the SCO layer1715*/17161717static int1718ng_btsocket_sco_send2(ng_btsocket_sco_pcb_p pcb)1719{1720struct mbuf *m = NULL;1721ng_hci_scodata_pkt_t *hdr = NULL;1722int error = 0;17231724mtx_assert(&pcb->pcb_mtx, MA_OWNED);17251726while (pcb->rt->pending < pcb->rt->num_pkts &&1727sbavail(&pcb->so->so_snd) > 0) {1728/* Get a copy of the first packet on send queue */1729m = m_dup(pcb->so->so_snd.sb_mb, M_NOWAIT);1730if (m == NULL) {1731error = ENOBUFS;1732break;1733}17341735/* Create SCO packet header */1736M_PREPEND(m, sizeof(*hdr), M_NOWAIT);1737if (m != NULL)1738if (m->m_len < sizeof(*hdr))1739m = m_pullup(m, sizeof(*hdr));17401741if (m == NULL) {1742error = ENOBUFS;1743break;1744}17451746/* Fill in the header */1747hdr = mtod(m, ng_hci_scodata_pkt_t *);1748hdr->type = NG_HCI_SCO_DATA_PKT;1749hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE(pcb->con_handle, 0, 0));1750hdr->length = m->m_pkthdr.len - sizeof(*hdr);17511752/* Send packet */1753NG_SEND_DATA_ONLY(error, pcb->rt->hook, m);1754if (error != 0)1755break;17561757pcb->rt->pending ++;1758}17591760return ((pcb->rt->pending > 0)? 0 : error);1761} /* ng_btsocket_sco_send2 */17621763/*1764* Get socket address1765*/17661767int1768ng_btsocket_sco_sockaddr(struct socket *so, struct sockaddr *sa)1769{1770ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so);1771struct sockaddr_sco *sco = (struct sockaddr_sco *)sa;17721773if (pcb == NULL)1774return (EINVAL);1775if (ng_btsocket_sco_node == NULL)1776return (EINVAL);17771778*sco = (struct sockaddr_sco ){1779.sco_len = sizeof(struct sockaddr_sco),1780.sco_family = AF_BLUETOOTH,1781};1782mtx_lock(&pcb->pcb_mtx);1783bcopy(&pcb->src, &sco->sco_bdaddr, sizeof(sco->sco_bdaddr));1784mtx_unlock(&pcb->pcb_mtx);17851786return (0);1787}17881789/*****************************************************************************1790*****************************************************************************1791** Misc. functions1792*****************************************************************************1793*****************************************************************************/17941795/*1796* Look for the socket that listens on given bdaddr.1797* Returns exact or close match (if any).1798* Caller must hold ng_btsocket_sco_sockets_mtx.1799* Returns with locked pcb.1800*/18011802static ng_btsocket_sco_pcb_p1803ng_btsocket_sco_pcb_by_addr(bdaddr_p bdaddr)1804{1805ng_btsocket_sco_pcb_p p = NULL, p1 = NULL;18061807mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED);18081809LIST_FOREACH(p, &ng_btsocket_sco_sockets, next) {1810mtx_lock(&p->pcb_mtx);18111812if (p->so == NULL || !SOLISTENING(p->so)) {1813mtx_unlock(&p->pcb_mtx);1814continue;1815}18161817if (bcmp(&p->src, bdaddr, sizeof(p->src)) == 0)1818return (p); /* return with locked pcb */18191820if (bcmp(&p->src, NG_HCI_BDADDR_ANY, sizeof(p->src)) == 0)1821p1 = p;18221823mtx_unlock(&p->pcb_mtx);1824}18251826if (p1 != NULL)1827mtx_lock(&p1->pcb_mtx);18281829return (p1);1830} /* ng_btsocket_sco_pcb_by_addr */18311832/*1833* Look for the socket that assigned to given source address and handle.1834* Caller must hold ng_btsocket_sco_sockets_mtx.1835* Returns with locked pcb.1836*/18371838static ng_btsocket_sco_pcb_p1839ng_btsocket_sco_pcb_by_handle(bdaddr_p src, int con_handle)1840{1841ng_btsocket_sco_pcb_p p = NULL;18421843mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED);18441845LIST_FOREACH(p, &ng_btsocket_sco_sockets, next) {1846mtx_lock(&p->pcb_mtx);18471848if (p->con_handle == con_handle &&1849bcmp(src, &p->src, sizeof(p->src)) == 0)1850return (p); /* return with locked pcb */18511852mtx_unlock(&p->pcb_mtx);1853}18541855return (NULL);1856} /* ng_btsocket_sco_pcb_by_handle */18571858/*1859* Look for the socket in CONNECTING state with given source and destination1860* addresses. Caller must hold ng_btsocket_sco_sockets_mtx.1861* Returns with locked pcb.1862*/18631864static ng_btsocket_sco_pcb_p1865ng_btsocket_sco_pcb_by_addrs(bdaddr_p src, bdaddr_p dst)1866{1867ng_btsocket_sco_pcb_p p = NULL;18681869mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED);18701871LIST_FOREACH(p, &ng_btsocket_sco_sockets, next) {1872mtx_lock(&p->pcb_mtx);18731874if (p->state == NG_BTSOCKET_SCO_CONNECTING &&1875bcmp(src, &p->src, sizeof(p->src)) == 0 &&1876bcmp(dst, &p->dst, sizeof(p->dst)) == 0)1877return (p); /* return with locked pcb */18781879mtx_unlock(&p->pcb_mtx);1880}18811882return (NULL);1883} /* ng_btsocket_sco_pcb_by_addrs */18841885/*1886* Set timeout on socket1887*/18881889static void1890ng_btsocket_sco_timeout(ng_btsocket_sco_pcb_p pcb)1891{1892mtx_assert(&pcb->pcb_mtx, MA_OWNED);18931894if (!(pcb->flags & NG_BTSOCKET_SCO_TIMO)) {1895pcb->flags |= NG_BTSOCKET_SCO_TIMO;1896callout_reset(&pcb->timo, bluetooth_sco_rtx_timeout(),1897ng_btsocket_sco_process_timeout, pcb);1898} else1899KASSERT(0,1900("%s: Duplicated socket timeout?!\n", __func__));1901} /* ng_btsocket_sco_timeout */19021903/*1904* Unset timeout on socket1905*/19061907static void1908ng_btsocket_sco_untimeout(ng_btsocket_sco_pcb_p pcb)1909{1910mtx_assert(&pcb->pcb_mtx, MA_OWNED);19111912if (pcb->flags & NG_BTSOCKET_SCO_TIMO) {1913callout_stop(&pcb->timo);1914pcb->flags &= ~NG_BTSOCKET_SCO_TIMO;1915} else1916KASSERT(0,1917("%s: No socket timeout?!\n", __func__));1918} /* ng_btsocket_sco_untimeout */19191920/*1921* Process timeout on socket1922*/19231924static void1925ng_btsocket_sco_process_timeout(void *xpcb)1926{1927ng_btsocket_sco_pcb_p pcb = (ng_btsocket_sco_pcb_p) xpcb;19281929mtx_lock(&pcb->pcb_mtx);19301931pcb->flags &= ~NG_BTSOCKET_SCO_TIMO;1932pcb->so->so_error = ETIMEDOUT;19331934switch (pcb->state) {1935case NG_BTSOCKET_SCO_CONNECTING:1936/* Connect timeout - close the socket */1937pcb->state = NG_BTSOCKET_SCO_CLOSED;1938soisdisconnected(pcb->so);1939break;19401941case NG_BTSOCKET_SCO_OPEN:1942/* Send timeout - did not get NGM_HCI_SYNC_CON_QUEUE */1943sbdroprecord(&pcb->so->so_snd);1944sowwakeup(pcb->so);1945/* XXX FIXME what to do with pcb->rt->pending??? */1946break;19471948case NG_BTSOCKET_SCO_DISCONNECTING:1949/* Disconnect timeout - disconnect the socket anyway */1950pcb->state = NG_BTSOCKET_SCO_CLOSED;1951soisdisconnected(pcb->so);1952break;19531954default:1955NG_BTSOCKET_SCO_ERR(1956"%s: Invalid socket state=%d\n", __func__, pcb->state);1957break;1958}19591960mtx_unlock(&pcb->pcb_mtx);1961} /* ng_btsocket_sco_process_timeout */196219631964