1107120Sjulian/*
2107120Sjulian * ng_btsocket_l2cap.c
3139823Simp */
4139823Simp
5139823Simp/*-
6107120Sjulian * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7107120Sjulian * All rights reserved.
8107120Sjulian *
9107120Sjulian * Redistribution and use in source and binary forms, with or without
10107120Sjulian * modification, are permitted provided that the following conditions
11107120Sjulian * are met:
12107120Sjulian * 1. Redistributions of source code must retain the above copyright
13107120Sjulian *    notice, this list of conditions and the following disclaimer.
14107120Sjulian * 2. Redistributions in binary form must reproduce the above copyright
15107120Sjulian *    notice, this list of conditions and the following disclaimer in the
16107120Sjulian *    documentation and/or other materials provided with the distribution.
17107120Sjulian *
18107120Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19107120Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20107120Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21107120Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22107120Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23107120Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24107120Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25107120Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26107120Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27107120Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28107120Sjulian * SUCH DAMAGE.
29107120Sjulian *
30121054Semax * $Id: ng_btsocket_l2cap.c,v 1.16 2003/09/14 23:29:06 max Exp $
31107120Sjulian * $FreeBSD$
32107120Sjulian */
33107120Sjulian
34107120Sjulian#include <sys/param.h>
35107120Sjulian#include <sys/systm.h>
36121054Semax#include <sys/bitstring.h>
37107120Sjulian#include <sys/domain.h>
38107120Sjulian#include <sys/endian.h>
39107120Sjulian#include <sys/errno.h>
40107120Sjulian#include <sys/filedesc.h>
41107120Sjulian#include <sys/ioccom.h>
42107120Sjulian#include <sys/kernel.h>
43107120Sjulian#include <sys/lock.h>
44107120Sjulian#include <sys/malloc.h>
45107120Sjulian#include <sys/mbuf.h>
46107120Sjulian#include <sys/mutex.h>
47107120Sjulian#include <sys/protosw.h>
48107120Sjulian#include <sys/queue.h>
49107120Sjulian#include <sys/socket.h>
50107120Sjulian#include <sys/socketvar.h>
51107120Sjulian#include <sys/sysctl.h>
52107120Sjulian#include <sys/taskqueue.h>
53218757Sbz
54218757Sbz#include <net/vnet.h>
55218757Sbz
56107120Sjulian#include <netgraph/ng_message.h>
57107120Sjulian#include <netgraph/netgraph.h>
58128688Semax#include <netgraph/bluetooth/include/ng_bluetooth.h>
59128688Semax#include <netgraph/bluetooth/include/ng_hci.h>
60128688Semax#include <netgraph/bluetooth/include/ng_l2cap.h>
61128688Semax#include <netgraph/bluetooth/include/ng_btsocket.h>
62128688Semax#include <netgraph/bluetooth/include/ng_btsocket_l2cap.h>
63107120Sjulian
64107120Sjulian/* MALLOC define */
65107120Sjulian#ifdef NG_SEPARATE_MALLOC
66227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_L2CAP, "netgraph_btsocks_l2cap",
67107120Sjulian		"Netgraph Bluetooth L2CAP sockets");
68107120Sjulian#else
69107120Sjulian#define M_NETGRAPH_BTSOCKET_L2CAP M_NETGRAPH
70107120Sjulian#endif /* NG_SEPARATE_MALLOC */
71107120Sjulian
72107120Sjulian/* Netgraph node methods */
73107120Sjulianstatic ng_constructor_t	ng_btsocket_l2cap_node_constructor;
74107120Sjulianstatic ng_rcvmsg_t	ng_btsocket_l2cap_node_rcvmsg;
75107120Sjulianstatic ng_shutdown_t	ng_btsocket_l2cap_node_shutdown;
76107120Sjulianstatic ng_newhook_t	ng_btsocket_l2cap_node_newhook;
77107120Sjulianstatic ng_connect_t	ng_btsocket_l2cap_node_connect;
78107120Sjulianstatic ng_rcvdata_t	ng_btsocket_l2cap_node_rcvdata;
79107120Sjulianstatic ng_disconnect_t	ng_btsocket_l2cap_node_disconnect;
80107120Sjulian
81107120Sjulianstatic void		ng_btsocket_l2cap_input   (void *, int);
82107120Sjulianstatic void		ng_btsocket_l2cap_rtclean (void *, int);
83107120Sjulian
84107120Sjulian/* Netgraph type descriptor */
85107120Sjulianstatic struct ng_type	typestruct = {
86129823Sjulian	.version =	NG_ABI_VERSION,
87129823Sjulian	.name =		NG_BTSOCKET_L2CAP_NODE_TYPE,
88129823Sjulian	.constructor =	ng_btsocket_l2cap_node_constructor,
89129823Sjulian	.rcvmsg =	ng_btsocket_l2cap_node_rcvmsg,
90129823Sjulian	.shutdown =	ng_btsocket_l2cap_node_shutdown,
91129823Sjulian	.newhook =	ng_btsocket_l2cap_node_newhook,
92129823Sjulian	.connect =	ng_btsocket_l2cap_node_connect,
93129823Sjulian	.rcvdata =	ng_btsocket_l2cap_node_rcvdata,
94129823Sjulian	.disconnect =	ng_btsocket_l2cap_node_disconnect,
95107120Sjulian};
96107120Sjulian
97107120Sjulian/* Globals */
98107120Sjulianextern int					ifqmaxlen;
99107120Sjulianstatic u_int32_t				ng_btsocket_l2cap_debug_level;
100107120Sjulianstatic node_p					ng_btsocket_l2cap_node;
101107120Sjulianstatic struct ng_bt_itemq			ng_btsocket_l2cap_queue;
102107120Sjulianstatic struct mtx				ng_btsocket_l2cap_queue_mtx;
103107120Sjulianstatic struct task				ng_btsocket_l2cap_queue_task;
104107120Sjulianstatic LIST_HEAD(, ng_btsocket_l2cap_pcb)	ng_btsocket_l2cap_sockets;
105107120Sjulianstatic struct mtx				ng_btsocket_l2cap_sockets_mtx;
106107120Sjulianstatic LIST_HEAD(, ng_btsocket_l2cap_rtentry)	ng_btsocket_l2cap_rt;
107107120Sjulianstatic struct mtx				ng_btsocket_l2cap_rt_mtx;
108107120Sjulianstatic struct task				ng_btsocket_l2cap_rt_task;
109181093Semaxstatic struct timeval				ng_btsocket_l2cap_lasttime;
110181093Semaxstatic int					ng_btsocket_l2cap_curpps;
111107120Sjulian
112107120Sjulian/* Sysctl tree */
113107120SjulianSYSCTL_DECL(_net_bluetooth_l2cap_sockets);
114227309Sedstatic SYSCTL_NODE(_net_bluetooth_l2cap_sockets, OID_AUTO, seq, CTLFLAG_RW,
115107120Sjulian	0, "Bluetooth SEQPACKET L2CAP sockets family");
116217320SmdfSYSCTL_UINT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, debug_level,
117107120Sjulian	CTLFLAG_RW,
118107120Sjulian	&ng_btsocket_l2cap_debug_level, NG_BTSOCKET_WARN_LEVEL,
119107120Sjulian	"Bluetooth SEQPACKET L2CAP sockets debug level");
120217320SmdfSYSCTL_UINT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, queue_len,
121107120Sjulian	CTLFLAG_RD,
122107120Sjulian	&ng_btsocket_l2cap_queue.len, 0,
123107120Sjulian	"Bluetooth SEQPACKET L2CAP sockets input queue length");
124217320SmdfSYSCTL_UINT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, queue_maxlen,
125107120Sjulian	CTLFLAG_RD,
126107120Sjulian	&ng_btsocket_l2cap_queue.maxlen, 0,
127107120Sjulian	"Bluetooth SEQPACKET L2CAP sockets input queue max. length");
128217320SmdfSYSCTL_UINT(_net_bluetooth_l2cap_sockets_seq, OID_AUTO, queue_drops,
129107120Sjulian	CTLFLAG_RD,
130107120Sjulian	&ng_btsocket_l2cap_queue.drops, 0,
131107120Sjulian	"Bluetooth SEQPACKET L2CAP sockets input queue drops");
132107120Sjulian
133107120Sjulian/* Debug */
134107120Sjulian#define NG_BTSOCKET_L2CAP_INFO \
135181093Semax	if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_INFO_LEVEL && \
136181093Semax	    ppsratecheck(&ng_btsocket_l2cap_lasttime, &ng_btsocket_l2cap_curpps, 1)) \
137107120Sjulian		printf
138107120Sjulian
139107120Sjulian#define NG_BTSOCKET_L2CAP_WARN \
140181093Semax	if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_WARN_LEVEL && \
141181093Semax	    ppsratecheck(&ng_btsocket_l2cap_lasttime, &ng_btsocket_l2cap_curpps, 1)) \
142107120Sjulian		printf
143107120Sjulian
144107120Sjulian#define NG_BTSOCKET_L2CAP_ERR \
145181093Semax	if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_ERR_LEVEL && \
146181093Semax	    ppsratecheck(&ng_btsocket_l2cap_lasttime, &ng_btsocket_l2cap_curpps, 1)) \
147107120Sjulian		printf
148107120Sjulian
149107120Sjulian#define NG_BTSOCKET_L2CAP_ALERT \
150181093Semax	if (ng_btsocket_l2cap_debug_level >= NG_BTSOCKET_ALERT_LEVEL && \
151181093Semax	    ppsratecheck(&ng_btsocket_l2cap_lasttime, &ng_btsocket_l2cap_curpps, 1)) \
152107120Sjulian		printf
153107120Sjulian
154107120Sjulian/*
155107120Sjulian * Netgraph message processing routines
156107120Sjulian */
157107120Sjulian
158107120Sjulianstatic int ng_btsocket_l2cap_process_l2ca_con_req_rsp
159161623Semax	(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
160107120Sjulianstatic int ng_btsocket_l2cap_process_l2ca_con_rsp_rsp
161161623Semax	(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
162107120Sjulianstatic int ng_btsocket_l2cap_process_l2ca_con_ind
163161623Semax	(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
164107120Sjulian
165107120Sjulianstatic int ng_btsocket_l2cap_process_l2ca_cfg_req_rsp
166161623Semax	(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
167107120Sjulianstatic int ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp
168161623Semax	(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
169107120Sjulianstatic int ng_btsocket_l2cap_process_l2ca_cfg_ind
170161623Semax	(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
171107120Sjulian
172107120Sjulianstatic int ng_btsocket_l2cap_process_l2ca_discon_rsp
173161623Semax	(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
174107120Sjulianstatic int ng_btsocket_l2cap_process_l2ca_discon_ind
175161623Semax	(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
176107120Sjulian
177107120Sjulianstatic int ng_btsocket_l2cap_process_l2ca_write_rsp
178161623Semax	(struct ng_mesg *, ng_btsocket_l2cap_rtentry_p);
179107120Sjulian
180107120Sjulian/*
181107120Sjulian * Send L2CA_xxx messages to the lower layer
182107120Sjulian */
183107120Sjulian
184107120Sjulianstatic int  ng_btsocket_l2cap_send_l2ca_con_req
185107120Sjulian	(ng_btsocket_l2cap_pcb_p);
186107120Sjulianstatic int  ng_btsocket_l2cap_send_l2ca_con_rsp_req
187281198Stakawata	(u_int32_t, ng_btsocket_l2cap_rtentry_p, bdaddr_p, int, int, int, int);
188107120Sjulianstatic int  ng_btsocket_l2cap_send_l2ca_cfg_req
189107120Sjulian	(ng_btsocket_l2cap_pcb_p);
190107120Sjulianstatic int  ng_btsocket_l2cap_send_l2ca_cfg_rsp
191107120Sjulian	(ng_btsocket_l2cap_pcb_p);
192107120Sjulianstatic int  ng_btsocket_l2cap_send_l2ca_discon_req
193107120Sjulian	(u_int32_t, ng_btsocket_l2cap_pcb_p);
194107120Sjulian
195107120Sjulianstatic int ng_btsocket_l2cap_send2
196107120Sjulian	(ng_btsocket_l2cap_pcb_p);
197107120Sjulian
198107120Sjulian/*
199107120Sjulian * Timeout processing routines
200107120Sjulian */
201107120Sjulian
202107120Sjulianstatic void ng_btsocket_l2cap_timeout         (ng_btsocket_l2cap_pcb_p);
203107120Sjulianstatic void ng_btsocket_l2cap_untimeout       (ng_btsocket_l2cap_pcb_p);
204107120Sjulianstatic void ng_btsocket_l2cap_process_timeout (void *);
205107120Sjulian
206107120Sjulian/*
207107120Sjulian * Other stuff
208107120Sjulian */
209107120Sjulian
210107120Sjulianstatic ng_btsocket_l2cap_pcb_p     ng_btsocket_l2cap_pcb_by_addr(bdaddr_p, int);
211107120Sjulianstatic ng_btsocket_l2cap_pcb_p     ng_btsocket_l2cap_pcb_by_token(u_int32_t);
212281198Stakawatastatic ng_btsocket_l2cap_pcb_p     ng_btsocket_l2cap_pcb_by_cid (bdaddr_p, int,int);
213107120Sjulianstatic int                         ng_btsocket_l2cap_result2errno(int);
214107120Sjulian
215281198Stakawatastatic int ng_btsock_l2cap_addrtype_to_linktype(int addrtype);
216290038Stakawata
217114878Sjulian#define ng_btsocket_l2cap_wakeup_input_task() \
218114878Sjulian	taskqueue_enqueue(taskqueue_swi_giant, &ng_btsocket_l2cap_queue_task)
219114878Sjulian
220114878Sjulian#define ng_btsocket_l2cap_wakeup_route_task() \
221114878Sjulian	taskqueue_enqueue(taskqueue_swi_giant, &ng_btsocket_l2cap_rt_task)
222114878Sjulian
223281198Stakawata
224281198Stakawata
225281198Stakawataint ng_btsock_l2cap_addrtype_to_linktype(int addrtype)
226281198Stakawata{
227281198Stakawata	switch(addrtype){
228281198Stakawata	case BDADDR_LE_PUBLIC:
229281198Stakawata		return NG_HCI_LINK_LE_PUBLIC;
230281198Stakawata	case BDADDR_LE_RANDOM:
231281198Stakawata		return NG_HCI_LINK_LE_RANDOM;
232281198Stakawata	default:
233281198Stakawata		return NG_HCI_LINK_ACL;
234281198Stakawata	}
235281198Stakawata}
236281198Stakawata
237281198Stakawata
238107120Sjulian/*****************************************************************************
239107120Sjulian *****************************************************************************
240107120Sjulian **                        Netgraph node interface
241107120Sjulian *****************************************************************************
242107120Sjulian *****************************************************************************/
243107120Sjulian
244107120Sjulian/*
245107120Sjulian * Netgraph node constructor. Do not allow to create node of this type.
246107120Sjulian */
247107120Sjulian
248107120Sjulianstatic int
249107120Sjulianng_btsocket_l2cap_node_constructor(node_p node)
250107120Sjulian{
251107120Sjulian	return (EINVAL);
252107120Sjulian} /* ng_btsocket_l2cap_node_constructor */
253107120Sjulian
254107120Sjulian/*
255107120Sjulian * Do local shutdown processing. Let old node go and create new fresh one.
256107120Sjulian */
257107120Sjulian
258107120Sjulianstatic int
259107120Sjulianng_btsocket_l2cap_node_shutdown(node_p node)
260107120Sjulian{
261107120Sjulian	int	error = 0;
262107120Sjulian
263107120Sjulian	NG_NODE_UNREF(node);
264107120Sjulian
265107120Sjulian	/* Create new node */
266107120Sjulian	error = ng_make_node_common(&typestruct, &ng_btsocket_l2cap_node);
267107120Sjulian	if (error != 0) {
268107120Sjulian		NG_BTSOCKET_L2CAP_ALERT(
269107120Sjulian"%s: Could not create Netgraph node, error=%d\n", __func__, error);
270107120Sjulian
271107120Sjulian		ng_btsocket_l2cap_node = NULL;
272107120Sjulian
273107120Sjulian		return (error);
274107120Sjulian	}
275107120Sjulian
276107120Sjulian	error = ng_name_node(ng_btsocket_l2cap_node,
277107120Sjulian				NG_BTSOCKET_L2CAP_NODE_TYPE);
278123812Salfred	if (error != 0) {
279107120Sjulian		NG_BTSOCKET_L2CAP_ALERT(
280107120Sjulian"%s: Could not name Netgraph node, error=%d\n", __func__, error);
281107120Sjulian
282107120Sjulian		NG_NODE_UNREF(ng_btsocket_l2cap_node);
283107120Sjulian		ng_btsocket_l2cap_node = NULL;
284107120Sjulian
285107120Sjulian		return (error);
286107120Sjulian	}
287107120Sjulian
288107120Sjulian	return (0);
289107120Sjulian} /* ng_btsocket_l2cap_node_shutdown */
290107120Sjulian
291107120Sjulian/*
292107120Sjulian * We allow any hook to be connected to the node.
293107120Sjulian */
294107120Sjulian
295107120Sjulianstatic int
296107120Sjulianng_btsocket_l2cap_node_newhook(node_p node, hook_p hook, char const *name)
297107120Sjulian{
298107120Sjulian	return (0);
299107120Sjulian} /* ng_btsocket_l2cap_node_newhook */
300107120Sjulian
301107120Sjulian/*
302107120Sjulian * Just say "YEP, that's OK by me!"
303107120Sjulian */
304107120Sjulian
305107120Sjulianstatic int
306107120Sjulianng_btsocket_l2cap_node_connect(hook_p hook)
307107120Sjulian{
308107120Sjulian	NG_HOOK_SET_PRIVATE(hook, NULL);
309107120Sjulian	NG_HOOK_REF(hook); /* Keep extra reference to the hook */
310107120Sjulian
311107120Sjulian#if 0
312107120Sjulian	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
313107120Sjulian	NG_HOOK_FORCE_QUEUE(hook);
314107120Sjulian#endif
315107120Sjulian
316107120Sjulian	return (0);
317107120Sjulian} /* ng_btsocket_l2cap_node_connect */
318107120Sjulian
319107120Sjulian/*
320107120Sjulian * Hook disconnection. Schedule route cleanup task
321107120Sjulian */
322107120Sjulian
323107120Sjulianstatic int
324107120Sjulianng_btsocket_l2cap_node_disconnect(hook_p hook)
325107120Sjulian{
326107120Sjulian	/*
327107120Sjulian	 * If hook has private information than we must have this hook in
328107120Sjulian	 * the routing table and must schedule cleaning for the routing table.
329107120Sjulian	 * Otherwise hook was connected but we never got "hook_info" message,
330107120Sjulian	 * so we have never added this hook to the routing table and it save
331107120Sjulian	 * to just delete it.
332107120Sjulian	 */
333107120Sjulian
334107120Sjulian	if (NG_HOOK_PRIVATE(hook) != NULL)
335114878Sjulian		return (ng_btsocket_l2cap_wakeup_route_task());
336107120Sjulian
337107120Sjulian	NG_HOOK_UNREF(hook); /* Remove extra reference */
338107120Sjulian
339107120Sjulian	return (0);
340107120Sjulian} /* ng_btsocket_l2cap_node_disconnect */
341107120Sjulian
342107120Sjulian/*
343107120Sjulian * Process incoming messages
344107120Sjulian */
345107120Sjulian
346107120Sjulianstatic int
347107120Sjulianng_btsocket_l2cap_node_rcvmsg(node_p node, item_p item, hook_p hook)
348107120Sjulian{
349107120Sjulian	struct ng_mesg	*msg = NGI_MSG(item); /* item still has message */
350107120Sjulian	int		 error = 0;
351107120Sjulian
352107120Sjulian	if (msg != NULL && msg->header.typecookie == NGM_L2CAP_COOKIE) {
353107120Sjulian		mtx_lock(&ng_btsocket_l2cap_queue_mtx);
354107120Sjulian		if (NG_BT_ITEMQ_FULL(&ng_btsocket_l2cap_queue)) {
355107120Sjulian			NG_BTSOCKET_L2CAP_ERR(
356107120Sjulian"%s: Input queue is full (msg)\n", __func__);
357107120Sjulian
358107120Sjulian			NG_BT_ITEMQ_DROP(&ng_btsocket_l2cap_queue);
359107120Sjulian			NG_FREE_ITEM(item);
360107120Sjulian			error = ENOBUFS;
361107120Sjulian		} else {
362107120Sjulian			if (hook != NULL) {
363107120Sjulian				NG_HOOK_REF(hook);
364107120Sjulian				NGI_SET_HOOK(item, hook);
365107120Sjulian			}
366107120Sjulian
367107120Sjulian			NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_l2cap_queue, item);
368114878Sjulian			error = ng_btsocket_l2cap_wakeup_input_task();
369107120Sjulian		}
370107120Sjulian		mtx_unlock(&ng_btsocket_l2cap_queue_mtx);
371107120Sjulian	} else {
372107120Sjulian		NG_FREE_ITEM(item);
373107120Sjulian		error = EINVAL;
374107120Sjulian	}
375107120Sjulian
376107120Sjulian	return (error);
377107120Sjulian} /* ng_btsocket_l2cap_node_rcvmsg */
378107120Sjulian
379107120Sjulian/*
380107120Sjulian * Receive data on a hook
381107120Sjulian */
382107120Sjulian
383107120Sjulianstatic int
384107120Sjulianng_btsocket_l2cap_node_rcvdata(hook_p hook, item_p item)
385107120Sjulian{
386107120Sjulian	int	error = 0;
387107120Sjulian
388107120Sjulian	mtx_lock(&ng_btsocket_l2cap_queue_mtx);
389107120Sjulian	if (NG_BT_ITEMQ_FULL(&ng_btsocket_l2cap_queue)) {
390107120Sjulian		NG_BTSOCKET_L2CAP_ERR(
391107120Sjulian"%s: Input queue is full (data)\n", __func__);
392107120Sjulian
393107120Sjulian		NG_BT_ITEMQ_DROP(&ng_btsocket_l2cap_queue);
394107120Sjulian		NG_FREE_ITEM(item);
395107120Sjulian		error = ENOBUFS;
396107120Sjulian	} else {
397107120Sjulian		NG_HOOK_REF(hook);
398107120Sjulian		NGI_SET_HOOK(item, hook);
399107120Sjulian
400107120Sjulian		NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_l2cap_queue, item);
401114878Sjulian		error = ng_btsocket_l2cap_wakeup_input_task();
402107120Sjulian	}
403107120Sjulian	mtx_unlock(&ng_btsocket_l2cap_queue_mtx);
404107120Sjulian
405107120Sjulian	return (error);
406107120Sjulian} /* ng_btsocket_l2cap_node_rcvdata */
407107120Sjulian
408107120Sjulian/*
409107120Sjulian * Process L2CA_Connect respose. Socket layer must have initiated connection,
410107120Sjulian * so we have to have a socket associated with message token.
411107120Sjulian */
412107120Sjulian
413107120Sjulianstatic int
414107120Sjulianng_btsocket_l2cap_process_l2ca_con_req_rsp(struct ng_mesg *msg,
415161623Semax		ng_btsocket_l2cap_rtentry_p rt)
416107120Sjulian{
417107120Sjulian	ng_l2cap_l2ca_con_op	*op = NULL;
418107120Sjulian	ng_btsocket_l2cap_pcb_t	*pcb = NULL;
419107120Sjulian	int			 error = 0;
420107120Sjulian
421107120Sjulian	if (msg->header.arglen != sizeof(*op))
422107120Sjulian		return (EMSGSIZE);
423107120Sjulian
424107120Sjulian	op = (ng_l2cap_l2ca_con_op *)(msg->data);
425107120Sjulian
426114878Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
427114878Sjulian
428107120Sjulian	/* Look for the socket with the token */
429107120Sjulian	pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);
430114878Sjulian	if (pcb == NULL) {
431114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
432107120Sjulian		return (ENOENT);
433114878Sjulian	}
434107120Sjulian
435107120Sjulian	mtx_lock(&pcb->pcb_mtx);
436107120Sjulian
437107120Sjulian	NG_BTSOCKET_L2CAP_INFO(
438107120Sjulian"%s: Got L2CA_Connect response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \
439107120Sjulian"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, status=%d, " \
440107120Sjulian"state=%d\n",	__func__, msg->header.token,
441107120Sjulian		pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
442107120Sjulian		pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
443107120Sjulian		pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
444107120Sjulian		pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
445107120Sjulian		pcb->psm, op->lcid, op->result, op->status,
446107120Sjulian		pcb->state);
447107120Sjulian
448107120Sjulian	if (pcb->state != NG_BTSOCKET_L2CAP_CONNECTING) {
449107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
450114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
451114878Sjulian
452107120Sjulian		return (ENOENT);
453107120Sjulian	}
454107120Sjulian
455107120Sjulian	ng_btsocket_l2cap_untimeout(pcb);
456107120Sjulian
457107120Sjulian	if (op->result == NG_L2CAP_PENDING) {
458107120Sjulian		ng_btsocket_l2cap_timeout(pcb);
459107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
460114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
461114878Sjulian
462107120Sjulian		return (0);
463107120Sjulian	}
464107120Sjulian
465281198Stakawata	if (op->result == NG_L2CAP_SUCCESS){
466290038Stakawata		if((pcb->idtype == NG_L2CAP_L2CA_IDTYPE_ATT)||
467290038Stakawata		   (pcb->idtype == NG_L2CAP_L2CA_IDTYPE_SMP)){
468290038Stakawata			pcb->encryption = op->encryption;					pcb->cid = op->lcid;
469290038Stakawata			if(pcb->need_encrypt && !(pcb->encryption)){
470290491Stakawata				ng_btsocket_l2cap_timeout(pcb);
471290038Stakawata				pcb->state = NG_BTSOCKET_L2CAP_W4_ENC_CHANGE;
472290038Stakawata			}else{
473290038Stakawata				pcb->state = NG_BTSOCKET_L2CAP_OPEN;
474290038Stakawata				soisconnected(pcb->so);
475290038Stakawata			}
476281198Stakawata		}else{
477281198Stakawata			/*
478281198Stakawata			 * Channel is now open, so update local channel ID and
479281198Stakawata			 * start configuration process. Source and destination
480281198Stakawata			 * addresses as well as route must be already set.
481281198Stakawata			 */
482281198Stakawata
483281198Stakawata			pcb->cid = op->lcid;
484290038Stakawata			pcb->encryption = op->encryption;
485281198Stakawata			error = ng_btsocket_l2cap_send_l2ca_cfg_req(pcb);
486281198Stakawata			if (error != 0) {
487281198Stakawata				/* Send disconnect request with "zero" token */
488281198Stakawata				ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
489281198Stakawata
490281198Stakawata				/* ... and close the socket */
491281198Stakawata				pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
492281198Stakawata				soisdisconnected(pcb->so);
493281198Stakawata			} else {
494281198Stakawata				pcb->cfg_state = NG_BTSOCKET_L2CAP_CFG_IN_SENT;
495281198Stakawata				pcb->state = NG_BTSOCKET_L2CAP_CONFIGURING;
496281198Stakawata
497281198Stakawata				ng_btsocket_l2cap_timeout(pcb);
498281198Stakawata			}
499107120Sjulian		}
500107120Sjulian	} else {
501107120Sjulian		/*
502107120Sjulian		 * We have failed to open connection, so convert result
503107120Sjulian		 * code to "errno" code and disconnect the socket. Channel
504107120Sjulian		 * already has been closed.
505107120Sjulian		 */
506107120Sjulian
507107120Sjulian		pcb->so->so_error = ng_btsocket_l2cap_result2errno(op->result);
508107120Sjulian		pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
509107120Sjulian		soisdisconnected(pcb->so);
510107120Sjulian	}
511107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
512114878Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
513107120Sjulian
514107120Sjulian	return (error);
515107120Sjulian} /* ng_btsocket_l2cap_process_l2ca_con_req_rsp */
516107120Sjulian
517107120Sjulian/*
518107120Sjulian * Process L2CA_ConnectRsp response
519107120Sjulian */
520107120Sjulian
521107120Sjulianstatic int
522107120Sjulianng_btsocket_l2cap_process_l2ca_con_rsp_rsp(struct ng_mesg *msg,
523161623Semax		ng_btsocket_l2cap_rtentry_p rt)
524107120Sjulian{
525107120Sjulian	ng_l2cap_l2ca_con_rsp_op	*op = NULL;
526107120Sjulian	ng_btsocket_l2cap_pcb_t		*pcb = NULL;
527107120Sjulian
528107120Sjulian	if (msg->header.arglen != sizeof(*op))
529107120Sjulian		return (EMSGSIZE);
530107120Sjulian
531107120Sjulian	op = (ng_l2cap_l2ca_con_rsp_op *)(msg->data);
532107120Sjulian
533114878Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
534114878Sjulian
535107120Sjulian	/* Look for the socket with the token */
536107120Sjulian	pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);
537114878Sjulian	if (pcb == NULL) {
538114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
539107120Sjulian		return (ENOENT);
540114878Sjulian	}
541107120Sjulian
542107120Sjulian	mtx_lock(&pcb->pcb_mtx);
543107120Sjulian
544107120Sjulian	NG_BTSOCKET_L2CAP_INFO(
545107120Sjulian"%s: Got L2CA_ConnectRsp response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \
546107120Sjulian"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d\n",
547107120Sjulian		__func__, msg->header.token,
548107120Sjulian		pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
549107120Sjulian		pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
550107120Sjulian		pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
551107120Sjulian		pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
552107120Sjulian		pcb->psm, pcb->cid, op->result, pcb->state);
553107120Sjulian
554107120Sjulian	if (pcb->state != NG_BTSOCKET_L2CAP_CONNECTING) {
555107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
556114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
557114878Sjulian
558107120Sjulian		return (ENOENT);
559107120Sjulian	}
560107120Sjulian
561107120Sjulian	ng_btsocket_l2cap_untimeout(pcb);
562107120Sjulian
563107120Sjulian	/* Check the result and disconnect the socket on failure */
564107120Sjulian	if (op->result != NG_L2CAP_SUCCESS) {
565107120Sjulian		/* Close the socket - channel already closed */
566107120Sjulian		pcb->so->so_error = ng_btsocket_l2cap_result2errno(op->result);
567107120Sjulian		pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
568107120Sjulian		soisdisconnected(pcb->so);
569107120Sjulian	} else {
570107120Sjulian		/* Move to CONFIGURING state and wait for CONFIG_IND */
571107120Sjulian		pcb->cfg_state = 0;
572107120Sjulian		pcb->state = NG_BTSOCKET_L2CAP_CONFIGURING;
573107120Sjulian		ng_btsocket_l2cap_timeout(pcb);
574107120Sjulian	}
575107120Sjulian
576107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
577114878Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
578107120Sjulian
579107120Sjulian	return (0);
580107120Sjulian} /* ng_btsocket_process_l2ca_con_rsp_rsp */
581107120Sjulian
582107120Sjulian/*
583107120Sjulian * Process L2CA_Connect indicator. Find socket that listens on address
584107120Sjulian * and PSM. Find exact or closest match. Create new socket and initiate
585107120Sjulian * connection.
586107120Sjulian */
587107120Sjulian
588107120Sjulianstatic int
589107120Sjulianng_btsocket_l2cap_process_l2ca_con_ind(struct ng_mesg *msg,
590161623Semax		ng_btsocket_l2cap_rtentry_p rt)
591107120Sjulian{
592107120Sjulian	ng_l2cap_l2ca_con_ind_ip	*ip = NULL;
593107120Sjulian	ng_btsocket_l2cap_pcb_t		*pcb = NULL, *pcb1 = NULL;
594107120Sjulian	int				 error = 0;
595107120Sjulian	u_int32_t			 token = 0;
596107120Sjulian	u_int16_t			 result = 0;
597107120Sjulian
598107120Sjulian	if (msg->header.arglen != sizeof(*ip))
599107120Sjulian		return (EMSGSIZE);
600107120Sjulian
601107120Sjulian	ip = (ng_l2cap_l2ca_con_ind_ip *)(msg->data);
602107120Sjulian
603107120Sjulian	NG_BTSOCKET_L2CAP_INFO(
604107120Sjulian"%s: Got L2CA_Connect indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \
605107120Sjulian"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, ident=%d\n",
606107120Sjulian		__func__,
607107120Sjulian		rt->src.b[5], rt->src.b[4], rt->src.b[3],
608107120Sjulian		rt->src.b[2], rt->src.b[1], rt->src.b[0],
609107120Sjulian		ip->bdaddr.b[5], ip->bdaddr.b[4], ip->bdaddr.b[3],
610107120Sjulian		ip->bdaddr.b[2], ip->bdaddr.b[1], ip->bdaddr.b[0],
611107120Sjulian		ip->psm, ip->lcid, ip->ident);
612114878Sjulian
613114878Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
614107120Sjulian
615107120Sjulian	pcb = ng_btsocket_l2cap_pcb_by_addr(&rt->src, ip->psm);
616107120Sjulian	if (pcb != NULL) {
617107120Sjulian		struct socket	*so1 = NULL;
618107120Sjulian
619107120Sjulian		mtx_lock(&pcb->pcb_mtx);
620107120Sjulian
621107120Sjulian		/*
622107120Sjulian		 * First check the pending connections queue and if we have
623107120Sjulian		 * space then create new socket and set proper source address.
624107120Sjulian		 */
625107120Sjulian
626218757Sbz		if (pcb->so->so_qlen <= pcb->so->so_qlimit) {
627218757Sbz			CURVNET_SET(pcb->so->so_vnet);
628107120Sjulian			so1 = sonewconn(pcb->so, 0);
629218757Sbz			CURVNET_RESTORE();
630218757Sbz		}
631107120Sjulian
632107120Sjulian		if (so1 == NULL) {
633107120Sjulian			result = NG_L2CAP_NO_RESOURCES;
634107120Sjulian			goto respond;
635107120Sjulian		}
636107120Sjulian
637107120Sjulian		/*
638107120Sjulian		 * If we got here than we have created new socket. So complete
639107120Sjulian		 * connection. If we we listening on specific address then copy
640107120Sjulian		 * source address from listening socket, otherwise copy source
641107120Sjulian		 * address from hook's routing information.
642107120Sjulian		 */
643107120Sjulian
644107120Sjulian		pcb1 = so2l2cap_pcb(so1);
645107120Sjulian		KASSERT((pcb1 != NULL),
646107120Sjulian("%s: pcb1 == NULL\n", __func__));
647107120Sjulian
648114878Sjulian 		mtx_lock(&pcb1->pcb_mtx);
649107120Sjulian
650107120Sjulian		if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src)) != 0)
651107120Sjulian			bcopy(&pcb->src, &pcb1->src, sizeof(pcb1->src));
652107120Sjulian		else
653107120Sjulian			bcopy(&rt->src, &pcb1->src, sizeof(pcb1->src));
654107120Sjulian
655107120Sjulian		pcb1->flags &= ~NG_BTSOCKET_L2CAP_CLIENT;
656107120Sjulian
657107120Sjulian		bcopy(&ip->bdaddr, &pcb1->dst, sizeof(pcb1->dst));
658107120Sjulian		pcb1->psm = ip->psm;
659107120Sjulian		pcb1->cid = ip->lcid;
660107120Sjulian		pcb1->rt = rt;
661107120Sjulian
662107120Sjulian		/* Copy socket settings */
663107120Sjulian		pcb1->imtu = pcb->imtu;
664107120Sjulian		bcopy(&pcb->oflow, &pcb1->oflow, sizeof(pcb1->oflow));
665107120Sjulian		pcb1->flush_timo = pcb->flush_timo;
666107120Sjulian
667107120Sjulian		token = pcb1->token;
668107120Sjulian	} else
669107120Sjulian		/* Nobody listens on requested BDADDR/PSM */
670107120Sjulian		result = NG_L2CAP_PSM_NOT_SUPPORTED;
671107120Sjulian
672107120Sjulianrespond:
673107120Sjulian	error = ng_btsocket_l2cap_send_l2ca_con_rsp_req(token, rt,
674281198Stakawata							&ip->bdaddr,
675281198Stakawata							ip->ident, ip->lcid,
676281198Stakawata							result,ip->linktype);
677107120Sjulian	if (pcb1 != NULL) {
678107120Sjulian		if (error != 0) {
679107120Sjulian			pcb1->so->so_error = error;
680107120Sjulian			pcb1->state = NG_BTSOCKET_L2CAP_CLOSED;
681107120Sjulian			soisdisconnected(pcb1->so);
682107120Sjulian		} else {
683107120Sjulian			pcb1->state = NG_BTSOCKET_L2CAP_CONNECTING;
684107120Sjulian			soisconnecting(pcb1->so);
685107120Sjulian
686107120Sjulian			ng_btsocket_l2cap_timeout(pcb1);
687107120Sjulian		}
688107120Sjulian
689107120Sjulian		mtx_unlock(&pcb1->pcb_mtx);
690107120Sjulian	}
691107120Sjulian
692107120Sjulian	if (pcb != NULL)
693107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
694107120Sjulian
695114878Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
696114878Sjulian
697107120Sjulian	return (error);
698107120Sjulian} /* ng_btsocket_l2cap_process_l2ca_con_ind */
699290038Stakawata/*Encryption Change*/
700290038Stakawatastatic int ng_btsocket_l2cap_process_l2ca_enc_change(struct ng_mesg *msg, ng_btsocket_l2cap_rtentry_p rt)
701290038Stakawata{
702290038Stakawata	ng_l2cap_l2ca_enc_chg_op	*op = NULL;
703290038Stakawata	ng_btsocket_l2cap_pcb_t		*pcb = NULL;
704107120Sjulian
705290038Stakawata
706290038Stakawata	if (msg->header.arglen != sizeof(*op))
707290038Stakawata		return (EMSGSIZE);
708290038Stakawata
709290038Stakawata	op = (ng_l2cap_l2ca_enc_chg_op *)(msg->data);
710290038Stakawata
711292660Semax	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
712292660Semax
713290038Stakawata	pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, op->lcid,
714290038Stakawata					   op->idtype);
715292660Semax	if (pcb == NULL) {
716292660Semax		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
717292660Semax		return (ENOENT);
718292660Semax	}
719292660Semax
720290038Stakawata	mtx_lock(&pcb->pcb_mtx);
721290038Stakawata	pcb->encryption = op->result;
722290038Stakawata
723290038Stakawata	if(pcb->need_encrypt){
724290491Stakawata		ng_btsocket_l2cap_untimeout(pcb);
725290038Stakawata		if(pcb->state != NG_BTSOCKET_L2CAP_W4_ENC_CHANGE){
726290038Stakawata			NG_BTSOCKET_L2CAP_WARN("%s: Invalid pcb status %d",
727290038Stakawata					       __func__, pcb->state);
728290038Stakawata		}else if(pcb->encryption){
729290038Stakawata			pcb->state = NG_BTSOCKET_L2CAP_OPEN;
730290038Stakawata			soisconnected(pcb->so);
731290038Stakawata		}else{
732290038Stakawata			pcb->so->so_error = EPERM;
733290491Stakawata			ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
734290038Stakawata			pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
735290038Stakawata			soisdisconnected(pcb->so);
736290038Stakawata		}
737290038Stakawata	}
738290038Stakawata	mtx_unlock(&pcb->pcb_mtx);
739292660Semax	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
740290038Stakawata
741290038Stakawata	return 0;
742290038Stakawata}
743107120Sjulian/*
744107120Sjulian * Process L2CA_Config response
745107120Sjulian */
746107120Sjulian
747107120Sjulianstatic int
748107120Sjulianng_btsocket_l2cap_process_l2ca_cfg_req_rsp(struct ng_mesg *msg,
749161623Semax		ng_btsocket_l2cap_rtentry_p rt)
750107120Sjulian{
751107120Sjulian	ng_l2cap_l2ca_cfg_op	*op = NULL;
752107120Sjulian	ng_btsocket_l2cap_pcb_p	 pcb = NULL;
753107120Sjulian
754107120Sjulian	if (msg->header.arglen != sizeof(*op))
755107120Sjulian		return (EMSGSIZE);
756107120Sjulian
757107120Sjulian	op = (ng_l2cap_l2ca_cfg_op *)(msg->data);
758107120Sjulian
759114878Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
760114878Sjulian
761107120Sjulian	/*
762107120Sjulian	 * Socket must have issued a Configure request, so we must have a
763107120Sjulian	 * socket that wants to be configured. Use Netgraph message token
764107120Sjulian	 * to find it
765107120Sjulian	 */
766107120Sjulian
767107120Sjulian	pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);
768107120Sjulian	if (pcb == NULL) {
769107120Sjulian		/*
770107120Sjulian		 * XXX FIXME what to do here? We could not find a
771107120Sjulian		 * socket with requested token. We even can not send
772107120Sjulian		 * Disconnect, because we do not know channel ID
773107120Sjulian		 */
774107120Sjulian
775114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
776107120Sjulian		return (ENOENT);
777107120Sjulian	}
778107120Sjulian
779107120Sjulian	mtx_lock(&pcb->pcb_mtx);
780107120Sjulian
781107120Sjulian        NG_BTSOCKET_L2CAP_INFO(
782107120Sjulian"%s: Got L2CA_Config response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \
783107120Sjulian"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d, " \
784107120Sjulian"cfg_state=%x\n",
785107120Sjulian		__func__, msg->header.token,
786107120Sjulian		pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
787107120Sjulian		pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
788107120Sjulian		pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
789107120Sjulian		pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
790107120Sjulian		pcb->psm, pcb->cid, op->result, pcb->state, pcb->cfg_state);
791107120Sjulian
792107120Sjulian	if (pcb->state != NG_BTSOCKET_L2CAP_CONFIGURING) {
793107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
794114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
795114878Sjulian
796107120Sjulian		return (ENOENT);
797107120Sjulian	}
798107120Sjulian
799107120Sjulian	if (op->result == NG_L2CAP_SUCCESS) {
800107120Sjulian		/*
801107120Sjulian		 * XXX FIXME Actually set flush and link timeout.
802107120Sjulian		 * Set QoS here if required. Resolve conficts (flush_timo).
803107120Sjulian		 * Save incoming MTU (peer's outgoing MTU) and outgoing flow
804107120Sjulian		 * spec.
805107120Sjulian		 */
806107120Sjulian
807107120Sjulian		pcb->imtu = op->imtu;
808107120Sjulian		bcopy(&op->oflow, &pcb->oflow, sizeof(pcb->oflow));
809107120Sjulian		pcb->flush_timo = op->flush_timo;
810107120Sjulian
811107120Sjulian		/*
812107120Sjulian		 * We have configured incoming side, so record it and check
813107120Sjulian		 * if configuration is complete. If complete then mark socket
814107120Sjulian		 * as connected, otherwise wait for the peer.
815107120Sjulian		 */
816107120Sjulian
817107120Sjulian		pcb->cfg_state &= ~NG_BTSOCKET_L2CAP_CFG_IN_SENT;
818107120Sjulian		pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_IN;
819107120Sjulian
820107120Sjulian		if (pcb->cfg_state == NG_BTSOCKET_L2CAP_CFG_BOTH) {
821107120Sjulian			/* Configuration complete - mark socket as open */
822107120Sjulian			ng_btsocket_l2cap_untimeout(pcb);
823107120Sjulian			pcb->state = NG_BTSOCKET_L2CAP_OPEN;
824107120Sjulian			soisconnected(pcb->so);
825107120Sjulian		}
826107120Sjulian	} else {
827107120Sjulian		/*
828107120Sjulian		 * Something went wrong. Could be unacceptable parameters,
829107120Sjulian		 * reject or unknown option. That's too bad, but we will
830107120Sjulian		 * not negotiate. Send Disconnect and close the channel.
831107120Sjulian		 */
832107120Sjulian
833107120Sjulian		ng_btsocket_l2cap_untimeout(pcb);
834107120Sjulian
835107120Sjulian		switch (op->result) {
836107120Sjulian		case NG_L2CAP_UNACCEPTABLE_PARAMS:
837107120Sjulian		case NG_L2CAP_UNKNOWN_OPTION:
838107120Sjulian			pcb->so->so_error = EINVAL;
839107120Sjulian			break;
840107120Sjulian
841107120Sjulian		default:
842107120Sjulian			pcb->so->so_error = ECONNRESET;
843107120Sjulian			break;
844107120Sjulian		}
845107120Sjulian
846107120Sjulian		/* Send disconnect with "zero" token */
847107120Sjulian		ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
848107120Sjulian
849107120Sjulian		/* ... and close the socket */
850107120Sjulian		pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
851107120Sjulian		soisdisconnected(pcb->so);
852107120Sjulian	}
853107120Sjulian
854107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
855114878Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
856107120Sjulian
857107120Sjulian	return (0);
858107120Sjulian} /* ng_btsocket_l2cap_process_l2ca_cfg_req_rsp */
859107120Sjulian
860107120Sjulian/*
861107120Sjulian * Process L2CA_ConfigRsp response
862107120Sjulian */
863107120Sjulian
864107120Sjulianstatic int
865107120Sjulianng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp(struct ng_mesg *msg,
866161623Semax		ng_btsocket_l2cap_rtentry_p rt)
867107120Sjulian{
868107120Sjulian	ng_l2cap_l2ca_cfg_rsp_op	*op = NULL;
869107120Sjulian	ng_btsocket_l2cap_pcb_t		*pcb = NULL;
870107120Sjulian	int				 error = 0;
871107120Sjulian
872107120Sjulian	if (msg->header.arglen != sizeof(*op))
873107120Sjulian		return (EMSGSIZE);
874107120Sjulian
875107120Sjulian	op = (ng_l2cap_l2ca_cfg_rsp_op *)(msg->data);
876107120Sjulian
877114878Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
878114878Sjulian
879107120Sjulian	/* Look for the socket with the token */
880107120Sjulian	pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);
881114878Sjulian	if (pcb == NULL) {
882114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
883107120Sjulian		return (ENOENT);
884114878Sjulian	}
885107120Sjulian
886107120Sjulian	mtx_lock(&pcb->pcb_mtx);
887107120Sjulian
888107120Sjulian        NG_BTSOCKET_L2CAP_INFO(
889107120Sjulian"%s: Got L2CA_ConfigRsp response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \
890107120Sjulian"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d, " \
891107120Sjulian"cfg_state=%x\n",
892107120Sjulian		__func__, msg->header.token,
893107120Sjulian		pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
894107120Sjulian		pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
895107120Sjulian		pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
896107120Sjulian		pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
897107120Sjulian		pcb->psm, pcb->cid, op->result, pcb->state, pcb->cfg_state);
898107120Sjulian
899107120Sjulian	if (pcb->state != NG_BTSOCKET_L2CAP_CONFIGURING) {
900107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
901114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
902114878Sjulian
903107120Sjulian		return (ENOENT);
904107120Sjulian	}
905107120Sjulian
906107120Sjulian	/* Check the result and disconnect socket of failure */
907107120Sjulian	if (op->result != NG_L2CAP_SUCCESS)
908107120Sjulian		goto disconnect;
909107120Sjulian
910107120Sjulian	/*
911107120Sjulian	 * Now we done with remote side configuration. Configure local
912107120Sjulian	 * side if we have not done it yet.
913107120Sjulian	 */
914107120Sjulian
915107120Sjulian	pcb->cfg_state &= ~NG_BTSOCKET_L2CAP_CFG_OUT_SENT;
916107120Sjulian	pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_OUT;
917107120Sjulian
918107120Sjulian	if (pcb->cfg_state == NG_BTSOCKET_L2CAP_CFG_BOTH) {
919107120Sjulian		/* Configuration complete - mask socket as open */
920107120Sjulian		ng_btsocket_l2cap_untimeout(pcb);
921107120Sjulian		pcb->state = NG_BTSOCKET_L2CAP_OPEN;
922107120Sjulian		soisconnected(pcb->so);
923107120Sjulian	} else {
924107120Sjulian		if (!(pcb->cfg_state & NG_BTSOCKET_L2CAP_CFG_IN_SENT)) {
925107120Sjulian			/* Send L2CA_Config request - incoming path */
926107120Sjulian			error = ng_btsocket_l2cap_send_l2ca_cfg_req(pcb);
927107120Sjulian			if (error != 0)
928107120Sjulian				goto disconnect;
929107120Sjulian
930107120Sjulian			pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_IN_SENT;
931107120Sjulian		}
932107120Sjulian	}
933107120Sjulian
934107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
935114878Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
936107120Sjulian
937107120Sjulian	return (error);
938107120Sjulian
939107120Sjuliandisconnect:
940107120Sjulian	ng_btsocket_l2cap_untimeout(pcb);
941107120Sjulian
942107120Sjulian	/* Send disconnect with "zero" token */
943107120Sjulian	ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
944107120Sjulian
945107120Sjulian	/* ... and close the socket */
946107120Sjulian	pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
947107120Sjulian	soisdisconnected(pcb->so);
948107120Sjulian
949107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
950114878Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
951107120Sjulian
952107120Sjulian	return (error);
953107120Sjulian} /* ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp */
954107120Sjulian
955107120Sjulian/*
956107120Sjulian * Process L2CA_Config indicator
957107120Sjulian */
958107120Sjulian
959107120Sjulianstatic int
960107120Sjulianng_btsocket_l2cap_process_l2ca_cfg_ind(struct ng_mesg *msg,
961161623Semax		ng_btsocket_l2cap_rtentry_p rt)
962107120Sjulian{
963107120Sjulian	ng_l2cap_l2ca_cfg_ind_ip	*ip = NULL;
964107120Sjulian	ng_btsocket_l2cap_pcb_t		*pcb = NULL;
965107120Sjulian	int				 error = 0;
966107120Sjulian
967107120Sjulian	if (msg->header.arglen != sizeof(*ip))
968107120Sjulian		return (EMSGSIZE);
969107120Sjulian
970107120Sjulian	ip = (ng_l2cap_l2ca_cfg_ind_ip *)(msg->data);
971107120Sjulian
972114878Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
973114878Sjulian
974107120Sjulian	/* Check for the open socket that has given channel ID */
975281198Stakawata	pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, ip->lcid,
976281198Stakawata					   NG_L2CAP_L2CA_IDTYPE_BREDR);
977114878Sjulian	if (pcb == NULL) {
978114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
979107120Sjulian		return (ENOENT);
980114878Sjulian	}
981107120Sjulian
982107120Sjulian	mtx_lock(&pcb->pcb_mtx);
983107120Sjulian
984107120Sjulian        NG_BTSOCKET_L2CAP_INFO(
985107120Sjulian"%s: Got L2CA_Config indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \
986107120Sjulian"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, state=%d, cfg_state=%x\n",
987107120Sjulian		__func__,
988107120Sjulian		pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
989107120Sjulian		pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
990107120Sjulian		pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
991107120Sjulian		pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
992107120Sjulian		pcb->psm, pcb->cid, pcb->state, pcb->cfg_state);
993107120Sjulian
994107120Sjulian	/* XXX FIXME re-configuration on open socket */
995107120Sjulian 	if (pcb->state != NG_BTSOCKET_L2CAP_CONFIGURING) {
996107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
997114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
998114878Sjulian
999107120Sjulian		return (ENOENT);
1000107120Sjulian	}
1001107120Sjulian
1002107120Sjulian	/*
1003107120Sjulian	 * XXX FIXME Actually set flush and link timeout. Set QoS here if
1004107120Sjulian	 * required. Resolve conficts (flush_timo). Note outgoing MTU (peer's
1005107120Sjulian	 * incoming MTU) and incoming flow spec.
1006107120Sjulian	 */
1007107120Sjulian
1008107120Sjulian	pcb->omtu = ip->omtu;
1009107120Sjulian	bcopy(&ip->iflow, &pcb->iflow, sizeof(pcb->iflow));
1010107120Sjulian	pcb->flush_timo = ip->flush_timo;
1011107120Sjulian
1012107120Sjulian	/*
1013107120Sjulian	 * Send L2CA_Config response to our peer and check for the errors,
1014107120Sjulian	 * if any send disconnect to close the channel.
1015107120Sjulian	 */
1016107120Sjulian
1017114878Sjulian	if (!(pcb->cfg_state & NG_BTSOCKET_L2CAP_CFG_OUT_SENT)) {
1018114878Sjulian		error = ng_btsocket_l2cap_send_l2ca_cfg_rsp(pcb);
1019114878Sjulian		if (error != 0) {
1020114878Sjulian			ng_btsocket_l2cap_untimeout(pcb);
1021107120Sjulian
1022114878Sjulian			pcb->so->so_error = error;
1023107120Sjulian
1024114878Sjulian			/* Send disconnect with "zero" token */
1025114878Sjulian			ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
1026107120Sjulian
1027114878Sjulian			/* ... and close the socket */
1028114878Sjulian			pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
1029114878Sjulian			soisdisconnected(pcb->so);
1030114878Sjulian		} else
1031114878Sjulian			pcb->cfg_state |= NG_BTSOCKET_L2CAP_CFG_OUT_SENT;
1032107120Sjulian	}
1033107120Sjulian
1034107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
1035114878Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1036107120Sjulian
1037107120Sjulian	return (error);
1038107120Sjulian} /* ng_btsocket_l2cap_process_l2cap_cfg_ind */
1039107120Sjulian
1040107120Sjulian/*
1041107120Sjulian * Process L2CA_Disconnect response
1042107120Sjulian */
1043107120Sjulian
1044107120Sjulianstatic int
1045107120Sjulianng_btsocket_l2cap_process_l2ca_discon_rsp(struct ng_mesg *msg,
1046161623Semax		ng_btsocket_l2cap_rtentry_p rt)
1047107120Sjulian{
1048107120Sjulian	ng_l2cap_l2ca_discon_op	*op = NULL;
1049107120Sjulian	ng_btsocket_l2cap_pcb_t	*pcb = NULL;
1050107120Sjulian
1051107120Sjulian	/* Check message */
1052107120Sjulian	if (msg->header.arglen != sizeof(*op))
1053107120Sjulian		return (EMSGSIZE);
1054107120Sjulian
1055107120Sjulian	op = (ng_l2cap_l2ca_discon_op *)(msg->data);
1056107120Sjulian
1057114878Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
1058114878Sjulian
1059107120Sjulian	/*
1060107120Sjulian	 * Socket layer must have issued L2CA_Disconnect request, so there
1061107120Sjulian	 * must be a socket that wants to be disconnected. Use Netgraph
1062107120Sjulian	 * message token to find it.
1063107120Sjulian	 */
1064107120Sjulian
1065107120Sjulian	pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);
1066114878Sjulian	if (pcb == NULL) {
1067114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1068107120Sjulian		return (0);
1069114878Sjulian	}
1070107120Sjulian
1071107120Sjulian	mtx_lock(&pcb->pcb_mtx);
1072107120Sjulian
1073107120Sjulian	/* XXX Close socket no matter what op->result says */
1074107120Sjulian	if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED) {
1075107120Sjulian       		NG_BTSOCKET_L2CAP_INFO(
1076107120Sjulian"%s: Got L2CA_Disconnect response, token=%d, src bdaddr=%x:%x:%x:%x:%x:%x, " \
1077107120Sjulian"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, state=%d\n",
1078107120Sjulian			__func__, msg->header.token,
1079107120Sjulian			pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
1080107120Sjulian			pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
1081107120Sjulian			pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
1082107120Sjulian			pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
1083107120Sjulian			pcb->psm, pcb->cid, op->result, pcb->state);
1084107120Sjulian
1085107120Sjulian		ng_btsocket_l2cap_untimeout(pcb);
1086107120Sjulian
1087107120Sjulian		pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
1088107120Sjulian		soisdisconnected(pcb->so);
1089107120Sjulian	}
1090107120Sjulian
1091107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
1092114878Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1093107120Sjulian
1094107120Sjulian	return (0);
1095107120Sjulian} /* ng_btsocket_l2cap_process_l2ca_discon_rsp */
1096107120Sjulian
1097107120Sjulian/*
1098107120Sjulian * Process L2CA_Disconnect indicator
1099107120Sjulian */
1100107120Sjulian
1101107120Sjulianstatic int
1102107120Sjulianng_btsocket_l2cap_process_l2ca_discon_ind(struct ng_mesg *msg,
1103161623Semax		ng_btsocket_l2cap_rtentry_p rt)
1104107120Sjulian{
1105107120Sjulian	ng_l2cap_l2ca_discon_ind_ip	*ip = NULL;
1106107120Sjulian	ng_btsocket_l2cap_pcb_t		*pcb = NULL;
1107107120Sjulian
1108107120Sjulian	/* Check message */
1109107120Sjulian	if (msg->header.arglen != sizeof(*ip))
1110107120Sjulian		return (EMSGSIZE);
1111107120Sjulian
1112107120Sjulian	ip = (ng_l2cap_l2ca_discon_ind_ip *)(msg->data);
1113107120Sjulian
1114114878Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
1115114878Sjulian
1116107120Sjulian	/* Look for the socket with given channel ID */
1117281198Stakawata	pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, ip->lcid,
1118301558Stakawata					   ip->idtype);
1119114878Sjulian	if (pcb == NULL) {
1120114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1121107120Sjulian		return (0);
1122114878Sjulian	}
1123107120Sjulian
1124107120Sjulian	/*
1125107120Sjulian	 * Channel has already been destroyed, so disconnect the socket
1126107120Sjulian	 * and be done with it. If there was any pending request we can
1127107120Sjulian	 * not do anything here anyway.
1128107120Sjulian	 */
1129107120Sjulian
1130107120Sjulian	mtx_lock(&pcb->pcb_mtx);
1131107120Sjulian
1132107120Sjulian       	NG_BTSOCKET_L2CAP_INFO(
1133107120Sjulian"%s: Got L2CA_Disconnect indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \
1134107120Sjulian"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, state=%d\n",
1135107120Sjulian		__func__,
1136107120Sjulian		pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
1137107120Sjulian		pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
1138107120Sjulian		pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
1139107120Sjulian		pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
1140107120Sjulian		pcb->psm, pcb->cid, pcb->state);
1141107120Sjulian
1142107120Sjulian	if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO)
1143107120Sjulian		ng_btsocket_l2cap_untimeout(pcb);
1144107120Sjulian
1145107120Sjulian	pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
1146107120Sjulian	soisdisconnected(pcb->so);
1147107120Sjulian
1148107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
1149114878Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1150107120Sjulian
1151107120Sjulian	return (0);
1152107120Sjulian} /* ng_btsocket_l2cap_process_l2ca_discon_ind */
1153107120Sjulian
1154107120Sjulian/*
1155107120Sjulian * Process L2CA_Write response
1156107120Sjulian */
1157107120Sjulian
1158107120Sjulianstatic int
1159107120Sjulianng_btsocket_l2cap_process_l2ca_write_rsp(struct ng_mesg *msg,
1160161623Semax		ng_btsocket_l2cap_rtentry_p rt)
1161107120Sjulian{
1162107120Sjulian	ng_l2cap_l2ca_write_op	*op = NULL;
1163107120Sjulian	ng_btsocket_l2cap_pcb_t	*pcb = NULL;
1164107120Sjulian
1165107120Sjulian	/* Check message */
1166107120Sjulian	if (msg->header.arglen != sizeof(*op))
1167107120Sjulian		return (EMSGSIZE);
1168107120Sjulian
1169107120Sjulian	op = (ng_l2cap_l2ca_write_op *)(msg->data);
1170107120Sjulian
1171114878Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
1172114878Sjulian
1173107120Sjulian	/* Look for the socket with given token */
1174107120Sjulian	pcb = ng_btsocket_l2cap_pcb_by_token(msg->header.token);
1175114878Sjulian	if (pcb == NULL) {
1176114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1177107120Sjulian		return (ENOENT);
1178114878Sjulian	}
1179107120Sjulian
1180107120Sjulian	mtx_lock(&pcb->pcb_mtx);
1181107120Sjulian
1182107120Sjulian       	NG_BTSOCKET_L2CAP_INFO(
1183107120Sjulian"%s: Got L2CA_Write response, src bdaddr=%x:%x:%x:%x:%x:%x, " \
1184107120Sjulian"dst bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, lcid=%d, result=%d, length=%d, " \
1185107120Sjulian"state=%d\n",		__func__,
1186107120Sjulian			pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
1187107120Sjulian			pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
1188107120Sjulian			pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
1189107120Sjulian			pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
1190107120Sjulian			pcb->psm, pcb->cid, op->result, op->length,
1191107120Sjulian			pcb->state);
1192107120Sjulian
1193107120Sjulian	if (pcb->state != NG_BTSOCKET_L2CAP_OPEN) {
1194107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
1195114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1196114878Sjulian
1197107120Sjulian		return (ENOENT);
1198107120Sjulian	}
1199107120Sjulian
1200107120Sjulian	ng_btsocket_l2cap_untimeout(pcb);
1201107120Sjulian
1202107120Sjulian	/*
1203107120Sjulian 	 * Check if we have more data to send
1204107120Sjulian 	 */
1205107120Sjulian	sbdroprecord(&pcb->so->so_snd);
1206274421Sglebius	if (sbavail(&pcb->so->so_snd) > 0) {
1207107120Sjulian		if (ng_btsocket_l2cap_send2(pcb) == 0)
1208107120Sjulian			ng_btsocket_l2cap_timeout(pcb);
1209107120Sjulian		else
1210107120Sjulian			sbdroprecord(&pcb->so->so_snd); /* XXX */
1211107120Sjulian	}
1212107120Sjulian
1213107120Sjulian	/*
1214107120Sjulian	 * Now set the result, drop packet from the socket send queue and
1215107120Sjulian	 * ask for more (wakeup sender)
1216107120Sjulian	 */
1217107120Sjulian
1218107120Sjulian	pcb->so->so_error = ng_btsocket_l2cap_result2errno(op->result);
1219107120Sjulian	sowwakeup(pcb->so);
1220107120Sjulian
1221107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
1222114878Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1223107120Sjulian
1224107120Sjulian	return (0);
1225107120Sjulian} /* ng_btsocket_l2cap_process_l2ca_write_rsp */
1226107120Sjulian
1227107120Sjulian/*
1228107120Sjulian * Send L2CA_Connect request
1229107120Sjulian */
1230107120Sjulian
1231107120Sjulianstatic int
1232107120Sjulianng_btsocket_l2cap_send_l2ca_con_req(ng_btsocket_l2cap_pcb_p pcb)
1233107120Sjulian{
1234107120Sjulian	struct ng_mesg		*msg = NULL;
1235107120Sjulian	ng_l2cap_l2ca_con_ip	*ip = NULL;
1236107120Sjulian	int			 error = 0;
1237107120Sjulian
1238107120Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
1239107120Sjulian
1240107120Sjulian	if (pcb->rt == NULL ||
1241107120Sjulian	    pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))
1242107120Sjulian		return (ENETDOWN);
1243107120Sjulian
1244107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON,
1245107120Sjulian		sizeof(*ip), M_NOWAIT);
1246107120Sjulian	if (msg == NULL)
1247107120Sjulian		return (ENOMEM);
1248107120Sjulian
1249107120Sjulian	msg->header.token = pcb->token;
1250107120Sjulian
1251107120Sjulian	ip = (ng_l2cap_l2ca_con_ip *)(msg->data);
1252107120Sjulian	bcopy(&pcb->dst, &ip->bdaddr, sizeof(ip->bdaddr));
1253107120Sjulian	ip->psm = pcb->psm;
1254281198Stakawata	ip->linktype = ng_btsock_l2cap_addrtype_to_linktype(pcb->dsttype);
1255290038Stakawata	ip->idtype = pcb->idtype;
1256123812Salfred	NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg,pcb->rt->hook, 0);
1257107120Sjulian
1258107120Sjulian	return (error);
1259107120Sjulian} /* ng_btsocket_l2cap_send_l2ca_con_req */
1260107120Sjulian
1261107120Sjulian/*
1262107120Sjulian * Send L2CA_Connect response
1263107120Sjulian */
1264107120Sjulian
1265107120Sjulianstatic int
1266107120Sjulianng_btsocket_l2cap_send_l2ca_con_rsp_req(u_int32_t token,
1267107120Sjulian		ng_btsocket_l2cap_rtentry_p rt, bdaddr_p dst, int ident,
1268281198Stakawata					int lcid, int result, int linktype)
1269107120Sjulian{
1270107120Sjulian	struct ng_mesg			*msg = NULL;
1271107120Sjulian	ng_l2cap_l2ca_con_rsp_ip	*ip = NULL;
1272107120Sjulian	int				 error = 0;
1273107120Sjulian
1274107120Sjulian	if (rt == NULL || rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))
1275107120Sjulian		return (ENETDOWN);
1276107120Sjulian
1277107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_RSP,
1278107120Sjulian		sizeof(*ip), M_NOWAIT);
1279107120Sjulian	if (msg == NULL)
1280107120Sjulian		return (ENOMEM);
1281107120Sjulian
1282107120Sjulian	msg->header.token = token;
1283107120Sjulian
1284107120Sjulian	ip = (ng_l2cap_l2ca_con_rsp_ip *)(msg->data);
1285107120Sjulian	bcopy(dst, &ip->bdaddr, sizeof(ip->bdaddr));
1286107120Sjulian	ip->ident = ident;
1287107120Sjulian	ip->lcid = lcid;
1288281198Stakawata	ip->linktype = linktype;
1289107120Sjulian	ip->result = result;
1290107120Sjulian	ip->status = 0;
1291107120Sjulian
1292123812Salfred	NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg, rt->hook, 0);
1293107120Sjulian
1294107120Sjulian	return (error);
1295107120Sjulian} /* ng_btsocket_l2cap_send_l2ca_con_rsp_req */
1296107120Sjulian
1297107120Sjulian/*
1298107120Sjulian * Send L2CA_Config request
1299107120Sjulian */
1300107120Sjulian
1301107120Sjulianstatic int
1302107120Sjulianng_btsocket_l2cap_send_l2ca_cfg_req(ng_btsocket_l2cap_pcb_p pcb)
1303107120Sjulian{
1304107120Sjulian	struct ng_mesg		*msg = NULL;
1305107120Sjulian	ng_l2cap_l2ca_cfg_ip	*ip = NULL;
1306107120Sjulian	int			 error = 0;
1307107120Sjulian
1308107120Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
1309107120Sjulian
1310107120Sjulian	if (pcb->rt == NULL ||
1311107120Sjulian	    pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))
1312107120Sjulian		return (ENETDOWN);
1313107120Sjulian
1314107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG,
1315107120Sjulian		sizeof(*ip), M_NOWAIT);
1316107120Sjulian	if (msg == NULL)
1317107120Sjulian		return (ENOMEM);
1318107120Sjulian
1319107120Sjulian	msg->header.token = pcb->token;
1320107120Sjulian
1321107120Sjulian	ip = (ng_l2cap_l2ca_cfg_ip *)(msg->data);
1322107120Sjulian	ip->lcid = pcb->cid;
1323107120Sjulian	ip->imtu = pcb->imtu;
1324107120Sjulian	bcopy(&pcb->oflow, &ip->oflow, sizeof(ip->oflow));
1325107120Sjulian	ip->flush_timo = pcb->flush_timo;
1326107120Sjulian	ip->link_timo = pcb->link_timo;
1327107120Sjulian
1328123812Salfred	NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg,pcb->rt->hook, 0);
1329107120Sjulian
1330107120Sjulian	return (error);
1331107120Sjulian} /* ng_btsocket_l2cap_send_l2ca_cfg_req */
1332107120Sjulian
1333107120Sjulian/*
1334107120Sjulian * Send L2CA_Config response
1335107120Sjulian */
1336107120Sjulian
1337107120Sjulianstatic int
1338107120Sjulianng_btsocket_l2cap_send_l2ca_cfg_rsp(ng_btsocket_l2cap_pcb_p pcb)
1339107120Sjulian{
1340107120Sjulian	struct ng_mesg			*msg = NULL;
1341107120Sjulian	ng_l2cap_l2ca_cfg_rsp_ip	*ip = NULL;
1342107120Sjulian	int				 error = 0;
1343107120Sjulian
1344107120Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
1345107120Sjulian
1346107120Sjulian	if (pcb->rt == NULL ||
1347107120Sjulian	    pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))
1348107120Sjulian		return (ENETDOWN);
1349107120Sjulian
1350107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_RSP,
1351107120Sjulian		sizeof(*ip), M_NOWAIT);
1352107120Sjulian	if (msg == NULL)
1353107120Sjulian		return (ENOMEM);
1354107120Sjulian
1355107120Sjulian	msg->header.token = pcb->token;
1356107120Sjulian
1357107120Sjulian	ip = (ng_l2cap_l2ca_cfg_rsp_ip *)(msg->data);
1358107120Sjulian	ip->lcid = pcb->cid;
1359107120Sjulian	ip->omtu = pcb->omtu;
1360107120Sjulian	bcopy(&pcb->iflow, &ip->iflow, sizeof(ip->iflow));
1361107120Sjulian
1362123812Salfred	NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg, pcb->rt->hook, 0);
1363107120Sjulian
1364107120Sjulian	return (error);
1365107120Sjulian} /* ng_btsocket_l2cap_send_l2ca_cfg_rsp */
1366107120Sjulian
1367107120Sjulian/*
1368107120Sjulian * Send L2CA_Disconnect request
1369107120Sjulian */
1370107120Sjulian
1371107120Sjulianstatic int
1372107120Sjulianng_btsocket_l2cap_send_l2ca_discon_req(u_int32_t token,
1373107120Sjulian		ng_btsocket_l2cap_pcb_p pcb)
1374107120Sjulian{
1375107120Sjulian	struct ng_mesg		*msg = NULL;
1376107120Sjulian	ng_l2cap_l2ca_discon_ip	*ip = NULL;
1377107120Sjulian	int			 error = 0;
1378107120Sjulian
1379107120Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
1380107120Sjulian
1381107120Sjulian	if (pcb->rt == NULL ||
1382107120Sjulian	    pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))
1383107120Sjulian		return (ENETDOWN);
1384107120Sjulian
1385107120Sjulian	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON,
1386107120Sjulian		sizeof(*ip), M_NOWAIT);
1387107120Sjulian	if (msg == NULL)
1388107120Sjulian		return (ENOMEM);
1389107120Sjulian
1390107120Sjulian	msg->header.token = token;
1391107120Sjulian
1392107120Sjulian	ip = (ng_l2cap_l2ca_discon_ip *)(msg->data);
1393107120Sjulian	ip->lcid = pcb->cid;
1394290038Stakawata	ip->idtype = pcb->idtype;
1395107120Sjulian
1396123812Salfred	NG_SEND_MSG_HOOK(error, ng_btsocket_l2cap_node, msg,pcb->rt->hook, 0);
1397107120Sjulian
1398107120Sjulian	return (error);
1399107120Sjulian} /* ng_btsocket_l2cap_send_l2ca_discon_req */
1400107120Sjulian
1401107120Sjulian/*****************************************************************************
1402107120Sjulian *****************************************************************************
1403107120Sjulian **                              Socket interface
1404107120Sjulian *****************************************************************************
1405107120Sjulian *****************************************************************************/
1406107120Sjulian
1407107120Sjulian/*
1408107120Sjulian * L2CAP sockets data input routine
1409107120Sjulian */
1410107120Sjulian
1411107120Sjulianstatic void
1412107120Sjulianng_btsocket_l2cap_data_input(struct mbuf *m, hook_p hook)
1413107120Sjulian{
1414107120Sjulian	ng_l2cap_hdr_t			*hdr = NULL;
1415107120Sjulian	ng_l2cap_clt_hdr_t		*clt_hdr = NULL;
1416107120Sjulian	ng_btsocket_l2cap_pcb_t		*pcb = NULL;
1417107120Sjulian	ng_btsocket_l2cap_rtentry_t	*rt = NULL;
1418281198Stakawata	uint16_t idtype;
1419107120Sjulian
1420107120Sjulian	if (hook == NULL) {
1421107120Sjulian		NG_BTSOCKET_L2CAP_ALERT(
1422107120Sjulian"%s: Invalid source hook for L2CAP data packet\n", __func__);
1423107120Sjulian		goto drop;
1424107120Sjulian	}
1425107120Sjulian
1426107120Sjulian	rt = (ng_btsocket_l2cap_rtentry_t *) NG_HOOK_PRIVATE(hook);
1427107120Sjulian	if (rt == NULL) {
1428107120Sjulian		NG_BTSOCKET_L2CAP_ALERT(
1429107120Sjulian"%s: Could not find out source bdaddr for L2CAP data packet\n", __func__);
1430107120Sjulian		goto drop;
1431107120Sjulian	}
1432107120Sjulian
1433281198Stakawata	m = m_pullup(m, sizeof(uint16_t));
1434281198Stakawata	idtype = *mtod(m, uint16_t *);
1435281198Stakawata	m_adj(m, sizeof(uint16_t));
1436281198Stakawata
1437107120Sjulian	/* Make sure we can access header */
1438107120Sjulian	if (m->m_pkthdr.len < sizeof(*hdr)) {
1439107120Sjulian		NG_BTSOCKET_L2CAP_ERR(
1440107120Sjulian"%s: L2CAP data packet too small, len=%d\n", __func__, m->m_pkthdr.len);
1441107120Sjulian		goto drop;
1442107120Sjulian	}
1443107120Sjulian
1444107120Sjulian	if (m->m_len < sizeof(*hdr)) {
1445107120Sjulian		m = m_pullup(m, sizeof(*hdr));
1446107120Sjulian		if (m == NULL)
1447107120Sjulian			goto drop;
1448107120Sjulian	}
1449107120Sjulian
1450107120Sjulian	/* Strip L2CAP packet header and verify packet length */
1451107120Sjulian	hdr = mtod(m, ng_l2cap_hdr_t *);
1452107120Sjulian	m_adj(m, sizeof(*hdr));
1453107120Sjulian
1454107120Sjulian	if (hdr->length != m->m_pkthdr.len) {
1455107120Sjulian		NG_BTSOCKET_L2CAP_ERR(
1456107120Sjulian"%s: Bad L2CAP data packet length, len=%d, length=%d\n",
1457107120Sjulian			__func__, m->m_pkthdr.len, hdr->length);
1458107120Sjulian		goto drop;
1459107120Sjulian	}
1460107120Sjulian
1461107120Sjulian	/*
1462107120Sjulian	 * Now process packet. Two cases:
1463107120Sjulian	 *
1464107120Sjulian	 * 1) Normal packet (cid != 2) then find connected socket and append
1465107120Sjulian	 *    mbuf to the socket queue. Wakeup socket.
1466107120Sjulian	 *
1467107120Sjulian	 * 2) Broadcast packet (cid == 2) then find all sockets that connected
1468107120Sjulian	 *    to the given PSM and have SO_BROADCAST bit set and append mbuf
1469107120Sjulian	 *    to the socket queue. Wakeup socket.
1470107120Sjulian	 */
1471107120Sjulian
1472107120Sjulian	NG_BTSOCKET_L2CAP_INFO(
1473114878Sjulian"%s: Received L2CAP data packet: src bdaddr=%x:%x:%x:%x:%x:%x, " \
1474107120Sjulian"dcid=%d, length=%d\n",
1475107120Sjulian		__func__,
1476107120Sjulian		rt->src.b[5], rt->src.b[4], rt->src.b[3],
1477107120Sjulian		rt->src.b[2], rt->src.b[1], rt->src.b[0],
1478107120Sjulian		hdr->dcid, hdr->length);
1479107120Sjulian
1480281198Stakawata	if ((hdr->dcid >= NG_L2CAP_FIRST_CID) ||
1481290038Stakawata	    (idtype == NG_L2CAP_L2CA_IDTYPE_ATT)||
1482290038Stakawata	    (idtype == NG_L2CAP_L2CA_IDTYPE_SMP)
1483290038Stakawata	    ){
1484114878Sjulian
1485114878Sjulian		mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
1486114878Sjulian
1487107120Sjulian		/* Normal packet: find connected socket */
1488281198Stakawata		pcb = ng_btsocket_l2cap_pcb_by_cid(&rt->src, hdr->dcid,idtype);
1489114878Sjulian		if (pcb == NULL) {
1490114878Sjulian			mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1491107120Sjulian			goto drop;
1492114878Sjulian		}
1493107120Sjulian
1494107120Sjulian		mtx_lock(&pcb->pcb_mtx);
1495107120Sjulian
1496107120Sjulian		if (pcb->state != NG_BTSOCKET_L2CAP_OPEN) {
1497107120Sjulian			NG_BTSOCKET_L2CAP_ERR(
1498107120Sjulian"%s: No connected socket found, src bdaddr=%x:%x:%x:%x:%x:%x, dcid=%d, " \
1499107120Sjulian"state=%d\n",			__func__,
1500107120Sjulian				rt->src.b[5], rt->src.b[4], rt->src.b[3],
1501107120Sjulian				rt->src.b[2], rt->src.b[1], rt->src.b[0],
1502107120Sjulian				hdr->dcid, pcb->state);
1503107120Sjulian
1504107120Sjulian			mtx_unlock(&pcb->pcb_mtx);
1505114878Sjulian			mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1506107120Sjulian			goto drop;
1507107120Sjulian		}
1508107120Sjulian
1509107120Sjulian		/* Check packet size against socket's incoming MTU */
1510107120Sjulian		if (hdr->length > pcb->imtu) {
1511107120Sjulian			NG_BTSOCKET_L2CAP_ERR(
1512107120Sjulian"%s: L2CAP data packet too big, src bdaddr=%x:%x:%x:%x:%x:%x, " \
1513107120Sjulian"dcid=%d, length=%d, imtu=%d\n",
1514107120Sjulian				__func__,
1515107120Sjulian				rt->src.b[5], rt->src.b[4], rt->src.b[3],
1516107120Sjulian				rt->src.b[2], rt->src.b[1], rt->src.b[0],
1517107120Sjulian				hdr->dcid, hdr->length, pcb->imtu);
1518107120Sjulian
1519107120Sjulian			mtx_unlock(&pcb->pcb_mtx);
1520114878Sjulian			mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1521107120Sjulian			goto drop;
1522107120Sjulian		}
1523107120Sjulian
1524107120Sjulian		/* Check if we have enough space in socket receive queue */
1525107120Sjulian		if (m->m_pkthdr.len > sbspace(&pcb->so->so_rcv)) {
1526107120Sjulian
1527107120Sjulian			/*
1528107120Sjulian			 * This is really bad. Receive queue on socket does
1529107120Sjulian			 * not have enough space for the packet. We do not
1530107120Sjulian			 * have any other choice but drop the packet. L2CAP
1531107120Sjulian			 * does not provide any flow control.
1532107120Sjulian			 */
1533107120Sjulian
1534107120Sjulian			NG_BTSOCKET_L2CAP_ERR(
1535107120Sjulian"%s: Not enough space in socket receive queue. Dropping L2CAP data packet, " \
1536107120Sjulian"src bdaddr=%x:%x:%x:%x:%x:%x, dcid=%d, len=%d, space=%ld\n",
1537107120Sjulian				__func__,
1538107120Sjulian				rt->src.b[5], rt->src.b[4], rt->src.b[3],
1539107120Sjulian				rt->src.b[2], rt->src.b[1], rt->src.b[0],
1540107120Sjulian				hdr->dcid, m->m_pkthdr.len,
1541107120Sjulian				sbspace(&pcb->so->so_rcv));
1542107120Sjulian
1543107120Sjulian			mtx_unlock(&pcb->pcb_mtx);
1544114878Sjulian			mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1545107120Sjulian			goto drop;
1546107120Sjulian		}
1547107120Sjulian
1548107120Sjulian		/* Append packet to the socket receive queue and wakeup */
1549107120Sjulian		sbappendrecord(&pcb->so->so_rcv, m);
1550107120Sjulian		m = NULL;
1551107120Sjulian
1552107120Sjulian		sorwakeup(pcb->so);
1553114878Sjulian
1554107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
1555114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1556114878Sjulian	} else if (hdr->dcid == NG_L2CAP_CLT_CID) {
1557107120Sjulian		/* Broadcast packet: give packet to all sockets  */
1558107120Sjulian
1559107120Sjulian		/* Check packet size against connectionless MTU */
1560107120Sjulian		if (hdr->length > NG_L2CAP_MTU_DEFAULT) {
1561107120Sjulian			NG_BTSOCKET_L2CAP_ERR(
1562107120Sjulian"%s: Connectionless L2CAP data packet too big, " \
1563107120Sjulian"src bdaddr=%x:%x:%x:%x:%x:%x, length=%d\n",
1564107120Sjulian				__func__,
1565107120Sjulian				rt->src.b[5], rt->src.b[4], rt->src.b[3],
1566107120Sjulian				rt->src.b[2], rt->src.b[1], rt->src.b[0],
1567107120Sjulian				hdr->length);
1568107120Sjulian			goto drop;
1569107120Sjulian		}
1570107120Sjulian
1571107120Sjulian		/* Make sure we can access connectionless header */
1572107120Sjulian		if (m->m_pkthdr.len < sizeof(*clt_hdr)) {
1573107120Sjulian			NG_BTSOCKET_L2CAP_ERR(
1574107120Sjulian"%s: Can not get L2CAP connectionless packet header, " \
1575107120Sjulian"src bdaddr=%x:%x:%x:%x:%x:%x, length=%d\n",
1576107120Sjulian				__func__,
1577107120Sjulian				rt->src.b[5], rt->src.b[4], rt->src.b[3],
1578107120Sjulian				rt->src.b[2], rt->src.b[1], rt->src.b[0],
1579107120Sjulian				hdr->length);
1580107120Sjulian			goto drop;
1581107120Sjulian		}
1582107120Sjulian
1583107120Sjulian		if (m->m_len < sizeof(*clt_hdr)) {
1584107120Sjulian			m = m_pullup(m, sizeof(*clt_hdr));
1585107120Sjulian			if (m == NULL)
1586107120Sjulian				goto drop;
1587107120Sjulian		}
1588107120Sjulian
1589107120Sjulian		/* Strip connectionless header and deliver packet */
1590107120Sjulian		clt_hdr = mtod(m, ng_l2cap_clt_hdr_t *);
1591107120Sjulian		m_adj(m, sizeof(*clt_hdr));
1592107120Sjulian
1593107120Sjulian		NG_BTSOCKET_L2CAP_INFO(
1594107120Sjulian"%s: Got L2CAP connectionless data packet, " \
1595107120Sjulian"src bdaddr=%x:%x:%x:%x:%x:%x, psm=%d, length=%d\n",
1596107120Sjulian			__func__,
1597107120Sjulian			rt->src.b[5], rt->src.b[4], rt->src.b[3],
1598107120Sjulian			rt->src.b[2], rt->src.b[1], rt->src.b[0],
1599107120Sjulian			clt_hdr->psm, hdr->length);
1600107120Sjulian
1601107120Sjulian		mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
1602107120Sjulian
1603107120Sjulian		LIST_FOREACH(pcb, &ng_btsocket_l2cap_sockets, next) {
1604107120Sjulian			struct mbuf	*copy = NULL;
1605107120Sjulian
1606107120Sjulian			mtx_lock(&pcb->pcb_mtx);
1607107120Sjulian
1608107120Sjulian			if (bcmp(&rt->src, &pcb->src, sizeof(pcb->src)) != 0 ||
1609107120Sjulian			    pcb->psm != clt_hdr->psm ||
1610107120Sjulian			    pcb->state != NG_BTSOCKET_L2CAP_OPEN ||
1611107120Sjulian			    (pcb->so->so_options & SO_BROADCAST) == 0 ||
1612107120Sjulian			    m->m_pkthdr.len > sbspace(&pcb->so->so_rcv))
1613107120Sjulian				goto next;
1614107120Sjulian
1615107120Sjulian			/*
1616107120Sjulian			 * Create a copy of the packet and append it to the
1617107120Sjulian			 * socket's queue. If m_dup() failed - no big deal
1618107120Sjulian			 * it is a broadcast traffic after all
1619107120Sjulian			 */
1620107120Sjulian
1621243882Sglebius			copy = m_dup(m, M_NOWAIT);
1622107120Sjulian			if (copy != NULL) {
1623107120Sjulian				sbappendrecord(&pcb->so->so_rcv, copy);
1624107120Sjulian				sorwakeup(pcb->so);
1625107120Sjulian			}
1626107120Sjuliannext:
1627107120Sjulian			mtx_unlock(&pcb->pcb_mtx);
1628107120Sjulian		}
1629114878Sjulian
1630107120Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1631107120Sjulian	}
1632107120Sjuliandrop:
1633107120Sjulian	NG_FREE_M(m); /* checks for m != NULL */
1634107120Sjulian} /* ng_btsocket_l2cap_data_input */
1635107120Sjulian
1636107120Sjulian/*
1637107120Sjulian * L2CAP sockets default message input routine
1638107120Sjulian */
1639107120Sjulian
1640107120Sjulianstatic void
1641107120Sjulianng_btsocket_l2cap_default_msg_input(struct ng_mesg *msg, hook_p hook)
1642107120Sjulian{
1643107120Sjulian	switch (msg->header.cmd) {
1644107120Sjulian	case NGM_L2CAP_NODE_HOOK_INFO: {
1645107120Sjulian		ng_btsocket_l2cap_rtentry_t	*rt = NULL;
1646281198Stakawata		ng_l2cap_node_hook_info_ep *ep =
1647281198Stakawata		  (ng_l2cap_node_hook_info_ep *)msg->data;
1648281198Stakawata		if (hook == NULL || msg->header.arglen != sizeof(*ep))
1649107120Sjulian			break;
1650107120Sjulian
1651281198Stakawata		if (bcmp(&ep->addr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
1652107120Sjulian			break;
1653107120Sjulian
1654114878Sjulian		mtx_lock(&ng_btsocket_l2cap_rt_mtx);
1655114878Sjulian
1656107120Sjulian		rt = (ng_btsocket_l2cap_rtentry_t *) NG_HOOK_PRIVATE(hook);
1657107120Sjulian		if (rt == NULL) {
1658184205Sdes			rt = malloc(sizeof(*rt),
1659107120Sjulian				M_NETGRAPH_BTSOCKET_L2CAP, M_NOWAIT|M_ZERO);
1660114878Sjulian			if (rt == NULL) {
1661114878Sjulian				mtx_unlock(&ng_btsocket_l2cap_rt_mtx);
1662107120Sjulian				break;
1663114878Sjulian			}
1664107120Sjulian
1665107120Sjulian			LIST_INSERT_HEAD(&ng_btsocket_l2cap_rt, rt, next);
1666107120Sjulian
1667107120Sjulian			NG_HOOK_SET_PRIVATE(hook, rt);
1668107120Sjulian		}
1669114878Sjulian
1670281198Stakawata		bcopy(&ep->addr, &rt->src, sizeof(rt->src));
1671107120Sjulian		rt->hook = hook;
1672107120Sjulian
1673114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_rt_mtx);
1674114878Sjulian
1675107120Sjulian		NG_BTSOCKET_L2CAP_INFO(
1676107120Sjulian"%s: Updating hook \"%s\", src bdaddr=%x:%x:%x:%x:%x:%x\n",
1677107120Sjulian			__func__, NG_HOOK_NAME(hook),
1678107120Sjulian			rt->src.b[5], rt->src.b[4], rt->src.b[3],
1679107120Sjulian			rt->src.b[2], rt->src.b[1], rt->src.b[0]);
1680107120Sjulian		} break;
1681107120Sjulian
1682107120Sjulian	default:
1683107120Sjulian		NG_BTSOCKET_L2CAP_WARN(
1684107120Sjulian"%s: Unknown message, cmd=%d\n", __func__, msg->header.cmd);
1685107120Sjulian		break;
1686107120Sjulian	}
1687107120Sjulian
1688107120Sjulian	NG_FREE_MSG(msg); /* Checks for msg != NULL */
1689107120Sjulian} /* ng_btsocket_l2cap_default_msg_input */
1690107120Sjulian
1691107120Sjulian/*
1692107120Sjulian * L2CAP sockets L2CA message input routine
1693107120Sjulian */
1694107120Sjulian
1695107120Sjulianstatic void
1696107120Sjulianng_btsocket_l2cap_l2ca_msg_input(struct ng_mesg *msg, hook_p hook)
1697107120Sjulian{
1698161623Semax	ng_btsocket_l2cap_rtentry_p	rt = NULL;
1699107120Sjulian
1700107120Sjulian	if (hook == NULL) {
1701107120Sjulian		NG_BTSOCKET_L2CAP_ALERT(
1702107120Sjulian"%s: Invalid source hook for L2CA message\n", __func__);
1703107120Sjulian		goto drop;
1704107120Sjulian	}
1705107120Sjulian
1706107120Sjulian	rt = (ng_btsocket_l2cap_rtentry_p) NG_HOOK_PRIVATE(hook);
1707107120Sjulian	if (rt == NULL) {
1708107120Sjulian		NG_BTSOCKET_L2CAP_ALERT(
1709107120Sjulian"%s: Could not find out source bdaddr for L2CA message\n", __func__);
1710107120Sjulian		goto drop;
1711107120Sjulian	}
1712107120Sjulian
1713107120Sjulian	switch (msg->header.cmd) {
1714107120Sjulian	case NGM_L2CAP_L2CA_CON: /* L2CA_Connect response */
1715161623Semax		ng_btsocket_l2cap_process_l2ca_con_req_rsp(msg, rt);
1716107120Sjulian		break;
1717107120Sjulian
1718107120Sjulian	case NGM_L2CAP_L2CA_CON_RSP: /* L2CA_ConnectRsp response */
1719161623Semax		ng_btsocket_l2cap_process_l2ca_con_rsp_rsp(msg, rt);
1720107120Sjulian		break;
1721107120Sjulian
1722107120Sjulian	case NGM_L2CAP_L2CA_CON_IND: /* L2CA_Connect indicator */
1723161623Semax		ng_btsocket_l2cap_process_l2ca_con_ind(msg, rt);
1724107120Sjulian		break;
1725107120Sjulian
1726107120Sjulian	case NGM_L2CAP_L2CA_CFG: /* L2CA_Config response */
1727161623Semax		ng_btsocket_l2cap_process_l2ca_cfg_req_rsp(msg, rt);
1728107120Sjulian		break;
1729107120Sjulian
1730107120Sjulian	case NGM_L2CAP_L2CA_CFG_RSP: /* L2CA_ConfigRsp response */
1731161623Semax		ng_btsocket_l2cap_process_l2ca_cfg_rsp_rsp(msg, rt);
1732107120Sjulian		break;
1733107120Sjulian
1734107120Sjulian	case NGM_L2CAP_L2CA_CFG_IND: /* L2CA_Config indicator */
1735161623Semax		ng_btsocket_l2cap_process_l2ca_cfg_ind(msg, rt);
1736107120Sjulian		break;
1737107120Sjulian
1738107120Sjulian	case NGM_L2CAP_L2CA_DISCON: /* L2CA_Disconnect response */
1739161623Semax		ng_btsocket_l2cap_process_l2ca_discon_rsp(msg, rt);
1740107120Sjulian		break;
1741107120Sjulian
1742107120Sjulian	case NGM_L2CAP_L2CA_DISCON_IND: /* L2CA_Disconnect indicator */
1743161623Semax		ng_btsocket_l2cap_process_l2ca_discon_ind(msg, rt);
1744107120Sjulian		break;
1745107120Sjulian
1746107120Sjulian	case NGM_L2CAP_L2CA_WRITE: /* L2CA_Write response */
1747161623Semax		ng_btsocket_l2cap_process_l2ca_write_rsp(msg, rt);
1748107120Sjulian		break;
1749290038Stakawata	case NGM_L2CAP_L2CA_ENC_CHANGE:
1750290038Stakawata		ng_btsocket_l2cap_process_l2ca_enc_change(msg, rt);
1751107120Sjulian
1752290038Stakawata		break;
1753107120Sjulian	/* XXX FIXME add other L2CA messages */
1754107120Sjulian
1755107120Sjulian	default:
1756107120Sjulian		NG_BTSOCKET_L2CAP_WARN(
1757107120Sjulian"%s: Unknown L2CA message, cmd=%d\n", __func__, msg->header.cmd);
1758107120Sjulian		break;
1759107120Sjulian	}
1760107120Sjuliandrop:
1761107120Sjulian	NG_FREE_MSG(msg);
1762107120Sjulian} /* ng_btsocket_l2cap_l2ca_msg_input */
1763107120Sjulian
1764107120Sjulian/*
1765107120Sjulian * L2CAP sockets input routine
1766107120Sjulian */
1767107120Sjulian
1768107120Sjulianstatic void
1769107120Sjulianng_btsocket_l2cap_input(void *context, int pending)
1770107120Sjulian{
1771107120Sjulian	item_p	item = NULL;
1772107120Sjulian	hook_p	hook = NULL;
1773107120Sjulian
1774107120Sjulian	for (;;) {
1775107120Sjulian		mtx_lock(&ng_btsocket_l2cap_queue_mtx);
1776107120Sjulian		NG_BT_ITEMQ_DEQUEUE(&ng_btsocket_l2cap_queue, item);
1777107120Sjulian		mtx_unlock(&ng_btsocket_l2cap_queue_mtx);
1778107120Sjulian
1779107120Sjulian		if (item == NULL)
1780107120Sjulian			break;
1781107120Sjulian
1782107120Sjulian		NGI_GET_HOOK(item, hook);
1783107120Sjulian		if (hook != NULL && NG_HOOK_NOT_VALID(hook))
1784107120Sjulian			goto drop;
1785107120Sjulian
1786107120Sjulian		switch(item->el_flags & NGQF_TYPE) {
1787107120Sjulian		case NGQF_DATA: {
1788107120Sjulian			struct mbuf     *m = NULL;
1789107120Sjulian
1790107120Sjulian			NGI_GET_M(item, m);
1791107120Sjulian			ng_btsocket_l2cap_data_input(m, hook);
1792107120Sjulian			} break;
1793107120Sjulian
1794107120Sjulian		case NGQF_MESG: {
1795107120Sjulian			struct ng_mesg  *msg = NULL;
1796107120Sjulian
1797107120Sjulian			NGI_GET_MSG(item, msg);
1798107120Sjulian
1799107120Sjulian			switch (msg->header.cmd) {
1800107120Sjulian			case NGM_L2CAP_L2CA_CON:
1801107120Sjulian			case NGM_L2CAP_L2CA_CON_RSP:
1802107120Sjulian			case NGM_L2CAP_L2CA_CON_IND:
1803107120Sjulian			case NGM_L2CAP_L2CA_CFG:
1804107120Sjulian			case NGM_L2CAP_L2CA_CFG_RSP:
1805107120Sjulian			case NGM_L2CAP_L2CA_CFG_IND:
1806107120Sjulian			case NGM_L2CAP_L2CA_DISCON:
1807107120Sjulian			case NGM_L2CAP_L2CA_DISCON_IND:
1808107120Sjulian			case NGM_L2CAP_L2CA_WRITE:
1809290038Stakawata			case NGM_L2CAP_L2CA_ENC_CHANGE:
1810107120Sjulian			/* XXX FIXME add other L2CA messages */
1811107120Sjulian				ng_btsocket_l2cap_l2ca_msg_input(msg, hook);
1812107120Sjulian				break;
1813107120Sjulian
1814107120Sjulian			default:
1815107120Sjulian				ng_btsocket_l2cap_default_msg_input(msg, hook);
1816107120Sjulian				break;
1817107120Sjulian			}
1818107120Sjulian			} break;
1819107120Sjulian
1820107120Sjulian		default:
1821107120Sjulian			KASSERT(0,
1822107120Sjulian("%s: invalid item type=%ld\n", __func__, (item->el_flags & NGQF_TYPE)));
1823107120Sjulian			break;
1824107120Sjulian		}
1825107120Sjuliandrop:
1826107120Sjulian		if (hook != NULL)
1827107120Sjulian			NG_HOOK_UNREF(hook);
1828107120Sjulian
1829107120Sjulian		NG_FREE_ITEM(item);
1830107120Sjulian	}
1831107120Sjulian} /* ng_btsocket_l2cap_input */
1832107120Sjulian
1833107120Sjulian/*
1834107120Sjulian * Route cleanup task. Gets scheduled when hook is disconnected. Here we
1835107120Sjulian * will find all sockets that use "invalid" hook and disconnect them.
1836107120Sjulian */
1837107120Sjulian
1838107120Sjulianstatic void
1839107120Sjulianng_btsocket_l2cap_rtclean(void *context, int pending)
1840107120Sjulian{
1841114878Sjulian	ng_btsocket_l2cap_pcb_p		pcb = NULL, pcb_next = NULL;
1842107120Sjulian	ng_btsocket_l2cap_rtentry_p	rt = NULL;
1843107120Sjulian
1844107120Sjulian	mtx_lock(&ng_btsocket_l2cap_rt_mtx);
1845107120Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
1846107120Sjulian
1847107120Sjulian	/*
1848107120Sjulian	 * First disconnect all sockets that use "invalid" hook
1849107120Sjulian	 */
1850107120Sjulian
1851114878Sjulian	for (pcb = LIST_FIRST(&ng_btsocket_l2cap_sockets); pcb != NULL; ) {
1852107120Sjulian		mtx_lock(&pcb->pcb_mtx);
1853114878Sjulian		pcb_next = LIST_NEXT(pcb, next);
1854107120Sjulian
1855107120Sjulian		if (pcb->rt != NULL &&
1856107120Sjulian		    pcb->rt->hook != NULL && NG_HOOK_NOT_VALID(pcb->rt->hook)) {
1857107120Sjulian			if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO)
1858107120Sjulian				ng_btsocket_l2cap_untimeout(pcb);
1859107120Sjulian
1860107120Sjulian			pcb->so->so_error = ENETDOWN;
1861107120Sjulian			pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
1862107120Sjulian			soisdisconnected(pcb->so);
1863107120Sjulian
1864107120Sjulian			pcb->token = 0;
1865114878Sjulian			pcb->cid = 0;
1866107120Sjulian			pcb->rt = NULL;
1867107120Sjulian		}
1868107120Sjulian
1869107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
1870114878Sjulian		pcb = pcb_next;
1871107120Sjulian	}
1872107120Sjulian
1873107120Sjulian	/*
1874107120Sjulian	 * Now cleanup routing table
1875107120Sjulian	 */
1876107120Sjulian
1877114878Sjulian	for (rt = LIST_FIRST(&ng_btsocket_l2cap_rt); rt != NULL; ) {
1878107120Sjulian		ng_btsocket_l2cap_rtentry_p	rt_next = LIST_NEXT(rt, next);
1879107120Sjulian
1880107120Sjulian		if (rt->hook != NULL && NG_HOOK_NOT_VALID(rt->hook)) {
1881107120Sjulian			LIST_REMOVE(rt, next);
1882107120Sjulian
1883107120Sjulian			NG_HOOK_SET_PRIVATE(rt->hook, NULL);
1884107120Sjulian			NG_HOOK_UNREF(rt->hook); /* Remove extra reference */
1885107120Sjulian
1886107120Sjulian			bzero(rt, sizeof(*rt));
1887184205Sdes			free(rt, M_NETGRAPH_BTSOCKET_L2CAP);
1888107120Sjulian		}
1889107120Sjulian
1890107120Sjulian		rt = rt_next;
1891107120Sjulian	}
1892107120Sjulian
1893107120Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
1894107120Sjulian	mtx_unlock(&ng_btsocket_l2cap_rt_mtx);
1895107120Sjulian} /* ng_btsocket_l2cap_rtclean */
1896107120Sjulian
1897107120Sjulian/*
1898107120Sjulian * Initialize everything
1899107120Sjulian */
1900107120Sjulian
1901107120Sjulianvoid
1902107120Sjulianng_btsocket_l2cap_init(void)
1903107120Sjulian{
1904107120Sjulian	int	error = 0;
1905107120Sjulian
1906267336Strociny	/* Skip initialization of globals for non-default instances. */
1907267336Strociny	if (!IS_DEFAULT_VNET(curvnet))
1908267336Strociny		return;
1909267336Strociny
1910107120Sjulian	ng_btsocket_l2cap_node = NULL;
1911107120Sjulian	ng_btsocket_l2cap_debug_level = NG_BTSOCKET_WARN_LEVEL;
1912107120Sjulian
1913107120Sjulian	/* Register Netgraph node type */
1914107120Sjulian	error = ng_newtype(&typestruct);
1915107120Sjulian	if (error != 0) {
1916107120Sjulian		NG_BTSOCKET_L2CAP_ALERT(
1917107120Sjulian"%s: Could not register Netgraph node type, error=%d\n", __func__, error);
1918107120Sjulian
1919107120Sjulian                return;
1920107120Sjulian	}
1921107120Sjulian
1922107120Sjulian	/* Create Netgrapg node */
1923107120Sjulian	error = ng_make_node_common(&typestruct, &ng_btsocket_l2cap_node);
1924107120Sjulian	if (error != 0) {
1925107120Sjulian		NG_BTSOCKET_L2CAP_ALERT(
1926107120Sjulian"%s: Could not create Netgraph node, error=%d\n", __func__, error);
1927107120Sjulian
1928107120Sjulian		ng_btsocket_l2cap_node = NULL;
1929107120Sjulian
1930107120Sjulian		return;
1931107120Sjulian	}
1932107120Sjulian
1933107120Sjulian	error = ng_name_node(ng_btsocket_l2cap_node,
1934107120Sjulian				NG_BTSOCKET_L2CAP_NODE_TYPE);
1935107120Sjulian	if (error != 0) {
1936107120Sjulian		NG_BTSOCKET_L2CAP_ALERT(
1937107120Sjulian"%s: Could not name Netgraph node, error=%d\n", __func__, error);
1938107120Sjulian
1939107120Sjulian		NG_NODE_UNREF(ng_btsocket_l2cap_node);
1940107120Sjulian		ng_btsocket_l2cap_node = NULL;
1941107120Sjulian
1942107120Sjulian		return;
1943107120Sjulian	}
1944107120Sjulian
1945107120Sjulian	/* Create input queue */
1946107120Sjulian	NG_BT_ITEMQ_INIT(&ng_btsocket_l2cap_queue, ifqmaxlen);
1947107120Sjulian	mtx_init(&ng_btsocket_l2cap_queue_mtx,
1948107120Sjulian		"btsocks_l2cap_queue_mtx", NULL, MTX_DEF);
1949107120Sjulian	TASK_INIT(&ng_btsocket_l2cap_queue_task, 0,
1950107120Sjulian		ng_btsocket_l2cap_input, NULL);
1951107120Sjulian
1952107120Sjulian	/* Create list of sockets */
1953107120Sjulian	LIST_INIT(&ng_btsocket_l2cap_sockets);
1954107120Sjulian	mtx_init(&ng_btsocket_l2cap_sockets_mtx,
1955107120Sjulian		"btsocks_l2cap_sockets_mtx", NULL, MTX_DEF);
1956107120Sjulian
1957107120Sjulian	/* Routing table */
1958107120Sjulian	LIST_INIT(&ng_btsocket_l2cap_rt);
1959107120Sjulian	mtx_init(&ng_btsocket_l2cap_rt_mtx,
1960107120Sjulian		"btsocks_l2cap_rt_mtx", NULL, MTX_DEF);
1961107120Sjulian	TASK_INIT(&ng_btsocket_l2cap_rt_task, 0,
1962107120Sjulian		ng_btsocket_l2cap_rtclean, NULL);
1963107120Sjulian} /* ng_btsocket_l2cap_init */
1964107120Sjulian
1965107120Sjulian/*
1966107120Sjulian * Abort connection on socket
1967107120Sjulian */
1968107120Sjulian
1969157366Srwatsonvoid
1970107120Sjulianng_btsocket_l2cap_abort(struct socket *so)
1971107120Sjulian{
1972107120Sjulian	so->so_error = ECONNABORTED;
1973107120Sjulian
1974160549Srwatson	(void)ng_btsocket_l2cap_disconnect(so);
1975107120Sjulian} /* ng_btsocket_l2cap_abort */
1976107120Sjulian
1977160549Srwatsonvoid
1978160549Srwatsonng_btsocket_l2cap_close(struct socket *so)
1979160549Srwatson{
1980160549Srwatson
1981160549Srwatson	(void)ng_btsocket_l2cap_disconnect(so);
1982160549Srwatson} /* ng_btsocket_l2cap_close */
1983160549Srwatson
1984107120Sjulian/*
1985107120Sjulian * Accept connection on socket. Nothing to do here, socket must be connected
1986107120Sjulian * and ready, so just return peer address and be done with it.
1987107120Sjulian */
1988107120Sjulian
1989107120Sjulianint
1990107120Sjulianng_btsocket_l2cap_accept(struct socket *so, struct sockaddr **nam)
1991107120Sjulian{
1992107120Sjulian	if (ng_btsocket_l2cap_node == NULL)
1993107120Sjulian		return (EINVAL);
1994107120Sjulian
1995107120Sjulian	return (ng_btsocket_l2cap_peeraddr(so, nam));
1996107120Sjulian} /* ng_btsocket_l2cap_accept */
1997107120Sjulian
1998107120Sjulian/*
1999107120Sjulian * Create and attach new socket
2000107120Sjulian */
2001107120Sjulian
2002107120Sjulianint
2003107120Sjulianng_btsocket_l2cap_attach(struct socket *so, int proto, struct thread *td)
2004107120Sjulian{
2005114878Sjulian	static u_int32_t	token = 0;
2006107120Sjulian	ng_btsocket_l2cap_pcb_p	pcb = so2l2cap_pcb(so);
2007107120Sjulian	int			error;
2008107120Sjulian
2009107120Sjulian	/* Check socket and protocol */
2010107120Sjulian	if (ng_btsocket_l2cap_node == NULL)
2011107120Sjulian		return (EPROTONOSUPPORT);
2012107120Sjulian	if (so->so_type != SOCK_SEQPACKET)
2013107120Sjulian		return (ESOCKTNOSUPPORT);
2014107120Sjulian
2015107120Sjulian#if 0 /* XXX sonewconn() calls "pru_attach" with proto == 0 */
2016107120Sjulian	if (proto != 0)
2017107120Sjulian		if (proto != BLUETOOTH_PROTO_L2CAP)
2018107120Sjulian			return (EPROTONOSUPPORT);
2019107120Sjulian#endif /* XXX */
2020107120Sjulian
2021107120Sjulian	if (pcb != NULL)
2022107120Sjulian		return (EISCONN);
2023107120Sjulian
2024107120Sjulian	/* Reserve send and receive space if it is not reserved yet */
2025107120Sjulian	if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) {
2026107120Sjulian		error = soreserve(so, NG_BTSOCKET_L2CAP_SENDSPACE,
2027107120Sjulian					NG_BTSOCKET_L2CAP_RECVSPACE);
2028107120Sjulian		if (error != 0)
2029107120Sjulian			return (error);
2030107120Sjulian	}
2031107120Sjulian
2032107120Sjulian	/* Allocate the PCB */
2033184205Sdes        pcb = malloc(sizeof(*pcb),
2034107120Sjulian		M_NETGRAPH_BTSOCKET_L2CAP, M_NOWAIT | M_ZERO);
2035107120Sjulian        if (pcb == NULL)
2036107120Sjulian                return (ENOMEM);
2037107120Sjulian
2038107120Sjulian	/* Link the PCB and the socket */
2039107120Sjulian	so->so_pcb = (caddr_t) pcb;
2040107120Sjulian	pcb->so = so;
2041107120Sjulian	pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
2042107120Sjulian
2043107120Sjulian	/* Initialize PCB */
2044107120Sjulian	pcb->imtu = pcb->omtu = NG_L2CAP_MTU_DEFAULT;
2045107120Sjulian
2046107120Sjulian	/* Default flow */
2047107120Sjulian	pcb->iflow.flags = 0x0;
2048107120Sjulian	pcb->iflow.service_type = NG_HCI_SERVICE_TYPE_BEST_EFFORT;
2049107120Sjulian	pcb->iflow.token_rate = 0xffffffff; /* maximum */
2050107120Sjulian	pcb->iflow.token_bucket_size = 0xffffffff; /* maximum */
2051107120Sjulian	pcb->iflow.peak_bandwidth = 0x00000000; /* maximum */
2052107120Sjulian	pcb->iflow.latency = 0xffffffff; /* don't care */
2053107120Sjulian	pcb->iflow.delay_variation = 0xffffffff; /* don't care */
2054107120Sjulian
2055107120Sjulian	bcopy(&pcb->iflow, &pcb->oflow, sizeof(pcb->oflow));
2056107120Sjulian
2057107120Sjulian	pcb->flush_timo = NG_L2CAP_FLUSH_TIMO_DEFAULT;
2058107120Sjulian	pcb->link_timo = NG_L2CAP_LINK_TIMO_DEFAULT;
2059107120Sjulian
2060114878Sjulian	/*
2061114878Sjulian	 * XXX Mark PCB mutex as DUPOK to prevent "duplicated lock of
2062114878Sjulian	 * the same type" message. When accepting new L2CAP connection
2063114878Sjulian	 * ng_btsocket_l2cap_process_l2ca_con_ind() holds both PCB mutexes
2064114878Sjulian	 * for "old" (accepting) PCB and "new" (created) PCB.
2065114878Sjulian	 */
2066114878Sjulian
2067114878Sjulian	mtx_init(&pcb->pcb_mtx, "btsocks_l2cap_pcb_mtx", NULL,
2068114878Sjulian		MTX_DEF|MTX_DUPOK);
2069271908Sjhb	callout_init_mtx(&pcb->timo, &pcb->pcb_mtx, 0);
2070114878Sjulian
2071114878Sjulian        /*
2072114878Sjulian	 * Add the PCB to the list
2073114878Sjulian	 *
2074114878Sjulian	 * XXX FIXME VERY IMPORTANT!
2075114878Sjulian	 *
2076114878Sjulian	 * This is totally FUBAR. We could get here in two cases:
2077114878Sjulian	 *
2078114878Sjulian	 * 1) When user calls socket()
2079298813Spfg	 * 2) When we need to accept new incoming connection and call
2080114878Sjulian	 *    sonewconn()
2081114878Sjulian	 *
2082170035Srwatson	 * In the first case we must acquire ng_btsocket_l2cap_sockets_mtx.
2083114878Sjulian	 * In the second case we hold ng_btsocket_l2cap_sockets_mtx already.
2084114878Sjulian	 * So we now need to distinguish between these cases. From reading
2085167907Smaxim	 * /sys/kern/uipc_socket.c we can find out that sonewconn() calls
2086114878Sjulian	 * pru_attach with proto == 0 and td == NULL. For now use this fact
2087114878Sjulian	 * to figure out if we were called from socket() or from sonewconn().
2088114878Sjulian	 */
2089114878Sjulian
2090114878Sjulian	if (td != NULL)
2091114878Sjulian		mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
2092114878Sjulian	else
2093114878Sjulian		mtx_assert(&ng_btsocket_l2cap_sockets_mtx, MA_OWNED);
2094114878Sjulian
2095114878Sjulian	/* Set PCB token. Use ng_btsocket_l2cap_sockets_mtx for protection */
2096114878Sjulian	if (++ token == 0)
2097114878Sjulian		token ++;
2098114878Sjulian
2099114878Sjulian	pcb->token = token;
2100114878Sjulian
2101107120Sjulian	LIST_INSERT_HEAD(&ng_btsocket_l2cap_sockets, pcb, next);
2102107120Sjulian
2103114878Sjulian	if (td != NULL)
2104114878Sjulian		mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
2105114878Sjulian
2106107120Sjulian        return (0);
2107107120Sjulian} /* ng_btsocket_l2cap_attach */
2108107120Sjulian
2109107120Sjulian/*
2110107120Sjulian * Bind socket
2111107120Sjulian */
2112107120Sjulian
2113107120Sjulianint
2114107120Sjulianng_btsocket_l2cap_bind(struct socket *so, struct sockaddr *nam,
2115107120Sjulian		struct thread *td)
2116107120Sjulian{
2117107120Sjulian	ng_btsocket_l2cap_pcb_t	*pcb = NULL;
2118107120Sjulian	struct sockaddr_l2cap	*sa = (struct sockaddr_l2cap *) nam;
2119107120Sjulian	int			 psm, error = 0;
2120107120Sjulian
2121107120Sjulian	if (ng_btsocket_l2cap_node == NULL)
2122107120Sjulian		return (EINVAL);
2123107120Sjulian
2124107120Sjulian	/* Verify address */
2125107120Sjulian	if (sa == NULL)
2126107120Sjulian		return (EINVAL);
2127107120Sjulian	if (sa->l2cap_family != AF_BLUETOOTH)
2128107120Sjulian		return (EAFNOSUPPORT);
2129281198Stakawata	/*For the time being, Not support LE binding.*/
2130281198Stakawata	if ((sa->l2cap_len != sizeof(*sa))&&
2131281198Stakawata	    (sa->l2cap_len != sizeof(struct sockaddr_l2cap_compat)))
2132107120Sjulian		return (EINVAL);
2133107120Sjulian
2134107120Sjulian	psm = le16toh(sa->l2cap_psm);
2135107120Sjulian
2136107120Sjulian	/*
2137107120Sjulian	 * Check if other socket has this address already (look for exact
2138107120Sjulian	 * match PSM and bdaddr) and assign socket address if it's available.
2139107120Sjulian	 *
2140107120Sjulian	 * Note: socket can be bound to ANY PSM (zero) thus allowing several
2141107120Sjulian	 * channels with the same PSM between the same pair of BD_ADDR'es.
2142107120Sjulian	 */
2143107120Sjulian
2144107120Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
2145107120Sjulian
2146107120Sjulian	LIST_FOREACH(pcb, &ng_btsocket_l2cap_sockets, next)
2147107120Sjulian		if (psm != 0 && psm == pcb->psm &&
2148107120Sjulian		    bcmp(&pcb->src, &sa->l2cap_bdaddr, sizeof(bdaddr_t)) == 0)
2149107120Sjulian			break;
2150107120Sjulian
2151107120Sjulian	if (pcb == NULL) {
2152107120Sjulian		/* Set socket address */
2153107120Sjulian		pcb = so2l2cap_pcb(so);
2154107120Sjulian		if (pcb != NULL) {
2155107120Sjulian			bcopy(&sa->l2cap_bdaddr, &pcb->src, sizeof(pcb->src));
2156107120Sjulian			pcb->psm = psm;
2157107120Sjulian		} else
2158107120Sjulian			error = EINVAL;
2159107120Sjulian	} else
2160107120Sjulian		error = EADDRINUSE;
2161107120Sjulian
2162107120Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
2163107120Sjulian
2164107120Sjulian	return (error);
2165107120Sjulian} /* ng_btsocket_l2cap_bind */
2166107120Sjulian
2167107120Sjulian/*
2168107120Sjulian * Connect socket
2169107120Sjulian */
2170107120Sjulian
2171107120Sjulianint
2172107120Sjulianng_btsocket_l2cap_connect(struct socket *so, struct sockaddr *nam,
2173107120Sjulian		struct thread *td)
2174107120Sjulian{
2175107120Sjulian	ng_btsocket_l2cap_pcb_t		*pcb = so2l2cap_pcb(so);
2176281198Stakawata	struct sockaddr_l2cap_compat	*sal = (struct sockaddr_l2cap_compat *) nam;
2177281198Stakawata	struct sockaddr_l2cap *sa  = (struct sockaddr_l2cap *)nam;
2178281198Stakawata	struct sockaddr_l2cap  ba;
2179107120Sjulian	ng_btsocket_l2cap_rtentry_t	*rt = NULL;
2180107120Sjulian	int				 have_src, error = 0;
2181290038Stakawata	int idtype = NG_L2CAP_L2CA_IDTYPE_BREDR;
2182107120Sjulian	/* Check socket */
2183107120Sjulian	if (pcb == NULL)
2184107120Sjulian		return (EINVAL);
2185107120Sjulian	if (ng_btsocket_l2cap_node == NULL)
2186107120Sjulian		return (EINVAL);
2187107120Sjulian	if (pcb->state == NG_BTSOCKET_L2CAP_CONNECTING)
2188107120Sjulian		return (EINPROGRESS);
2189107120Sjulian
2190107120Sjulian	/* Verify address */
2191107120Sjulian	if (sa == NULL)
2192107120Sjulian		return (EINVAL);
2193107120Sjulian	if (sa->l2cap_family != AF_BLUETOOTH)
2194107120Sjulian		return (EAFNOSUPPORT);
2195281198Stakawata	if (sa->l2cap_len == sizeof(*sal)){
2196281198Stakawata		bcopy(sal, &ba, sizeof(*sal));
2197281198Stakawata		sa = &ba;
2198281198Stakawata		sa->l2cap_len = sizeof(*sa);
2199281198Stakawata		sa->l2cap_bdaddr_type = BDADDR_BREDR;
2200281198Stakawata	}
2201107120Sjulian	if (sa->l2cap_len != sizeof(*sa))
2202107120Sjulian		return (EINVAL);
2203281198Stakawata	if ((sa->l2cap_psm &&  sa->l2cap_cid))
2204281198Stakawata		return EINVAL;
2205281198Stakawata	if (bcmp(&sa->l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
2206107120Sjulian		return (EDESTADDRREQ);
2207281198Stakawata	if((sa->l2cap_bdaddr_type == BDADDR_BREDR)&&
2208281198Stakawata	   (sa->l2cap_psm == 0))
2209281198Stakawata		return EDESTADDRREQ;
2210290038Stakawata	if(sa->l2cap_bdaddr_type != BDADDR_BREDR){
2211290038Stakawata		if(sa->l2cap_cid == NG_L2CAP_ATT_CID){
2212290038Stakawata			idtype = NG_L2CAP_L2CA_IDTYPE_ATT;
2213290038Stakawata		}else if (sa->l2cap_cid == NG_L2CAP_SMP_CID){
2214290038Stakawata			idtype =NG_L2CAP_L2CA_IDTYPE_SMP;
2215290038Stakawata		}else{
2216290038Stakawata			//if cid == 0 idtype = NG_L2CAP_L2CA_IDTYPE_LE;
2217290038Stakawata			// Not supported yet
2218290038Stakawata			return EINVAL;
2219290038Stakawata		}
2220281198Stakawata	}
2221107120Sjulian	if (pcb->psm != 0 && pcb->psm != le16toh(sa->l2cap_psm))
2222107120Sjulian		return (EINVAL);
2223107120Sjulian	/*
2224107120Sjulian	 * Routing. Socket should be bound to some source address. The source
2225107120Sjulian	 * address can be ANY. Destination address must be set and it must not
2226107120Sjulian	 * be ANY. If source address is ANY then find first rtentry that has
2227107120Sjulian	 * src != dst.
2228107120Sjulian	 */
2229107120Sjulian
2230114878Sjulian	mtx_lock(&ng_btsocket_l2cap_rt_mtx);
2231114878Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
2232114878Sjulian	mtx_lock(&pcb->pcb_mtx);
2233114878Sjulian
2234114878Sjulian	/* Send destination address and PSM */
2235114878Sjulian	bcopy(&sa->l2cap_bdaddr, &pcb->dst, sizeof(pcb->dst));
2236114878Sjulian	pcb->psm = le16toh(sa->l2cap_psm);
2237281198Stakawata	pcb->dsttype = sa->l2cap_bdaddr_type;
2238290038Stakawata	pcb->cid = 0;
2239290038Stakawata	pcb->idtype = idtype;
2240107120Sjulian	pcb->rt = NULL;
2241107120Sjulian	have_src = bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src));
2242107120Sjulian
2243107120Sjulian	LIST_FOREACH(rt, &ng_btsocket_l2cap_rt, next) {
2244107120Sjulian		if (rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))
2245107120Sjulian			continue;
2246107120Sjulian
2247107120Sjulian		/* Match src and dst */
2248107120Sjulian		if (have_src) {
2249107120Sjulian			if (bcmp(&pcb->src, &rt->src, sizeof(rt->src)) == 0)
2250107120Sjulian				break;
2251107120Sjulian		} else {
2252107120Sjulian			if (bcmp(&pcb->dst, &rt->src, sizeof(rt->src)) != 0)
2253107120Sjulian				break;
2254107120Sjulian		}
2255107120Sjulian	}
2256107120Sjulian
2257107120Sjulian	if (rt != NULL) {
2258107120Sjulian		pcb->rt = rt;
2259107120Sjulian
2260281198Stakawata		if (!have_src){
2261107120Sjulian			bcopy(&rt->src, &pcb->src, sizeof(pcb->src));
2262281198Stakawata			pcb->srctype =
2263281198Stakawata			  (sa->l2cap_bdaddr_type == BDADDR_BREDR)?
2264290038Stakawata			  BDADDR_BREDR : BDADDR_LE_PUBLIC;
2265281198Stakawata		}
2266107120Sjulian	} else
2267107120Sjulian		error = EHOSTUNREACH;
2268107120Sjulian
2269107120Sjulian	/*
2270107120Sjulian	 * Send L2CA_Connect request
2271107120Sjulian	 */
2272107120Sjulian
2273107120Sjulian	if (error == 0) {
2274107120Sjulian		error = ng_btsocket_l2cap_send_l2ca_con_req(pcb);
2275107120Sjulian		if (error == 0) {
2276107120Sjulian			pcb->flags |= NG_BTSOCKET_L2CAP_CLIENT;
2277107120Sjulian			pcb->state = NG_BTSOCKET_L2CAP_CONNECTING;
2278107120Sjulian			soisconnecting(pcb->so);
2279107120Sjulian
2280107120Sjulian			ng_btsocket_l2cap_timeout(pcb);
2281107120Sjulian		}
2282107120Sjulian	}
2283107120Sjulian
2284114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
2285114878Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
2286114878Sjulian	mtx_unlock(&ng_btsocket_l2cap_rt_mtx);
2287114878Sjulian
2288107120Sjulian	return (error);
2289107120Sjulian} /* ng_btsocket_l2cap_connect */
2290107120Sjulian
2291107120Sjulian/*
2292107120Sjulian * Process ioctl's calls on socket
2293107120Sjulian */
2294107120Sjulian
2295107120Sjulianint
2296107120Sjulianng_btsocket_l2cap_control(struct socket *so, u_long cmd, caddr_t data,
2297107120Sjulian		struct ifnet *ifp, struct thread *td)
2298107120Sjulian{
2299107120Sjulian	return (EINVAL);
2300107120Sjulian} /* ng_btsocket_l2cap_control */
2301107120Sjulian
2302107120Sjulian/*
2303107120Sjulian * Process getsockopt/setsockopt system calls
2304107120Sjulian */
2305107120Sjulian
2306107120Sjulianint
2307107120Sjulianng_btsocket_l2cap_ctloutput(struct socket *so, struct sockopt *sopt)
2308107120Sjulian{
2309107120Sjulian	ng_btsocket_l2cap_pcb_p	pcb = so2l2cap_pcb(so);
2310107120Sjulian	int			error = 0;
2311107120Sjulian	ng_l2cap_cfg_opt_val_t	v;
2312107120Sjulian
2313107120Sjulian	if (pcb == NULL)
2314107120Sjulian		return (EINVAL);
2315107120Sjulian	if (ng_btsocket_l2cap_node == NULL)
2316107120Sjulian		return (EINVAL);
2317107120Sjulian
2318107120Sjulian	if (sopt->sopt_level != SOL_L2CAP)
2319107120Sjulian		return (0);
2320107120Sjulian
2321107120Sjulian	mtx_lock(&pcb->pcb_mtx);
2322107120Sjulian
2323107120Sjulian	switch (sopt->sopt_dir) {
2324107120Sjulian	case SOPT_GET:
2325107120Sjulian		switch (sopt->sopt_name) {
2326107120Sjulian		case SO_L2CAP_IMTU: /* get incoming MTU */
2327107120Sjulian			error = sooptcopyout(sopt, &pcb->imtu,
2328107120Sjulian						sizeof(pcb->imtu));
2329107120Sjulian			break;
2330107120Sjulian
2331107120Sjulian		case SO_L2CAP_OMTU: /* get outgoing (peer incoming) MTU */
2332107120Sjulian			error = sooptcopyout(sopt, &pcb->omtu,
2333107120Sjulian						sizeof(pcb->omtu));
2334107120Sjulian			break;
2335107120Sjulian
2336107120Sjulian		case SO_L2CAP_IFLOW: /* get incoming flow spec. */
2337107120Sjulian			error = sooptcopyout(sopt, &pcb->iflow,
2338107120Sjulian						sizeof(pcb->iflow));
2339107120Sjulian			break;
2340107120Sjulian
2341107120Sjulian		case SO_L2CAP_OFLOW: /* get outgoing flow spec. */
2342107120Sjulian			error = sooptcopyout(sopt, &pcb->oflow,
2343107120Sjulian						sizeof(pcb->oflow));
2344107120Sjulian			break;
2345107120Sjulian
2346107120Sjulian		case SO_L2CAP_FLUSH: /* get flush timeout */
2347107120Sjulian			error = sooptcopyout(sopt, &pcb->flush_timo,
2348107120Sjulian						sizeof(pcb->flush_timo));
2349107120Sjulian			break;
2350290038Stakawata		case SO_L2CAP_ENCRYPTED: /* get encrypt required */
2351290038Stakawata			error = sooptcopyout(sopt, &pcb->need_encrypt,
2352290038Stakawata						sizeof(pcb->need_encrypt));
2353290038Stakawata			break;
2354107120Sjulian
2355290038Stakawata
2356107120Sjulian		default:
2357107120Sjulian			error = ENOPROTOOPT;
2358107120Sjulian			break;
2359107120Sjulian		}
2360107120Sjulian		break;
2361107120Sjulian
2362107120Sjulian	case SOPT_SET:
2363107120Sjulian		/*
2364114878Sjulian		 * XXX
2365114878Sjulian		 * We do not allow to change these parameters while socket is
2366114878Sjulian		 * connected or we are in the process of creating a connection.
2367114878Sjulian		 * May be this should indicate re-configuration of the open
2368114878Sjulian		 * channel?
2369107120Sjulian		 */
2370107120Sjulian
2371173231Semax		if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED) {
2372173231Semax			error = EACCES;
2373173231Semax			break;
2374173231Semax		}
2375107120Sjulian
2376107120Sjulian		switch (sopt->sopt_name) {
2377107120Sjulian		case SO_L2CAP_IMTU: /* set incoming MTU */
2378107120Sjulian			error = sooptcopyin(sopt, &v, sizeof(v), sizeof(v.mtu));
2379107120Sjulian			if (error == 0)
2380107120Sjulian				pcb->imtu = v.mtu;
2381107120Sjulian			break;
2382107120Sjulian
2383107120Sjulian		case SO_L2CAP_OFLOW: /* set outgoing flow spec. */
2384107120Sjulian			error = sooptcopyin(sopt, &v, sizeof(v),sizeof(v.flow));
2385107120Sjulian			if (error == 0)
2386107120Sjulian				bcopy(&v.flow, &pcb->oflow, sizeof(pcb->oflow));
2387107120Sjulian			break;
2388107120Sjulian
2389107120Sjulian		case SO_L2CAP_FLUSH: /* set flush timeout */
2390107120Sjulian			error = sooptcopyin(sopt, &v, sizeof(v),
2391107120Sjulian						sizeof(v.flush_timo));
2392107120Sjulian			if (error == 0)
2393107120Sjulian				pcb->flush_timo = v.flush_timo;
2394107120Sjulian			break;
2395290038Stakawata		case SO_L2CAP_ENCRYPTED: /*set connect encryption opt*/
2396290038Stakawata			if((pcb->state != NG_BTSOCKET_L2CAP_OPEN) &&
2397290038Stakawata			   (pcb->state != NG_BTSOCKET_L2CAP_W4_ENC_CHANGE)){
2398290038Stakawata				error = sooptcopyin(sopt, &v, sizeof(v),
2399290038Stakawata						    sizeof(v.encryption));
2400290038Stakawata				if(error == 0)
2401290038Stakawata					pcb->need_encrypt = (v.encryption)?1:0;
2402290038Stakawata			}else{
2403290038Stakawata				error = EINVAL;
2404290038Stakawata			}
2405290038Stakawata			break;
2406107120Sjulian		default:
2407107120Sjulian			error = ENOPROTOOPT;
2408107120Sjulian			break;
2409107120Sjulian		}
2410107120Sjulian		break;
2411107120Sjulian
2412107120Sjulian	default:
2413107120Sjulian		error = EINVAL;
2414107120Sjulian		break;
2415107120Sjulian	}
2416107120Sjulian
2417107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
2418107120Sjulian
2419107120Sjulian	return (error);
2420107120Sjulian} /* ng_btsocket_l2cap_ctloutput */
2421107120Sjulian
2422107120Sjulian/*
2423107120Sjulian * Detach and destroy socket
2424107120Sjulian */
2425107120Sjulian
2426157370Srwatsonvoid
2427107120Sjulianng_btsocket_l2cap_detach(struct socket *so)
2428107120Sjulian{
2429107120Sjulian	ng_btsocket_l2cap_pcb_p	pcb = so2l2cap_pcb(so);
2430107120Sjulian
2431157370Srwatson	KASSERT(pcb != NULL, ("ng_btsocket_l2cap_detach: pcb == NULL"));
2432157370Srwatson
2433107120Sjulian	if (ng_btsocket_l2cap_node == NULL)
2434157370Srwatson		return;
2435107120Sjulian
2436114878Sjulian	mtx_lock(&ng_btsocket_l2cap_sockets_mtx);
2437107120Sjulian	mtx_lock(&pcb->pcb_mtx);
2438107120Sjulian
2439107120Sjulian	/* XXX what to do with pending request? */
2440107120Sjulian	if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO)
2441107120Sjulian		ng_btsocket_l2cap_untimeout(pcb);
2442107120Sjulian
2443107120Sjulian	if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED &&
2444107120Sjulian	    pcb->state != NG_BTSOCKET_L2CAP_DISCONNECTING)
2445107120Sjulian		/* Send disconnect request with "zero" token */
2446107120Sjulian		ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
2447107120Sjulian
2448107120Sjulian	pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
2449107120Sjulian
2450114878Sjulian	LIST_REMOVE(pcb, next);
2451107120Sjulian
2452107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
2453107120Sjulian	mtx_unlock(&ng_btsocket_l2cap_sockets_mtx);
2454107120Sjulian
2455107120Sjulian	mtx_destroy(&pcb->pcb_mtx);
2456107120Sjulian	bzero(pcb, sizeof(*pcb));
2457184205Sdes	free(pcb, M_NETGRAPH_BTSOCKET_L2CAP);
2458107120Sjulian
2459114878Sjulian	soisdisconnected(so);
2460114878Sjulian	so->so_pcb = NULL;
2461107120Sjulian} /* ng_btsocket_l2cap_detach */
2462107120Sjulian
2463107120Sjulian/*
2464107120Sjulian * Disconnect socket
2465107120Sjulian */
2466107120Sjulian
2467107120Sjulianint
2468107120Sjulianng_btsocket_l2cap_disconnect(struct socket *so)
2469107120Sjulian{
2470107120Sjulian	ng_btsocket_l2cap_pcb_p	pcb = so2l2cap_pcb(so);
2471107120Sjulian	int			error = 0;
2472107120Sjulian
2473107120Sjulian	if (pcb == NULL)
2474107120Sjulian		return (EINVAL);
2475107120Sjulian	if (ng_btsocket_l2cap_node == NULL)
2476107120Sjulian		return (EINVAL);
2477107120Sjulian
2478107120Sjulian	mtx_lock(&pcb->pcb_mtx);
2479107120Sjulian
2480107120Sjulian	if (pcb->state == NG_BTSOCKET_L2CAP_DISCONNECTING) {
2481107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
2482107120Sjulian		return (EINPROGRESS);
2483107120Sjulian	}
2484107120Sjulian
2485107120Sjulian	if (pcb->state != NG_BTSOCKET_L2CAP_CLOSED) {
2486107120Sjulian		/* XXX FIXME what to do with pending request? */
2487107120Sjulian		if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO)
2488107120Sjulian			ng_btsocket_l2cap_untimeout(pcb);
2489107120Sjulian
2490107120Sjulian		error = ng_btsocket_l2cap_send_l2ca_discon_req(pcb->token, pcb);
2491107120Sjulian		if (error == 0) {
2492107120Sjulian			pcb->state = NG_BTSOCKET_L2CAP_DISCONNECTING;
2493107120Sjulian			soisdisconnecting(so);
2494107120Sjulian
2495107120Sjulian			ng_btsocket_l2cap_timeout(pcb);
2496107120Sjulian		}
2497107120Sjulian
2498107120Sjulian		/* XXX FIXME what to do if error != 0 */
2499107120Sjulian	}
2500107120Sjulian
2501107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
2502107120Sjulian
2503107120Sjulian	return (error);
2504107120Sjulian} /* ng_btsocket_l2cap_disconnect */
2505107120Sjulian
2506107120Sjulian/*
2507107120Sjulian * Listen on socket
2508107120Sjulian */
2509107120Sjulian
2510107120Sjulianint
2511151888Srwatsonng_btsocket_l2cap_listen(struct socket *so, int backlog, struct thread *td)
2512107120Sjulian{
2513107120Sjulian	ng_btsocket_l2cap_pcb_p	pcb = so2l2cap_pcb(so);
2514142190Srwatson	int error;
2515107120Sjulian
2516142190Srwatson	SOCK_LOCK(so);
2517142190Srwatson	error = solisten_proto_check(so);
2518142190Srwatson	if (error != 0)
2519142190Srwatson		goto out;
2520142190Srwatson	if (pcb == NULL) {
2521142190Srwatson		error = EINVAL;
2522142190Srwatson		goto out;
2523142190Srwatson	}
2524142190Srwatson	if (ng_btsocket_l2cap_node == NULL) {
2525142190Srwatson		error = EINVAL;
2526142190Srwatson		goto out;
2527142190Srwatson	}
2528142190Srwatson	if (pcb->psm == 0) {
2529171937Semax		error = EADDRNOTAVAIL;
2530142190Srwatson		goto out;
2531142190Srwatson	}
2532151888Srwatson	solisten_proto(so, backlog);
2533142190Srwatsonout:
2534142190Srwatson	SOCK_UNLOCK(so);
2535142190Srwatson	return (error);
2536107120Sjulian} /* ng_btsocket_listen */
2537107120Sjulian
2538107120Sjulian/*
2539107120Sjulian * Get peer address
2540107120Sjulian */
2541107120Sjulian
2542107120Sjulianint
2543107120Sjulianng_btsocket_l2cap_peeraddr(struct socket *so, struct sockaddr **nam)
2544107120Sjulian{
2545107120Sjulian	ng_btsocket_l2cap_pcb_p	pcb = so2l2cap_pcb(so);
2546107120Sjulian	struct sockaddr_l2cap	sa;
2547107120Sjulian
2548107120Sjulian	if (pcb == NULL)
2549107120Sjulian		return (EINVAL);
2550107120Sjulian	if (ng_btsocket_l2cap_node == NULL)
2551107120Sjulian		return (EINVAL);
2552107120Sjulian
2553107120Sjulian	bcopy(&pcb->dst, &sa.l2cap_bdaddr, sizeof(sa.l2cap_bdaddr));
2554107120Sjulian	sa.l2cap_psm = htole16(pcb->psm);
2555107120Sjulian	sa.l2cap_len = sizeof(sa);
2556107120Sjulian	sa.l2cap_family = AF_BLUETOOTH;
2557290038Stakawata	switch(pcb->idtype){
2558290038Stakawata	case NG_L2CAP_L2CA_IDTYPE_ATT:
2559290038Stakawata		sa.l2cap_cid = NG_L2CAP_ATT_CID;
2560290038Stakawata		break;
2561290038Stakawata	case NG_L2CAP_L2CA_IDTYPE_SMP:
2562290038Stakawata		sa.l2cap_cid = NG_L2CAP_SMP_CID;
2563290038Stakawata		break;
2564290038Stakawata	default:
2565290038Stakawata		sa.l2cap_cid = 0;
2566290038Stakawata		break;
2567290038Stakawata	}
2568281198Stakawata	sa.l2cap_bdaddr_type = pcb->dsttype;
2569126425Srwatson	*nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT);
2570107120Sjulian
2571107120Sjulian	return ((*nam == NULL)? ENOMEM : 0);
2572107120Sjulian} /* ng_btsocket_l2cap_peeraddr */
2573107120Sjulian
2574107120Sjulian/*
2575107120Sjulian * Send data to socket
2576107120Sjulian */
2577107120Sjulian
2578107120Sjulianint
2579107120Sjulianng_btsocket_l2cap_send(struct socket *so, int flags, struct mbuf *m,
2580107120Sjulian		struct sockaddr *nam, struct mbuf *control, struct thread *td)
2581107120Sjulian{
2582107120Sjulian	ng_btsocket_l2cap_pcb_t	*pcb = so2l2cap_pcb(so);
2583107120Sjulian	int			 error = 0;
2584107120Sjulian
2585107120Sjulian	if (ng_btsocket_l2cap_node == NULL) {
2586107120Sjulian		error = ENETDOWN;
2587107120Sjulian		goto drop;
2588107120Sjulian	}
2589107120Sjulian
2590107120Sjulian	/* Check socket and input */
2591107120Sjulian	if (pcb == NULL || m == NULL || control != NULL) {
2592107120Sjulian		error = EINVAL;
2593107120Sjulian		goto drop;
2594107120Sjulian	}
2595107120Sjulian
2596107120Sjulian	mtx_lock(&pcb->pcb_mtx);
2597107120Sjulian
2598107120Sjulian	/* Make sure socket is connected */
2599107120Sjulian	if (pcb->state != NG_BTSOCKET_L2CAP_OPEN) {
2600107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
2601107120Sjulian		error = ENOTCONN;
2602107120Sjulian		goto drop;
2603107120Sjulian	}
2604107120Sjulian
2605107120Sjulian	/* Check route */
2606107120Sjulian	if (pcb->rt == NULL ||
2607107120Sjulian	    pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) {
2608107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
2609107120Sjulian		error = ENETDOWN;
2610107120Sjulian		goto drop;
2611107120Sjulian	}
2612107120Sjulian
2613298813Spfg	/* Check packet size against outgoing (peer's incoming) MTU) */
2614107120Sjulian	if (m->m_pkthdr.len > pcb->omtu) {
2615107120Sjulian		NG_BTSOCKET_L2CAP_ERR(
2616107120Sjulian"%s: Packet too big, len=%d, omtu=%d\n", __func__, m->m_pkthdr.len, pcb->omtu);
2617107120Sjulian
2618107120Sjulian		mtx_unlock(&pcb->pcb_mtx);
2619107120Sjulian		error = EMSGSIZE;
2620107120Sjulian		goto drop;
2621107120Sjulian	}
2622107120Sjulian
2623107120Sjulian	/*
2624107120Sjulian	 * First put packet on socket send queue. Then check if we have
2625107120Sjulian	 * pending timeout. If we do not have timeout then we must send
2626107120Sjulian	 * packet and schedule timeout. Otherwise do nothing and wait for
2627107120Sjulian	 * L2CA_WRITE_RSP.
2628107120Sjulian	 */
2629107120Sjulian
2630107120Sjulian	sbappendrecord(&pcb->so->so_snd, m);
2631107120Sjulian	m = NULL;
2632107120Sjulian
2633107120Sjulian	if (!(pcb->flags & NG_BTSOCKET_L2CAP_TIMO)) {
2634107120Sjulian		error = ng_btsocket_l2cap_send2(pcb);
2635107120Sjulian		if (error == 0)
2636107120Sjulian			ng_btsocket_l2cap_timeout(pcb);
2637107120Sjulian		else
2638107120Sjulian			sbdroprecord(&pcb->so->so_snd); /* XXX */
2639107120Sjulian	}
2640107120Sjulian
2641107120Sjulian	mtx_unlock(&pcb->pcb_mtx);
2642107120Sjuliandrop:
2643107120Sjulian	NG_FREE_M(m); /* checks for != NULL */
2644107120Sjulian	NG_FREE_M(control);
2645107120Sjulian
2646107120Sjulian	return (error);
2647107120Sjulian} /* ng_btsocket_l2cap_send */
2648107120Sjulian
2649107120Sjulian/*
2650107120Sjulian * Send first packet in the socket queue to the L2CAP layer
2651107120Sjulian */
2652107120Sjulian
2653107120Sjulianstatic int
2654107120Sjulianng_btsocket_l2cap_send2(ng_btsocket_l2cap_pcb_p pcb)
2655107120Sjulian{
2656107120Sjulian	struct	mbuf		*m = NULL;
2657107120Sjulian	ng_l2cap_l2ca_hdr_t	*hdr = NULL;
2658107120Sjulian	int			 error = 0;
2659107120Sjulian
2660107120Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
2661107120Sjulian
2662274421Sglebius	if (sbavail(&pcb->so->so_snd) == 0)
2663107120Sjulian		return (EINVAL); /* XXX */
2664107120Sjulian
2665243882Sglebius	m = m_dup(pcb->so->so_snd.sb_mb, M_NOWAIT);
2666107120Sjulian	if (m == NULL)
2667107120Sjulian		return (ENOBUFS);
2668107120Sjulian
2669107120Sjulian	/* Create L2CA packet header */
2670243882Sglebius	M_PREPEND(m, sizeof(*hdr), M_NOWAIT);
2671107120Sjulian	if (m != NULL)
2672107120Sjulian		if (m->m_len < sizeof(*hdr))
2673107120Sjulian			m = m_pullup(m, sizeof(*hdr));
2674107120Sjulian
2675107120Sjulian	if (m == NULL) {
2676107120Sjulian		NG_BTSOCKET_L2CAP_ERR(
2677107120Sjulian"%s: Failed to create L2CA packet header\n", __func__);
2678107120Sjulian
2679107120Sjulian		return (ENOBUFS);
2680107120Sjulian	}
2681107120Sjulian
2682107120Sjulian	hdr = mtod(m, ng_l2cap_l2ca_hdr_t *);
2683107120Sjulian	hdr->token = pcb->token;
2684107120Sjulian	hdr->length = m->m_pkthdr.len - sizeof(*hdr);
2685107120Sjulian	hdr->lcid = pcb->cid;
2686290038Stakawata	hdr->idtype = pcb->idtype;
2687107120Sjulian	NG_BTSOCKET_L2CAP_INFO(
2688107120Sjulian"%s: Sending packet: len=%d, length=%d, lcid=%d, token=%d, state=%d\n",
2689107120Sjulian		__func__, m->m_pkthdr.len, hdr->length, hdr->lcid,
2690107120Sjulian		hdr->token, pcb->state);
2691107120Sjulian
2692107120Sjulian	/*
2693298813Spfg	 * If we got here than we have successfully creates new L2CAP
2694107120Sjulian	 * data packet and now we can send it to the L2CAP layer
2695107120Sjulian	 */
2696107120Sjulian
2697107120Sjulian	NG_SEND_DATA_ONLY(error, pcb->rt->hook, m);
2698107120Sjulian
2699107120Sjulian	return (error);
2700107120Sjulian} /* ng_btsocket_l2cap_send2 */
2701107120Sjulian
2702107120Sjulian/*
2703107120Sjulian * Get socket address
2704107120Sjulian */
2705107120Sjulian
2706107120Sjulianint
2707107120Sjulianng_btsocket_l2cap_sockaddr(struct socket *so, struct sockaddr **nam)
2708107120Sjulian{
2709107120Sjulian	ng_btsocket_l2cap_pcb_p	pcb = so2l2cap_pcb(so);
2710107120Sjulian	struct sockaddr_l2cap	sa;
2711107120Sjulian
2712107120Sjulian	if (pcb == NULL)
2713107120Sjulian		return (EINVAL);
2714107120Sjulian	if (ng_btsocket_l2cap_node == NULL)
2715107120Sjulian		return (EINVAL);
2716107120Sjulian
2717107120Sjulian	bcopy(&pcb->src, &sa.l2cap_bdaddr, sizeof(sa.l2cap_bdaddr));
2718107120Sjulian	sa.l2cap_psm = htole16(pcb->psm);
2719107120Sjulian	sa.l2cap_len = sizeof(sa);
2720107120Sjulian	sa.l2cap_family = AF_BLUETOOTH;
2721281198Stakawata	sa.l2cap_cid = 0;
2722281198Stakawata	sa.l2cap_bdaddr_type = pcb->srctype;
2723107120Sjulian
2724126425Srwatson	*nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT);
2725107120Sjulian
2726107120Sjulian	return ((*nam == NULL)? ENOMEM : 0);
2727107120Sjulian} /* ng_btsocket_l2cap_sockaddr */
2728107120Sjulian
2729107120Sjulian/*****************************************************************************
2730107120Sjulian *****************************************************************************
2731107120Sjulian **                              Misc. functions
2732107120Sjulian *****************************************************************************
2733107120Sjulian *****************************************************************************/
2734107120Sjulian
2735107120Sjulian/*
2736107120Sjulian * Look for the socket that listens on given PSM and bdaddr. Returns exact or
2737114878Sjulian * close match (if any). Caller must hold ng_btsocket_l2cap_sockets_mtx.
2738107120Sjulian */
2739107120Sjulian
2740107120Sjulianstatic ng_btsocket_l2cap_pcb_p
2741107120Sjulianng_btsocket_l2cap_pcb_by_addr(bdaddr_p bdaddr, int psm)
2742107120Sjulian{
2743107120Sjulian	ng_btsocket_l2cap_pcb_p	p = NULL, p1 = NULL;
2744107120Sjulian
2745114878Sjulian	mtx_assert(&ng_btsocket_l2cap_sockets_mtx, MA_OWNED);
2746107120Sjulian
2747107120Sjulian	LIST_FOREACH(p, &ng_btsocket_l2cap_sockets, next) {
2748107120Sjulian		if (p->so == NULL || !(p->so->so_options & SO_ACCEPTCONN) ||
2749107120Sjulian		    p->psm != psm)
2750107120Sjulian			continue;
2751107120Sjulian
2752107120Sjulian		if (bcmp(&p->src, bdaddr, sizeof(p->src)) == 0)
2753107120Sjulian			break;
2754107120Sjulian
2755107120Sjulian		if (bcmp(&p->src, NG_HCI_BDADDR_ANY, sizeof(p->src)) == 0)
2756107120Sjulian			p1 = p;
2757107120Sjulian	}
2758107120Sjulian
2759107120Sjulian	return ((p != NULL)? p : p1);
2760107120Sjulian} /* ng_btsocket_l2cap_pcb_by_addr */
2761107120Sjulian
2762107120Sjulian/*
2763114878Sjulian * Look for the socket that has given token.
2764114878Sjulian * Caller must hold ng_btsocket_l2cap_sockets_mtx.
2765107120Sjulian */
2766107120Sjulian
2767107120Sjulianstatic ng_btsocket_l2cap_pcb_p
2768107120Sjulianng_btsocket_l2cap_pcb_by_token(u_int32_t token)
2769107120Sjulian{
2770107120Sjulian	ng_btsocket_l2cap_pcb_p	p = NULL;
2771107120Sjulian
2772114878Sjulian	if (token == 0)
2773114878Sjulian		return (NULL);
2774107120Sjulian
2775114878Sjulian	mtx_assert(&ng_btsocket_l2cap_sockets_mtx, MA_OWNED);
2776107120Sjulian
2777114878Sjulian	LIST_FOREACH(p, &ng_btsocket_l2cap_sockets, next)
2778114878Sjulian		if (p->token == token)
2779114878Sjulian			break;
2780107120Sjulian
2781107120Sjulian	return (p);
2782107120Sjulian} /* ng_btsocket_l2cap_pcb_by_token */
2783107120Sjulian
2784107120Sjulian/*
2785114878Sjulian * Look for the socket that assigned to given source address and channel ID.
2786114878Sjulian * Caller must hold ng_btsocket_l2cap_sockets_mtx
2787107120Sjulian */
2788107120Sjulian
2789107120Sjulianstatic ng_btsocket_l2cap_pcb_p
2790281198Stakawatang_btsocket_l2cap_pcb_by_cid(bdaddr_p src, int cid, int idtype)
2791107120Sjulian{
2792107120Sjulian	ng_btsocket_l2cap_pcb_p	p = NULL;
2793107120Sjulian
2794114878Sjulian	mtx_assert(&ng_btsocket_l2cap_sockets_mtx, MA_OWNED);
2795107120Sjulian
2796281198Stakawata	LIST_FOREACH(p, &ng_btsocket_l2cap_sockets, next){
2797281198Stakawata		if (p->cid == cid &&
2798281198Stakawata		    bcmp(src, &p->src, sizeof(p->src)) == 0&&
2799290038Stakawata		    p->idtype == idtype)
2800107120Sjulian			break;
2801107120Sjulian
2802281198Stakawata	}
2803107120Sjulian	return (p);
2804107120Sjulian} /* ng_btsocket_l2cap_pcb_by_cid */
2805107120Sjulian
2806107120Sjulian/*
2807107120Sjulian * Set timeout on socket
2808107120Sjulian */
2809107120Sjulian
2810107120Sjulianstatic void
2811107120Sjulianng_btsocket_l2cap_timeout(ng_btsocket_l2cap_pcb_p pcb)
2812107120Sjulian{
2813107120Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
2814107120Sjulian
2815107120Sjulian	if (!(pcb->flags & NG_BTSOCKET_L2CAP_TIMO)) {
2816107120Sjulian		pcb->flags |= NG_BTSOCKET_L2CAP_TIMO;
2817271908Sjhb		callout_reset(&pcb->timo, bluetooth_l2cap_ertx_timeout(),
2818271908Sjhb		    ng_btsocket_l2cap_process_timeout, pcb);
2819107120Sjulian	} else
2820107120Sjulian		KASSERT(0,
2821107120Sjulian("%s: Duplicated socket timeout?!\n", __func__));
2822107120Sjulian} /* ng_btsocket_l2cap_timeout */
2823107120Sjulian
2824107120Sjulian/*
2825107120Sjulian * Unset timeout on socket
2826107120Sjulian */
2827107120Sjulian
2828107120Sjulianstatic void
2829107120Sjulianng_btsocket_l2cap_untimeout(ng_btsocket_l2cap_pcb_p pcb)
2830107120Sjulian{
2831107120Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
2832107120Sjulian
2833107120Sjulian	if (pcb->flags & NG_BTSOCKET_L2CAP_TIMO) {
2834271908Sjhb		callout_stop(&pcb->timo);
2835107120Sjulian		pcb->flags &= ~NG_BTSOCKET_L2CAP_TIMO;
2836107120Sjulian	} else
2837107120Sjulian		KASSERT(0,
2838107120Sjulian("%s: No socket timeout?!\n", __func__));
2839107120Sjulian} /* ng_btsocket_l2cap_untimeout */
2840107120Sjulian
2841107120Sjulian/*
2842107120Sjulian * Process timeout on socket
2843107120Sjulian */
2844107120Sjulian
2845107120Sjulianstatic void
2846107120Sjulianng_btsocket_l2cap_process_timeout(void *xpcb)
2847107120Sjulian{
2848161623Semax	ng_btsocket_l2cap_pcb_p	pcb = (ng_btsocket_l2cap_pcb_p) xpcb;
2849107120Sjulian
2850271908Sjhb	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
2851107120Sjulian
2852107120Sjulian	pcb->flags &= ~NG_BTSOCKET_L2CAP_TIMO;
2853107120Sjulian	pcb->so->so_error = ETIMEDOUT;
2854107120Sjulian
2855107120Sjulian	switch (pcb->state) {
2856107120Sjulian	case NG_BTSOCKET_L2CAP_CONNECTING:
2857107120Sjulian	case NG_BTSOCKET_L2CAP_CONFIGURING:
2858290491Stakawata	case NG_BTSOCKET_L2CAP_W4_ENC_CHANGE:
2859107120Sjulian		/* Send disconnect request with "zero" token */
2860107120Sjulian		if (pcb->cid != 0)
2861107120Sjulian			ng_btsocket_l2cap_send_l2ca_discon_req(0, pcb);
2862107120Sjulian
2863107120Sjulian		/* ... and close the socket */
2864107120Sjulian		pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
2865107120Sjulian		soisdisconnected(pcb->so);
2866107120Sjulian		break;
2867107120Sjulian
2868107120Sjulian	case NG_BTSOCKET_L2CAP_OPEN:
2869107120Sjulian		/* Send timeout - drop packet and wakeup sender */
2870107120Sjulian		sbdroprecord(&pcb->so->so_snd);
2871107120Sjulian		sowwakeup(pcb->so);
2872107120Sjulian		break;
2873107120Sjulian
2874107120Sjulian	case NG_BTSOCKET_L2CAP_DISCONNECTING:
2875107120Sjulian		/* Disconnect timeout - disconnect the socket anyway */
2876107120Sjulian		pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
2877107120Sjulian		soisdisconnected(pcb->so);
2878107120Sjulian		break;
2879107120Sjulian
2880107120Sjulian	default:
2881107120Sjulian		NG_BTSOCKET_L2CAP_ERR(
2882107120Sjulian"%s: Invalid socket state=%d\n", __func__, pcb->state);
2883107120Sjulian		break;
2884107120Sjulian	}
2885107120Sjulian} /* ng_btsocket_l2cap_process_timeout */
2886107120Sjulian
2887107120Sjulian/*
2888107120Sjulian * Translate HCI/L2CAP error code into "errno" code
2889107120Sjulian * XXX Note: Some L2CAP and HCI error codes have the same value, but
2890107120Sjulian *     different meaning
2891107120Sjulian */
2892107120Sjulian
2893107120Sjulianstatic int
2894107120Sjulianng_btsocket_l2cap_result2errno(int result)
2895107120Sjulian{
2896107120Sjulian	switch (result) {
2897107120Sjulian	case 0x00: /* No error */
2898107120Sjulian		return (0);
2899107120Sjulian
2900107120Sjulian	case 0x01: /* Unknown HCI command */
2901107120Sjulian		return (ENODEV);
2902107120Sjulian
2903107120Sjulian	case 0x02: /* No connection */
2904107120Sjulian		return (ENOTCONN);
2905107120Sjulian
2906107120Sjulian	case 0x03: /* Hardware failure */
2907107120Sjulian		return (EIO);
2908107120Sjulian
2909107120Sjulian	case 0x04: /* Page timeout */
2910107120Sjulian		return (EHOSTDOWN);
2911107120Sjulian
2912107120Sjulian	case 0x05: /* Authentication failure */
2913107120Sjulian	case 0x06: /* Key missing */
2914107120Sjulian	case 0x18: /* Pairing not allowed */
2915107120Sjulian	case 0x21: /* Role change not allowed */
2916107120Sjulian	case 0x24: /* LMP PSU not allowed */
2917107120Sjulian	case 0x25: /* Encryption mode not acceptable */
2918107120Sjulian	case 0x26: /* Unit key used */
2919107120Sjulian		return (EACCES);
2920107120Sjulian
2921107120Sjulian	case 0x07: /* Memory full */
2922107120Sjulian		return (ENOMEM);
2923107120Sjulian
2924107120Sjulian	case 0x08:   /* Connection timeout */
2925107120Sjulian	case 0x10:   /* Host timeout */
2926107120Sjulian	case 0x22:   /* LMP response timeout */
2927107120Sjulian	case 0xee:   /* HCI timeout */
2928107120Sjulian	case 0xeeee: /* L2CAP timeout */
2929107120Sjulian		return (ETIMEDOUT);
2930107120Sjulian
2931107120Sjulian	case 0x09: /* Max number of connections */
2932107120Sjulian	case 0x0a: /* Max number of SCO connections to a unit */
2933107120Sjulian		return (EMLINK);
2934107120Sjulian
2935107120Sjulian	case 0x0b: /* ACL connection already exists */
2936107120Sjulian		return (EEXIST);
2937107120Sjulian
2938107120Sjulian	case 0x0c: /* Command disallowed */
2939107120Sjulian		return (EBUSY);
2940107120Sjulian
2941107120Sjulian	case 0x0d: /* Host rejected due to limited resources */
2942107120Sjulian	case 0x0e: /* Host rejected due to securiity reasons */
2943107120Sjulian	case 0x0f: /* Host rejected due to remote unit is a personal unit */
2944107120Sjulian	case 0x1b: /* SCO offset rejected */
2945107120Sjulian	case 0x1c: /* SCO interval rejected */
2946107120Sjulian	case 0x1d: /* SCO air mode rejected */
2947107120Sjulian		return (ECONNREFUSED);
2948107120Sjulian
2949107120Sjulian	case 0x11: /* Unsupported feature or parameter value */
2950107120Sjulian	case 0x19: /* Unknown LMP PDU */
2951107120Sjulian	case 0x1a: /* Unsupported remote feature */
2952107120Sjulian	case 0x20: /* Unsupported LMP parameter value */
2953107120Sjulian	case 0x27: /* QoS is not supported */
2954107120Sjulian	case 0x29: /* Paring with unit key not supported */
2955107120Sjulian		return (EOPNOTSUPP);
2956107120Sjulian
2957107120Sjulian	case 0x12: /* Invalid HCI command parameter */
2958107120Sjulian	case 0x1e: /* Invalid LMP parameters */
2959107120Sjulian		return (EINVAL);
2960107120Sjulian
2961107120Sjulian	case 0x13: /* Other end terminated connection: User ended connection */
2962107120Sjulian	case 0x14: /* Other end terminated connection: Low resources */
2963107120Sjulian	case 0x15: /* Other end terminated connection: About to power off */
2964107120Sjulian		return (ECONNRESET);
2965107120Sjulian
2966107120Sjulian	case 0x16: /* Connection terminated by local host */
2967107120Sjulian		return (ECONNABORTED);
2968107120Sjulian
2969107120Sjulian#if 0 /* XXX not yet */
2970107120Sjulian	case 0x17: /* Repeated attempts */
2971107120Sjulian	case 0x1f: /* Unspecified error */
2972107120Sjulian	case 0x23: /* LMP error transaction collision */
2973107120Sjulian	case 0x28: /* Instant passed */
2974107120Sjulian#endif
2975107120Sjulian	}
2976107120Sjulian
2977107120Sjulian	return (ENOSYS);
2978107120Sjulian} /* ng_btsocket_l2cap_result2errno */
2979107120Sjulian
2980