Path: blob/main/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c
34677 views
/*1* ng_btsocket_l2cap.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_l2cap.c,v 1.16 2003/09/14 23:29:06 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_l2cap.h>6364/* MALLOC define */65#ifdef NG_SEPARATE_MALLOC66static MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_L2CAP, "netgraph_btsocks_l2cap",67"Netgraph Bluetooth L2CAP sockets");68#else69#define M_NETGRAPH_BTSOCKET_L2CAP M_NETGRAPH70#endif /* NG_SEPARATE_MALLOC */7172/* Netgraph node methods */73static ng_constructor_t ng_btsocket_l2cap_node_constructor;74static ng_rcvmsg_t ng_btsocket_l2cap_node_rcvmsg;75static ng_shutdown_t ng_btsocket_l2cap_node_shutdown;76static ng_newhook_t ng_btsocket_l2cap_node_newhook;77static ng_connect_t ng_btsocket_l2cap_node_connect;78static ng_rcvdata_t ng_btsocket_l2cap_node_rcvdata;79static ng_disconnect_t ng_btsocket_l2cap_node_disconnect;8081static void ng_btsocket_l2cap_input (void *, int);82static void ng_btsocket_l2cap_rtclean (void *, int);8384/* Netgraph type descriptor */85static struct ng_type typestruct = {86.version = NG_ABI_VERSION,87.name = NG_BTSOCKET_L2CAP_NODE_TYPE,88.constructor = ng_btsocket_l2cap_node_constructor,89.rcvmsg = ng_btsocket_l2cap_node_rcvmsg,90.shutdown = ng_btsocket_l2cap_node_shutdown,91.newhook = ng_btsocket_l2cap_node_newhook,92.connect = ng_btsocket_l2cap_node_connect,93.rcvdata = ng_btsocket_l2cap_node_rcvdata,94.disconnect = ng_btsocket_l2cap_node_disconnect,95};9697/* Globals */98extern int ifqmaxlen;99static u_int32_t ng_btsocket_l2cap_debug_level;100static node_p ng_btsocket_l2cap_node;101static struct ng_bt_itemq ng_btsocket_l2cap_queue;102static struct mtx ng_btsocket_l2cap_queue_mtx;103static struct task ng_btsocket_l2cap_queue_task;104static LIST_HEAD(, ng_btsocket_l2cap_pcb) ng_btsocket_l2cap_sockets;105static struct mtx ng_btsocket_l2cap_sockets_mtx;106static LIST_HEAD(, ng_btsocket_l2cap_rtentry) ng_btsocket_l2cap_rt;107static struct mtx ng_btsocket_l2cap_rt_mtx;108static struct task ng_btsocket_l2cap_rt_task;109static struct timeval ng_btsocket_l2cap_lasttime;110static int ng_btsocket_l2cap_curpps;111112/* Sysctl tree */113SYSCTL_DECL(_net_bluetooth_l2cap_sockets);114static SYSCTL_NODE(_net_bluetooth_l2cap_sockets, OID_AUTO, seq,115CTLFLAG_RW | CTLFLAG_MPSAFE, 0,116"Bluetooth SEQPACKET L2CAP sockets family");117SYSCTL_UINT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, debug_level,118CTLFLAG_RW,119&ng_btsocket_l2cap_debug_level, NG_BTSOCKET_WARN_LEVEL,120"Bluetooth SEQPACKET L2CAP sockets debug level");121SYSCTL_UINT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, queue_len,122CTLFLAG_RD,123&ng_btsocket_l2cap_queue.len, 0,124"Bluetooth SEQPACKET L2CAP sockets input queue length");125SYSCTL_UINT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, queue_maxlen,126CTLFLAG_RD,127&ng_btsocket_l2cap_queue.maxlen, 0,128"Bluetooth SEQPACKET L2CAP sockets input queue max. length");129SYSCTL_UINT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, queue_drops,130CTLFLAG_RD,131&ng_btsocket_l2cap_queue.drops, 0,132"Bluetooth SEQPACKET L2CAP sockets input queue drops");133134/* Debug */135#define NG_BTSOCKET_L2CAP_INFO \136if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_INFO_LEVEL && \137ppsratecheck(&ng_btsocket_l2cap_lasttime, &ng_btsocket_l2cap_curpps, 1)) \138printf139140#define NG_BTSOCKET_L2CAP_WARN \141if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_WARN_LEVEL && \142ppsratecheck(&ng_btsocket_l2cap_lasttime, &ng_btsocket_l2cap_curpps, 1)) \143printf144145#define NG_BTSOCKET_L2CAP_ERR \146if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_ERR_LEVEL && \147ppsratecheck(&ng_btsocket_l2cap_lasttime, &ng_btsocket_l2cap_curpps, 1)) \148printf149150#define NG_BTSOCKET_L2CAP_ALERT \151if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_ALERT_LEVEL && \152ppsratecheck(&ng_btsocket_l2cap_lasttime, &ng_btsocket_l2cap_curpps, 1)) \153printf154155/*156* Netgraph message processing routines157*/158159static int ng_btsocket_l2cap_process_l2ca_con_req_rsp160(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);161static int ng_btsocket_l2cap_process_l2ca_con_rsp_rsp162(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);163static int ng_btsocket_l2cap_process_l2ca_con_ind164(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);165166static int ng_btsocket_l2cap_process_l2ca_cfg_req_rsp167(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);168static int ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp169(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);170static int ng_btsocket_l2cap_process_l2ca_cfg_ind171(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);172173static int ng_btsocket_l2cap_process_l2ca_discon_rsp174(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);175static int ng_btsocket_l2cap_process_l2ca_discon_ind176(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);177178static int ng_btsocket_l2cap_process_l2ca_write_rsp179(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);180181/*182* Send L2CA_xxx messages to the lower layer183*/184185static int ng_btsocket_l2cap_send_l2ca_con_req186(ng_btsocket_l2cap_pcb_p);187static int ng_btsocket_l2cap_send_l2ca_con_rsp_req188(u_int32_t, ng_btsocket_l2cap_rtentry_p, bdaddr_p, int, int, int, int);189static int ng_btsocket_l2cap_send_l2ca_cfg_req190(ng_btsocket_l2cap_pcb_p);191static int ng_btsocket_l2cap_send_l2ca_cfg_rsp192(ng_btsocket_l2cap_pcb_p);193static int ng_btsocket_l2cap_send_l2ca_discon_req194(u_int32_t, ng_btsocket_l2cap_pcb_p);195196static int ng_btsocket_l2cap_send2197(ng_btsocket_l2cap_pcb_p);198199/*200* Timeout processing routines201*/202203static void ng_btsocket_l2cap_timeout (ng_btsocket_l2cap_pcb_p);204static void ng_btsocket_l2cap_untimeout (ng_btsocket_l2cap_pcb_p);205static void ng_btsocket_l2cap_process_timeout (void *);206207/*208* Other stuff209*/210211static ng_btsocket_l2cap_pcb_p ng_btsocket_l2cap_pcb_by_addr(bdaddr_p, int);212static ng_btsocket_l2cap_pcb_p ng_btsocket_l2cap_pcb_by_token(u_int32_t);213static ng_btsocket_l2cap_pcb_p ng_btsocket_l2cap_pcb_by_cid (bdaddr_p, int,int);214static int ng_btsocket_l2cap_result2errno(int);215216static int ng_btsock_l2cap_addrtype_to_linktype(int addrtype);217218#define ng_btsocket_l2cap_wakeup_input_task() \219taskqueue_enqueue(taskqueue_swi_giant, &ng_btsocket_l2cap_queue_task)220221#define ng_btsocket_l2cap_wakeup_route_task() \222taskqueue_enqueue(taskqueue_swi_giant, &ng_btsocket_l2cap_rt_task)223224int ng_btsock_l2cap_addrtype_to_linktype(int addrtype)225{226switch(addrtype){227case BDADDR_LE_PUBLIC:228return NG_HCI_LINK_LE_PUBLIC;229case BDADDR_LE_RANDOM:230return NG_HCI_LINK_LE_RANDOM;231default:232return NG_HCI_LINK_ACL;233}234}235236/*****************************************************************************237*****************************************************************************238** Netgraph node interface239*****************************************************************************240*****************************************************************************/241242/*243* Netgraph node constructor. Do not allow to create node of this type.244*/245246static int247ng_btsocket_l2cap_node_constructor(node_p node)248{249return (EINVAL);250} /* ng_btsocket_l2cap_node_constructor */251252/*253* Do local shutdown processing. Let old node go and create new fresh one.254*/255256static int257ng_btsocket_l2cap_node_shutdown(node_p node)258{259int error = 0;260261NG_NODE_UNREF(node);262263/* Create new node */264error = ng_make_node_common(&typestruct, &ng_btsocket_l2cap_node);265if (error != 0) {266NG_BTSOCKET_L2CAP_ALERT(267"%s: Could not create Netgraph node, error=%d\n", __func__, error);268269ng_btsocket_l2cap_node = NULL;270271return (error);272}273274error = ng_name_node(ng_btsocket_l2cap_node,275NG_BTSOCKET_L2CAP_NODE_TYPE);276if (error != 0) {277NG_BTSOCKET_L2CAP_ALERT(278"%s: Could not name Netgraph node, error=%d\n", __func__, error);279280NG_NODE_UNREF(ng_btsocket_l2cap_node);281ng_btsocket_l2cap_node = NULL;282283return (error);284}285286return (0);287} /* ng_btsocket_l2cap_node_shutdown */288289/*290* We allow any hook to be connected to the node.291*/292293static int294ng_btsocket_l2cap_node_newhook(node_p node, hook_p hook, char const *name)295{296return (0);297} /* ng_btsocket_l2cap_node_newhook */298299/*300* Just say "YEP, that's OK by me!"301*/302303static int304ng_btsocket_l2cap_node_connect(hook_p hook)305{306NG_HOOK_SET_PRIVATE(hook, NULL);307NG_HOOK_REF(hook); /* Keep extra reference to the hook */308309#if 0310NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));311NG_HOOK_FORCE_QUEUE(hook);312#endif313314return (0);315} /* ng_btsocket_l2cap_node_connect */316317/*318* Hook disconnection. Schedule route cleanup task319*/320321static int322ng_btsocket_l2cap_node_disconnect(hook_p hook)323{324/*325* If hook has private information than we must have this hook in326* the routing table and must schedule cleaning for the routing table.327* Otherwise hook was connected but we never got "hook_info" message,328* so we have never added this hook to the routing table and it save329* to just delete it.330*/331332if (NG_HOOK_PRIVATE(hook) != NULL)333return (ng_btsocket_l2cap_wakeup_route_task());334335NG_HOOK_UNREF(hook); /* Remove extra reference */336337return (0);338} /* ng_btsocket_l2cap_node_disconnect */339340/*341* Process incoming messages342*/343344static int345ng_btsocket_l2cap_node_rcvmsg(node_p node, item_p item, hook_p hook)346{347struct ng_mesg *msg = NGI_MSG(item); /* item still has message */348int error = 0;349350if (msg != NULL && msg->header.typecookie == NGM_L2CAP_COOKIE) {351mtx_lock(&ng_btsocket_l2cap_queue_mtx);352if (NG_BT_ITEMQ_FULL(&ng_btsocket_l2cap_queue)) {353NG_BTSOCKET_L2CAP_ERR(354"%s: Input queue is full (msg)\n", __func__);355356NG_BT_ITEMQ_DROP(&ng_btsocket_l2cap_queue);357NG_FREE_ITEM(item);358error = ENOBUFS;359} else {360if (hook != NULL) {361NG_HOOK_REF(hook);362NGI_SET_HOOK(item, hook);363}364365NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_l2cap_queue, item);366error = ng_btsocket_l2cap_wakeup_input_task();367}368mtx_unlock(&ng_btsocket_l2cap_queue_mtx);369} else {370NG_FREE_ITEM(item);371error = EINVAL;372}373374return (error);375} /* ng_btsocket_l2cap_node_rcvmsg */376377/*378* Receive data on a hook379*/380381static int382ng_btsocket_l2cap_node_rcvdata(hook_p hook, item_p item)383{384int error = 0;385386mtx_lock(&ng_btsocket_l2cap_queue_mtx);387if (NG_BT_ITEMQ_FULL(&ng_btsocket_l2cap_queue)) {388NG_BTSOCKET_L2CAP_ERR(389"%s: Input queue is full (data)\n", __func__);390391NG_BT_ITEMQ_DROP(&ng_btsocket_l2cap_queue);392NG_FREE_ITEM(item);393error = ENOBUFS;394} else {395NG_HOOK_REF(hook);396NGI_SET_HOOK(item, hook);397398NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_l2cap_queue, item);399error = ng_btsocket_l2cap_wakeup_input_task();400}401mtx_unlock(&ng_btsocket_l2cap_queue_mtx);402403return (error);404} /* ng_btsocket_l2cap_node_rcvdata */405406/*407* Process L2CA_Connect respose. Socket layer must have initiated connection,408* so we have to have a socket associated with message token.409*/410411static int412ng_btsocket_l2cap_process_l2ca_con_req_rsp(struct ng_mesg *msg,413ng_btsocket_l2cap_rtentry_p rt)414{415ng_l2cap_l2ca_con_op *op = NULL;416ng_btsocket_l2cap_pcb_t *pcb = NULL;417int error = 0;418419if (msg->header.arglen != sizeof(*op))420return (EMSGSIZE);421422op = (ng_l2cap_l2ca_con_op *)(msg->data);423424mtx_lock(&ng_btsocket_l2cap_sockets_mtx);425426/* Look for the socket with the token */427pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);428if (pcb == NULL) {429mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);430return (ENOENT);431}432433mtx_lock(&pcb->pcb_mtx);434435NG_BTSOCKET_L2CAP_INFO(436"%s: Got L2CA_Connect response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \437"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, status=%d, " \438"state=%d\n", __func__, msg->header.token,439pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],440pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],441pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],442pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],443pcb->psm, op->lcid, op->result, op->status,444pcb->state);445446if (pcb->state != NG_BTSOCKET_L2CAP_CONNECTING) {447mtx_unlock(&pcb->pcb_mtx);448mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);449450return (ENOENT);451}452453ng_btsocket_l2cap_untimeout(pcb);454455if (op->result == NG_L2CAP_PENDING) {456ng_btsocket_l2cap_timeout(pcb);457mtx_unlock(&pcb->pcb_mtx);458mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);459460return (0);461}462463if (op->result == NG_L2CAP_SUCCESS){464if((pcb->idtype == NG_L2CAP_L2CA_IDTYPE_ATT)||465(pcb->idtype == NG_L2CAP_L2CA_IDTYPE_SMP)){466pcb->encryption = op->encryption; pcb->cid = op->lcid;467if(pcb->need_encrypt && !(pcb->encryption)){468ng_btsocket_l2cap_timeout(pcb);469pcb->state = NG_BTSOCKET_L2CAP_W4_ENC_CHANGE;470}else{471pcb->state = NG_BTSOCKET_L2CAP_OPEN;472soisconnected(pcb->so);473}474}else{475/*476* Channel is now open, so update local channel ID and477* start configuration process. Source and destination478* addresses as well as route must be already set.479*/480481pcb->cid = op->lcid;482pcb->encryption = op->encryption;483error = ng_btsocket_l2cap_send_l2ca_cfg_req(pcb);484if (error != 0) {485/* Send disconnect request with "zero" token */486ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);487488/* ... and close the socket */489pcb->state = NG_BTSOCKET_L2CAP_CLOSED;490soisdisconnected(pcb->so);491} else {492pcb->cfg_state = NG_BTSOCKET_L2CAP_CFG_IN_SENT;493pcb->state = NG_BTSOCKET_L2CAP_CONFIGURING;494495ng_btsocket_l2cap_timeout(pcb);496}497}498} else {499/*500* We have failed to open connection, so convert result501* code to "errno" code and disconnect the socket. Channel502* already has been closed.503*/504505pcb->so->so_error = ng_btsocket_l2cap_result2errno(op->result);506pcb->state = NG_BTSOCKET_L2CAP_CLOSED;507soisdisconnected(pcb->so);508}509mtx_unlock(&pcb->pcb_mtx);510mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);511512return (error);513} /* ng_btsocket_l2cap_process_l2ca_con_req_rsp */514515/*516* Process L2CA_ConnectRsp response517*/518519static int520ng_btsocket_l2cap_process_l2ca_con_rsp_rsp(struct ng_mesg *msg,521ng_btsocket_l2cap_rtentry_p rt)522{523ng_l2cap_l2ca_con_rsp_op *op = NULL;524ng_btsocket_l2cap_pcb_t *pcb = NULL;525526if (msg->header.arglen != sizeof(*op))527return (EMSGSIZE);528529op = (ng_l2cap_l2ca_con_rsp_op *)(msg->data);530531mtx_lock(&ng_btsocket_l2cap_sockets_mtx);532533/* Look for the socket with the token */534pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);535if (pcb == NULL) {536mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);537return (ENOENT);538}539540mtx_lock(&pcb->pcb_mtx);541542NG_BTSOCKET_L2CAP_INFO(543"%s: Got L2CA_ConnectRsp response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \544"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d\n",545__func__, msg->header.token,546pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],547pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],548pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],549pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],550pcb->psm, pcb->cid, op->result, pcb->state);551552if (pcb->state != NG_BTSOCKET_L2CAP_CONNECTING) {553mtx_unlock(&pcb->pcb_mtx);554mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);555556return (ENOENT);557}558559ng_btsocket_l2cap_untimeout(pcb);560561/* Check the result and disconnect the socket on failure */562if (op->result != NG_L2CAP_SUCCESS) {563/* Close the socket - channel already closed */564pcb->so->so_error = ng_btsocket_l2cap_result2errno(op->result);565pcb->state = NG_BTSOCKET_L2CAP_CLOSED;566soisdisconnected(pcb->so);567} else {568/* Move to CONFIGURING state and wait for CONFIG_IND */569pcb->cfg_state = 0;570pcb->state = NG_BTSOCKET_L2CAP_CONFIGURING;571ng_btsocket_l2cap_timeout(pcb);572}573574mtx_unlock(&pcb->pcb_mtx);575mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);576577return (0);578} /* ng_btsocket_process_l2ca_con_rsp_rsp */579580/*581* Process L2CA_Connect indicator. Find socket that listens on address582* and PSM. Find exact or closest match. Create new socket and initiate583* connection.584*/585586static int587ng_btsocket_l2cap_process_l2ca_con_ind(struct ng_mesg *msg,588ng_btsocket_l2cap_rtentry_p rt)589{590ng_l2cap_l2ca_con_ind_ip *ip = NULL;591ng_btsocket_l2cap_pcb_t *pcb = NULL, *pcb1 = NULL;592int error = 0;593u_int32_t token = 0;594u_int16_t result = 0;595596if (msg->header.arglen != sizeof(*ip))597return (EMSGSIZE);598599ip = (ng_l2cap_l2ca_con_ind_ip *)(msg->data);600601NG_BTSOCKET_L2CAP_INFO(602"%s: Got L2CA_Connect indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \603"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, ident=%d\n",604__func__,605rt->src.b[5], rt->src.b[4], rt->src.b[3],606rt->src.b[2], rt->src.b[1], rt->src.b[0],607ip->bdaddr.b[5], ip->bdaddr.b[4], ip->bdaddr.b[3],608ip->bdaddr.b[2], ip->bdaddr.b[1], ip->bdaddr.b[0],609ip->psm, ip->lcid, ip->ident);610611mtx_lock(&ng_btsocket_l2cap_sockets_mtx);612613pcb = ng_btsocket_l2cap_pcb_by_addr(&rt->src, ip->psm);614if (pcb != NULL) {615struct socket *so1;616617mtx_lock(&pcb->pcb_mtx);618619CURVNET_SET(pcb->so->so_vnet);620so1 = sonewconn(pcb->so, 0);621CURVNET_RESTORE();622if (so1 == NULL) {623result = NG_L2CAP_NO_RESOURCES;624goto respond;625}626627/*628* If we got here than we have created new socket. So complete629* connection. If we we listening on specific address then copy630* source address from listening socket, otherwise copy source631* address from hook's routing information.632*/633634pcb1 = so2l2cap_pcb(so1);635KASSERT((pcb1 != NULL),636("%s: pcb1 == NULL\n", __func__));637638mtx_lock(&pcb1->pcb_mtx);639640if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src)) != 0)641bcopy(&pcb->src, &pcb1->src, sizeof(pcb1->src));642else643bcopy(&rt->src, &pcb1->src, sizeof(pcb1->src));644645pcb1->flags &= ~NG_BTSOCKET_L2CAP_CLIENT;646647bcopy(&ip->bdaddr, &pcb1->dst, sizeof(pcb1->dst));648pcb1->psm = ip->psm;649pcb1->cid = ip->lcid;650pcb1->rt = rt;651652/* Copy socket settings */653pcb1->imtu = pcb->imtu;654bcopy(&pcb->oflow, &pcb1->oflow, sizeof(pcb1->oflow));655pcb1->flush_timo = pcb->flush_timo;656657token = pcb1->token;658} else659/* Nobody listens on requested BDADDR/PSM */660result = NG_L2CAP_PSM_NOT_SUPPORTED;661662respond:663error = ng_btsocket_l2cap_send_l2ca_con_rsp_req(token, rt,664&ip->bdaddr,665ip->ident, ip->lcid,666result,ip->linktype);667if (pcb1 != NULL) {668if (error != 0) {669pcb1->so->so_error = error;670pcb1->state = NG_BTSOCKET_L2CAP_CLOSED;671soisdisconnected(pcb1->so);672} else {673pcb1->state = NG_BTSOCKET_L2CAP_CONNECTING;674soisconnecting(pcb1->so);675676ng_btsocket_l2cap_timeout(pcb1);677}678679mtx_unlock(&pcb1->pcb_mtx);680}681682if (pcb != NULL)683mtx_unlock(&pcb->pcb_mtx);684685mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);686687return (error);688} /* ng_btsocket_l2cap_process_l2ca_con_ind */689/*Encryption Change*/690static int ng_btsocket_l2cap_process_l2ca_enc_change(struct ng_mesg *msg, ng_btsocket_l2cap_rtentry_p rt)691{692ng_l2cap_l2ca_enc_chg_op *op = NULL;693ng_btsocket_l2cap_pcb_t *pcb = NULL;694695if (msg->header.arglen != sizeof(*op))696return (EMSGSIZE);697698op = (ng_l2cap_l2ca_enc_chg_op *)(msg->data);699700mtx_lock(&ng_btsocket_l2cap_sockets_mtx);701702pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, op->lcid,703op->idtype);704if (pcb == NULL) {705mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);706return (ENOENT);707}708709mtx_lock(&pcb->pcb_mtx);710pcb->encryption = op->result;711712if(pcb->need_encrypt){713ng_btsocket_l2cap_untimeout(pcb);714if(pcb->state != NG_BTSOCKET_L2CAP_W4_ENC_CHANGE){715NG_BTSOCKET_L2CAP_WARN("%s: Invalid pcb status %d",716__func__, pcb->state);717}else if(pcb->encryption){718pcb->state = NG_BTSOCKET_L2CAP_OPEN;719soisconnected(pcb->so);720}else{721pcb->so->so_error = EPERM;722ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);723pcb->state = NG_BTSOCKET_L2CAP_CLOSED;724soisdisconnected(pcb->so);725}726}727mtx_unlock(&pcb->pcb_mtx);728mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);729730return 0;731}732/*733* Process L2CA_Config response734*/735736static int737ng_btsocket_l2cap_process_l2ca_cfg_req_rsp(struct ng_mesg *msg,738ng_btsocket_l2cap_rtentry_p rt)739{740ng_l2cap_l2ca_cfg_op *op = NULL;741ng_btsocket_l2cap_pcb_p pcb = NULL;742743if (msg->header.arglen != sizeof(*op))744return (EMSGSIZE);745746op = (ng_l2cap_l2ca_cfg_op *)(msg->data);747748mtx_lock(&ng_btsocket_l2cap_sockets_mtx);749750/*751* Socket must have issued a Configure request, so we must have a752* socket that wants to be configured. Use Netgraph message token753* to find it754*/755756pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);757if (pcb == NULL) {758/*759* XXX FIXME what to do here? We could not find a760* socket with requested token. We even can not send761* Disconnect, because we do not know channel ID762*/763764mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);765return (ENOENT);766}767768mtx_lock(&pcb->pcb_mtx);769770NG_BTSOCKET_L2CAP_INFO(771"%s: Got L2CA_Config response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \772"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d, " \773"cfg_state=%x\n",774__func__, msg->header.token,775pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],776pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],777pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],778pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],779pcb->psm, pcb->cid, op->result, pcb->state, pcb->cfg_state);780781if (pcb->state != NG_BTSOCKET_L2CAP_CONFIGURING) {782mtx_unlock(&pcb->pcb_mtx);783mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);784785return (ENOENT);786}787788if (op->result == NG_L2CAP_SUCCESS) {789/*790* XXX FIXME Actually set flush and link timeout.791* Set QoS here if required. Resolve conflicts (flush_timo).792* Save incoming MTU (peer's outgoing MTU) and outgoing flow793* spec.794*/795796pcb->imtu = op->imtu;797bcopy(&op->oflow, &pcb->oflow, sizeof(pcb->oflow));798pcb->flush_timo = op->flush_timo;799800/*801* We have configured incoming side, so record it and check802* if configuration is complete. If complete then mark socket803* as connected, otherwise wait for the peer.804*/805806pcb->cfg_state &= ~NG_BTSOCKET_L2CAP_CFG_IN_SENT;807pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_IN;808809if (pcb->cfg_state == NG_BTSOCKET_L2CAP_CFG_BOTH) {810/* Configuration complete - mark socket as open */811ng_btsocket_l2cap_untimeout(pcb);812pcb->state = NG_BTSOCKET_L2CAP_OPEN;813soisconnected(pcb->so);814}815} else {816/*817* Something went wrong. Could be unacceptable parameters,818* reject or unknown option. That's too bad, but we will819* not negotiate. Send Disconnect and close the channel.820*/821822ng_btsocket_l2cap_untimeout(pcb);823824switch (op->result) {825case NG_L2CAP_UNACCEPTABLE_PARAMS:826case NG_L2CAP_UNKNOWN_OPTION:827pcb->so->so_error = EINVAL;828break;829830default:831pcb->so->so_error = ECONNRESET;832break;833}834835/* Send disconnect with "zero" token */836ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);837838/* ... and close the socket */839pcb->state = NG_BTSOCKET_L2CAP_CLOSED;840soisdisconnected(pcb->so);841}842843mtx_unlock(&pcb->pcb_mtx);844mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);845846return (0);847} /* ng_btsocket_l2cap_process_l2ca_cfg_req_rsp */848849/*850* Process L2CA_ConfigRsp response851*/852853static int854ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp(struct ng_mesg *msg,855ng_btsocket_l2cap_rtentry_p rt)856{857ng_l2cap_l2ca_cfg_rsp_op *op = NULL;858ng_btsocket_l2cap_pcb_t *pcb = NULL;859int error = 0;860861if (msg->header.arglen != sizeof(*op))862return (EMSGSIZE);863864op = (ng_l2cap_l2ca_cfg_rsp_op *)(msg->data);865866mtx_lock(&ng_btsocket_l2cap_sockets_mtx);867868/* Look for the socket with the token */869pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);870if (pcb == NULL) {871mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);872return (ENOENT);873}874875mtx_lock(&pcb->pcb_mtx);876877NG_BTSOCKET_L2CAP_INFO(878"%s: Got L2CA_ConfigRsp response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \879"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d, " \880"cfg_state=%x\n",881__func__, msg->header.token,882pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],883pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],884pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],885pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],886pcb->psm, pcb->cid, op->result, pcb->state, pcb->cfg_state);887888if (pcb->state != NG_BTSOCKET_L2CAP_CONFIGURING) {889mtx_unlock(&pcb->pcb_mtx);890mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);891892return (ENOENT);893}894895/* Check the result and disconnect socket of failure */896if (op->result != NG_L2CAP_SUCCESS)897goto disconnect;898899/*900* Now we done with remote side configuration. Configure local901* side if we have not done it yet.902*/903904pcb->cfg_state &= ~NG_BTSOCKET_L2CAP_CFG_OUT_SENT;905pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_OUT;906907if (pcb->cfg_state == NG_BTSOCKET_L2CAP_CFG_BOTH) {908/* Configuration complete - mask socket as open */909ng_btsocket_l2cap_untimeout(pcb);910pcb->state = NG_BTSOCKET_L2CAP_OPEN;911soisconnected(pcb->so);912} else {913if (!(pcb->cfg_state & NG_BTSOCKET_L2CAP_CFG_IN_SENT)) {914/* Send L2CA_Config request - incoming path */915error = ng_btsocket_l2cap_send_l2ca_cfg_req(pcb);916if (error != 0)917goto disconnect;918919pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_IN_SENT;920}921}922923mtx_unlock(&pcb->pcb_mtx);924mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);925926return (error);927928disconnect:929ng_btsocket_l2cap_untimeout(pcb);930931/* Send disconnect with "zero" token */932ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);933934/* ... and close the socket */935pcb->state = NG_BTSOCKET_L2CAP_CLOSED;936soisdisconnected(pcb->so);937938mtx_unlock(&pcb->pcb_mtx);939mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);940941return (error);942} /* ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp */943944/*945* Process L2CA_Config indicator946*/947948static int949ng_btsocket_l2cap_process_l2ca_cfg_ind(struct ng_mesg *msg,950ng_btsocket_l2cap_rtentry_p rt)951{952ng_l2cap_l2ca_cfg_ind_ip *ip = NULL;953ng_btsocket_l2cap_pcb_t *pcb = NULL;954int error = 0;955956if (msg->header.arglen != sizeof(*ip))957return (EMSGSIZE);958959ip = (ng_l2cap_l2ca_cfg_ind_ip *)(msg->data);960961mtx_lock(&ng_btsocket_l2cap_sockets_mtx);962963/* Check for the open socket that has given channel ID */964pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, ip->lcid,965NG_L2CAP_L2CA_IDTYPE_BREDR);966if (pcb == NULL) {967mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);968return (ENOENT);969}970971mtx_lock(&pcb->pcb_mtx);972973NG_BTSOCKET_L2CAP_INFO(974"%s: Got L2CA_Config indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \975"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, state=%d, cfg_state=%x\n",976__func__,977pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],978pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],979pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],980pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],981pcb->psm, pcb->cid, pcb->state, pcb->cfg_state);982983/* XXX FIXME re-configuration on open socket */984if (pcb->state != NG_BTSOCKET_L2CAP_CONFIGURING) {985mtx_unlock(&pcb->pcb_mtx);986mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);987988return (ENOENT);989}990991/*992* XXX FIXME Actually set flush and link timeout. Set QoS here if993* required. Resolve conflicts (flush_timo). Note outgoing MTU (peer's994* incoming MTU) and incoming flow spec.995*/996997pcb->omtu = ip->omtu;998bcopy(&ip->iflow, &pcb->iflow, sizeof(pcb->iflow));999pcb->flush_timo = ip->flush_timo;10001001/*1002* Send L2CA_Config response to our peer and check for the errors,1003* if any send disconnect to close the channel.1004*/10051006if (!(pcb->cfg_state & NG_BTSOCKET_L2CAP_CFG_OUT_SENT)) {1007error = ng_btsocket_l2cap_send_l2ca_cfg_rsp(pcb);1008if (error != 0) {1009ng_btsocket_l2cap_untimeout(pcb);10101011pcb->so->so_error = error;10121013/* Send disconnect with "zero" token */1014ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);10151016/* ... and close the socket */1017pcb->state = NG_BTSOCKET_L2CAP_CLOSED;1018soisdisconnected(pcb->so);1019} else1020pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_OUT_SENT;1021}10221023mtx_unlock(&pcb->pcb_mtx);1024mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);10251026return (error);1027} /* ng_btsocket_l2cap_process_l2cap_cfg_ind */10281029/*1030* Process L2CA_Disconnect response1031*/10321033static int1034ng_btsocket_l2cap_process_l2ca_discon_rsp(struct ng_mesg *msg,1035ng_btsocket_l2cap_rtentry_p rt)1036{1037ng_l2cap_l2ca_discon_op *op = NULL;1038ng_btsocket_l2cap_pcb_t *pcb = NULL;10391040/* Check message */1041if (msg->header.arglen != sizeof(*op))1042return (EMSGSIZE);10431044op = (ng_l2cap_l2ca_discon_op *)(msg->data);10451046mtx_lock(&ng_btsocket_l2cap_sockets_mtx);10471048/*1049* Socket layer must have issued L2CA_Disconnect request, so there1050* must be a socket that wants to be disconnected. Use Netgraph1051* message token to find it.1052*/10531054pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);1055if (pcb == NULL) {1056mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);1057return (0);1058}10591060mtx_lock(&pcb->pcb_mtx);10611062/* XXX Close socket no matter what op->result says */1063if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED) {1064NG_BTSOCKET_L2CAP_INFO(1065"%s: Got L2CA_Disconnect response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \1066"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d\n",1067__func__, msg->header.token,1068pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],1069pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],1070pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],1071pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],1072pcb->psm, pcb->cid, op->result, pcb->state);10731074ng_btsocket_l2cap_untimeout(pcb);10751076pcb->state = NG_BTSOCKET_L2CAP_CLOSED;1077soisdisconnected(pcb->so);1078}10791080mtx_unlock(&pcb->pcb_mtx);1081mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);10821083return (0);1084} /* ng_btsocket_l2cap_process_l2ca_discon_rsp */10851086/*1087* Process L2CA_Disconnect indicator1088*/10891090static int1091ng_btsocket_l2cap_process_l2ca_discon_ind(struct ng_mesg *msg,1092ng_btsocket_l2cap_rtentry_p rt)1093{1094ng_l2cap_l2ca_discon_ind_ip *ip = NULL;1095ng_btsocket_l2cap_pcb_t *pcb = NULL;10961097/* Check message */1098if (msg->header.arglen != sizeof(*ip))1099return (EMSGSIZE);11001101ip = (ng_l2cap_l2ca_discon_ind_ip *)(msg->data);11021103mtx_lock(&ng_btsocket_l2cap_sockets_mtx);11041105/* Look for the socket with given channel ID */1106pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, ip->lcid,1107ip->idtype);1108if (pcb == NULL) {1109mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);1110return (0);1111}11121113/*1114* Channel has already been destroyed, so disconnect the socket1115* and be done with it. If there was any pending request we can1116* not do anything here anyway.1117*/11181119mtx_lock(&pcb->pcb_mtx);11201121NG_BTSOCKET_L2CAP_INFO(1122"%s: Got L2CA_Disconnect indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \1123"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, state=%d\n",1124__func__,1125pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],1126pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],1127pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],1128pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],1129pcb->psm, pcb->cid, pcb->state);11301131if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO)1132ng_btsocket_l2cap_untimeout(pcb);11331134pcb->state = NG_BTSOCKET_L2CAP_CLOSED;1135soisdisconnected(pcb->so);11361137mtx_unlock(&pcb->pcb_mtx);1138mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);11391140return (0);1141} /* ng_btsocket_l2cap_process_l2ca_discon_ind */11421143/*1144* Process L2CA_Write response1145*/11461147static int1148ng_btsocket_l2cap_process_l2ca_write_rsp(struct ng_mesg *msg,1149ng_btsocket_l2cap_rtentry_p rt)1150{1151ng_l2cap_l2ca_write_op *op = NULL;1152ng_btsocket_l2cap_pcb_t *pcb = NULL;11531154/* Check message */1155if (msg->header.arglen != sizeof(*op))1156return (EMSGSIZE);11571158op = (ng_l2cap_l2ca_write_op *)(msg->data);11591160mtx_lock(&ng_btsocket_l2cap_sockets_mtx);11611162/* Look for the socket with given token */1163pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);1164if (pcb == NULL) {1165mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);1166return (ENOENT);1167}11681169mtx_lock(&pcb->pcb_mtx);11701171NG_BTSOCKET_L2CAP_INFO(1172"%s: Got L2CA_Write response, src bdaddr=%x:%x:%x:%x:%x:%x, " \1173"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, length=%d, " \1174"state=%d\n", __func__,1175pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],1176pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],1177pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],1178pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],1179pcb->psm, pcb->cid, op->result, op->length,1180pcb->state);11811182if (pcb->state != NG_BTSOCKET_L2CAP_OPEN) {1183mtx_unlock(&pcb->pcb_mtx);1184mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);11851186return (ENOENT);1187}11881189ng_btsocket_l2cap_untimeout(pcb);11901191/*1192* Check if we have more data to send1193*/1194sbdroprecord(&pcb->so->so_snd);1195if (sbavail(&pcb->so->so_snd) > 0) {1196if (ng_btsocket_l2cap_send2(pcb) == 0)1197ng_btsocket_l2cap_timeout(pcb);1198else1199sbdroprecord(&pcb->so->so_snd); /* XXX */1200}12011202/*1203* Now set the result, drop packet from the socket send queue and1204* ask for more (wakeup sender)1205*/12061207pcb->so->so_error = ng_btsocket_l2cap_result2errno(op->result);1208sowwakeup(pcb->so);12091210mtx_unlock(&pcb->pcb_mtx);1211mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);12121213return (0);1214} /* ng_btsocket_l2cap_process_l2ca_write_rsp */12151216/*1217* Send L2CA_Connect request1218*/12191220static int1221ng_btsocket_l2cap_send_l2ca_con_req(ng_btsocket_l2cap_pcb_p pcb)1222{1223struct ng_mesg *msg = NULL;1224ng_l2cap_l2ca_con_ip *ip = NULL;1225int error = 0;12261227mtx_assert(&pcb->pcb_mtx, MA_OWNED);12281229if (pcb->rt == NULL ||1230pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))1231return (ENETDOWN);12321233NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON,1234sizeof(*ip), M_NOWAIT);1235if (msg == NULL)1236return (ENOMEM);12371238msg->header.token = pcb->token;12391240ip = (ng_l2cap_l2ca_con_ip *)(msg->data);1241bcopy(&pcb->dst, &ip->bdaddr, sizeof(ip->bdaddr));1242ip->psm = pcb->psm;1243ip->linktype = ng_btsock_l2cap_addrtype_to_linktype(pcb->dsttype);1244ip->idtype = pcb->idtype;1245NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg,pcb->rt->hook, 0);12461247return (error);1248} /* ng_btsocket_l2cap_send_l2ca_con_req */12491250/*1251* Send L2CA_Connect response1252*/12531254static int1255ng_btsocket_l2cap_send_l2ca_con_rsp_req(u_int32_t token,1256ng_btsocket_l2cap_rtentry_p rt, bdaddr_p dst, int ident,1257int lcid, int result, int linktype)1258{1259struct ng_mesg *msg = NULL;1260ng_l2cap_l2ca_con_rsp_ip *ip = NULL;1261int error = 0;12621263if (rt == NULL || rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))1264return (ENETDOWN);12651266NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_RSP,1267sizeof(*ip), M_NOWAIT);1268if (msg == NULL)1269return (ENOMEM);12701271msg->header.token = token;12721273ip = (ng_l2cap_l2ca_con_rsp_ip *)(msg->data);1274bcopy(dst, &ip->bdaddr, sizeof(ip->bdaddr));1275ip->ident = ident;1276ip->lcid = lcid;1277ip->linktype = linktype;1278ip->result = result;1279ip->status = 0;12801281NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg, rt->hook, 0);12821283return (error);1284} /* ng_btsocket_l2cap_send_l2ca_con_rsp_req */12851286/*1287* Send L2CA_Config request1288*/12891290static int1291ng_btsocket_l2cap_send_l2ca_cfg_req(ng_btsocket_l2cap_pcb_p pcb)1292{1293struct ng_mesg *msg = NULL;1294ng_l2cap_l2ca_cfg_ip *ip = NULL;1295int error = 0;12961297mtx_assert(&pcb->pcb_mtx, MA_OWNED);12981299if (pcb->rt == NULL ||1300pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))1301return (ENETDOWN);13021303NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG,1304sizeof(*ip), M_NOWAIT);1305if (msg == NULL)1306return (ENOMEM);13071308msg->header.token = pcb->token;13091310ip = (ng_l2cap_l2ca_cfg_ip *)(msg->data);1311ip->lcid = pcb->cid;1312ip->imtu = pcb->imtu;1313bcopy(&pcb->oflow, &ip->oflow, sizeof(ip->oflow));1314ip->flush_timo = pcb->flush_timo;1315ip->link_timo = pcb->link_timo;13161317NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg,pcb->rt->hook, 0);13181319return (error);1320} /* ng_btsocket_l2cap_send_l2ca_cfg_req */13211322/*1323* Send L2CA_Config response1324*/13251326static int1327ng_btsocket_l2cap_send_l2ca_cfg_rsp(ng_btsocket_l2cap_pcb_p pcb)1328{1329struct ng_mesg *msg = NULL;1330ng_l2cap_l2ca_cfg_rsp_ip *ip = NULL;1331int error = 0;13321333mtx_assert(&pcb->pcb_mtx, MA_OWNED);13341335if (pcb->rt == NULL ||1336pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))1337return (ENETDOWN);13381339NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_RSP,1340sizeof(*ip), M_NOWAIT);1341if (msg == NULL)1342return (ENOMEM);13431344msg->header.token = pcb->token;13451346ip = (ng_l2cap_l2ca_cfg_rsp_ip *)(msg->data);1347ip->lcid = pcb->cid;1348ip->omtu = pcb->omtu;1349bcopy(&pcb->iflow, &ip->iflow, sizeof(ip->iflow));13501351NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg, pcb->rt->hook, 0);13521353return (error);1354} /* ng_btsocket_l2cap_send_l2ca_cfg_rsp */13551356/*1357* Send L2CA_Disconnect request1358*/13591360static int1361ng_btsocket_l2cap_send_l2ca_discon_req(u_int32_t token,1362ng_btsocket_l2cap_pcb_p pcb)1363{1364struct ng_mesg *msg = NULL;1365ng_l2cap_l2ca_discon_ip *ip = NULL;1366int error = 0;13671368mtx_assert(&pcb->pcb_mtx, MA_OWNED);13691370if (pcb->rt == NULL ||1371pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))1372return (ENETDOWN);13731374NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON,1375sizeof(*ip), M_NOWAIT);1376if (msg == NULL)1377return (ENOMEM);13781379msg->header.token = token;13801381ip = (ng_l2cap_l2ca_discon_ip *)(msg->data);1382ip->lcid = pcb->cid;1383ip->idtype = pcb->idtype;13841385NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg,pcb->rt->hook, 0);13861387return (error);1388} /* ng_btsocket_l2cap_send_l2ca_discon_req */13891390/*****************************************************************************1391*****************************************************************************1392** Socket interface1393*****************************************************************************1394*****************************************************************************/13951396/*1397* L2CAP sockets data input routine1398*/13991400static void1401ng_btsocket_l2cap_data_input(struct mbuf *m, hook_p hook)1402{1403ng_l2cap_hdr_t *hdr = NULL;1404ng_l2cap_clt_hdr_t *clt_hdr = NULL;1405ng_btsocket_l2cap_pcb_t *pcb = NULL;1406ng_btsocket_l2cap_rtentry_t *rt = NULL;1407uint16_t idtype;14081409if (hook == NULL) {1410NG_BTSOCKET_L2CAP_ALERT(1411"%s: Invalid source hook for L2CAP data packet\n", __func__);1412goto drop;1413}14141415rt = (ng_btsocket_l2cap_rtentry_t *) NG_HOOK_PRIVATE(hook);1416if (rt == NULL) {1417NG_BTSOCKET_L2CAP_ALERT(1418"%s: Could not find out source bdaddr for L2CAP data packet\n", __func__);1419goto drop;1420}14211422m = m_pullup(m, sizeof(uint16_t));1423idtype = *mtod(m, uint16_t *);1424m_adj(m, sizeof(uint16_t));14251426/* Make sure we can access header */1427if (m->m_pkthdr.len < sizeof(*hdr)) {1428NG_BTSOCKET_L2CAP_ERR(1429"%s: L2CAP data packet too small, len=%d\n", __func__, m->m_pkthdr.len);1430goto drop;1431}14321433if (m->m_len < sizeof(*hdr)) {1434m = m_pullup(m, sizeof(*hdr));1435if (m == NULL)1436goto drop;1437}14381439/* Strip L2CAP packet header and verify packet length */1440hdr = mtod(m, ng_l2cap_hdr_t *);1441m_adj(m, sizeof(*hdr));14421443if (hdr->length != m->m_pkthdr.len) {1444NG_BTSOCKET_L2CAP_ERR(1445"%s: Bad L2CAP data packet length, len=%d, length=%d\n",1446__func__, m->m_pkthdr.len, hdr->length);1447goto drop;1448}14491450/*1451* Now process packet. Two cases:1452*1453* 1) Normal packet (cid != 2) then find connected socket and append1454* mbuf to the socket queue. Wakeup socket.1455*1456* 2) Broadcast packet (cid == 2) then find all sockets that connected1457* to the given PSM and have SO_BROADCAST bit set and append mbuf1458* to the socket queue. Wakeup socket.1459*/14601461NG_BTSOCKET_L2CAP_INFO(1462"%s: Received L2CAP data packet: src bdaddr=%x:%x:%x:%x:%x:%x, " \1463"dcid=%d, length=%d\n",1464__func__,1465rt->src.b[5], rt->src.b[4], rt->src.b[3],1466rt->src.b[2], rt->src.b[1], rt->src.b[0],1467hdr->dcid, hdr->length);14681469if ((hdr->dcid >= NG_L2CAP_FIRST_CID) ||1470(idtype == NG_L2CAP_L2CA_IDTYPE_ATT)||1471(idtype == NG_L2CAP_L2CA_IDTYPE_SMP)1472){1473mtx_lock(&ng_btsocket_l2cap_sockets_mtx);14741475/* Normal packet: find connected socket */1476pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, hdr->dcid,idtype);1477if (pcb == NULL) {1478mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);1479goto drop;1480}14811482mtx_lock(&pcb->pcb_mtx);14831484if (pcb->state != NG_BTSOCKET_L2CAP_OPEN) {1485NG_BTSOCKET_L2CAP_ERR(1486"%s: No connected socket found, src bdaddr=%x:%x:%x:%x:%x:%x, dcid=%d, " \1487"state=%d\n", __func__,1488rt->src.b[5], rt->src.b[4], rt->src.b[3],1489rt->src.b[2], rt->src.b[1], rt->src.b[0],1490hdr->dcid, pcb->state);14911492mtx_unlock(&pcb->pcb_mtx);1493mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);1494goto drop;1495}14961497/* Check packet size against socket's incoming MTU */1498if (hdr->length > pcb->imtu) {1499NG_BTSOCKET_L2CAP_ERR(1500"%s: L2CAP data packet too big, src bdaddr=%x:%x:%x:%x:%x:%x, " \1501"dcid=%d, length=%d, imtu=%d\n",1502__func__,1503rt->src.b[5], rt->src.b[4], rt->src.b[3],1504rt->src.b[2], rt->src.b[1], rt->src.b[0],1505hdr->dcid, hdr->length, pcb->imtu);15061507mtx_unlock(&pcb->pcb_mtx);1508mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);1509goto drop;1510}15111512/* Check if we have enough space in socket receive queue */1513if (m->m_pkthdr.len > sbspace(&pcb->so->so_rcv)) {1514/*1515* This is really bad. Receive queue on socket does1516* not have enough space for the packet. We do not1517* have any other choice but drop the packet. L2CAP1518* does not provide any flow control.1519*/15201521NG_BTSOCKET_L2CAP_ERR(1522"%s: Not enough space in socket receive queue. Dropping L2CAP data packet, " \1523"src bdaddr=%x:%x:%x:%x:%x:%x, dcid=%d, len=%d, space=%ld\n",1524__func__,1525rt->src.b[5], rt->src.b[4], rt->src.b[3],1526rt->src.b[2], rt->src.b[1], rt->src.b[0],1527hdr->dcid, m->m_pkthdr.len,1528sbspace(&pcb->so->so_rcv));15291530mtx_unlock(&pcb->pcb_mtx);1531mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);1532goto drop;1533}15341535/* Append packet to the socket receive queue and wakeup */1536sbappendrecord(&pcb->so->so_rcv, m);1537m = NULL;15381539sorwakeup(pcb->so);15401541mtx_unlock(&pcb->pcb_mtx);1542mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);1543} else if (hdr->dcid == NG_L2CAP_CLT_CID) {1544/* Broadcast packet: give packet to all sockets */15451546/* Check packet size against connectionless MTU */1547if (hdr->length > NG_L2CAP_MTU_DEFAULT) {1548NG_BTSOCKET_L2CAP_ERR(1549"%s: Connectionless L2CAP data packet too big, " \1550"src bdaddr=%x:%x:%x:%x:%x:%x, length=%d\n",1551__func__,1552rt->src.b[5], rt->src.b[4], rt->src.b[3],1553rt->src.b[2], rt->src.b[1], rt->src.b[0],1554hdr->length);1555goto drop;1556}15571558/* Make sure we can access connectionless header */1559if (m->m_pkthdr.len < sizeof(*clt_hdr)) {1560NG_BTSOCKET_L2CAP_ERR(1561"%s: Can not get L2CAP connectionless packet header, " \1562"src bdaddr=%x:%x:%x:%x:%x:%x, length=%d\n",1563__func__,1564rt->src.b[5], rt->src.b[4], rt->src.b[3],1565rt->src.b[2], rt->src.b[1], rt->src.b[0],1566hdr->length);1567goto drop;1568}15691570if (m->m_len < sizeof(*clt_hdr)) {1571m = m_pullup(m, sizeof(*clt_hdr));1572if (m == NULL)1573goto drop;1574}15751576/* Strip connectionless header and deliver packet */1577clt_hdr = mtod(m, ng_l2cap_clt_hdr_t *);1578m_adj(m, sizeof(*clt_hdr));15791580NG_BTSOCKET_L2CAP_INFO(1581"%s: Got L2CAP connectionless data packet, " \1582"src bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, length=%d\n",1583__func__,1584rt->src.b[5], rt->src.b[4], rt->src.b[3],1585rt->src.b[2], rt->src.b[1], rt->src.b[0],1586clt_hdr->psm, hdr->length);15871588mtx_lock(&ng_btsocket_l2cap_sockets_mtx);15891590LIST_FOREACH(pcb, &ng_btsocket_l2cap_sockets, next) {1591struct mbuf *copy = NULL;15921593mtx_lock(&pcb->pcb_mtx);15941595if (bcmp(&rt->src, &pcb->src, sizeof(pcb->src)) != 0 ||1596pcb->psm != clt_hdr->psm ||1597pcb->state != NG_BTSOCKET_L2CAP_OPEN ||1598(pcb->so->so_options & SO_BROADCAST) == 0 ||1599m->m_pkthdr.len > sbspace(&pcb->so->so_rcv))1600goto next;16011602/*1603* Create a copy of the packet and append it to the1604* socket's queue. If m_dup() failed - no big deal1605* it is a broadcast traffic after all1606*/16071608copy = m_dup(m, M_NOWAIT);1609if (copy != NULL) {1610sbappendrecord(&pcb->so->so_rcv, copy);1611sorwakeup(pcb->so);1612}1613next:1614mtx_unlock(&pcb->pcb_mtx);1615}16161617mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);1618}1619drop:1620NG_FREE_M(m); /* checks for m != NULL */1621} /* ng_btsocket_l2cap_data_input */16221623/*1624* L2CAP sockets default message input routine1625*/16261627static void1628ng_btsocket_l2cap_default_msg_input(struct ng_mesg *msg, hook_p hook)1629{1630switch (msg->header.cmd) {1631case NGM_L2CAP_NODE_HOOK_INFO: {1632ng_btsocket_l2cap_rtentry_t *rt = NULL;1633ng_l2cap_node_hook_info_ep *ep =1634(ng_l2cap_node_hook_info_ep *)msg->data;1635if (hook == NULL || msg->header.arglen != sizeof(*ep))1636break;16371638if (bcmp(&ep->addr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)1639break;16401641mtx_lock(&ng_btsocket_l2cap_rt_mtx);16421643rt = (ng_btsocket_l2cap_rtentry_t *) NG_HOOK_PRIVATE(hook);1644if (rt == NULL) {1645rt = malloc(sizeof(*rt),1646M_NETGRAPH_BTSOCKET_L2CAP, M_NOWAIT|M_ZERO);1647if (rt == NULL) {1648mtx_unlock(&ng_btsocket_l2cap_rt_mtx);1649break;1650}16511652LIST_INSERT_HEAD(&ng_btsocket_l2cap_rt, rt, next);16531654NG_HOOK_SET_PRIVATE(hook, rt);1655}16561657bcopy(&ep->addr, &rt->src, sizeof(rt->src));1658rt->hook = hook;16591660mtx_unlock(&ng_btsocket_l2cap_rt_mtx);16611662NG_BTSOCKET_L2CAP_INFO(1663"%s: Updating hook \"%s\", src bdaddr=%x:%x:%x:%x:%x:%x\n",1664__func__, NG_HOOK_NAME(hook),1665rt->src.b[5], rt->src.b[4], rt->src.b[3],1666rt->src.b[2], rt->src.b[1], rt->src.b[0]);1667} break;16681669default:1670NG_BTSOCKET_L2CAP_WARN(1671"%s: Unknown message, cmd=%d\n", __func__, msg->header.cmd);1672break;1673}16741675NG_FREE_MSG(msg); /* Checks for msg != NULL */1676} /* ng_btsocket_l2cap_default_msg_input */16771678/*1679* L2CAP sockets L2CA message input routine1680*/16811682static void1683ng_btsocket_l2cap_l2ca_msg_input(struct ng_mesg *msg, hook_p hook)1684{1685ng_btsocket_l2cap_rtentry_p rt = NULL;16861687if (hook == NULL) {1688NG_BTSOCKET_L2CAP_ALERT(1689"%s: Invalid source hook for L2CA message\n", __func__);1690goto drop;1691}16921693rt = (ng_btsocket_l2cap_rtentry_p) NG_HOOK_PRIVATE(hook);1694if (rt == NULL) {1695NG_BTSOCKET_L2CAP_ALERT(1696"%s: Could not find out source bdaddr for L2CA message\n", __func__);1697goto drop;1698}16991700switch (msg->header.cmd) {1701case NGM_L2CAP_L2CA_CON: /* L2CA_Connect response */1702ng_btsocket_l2cap_process_l2ca_con_req_rsp(msg, rt);1703break;17041705case NGM_L2CAP_L2CA_CON_RSP: /* L2CA_ConnectRsp response */1706ng_btsocket_l2cap_process_l2ca_con_rsp_rsp(msg, rt);1707break;17081709case NGM_L2CAP_L2CA_CON_IND: /* L2CA_Connect indicator */1710ng_btsocket_l2cap_process_l2ca_con_ind(msg, rt);1711break;17121713case NGM_L2CAP_L2CA_CFG: /* L2CA_Config response */1714ng_btsocket_l2cap_process_l2ca_cfg_req_rsp(msg, rt);1715break;17161717case NGM_L2CAP_L2CA_CFG_RSP: /* L2CA_ConfigRsp response */1718ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp(msg, rt);1719break;17201721case NGM_L2CAP_L2CA_CFG_IND: /* L2CA_Config indicator */1722ng_btsocket_l2cap_process_l2ca_cfg_ind(msg, rt);1723break;17241725case NGM_L2CAP_L2CA_DISCON: /* L2CA_Disconnect response */1726ng_btsocket_l2cap_process_l2ca_discon_rsp(msg, rt);1727break;17281729case NGM_L2CAP_L2CA_DISCON_IND: /* L2CA_Disconnect indicator */1730ng_btsocket_l2cap_process_l2ca_discon_ind(msg, rt);1731break;17321733case NGM_L2CAP_L2CA_WRITE: /* L2CA_Write response */1734ng_btsocket_l2cap_process_l2ca_write_rsp(msg, rt);1735break;1736case NGM_L2CAP_L2CA_ENC_CHANGE:1737ng_btsocket_l2cap_process_l2ca_enc_change(msg, rt);17381739break;1740/* XXX FIXME add other L2CA messages */17411742default:1743NG_BTSOCKET_L2CAP_WARN(1744"%s: Unknown L2CA message, cmd=%d\n", __func__, msg->header.cmd);1745break;1746}1747drop:1748NG_FREE_MSG(msg);1749} /* ng_btsocket_l2cap_l2ca_msg_input */17501751/*1752* L2CAP sockets input routine1753*/17541755static void1756ng_btsocket_l2cap_input(void *context, int pending)1757{1758item_p item = NULL;1759hook_p hook = NULL;17601761for (;;) {1762mtx_lock(&ng_btsocket_l2cap_queue_mtx);1763NG_BT_ITEMQ_DEQUEUE(&ng_btsocket_l2cap_queue, item);1764mtx_unlock(&ng_btsocket_l2cap_queue_mtx);17651766if (item == NULL)1767break;17681769NGI_GET_HOOK(item, hook);1770if (hook != NULL && NG_HOOK_NOT_VALID(hook))1771goto drop;17721773switch(item->el_flags & NGQF_TYPE) {1774case NGQF_DATA: {1775struct mbuf *m = NULL;17761777NGI_GET_M(item, m);1778ng_btsocket_l2cap_data_input(m, hook);1779} break;17801781case NGQF_MESG: {1782struct ng_mesg *msg = NULL;17831784NGI_GET_MSG(item, msg);17851786switch (msg->header.cmd) {1787case NGM_L2CAP_L2CA_CON:1788case NGM_L2CAP_L2CA_CON_RSP:1789case NGM_L2CAP_L2CA_CON_IND:1790case NGM_L2CAP_L2CA_CFG:1791case NGM_L2CAP_L2CA_CFG_RSP:1792case NGM_L2CAP_L2CA_CFG_IND:1793case NGM_L2CAP_L2CA_DISCON:1794case NGM_L2CAP_L2CA_DISCON_IND:1795case NGM_L2CAP_L2CA_WRITE:1796case NGM_L2CAP_L2CA_ENC_CHANGE:1797/* XXX FIXME add other L2CA messages */1798ng_btsocket_l2cap_l2ca_msg_input(msg, hook);1799break;18001801default:1802ng_btsocket_l2cap_default_msg_input(msg, hook);1803break;1804}1805} break;18061807default:1808KASSERT(0,1809("%s: invalid item type=%ld\n", __func__, (item->el_flags & NGQF_TYPE)));1810break;1811}1812drop:1813if (hook != NULL)1814NG_HOOK_UNREF(hook);18151816NG_FREE_ITEM(item);1817}1818} /* ng_btsocket_l2cap_input */18191820/*1821* Route cleanup task. Gets scheduled when hook is disconnected. Here we1822* will find all sockets that use "invalid" hook and disconnect them.1823*/18241825static void1826ng_btsocket_l2cap_rtclean(void *context, int pending)1827{1828ng_btsocket_l2cap_pcb_p pcb = NULL, pcb_next = NULL;1829ng_btsocket_l2cap_rtentry_p rt = NULL;18301831mtx_lock(&ng_btsocket_l2cap_rt_mtx);1832mtx_lock(&ng_btsocket_l2cap_sockets_mtx);18331834/*1835* First disconnect all sockets that use "invalid" hook1836*/18371838for (pcb = LIST_FIRST(&ng_btsocket_l2cap_sockets); pcb != NULL; ) {1839mtx_lock(&pcb->pcb_mtx);1840pcb_next = LIST_NEXT(pcb, next);18411842if (pcb->rt != NULL &&1843pcb->rt->hook != NULL && NG_HOOK_NOT_VALID(pcb->rt->hook)) {1844if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO)1845ng_btsocket_l2cap_untimeout(pcb);18461847pcb->so->so_error = ENETDOWN;1848pcb->state = NG_BTSOCKET_L2CAP_CLOSED;1849soisdisconnected(pcb->so);18501851pcb->token = 0;1852pcb->cid = 0;1853pcb->rt = NULL;1854}18551856mtx_unlock(&pcb->pcb_mtx);1857pcb = pcb_next;1858}18591860/*1861* Now cleanup routing table1862*/18631864for (rt = LIST_FIRST(&ng_btsocket_l2cap_rt); rt != NULL; ) {1865ng_btsocket_l2cap_rtentry_p rt_next = LIST_NEXT(rt, next);18661867if (rt->hook != NULL && NG_HOOK_NOT_VALID(rt->hook)) {1868LIST_REMOVE(rt, next);18691870NG_HOOK_SET_PRIVATE(rt->hook, NULL);1871NG_HOOK_UNREF(rt->hook); /* Remove extra reference */18721873bzero(rt, sizeof(*rt));1874free(rt, M_NETGRAPH_BTSOCKET_L2CAP);1875}18761877rt = rt_next;1878}18791880mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);1881mtx_unlock(&ng_btsocket_l2cap_rt_mtx);1882} /* ng_btsocket_l2cap_rtclean */18831884/*1885* Initialize everything1886*/18871888static void1889ng_btsocket_l2cap_init(void *arg __unused)1890{1891int error = 0;18921893ng_btsocket_l2cap_node = NULL;1894ng_btsocket_l2cap_debug_level = NG_BTSOCKET_WARN_LEVEL;18951896/* Register Netgraph node type */1897error = ng_newtype(&typestruct);1898if (error != 0) {1899NG_BTSOCKET_L2CAP_ALERT(1900"%s: Could not register Netgraph node type, error=%d\n", __func__, error);19011902return;1903}19041905/* Create Netgrapg node */1906error = ng_make_node_common(&typestruct, &ng_btsocket_l2cap_node);1907if (error != 0) {1908NG_BTSOCKET_L2CAP_ALERT(1909"%s: Could not create Netgraph node, error=%d\n", __func__, error);19101911ng_btsocket_l2cap_node = NULL;19121913return;1914}19151916error = ng_name_node(ng_btsocket_l2cap_node,1917NG_BTSOCKET_L2CAP_NODE_TYPE);1918if (error != 0) {1919NG_BTSOCKET_L2CAP_ALERT(1920"%s: Could not name Netgraph node, error=%d\n", __func__, error);19211922NG_NODE_UNREF(ng_btsocket_l2cap_node);1923ng_btsocket_l2cap_node = NULL;19241925return;1926}19271928/* Create input queue */1929NG_BT_ITEMQ_INIT(&ng_btsocket_l2cap_queue, ifqmaxlen);1930mtx_init(&ng_btsocket_l2cap_queue_mtx,1931"btsocks_l2cap_queue_mtx", NULL, MTX_DEF);1932TASK_INIT(&ng_btsocket_l2cap_queue_task, 0,1933ng_btsocket_l2cap_input, NULL);19341935/* Create list of sockets */1936LIST_INIT(&ng_btsocket_l2cap_sockets);1937mtx_init(&ng_btsocket_l2cap_sockets_mtx,1938"btsocks_l2cap_sockets_mtx", NULL, MTX_DEF);19391940/* Routing table */1941LIST_INIT(&ng_btsocket_l2cap_rt);1942mtx_init(&ng_btsocket_l2cap_rt_mtx,1943"btsocks_l2cap_rt_mtx", NULL, MTX_DEF);1944TASK_INIT(&ng_btsocket_l2cap_rt_task, 0,1945ng_btsocket_l2cap_rtclean, NULL);1946} /* ng_btsocket_l2cap_init */1947SYSINIT(ng_btsocket_l2cap_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD,1948ng_btsocket_l2cap_init, NULL);19491950/*1951* Abort connection on socket1952*/19531954void1955ng_btsocket_l2cap_abort(struct socket *so)1956{1957so->so_error = ECONNABORTED;19581959(void)ng_btsocket_l2cap_disconnect(so);1960} /* ng_btsocket_l2cap_abort */19611962void1963ng_btsocket_l2cap_close(struct socket *so)1964{19651966(void)ng_btsocket_l2cap_disconnect(so);1967} /* ng_btsocket_l2cap_close */19681969/*1970* Create and attach new socket1971*/19721973int1974ng_btsocket_l2cap_attach(struct socket *so, int proto, struct thread *td)1975{1976static u_int32_t token = 0;1977ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);1978int error;19791980/* Check socket and protocol */1981if (ng_btsocket_l2cap_node == NULL)1982return (EPROTONOSUPPORT);1983if (so->so_type != SOCK_SEQPACKET)1984return (ESOCKTNOSUPPORT);19851986#if 0 /* XXX sonewconn() calls pr_attach() with proto == 0 */1987if (proto != 0)1988if (proto != BLUETOOTH_PROTO_L2CAP)1989return (EPROTONOSUPPORT);1990#endif /* XXX */19911992if (pcb != NULL)1993return (EISCONN);19941995/* Reserve send and receive space if it is not reserved yet */1996if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) {1997error = soreserve(so, NG_BTSOCKET_L2CAP_SENDSPACE,1998NG_BTSOCKET_L2CAP_RECVSPACE);1999if (error != 0)2000return (error);2001}20022003/* Allocate the PCB */2004pcb = malloc(sizeof(*pcb),2005M_NETGRAPH_BTSOCKET_L2CAP, M_NOWAIT | M_ZERO);2006if (pcb == NULL)2007return (ENOMEM);20082009/* Link the PCB and the socket */2010so->so_pcb = (caddr_t) pcb;2011pcb->so = so;2012pcb->state = NG_BTSOCKET_L2CAP_CLOSED;20132014/* Initialize PCB */2015pcb->imtu = pcb->omtu = NG_L2CAP_MTU_DEFAULT;20162017/* Default flow */2018pcb->iflow.flags = 0x0;2019pcb->iflow.service_type = NG_HCI_SERVICE_TYPE_BEST_EFFORT;2020pcb->iflow.token_rate = 0xffffffff; /* maximum */2021pcb->iflow.token_bucket_size = 0xffffffff; /* maximum */2022pcb->iflow.peak_bandwidth = 0x00000000; /* maximum */2023pcb->iflow.latency = 0xffffffff; /* don't care */2024pcb->iflow.delay_variation = 0xffffffff; /* don't care */20252026bcopy(&pcb->iflow, &pcb->oflow, sizeof(pcb->oflow));20272028pcb->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT;2029pcb->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT;20302031/*2032* XXX Mark PCB mutex as DUPOK to prevent "duplicated lock of2033* the same type" message. When accepting new L2CAP connection2034* ng_btsocket_l2cap_process_l2ca_con_ind() holds both PCB mutexes2035* for "old" (accepting) PCB and "new" (created) PCB.2036*/20372038mtx_init(&pcb->pcb_mtx, "btsocks_l2cap_pcb_mtx", NULL,2039MTX_DEF|MTX_DUPOK);2040callout_init_mtx(&pcb->timo, &pcb->pcb_mtx, 0);20412042/*2043* Add the PCB to the list2044*2045* XXX FIXME VERY IMPORTANT!2046*2047* This is totally FUBAR. We could get here in two cases:2048*2049* 1) When user calls socket()2050* 2) When we need to accept new incoming connection and call2051* sonewconn()2052*2053* In the first case we must acquire ng_btsocket_l2cap_sockets_mtx.2054* In the second case we hold ng_btsocket_l2cap_sockets_mtx already.2055* So we now need to distinguish between these cases. From reading2056* /sys/kern/uipc_socket.c we can find out that sonewconn() calls2057* pr_attach() with proto == 0 and td == NULL. For now use this fact2058* to figure out if we were called from socket() or from sonewconn().2059*/20602061if (td != NULL)2062mtx_lock(&ng_btsocket_l2cap_sockets_mtx);2063else2064mtx_assert(&ng_btsocket_l2cap_sockets_mtx, MA_OWNED);20652066/* Set PCB token. Use ng_btsocket_l2cap_sockets_mtx for protection */2067if (++ token == 0)2068token ++;20692070pcb->token = token;20712072LIST_INSERT_HEAD(&ng_btsocket_l2cap_sockets, pcb, next);20732074if (td != NULL)2075mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);20762077return (0);2078} /* ng_btsocket_l2cap_attach */20792080/*2081* Bind socket2082*/20832084int2085ng_btsocket_l2cap_bind(struct socket *so, struct sockaddr *nam,2086struct thread *td)2087{2088ng_btsocket_l2cap_pcb_t *pcb = NULL;2089struct sockaddr_l2cap *sa = (struct sockaddr_l2cap *) nam;2090int psm, error = 0;20912092if (ng_btsocket_l2cap_node == NULL)2093return (EINVAL);20942095/* Verify address */2096if (sa == NULL)2097return (EINVAL);2098if (sa->l2cap_family != AF_BLUETOOTH)2099return (EAFNOSUPPORT);2100/*For the time being, Not support LE binding.*/2101if ((sa->l2cap_len != sizeof(*sa))&&2102(sa->l2cap_len != sizeof(struct sockaddr_l2cap_compat)))2103return (EINVAL);21042105psm = le16toh(sa->l2cap_psm);21062107/*2108* Check if other socket has this address already (look for exact2109* match PSM and bdaddr) and assign socket address if it's available.2110*2111* Note: socket can be bound to ANY PSM (zero) thus allowing several2112* channels with the same PSM between the same pair of BD_ADDR'es.2113*/21142115mtx_lock(&ng_btsocket_l2cap_sockets_mtx);21162117LIST_FOREACH(pcb, &ng_btsocket_l2cap_sockets, next)2118if (psm != 0 && psm == pcb->psm &&2119bcmp(&pcb->src, &sa->l2cap_bdaddr, sizeof(bdaddr_t)) == 0)2120break;21212122if (pcb == NULL) {2123/* Set socket address */2124pcb = so2l2cap_pcb(so);2125if (pcb != NULL) {2126bcopy(&sa->l2cap_bdaddr, &pcb->src, sizeof(pcb->src));2127pcb->psm = psm;2128} else2129error = EINVAL;2130} else2131error = EADDRINUSE;21322133mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);21342135return (error);2136} /* ng_btsocket_l2cap_bind */21372138/*2139* Connect socket2140*/21412142int2143ng_btsocket_l2cap_connect(struct socket *so, struct sockaddr *nam,2144struct thread *td)2145{2146ng_btsocket_l2cap_pcb_t *pcb = so2l2cap_pcb(so);2147struct sockaddr_l2cap_compat *sal = (struct sockaddr_l2cap_compat *) nam;2148struct sockaddr_l2cap *sa = (struct sockaddr_l2cap *)nam;2149struct sockaddr_l2cap ba;2150ng_btsocket_l2cap_rtentry_t *rt = NULL;2151int have_src, error = 0;2152int idtype = NG_L2CAP_L2CA_IDTYPE_BREDR;2153/* Check socket */2154if (pcb == NULL)2155return (EINVAL);2156if (ng_btsocket_l2cap_node == NULL)2157return (EINVAL);2158if (pcb->state == NG_BTSOCKET_L2CAP_CONNECTING)2159return (EINPROGRESS);21602161/* Verify address */2162if (sa == NULL)2163return (EINVAL);2164if (sa->l2cap_family != AF_BLUETOOTH)2165return (EAFNOSUPPORT);2166if (sa->l2cap_len == sizeof(*sal)){2167bcopy(sal, &ba, sizeof(*sal));2168sa = &ba;2169sa->l2cap_len = sizeof(*sa);2170sa->l2cap_bdaddr_type = BDADDR_BREDR;2171}2172if (sa->l2cap_len != sizeof(*sa))2173return (EINVAL);2174if ((sa->l2cap_psm && sa->l2cap_cid))2175return EINVAL;2176if (bcmp(&sa->l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)2177return (EDESTADDRREQ);2178if((sa->l2cap_bdaddr_type == BDADDR_BREDR)&&2179(sa->l2cap_psm == 0))2180return EDESTADDRREQ;2181if(sa->l2cap_bdaddr_type != BDADDR_BREDR){2182if(sa->l2cap_cid == NG_L2CAP_ATT_CID){2183idtype = NG_L2CAP_L2CA_IDTYPE_ATT;2184}else if (sa->l2cap_cid == NG_L2CAP_SMP_CID){2185idtype =NG_L2CAP_L2CA_IDTYPE_SMP;2186}else{2187//if cid == 0 idtype = NG_L2CAP_L2CA_IDTYPE_LE;2188// Not supported yet2189return EINVAL;2190}2191}2192if (pcb->psm != 0 && pcb->psm != le16toh(sa->l2cap_psm))2193return (EINVAL);2194/*2195* Routing. Socket should be bound to some source address. The source2196* address can be ANY. Destination address must be set and it must not2197* be ANY. If source address is ANY then find first rtentry that has2198* src != dst.2199*/22002201mtx_lock(&ng_btsocket_l2cap_rt_mtx);2202mtx_lock(&ng_btsocket_l2cap_sockets_mtx);2203mtx_lock(&pcb->pcb_mtx);22042205/* Send destination address and PSM */2206bcopy(&sa->l2cap_bdaddr, &pcb->dst, sizeof(pcb->dst));2207pcb->psm = le16toh(sa->l2cap_psm);2208pcb->dsttype = sa->l2cap_bdaddr_type;2209pcb->cid = 0;2210pcb->idtype = idtype;2211pcb->rt = NULL;2212have_src = bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src));22132214LIST_FOREACH(rt, &ng_btsocket_l2cap_rt, next) {2215if (rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))2216continue;22172218/* Match src and dst */2219if (have_src) {2220if (bcmp(&pcb->src, &rt->src, sizeof(rt->src)) == 0)2221break;2222} else {2223if (bcmp(&pcb->dst, &rt->src, sizeof(rt->src)) != 0)2224break;2225}2226}22272228if (rt != NULL) {2229pcb->rt = rt;22302231if (!have_src){2232bcopy(&rt->src, &pcb->src, sizeof(pcb->src));2233pcb->srctype =2234(sa->l2cap_bdaddr_type == BDADDR_BREDR)?2235BDADDR_BREDR : BDADDR_LE_PUBLIC;2236}2237} else2238error = EHOSTUNREACH;22392240/*2241* Send L2CA_Connect request2242*/22432244if (error == 0) {2245error = ng_btsocket_l2cap_send_l2ca_con_req(pcb);2246if (error == 0) {2247pcb->flags |= NG_BTSOCKET_L2CAP_CLIENT;2248pcb->state = NG_BTSOCKET_L2CAP_CONNECTING;2249soisconnecting(pcb->so);22502251ng_btsocket_l2cap_timeout(pcb);2252}2253}22542255mtx_unlock(&pcb->pcb_mtx);2256mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);2257mtx_unlock(&ng_btsocket_l2cap_rt_mtx);22582259return (error);2260} /* ng_btsocket_l2cap_connect */22612262/*2263* Process ioctl's calls on socket2264*/22652266int2267ng_btsocket_l2cap_control(struct socket *so, u_long cmd, void *data,2268struct ifnet *ifp, struct thread *td)2269{2270return (EINVAL);2271} /* ng_btsocket_l2cap_control */22722273/*2274* Process getsockopt/setsockopt system calls2275*/22762277int2278ng_btsocket_l2cap_ctloutput(struct socket *so, struct sockopt *sopt)2279{2280ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);2281int error = 0;2282ng_l2cap_cfg_opt_val_t v;22832284if (pcb == NULL)2285return (EINVAL);2286if (ng_btsocket_l2cap_node == NULL)2287return (EINVAL);22882289if (sopt->sopt_level != SOL_L2CAP)2290return (0);22912292mtx_lock(&pcb->pcb_mtx);22932294switch (sopt->sopt_dir) {2295case SOPT_GET:2296switch (sopt->sopt_name) {2297case SO_L2CAP_IMTU: /* get incoming MTU */2298error = sooptcopyout(sopt, &pcb->imtu,2299sizeof(pcb->imtu));2300break;23012302case SO_L2CAP_OMTU: /* get outgoing (peer incoming) MTU */2303error = sooptcopyout(sopt, &pcb->omtu,2304sizeof(pcb->omtu));2305break;23062307case SO_L2CAP_IFLOW: /* get incoming flow spec. */2308error = sooptcopyout(sopt, &pcb->iflow,2309sizeof(pcb->iflow));2310break;23112312case SO_L2CAP_OFLOW: /* get outgoing flow spec. */2313error = sooptcopyout(sopt, &pcb->oflow,2314sizeof(pcb->oflow));2315break;23162317case SO_L2CAP_FLUSH: /* get flush timeout */2318error = sooptcopyout(sopt, &pcb->flush_timo,2319sizeof(pcb->flush_timo));2320break;2321case SO_L2CAP_ENCRYPTED: /* get encrypt required */2322error = sooptcopyout(sopt, &pcb->need_encrypt,2323sizeof(pcb->need_encrypt));2324break;23252326default:2327error = ENOPROTOOPT;2328break;2329}2330break;23312332case SOPT_SET:2333/*2334* XXX2335* We do not allow to change these parameters while socket is2336* connected or we are in the process of creating a connection.2337* May be this should indicate re-configuration of the open2338* channel?2339*/23402341if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED) {2342error = EACCES;2343break;2344}23452346switch (sopt->sopt_name) {2347case SO_L2CAP_IMTU: /* set incoming MTU */2348error = sooptcopyin(sopt, &v, sizeof(v), sizeof(v.mtu));2349if (error == 0)2350pcb->imtu = v.mtu;2351break;23522353case SO_L2CAP_OFLOW: /* set outgoing flow spec. */2354error = sooptcopyin(sopt, &v, sizeof(v),sizeof(v.flow));2355if (error == 0)2356bcopy(&v.flow, &pcb->oflow, sizeof(pcb->oflow));2357break;23582359case SO_L2CAP_FLUSH: /* set flush timeout */2360error = sooptcopyin(sopt, &v, sizeof(v),2361sizeof(v.flush_timo));2362if (error == 0)2363pcb->flush_timo = v.flush_timo;2364break;2365case SO_L2CAP_ENCRYPTED: /*set connect encryption opt*/2366if((pcb->state != NG_BTSOCKET_L2CAP_OPEN) &&2367(pcb->state != NG_BTSOCKET_L2CAP_W4_ENC_CHANGE)){2368error = sooptcopyin(sopt, &v, sizeof(v),2369sizeof(v.encryption));2370if(error == 0)2371pcb->need_encrypt = (v.encryption)?1:0;2372}else{2373error = EINVAL;2374}2375break;2376default:2377error = ENOPROTOOPT;2378break;2379}2380break;23812382default:2383error = EINVAL;2384break;2385}23862387mtx_unlock(&pcb->pcb_mtx);23882389return (error);2390} /* ng_btsocket_l2cap_ctloutput */23912392/*2393* Detach and destroy socket2394*/23952396void2397ng_btsocket_l2cap_detach(struct socket *so)2398{2399ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);24002401KASSERT(pcb != NULL, ("ng_btsocket_l2cap_detach: pcb == NULL"));24022403if (ng_btsocket_l2cap_node == NULL)2404return;24052406mtx_lock(&ng_btsocket_l2cap_sockets_mtx);2407mtx_lock(&pcb->pcb_mtx);24082409/* XXX what to do with pending request? */2410if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO)2411ng_btsocket_l2cap_untimeout(pcb);24122413if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED &&2414pcb->state != NG_BTSOCKET_L2CAP_DISCONNECTING)2415/* Send disconnect request with "zero" token */2416ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);24172418pcb->state = NG_BTSOCKET_L2CAP_CLOSED;24192420LIST_REMOVE(pcb, next);24212422mtx_unlock(&pcb->pcb_mtx);2423mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);24242425mtx_destroy(&pcb->pcb_mtx);2426bzero(pcb, sizeof(*pcb));2427free(pcb, M_NETGRAPH_BTSOCKET_L2CAP);24282429soisdisconnected(so);2430so->so_pcb = NULL;2431} /* ng_btsocket_l2cap_detach */24322433/*2434* Disconnect socket2435*/24362437int2438ng_btsocket_l2cap_disconnect(struct socket *so)2439{2440ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);2441int error = 0;24422443if (pcb == NULL)2444return (EINVAL);2445if (ng_btsocket_l2cap_node == NULL)2446return (EINVAL);24472448mtx_lock(&pcb->pcb_mtx);24492450if (pcb->state == NG_BTSOCKET_L2CAP_DISCONNECTING) {2451mtx_unlock(&pcb->pcb_mtx);2452return (EINPROGRESS);2453}24542455if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED) {2456/* XXX FIXME what to do with pending request? */2457if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO)2458ng_btsocket_l2cap_untimeout(pcb);24592460error = ng_btsocket_l2cap_send_l2ca_discon_req(pcb->token, pcb);2461if (error == 0) {2462pcb->state = NG_BTSOCKET_L2CAP_DISCONNECTING;2463soisdisconnecting(so);24642465ng_btsocket_l2cap_timeout(pcb);2466}24672468/* XXX FIXME what to do if error != 0 */2469}24702471mtx_unlock(&pcb->pcb_mtx);24722473return (error);2474} /* ng_btsocket_l2cap_disconnect */24752476/*2477* Listen on socket2478*/24792480int2481ng_btsocket_l2cap_listen(struct socket *so, int backlog, struct thread *td)2482{2483ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);2484int error;24852486SOCK_LOCK(so);2487error = solisten_proto_check(so);2488if (error != 0)2489goto out;2490if (pcb == NULL) {2491solisten_proto_abort(so);2492error = EINVAL;2493goto out;2494}2495if (ng_btsocket_l2cap_node == NULL) {2496solisten_proto_abort(so);2497error = EINVAL;2498goto out;2499}2500if (pcb->psm == 0) {2501solisten_proto_abort(so);2502error = EADDRNOTAVAIL;2503goto out;2504}2505solisten_proto(so, backlog);2506out:2507SOCK_UNLOCK(so);2508return (error);2509} /* ng_btsocket_listen */25102511/*2512* Return peer address for getpeername(2) or for accept(2). For the latter2513* case no extra work to do here, socket must be connected and ready.2514*/2515int2516ng_btsocket_l2cap_peeraddr(struct socket *so, struct sockaddr *sa)2517{2518ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);2519struct sockaddr_l2cap *l2cap = (struct sockaddr_l2cap *)sa;25202521if (pcb == NULL)2522return (EINVAL);2523if (ng_btsocket_l2cap_node == NULL)2524return (EINVAL);25252526*l2cap = (struct sockaddr_l2cap ){2527.l2cap_len = sizeof(struct sockaddr_l2cap),2528.l2cap_family = AF_BLUETOOTH,2529.l2cap_psm = htole16(pcb->psm),2530};2531bcopy(&pcb->dst, &l2cap->l2cap_bdaddr, sizeof(l2cap->l2cap_bdaddr));2532switch(pcb->idtype){2533case NG_L2CAP_L2CA_IDTYPE_ATT:2534l2cap->l2cap_cid = NG_L2CAP_ATT_CID;2535break;2536case NG_L2CAP_L2CA_IDTYPE_SMP:2537l2cap->l2cap_cid = NG_L2CAP_SMP_CID;2538break;2539default:2540l2cap->l2cap_cid = 0;2541break;2542}2543l2cap->l2cap_bdaddr_type = pcb->dsttype;25442545return (0);2546}25472548/*2549* Send data to socket2550*/25512552int2553ng_btsocket_l2cap_send(struct socket *so, int flags, struct mbuf *m,2554struct sockaddr *nam, struct mbuf *control, struct thread *td)2555{2556ng_btsocket_l2cap_pcb_t *pcb = so2l2cap_pcb(so);2557int error = 0;25582559if (ng_btsocket_l2cap_node == NULL) {2560error = ENETDOWN;2561goto drop;2562}25632564/* Check socket and input */2565if (pcb == NULL || m == NULL || control != NULL) {2566error = EINVAL;2567goto drop;2568}25692570mtx_lock(&pcb->pcb_mtx);25712572/* Make sure socket is connected */2573if (pcb->state != NG_BTSOCKET_L2CAP_OPEN) {2574mtx_unlock(&pcb->pcb_mtx);2575error = ENOTCONN;2576goto drop;2577}25782579/* Check route */2580if (pcb->rt == NULL ||2581pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) {2582mtx_unlock(&pcb->pcb_mtx);2583error = ENETDOWN;2584goto drop;2585}25862587/* Check packet size against outgoing (peer's incoming) MTU) */2588if (m->m_pkthdr.len > pcb->omtu) {2589NG_BTSOCKET_L2CAP_ERR(2590"%s: Packet too big, len=%d, omtu=%d\n", __func__, m->m_pkthdr.len, pcb->omtu);25912592mtx_unlock(&pcb->pcb_mtx);2593error = EMSGSIZE;2594goto drop;2595}25962597/*2598* First put packet on socket send queue. Then check if we have2599* pending timeout. If we do not have timeout then we must send2600* packet and schedule timeout. Otherwise do nothing and wait for2601* L2CA_WRITE_RSP.2602*/26032604sbappendrecord(&pcb->so->so_snd, m);2605m = NULL;26062607if (!(pcb->flags & NG_BTSOCKET_L2CAP_TIMO)) {2608error = ng_btsocket_l2cap_send2(pcb);2609if (error == 0)2610ng_btsocket_l2cap_timeout(pcb);2611else2612sbdroprecord(&pcb->so->so_snd); /* XXX */2613}26142615mtx_unlock(&pcb->pcb_mtx);2616drop:2617NG_FREE_M(m); /* checks for != NULL */2618NG_FREE_M(control);26192620return (error);2621} /* ng_btsocket_l2cap_send */26222623/*2624* Send first packet in the socket queue to the L2CAP layer2625*/26262627static int2628ng_btsocket_l2cap_send2(ng_btsocket_l2cap_pcb_p pcb)2629{2630struct mbuf *m = NULL;2631ng_l2cap_l2ca_hdr_t *hdr = NULL;2632int error = 0;26332634mtx_assert(&pcb->pcb_mtx, MA_OWNED);26352636if (sbavail(&pcb->so->so_snd) == 0)2637return (EINVAL); /* XXX */26382639m = m_dup(pcb->so->so_snd.sb_mb, M_NOWAIT);2640if (m == NULL)2641return (ENOBUFS);26422643/* Create L2CA packet header */2644M_PREPEND(m, sizeof(*hdr), M_NOWAIT);2645if (m != NULL)2646if (m->m_len < sizeof(*hdr))2647m = m_pullup(m, sizeof(*hdr));26482649if (m == NULL) {2650NG_BTSOCKET_L2CAP_ERR(2651"%s: Failed to create L2CA packet header\n", __func__);26522653return (ENOBUFS);2654}26552656hdr = mtod(m, ng_l2cap_l2ca_hdr_t *);2657hdr->token = pcb->token;2658hdr->length = m->m_pkthdr.len - sizeof(*hdr);2659hdr->lcid = pcb->cid;2660hdr->idtype = pcb->idtype;2661NG_BTSOCKET_L2CAP_INFO(2662"%s: Sending packet: len=%d, length=%d, lcid=%d, token=%d, state=%d\n",2663__func__, m->m_pkthdr.len, hdr->length, hdr->lcid,2664hdr->token, pcb->state);26652666/*2667* If we got here than we have successfully creates new L2CAP2668* data packet and now we can send it to the L2CAP layer2669*/26702671NG_SEND_DATA_ONLY(error, pcb->rt->hook, m);26722673return (error);2674} /* ng_btsocket_l2cap_send2 */26752676/*2677* Get socket address2678*/2679int2680ng_btsocket_l2cap_sockaddr(struct socket *so, struct sockaddr *sa)2681{2682ng_btsocket_l2cap_pcb_p pcb = so2l2cap_pcb(so);2683struct sockaddr_l2cap *l2cap = (struct sockaddr_l2cap *)sa;26842685if (pcb == NULL)2686return (EINVAL);2687if (ng_btsocket_l2cap_node == NULL)2688return (EINVAL);26892690*l2cap = (struct sockaddr_l2cap ){2691.l2cap_len = sizeof(struct sockaddr_l2cap),2692.l2cap_family = AF_BLUETOOTH,2693.l2cap_psm = htole16(pcb->psm),2694.l2cap_bdaddr_type = pcb->srctype,2695};2696bcopy(&pcb->src, &l2cap->l2cap_bdaddr, sizeof(l2cap->l2cap_bdaddr));26972698return (0);2699}27002701/*****************************************************************************2702*****************************************************************************2703** Misc. functions2704*****************************************************************************2705*****************************************************************************/27062707/*2708* Look for the socket that listens on given PSM and bdaddr. Returns exact or2709* close match (if any). Caller must hold ng_btsocket_l2cap_sockets_mtx.2710*/27112712static ng_btsocket_l2cap_pcb_p2713ng_btsocket_l2cap_pcb_by_addr(bdaddr_p bdaddr, int psm)2714{2715ng_btsocket_l2cap_pcb_p p = NULL, p1 = NULL;27162717mtx_assert(&ng_btsocket_l2cap_sockets_mtx, MA_OWNED);27182719LIST_FOREACH(p, &ng_btsocket_l2cap_sockets, next) {2720if (p->so == NULL || !SOLISTENING(p->so) || p->psm != psm)2721continue;27222723if (bcmp(&p->src, bdaddr, sizeof(p->src)) == 0)2724break;27252726if (bcmp(&p->src, NG_HCI_BDADDR_ANY, sizeof(p->src)) == 0)2727p1 = p;2728}27292730return ((p != NULL)? p : p1);2731} /* ng_btsocket_l2cap_pcb_by_addr */27322733/*2734* Look for the socket that has given token.2735* Caller must hold ng_btsocket_l2cap_sockets_mtx.2736*/27372738static ng_btsocket_l2cap_pcb_p2739ng_btsocket_l2cap_pcb_by_token(u_int32_t token)2740{2741ng_btsocket_l2cap_pcb_p p = NULL;27422743if (token == 0)2744return (NULL);27452746mtx_assert(&ng_btsocket_l2cap_sockets_mtx, MA_OWNED);27472748LIST_FOREACH(p, &ng_btsocket_l2cap_sockets, next)2749if (p->token == token)2750break;27512752return (p);2753} /* ng_btsocket_l2cap_pcb_by_token */27542755/*2756* Look for the socket that assigned to given source address and channel ID.2757* Caller must hold ng_btsocket_l2cap_sockets_mtx2758*/27592760static ng_btsocket_l2cap_pcb_p2761ng_btsocket_l2cap_pcb_by_cid(bdaddr_p src, int cid, int idtype)2762{2763ng_btsocket_l2cap_pcb_p p = NULL;27642765mtx_assert(&ng_btsocket_l2cap_sockets_mtx, MA_OWNED);27662767LIST_FOREACH(p, &ng_btsocket_l2cap_sockets, next){2768if (p->cid == cid &&2769bcmp(src, &p->src, sizeof(p->src)) == 0&&2770p->idtype == idtype)2771break;2772}2773return (p);2774} /* ng_btsocket_l2cap_pcb_by_cid */27752776/*2777* Set timeout on socket2778*/27792780static void2781ng_btsocket_l2cap_timeout(ng_btsocket_l2cap_pcb_p pcb)2782{2783mtx_assert(&pcb->pcb_mtx, MA_OWNED);27842785if (!(pcb->flags & NG_BTSOCKET_L2CAP_TIMO)) {2786pcb->flags |= NG_BTSOCKET_L2CAP_TIMO;2787callout_reset(&pcb->timo, bluetooth_l2cap_ertx_timeout(),2788ng_btsocket_l2cap_process_timeout, pcb);2789} else2790KASSERT(0,2791("%s: Duplicated socket timeout?!\n", __func__));2792} /* ng_btsocket_l2cap_timeout */27932794/*2795* Unset timeout on socket2796*/27972798static void2799ng_btsocket_l2cap_untimeout(ng_btsocket_l2cap_pcb_p pcb)2800{2801mtx_assert(&pcb->pcb_mtx, MA_OWNED);28022803if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO) {2804callout_stop(&pcb->timo);2805pcb->flags &= ~NG_BTSOCKET_L2CAP_TIMO;2806} else2807KASSERT(0,2808("%s: No socket timeout?!\n", __func__));2809} /* ng_btsocket_l2cap_untimeout */28102811/*2812* Process timeout on socket2813*/28142815static void2816ng_btsocket_l2cap_process_timeout(void *xpcb)2817{2818ng_btsocket_l2cap_pcb_p pcb = (ng_btsocket_l2cap_pcb_p) xpcb;28192820mtx_assert(&pcb->pcb_mtx, MA_OWNED);28212822pcb->flags &= ~NG_BTSOCKET_L2CAP_TIMO;2823pcb->so->so_error = ETIMEDOUT;28242825switch (pcb->state) {2826case NG_BTSOCKET_L2CAP_CONNECTING:2827case NG_BTSOCKET_L2CAP_CONFIGURING:2828case NG_BTSOCKET_L2CAP_W4_ENC_CHANGE:2829/* Send disconnect request with "zero" token */2830if (pcb->cid != 0)2831ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);28322833/* ... and close the socket */2834pcb->state = NG_BTSOCKET_L2CAP_CLOSED;2835soisdisconnected(pcb->so);2836break;28372838case NG_BTSOCKET_L2CAP_OPEN:2839/* Send timeout - drop packet and wakeup sender */2840sbdroprecord(&pcb->so->so_snd);2841sowwakeup(pcb->so);2842break;28432844case NG_BTSOCKET_L2CAP_DISCONNECTING:2845/* Disconnect timeout - disconnect the socket anyway */2846pcb->state = NG_BTSOCKET_L2CAP_CLOSED;2847soisdisconnected(pcb->so);2848break;28492850default:2851NG_BTSOCKET_L2CAP_ERR(2852"%s: Invalid socket state=%d\n", __func__, pcb->state);2853break;2854}2855} /* ng_btsocket_l2cap_process_timeout */28562857/*2858* Translate HCI/L2CAP error code into "errno" code2859* XXX Note: Some L2CAP and HCI error codes have the same value, but2860* different meaning2861*/28622863static int2864ng_btsocket_l2cap_result2errno(int result)2865{2866switch (result) {2867case 0x00: /* No error */2868return (0);28692870case 0x01: /* Unknown HCI command */2871return (ENODEV);28722873case 0x02: /* No connection */2874return (ENOTCONN);28752876case 0x03: /* Hardware failure */2877return (EIO);28782879case 0x04: /* Page timeout */2880return (EHOSTDOWN);28812882case 0x05: /* Authentication failure */2883case 0x06: /* Key missing */2884case 0x18: /* Pairing not allowed */2885case 0x21: /* Role change not allowed */2886case 0x24: /* LMP PSU not allowed */2887case 0x25: /* Encryption mode not acceptable */2888case 0x26: /* Unit key used */2889return (EACCES);28902891case 0x07: /* Memory full */2892return (ENOMEM);28932894case 0x08: /* Connection timeout */2895case 0x10: /* Host timeout */2896case 0x22: /* LMP response timeout */2897case 0xee: /* HCI timeout */2898case 0xeeee: /* L2CAP timeout */2899return (ETIMEDOUT);29002901case 0x09: /* Max number of connections */2902case 0x0a: /* Max number of SCO connections to a unit */2903return (EMLINK);29042905case 0x0b: /* ACL connection already exists */2906return (EEXIST);29072908case 0x0c: /* Command disallowed */2909return (EBUSY);29102911case 0x0d: /* Host rejected due to limited resources */2912case 0x0e: /* Host rejected due to securiity reasons */2913case 0x0f: /* Host rejected due to remote unit is a personal unit */2914case 0x1b: /* SCO offset rejected */2915case 0x1c: /* SCO interval rejected */2916case 0x1d: /* SCO air mode rejected */2917return (ECONNREFUSED);29182919case 0x11: /* Unsupported feature or parameter value */2920case 0x19: /* Unknown LMP PDU */2921case 0x1a: /* Unsupported remote feature */2922case 0x20: /* Unsupported LMP parameter value */2923case 0x27: /* QoS is not supported */2924case 0x29: /* Paring with unit key not supported */2925return (EOPNOTSUPP);29262927case 0x12: /* Invalid HCI command parameter */2928case 0x1e: /* Invalid LMP parameters */2929return (EINVAL);29302931case 0x13: /* Other end terminated connection: User ended connection */2932case 0x14: /* Other end terminated connection: Low resources */2933case 0x15: /* Other end terminated connection: About to power off */2934return (ECONNRESET);29352936case 0x16: /* Connection terminated by local host */2937return (ECONNABORTED);29382939#if 0 /* XXX not yet */2940case 0x17: /* Repeated attempts */2941case 0x1f: /* Unspecified error */2942case 0x23: /* LMP error transaction collision */2943case 0x28: /* Instant passed */2944#endif2945}29462947return (ENOSYS);2948} /* ng_btsocket_l2cap_result2errno */294929502951