1181033Semax/* 2181033Semax * ng_btsocket_sco.c 3181033Semax */ 4181033Semax 5181033Semax/*- 6181033Semax * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> 7181033Semax * All rights reserved. 8181033Semax * 9181033Semax * Redistribution and use in source and binary forms, with or without 10181033Semax * modification, are permitted provided that the following conditions 11181033Semax * are met: 12181033Semax * 1. Redistributions of source code must retain the above copyright 13181033Semax * notice, this list of conditions and the following disclaimer. 14181033Semax * 2. Redistributions in binary form must reproduce the above copyright 15181033Semax * notice, this list of conditions and the following disclaimer in the 16181033Semax * documentation and/or other materials provided with the distribution. 17181033Semax * 18181033Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19181033Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20181033Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21181033Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22181033Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23181033Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24181033Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25181033Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26181033Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27181033Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28181033Semax * SUCH DAMAGE. 29181033Semax * 30181033Semax * $Id: ng_btsocket_sco.c,v 1.2 2005/10/31 18:08:51 max Exp $ 31181033Semax * $FreeBSD$ 32181033Semax */ 33181033Semax 34181033Semax#include <sys/param.h> 35181033Semax#include <sys/systm.h> 36181033Semax#include <sys/bitstring.h> 37181033Semax#include <sys/domain.h> 38181033Semax#include <sys/endian.h> 39181033Semax#include <sys/errno.h> 40181033Semax#include <sys/filedesc.h> 41181033Semax#include <sys/ioccom.h> 42181033Semax#include <sys/kernel.h> 43181033Semax#include <sys/lock.h> 44181033Semax#include <sys/malloc.h> 45181033Semax#include <sys/mbuf.h> 46181033Semax#include <sys/mutex.h> 47181033Semax#include <sys/protosw.h> 48181033Semax#include <sys/queue.h> 49181033Semax#include <sys/socket.h> 50181033Semax#include <sys/socketvar.h> 51181033Semax#include <sys/sysctl.h> 52181033Semax#include <sys/taskqueue.h> 53218757Sbz 54218757Sbz#include <net/vnet.h> 55218757Sbz 56181033Semax#include <netgraph/ng_message.h> 57181033Semax#include <netgraph/netgraph.h> 58181033Semax#include <netgraph/bluetooth/include/ng_bluetooth.h> 59181033Semax#include <netgraph/bluetooth/include/ng_hci.h> 60181033Semax#include <netgraph/bluetooth/include/ng_l2cap.h> 61181033Semax#include <netgraph/bluetooth/include/ng_btsocket.h> 62181033Semax#include <netgraph/bluetooth/include/ng_btsocket_sco.h> 63181033Semax 64181033Semax/* MALLOC define */ 65181033Semax#ifdef NG_SEPARATE_MALLOC 66227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_SCO, "netgraph_btsocks_sco", 67181033Semax "Netgraph Bluetooth SCO sockets"); 68181033Semax#else 69181033Semax#define M_NETGRAPH_BTSOCKET_SCO M_NETGRAPH 70181033Semax#endif /* NG_SEPARATE_MALLOC */ 71181033Semax 72181033Semax/* Netgraph node methods */ 73181033Semaxstatic ng_constructor_t ng_btsocket_sco_node_constructor; 74181033Semaxstatic ng_rcvmsg_t ng_btsocket_sco_node_rcvmsg; 75181033Semaxstatic ng_shutdown_t ng_btsocket_sco_node_shutdown; 76181033Semaxstatic ng_newhook_t ng_btsocket_sco_node_newhook; 77181033Semaxstatic ng_connect_t ng_btsocket_sco_node_connect; 78181033Semaxstatic ng_rcvdata_t ng_btsocket_sco_node_rcvdata; 79181033Semaxstatic ng_disconnect_t ng_btsocket_sco_node_disconnect; 80181033Semax 81181033Semaxstatic void ng_btsocket_sco_input (void *, int); 82181033Semaxstatic void ng_btsocket_sco_rtclean (void *, int); 83181033Semax 84181033Semax/* Netgraph type descriptor */ 85181033Semaxstatic struct ng_type typestruct = { 86181033Semax .version = NG_ABI_VERSION, 87181033Semax .name = NG_BTSOCKET_SCO_NODE_TYPE, 88181033Semax .constructor = ng_btsocket_sco_node_constructor, 89181033Semax .rcvmsg = ng_btsocket_sco_node_rcvmsg, 90181033Semax .shutdown = ng_btsocket_sco_node_shutdown, 91181033Semax .newhook = ng_btsocket_sco_node_newhook, 92181033Semax .connect = ng_btsocket_sco_node_connect, 93181033Semax .rcvdata = ng_btsocket_sco_node_rcvdata, 94181033Semax .disconnect = ng_btsocket_sco_node_disconnect, 95181033Semax}; 96181033Semax 97181033Semax/* Globals */ 98181033Semaxstatic u_int32_t ng_btsocket_sco_debug_level; 99181033Semaxstatic node_p ng_btsocket_sco_node; 100181033Semaxstatic struct ng_bt_itemq ng_btsocket_sco_queue; 101181033Semaxstatic struct mtx ng_btsocket_sco_queue_mtx; 102181033Semaxstatic struct task ng_btsocket_sco_queue_task; 103181033Semaxstatic struct mtx ng_btsocket_sco_sockets_mtx; 104181033Semaxstatic LIST_HEAD(, ng_btsocket_sco_pcb) ng_btsocket_sco_sockets; 105181033Semaxstatic LIST_HEAD(, ng_btsocket_sco_rtentry) ng_btsocket_sco_rt; 106181033Semaxstatic struct mtx ng_btsocket_sco_rt_mtx; 107181033Semaxstatic struct task ng_btsocket_sco_rt_task; 108181093Semaxstatic struct timeval ng_btsocket_sco_lasttime; 109181093Semaxstatic int ng_btsocket_sco_curpps; 110181033Semax 111181033Semax/* Sysctl tree */ 112181033SemaxSYSCTL_DECL(_net_bluetooth_sco_sockets); 113227309Sedstatic SYSCTL_NODE(_net_bluetooth_sco_sockets, OID_AUTO, seq, CTLFLAG_RW, 114181033Semax 0, "Bluetooth SEQPACKET SCO sockets family"); 115217320SmdfSYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, debug_level, 116181033Semax CTLFLAG_RW, 117181033Semax &ng_btsocket_sco_debug_level, NG_BTSOCKET_WARN_LEVEL, 118181033Semax "Bluetooth SEQPACKET SCO sockets debug level"); 119217320SmdfSYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, queue_len, 120181033Semax CTLFLAG_RD, 121181033Semax &ng_btsocket_sco_queue.len, 0, 122181033Semax "Bluetooth SEQPACKET SCO sockets input queue length"); 123217320SmdfSYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, queue_maxlen, 124181033Semax CTLFLAG_RD, 125181033Semax &ng_btsocket_sco_queue.maxlen, 0, 126181033Semax "Bluetooth SEQPACKET SCO sockets input queue max. length"); 127217320SmdfSYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, queue_drops, 128181033Semax CTLFLAG_RD, 129181033Semax &ng_btsocket_sco_queue.drops, 0, 130181033Semax "Bluetooth SEQPACKET SCO sockets input queue drops"); 131181033Semax 132181033Semax/* Debug */ 133181033Semax#define NG_BTSOCKET_SCO_INFO \ 134181093Semax if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_INFO_LEVEL && \ 135181093Semax ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \ 136181033Semax printf 137181033Semax 138181033Semax#define NG_BTSOCKET_SCO_WARN \ 139181093Semax if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_WARN_LEVEL && \ 140181093Semax ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \ 141181033Semax printf 142181033Semax 143181033Semax#define NG_BTSOCKET_SCO_ERR \ 144181093Semax if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_ERR_LEVEL && \ 145181093Semax ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \ 146181033Semax printf 147181033Semax 148181033Semax#define NG_BTSOCKET_SCO_ALERT \ 149181093Semax if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_ALERT_LEVEL && \ 150181093Semax ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \ 151181033Semax printf 152181033Semax 153181033Semax/* 154181033Semax * Netgraph message processing routines 155181033Semax */ 156181033Semax 157181033Semaxstatic int ng_btsocket_sco_process_lp_con_cfm 158181033Semax (struct ng_mesg *, ng_btsocket_sco_rtentry_p); 159181033Semaxstatic int ng_btsocket_sco_process_lp_con_ind 160181033Semax (struct ng_mesg *, ng_btsocket_sco_rtentry_p); 161181033Semaxstatic int ng_btsocket_sco_process_lp_discon_ind 162181033Semax (struct ng_mesg *, ng_btsocket_sco_rtentry_p); 163181033Semax 164181033Semax/* 165181033Semax * Send LP messages to the lower layer 166181033Semax */ 167181033Semax 168181033Semaxstatic int ng_btsocket_sco_send_lp_con_req 169181033Semax (ng_btsocket_sco_pcb_p); 170181033Semaxstatic int ng_btsocket_sco_send_lp_con_rsp 171181033Semax (ng_btsocket_sco_rtentry_p, bdaddr_p, int); 172181033Semaxstatic int ng_btsocket_sco_send_lp_discon_req 173181033Semax (ng_btsocket_sco_pcb_p); 174181033Semax 175181033Semaxstatic int ng_btsocket_sco_send2 176181033Semax (ng_btsocket_sco_pcb_p); 177181033Semax 178181033Semax/* 179181033Semax * Timeout processing routines 180181033Semax */ 181181033Semax 182181033Semaxstatic void ng_btsocket_sco_timeout (ng_btsocket_sco_pcb_p); 183181033Semaxstatic void ng_btsocket_sco_untimeout (ng_btsocket_sco_pcb_p); 184181033Semaxstatic void ng_btsocket_sco_process_timeout (void *); 185181033Semax 186181033Semax/* 187181033Semax * Other stuff 188181033Semax */ 189181033Semax 190181033Semaxstatic ng_btsocket_sco_pcb_p ng_btsocket_sco_pcb_by_addr(bdaddr_p); 191181033Semaxstatic ng_btsocket_sco_pcb_p ng_btsocket_sco_pcb_by_handle(bdaddr_p, int); 192181033Semaxstatic ng_btsocket_sco_pcb_p ng_btsocket_sco_pcb_by_addrs(bdaddr_p, bdaddr_p); 193181033Semax 194181033Semax#define ng_btsocket_sco_wakeup_input_task() \ 195181033Semax taskqueue_enqueue(taskqueue_swi, &ng_btsocket_sco_queue_task) 196181033Semax 197181033Semax#define ng_btsocket_sco_wakeup_route_task() \ 198181033Semax taskqueue_enqueue(taskqueue_swi, &ng_btsocket_sco_rt_task) 199181033Semax 200181033Semax/***************************************************************************** 201181033Semax ***************************************************************************** 202181033Semax ** Netgraph node interface 203181033Semax ***************************************************************************** 204181033Semax *****************************************************************************/ 205181033Semax 206181033Semax/* 207181033Semax * Netgraph node constructor. Do not allow to create node of this type. 208181033Semax */ 209181033Semax 210181033Semaxstatic int 211181033Semaxng_btsocket_sco_node_constructor(node_p node) 212181033Semax{ 213181033Semax return (EINVAL); 214181033Semax} /* ng_btsocket_sco_node_constructor */ 215181033Semax 216181033Semax/* 217181033Semax * Do local shutdown processing. Let old node go and create new fresh one. 218181033Semax */ 219181033Semax 220181033Semaxstatic int 221181033Semaxng_btsocket_sco_node_shutdown(node_p node) 222181033Semax{ 223181033Semax int error = 0; 224181033Semax 225181033Semax NG_NODE_UNREF(node); 226181033Semax 227181033Semax /* Create new node */ 228181033Semax error = ng_make_node_common(&typestruct, &ng_btsocket_sco_node); 229181033Semax if (error != 0) { 230181033Semax NG_BTSOCKET_SCO_ALERT( 231181033Semax"%s: Could not create Netgraph node, error=%d\n", __func__, error); 232181033Semax 233181033Semax ng_btsocket_sco_node = NULL; 234181033Semax 235181033Semax return (error); 236181033Semax } 237181033Semax 238181033Semax error = ng_name_node(ng_btsocket_sco_node, 239181033Semax NG_BTSOCKET_SCO_NODE_TYPE); 240181033Semax if (error != 0) { 241181033Semax NG_BTSOCKET_SCO_ALERT( 242181033Semax"%s: Could not name Netgraph node, error=%d\n", __func__, error); 243181033Semax 244181033Semax NG_NODE_UNREF(ng_btsocket_sco_node); 245181033Semax ng_btsocket_sco_node = NULL; 246181033Semax 247181033Semax return (error); 248181033Semax } 249181033Semax 250181033Semax return (0); 251181033Semax} /* ng_btsocket_sco_node_shutdown */ 252181033Semax 253181033Semax/* 254181033Semax * We allow any hook to be connected to the node. 255181033Semax */ 256181033Semax 257181033Semaxstatic int 258181033Semaxng_btsocket_sco_node_newhook(node_p node, hook_p hook, char const *name) 259181033Semax{ 260181033Semax return (0); 261181033Semax} /* ng_btsocket_sco_node_newhook */ 262181033Semax 263181033Semax/* 264181033Semax * Just say "YEP, that's OK by me!" 265181033Semax */ 266181033Semax 267181033Semaxstatic int 268181033Semaxng_btsocket_sco_node_connect(hook_p hook) 269181033Semax{ 270181033Semax NG_HOOK_SET_PRIVATE(hook, NULL); 271181033Semax NG_HOOK_REF(hook); /* Keep extra reference to the hook */ 272181033Semax 273181033Semax#if 0 274181033Semax NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 275181033Semax NG_HOOK_FORCE_QUEUE(hook); 276181033Semax#endif 277181033Semax 278181033Semax return (0); 279181033Semax} /* ng_btsocket_sco_node_connect */ 280181033Semax 281181033Semax/* 282181033Semax * Hook disconnection. Schedule route cleanup task 283181033Semax */ 284181033Semax 285181033Semaxstatic int 286181033Semaxng_btsocket_sco_node_disconnect(hook_p hook) 287181033Semax{ 288181033Semax /* 289181033Semax * If hook has private information than we must have this hook in 290181033Semax * the routing table and must schedule cleaning for the routing table. 291181033Semax * Otherwise hook was connected but we never got "hook_info" message, 292181033Semax * so we have never added this hook to the routing table and it save 293181033Semax * to just delete it. 294181033Semax */ 295181033Semax 296181033Semax if (NG_HOOK_PRIVATE(hook) != NULL) 297181033Semax return (ng_btsocket_sco_wakeup_route_task()); 298181033Semax 299181033Semax NG_HOOK_UNREF(hook); /* Remove extra reference */ 300181033Semax 301181033Semax return (0); 302181033Semax} /* ng_btsocket_sco_node_disconnect */ 303181033Semax 304181033Semax/* 305181033Semax * Process incoming messages 306181033Semax */ 307181033Semax 308181033Semaxstatic int 309181033Semaxng_btsocket_sco_node_rcvmsg(node_p node, item_p item, hook_p hook) 310181033Semax{ 311181033Semax struct ng_mesg *msg = NGI_MSG(item); /* item still has message */ 312181033Semax int error = 0; 313181033Semax 314181033Semax if (msg != NULL && msg->header.typecookie == NGM_HCI_COOKIE) { 315181033Semax mtx_lock(&ng_btsocket_sco_queue_mtx); 316181033Semax if (NG_BT_ITEMQ_FULL(&ng_btsocket_sco_queue)) { 317181033Semax NG_BTSOCKET_SCO_ERR( 318181033Semax"%s: Input queue is full (msg)\n", __func__); 319181033Semax 320181033Semax NG_BT_ITEMQ_DROP(&ng_btsocket_sco_queue); 321181033Semax NG_FREE_ITEM(item); 322181033Semax error = ENOBUFS; 323181033Semax } else { 324181033Semax if (hook != NULL) { 325181033Semax NG_HOOK_REF(hook); 326181033Semax NGI_SET_HOOK(item, hook); 327181033Semax } 328181033Semax 329181033Semax NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_sco_queue, item); 330181033Semax error = ng_btsocket_sco_wakeup_input_task(); 331181033Semax } 332181033Semax mtx_unlock(&ng_btsocket_sco_queue_mtx); 333181033Semax } else { 334181033Semax NG_FREE_ITEM(item); 335181033Semax error = EINVAL; 336181033Semax } 337181033Semax 338181033Semax return (error); 339181033Semax} /* ng_btsocket_sco_node_rcvmsg */ 340181033Semax 341181033Semax/* 342181033Semax * Receive data on a hook 343181033Semax */ 344181033Semax 345181033Semaxstatic int 346181033Semaxng_btsocket_sco_node_rcvdata(hook_p hook, item_p item) 347181033Semax{ 348181033Semax int error = 0; 349181033Semax 350181033Semax mtx_lock(&ng_btsocket_sco_queue_mtx); 351181033Semax if (NG_BT_ITEMQ_FULL(&ng_btsocket_sco_queue)) { 352181033Semax NG_BTSOCKET_SCO_ERR( 353181033Semax"%s: Input queue is full (data)\n", __func__); 354181033Semax 355181033Semax NG_BT_ITEMQ_DROP(&ng_btsocket_sco_queue); 356181033Semax NG_FREE_ITEM(item); 357181033Semax error = ENOBUFS; 358181033Semax } else { 359181033Semax NG_HOOK_REF(hook); 360181033Semax NGI_SET_HOOK(item, hook); 361181033Semax 362181033Semax NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_sco_queue, item); 363181033Semax error = ng_btsocket_sco_wakeup_input_task(); 364181033Semax } 365181033Semax mtx_unlock(&ng_btsocket_sco_queue_mtx); 366181033Semax 367181033Semax return (error); 368181033Semax} /* ng_btsocket_sco_node_rcvdata */ 369181033Semax 370181033Semax/* 371181033Semax * Process LP_ConnectCfm event from the lower layer protocol 372181033Semax */ 373181033Semax 374181033Semaxstatic int 375181033Semaxng_btsocket_sco_process_lp_con_cfm(struct ng_mesg *msg, 376181033Semax ng_btsocket_sco_rtentry_p rt) 377181033Semax{ 378181033Semax ng_hci_lp_con_cfm_ep *ep = NULL; 379181033Semax ng_btsocket_sco_pcb_t *pcb = NULL; 380181033Semax int error = 0; 381181033Semax 382181033Semax if (msg->header.arglen != sizeof(*ep)) 383181033Semax return (EMSGSIZE); 384181033Semax 385181033Semax ep = (ng_hci_lp_con_cfm_ep *)(msg->data); 386181033Semax 387181033Semax mtx_lock(&ng_btsocket_sco_sockets_mtx); 388181033Semax 389181033Semax /* Look for the socket with the token */ 390181033Semax pcb = ng_btsocket_sco_pcb_by_addrs(&rt->src, &ep->bdaddr); 391181033Semax if (pcb == NULL) { 392181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 393181033Semax return (ENOENT); 394181033Semax } 395181033Semax 396181033Semax /* pcb is locked */ 397181033Semax 398181033Semax NG_BTSOCKET_SCO_INFO( 399181033Semax"%s: Got LP_ConnectCfm response, src bdaddr=%x:%x:%x:%x:%x:%x, " \ 400181033Semax"dst bdaddr=%x:%x:%x:%x:%x:%x, status=%d, handle=%d, state=%d\n", 401181033Semax __func__, 402181033Semax pcb->src.b[5], pcb->src.b[4], pcb->src.b[3], 403181033Semax pcb->src.b[2], pcb->src.b[1], pcb->src.b[0], 404181033Semax pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3], 405181033Semax pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0], 406181033Semax ep->status, ep->con_handle, pcb->state); 407181033Semax 408181033Semax if (pcb->state != NG_BTSOCKET_SCO_CONNECTING) { 409181033Semax mtx_unlock(&pcb->pcb_mtx); 410181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 411181033Semax 412181033Semax return (ENOENT); 413181033Semax } 414181033Semax 415181033Semax ng_btsocket_sco_untimeout(pcb); 416181033Semax 417181033Semax if (ep->status == 0) { 418181033Semax /* 419181033Semax * Connection is open. Update connection handle and 420181033Semax * socket state 421181033Semax */ 422181033Semax 423181033Semax pcb->con_handle = ep->con_handle; 424181033Semax pcb->state = NG_BTSOCKET_SCO_OPEN; 425181033Semax soisconnected(pcb->so); 426181033Semax } else { 427181033Semax /* 428181033Semax * We have failed to open connection, so disconnect the socket 429181033Semax */ 430181033Semax 431181033Semax pcb->so->so_error = ECONNREFUSED; /* XXX convert status ??? */ 432181033Semax pcb->state = NG_BTSOCKET_SCO_CLOSED; 433181033Semax soisdisconnected(pcb->so); 434181033Semax } 435181033Semax 436181033Semax mtx_unlock(&pcb->pcb_mtx); 437181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 438181033Semax 439181033Semax return (error); 440181033Semax} /* ng_btsocket_sco_process_lp_con_cfm */ 441181033Semax 442181033Semax/* 443181033Semax * Process LP_ConnectInd indicator. Find socket that listens on address. 444181033Semax * Find exact or closest match. 445181033Semax */ 446181033Semax 447181033Semaxstatic int 448181033Semaxng_btsocket_sco_process_lp_con_ind(struct ng_mesg *msg, 449181033Semax ng_btsocket_sco_rtentry_p rt) 450181033Semax{ 451181033Semax ng_hci_lp_con_ind_ep *ep = NULL; 452181033Semax ng_btsocket_sco_pcb_t *pcb = NULL, *pcb1 = NULL; 453181033Semax int error = 0; 454181033Semax u_int16_t status = 0; 455181033Semax 456181033Semax if (msg->header.arglen != sizeof(*ep)) 457181033Semax return (EMSGSIZE); 458181033Semax 459181033Semax ep = (ng_hci_lp_con_ind_ep *)(msg->data); 460181033Semax 461181033Semax NG_BTSOCKET_SCO_INFO( 462181033Semax"%s: Got LP_ConnectInd indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \ 463181033Semax"dst bdaddr=%x:%x:%x:%x:%x:%x\n", 464181033Semax __func__, 465181033Semax rt->src.b[5], rt->src.b[4], rt->src.b[3], 466181033Semax rt->src.b[2], rt->src.b[1], rt->src.b[0], 467181033Semax ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3], 468181033Semax ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]); 469181033Semax 470181033Semax mtx_lock(&ng_btsocket_sco_sockets_mtx); 471181033Semax 472181033Semax pcb = ng_btsocket_sco_pcb_by_addr(&rt->src); 473181033Semax if (pcb != NULL) { 474181033Semax struct socket *so1 = NULL; 475181033Semax 476181033Semax /* pcb is locked */ 477181033Semax 478181033Semax /* 479181033Semax * First check the pending connections queue and if we have 480181033Semax * space then create new socket and set proper source address. 481181033Semax */ 482181033Semax 483218757Sbz if (pcb->so->so_qlen <= pcb->so->so_qlimit) { 484218757Sbz CURVNET_SET(pcb->so->so_vnet); 485181033Semax so1 = sonewconn(pcb->so, 0); 486218757Sbz CURVNET_RESTORE(); 487218757Sbz } 488181033Semax 489181033Semax if (so1 == NULL) { 490181033Semax status = 0x0d; /* Rejected due to limited resources */ 491181033Semax goto respond; 492181033Semax } 493181033Semax 494181033Semax /* 495181033Semax * If we got here than we have created new socket. So complete 496181033Semax * connection. If we we listening on specific address then copy 497181033Semax * source address from listening socket, otherwise copy source 498181033Semax * address from hook's routing information. 499181033Semax */ 500181033Semax 501181033Semax pcb1 = so2sco_pcb(so1); 502181033Semax KASSERT((pcb1 != NULL), 503181033Semax("%s: pcb1 == NULL\n", __func__)); 504181033Semax 505181033Semax mtx_lock(&pcb1->pcb_mtx); 506181033Semax 507181033Semax if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src)) != 0) 508181033Semax bcopy(&pcb->src, &pcb1->src, sizeof(pcb1->src)); 509181033Semax else 510181033Semax bcopy(&rt->src, &pcb1->src, sizeof(pcb1->src)); 511181033Semax 512181033Semax pcb1->flags &= ~NG_BTSOCKET_SCO_CLIENT; 513181033Semax 514181033Semax bcopy(&ep->bdaddr, &pcb1->dst, sizeof(pcb1->dst)); 515181033Semax pcb1->rt = rt; 516181033Semax } else 517181033Semax /* Nobody listens on requested BDADDR */ 518181033Semax status = 0x1f; /* Unspecified Error */ 519181033Semax 520181033Semaxrespond: 521181033Semax error = ng_btsocket_sco_send_lp_con_rsp(rt, &ep->bdaddr, status); 522181033Semax if (pcb1 != NULL) { 523181033Semax if (error != 0) { 524181033Semax pcb1->so->so_error = error; 525181033Semax pcb1->state = NG_BTSOCKET_SCO_CLOSED; 526181033Semax soisdisconnected(pcb1->so); 527181033Semax } else { 528181033Semax pcb1->state = NG_BTSOCKET_SCO_CONNECTING; 529181033Semax soisconnecting(pcb1->so); 530181033Semax 531181033Semax ng_btsocket_sco_timeout(pcb1); 532181033Semax } 533181033Semax 534181033Semax mtx_unlock(&pcb1->pcb_mtx); 535181033Semax } 536181033Semax 537181033Semax if (pcb != NULL) 538181033Semax mtx_unlock(&pcb->pcb_mtx); 539181033Semax 540181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 541181033Semax 542181033Semax return (error); 543181033Semax} /* ng_btsocket_sco_process_lp_con_ind */ 544181033Semax 545181033Semax/* 546181033Semax * Process LP_DisconnectInd indicator 547181033Semax */ 548181033Semax 549181033Semaxstatic int 550181033Semaxng_btsocket_sco_process_lp_discon_ind(struct ng_mesg *msg, 551181033Semax ng_btsocket_sco_rtentry_p rt) 552181033Semax{ 553181033Semax ng_hci_lp_discon_ind_ep *ep = NULL; 554181033Semax ng_btsocket_sco_pcb_t *pcb = NULL; 555181033Semax 556181033Semax /* Check message */ 557181033Semax if (msg->header.arglen != sizeof(*ep)) 558181033Semax return (EMSGSIZE); 559181033Semax 560181033Semax ep = (ng_hci_lp_discon_ind_ep *)(msg->data); 561181033Semax 562181033Semax mtx_lock(&ng_btsocket_sco_sockets_mtx); 563181033Semax 564181033Semax /* Look for the socket with given channel ID */ 565181033Semax pcb = ng_btsocket_sco_pcb_by_handle(&rt->src, ep->con_handle); 566181033Semax if (pcb == NULL) { 567181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 568181033Semax return (0); 569181033Semax } 570181033Semax 571181033Semax /* 572181033Semax * Disconnect the socket. If there was any pending request we can 573181033Semax * not do anything here anyway. 574181033Semax */ 575181033Semax 576181033Semax /* pcb is locked */ 577181033Semax 578181033Semax NG_BTSOCKET_SCO_INFO( 579181033Semax"%s: Got LP_DisconnectInd indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \ 580181033Semax"dst bdaddr=%x:%x:%x:%x:%x:%x, handle=%d, state=%d\n", 581181033Semax __func__, 582181033Semax pcb->src.b[5], pcb->src.b[4], pcb->src.b[3], 583181033Semax pcb->src.b[2], pcb->src.b[1], pcb->src.b[0], 584181033Semax pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3], 585181033Semax pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0], 586181033Semax pcb->con_handle, pcb->state); 587181033Semax 588181033Semax if (pcb->flags & NG_BTSOCKET_SCO_TIMO) 589181033Semax ng_btsocket_sco_untimeout(pcb); 590181033Semax 591181033Semax pcb->state = NG_BTSOCKET_SCO_CLOSED; 592181033Semax soisdisconnected(pcb->so); 593181033Semax 594181033Semax mtx_unlock(&pcb->pcb_mtx); 595181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 596181033Semax 597181033Semax return (0); 598181033Semax} /* ng_btsocket_sco_process_lp_discon_ind */ 599181033Semax 600181033Semax/* 601181033Semax * Send LP_ConnectReq request 602181033Semax */ 603181033Semax 604181033Semaxstatic int 605181033Semaxng_btsocket_sco_send_lp_con_req(ng_btsocket_sco_pcb_p pcb) 606181033Semax{ 607181033Semax struct ng_mesg *msg = NULL; 608181033Semax ng_hci_lp_con_req_ep *ep = NULL; 609181033Semax int error = 0; 610181033Semax 611181033Semax mtx_assert(&pcb->pcb_mtx, MA_OWNED); 612181033Semax 613181033Semax if (pcb->rt == NULL || 614181033Semax pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) 615181033Semax return (ENETDOWN); 616181033Semax 617181033Semax NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_REQ, 618181033Semax sizeof(*ep), M_NOWAIT); 619181033Semax if (msg == NULL) 620181033Semax return (ENOMEM); 621181033Semax 622181033Semax ep = (ng_hci_lp_con_req_ep *)(msg->data); 623181033Semax ep->link_type = NG_HCI_LINK_SCO; 624181033Semax bcopy(&pcb->dst, &ep->bdaddr, sizeof(ep->bdaddr)); 625181033Semax 626181033Semax NG_SEND_MSG_HOOK(error, ng_btsocket_sco_node, msg, pcb->rt->hook, 0); 627181033Semax 628181033Semax return (error); 629181033Semax} /* ng_btsocket_sco_send_lp_con_req */ 630181033Semax 631181033Semax/* 632181033Semax * Send LP_ConnectRsp response 633181033Semax */ 634181033Semax 635181033Semaxstatic int 636181033Semaxng_btsocket_sco_send_lp_con_rsp(ng_btsocket_sco_rtentry_p rt, bdaddr_p dst, int status) 637181033Semax{ 638181033Semax struct ng_mesg *msg = NULL; 639181033Semax ng_hci_lp_con_rsp_ep *ep = NULL; 640181033Semax int error = 0; 641181033Semax 642181033Semax if (rt == NULL || rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook)) 643181033Semax return (ENETDOWN); 644181033Semax 645181033Semax NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP, 646181033Semax sizeof(*ep), M_NOWAIT); 647181033Semax if (msg == NULL) 648181033Semax return (ENOMEM); 649181033Semax 650181033Semax ep = (ng_hci_lp_con_rsp_ep *)(msg->data); 651181033Semax ep->status = status; 652181033Semax ep->link_type = NG_HCI_LINK_SCO; 653181033Semax bcopy(dst, &ep->bdaddr, sizeof(ep->bdaddr)); 654181033Semax 655181033Semax NG_SEND_MSG_HOOK(error, ng_btsocket_sco_node, msg, rt->hook, 0); 656181033Semax 657181033Semax return (error); 658181033Semax} /* ng_btsocket_sco_send_lp_con_rsp */ 659181033Semax 660181033Semax/* 661181033Semax * Send LP_DisconReq request 662181033Semax */ 663181033Semax 664181033Semaxstatic int 665181033Semaxng_btsocket_sco_send_lp_discon_req(ng_btsocket_sco_pcb_p pcb) 666181033Semax{ 667181033Semax struct ng_mesg *msg = NULL; 668181033Semax ng_hci_lp_discon_req_ep *ep = NULL; 669181033Semax int error = 0; 670181033Semax 671181033Semax mtx_assert(&pcb->pcb_mtx, MA_OWNED); 672181033Semax 673181033Semax if (pcb->rt == NULL || 674181033Semax pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) 675181033Semax return (ENETDOWN); 676181033Semax 677181033Semax NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_REQ, 678181033Semax sizeof(*ep), M_NOWAIT); 679181033Semax if (msg == NULL) 680181033Semax return (ENOMEM); 681181033Semax 682181033Semax ep = (ng_hci_lp_discon_req_ep *)(msg->data); 683181033Semax ep->con_handle = pcb->con_handle; 684181033Semax ep->reason = 0x13; /* User Ended Connection */ 685181033Semax 686181033Semax NG_SEND_MSG_HOOK(error, ng_btsocket_sco_node, msg, pcb->rt->hook, 0); 687181033Semax 688181033Semax return (error); 689181033Semax} /* ng_btsocket_sco_send_lp_discon_req */ 690181033Semax 691181033Semax/***************************************************************************** 692181033Semax ***************************************************************************** 693181033Semax ** Socket interface 694181033Semax ***************************************************************************** 695181033Semax *****************************************************************************/ 696181033Semax 697181033Semax/* 698181033Semax * SCO sockets data input routine 699181033Semax */ 700181033Semax 701181033Semaxstatic void 702181033Semaxng_btsocket_sco_data_input(struct mbuf *m, hook_p hook) 703181033Semax{ 704181033Semax ng_hci_scodata_pkt_t *hdr = NULL; 705181033Semax ng_btsocket_sco_pcb_t *pcb = NULL; 706181033Semax ng_btsocket_sco_rtentry_t *rt = NULL; 707181033Semax u_int16_t con_handle; 708181033Semax 709181033Semax if (hook == NULL) { 710181033Semax NG_BTSOCKET_SCO_ALERT( 711181033Semax"%s: Invalid source hook for SCO data packet\n", __func__); 712181033Semax goto drop; 713181033Semax } 714181033Semax 715181033Semax rt = (ng_btsocket_sco_rtentry_t *) NG_HOOK_PRIVATE(hook); 716181033Semax if (rt == NULL) { 717181033Semax NG_BTSOCKET_SCO_ALERT( 718181033Semax"%s: Could not find out source bdaddr for SCO data packet\n", __func__); 719181033Semax goto drop; 720181033Semax } 721181033Semax 722181033Semax /* Make sure we can access header */ 723181033Semax if (m->m_pkthdr.len < sizeof(*hdr)) { 724181033Semax NG_BTSOCKET_SCO_ERR( 725181033Semax"%s: SCO data packet too small, len=%d\n", __func__, m->m_pkthdr.len); 726181033Semax goto drop; 727181033Semax } 728181033Semax 729181033Semax if (m->m_len < sizeof(*hdr)) { 730181033Semax m = m_pullup(m, sizeof(*hdr)); 731181033Semax if (m == NULL) 732181033Semax goto drop; 733181033Semax } 734181033Semax 735181033Semax /* Strip SCO packet header and verify packet length */ 736181033Semax hdr = mtod(m, ng_hci_scodata_pkt_t *); 737181033Semax m_adj(m, sizeof(*hdr)); 738181033Semax 739181033Semax if (hdr->length != m->m_pkthdr.len) { 740181033Semax NG_BTSOCKET_SCO_ERR( 741181033Semax"%s: Bad SCO data packet length, len=%d, length=%d\n", 742181033Semax __func__, m->m_pkthdr.len, hdr->length); 743181033Semax goto drop; 744181033Semax } 745181033Semax 746181033Semax /* 747181033Semax * Now process packet 748181033Semax */ 749181033Semax 750181033Semax con_handle = NG_HCI_CON_HANDLE(le16toh(hdr->con_handle)); 751181033Semax 752181033Semax NG_BTSOCKET_SCO_INFO( 753181033Semax"%s: Received SCO data packet: src bdaddr=%x:%x:%x:%x:%x:%x, handle=%d, " \ 754181033Semax"length=%d\n", __func__, 755181033Semax rt->src.b[5], rt->src.b[4], rt->src.b[3], 756181033Semax rt->src.b[2], rt->src.b[1], rt->src.b[0], 757181033Semax con_handle, hdr->length); 758181033Semax 759181033Semax mtx_lock(&ng_btsocket_sco_sockets_mtx); 760181033Semax 761181033Semax /* Find socket */ 762181033Semax pcb = ng_btsocket_sco_pcb_by_handle(&rt->src, con_handle); 763181033Semax if (pcb == NULL) { 764181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 765181033Semax goto drop; 766181033Semax } 767181033Semax 768181033Semax /* pcb is locked */ 769181033Semax 770181033Semax if (pcb->state != NG_BTSOCKET_SCO_OPEN) { 771181033Semax NG_BTSOCKET_SCO_ERR( 772181033Semax"%s: No connected socket found, src bdaddr=%x:%x:%x:%x:%x:%x, state=%d\n", 773181033Semax __func__, 774181033Semax rt->src.b[5], rt->src.b[4], rt->src.b[3], 775181033Semax rt->src.b[2], rt->src.b[1], rt->src.b[0], 776181033Semax pcb->state); 777181033Semax 778181033Semax mtx_unlock(&pcb->pcb_mtx); 779181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 780181033Semax goto drop; 781181033Semax } 782181033Semax 783181033Semax /* Check if we have enough space in socket receive queue */ 784181033Semax if (m->m_pkthdr.len > sbspace(&pcb->so->so_rcv)) { 785181033Semax NG_BTSOCKET_SCO_ERR( 786181033Semax"%s: Not enough space in socket receive queue. Dropping SCO data packet, " \ 787181033Semax"src bdaddr=%x:%x:%x:%x:%x:%x, len=%d, space=%ld\n", 788181033Semax __func__, 789181033Semax rt->src.b[5], rt->src.b[4], rt->src.b[3], 790181033Semax rt->src.b[2], rt->src.b[1], rt->src.b[0], 791181033Semax m->m_pkthdr.len, 792181033Semax sbspace(&pcb->so->so_rcv)); 793181033Semax 794181033Semax mtx_unlock(&pcb->pcb_mtx); 795181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 796181033Semax goto drop; 797181033Semax } 798181033Semax 799181033Semax /* Append packet to the socket receive queue and wakeup */ 800181033Semax sbappendrecord(&pcb->so->so_rcv, m); 801181033Semax m = NULL; 802181033Semax 803181033Semax sorwakeup(pcb->so); 804181033Semax 805181033Semax mtx_unlock(&pcb->pcb_mtx); 806181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 807181033Semaxdrop: 808181033Semax NG_FREE_M(m); /* checks for m != NULL */ 809181033Semax} /* ng_btsocket_sco_data_input */ 810181033Semax 811181033Semax/* 812181033Semax * SCO sockets default message input routine 813181033Semax */ 814181033Semax 815181033Semaxstatic void 816181033Semaxng_btsocket_sco_default_msg_input(struct ng_mesg *msg, hook_p hook) 817181033Semax{ 818181033Semax ng_btsocket_sco_rtentry_t *rt = NULL; 819181033Semax 820181033Semax if (hook == NULL || NG_HOOK_NOT_VALID(hook)) 821181033Semax return; 822181033Semax 823181033Semax rt = (ng_btsocket_sco_rtentry_t *) NG_HOOK_PRIVATE(hook); 824181033Semax 825181033Semax switch (msg->header.cmd) { 826181033Semax case NGM_HCI_NODE_UP: { 827181033Semax ng_hci_node_up_ep *ep = NULL; 828181033Semax 829181033Semax if (msg->header.arglen != sizeof(*ep)) 830181033Semax break; 831181033Semax 832181033Semax ep = (ng_hci_node_up_ep *)(msg->data); 833181033Semax if (bcmp(&ep->bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0) 834181033Semax break; 835181033Semax 836181033Semax if (rt == NULL) { 837184205Sdes rt = malloc(sizeof(*rt), 838181033Semax M_NETGRAPH_BTSOCKET_SCO, M_NOWAIT|M_ZERO); 839181033Semax if (rt == NULL) 840181033Semax break; 841181033Semax 842181033Semax NG_HOOK_SET_PRIVATE(hook, rt); 843181033Semax 844181033Semax mtx_lock(&ng_btsocket_sco_rt_mtx); 845181033Semax 846181033Semax LIST_INSERT_HEAD(&ng_btsocket_sco_rt, rt, next); 847181033Semax } else 848181033Semax mtx_lock(&ng_btsocket_sco_rt_mtx); 849181033Semax 850181033Semax bcopy(&ep->bdaddr, &rt->src, sizeof(rt->src)); 851181033Semax rt->pkt_size = (ep->pkt_size == 0)? 60 : ep->pkt_size; 852181033Semax rt->num_pkts = ep->num_pkts; 853181033Semax rt->hook = hook; 854181033Semax 855181033Semax mtx_unlock(&ng_btsocket_sco_rt_mtx); 856181033Semax 857181033Semax NG_BTSOCKET_SCO_INFO( 858181033Semax"%s: Updating hook \"%s\", src bdaddr=%x:%x:%x:%x:%x:%x, pkt_size=%d, " \ 859181033Semax"num_pkts=%d\n", __func__, NG_HOOK_NAME(hook), 860181033Semax rt->src.b[5], rt->src.b[4], rt->src.b[3], 861181033Semax rt->src.b[2], rt->src.b[1], rt->src.b[0], 862181033Semax rt->pkt_size, rt->num_pkts); 863181033Semax } break; 864181033Semax 865181033Semax case NGM_HCI_SYNC_CON_QUEUE: { 866181033Semax ng_hci_sync_con_queue_ep *ep = NULL; 867181033Semax ng_btsocket_sco_pcb_t *pcb = NULL; 868181033Semax 869181033Semax if (rt == NULL || msg->header.arglen != sizeof(*ep)) 870181033Semax break; 871181033Semax 872181033Semax ep = (ng_hci_sync_con_queue_ep *)(msg->data); 873181033Semax 874181033Semax rt->pending -= ep->completed; 875181033Semax if (rt->pending < 0) { 876181033Semax NG_BTSOCKET_SCO_WARN( 877181033Semax"%s: Pending packet counter is out of sync! bdaddr=%x:%x:%x:%x:%x:%x, " \ 878181033Semax"handle=%d, pending=%d, completed=%d\n", 879181033Semax __func__, 880181033Semax rt->src.b[5], rt->src.b[4], rt->src.b[3], 881181033Semax rt->src.b[2], rt->src.b[1], rt->src.b[0], 882181033Semax ep->con_handle, rt->pending, 883181033Semax ep->completed); 884181033Semax 885181033Semax rt->pending = 0; 886181033Semax } 887181033Semax 888181033Semax mtx_lock(&ng_btsocket_sco_sockets_mtx); 889181033Semax 890181033Semax /* Find socket */ 891181033Semax pcb = ng_btsocket_sco_pcb_by_handle(&rt->src, ep->con_handle); 892181033Semax if (pcb == NULL) { 893181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 894181033Semax break; 895181033Semax } 896181033Semax 897181033Semax /* pcb is locked */ 898181033Semax 899181033Semax /* Check state */ 900181033Semax if (pcb->state == NG_BTSOCKET_SCO_OPEN) { 901181033Semax /* Remove timeout */ 902181033Semax ng_btsocket_sco_untimeout(pcb); 903181033Semax 904181033Semax /* Drop completed packets from the send queue */ 905181033Semax for (; ep->completed > 0; ep->completed --) 906181033Semax sbdroprecord(&pcb->so->so_snd); 907181033Semax 908181033Semax /* Send more if we have any */ 909274421Sglebius if (sbavail(&pcb->so->so_snd) > 0) 910181033Semax if (ng_btsocket_sco_send2(pcb) == 0) 911181033Semax ng_btsocket_sco_timeout(pcb); 912181033Semax 913181033Semax /* Wake up writers */ 914181033Semax sowwakeup(pcb->so); 915181033Semax } 916181033Semax 917181033Semax mtx_unlock(&pcb->pcb_mtx); 918181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 919181033Semax } break; 920181033Semax 921181033Semax default: 922181033Semax NG_BTSOCKET_SCO_WARN( 923181033Semax"%s: Unknown message, cmd=%d\n", __func__, msg->header.cmd); 924181033Semax break; 925181033Semax } 926181033Semax 927181033Semax NG_FREE_MSG(msg); /* Checks for msg != NULL */ 928181033Semax} /* ng_btsocket_sco_default_msg_input */ 929181033Semax 930181033Semax/* 931181033Semax * SCO sockets LP message input routine 932181033Semax */ 933181033Semax 934181033Semaxstatic void 935181033Semaxng_btsocket_sco_lp_msg_input(struct ng_mesg *msg, hook_p hook) 936181033Semax{ 937181033Semax ng_btsocket_sco_rtentry_p rt = NULL; 938181033Semax 939181033Semax if (hook == NULL) { 940181033Semax NG_BTSOCKET_SCO_ALERT( 941181033Semax"%s: Invalid source hook for LP message\n", __func__); 942181033Semax goto drop; 943181033Semax } 944181033Semax 945181033Semax rt = (ng_btsocket_sco_rtentry_p) NG_HOOK_PRIVATE(hook); 946181033Semax if (rt == NULL) { 947181033Semax NG_BTSOCKET_SCO_ALERT( 948181033Semax"%s: Could not find out source bdaddr for LP message\n", __func__); 949181033Semax goto drop; 950181033Semax } 951181033Semax 952181033Semax switch (msg->header.cmd) { 953181033Semax case NGM_HCI_LP_CON_CFM: /* Connection Confirmation Event */ 954181033Semax ng_btsocket_sco_process_lp_con_cfm(msg, rt); 955181033Semax break; 956181033Semax 957181033Semax case NGM_HCI_LP_CON_IND: /* Connection Indication Event */ 958181033Semax ng_btsocket_sco_process_lp_con_ind(msg, rt); 959181033Semax break; 960181033Semax 961181033Semax case NGM_HCI_LP_DISCON_IND: /* Disconnection Indication Event */ 962181033Semax ng_btsocket_sco_process_lp_discon_ind(msg, rt); 963181033Semax break; 964181033Semax 965181033Semax /* XXX FIXME add other LP messages */ 966181033Semax 967181033Semax default: 968181033Semax NG_BTSOCKET_SCO_WARN( 969181033Semax"%s: Unknown LP message, cmd=%d\n", __func__, msg->header.cmd); 970181033Semax break; 971181033Semax } 972181033Semaxdrop: 973181033Semax NG_FREE_MSG(msg); 974181033Semax} /* ng_btsocket_sco_lp_msg_input */ 975181033Semax 976181033Semax/* 977181033Semax * SCO sockets input routine 978181033Semax */ 979181033Semax 980181033Semaxstatic void 981181033Semaxng_btsocket_sco_input(void *context, int pending) 982181033Semax{ 983181033Semax item_p item = NULL; 984181033Semax hook_p hook = NULL; 985181033Semax 986181033Semax for (;;) { 987181033Semax mtx_lock(&ng_btsocket_sco_queue_mtx); 988181033Semax NG_BT_ITEMQ_DEQUEUE(&ng_btsocket_sco_queue, item); 989181033Semax mtx_unlock(&ng_btsocket_sco_queue_mtx); 990181033Semax 991181033Semax if (item == NULL) 992181033Semax break; 993181033Semax 994181033Semax NGI_GET_HOOK(item, hook); 995181033Semax if (hook != NULL && NG_HOOK_NOT_VALID(hook)) 996181033Semax goto drop; 997181033Semax 998181033Semax switch(item->el_flags & NGQF_TYPE) { 999181033Semax case NGQF_DATA: { 1000181033Semax struct mbuf *m = NULL; 1001181033Semax 1002181033Semax NGI_GET_M(item, m); 1003181033Semax ng_btsocket_sco_data_input(m, hook); 1004181033Semax } break; 1005181033Semax 1006181033Semax case NGQF_MESG: { 1007181033Semax struct ng_mesg *msg = NULL; 1008181033Semax 1009181033Semax NGI_GET_MSG(item, msg); 1010181033Semax 1011181033Semax switch (msg->header.cmd) { 1012181033Semax case NGM_HCI_LP_CON_CFM: 1013181033Semax case NGM_HCI_LP_CON_IND: 1014181033Semax case NGM_HCI_LP_DISCON_IND: 1015181033Semax /* XXX FIXME add other LP messages */ 1016181033Semax ng_btsocket_sco_lp_msg_input(msg, hook); 1017181033Semax break; 1018181033Semax 1019181033Semax default: 1020181033Semax ng_btsocket_sco_default_msg_input(msg, hook); 1021181033Semax break; 1022181033Semax } 1023181033Semax } break; 1024181033Semax 1025181033Semax default: 1026181033Semax KASSERT(0, 1027181033Semax("%s: invalid item type=%ld\n", __func__, (item->el_flags & NGQF_TYPE))); 1028181033Semax break; 1029181033Semax } 1030181033Semaxdrop: 1031181033Semax if (hook != NULL) 1032181033Semax NG_HOOK_UNREF(hook); 1033181033Semax 1034181033Semax NG_FREE_ITEM(item); 1035181033Semax } 1036181033Semax} /* ng_btsocket_sco_input */ 1037181033Semax 1038181033Semax/* 1039181033Semax * Route cleanup task. Gets scheduled when hook is disconnected. Here we 1040181033Semax * will find all sockets that use "invalid" hook and disconnect them. 1041181033Semax */ 1042181033Semax 1043181033Semaxstatic void 1044181033Semaxng_btsocket_sco_rtclean(void *context, int pending) 1045181033Semax{ 1046181033Semax ng_btsocket_sco_pcb_p pcb = NULL, pcb_next = NULL; 1047181033Semax ng_btsocket_sco_rtentry_p rt = NULL; 1048181033Semax 1049181033Semax /* 1050181033Semax * First disconnect all sockets that use "invalid" hook 1051181033Semax */ 1052181033Semax 1053181033Semax mtx_lock(&ng_btsocket_sco_sockets_mtx); 1054181033Semax 1055181033Semax for(pcb = LIST_FIRST(&ng_btsocket_sco_sockets); pcb != NULL; ) { 1056181033Semax mtx_lock(&pcb->pcb_mtx); 1057181033Semax pcb_next = LIST_NEXT(pcb, next); 1058181033Semax 1059181033Semax if (pcb->rt != NULL && 1060181033Semax pcb->rt->hook != NULL && NG_HOOK_NOT_VALID(pcb->rt->hook)) { 1061181033Semax if (pcb->flags & NG_BTSOCKET_SCO_TIMO) 1062181033Semax ng_btsocket_sco_untimeout(pcb); 1063181033Semax 1064181033Semax pcb->rt = NULL; 1065181033Semax pcb->so->so_error = ENETDOWN; 1066181033Semax pcb->state = NG_BTSOCKET_SCO_CLOSED; 1067181033Semax soisdisconnected(pcb->so); 1068181033Semax } 1069181033Semax 1070181033Semax mtx_unlock(&pcb->pcb_mtx); 1071181033Semax pcb = pcb_next; 1072181033Semax } 1073181033Semax 1074181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 1075181033Semax 1076181033Semax /* 1077181033Semax * Now cleanup routing table 1078181033Semax */ 1079181033Semax 1080181033Semax mtx_lock(&ng_btsocket_sco_rt_mtx); 1081181033Semax 1082181033Semax for (rt = LIST_FIRST(&ng_btsocket_sco_rt); rt != NULL; ) { 1083181033Semax ng_btsocket_sco_rtentry_p rt_next = LIST_NEXT(rt, next); 1084181033Semax 1085181033Semax if (rt->hook != NULL && NG_HOOK_NOT_VALID(rt->hook)) { 1086181033Semax LIST_REMOVE(rt, next); 1087181033Semax 1088181033Semax NG_HOOK_SET_PRIVATE(rt->hook, NULL); 1089181033Semax NG_HOOK_UNREF(rt->hook); /* Remove extra reference */ 1090181033Semax 1091181033Semax bzero(rt, sizeof(*rt)); 1092184205Sdes free(rt, M_NETGRAPH_BTSOCKET_SCO); 1093181033Semax } 1094181033Semax 1095181033Semax rt = rt_next; 1096181033Semax } 1097181033Semax 1098181033Semax mtx_unlock(&ng_btsocket_sco_rt_mtx); 1099181033Semax} /* ng_btsocket_sco_rtclean */ 1100181033Semax 1101181033Semax/* 1102181033Semax * Initialize everything 1103181033Semax */ 1104181033Semax 1105181033Semaxvoid 1106181033Semaxng_btsocket_sco_init(void) 1107181033Semax{ 1108181033Semax int error = 0; 1109181033Semax 1110267336Strociny /* Skip initialization of globals for non-default instances. */ 1111267336Strociny if (!IS_DEFAULT_VNET(curvnet)) 1112267336Strociny return; 1113267336Strociny 1114181033Semax ng_btsocket_sco_node = NULL; 1115181033Semax ng_btsocket_sco_debug_level = NG_BTSOCKET_WARN_LEVEL; 1116181033Semax 1117181033Semax /* Register Netgraph node type */ 1118181033Semax error = ng_newtype(&typestruct); 1119181033Semax if (error != 0) { 1120181033Semax NG_BTSOCKET_SCO_ALERT( 1121181033Semax"%s: Could not register Netgraph node type, error=%d\n", __func__, error); 1122181033Semax 1123181033Semax return; 1124181033Semax } 1125181033Semax 1126181033Semax /* Create Netgrapg node */ 1127181033Semax error = ng_make_node_common(&typestruct, &ng_btsocket_sco_node); 1128181033Semax if (error != 0) { 1129181033Semax NG_BTSOCKET_SCO_ALERT( 1130181033Semax"%s: Could not create Netgraph node, error=%d\n", __func__, error); 1131181033Semax 1132181033Semax ng_btsocket_sco_node = NULL; 1133181033Semax 1134181033Semax return; 1135181033Semax } 1136181033Semax 1137181033Semax error = ng_name_node(ng_btsocket_sco_node, NG_BTSOCKET_SCO_NODE_TYPE); 1138181033Semax if (error != 0) { 1139181033Semax NG_BTSOCKET_SCO_ALERT( 1140181033Semax"%s: Could not name Netgraph node, error=%d\n", __func__, error); 1141181033Semax 1142181033Semax NG_NODE_UNREF(ng_btsocket_sco_node); 1143181033Semax ng_btsocket_sco_node = NULL; 1144181033Semax 1145181033Semax return; 1146181033Semax } 1147181033Semax 1148181033Semax /* Create input queue */ 1149181033Semax NG_BT_ITEMQ_INIT(&ng_btsocket_sco_queue, 300); 1150181033Semax mtx_init(&ng_btsocket_sco_queue_mtx, 1151181033Semax "btsocks_sco_queue_mtx", NULL, MTX_DEF); 1152181033Semax TASK_INIT(&ng_btsocket_sco_queue_task, 0, 1153181033Semax ng_btsocket_sco_input, NULL); 1154181033Semax 1155181033Semax /* Create list of sockets */ 1156181033Semax LIST_INIT(&ng_btsocket_sco_sockets); 1157181033Semax mtx_init(&ng_btsocket_sco_sockets_mtx, 1158181033Semax "btsocks_sco_sockets_mtx", NULL, MTX_DEF); 1159181033Semax 1160181033Semax /* Routing table */ 1161181033Semax LIST_INIT(&ng_btsocket_sco_rt); 1162181033Semax mtx_init(&ng_btsocket_sco_rt_mtx, 1163181033Semax "btsocks_sco_rt_mtx", NULL, MTX_DEF); 1164181033Semax TASK_INIT(&ng_btsocket_sco_rt_task, 0, 1165181033Semax ng_btsocket_sco_rtclean, NULL); 1166181033Semax} /* ng_btsocket_sco_init */ 1167181033Semax 1168181033Semax/* 1169181033Semax * Abort connection on socket 1170181033Semax */ 1171181033Semax 1172181033Semaxvoid 1173181033Semaxng_btsocket_sco_abort(struct socket *so) 1174181033Semax{ 1175181033Semax so->so_error = ECONNABORTED; 1176181033Semax 1177181033Semax (void) ng_btsocket_sco_disconnect(so); 1178181033Semax} /* ng_btsocket_sco_abort */ 1179181033Semax 1180181033Semaxvoid 1181181033Semaxng_btsocket_sco_close(struct socket *so) 1182181033Semax{ 1183181033Semax (void) ng_btsocket_sco_disconnect(so); 1184181033Semax} /* ng_btsocket_sco_close */ 1185181033Semax 1186181033Semax/* 1187181033Semax * Accept connection on socket. Nothing to do here, socket must be connected 1188181033Semax * and ready, so just return peer address and be done with it. 1189181033Semax */ 1190181033Semax 1191181033Semaxint 1192181033Semaxng_btsocket_sco_accept(struct socket *so, struct sockaddr **nam) 1193181033Semax{ 1194181033Semax if (ng_btsocket_sco_node == NULL) 1195181033Semax return (EINVAL); 1196181033Semax 1197181033Semax return (ng_btsocket_sco_peeraddr(so, nam)); 1198181033Semax} /* ng_btsocket_sco_accept */ 1199181033Semax 1200181033Semax/* 1201181033Semax * Create and attach new socket 1202181033Semax */ 1203181033Semax 1204181033Semaxint 1205181033Semaxng_btsocket_sco_attach(struct socket *so, int proto, struct thread *td) 1206181033Semax{ 1207181033Semax ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 1208181033Semax int error; 1209181033Semax 1210181033Semax /* Check socket and protocol */ 1211181033Semax if (ng_btsocket_sco_node == NULL) 1212181033Semax return (EPROTONOSUPPORT); 1213181033Semax if (so->so_type != SOCK_SEQPACKET) 1214181033Semax return (ESOCKTNOSUPPORT); 1215181033Semax 1216181033Semax#if 0 /* XXX sonewconn() calls "pru_attach" with proto == 0 */ 1217181033Semax if (proto != 0) 1218181033Semax if (proto != BLUETOOTH_PROTO_SCO) 1219181033Semax return (EPROTONOSUPPORT); 1220181033Semax#endif /* XXX */ 1221181033Semax 1222181033Semax if (pcb != NULL) 1223181033Semax return (EISCONN); 1224181033Semax 1225181033Semax /* Reserve send and receive space if it is not reserved yet */ 1226181033Semax if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) { 1227181033Semax error = soreserve(so, NG_BTSOCKET_SCO_SENDSPACE, 1228181033Semax NG_BTSOCKET_SCO_RECVSPACE); 1229181033Semax if (error != 0) 1230181033Semax return (error); 1231181033Semax } 1232181033Semax 1233181033Semax /* Allocate the PCB */ 1234184205Sdes pcb = malloc(sizeof(*pcb), 1235181033Semax M_NETGRAPH_BTSOCKET_SCO, M_NOWAIT | M_ZERO); 1236181033Semax if (pcb == NULL) 1237181033Semax return (ENOMEM); 1238181033Semax 1239181033Semax /* Link the PCB and the socket */ 1240181033Semax so->so_pcb = (caddr_t) pcb; 1241181033Semax pcb->so = so; 1242181033Semax pcb->state = NG_BTSOCKET_SCO_CLOSED; 1243181033Semax 1244181033Semax callout_init(&pcb->timo, 1); 1245181033Semax 1246181033Semax /* 1247181033Semax * Mark PCB mutex as DUPOK to prevent "duplicated lock of 1248181033Semax * the same type" message. When accepting new SCO connection 1249181033Semax * ng_btsocket_sco_process_lp_con_ind() holds both PCB mutexes 1250181033Semax * for "old" (accepting) PCB and "new" (created) PCB. 1251181033Semax */ 1252181033Semax 1253181033Semax mtx_init(&pcb->pcb_mtx, "btsocks_sco_pcb_mtx", NULL, 1254181033Semax MTX_DEF|MTX_DUPOK); 1255181033Semax 1256181033Semax /* 1257181033Semax * Add the PCB to the list 1258181033Semax * 1259181033Semax * XXX FIXME VERY IMPORTANT! 1260181033Semax * 1261181033Semax * This is totally FUBAR. We could get here in two cases: 1262181033Semax * 1263181033Semax * 1) When user calls socket() 1264298813Spfg * 2) When we need to accept new incoming connection and call 1265181033Semax * sonewconn() 1266181033Semax * 1267298813Spfg * In the first case we must acquire ng_btsocket_sco_sockets_mtx. 1268181033Semax * In the second case we hold ng_btsocket_sco_sockets_mtx already. 1269181033Semax * So we now need to distinguish between these cases. From reading 1270181033Semax * /sys/kern/uipc_socket2.c we can find out that sonewconn() calls 1271181033Semax * pru_attach with proto == 0 and td == NULL. For now use this fact 1272181033Semax * to figure out if we were called from socket() or from sonewconn(). 1273181033Semax */ 1274181033Semax 1275181033Semax if (td != NULL) 1276181033Semax mtx_lock(&ng_btsocket_sco_sockets_mtx); 1277181033Semax else 1278181033Semax mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED); 1279181033Semax 1280181033Semax LIST_INSERT_HEAD(&ng_btsocket_sco_sockets, pcb, next); 1281181033Semax 1282181033Semax if (td != NULL) 1283181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 1284181033Semax 1285181033Semax return (0); 1286181033Semax} /* ng_btsocket_sco_attach */ 1287181033Semax 1288181033Semax/* 1289181033Semax * Bind socket 1290181033Semax */ 1291181033Semax 1292181033Semaxint 1293181033Semaxng_btsocket_sco_bind(struct socket *so, struct sockaddr *nam, 1294181033Semax struct thread *td) 1295181033Semax{ 1296181033Semax ng_btsocket_sco_pcb_t *pcb = NULL; 1297181033Semax struct sockaddr_sco *sa = (struct sockaddr_sco *) nam; 1298181033Semax 1299181033Semax if (ng_btsocket_sco_node == NULL) 1300181033Semax return (EINVAL); 1301181033Semax 1302181033Semax /* Verify address */ 1303181033Semax if (sa == NULL) 1304181033Semax return (EINVAL); 1305181033Semax if (sa->sco_family != AF_BLUETOOTH) 1306181033Semax return (EAFNOSUPPORT); 1307181033Semax if (sa->sco_len != sizeof(*sa)) 1308181033Semax return (EINVAL); 1309181033Semax 1310181033Semax mtx_lock(&ng_btsocket_sco_sockets_mtx); 1311181033Semax 1312181033Semax /* 1313181033Semax * Check if other socket has this address already (look for exact 1314181033Semax * match in bdaddr) and assign socket address if it's available. 1315181033Semax */ 1316181033Semax 1317181033Semax if (bcmp(&sa->sco_bdaddr, NG_HCI_BDADDR_ANY, sizeof(sa->sco_bdaddr)) != 0) { 1318181033Semax LIST_FOREACH(pcb, &ng_btsocket_sco_sockets, next) { 1319181033Semax mtx_lock(&pcb->pcb_mtx); 1320181033Semax 1321181033Semax if (bcmp(&pcb->src, &sa->sco_bdaddr, sizeof(bdaddr_t)) == 0) { 1322181033Semax mtx_unlock(&pcb->pcb_mtx); 1323181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 1324181033Semax 1325181033Semax return (EADDRINUSE); 1326181033Semax } 1327181033Semax 1328181033Semax mtx_unlock(&pcb->pcb_mtx); 1329181033Semax } 1330181033Semax 1331181033Semax } 1332181033Semax 1333181033Semax pcb = so2sco_pcb(so); 1334181033Semax if (pcb == NULL) { 1335181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 1336181033Semax return (EINVAL); 1337181033Semax } 1338181033Semax 1339181033Semax mtx_lock(&pcb->pcb_mtx); 1340181033Semax bcopy(&sa->sco_bdaddr, &pcb->src, sizeof(pcb->src)); 1341181033Semax mtx_unlock(&pcb->pcb_mtx); 1342181033Semax 1343181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 1344181033Semax 1345181033Semax return (0); 1346181033Semax} /* ng_btsocket_sco_bind */ 1347181033Semax 1348181033Semax/* 1349181033Semax * Connect socket 1350181033Semax */ 1351181033Semax 1352181033Semaxint 1353181033Semaxng_btsocket_sco_connect(struct socket *so, struct sockaddr *nam, 1354181033Semax struct thread *td) 1355181033Semax{ 1356181033Semax ng_btsocket_sco_pcb_t *pcb = so2sco_pcb(so); 1357181033Semax struct sockaddr_sco *sa = (struct sockaddr_sco *) nam; 1358181033Semax ng_btsocket_sco_rtentry_t *rt = NULL; 1359181033Semax int have_src, error = 0; 1360181033Semax 1361181033Semax /* Check socket */ 1362181033Semax if (pcb == NULL) 1363181033Semax return (EINVAL); 1364181033Semax if (ng_btsocket_sco_node == NULL) 1365181033Semax return (EINVAL); 1366181033Semax 1367181033Semax /* Verify address */ 1368181033Semax if (sa == NULL) 1369181033Semax return (EINVAL); 1370181033Semax if (sa->sco_family != AF_BLUETOOTH) 1371181033Semax return (EAFNOSUPPORT); 1372181033Semax if (sa->sco_len != sizeof(*sa)) 1373181033Semax return (EINVAL); 1374181033Semax if (bcmp(&sa->sco_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0) 1375181033Semax return (EDESTADDRREQ); 1376181033Semax 1377181033Semax /* 1378181033Semax * Routing. Socket should be bound to some source address. The source 1379181033Semax * address can be ANY. Destination address must be set and it must not 1380181033Semax * be ANY. If source address is ANY then find first rtentry that has 1381181033Semax * src != dst. 1382181033Semax */ 1383181033Semax 1384181033Semax mtx_lock(&ng_btsocket_sco_rt_mtx); 1385181033Semax mtx_lock(&pcb->pcb_mtx); 1386181033Semax 1387181033Semax if (pcb->state == NG_BTSOCKET_SCO_CONNECTING) { 1388181033Semax mtx_unlock(&pcb->pcb_mtx); 1389181033Semax mtx_unlock(&ng_btsocket_sco_rt_mtx); 1390181033Semax 1391181033Semax return (EINPROGRESS); 1392181033Semax } 1393181033Semax 1394181033Semax if (bcmp(&sa->sco_bdaddr, &pcb->src, sizeof(pcb->src)) == 0) { 1395181033Semax mtx_unlock(&pcb->pcb_mtx); 1396181033Semax mtx_unlock(&ng_btsocket_sco_rt_mtx); 1397181033Semax 1398181033Semax return (EINVAL); 1399181033Semax } 1400181033Semax 1401181033Semax /* Send destination address and PSM */ 1402181033Semax bcopy(&sa->sco_bdaddr, &pcb->dst, sizeof(pcb->dst)); 1403181033Semax 1404181033Semax pcb->rt = NULL; 1405181033Semax have_src = bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src)); 1406181033Semax 1407181033Semax LIST_FOREACH(rt, &ng_btsocket_sco_rt, next) { 1408181033Semax if (rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook)) 1409181033Semax continue; 1410181033Semax 1411181033Semax /* Match src and dst */ 1412181033Semax if (have_src) { 1413181033Semax if (bcmp(&pcb->src, &rt->src, sizeof(rt->src)) == 0) 1414181033Semax break; 1415181033Semax } else { 1416181033Semax if (bcmp(&pcb->dst, &rt->src, sizeof(rt->src)) != 0) 1417181033Semax break; 1418181033Semax } 1419181033Semax } 1420181033Semax 1421181033Semax if (rt != NULL) { 1422181033Semax pcb->rt = rt; 1423181033Semax 1424181033Semax if (!have_src) 1425181033Semax bcopy(&rt->src, &pcb->src, sizeof(pcb->src)); 1426181033Semax } else 1427181033Semax error = EHOSTUNREACH; 1428181033Semax 1429181033Semax /* 1430181033Semax * Send LP_Connect request 1431181033Semax */ 1432181033Semax 1433181033Semax if (error == 0) { 1434181033Semax error = ng_btsocket_sco_send_lp_con_req(pcb); 1435181033Semax if (error == 0) { 1436181033Semax pcb->flags |= NG_BTSOCKET_SCO_CLIENT; 1437181033Semax pcb->state = NG_BTSOCKET_SCO_CONNECTING; 1438181033Semax soisconnecting(pcb->so); 1439181033Semax 1440181033Semax ng_btsocket_sco_timeout(pcb); 1441181033Semax } 1442181033Semax } 1443181033Semax 1444181033Semax mtx_unlock(&pcb->pcb_mtx); 1445181033Semax mtx_unlock(&ng_btsocket_sco_rt_mtx); 1446181033Semax 1447181033Semax return (error); 1448181033Semax} /* ng_btsocket_sco_connect */ 1449181033Semax 1450181033Semax/* 1451181033Semax * Process ioctl's calls on socket 1452181033Semax */ 1453181033Semax 1454181033Semaxint 1455181033Semaxng_btsocket_sco_control(struct socket *so, u_long cmd, caddr_t data, 1456181033Semax struct ifnet *ifp, struct thread *td) 1457181033Semax{ 1458181033Semax return (EINVAL); 1459181033Semax} /* ng_btsocket_sco_control */ 1460181033Semax 1461181033Semax/* 1462181033Semax * Process getsockopt/setsockopt system calls 1463181033Semax */ 1464181033Semax 1465181033Semaxint 1466181033Semaxng_btsocket_sco_ctloutput(struct socket *so, struct sockopt *sopt) 1467181033Semax{ 1468181033Semax ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 1469181033Semax int error, tmp; 1470181033Semax 1471181033Semax if (ng_btsocket_sco_node == NULL) 1472181033Semax return (EINVAL); 1473181033Semax if (pcb == NULL) 1474181033Semax return (EINVAL); 1475181033Semax 1476181033Semax if (sopt->sopt_level != SOL_SCO) 1477181033Semax return (0); 1478181033Semax 1479181033Semax mtx_lock(&pcb->pcb_mtx); 1480181033Semax 1481181033Semax switch (sopt->sopt_dir) { 1482181033Semax case SOPT_GET: 1483181033Semax if (pcb->state != NG_BTSOCKET_SCO_OPEN) { 1484181033Semax error = ENOTCONN; 1485181033Semax break; 1486181033Semax } 1487181033Semax 1488181033Semax switch (sopt->sopt_name) { 1489181033Semax case SO_SCO_MTU: 1490181033Semax tmp = pcb->rt->pkt_size; 1491181033Semax error = sooptcopyout(sopt, &tmp, sizeof(tmp)); 1492181033Semax break; 1493181033Semax 1494181033Semax case SO_SCO_CONNINFO: 1495181033Semax tmp = pcb->con_handle; 1496181033Semax error = sooptcopyout(sopt, &tmp, sizeof(tmp)); 1497181033Semax break; 1498181033Semax 1499181033Semax default: 1500181033Semax error = EINVAL; 1501181033Semax break; 1502181033Semax } 1503181033Semax break; 1504181033Semax 1505181033Semax case SOPT_SET: 1506181033Semax error = ENOPROTOOPT; 1507181033Semax break; 1508181033Semax 1509181033Semax default: 1510181033Semax error = EINVAL; 1511181033Semax break; 1512181033Semax } 1513181033Semax 1514181033Semax mtx_unlock(&pcb->pcb_mtx); 1515181033Semax 1516181033Semax return (error); 1517181033Semax} /* ng_btsocket_sco_ctloutput */ 1518181033Semax 1519181033Semax/* 1520181033Semax * Detach and destroy socket 1521181033Semax */ 1522181033Semax 1523181033Semaxvoid 1524181033Semaxng_btsocket_sco_detach(struct socket *so) 1525181033Semax{ 1526181033Semax ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 1527181033Semax 1528181033Semax KASSERT(pcb != NULL, ("ng_btsocket_sco_detach: pcb == NULL")); 1529181033Semax 1530181033Semax if (ng_btsocket_sco_node == NULL) 1531181033Semax return; 1532181033Semax 1533181033Semax mtx_lock(&ng_btsocket_sco_sockets_mtx); 1534181033Semax mtx_lock(&pcb->pcb_mtx); 1535181033Semax 1536181033Semax if (pcb->flags & NG_BTSOCKET_SCO_TIMO) 1537181033Semax ng_btsocket_sco_untimeout(pcb); 1538181033Semax 1539181033Semax if (pcb->state == NG_BTSOCKET_SCO_OPEN) 1540181033Semax ng_btsocket_sco_send_lp_discon_req(pcb); 1541181033Semax 1542181033Semax pcb->state = NG_BTSOCKET_SCO_CLOSED; 1543181033Semax 1544181033Semax LIST_REMOVE(pcb, next); 1545181033Semax 1546181033Semax mtx_unlock(&pcb->pcb_mtx); 1547181033Semax mtx_unlock(&ng_btsocket_sco_sockets_mtx); 1548181033Semax 1549181033Semax mtx_destroy(&pcb->pcb_mtx); 1550181033Semax bzero(pcb, sizeof(*pcb)); 1551184205Sdes free(pcb, M_NETGRAPH_BTSOCKET_SCO); 1552181033Semax 1553181033Semax soisdisconnected(so); 1554181033Semax so->so_pcb = NULL; 1555181033Semax} /* ng_btsocket_sco_detach */ 1556181033Semax 1557181033Semax/* 1558181033Semax * Disconnect socket 1559181033Semax */ 1560181033Semax 1561181033Semaxint 1562181033Semaxng_btsocket_sco_disconnect(struct socket *so) 1563181033Semax{ 1564181033Semax ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 1565181033Semax 1566181033Semax if (pcb == NULL) 1567181033Semax return (EINVAL); 1568181033Semax if (ng_btsocket_sco_node == NULL) 1569181033Semax return (EINVAL); 1570181033Semax 1571181033Semax mtx_lock(&pcb->pcb_mtx); 1572181033Semax 1573181033Semax if (pcb->state == NG_BTSOCKET_SCO_DISCONNECTING) { 1574181033Semax mtx_unlock(&pcb->pcb_mtx); 1575181033Semax 1576181033Semax return (EINPROGRESS); 1577181033Semax } 1578181033Semax 1579181033Semax if (pcb->flags & NG_BTSOCKET_SCO_TIMO) 1580181033Semax ng_btsocket_sco_untimeout(pcb); 1581181033Semax 1582181033Semax if (pcb->state == NG_BTSOCKET_SCO_OPEN) { 1583181033Semax ng_btsocket_sco_send_lp_discon_req(pcb); 1584181033Semax 1585181033Semax pcb->state = NG_BTSOCKET_SCO_DISCONNECTING; 1586181033Semax soisdisconnecting(so); 1587181033Semax 1588181033Semax ng_btsocket_sco_timeout(pcb); 1589181033Semax } else { 1590181033Semax pcb->state = NG_BTSOCKET_SCO_CLOSED; 1591181033Semax soisdisconnected(so); 1592181033Semax } 1593181033Semax 1594181033Semax mtx_unlock(&pcb->pcb_mtx); 1595181033Semax 1596181033Semax return (0); 1597181033Semax} /* ng_btsocket_sco_disconnect */ 1598181033Semax 1599181033Semax/* 1600181033Semax * Listen on socket 1601181033Semax */ 1602181033Semax 1603181033Semaxint 1604181033Semaxng_btsocket_sco_listen(struct socket *so, int backlog, struct thread *td) 1605181033Semax{ 1606181033Semax ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 1607181033Semax int error; 1608181033Semax 1609181033Semax if (pcb == NULL) 1610181033Semax return (EINVAL); 1611181033Semax if (ng_btsocket_sco_node == NULL) 1612181033Semax return (EINVAL); 1613181033Semax 1614181033Semax SOCK_LOCK(so); 1615181033Semax mtx_lock(&pcb->pcb_mtx); 1616181033Semax 1617181033Semax error = solisten_proto_check(so); 1618181033Semax if (error != 0) 1619181033Semax goto out; 1620181033Semax#if 0 1621181033Semax if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0) { 1622181033Semax error = EDESTADDRREQ; 1623181033Semax goto out; 1624181033Semax } 1625181033Semax#endif 1626181033Semax solisten_proto(so, backlog); 1627181033Semaxout: 1628181033Semax mtx_unlock(&pcb->pcb_mtx); 1629181033Semax SOCK_UNLOCK(so); 1630181033Semax 1631181033Semax return (error); 1632181033Semax} /* ng_btsocket_listen */ 1633181033Semax 1634181033Semax/* 1635181033Semax * Get peer address 1636181033Semax */ 1637181033Semax 1638181033Semaxint 1639181033Semaxng_btsocket_sco_peeraddr(struct socket *so, struct sockaddr **nam) 1640181033Semax{ 1641181033Semax ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 1642181033Semax struct sockaddr_sco sa; 1643181033Semax 1644181033Semax if (pcb == NULL) 1645181033Semax return (EINVAL); 1646181033Semax if (ng_btsocket_sco_node == NULL) 1647181033Semax return (EINVAL); 1648181033Semax 1649181033Semax mtx_lock(&pcb->pcb_mtx); 1650181033Semax bcopy(&pcb->dst, &sa.sco_bdaddr, sizeof(sa.sco_bdaddr)); 1651181033Semax mtx_unlock(&pcb->pcb_mtx); 1652181033Semax 1653181033Semax sa.sco_len = sizeof(sa); 1654181033Semax sa.sco_family = AF_BLUETOOTH; 1655181033Semax 1656181033Semax *nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT); 1657181033Semax 1658181033Semax return ((*nam == NULL)? ENOMEM : 0); 1659181033Semax} /* ng_btsocket_sco_peeraddr */ 1660181033Semax 1661181033Semax/* 1662181033Semax * Send data to socket 1663181033Semax */ 1664181033Semax 1665181033Semaxint 1666181033Semaxng_btsocket_sco_send(struct socket *so, int flags, struct mbuf *m, 1667181033Semax struct sockaddr *nam, struct mbuf *control, struct thread *td) 1668181033Semax{ 1669181033Semax ng_btsocket_sco_pcb_t *pcb = so2sco_pcb(so); 1670181033Semax int error = 0; 1671181033Semax 1672181033Semax if (ng_btsocket_sco_node == NULL) { 1673181033Semax error = ENETDOWN; 1674181033Semax goto drop; 1675181033Semax } 1676181033Semax 1677181033Semax /* Check socket and input */ 1678181033Semax if (pcb == NULL || m == NULL || control != NULL) { 1679181033Semax error = EINVAL; 1680181033Semax goto drop; 1681181033Semax } 1682181033Semax 1683181033Semax mtx_lock(&pcb->pcb_mtx); 1684181033Semax 1685181033Semax /* Make sure socket is connected */ 1686181033Semax if (pcb->state != NG_BTSOCKET_SCO_OPEN) { 1687181033Semax mtx_unlock(&pcb->pcb_mtx); 1688181033Semax error = ENOTCONN; 1689181033Semax goto drop; 1690181033Semax } 1691181033Semax 1692181033Semax /* Check route */ 1693181033Semax if (pcb->rt == NULL || 1694181033Semax pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) { 1695181033Semax mtx_unlock(&pcb->pcb_mtx); 1696181033Semax error = ENETDOWN; 1697181033Semax goto drop; 1698181033Semax } 1699181033Semax 1700181033Semax /* Check packet size */ 1701181033Semax if (m->m_pkthdr.len > pcb->rt->pkt_size) { 1702181033Semax NG_BTSOCKET_SCO_ERR( 1703181033Semax"%s: Packet too big, len=%d, pkt_size=%d\n", 1704181033Semax __func__, m->m_pkthdr.len, pcb->rt->pkt_size); 1705181033Semax 1706181033Semax mtx_unlock(&pcb->pcb_mtx); 1707181033Semax error = EMSGSIZE; 1708181033Semax goto drop; 1709181033Semax } 1710181033Semax 1711181033Semax /* 1712181033Semax * First put packet on socket send queue. Then check if we have 1713181033Semax * pending timeout. If we do not have timeout then we must send 1714181033Semax * packet and schedule timeout. Otherwise do nothing and wait for 1715181033Semax * NGM_HCI_SYNC_CON_QUEUE message. 1716181033Semax */ 1717181033Semax 1718181033Semax sbappendrecord(&pcb->so->so_snd, m); 1719181033Semax m = NULL; 1720181033Semax 1721181033Semax if (!(pcb->flags & NG_BTSOCKET_SCO_TIMO)) { 1722181033Semax error = ng_btsocket_sco_send2(pcb); 1723181033Semax if (error == 0) 1724181033Semax ng_btsocket_sco_timeout(pcb); 1725181033Semax else 1726181033Semax sbdroprecord(&pcb->so->so_snd); /* XXX */ 1727181033Semax } 1728181033Semax 1729181033Semax mtx_unlock(&pcb->pcb_mtx); 1730181033Semaxdrop: 1731181033Semax NG_FREE_M(m); /* checks for != NULL */ 1732181033Semax NG_FREE_M(control); 1733181033Semax 1734181033Semax return (error); 1735181033Semax} /* ng_btsocket_sco_send */ 1736181033Semax 1737181033Semax/* 1738181033Semax * Send first packet in the socket queue to the SCO layer 1739181033Semax */ 1740181033Semax 1741181033Semaxstatic int 1742181033Semaxng_btsocket_sco_send2(ng_btsocket_sco_pcb_p pcb) 1743181033Semax{ 1744181033Semax struct mbuf *m = NULL; 1745181033Semax ng_hci_scodata_pkt_t *hdr = NULL; 1746181033Semax int error = 0; 1747181033Semax 1748181033Semax mtx_assert(&pcb->pcb_mtx, MA_OWNED); 1749181033Semax 1750181033Semax while (pcb->rt->pending < pcb->rt->num_pkts && 1751274421Sglebius sbavail(&pcb->so->so_snd) > 0) { 1752181033Semax /* Get a copy of the first packet on send queue */ 1753243882Sglebius m = m_dup(pcb->so->so_snd.sb_mb, M_NOWAIT); 1754181033Semax if (m == NULL) { 1755181033Semax error = ENOBUFS; 1756181033Semax break; 1757181033Semax } 1758181033Semax 1759181033Semax /* Create SCO packet header */ 1760243882Sglebius M_PREPEND(m, sizeof(*hdr), M_NOWAIT); 1761181033Semax if (m != NULL) 1762181033Semax if (m->m_len < sizeof(*hdr)) 1763181033Semax m = m_pullup(m, sizeof(*hdr)); 1764181033Semax 1765181033Semax if (m == NULL) { 1766181033Semax error = ENOBUFS; 1767181033Semax break; 1768181033Semax } 1769181033Semax 1770181033Semax /* Fill in the header */ 1771181033Semax hdr = mtod(m, ng_hci_scodata_pkt_t *); 1772181033Semax hdr->type = NG_HCI_SCO_DATA_PKT; 1773181033Semax hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE(pcb->con_handle, 0, 0)); 1774181033Semax hdr->length = m->m_pkthdr.len - sizeof(*hdr); 1775181033Semax 1776181033Semax /* Send packet */ 1777181033Semax NG_SEND_DATA_ONLY(error, pcb->rt->hook, m); 1778181033Semax if (error != 0) 1779181033Semax break; 1780181033Semax 1781181033Semax pcb->rt->pending ++; 1782181033Semax } 1783181033Semax 1784181033Semax return ((pcb->rt->pending > 0)? 0 : error); 1785181033Semax} /* ng_btsocket_sco_send2 */ 1786181033Semax 1787181033Semax/* 1788181033Semax * Get socket address 1789181033Semax */ 1790181033Semax 1791181033Semaxint 1792181033Semaxng_btsocket_sco_sockaddr(struct socket *so, struct sockaddr **nam) 1793181033Semax{ 1794181033Semax ng_btsocket_sco_pcb_p pcb = so2sco_pcb(so); 1795181033Semax struct sockaddr_sco sa; 1796181033Semax 1797181033Semax if (pcb == NULL) 1798181033Semax return (EINVAL); 1799181033Semax if (ng_btsocket_sco_node == NULL) 1800181033Semax return (EINVAL); 1801181033Semax 1802181033Semax mtx_lock(&pcb->pcb_mtx); 1803181033Semax bcopy(&pcb->src, &sa.sco_bdaddr, sizeof(sa.sco_bdaddr)); 1804181033Semax mtx_unlock(&pcb->pcb_mtx); 1805181033Semax 1806181033Semax sa.sco_len = sizeof(sa); 1807181033Semax sa.sco_family = AF_BLUETOOTH; 1808181033Semax 1809181033Semax *nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT); 1810181033Semax 1811181033Semax return ((*nam == NULL)? ENOMEM : 0); 1812181033Semax} /* ng_btsocket_sco_sockaddr */ 1813181033Semax 1814181033Semax/***************************************************************************** 1815181033Semax ***************************************************************************** 1816181033Semax ** Misc. functions 1817181033Semax ***************************************************************************** 1818181033Semax *****************************************************************************/ 1819181033Semax 1820181033Semax/* 1821181033Semax * Look for the socket that listens on given bdaddr. 1822181033Semax * Returns exact or close match (if any). 1823181033Semax * Caller must hold ng_btsocket_sco_sockets_mtx. 1824181033Semax * Returns with locked pcb. 1825181033Semax */ 1826181033Semax 1827181033Semaxstatic ng_btsocket_sco_pcb_p 1828181033Semaxng_btsocket_sco_pcb_by_addr(bdaddr_p bdaddr) 1829181033Semax{ 1830181033Semax ng_btsocket_sco_pcb_p p = NULL, p1 = NULL; 1831181033Semax 1832181033Semax mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED); 1833181033Semax 1834181033Semax LIST_FOREACH(p, &ng_btsocket_sco_sockets, next) { 1835181033Semax mtx_lock(&p->pcb_mtx); 1836181033Semax 1837181033Semax if (p->so == NULL || !(p->so->so_options & SO_ACCEPTCONN)) { 1838181033Semax mtx_unlock(&p->pcb_mtx); 1839181033Semax continue; 1840181033Semax } 1841181033Semax 1842181033Semax if (bcmp(&p->src, bdaddr, sizeof(p->src)) == 0) 1843181033Semax return (p); /* return with locked pcb */ 1844181033Semax 1845181033Semax if (bcmp(&p->src, NG_HCI_BDADDR_ANY, sizeof(p->src)) == 0) 1846181033Semax p1 = p; 1847181033Semax 1848181033Semax mtx_unlock(&p->pcb_mtx); 1849181033Semax } 1850181033Semax 1851181088Semax if (p1 != NULL) 1852181088Semax mtx_lock(&p1->pcb_mtx); 1853181088Semax 1854181033Semax return (p1); 1855181033Semax} /* ng_btsocket_sco_pcb_by_addr */ 1856181033Semax 1857181033Semax/* 1858181033Semax * Look for the socket that assigned to given source address and handle. 1859181033Semax * Caller must hold ng_btsocket_sco_sockets_mtx. 1860181033Semax * Returns with locked pcb. 1861181033Semax */ 1862181033Semax 1863181033Semaxstatic ng_btsocket_sco_pcb_p 1864181033Semaxng_btsocket_sco_pcb_by_handle(bdaddr_p src, int con_handle) 1865181033Semax{ 1866181033Semax ng_btsocket_sco_pcb_p p = NULL; 1867181033Semax 1868181033Semax mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED); 1869181033Semax 1870181033Semax LIST_FOREACH(p, &ng_btsocket_sco_sockets, next) { 1871181033Semax mtx_lock(&p->pcb_mtx); 1872181033Semax 1873181033Semax if (p->con_handle == con_handle && 1874181033Semax bcmp(src, &p->src, sizeof(p->src)) == 0) 1875181033Semax return (p); /* return with locked pcb */ 1876181033Semax 1877181033Semax mtx_unlock(&p->pcb_mtx); 1878181033Semax } 1879181033Semax 1880181033Semax return (NULL); 1881181033Semax} /* ng_btsocket_sco_pcb_by_handle */ 1882181033Semax 1883181033Semax/* 1884181033Semax * Look for the socket in CONNECTING state with given source and destination 1885181033Semax * addresses. Caller must hold ng_btsocket_sco_sockets_mtx. 1886181033Semax * Returns with locked pcb. 1887181033Semax */ 1888181033Semax 1889181033Semaxstatic ng_btsocket_sco_pcb_p 1890181033Semaxng_btsocket_sco_pcb_by_addrs(bdaddr_p src, bdaddr_p dst) 1891181033Semax{ 1892181033Semax ng_btsocket_sco_pcb_p p = NULL; 1893181033Semax 1894181033Semax mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED); 1895181033Semax 1896181033Semax LIST_FOREACH(p, &ng_btsocket_sco_sockets, next) { 1897181033Semax mtx_lock(&p->pcb_mtx); 1898181033Semax 1899181033Semax if (p->state == NG_BTSOCKET_SCO_CONNECTING && 1900181033Semax bcmp(src, &p->src, sizeof(p->src)) == 0 && 1901181033Semax bcmp(dst, &p->dst, sizeof(p->dst)) == 0) 1902181033Semax return (p); /* return with locked pcb */ 1903181033Semax 1904181033Semax mtx_unlock(&p->pcb_mtx); 1905181033Semax } 1906181033Semax 1907181033Semax return (NULL); 1908181033Semax} /* ng_btsocket_sco_pcb_by_addrs */ 1909181033Semax 1910181033Semax/* 1911181033Semax * Set timeout on socket 1912181033Semax */ 1913181033Semax 1914181033Semaxstatic void 1915181033Semaxng_btsocket_sco_timeout(ng_btsocket_sco_pcb_p pcb) 1916181033Semax{ 1917181033Semax mtx_assert(&pcb->pcb_mtx, MA_OWNED); 1918181033Semax 1919181033Semax if (!(pcb->flags & NG_BTSOCKET_SCO_TIMO)) { 1920181033Semax pcb->flags |= NG_BTSOCKET_SCO_TIMO; 1921181033Semax callout_reset(&pcb->timo, bluetooth_sco_rtx_timeout(), 1922181033Semax ng_btsocket_sco_process_timeout, pcb); 1923181033Semax } else 1924181033Semax KASSERT(0, 1925181033Semax("%s: Duplicated socket timeout?!\n", __func__)); 1926181033Semax} /* ng_btsocket_sco_timeout */ 1927181033Semax 1928181033Semax/* 1929181033Semax * Unset timeout on socket 1930181033Semax */ 1931181033Semax 1932181033Semaxstatic void 1933181033Semaxng_btsocket_sco_untimeout(ng_btsocket_sco_pcb_p pcb) 1934181033Semax{ 1935181033Semax mtx_assert(&pcb->pcb_mtx, MA_OWNED); 1936181033Semax 1937181033Semax if (pcb->flags & NG_BTSOCKET_SCO_TIMO) { 1938181033Semax callout_stop(&pcb->timo); 1939181033Semax pcb->flags &= ~NG_BTSOCKET_SCO_TIMO; 1940181033Semax } else 1941181033Semax KASSERT(0, 1942181033Semax("%s: No socket timeout?!\n", __func__)); 1943181033Semax} /* ng_btsocket_sco_untimeout */ 1944181033Semax 1945181033Semax/* 1946181033Semax * Process timeout on socket 1947181033Semax */ 1948181033Semax 1949181033Semaxstatic void 1950181033Semaxng_btsocket_sco_process_timeout(void *xpcb) 1951181033Semax{ 1952181033Semax ng_btsocket_sco_pcb_p pcb = (ng_btsocket_sco_pcb_p) xpcb; 1953181033Semax 1954181033Semax mtx_lock(&pcb->pcb_mtx); 1955181033Semax 1956181033Semax pcb->flags &= ~NG_BTSOCKET_SCO_TIMO; 1957181033Semax pcb->so->so_error = ETIMEDOUT; 1958181033Semax 1959181033Semax switch (pcb->state) { 1960181033Semax case NG_BTSOCKET_SCO_CONNECTING: 1961181033Semax /* Connect timeout - close the socket */ 1962181033Semax pcb->state = NG_BTSOCKET_SCO_CLOSED; 1963181033Semax soisdisconnected(pcb->so); 1964181033Semax break; 1965181033Semax 1966181033Semax case NG_BTSOCKET_SCO_OPEN: 1967181033Semax /* Send timeout - did not get NGM_HCI_SYNC_CON_QUEUE */ 1968181033Semax sbdroprecord(&pcb->so->so_snd); 1969181033Semax sowwakeup(pcb->so); 1970181033Semax /* XXX FIXME what to do with pcb->rt->pending??? */ 1971181033Semax break; 1972181033Semax 1973181033Semax case NG_BTSOCKET_SCO_DISCONNECTING: 1974181033Semax /* Disconnect timeout - disconnect the socket anyway */ 1975181033Semax pcb->state = NG_BTSOCKET_SCO_CLOSED; 1976181033Semax soisdisconnected(pcb->so); 1977181033Semax break; 1978181033Semax 1979181033Semax default: 1980181033Semax NG_BTSOCKET_SCO_ERR( 1981181033Semax"%s: Invalid socket state=%d\n", __func__, pcb->state); 1982181033Semax break; 1983181033Semax } 1984181033Semax 1985181033Semax mtx_unlock(&pcb->pcb_mtx); 1986181033Semax} /* ng_btsocket_sco_process_timeout */ 1987181033Semax 1988