xref: /freebsd/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap_raw.c (revision 0fac350c54d0a72f5341e15021efcde63eb58a4b)
1878ed226SJulian Elischer /*
2878ed226SJulian Elischer  * ng_btsocket_l2cap_raw.c
3c398230bSWarner Losh  */
4c398230bSWarner Losh 
5c398230bSWarner Losh /*-
64d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
7fe267a55SPedro F. Giffuni  *
8878ed226SJulian Elischer  * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
9878ed226SJulian Elischer  * All rights reserved.
10878ed226SJulian Elischer  *
11878ed226SJulian Elischer  * Redistribution and use in source and binary forms, with or without
12878ed226SJulian Elischer  * modification, are permitted provided that the following conditions
13878ed226SJulian Elischer  * are met:
14878ed226SJulian Elischer  * 1. Redistributions of source code must retain the above copyright
15878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer.
16878ed226SJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
17878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
18878ed226SJulian Elischer  *    documentation and/or other materials provided with the distribution.
19878ed226SJulian Elischer  *
20878ed226SJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21878ed226SJulian Elischer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22878ed226SJulian Elischer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23878ed226SJulian Elischer  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24878ed226SJulian Elischer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25878ed226SJulian Elischer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26878ed226SJulian Elischer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27878ed226SJulian Elischer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28878ed226SJulian Elischer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29878ed226SJulian Elischer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30878ed226SJulian Elischer  * SUCH DAMAGE.
31878ed226SJulian Elischer  *
320986ab12SMaksim Yevmenkin  * $Id: ng_btsocket_l2cap_raw.c,v 1.12 2003/09/14 23:29:06 max Exp $
33878ed226SJulian Elischer  */
34878ed226SJulian Elischer 
35878ed226SJulian Elischer #include <sys/param.h>
36878ed226SJulian Elischer #include <sys/systm.h>
370986ab12SMaksim Yevmenkin #include <sys/bitstring.h>
38878ed226SJulian Elischer #include <sys/domain.h>
39878ed226SJulian Elischer #include <sys/errno.h>
40878ed226SJulian Elischer #include <sys/filedesc.h>
41878ed226SJulian Elischer #include <sys/ioccom.h>
42878ed226SJulian Elischer #include <sys/kernel.h>
43878ed226SJulian Elischer #include <sys/lock.h>
44878ed226SJulian Elischer #include <sys/malloc.h>
45878ed226SJulian Elischer #include <sys/mbuf.h>
46878ed226SJulian Elischer #include <sys/mutex.h>
47acd3428bSRobert Watson #include <sys/priv.h>
48878ed226SJulian Elischer #include <sys/protosw.h>
49878ed226SJulian Elischer #include <sys/queue.h>
50878ed226SJulian Elischer #include <sys/socket.h>
51878ed226SJulian Elischer #include <sys/socketvar.h>
52878ed226SJulian Elischer #include <sys/sysctl.h>
53878ed226SJulian Elischer #include <sys/taskqueue.h>
544a8e4eb5SMikolaj Golub 
554a8e4eb5SMikolaj Golub #include <net/vnet.h>
564a8e4eb5SMikolaj Golub 
57878ed226SJulian Elischer #include <netgraph/ng_message.h>
58878ed226SJulian Elischer #include <netgraph/netgraph.h>
59b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_bluetooth.h>
60b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_hci.h>
61b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_l2cap.h>
62b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_btsocket.h>
63b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_btsocket_l2cap.h>
64878ed226SJulian Elischer 
65878ed226SJulian Elischer /* MALLOC define */
66878ed226SJulian Elischer #ifdef NG_SEPARATE_MALLOC
67d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_L2CAP_RAW,
68d745c852SEd Schouten     "netgraph_btsocks_l2cap_raw", "Netgraph Bluetooth raw L2CAP sockets");
69878ed226SJulian Elischer #else
70878ed226SJulian Elischer #define M_NETGRAPH_BTSOCKET_L2CAP_RAW M_NETGRAPH
71878ed226SJulian Elischer #endif /* NG_SEPARATE_MALLOC */
72878ed226SJulian Elischer 
73878ed226SJulian Elischer /* Netgraph node methods */
74878ed226SJulian Elischer static ng_constructor_t	ng_btsocket_l2cap_raw_node_constructor;
75878ed226SJulian Elischer static ng_rcvmsg_t	ng_btsocket_l2cap_raw_node_rcvmsg;
76878ed226SJulian Elischer static ng_shutdown_t	ng_btsocket_l2cap_raw_node_shutdown;
77878ed226SJulian Elischer static ng_newhook_t	ng_btsocket_l2cap_raw_node_newhook;
78878ed226SJulian Elischer static ng_connect_t	ng_btsocket_l2cap_raw_node_connect;
79878ed226SJulian Elischer static ng_rcvdata_t	ng_btsocket_l2cap_raw_node_rcvdata;
80878ed226SJulian Elischer static ng_disconnect_t	ng_btsocket_l2cap_raw_node_disconnect;
81878ed226SJulian Elischer 
82878ed226SJulian Elischer static void		ng_btsocket_l2cap_raw_input     (void *, int);
83878ed226SJulian Elischer static void		ng_btsocket_l2cap_raw_rtclean   (void *, int);
84878ed226SJulian Elischer static void		ng_btsocket_l2cap_raw_get_token (u_int32_t *);
85878ed226SJulian Elischer 
86f2bb1caeSJulian Elischer static int		ng_btsocket_l2cap_raw_send_ngmsg
87f2bb1caeSJulian Elischer 				(hook_p, int, void *, int);
88f2bb1caeSJulian Elischer static int		ng_btsocket_l2cap_raw_send_sync_ngmsg
89f2bb1caeSJulian Elischer 				(ng_btsocket_l2cap_raw_pcb_p, int, void *, int);
90f2bb1caeSJulian Elischer 
91f2bb1caeSJulian Elischer #define ng_btsocket_l2cap_raw_wakeup_input_task() \
92d6279c10SMaksim Yevmenkin 	taskqueue_enqueue(taskqueue_swi, &ng_btsocket_l2cap_raw_queue_task)
93f2bb1caeSJulian Elischer 
94f2bb1caeSJulian Elischer #define ng_btsocket_l2cap_raw_wakeup_route_task() \
95d6279c10SMaksim Yevmenkin 	taskqueue_enqueue(taskqueue_swi, &ng_btsocket_l2cap_raw_rt_task)
96f2bb1caeSJulian Elischer 
97878ed226SJulian Elischer /* Netgraph type descriptor */
98878ed226SJulian Elischer static struct ng_type	typestruct = {
99f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
100f8aae777SJulian Elischer 	.name =		NG_BTSOCKET_L2CAP_RAW_NODE_TYPE,
101f8aae777SJulian Elischer 	.constructor =	ng_btsocket_l2cap_raw_node_constructor,
102f8aae777SJulian Elischer 	.rcvmsg =	ng_btsocket_l2cap_raw_node_rcvmsg,
103f8aae777SJulian Elischer 	.shutdown =	ng_btsocket_l2cap_raw_node_shutdown,
104f8aae777SJulian Elischer 	.newhook =	ng_btsocket_l2cap_raw_node_newhook,
105f8aae777SJulian Elischer 	.connect =	ng_btsocket_l2cap_raw_node_connect,
106f8aae777SJulian Elischer 	.rcvdata =	ng_btsocket_l2cap_raw_node_rcvdata,
107f8aae777SJulian Elischer 	.disconnect =	ng_btsocket_l2cap_raw_node_disconnect,
108878ed226SJulian Elischer };
109878ed226SJulian Elischer 
110878ed226SJulian Elischer /* Globals */
111878ed226SJulian Elischer extern int					ifqmaxlen;
112878ed226SJulian Elischer static u_int32_t				ng_btsocket_l2cap_raw_debug_level;
113878ed226SJulian Elischer static u_int32_t				ng_btsocket_l2cap_raw_ioctl_timeout;
114878ed226SJulian Elischer static node_p					ng_btsocket_l2cap_raw_node;
115878ed226SJulian Elischer static struct ng_bt_itemq			ng_btsocket_l2cap_raw_queue;
116878ed226SJulian Elischer static struct mtx				ng_btsocket_l2cap_raw_queue_mtx;
117878ed226SJulian Elischer static struct task				ng_btsocket_l2cap_raw_queue_task;
118878ed226SJulian Elischer static LIST_HEAD(, ng_btsocket_l2cap_raw_pcb)	ng_btsocket_l2cap_raw_sockets;
119878ed226SJulian Elischer static struct mtx				ng_btsocket_l2cap_raw_sockets_mtx;
120878ed226SJulian Elischer static u_int32_t				ng_btsocket_l2cap_raw_token;
121878ed226SJulian Elischer static struct mtx				ng_btsocket_l2cap_raw_token_mtx;
122878ed226SJulian Elischer static LIST_HEAD(, ng_btsocket_l2cap_rtentry)	ng_btsocket_l2cap_raw_rt;
123878ed226SJulian Elischer static struct mtx				ng_btsocket_l2cap_raw_rt_mtx;
124878ed226SJulian Elischer static struct task				ng_btsocket_l2cap_raw_rt_task;
1254fa708efSMaksim Yevmenkin static struct timeval				ng_btsocket_l2cap_raw_lasttime;
1264fa708efSMaksim Yevmenkin static int					ng_btsocket_l2cap_raw_curpps;
127878ed226SJulian Elischer 
128878ed226SJulian Elischer /* Sysctl tree */
129878ed226SJulian Elischer SYSCTL_DECL(_net_bluetooth_l2cap_sockets);
1307029da5cSPawel Biernacki static SYSCTL_NODE(_net_bluetooth_l2cap_sockets, OID_AUTO, raw,
1317029da5cSPawel Biernacki     CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
1327029da5cSPawel Biernacki     "Bluetooth raw L2CAP sockets family");
133f29fc085SMatthew D Fleming SYSCTL_UINT(_net_bluetooth_l2cap_sockets_raw, OID_AUTO, debug_level,
134878ed226SJulian Elischer 	CTLFLAG_RW,
135878ed226SJulian Elischer 	&ng_btsocket_l2cap_raw_debug_level, NG_BTSOCKET_WARN_LEVEL,
136878ed226SJulian Elischer 	"Bluetooth raw L2CAP sockets debug level");
137f29fc085SMatthew D Fleming SYSCTL_UINT(_net_bluetooth_l2cap_sockets_raw, OID_AUTO, ioctl_timeout,
138878ed226SJulian Elischer 	CTLFLAG_RW,
139878ed226SJulian Elischer 	&ng_btsocket_l2cap_raw_ioctl_timeout, 5,
140878ed226SJulian Elischer 	"Bluetooth raw L2CAP sockets ioctl timeout");
141f29fc085SMatthew D Fleming SYSCTL_UINT(_net_bluetooth_l2cap_sockets_raw, OID_AUTO, queue_len,
142878ed226SJulian Elischer 	CTLFLAG_RD,
143878ed226SJulian Elischer 	&ng_btsocket_l2cap_raw_queue.len, 0,
144878ed226SJulian Elischer 	"Bluetooth raw L2CAP sockets input queue length");
145f29fc085SMatthew D Fleming SYSCTL_UINT(_net_bluetooth_l2cap_sockets_raw, OID_AUTO, queue_maxlen,
146878ed226SJulian Elischer 	CTLFLAG_RD,
147878ed226SJulian Elischer 	&ng_btsocket_l2cap_raw_queue.maxlen, 0,
148878ed226SJulian Elischer 	"Bluetooth raw L2CAP sockets input queue max. length");
149f29fc085SMatthew D Fleming SYSCTL_UINT(_net_bluetooth_l2cap_sockets_raw, OID_AUTO, queue_drops,
150878ed226SJulian Elischer 	CTLFLAG_RD,
151878ed226SJulian Elischer 	&ng_btsocket_l2cap_raw_queue.drops, 0,
152878ed226SJulian Elischer 	"Bluetooth raw L2CAP sockets input queue drops");
153878ed226SJulian Elischer 
154878ed226SJulian Elischer /* Debug */
155878ed226SJulian Elischer #define NG_BTSOCKET_L2CAP_RAW_INFO \
1564fa708efSMaksim Yevmenkin 	if (ng_btsocket_l2cap_raw_debug_level >= NG_BTSOCKET_INFO_LEVEL && \
1574fa708efSMaksim Yevmenkin 	    ppsratecheck(&ng_btsocket_l2cap_raw_lasttime, &ng_btsocket_l2cap_raw_curpps, 1)) \
158878ed226SJulian Elischer 		printf
159878ed226SJulian Elischer 
160878ed226SJulian Elischer #define NG_BTSOCKET_L2CAP_RAW_WARN \
1614fa708efSMaksim Yevmenkin 	if (ng_btsocket_l2cap_raw_debug_level >= NG_BTSOCKET_WARN_LEVEL && \
1624fa708efSMaksim Yevmenkin 	    ppsratecheck(&ng_btsocket_l2cap_raw_lasttime, &ng_btsocket_l2cap_raw_curpps, 1)) \
163878ed226SJulian Elischer 		printf
164878ed226SJulian Elischer 
165878ed226SJulian Elischer #define NG_BTSOCKET_L2CAP_RAW_ERR \
1664fa708efSMaksim Yevmenkin 	if (ng_btsocket_l2cap_raw_debug_level >= NG_BTSOCKET_ERR_LEVEL && \
1674fa708efSMaksim Yevmenkin 	    ppsratecheck(&ng_btsocket_l2cap_raw_lasttime, &ng_btsocket_l2cap_raw_curpps, 1)) \
168878ed226SJulian Elischer 		printf
169878ed226SJulian Elischer 
170878ed226SJulian Elischer #define NG_BTSOCKET_L2CAP_RAW_ALERT \
1714fa708efSMaksim Yevmenkin 	if (ng_btsocket_l2cap_raw_debug_level >= NG_BTSOCKET_ALERT_LEVEL && \
1724fa708efSMaksim Yevmenkin 	    ppsratecheck(&ng_btsocket_l2cap_raw_lasttime, &ng_btsocket_l2cap_raw_curpps, 1)) \
173878ed226SJulian Elischer 		printf
174878ed226SJulian Elischer 
175878ed226SJulian Elischer /*****************************************************************************
176878ed226SJulian Elischer  *****************************************************************************
177878ed226SJulian Elischer  **                        Netgraph node interface
178878ed226SJulian Elischer  *****************************************************************************
179878ed226SJulian Elischer  *****************************************************************************/
180878ed226SJulian Elischer 
181878ed226SJulian Elischer /*
182878ed226SJulian Elischer  * Netgraph node constructor. Do not allow to create node of this type.
183878ed226SJulian Elischer  */
184878ed226SJulian Elischer 
185878ed226SJulian Elischer static int
ng_btsocket_l2cap_raw_node_constructor(node_p node)186878ed226SJulian Elischer ng_btsocket_l2cap_raw_node_constructor(node_p node)
187878ed226SJulian Elischer {
188878ed226SJulian Elischer 	return (EINVAL);
189878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_node_constructor */
190878ed226SJulian Elischer 
191878ed226SJulian Elischer /*
192878ed226SJulian Elischer  * Do local shutdown processing. Let old node go and create new fresh one.
193878ed226SJulian Elischer  */
194878ed226SJulian Elischer 
195878ed226SJulian Elischer static int
ng_btsocket_l2cap_raw_node_shutdown(node_p node)196878ed226SJulian Elischer ng_btsocket_l2cap_raw_node_shutdown(node_p node)
197878ed226SJulian Elischer {
198878ed226SJulian Elischer 	int	error = 0;
199878ed226SJulian Elischer 
200878ed226SJulian Elischer 	NG_NODE_UNREF(node);
201878ed226SJulian Elischer 
202878ed226SJulian Elischer 	/* Create new node */
203878ed226SJulian Elischer 	error = ng_make_node_common(&typestruct, &ng_btsocket_l2cap_raw_node);
204878ed226SJulian Elischer 	if (error != 0) {
205878ed226SJulian Elischer 		NG_BTSOCKET_L2CAP_RAW_ALERT(
206878ed226SJulian Elischer "%s: Could not create Netgraph node, error=%d\n", __func__, error);
207878ed226SJulian Elischer 
208878ed226SJulian Elischer 		ng_btsocket_l2cap_raw_node = NULL;
209878ed226SJulian Elischer 
210878ed226SJulian Elischer 		return (error);
211878ed226SJulian Elischer 	}
212878ed226SJulian Elischer 
213878ed226SJulian Elischer 	error = ng_name_node(ng_btsocket_l2cap_raw_node,
214878ed226SJulian Elischer 				NG_BTSOCKET_L2CAP_RAW_NODE_TYPE);
215b9fe2d6cSAlfred Perlstein 	if (error != 0) {
216878ed226SJulian Elischer 		NG_BTSOCKET_L2CAP_RAW_ALERT(
217878ed226SJulian Elischer "%s: Could not name Netgraph node, error=%d\n", __func__, error);
218878ed226SJulian Elischer 
219878ed226SJulian Elischer 		NG_NODE_UNREF(ng_btsocket_l2cap_raw_node);
220878ed226SJulian Elischer 		ng_btsocket_l2cap_raw_node = NULL;
221878ed226SJulian Elischer 
222878ed226SJulian Elischer 		return (error);
223878ed226SJulian Elischer 	}
224878ed226SJulian Elischer 
225878ed226SJulian Elischer 	return (0);
226878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_node_shutdown */
227878ed226SJulian Elischer 
228878ed226SJulian Elischer /*
229878ed226SJulian Elischer  * We allow any hook to be connected to the node.
230878ed226SJulian Elischer  */
231878ed226SJulian Elischer 
232878ed226SJulian Elischer static int
ng_btsocket_l2cap_raw_node_newhook(node_p node,hook_p hook,char const * name)233878ed226SJulian Elischer ng_btsocket_l2cap_raw_node_newhook(node_p node, hook_p hook, char const *name)
234878ed226SJulian Elischer {
235878ed226SJulian Elischer 	return (0);
236878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_node_newhook */
237878ed226SJulian Elischer 
238878ed226SJulian Elischer /*
239878ed226SJulian Elischer  * Just say "YEP, that's OK by me!"
240878ed226SJulian Elischer  */
241878ed226SJulian Elischer 
242878ed226SJulian Elischer static int
ng_btsocket_l2cap_raw_node_connect(hook_p hook)243878ed226SJulian Elischer ng_btsocket_l2cap_raw_node_connect(hook_p hook)
244878ed226SJulian Elischer {
245878ed226SJulian Elischer 	NG_HOOK_SET_PRIVATE(hook, NULL);
246878ed226SJulian Elischer 	NG_HOOK_REF(hook); /* Keep extra reference to the hook */
247878ed226SJulian Elischer 
248878ed226SJulian Elischer 	return (0);
249878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_node_connect */
250878ed226SJulian Elischer 
251878ed226SJulian Elischer /*
252878ed226SJulian Elischer  * Hook disconnection. Schedule route cleanup task
253878ed226SJulian Elischer  */
254878ed226SJulian Elischer 
255878ed226SJulian Elischer static int
ng_btsocket_l2cap_raw_node_disconnect(hook_p hook)256878ed226SJulian Elischer ng_btsocket_l2cap_raw_node_disconnect(hook_p hook)
257878ed226SJulian Elischer {
258878ed226SJulian Elischer 	/*
259878ed226SJulian Elischer 	 * If hook has private information than we must have this hook in
260878ed226SJulian Elischer 	 * the routing table and must schedule cleaning for the routing table.
261878ed226SJulian Elischer 	 * Otherwise hook was connected but we never got "hook_info" message,
262878ed226SJulian Elischer 	 * so we have never added this hook to the routing table and it save
263878ed226SJulian Elischer 	 * to just delete it.
264878ed226SJulian Elischer 	 */
265878ed226SJulian Elischer 
266878ed226SJulian Elischer 	if (NG_HOOK_PRIVATE(hook) != NULL)
267f2bb1caeSJulian Elischer 		return (ng_btsocket_l2cap_raw_wakeup_route_task());
268878ed226SJulian Elischer 
269878ed226SJulian Elischer 	NG_HOOK_UNREF(hook); /* Remove extra reference */
270878ed226SJulian Elischer 
271878ed226SJulian Elischer 	return (0);
272878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_node_disconnect */
273878ed226SJulian Elischer 
274878ed226SJulian Elischer /*
275878ed226SJulian Elischer  * Process incoming messages
276878ed226SJulian Elischer  */
277878ed226SJulian Elischer 
278878ed226SJulian Elischer static int
ng_btsocket_l2cap_raw_node_rcvmsg(node_p node,item_p item,hook_p hook)279878ed226SJulian Elischer ng_btsocket_l2cap_raw_node_rcvmsg(node_p node, item_p item, hook_p hook)
280878ed226SJulian Elischer {
281878ed226SJulian Elischer 	struct ng_mesg	*msg = NGI_MSG(item); /* item still has message */
282878ed226SJulian Elischer 	int		 error = 0;
283878ed226SJulian Elischer 
284878ed226SJulian Elischer 	if (msg != NULL && msg->header.typecookie == NGM_L2CAP_COOKIE) {
285f2bb1caeSJulian Elischer 		/*
286f2bb1caeSJulian Elischer 		 * NGM_L2CAP_NODE_HOOK_INFO is special message initiated by
287f2bb1caeSJulian Elischer 		 * L2CAP layer. Ignore all other messages if they are not
288f2bb1caeSJulian Elischer 		 * replies or token is zero
289f2bb1caeSJulian Elischer 		 */
290f2bb1caeSJulian Elischer 
291f2bb1caeSJulian Elischer 		if (msg->header.cmd != NGM_L2CAP_NODE_HOOK_INFO) {
292f2bb1caeSJulian Elischer 			if (msg->header.token == 0 ||
293f2bb1caeSJulian Elischer 			    !(msg->header.flags & NGF_RESP)) {
294f2bb1caeSJulian Elischer 				NG_FREE_ITEM(item);
295f2bb1caeSJulian Elischer 				return (0);
296f2bb1caeSJulian Elischer 			}
297f2bb1caeSJulian Elischer 		}
298f2bb1caeSJulian Elischer 
299878ed226SJulian Elischer 		mtx_lock(&ng_btsocket_l2cap_raw_queue_mtx);
300878ed226SJulian Elischer 		if (NG_BT_ITEMQ_FULL(&ng_btsocket_l2cap_raw_queue)) {
301878ed226SJulian Elischer 			NG_BTSOCKET_L2CAP_RAW_ERR(
302878ed226SJulian Elischer "%s: Input queue is full\n", __func__);
303878ed226SJulian Elischer 
304878ed226SJulian Elischer 			NG_BT_ITEMQ_DROP(&ng_btsocket_l2cap_raw_queue);
305878ed226SJulian Elischer 			NG_FREE_ITEM(item);
306878ed226SJulian Elischer 			error = ENOBUFS;
307878ed226SJulian Elischer 		} else {
308878ed226SJulian Elischer 			if (hook != NULL) {
309878ed226SJulian Elischer 				NG_HOOK_REF(hook);
310878ed226SJulian Elischer 				NGI_SET_HOOK(item, hook);
311878ed226SJulian Elischer 			}
312878ed226SJulian Elischer 
313878ed226SJulian Elischer 			NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_l2cap_raw_queue, item);
314f2bb1caeSJulian Elischer 			error = ng_btsocket_l2cap_raw_wakeup_input_task();
315878ed226SJulian Elischer 		}
316878ed226SJulian Elischer 		mtx_unlock(&ng_btsocket_l2cap_raw_queue_mtx);
317878ed226SJulian Elischer 	} else {
318878ed226SJulian Elischer 		NG_FREE_ITEM(item);
319878ed226SJulian Elischer 		error = EINVAL;
320878ed226SJulian Elischer 	}
321878ed226SJulian Elischer 
322878ed226SJulian Elischer 	return (error);
323878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_node_rcvmsg */
324878ed226SJulian Elischer 
325878ed226SJulian Elischer /*
326878ed226SJulian Elischer  * Receive data on a hook
327878ed226SJulian Elischer  */
328878ed226SJulian Elischer 
329878ed226SJulian Elischer static int
ng_btsocket_l2cap_raw_node_rcvdata(hook_p hook,item_p item)330878ed226SJulian Elischer ng_btsocket_l2cap_raw_node_rcvdata(hook_p hook, item_p item)
331878ed226SJulian Elischer {
332878ed226SJulian Elischer 	NG_FREE_ITEM(item);
333878ed226SJulian Elischer 
334878ed226SJulian Elischer 	return (EINVAL);
335878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_node_rcvdata */
336878ed226SJulian Elischer 
337878ed226SJulian Elischer /*****************************************************************************
338878ed226SJulian Elischer  *****************************************************************************
339878ed226SJulian Elischer  **                              Socket interface
340878ed226SJulian Elischer  *****************************************************************************
341878ed226SJulian Elischer  *****************************************************************************/
342878ed226SJulian Elischer 
343878ed226SJulian Elischer /*
344878ed226SJulian Elischer  * L2CAP sockets input routine
345878ed226SJulian Elischer  */
346878ed226SJulian Elischer 
347878ed226SJulian Elischer static void
ng_btsocket_l2cap_raw_input(void * context,int pending)348878ed226SJulian Elischer ng_btsocket_l2cap_raw_input(void *context, int pending)
349878ed226SJulian Elischer {
350878ed226SJulian Elischer 	item_p		 item = NULL;
351878ed226SJulian Elischer 	hook_p		 hook = NULL;
352878ed226SJulian Elischer 	struct ng_mesg  *msg = NULL;
353878ed226SJulian Elischer 
354878ed226SJulian Elischer 	for (;;) {
355878ed226SJulian Elischer 		mtx_lock(&ng_btsocket_l2cap_raw_queue_mtx);
356878ed226SJulian Elischer 		NG_BT_ITEMQ_DEQUEUE(&ng_btsocket_l2cap_raw_queue, item);
357878ed226SJulian Elischer 		mtx_unlock(&ng_btsocket_l2cap_raw_queue_mtx);
358878ed226SJulian Elischer 
359878ed226SJulian Elischer 		if (item == NULL)
360878ed226SJulian Elischer 			break;
361878ed226SJulian Elischer 
362878ed226SJulian Elischer 		KASSERT((item->el_flags & NGQF_TYPE) == NGQF_MESG,
363878ed226SJulian Elischer ("%s: invalid item type=%ld\n", __func__, (item->el_flags & NGQF_TYPE)));
364878ed226SJulian Elischer 
365878ed226SJulian Elischer 		NGI_GET_MSG(item, msg);
366878ed226SJulian Elischer 		NGI_GET_HOOK(item, hook);
367878ed226SJulian Elischer 		NG_FREE_ITEM(item);
368878ed226SJulian Elischer 
369878ed226SJulian Elischer 		switch (msg->header.cmd) {
370878ed226SJulian Elischer 		case NGM_L2CAP_NODE_HOOK_INFO: {
371878ed226SJulian Elischer 			ng_btsocket_l2cap_rtentry_t	*rt = NULL;
372878ed226SJulian Elischer 
373878ed226SJulian Elischer 			if (hook == NULL || NG_HOOK_NOT_VALID(hook) ||
374878ed226SJulian Elischer 			    msg->header.arglen != sizeof(bdaddr_t))
375878ed226SJulian Elischer 				break;
376878ed226SJulian Elischer 
377878ed226SJulian Elischer 			if (bcmp(msg->data, NG_HCI_BDADDR_ANY,
378878ed226SJulian Elischer 					sizeof(bdaddr_t)) == 0)
379878ed226SJulian Elischer 				break;
380878ed226SJulian Elischer 
381878ed226SJulian Elischer 			rt = (ng_btsocket_l2cap_rtentry_t *)
382878ed226SJulian Elischer 				NG_HOOK_PRIVATE(hook);
383878ed226SJulian Elischer 			if (rt == NULL) {
3841ede983cSDag-Erling Smørgrav 				rt = malloc(sizeof(*rt),
385878ed226SJulian Elischer 					M_NETGRAPH_BTSOCKET_L2CAP_RAW,
386878ed226SJulian Elischer 					M_NOWAIT|M_ZERO);
387d6279c10SMaksim Yevmenkin 				if (rt == NULL)
388878ed226SJulian Elischer 					break;
389d6279c10SMaksim Yevmenkin 
390d6279c10SMaksim Yevmenkin 				NG_HOOK_SET_PRIVATE(hook, rt);
391d6279c10SMaksim Yevmenkin 
392d6279c10SMaksim Yevmenkin 				mtx_lock(&ng_btsocket_l2cap_raw_rt_mtx);
393878ed226SJulian Elischer 
394878ed226SJulian Elischer 				LIST_INSERT_HEAD(&ng_btsocket_l2cap_raw_rt,
395878ed226SJulian Elischer 					rt, next);
396d6279c10SMaksim Yevmenkin 			} else
397d6279c10SMaksim Yevmenkin 				mtx_lock(&ng_btsocket_l2cap_raw_rt_mtx);
398878ed226SJulian Elischer 
399878ed226SJulian Elischer 			bcopy(msg->data, &rt->src, sizeof(rt->src));
400878ed226SJulian Elischer 			rt->hook = hook;
401878ed226SJulian Elischer 
402878ed226SJulian Elischer 			NG_BTSOCKET_L2CAP_RAW_INFO(
403878ed226SJulian Elischer "%s: Updating hook \"%s\", src bdaddr=%x:%x:%x:%x:%x:%x\n",
404878ed226SJulian Elischer 				__func__, NG_HOOK_NAME(hook),
405878ed226SJulian Elischer 				rt->src.b[5], rt->src.b[4], rt->src.b[3],
406878ed226SJulian Elischer 				rt->src.b[2], rt->src.b[1], rt->src.b[0]);
407d6279c10SMaksim Yevmenkin 
408d6279c10SMaksim Yevmenkin 			mtx_unlock(&ng_btsocket_l2cap_raw_rt_mtx);
409878ed226SJulian Elischer 			} break;
410878ed226SJulian Elischer 
411878ed226SJulian Elischer 		case NGM_L2CAP_NODE_GET_FLAGS:
412878ed226SJulian Elischer 		case NGM_L2CAP_NODE_GET_DEBUG:
413878ed226SJulian Elischer 		case NGM_L2CAP_NODE_GET_CON_LIST:
414878ed226SJulian Elischer 		case NGM_L2CAP_NODE_GET_CHAN_LIST:
415f2bb1caeSJulian Elischer 		case NGM_L2CAP_NODE_GET_AUTO_DISCON_TIMO:
416878ed226SJulian Elischer 		case NGM_L2CAP_L2CA_PING:
417878ed226SJulian Elischer 		case NGM_L2CAP_L2CA_GET_INFO: {
418878ed226SJulian Elischer 			ng_btsocket_l2cap_raw_pcb_p	pcb = NULL;
419878ed226SJulian Elischer 
420878ed226SJulian Elischer 			mtx_lock(&ng_btsocket_l2cap_raw_sockets_mtx);
421878ed226SJulian Elischer 
422f2bb1caeSJulian Elischer 			LIST_FOREACH(pcb,&ng_btsocket_l2cap_raw_sockets,next) {
423f2bb1caeSJulian Elischer 				mtx_lock(&pcb->pcb_mtx);
424f2bb1caeSJulian Elischer 
425878ed226SJulian Elischer 				if (pcb->token == msg->header.token) {
426878ed226SJulian Elischer 					pcb->msg = msg;
427878ed226SJulian Elischer 					msg = NULL;
428878ed226SJulian Elischer 					wakeup(&pcb->msg);
429f2bb1caeSJulian Elischer 					mtx_unlock(&pcb->pcb_mtx);
430878ed226SJulian Elischer 					break;
431878ed226SJulian Elischer 				}
432878ed226SJulian Elischer 
433f2bb1caeSJulian Elischer 				mtx_unlock(&pcb->pcb_mtx);
434f2bb1caeSJulian Elischer 			}
435f2bb1caeSJulian Elischer 
436878ed226SJulian Elischer 			mtx_unlock(&ng_btsocket_l2cap_raw_sockets_mtx);
437878ed226SJulian Elischer 			} break;
438878ed226SJulian Elischer 
439878ed226SJulian Elischer 		default:
440878ed226SJulian Elischer 			NG_BTSOCKET_L2CAP_RAW_WARN(
441878ed226SJulian Elischer "%s: Unknown message, cmd=%d\n", __func__, msg->header.cmd);
442878ed226SJulian Elischer 			break;
443878ed226SJulian Elischer 		}
444878ed226SJulian Elischer 
445878ed226SJulian Elischer 		if (hook != NULL)
446878ed226SJulian Elischer 			NG_HOOK_UNREF(hook); /* remove extra reference */
447878ed226SJulian Elischer 
448878ed226SJulian Elischer 		NG_FREE_MSG(msg); /* Checks for msg != NULL */
449878ed226SJulian Elischer 	}
450f2bb1caeSJulian Elischer } /* ng_btsocket_l2cap_raw_input */
451878ed226SJulian Elischer 
452878ed226SJulian Elischer /*
453878ed226SJulian Elischer  * Route cleanup task. Gets scheduled when hook is disconnected. Here we
454878ed226SJulian Elischer  * will find all sockets that use "invalid" hook and disconnect them.
455878ed226SJulian Elischer  */
456878ed226SJulian Elischer 
457878ed226SJulian Elischer static void
ng_btsocket_l2cap_raw_rtclean(void * context,int pending)458878ed226SJulian Elischer ng_btsocket_l2cap_raw_rtclean(void *context, int pending)
459878ed226SJulian Elischer {
460878ed226SJulian Elischer 	ng_btsocket_l2cap_raw_pcb_p	pcb = NULL;
461878ed226SJulian Elischer 	ng_btsocket_l2cap_rtentry_p	rt = NULL;
462878ed226SJulian Elischer 
463878ed226SJulian Elischer 	/*
464878ed226SJulian Elischer 	 * First disconnect all sockets that use "invalid" hook
465878ed226SJulian Elischer 	 */
466878ed226SJulian Elischer 
467d6279c10SMaksim Yevmenkin 	mtx_lock(&ng_btsocket_l2cap_raw_sockets_mtx);
468d6279c10SMaksim Yevmenkin 
469f2bb1caeSJulian Elischer 	LIST_FOREACH(pcb, &ng_btsocket_l2cap_raw_sockets, next) {
470f2bb1caeSJulian Elischer 		mtx_lock(&pcb->pcb_mtx);
471f2bb1caeSJulian Elischer 
472878ed226SJulian Elischer 		if (pcb->rt != NULL &&
473878ed226SJulian Elischer 		    pcb->rt->hook != NULL && NG_HOOK_NOT_VALID(pcb->rt->hook)) {
474878ed226SJulian Elischer 			if (pcb->so != NULL &&
475878ed226SJulian Elischer 			    pcb->so->so_state & SS_ISCONNECTED)
476878ed226SJulian Elischer 				soisdisconnected(pcb->so);
477878ed226SJulian Elischer 
478878ed226SJulian Elischer 			pcb->rt = NULL;
479878ed226SJulian Elischer 		}
480878ed226SJulian Elischer 
481f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
482f2bb1caeSJulian Elischer 	}
483f2bb1caeSJulian Elischer 
484d6279c10SMaksim Yevmenkin 	mtx_unlock(&ng_btsocket_l2cap_raw_sockets_mtx);
485d6279c10SMaksim Yevmenkin 
486878ed226SJulian Elischer 	/*
487878ed226SJulian Elischer 	 * Now cleanup routing table
488878ed226SJulian Elischer 	 */
489878ed226SJulian Elischer 
490d6279c10SMaksim Yevmenkin 	mtx_lock(&ng_btsocket_l2cap_raw_rt_mtx);
491d6279c10SMaksim Yevmenkin 
492f2bb1caeSJulian Elischer 	for (rt = LIST_FIRST(&ng_btsocket_l2cap_raw_rt); rt != NULL; ) {
493878ed226SJulian Elischer 		ng_btsocket_l2cap_rtentry_p	rt_next = LIST_NEXT(rt, next);
494878ed226SJulian Elischer 
495878ed226SJulian Elischer 		if (rt->hook != NULL && NG_HOOK_NOT_VALID(rt->hook)) {
496878ed226SJulian Elischer 			LIST_REMOVE(rt, next);
497878ed226SJulian Elischer 
498878ed226SJulian Elischer 			NG_HOOK_SET_PRIVATE(rt->hook, NULL);
499878ed226SJulian Elischer 			NG_HOOK_UNREF(rt->hook); /* Remove extra reference */
500878ed226SJulian Elischer 
501878ed226SJulian Elischer 			bzero(rt, sizeof(*rt));
5021ede983cSDag-Erling Smørgrav 			free(rt, M_NETGRAPH_BTSOCKET_L2CAP_RAW);
503878ed226SJulian Elischer 		}
504878ed226SJulian Elischer 
505878ed226SJulian Elischer 		rt = rt_next;
506878ed226SJulian Elischer 	}
507878ed226SJulian Elischer 
508878ed226SJulian Elischer 	mtx_unlock(&ng_btsocket_l2cap_raw_rt_mtx);
509878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_rtclean */
510878ed226SJulian Elischer 
511878ed226SJulian Elischer /*
512878ed226SJulian Elischer  * Initialize everything
513878ed226SJulian Elischer  */
514878ed226SJulian Elischer 
51589128ff3SGleb Smirnoff static void
ng_btsocket_l2cap_raw_init(void * arg __unused)51689128ff3SGleb Smirnoff ng_btsocket_l2cap_raw_init(void *arg __unused)
517878ed226SJulian Elischer {
518878ed226SJulian Elischer 	int	error = 0;
519878ed226SJulian Elischer 
520878ed226SJulian Elischer 	ng_btsocket_l2cap_raw_node = NULL;
521878ed226SJulian Elischer 	ng_btsocket_l2cap_raw_debug_level = NG_BTSOCKET_WARN_LEVEL;
522878ed226SJulian Elischer 	ng_btsocket_l2cap_raw_ioctl_timeout = 5;
523878ed226SJulian Elischer 
524878ed226SJulian Elischer 	/* Register Netgraph node type */
525878ed226SJulian Elischer 	error = ng_newtype(&typestruct);
526878ed226SJulian Elischer 	if (error != 0) {
527878ed226SJulian Elischer 		NG_BTSOCKET_L2CAP_RAW_ALERT(
528878ed226SJulian Elischer "%s: Could not register Netgraph node type, error=%d\n", __func__, error);
529878ed226SJulian Elischer 
530878ed226SJulian Elischer                 return;
531878ed226SJulian Elischer 	}
532878ed226SJulian Elischer 
533878ed226SJulian Elischer 	/* Create Netgrapg node */
534878ed226SJulian Elischer 	error = ng_make_node_common(&typestruct, &ng_btsocket_l2cap_raw_node);
535878ed226SJulian Elischer 	if (error != 0) {
536878ed226SJulian Elischer 		NG_BTSOCKET_L2CAP_RAW_ALERT(
537878ed226SJulian Elischer "%s: Could not create Netgraph node, error=%d\n", __func__, error);
538878ed226SJulian Elischer 
539878ed226SJulian Elischer 		ng_btsocket_l2cap_raw_node = NULL;
540878ed226SJulian Elischer 
541878ed226SJulian Elischer 		return;
542878ed226SJulian Elischer 	}
543878ed226SJulian Elischer 
544878ed226SJulian Elischer 	error = ng_name_node(ng_btsocket_l2cap_raw_node,
545878ed226SJulian Elischer 				NG_BTSOCKET_L2CAP_RAW_NODE_TYPE);
546878ed226SJulian Elischer 	if (error != 0) {
547878ed226SJulian Elischer 		NG_BTSOCKET_L2CAP_RAW_ALERT(
548878ed226SJulian Elischer "%s: Could not name Netgraph node, error=%d\n", __func__, error);
549878ed226SJulian Elischer 
550878ed226SJulian Elischer 		NG_NODE_UNREF(ng_btsocket_l2cap_raw_node);
551878ed226SJulian Elischer 		ng_btsocket_l2cap_raw_node = NULL;
552878ed226SJulian Elischer 
553878ed226SJulian Elischer 		return;
554878ed226SJulian Elischer 	}
555878ed226SJulian Elischer 
556878ed226SJulian Elischer 	/* Create input queue */
557878ed226SJulian Elischer 	NG_BT_ITEMQ_INIT(&ng_btsocket_l2cap_raw_queue, ifqmaxlen);
558878ed226SJulian Elischer 	mtx_init(&ng_btsocket_l2cap_raw_queue_mtx,
559db37c09aSMaksim Yevmenkin 		"btsocks_l2cap_raw_queue_mtx", NULL, MTX_DEF);
560878ed226SJulian Elischer 	TASK_INIT(&ng_btsocket_l2cap_raw_queue_task, 0,
561878ed226SJulian Elischer 		ng_btsocket_l2cap_raw_input, NULL);
562878ed226SJulian Elischer 
563878ed226SJulian Elischer 	/* Create list of sockets */
564878ed226SJulian Elischer 	LIST_INIT(&ng_btsocket_l2cap_raw_sockets);
565878ed226SJulian Elischer 	mtx_init(&ng_btsocket_l2cap_raw_sockets_mtx,
566db37c09aSMaksim Yevmenkin 		"btsocks_l2cap_raw_sockets_mtx", NULL, MTX_DEF);
567878ed226SJulian Elischer 
568878ed226SJulian Elischer 	/* Tokens */
569878ed226SJulian Elischer 	ng_btsocket_l2cap_raw_token = 0;
570878ed226SJulian Elischer 	mtx_init(&ng_btsocket_l2cap_raw_token_mtx,
571db37c09aSMaksim Yevmenkin 		"btsocks_l2cap_raw_token_mtx", NULL, MTX_DEF);
572878ed226SJulian Elischer 
573878ed226SJulian Elischer 	/* Routing table */
574878ed226SJulian Elischer 	LIST_INIT(&ng_btsocket_l2cap_raw_rt);
575878ed226SJulian Elischer 	mtx_init(&ng_btsocket_l2cap_raw_rt_mtx,
576db37c09aSMaksim Yevmenkin 		"btsocks_l2cap_raw_rt_mtx", NULL, MTX_DEF);
577878ed226SJulian Elischer 	TASK_INIT(&ng_btsocket_l2cap_raw_rt_task, 0,
578878ed226SJulian Elischer 		ng_btsocket_l2cap_raw_rtclean, NULL);
579878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_init */
58089128ff3SGleb Smirnoff SYSINIT(ng_btsocket_l2cap_raw_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD,
58189128ff3SGleb Smirnoff     ng_btsocket_l2cap_raw_init, NULL);
582878ed226SJulian Elischer 
583878ed226SJulian Elischer /*
584878ed226SJulian Elischer  * Abort connection on socket
585878ed226SJulian Elischer  */
586878ed226SJulian Elischer 
587ac45e92fSRobert Watson void
ng_btsocket_l2cap_raw_abort(struct socket * so)588878ed226SJulian Elischer ng_btsocket_l2cap_raw_abort(struct socket *so)
589878ed226SJulian Elischer {
590a152f8a3SRobert Watson 
591a152f8a3SRobert Watson 	(void)ng_btsocket_l2cap_raw_disconnect(so);
592878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_abort */
593878ed226SJulian Elischer 
594a152f8a3SRobert Watson void
ng_btsocket_l2cap_raw_close(struct socket * so)595a152f8a3SRobert Watson ng_btsocket_l2cap_raw_close(struct socket *so)
596a152f8a3SRobert Watson {
597a152f8a3SRobert Watson 
598a152f8a3SRobert Watson 	(void)ng_btsocket_l2cap_raw_disconnect(so);
599a152f8a3SRobert Watson } /* ng_btsocket_l2cap_raw_close */
600a152f8a3SRobert Watson 
601878ed226SJulian Elischer /*
602878ed226SJulian Elischer  * Create and attach new socket
603878ed226SJulian Elischer  */
604878ed226SJulian Elischer 
605878ed226SJulian Elischer int
ng_btsocket_l2cap_raw_attach(struct socket * so,int proto,struct thread * td)606878ed226SJulian Elischer ng_btsocket_l2cap_raw_attach(struct socket *so, int proto, struct thread *td)
607878ed226SJulian Elischer {
608878ed226SJulian Elischer 	ng_btsocket_l2cap_raw_pcb_p	pcb = so2l2cap_raw_pcb(so);
609878ed226SJulian Elischer 	int				error;
610878ed226SJulian Elischer 
611878ed226SJulian Elischer 	if (pcb != NULL)
612878ed226SJulian Elischer 		return (EISCONN);
613878ed226SJulian Elischer 
614878ed226SJulian Elischer 	if (ng_btsocket_l2cap_raw_node == NULL)
615878ed226SJulian Elischer 		return (EPROTONOSUPPORT);
616878ed226SJulian Elischer 	if (so->so_type != SOCK_RAW)
617878ed226SJulian Elischer 		return (ESOCKTNOSUPPORT);
618878ed226SJulian Elischer 
619878ed226SJulian Elischer 	/* Reserve send and receive space if it is not reserved yet */
620878ed226SJulian Elischer 	error = soreserve(so, NG_BTSOCKET_L2CAP_RAW_SENDSPACE,
621878ed226SJulian Elischer 			NG_BTSOCKET_L2CAP_RAW_RECVSPACE);
622878ed226SJulian Elischer 	if (error != 0)
623878ed226SJulian Elischer 		return (error);
624878ed226SJulian Elischer 
625878ed226SJulian Elischer 	/* Allocate the PCB */
6261ede983cSDag-Erling Smørgrav         pcb = malloc(sizeof(*pcb),
627f2bb1caeSJulian Elischer 		M_NETGRAPH_BTSOCKET_L2CAP_RAW, M_NOWAIT|M_ZERO);
628878ed226SJulian Elischer         if (pcb == NULL)
629878ed226SJulian Elischer                 return (ENOMEM);
630878ed226SJulian Elischer 
631878ed226SJulian Elischer 	/* Link the PCB and the socket */
632878ed226SJulian Elischer 	so->so_pcb = (caddr_t) pcb;
633878ed226SJulian Elischer 	pcb->so = so;
634878ed226SJulian Elischer 
635acd3428bSRobert Watson 	if (priv_check(td, PRIV_NETBLUETOOTH_RAW) == 0)
636f2bb1caeSJulian Elischer 		pcb->flags |= NG_BTSOCKET_L2CAP_RAW_PRIVILEGED;
637f2bb1caeSJulian Elischer 
638f2bb1caeSJulian Elischer 	mtx_init(&pcb->pcb_mtx, "btsocks_l2cap_raw_pcb_mtx", NULL, MTX_DEF);
639f2bb1caeSJulian Elischer 
640878ed226SJulian Elischer         /* Add the PCB to the list */
641878ed226SJulian Elischer 	mtx_lock(&ng_btsocket_l2cap_raw_sockets_mtx);
642878ed226SJulian Elischer 	LIST_INSERT_HEAD(&ng_btsocket_l2cap_raw_sockets, pcb, next);
643878ed226SJulian Elischer 	mtx_unlock(&ng_btsocket_l2cap_raw_sockets_mtx);
644878ed226SJulian Elischer 
645878ed226SJulian Elischer         return (0);
646878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_attach */
647878ed226SJulian Elischer 
648878ed226SJulian Elischer /*
649878ed226SJulian Elischer  * Bind socket
650878ed226SJulian Elischer  */
651878ed226SJulian Elischer 
652878ed226SJulian Elischer int
ng_btsocket_l2cap_raw_bind(struct socket * so,struct sockaddr * nam,struct thread * td)653878ed226SJulian Elischer ng_btsocket_l2cap_raw_bind(struct socket *so, struct sockaddr *nam,
654878ed226SJulian Elischer 		struct thread *td)
655878ed226SJulian Elischer {
656878ed226SJulian Elischer 	ng_btsocket_l2cap_raw_pcb_t	*pcb = so2l2cap_raw_pcb(so);
657878ed226SJulian Elischer 	struct sockaddr_l2cap		*sa = (struct sockaddr_l2cap *) nam;
658f2bb1caeSJulian Elischer 	ng_btsocket_l2cap_rtentry_t	*rt = NULL;
659878ed226SJulian Elischer 
660878ed226SJulian Elischer 	if (pcb == NULL)
661878ed226SJulian Elischer 		return (EINVAL);
662878ed226SJulian Elischer 	if (ng_btsocket_l2cap_raw_node == NULL)
663878ed226SJulian Elischer 		return (EINVAL);
664878ed226SJulian Elischer 
665878ed226SJulian Elischer 	if (sa == NULL)
666878ed226SJulian Elischer 		return (EINVAL);
667878ed226SJulian Elischer 	if (sa->l2cap_family != AF_BLUETOOTH)
668878ed226SJulian Elischer 		return (EAFNOSUPPORT);
669fbc48c2bSTakanori Watanabe 	if((sa->l2cap_len != sizeof(*sa))&&
670fbc48c2bSTakanori Watanabe 	   (sa->l2cap_len != sizeof(struct sockaddr_l2cap_compat)))
671878ed226SJulian Elischer 		return (EINVAL);
672878ed226SJulian Elischer 
673d6279c10SMaksim Yevmenkin 	if (bcmp(&sa->l2cap_bdaddr, NG_HCI_BDADDR_ANY,
674d6279c10SMaksim Yevmenkin 				sizeof(sa->l2cap_bdaddr)) != 0) {
675f2bb1caeSJulian Elischer 		mtx_lock(&ng_btsocket_l2cap_raw_rt_mtx);
676f2bb1caeSJulian Elischer 
677f2bb1caeSJulian Elischer 		LIST_FOREACH(rt, &ng_btsocket_l2cap_raw_rt, next) {
678f2bb1caeSJulian Elischer 			if (rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))
679f2bb1caeSJulian Elischer 				continue;
680f2bb1caeSJulian Elischer 
681d6279c10SMaksim Yevmenkin 			if (bcmp(&sa->l2cap_bdaddr, &rt->src,
682d6279c10SMaksim Yevmenkin 					sizeof(rt->src)) == 0)
683f2bb1caeSJulian Elischer 				break;
684f2bb1caeSJulian Elischer 		}
685f2bb1caeSJulian Elischer 
686f2bb1caeSJulian Elischer 		mtx_unlock(&ng_btsocket_l2cap_raw_rt_mtx);
687f2bb1caeSJulian Elischer 
688d6279c10SMaksim Yevmenkin 		if (rt == NULL)
689d6279c10SMaksim Yevmenkin 			return (ENETDOWN);
690d6279c10SMaksim Yevmenkin 	} else
691d6279c10SMaksim Yevmenkin 		rt = NULL;
692d6279c10SMaksim Yevmenkin 
693d6279c10SMaksim Yevmenkin 	mtx_lock(&pcb->pcb_mtx);
694d6279c10SMaksim Yevmenkin 	bcopy(&sa->l2cap_bdaddr, &pcb->src, sizeof(pcb->src));
695d6279c10SMaksim Yevmenkin 	pcb->rt = rt;
696d6279c10SMaksim Yevmenkin 	mtx_unlock(&pcb->pcb_mtx);
697d6279c10SMaksim Yevmenkin 
698d6279c10SMaksim Yevmenkin 	return (0);
699878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_bind */
700878ed226SJulian Elischer 
701878ed226SJulian Elischer /*
702878ed226SJulian Elischer  * Connect socket
703878ed226SJulian Elischer  */
704878ed226SJulian Elischer 
705878ed226SJulian Elischer int
ng_btsocket_l2cap_raw_connect(struct socket * so,struct sockaddr * nam,struct thread * td)706878ed226SJulian Elischer ng_btsocket_l2cap_raw_connect(struct socket *so, struct sockaddr *nam,
707878ed226SJulian Elischer 		struct thread *td)
708878ed226SJulian Elischer {
709878ed226SJulian Elischer 	ng_btsocket_l2cap_raw_pcb_t	*pcb = so2l2cap_raw_pcb(so);
710878ed226SJulian Elischer 	struct sockaddr_l2cap		*sa = (struct sockaddr_l2cap *) nam;
711878ed226SJulian Elischer 	ng_btsocket_l2cap_rtentry_t	*rt = NULL;
712f2bb1caeSJulian Elischer 	int				 error;
713878ed226SJulian Elischer 
714878ed226SJulian Elischer 	if (pcb == NULL)
715878ed226SJulian Elischer 		return (EINVAL);
716878ed226SJulian Elischer 	if (ng_btsocket_l2cap_raw_node == NULL)
717878ed226SJulian Elischer 		return (EINVAL);
718878ed226SJulian Elischer 
719878ed226SJulian Elischer 	if (sa == NULL)
720878ed226SJulian Elischer 		return (EINVAL);
721878ed226SJulian Elischer 	if (sa->l2cap_family != AF_BLUETOOTH)
722878ed226SJulian Elischer 		return (EAFNOSUPPORT);
723fbc48c2bSTakanori Watanabe 	if((sa->l2cap_len != sizeof(*sa))&&
724fbc48c2bSTakanori Watanabe 	   (sa->l2cap_len != sizeof(struct sockaddr_l2cap_compat)))
725878ed226SJulian Elischer 		return (EINVAL);
726fbc48c2bSTakanori Watanabe 
727878ed226SJulian Elischer 	if (bcmp(&sa->l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
728878ed226SJulian Elischer 		return (EINVAL);
729878ed226SJulian Elischer 
730f2bb1caeSJulian Elischer 	mtx_lock(&pcb->pcb_mtx);
731f2bb1caeSJulian Elischer 
732f2bb1caeSJulian Elischer 	bcopy(&sa->l2cap_bdaddr, &pcb->dst, sizeof(pcb->dst));
733f2bb1caeSJulian Elischer 
734f2bb1caeSJulian Elischer 	if (bcmp(&pcb->src, &pcb->dst, sizeof(pcb->src)) == 0) {
735f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
736f2bb1caeSJulian Elischer 
737f2bb1caeSJulian Elischer 		return (EADDRNOTAVAIL);
738f2bb1caeSJulian Elischer 	}
739f2bb1caeSJulian Elischer 
740f2bb1caeSJulian Elischer 	/*
741f2bb1caeSJulian Elischer 	 * If there is route already - use it
742f2bb1caeSJulian Elischer 	 */
743f2bb1caeSJulian Elischer 
744f2bb1caeSJulian Elischer 	if (pcb->rt != NULL) {
745f2bb1caeSJulian Elischer 		soisconnected(so);
746f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
747f2bb1caeSJulian Elischer 
748f2bb1caeSJulian Elischer 		return (0);
749f2bb1caeSJulian Elischer 	}
750f2bb1caeSJulian Elischer 
751f2bb1caeSJulian Elischer 	/*
752f2bb1caeSJulian Elischer 	 * Find the first hook that does not match specified destination address
753f2bb1caeSJulian Elischer 	 */
754878ed226SJulian Elischer 
755d6279c10SMaksim Yevmenkin 	mtx_lock(&ng_btsocket_l2cap_raw_rt_mtx);
756d6279c10SMaksim Yevmenkin 
757878ed226SJulian Elischer 	LIST_FOREACH(rt, &ng_btsocket_l2cap_raw_rt, next) {
758878ed226SJulian Elischer 		if (rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))
759878ed226SJulian Elischer 			continue;
760878ed226SJulian Elischer 
761f2bb1caeSJulian Elischer 		if (bcmp(&pcb->dst, &rt->src, sizeof(rt->src)) != 0)
762878ed226SJulian Elischer 			break;
763878ed226SJulian Elischer 	}
764878ed226SJulian Elischer 
765878ed226SJulian Elischer 	if (rt != NULL) {
766878ed226SJulian Elischer 		soisconnected(so);
767878ed226SJulian Elischer 
768f2bb1caeSJulian Elischer 		pcb->rt = rt;
769f2bb1caeSJulian Elischer 		bcopy(&rt->src, &pcb->src, sizeof(pcb->src));
770f2bb1caeSJulian Elischer 
771f2bb1caeSJulian Elischer 		error = 0;
772f2bb1caeSJulian Elischer 	} else
773f2bb1caeSJulian Elischer 		error = ENETDOWN;
774f2bb1caeSJulian Elischer 
775878ed226SJulian Elischer 	mtx_unlock(&ng_btsocket_l2cap_raw_rt_mtx);
776d6279c10SMaksim Yevmenkin 	mtx_unlock(&pcb->pcb_mtx);
777878ed226SJulian Elischer 
778f2bb1caeSJulian Elischer 	return  (error);
779878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_connect */
780878ed226SJulian Elischer 
781878ed226SJulian Elischer /*
782878ed226SJulian Elischer  * Process ioctl's calls on socket
783878ed226SJulian Elischer  */
784878ed226SJulian Elischer 
785878ed226SJulian Elischer int
ng_btsocket_l2cap_raw_control(struct socket * so,u_long cmd,void * data,struct ifnet * ifp,struct thread * td)786f277746eSGleb Smirnoff ng_btsocket_l2cap_raw_control(struct socket *so, u_long cmd, void *data,
787878ed226SJulian Elischer 		struct ifnet *ifp, struct thread *td)
788878ed226SJulian Elischer {
789878ed226SJulian Elischer 	ng_btsocket_l2cap_raw_pcb_p	 pcb = so2l2cap_raw_pcb(so);
790878ed226SJulian Elischer 	struct ng_mesg			*msg = NULL;
791878ed226SJulian Elischer 	int				 error = 0;
792878ed226SJulian Elischer 
793878ed226SJulian Elischer 	if (pcb == NULL)
794878ed226SJulian Elischer 		return (EINVAL);
795878ed226SJulian Elischer 	if (ng_btsocket_l2cap_raw_node == NULL)
796878ed226SJulian Elischer 		return (EINVAL);
797878ed226SJulian Elischer 
798f2bb1caeSJulian Elischer 	mtx_lock(&pcb->pcb_mtx);
799878ed226SJulian Elischer 
800f2bb1caeSJulian Elischer 	/* Check if we route info */
801f2bb1caeSJulian Elischer 	if (pcb->rt == NULL) {
802f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
803878ed226SJulian Elischer 		return (EHOSTUNREACH);
804878ed226SJulian Elischer 	}
805878ed226SJulian Elischer 
806f2bb1caeSJulian Elischer 	/* Check if we have pending ioctl() */
807f2bb1caeSJulian Elischer 	if (pcb->token != 0) {
808f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
809f2bb1caeSJulian Elischer 		return (EBUSY);
810f2bb1caeSJulian Elischer 	}
811878ed226SJulian Elischer 
812878ed226SJulian Elischer 	switch (cmd) {
813878ed226SJulian Elischer 	case SIOC_L2CAP_NODE_GET_FLAGS: {
814878ed226SJulian Elischer 		struct ng_btsocket_l2cap_raw_node_flags	*p =
815878ed226SJulian Elischer 			(struct ng_btsocket_l2cap_raw_node_flags *) data;
816878ed226SJulian Elischer 
817f2bb1caeSJulian Elischer 		error = ng_btsocket_l2cap_raw_send_sync_ngmsg(pcb,
818f2bb1caeSJulian Elischer 				NGM_L2CAP_NODE_GET_FLAGS,
819f2bb1caeSJulian Elischer 				&p->flags, sizeof(p->flags));
820878ed226SJulian Elischer 		} break;
821878ed226SJulian Elischer 
822878ed226SJulian Elischer 	case SIOC_L2CAP_NODE_GET_DEBUG: {
823878ed226SJulian Elischer 		struct ng_btsocket_l2cap_raw_node_debug	*p =
824878ed226SJulian Elischer 			(struct ng_btsocket_l2cap_raw_node_debug *) data;
825878ed226SJulian Elischer 
826f2bb1caeSJulian Elischer 		error = ng_btsocket_l2cap_raw_send_sync_ngmsg(pcb,
827f2bb1caeSJulian Elischer 				NGM_L2CAP_NODE_GET_DEBUG,
828f2bb1caeSJulian Elischer 				&p->debug, sizeof(p->debug));
829878ed226SJulian Elischer 		} break;
830878ed226SJulian Elischer 
831878ed226SJulian Elischer 	case SIOC_L2CAP_NODE_SET_DEBUG: {
832878ed226SJulian Elischer 		struct ng_btsocket_l2cap_raw_node_debug	*p =
833878ed226SJulian Elischer 			(struct ng_btsocket_l2cap_raw_node_debug *) data;
834878ed226SJulian Elischer 
835f2bb1caeSJulian Elischer 		if (pcb->flags & NG_BTSOCKET_L2CAP_RAW_PRIVILEGED)
836f2bb1caeSJulian Elischer 			error = ng_btsocket_l2cap_raw_send_ngmsg(pcb->rt->hook,
837f2bb1caeSJulian Elischer 					NGM_L2CAP_NODE_SET_DEBUG,
838f2bb1caeSJulian Elischer 					&p->debug, sizeof(p->debug));
839f2bb1caeSJulian Elischer 		else
840f2bb1caeSJulian Elischer 			error = EPERM;
841878ed226SJulian Elischer 		} break;
842878ed226SJulian Elischer 
843878ed226SJulian Elischer 	case SIOC_L2CAP_NODE_GET_CON_LIST: {
844878ed226SJulian Elischer 		struct ng_btsocket_l2cap_raw_con_list	*p =
845878ed226SJulian Elischer 			(struct ng_btsocket_l2cap_raw_con_list *) data;
846878ed226SJulian Elischer 		ng_l2cap_node_con_list_ep		*p1 = NULL;
847878ed226SJulian Elischer                 ng_l2cap_node_con_ep			*p2 = NULL;
848878ed226SJulian Elischer 
849878ed226SJulian Elischer 		if (p->num_connections == 0 ||
850878ed226SJulian Elischer 		    p->num_connections > NG_L2CAP_MAX_CON_NUM ||
851878ed226SJulian Elischer 		    p->connections == NULL) {
852444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
853444e5d09SMaksim Yevmenkin 			return (EINVAL);
854878ed226SJulian Elischer 		}
855878ed226SJulian Elischer 
856878ed226SJulian Elischer 		NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_NODE_GET_CON_LIST,
857f2bb1caeSJulian Elischer 			0, M_NOWAIT);
858878ed226SJulian Elischer 		if (msg == NULL) {
859444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
860444e5d09SMaksim Yevmenkin 			return (ENOMEM);
861878ed226SJulian Elischer 		}
862f2bb1caeSJulian Elischer 		ng_btsocket_l2cap_raw_get_token(&msg->header.token);
863f2bb1caeSJulian Elischer 		pcb->token = msg->header.token;
864f2bb1caeSJulian Elischer 		pcb->msg = NULL;
865878ed226SJulian Elischer 
866878ed226SJulian Elischer 		NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node, msg,
867b9fe2d6cSAlfred Perlstein 			pcb->rt->hook, 0);
868878ed226SJulian Elischer 		if (error != 0) {
869878ed226SJulian Elischer 			pcb->token = 0;
870444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
871444e5d09SMaksim Yevmenkin 			return (error);
872878ed226SJulian Elischer 		}
873878ed226SJulian Elischer 
874f2bb1caeSJulian Elischer 		error = msleep(&pcb->msg, &pcb->pcb_mtx, PZERO|PCATCH, "l2ctl",
875878ed226SJulian Elischer 				ng_btsocket_l2cap_raw_ioctl_timeout * hz);
876878ed226SJulian Elischer 		pcb->token = 0;
877f2bb1caeSJulian Elischer 
878444e5d09SMaksim Yevmenkin 		if (error != 0) {
879444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
880444e5d09SMaksim Yevmenkin 			return (error);
881444e5d09SMaksim Yevmenkin 		}
882878ed226SJulian Elischer 
883444e5d09SMaksim Yevmenkin 		msg = pcb->msg;
884444e5d09SMaksim Yevmenkin 		pcb->msg = NULL;
885444e5d09SMaksim Yevmenkin 
886444e5d09SMaksim Yevmenkin 		mtx_unlock(&pcb->pcb_mtx);
887444e5d09SMaksim Yevmenkin 
888444e5d09SMaksim Yevmenkin 		if (msg != NULL &&
889444e5d09SMaksim Yevmenkin 		    msg->header.cmd == NGM_L2CAP_NODE_GET_CON_LIST) {
890878ed226SJulian Elischer 			/* Return data back to user space */
891444e5d09SMaksim Yevmenkin 			p1 = (ng_l2cap_node_con_list_ep *)(msg->data);
892878ed226SJulian Elischer 			p2 = (ng_l2cap_node_con_ep *)(p1 + 1);
893878ed226SJulian Elischer 
894878ed226SJulian Elischer 			p->num_connections = min(p->num_connections,
895878ed226SJulian Elischer 						p1->num_connections);
896878ed226SJulian Elischer 			if (p->num_connections > 0)
897878ed226SJulian Elischer 				error = copyout((caddr_t) p2,
898878ed226SJulian Elischer 					(caddr_t) p->connections,
899878ed226SJulian Elischer 					p->num_connections * sizeof(*p2));
900878ed226SJulian Elischer 		} else
901878ed226SJulian Elischer 			error = EINVAL;
902878ed226SJulian Elischer 
903444e5d09SMaksim Yevmenkin 		NG_FREE_MSG(msg); /* checks for != NULL */
904444e5d09SMaksim Yevmenkin 		return (error);
905444e5d09SMaksim Yevmenkin 		} /* NOTREACHED */
906878ed226SJulian Elischer 
907878ed226SJulian Elischer 	case SIOC_L2CAP_NODE_GET_CHAN_LIST: {
908878ed226SJulian Elischer 		struct ng_btsocket_l2cap_raw_chan_list	*p =
909878ed226SJulian Elischer 			(struct ng_btsocket_l2cap_raw_chan_list *) data;
910878ed226SJulian Elischer 		ng_l2cap_node_chan_list_ep		*p1 = NULL;
911878ed226SJulian Elischer                 ng_l2cap_node_chan_ep			*p2 = NULL;
912878ed226SJulian Elischer 
913878ed226SJulian Elischer 		if (p->num_channels == 0 ||
914878ed226SJulian Elischer 		    p->num_channels > NG_L2CAP_MAX_CHAN_NUM ||
915878ed226SJulian Elischer 		    p->channels == NULL) {
916444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
917444e5d09SMaksim Yevmenkin 			return (EINVAL);
918878ed226SJulian Elischer 		}
919878ed226SJulian Elischer 
920878ed226SJulian Elischer 		NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE,
921f2bb1caeSJulian Elischer 			NGM_L2CAP_NODE_GET_CHAN_LIST, 0, M_NOWAIT);
922878ed226SJulian Elischer 		if (msg == NULL) {
923444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
924444e5d09SMaksim Yevmenkin 			return (ENOMEM);
925878ed226SJulian Elischer 		}
926f2bb1caeSJulian Elischer 		ng_btsocket_l2cap_raw_get_token(&msg->header.token);
927f2bb1caeSJulian Elischer 		pcb->token = msg->header.token;
928f2bb1caeSJulian Elischer 		pcb->msg = NULL;
929878ed226SJulian Elischer 
930878ed226SJulian Elischer 		NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node, msg,
931b9fe2d6cSAlfred Perlstein 			pcb->rt->hook, 0);
932878ed226SJulian Elischer 		if (error != 0) {
933878ed226SJulian Elischer 			pcb->token = 0;
934444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
935444e5d09SMaksim Yevmenkin 			return (error);
936878ed226SJulian Elischer 		}
937878ed226SJulian Elischer 
938f2bb1caeSJulian Elischer 		error = msleep(&pcb->msg, &pcb->pcb_mtx, PZERO|PCATCH, "l2ctl",
939878ed226SJulian Elischer 				ng_btsocket_l2cap_raw_ioctl_timeout * hz);
940878ed226SJulian Elischer 		pcb->token = 0;
941f2bb1caeSJulian Elischer 
942444e5d09SMaksim Yevmenkin 		if (error != 0) {
943444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
944444e5d09SMaksim Yevmenkin 			return (error);
945444e5d09SMaksim Yevmenkin 		}
946878ed226SJulian Elischer 
947444e5d09SMaksim Yevmenkin 		msg = pcb->msg;
948444e5d09SMaksim Yevmenkin 		pcb->msg = NULL;
949444e5d09SMaksim Yevmenkin 
950444e5d09SMaksim Yevmenkin 		mtx_unlock(&pcb->pcb_mtx);
951444e5d09SMaksim Yevmenkin 
952444e5d09SMaksim Yevmenkin 		if (msg != NULL &&
953444e5d09SMaksim Yevmenkin 		    msg->header.cmd == NGM_L2CAP_NODE_GET_CHAN_LIST) {
954878ed226SJulian Elischer 			/* Return data back to user space */
955444e5d09SMaksim Yevmenkin 			p1 = (ng_l2cap_node_chan_list_ep *)(msg->data);
956878ed226SJulian Elischer 			p2 = (ng_l2cap_node_chan_ep *)(p1 + 1);
957878ed226SJulian Elischer 
958878ed226SJulian Elischer 			p->num_channels = min(p->num_channels,
959878ed226SJulian Elischer 						p1->num_channels);
960878ed226SJulian Elischer 			if (p->num_channels > 0)
961878ed226SJulian Elischer 				error = copyout((caddr_t) p2,
962878ed226SJulian Elischer 						(caddr_t) p->channels,
963878ed226SJulian Elischer 						p->num_channels * sizeof(*p2));
964878ed226SJulian Elischer 		} else
965878ed226SJulian Elischer 			error = EINVAL;
966878ed226SJulian Elischer 
967444e5d09SMaksim Yevmenkin 		NG_FREE_MSG(msg); /* checks for != NULL */
968444e5d09SMaksim Yevmenkin 		return (error);
969444e5d09SMaksim Yevmenkin 		} /* NOTREACHED */
970878ed226SJulian Elischer 
971878ed226SJulian Elischer 	case SIOC_L2CAP_L2CA_PING: {
972878ed226SJulian Elischer 		struct ng_btsocket_l2cap_raw_ping	*p =
973878ed226SJulian Elischer 			(struct ng_btsocket_l2cap_raw_ping *) data;
974878ed226SJulian Elischer 		ng_l2cap_l2ca_ping_ip			*ip = NULL;
975878ed226SJulian Elischer 		ng_l2cap_l2ca_ping_op			*op = NULL;
976878ed226SJulian Elischer 
977878ed226SJulian Elischer 		if ((p->echo_size != 0 && p->echo_data == NULL) ||
978878ed226SJulian Elischer 		     p->echo_size > NG_L2CAP_MAX_ECHO_SIZE) {
979444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
980444e5d09SMaksim Yevmenkin 			return (EINVAL);
981878ed226SJulian Elischer 		}
982878ed226SJulian Elischer 
983878ed226SJulian Elischer 		NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE,
984878ed226SJulian Elischer 			NGM_L2CAP_L2CA_PING, sizeof(*ip) + p->echo_size,
985f2bb1caeSJulian Elischer 			M_NOWAIT);
986878ed226SJulian Elischer 		if (msg == NULL) {
987444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
988444e5d09SMaksim Yevmenkin 			return (ENOMEM);
989878ed226SJulian Elischer 		}
990f2bb1caeSJulian Elischer 		ng_btsocket_l2cap_raw_get_token(&msg->header.token);
991f2bb1caeSJulian Elischer 		pcb->token = msg->header.token;
992f2bb1caeSJulian Elischer 		pcb->msg = NULL;
993878ed226SJulian Elischer 
994878ed226SJulian Elischer 		ip = (ng_l2cap_l2ca_ping_ip *)(msg->data);
995f2bb1caeSJulian Elischer 		bcopy(&pcb->dst, &ip->bdaddr, sizeof(ip->bdaddr));
996878ed226SJulian Elischer 		ip->echo_size = p->echo_size;
997878ed226SJulian Elischer 
998878ed226SJulian Elischer 		if (ip->echo_size > 0) {
999444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
1000878ed226SJulian Elischer 			error = copyin(p->echo_data, ip + 1, p->echo_size);
1001444e5d09SMaksim Yevmenkin 			mtx_lock(&pcb->pcb_mtx);
1002444e5d09SMaksim Yevmenkin 
1003878ed226SJulian Elischer 			if (error != 0) {
1004878ed226SJulian Elischer 				NG_FREE_MSG(msg);
1005878ed226SJulian Elischer 				pcb->token = 0;
1006444e5d09SMaksim Yevmenkin 				mtx_unlock(&pcb->pcb_mtx);
1007444e5d09SMaksim Yevmenkin 				return (error);
1008878ed226SJulian Elischer 			}
1009878ed226SJulian Elischer 		}
1010878ed226SJulian Elischer 
1011878ed226SJulian Elischer 		NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node, msg,
1012b9fe2d6cSAlfred Perlstein 			pcb->rt->hook, 0);
1013878ed226SJulian Elischer 		if (error != 0) {
1014878ed226SJulian Elischer 			pcb->token = 0;
1015444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
1016444e5d09SMaksim Yevmenkin 			return (error);
1017878ed226SJulian Elischer 		}
1018878ed226SJulian Elischer 
1019f2bb1caeSJulian Elischer 		error = msleep(&pcb->msg, &pcb->pcb_mtx, PZERO|PCATCH, "l2ctl",
1020878ed226SJulian Elischer 				bluetooth_l2cap_rtx_timeout());
1021878ed226SJulian Elischer 		pcb->token = 0;
1022f2bb1caeSJulian Elischer 
1023444e5d09SMaksim Yevmenkin 		if (error != 0) {
1024444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
1025444e5d09SMaksim Yevmenkin 			return (error);
1026444e5d09SMaksim Yevmenkin 		}
1027878ed226SJulian Elischer 
1028444e5d09SMaksim Yevmenkin 		msg = pcb->msg;
1029444e5d09SMaksim Yevmenkin 		pcb->msg = NULL;
1030444e5d09SMaksim Yevmenkin 
1031444e5d09SMaksim Yevmenkin 		mtx_unlock(&pcb->pcb_mtx);
1032444e5d09SMaksim Yevmenkin 
1033444e5d09SMaksim Yevmenkin 		if (msg != NULL &&
1034444e5d09SMaksim Yevmenkin 		    msg->header.cmd == NGM_L2CAP_L2CA_PING) {
1035878ed226SJulian Elischer 			/* Return data back to the user space */
1036444e5d09SMaksim Yevmenkin 			op = (ng_l2cap_l2ca_ping_op *)(msg->data);
1037878ed226SJulian Elischer 			p->result = op->result;
1038878ed226SJulian Elischer 			p->echo_size = min(p->echo_size, op->echo_size);
1039878ed226SJulian Elischer 
1040878ed226SJulian Elischer 			if (p->echo_size > 0)
1041878ed226SJulian Elischer 				error = copyout(op + 1, p->echo_data,
1042878ed226SJulian Elischer 						p->echo_size);
1043878ed226SJulian Elischer 		} else
1044878ed226SJulian Elischer 			error = EINVAL;
1045878ed226SJulian Elischer 
1046444e5d09SMaksim Yevmenkin 		NG_FREE_MSG(msg); /* checks for != NULL */
1047444e5d09SMaksim Yevmenkin 		return (error);
1048444e5d09SMaksim Yevmenkin 		} /* NOTREACHED */
1049878ed226SJulian Elischer 
1050878ed226SJulian Elischer 	case SIOC_L2CAP_L2CA_GET_INFO: {
1051878ed226SJulian Elischer 		struct ng_btsocket_l2cap_raw_get_info	*p =
1052878ed226SJulian Elischer 			(struct ng_btsocket_l2cap_raw_get_info *) data;
1053878ed226SJulian Elischer 		ng_l2cap_l2ca_get_info_ip		*ip = NULL;
1054878ed226SJulian Elischer 		ng_l2cap_l2ca_get_info_op		*op = NULL;
1055878ed226SJulian Elischer 
1056f2bb1caeSJulian Elischer 		if (!(pcb->flags & NG_BTSOCKET_L2CAP_RAW_PRIVILEGED)) {
1057444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
1058444e5d09SMaksim Yevmenkin 			return (EPERM);
1059f2bb1caeSJulian Elischer 		}
1060f2bb1caeSJulian Elischer 
1061f2bb1caeSJulian Elischer 		if (p->info_size != 0 && p->info_data == NULL) {
1062444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
1063444e5d09SMaksim Yevmenkin 			return (EINVAL);
1064878ed226SJulian Elischer 		}
1065878ed226SJulian Elischer 
1066878ed226SJulian Elischer 		NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE,
1067878ed226SJulian Elischer 			NGM_L2CAP_L2CA_GET_INFO, sizeof(*ip) + p->info_size,
1068f2bb1caeSJulian Elischer 			M_NOWAIT);
1069878ed226SJulian Elischer 		if (msg == NULL) {
1070444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
1071444e5d09SMaksim Yevmenkin 			return (ENOMEM);
1072878ed226SJulian Elischer 		}
1073f2bb1caeSJulian Elischer 		ng_btsocket_l2cap_raw_get_token(&msg->header.token);
1074f2bb1caeSJulian Elischer 		pcb->token = msg->header.token;
1075f2bb1caeSJulian Elischer 		pcb->msg = NULL;
1076878ed226SJulian Elischer 
1077878ed226SJulian Elischer 		ip = (ng_l2cap_l2ca_get_info_ip *)(msg->data);
1078f2bb1caeSJulian Elischer 		bcopy(&pcb->dst, &ip->bdaddr, sizeof(ip->bdaddr));
1079878ed226SJulian Elischer 		ip->info_type = p->info_type;
1080878ed226SJulian Elischer 
1081878ed226SJulian Elischer 		NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node, msg,
1082b9fe2d6cSAlfred Perlstein 			pcb->rt->hook, 0);
1083878ed226SJulian Elischer 		if (error != 0) {
1084878ed226SJulian Elischer 			pcb->token = 0;
1085444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
1086444e5d09SMaksim Yevmenkin 			return (error);
1087878ed226SJulian Elischer 		}
1088878ed226SJulian Elischer 
1089f2bb1caeSJulian Elischer 		error = msleep(&pcb->msg, &pcb->pcb_mtx, PZERO|PCATCH, "l2ctl",
1090878ed226SJulian Elischer 				bluetooth_l2cap_rtx_timeout());
1091878ed226SJulian Elischer 		pcb->token = 0;
1092f2bb1caeSJulian Elischer 
1093444e5d09SMaksim Yevmenkin 		if (error != 0) {
1094444e5d09SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
1095444e5d09SMaksim Yevmenkin 			return (error);
1096444e5d09SMaksim Yevmenkin 		}
1097878ed226SJulian Elischer 
1098444e5d09SMaksim Yevmenkin 		msg = pcb->msg;
1099444e5d09SMaksim Yevmenkin 		pcb->msg = NULL;
1100444e5d09SMaksim Yevmenkin 
1101444e5d09SMaksim Yevmenkin 		mtx_unlock(&pcb->pcb_mtx);
1102444e5d09SMaksim Yevmenkin 
1103444e5d09SMaksim Yevmenkin 		if (msg != NULL &&
1104444e5d09SMaksim Yevmenkin 		    msg->header.cmd == NGM_L2CAP_L2CA_GET_INFO) {
1105878ed226SJulian Elischer 			/* Return data back to the user space */
1106444e5d09SMaksim Yevmenkin 			op = (ng_l2cap_l2ca_get_info_op *)(msg->data);
1107878ed226SJulian Elischer 			p->result = op->result;
1108878ed226SJulian Elischer 			p->info_size = min(p->info_size, op->info_size);
1109878ed226SJulian Elischer 
1110878ed226SJulian Elischer 			if (p->info_size > 0)
1111878ed226SJulian Elischer 				error = copyout(op + 1, p->info_data,
1112878ed226SJulian Elischer 						p->info_size);
1113878ed226SJulian Elischer 		} else
1114878ed226SJulian Elischer 			error = EINVAL;
1115878ed226SJulian Elischer 
1116444e5d09SMaksim Yevmenkin 		NG_FREE_MSG(msg); /* checks for != NULL */
1117444e5d09SMaksim Yevmenkin 		return (error);
1118444e5d09SMaksim Yevmenkin 		} /* NOTREACHED */
1119f2bb1caeSJulian Elischer 
1120f2bb1caeSJulian Elischer 	case SIOC_L2CAP_NODE_GET_AUTO_DISCON_TIMO: {
1121f2bb1caeSJulian Elischer 		struct ng_btsocket_l2cap_raw_auto_discon_timo	*p =
1122f2bb1caeSJulian Elischer 			(struct ng_btsocket_l2cap_raw_auto_discon_timo *) data;
1123f2bb1caeSJulian Elischer 
1124f2bb1caeSJulian Elischer 		error = ng_btsocket_l2cap_raw_send_sync_ngmsg(pcb,
1125f2bb1caeSJulian Elischer 				NGM_L2CAP_NODE_GET_AUTO_DISCON_TIMO,
1126f2bb1caeSJulian Elischer 				&p->timeout, sizeof(p->timeout));
1127f2bb1caeSJulian Elischer 		} break;
1128f2bb1caeSJulian Elischer 
1129f2bb1caeSJulian Elischer 	case SIOC_L2CAP_NODE_SET_AUTO_DISCON_TIMO: {
1130f2bb1caeSJulian Elischer 		struct ng_btsocket_l2cap_raw_auto_discon_timo	*p =
1131f2bb1caeSJulian Elischer 			(struct ng_btsocket_l2cap_raw_auto_discon_timo *) data;
1132f2bb1caeSJulian Elischer 
1133f2bb1caeSJulian Elischer 		if (pcb->flags & NG_BTSOCKET_L2CAP_RAW_PRIVILEGED)
1134f2bb1caeSJulian Elischer 			error = ng_btsocket_l2cap_raw_send_ngmsg(pcb->rt->hook,
1135f2bb1caeSJulian Elischer 					NGM_L2CAP_NODE_SET_AUTO_DISCON_TIMO,
1136f2bb1caeSJulian Elischer 					&p->timeout, sizeof(p->timeout));
1137f2bb1caeSJulian Elischer 		else
1138f2bb1caeSJulian Elischer 			error = EPERM;
1139878ed226SJulian Elischer 		} break;
1140878ed226SJulian Elischer 
1141878ed226SJulian Elischer 	default:
1142878ed226SJulian Elischer 		error = EINVAL;
1143878ed226SJulian Elischer 		break;
1144878ed226SJulian Elischer 	}
1145878ed226SJulian Elischer 
1146f2bb1caeSJulian Elischer 	mtx_unlock(&pcb->pcb_mtx);
1147f2bb1caeSJulian Elischer 
1148878ed226SJulian Elischer 	return (error);
1149878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_control */
1150878ed226SJulian Elischer 
1151878ed226SJulian Elischer /*
1152878ed226SJulian Elischer  * Detach and destroy socket
1153878ed226SJulian Elischer  */
1154878ed226SJulian Elischer 
1155bc725eafSRobert Watson void
ng_btsocket_l2cap_raw_detach(struct socket * so)1156878ed226SJulian Elischer ng_btsocket_l2cap_raw_detach(struct socket *so)
1157878ed226SJulian Elischer {
1158878ed226SJulian Elischer 	ng_btsocket_l2cap_raw_pcb_p	pcb = so2l2cap_raw_pcb(so);
1159878ed226SJulian Elischer 
1160bc725eafSRobert Watson 	KASSERT(pcb != NULL, ("nt_btsocket_l2cap_raw_detach: pcb == NULL"));
1161878ed226SJulian Elischer 	if (ng_btsocket_l2cap_raw_node == NULL)
1162bc725eafSRobert Watson 		return;
1163878ed226SJulian Elischer 
1164878ed226SJulian Elischer 	mtx_lock(&ng_btsocket_l2cap_raw_sockets_mtx);
1165f2bb1caeSJulian Elischer 	mtx_lock(&pcb->pcb_mtx);
1166f2bb1caeSJulian Elischer 
1167878ed226SJulian Elischer 	LIST_REMOVE(pcb, next);
1168f2bb1caeSJulian Elischer 
1169f2bb1caeSJulian Elischer 	mtx_unlock(&pcb->pcb_mtx);
1170878ed226SJulian Elischer 	mtx_unlock(&ng_btsocket_l2cap_raw_sockets_mtx);
1171878ed226SJulian Elischer 
1172f2bb1caeSJulian Elischer 	mtx_destroy(&pcb->pcb_mtx);
1173f2bb1caeSJulian Elischer 
1174878ed226SJulian Elischer 	bzero(pcb, sizeof(*pcb));
11751ede983cSDag-Erling Smørgrav 	free(pcb, M_NETGRAPH_BTSOCKET_L2CAP_RAW);
1176878ed226SJulian Elischer 
1177f2bb1caeSJulian Elischer 	so->so_pcb = NULL;
1178878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_detach */
1179878ed226SJulian Elischer 
1180878ed226SJulian Elischer /*
1181878ed226SJulian Elischer  * Disconnect socket
1182878ed226SJulian Elischer  */
1183878ed226SJulian Elischer 
1184878ed226SJulian Elischer int
ng_btsocket_l2cap_raw_disconnect(struct socket * so)1185878ed226SJulian Elischer ng_btsocket_l2cap_raw_disconnect(struct socket *so)
1186878ed226SJulian Elischer {
1187878ed226SJulian Elischer 	ng_btsocket_l2cap_raw_pcb_p	pcb = so2l2cap_raw_pcb(so);
1188878ed226SJulian Elischer 
1189878ed226SJulian Elischer 	if (pcb == NULL)
1190878ed226SJulian Elischer 		return (EINVAL);
1191878ed226SJulian Elischer 	if (ng_btsocket_l2cap_raw_node == NULL)
1192878ed226SJulian Elischer 		return (EINVAL);
1193878ed226SJulian Elischer 
1194f2bb1caeSJulian Elischer 	mtx_lock(&pcb->pcb_mtx);
1195878ed226SJulian Elischer 	pcb->rt = NULL;
1196878ed226SJulian Elischer 	soisdisconnected(so);
1197f2bb1caeSJulian Elischer 	mtx_unlock(&pcb->pcb_mtx);
1198878ed226SJulian Elischer 
1199878ed226SJulian Elischer 	return (0);
1200878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_disconnect */
1201878ed226SJulian Elischer 
1202878ed226SJulian Elischer /*
1203878ed226SJulian Elischer  * Get peer address
1204878ed226SJulian Elischer  */
1205878ed226SJulian Elischer 
1206878ed226SJulian Elischer int
ng_btsocket_l2cap_raw_peeraddr(struct socket * so,struct sockaddr * sa)1207*0fac350cSGleb Smirnoff ng_btsocket_l2cap_raw_peeraddr(struct socket *so, struct sockaddr *sa)
1208878ed226SJulian Elischer {
1209f2bb1caeSJulian Elischer 	ng_btsocket_l2cap_raw_pcb_p	pcb = so2l2cap_raw_pcb(so);
1210*0fac350cSGleb Smirnoff 	struct sockaddr_l2cap *l2cap = (struct sockaddr_l2cap *)sa;
1211f2bb1caeSJulian Elischer 
1212f2bb1caeSJulian Elischer 	if (pcb == NULL)
1213f2bb1caeSJulian Elischer 		return (EINVAL);
1214f2bb1caeSJulian Elischer 	if (ng_btsocket_l2cap_raw_node == NULL)
1215f2bb1caeSJulian Elischer 		return (EINVAL);
1216f2bb1caeSJulian Elischer 
1217*0fac350cSGleb Smirnoff 	*l2cap = (struct sockaddr_l2cap ){
1218*0fac350cSGleb Smirnoff 		.l2cap_len = sizeof(struct sockaddr_l2cap),
1219*0fac350cSGleb Smirnoff 		.l2cap_family = AF_BLUETOOTH,
1220*0fac350cSGleb Smirnoff 		.l2cap_bdaddr_type = BDADDR_BREDR,
1221*0fac350cSGleb Smirnoff 	};
1222*0fac350cSGleb Smirnoff 
1223d6279c10SMaksim Yevmenkin 	mtx_lock(&pcb->pcb_mtx);
1224*0fac350cSGleb Smirnoff 	bcopy(&pcb->dst, &l2cap->l2cap_bdaddr, sizeof(l2cap->l2cap_bdaddr));
1225d6279c10SMaksim Yevmenkin 	mtx_unlock(&pcb->pcb_mtx);
1226d6279c10SMaksim Yevmenkin 
1227*0fac350cSGleb Smirnoff 	return (0);
1228*0fac350cSGleb Smirnoff }
1229878ed226SJulian Elischer 
1230878ed226SJulian Elischer /*
1231878ed226SJulian Elischer  * Send data to socket
1232878ed226SJulian Elischer  */
1233878ed226SJulian Elischer 
1234878ed226SJulian Elischer int
ng_btsocket_l2cap_raw_send(struct socket * so,int flags,struct mbuf * m,struct sockaddr * nam,struct mbuf * control,struct thread * td)1235878ed226SJulian Elischer ng_btsocket_l2cap_raw_send(struct socket *so, int flags, struct mbuf *m,
1236878ed226SJulian Elischer 		struct sockaddr *nam, struct mbuf *control, struct thread *td)
1237878ed226SJulian Elischer {
1238878ed226SJulian Elischer 	NG_FREE_M(m); /* Checks for m != NULL */
1239878ed226SJulian Elischer 	NG_FREE_M(control);
1240878ed226SJulian Elischer 
1241878ed226SJulian Elischer 	return (EOPNOTSUPP);
1242878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_send */
1243878ed226SJulian Elischer 
1244878ed226SJulian Elischer /*
1245878ed226SJulian Elischer  * Get socket address
1246878ed226SJulian Elischer  */
1247878ed226SJulian Elischer 
1248878ed226SJulian Elischer int
ng_btsocket_l2cap_raw_sockaddr(struct socket * so,struct sockaddr * sa)1249*0fac350cSGleb Smirnoff ng_btsocket_l2cap_raw_sockaddr(struct socket *so, struct sockaddr *sa)
1250878ed226SJulian Elischer {
1251878ed226SJulian Elischer 	ng_btsocket_l2cap_raw_pcb_p	pcb = so2l2cap_raw_pcb(so);
1252*0fac350cSGleb Smirnoff 	struct sockaddr_l2cap *l2cap = (struct sockaddr_l2cap *)sa;
1253878ed226SJulian Elischer 
1254878ed226SJulian Elischer 	if (pcb == NULL)
1255878ed226SJulian Elischer 		return (EINVAL);
1256878ed226SJulian Elischer 	if (ng_btsocket_l2cap_raw_node == NULL)
1257878ed226SJulian Elischer 		return (EINVAL);
1258878ed226SJulian Elischer 
1259*0fac350cSGleb Smirnoff 	*l2cap = (struct sockaddr_l2cap ){
1260*0fac350cSGleb Smirnoff 		.l2cap_len = sizeof(struct sockaddr_l2cap),
1261*0fac350cSGleb Smirnoff 		.l2cap_family = AF_BLUETOOTH,
1262*0fac350cSGleb Smirnoff 		.l2cap_bdaddr_type = BDADDR_BREDR,
1263*0fac350cSGleb Smirnoff 	};
1264*0fac350cSGleb Smirnoff 
1265d6279c10SMaksim Yevmenkin 	mtx_lock(&pcb->pcb_mtx);
1266*0fac350cSGleb Smirnoff 	bcopy(&pcb->src, &l2cap->l2cap_bdaddr, sizeof(l2cap->l2cap_bdaddr));
1267d6279c10SMaksim Yevmenkin 	mtx_unlock(&pcb->pcb_mtx);
1268d6279c10SMaksim Yevmenkin 
1269*0fac350cSGleb Smirnoff 	return (0);
1270*0fac350cSGleb Smirnoff }
1271878ed226SJulian Elischer 
1272878ed226SJulian Elischer /*
1273878ed226SJulian Elischer  * Get next token
1274878ed226SJulian Elischer  */
1275878ed226SJulian Elischer 
1276878ed226SJulian Elischer static void
ng_btsocket_l2cap_raw_get_token(u_int32_t * token)1277878ed226SJulian Elischer ng_btsocket_l2cap_raw_get_token(u_int32_t *token)
1278878ed226SJulian Elischer {
1279878ed226SJulian Elischer 	mtx_lock(&ng_btsocket_l2cap_raw_token_mtx);
1280878ed226SJulian Elischer 
1281878ed226SJulian Elischer 	if (++ ng_btsocket_l2cap_raw_token == 0)
1282878ed226SJulian Elischer 		ng_btsocket_l2cap_raw_token = 1;
1283878ed226SJulian Elischer 
1284878ed226SJulian Elischer 	*token = ng_btsocket_l2cap_raw_token;
1285878ed226SJulian Elischer 
1286878ed226SJulian Elischer 	mtx_unlock(&ng_btsocket_l2cap_raw_token_mtx);
1287878ed226SJulian Elischer } /* ng_btsocket_l2cap_raw_get_token */
1288878ed226SJulian Elischer 
1289f2bb1caeSJulian Elischer /*
1290f2bb1caeSJulian Elischer  * Send Netgraph message to the node - do not expect reply
1291f2bb1caeSJulian Elischer  */
1292f2bb1caeSJulian Elischer 
1293f2bb1caeSJulian Elischer static int
ng_btsocket_l2cap_raw_send_ngmsg(hook_p hook,int cmd,void * arg,int arglen)1294f2bb1caeSJulian Elischer ng_btsocket_l2cap_raw_send_ngmsg(hook_p hook, int cmd, void *arg, int arglen)
1295f2bb1caeSJulian Elischer {
1296f2bb1caeSJulian Elischer 	struct ng_mesg	*msg = NULL;
1297f2bb1caeSJulian Elischer 	int		 error = 0;
1298f2bb1caeSJulian Elischer 
1299f2bb1caeSJulian Elischer 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, cmd, arglen, M_NOWAIT);
1300f2bb1caeSJulian Elischer 	if (msg == NULL)
1301f2bb1caeSJulian Elischer 		return (ENOMEM);
1302f2bb1caeSJulian Elischer 
1303f2bb1caeSJulian Elischer 	if (arg != NULL && arglen > 0)
1304f2bb1caeSJulian Elischer 		bcopy(arg, msg->data, arglen);
1305f2bb1caeSJulian Elischer 
1306b9fe2d6cSAlfred Perlstein 	NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node, msg, hook, 0);
1307f2bb1caeSJulian Elischer 
1308f2bb1caeSJulian Elischer 	return (error);
1309f2bb1caeSJulian Elischer } /* ng_btsocket_l2cap_raw_send_ngmsg */
1310f2bb1caeSJulian Elischer 
1311f2bb1caeSJulian Elischer /*
1312f2bb1caeSJulian Elischer  * Send Netgraph message to the node (no data) and wait for reply
1313f2bb1caeSJulian Elischer  */
1314f2bb1caeSJulian Elischer 
1315f2bb1caeSJulian Elischer static int
ng_btsocket_l2cap_raw_send_sync_ngmsg(ng_btsocket_l2cap_raw_pcb_p pcb,int cmd,void * rsp,int rsplen)1316f2bb1caeSJulian Elischer ng_btsocket_l2cap_raw_send_sync_ngmsg(ng_btsocket_l2cap_raw_pcb_p pcb,
1317f2bb1caeSJulian Elischer 		int cmd, void *rsp, int rsplen)
1318f2bb1caeSJulian Elischer {
1319f2bb1caeSJulian Elischer 	struct ng_mesg	*msg = NULL;
1320f2bb1caeSJulian Elischer 	int		 error = 0;
1321f2bb1caeSJulian Elischer 
1322f2bb1caeSJulian Elischer 	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
1323f2bb1caeSJulian Elischer 
1324f2bb1caeSJulian Elischer 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, cmd, 0, M_NOWAIT);
1325f2bb1caeSJulian Elischer 	if (msg == NULL)
1326f2bb1caeSJulian Elischer 		return (ENOMEM);
1327f2bb1caeSJulian Elischer 
1328f2bb1caeSJulian Elischer 	ng_btsocket_l2cap_raw_get_token(&msg->header.token);
1329f2bb1caeSJulian Elischer 	pcb->token = msg->header.token;
1330f2bb1caeSJulian Elischer 	pcb->msg = NULL;
1331f2bb1caeSJulian Elischer 
1332f2bb1caeSJulian Elischer 	NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_raw_node, msg,
1333b9fe2d6cSAlfred Perlstein 		pcb->rt->hook, 0);
1334f2bb1caeSJulian Elischer 	if (error != 0) {
1335f2bb1caeSJulian Elischer 		pcb->token = 0;
1336f2bb1caeSJulian Elischer 		return (error);
1337f2bb1caeSJulian Elischer 	}
1338f2bb1caeSJulian Elischer 
1339f2bb1caeSJulian Elischer 	error = msleep(&pcb->msg, &pcb->pcb_mtx, PZERO|PCATCH, "l2ctl",
1340f2bb1caeSJulian Elischer 			ng_btsocket_l2cap_raw_ioctl_timeout * hz);
1341f2bb1caeSJulian Elischer 	pcb->token = 0;
1342f2bb1caeSJulian Elischer 
1343f2bb1caeSJulian Elischer 	if (error != 0)
1344f2bb1caeSJulian Elischer 		return (error);
1345f2bb1caeSJulian Elischer 
1346f2bb1caeSJulian Elischer 	if (pcb->msg != NULL && pcb->msg->header.cmd == cmd)
1347f2bb1caeSJulian Elischer 		bcopy(pcb->msg->data, rsp, rsplen);
1348f2bb1caeSJulian Elischer 	else
1349f2bb1caeSJulian Elischer 		error = EINVAL;
1350f2bb1caeSJulian Elischer 
1351f2bb1caeSJulian Elischer 	NG_FREE_MSG(pcb->msg); /* checks for != NULL */
1352f2bb1caeSJulian Elischer 
1353f2bb1caeSJulian Elischer 	return (0);
1354f2bb1caeSJulian Elischer } /* ng_btsocket_l2cap_raw_send_sync_ngmsg */
1355