148698a83SMaksim Yevmenkin /* 248698a83SMaksim Yevmenkin * ng_btsocket_sco.c 348698a83SMaksim Yevmenkin */ 448698a83SMaksim Yevmenkin 548698a83SMaksim Yevmenkin /*- 648698a83SMaksim Yevmenkin * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> 748698a83SMaksim Yevmenkin * All rights reserved. 848698a83SMaksim Yevmenkin * 948698a83SMaksim Yevmenkin * Redistribution and use in source and binary forms, with or without 1048698a83SMaksim Yevmenkin * modification, are permitted provided that the following conditions 1148698a83SMaksim Yevmenkin * are met: 1248698a83SMaksim Yevmenkin * 1. Redistributions of source code must retain the above copyright 1348698a83SMaksim Yevmenkin * notice, this list of conditions and the following disclaimer. 1448698a83SMaksim Yevmenkin * 2. Redistributions in binary form must reproduce the above copyright 1548698a83SMaksim Yevmenkin * notice, this list of conditions and the following disclaimer in the 1648698a83SMaksim Yevmenkin * documentation and/or other materials provided with the distribution. 1748698a83SMaksim Yevmenkin * 1848698a83SMaksim Yevmenkin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1948698a83SMaksim Yevmenkin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2048698a83SMaksim Yevmenkin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2148698a83SMaksim Yevmenkin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2248698a83SMaksim Yevmenkin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2348698a83SMaksim Yevmenkin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2448698a83SMaksim Yevmenkin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2548698a83SMaksim Yevmenkin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2648698a83SMaksim Yevmenkin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2748698a83SMaksim Yevmenkin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2848698a83SMaksim Yevmenkin * SUCH DAMAGE. 2948698a83SMaksim Yevmenkin * 3048698a83SMaksim Yevmenkin * $Id: ng_btsocket_sco.c,v 1.2 2005/10/31 18:08:51 max Exp $ 3148698a83SMaksim Yevmenkin * $FreeBSD$ 3248698a83SMaksim Yevmenkin */ 3348698a83SMaksim Yevmenkin 3448698a83SMaksim Yevmenkin #include <sys/param.h> 3548698a83SMaksim Yevmenkin #include <sys/systm.h> 3648698a83SMaksim Yevmenkin #include <sys/bitstring.h> 3748698a83SMaksim Yevmenkin #include <sys/domain.h> 3848698a83SMaksim Yevmenkin #include <sys/endian.h> 3948698a83SMaksim Yevmenkin #include <sys/errno.h> 4048698a83SMaksim Yevmenkin #include <sys/filedesc.h> 4148698a83SMaksim Yevmenkin #include <sys/ioccom.h> 4248698a83SMaksim Yevmenkin #include <sys/kernel.h> 4348698a83SMaksim Yevmenkin #include <sys/lock.h> 4448698a83SMaksim Yevmenkin #include <sys/malloc.h> 4548698a83SMaksim Yevmenkin #include <sys/mbuf.h> 4648698a83SMaksim Yevmenkin #include <sys/mutex.h> 4748698a83SMaksim Yevmenkin #include <sys/protosw.h> 4848698a83SMaksim Yevmenkin #include <sys/queue.h> 4948698a83SMaksim Yevmenkin #include <sys/socket.h> 5048698a83SMaksim Yevmenkin #include <sys/socketvar.h> 5148698a83SMaksim Yevmenkin #include <sys/sysctl.h> 5248698a83SMaksim Yevmenkin #include <sys/taskqueue.h> 531fb51a12SBjoern A. Zeeb 541fb51a12SBjoern A. Zeeb #include <net/vnet.h> 551fb51a12SBjoern A. Zeeb 5648698a83SMaksim Yevmenkin #include <netgraph/ng_message.h> 5748698a83SMaksim Yevmenkin #include <netgraph/netgraph.h> 5848698a83SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_bluetooth.h> 5948698a83SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_hci.h> 6048698a83SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_l2cap.h> 6148698a83SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_btsocket.h> 6248698a83SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_btsocket_sco.h> 6348698a83SMaksim Yevmenkin 6448698a83SMaksim Yevmenkin /* MALLOC define */ 6548698a83SMaksim Yevmenkin #ifdef NG_SEPARATE_MALLOC 66d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_SCO, "netgraph_btsocks_sco", 6748698a83SMaksim Yevmenkin "Netgraph Bluetooth SCO sockets"); 6848698a83SMaksim Yevmenkin #else 6948698a83SMaksim Yevmenkin #define M_NETGRAPH_BTSOCKET_SCO M_NETGRAPH 7048698a83SMaksim Yevmenkin #endif /* NG_SEPARATE_MALLOC */ 7148698a83SMaksim Yevmenkin 7248698a83SMaksim Yevmenkin /* Netgraph node methods */ 7348698a83SMaksim Yevmenkin static ng_constructor_t ng_btsocket_sco_node_constructor; 7448698a83SMaksim Yevmenkin static ng_rcvmsg_t ng_btsocket_sco_node_rcvmsg; 7548698a83SMaksim Yevmenkin static ng_shutdown_t ng_btsocket_sco_node_shutdown; 7648698a83SMaksim Yevmenkin static ng_newhook_t ng_btsocket_sco_node_newhook; 7748698a83SMaksim Yevmenkin static ng_connect_t ng_btsocket_sco_node_connect; 7848698a83SMaksim Yevmenkin static ng_rcvdata_t ng_btsocket_sco_node_rcvdata; 7948698a83SMaksim Yevmenkin static ng_disconnect_t ng_btsocket_sco_node_disconnect; 8048698a83SMaksim Yevmenkin 8148698a83SMaksim Yevmenkin static void ng_btsocket_sco_input (void *, int); 8248698a83SMaksim Yevmenkin static void ng_btsocket_sco_rtclean (void *, int); 8348698a83SMaksim Yevmenkin 8448698a83SMaksim Yevmenkin /* Netgraph type descriptor */ 8548698a83SMaksim Yevmenkin static struct ng_type typestruct = { 8648698a83SMaksim Yevmenkin .version = NG_ABI_VERSION, 8748698a83SMaksim Yevmenkin .name = NG_BTSOCKET_SCO_NODE_TYPE, 8848698a83SMaksim Yevmenkin .constructor = ng_btsocket_sco_node_constructor, 8948698a83SMaksim Yevmenkin .rcvmsg = ng_btsocket_sco_node_rcvmsg, 9048698a83SMaksim Yevmenkin .shutdown = ng_btsocket_sco_node_shutdown, 9148698a83SMaksim Yevmenkin .newhook = ng_btsocket_sco_node_newhook, 9248698a83SMaksim Yevmenkin .connect = ng_btsocket_sco_node_connect, 9348698a83SMaksim Yevmenkin .rcvdata = ng_btsocket_sco_node_rcvdata, 9448698a83SMaksim Yevmenkin .disconnect = ng_btsocket_sco_node_disconnect, 9548698a83SMaksim Yevmenkin }; 9648698a83SMaksim Yevmenkin 9748698a83SMaksim Yevmenkin /* Globals */ 9848698a83SMaksim Yevmenkin static u_int32_t ng_btsocket_sco_debug_level; 9948698a83SMaksim Yevmenkin static node_p ng_btsocket_sco_node; 10048698a83SMaksim Yevmenkin static struct ng_bt_itemq ng_btsocket_sco_queue; 10148698a83SMaksim Yevmenkin static struct mtx ng_btsocket_sco_queue_mtx; 10248698a83SMaksim Yevmenkin static struct task ng_btsocket_sco_queue_task; 10348698a83SMaksim Yevmenkin static struct mtx ng_btsocket_sco_sockets_mtx; 10448698a83SMaksim Yevmenkin static LIST_HEAD(, ng_btsocket_sco_pcb) ng_btsocket_sco_sockets; 10548698a83SMaksim Yevmenkin static LIST_HEAD(, ng_btsocket_sco_rtentry) ng_btsocket_sco_rt; 10648698a83SMaksim Yevmenkin static struct mtx ng_btsocket_sco_rt_mtx; 10748698a83SMaksim Yevmenkin static struct task ng_btsocket_sco_rt_task; 1084fa708efSMaksim Yevmenkin static struct timeval ng_btsocket_sco_lasttime; 1094fa708efSMaksim Yevmenkin static int ng_btsocket_sco_curpps; 11048698a83SMaksim Yevmenkin 11148698a83SMaksim Yevmenkin /* Sysctl tree */ 11248698a83SMaksim Yevmenkin SYSCTL_DECL(_net_bluetooth_sco_sockets); 1136472ac3dSEd Schouten static SYSCTL_NODE(_net_bluetooth_sco_sockets, OID_AUTO, seq, CTLFLAG_RW, 11448698a83SMaksim Yevmenkin 0, "Bluetooth SEQPACKET SCO sockets family"); 115f29fc085SMatthew D Fleming SYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, debug_level, 11648698a83SMaksim Yevmenkin CTLFLAG_RW, 11748698a83SMaksim Yevmenkin &ng_btsocket_sco_debug_level, NG_BTSOCKET_WARN_LEVEL, 11848698a83SMaksim Yevmenkin "Bluetooth SEQPACKET SCO sockets debug level"); 119f29fc085SMatthew D Fleming SYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, queue_len, 12048698a83SMaksim Yevmenkin CTLFLAG_RD, 12148698a83SMaksim Yevmenkin &ng_btsocket_sco_queue.len, 0, 12248698a83SMaksim Yevmenkin "Bluetooth SEQPACKET SCO sockets input queue length"); 123f29fc085SMatthew D Fleming SYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, queue_maxlen, 12448698a83SMaksim Yevmenkin CTLFLAG_RD, 12548698a83SMaksim Yevmenkin &ng_btsocket_sco_queue.maxlen, 0, 12648698a83SMaksim Yevmenkin "Bluetooth SEQPACKET SCO sockets input queue max. length"); 127f29fc085SMatthew D Fleming SYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, queue_drops, 12848698a83SMaksim Yevmenkin CTLFLAG_RD, 12948698a83SMaksim Yevmenkin &ng_btsocket_sco_queue.drops, 0, 13048698a83SMaksim Yevmenkin "Bluetooth SEQPACKET SCO sockets input queue drops"); 13148698a83SMaksim Yevmenkin 13248698a83SMaksim Yevmenkin /* Debug */ 13348698a83SMaksim Yevmenkin #define NG_BTSOCKET_SCO_INFO \ 1344fa708efSMaksim Yevmenkin if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_INFO_LEVEL && \ 1354fa708efSMaksim Yevmenkin ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \ 13648698a83SMaksim Yevmenkin printf 13748698a83SMaksim Yevmenkin 13848698a83SMaksim Yevmenkin #define NG_BTSOCKET_SCO_WARN \ 1394fa708efSMaksim Yevmenkin if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_WARN_LEVEL && \ 1404fa708efSMaksim Yevmenkin ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \ 14148698a83SMaksim Yevmenkin printf 14248698a83SMaksim Yevmenkin 14348698a83SMaksim Yevmenkin #define NG_BTSOCKET_SCO_ERR \ 1444fa708efSMaksim Yevmenkin if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_ERR_LEVEL && \ 1454fa708efSMaksim Yevmenkin ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \ 14648698a83SMaksim Yevmenkin printf 14748698a83SMaksim Yevmenkin 14848698a83SMaksim Yevmenkin #define NG_BTSOCKET_SCO_ALERT \ 1494fa708efSMaksim Yevmenkin if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_ALERT_LEVEL && \ 1504fa708efSMaksim Yevmenkin ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \ 15148698a83SMaksim Yevmenkin printf 15248698a83SMaksim Yevmenkin 15348698a83SMaksim Yevmenkin /* 15448698a83SMaksim Yevmenkin * Netgraph message processing routines 15548698a83SMaksim Yevmenkin */ 15648698a83SMaksim Yevmenkin 15748698a83SMaksim Yevmenkin static int ng_btsocket_sco_process_lp_con_cfm 15848698a83SMaksim Yevmenkin (struct ng_mesg *, ng_btsocket_sco_rtentry_p); 15948698a83SMaksim Yevmenkin static int ng_btsocket_sco_process_lp_con_ind 16048698a83SMaksim Yevmenkin (struct ng_mesg *, ng_btsocket_sco_rtentry_p); 16148698a83SMaksim Yevmenkin static int ng_btsocket_sco_process_lp_discon_ind 16248698a83SMaksim Yevmenkin (struct ng_mesg *, ng_btsocket_sco_rtentry_p); 16348698a83SMaksim Yevmenkin 16448698a83SMaksim Yevmenkin /* 16548698a83SMaksim Yevmenkin * Send LP messages to the lower layer 16648698a83SMaksim Yevmenkin */ 16748698a83SMaksim Yevmenkin 16848698a83SMaksim Yevmenkin static int ng_btsocket_sco_send_lp_con_req 16948698a83SMaksim Yevmenkin (ng_btsocket_sco_pcb_p); 17048698a83SMaksim Yevmenkin static int ng_btsocket_sco_send_lp_con_rsp 17148698a83SMaksim Yevmenkin (ng_btsocket_sco_rtentry_p, bdaddr_p, int); 17248698a83SMaksim Yevmenkin static int ng_btsocket_sco_send_lp_discon_req 17348698a83SMaksim Yevmenkin (ng_btsocket_sco_pcb_p); 17448698a83SMaksim Yevmenkin 17548698a83SMaksim Yevmenkin static int ng_btsocket_sco_send2 17648698a83SMaksim Yevmenkin (ng_btsocket_sco_pcb_p); 17748698a83SMaksim Yevmenkin 17848698a83SMaksim Yevmenkin /* 17948698a83SMaksim Yevmenkin * Timeout processing routines 18048698a83SMaksim Yevmenkin */ 18148698a83SMaksim Yevmenkin 18248698a83SMaksim Yevmenkin static void ng_btsocket_sco_timeout (ng_btsocket_sco_pcb_p); 18348698a83SMaksim Yevmenkin static void ng_btsocket_sco_untimeout (ng_btsocket_sco_pcb_p); 18448698a83SMaksim Yevmenkin static void ng_btsocket_sco_process_timeout (void *); 18548698a83SMaksim Yevmenkin 18648698a83SMaksim Yevmenkin /* 18748698a83SMaksim Yevmenkin * Other stuff 18848698a83SMaksim Yevmenkin */ 18948698a83SMaksim Yevmenkin 19048698a83SMaksim Yevmenkin static ng_btsocket_sco_pcb_p ng_btsocket_sco_pcb_by_addr(bdaddr_p); 19148698a83SMaksim Yevmenkin static ng_btsocket_sco_pcb_p ng_btsocket_sco_pcb_by_handle(bdaddr_p, int); 19248698a83SMaksim Yevmenkin static ng_btsocket_sco_pcb_p ng_btsocket_sco_pcb_by_addrs(bdaddr_p, bdaddr_p); 19348698a83SMaksim Yevmenkin 19448698a83SMaksim Yevmenkin #define ng_btsocket_sco_wakeup_input_task() \ 19548698a83SMaksim Yevmenkin taskqueue_enqueue(taskqueue_swi, &ng_btsocket_sco_queue_task) 19648698a83SMaksim Yevmenkin 19748698a83SMaksim Yevmenkin #define ng_btsocket_sco_wakeup_route_task() \ 19848698a83SMaksim Yevmenkin taskqueue_enqueue(taskqueue_swi, &ng_btsocket_sco_rt_task) 19948698a83SMaksim Yevmenkin 20048698a83SMaksim Yevmenkin /***************************************************************************** 20148698a83SMaksim Yevmenkin ***************************************************************************** 20248698a83SMaksim Yevmenkin ** Netgraph node interface 20348698a83SMaksim Yevmenkin ***************************************************************************** 20448698a83SMaksim Yevmenkin *****************************************************************************/ 20548698a83SMaksim Yevmenkin 20648698a83SMaksim Yevmenkin /* 20748698a83SMaksim Yevmenkin * Netgraph node constructor. Do not allow to create node of this type. 20848698a83SMaksim Yevmenkin */ 20948698a83SMaksim Yevmenkin 21048698a83SMaksim Yevmenkin static int 21148698a83SMaksim Yevmenkin ng_btsocket_sco_node_constructor(node_p node) 21248698a83SMaksim Yevmenkin { 21348698a83SMaksim Yevmenkin return (EINVAL); 21448698a83SMaksim Yevmenkin } /* ng_btsocket_sco_node_constructor */ 21548698a83SMaksim Yevmenkin 21648698a83SMaksim Yevmenkin /* 21748698a83SMaksim Yevmenkin * Do local shutdown processing. Let old node go and create new fresh one. 21848698a83SMaksim Yevmenkin */ 21948698a83SMaksim Yevmenkin 22048698a83SMaksim Yevmenkin static int 22148698a83SMaksim Yevmenkin ng_btsocket_sco_node_shutdown(node_p node) 22248698a83SMaksim Yevmenkin { 22348698a83SMaksim Yevmenkin int error = 0; 22448698a83SMaksim Yevmenkin 22548698a83SMaksim Yevmenkin NG_NODE_UNREF(node); 22648698a83SMaksim Yevmenkin 22748698a83SMaksim Yevmenkin /* Create new node */ 22848698a83SMaksim Yevmenkin error = ng_make_node_common(&typestruct, &ng_btsocket_sco_node); 22948698a83SMaksim Yevmenkin if (error != 0) { 23048698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ALERT( 23148698a83SMaksim Yevmenkin "%s: Could not create Netgraph node, error=%d\n", __func__, error); 23248698a83SMaksim Yevmenkin 23348698a83SMaksim Yevmenkin ng_btsocket_sco_node = NULL; 23448698a83SMaksim Yevmenkin 23548698a83SMaksim Yevmenkin return (error); 23648698a83SMaksim Yevmenkin } 23748698a83SMaksim Yevmenkin 23848698a83SMaksim Yevmenkin error = ng_name_node(ng_btsocket_sco_node, 23948698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_NODE_TYPE); 24048698a83SMaksim Yevmenkin if (error != 0) { 24148698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ALERT( 24248698a83SMaksim Yevmenkin "%s: Could not name Netgraph node, error=%d\n", __func__, error); 24348698a83SMaksim Yevmenkin 24448698a83SMaksim Yevmenkin NG_NODE_UNREF(ng_btsocket_sco_node); 24548698a83SMaksim Yevmenkin ng_btsocket_sco_node = NULL; 24648698a83SMaksim Yevmenkin 24748698a83SMaksim Yevmenkin return (error); 24848698a83SMaksim Yevmenkin } 24948698a83SMaksim Yevmenkin 25048698a83SMaksim Yevmenkin return (0); 25148698a83SMaksim Yevmenkin } /* ng_btsocket_sco_node_shutdown */ 25248698a83SMaksim Yevmenkin 25348698a83SMaksim Yevmenkin /* 25448698a83SMaksim Yevmenkin * We allow any hook to be connected to the node. 25548698a83SMaksim Yevmenkin */ 25648698a83SMaksim Yevmenkin 25748698a83SMaksim Yevmenkin static int 25848698a83SMaksim Yevmenkin ng_btsocket_sco_node_newhook(node_p node, hook_p hook, char const *name) 25948698a83SMaksim Yevmenkin { 26048698a83SMaksim Yevmenkin return (0); 26148698a83SMaksim Yevmenkin } /* ng_btsocket_sco_node_newhook */ 26248698a83SMaksim Yevmenkin 26348698a83SMaksim Yevmenkin /* 26448698a83SMaksim Yevmenkin * Just say "YEP, that's OK by me!" 26548698a83SMaksim Yevmenkin */ 26648698a83SMaksim Yevmenkin 26748698a83SMaksim Yevmenkin static int 26848698a83SMaksim Yevmenkin ng_btsocket_sco_node_connect(hook_p hook) 26948698a83SMaksim Yevmenkin { 27048698a83SMaksim Yevmenkin NG_HOOK_SET_PRIVATE(hook, NULL); 27148698a83SMaksim Yevmenkin NG_HOOK_REF(hook); /* Keep extra reference to the hook */ 27248698a83SMaksim Yevmenkin 27348698a83SMaksim Yevmenkin #if 0 27448698a83SMaksim Yevmenkin NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 27548698a83SMaksim Yevmenkin NG_HOOK_FORCE_QUEUE(hook); 27648698a83SMaksim Yevmenkin #endif 27748698a83SMaksim Yevmenkin 27848698a83SMaksim Yevmenkin return (0); 27948698a83SMaksim Yevmenkin } /* ng_btsocket_sco_node_connect */ 28048698a83SMaksim Yevmenkin 28148698a83SMaksim Yevmenkin /* 28248698a83SMaksim Yevmenkin * Hook disconnection. Schedule route cleanup task 28348698a83SMaksim Yevmenkin */ 28448698a83SMaksim Yevmenkin 28548698a83SMaksim Yevmenkin static int 28648698a83SMaksim Yevmenkin ng_btsocket_sco_node_disconnect(hook_p hook) 28748698a83SMaksim Yevmenkin { 28848698a83SMaksim Yevmenkin /* 28948698a83SMaksim Yevmenkin * If hook has private information than we must have this hook in 29048698a83SMaksim Yevmenkin * the routing table and must schedule cleaning for the routing table. 29148698a83SMaksim Yevmenkin * Otherwise hook was connected but we never got "hook_info" message, 29248698a83SMaksim Yevmenkin * so we have never added this hook to the routing table and it save 29348698a83SMaksim Yevmenkin * to just delete it. 29448698a83SMaksim Yevmenkin */ 29548698a83SMaksim Yevmenkin 29648698a83SMaksim Yevmenkin if (NG_HOOK_PRIVATE(hook) != NULL) 29748698a83SMaksim Yevmenkin return (ng_btsocket_sco_wakeup_route_task()); 29848698a83SMaksim Yevmenkin 29948698a83SMaksim Yevmenkin NG_HOOK_UNREF(hook); /* Remove extra reference */ 30048698a83SMaksim Yevmenkin 30148698a83SMaksim Yevmenkin return (0); 30248698a83SMaksim Yevmenkin } /* ng_btsocket_sco_node_disconnect */ 30348698a83SMaksim Yevmenkin 30448698a83SMaksim Yevmenkin /* 30548698a83SMaksim Yevmenkin * Process incoming messages 30648698a83SMaksim Yevmenkin */ 30748698a83SMaksim Yevmenkin 30848698a83SMaksim Yevmenkin static int 30948698a83SMaksim Yevmenkin ng_btsocket_sco_node_rcvmsg(node_p node, item_p item, hook_p hook) 31048698a83SMaksim Yevmenkin { 31148698a83SMaksim Yevmenkin struct ng_mesg *msg = NGI_MSG(item); /* item still has message */ 31248698a83SMaksim Yevmenkin int error = 0; 31348698a83SMaksim Yevmenkin 31448698a83SMaksim Yevmenkin if (msg != NULL && msg->header.typecookie == NGM_HCI_COOKIE) { 31548698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_queue_mtx); 31648698a83SMaksim Yevmenkin if (NG_BT_ITEMQ_FULL(&ng_btsocket_sco_queue)) { 31748698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ERR( 31848698a83SMaksim Yevmenkin "%s: Input queue is full (msg)\n", __func__); 31948698a83SMaksim Yevmenkin 32048698a83SMaksim Yevmenkin NG_BT_ITEMQ_DROP(&ng_btsocket_sco_queue); 32148698a83SMaksim Yevmenkin NG_FREE_ITEM(item); 32248698a83SMaksim Yevmenkin error = ENOBUFS; 32348698a83SMaksim Yevmenkin } else { 32448698a83SMaksim Yevmenkin if (hook != NULL) { 32548698a83SMaksim Yevmenkin NG_HOOK_REF(hook); 32648698a83SMaksim Yevmenkin NGI_SET_HOOK(item, hook); 32748698a83SMaksim Yevmenkin } 32848698a83SMaksim Yevmenkin 32948698a83SMaksim Yevmenkin NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_sco_queue, item); 33048698a83SMaksim Yevmenkin error = ng_btsocket_sco_wakeup_input_task(); 33148698a83SMaksim Yevmenkin } 33248698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_queue_mtx); 33348698a83SMaksim Yevmenkin } else { 33448698a83SMaksim Yevmenkin NG_FREE_ITEM(item); 33548698a83SMaksim Yevmenkin error = EINVAL; 33648698a83SMaksim Yevmenkin } 33748698a83SMaksim Yevmenkin 33848698a83SMaksim Yevmenkin return (error); 33948698a83SMaksim Yevmenkin } /* ng_btsocket_sco_node_rcvmsg */ 34048698a83SMaksim Yevmenkin 34148698a83SMaksim Yevmenkin /* 34248698a83SMaksim Yevmenkin * Receive data on a hook 34348698a83SMaksim Yevmenkin */ 34448698a83SMaksim Yevmenkin 34548698a83SMaksim Yevmenkin static int 34648698a83SMaksim Yevmenkin ng_btsocket_sco_node_rcvdata(hook_p hook, item_p item) 34748698a83SMaksim Yevmenkin { 34848698a83SMaksim Yevmenkin int error = 0; 34948698a83SMaksim Yevmenkin 35048698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_queue_mtx); 35148698a83SMaksim Yevmenkin if (NG_BT_ITEMQ_FULL(&ng_btsocket_sco_queue)) { 35248698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ERR( 35348698a83SMaksim Yevmenkin "%s: Input queue is full (data)\n", __func__); 35448698a83SMaksim Yevmenkin 35548698a83SMaksim Yevmenkin NG_BT_ITEMQ_DROP(&ng_btsocket_sco_queue); 35648698a83SMaksim Yevmenkin NG_FREE_ITEM(item); 35748698a83SMaksim Yevmenkin error = ENOBUFS; 35848698a83SMaksim Yevmenkin } else { 35948698a83SMaksim Yevmenkin NG_HOOK_REF(hook); 36048698a83SMaksim Yevmenkin NGI_SET_HOOK(item, hook); 36148698a83SMaksim Yevmenkin 36248698a83SMaksim Yevmenkin NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_sco_queue, item); 36348698a83SMaksim Yevmenkin error = ng_btsocket_sco_wakeup_input_task(); 36448698a83SMaksim Yevmenkin } 36548698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_queue_mtx); 36648698a83SMaksim Yevmenkin 36748698a83SMaksim Yevmenkin return (error); 36848698a83SMaksim Yevmenkin } /* ng_btsocket_sco_node_rcvdata */ 36948698a83SMaksim Yevmenkin 37048698a83SMaksim Yevmenkin /* 37148698a83SMaksim Yevmenkin * Process LP_ConnectCfm event from the lower layer protocol 37248698a83SMaksim Yevmenkin */ 37348698a83SMaksim Yevmenkin 37448698a83SMaksim Yevmenkin static int 37548698a83SMaksim Yevmenkin ng_btsocket_sco_process_lp_con_cfm(struct ng_mesg *msg, 37648698a83SMaksim Yevmenkin ng_btsocket_sco_rtentry_p rt) 37748698a83SMaksim Yevmenkin { 37848698a83SMaksim Yevmenkin ng_hci_lp_con_cfm_ep *ep = NULL; 37948698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_t *pcb = NULL; 38048698a83SMaksim Yevmenkin int error = 0; 38148698a83SMaksim Yevmenkin 38248698a83SMaksim Yevmenkin if (msg->header.arglen != sizeof(*ep)) 38348698a83SMaksim Yevmenkin return (EMSGSIZE); 38448698a83SMaksim Yevmenkin 38548698a83SMaksim Yevmenkin ep = (ng_hci_lp_con_cfm_ep *)(msg->data); 38648698a83SMaksim Yevmenkin 38748698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_sockets_mtx); 38848698a83SMaksim Yevmenkin 38948698a83SMaksim Yevmenkin /* Look for the socket with the token */ 39048698a83SMaksim Yevmenkin pcb = ng_btsocket_sco_pcb_by_addrs(&rt->src, &ep->bdaddr); 39148698a83SMaksim Yevmenkin if (pcb == NULL) { 39248698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 39348698a83SMaksim Yevmenkin return (ENOENT); 39448698a83SMaksim Yevmenkin } 39548698a83SMaksim Yevmenkin 39648698a83SMaksim Yevmenkin /* pcb is locked */ 39748698a83SMaksim Yevmenkin 39848698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_INFO( 39948698a83SMaksim Yevmenkin "%s: Got LP_ConnectCfm response, src bdaddr=%x:%x:%x:%x:%x:%x, " \ 40048698a83SMaksim Yevmenkin "dst bdaddr=%x:%x:%x:%x:%x:%x, status=%d, handle=%d, state=%d\n", 40148698a83SMaksim Yevmenkin __func__, 40248698a83SMaksim Yevmenkin pcb->src.b[5], pcb->src.b[4], pcb->src.b[3], 40348698a83SMaksim Yevmenkin pcb->src.b[2], pcb->src.b[1], pcb->src.b[0], 40448698a83SMaksim Yevmenkin pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3], 40548698a83SMaksim Yevmenkin pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0], 40648698a83SMaksim Yevmenkin ep->status, ep->con_handle, pcb->state); 40748698a83SMaksim Yevmenkin 40848698a83SMaksim Yevmenkin if (pcb->state != NG_BTSOCKET_SCO_CONNECTING) { 40948698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 41048698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 41148698a83SMaksim Yevmenkin 41248698a83SMaksim Yevmenkin return (ENOENT); 41348698a83SMaksim Yevmenkin } 41448698a83SMaksim Yevmenkin 41548698a83SMaksim Yevmenkin ng_btsocket_sco_untimeout(pcb); 41648698a83SMaksim Yevmenkin 41748698a83SMaksim Yevmenkin if (ep->status == 0) { 41848698a83SMaksim Yevmenkin /* 41948698a83SMaksim Yevmenkin * Connection is open. Update connection handle and 42048698a83SMaksim Yevmenkin * socket state 42148698a83SMaksim Yevmenkin */ 42248698a83SMaksim Yevmenkin 42348698a83SMaksim Yevmenkin pcb->con_handle = ep->con_handle; 42448698a83SMaksim Yevmenkin pcb->state = NG_BTSOCKET_SCO_OPEN; 42548698a83SMaksim Yevmenkin soisconnected(pcb->so); 42648698a83SMaksim Yevmenkin } else { 42748698a83SMaksim Yevmenkin /* 42848698a83SMaksim Yevmenkin * We have failed to open connection, so disconnect the socket 42948698a83SMaksim Yevmenkin */ 43048698a83SMaksim Yevmenkin 43148698a83SMaksim Yevmenkin pcb->so->so_error = ECONNREFUSED; /* XXX convert status ??? */ 43248698a83SMaksim Yevmenkin pcb->state = NG_BTSOCKET_SCO_CLOSED; 43348698a83SMaksim Yevmenkin soisdisconnected(pcb->so); 43448698a83SMaksim Yevmenkin } 43548698a83SMaksim Yevmenkin 43648698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 43748698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 43848698a83SMaksim Yevmenkin 43948698a83SMaksim Yevmenkin return (error); 44048698a83SMaksim Yevmenkin } /* ng_btsocket_sco_process_lp_con_cfm */ 44148698a83SMaksim Yevmenkin 44248698a83SMaksim Yevmenkin /* 44348698a83SMaksim Yevmenkin * Process LP_ConnectInd indicator. Find socket that listens on address. 44448698a83SMaksim Yevmenkin * Find exact or closest match. 44548698a83SMaksim Yevmenkin */ 44648698a83SMaksim Yevmenkin 44748698a83SMaksim Yevmenkin static int 44848698a83SMaksim Yevmenkin ng_btsocket_sco_process_lp_con_ind(struct ng_mesg *msg, 44948698a83SMaksim Yevmenkin ng_btsocket_sco_rtentry_p rt) 45048698a83SMaksim Yevmenkin { 45148698a83SMaksim Yevmenkin ng_hci_lp_con_ind_ep *ep = NULL; 45248698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_t *pcb = NULL, *pcb1 = NULL; 45348698a83SMaksim Yevmenkin int error = 0; 45448698a83SMaksim Yevmenkin u_int16_t status = 0; 45548698a83SMaksim Yevmenkin 45648698a83SMaksim Yevmenkin if (msg->header.arglen != sizeof(*ep)) 45748698a83SMaksim Yevmenkin return (EMSGSIZE); 45848698a83SMaksim Yevmenkin 45948698a83SMaksim Yevmenkin ep = (ng_hci_lp_con_ind_ep *)(msg->data); 46048698a83SMaksim Yevmenkin 46148698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_INFO( 46248698a83SMaksim Yevmenkin "%s: Got LP_ConnectInd indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \ 46348698a83SMaksim Yevmenkin "dst bdaddr=%x:%x:%x:%x:%x:%x\n", 46448698a83SMaksim Yevmenkin __func__, 46548698a83SMaksim Yevmenkin rt->src.b[5], rt->src.b[4], rt->src.b[3], 46648698a83SMaksim Yevmenkin rt->src.b[2], rt->src.b[1], rt->src.b[0], 46748698a83SMaksim Yevmenkin ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], 46848698a83SMaksim Yevmenkin ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); 46948698a83SMaksim Yevmenkin 47048698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_sockets_mtx); 47148698a83SMaksim Yevmenkin 47248698a83SMaksim Yevmenkin pcb = ng_btsocket_sco_pcb_by_addr(&rt->src); 47348698a83SMaksim Yevmenkin if (pcb != NULL) { 47448698a83SMaksim Yevmenkin struct socket *so1 = NULL; 47548698a83SMaksim Yevmenkin 47648698a83SMaksim Yevmenkin /* pcb is locked */ 47748698a83SMaksim Yevmenkin 47848698a83SMaksim Yevmenkin /* 47948698a83SMaksim Yevmenkin * First check the pending connections queue and if we have 48048698a83SMaksim Yevmenkin * space then create new socket and set proper source address. 48148698a83SMaksim Yevmenkin */ 48248698a83SMaksim Yevmenkin 4831fb51a12SBjoern A. Zeeb if (pcb->so->so_qlen <= pcb->so->so_qlimit) { 4841fb51a12SBjoern A. Zeeb CURVNET_SET(pcb->so->so_vnet); 48548698a83SMaksim Yevmenkin so1 = sonewconn(pcb->so, 0); 4861fb51a12SBjoern A. Zeeb CURVNET_RESTORE(); 4871fb51a12SBjoern A. Zeeb } 48848698a83SMaksim Yevmenkin 48948698a83SMaksim Yevmenkin if (so1 == NULL) { 49048698a83SMaksim Yevmenkin status = 0x0d; /* Rejected due to limited resources */ 49148698a83SMaksim Yevmenkin goto respond; 49248698a83SMaksim Yevmenkin } 49348698a83SMaksim Yevmenkin 49448698a83SMaksim Yevmenkin /* 49548698a83SMaksim Yevmenkin * If we got here than we have created new socket. So complete 49648698a83SMaksim Yevmenkin * connection. If we we listening on specific address then copy 49748698a83SMaksim Yevmenkin * source address from listening socket, otherwise copy source 49848698a83SMaksim Yevmenkin * address from hook's routing information. 49948698a83SMaksim Yevmenkin */ 50048698a83SMaksim Yevmenkin 50148698a83SMaksim Yevmenkin pcb1 = so2sco_pcb(so1); 50248698a83SMaksim Yevmenkin KASSERT((pcb1 != NULL), 50348698a83SMaksim Yevmenkin ("%s: pcb1 == NULL\n", __func__)); 50448698a83SMaksim Yevmenkin 50548698a83SMaksim Yevmenkin mtx_lock(&pcb1->pcb_mtx); 50648698a83SMaksim Yevmenkin 50748698a83SMaksim Yevmenkin if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src)) != 0) 50848698a83SMaksim Yevmenkin bcopy(&pcb->src, &pcb1->src, sizeof(pcb1->src)); 50948698a83SMaksim Yevmenkin else 51048698a83SMaksim Yevmenkin bcopy(&rt->src, &pcb1->src, sizeof(pcb1->src)); 51148698a83SMaksim Yevmenkin 51248698a83SMaksim Yevmenkin pcb1->flags &= ~NG_BTSOCKET_SCO_CLIENT; 51348698a83SMaksim Yevmenkin 51448698a83SMaksim Yevmenkin bcopy(&ep->bdaddr, &pcb1->dst, sizeof(pcb1->dst)); 51548698a83SMaksim Yevmenkin pcb1->rt = rt; 51648698a83SMaksim Yevmenkin } else 51748698a83SMaksim Yevmenkin /* Nobody listens on requested BDADDR */ 51848698a83SMaksim Yevmenkin status = 0x1f; /* Unspecified Error */ 51948698a83SMaksim Yevmenkin 52048698a83SMaksim Yevmenkin respond: 52148698a83SMaksim Yevmenkin error = ng_btsocket_sco_send_lp_con_rsp(rt, &ep->bdaddr, status); 52248698a83SMaksim Yevmenkin if (pcb1 != NULL) { 52348698a83SMaksim Yevmenkin if (error != 0) { 52448698a83SMaksim Yevmenkin pcb1->so->so_error = error; 52548698a83SMaksim Yevmenkin pcb1->state = NG_BTSOCKET_SCO_CLOSED; 52648698a83SMaksim Yevmenkin soisdisconnected(pcb1->so); 52748698a83SMaksim Yevmenkin } else { 52848698a83SMaksim Yevmenkin pcb1->state = NG_BTSOCKET_SCO_CONNECTING; 52948698a83SMaksim Yevmenkin soisconnecting(pcb1->so); 53048698a83SMaksim Yevmenkin 53148698a83SMaksim Yevmenkin ng_btsocket_sco_timeout(pcb1); 53248698a83SMaksim Yevmenkin } 53348698a83SMaksim Yevmenkin 53448698a83SMaksim Yevmenkin mtx_unlock(&pcb1->pcb_mtx); 53548698a83SMaksim Yevmenkin } 53648698a83SMaksim Yevmenkin 53748698a83SMaksim Yevmenkin if (pcb != NULL) 53848698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 53948698a83SMaksim Yevmenkin 54048698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 54148698a83SMaksim Yevmenkin 54248698a83SMaksim Yevmenkin return (error); 54348698a83SMaksim Yevmenkin } /* ng_btsocket_sco_process_lp_con_ind */ 54448698a83SMaksim Yevmenkin 54548698a83SMaksim Yevmenkin /* 54648698a83SMaksim Yevmenkin * Process LP_DisconnectInd indicator 54748698a83SMaksim Yevmenkin */ 54848698a83SMaksim Yevmenkin 54948698a83SMaksim Yevmenkin static int 55048698a83SMaksim Yevmenkin ng_btsocket_sco_process_lp_discon_ind(struct ng_mesg *msg, 55148698a83SMaksim Yevmenkin ng_btsocket_sco_rtentry_p rt) 55248698a83SMaksim Yevmenkin { 55348698a83SMaksim Yevmenkin ng_hci_lp_discon_ind_ep *ep = NULL; 55448698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_t *pcb = NULL; 55548698a83SMaksim Yevmenkin 55648698a83SMaksim Yevmenkin /* Check message */ 55748698a83SMaksim Yevmenkin if (msg->header.arglen != sizeof(*ep)) 55848698a83SMaksim Yevmenkin return (EMSGSIZE); 55948698a83SMaksim Yevmenkin 56048698a83SMaksim Yevmenkin ep = (ng_hci_lp_discon_ind_ep *)(msg->data); 56148698a83SMaksim Yevmenkin 56248698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_sockets_mtx); 56348698a83SMaksim Yevmenkin 56448698a83SMaksim Yevmenkin /* Look for the socket with given channel ID */ 56548698a83SMaksim Yevmenkin pcb = ng_btsocket_sco_pcb_by_handle(&rt->src, ep->con_handle); 56648698a83SMaksim Yevmenkin if (pcb == NULL) { 56748698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 56848698a83SMaksim Yevmenkin return (0); 56948698a83SMaksim Yevmenkin } 57048698a83SMaksim Yevmenkin 57148698a83SMaksim Yevmenkin /* 57248698a83SMaksim Yevmenkin * Disconnect the socket. If there was any pending request we can 57348698a83SMaksim Yevmenkin * not do anything here anyway. 57448698a83SMaksim Yevmenkin */ 57548698a83SMaksim Yevmenkin 57648698a83SMaksim Yevmenkin /* pcb is locked */ 57748698a83SMaksim Yevmenkin 57848698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_INFO( 57948698a83SMaksim Yevmenkin "%s: Got LP_DisconnectInd indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \ 58048698a83SMaksim Yevmenkin "dst bdaddr=%x:%x:%x:%x:%x:%x, handle=%d, state=%d\n", 58148698a83SMaksim Yevmenkin __func__, 58248698a83SMaksim Yevmenkin pcb->src.b[5], pcb->src.b[4], pcb->src.b[3], 58348698a83SMaksim Yevmenkin pcb->src.b[2], pcb->src.b[1], pcb->src.b[0], 58448698a83SMaksim Yevmenkin pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3], 58548698a83SMaksim Yevmenkin pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0], 58648698a83SMaksim Yevmenkin pcb->con_handle, pcb->state); 58748698a83SMaksim Yevmenkin 58848698a83SMaksim Yevmenkin if (pcb->flags & NG_BTSOCKET_SCO_TIMO) 58948698a83SMaksim Yevmenkin ng_btsocket_sco_untimeout(pcb); 59048698a83SMaksim Yevmenkin 59148698a83SMaksim Yevmenkin pcb->state = NG_BTSOCKET_SCO_CLOSED; 59248698a83SMaksim Yevmenkin soisdisconnected(pcb->so); 59348698a83SMaksim Yevmenkin 59448698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 59548698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 59648698a83SMaksim Yevmenkin 59748698a83SMaksim Yevmenkin return (0); 59848698a83SMaksim Yevmenkin } /* ng_btsocket_sco_process_lp_discon_ind */ 59948698a83SMaksim Yevmenkin 60048698a83SMaksim Yevmenkin /* 60148698a83SMaksim Yevmenkin * Send LP_ConnectReq request 60248698a83SMaksim Yevmenkin */ 60348698a83SMaksim Yevmenkin 60448698a83SMaksim Yevmenkin static int 60548698a83SMaksim Yevmenkin ng_btsocket_sco_send_lp_con_req(ng_btsocket_sco_pcb_p pcb) 60648698a83SMaksim Yevmenkin { 60748698a83SMaksim Yevmenkin struct ng_mesg *msg = NULL; 60848698a83SMaksim Yevmenkin ng_hci_lp_con_req_ep *ep = NULL; 60948698a83SMaksim Yevmenkin int error = 0; 61048698a83SMaksim Yevmenkin 61148698a83SMaksim Yevmenkin mtx_assert(&pcb->pcb_mtx, MA_OWNED); 61248698a83SMaksim Yevmenkin 61348698a83SMaksim Yevmenkin if (pcb->rt == NULL || 61448698a83SMaksim Yevmenkin pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) 61548698a83SMaksim Yevmenkin return (ENETDOWN); 61648698a83SMaksim Yevmenkin 61748698a83SMaksim Yevmenkin NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_REQ, 61848698a83SMaksim Yevmenkin sizeof(*ep), M_NOWAIT); 61948698a83SMaksim Yevmenkin if (msg == NULL) 62048698a83SMaksim Yevmenkin return (ENOMEM); 62148698a83SMaksim Yevmenkin 62248698a83SMaksim Yevmenkin ep = (ng_hci_lp_con_req_ep *)(msg->data); 62348698a83SMaksim Yevmenkin ep->link_type = NG_HCI_LINK_SCO; 62448698a83SMaksim Yevmenkin bcopy(&pcb->dst, &ep->bdaddr, sizeof(ep->bdaddr)); 62548698a83SMaksim Yevmenkin 62648698a83SMaksim Yevmenkin NG_SEND_MSG_HOOK(error, ng_btsocket_sco_node, msg, pcb->rt->hook, 0); 62748698a83SMaksim Yevmenkin 62848698a83SMaksim Yevmenkin return (error); 62948698a83SMaksim Yevmenkin } /* ng_btsocket_sco_send_lp_con_req */ 63048698a83SMaksim Yevmenkin 63148698a83SMaksim Yevmenkin /* 63248698a83SMaksim Yevmenkin * Send LP_ConnectRsp response 63348698a83SMaksim Yevmenkin */ 63448698a83SMaksim Yevmenkin 63548698a83SMaksim Yevmenkin static int 63648698a83SMaksim Yevmenkin ng_btsocket_sco_send_lp_con_rsp(ng_btsocket_sco_rtentry_p rt, bdaddr_p dst, int status) 63748698a83SMaksim Yevmenkin { 63848698a83SMaksim Yevmenkin struct ng_mesg *msg = NULL; 63948698a83SMaksim Yevmenkin ng_hci_lp_con_rsp_ep *ep = NULL; 64048698a83SMaksim Yevmenkin int error = 0; 64148698a83SMaksim Yevmenkin 64248698a83SMaksim Yevmenkin if (rt == NULL || rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook)) 64348698a83SMaksim Yevmenkin return (ENETDOWN); 64448698a83SMaksim Yevmenkin 64548698a83SMaksim Yevmenkin NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP, 64648698a83SMaksim Yevmenkin sizeof(*ep), M_NOWAIT); 64748698a83SMaksim Yevmenkin if (msg == NULL) 64848698a83SMaksim Yevmenkin return (ENOMEM); 64948698a83SMaksim Yevmenkin 65048698a83SMaksim Yevmenkin ep = (ng_hci_lp_con_rsp_ep *)(msg->data); 65148698a83SMaksim Yevmenkin ep->status = status; 65248698a83SMaksim Yevmenkin ep->link_type = NG_HCI_LINK_SCO; 65348698a83SMaksim Yevmenkin bcopy(dst, &ep->bdaddr, sizeof(ep->bdaddr)); 65448698a83SMaksim Yevmenkin 65548698a83SMaksim Yevmenkin NG_SEND_MSG_HOOK(error, ng_btsocket_sco_node, msg, rt->hook, 0); 65648698a83SMaksim Yevmenkin 65748698a83SMaksim Yevmenkin return (error); 65848698a83SMaksim Yevmenkin } /* ng_btsocket_sco_send_lp_con_rsp */ 65948698a83SMaksim Yevmenkin 66048698a83SMaksim Yevmenkin /* 66148698a83SMaksim Yevmenkin * Send LP_DisconReq request 66248698a83SMaksim Yevmenkin */ 66348698a83SMaksim Yevmenkin 66448698a83SMaksim Yevmenkin static int 66548698a83SMaksim Yevmenkin ng_btsocket_sco_send_lp_discon_req(ng_btsocket_sco_pcb_p pcb) 66648698a83SMaksim Yevmenkin { 66748698a83SMaksim Yevmenkin struct ng_mesg *msg = NULL; 66848698a83SMaksim Yevmenkin ng_hci_lp_discon_req_ep *ep = NULL; 66948698a83SMaksim Yevmenkin int error = 0; 67048698a83SMaksim Yevmenkin 67148698a83SMaksim Yevmenkin mtx_assert(&pcb->pcb_mtx, MA_OWNED); 67248698a83SMaksim Yevmenkin 67348698a83SMaksim Yevmenkin if (pcb->rt == NULL || 67448698a83SMaksim Yevmenkin pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) 67548698a83SMaksim Yevmenkin return (ENETDOWN); 67648698a83SMaksim Yevmenkin 67748698a83SMaksim Yevmenkin NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_REQ, 67848698a83SMaksim Yevmenkin sizeof(*ep), M_NOWAIT); 67948698a83SMaksim Yevmenkin if (msg == NULL) 68048698a83SMaksim Yevmenkin return (ENOMEM); 68148698a83SMaksim Yevmenkin 68248698a83SMaksim Yevmenkin ep = (ng_hci_lp_discon_req_ep *)(msg->data); 68348698a83SMaksim Yevmenkin ep->con_handle = pcb->con_handle; 68448698a83SMaksim Yevmenkin ep->reason = 0x13; /* User Ended Connection */ 68548698a83SMaksim Yevmenkin 68648698a83SMaksim Yevmenkin NG_SEND_MSG_HOOK(error, ng_btsocket_sco_node, msg, pcb->rt->hook, 0); 68748698a83SMaksim Yevmenkin 68848698a83SMaksim Yevmenkin return (error); 68948698a83SMaksim Yevmenkin } /* ng_btsocket_sco_send_lp_discon_req */ 69048698a83SMaksim Yevmenkin 69148698a83SMaksim Yevmenkin /***************************************************************************** 69248698a83SMaksim Yevmenkin ***************************************************************************** 69348698a83SMaksim Yevmenkin ** Socket interface 69448698a83SMaksim Yevmenkin ***************************************************************************** 69548698a83SMaksim Yevmenkin *****************************************************************************/ 69648698a83SMaksim Yevmenkin 69748698a83SMaksim Yevmenkin /* 69848698a83SMaksim Yevmenkin * SCO sockets data input routine 69948698a83SMaksim Yevmenkin */ 70048698a83SMaksim Yevmenkin 70148698a83SMaksim Yevmenkin static void 70248698a83SMaksim Yevmenkin ng_btsocket_sco_data_input(struct mbuf *m, hook_p hook) 70348698a83SMaksim Yevmenkin { 70448698a83SMaksim Yevmenkin ng_hci_scodata_pkt_t *hdr = NULL; 70548698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_t *pcb = NULL; 70648698a83SMaksim Yevmenkin ng_btsocket_sco_rtentry_t *rt = NULL; 70748698a83SMaksim Yevmenkin u_int16_t con_handle; 70848698a83SMaksim Yevmenkin 70948698a83SMaksim Yevmenkin if (hook == NULL) { 71048698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ALERT( 71148698a83SMaksim Yevmenkin "%s: Invalid source hook for SCO data packet\n", __func__); 71248698a83SMaksim Yevmenkin goto drop; 71348698a83SMaksim Yevmenkin } 71448698a83SMaksim Yevmenkin 71548698a83SMaksim Yevmenkin rt = (ng_btsocket_sco_rtentry_t *) NG_HOOK_PRIVATE(hook); 71648698a83SMaksim Yevmenkin if (rt == NULL) { 71748698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ALERT( 71848698a83SMaksim Yevmenkin "%s: Could not find out source bdaddr for SCO data packet\n", __func__); 71948698a83SMaksim Yevmenkin goto drop; 72048698a83SMaksim Yevmenkin } 72148698a83SMaksim Yevmenkin 72248698a83SMaksim Yevmenkin /* Make sure we can access header */ 72348698a83SMaksim Yevmenkin if (m->m_pkthdr.len < sizeof(*hdr)) { 72448698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ERR( 72548698a83SMaksim Yevmenkin "%s: SCO data packet too small, len=%d\n", __func__, m->m_pkthdr.len); 72648698a83SMaksim Yevmenkin goto drop; 72748698a83SMaksim Yevmenkin } 72848698a83SMaksim Yevmenkin 72948698a83SMaksim Yevmenkin if (m->m_len < sizeof(*hdr)) { 73048698a83SMaksim Yevmenkin m = m_pullup(m, sizeof(*hdr)); 73148698a83SMaksim Yevmenkin if (m == NULL) 73248698a83SMaksim Yevmenkin goto drop; 73348698a83SMaksim Yevmenkin } 73448698a83SMaksim Yevmenkin 73548698a83SMaksim Yevmenkin /* Strip SCO packet header and verify packet length */ 73648698a83SMaksim Yevmenkin hdr = mtod(m, ng_hci_scodata_pkt_t *); 73748698a83SMaksim Yevmenkin m_adj(m, sizeof(*hdr)); 73848698a83SMaksim Yevmenkin 73948698a83SMaksim Yevmenkin if (hdr->length != m->m_pkthdr.len) { 74048698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ERR( 74148698a83SMaksim Yevmenkin "%s: Bad SCO data packet length, len=%d, length=%d\n", 74248698a83SMaksim Yevmenkin __func__, m->m_pkthdr.len, hdr->length); 74348698a83SMaksim Yevmenkin goto drop; 74448698a83SMaksim Yevmenkin } 74548698a83SMaksim Yevmenkin 74648698a83SMaksim Yevmenkin /* 74748698a83SMaksim Yevmenkin * Now process packet 74848698a83SMaksim Yevmenkin */ 74948698a83SMaksim Yevmenkin 75048698a83SMaksim Yevmenkin con_handle = NG_HCI_CON_HANDLE(le16toh(hdr->con_handle)); 75148698a83SMaksim Yevmenkin 75248698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_INFO( 75348698a83SMaksim Yevmenkin "%s: Received SCO data packet: src bdaddr=%x:%x:%x:%x:%x:%x, handle=%d, " \ 75448698a83SMaksim Yevmenkin "length=%d\n", __func__, 75548698a83SMaksim Yevmenkin rt->src.b[5], rt->src.b[4], rt->src.b[3], 75648698a83SMaksim Yevmenkin rt->src.b[2], rt->src.b[1], rt->src.b[0], 75748698a83SMaksim Yevmenkin con_handle, hdr->length); 75848698a83SMaksim Yevmenkin 75948698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_sockets_mtx); 76048698a83SMaksim Yevmenkin 76148698a83SMaksim Yevmenkin /* Find socket */ 76248698a83SMaksim Yevmenkin pcb = ng_btsocket_sco_pcb_by_handle(&rt->src, con_handle); 76348698a83SMaksim Yevmenkin if (pcb == NULL) { 76448698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 76548698a83SMaksim Yevmenkin goto drop; 76648698a83SMaksim Yevmenkin } 76748698a83SMaksim Yevmenkin 76848698a83SMaksim Yevmenkin /* pcb is locked */ 76948698a83SMaksim Yevmenkin 77048698a83SMaksim Yevmenkin if (pcb->state != NG_BTSOCKET_SCO_OPEN) { 77148698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ERR( 77248698a83SMaksim Yevmenkin "%s: No connected socket found, src bdaddr=%x:%x:%x:%x:%x:%x, state=%d\n", 77348698a83SMaksim Yevmenkin __func__, 77448698a83SMaksim Yevmenkin rt->src.b[5], rt->src.b[4], rt->src.b[3], 77548698a83SMaksim Yevmenkin rt->src.b[2], rt->src.b[1], rt->src.b[0], 77648698a83SMaksim Yevmenkin pcb->state); 77748698a83SMaksim Yevmenkin 77848698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 77948698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 78048698a83SMaksim Yevmenkin goto drop; 78148698a83SMaksim Yevmenkin } 78248698a83SMaksim Yevmenkin 78348698a83SMaksim Yevmenkin /* Check if we have enough space in socket receive queue */ 78448698a83SMaksim Yevmenkin if (m->m_pkthdr.len > sbspace(&pcb->so->so_rcv)) { 78548698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ERR( 78648698a83SMaksim Yevmenkin "%s: Not enough space in socket receive queue. Dropping SCO data packet, " \ 78748698a83SMaksim Yevmenkin "src bdaddr=%x:%x:%x:%x:%x:%x, len=%d, space=%ld\n", 78848698a83SMaksim Yevmenkin __func__, 78948698a83SMaksim Yevmenkin rt->src.b[5], rt->src.b[4], rt->src.b[3], 79048698a83SMaksim Yevmenkin rt->src.b[2], rt->src.b[1], rt->src.b[0], 79148698a83SMaksim Yevmenkin m->m_pkthdr.len, 79248698a83SMaksim Yevmenkin sbspace(&pcb->so->so_rcv)); 79348698a83SMaksim Yevmenkin 79448698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 79548698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 79648698a83SMaksim Yevmenkin goto drop; 79748698a83SMaksim Yevmenkin } 79848698a83SMaksim Yevmenkin 79948698a83SMaksim Yevmenkin /* Append packet to the socket receive queue and wakeup */ 80048698a83SMaksim Yevmenkin sbappendrecord(&pcb->so->so_rcv, m); 80148698a83SMaksim Yevmenkin m = NULL; 80248698a83SMaksim Yevmenkin 80348698a83SMaksim Yevmenkin sorwakeup(pcb->so); 80448698a83SMaksim Yevmenkin 80548698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 80648698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 80748698a83SMaksim Yevmenkin drop: 80848698a83SMaksim Yevmenkin NG_FREE_M(m); /* checks for m != NULL */ 80948698a83SMaksim Yevmenkin } /* ng_btsocket_sco_data_input */ 81048698a83SMaksim Yevmenkin 81148698a83SMaksim Yevmenkin /* 81248698a83SMaksim Yevmenkin * SCO sockets default message input routine 81348698a83SMaksim Yevmenkin */ 81448698a83SMaksim Yevmenkin 81548698a83SMaksim Yevmenkin static void 81648698a83SMaksim Yevmenkin ng_btsocket_sco_default_msg_input(struct ng_mesg *msg, hook_p hook) 81748698a83SMaksim Yevmenkin { 81848698a83SMaksim Yevmenkin ng_btsocket_sco_rtentry_t *rt = NULL; 81948698a83SMaksim Yevmenkin 82048698a83SMaksim Yevmenkin if (hook == NULL || NG_HOOK_NOT_VALID(hook)) 82148698a83SMaksim Yevmenkin return; 82248698a83SMaksim Yevmenkin 82348698a83SMaksim Yevmenkin rt = (ng_btsocket_sco_rtentry_t *) NG_HOOK_PRIVATE(hook); 82448698a83SMaksim Yevmenkin 82548698a83SMaksim Yevmenkin switch (msg->header.cmd) { 82648698a83SMaksim Yevmenkin case NGM_HCI_NODE_UP: { 82748698a83SMaksim Yevmenkin ng_hci_node_up_ep *ep = NULL; 82848698a83SMaksim Yevmenkin 82948698a83SMaksim Yevmenkin if (msg->header.arglen != sizeof(*ep)) 83048698a83SMaksim Yevmenkin break; 83148698a83SMaksim Yevmenkin 83248698a83SMaksim Yevmenkin ep = (ng_hci_node_up_ep *)(msg->data); 83348698a83SMaksim Yevmenkin if (bcmp(&ep->bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0) 83448698a83SMaksim Yevmenkin break; 83548698a83SMaksim Yevmenkin 83648698a83SMaksim Yevmenkin if (rt == NULL) { 8371ede983cSDag-Erling Smørgrav rt = malloc(sizeof(*rt), 83848698a83SMaksim Yevmenkin M_NETGRAPH_BTSOCKET_SCO, M_NOWAIT|M_ZERO); 83948698a83SMaksim Yevmenkin if (rt == NULL) 84048698a83SMaksim Yevmenkin break; 84148698a83SMaksim Yevmenkin 84248698a83SMaksim Yevmenkin NG_HOOK_SET_PRIVATE(hook, rt); 84348698a83SMaksim Yevmenkin 84448698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_rt_mtx); 84548698a83SMaksim Yevmenkin 84648698a83SMaksim Yevmenkin LIST_INSERT_HEAD(&ng_btsocket_sco_rt, rt, next); 84748698a83SMaksim Yevmenkin } else 84848698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_rt_mtx); 84948698a83SMaksim Yevmenkin 85048698a83SMaksim Yevmenkin bcopy(&ep->bdaddr, &rt->src, sizeof(rt->src)); 85148698a83SMaksim Yevmenkin rt->pkt_size = (ep->pkt_size == 0)? 60 : ep->pkt_size; 85248698a83SMaksim Yevmenkin rt->num_pkts = ep->num_pkts; 85348698a83SMaksim Yevmenkin rt->hook = hook; 85448698a83SMaksim Yevmenkin 85548698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_rt_mtx); 85648698a83SMaksim Yevmenkin 85748698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_INFO( 85848698a83SMaksim Yevmenkin "%s: Updating hook \"%s\", src bdaddr=%x:%x:%x:%x:%x:%x, pkt_size=%d, " \ 85948698a83SMaksim Yevmenkin "num_pkts=%d\n", __func__, NG_HOOK_NAME(hook), 86048698a83SMaksim Yevmenkin rt->src.b[5], rt->src.b[4], rt->src.b[3], 86148698a83SMaksim Yevmenkin rt->src.b[2], rt->src.b[1], rt->src.b[0], 86248698a83SMaksim Yevmenkin rt->pkt_size, rt->num_pkts); 86348698a83SMaksim Yevmenkin } break; 86448698a83SMaksim Yevmenkin 86548698a83SMaksim Yevmenkin case NGM_HCI_SYNC_CON_QUEUE: { 86648698a83SMaksim Yevmenkin ng_hci_sync_con_queue_ep *ep = NULL; 86748698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_t *pcb = NULL; 86848698a83SMaksim Yevmenkin 86948698a83SMaksim Yevmenkin if (rt == NULL || msg->header.arglen != sizeof(*ep)) 87048698a83SMaksim Yevmenkin break; 87148698a83SMaksim Yevmenkin 87248698a83SMaksim Yevmenkin ep = (ng_hci_sync_con_queue_ep *)(msg->data); 87348698a83SMaksim Yevmenkin 87448698a83SMaksim Yevmenkin rt->pending -= ep->completed; 87548698a83SMaksim Yevmenkin if (rt->pending < 0) { 87648698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_WARN( 87748698a83SMaksim Yevmenkin "%s: Pending packet counter is out of sync! bdaddr=%x:%x:%x:%x:%x:%x, " \ 87848698a83SMaksim Yevmenkin "handle=%d, pending=%d, completed=%d\n", 87948698a83SMaksim Yevmenkin __func__, 88048698a83SMaksim Yevmenkin rt->src.b[5], rt->src.b[4], rt->src.b[3], 88148698a83SMaksim Yevmenkin rt->src.b[2], rt->src.b[1], rt->src.b[0], 88248698a83SMaksim Yevmenkin ep->con_handle, rt->pending, 88348698a83SMaksim Yevmenkin ep->completed); 88448698a83SMaksim Yevmenkin 88548698a83SMaksim Yevmenkin rt->pending = 0; 88648698a83SMaksim Yevmenkin } 88748698a83SMaksim Yevmenkin 88848698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_sockets_mtx); 88948698a83SMaksim Yevmenkin 89048698a83SMaksim Yevmenkin /* Find socket */ 89148698a83SMaksim Yevmenkin pcb = ng_btsocket_sco_pcb_by_handle(&rt->src, ep->con_handle); 89248698a83SMaksim Yevmenkin if (pcb == NULL) { 89348698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 89448698a83SMaksim Yevmenkin break; 89548698a83SMaksim Yevmenkin } 89648698a83SMaksim Yevmenkin 89748698a83SMaksim Yevmenkin /* pcb is locked */ 89848698a83SMaksim Yevmenkin 89948698a83SMaksim Yevmenkin /* Check state */ 90048698a83SMaksim Yevmenkin if (pcb->state == NG_BTSOCKET_SCO_OPEN) { 90148698a83SMaksim Yevmenkin /* Remove timeout */ 90248698a83SMaksim Yevmenkin ng_btsocket_sco_untimeout(pcb); 90348698a83SMaksim Yevmenkin 90448698a83SMaksim Yevmenkin /* Drop completed packets from the send queue */ 90548698a83SMaksim Yevmenkin for (; ep->completed > 0; ep->completed --) 90648698a83SMaksim Yevmenkin sbdroprecord(&pcb->so->so_snd); 90748698a83SMaksim Yevmenkin 90848698a83SMaksim Yevmenkin /* Send more if we have any */ 90948698a83SMaksim Yevmenkin if (pcb->so->so_snd.sb_cc > 0) 91048698a83SMaksim Yevmenkin if (ng_btsocket_sco_send2(pcb) == 0) 91148698a83SMaksim Yevmenkin ng_btsocket_sco_timeout(pcb); 91248698a83SMaksim Yevmenkin 91348698a83SMaksim Yevmenkin /* Wake up writers */ 91448698a83SMaksim Yevmenkin sowwakeup(pcb->so); 91548698a83SMaksim Yevmenkin } 91648698a83SMaksim Yevmenkin 91748698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 91848698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 91948698a83SMaksim Yevmenkin } break; 92048698a83SMaksim Yevmenkin 92148698a83SMaksim Yevmenkin default: 92248698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_WARN( 92348698a83SMaksim Yevmenkin "%s: Unknown message, cmd=%d\n", __func__, msg->header.cmd); 92448698a83SMaksim Yevmenkin break; 92548698a83SMaksim Yevmenkin } 92648698a83SMaksim Yevmenkin 92748698a83SMaksim Yevmenkin NG_FREE_MSG(msg); /* Checks for msg != NULL */ 92848698a83SMaksim Yevmenkin } /* ng_btsocket_sco_default_msg_input */ 92948698a83SMaksim Yevmenkin 93048698a83SMaksim Yevmenkin /* 93148698a83SMaksim Yevmenkin * SCO sockets LP message input routine 93248698a83SMaksim Yevmenkin */ 93348698a83SMaksim Yevmenkin 93448698a83SMaksim Yevmenkin static void 93548698a83SMaksim Yevmenkin ng_btsocket_sco_lp_msg_input(struct ng_mesg *msg, hook_p hook) 93648698a83SMaksim Yevmenkin { 93748698a83SMaksim Yevmenkin ng_btsocket_sco_rtentry_p rt = NULL; 93848698a83SMaksim Yevmenkin 93948698a83SMaksim Yevmenkin if (hook == NULL) { 94048698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ALERT( 94148698a83SMaksim Yevmenkin "%s: Invalid source hook for LP message\n", __func__); 94248698a83SMaksim Yevmenkin goto drop; 94348698a83SMaksim Yevmenkin } 94448698a83SMaksim Yevmenkin 94548698a83SMaksim Yevmenkin rt = (ng_btsocket_sco_rtentry_p) NG_HOOK_PRIVATE(hook); 94648698a83SMaksim Yevmenkin if (rt == NULL) { 94748698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ALERT( 94848698a83SMaksim Yevmenkin "%s: Could not find out source bdaddr for LP message\n", __func__); 94948698a83SMaksim Yevmenkin goto drop; 95048698a83SMaksim Yevmenkin } 95148698a83SMaksim Yevmenkin 95248698a83SMaksim Yevmenkin switch (msg->header.cmd) { 95348698a83SMaksim Yevmenkin case NGM_HCI_LP_CON_CFM: /* Connection Confirmation Event */ 95448698a83SMaksim Yevmenkin ng_btsocket_sco_process_lp_con_cfm(msg, rt); 95548698a83SMaksim Yevmenkin break; 95648698a83SMaksim Yevmenkin 95748698a83SMaksim Yevmenkin case NGM_HCI_LP_CON_IND: /* Connection Indication Event */ 95848698a83SMaksim Yevmenkin ng_btsocket_sco_process_lp_con_ind(msg, rt); 95948698a83SMaksim Yevmenkin break; 96048698a83SMaksim Yevmenkin 96148698a83SMaksim Yevmenkin case NGM_HCI_LP_DISCON_IND: /* Disconnection Indication Event */ 96248698a83SMaksim Yevmenkin ng_btsocket_sco_process_lp_discon_ind(msg, rt); 96348698a83SMaksim Yevmenkin break; 96448698a83SMaksim Yevmenkin 96548698a83SMaksim Yevmenkin /* XXX FIXME add other LP messages */ 96648698a83SMaksim Yevmenkin 96748698a83SMaksim Yevmenkin default: 96848698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_WARN( 96948698a83SMaksim Yevmenkin "%s: Unknown LP message, cmd=%d\n", __func__, msg->header.cmd); 97048698a83SMaksim Yevmenkin break; 97148698a83SMaksim Yevmenkin } 97248698a83SMaksim Yevmenkin drop: 97348698a83SMaksim Yevmenkin NG_FREE_MSG(msg); 97448698a83SMaksim Yevmenkin } /* ng_btsocket_sco_lp_msg_input */ 97548698a83SMaksim Yevmenkin 97648698a83SMaksim Yevmenkin /* 97748698a83SMaksim Yevmenkin * SCO sockets input routine 97848698a83SMaksim Yevmenkin */ 97948698a83SMaksim Yevmenkin 98048698a83SMaksim Yevmenkin static void 98148698a83SMaksim Yevmenkin ng_btsocket_sco_input(void *context, int pending) 98248698a83SMaksim Yevmenkin { 98348698a83SMaksim Yevmenkin item_p item = NULL; 98448698a83SMaksim Yevmenkin hook_p hook = NULL; 98548698a83SMaksim Yevmenkin 98648698a83SMaksim Yevmenkin for (;;) { 98748698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_queue_mtx); 98848698a83SMaksim Yevmenkin NG_BT_ITEMQ_DEQUEUE(&ng_btsocket_sco_queue, item); 98948698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_queue_mtx); 99048698a83SMaksim Yevmenkin 99148698a83SMaksim Yevmenkin if (item == NULL) 99248698a83SMaksim Yevmenkin break; 99348698a83SMaksim Yevmenkin 99448698a83SMaksim Yevmenkin NGI_GET_HOOK(item, hook); 99548698a83SMaksim Yevmenkin if (hook != NULL && NG_HOOK_NOT_VALID(hook)) 99648698a83SMaksim Yevmenkin goto drop; 99748698a83SMaksim Yevmenkin 99848698a83SMaksim Yevmenkin switch(item->el_flags & NGQF_TYPE) { 99948698a83SMaksim Yevmenkin case NGQF_DATA: { 100048698a83SMaksim Yevmenkin struct mbuf *m = NULL; 100148698a83SMaksim Yevmenkin 100248698a83SMaksim Yevmenkin NGI_GET_M(item, m); 100348698a83SMaksim Yevmenkin ng_btsocket_sco_data_input(m, hook); 100448698a83SMaksim Yevmenkin } break; 100548698a83SMaksim Yevmenkin 100648698a83SMaksim Yevmenkin case NGQF_MESG: { 100748698a83SMaksim Yevmenkin struct ng_mesg *msg = NULL; 100848698a83SMaksim Yevmenkin 100948698a83SMaksim Yevmenkin NGI_GET_MSG(item, msg); 101048698a83SMaksim Yevmenkin 101148698a83SMaksim Yevmenkin switch (msg->header.cmd) { 101248698a83SMaksim Yevmenkin case NGM_HCI_LP_CON_CFM: 101348698a83SMaksim Yevmenkin case NGM_HCI_LP_CON_IND: 101448698a83SMaksim Yevmenkin case NGM_HCI_LP_DISCON_IND: 101548698a83SMaksim Yevmenkin /* XXX FIXME add other LP messages */ 101648698a83SMaksim Yevmenkin ng_btsocket_sco_lp_msg_input(msg, hook); 101748698a83SMaksim Yevmenkin break; 101848698a83SMaksim Yevmenkin 101948698a83SMaksim Yevmenkin default: 102048698a83SMaksim Yevmenkin ng_btsocket_sco_default_msg_input(msg, hook); 102148698a83SMaksim Yevmenkin break; 102248698a83SMaksim Yevmenkin } 102348698a83SMaksim Yevmenkin } break; 102448698a83SMaksim Yevmenkin 102548698a83SMaksim Yevmenkin default: 102648698a83SMaksim Yevmenkin KASSERT(0, 102748698a83SMaksim Yevmenkin ("%s: invalid item type=%ld\n", __func__, (item->el_flags & NGQF_TYPE))); 102848698a83SMaksim Yevmenkin break; 102948698a83SMaksim Yevmenkin } 103048698a83SMaksim Yevmenkin drop: 103148698a83SMaksim Yevmenkin if (hook != NULL) 103248698a83SMaksim Yevmenkin NG_HOOK_UNREF(hook); 103348698a83SMaksim Yevmenkin 103448698a83SMaksim Yevmenkin NG_FREE_ITEM(item); 103548698a83SMaksim Yevmenkin } 103648698a83SMaksim Yevmenkin } /* ng_btsocket_sco_input */ 103748698a83SMaksim Yevmenkin 103848698a83SMaksim Yevmenkin /* 103948698a83SMaksim Yevmenkin * Route cleanup task. Gets scheduled when hook is disconnected. Here we 104048698a83SMaksim Yevmenkin * will find all sockets that use "invalid" hook and disconnect them. 104148698a83SMaksim Yevmenkin */ 104248698a83SMaksim Yevmenkin 104348698a83SMaksim Yevmenkin static void 104448698a83SMaksim Yevmenkin ng_btsocket_sco_rtclean(void *context, int pending) 104548698a83SMaksim Yevmenkin { 104648698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_p pcb = NULL, pcb_next = NULL; 104748698a83SMaksim Yevmenkin ng_btsocket_sco_rtentry_p rt = NULL; 104848698a83SMaksim Yevmenkin 104948698a83SMaksim Yevmenkin /* 105048698a83SMaksim Yevmenkin * First disconnect all sockets that use "invalid" hook 105148698a83SMaksim Yevmenkin */ 105248698a83SMaksim Yevmenkin 105348698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_sockets_mtx); 105448698a83SMaksim Yevmenkin 105548698a83SMaksim Yevmenkin for(pcb = LIST_FIRST(&ng_btsocket_sco_sockets); pcb != NULL; ) { 105648698a83SMaksim Yevmenkin mtx_lock(&pcb->pcb_mtx); 105748698a83SMaksim Yevmenkin pcb_next = LIST_NEXT(pcb, next); 105848698a83SMaksim Yevmenkin 105948698a83SMaksim Yevmenkin if (pcb->rt != NULL && 106048698a83SMaksim Yevmenkin pcb->rt->hook != NULL && NG_HOOK_NOT_VALID(pcb->rt->hook)) { 106148698a83SMaksim Yevmenkin if (pcb->flags & NG_BTSOCKET_SCO_TIMO) 106248698a83SMaksim Yevmenkin ng_btsocket_sco_untimeout(pcb); 106348698a83SMaksim Yevmenkin 106448698a83SMaksim Yevmenkin pcb->rt = NULL; 106548698a83SMaksim Yevmenkin pcb->so->so_error = ENETDOWN; 106648698a83SMaksim Yevmenkin pcb->state = NG_BTSOCKET_SCO_CLOSED; 106748698a83SMaksim Yevmenkin soisdisconnected(pcb->so); 106848698a83SMaksim Yevmenkin } 106948698a83SMaksim Yevmenkin 107048698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 107148698a83SMaksim Yevmenkin pcb = pcb_next; 107248698a83SMaksim Yevmenkin } 107348698a83SMaksim Yevmenkin 107448698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 107548698a83SMaksim Yevmenkin 107648698a83SMaksim Yevmenkin /* 107748698a83SMaksim Yevmenkin * Now cleanup routing table 107848698a83SMaksim Yevmenkin */ 107948698a83SMaksim Yevmenkin 108048698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_rt_mtx); 108148698a83SMaksim Yevmenkin 108248698a83SMaksim Yevmenkin for (rt = LIST_FIRST(&ng_btsocket_sco_rt); rt != NULL; ) { 108348698a83SMaksim Yevmenkin ng_btsocket_sco_rtentry_p rt_next = LIST_NEXT(rt, next); 108448698a83SMaksim Yevmenkin 108548698a83SMaksim Yevmenkin if (rt->hook != NULL && NG_HOOK_NOT_VALID(rt->hook)) { 108648698a83SMaksim Yevmenkin LIST_REMOVE(rt, next); 108748698a83SMaksim Yevmenkin 108848698a83SMaksim Yevmenkin NG_HOOK_SET_PRIVATE(rt->hook, NULL); 108948698a83SMaksim Yevmenkin NG_HOOK_UNREF(rt->hook); /* Remove extra reference */ 109048698a83SMaksim Yevmenkin 109148698a83SMaksim Yevmenkin bzero(rt, sizeof(*rt)); 10921ede983cSDag-Erling Smørgrav free(rt, M_NETGRAPH_BTSOCKET_SCO); 109348698a83SMaksim Yevmenkin } 109448698a83SMaksim Yevmenkin 109548698a83SMaksim Yevmenkin rt = rt_next; 109648698a83SMaksim Yevmenkin } 109748698a83SMaksim Yevmenkin 109848698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_rt_mtx); 109948698a83SMaksim Yevmenkin } /* ng_btsocket_sco_rtclean */ 110048698a83SMaksim Yevmenkin 110148698a83SMaksim Yevmenkin /* 110248698a83SMaksim Yevmenkin * Initialize everything 110348698a83SMaksim Yevmenkin */ 110448698a83SMaksim Yevmenkin 110548698a83SMaksim Yevmenkin void 110648698a83SMaksim Yevmenkin ng_btsocket_sco_init(void) 110748698a83SMaksim Yevmenkin { 110848698a83SMaksim Yevmenkin int error = 0; 110948698a83SMaksim Yevmenkin 111048698a83SMaksim Yevmenkin ng_btsocket_sco_node = NULL; 111148698a83SMaksim Yevmenkin ng_btsocket_sco_debug_level = NG_BTSOCKET_WARN_LEVEL; 111248698a83SMaksim Yevmenkin 111348698a83SMaksim Yevmenkin /* Register Netgraph node type */ 111448698a83SMaksim Yevmenkin error = ng_newtype(&typestruct); 111548698a83SMaksim Yevmenkin if (error != 0) { 111648698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ALERT( 111748698a83SMaksim Yevmenkin "%s: Could not register Netgraph node type, error=%d\n", __func__, error); 111848698a83SMaksim Yevmenkin 111948698a83SMaksim Yevmenkin return; 112048698a83SMaksim Yevmenkin } 112148698a83SMaksim Yevmenkin 112248698a83SMaksim Yevmenkin /* Create Netgrapg node */ 112348698a83SMaksim Yevmenkin error = ng_make_node_common(&typestruct, &ng_btsocket_sco_node); 112448698a83SMaksim Yevmenkin if (error != 0) { 112548698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ALERT( 112648698a83SMaksim Yevmenkin "%s: Could not create Netgraph node, error=%d\n", __func__, error); 112748698a83SMaksim Yevmenkin 112848698a83SMaksim Yevmenkin ng_btsocket_sco_node = NULL; 112948698a83SMaksim Yevmenkin 113048698a83SMaksim Yevmenkin return; 113148698a83SMaksim Yevmenkin } 113248698a83SMaksim Yevmenkin 113348698a83SMaksim Yevmenkin error = ng_name_node(ng_btsocket_sco_node, NG_BTSOCKET_SCO_NODE_TYPE); 113448698a83SMaksim Yevmenkin if (error != 0) { 113548698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ALERT( 113648698a83SMaksim Yevmenkin "%s: Could not name Netgraph node, error=%d\n", __func__, error); 113748698a83SMaksim Yevmenkin 113848698a83SMaksim Yevmenkin NG_NODE_UNREF(ng_btsocket_sco_node); 113948698a83SMaksim Yevmenkin ng_btsocket_sco_node = NULL; 114048698a83SMaksim Yevmenkin 114148698a83SMaksim Yevmenkin return; 114248698a83SMaksim Yevmenkin } 114348698a83SMaksim Yevmenkin 114448698a83SMaksim Yevmenkin /* Create input queue */ 114548698a83SMaksim Yevmenkin NG_BT_ITEMQ_INIT(&ng_btsocket_sco_queue, 300); 114648698a83SMaksim Yevmenkin mtx_init(&ng_btsocket_sco_queue_mtx, 114748698a83SMaksim Yevmenkin "btsocks_sco_queue_mtx", NULL, MTX_DEF); 114848698a83SMaksim Yevmenkin TASK_INIT(&ng_btsocket_sco_queue_task, 0, 114948698a83SMaksim Yevmenkin ng_btsocket_sco_input, NULL); 115048698a83SMaksim Yevmenkin 115148698a83SMaksim Yevmenkin /* Create list of sockets */ 115248698a83SMaksim Yevmenkin LIST_INIT(&ng_btsocket_sco_sockets); 115348698a83SMaksim Yevmenkin mtx_init(&ng_btsocket_sco_sockets_mtx, 115448698a83SMaksim Yevmenkin "btsocks_sco_sockets_mtx", NULL, MTX_DEF); 115548698a83SMaksim Yevmenkin 115648698a83SMaksim Yevmenkin /* Routing table */ 115748698a83SMaksim Yevmenkin LIST_INIT(&ng_btsocket_sco_rt); 115848698a83SMaksim Yevmenkin mtx_init(&ng_btsocket_sco_rt_mtx, 115948698a83SMaksim Yevmenkin "btsocks_sco_rt_mtx", NULL, MTX_DEF); 116048698a83SMaksim Yevmenkin TASK_INIT(&ng_btsocket_sco_rt_task, 0, 116148698a83SMaksim Yevmenkin ng_btsocket_sco_rtclean, NULL); 116248698a83SMaksim Yevmenkin } /* ng_btsocket_sco_init */ 116348698a83SMaksim Yevmenkin 116448698a83SMaksim Yevmenkin /* 116548698a83SMaksim Yevmenkin * Abort connection on socket 116648698a83SMaksim Yevmenkin */ 116748698a83SMaksim Yevmenkin 116848698a83SMaksim Yevmenkin void 116948698a83SMaksim Yevmenkin ng_btsocket_sco_abort(struct socket *so) 117048698a83SMaksim Yevmenkin { 117148698a83SMaksim Yevmenkin so->so_error = ECONNABORTED; 117248698a83SMaksim Yevmenkin 117348698a83SMaksim Yevmenkin (void) ng_btsocket_sco_disconnect(so); 117448698a83SMaksim Yevmenkin } /* ng_btsocket_sco_abort */ 117548698a83SMaksim Yevmenkin 117648698a83SMaksim Yevmenkin void 117748698a83SMaksim Yevmenkin ng_btsocket_sco_close(struct socket *so) 117848698a83SMaksim Yevmenkin { 117948698a83SMaksim Yevmenkin (void) ng_btsocket_sco_disconnect(so); 118048698a83SMaksim Yevmenkin } /* ng_btsocket_sco_close */ 118148698a83SMaksim Yevmenkin 118248698a83SMaksim Yevmenkin /* 118348698a83SMaksim Yevmenkin * Accept connection on socket. Nothing to do here, socket must be connected 118448698a83SMaksim Yevmenkin * and ready, so just return peer address and be done with it. 118548698a83SMaksim Yevmenkin */ 118648698a83SMaksim Yevmenkin 118748698a83SMaksim Yevmenkin int 118848698a83SMaksim Yevmenkin ng_btsocket_sco_accept(struct socket *so, struct sockaddr **nam) 118948698a83SMaksim Yevmenkin { 119048698a83SMaksim Yevmenkin if (ng_btsocket_sco_node == NULL) 119148698a83SMaksim Yevmenkin return (EINVAL); 119248698a83SMaksim Yevmenkin 119348698a83SMaksim Yevmenkin return (ng_btsocket_sco_peeraddr(so, nam)); 119448698a83SMaksim Yevmenkin } /* ng_btsocket_sco_accept */ 119548698a83SMaksim Yevmenkin 119648698a83SMaksim Yevmenkin /* 119748698a83SMaksim Yevmenkin * Create and attach new socket 119848698a83SMaksim Yevmenkin */ 119948698a83SMaksim Yevmenkin 120048698a83SMaksim Yevmenkin int 120148698a83SMaksim Yevmenkin ng_btsocket_sco_attach(struct socket *so, int proto, struct thread *td) 120248698a83SMaksim Yevmenkin { 120348698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 120448698a83SMaksim Yevmenkin int error; 120548698a83SMaksim Yevmenkin 120648698a83SMaksim Yevmenkin /* Check socket and protocol */ 120748698a83SMaksim Yevmenkin if (ng_btsocket_sco_node == NULL) 120848698a83SMaksim Yevmenkin return (EPROTONOSUPPORT); 120948698a83SMaksim Yevmenkin if (so->so_type != SOCK_SEQPACKET) 121048698a83SMaksim Yevmenkin return (ESOCKTNOSUPPORT); 121148698a83SMaksim Yevmenkin 121248698a83SMaksim Yevmenkin #if 0 /* XXX sonewconn() calls "pru_attach" with proto == 0 */ 121348698a83SMaksim Yevmenkin if (proto != 0) 121448698a83SMaksim Yevmenkin if (proto != BLUETOOTH_PROTO_SCO) 121548698a83SMaksim Yevmenkin return (EPROTONOSUPPORT); 121648698a83SMaksim Yevmenkin #endif /* XXX */ 121748698a83SMaksim Yevmenkin 121848698a83SMaksim Yevmenkin if (pcb != NULL) 121948698a83SMaksim Yevmenkin return (EISCONN); 122048698a83SMaksim Yevmenkin 122148698a83SMaksim Yevmenkin /* Reserve send and receive space if it is not reserved yet */ 122248698a83SMaksim Yevmenkin if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) { 122348698a83SMaksim Yevmenkin error = soreserve(so, NG_BTSOCKET_SCO_SENDSPACE, 122448698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_RECVSPACE); 122548698a83SMaksim Yevmenkin if (error != 0) 122648698a83SMaksim Yevmenkin return (error); 122748698a83SMaksim Yevmenkin } 122848698a83SMaksim Yevmenkin 122948698a83SMaksim Yevmenkin /* Allocate the PCB */ 12301ede983cSDag-Erling Smørgrav pcb = malloc(sizeof(*pcb), 123148698a83SMaksim Yevmenkin M_NETGRAPH_BTSOCKET_SCO, M_NOWAIT | M_ZERO); 123248698a83SMaksim Yevmenkin if (pcb == NULL) 123348698a83SMaksim Yevmenkin return (ENOMEM); 123448698a83SMaksim Yevmenkin 123548698a83SMaksim Yevmenkin /* Link the PCB and the socket */ 123648698a83SMaksim Yevmenkin so->so_pcb = (caddr_t) pcb; 123748698a83SMaksim Yevmenkin pcb->so = so; 123848698a83SMaksim Yevmenkin pcb->state = NG_BTSOCKET_SCO_CLOSED; 123948698a83SMaksim Yevmenkin 124048698a83SMaksim Yevmenkin callout_init(&pcb->timo, 1); 124148698a83SMaksim Yevmenkin 124248698a83SMaksim Yevmenkin /* 124348698a83SMaksim Yevmenkin * Mark PCB mutex as DUPOK to prevent "duplicated lock of 124448698a83SMaksim Yevmenkin * the same type" message. When accepting new SCO connection 124548698a83SMaksim Yevmenkin * ng_btsocket_sco_process_lp_con_ind() holds both PCB mutexes 124648698a83SMaksim Yevmenkin * for "old" (accepting) PCB and "new" (created) PCB. 124748698a83SMaksim Yevmenkin */ 124848698a83SMaksim Yevmenkin 124948698a83SMaksim Yevmenkin mtx_init(&pcb->pcb_mtx, "btsocks_sco_pcb_mtx", NULL, 125048698a83SMaksim Yevmenkin MTX_DEF|MTX_DUPOK); 125148698a83SMaksim Yevmenkin 125248698a83SMaksim Yevmenkin /* 125348698a83SMaksim Yevmenkin * Add the PCB to the list 125448698a83SMaksim Yevmenkin * 125548698a83SMaksim Yevmenkin * XXX FIXME VERY IMPORTANT! 125648698a83SMaksim Yevmenkin * 125748698a83SMaksim Yevmenkin * This is totally FUBAR. We could get here in two cases: 125848698a83SMaksim Yevmenkin * 125948698a83SMaksim Yevmenkin * 1) When user calls socket() 126048698a83SMaksim Yevmenkin * 2) When we need to accept new incomming connection and call 126148698a83SMaksim Yevmenkin * sonewconn() 126248698a83SMaksim Yevmenkin * 126348698a83SMaksim Yevmenkin * In the first case we must aquire ng_btsocket_sco_sockets_mtx. 126448698a83SMaksim Yevmenkin * In the second case we hold ng_btsocket_sco_sockets_mtx already. 126548698a83SMaksim Yevmenkin * So we now need to distinguish between these cases. From reading 126648698a83SMaksim Yevmenkin * /sys/kern/uipc_socket2.c we can find out that sonewconn() calls 126748698a83SMaksim Yevmenkin * pru_attach with proto == 0 and td == NULL. For now use this fact 126848698a83SMaksim Yevmenkin * to figure out if we were called from socket() or from sonewconn(). 126948698a83SMaksim Yevmenkin */ 127048698a83SMaksim Yevmenkin 127148698a83SMaksim Yevmenkin if (td != NULL) 127248698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_sockets_mtx); 127348698a83SMaksim Yevmenkin else 127448698a83SMaksim Yevmenkin mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED); 127548698a83SMaksim Yevmenkin 127648698a83SMaksim Yevmenkin LIST_INSERT_HEAD(&ng_btsocket_sco_sockets, pcb, next); 127748698a83SMaksim Yevmenkin 127848698a83SMaksim Yevmenkin if (td != NULL) 127948698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 128048698a83SMaksim Yevmenkin 128148698a83SMaksim Yevmenkin return (0); 128248698a83SMaksim Yevmenkin } /* ng_btsocket_sco_attach */ 128348698a83SMaksim Yevmenkin 128448698a83SMaksim Yevmenkin /* 128548698a83SMaksim Yevmenkin * Bind socket 128648698a83SMaksim Yevmenkin */ 128748698a83SMaksim Yevmenkin 128848698a83SMaksim Yevmenkin int 128948698a83SMaksim Yevmenkin ng_btsocket_sco_bind(struct socket *so, struct sockaddr *nam, 129048698a83SMaksim Yevmenkin struct thread *td) 129148698a83SMaksim Yevmenkin { 129248698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_t *pcb = NULL; 129348698a83SMaksim Yevmenkin struct sockaddr_sco *sa = (struct sockaddr_sco *) nam; 129448698a83SMaksim Yevmenkin 129548698a83SMaksim Yevmenkin if (ng_btsocket_sco_node == NULL) 129648698a83SMaksim Yevmenkin return (EINVAL); 129748698a83SMaksim Yevmenkin 129848698a83SMaksim Yevmenkin /* Verify address */ 129948698a83SMaksim Yevmenkin if (sa == NULL) 130048698a83SMaksim Yevmenkin return (EINVAL); 130148698a83SMaksim Yevmenkin if (sa->sco_family != AF_BLUETOOTH) 130248698a83SMaksim Yevmenkin return (EAFNOSUPPORT); 130348698a83SMaksim Yevmenkin if (sa->sco_len != sizeof(*sa)) 130448698a83SMaksim Yevmenkin return (EINVAL); 130548698a83SMaksim Yevmenkin 130648698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_sockets_mtx); 130748698a83SMaksim Yevmenkin 130848698a83SMaksim Yevmenkin /* 130948698a83SMaksim Yevmenkin * Check if other socket has this address already (look for exact 131048698a83SMaksim Yevmenkin * match in bdaddr) and assign socket address if it's available. 131148698a83SMaksim Yevmenkin */ 131248698a83SMaksim Yevmenkin 131348698a83SMaksim Yevmenkin if (bcmp(&sa->sco_bdaddr, NG_HCI_BDADDR_ANY, sizeof(sa->sco_bdaddr)) != 0) { 131448698a83SMaksim Yevmenkin LIST_FOREACH(pcb, &ng_btsocket_sco_sockets, next) { 131548698a83SMaksim Yevmenkin mtx_lock(&pcb->pcb_mtx); 131648698a83SMaksim Yevmenkin 131748698a83SMaksim Yevmenkin if (bcmp(&pcb->src, &sa->sco_bdaddr, sizeof(bdaddr_t)) == 0) { 131848698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 131948698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 132048698a83SMaksim Yevmenkin 132148698a83SMaksim Yevmenkin return (EADDRINUSE); 132248698a83SMaksim Yevmenkin } 132348698a83SMaksim Yevmenkin 132448698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 132548698a83SMaksim Yevmenkin } 132648698a83SMaksim Yevmenkin 132748698a83SMaksim Yevmenkin } 132848698a83SMaksim Yevmenkin 132948698a83SMaksim Yevmenkin pcb = so2sco_pcb(so); 133048698a83SMaksim Yevmenkin if (pcb == NULL) { 133148698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 133248698a83SMaksim Yevmenkin return (EINVAL); 133348698a83SMaksim Yevmenkin } 133448698a83SMaksim Yevmenkin 133548698a83SMaksim Yevmenkin mtx_lock(&pcb->pcb_mtx); 133648698a83SMaksim Yevmenkin bcopy(&sa->sco_bdaddr, &pcb->src, sizeof(pcb->src)); 133748698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 133848698a83SMaksim Yevmenkin 133948698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 134048698a83SMaksim Yevmenkin 134148698a83SMaksim Yevmenkin return (0); 134248698a83SMaksim Yevmenkin } /* ng_btsocket_sco_bind */ 134348698a83SMaksim Yevmenkin 134448698a83SMaksim Yevmenkin /* 134548698a83SMaksim Yevmenkin * Connect socket 134648698a83SMaksim Yevmenkin */ 134748698a83SMaksim Yevmenkin 134848698a83SMaksim Yevmenkin int 134948698a83SMaksim Yevmenkin ng_btsocket_sco_connect(struct socket *so, struct sockaddr *nam, 135048698a83SMaksim Yevmenkin struct thread *td) 135148698a83SMaksim Yevmenkin { 135248698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_t *pcb = so2sco_pcb(so); 135348698a83SMaksim Yevmenkin struct sockaddr_sco *sa = (struct sockaddr_sco *) nam; 135448698a83SMaksim Yevmenkin ng_btsocket_sco_rtentry_t *rt = NULL; 135548698a83SMaksim Yevmenkin int have_src, error = 0; 135648698a83SMaksim Yevmenkin 135748698a83SMaksim Yevmenkin /* Check socket */ 135848698a83SMaksim Yevmenkin if (pcb == NULL) 135948698a83SMaksim Yevmenkin return (EINVAL); 136048698a83SMaksim Yevmenkin if (ng_btsocket_sco_node == NULL) 136148698a83SMaksim Yevmenkin return (EINVAL); 136248698a83SMaksim Yevmenkin 136348698a83SMaksim Yevmenkin /* Verify address */ 136448698a83SMaksim Yevmenkin if (sa == NULL) 136548698a83SMaksim Yevmenkin return (EINVAL); 136648698a83SMaksim Yevmenkin if (sa->sco_family != AF_BLUETOOTH) 136748698a83SMaksim Yevmenkin return (EAFNOSUPPORT); 136848698a83SMaksim Yevmenkin if (sa->sco_len != sizeof(*sa)) 136948698a83SMaksim Yevmenkin return (EINVAL); 137048698a83SMaksim Yevmenkin if (bcmp(&sa->sco_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0) 137148698a83SMaksim Yevmenkin return (EDESTADDRREQ); 137248698a83SMaksim Yevmenkin 137348698a83SMaksim Yevmenkin /* 137448698a83SMaksim Yevmenkin * Routing. Socket should be bound to some source address. The source 137548698a83SMaksim Yevmenkin * address can be ANY. Destination address must be set and it must not 137648698a83SMaksim Yevmenkin * be ANY. If source address is ANY then find first rtentry that has 137748698a83SMaksim Yevmenkin * src != dst. 137848698a83SMaksim Yevmenkin */ 137948698a83SMaksim Yevmenkin 138048698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_rt_mtx); 138148698a83SMaksim Yevmenkin mtx_lock(&pcb->pcb_mtx); 138248698a83SMaksim Yevmenkin 138348698a83SMaksim Yevmenkin if (pcb->state == NG_BTSOCKET_SCO_CONNECTING) { 138448698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 138548698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_rt_mtx); 138648698a83SMaksim Yevmenkin 138748698a83SMaksim Yevmenkin return (EINPROGRESS); 138848698a83SMaksim Yevmenkin } 138948698a83SMaksim Yevmenkin 139048698a83SMaksim Yevmenkin if (bcmp(&sa->sco_bdaddr, &pcb->src, sizeof(pcb->src)) == 0) { 139148698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 139248698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_rt_mtx); 139348698a83SMaksim Yevmenkin 139448698a83SMaksim Yevmenkin return (EINVAL); 139548698a83SMaksim Yevmenkin } 139648698a83SMaksim Yevmenkin 139748698a83SMaksim Yevmenkin /* Send destination address and PSM */ 139848698a83SMaksim Yevmenkin bcopy(&sa->sco_bdaddr, &pcb->dst, sizeof(pcb->dst)); 139948698a83SMaksim Yevmenkin 140048698a83SMaksim Yevmenkin pcb->rt = NULL; 140148698a83SMaksim Yevmenkin have_src = bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src)); 140248698a83SMaksim Yevmenkin 140348698a83SMaksim Yevmenkin LIST_FOREACH(rt, &ng_btsocket_sco_rt, next) { 140448698a83SMaksim Yevmenkin if (rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook)) 140548698a83SMaksim Yevmenkin continue; 140648698a83SMaksim Yevmenkin 140748698a83SMaksim Yevmenkin /* Match src and dst */ 140848698a83SMaksim Yevmenkin if (have_src) { 140948698a83SMaksim Yevmenkin if (bcmp(&pcb->src, &rt->src, sizeof(rt->src)) == 0) 141048698a83SMaksim Yevmenkin break; 141148698a83SMaksim Yevmenkin } else { 141248698a83SMaksim Yevmenkin if (bcmp(&pcb->dst, &rt->src, sizeof(rt->src)) != 0) 141348698a83SMaksim Yevmenkin break; 141448698a83SMaksim Yevmenkin } 141548698a83SMaksim Yevmenkin } 141648698a83SMaksim Yevmenkin 141748698a83SMaksim Yevmenkin if (rt != NULL) { 141848698a83SMaksim Yevmenkin pcb->rt = rt; 141948698a83SMaksim Yevmenkin 142048698a83SMaksim Yevmenkin if (!have_src) 142148698a83SMaksim Yevmenkin bcopy(&rt->src, &pcb->src, sizeof(pcb->src)); 142248698a83SMaksim Yevmenkin } else 142348698a83SMaksim Yevmenkin error = EHOSTUNREACH; 142448698a83SMaksim Yevmenkin 142548698a83SMaksim Yevmenkin /* 142648698a83SMaksim Yevmenkin * Send LP_Connect request 142748698a83SMaksim Yevmenkin */ 142848698a83SMaksim Yevmenkin 142948698a83SMaksim Yevmenkin if (error == 0) { 143048698a83SMaksim Yevmenkin error = ng_btsocket_sco_send_lp_con_req(pcb); 143148698a83SMaksim Yevmenkin if (error == 0) { 143248698a83SMaksim Yevmenkin pcb->flags |= NG_BTSOCKET_SCO_CLIENT; 143348698a83SMaksim Yevmenkin pcb->state = NG_BTSOCKET_SCO_CONNECTING; 143448698a83SMaksim Yevmenkin soisconnecting(pcb->so); 143548698a83SMaksim Yevmenkin 143648698a83SMaksim Yevmenkin ng_btsocket_sco_timeout(pcb); 143748698a83SMaksim Yevmenkin } 143848698a83SMaksim Yevmenkin } 143948698a83SMaksim Yevmenkin 144048698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 144148698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_rt_mtx); 144248698a83SMaksim Yevmenkin 144348698a83SMaksim Yevmenkin return (error); 144448698a83SMaksim Yevmenkin } /* ng_btsocket_sco_connect */ 144548698a83SMaksim Yevmenkin 144648698a83SMaksim Yevmenkin /* 144748698a83SMaksim Yevmenkin * Process ioctl's calls on socket 144848698a83SMaksim Yevmenkin */ 144948698a83SMaksim Yevmenkin 145048698a83SMaksim Yevmenkin int 145148698a83SMaksim Yevmenkin ng_btsocket_sco_control(struct socket *so, u_long cmd, caddr_t data, 145248698a83SMaksim Yevmenkin struct ifnet *ifp, struct thread *td) 145348698a83SMaksim Yevmenkin { 145448698a83SMaksim Yevmenkin return (EINVAL); 145548698a83SMaksim Yevmenkin } /* ng_btsocket_sco_control */ 145648698a83SMaksim Yevmenkin 145748698a83SMaksim Yevmenkin /* 145848698a83SMaksim Yevmenkin * Process getsockopt/setsockopt system calls 145948698a83SMaksim Yevmenkin */ 146048698a83SMaksim Yevmenkin 146148698a83SMaksim Yevmenkin int 146248698a83SMaksim Yevmenkin ng_btsocket_sco_ctloutput(struct socket *so, struct sockopt *sopt) 146348698a83SMaksim Yevmenkin { 146448698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 146548698a83SMaksim Yevmenkin int error, tmp; 146648698a83SMaksim Yevmenkin 146748698a83SMaksim Yevmenkin if (ng_btsocket_sco_node == NULL) 146848698a83SMaksim Yevmenkin return (EINVAL); 146948698a83SMaksim Yevmenkin if (pcb == NULL) 147048698a83SMaksim Yevmenkin return (EINVAL); 147148698a83SMaksim Yevmenkin 147248698a83SMaksim Yevmenkin if (sopt->sopt_level != SOL_SCO) 147348698a83SMaksim Yevmenkin return (0); 147448698a83SMaksim Yevmenkin 147548698a83SMaksim Yevmenkin mtx_lock(&pcb->pcb_mtx); 147648698a83SMaksim Yevmenkin 147748698a83SMaksim Yevmenkin switch (sopt->sopt_dir) { 147848698a83SMaksim Yevmenkin case SOPT_GET: 147948698a83SMaksim Yevmenkin if (pcb->state != NG_BTSOCKET_SCO_OPEN) { 148048698a83SMaksim Yevmenkin error = ENOTCONN; 148148698a83SMaksim Yevmenkin break; 148248698a83SMaksim Yevmenkin } 148348698a83SMaksim Yevmenkin 148448698a83SMaksim Yevmenkin switch (sopt->sopt_name) { 148548698a83SMaksim Yevmenkin case SO_SCO_MTU: 148648698a83SMaksim Yevmenkin tmp = pcb->rt->pkt_size; 148748698a83SMaksim Yevmenkin error = sooptcopyout(sopt, &tmp, sizeof(tmp)); 148848698a83SMaksim Yevmenkin break; 148948698a83SMaksim Yevmenkin 149048698a83SMaksim Yevmenkin case SO_SCO_CONNINFO: 149148698a83SMaksim Yevmenkin tmp = pcb->con_handle; 149248698a83SMaksim Yevmenkin error = sooptcopyout(sopt, &tmp, sizeof(tmp)); 149348698a83SMaksim Yevmenkin break; 149448698a83SMaksim Yevmenkin 149548698a83SMaksim Yevmenkin default: 149648698a83SMaksim Yevmenkin error = EINVAL; 149748698a83SMaksim Yevmenkin break; 149848698a83SMaksim Yevmenkin } 149948698a83SMaksim Yevmenkin break; 150048698a83SMaksim Yevmenkin 150148698a83SMaksim Yevmenkin case SOPT_SET: 150248698a83SMaksim Yevmenkin error = ENOPROTOOPT; 150348698a83SMaksim Yevmenkin break; 150448698a83SMaksim Yevmenkin 150548698a83SMaksim Yevmenkin default: 150648698a83SMaksim Yevmenkin error = EINVAL; 150748698a83SMaksim Yevmenkin break; 150848698a83SMaksim Yevmenkin } 150948698a83SMaksim Yevmenkin 151048698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 151148698a83SMaksim Yevmenkin 151248698a83SMaksim Yevmenkin return (error); 151348698a83SMaksim Yevmenkin } /* ng_btsocket_sco_ctloutput */ 151448698a83SMaksim Yevmenkin 151548698a83SMaksim Yevmenkin /* 151648698a83SMaksim Yevmenkin * Detach and destroy socket 151748698a83SMaksim Yevmenkin */ 151848698a83SMaksim Yevmenkin 151948698a83SMaksim Yevmenkin void 152048698a83SMaksim Yevmenkin ng_btsocket_sco_detach(struct socket *so) 152148698a83SMaksim Yevmenkin { 152248698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 152348698a83SMaksim Yevmenkin 152448698a83SMaksim Yevmenkin KASSERT(pcb != NULL, ("ng_btsocket_sco_detach: pcb == NULL")); 152548698a83SMaksim Yevmenkin 152648698a83SMaksim Yevmenkin if (ng_btsocket_sco_node == NULL) 152748698a83SMaksim Yevmenkin return; 152848698a83SMaksim Yevmenkin 152948698a83SMaksim Yevmenkin mtx_lock(&ng_btsocket_sco_sockets_mtx); 153048698a83SMaksim Yevmenkin mtx_lock(&pcb->pcb_mtx); 153148698a83SMaksim Yevmenkin 153248698a83SMaksim Yevmenkin if (pcb->flags & NG_BTSOCKET_SCO_TIMO) 153348698a83SMaksim Yevmenkin ng_btsocket_sco_untimeout(pcb); 153448698a83SMaksim Yevmenkin 153548698a83SMaksim Yevmenkin if (pcb->state == NG_BTSOCKET_SCO_OPEN) 153648698a83SMaksim Yevmenkin ng_btsocket_sco_send_lp_discon_req(pcb); 153748698a83SMaksim Yevmenkin 153848698a83SMaksim Yevmenkin pcb->state = NG_BTSOCKET_SCO_CLOSED; 153948698a83SMaksim Yevmenkin 154048698a83SMaksim Yevmenkin LIST_REMOVE(pcb, next); 154148698a83SMaksim Yevmenkin 154248698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 154348698a83SMaksim Yevmenkin mtx_unlock(&ng_btsocket_sco_sockets_mtx); 154448698a83SMaksim Yevmenkin 154548698a83SMaksim Yevmenkin mtx_destroy(&pcb->pcb_mtx); 154648698a83SMaksim Yevmenkin bzero(pcb, sizeof(*pcb)); 15471ede983cSDag-Erling Smørgrav free(pcb, M_NETGRAPH_BTSOCKET_SCO); 154848698a83SMaksim Yevmenkin 154948698a83SMaksim Yevmenkin soisdisconnected(so); 155048698a83SMaksim Yevmenkin so->so_pcb = NULL; 155148698a83SMaksim Yevmenkin } /* ng_btsocket_sco_detach */ 155248698a83SMaksim Yevmenkin 155348698a83SMaksim Yevmenkin /* 155448698a83SMaksim Yevmenkin * Disconnect socket 155548698a83SMaksim Yevmenkin */ 155648698a83SMaksim Yevmenkin 155748698a83SMaksim Yevmenkin int 155848698a83SMaksim Yevmenkin ng_btsocket_sco_disconnect(struct socket *so) 155948698a83SMaksim Yevmenkin { 156048698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 156148698a83SMaksim Yevmenkin 156248698a83SMaksim Yevmenkin if (pcb == NULL) 156348698a83SMaksim Yevmenkin return (EINVAL); 156448698a83SMaksim Yevmenkin if (ng_btsocket_sco_node == NULL) 156548698a83SMaksim Yevmenkin return (EINVAL); 156648698a83SMaksim Yevmenkin 156748698a83SMaksim Yevmenkin mtx_lock(&pcb->pcb_mtx); 156848698a83SMaksim Yevmenkin 156948698a83SMaksim Yevmenkin if (pcb->state == NG_BTSOCKET_SCO_DISCONNECTING) { 157048698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 157148698a83SMaksim Yevmenkin 157248698a83SMaksim Yevmenkin return (EINPROGRESS); 157348698a83SMaksim Yevmenkin } 157448698a83SMaksim Yevmenkin 157548698a83SMaksim Yevmenkin if (pcb->flags & NG_BTSOCKET_SCO_TIMO) 157648698a83SMaksim Yevmenkin ng_btsocket_sco_untimeout(pcb); 157748698a83SMaksim Yevmenkin 157848698a83SMaksim Yevmenkin if (pcb->state == NG_BTSOCKET_SCO_OPEN) { 157948698a83SMaksim Yevmenkin ng_btsocket_sco_send_lp_discon_req(pcb); 158048698a83SMaksim Yevmenkin 158148698a83SMaksim Yevmenkin pcb->state = NG_BTSOCKET_SCO_DISCONNECTING; 158248698a83SMaksim Yevmenkin soisdisconnecting(so); 158348698a83SMaksim Yevmenkin 158448698a83SMaksim Yevmenkin ng_btsocket_sco_timeout(pcb); 158548698a83SMaksim Yevmenkin } else { 158648698a83SMaksim Yevmenkin pcb->state = NG_BTSOCKET_SCO_CLOSED; 158748698a83SMaksim Yevmenkin soisdisconnected(so); 158848698a83SMaksim Yevmenkin } 158948698a83SMaksim Yevmenkin 159048698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 159148698a83SMaksim Yevmenkin 159248698a83SMaksim Yevmenkin return (0); 159348698a83SMaksim Yevmenkin } /* ng_btsocket_sco_disconnect */ 159448698a83SMaksim Yevmenkin 159548698a83SMaksim Yevmenkin /* 159648698a83SMaksim Yevmenkin * Listen on socket 159748698a83SMaksim Yevmenkin */ 159848698a83SMaksim Yevmenkin 159948698a83SMaksim Yevmenkin int 160048698a83SMaksim Yevmenkin ng_btsocket_sco_listen(struct socket *so, int backlog, struct thread *td) 160148698a83SMaksim Yevmenkin { 160248698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 160348698a83SMaksim Yevmenkin int error; 160448698a83SMaksim Yevmenkin 160548698a83SMaksim Yevmenkin if (pcb == NULL) 160648698a83SMaksim Yevmenkin return (EINVAL); 160748698a83SMaksim Yevmenkin if (ng_btsocket_sco_node == NULL) 160848698a83SMaksim Yevmenkin return (EINVAL); 160948698a83SMaksim Yevmenkin 161048698a83SMaksim Yevmenkin SOCK_LOCK(so); 161148698a83SMaksim Yevmenkin mtx_lock(&pcb->pcb_mtx); 161248698a83SMaksim Yevmenkin 161348698a83SMaksim Yevmenkin error = solisten_proto_check(so); 161448698a83SMaksim Yevmenkin if (error != 0) 161548698a83SMaksim Yevmenkin goto out; 161648698a83SMaksim Yevmenkin #if 0 161748698a83SMaksim Yevmenkin if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0) { 161848698a83SMaksim Yevmenkin error = EDESTADDRREQ; 161948698a83SMaksim Yevmenkin goto out; 162048698a83SMaksim Yevmenkin } 162148698a83SMaksim Yevmenkin #endif 162248698a83SMaksim Yevmenkin solisten_proto(so, backlog); 162348698a83SMaksim Yevmenkin out: 162448698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 162548698a83SMaksim Yevmenkin SOCK_UNLOCK(so); 162648698a83SMaksim Yevmenkin 162748698a83SMaksim Yevmenkin return (error); 162848698a83SMaksim Yevmenkin } /* ng_btsocket_listen */ 162948698a83SMaksim Yevmenkin 163048698a83SMaksim Yevmenkin /* 163148698a83SMaksim Yevmenkin * Get peer address 163248698a83SMaksim Yevmenkin */ 163348698a83SMaksim Yevmenkin 163448698a83SMaksim Yevmenkin int 163548698a83SMaksim Yevmenkin ng_btsocket_sco_peeraddr(struct socket *so, struct sockaddr **nam) 163648698a83SMaksim Yevmenkin { 163748698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 163848698a83SMaksim Yevmenkin struct sockaddr_sco sa; 163948698a83SMaksim Yevmenkin 164048698a83SMaksim Yevmenkin if (pcb == NULL) 164148698a83SMaksim Yevmenkin return (EINVAL); 164248698a83SMaksim Yevmenkin if (ng_btsocket_sco_node == NULL) 164348698a83SMaksim Yevmenkin return (EINVAL); 164448698a83SMaksim Yevmenkin 164548698a83SMaksim Yevmenkin mtx_lock(&pcb->pcb_mtx); 164648698a83SMaksim Yevmenkin bcopy(&pcb->dst, &sa.sco_bdaddr, sizeof(sa.sco_bdaddr)); 164748698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 164848698a83SMaksim Yevmenkin 164948698a83SMaksim Yevmenkin sa.sco_len = sizeof(sa); 165048698a83SMaksim Yevmenkin sa.sco_family = AF_BLUETOOTH; 165148698a83SMaksim Yevmenkin 165248698a83SMaksim Yevmenkin *nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT); 165348698a83SMaksim Yevmenkin 165448698a83SMaksim Yevmenkin return ((*nam == NULL)? ENOMEM : 0); 165548698a83SMaksim Yevmenkin } /* ng_btsocket_sco_peeraddr */ 165648698a83SMaksim Yevmenkin 165748698a83SMaksim Yevmenkin /* 165848698a83SMaksim Yevmenkin * Send data to socket 165948698a83SMaksim Yevmenkin */ 166048698a83SMaksim Yevmenkin 166148698a83SMaksim Yevmenkin int 166248698a83SMaksim Yevmenkin ng_btsocket_sco_send(struct socket *so, int flags, struct mbuf *m, 166348698a83SMaksim Yevmenkin struct sockaddr *nam, struct mbuf *control, struct thread *td) 166448698a83SMaksim Yevmenkin { 166548698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_t *pcb = so2sco_pcb(so); 166648698a83SMaksim Yevmenkin int error = 0; 166748698a83SMaksim Yevmenkin 166848698a83SMaksim Yevmenkin if (ng_btsocket_sco_node == NULL) { 166948698a83SMaksim Yevmenkin error = ENETDOWN; 167048698a83SMaksim Yevmenkin goto drop; 167148698a83SMaksim Yevmenkin } 167248698a83SMaksim Yevmenkin 167348698a83SMaksim Yevmenkin /* Check socket and input */ 167448698a83SMaksim Yevmenkin if (pcb == NULL || m == NULL || control != NULL) { 167548698a83SMaksim Yevmenkin error = EINVAL; 167648698a83SMaksim Yevmenkin goto drop; 167748698a83SMaksim Yevmenkin } 167848698a83SMaksim Yevmenkin 167948698a83SMaksim Yevmenkin mtx_lock(&pcb->pcb_mtx); 168048698a83SMaksim Yevmenkin 168148698a83SMaksim Yevmenkin /* Make sure socket is connected */ 168248698a83SMaksim Yevmenkin if (pcb->state != NG_BTSOCKET_SCO_OPEN) { 168348698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 168448698a83SMaksim Yevmenkin error = ENOTCONN; 168548698a83SMaksim Yevmenkin goto drop; 168648698a83SMaksim Yevmenkin } 168748698a83SMaksim Yevmenkin 168848698a83SMaksim Yevmenkin /* Check route */ 168948698a83SMaksim Yevmenkin if (pcb->rt == NULL || 169048698a83SMaksim Yevmenkin pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) { 169148698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 169248698a83SMaksim Yevmenkin error = ENETDOWN; 169348698a83SMaksim Yevmenkin goto drop; 169448698a83SMaksim Yevmenkin } 169548698a83SMaksim Yevmenkin 169648698a83SMaksim Yevmenkin /* Check packet size */ 169748698a83SMaksim Yevmenkin if (m->m_pkthdr.len > pcb->rt->pkt_size) { 169848698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ERR( 169948698a83SMaksim Yevmenkin "%s: Packet too big, len=%d, pkt_size=%d\n", 170048698a83SMaksim Yevmenkin __func__, m->m_pkthdr.len, pcb->rt->pkt_size); 170148698a83SMaksim Yevmenkin 170248698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 170348698a83SMaksim Yevmenkin error = EMSGSIZE; 170448698a83SMaksim Yevmenkin goto drop; 170548698a83SMaksim Yevmenkin } 170648698a83SMaksim Yevmenkin 170748698a83SMaksim Yevmenkin /* 170848698a83SMaksim Yevmenkin * First put packet on socket send queue. Then check if we have 170948698a83SMaksim Yevmenkin * pending timeout. If we do not have timeout then we must send 171048698a83SMaksim Yevmenkin * packet and schedule timeout. Otherwise do nothing and wait for 171148698a83SMaksim Yevmenkin * NGM_HCI_SYNC_CON_QUEUE message. 171248698a83SMaksim Yevmenkin */ 171348698a83SMaksim Yevmenkin 171448698a83SMaksim Yevmenkin sbappendrecord(&pcb->so->so_snd, m); 171548698a83SMaksim Yevmenkin m = NULL; 171648698a83SMaksim Yevmenkin 171748698a83SMaksim Yevmenkin if (!(pcb->flags & NG_BTSOCKET_SCO_TIMO)) { 171848698a83SMaksim Yevmenkin error = ng_btsocket_sco_send2(pcb); 171948698a83SMaksim Yevmenkin if (error == 0) 172048698a83SMaksim Yevmenkin ng_btsocket_sco_timeout(pcb); 172148698a83SMaksim Yevmenkin else 172248698a83SMaksim Yevmenkin sbdroprecord(&pcb->so->so_snd); /* XXX */ 172348698a83SMaksim Yevmenkin } 172448698a83SMaksim Yevmenkin 172548698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 172648698a83SMaksim Yevmenkin drop: 172748698a83SMaksim Yevmenkin NG_FREE_M(m); /* checks for != NULL */ 172848698a83SMaksim Yevmenkin NG_FREE_M(control); 172948698a83SMaksim Yevmenkin 173048698a83SMaksim Yevmenkin return (error); 173148698a83SMaksim Yevmenkin } /* ng_btsocket_sco_send */ 173248698a83SMaksim Yevmenkin 173348698a83SMaksim Yevmenkin /* 173448698a83SMaksim Yevmenkin * Send first packet in the socket queue to the SCO layer 173548698a83SMaksim Yevmenkin */ 173648698a83SMaksim Yevmenkin 173748698a83SMaksim Yevmenkin static int 173848698a83SMaksim Yevmenkin ng_btsocket_sco_send2(ng_btsocket_sco_pcb_p pcb) 173948698a83SMaksim Yevmenkin { 174048698a83SMaksim Yevmenkin struct mbuf *m = NULL; 174148698a83SMaksim Yevmenkin ng_hci_scodata_pkt_t *hdr = NULL; 174248698a83SMaksim Yevmenkin int error = 0; 174348698a83SMaksim Yevmenkin 174448698a83SMaksim Yevmenkin mtx_assert(&pcb->pcb_mtx, MA_OWNED); 174548698a83SMaksim Yevmenkin 174648698a83SMaksim Yevmenkin while (pcb->rt->pending < pcb->rt->num_pkts && 174748698a83SMaksim Yevmenkin pcb->so->so_snd.sb_cc > 0) { 174848698a83SMaksim Yevmenkin /* Get a copy of the first packet on send queue */ 1749*eb1b1807SGleb Smirnoff m = m_dup(pcb->so->so_snd.sb_mb, M_NOWAIT); 175048698a83SMaksim Yevmenkin if (m == NULL) { 175148698a83SMaksim Yevmenkin error = ENOBUFS; 175248698a83SMaksim Yevmenkin break; 175348698a83SMaksim Yevmenkin } 175448698a83SMaksim Yevmenkin 175548698a83SMaksim Yevmenkin /* Create SCO packet header */ 1756*eb1b1807SGleb Smirnoff M_PREPEND(m, sizeof(*hdr), M_NOWAIT); 175748698a83SMaksim Yevmenkin if (m != NULL) 175848698a83SMaksim Yevmenkin if (m->m_len < sizeof(*hdr)) 175948698a83SMaksim Yevmenkin m = m_pullup(m, sizeof(*hdr)); 176048698a83SMaksim Yevmenkin 176148698a83SMaksim Yevmenkin if (m == NULL) { 176248698a83SMaksim Yevmenkin error = ENOBUFS; 176348698a83SMaksim Yevmenkin break; 176448698a83SMaksim Yevmenkin } 176548698a83SMaksim Yevmenkin 176648698a83SMaksim Yevmenkin /* Fill in the header */ 176748698a83SMaksim Yevmenkin hdr = mtod(m, ng_hci_scodata_pkt_t *); 176848698a83SMaksim Yevmenkin hdr->type = NG_HCI_SCO_DATA_PKT; 176948698a83SMaksim Yevmenkin hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE(pcb->con_handle, 0, 0)); 177048698a83SMaksim Yevmenkin hdr->length = m->m_pkthdr.len - sizeof(*hdr); 177148698a83SMaksim Yevmenkin 177248698a83SMaksim Yevmenkin /* Send packet */ 177348698a83SMaksim Yevmenkin NG_SEND_DATA_ONLY(error, pcb->rt->hook, m); 177448698a83SMaksim Yevmenkin if (error != 0) 177548698a83SMaksim Yevmenkin break; 177648698a83SMaksim Yevmenkin 177748698a83SMaksim Yevmenkin pcb->rt->pending ++; 177848698a83SMaksim Yevmenkin } 177948698a83SMaksim Yevmenkin 178048698a83SMaksim Yevmenkin return ((pcb->rt->pending > 0)? 0 : error); 178148698a83SMaksim Yevmenkin } /* ng_btsocket_sco_send2 */ 178248698a83SMaksim Yevmenkin 178348698a83SMaksim Yevmenkin /* 178448698a83SMaksim Yevmenkin * Get socket address 178548698a83SMaksim Yevmenkin */ 178648698a83SMaksim Yevmenkin 178748698a83SMaksim Yevmenkin int 178848698a83SMaksim Yevmenkin ng_btsocket_sco_sockaddr(struct socket *so, struct sockaddr **nam) 178948698a83SMaksim Yevmenkin { 179048698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 179148698a83SMaksim Yevmenkin struct sockaddr_sco sa; 179248698a83SMaksim Yevmenkin 179348698a83SMaksim Yevmenkin if (pcb == NULL) 179448698a83SMaksim Yevmenkin return (EINVAL); 179548698a83SMaksim Yevmenkin if (ng_btsocket_sco_node == NULL) 179648698a83SMaksim Yevmenkin return (EINVAL); 179748698a83SMaksim Yevmenkin 179848698a83SMaksim Yevmenkin mtx_lock(&pcb->pcb_mtx); 179948698a83SMaksim Yevmenkin bcopy(&pcb->src, &sa.sco_bdaddr, sizeof(sa.sco_bdaddr)); 180048698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 180148698a83SMaksim Yevmenkin 180248698a83SMaksim Yevmenkin sa.sco_len = sizeof(sa); 180348698a83SMaksim Yevmenkin sa.sco_family = AF_BLUETOOTH; 180448698a83SMaksim Yevmenkin 180548698a83SMaksim Yevmenkin *nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT); 180648698a83SMaksim Yevmenkin 180748698a83SMaksim Yevmenkin return ((*nam == NULL)? ENOMEM : 0); 180848698a83SMaksim Yevmenkin } /* ng_btsocket_sco_sockaddr */ 180948698a83SMaksim Yevmenkin 181048698a83SMaksim Yevmenkin /***************************************************************************** 181148698a83SMaksim Yevmenkin ***************************************************************************** 181248698a83SMaksim Yevmenkin ** Misc. functions 181348698a83SMaksim Yevmenkin ***************************************************************************** 181448698a83SMaksim Yevmenkin *****************************************************************************/ 181548698a83SMaksim Yevmenkin 181648698a83SMaksim Yevmenkin /* 181748698a83SMaksim Yevmenkin * Look for the socket that listens on given bdaddr. 181848698a83SMaksim Yevmenkin * Returns exact or close match (if any). 181948698a83SMaksim Yevmenkin * Caller must hold ng_btsocket_sco_sockets_mtx. 182048698a83SMaksim Yevmenkin * Returns with locked pcb. 182148698a83SMaksim Yevmenkin */ 182248698a83SMaksim Yevmenkin 182348698a83SMaksim Yevmenkin static ng_btsocket_sco_pcb_p 182448698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_by_addr(bdaddr_p bdaddr) 182548698a83SMaksim Yevmenkin { 182648698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_p p = NULL, p1 = NULL; 182748698a83SMaksim Yevmenkin 182848698a83SMaksim Yevmenkin mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED); 182948698a83SMaksim Yevmenkin 183048698a83SMaksim Yevmenkin LIST_FOREACH(p, &ng_btsocket_sco_sockets, next) { 183148698a83SMaksim Yevmenkin mtx_lock(&p->pcb_mtx); 183248698a83SMaksim Yevmenkin 183348698a83SMaksim Yevmenkin if (p->so == NULL || !(p->so->so_options & SO_ACCEPTCONN)) { 183448698a83SMaksim Yevmenkin mtx_unlock(&p->pcb_mtx); 183548698a83SMaksim Yevmenkin continue; 183648698a83SMaksim Yevmenkin } 183748698a83SMaksim Yevmenkin 183848698a83SMaksim Yevmenkin if (bcmp(&p->src, bdaddr, sizeof(p->src)) == 0) 183948698a83SMaksim Yevmenkin return (p); /* return with locked pcb */ 184048698a83SMaksim Yevmenkin 184148698a83SMaksim Yevmenkin if (bcmp(&p->src, NG_HCI_BDADDR_ANY, sizeof(p->src)) == 0) 184248698a83SMaksim Yevmenkin p1 = p; 184348698a83SMaksim Yevmenkin 184448698a83SMaksim Yevmenkin mtx_unlock(&p->pcb_mtx); 184548698a83SMaksim Yevmenkin } 184648698a83SMaksim Yevmenkin 18479b5b5167SMaksim Yevmenkin if (p1 != NULL) 18489b5b5167SMaksim Yevmenkin mtx_lock(&p1->pcb_mtx); 18499b5b5167SMaksim Yevmenkin 185048698a83SMaksim Yevmenkin return (p1); 185148698a83SMaksim Yevmenkin } /* ng_btsocket_sco_pcb_by_addr */ 185248698a83SMaksim Yevmenkin 185348698a83SMaksim Yevmenkin /* 185448698a83SMaksim Yevmenkin * Look for the socket that assigned to given source address and handle. 185548698a83SMaksim Yevmenkin * Caller must hold ng_btsocket_sco_sockets_mtx. 185648698a83SMaksim Yevmenkin * Returns with locked pcb. 185748698a83SMaksim Yevmenkin */ 185848698a83SMaksim Yevmenkin 185948698a83SMaksim Yevmenkin static ng_btsocket_sco_pcb_p 186048698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_by_handle(bdaddr_p src, int con_handle) 186148698a83SMaksim Yevmenkin { 186248698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_p p = NULL; 186348698a83SMaksim Yevmenkin 186448698a83SMaksim Yevmenkin mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED); 186548698a83SMaksim Yevmenkin 186648698a83SMaksim Yevmenkin LIST_FOREACH(p, &ng_btsocket_sco_sockets, next) { 186748698a83SMaksim Yevmenkin mtx_lock(&p->pcb_mtx); 186848698a83SMaksim Yevmenkin 186948698a83SMaksim Yevmenkin if (p->con_handle == con_handle && 187048698a83SMaksim Yevmenkin bcmp(src, &p->src, sizeof(p->src)) == 0) 187148698a83SMaksim Yevmenkin return (p); /* return with locked pcb */ 187248698a83SMaksim Yevmenkin 187348698a83SMaksim Yevmenkin mtx_unlock(&p->pcb_mtx); 187448698a83SMaksim Yevmenkin } 187548698a83SMaksim Yevmenkin 187648698a83SMaksim Yevmenkin return (NULL); 187748698a83SMaksim Yevmenkin } /* ng_btsocket_sco_pcb_by_handle */ 187848698a83SMaksim Yevmenkin 187948698a83SMaksim Yevmenkin /* 188048698a83SMaksim Yevmenkin * Look for the socket in CONNECTING state with given source and destination 188148698a83SMaksim Yevmenkin * addresses. Caller must hold ng_btsocket_sco_sockets_mtx. 188248698a83SMaksim Yevmenkin * Returns with locked pcb. 188348698a83SMaksim Yevmenkin */ 188448698a83SMaksim Yevmenkin 188548698a83SMaksim Yevmenkin static ng_btsocket_sco_pcb_p 188648698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_by_addrs(bdaddr_p src, bdaddr_p dst) 188748698a83SMaksim Yevmenkin { 188848698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_p p = NULL; 188948698a83SMaksim Yevmenkin 189048698a83SMaksim Yevmenkin mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED); 189148698a83SMaksim Yevmenkin 189248698a83SMaksim Yevmenkin LIST_FOREACH(p, &ng_btsocket_sco_sockets, next) { 189348698a83SMaksim Yevmenkin mtx_lock(&p->pcb_mtx); 189448698a83SMaksim Yevmenkin 189548698a83SMaksim Yevmenkin if (p->state == NG_BTSOCKET_SCO_CONNECTING && 189648698a83SMaksim Yevmenkin bcmp(src, &p->src, sizeof(p->src)) == 0 && 189748698a83SMaksim Yevmenkin bcmp(dst, &p->dst, sizeof(p->dst)) == 0) 189848698a83SMaksim Yevmenkin return (p); /* return with locked pcb */ 189948698a83SMaksim Yevmenkin 190048698a83SMaksim Yevmenkin mtx_unlock(&p->pcb_mtx); 190148698a83SMaksim Yevmenkin } 190248698a83SMaksim Yevmenkin 190348698a83SMaksim Yevmenkin return (NULL); 190448698a83SMaksim Yevmenkin } /* ng_btsocket_sco_pcb_by_addrs */ 190548698a83SMaksim Yevmenkin 190648698a83SMaksim Yevmenkin /* 190748698a83SMaksim Yevmenkin * Set timeout on socket 190848698a83SMaksim Yevmenkin */ 190948698a83SMaksim Yevmenkin 191048698a83SMaksim Yevmenkin static void 191148698a83SMaksim Yevmenkin ng_btsocket_sco_timeout(ng_btsocket_sco_pcb_p pcb) 191248698a83SMaksim Yevmenkin { 191348698a83SMaksim Yevmenkin mtx_assert(&pcb->pcb_mtx, MA_OWNED); 191448698a83SMaksim Yevmenkin 191548698a83SMaksim Yevmenkin if (!(pcb->flags & NG_BTSOCKET_SCO_TIMO)) { 191648698a83SMaksim Yevmenkin pcb->flags |= NG_BTSOCKET_SCO_TIMO; 191748698a83SMaksim Yevmenkin callout_reset(&pcb->timo, bluetooth_sco_rtx_timeout(), 191848698a83SMaksim Yevmenkin ng_btsocket_sco_process_timeout, pcb); 191948698a83SMaksim Yevmenkin } else 192048698a83SMaksim Yevmenkin KASSERT(0, 192148698a83SMaksim Yevmenkin ("%s: Duplicated socket timeout?!\n", __func__)); 192248698a83SMaksim Yevmenkin } /* ng_btsocket_sco_timeout */ 192348698a83SMaksim Yevmenkin 192448698a83SMaksim Yevmenkin /* 192548698a83SMaksim Yevmenkin * Unset timeout on socket 192648698a83SMaksim Yevmenkin */ 192748698a83SMaksim Yevmenkin 192848698a83SMaksim Yevmenkin static void 192948698a83SMaksim Yevmenkin ng_btsocket_sco_untimeout(ng_btsocket_sco_pcb_p pcb) 193048698a83SMaksim Yevmenkin { 193148698a83SMaksim Yevmenkin mtx_assert(&pcb->pcb_mtx, MA_OWNED); 193248698a83SMaksim Yevmenkin 193348698a83SMaksim Yevmenkin if (pcb->flags & NG_BTSOCKET_SCO_TIMO) { 193448698a83SMaksim Yevmenkin callout_stop(&pcb->timo); 193548698a83SMaksim Yevmenkin pcb->flags &= ~NG_BTSOCKET_SCO_TIMO; 193648698a83SMaksim Yevmenkin } else 193748698a83SMaksim Yevmenkin KASSERT(0, 193848698a83SMaksim Yevmenkin ("%s: No socket timeout?!\n", __func__)); 193948698a83SMaksim Yevmenkin } /* ng_btsocket_sco_untimeout */ 194048698a83SMaksim Yevmenkin 194148698a83SMaksim Yevmenkin /* 194248698a83SMaksim Yevmenkin * Process timeout on socket 194348698a83SMaksim Yevmenkin */ 194448698a83SMaksim Yevmenkin 194548698a83SMaksim Yevmenkin static void 194648698a83SMaksim Yevmenkin ng_btsocket_sco_process_timeout(void *xpcb) 194748698a83SMaksim Yevmenkin { 194848698a83SMaksim Yevmenkin ng_btsocket_sco_pcb_p pcb = (ng_btsocket_sco_pcb_p) xpcb; 194948698a83SMaksim Yevmenkin 195048698a83SMaksim Yevmenkin mtx_lock(&pcb->pcb_mtx); 195148698a83SMaksim Yevmenkin 195248698a83SMaksim Yevmenkin pcb->flags &= ~NG_BTSOCKET_SCO_TIMO; 195348698a83SMaksim Yevmenkin pcb->so->so_error = ETIMEDOUT; 195448698a83SMaksim Yevmenkin 195548698a83SMaksim Yevmenkin switch (pcb->state) { 195648698a83SMaksim Yevmenkin case NG_BTSOCKET_SCO_CONNECTING: 195748698a83SMaksim Yevmenkin /* Connect timeout - close the socket */ 195848698a83SMaksim Yevmenkin pcb->state = NG_BTSOCKET_SCO_CLOSED; 195948698a83SMaksim Yevmenkin soisdisconnected(pcb->so); 196048698a83SMaksim Yevmenkin break; 196148698a83SMaksim Yevmenkin 196248698a83SMaksim Yevmenkin case NG_BTSOCKET_SCO_OPEN: 196348698a83SMaksim Yevmenkin /* Send timeout - did not get NGM_HCI_SYNC_CON_QUEUE */ 196448698a83SMaksim Yevmenkin sbdroprecord(&pcb->so->so_snd); 196548698a83SMaksim Yevmenkin sowwakeup(pcb->so); 196648698a83SMaksim Yevmenkin /* XXX FIXME what to do with pcb->rt->pending??? */ 196748698a83SMaksim Yevmenkin break; 196848698a83SMaksim Yevmenkin 196948698a83SMaksim Yevmenkin case NG_BTSOCKET_SCO_DISCONNECTING: 197048698a83SMaksim Yevmenkin /* Disconnect timeout - disconnect the socket anyway */ 197148698a83SMaksim Yevmenkin pcb->state = NG_BTSOCKET_SCO_CLOSED; 197248698a83SMaksim Yevmenkin soisdisconnected(pcb->so); 197348698a83SMaksim Yevmenkin break; 197448698a83SMaksim Yevmenkin 197548698a83SMaksim Yevmenkin default: 197648698a83SMaksim Yevmenkin NG_BTSOCKET_SCO_ERR( 197748698a83SMaksim Yevmenkin "%s: Invalid socket state=%d\n", __func__, pcb->state); 197848698a83SMaksim Yevmenkin break; 197948698a83SMaksim Yevmenkin } 198048698a83SMaksim Yevmenkin 198148698a83SMaksim Yevmenkin mtx_unlock(&pcb->pcb_mtx); 198248698a83SMaksim Yevmenkin } /* ng_btsocket_sco_process_timeout */ 198348698a83SMaksim Yevmenkin 1984