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