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