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