1f2bb1caeSJulian Elischer /*
2f2bb1caeSJulian Elischer  * ng_btsocket_rfcomm.c
3c398230bSWarner Losh  */
4c398230bSWarner Losh 
5c398230bSWarner Losh /*-
6fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
7fe267a55SPedro F. Giffuni  *
8f2bb1caeSJulian Elischer  * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
9f2bb1caeSJulian Elischer  * All rights reserved.
10f2bb1caeSJulian Elischer  *
11f2bb1caeSJulian Elischer  * Redistribution and use in source and binary forms, with or without
12f2bb1caeSJulian Elischer  * modification, are permitted provided that the following conditions
13f2bb1caeSJulian Elischer  * are met:
14f2bb1caeSJulian Elischer  * 1. Redistributions of source code must retain the above copyright
15f2bb1caeSJulian Elischer  *    notice, this list of conditions and the following disclaimer.
16f2bb1caeSJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
17f2bb1caeSJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
18f2bb1caeSJulian Elischer  *    documentation and/or other materials provided with the distribution.
19f2bb1caeSJulian Elischer  *
20f2bb1caeSJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21f2bb1caeSJulian Elischer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22f2bb1caeSJulian Elischer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23f2bb1caeSJulian Elischer  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24f2bb1caeSJulian Elischer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25f2bb1caeSJulian Elischer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26f2bb1caeSJulian Elischer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27f2bb1caeSJulian Elischer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28f2bb1caeSJulian Elischer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29f2bb1caeSJulian Elischer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30f2bb1caeSJulian Elischer  * SUCH DAMAGE.
31f2bb1caeSJulian Elischer  *
320986ab12SMaksim Yevmenkin  * $Id: ng_btsocket_rfcomm.c,v 1.28 2003/09/14 23:29:06 max Exp $
33f2bb1caeSJulian Elischer  * $FreeBSD$
34f2bb1caeSJulian Elischer  */
35f2bb1caeSJulian Elischer 
36f2bb1caeSJulian Elischer #include <sys/param.h>
37f2bb1caeSJulian Elischer #include <sys/systm.h>
380986ab12SMaksim Yevmenkin #include <sys/bitstring.h>
39f2bb1caeSJulian Elischer #include <sys/domain.h>
40f2bb1caeSJulian Elischer #include <sys/endian.h>
41f2bb1caeSJulian Elischer #include <sys/errno.h>
42f2bb1caeSJulian Elischer #include <sys/filedesc.h>
43f2bb1caeSJulian Elischer #include <sys/ioccom.h>
44f2bb1caeSJulian Elischer #include <sys/kernel.h>
45f2bb1caeSJulian Elischer #include <sys/lock.h>
46f2bb1caeSJulian Elischer #include <sys/malloc.h>
47f2bb1caeSJulian Elischer #include <sys/mbuf.h>
48f2bb1caeSJulian Elischer #include <sys/mutex.h>
49f2bb1caeSJulian Elischer #include <sys/proc.h>
50f2bb1caeSJulian Elischer #include <sys/protosw.h>
51f2bb1caeSJulian Elischer #include <sys/queue.h>
52f2bb1caeSJulian Elischer #include <sys/socket.h>
53f2bb1caeSJulian Elischer #include <sys/socketvar.h>
54f2bb1caeSJulian Elischer #include <sys/sysctl.h>
55f2bb1caeSJulian Elischer #include <sys/taskqueue.h>
56f2bb1caeSJulian Elischer #include <sys/uio.h>
571fb51a12SBjoern A. Zeeb 
581fb51a12SBjoern A. Zeeb #include <net/vnet.h>
591fb51a12SBjoern A. Zeeb 
60f2bb1caeSJulian Elischer #include <netgraph/ng_message.h>
61f2bb1caeSJulian Elischer #include <netgraph/netgraph.h>
62b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_bluetooth.h>
63b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_hci.h>
64b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_l2cap.h>
65b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_btsocket.h>
66b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_btsocket_l2cap.h>
67b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_btsocket_rfcomm.h>
68f2bb1caeSJulian Elischer 
69f2bb1caeSJulian Elischer /* MALLOC define */
70f2bb1caeSJulian Elischer #ifdef NG_SEPARATE_MALLOC
71d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_RFCOMM, "netgraph_btsocks_rfcomm",
72f2bb1caeSJulian Elischer 		"Netgraph Bluetooth RFCOMM sockets");
73f2bb1caeSJulian Elischer #else
74f2bb1caeSJulian Elischer #define M_NETGRAPH_BTSOCKET_RFCOMM M_NETGRAPH
75f2bb1caeSJulian Elischer #endif /* NG_SEPARATE_MALLOC */
76f2bb1caeSJulian Elischer 
77f2bb1caeSJulian Elischer /* Debug */
78f2bb1caeSJulian Elischer #define NG_BTSOCKET_RFCOMM_INFO \
794fa708efSMaksim Yevmenkin 	if (ng_btsocket_rfcomm_debug_level >= NG_BTSOCKET_INFO_LEVEL && \
804fa708efSMaksim Yevmenkin 	    ppsratecheck(&ng_btsocket_rfcomm_lasttime, &ng_btsocket_rfcomm_curpps, 1)) \
81f2bb1caeSJulian Elischer 		printf
82f2bb1caeSJulian Elischer 
83f2bb1caeSJulian Elischer #define NG_BTSOCKET_RFCOMM_WARN \
844fa708efSMaksim Yevmenkin 	if (ng_btsocket_rfcomm_debug_level >= NG_BTSOCKET_WARN_LEVEL && \
854fa708efSMaksim Yevmenkin 	    ppsratecheck(&ng_btsocket_rfcomm_lasttime, &ng_btsocket_rfcomm_curpps, 1)) \
86f2bb1caeSJulian Elischer 		printf
87f2bb1caeSJulian Elischer 
88f2bb1caeSJulian Elischer #define NG_BTSOCKET_RFCOMM_ERR \
894fa708efSMaksim Yevmenkin 	if (ng_btsocket_rfcomm_debug_level >= NG_BTSOCKET_ERR_LEVEL && \
904fa708efSMaksim Yevmenkin 	    ppsratecheck(&ng_btsocket_rfcomm_lasttime, &ng_btsocket_rfcomm_curpps, 1)) \
91f2bb1caeSJulian Elischer 		printf
92f2bb1caeSJulian Elischer 
93f2bb1caeSJulian Elischer #define NG_BTSOCKET_RFCOMM_ALERT \
944fa708efSMaksim Yevmenkin 	if (ng_btsocket_rfcomm_debug_level >= NG_BTSOCKET_ALERT_LEVEL && \
954fa708efSMaksim Yevmenkin 	    ppsratecheck(&ng_btsocket_rfcomm_lasttime, &ng_btsocket_rfcomm_curpps, 1)) \
96f2bb1caeSJulian Elischer 		printf
97f2bb1caeSJulian Elischer 
98f2bb1caeSJulian Elischer #define	ALOT	0x7fff
99f2bb1caeSJulian Elischer 
100f2bb1caeSJulian Elischer /* Local prototypes */
10174fb0ba7SJohn Baldwin static int ng_btsocket_rfcomm_upcall
102f2bb1caeSJulian Elischer 	(struct socket *so, void *arg, int waitflag);
103f2bb1caeSJulian Elischer static void ng_btsocket_rfcomm_sessions_task
104f2bb1caeSJulian Elischer 	(void *ctx, int pending);
105f2bb1caeSJulian Elischer static void ng_btsocket_rfcomm_session_task
106f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s);
107f2bb1caeSJulian Elischer #define ng_btsocket_rfcomm_task_wakeup() \
108f2bb1caeSJulian Elischer 	taskqueue_enqueue(taskqueue_swi_giant, &ng_btsocket_rfcomm_task)
109f2bb1caeSJulian Elischer 
110f2bb1caeSJulian Elischer static ng_btsocket_rfcomm_pcb_p ng_btsocket_rfcomm_connect_ind
111f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, int channel);
112f2bb1caeSJulian Elischer static void ng_btsocket_rfcomm_connect_cfm
113f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s);
114f2bb1caeSJulian Elischer 
115f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_session_create
116f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p *sp, struct socket *l2so,
117f2bb1caeSJulian Elischer 	 bdaddr_p src, bdaddr_p dst, struct thread *td);
118f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_session_accept
119f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s0);
120f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_session_connect
121f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s);
122f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_session_receive
123f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s);
124f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_session_send
125f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s);
126f2bb1caeSJulian Elischer static void ng_btsocket_rfcomm_session_clean
127f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s);
128f2bb1caeSJulian Elischer static void ng_btsocket_rfcomm_session_process_pcb
129f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s);
130f2bb1caeSJulian Elischer static ng_btsocket_rfcomm_session_p ng_btsocket_rfcomm_session_by_addr
131f2bb1caeSJulian Elischer 	(bdaddr_p src, bdaddr_p dst);
132f2bb1caeSJulian Elischer 
133f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_frame
134f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
135f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_sabm
136f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, int dlci);
137f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_disc
138f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, int dlci);
139f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_ua
140f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, int dlci);
141f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_dm
142f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, int dlci);
143f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_uih
144f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, int dlci, int pf, struct mbuf *m0);
145f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_mcc
146f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
147f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_test
148f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
149f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_fc
150f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
151f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_msc
152f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
153f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_rpn
154f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
155f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_rls
156f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
157f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_receive_pn
158f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
159f2bb1caeSJulian Elischer static void ng_btsocket_rfcomm_set_pn
160f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_pcb_p pcb, u_int8_t cr, u_int8_t flow_control,
161f2bb1caeSJulian Elischer 	 u_int8_t credits, u_int16_t mtu);
162f2bb1caeSJulian Elischer 
163f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_send_command
164f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, u_int8_t type, u_int8_t dlci);
165f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_send_uih
166f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, u_int8_t address, u_int8_t pf,
167f2bb1caeSJulian Elischer 	 u_int8_t credits, struct mbuf *data);
168f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_send_msc
169f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_pcb_p pcb);
170f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_send_pn
171f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_pcb_p pcb);
172f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_send_credits
173f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_pcb_p pcb);
174f2bb1caeSJulian Elischer 
175f2bb1caeSJulian Elischer static int ng_btsocket_rfcomm_pcb_send
176f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_pcb_p pcb, int limit);
1777c380856SMaksim Yevmenkin static void ng_btsocket_rfcomm_pcb_kill
178f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_pcb_p pcb, int error);
179f2bb1caeSJulian Elischer static ng_btsocket_rfcomm_pcb_p ng_btsocket_rfcomm_pcb_by_dlci
180f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_session_p s, int dlci);
181f2bb1caeSJulian Elischer static ng_btsocket_rfcomm_pcb_p ng_btsocket_rfcomm_pcb_listener
182f2bb1caeSJulian Elischer 	(bdaddr_p src, int channel);
183f2bb1caeSJulian Elischer 
184f2bb1caeSJulian Elischer static void ng_btsocket_rfcomm_timeout
185f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_pcb_p pcb);
186f2bb1caeSJulian Elischer static void ng_btsocket_rfcomm_untimeout
187f2bb1caeSJulian Elischer 	(ng_btsocket_rfcomm_pcb_p pcb);
188f2bb1caeSJulian Elischer static void ng_btsocket_rfcomm_process_timeout
189f2bb1caeSJulian Elischer 	(void *xpcb);
190f2bb1caeSJulian Elischer 
191f2bb1caeSJulian Elischer static struct mbuf * ng_btsocket_rfcomm_prepare_packet
192f2bb1caeSJulian Elischer 	(struct sockbuf *sb, int length);
193f2bb1caeSJulian Elischer 
194f2bb1caeSJulian Elischer /* Globals */
195f2bb1caeSJulian Elischer extern int					ifqmaxlen;
196f2bb1caeSJulian Elischer static u_int32_t				ng_btsocket_rfcomm_debug_level;
197f2bb1caeSJulian Elischer static u_int32_t				ng_btsocket_rfcomm_timo;
198f2bb1caeSJulian Elischer struct task					ng_btsocket_rfcomm_task;
199f2bb1caeSJulian Elischer static LIST_HEAD(, ng_btsocket_rfcomm_session)	ng_btsocket_rfcomm_sessions;
200f2bb1caeSJulian Elischer static struct mtx				ng_btsocket_rfcomm_sessions_mtx;
201f2bb1caeSJulian Elischer static LIST_HEAD(, ng_btsocket_rfcomm_pcb)	ng_btsocket_rfcomm_sockets;
202f2bb1caeSJulian Elischer static struct mtx				ng_btsocket_rfcomm_sockets_mtx;
2034fa708efSMaksim Yevmenkin static struct timeval				ng_btsocket_rfcomm_lasttime;
2044fa708efSMaksim Yevmenkin static int					ng_btsocket_rfcomm_curpps;
205f2bb1caeSJulian Elischer 
206f2bb1caeSJulian Elischer /* Sysctl tree */
207f2bb1caeSJulian Elischer SYSCTL_DECL(_net_bluetooth_rfcomm_sockets);
2087029da5cSPawel Biernacki static SYSCTL_NODE(_net_bluetooth_rfcomm_sockets, OID_AUTO, stream,
2097029da5cSPawel Biernacki     CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
2107029da5cSPawel Biernacki     "Bluetooth STREAM RFCOMM sockets family");
211f29fc085SMatthew D Fleming SYSCTL_UINT(_net_bluetooth_rfcomm_sockets_stream, OID_AUTO, debug_level,
212f2bb1caeSJulian Elischer 	CTLFLAG_RW,
213f2bb1caeSJulian Elischer 	&ng_btsocket_rfcomm_debug_level, NG_BTSOCKET_INFO_LEVEL,
214f2bb1caeSJulian Elischer 	"Bluetooth STREAM RFCOMM sockets debug level");
215f29fc085SMatthew D Fleming SYSCTL_UINT(_net_bluetooth_rfcomm_sockets_stream, OID_AUTO, timeout,
216f2bb1caeSJulian Elischer 	CTLFLAG_RW,
217f2bb1caeSJulian Elischer 	&ng_btsocket_rfcomm_timo, 60,
218f2bb1caeSJulian Elischer 	"Bluetooth STREAM RFCOMM sockets timeout");
219f2bb1caeSJulian Elischer 
220f2bb1caeSJulian Elischer /*****************************************************************************
221f2bb1caeSJulian Elischer  *****************************************************************************
222f2bb1caeSJulian Elischer  **                              RFCOMM CRC
223f2bb1caeSJulian Elischer  *****************************************************************************
224f2bb1caeSJulian Elischer  *****************************************************************************/
225f2bb1caeSJulian Elischer 
226f2bb1caeSJulian Elischer static u_int8_t	ng_btsocket_rfcomm_crc_table[256] = {
227f2bb1caeSJulian Elischer 	0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
228f2bb1caeSJulian Elischer 	0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
229f2bb1caeSJulian Elischer 	0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
230f2bb1caeSJulian Elischer 	0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
231f2bb1caeSJulian Elischer 
232f2bb1caeSJulian Elischer 	0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
233f2bb1caeSJulian Elischer 	0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
234f2bb1caeSJulian Elischer 	0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
235f2bb1caeSJulian Elischer 	0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
236f2bb1caeSJulian Elischer 
237f2bb1caeSJulian Elischer 	0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
238f2bb1caeSJulian Elischer 	0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
239f2bb1caeSJulian Elischer 	0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
240f2bb1caeSJulian Elischer 	0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
241f2bb1caeSJulian Elischer 
242f2bb1caeSJulian Elischer 	0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
243f2bb1caeSJulian Elischer 	0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
244f2bb1caeSJulian Elischer 	0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
245f2bb1caeSJulian Elischer 	0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
246f2bb1caeSJulian Elischer 
247f2bb1caeSJulian Elischer 	0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
248f2bb1caeSJulian Elischer 	0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
249f2bb1caeSJulian Elischer 	0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
250f2bb1caeSJulian Elischer 	0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
251f2bb1caeSJulian Elischer 
252f2bb1caeSJulian Elischer 	0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
253f2bb1caeSJulian Elischer 	0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
254f2bb1caeSJulian Elischer 	0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
255f2bb1caeSJulian Elischer 	0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
256f2bb1caeSJulian Elischer 
257f2bb1caeSJulian Elischer 	0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
258f2bb1caeSJulian Elischer 	0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
259f2bb1caeSJulian Elischer 	0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
260f2bb1caeSJulian Elischer 	0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
261f2bb1caeSJulian Elischer 
262f2bb1caeSJulian Elischer 	0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
263f2bb1caeSJulian Elischer 	0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
264f2bb1caeSJulian Elischer 	0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
265f2bb1caeSJulian Elischer 	0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
266f2bb1caeSJulian Elischer };
267f2bb1caeSJulian Elischer 
268f2bb1caeSJulian Elischer /* CRC */
269f2bb1caeSJulian Elischer static u_int8_t
270f2bb1caeSJulian Elischer ng_btsocket_rfcomm_crc(u_int8_t *data, int length)
271f2bb1caeSJulian Elischer {
272f2bb1caeSJulian Elischer 	u_int8_t	crc = 0xff;
273f2bb1caeSJulian Elischer 
274f2bb1caeSJulian Elischer 	while (length --)
275f2bb1caeSJulian Elischer 		crc = ng_btsocket_rfcomm_crc_table[crc ^ *data++];
276f2bb1caeSJulian Elischer 
277f2bb1caeSJulian Elischer 	return (crc);
278f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_crc */
279f2bb1caeSJulian Elischer 
280f2bb1caeSJulian Elischer /* FCS on 2 bytes */
281f2bb1caeSJulian Elischer static u_int8_t
282f2bb1caeSJulian Elischer ng_btsocket_rfcomm_fcs2(u_int8_t *data)
283f2bb1caeSJulian Elischer {
284f2bb1caeSJulian Elischer 	return (0xff - ng_btsocket_rfcomm_crc(data, 2));
285f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_fcs2 */
286f2bb1caeSJulian Elischer 
287f2bb1caeSJulian Elischer /* FCS on 3 bytes */
288f2bb1caeSJulian Elischer static u_int8_t
289f2bb1caeSJulian Elischer ng_btsocket_rfcomm_fcs3(u_int8_t *data)
290f2bb1caeSJulian Elischer {
291f2bb1caeSJulian Elischer 	return (0xff - ng_btsocket_rfcomm_crc(data, 3));
292f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_fcs3 */
293f2bb1caeSJulian Elischer 
294f2bb1caeSJulian Elischer /*
295f2bb1caeSJulian Elischer  * Check FCS
296f2bb1caeSJulian Elischer  *
297f2bb1caeSJulian Elischer  * From Bluetooth spec
298f2bb1caeSJulian Elischer  *
299f2bb1caeSJulian Elischer  * "... In 07.10, the frame check sequence (FCS) is calculated on different
300f2bb1caeSJulian Elischer  * sets of fields for different frame types. These are the fields that the
301f2bb1caeSJulian Elischer  * FCS are calculated on:
302f2bb1caeSJulian Elischer  *
303f2bb1caeSJulian Elischer  * For SABM, DISC, UA, DM frames: on Address, Control and length field.
304f2bb1caeSJulian Elischer  * For UIH frames: on Address and Control field.
305f2bb1caeSJulian Elischer  *
306f2bb1caeSJulian Elischer  * (This is stated here for clarification, and to set the standard for RFCOMM;
307f2bb1caeSJulian Elischer  * the fields included in FCS calculation have actually changed in version
308f2bb1caeSJulian Elischer  * 7.0.0 of TS 07.10, but RFCOMM will not change the FCS calculation scheme
309f2bb1caeSJulian Elischer  * from the one above.) ..."
310f2bb1caeSJulian Elischer  */
311f2bb1caeSJulian Elischer 
312f2bb1caeSJulian Elischer static int
313f2bb1caeSJulian Elischer ng_btsocket_rfcomm_check_fcs(u_int8_t *data, int type, u_int8_t fcs)
314f2bb1caeSJulian Elischer {
315f2bb1caeSJulian Elischer 	if (type != RFCOMM_FRAME_UIH)
316f2bb1caeSJulian Elischer 		return (ng_btsocket_rfcomm_fcs3(data) != fcs);
317f2bb1caeSJulian Elischer 
318f2bb1caeSJulian Elischer 	return (ng_btsocket_rfcomm_fcs2(data) != fcs);
319f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_check_fcs */
320f2bb1caeSJulian Elischer 
321f2bb1caeSJulian Elischer /*****************************************************************************
322f2bb1caeSJulian Elischer  *****************************************************************************
323f2bb1caeSJulian Elischer  **                              Socket interface
324f2bb1caeSJulian Elischer  *****************************************************************************
325f2bb1caeSJulian Elischer  *****************************************************************************/
326f2bb1caeSJulian Elischer 
327f2bb1caeSJulian Elischer /*
328f2bb1caeSJulian Elischer  * Initialize everything
329f2bb1caeSJulian Elischer  */
330f2bb1caeSJulian Elischer 
331f2bb1caeSJulian Elischer void
332f2bb1caeSJulian Elischer ng_btsocket_rfcomm_init(void)
333f2bb1caeSJulian Elischer {
3344a8e4eb5SMikolaj Golub 
3354a8e4eb5SMikolaj Golub 	/* Skip initialization of globals for non-default instances. */
3364a8e4eb5SMikolaj Golub 	if (!IS_DEFAULT_VNET(curvnet))
3374a8e4eb5SMikolaj Golub 		return;
3384a8e4eb5SMikolaj Golub 
339f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_debug_level = NG_BTSOCKET_WARN_LEVEL;
340f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_timo = 60;
341f2bb1caeSJulian Elischer 
342f2bb1caeSJulian Elischer 	/* RFCOMM task */
343f2bb1caeSJulian Elischer 	TASK_INIT(&ng_btsocket_rfcomm_task, 0,
344f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_sessions_task, NULL);
345f2bb1caeSJulian Elischer 
346f2bb1caeSJulian Elischer 	/* RFCOMM sessions list */
347f2bb1caeSJulian Elischer 	LIST_INIT(&ng_btsocket_rfcomm_sessions);
348f2bb1caeSJulian Elischer 	mtx_init(&ng_btsocket_rfcomm_sessions_mtx,
349f2bb1caeSJulian Elischer 		"btsocks_rfcomm_sessions_mtx", NULL, MTX_DEF);
350f2bb1caeSJulian Elischer 
351f2bb1caeSJulian Elischer 	/* RFCOMM sockets list */
352f2bb1caeSJulian Elischer 	LIST_INIT(&ng_btsocket_rfcomm_sockets);
353f2bb1caeSJulian Elischer 	mtx_init(&ng_btsocket_rfcomm_sockets_mtx,
354f2bb1caeSJulian Elischer 		"btsocks_rfcomm_sockets_mtx", NULL, MTX_DEF);
355f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_init */
356f2bb1caeSJulian Elischer 
357f2bb1caeSJulian Elischer /*
358f2bb1caeSJulian Elischer  * Abort connection on socket
359f2bb1caeSJulian Elischer  */
360f2bb1caeSJulian Elischer 
361ac45e92fSRobert Watson void
362f2bb1caeSJulian Elischer ng_btsocket_rfcomm_abort(struct socket *so)
363f2bb1caeSJulian Elischer {
364f2bb1caeSJulian Elischer 
365a152f8a3SRobert Watson 	so->so_error = ECONNABORTED;
366a152f8a3SRobert Watson 	(void)ng_btsocket_rfcomm_disconnect(so);
367f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_abort */
368f2bb1caeSJulian Elischer 
369a152f8a3SRobert Watson void
370a152f8a3SRobert Watson ng_btsocket_rfcomm_close(struct socket *so)
371a152f8a3SRobert Watson {
372a152f8a3SRobert Watson 
373a152f8a3SRobert Watson 	(void)ng_btsocket_rfcomm_disconnect(so);
374a152f8a3SRobert Watson } /* ng_btsocket_rfcomm_close */
375a152f8a3SRobert Watson 
376f2bb1caeSJulian Elischer /*
377f2bb1caeSJulian Elischer  * Accept connection on socket. Nothing to do here, socket must be connected
378f2bb1caeSJulian Elischer  * and ready, so just return peer address and be done with it.
379f2bb1caeSJulian Elischer  */
380f2bb1caeSJulian Elischer 
381f2bb1caeSJulian Elischer int
382f2bb1caeSJulian Elischer ng_btsocket_rfcomm_accept(struct socket *so, struct sockaddr **nam)
383f2bb1caeSJulian Elischer {
384f2bb1caeSJulian Elischer 	return (ng_btsocket_rfcomm_peeraddr(so, nam));
385f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_accept */
386f2bb1caeSJulian Elischer 
387f2bb1caeSJulian Elischer /*
388f2bb1caeSJulian Elischer  * Create and attach new socket
389f2bb1caeSJulian Elischer  */
390f2bb1caeSJulian Elischer 
391f2bb1caeSJulian Elischer int
392f2bb1caeSJulian Elischer ng_btsocket_rfcomm_attach(struct socket *so, int proto, struct thread *td)
393f2bb1caeSJulian Elischer {
394f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
395f2bb1caeSJulian Elischer 	int				error;
396f2bb1caeSJulian Elischer 
397f2bb1caeSJulian Elischer 	/* Check socket and protocol */
398f2bb1caeSJulian Elischer 	if (so->so_type != SOCK_STREAM)
399f2bb1caeSJulian Elischer 		return (ESOCKTNOSUPPORT);
400f2bb1caeSJulian Elischer 
401f2bb1caeSJulian Elischer #if 0 /* XXX sonewconn() calls "pru_attach" with proto == 0 */
402f2bb1caeSJulian Elischer 	if (proto != 0)
403f2bb1caeSJulian Elischer 		if (proto != BLUETOOTH_PROTO_RFCOMM)
404f2bb1caeSJulian Elischer 			return (EPROTONOSUPPORT);
405f2bb1caeSJulian Elischer #endif /* XXX */
406f2bb1caeSJulian Elischer 
407f2bb1caeSJulian Elischer 	if (pcb != NULL)
408f2bb1caeSJulian Elischer 		return (EISCONN);
409f2bb1caeSJulian Elischer 
410f2bb1caeSJulian Elischer 	/* Reserve send and receive space if it is not reserved yet */
411f2bb1caeSJulian Elischer 	if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) {
412f2bb1caeSJulian Elischer 		error = soreserve(so, NG_BTSOCKET_RFCOMM_SENDSPACE,
413f2bb1caeSJulian Elischer 					NG_BTSOCKET_RFCOMM_RECVSPACE);
414f2bb1caeSJulian Elischer 		if (error != 0)
415f2bb1caeSJulian Elischer 			return (error);
416f2bb1caeSJulian Elischer 	}
417f2bb1caeSJulian Elischer 
418f2bb1caeSJulian Elischer 	/* Allocate the PCB */
4191ede983cSDag-Erling Smørgrav         pcb = malloc(sizeof(*pcb),
420f2bb1caeSJulian Elischer 		M_NETGRAPH_BTSOCKET_RFCOMM, M_NOWAIT | M_ZERO);
421f2bb1caeSJulian Elischer         if (pcb == NULL)
422f2bb1caeSJulian Elischer                 return (ENOMEM);
423f2bb1caeSJulian Elischer 
424f2bb1caeSJulian Elischer 	/* Link the PCB and the socket */
425f2bb1caeSJulian Elischer 	so->so_pcb = (caddr_t) pcb;
426f2bb1caeSJulian Elischer 	pcb->so = so;
427f2bb1caeSJulian Elischer 
428f2bb1caeSJulian Elischer 	/* Initialize PCB */
429f2bb1caeSJulian Elischer 	pcb->state = NG_BTSOCKET_RFCOMM_DLC_CLOSED;
430f2bb1caeSJulian Elischer 	pcb->flags = NG_BTSOCKET_RFCOMM_DLC_CFC;
431f2bb1caeSJulian Elischer 
432f2bb1caeSJulian Elischer 	pcb->lmodem =
433f2bb1caeSJulian Elischer 	pcb->rmodem = (RFCOMM_MODEM_RTC | RFCOMM_MODEM_RTR | RFCOMM_MODEM_DV);
434f2bb1caeSJulian Elischer 
435f2bb1caeSJulian Elischer 	pcb->mtu = RFCOMM_DEFAULT_MTU;
436f2bb1caeSJulian Elischer 	pcb->tx_cred = 0;
437f2bb1caeSJulian Elischer 	pcb->rx_cred = RFCOMM_DEFAULT_CREDITS;
438f2bb1caeSJulian Elischer 
439f2bb1caeSJulian Elischer 	mtx_init(&pcb->pcb_mtx, "btsocks_rfcomm_pcb_mtx", NULL, MTX_DEF);
4403d1592efSJohn Baldwin 	callout_init_mtx(&pcb->timo, &pcb->pcb_mtx, 0);
441f2bb1caeSJulian Elischer 
442f2bb1caeSJulian Elischer 	/* Add the PCB to the list */
443f2bb1caeSJulian Elischer 	mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
444f2bb1caeSJulian Elischer 	LIST_INSERT_HEAD(&ng_btsocket_rfcomm_sockets, pcb, next);
445f2bb1caeSJulian Elischer 	mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
446f2bb1caeSJulian Elischer 
447f2bb1caeSJulian Elischer         return (0);
448f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_attach */
449f2bb1caeSJulian Elischer 
450f2bb1caeSJulian Elischer /*
451f2bb1caeSJulian Elischer  * Bind socket
452f2bb1caeSJulian Elischer  */
453f2bb1caeSJulian Elischer 
454f2bb1caeSJulian Elischer int
455f2bb1caeSJulian Elischer ng_btsocket_rfcomm_bind(struct socket *so, struct sockaddr *nam,
456f2bb1caeSJulian Elischer 		struct thread *td)
457f2bb1caeSJulian Elischer {
458a6f3c1e3SMaksim Yevmenkin 	ng_btsocket_rfcomm_pcb_t	*pcb = so2rfcomm_pcb(so), *pcb1;
459f2bb1caeSJulian Elischer 	struct sockaddr_rfcomm		*sa = (struct sockaddr_rfcomm *) nam;
460f2bb1caeSJulian Elischer 
461f2bb1caeSJulian Elischer 	if (pcb == NULL)
462f2bb1caeSJulian Elischer 		return (EINVAL);
463f2bb1caeSJulian Elischer 
464f2bb1caeSJulian Elischer 	/* Verify address */
465f2bb1caeSJulian Elischer 	if (sa == NULL)
466f2bb1caeSJulian Elischer 		return (EINVAL);
467f2bb1caeSJulian Elischer 	if (sa->rfcomm_family != AF_BLUETOOTH)
468f2bb1caeSJulian Elischer 		return (EAFNOSUPPORT);
469f2bb1caeSJulian Elischer 	if (sa->rfcomm_len != sizeof(*sa))
470f2bb1caeSJulian Elischer 		return (EINVAL);
471f2bb1caeSJulian Elischer 	if (sa->rfcomm_channel > 30)
472f2bb1caeSJulian Elischer 		return (EINVAL);
473a6f3c1e3SMaksim Yevmenkin 
474a6f3c1e3SMaksim Yevmenkin 	mtx_lock(&pcb->pcb_mtx);
475a6f3c1e3SMaksim Yevmenkin 
476a6f3c1e3SMaksim Yevmenkin 	if (sa->rfcomm_channel != 0) {
477a6f3c1e3SMaksim Yevmenkin 		mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
478a6f3c1e3SMaksim Yevmenkin 
479a6f3c1e3SMaksim Yevmenkin 		LIST_FOREACH(pcb1, &ng_btsocket_rfcomm_sockets, next) {
480a6f3c1e3SMaksim Yevmenkin 			if (pcb1->channel == sa->rfcomm_channel &&
481a6f3c1e3SMaksim Yevmenkin 			    bcmp(&pcb1->src, &sa->rfcomm_bdaddr,
482a6f3c1e3SMaksim Yevmenkin 					sizeof(pcb1->src)) == 0) {
483a6f3c1e3SMaksim Yevmenkin 				mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
484a6f3c1e3SMaksim Yevmenkin 				mtx_unlock(&pcb->pcb_mtx);
485a6f3c1e3SMaksim Yevmenkin 
486f2bb1caeSJulian Elischer 				return (EADDRINUSE);
487a6f3c1e3SMaksim Yevmenkin 			}
488a6f3c1e3SMaksim Yevmenkin 		}
489a6f3c1e3SMaksim Yevmenkin 
490a6f3c1e3SMaksim Yevmenkin 		mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
491a6f3c1e3SMaksim Yevmenkin 	}
492f2bb1caeSJulian Elischer 
493f2bb1caeSJulian Elischer 	bcopy(&sa->rfcomm_bdaddr, &pcb->src, sizeof(pcb->src));
494f2bb1caeSJulian Elischer 	pcb->channel = sa->rfcomm_channel;
495f2bb1caeSJulian Elischer 
496a6f3c1e3SMaksim Yevmenkin 	mtx_unlock(&pcb->pcb_mtx);
497a6f3c1e3SMaksim Yevmenkin 
498f2bb1caeSJulian Elischer 	return (0);
499f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_bind */
500f2bb1caeSJulian Elischer 
501f2bb1caeSJulian Elischer /*
502f2bb1caeSJulian Elischer  * Connect socket
503f2bb1caeSJulian Elischer  */
504f2bb1caeSJulian Elischer 
505f2bb1caeSJulian Elischer int
506f2bb1caeSJulian Elischer ng_btsocket_rfcomm_connect(struct socket *so, struct sockaddr *nam,
507f2bb1caeSJulian Elischer 		struct thread *td)
508f2bb1caeSJulian Elischer {
509f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_t	*pcb = so2rfcomm_pcb(so);
510f2bb1caeSJulian Elischer 	struct sockaddr_rfcomm		*sa = (struct sockaddr_rfcomm *) nam;
511f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_session_t	*s = NULL;
512f2bb1caeSJulian Elischer 	struct socket			*l2so = NULL;
513f2bb1caeSJulian Elischer 	int				 dlci, error = 0;
514f2bb1caeSJulian Elischer 
515f2bb1caeSJulian Elischer 	if (pcb == NULL)
516f2bb1caeSJulian Elischer 		return (EINVAL);
517f2bb1caeSJulian Elischer 
518f2bb1caeSJulian Elischer 	/* Verify address */
519f2bb1caeSJulian Elischer 	if (sa == NULL)
520f2bb1caeSJulian Elischer 		return (EINVAL);
521f2bb1caeSJulian Elischer 	if (sa->rfcomm_family != AF_BLUETOOTH)
522f2bb1caeSJulian Elischer 		return (EAFNOSUPPORT);
523f2bb1caeSJulian Elischer 	if (sa->rfcomm_len != sizeof(*sa))
524f2bb1caeSJulian Elischer 		return (EINVAL);
525f2bb1caeSJulian Elischer 	if (sa->rfcomm_channel > 30)
526f2bb1caeSJulian Elischer 		return (EINVAL);
527f2bb1caeSJulian Elischer 	if (sa->rfcomm_channel == 0 ||
528f2bb1caeSJulian Elischer 	    bcmp(&sa->rfcomm_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
529f2bb1caeSJulian Elischer 		return (EDESTADDRREQ);
530f2bb1caeSJulian Elischer 
531f2bb1caeSJulian Elischer 	/*
532320a8190SMaksim Yevmenkin 	 * Note that we will not check for errors in socreate() because
533320a8190SMaksim Yevmenkin 	 * if we failed to create L2CAP socket at this point we still
534320a8190SMaksim Yevmenkin 	 * might have already open session.
535f2bb1caeSJulian Elischer 	 */
536f2bb1caeSJulian Elischer 
537f2bb1caeSJulian Elischer 	error = socreate(PF_BLUETOOTH, &l2so, SOCK_SEQPACKET,
538f2bb1caeSJulian Elischer 			BLUETOOTH_PROTO_L2CAP, td->td_ucred, td);
539f2bb1caeSJulian Elischer 
540f2bb1caeSJulian Elischer 	/*
541f2bb1caeSJulian Elischer 	 * Look for session between "pcb->src" and "sa->rfcomm_bdaddr" (dst)
542f2bb1caeSJulian Elischer 	 */
543f2bb1caeSJulian Elischer 
544f2bb1caeSJulian Elischer 	mtx_lock(&ng_btsocket_rfcomm_sessions_mtx);
545f2bb1caeSJulian Elischer 
546f2bb1caeSJulian Elischer 	s = ng_btsocket_rfcomm_session_by_addr(&pcb->src, &sa->rfcomm_bdaddr);
547f2bb1caeSJulian Elischer 	if (s == NULL) {
548f2bb1caeSJulian Elischer 		/*
549f2bb1caeSJulian Elischer 		 * We need to create new RFCOMM session. Check if we have L2CAP
550f2bb1caeSJulian Elischer 		 * socket. If l2so == NULL then error has the error code from
551f2bb1caeSJulian Elischer 		 * socreate()
552f2bb1caeSJulian Elischer 		 */
553f2bb1caeSJulian Elischer 
554f2bb1caeSJulian Elischer 		if (l2so == NULL) {
555f2bb1caeSJulian Elischer 			mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
556f2bb1caeSJulian Elischer 			return (error);
557f2bb1caeSJulian Elischer 		}
558f2bb1caeSJulian Elischer 
559f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_session_create(&s, l2so,
560f2bb1caeSJulian Elischer 				&pcb->src, &sa->rfcomm_bdaddr, td);
561f2bb1caeSJulian Elischer 		if (error != 0) {
562f2bb1caeSJulian Elischer 			mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
563f2bb1caeSJulian Elischer 			soclose(l2so);
564f2bb1caeSJulian Elischer 
565f2bb1caeSJulian Elischer 			return (error);
566f2bb1caeSJulian Elischer 		}
567f2bb1caeSJulian Elischer 	} else if (l2so != NULL)
568f2bb1caeSJulian Elischer 		soclose(l2so); /* we don't need new L2CAP socket */
569f2bb1caeSJulian Elischer 
570f2bb1caeSJulian Elischer 	/*
5716bccea7cSRebecca Cran 	 * Check if we already have the same DLCI the same session
572f2bb1caeSJulian Elischer 	 */
573f2bb1caeSJulian Elischer 
574f2bb1caeSJulian Elischer 	mtx_lock(&s->session_mtx);
575f2bb1caeSJulian Elischer 	mtx_lock(&pcb->pcb_mtx);
576f2bb1caeSJulian Elischer 
577f2bb1caeSJulian Elischer 	dlci = RFCOMM_MKDLCI(!INITIATOR(s), sa->rfcomm_channel);
578f2bb1caeSJulian Elischer 
579f2bb1caeSJulian Elischer 	if (ng_btsocket_rfcomm_pcb_by_dlci(s, dlci) != NULL) {
580f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
581f2bb1caeSJulian Elischer 		mtx_unlock(&s->session_mtx);
582f2bb1caeSJulian Elischer 		mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
583f2bb1caeSJulian Elischer 
584f2bb1caeSJulian Elischer 		return (EBUSY);
585f2bb1caeSJulian Elischer 	}
586f2bb1caeSJulian Elischer 
587f2bb1caeSJulian Elischer 	/*
588f2bb1caeSJulian Elischer 	 * Check session state and if its not acceptable then refuse connection
589f2bb1caeSJulian Elischer 	 */
590f2bb1caeSJulian Elischer 
591f2bb1caeSJulian Elischer 	switch (s->state) {
592f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_SESSION_CONNECTING:
593f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
594f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_SESSION_OPEN:
595f2bb1caeSJulian Elischer 		/*
596f2bb1caeSJulian Elischer 		 * Update destination address and channel and attach
597f2bb1caeSJulian Elischer 		 * DLC to the session
598f2bb1caeSJulian Elischer 		 */
599f2bb1caeSJulian Elischer 
600f2bb1caeSJulian Elischer 		bcopy(&sa->rfcomm_bdaddr, &pcb->dst, sizeof(pcb->dst));
601f2bb1caeSJulian Elischer 		pcb->channel = sa->rfcomm_channel;
602f2bb1caeSJulian Elischer 		pcb->dlci = dlci;
603f2bb1caeSJulian Elischer 
604f2bb1caeSJulian Elischer 		LIST_INSERT_HEAD(&s->dlcs, pcb, session_next);
605f2bb1caeSJulian Elischer 		pcb->session = s;
606f2bb1caeSJulian Elischer 
607f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_timeout(pcb);
608f2bb1caeSJulian Elischer 		soisconnecting(pcb->so);
609f2bb1caeSJulian Elischer 
610f2bb1caeSJulian Elischer 		if (s->state == NG_BTSOCKET_RFCOMM_SESSION_OPEN) {
611f2bb1caeSJulian Elischer 			pcb->mtu = s->mtu;
612f2bb1caeSJulian Elischer 			bcopy(&so2l2cap_pcb(s->l2so)->src, &pcb->src,
613f2bb1caeSJulian Elischer 				sizeof(pcb->src));
614f2bb1caeSJulian Elischer 
615f2bb1caeSJulian Elischer 			pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONFIGURING;
616f2bb1caeSJulian Elischer 
617f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_send_pn(pcb);
618f2bb1caeSJulian Elischer 			if (error == 0)
619f2bb1caeSJulian Elischer 				error = ng_btsocket_rfcomm_task_wakeup();
620f2bb1caeSJulian Elischer 		} else
621f2bb1caeSJulian Elischer 			pcb->state = NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT;
622f2bb1caeSJulian Elischer 		break;
623f2bb1caeSJulian Elischer 
624f2bb1caeSJulian Elischer 	default:
625f2bb1caeSJulian Elischer 		error = ECONNRESET;
626f2bb1caeSJulian Elischer 		break;
627f2bb1caeSJulian Elischer 	}
628f2bb1caeSJulian Elischer 
629f2bb1caeSJulian Elischer 	mtx_unlock(&pcb->pcb_mtx);
630f2bb1caeSJulian Elischer 	mtx_unlock(&s->session_mtx);
631f2bb1caeSJulian Elischer 	mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
632f2bb1caeSJulian Elischer 
633f2bb1caeSJulian Elischer 	return (error);
634f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_connect */
635f2bb1caeSJulian Elischer 
636f2bb1caeSJulian Elischer /*
637f2bb1caeSJulian Elischer  * Process ioctl's calls on socket.
638f2bb1caeSJulian Elischer  * XXX FIXME this should provide interface to the RFCOMM multiplexor channel
639f2bb1caeSJulian Elischer  */
640f2bb1caeSJulian Elischer 
641f2bb1caeSJulian Elischer int
642f2bb1caeSJulian Elischer ng_btsocket_rfcomm_control(struct socket *so, u_long cmd, caddr_t data,
643f2bb1caeSJulian Elischer 		struct ifnet *ifp, struct thread *td)
644f2bb1caeSJulian Elischer {
645f2bb1caeSJulian Elischer 	return (EINVAL);
646f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_control */
647f2bb1caeSJulian Elischer 
648f2bb1caeSJulian Elischer /*
649f2bb1caeSJulian Elischer  * Process getsockopt/setsockopt system calls
650f2bb1caeSJulian Elischer  */
651f2bb1caeSJulian Elischer 
652f2bb1caeSJulian Elischer int
653f2bb1caeSJulian Elischer ng_btsocket_rfcomm_ctloutput(struct socket *so, struct sockopt *sopt)
654f2bb1caeSJulian Elischer {
655f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p		pcb = so2rfcomm_pcb(so);
656f2bb1caeSJulian Elischer 	struct ng_btsocket_rfcomm_fc_info	fcinfo;
657f2bb1caeSJulian Elischer 	int					error = 0;
658f2bb1caeSJulian Elischer 
659f2bb1caeSJulian Elischer 	if (pcb == NULL)
660f2bb1caeSJulian Elischer 		return (EINVAL);
661f2bb1caeSJulian Elischer 	if (sopt->sopt_level != SOL_RFCOMM)
662f2bb1caeSJulian Elischer 		return (0);
663f2bb1caeSJulian Elischer 
664f2bb1caeSJulian Elischer 	mtx_lock(&pcb->pcb_mtx);
665f2bb1caeSJulian Elischer 
666f2bb1caeSJulian Elischer 	switch (sopt->sopt_dir) {
667f2bb1caeSJulian Elischer 	case SOPT_GET:
668f2bb1caeSJulian Elischer 		switch (sopt->sopt_name) {
669f2bb1caeSJulian Elischer 		case SO_RFCOMM_MTU:
670f2bb1caeSJulian Elischer 			error = sooptcopyout(sopt, &pcb->mtu, sizeof(pcb->mtu));
671f2bb1caeSJulian Elischer 			break;
672f2bb1caeSJulian Elischer 
673f2bb1caeSJulian Elischer 		case SO_RFCOMM_FC_INFO:
674f2bb1caeSJulian Elischer 			fcinfo.lmodem = pcb->lmodem;
675f2bb1caeSJulian Elischer 			fcinfo.rmodem = pcb->rmodem;
676f2bb1caeSJulian Elischer 			fcinfo.tx_cred = pcb->tx_cred;
677f2bb1caeSJulian Elischer 			fcinfo.rx_cred = pcb->rx_cred;
678f2bb1caeSJulian Elischer 			fcinfo.cfc = (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC)?
679f2bb1caeSJulian Elischer 				1 : 0;
680f2bb1caeSJulian Elischer 			fcinfo.reserved = 0;
681f2bb1caeSJulian Elischer 
682f2bb1caeSJulian Elischer 			error = sooptcopyout(sopt, &fcinfo, sizeof(fcinfo));
683f2bb1caeSJulian Elischer 			break;
684f2bb1caeSJulian Elischer 
685f2bb1caeSJulian Elischer 		default:
686f2bb1caeSJulian Elischer 			error = ENOPROTOOPT;
687f2bb1caeSJulian Elischer 			break;
688f2bb1caeSJulian Elischer 		}
689f2bb1caeSJulian Elischer 		break;
690f2bb1caeSJulian Elischer 
691f2bb1caeSJulian Elischer 	case SOPT_SET:
692f2bb1caeSJulian Elischer 		switch (sopt->sopt_name) {
693f2bb1caeSJulian Elischer 		default:
694f2bb1caeSJulian Elischer 			error = ENOPROTOOPT;
695f2bb1caeSJulian Elischer 			break;
696f2bb1caeSJulian Elischer 		}
697f2bb1caeSJulian Elischer 		break;
698f2bb1caeSJulian Elischer 
699f2bb1caeSJulian Elischer 	default:
700f2bb1caeSJulian Elischer 		error = EINVAL;
701f2bb1caeSJulian Elischer 		break;
702f2bb1caeSJulian Elischer 	}
703f2bb1caeSJulian Elischer 
704f2bb1caeSJulian Elischer 	mtx_unlock(&pcb->pcb_mtx);
705f2bb1caeSJulian Elischer 
706f2bb1caeSJulian Elischer 	return (error);
707f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_ctloutput */
708f2bb1caeSJulian Elischer 
709f2bb1caeSJulian Elischer /*
710f2bb1caeSJulian Elischer  * Detach and destroy socket
711f2bb1caeSJulian Elischer  */
712f2bb1caeSJulian Elischer 
713bc725eafSRobert Watson void
714f2bb1caeSJulian Elischer ng_btsocket_rfcomm_detach(struct socket *so)
715f2bb1caeSJulian Elischer {
716f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
717f2bb1caeSJulian Elischer 
718bc725eafSRobert Watson 	KASSERT(pcb != NULL, ("ng_btsocket_rfcomm_detach: pcb == NULL"));
719f2bb1caeSJulian Elischer 
720f2bb1caeSJulian Elischer 	mtx_lock(&pcb->pcb_mtx);
721f2bb1caeSJulian Elischer 
722f2bb1caeSJulian Elischer 	switch (pcb->state) {
723f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
724f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING:
725f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
726f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_DLC_CONNECTED:
727f2bb1caeSJulian Elischer 		/* XXX What to do with pending request? */
728f2bb1caeSJulian Elischer 		if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)
729f2bb1caeSJulian Elischer 			ng_btsocket_rfcomm_untimeout(pcb);
730f2bb1caeSJulian Elischer 
731f2bb1caeSJulian Elischer 		if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT)
732f2bb1caeSJulian Elischer 			pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_DETACHED;
733f2bb1caeSJulian Elischer 		else
734f2bb1caeSJulian Elischer 			pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING;
735f2bb1caeSJulian Elischer 
736f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_task_wakeup();
737f2bb1caeSJulian Elischer 		break;
738f2bb1caeSJulian Elischer 
739f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
740f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_task_wakeup();
741f2bb1caeSJulian Elischer 		break;
742f2bb1caeSJulian Elischer 	}
743f2bb1caeSJulian Elischer 
744f2bb1caeSJulian Elischer 	while (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CLOSED)
745f2bb1caeSJulian Elischer 		msleep(&pcb->state, &pcb->pcb_mtx, PZERO, "rf_det", 0);
746f2bb1caeSJulian Elischer 
747f2bb1caeSJulian Elischer 	if (pcb->session != NULL)
748f2bb1caeSJulian Elischer 		panic("%s: pcb->session != NULL\n", __func__);
749f2bb1caeSJulian Elischer 	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)
750f2bb1caeSJulian Elischer 		panic("%s: timeout on closed DLC, flags=%#x\n",
751f2bb1caeSJulian Elischer 			__func__, pcb->flags);
752f2bb1caeSJulian Elischer 
753f2bb1caeSJulian Elischer 	mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
754f2bb1caeSJulian Elischer 	LIST_REMOVE(pcb, next);
755f2bb1caeSJulian Elischer 	mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
756f2bb1caeSJulian Elischer 
757f2bb1caeSJulian Elischer 	mtx_unlock(&pcb->pcb_mtx);
758f2bb1caeSJulian Elischer 
759f2bb1caeSJulian Elischer 	mtx_destroy(&pcb->pcb_mtx);
760f2bb1caeSJulian Elischer 	bzero(pcb, sizeof(*pcb));
7611ede983cSDag-Erling Smørgrav 	free(pcb, M_NETGRAPH_BTSOCKET_RFCOMM);
762f2bb1caeSJulian Elischer 
763f2bb1caeSJulian Elischer 	soisdisconnected(so);
764f2bb1caeSJulian Elischer 	so->so_pcb = NULL;
765f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_detach */
766f2bb1caeSJulian Elischer 
767f2bb1caeSJulian Elischer /*
768f2bb1caeSJulian Elischer  * Disconnect socket
769f2bb1caeSJulian Elischer  */
770f2bb1caeSJulian Elischer 
771f2bb1caeSJulian Elischer int
772f2bb1caeSJulian Elischer ng_btsocket_rfcomm_disconnect(struct socket *so)
773f2bb1caeSJulian Elischer {
774f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
775f2bb1caeSJulian Elischer 
776f2bb1caeSJulian Elischer 	if (pcb == NULL)
777f2bb1caeSJulian Elischer 		return (EINVAL);
778f2bb1caeSJulian Elischer 
779f2bb1caeSJulian Elischer 	mtx_lock(&pcb->pcb_mtx);
780f2bb1caeSJulian Elischer 
781f2bb1caeSJulian Elischer 	if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING) {
782f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
783f2bb1caeSJulian Elischer 		return (EINPROGRESS);
784f2bb1caeSJulian Elischer 	}
785f2bb1caeSJulian Elischer 
786f2bb1caeSJulian Elischer 	/* XXX What to do with pending request? */
787f2bb1caeSJulian Elischer 	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)
788f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_untimeout(pcb);
789f2bb1caeSJulian Elischer 
790f2bb1caeSJulian Elischer 	switch (pcb->state) {
791f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING: /* XXX can we get here? */
792f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_DLC_CONNECTING: /* XXX can we get here? */
793f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_DLC_CONNECTED:
794f2bb1caeSJulian Elischer 
795f2bb1caeSJulian Elischer 		/*
796f2bb1caeSJulian Elischer 		 * Just change DLC state and enqueue RFCOMM task. It will
797f2bb1caeSJulian Elischer 		 * queue and send DISC on the DLC.
798f2bb1caeSJulian Elischer 		 */
799f2bb1caeSJulian Elischer 
800f2bb1caeSJulian Elischer 		pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING;
801f2bb1caeSJulian Elischer 		soisdisconnecting(so);
802f2bb1caeSJulian Elischer 
803f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_task_wakeup();
804f2bb1caeSJulian Elischer 		break;
805f2bb1caeSJulian Elischer 
8067c380856SMaksim Yevmenkin 	case NG_BTSOCKET_RFCOMM_DLC_CLOSED:
8077c380856SMaksim Yevmenkin 	case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
8087c380856SMaksim Yevmenkin 		break;
8097c380856SMaksim Yevmenkin 
810f2bb1caeSJulian Elischer 	default:
811f2bb1caeSJulian Elischer 		panic("%s: Invalid DLC state=%d, flags=%#x\n",
812f2bb1caeSJulian Elischer 			__func__, pcb->state, pcb->flags);
813f2bb1caeSJulian Elischer 		break;
814f2bb1caeSJulian Elischer 	}
815f2bb1caeSJulian Elischer 
816f2bb1caeSJulian Elischer 	mtx_unlock(&pcb->pcb_mtx);
817f2bb1caeSJulian Elischer 
818f2bb1caeSJulian Elischer 	return (0);
819f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_disconnect */
820f2bb1caeSJulian Elischer 
821f2bb1caeSJulian Elischer /*
822f2bb1caeSJulian Elischer  * Listen on socket. First call to listen() will create listening RFCOMM session
823f2bb1caeSJulian Elischer  */
824f2bb1caeSJulian Elischer 
825f2bb1caeSJulian Elischer int
826d374e81eSRobert Watson ng_btsocket_rfcomm_listen(struct socket *so, int backlog, struct thread *td)
827f2bb1caeSJulian Elischer {
828a6f3c1e3SMaksim Yevmenkin 	ng_btsocket_rfcomm_pcb_p	 pcb = so2rfcomm_pcb(so), pcb1;
829f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_session_p	 s = NULL;
830f2bb1caeSJulian Elischer 	struct socket			*l2so = NULL;
831a6f3c1e3SMaksim Yevmenkin 	int				 error, socreate_error, usedchannels;
832f2bb1caeSJulian Elischer 
833f2bb1caeSJulian Elischer 	if (pcb == NULL)
834f2bb1caeSJulian Elischer 		return (EINVAL);
835a6f3c1e3SMaksim Yevmenkin 	if (pcb->channel > 30)
836d46210e6SMaksim Yevmenkin 		return (EADDRNOTAVAIL);
837f2bb1caeSJulian Elischer 
838a6f3c1e3SMaksim Yevmenkin 	usedchannels = 0;
839a6f3c1e3SMaksim Yevmenkin 
840a6f3c1e3SMaksim Yevmenkin 	mtx_lock(&pcb->pcb_mtx);
841a6f3c1e3SMaksim Yevmenkin 
842a6f3c1e3SMaksim Yevmenkin 	if (pcb->channel == 0) {
843a6f3c1e3SMaksim Yevmenkin 		mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
844a6f3c1e3SMaksim Yevmenkin 
845a6f3c1e3SMaksim Yevmenkin 		LIST_FOREACH(pcb1, &ng_btsocket_rfcomm_sockets, next)
846a6f3c1e3SMaksim Yevmenkin 			if (pcb1->channel != 0 &&
847a6f3c1e3SMaksim Yevmenkin 			    bcmp(&pcb1->src, &pcb->src, sizeof(pcb->src)) == 0)
848a6f3c1e3SMaksim Yevmenkin 				usedchannels |= (1 << (pcb1->channel - 1));
849a6f3c1e3SMaksim Yevmenkin 
850a6f3c1e3SMaksim Yevmenkin 		for (pcb->channel = 30; pcb->channel > 0; pcb->channel --)
851a6f3c1e3SMaksim Yevmenkin 			if (!(usedchannels & (1 << (pcb->channel - 1))))
852a6f3c1e3SMaksim Yevmenkin 				break;
853a6f3c1e3SMaksim Yevmenkin 
854a6f3c1e3SMaksim Yevmenkin 		if (pcb->channel == 0) {
855a6f3c1e3SMaksim Yevmenkin 			mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
856a6f3c1e3SMaksim Yevmenkin 			mtx_unlock(&pcb->pcb_mtx);
857a6f3c1e3SMaksim Yevmenkin 
858a6f3c1e3SMaksim Yevmenkin 			return (EADDRNOTAVAIL);
859a6f3c1e3SMaksim Yevmenkin 		}
860a6f3c1e3SMaksim Yevmenkin 
861a6f3c1e3SMaksim Yevmenkin 		mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
862a6f3c1e3SMaksim Yevmenkin 	}
863a6f3c1e3SMaksim Yevmenkin 
864a6f3c1e3SMaksim Yevmenkin 	mtx_unlock(&pcb->pcb_mtx);
865a6f3c1e3SMaksim Yevmenkin 
866f2bb1caeSJulian Elischer 	/*
867320a8190SMaksim Yevmenkin 	 * Note that we will not check for errors in socreate() because
868320a8190SMaksim Yevmenkin 	 * if we failed to create L2CAP socket at this point we still
869320a8190SMaksim Yevmenkin 	 * might have already open session.
870f2bb1caeSJulian Elischer 	 */
871f2bb1caeSJulian Elischer 
8720daccb9cSRobert Watson 	socreate_error = socreate(PF_BLUETOOTH, &l2so, SOCK_SEQPACKET,
873f2bb1caeSJulian Elischer 			BLUETOOTH_PROTO_L2CAP, td->td_ucred, td);
874f2bb1caeSJulian Elischer 
875f2bb1caeSJulian Elischer 	/*
8760daccb9cSRobert Watson 	 * Transition the socket and session into the LISTENING state.  Check
8770daccb9cSRobert Watson 	 * for collisions first, as there can only be one.
878f2bb1caeSJulian Elischer 	 */
879f2bb1caeSJulian Elischer 	mtx_lock(&ng_btsocket_rfcomm_sessions_mtx);
8800daccb9cSRobert Watson 	SOCK_LOCK(so);
8810daccb9cSRobert Watson 	error = solisten_proto_check(so);
8827c380856SMaksim Yevmenkin 	SOCK_UNLOCK(so);
8830daccb9cSRobert Watson 	if (error != 0)
8840daccb9cSRobert Watson 		goto out;
885f2bb1caeSJulian Elischer 
886f2bb1caeSJulian Elischer 	LIST_FOREACH(s, &ng_btsocket_rfcomm_sessions, next)
887f2bb1caeSJulian Elischer 		if (s->state == NG_BTSOCKET_RFCOMM_SESSION_LISTENING)
888f2bb1caeSJulian Elischer 			break;
889f2bb1caeSJulian Elischer 
890f2bb1caeSJulian Elischer 	if (s == NULL) {
891f2bb1caeSJulian Elischer 		/*
892f2bb1caeSJulian Elischer 		 * We need to create default RFCOMM session. Check if we have
893f2bb1caeSJulian Elischer 		 * L2CAP socket. If l2so == NULL then error has the error code
894f2bb1caeSJulian Elischer 		 * from socreate()
895f2bb1caeSJulian Elischer 		 */
896f2bb1caeSJulian Elischer 		if (l2so == NULL) {
8970daccb9cSRobert Watson 			error = socreate_error;
8980daccb9cSRobert Watson 			goto out;
899f2bb1caeSJulian Elischer 		}
900f2bb1caeSJulian Elischer 
901f2bb1caeSJulian Elischer 		/*
902f2bb1caeSJulian Elischer 		 * Create default listen RFCOMM session. The default RFCOMM
903f2bb1caeSJulian Elischer 		 * session will listen on ANY address.
904f2bb1caeSJulian Elischer 		 *
905f2bb1caeSJulian Elischer 		 * XXX FIXME Note that currently there is no way to adjust MTU
906f2bb1caeSJulian Elischer 		 * for the default session.
907f2bb1caeSJulian Elischer 		 */
908f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_session_create(&s, l2so,
909f2bb1caeSJulian Elischer 					NG_HCI_BDADDR_ANY, NULL, td);
9100daccb9cSRobert Watson 		if (error != 0)
9110daccb9cSRobert Watson 			goto out;
9120daccb9cSRobert Watson 		l2so = NULL;
913f2bb1caeSJulian Elischer 	}
9147c380856SMaksim Yevmenkin 	SOCK_LOCK(so);
915d374e81eSRobert Watson 	solisten_proto(so, backlog);
9160daccb9cSRobert Watson 	SOCK_UNLOCK(so);
9177c380856SMaksim Yevmenkin out:
918f2bb1caeSJulian Elischer 	mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
9190daccb9cSRobert Watson 	/*
9200daccb9cSRobert Watson 	 * If we still have an l2so reference here, it's unneeded, so release
9210daccb9cSRobert Watson 	 * it.
9220daccb9cSRobert Watson 	 */
9230daccb9cSRobert Watson 	if (l2so != NULL)
9240daccb9cSRobert Watson 		soclose(l2so);
9250daccb9cSRobert Watson 	return (error);
926f2bb1caeSJulian Elischer } /* ng_btsocket_listen */
927f2bb1caeSJulian Elischer 
928f2bb1caeSJulian Elischer /*
929f2bb1caeSJulian Elischer  * Get peer address
930f2bb1caeSJulian Elischer  */
931f2bb1caeSJulian Elischer 
932f2bb1caeSJulian Elischer int
933f2bb1caeSJulian Elischer ng_btsocket_rfcomm_peeraddr(struct socket *so, struct sockaddr **nam)
934f2bb1caeSJulian Elischer {
935f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
936f2bb1caeSJulian Elischer 	struct sockaddr_rfcomm		sa;
937f2bb1caeSJulian Elischer 
938f2bb1caeSJulian Elischer 	if (pcb == NULL)
939f2bb1caeSJulian Elischer 		return (EINVAL);
940f2bb1caeSJulian Elischer 
941f2bb1caeSJulian Elischer 	bcopy(&pcb->dst, &sa.rfcomm_bdaddr, sizeof(sa.rfcomm_bdaddr));
942f2bb1caeSJulian Elischer 	sa.rfcomm_channel = pcb->channel;
943f2bb1caeSJulian Elischer 	sa.rfcomm_len = sizeof(sa);
944f2bb1caeSJulian Elischer 	sa.rfcomm_family = AF_BLUETOOTH;
945f2bb1caeSJulian Elischer 
946746e5bf0SRobert Watson 	*nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT);
947f2bb1caeSJulian Elischer 
948f2bb1caeSJulian Elischer 	return ((*nam == NULL)? ENOMEM : 0);
949f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_peeraddr */
950f2bb1caeSJulian Elischer 
951f2bb1caeSJulian Elischer /*
952f2bb1caeSJulian Elischer  * Send data to socket
953f2bb1caeSJulian Elischer  */
954f2bb1caeSJulian Elischer 
955f2bb1caeSJulian Elischer int
956f2bb1caeSJulian Elischer ng_btsocket_rfcomm_send(struct socket *so, int flags, struct mbuf *m,
957f2bb1caeSJulian Elischer 		struct sockaddr *nam, struct mbuf *control, struct thread *td)
958f2bb1caeSJulian Elischer {
959f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_t	*pcb = so2rfcomm_pcb(so);
960f2bb1caeSJulian Elischer 	int				 error = 0;
961f2bb1caeSJulian Elischer 
962f2bb1caeSJulian Elischer 	/* Check socket and input */
963f2bb1caeSJulian Elischer 	if (pcb == NULL || m == NULL || control != NULL) {
964f2bb1caeSJulian Elischer 		error = EINVAL;
965f2bb1caeSJulian Elischer 		goto drop;
966f2bb1caeSJulian Elischer 	}
967f2bb1caeSJulian Elischer 
968f2bb1caeSJulian Elischer 	mtx_lock(&pcb->pcb_mtx);
969f2bb1caeSJulian Elischer 
970f2bb1caeSJulian Elischer 	/* Make sure DLC is connected */
971f2bb1caeSJulian Elischer 	if (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTED) {
972f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
973f2bb1caeSJulian Elischer 		error = ENOTCONN;
974f2bb1caeSJulian Elischer 		goto drop;
975f2bb1caeSJulian Elischer 	}
976f2bb1caeSJulian Elischer 
977f2bb1caeSJulian Elischer 	/* Put the packet on the socket's send queue and wakeup RFCOMM task */
978829fae90SGleb Smirnoff 	sbappend(&pcb->so->so_snd, m, flags);
979f2bb1caeSJulian Elischer 	m = NULL;
980f2bb1caeSJulian Elischer 
981f2bb1caeSJulian Elischer 	if (!(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_SENDING)) {
982f2bb1caeSJulian Elischer 		pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_SENDING;
983f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_task_wakeup();
984f2bb1caeSJulian Elischer 	}
985f2bb1caeSJulian Elischer 
986f2bb1caeSJulian Elischer 	mtx_unlock(&pcb->pcb_mtx);
987f2bb1caeSJulian Elischer drop:
988f2bb1caeSJulian Elischer 	NG_FREE_M(m); /* checks for != NULL */
989f2bb1caeSJulian Elischer 	NG_FREE_M(control);
990f2bb1caeSJulian Elischer 
991f2bb1caeSJulian Elischer 	return (error);
992f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_send */
993f2bb1caeSJulian Elischer 
994f2bb1caeSJulian Elischer /*
995f2bb1caeSJulian Elischer  * Get socket address
996f2bb1caeSJulian Elischer  */
997f2bb1caeSJulian Elischer 
998f2bb1caeSJulian Elischer int
999f2bb1caeSJulian Elischer ng_btsocket_rfcomm_sockaddr(struct socket *so, struct sockaddr **nam)
1000f2bb1caeSJulian Elischer {
1001f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
1002f2bb1caeSJulian Elischer 	struct sockaddr_rfcomm		sa;
1003f2bb1caeSJulian Elischer 
1004f2bb1caeSJulian Elischer 	if (pcb == NULL)
1005f2bb1caeSJulian Elischer 		return (EINVAL);
1006f2bb1caeSJulian Elischer 
1007f2bb1caeSJulian Elischer 	bcopy(&pcb->src, &sa.rfcomm_bdaddr, sizeof(sa.rfcomm_bdaddr));
1008f2bb1caeSJulian Elischer 	sa.rfcomm_channel = pcb->channel;
1009f2bb1caeSJulian Elischer 	sa.rfcomm_len = sizeof(sa);
1010f2bb1caeSJulian Elischer 	sa.rfcomm_family = AF_BLUETOOTH;
1011f2bb1caeSJulian Elischer 
1012746e5bf0SRobert Watson 	*nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT);
1013f2bb1caeSJulian Elischer 
1014f2bb1caeSJulian Elischer 	return ((*nam == NULL)? ENOMEM : 0);
1015f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_sockaddr */
1016f2bb1caeSJulian Elischer 
1017f2bb1caeSJulian Elischer /*
1018f2bb1caeSJulian Elischer  * Upcall function for L2CAP sockets. Enqueue RFCOMM task.
1019f2bb1caeSJulian Elischer  */
1020f2bb1caeSJulian Elischer 
102174fb0ba7SJohn Baldwin static int
1022f2bb1caeSJulian Elischer ng_btsocket_rfcomm_upcall(struct socket *so, void *arg, int waitflag)
1023f2bb1caeSJulian Elischer {
1024f2bb1caeSJulian Elischer 	int	error;
1025f2bb1caeSJulian Elischer 
1026f2bb1caeSJulian Elischer 	if (so == NULL)
1027f2bb1caeSJulian Elischer 		panic("%s: so == NULL\n", __func__);
1028f2bb1caeSJulian Elischer 
1029f2bb1caeSJulian Elischer 	if ((error = ng_btsocket_rfcomm_task_wakeup()) != 0)
1030f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ALERT(
1031f2bb1caeSJulian Elischer "%s: Could not enqueue RFCOMM task, error=%d\n", __func__, error);
103274fb0ba7SJohn Baldwin 	return (SU_OK);
1033f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_upcall */
1034f2bb1caeSJulian Elischer 
1035f2bb1caeSJulian Elischer /*
1036f2bb1caeSJulian Elischer  * RFCOMM task. Will handle all RFCOMM sessions in one pass.
1037f2bb1caeSJulian Elischer  * XXX FIXME does not scale very well
1038f2bb1caeSJulian Elischer  */
1039f2bb1caeSJulian Elischer 
1040f2bb1caeSJulian Elischer static void
1041f2bb1caeSJulian Elischer ng_btsocket_rfcomm_sessions_task(void *ctx, int pending)
1042f2bb1caeSJulian Elischer {
1043f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_session_p	s = NULL, s_next = NULL;
1044f2bb1caeSJulian Elischer 
1045f2bb1caeSJulian Elischer 	mtx_lock(&ng_btsocket_rfcomm_sessions_mtx);
1046f2bb1caeSJulian Elischer 
1047f2bb1caeSJulian Elischer 	for (s = LIST_FIRST(&ng_btsocket_rfcomm_sessions); s != NULL; ) {
1048f2bb1caeSJulian Elischer 		mtx_lock(&s->session_mtx);
1049f2bb1caeSJulian Elischer 		s_next = LIST_NEXT(s, next);
1050f2bb1caeSJulian Elischer 
1051f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_session_task(s);
1052f2bb1caeSJulian Elischer 
1053f2bb1caeSJulian Elischer 		if (s->state == NG_BTSOCKET_RFCOMM_SESSION_CLOSED) {
1054f2bb1caeSJulian Elischer 			/* Unlink and clean the session */
1055f2bb1caeSJulian Elischer 			LIST_REMOVE(s, next);
1056f2bb1caeSJulian Elischer 
1057f2bb1caeSJulian Elischer 			NG_BT_MBUFQ_DRAIN(&s->outq);
1058f2bb1caeSJulian Elischer 			if (!LIST_EMPTY(&s->dlcs))
1059f2bb1caeSJulian Elischer 				panic("%s: DLC list is not empty\n", __func__);
1060f2bb1caeSJulian Elischer 
1061f2bb1caeSJulian Elischer 			/* Close L2CAP socket */
10629535efc0SRobert Watson 			SOCKBUF_LOCK(&s->l2so->so_rcv);
106374fb0ba7SJohn Baldwin 			soupcall_clear(s->l2so, SO_RCV);
10649535efc0SRobert Watson 			SOCKBUF_UNLOCK(&s->l2so->so_rcv);
10659535efc0SRobert Watson 			SOCKBUF_LOCK(&s->l2so->so_snd);
106674fb0ba7SJohn Baldwin 			soupcall_clear(s->l2so, SO_SND);
10679535efc0SRobert Watson 			SOCKBUF_UNLOCK(&s->l2so->so_snd);
1068f2bb1caeSJulian Elischer 			soclose(s->l2so);
1069f2bb1caeSJulian Elischer 
1070f2bb1caeSJulian Elischer 			mtx_unlock(&s->session_mtx);
1071f2bb1caeSJulian Elischer 
1072f2bb1caeSJulian Elischer 			mtx_destroy(&s->session_mtx);
1073f2bb1caeSJulian Elischer 			bzero(s, sizeof(*s));
10741ede983cSDag-Erling Smørgrav 			free(s, M_NETGRAPH_BTSOCKET_RFCOMM);
1075f2bb1caeSJulian Elischer 		} else
1076f2bb1caeSJulian Elischer 			mtx_unlock(&s->session_mtx);
1077f2bb1caeSJulian Elischer 
1078f2bb1caeSJulian Elischer 		s = s_next;
1079f2bb1caeSJulian Elischer 	}
1080f2bb1caeSJulian Elischer 
1081f2bb1caeSJulian Elischer 	mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
1082f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_sessions_task */
1083f2bb1caeSJulian Elischer 
1084f2bb1caeSJulian Elischer /*
1085f2bb1caeSJulian Elischer  * Process RFCOMM session. Will handle all RFCOMM sockets in one pass.
1086f2bb1caeSJulian Elischer  */
1087f2bb1caeSJulian Elischer 
1088f2bb1caeSJulian Elischer static void
1089f2bb1caeSJulian Elischer ng_btsocket_rfcomm_session_task(ng_btsocket_rfcomm_session_p s)
1090f2bb1caeSJulian Elischer {
1091f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
1092f2bb1caeSJulian Elischer 
1093c0b99ffaSRobert Watson 	if (s->l2so->so_rcv.sb_state & SBS_CANTRCVMORE) {
1094f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_INFO(
1095f2bb1caeSJulian Elischer "%s: L2CAP connection has been terminated, so=%p, so_state=%#x, so_count=%d, " \
1096f2bb1caeSJulian Elischer "state=%d, flags=%#x\n", __func__, s->l2so, s->l2so->so_state,
1097f2bb1caeSJulian Elischer 			s->l2so->so_count, s->state, s->flags);
1098f2bb1caeSJulian Elischer 
1099f2bb1caeSJulian Elischer 		s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1100f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_session_clean(s);
1101f2bb1caeSJulian Elischer 	}
1102f2bb1caeSJulian Elischer 
1103f2bb1caeSJulian Elischer 	/* Now process upcall */
1104f2bb1caeSJulian Elischer 	switch (s->state) {
1105f2bb1caeSJulian Elischer 	/* Try to accept new L2CAP connection(s) */
1106f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_SESSION_LISTENING:
1107f2bb1caeSJulian Elischer 		while (ng_btsocket_rfcomm_session_accept(s) == 0)
1108f2bb1caeSJulian Elischer 			;
1109f2bb1caeSJulian Elischer 		break;
1110f2bb1caeSJulian Elischer 
1111f2bb1caeSJulian Elischer 	/* Process the results of the L2CAP connect */
1112f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_SESSION_CONNECTING:
1113f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_session_process_pcb(s);
1114f2bb1caeSJulian Elischer 
1115f2bb1caeSJulian Elischer 		if (ng_btsocket_rfcomm_session_connect(s) != 0) {
1116f2bb1caeSJulian Elischer 			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1117f2bb1caeSJulian Elischer 			ng_btsocket_rfcomm_session_clean(s);
1118f2bb1caeSJulian Elischer 		}
1119f2bb1caeSJulian Elischer 		break;
1120f2bb1caeSJulian Elischer 
1121f2bb1caeSJulian Elischer 	/* Try to receive/send more data */
1122f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
1123f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_SESSION_OPEN:
1124f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING:
1125f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_session_process_pcb(s);
1126f2bb1caeSJulian Elischer 
1127f2bb1caeSJulian Elischer 		if (ng_btsocket_rfcomm_session_receive(s) != 0) {
1128f2bb1caeSJulian Elischer 			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1129f2bb1caeSJulian Elischer 			ng_btsocket_rfcomm_session_clean(s);
1130f2bb1caeSJulian Elischer 		} else if (ng_btsocket_rfcomm_session_send(s) != 0) {
1131f2bb1caeSJulian Elischer 			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1132f2bb1caeSJulian Elischer 			ng_btsocket_rfcomm_session_clean(s);
1133f2bb1caeSJulian Elischer 		}
1134f2bb1caeSJulian Elischer 		break;
1135f2bb1caeSJulian Elischer 
1136f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_SESSION_CLOSED:
1137f2bb1caeSJulian Elischer 		break;
1138f2bb1caeSJulian Elischer 
1139f2bb1caeSJulian Elischer 	default:
1140f2bb1caeSJulian Elischer 		panic("%s: Invalid session state=%d, flags=%#x\n",
1141f2bb1caeSJulian Elischer 			__func__, s->state, s->flags);
1142f2bb1caeSJulian Elischer 		break;
1143f2bb1caeSJulian Elischer 	}
1144f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_session_task */
1145f2bb1caeSJulian Elischer 
1146f2bb1caeSJulian Elischer /*
1147f2bb1caeSJulian Elischer  * Process RFCOMM connection indicator. Caller must hold s->session_mtx
1148f2bb1caeSJulian Elischer  */
1149f2bb1caeSJulian Elischer 
1150f2bb1caeSJulian Elischer static ng_btsocket_rfcomm_pcb_p
1151f2bb1caeSJulian Elischer ng_btsocket_rfcomm_connect_ind(ng_btsocket_rfcomm_session_p s, int channel)
1152f2bb1caeSJulian Elischer {
1153f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	 pcb = NULL, pcb1 = NULL;
1154f2bb1caeSJulian Elischer 	ng_btsocket_l2cap_pcb_p		 l2pcb = NULL;
1155779f106aSGleb Smirnoff 	struct socket			*so1;
1156f2bb1caeSJulian Elischer 
1157f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
1158f2bb1caeSJulian Elischer 
1159f2bb1caeSJulian Elischer 	/*
1160f2bb1caeSJulian Elischer 	 * Try to find RFCOMM socket that listens on given source address
1161f2bb1caeSJulian Elischer 	 * and channel. This will return the best possible match.
1162f2bb1caeSJulian Elischer 	 */
1163f2bb1caeSJulian Elischer 
1164f2bb1caeSJulian Elischer 	l2pcb = so2l2cap_pcb(s->l2so);
1165f2bb1caeSJulian Elischer 	pcb = ng_btsocket_rfcomm_pcb_listener(&l2pcb->src, channel);
1166f2bb1caeSJulian Elischer 	if (pcb == NULL)
1167f2bb1caeSJulian Elischer 		return (NULL);
1168f2bb1caeSJulian Elischer 
1169f2bb1caeSJulian Elischer 	/*
1170f2bb1caeSJulian Elischer 	 * Check the pending connections queue and if we have space then
1171f2bb1caeSJulian Elischer 	 * create new socket and set proper source and destination address,
1172f2bb1caeSJulian Elischer 	 * and channel.
1173f2bb1caeSJulian Elischer 	 */
1174f2bb1caeSJulian Elischer 
1175f2bb1caeSJulian Elischer 	mtx_lock(&pcb->pcb_mtx);
1176f2bb1caeSJulian Elischer 
11771fb51a12SBjoern A. Zeeb 	CURVNET_SET(pcb->so->so_vnet);
1178f2bb1caeSJulian Elischer 	so1 = sonewconn(pcb->so, 0);
11791fb51a12SBjoern A. Zeeb 	CURVNET_RESTORE();
1180f2bb1caeSJulian Elischer 
1181f2bb1caeSJulian Elischer 	mtx_unlock(&pcb->pcb_mtx);
1182f2bb1caeSJulian Elischer 
1183f2bb1caeSJulian Elischer 	if (so1 == NULL)
1184f2bb1caeSJulian Elischer 		return (NULL);
1185f2bb1caeSJulian Elischer 
1186f2bb1caeSJulian Elischer 	/*
1187f2bb1caeSJulian Elischer 	 * If we got here than we have created new socket. So complete the
1188f2bb1caeSJulian Elischer 	 * connection. Set source and destination address from the session.
1189f2bb1caeSJulian Elischer 	 */
1190f2bb1caeSJulian Elischer 
1191f2bb1caeSJulian Elischer 	pcb1 = so2rfcomm_pcb(so1);
1192f2bb1caeSJulian Elischer 	if (pcb1 == NULL)
1193f2bb1caeSJulian Elischer 		panic("%s: pcb1 == NULL\n", __func__);
1194f2bb1caeSJulian Elischer 
1195f2bb1caeSJulian Elischer 	mtx_lock(&pcb1->pcb_mtx);
1196f2bb1caeSJulian Elischer 
1197f2bb1caeSJulian Elischer 	bcopy(&l2pcb->src, &pcb1->src, sizeof(pcb1->src));
1198f2bb1caeSJulian Elischer 	bcopy(&l2pcb->dst, &pcb1->dst, sizeof(pcb1->dst));
1199f2bb1caeSJulian Elischer 	pcb1->channel = channel;
1200f2bb1caeSJulian Elischer 
1201f2bb1caeSJulian Elischer 	/* Link new DLC to the session. We already hold s->session_mtx */
1202f2bb1caeSJulian Elischer 	LIST_INSERT_HEAD(&s->dlcs, pcb1, session_next);
1203f2bb1caeSJulian Elischer 	pcb1->session = s;
1204f2bb1caeSJulian Elischer 
1205f2bb1caeSJulian Elischer 	mtx_unlock(&pcb1->pcb_mtx);
1206f2bb1caeSJulian Elischer 
1207f2bb1caeSJulian Elischer 	return (pcb1);
1208f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_connect_ind */
1209f2bb1caeSJulian Elischer 
1210f2bb1caeSJulian Elischer /*
1211f2bb1caeSJulian Elischer  * Process RFCOMM connect confirmation. Caller must hold s->session_mtx.
1212f2bb1caeSJulian Elischer  */
1213f2bb1caeSJulian Elischer 
1214f2bb1caeSJulian Elischer static void
1215f2bb1caeSJulian Elischer ng_btsocket_rfcomm_connect_cfm(ng_btsocket_rfcomm_session_p s)
1216f2bb1caeSJulian Elischer {
1217f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = NULL, pcb_next = NULL;
1218f2bb1caeSJulian Elischer 	int				error;
1219f2bb1caeSJulian Elischer 
1220f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
1221f2bb1caeSJulian Elischer 
1222f2bb1caeSJulian Elischer 	/*
1223f2bb1caeSJulian Elischer 	 * Wake up all waiting sockets and send PN request for each of them.
1224f2bb1caeSJulian Elischer 	 * Note that timeout already been set in ng_btsocket_rfcomm_connect()
1225f2bb1caeSJulian Elischer 	 *
1226f2bb1caeSJulian Elischer 	 * Note: cannot use LIST_FOREACH because ng_btsocket_rfcomm_pcb_kill
1227f2bb1caeSJulian Elischer 	 * will unlink DLC from the session
1228f2bb1caeSJulian Elischer 	 */
1229f2bb1caeSJulian Elischer 
1230f2bb1caeSJulian Elischer 	for (pcb = LIST_FIRST(&s->dlcs); pcb != NULL; ) {
1231f2bb1caeSJulian Elischer 		mtx_lock(&pcb->pcb_mtx);
1232f2bb1caeSJulian Elischer 		pcb_next = LIST_NEXT(pcb, session_next);
1233f2bb1caeSJulian Elischer 
1234f2bb1caeSJulian Elischer 		if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT) {
1235f2bb1caeSJulian Elischer 			pcb->mtu = s->mtu;
1236f2bb1caeSJulian Elischer 			bcopy(&so2l2cap_pcb(s->l2so)->src, &pcb->src,
1237f2bb1caeSJulian Elischer 				sizeof(pcb->src));
1238f2bb1caeSJulian Elischer 
1239f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_send_pn(pcb);
1240f2bb1caeSJulian Elischer 			if (error == 0)
1241f2bb1caeSJulian Elischer 				pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONFIGURING;
1242f2bb1caeSJulian Elischer 			else
12437c380856SMaksim Yevmenkin 				ng_btsocket_rfcomm_pcb_kill(pcb, error);
1244f2bb1caeSJulian Elischer 		}
1245f2bb1caeSJulian Elischer 
1246f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
1247f2bb1caeSJulian Elischer 		pcb = pcb_next;
1248f2bb1caeSJulian Elischer 	}
1249f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_connect_cfm */
1250f2bb1caeSJulian Elischer 
1251f2bb1caeSJulian Elischer /*****************************************************************************
1252f2bb1caeSJulian Elischer  *****************************************************************************
1253f2bb1caeSJulian Elischer  **                              RFCOMM sessions
1254f2bb1caeSJulian Elischer  *****************************************************************************
1255f2bb1caeSJulian Elischer  *****************************************************************************/
1256f2bb1caeSJulian Elischer 
1257f2bb1caeSJulian Elischer /*
1258f2bb1caeSJulian Elischer  * Create new RFCOMM session. That function WILL NOT take ownership over l2so.
1259f2bb1caeSJulian Elischer  * Caller MUST free l2so if function failed.
1260f2bb1caeSJulian Elischer  */
1261f2bb1caeSJulian Elischer 
1262f2bb1caeSJulian Elischer static int
1263f2bb1caeSJulian Elischer ng_btsocket_rfcomm_session_create(ng_btsocket_rfcomm_session_p *sp,
1264f2bb1caeSJulian Elischer 		struct socket *l2so, bdaddr_p src, bdaddr_p dst,
1265f2bb1caeSJulian Elischer 		struct thread *td)
1266f2bb1caeSJulian Elischer {
1267f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_session_p	s = NULL;
1268f2bb1caeSJulian Elischer 	struct sockaddr_l2cap		l2sa;
1269f2bb1caeSJulian Elischer 	struct sockopt			l2sopt;
1270231e9556SMaksim Yevmenkin 	int				error;
1271231e9556SMaksim Yevmenkin 	u_int16_t			mtu;
1272f2bb1caeSJulian Elischer 
1273f2bb1caeSJulian Elischer 	mtx_assert(&ng_btsocket_rfcomm_sessions_mtx, MA_OWNED);
1274f2bb1caeSJulian Elischer 
1275f2bb1caeSJulian Elischer 	/* Allocate the RFCOMM session */
12761ede983cSDag-Erling Smørgrav         s = malloc(sizeof(*s),
1277f2bb1caeSJulian Elischer 		M_NETGRAPH_BTSOCKET_RFCOMM, M_NOWAIT | M_ZERO);
1278f2bb1caeSJulian Elischer         if (s == NULL)
1279f2bb1caeSJulian Elischer                 return (ENOMEM);
1280f2bb1caeSJulian Elischer 
1281f2bb1caeSJulian Elischer 	/* Set defaults */
1282f2bb1caeSJulian Elischer 	s->mtu = RFCOMM_DEFAULT_MTU;
1283f2bb1caeSJulian Elischer 	s->flags = 0;
1284f2bb1caeSJulian Elischer 	s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1285f2bb1caeSJulian Elischer 	NG_BT_MBUFQ_INIT(&s->outq, ifqmaxlen);
1286f2bb1caeSJulian Elischer 
1287f2bb1caeSJulian Elischer 	/*
1288f2bb1caeSJulian Elischer 	 * XXX Mark session mutex as DUPOK to prevent "duplicated lock of
1289f2bb1caeSJulian Elischer 	 * the same type" message. When accepting new L2CAP connection
1290f2bb1caeSJulian Elischer 	 * ng_btsocket_rfcomm_session_accept() holds both session mutexes
1291f2bb1caeSJulian Elischer 	 * for "old" (accepting) session and "new" (created) session.
1292f2bb1caeSJulian Elischer 	 */
1293f2bb1caeSJulian Elischer 
1294f2bb1caeSJulian Elischer 	mtx_init(&s->session_mtx, "btsocks_rfcomm_session_mtx", NULL,
1295f2bb1caeSJulian Elischer 		MTX_DEF|MTX_DUPOK);
1296f2bb1caeSJulian Elischer 
1297f2bb1caeSJulian Elischer 	LIST_INIT(&s->dlcs);
1298f2bb1caeSJulian Elischer 
1299f2bb1caeSJulian Elischer 	/* Prepare L2CAP socket */
13009535efc0SRobert Watson 	SOCKBUF_LOCK(&l2so->so_rcv);
130174fb0ba7SJohn Baldwin 	soupcall_set(l2so, SO_RCV, ng_btsocket_rfcomm_upcall, NULL);
13029535efc0SRobert Watson 	SOCKBUF_UNLOCK(&l2so->so_rcv);
13039535efc0SRobert Watson 	SOCKBUF_LOCK(&l2so->so_snd);
130474fb0ba7SJohn Baldwin 	soupcall_set(l2so, SO_SND, ng_btsocket_rfcomm_upcall, NULL);
13059535efc0SRobert Watson 	SOCKBUF_UNLOCK(&l2so->so_snd);
1306f2bb1caeSJulian Elischer 	l2so->so_state |= SS_NBIO;
1307f2bb1caeSJulian Elischer 	s->l2so = l2so;
1308f2bb1caeSJulian Elischer 
1309f2bb1caeSJulian Elischer 	mtx_lock(&s->session_mtx);
1310f2bb1caeSJulian Elischer 
1311f2bb1caeSJulian Elischer 	/*
1312f2bb1caeSJulian Elischer 	 * "src" == NULL and "dst" == NULL means just create session.
1313f2bb1caeSJulian Elischer 	 * caller must do the rest
1314f2bb1caeSJulian Elischer 	 */
1315f2bb1caeSJulian Elischer 
1316f2bb1caeSJulian Elischer 	if (src == NULL && dst == NULL)
1317f2bb1caeSJulian Elischer 		goto done;
1318f2bb1caeSJulian Elischer 
1319f2bb1caeSJulian Elischer 	/*
1320f2bb1caeSJulian Elischer 	 * Set incoming MTU on L2CAP socket. It is RFCOMM session default MTU
1321f2bb1caeSJulian Elischer 	 * plus 5 bytes: RFCOMM frame header, one extra byte for length and one
1322f2bb1caeSJulian Elischer 	 * extra byte for credits.
1323f2bb1caeSJulian Elischer 	 */
1324f2bb1caeSJulian Elischer 
1325f2bb1caeSJulian Elischer 	mtu = s->mtu + sizeof(struct rfcomm_frame_hdr) + 1 + 1;
1326f2bb1caeSJulian Elischer 
1327f2bb1caeSJulian Elischer 	l2sopt.sopt_dir = SOPT_SET;
1328f2bb1caeSJulian Elischer 	l2sopt.sopt_level = SOL_L2CAP;
1329f2bb1caeSJulian Elischer 	l2sopt.sopt_name = SO_L2CAP_IMTU;
1330f2bb1caeSJulian Elischer 	l2sopt.sopt_val = (void *) &mtu;
1331f2bb1caeSJulian Elischer 	l2sopt.sopt_valsize = sizeof(mtu);
1332f2bb1caeSJulian Elischer 	l2sopt.sopt_td = NULL;
1333f2bb1caeSJulian Elischer 
1334f2bb1caeSJulian Elischer 	error = sosetopt(s->l2so, &l2sopt);
1335f2bb1caeSJulian Elischer 	if (error != 0)
1336f2bb1caeSJulian Elischer 		goto bad;
1337f2bb1caeSJulian Elischer 
1338f2bb1caeSJulian Elischer 	/* Bind socket to "src" address */
1339f2bb1caeSJulian Elischer 	l2sa.l2cap_len = sizeof(l2sa);
1340f2bb1caeSJulian Elischer 	l2sa.l2cap_family = AF_BLUETOOTH;
1341f2bb1caeSJulian Elischer 	l2sa.l2cap_psm = (dst == NULL)? htole16(NG_L2CAP_PSM_RFCOMM) : 0;
1342f2bb1caeSJulian Elischer 	bcopy(src, &l2sa.l2cap_bdaddr, sizeof(l2sa.l2cap_bdaddr));
134399043514STakanori Watanabe 	l2sa.l2cap_cid = 0;
134499043514STakanori Watanabe 	l2sa.l2cap_bdaddr_type = BDADDR_BREDR;
1345f2bb1caeSJulian Elischer 
1346f2bb1caeSJulian Elischer 	error = sobind(s->l2so, (struct sockaddr *) &l2sa, td);
1347f2bb1caeSJulian Elischer 	if (error != 0)
1348f2bb1caeSJulian Elischer 		goto bad;
1349f2bb1caeSJulian Elischer 
1350f2bb1caeSJulian Elischer 	/* If "dst" is not NULL then initiate connect(), otherwise listen() */
1351f2bb1caeSJulian Elischer 	if (dst == NULL) {
1352f2bb1caeSJulian Elischer 		s->flags = 0;
1353f2bb1caeSJulian Elischer 		s->state = NG_BTSOCKET_RFCOMM_SESSION_LISTENING;
1354f2bb1caeSJulian Elischer 
1355f2bb1caeSJulian Elischer 		error = solisten(s->l2so, 10, td);
1356f2bb1caeSJulian Elischer 		if (error != 0)
1357f2bb1caeSJulian Elischer 			goto bad;
1358f2bb1caeSJulian Elischer 	} else {
1359f2bb1caeSJulian Elischer 		s->flags = NG_BTSOCKET_RFCOMM_SESSION_INITIATOR;
1360f2bb1caeSJulian Elischer 		s->state = NG_BTSOCKET_RFCOMM_SESSION_CONNECTING;
1361f2bb1caeSJulian Elischer 
1362f2bb1caeSJulian Elischer 		l2sa.l2cap_len = sizeof(l2sa);
1363f2bb1caeSJulian Elischer 		l2sa.l2cap_family = AF_BLUETOOTH;
1364f2bb1caeSJulian Elischer 		l2sa.l2cap_psm = htole16(NG_L2CAP_PSM_RFCOMM);
1365f2bb1caeSJulian Elischer 	        bcopy(dst, &l2sa.l2cap_bdaddr, sizeof(l2sa.l2cap_bdaddr));
136699043514STakanori Watanabe 		l2sa.l2cap_cid = 0;
136799043514STakanori Watanabe 		l2sa.l2cap_bdaddr_type = BDADDR_BREDR;
1368f2bb1caeSJulian Elischer 
1369f2bb1caeSJulian Elischer 		error = soconnect(s->l2so, (struct sockaddr *) &l2sa, td);
1370f2bb1caeSJulian Elischer 		if (error != 0)
1371f2bb1caeSJulian Elischer 			goto bad;
1372f2bb1caeSJulian Elischer 	}
1373f2bb1caeSJulian Elischer 
1374f2bb1caeSJulian Elischer done:
1375f2bb1caeSJulian Elischer 	LIST_INSERT_HEAD(&ng_btsocket_rfcomm_sessions, s, next);
1376f2bb1caeSJulian Elischer 	*sp = s;
1377f2bb1caeSJulian Elischer 
1378f2bb1caeSJulian Elischer 	mtx_unlock(&s->session_mtx);
1379f2bb1caeSJulian Elischer 
1380f2bb1caeSJulian Elischer 	return (0);
1381f2bb1caeSJulian Elischer 
1382f2bb1caeSJulian Elischer bad:
1383f2bb1caeSJulian Elischer 	mtx_unlock(&s->session_mtx);
1384f2bb1caeSJulian Elischer 
1385f2bb1caeSJulian Elischer 	/* Return L2CAP socket back to its original state */
13869535efc0SRobert Watson 	SOCKBUF_LOCK(&l2so->so_rcv);
138774fb0ba7SJohn Baldwin 	soupcall_clear(s->l2so, SO_RCV);
138868548aa4SRobert Watson 	SOCKBUF_UNLOCK(&l2so->so_rcv);
13899535efc0SRobert Watson 	SOCKBUF_LOCK(&l2so->so_snd);
139074fb0ba7SJohn Baldwin 	soupcall_clear(s->l2so, SO_SND);
139168548aa4SRobert Watson 	SOCKBUF_UNLOCK(&l2so->so_snd);
1392f2bb1caeSJulian Elischer 	l2so->so_state &= ~SS_NBIO;
1393f2bb1caeSJulian Elischer 
1394f2bb1caeSJulian Elischer 	mtx_destroy(&s->session_mtx);
1395f2bb1caeSJulian Elischer 	bzero(s, sizeof(*s));
13961ede983cSDag-Erling Smørgrav 	free(s, M_NETGRAPH_BTSOCKET_RFCOMM);
1397f2bb1caeSJulian Elischer 
1398f2bb1caeSJulian Elischer 	return (error);
1399f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_session_create */
1400f2bb1caeSJulian Elischer 
1401f2bb1caeSJulian Elischer /*
1402f2bb1caeSJulian Elischer  * Process accept() on RFCOMM session
1403f2bb1caeSJulian Elischer  * XXX FIXME locking for "l2so"?
1404f2bb1caeSJulian Elischer  */
1405f2bb1caeSJulian Elischer 
1406f2bb1caeSJulian Elischer static int
1407f2bb1caeSJulian Elischer ng_btsocket_rfcomm_session_accept(ng_btsocket_rfcomm_session_p s0)
1408f2bb1caeSJulian Elischer {
1409779f106aSGleb Smirnoff 	struct socket			*l2so;
1410f2bb1caeSJulian Elischer 	struct sockaddr_l2cap		*l2sa = NULL;
1411f2bb1caeSJulian Elischer 	ng_btsocket_l2cap_pcb_t		*l2pcb = NULL;
1412f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_session_p	 s = NULL;
1413779f106aSGleb Smirnoff 	int				 error;
1414f2bb1caeSJulian Elischer 
1415f2bb1caeSJulian Elischer 	mtx_assert(&ng_btsocket_rfcomm_sessions_mtx, MA_OWNED);
1416f2bb1caeSJulian Elischer 	mtx_assert(&s0->session_mtx, MA_OWNED);
1417f2bb1caeSJulian Elischer 
1418779f106aSGleb Smirnoff 	SOLISTEN_LOCK(s0->l2so);
1419779f106aSGleb Smirnoff 	error = solisten_dequeue(s0->l2so, &l2so, 0);
1420779f106aSGleb Smirnoff 	if (error == EWOULDBLOCK)
1421779f106aSGleb Smirnoff 		return (error);
1422779f106aSGleb Smirnoff 	if (error) {
1423f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ERR(
1424f2bb1caeSJulian Elischer "%s: Could not accept connection on L2CAP socket, error=%d\n", __func__, error);
1425f2bb1caeSJulian Elischer 		return (error);
1426f2bb1caeSJulian Elischer 	}
1427f2bb1caeSJulian Elischer 
1428f2bb1caeSJulian Elischer 	error = soaccept(l2so, (struct sockaddr **) &l2sa);
1429f2bb1caeSJulian Elischer 	if (error != 0) {
1430f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ERR(
1431f2bb1caeSJulian Elischer "%s: soaccept() on L2CAP socket failed, error=%d\n", __func__, error);
1432f2bb1caeSJulian Elischer 		soclose(l2so);
1433f2bb1caeSJulian Elischer 
1434f2bb1caeSJulian Elischer 		return (error);
1435f2bb1caeSJulian Elischer 	}
1436f2bb1caeSJulian Elischer 
1437f2bb1caeSJulian Elischer 	/*
1438f2bb1caeSJulian Elischer 	 * Check if there is already active RFCOMM session between two devices.
1439f2bb1caeSJulian Elischer 	 * If so then close L2CAP connection. We only support one RFCOMM session
1440f2bb1caeSJulian Elischer 	 * between each pair of devices. Note that here we assume session in any
1441f2bb1caeSJulian Elischer 	 * state. The session even could be in the middle of disconnecting.
1442f2bb1caeSJulian Elischer 	 */
1443f2bb1caeSJulian Elischer 
1444f2bb1caeSJulian Elischer 	l2pcb = so2l2cap_pcb(l2so);
1445f2bb1caeSJulian Elischer 	s = ng_btsocket_rfcomm_session_by_addr(&l2pcb->src, &l2pcb->dst);
1446f2bb1caeSJulian Elischer 	if (s == NULL) {
1447f2bb1caeSJulian Elischer 		/* Create a new RFCOMM session */
1448f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_session_create(&s, l2so, NULL, NULL,
1449f2bb1caeSJulian Elischer 				curthread /* XXX */);
1450f2bb1caeSJulian Elischer 		if (error == 0) {
1451f2bb1caeSJulian Elischer 			mtx_lock(&s->session_mtx);
1452f2bb1caeSJulian Elischer 
1453f2bb1caeSJulian Elischer 			s->flags = 0;
1454f2bb1caeSJulian Elischer 			s->state = NG_BTSOCKET_RFCOMM_SESSION_CONNECTED;
1455f2bb1caeSJulian Elischer 
1456f2bb1caeSJulian Elischer 			/*
1457053359b7SPedro F. Giffuni 			 * Adjust MTU on incoming connection. Reserve 5 bytes:
1458f2bb1caeSJulian Elischer 			 * RFCOMM frame header, one extra byte for length and
1459f2bb1caeSJulian Elischer 			 * one extra byte for credits.
1460f2bb1caeSJulian Elischer 			 */
1461f2bb1caeSJulian Elischer 
1462f2bb1caeSJulian Elischer 			s->mtu = min(l2pcb->imtu, l2pcb->omtu) -
1463f2bb1caeSJulian Elischer 					sizeof(struct rfcomm_frame_hdr) - 1 - 1;
1464f2bb1caeSJulian Elischer 
1465f2bb1caeSJulian Elischer 			mtx_unlock(&s->session_mtx);
1466f2bb1caeSJulian Elischer 		} else {
1467f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_ALERT(
1468f2bb1caeSJulian Elischer "%s: Failed to create new RFCOMM session, error=%d\n", __func__, error);
1469f2bb1caeSJulian Elischer 
1470f2bb1caeSJulian Elischer 			soclose(l2so);
1471f2bb1caeSJulian Elischer 		}
1472f2bb1caeSJulian Elischer 	} else {
1473f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_WARN(
1474f2bb1caeSJulian Elischer "%s: Rejecting duplicating RFCOMM session between src=%x:%x:%x:%x:%x:%x and " \
1475f2bb1caeSJulian Elischer "dst=%x:%x:%x:%x:%x:%x, state=%d, flags=%#x\n",	__func__,
1476f2bb1caeSJulian Elischer 			l2pcb->src.b[5], l2pcb->src.b[4], l2pcb->src.b[3],
1477f2bb1caeSJulian Elischer 			l2pcb->src.b[2], l2pcb->src.b[1], l2pcb->src.b[0],
1478f2bb1caeSJulian Elischer 			l2pcb->dst.b[5], l2pcb->dst.b[4], l2pcb->dst.b[3],
1479f2bb1caeSJulian Elischer 			l2pcb->dst.b[2], l2pcb->dst.b[1], l2pcb->dst.b[0],
1480f2bb1caeSJulian Elischer 			s->state, s->flags);
1481f2bb1caeSJulian Elischer 
1482f2bb1caeSJulian Elischer 		error = EBUSY;
1483f2bb1caeSJulian Elischer 		soclose(l2so);
1484f2bb1caeSJulian Elischer 	}
1485f2bb1caeSJulian Elischer 
1486f2bb1caeSJulian Elischer 	return (error);
1487f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_session_accept */
1488f2bb1caeSJulian Elischer 
1489f2bb1caeSJulian Elischer /*
1490f2bb1caeSJulian Elischer  * Process connect() on RFCOMM session
1491f2bb1caeSJulian Elischer  * XXX FIXME locking for "l2so"?
1492f2bb1caeSJulian Elischer  */
1493f2bb1caeSJulian Elischer 
1494f2bb1caeSJulian Elischer static int
1495f2bb1caeSJulian Elischer ng_btsocket_rfcomm_session_connect(ng_btsocket_rfcomm_session_p s)
1496f2bb1caeSJulian Elischer {
1497f2bb1caeSJulian Elischer 	ng_btsocket_l2cap_pcb_p	l2pcb = so2l2cap_pcb(s->l2so);
1498f2bb1caeSJulian Elischer 	int			error;
1499f2bb1caeSJulian Elischer 
1500f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
1501f2bb1caeSJulian Elischer 
1502f2bb1caeSJulian Elischer 	/* First check if connection has failed */
1503f2bb1caeSJulian Elischer 	if ((error = s->l2so->so_error) != 0) {
1504f2bb1caeSJulian Elischer 		s->l2so->so_error = 0;
1505f2bb1caeSJulian Elischer 
1506f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ERR(
1507f2bb1caeSJulian Elischer "%s: Could not connect RFCOMM session, error=%d, state=%d, flags=%#x\n",
1508f2bb1caeSJulian Elischer 			__func__, error, s->state, s->flags);
1509f2bb1caeSJulian Elischer 
1510f2bb1caeSJulian Elischer 		return (error);
1511f2bb1caeSJulian Elischer 	}
1512f2bb1caeSJulian Elischer 
1513f2bb1caeSJulian Elischer 	/* Is connection still in progress? */
1514f2bb1caeSJulian Elischer 	if (s->l2so->so_state & SS_ISCONNECTING)
1515f2bb1caeSJulian Elischer 		return (0);
1516f2bb1caeSJulian Elischer 
1517f2bb1caeSJulian Elischer 	/*
1518f2bb1caeSJulian Elischer 	 * If we got here then we are connected. Send SABM on DLCI 0 to
1519f2bb1caeSJulian Elischer 	 * open multiplexor channel.
1520f2bb1caeSJulian Elischer 	 */
1521f2bb1caeSJulian Elischer 
1522f2bb1caeSJulian Elischer 	if (error == 0) {
1523f2bb1caeSJulian Elischer 		s->state = NG_BTSOCKET_RFCOMM_SESSION_CONNECTED;
1524f2bb1caeSJulian Elischer 
1525f2bb1caeSJulian Elischer 		/*
1526f2bb1caeSJulian Elischer 		 * Adjust MTU on outgoing connection. Reserve 5 bytes: RFCOMM
1527f2bb1caeSJulian Elischer 		 * frame header, one extra byte for length and one extra byte
1528f2bb1caeSJulian Elischer 		 * for credits.
1529f2bb1caeSJulian Elischer 		 */
1530f2bb1caeSJulian Elischer 
1531f2bb1caeSJulian Elischer 		s->mtu = min(l2pcb->imtu, l2pcb->omtu) -
1532f2bb1caeSJulian Elischer 				sizeof(struct rfcomm_frame_hdr) - 1 - 1;
1533f2bb1caeSJulian Elischer 
1534f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_SABM,0);
1535f2bb1caeSJulian Elischer 		if (error == 0)
1536f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_task_wakeup();
1537f2bb1caeSJulian Elischer 	}
1538f2bb1caeSJulian Elischer 
1539f2bb1caeSJulian Elischer 	return (error);
1540f2bb1caeSJulian Elischer }/* ng_btsocket_rfcomm_session_connect */
1541f2bb1caeSJulian Elischer 
1542f2bb1caeSJulian Elischer /*
1543f2bb1caeSJulian Elischer  * Receive data on RFCOMM session
1544f2bb1caeSJulian Elischer  * XXX FIXME locking for "l2so"?
1545f2bb1caeSJulian Elischer  */
1546f2bb1caeSJulian Elischer 
1547f2bb1caeSJulian Elischer static int
1548f2bb1caeSJulian Elischer ng_btsocket_rfcomm_session_receive(ng_btsocket_rfcomm_session_p s)
1549f2bb1caeSJulian Elischer {
1550f2bb1caeSJulian Elischer 	struct mbuf	*m = NULL;
1551f2bb1caeSJulian Elischer 	struct uio	 uio;
1552f2bb1caeSJulian Elischer 	int		 more, flags, error;
1553f2bb1caeSJulian Elischer 
1554f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
1555f2bb1caeSJulian Elischer 
1556f2bb1caeSJulian Elischer 	/* Can we read from the L2CAP socket? */
1557f2bb1caeSJulian Elischer 	if (!soreadable(s->l2so))
1558f2bb1caeSJulian Elischer 		return (0);
1559f2bb1caeSJulian Elischer 
1560f2bb1caeSJulian Elischer 	/* First check for error on L2CAP socket */
1561f2bb1caeSJulian Elischer 	if ((error = s->l2so->so_error) != 0) {
1562f2bb1caeSJulian Elischer 		s->l2so->so_error = 0;
1563f2bb1caeSJulian Elischer 
1564f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ERR(
1565f2bb1caeSJulian Elischer "%s: Could not receive data from L2CAP socket, error=%d, state=%d, flags=%#x\n",
1566f2bb1caeSJulian Elischer 			__func__, error, s->state, s->flags);
1567f2bb1caeSJulian Elischer 
1568f2bb1caeSJulian Elischer 		return (error);
1569f2bb1caeSJulian Elischer 	}
1570f2bb1caeSJulian Elischer 
1571f2bb1caeSJulian Elischer 	/*
1572f2bb1caeSJulian Elischer 	 * Read all packets from the L2CAP socket.
1573f2bb1caeSJulian Elischer 	 * XXX FIXME/VERIFY is that correct? For now use m->m_nextpkt as
1574f2bb1caeSJulian Elischer 	 * indication that there is more packets on the socket's buffer.
1575f2bb1caeSJulian Elischer 	 * Also what should we use in uio.uio_resid?
1576f2bb1caeSJulian Elischer 	 * May be s->mtu + sizeof(struct rfcomm_frame_hdr) + 1 + 1?
1577f2bb1caeSJulian Elischer 	 */
1578f2bb1caeSJulian Elischer 
1579f2bb1caeSJulian Elischer 	for (more = 1; more; ) {
1580f2bb1caeSJulian Elischer 		/* Try to get next packet from socket */
1581f2bb1caeSJulian Elischer 		bzero(&uio, sizeof(uio));
1582f2bb1caeSJulian Elischer /*		uio.uio_td = NULL; */
1583f2bb1caeSJulian Elischer 		uio.uio_resid = 1000000000;
1584f2bb1caeSJulian Elischer 		flags = MSG_DONTWAIT;
1585f2bb1caeSJulian Elischer 
1586f2bb1caeSJulian Elischer 		m = NULL;
1587b0668f71SRobert Watson 		error = soreceive(s->l2so, NULL, &uio, &m,
1588b0668f71SRobert Watson 		    (struct mbuf **) NULL, &flags);
1589f2bb1caeSJulian Elischer 		if (error != 0) {
1590f2bb1caeSJulian Elischer 			if (error == EWOULDBLOCK)
1591f2bb1caeSJulian Elischer 				return (0); /* XXX can happen? */
1592f2bb1caeSJulian Elischer 
1593f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_ERR(
1594f2bb1caeSJulian Elischer "%s: Could not receive data from L2CAP socket, error=%d\n", __func__, error);
1595f2bb1caeSJulian Elischer 
1596f2bb1caeSJulian Elischer 			return (error);
1597f2bb1caeSJulian Elischer 		}
1598f2bb1caeSJulian Elischer 
1599f2bb1caeSJulian Elischer 		more = (m->m_nextpkt != NULL);
1600f2bb1caeSJulian Elischer 		m->m_nextpkt = NULL;
1601f2bb1caeSJulian Elischer 
1602f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_receive_frame(s, m);
1603f2bb1caeSJulian Elischer 	}
1604f2bb1caeSJulian Elischer 
1605f2bb1caeSJulian Elischer 	return (0);
1606f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_session_receive */
1607f2bb1caeSJulian Elischer 
1608f2bb1caeSJulian Elischer /*
1609f2bb1caeSJulian Elischer  * Send data on RFCOMM session
1610f2bb1caeSJulian Elischer  * XXX FIXME locking for "l2so"?
1611f2bb1caeSJulian Elischer  */
1612f2bb1caeSJulian Elischer 
1613f2bb1caeSJulian Elischer static int
1614f2bb1caeSJulian Elischer ng_btsocket_rfcomm_session_send(ng_btsocket_rfcomm_session_p s)
1615f2bb1caeSJulian Elischer {
1616f2bb1caeSJulian Elischer 	struct mbuf	*m = NULL;
1617f2bb1caeSJulian Elischer 	int		 error;
1618f2bb1caeSJulian Elischer 
1619f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
1620f2bb1caeSJulian Elischer 
1621f2bb1caeSJulian Elischer 	/* Send as much as we can from the session queue */
1622f2bb1caeSJulian Elischer 	while (sowriteable(s->l2so)) {
1623f2bb1caeSJulian Elischer 		/* Check if socket still OK */
1624f2bb1caeSJulian Elischer 		if ((error = s->l2so->so_error) != 0) {
1625f2bb1caeSJulian Elischer 			s->l2so->so_error = 0;
1626f2bb1caeSJulian Elischer 
1627f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_ERR(
1628f2bb1caeSJulian Elischer "%s: Detected error=%d on L2CAP socket, state=%d, flags=%#x\n",
1629f2bb1caeSJulian Elischer 				__func__, error, s->state, s->flags);
1630f2bb1caeSJulian Elischer 
1631f2bb1caeSJulian Elischer 			return (error);
1632f2bb1caeSJulian Elischer 		}
1633f2bb1caeSJulian Elischer 
1634f2bb1caeSJulian Elischer 		NG_BT_MBUFQ_DEQUEUE(&s->outq, m);
1635f2bb1caeSJulian Elischer 		if (m == NULL)
1636f2bb1caeSJulian Elischer 			return (0); /* we are done */
1637f2bb1caeSJulian Elischer 
1638f2bb1caeSJulian Elischer 		/* Call send function on the L2CAP socket */
1639280c458aSMaksim Yevmenkin 		error = (*s->l2so->so_proto->pr_usrreqs->pru_send)(s->l2so,
1640280c458aSMaksim Yevmenkin 				0, m, NULL, NULL, curthread /* XXX */);
1641f2bb1caeSJulian Elischer 		if (error != 0) {
1642f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_ERR(
1643f2bb1caeSJulian Elischer "%s: Could not send data to L2CAP socket, error=%d\n", __func__, error);
1644f2bb1caeSJulian Elischer 
1645f2bb1caeSJulian Elischer 			return (error);
1646f2bb1caeSJulian Elischer 		}
1647f2bb1caeSJulian Elischer 	}
1648f2bb1caeSJulian Elischer 
1649f2bb1caeSJulian Elischer 	return (0);
1650f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_session_send */
1651f2bb1caeSJulian Elischer 
1652f2bb1caeSJulian Elischer /*
1653f2bb1caeSJulian Elischer  * Close and disconnect all DLCs for the given session. Caller must hold
1654f2bb1caeSJulian Elischer  * s->sesson_mtx. Will wakeup session.
1655f2bb1caeSJulian Elischer  */
1656f2bb1caeSJulian Elischer 
1657f2bb1caeSJulian Elischer static void
1658f2bb1caeSJulian Elischer ng_btsocket_rfcomm_session_clean(ng_btsocket_rfcomm_session_p s)
1659f2bb1caeSJulian Elischer {
1660f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = NULL, pcb_next = NULL;
1661f2bb1caeSJulian Elischer 	int				error;
1662f2bb1caeSJulian Elischer 
1663f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
1664f2bb1caeSJulian Elischer 
1665f2bb1caeSJulian Elischer 	/*
1666f2bb1caeSJulian Elischer 	 * Note: cannot use LIST_FOREACH because ng_btsocket_rfcomm_pcb_kill
1667f2bb1caeSJulian Elischer 	 * will unlink DLC from the session
1668f2bb1caeSJulian Elischer 	 */
1669f2bb1caeSJulian Elischer 
1670f2bb1caeSJulian Elischer 	for (pcb = LIST_FIRST(&s->dlcs); pcb != NULL; ) {
1671f2bb1caeSJulian Elischer 		mtx_lock(&pcb->pcb_mtx);
1672f2bb1caeSJulian Elischer 		pcb_next = LIST_NEXT(pcb, session_next);
1673f2bb1caeSJulian Elischer 
1674f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_INFO(
1675f2bb1caeSJulian Elischer "%s: Disconnecting dlci=%d, state=%d, flags=%#x\n",
1676f2bb1caeSJulian Elischer 			__func__, pcb->dlci, pcb->state, pcb->flags);
1677f2bb1caeSJulian Elischer 
1678f2bb1caeSJulian Elischer 		if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_CONNECTED)
1679f2bb1caeSJulian Elischer 			error = ECONNRESET;
1680f2bb1caeSJulian Elischer 		else
1681f2bb1caeSJulian Elischer 			error = ECONNREFUSED;
1682f2bb1caeSJulian Elischer 
16837c380856SMaksim Yevmenkin 		ng_btsocket_rfcomm_pcb_kill(pcb, error);
1684f2bb1caeSJulian Elischer 
1685f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
1686f2bb1caeSJulian Elischer 		pcb = pcb_next;
1687f2bb1caeSJulian Elischer 	}
1688f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_session_clean */
1689f2bb1caeSJulian Elischer 
1690f2bb1caeSJulian Elischer /*
1691f2bb1caeSJulian Elischer  * Process all DLCs on the session. Caller MUST hold s->session_mtx.
1692f2bb1caeSJulian Elischer  */
1693f2bb1caeSJulian Elischer 
1694f2bb1caeSJulian Elischer static void
1695f2bb1caeSJulian Elischer ng_btsocket_rfcomm_session_process_pcb(ng_btsocket_rfcomm_session_p s)
1696f2bb1caeSJulian Elischer {
1697f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = NULL, pcb_next = NULL;
1698f2bb1caeSJulian Elischer 	int				error;
1699f2bb1caeSJulian Elischer 
1700f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
1701f2bb1caeSJulian Elischer 
1702f2bb1caeSJulian Elischer 	/*
1703f2bb1caeSJulian Elischer 	 * Note: cannot use LIST_FOREACH because ng_btsocket_rfcomm_pcb_kill
1704f2bb1caeSJulian Elischer 	 * will unlink DLC from the session
1705f2bb1caeSJulian Elischer 	 */
1706f2bb1caeSJulian Elischer 
1707f2bb1caeSJulian Elischer 	for (pcb = LIST_FIRST(&s->dlcs); pcb != NULL; ) {
1708f2bb1caeSJulian Elischer 		mtx_lock(&pcb->pcb_mtx);
1709f2bb1caeSJulian Elischer 		pcb_next = LIST_NEXT(pcb, session_next);
1710f2bb1caeSJulian Elischer 
1711f2bb1caeSJulian Elischer 		switch (pcb->state) {
1712f2bb1caeSJulian Elischer 
1713f2bb1caeSJulian Elischer 		/*
1714f2bb1caeSJulian Elischer 		 * If DLC in W4_CONNECT state then we should check for both
1715f2bb1caeSJulian Elischer 		 * timeout and detach.
1716f2bb1caeSJulian Elischer 		 */
1717f2bb1caeSJulian Elischer 
1718f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
17197c380856SMaksim Yevmenkin 			if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_DETACHED)
17207c380856SMaksim Yevmenkin 				ng_btsocket_rfcomm_pcb_kill(pcb, 0);
17217c380856SMaksim Yevmenkin 			else if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT)
17227c380856SMaksim Yevmenkin 				ng_btsocket_rfcomm_pcb_kill(pcb, ETIMEDOUT);
1723f2bb1caeSJulian Elischer 			break;
1724f2bb1caeSJulian Elischer 
1725f2bb1caeSJulian Elischer 		/*
1726f2bb1caeSJulian Elischer 		 * If DLC in CONFIGURING or CONNECTING state then we only
1727f2bb1caeSJulian Elischer 		 * should check for timeout. If detach() was called then
1728f2bb1caeSJulian Elischer 		 * DLC will be moved into DISCONNECTING state.
1729f2bb1caeSJulian Elischer 		 */
1730f2bb1caeSJulian Elischer 
1731f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING:
1732f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
1733f2bb1caeSJulian Elischer 			if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT)
17347c380856SMaksim Yevmenkin 				ng_btsocket_rfcomm_pcb_kill(pcb, ETIMEDOUT);
1735f2bb1caeSJulian Elischer 			break;
1736f2bb1caeSJulian Elischer 
1737f2bb1caeSJulian Elischer 		/*
1738f2bb1caeSJulian Elischer 		 * If DLC in CONNECTED state then we need to send data (if any)
1739f2bb1caeSJulian Elischer 		 * from the socket's send queue. Note that we will send data
1740f2bb1caeSJulian Elischer 		 * from either all sockets or none. This may overload session's
1741f2bb1caeSJulian Elischer 		 * outgoing queue (but we do not check for that).
1742f2bb1caeSJulian Elischer 		 *
1743f2bb1caeSJulian Elischer  		 * XXX FIXME need scheduler for RFCOMM sockets
1744f2bb1caeSJulian Elischer 		 */
1745f2bb1caeSJulian Elischer 
1746f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_DLC_CONNECTED:
1747f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_pcb_send(pcb, ALOT);
1748f2bb1caeSJulian Elischer 			if (error != 0)
17497c380856SMaksim Yevmenkin 				ng_btsocket_rfcomm_pcb_kill(pcb, error);
1750f2bb1caeSJulian Elischer 			break;
1751f2bb1caeSJulian Elischer 
1752f2bb1caeSJulian Elischer 		/*
1753f2bb1caeSJulian Elischer 		 * If DLC in DISCONNECTING state then we must send DISC frame.
1754f2bb1caeSJulian Elischer 		 * Note that if DLC has timeout set then we do not need to
1755f2bb1caeSJulian Elischer 		 * resend DISC frame.
1756f2bb1caeSJulian Elischer 		 *
1757f2bb1caeSJulian Elischer 		 * XXX FIXME need to drain all data from the socket's queue
1758f2bb1caeSJulian Elischer 		 * if LINGER option was set
1759f2bb1caeSJulian Elischer 		 */
1760f2bb1caeSJulian Elischer 
1761f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
1762f2bb1caeSJulian Elischer 			if (!(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)) {
1763f2bb1caeSJulian Elischer 				error = ng_btsocket_rfcomm_send_command(
1764f2bb1caeSJulian Elischer 						pcb->session, RFCOMM_FRAME_DISC,
1765f2bb1caeSJulian Elischer 						pcb->dlci);
1766f2bb1caeSJulian Elischer 				if (error == 0)
1767f2bb1caeSJulian Elischer 					ng_btsocket_rfcomm_timeout(pcb);
17687c380856SMaksim Yevmenkin 				else
17697c380856SMaksim Yevmenkin 					ng_btsocket_rfcomm_pcb_kill(pcb, error);
1770f2bb1caeSJulian Elischer 			} else if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT)
17717c380856SMaksim Yevmenkin 				ng_btsocket_rfcomm_pcb_kill(pcb, ETIMEDOUT);
1772f2bb1caeSJulian Elischer 			break;
1773f2bb1caeSJulian Elischer 
1774f2bb1caeSJulian Elischer /*		case NG_BTSOCKET_RFCOMM_DLC_CLOSED: */
1775f2bb1caeSJulian Elischer 		default:
1776f2bb1caeSJulian Elischer 			panic("%s: Invalid DLC state=%d, flags=%#x\n",
1777f2bb1caeSJulian Elischer 				__func__, pcb->state, pcb->flags);
1778f2bb1caeSJulian Elischer 			break;
1779f2bb1caeSJulian Elischer 		}
1780f2bb1caeSJulian Elischer 
1781f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
1782f2bb1caeSJulian Elischer 		pcb = pcb_next;
1783f2bb1caeSJulian Elischer 	}
1784f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_session_process_pcb */
1785f2bb1caeSJulian Elischer 
1786f2bb1caeSJulian Elischer /*
1787f2bb1caeSJulian Elischer  * Find RFCOMM session between "src" and "dst".
1788f2bb1caeSJulian Elischer  * Caller MUST hold ng_btsocket_rfcomm_sessions_mtx.
1789f2bb1caeSJulian Elischer  */
1790f2bb1caeSJulian Elischer 
1791f2bb1caeSJulian Elischer static ng_btsocket_rfcomm_session_p
1792f2bb1caeSJulian Elischer ng_btsocket_rfcomm_session_by_addr(bdaddr_p src, bdaddr_p dst)
1793f2bb1caeSJulian Elischer {
1794f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_session_p	s = NULL;
1795f2bb1caeSJulian Elischer 	ng_btsocket_l2cap_pcb_p		l2pcb = NULL;
1796f2bb1caeSJulian Elischer 	int				any_src;
1797f2bb1caeSJulian Elischer 
1798f2bb1caeSJulian Elischer 	mtx_assert(&ng_btsocket_rfcomm_sessions_mtx, MA_OWNED);
1799f2bb1caeSJulian Elischer 
1800f2bb1caeSJulian Elischer 	any_src = (bcmp(src, NG_HCI_BDADDR_ANY, sizeof(*src)) == 0);
1801f2bb1caeSJulian Elischer 
1802f2bb1caeSJulian Elischer 	LIST_FOREACH(s, &ng_btsocket_rfcomm_sessions, next) {
1803f2bb1caeSJulian Elischer 		l2pcb = so2l2cap_pcb(s->l2so);
1804f2bb1caeSJulian Elischer 
1805f2bb1caeSJulian Elischer 		if ((any_src || bcmp(&l2pcb->src, src, sizeof(*src)) == 0) &&
1806f2bb1caeSJulian Elischer 		    bcmp(&l2pcb->dst, dst, sizeof(*dst)) == 0)
1807f2bb1caeSJulian Elischer 			break;
1808f2bb1caeSJulian Elischer 	}
1809f2bb1caeSJulian Elischer 
1810f2bb1caeSJulian Elischer 	return (s);
1811f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_session_by_addr */
1812f2bb1caeSJulian Elischer 
1813f2bb1caeSJulian Elischer /*****************************************************************************
1814f2bb1caeSJulian Elischer  *****************************************************************************
1815f2bb1caeSJulian Elischer  **                                  RFCOMM
1816f2bb1caeSJulian Elischer  *****************************************************************************
1817f2bb1caeSJulian Elischer  *****************************************************************************/
1818f2bb1caeSJulian Elischer 
1819f2bb1caeSJulian Elischer /*
1820f2bb1caeSJulian Elischer  * Process incoming RFCOMM frame. Caller must hold s->session_mtx.
1821f2bb1caeSJulian Elischer  * XXX FIXME check frame length
1822f2bb1caeSJulian Elischer  */
1823f2bb1caeSJulian Elischer 
1824f2bb1caeSJulian Elischer static int
1825f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_frame(ng_btsocket_rfcomm_session_p s,
1826f2bb1caeSJulian Elischer 		struct mbuf *m0)
1827f2bb1caeSJulian Elischer {
1828f2bb1caeSJulian Elischer 	struct rfcomm_frame_hdr	*hdr = NULL;
1829f2bb1caeSJulian Elischer 	struct mbuf		*m = NULL;
1830f2bb1caeSJulian Elischer 	u_int16_t		 length;
1831f2bb1caeSJulian Elischer 	u_int8_t		 dlci, type;
1832f2bb1caeSJulian Elischer 	int			 error = 0;
1833f2bb1caeSJulian Elischer 
1834f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
1835f2bb1caeSJulian Elischer 
1836f2bb1caeSJulian Elischer 	/* Pullup as much as we can into first mbuf (for direct access) */
1837f2bb1caeSJulian Elischer 	length = min(m0->m_pkthdr.len, MHLEN);
1838f2bb1caeSJulian Elischer 	if (m0->m_len < length) {
1839f2bb1caeSJulian Elischer 		if ((m0 = m_pullup(m0, length)) == NULL) {
1840f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_ALERT(
1841f2bb1caeSJulian Elischer "%s: m_pullup(%d) failed\n", __func__, length);
1842f2bb1caeSJulian Elischer 
1843f2bb1caeSJulian Elischer 			return (ENOBUFS);
1844f2bb1caeSJulian Elischer 		}
1845f2bb1caeSJulian Elischer 	}
1846f2bb1caeSJulian Elischer 
1847f2bb1caeSJulian Elischer 	hdr = mtod(m0, struct rfcomm_frame_hdr *);
1848f2bb1caeSJulian Elischer 	dlci = RFCOMM_DLCI(hdr->address);
1849f2bb1caeSJulian Elischer 	type = RFCOMM_TYPE(hdr->control);
1850f2bb1caeSJulian Elischer 
1851f2bb1caeSJulian Elischer 	/* Test EA bit in length. If not set then we have 2 bytes of length */
1852f2bb1caeSJulian Elischer 	if (!RFCOMM_EA(hdr->length)) {
1853f2bb1caeSJulian Elischer 		bcopy(&hdr->length, &length, sizeof(length));
1854f93b258cSMaksim Yevmenkin 		length = le16toh(length) >> 1;
1855f2bb1caeSJulian Elischer 		m_adj(m0, sizeof(*hdr) + 1);
1856f2bb1caeSJulian Elischer 	} else {
1857f2bb1caeSJulian Elischer 		length = hdr->length >> 1;
1858f2bb1caeSJulian Elischer 		m_adj(m0, sizeof(*hdr));
1859f2bb1caeSJulian Elischer 	}
1860f2bb1caeSJulian Elischer 
1861f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
1862f2bb1caeSJulian Elischer "%s: Got frame type=%#x, dlci=%d, length=%d, cr=%d, pf=%d, len=%d\n",
1863f2bb1caeSJulian Elischer 		__func__, type, dlci, length, RFCOMM_CR(hdr->address),
1864f2bb1caeSJulian Elischer 		RFCOMM_PF(hdr->control), m0->m_pkthdr.len);
1865f2bb1caeSJulian Elischer 
1866f2bb1caeSJulian Elischer 	/*
1867f2bb1caeSJulian Elischer 	 * Get FCS (the last byte in the frame)
1868f2bb1caeSJulian Elischer 	 * XXX this will not work if mbuf chain ends with empty mbuf.
1869f2bb1caeSJulian Elischer 	 * XXX let's hope it never happens :)
1870f2bb1caeSJulian Elischer 	 */
1871f2bb1caeSJulian Elischer 
1872f2bb1caeSJulian Elischer 	for (m = m0; m->m_next != NULL; m = m->m_next)
1873f2bb1caeSJulian Elischer 		;
1874f2bb1caeSJulian Elischer 	if (m->m_len <= 0)
1875f2bb1caeSJulian Elischer 		panic("%s: Empty mbuf at the end of the chain, len=%d\n",
1876f2bb1caeSJulian Elischer 			__func__, m->m_len);
1877f2bb1caeSJulian Elischer 
1878f2bb1caeSJulian Elischer 	/*
1879f2bb1caeSJulian Elischer 	 * Check FCS. We only need to calculate FCS on first 2 or 3 bytes
1880f2bb1caeSJulian Elischer 	 * and already m_pullup'ed mbuf chain, so it should be safe.
1881f2bb1caeSJulian Elischer 	 */
1882f2bb1caeSJulian Elischer 
1883f2bb1caeSJulian Elischer 	if (ng_btsocket_rfcomm_check_fcs((u_int8_t *) hdr, type, m->m_data[m->m_len - 1])) {
1884f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ERR(
1885f2bb1caeSJulian Elischer "%s: Invalid RFCOMM packet. Bad checksum\n", __func__);
1886f2bb1caeSJulian Elischer 		NG_FREE_M(m0);
1887f2bb1caeSJulian Elischer 
1888f2bb1caeSJulian Elischer 		return (EINVAL);
1889f2bb1caeSJulian Elischer 	}
1890f2bb1caeSJulian Elischer 
1891f2bb1caeSJulian Elischer 	m_adj(m0, -1); /* Trim FCS byte */
1892f2bb1caeSJulian Elischer 
1893f2bb1caeSJulian Elischer 	/*
1894f2bb1caeSJulian Elischer 	 * Process RFCOMM frame.
1895f2bb1caeSJulian Elischer 	 *
1896f2bb1caeSJulian Elischer 	 * From TS 07.10 spec
1897f2bb1caeSJulian Elischer 	 *
1898f2bb1caeSJulian Elischer 	 * "... In the case where a SABM or DISC command with the P bit set
1899f2bb1caeSJulian Elischer 	 * to 0 is received then the received frame shall be discarded..."
1900f2bb1caeSJulian Elischer  	 *
1901f2bb1caeSJulian Elischer 	 * "... If a unsolicited DM response is received then the frame shall
1902f2bb1caeSJulian Elischer 	 * be processed irrespective of the P/F setting... "
1903f2bb1caeSJulian Elischer 	 *
1904f2bb1caeSJulian Elischer 	 * "... The station may transmit response frames with the F bit set
1905f2bb1caeSJulian Elischer 	 * to 0 at any opportunity on an asynchronous basis. However, in the
1906f2bb1caeSJulian Elischer 	 * case where a UA response is received with the F bit set to 0 then
1907f2bb1caeSJulian Elischer 	 * the received frame shall be discarded..."
1908f2bb1caeSJulian Elischer 	 *
1909f2bb1caeSJulian Elischer 	 * From Bluetooth spec
1910f2bb1caeSJulian Elischer 	 *
1911f2bb1caeSJulian Elischer 	 * "... When credit based flow control is being used, the meaning of
1912f2bb1caeSJulian Elischer 	 * the P/F bit in the control field of the RFCOMM header is redefined
1913f2bb1caeSJulian Elischer 	 * for UIH frames..."
1914f2bb1caeSJulian Elischer 	 */
1915f2bb1caeSJulian Elischer 
1916f2bb1caeSJulian Elischer 	switch (type) {
1917f2bb1caeSJulian Elischer 	case RFCOMM_FRAME_SABM:
1918f2bb1caeSJulian Elischer 		if (RFCOMM_PF(hdr->control))
1919f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_receive_sabm(s, dlci);
1920f2bb1caeSJulian Elischer 		break;
1921f2bb1caeSJulian Elischer 
1922f2bb1caeSJulian Elischer 	case RFCOMM_FRAME_DISC:
1923f2bb1caeSJulian Elischer 		if (RFCOMM_PF(hdr->control))
1924f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_receive_disc(s, dlci);
1925f2bb1caeSJulian Elischer 		break;
1926f2bb1caeSJulian Elischer 
1927f2bb1caeSJulian Elischer 	case RFCOMM_FRAME_UA:
1928f2bb1caeSJulian Elischer 		if (RFCOMM_PF(hdr->control))
1929f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_receive_ua(s, dlci);
1930f2bb1caeSJulian Elischer 		break;
1931f2bb1caeSJulian Elischer 
1932f2bb1caeSJulian Elischer 	case RFCOMM_FRAME_DM:
1933f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_receive_dm(s, dlci);
1934f2bb1caeSJulian Elischer 		break;
1935f2bb1caeSJulian Elischer 
1936f2bb1caeSJulian Elischer 	case RFCOMM_FRAME_UIH:
1937f2bb1caeSJulian Elischer 		if (dlci == 0)
1938f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_receive_mcc(s, m0);
1939f2bb1caeSJulian Elischer 		else
1940f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_receive_uih(s, dlci,
1941f2bb1caeSJulian Elischer 					RFCOMM_PF(hdr->control), m0);
1942f2bb1caeSJulian Elischer 
1943f2bb1caeSJulian Elischer 		return (error);
1944f2bb1caeSJulian Elischer 		/* NOT REACHED */
1945f2bb1caeSJulian Elischer 
1946f2bb1caeSJulian Elischer 	default:
1947f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ERR(
1948f2bb1caeSJulian Elischer "%s: Invalid RFCOMM packet. Unknown type=%#x\n", __func__, type);
1949f2bb1caeSJulian Elischer 		error = EINVAL;
1950f2bb1caeSJulian Elischer 		break;
1951f2bb1caeSJulian Elischer 	}
1952f2bb1caeSJulian Elischer 
1953f2bb1caeSJulian Elischer 	NG_FREE_M(m0);
1954f2bb1caeSJulian Elischer 
1955f2bb1caeSJulian Elischer 	return (error);
1956f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_frame */
1957f2bb1caeSJulian Elischer 
1958f2bb1caeSJulian Elischer /*
1959f2bb1caeSJulian Elischer  * Process RFCOMM SABM frame
1960f2bb1caeSJulian Elischer  */
1961f2bb1caeSJulian Elischer 
1962f2bb1caeSJulian Elischer static int
1963f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_sabm(ng_btsocket_rfcomm_session_p s, int dlci)
1964f2bb1caeSJulian Elischer {
1965f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
1966f2bb1caeSJulian Elischer 	int				error = 0;
1967f2bb1caeSJulian Elischer 
1968f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
1969f2bb1caeSJulian Elischer 
1970f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
1971f2bb1caeSJulian Elischer "%s: Got SABM, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
1972f2bb1caeSJulian Elischer 		__func__, s->state, s->flags, s->mtu, dlci);
1973f2bb1caeSJulian Elischer 
1974f2bb1caeSJulian Elischer 	/* DLCI == 0 means open multiplexor channel */
1975f2bb1caeSJulian Elischer 	if (dlci == 0) {
1976f2bb1caeSJulian Elischer 		switch (s->state) {
1977f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
1978f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_SESSION_OPEN:
1979f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_send_command(s,
1980f2bb1caeSJulian Elischer 					RFCOMM_FRAME_UA, dlci);
1981f2bb1caeSJulian Elischer 			if (error == 0) {
1982f2bb1caeSJulian Elischer 				s->state = NG_BTSOCKET_RFCOMM_SESSION_OPEN;
1983f2bb1caeSJulian Elischer 				ng_btsocket_rfcomm_connect_cfm(s);
1984f2bb1caeSJulian Elischer 			} else {
1985f2bb1caeSJulian Elischer 				s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1986f2bb1caeSJulian Elischer 				ng_btsocket_rfcomm_session_clean(s);
1987f2bb1caeSJulian Elischer 			}
1988f2bb1caeSJulian Elischer 			break;
1989f2bb1caeSJulian Elischer 
1990f2bb1caeSJulian Elischer 		default:
1991f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_WARN(
1992f2bb1caeSJulian Elischer "%s: Got SABM for session in invalid state state=%d, flags=%#x\n",
1993f2bb1caeSJulian Elischer 				__func__, s->state, s->flags);
1994f2bb1caeSJulian Elischer 			error = EINVAL;
1995f2bb1caeSJulian Elischer 			break;
1996f2bb1caeSJulian Elischer 		}
1997f2bb1caeSJulian Elischer 
1998f2bb1caeSJulian Elischer 		return (error);
1999f2bb1caeSJulian Elischer 	}
2000f2bb1caeSJulian Elischer 
2001f2bb1caeSJulian Elischer 	/* Make sure multiplexor channel is open */
2002f2bb1caeSJulian Elischer 	if (s->state != NG_BTSOCKET_RFCOMM_SESSION_OPEN) {
2003f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ERR(
2004f2bb1caeSJulian Elischer "%s: Got SABM for dlci=%d with mulitplexor channel closed, state=%d, " \
2005f2bb1caeSJulian Elischer "flags=%#x\n",		__func__, dlci, s->state, s->flags);
2006f2bb1caeSJulian Elischer 
2007f2bb1caeSJulian Elischer 		return (EINVAL);
2008f2bb1caeSJulian Elischer 	}
2009f2bb1caeSJulian Elischer 
2010f2bb1caeSJulian Elischer 	/*
2011f2bb1caeSJulian Elischer 	 * Check if we have this DLCI. This might happen when remote
2012f2bb1caeSJulian Elischer 	 * peer uses PN command before actual open (SABM) happens.
2013f2bb1caeSJulian Elischer 	 */
2014f2bb1caeSJulian Elischer 
2015f2bb1caeSJulian Elischer 	pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2016f2bb1caeSJulian Elischer 	if (pcb != NULL) {
2017f2bb1caeSJulian Elischer 		mtx_lock(&pcb->pcb_mtx);
2018f2bb1caeSJulian Elischer 
2019f2bb1caeSJulian Elischer 		if (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTING) {
2020f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_ERR(
2021f2bb1caeSJulian Elischer "%s: Got SABM for dlci=%d in invalid state=%d, flags=%#x\n",
2022f2bb1caeSJulian Elischer 				__func__, dlci, pcb->state, pcb->flags);
2023f2bb1caeSJulian Elischer 			mtx_unlock(&pcb->pcb_mtx);
2024f2bb1caeSJulian Elischer 
2025f2bb1caeSJulian Elischer 			return (ENOENT);
2026f2bb1caeSJulian Elischer 		}
2027f2bb1caeSJulian Elischer 
2028f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_untimeout(pcb);
2029f2bb1caeSJulian Elischer 
2030f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_UA,dlci);
2031f2bb1caeSJulian Elischer 		if (error == 0)
2032f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_send_msc(pcb);
2033f2bb1caeSJulian Elischer 
2034f2bb1caeSJulian Elischer 		if (error == 0) {
2035f2bb1caeSJulian Elischer 			pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTED;
2036f2bb1caeSJulian Elischer 			soisconnected(pcb->so);
20377c380856SMaksim Yevmenkin 		} else
20387c380856SMaksim Yevmenkin 			ng_btsocket_rfcomm_pcb_kill(pcb, error);
2039f2bb1caeSJulian Elischer 
2040f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
2041f2bb1caeSJulian Elischer 
2042f2bb1caeSJulian Elischer 		return (error);
2043f2bb1caeSJulian Elischer 	}
2044f2bb1caeSJulian Elischer 
2045f2bb1caeSJulian Elischer 	/*
2046f2bb1caeSJulian Elischer 	 * We do not have requested DLCI, so it must be an incoming connection
2047f2bb1caeSJulian Elischer 	 * with default parameters. Try to accept it.
2048f2bb1caeSJulian Elischer 	 */
2049f2bb1caeSJulian Elischer 
2050f2bb1caeSJulian Elischer 	pcb = ng_btsocket_rfcomm_connect_ind(s, RFCOMM_SRVCHANNEL(dlci));
2051f2bb1caeSJulian Elischer 	if (pcb != NULL) {
2052f2bb1caeSJulian Elischer 		mtx_lock(&pcb->pcb_mtx);
2053f2bb1caeSJulian Elischer 
2054f2bb1caeSJulian Elischer 		pcb->dlci = dlci;
2055f2bb1caeSJulian Elischer 
2056f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_UA,dlci);
20570986ab12SMaksim Yevmenkin 		if (error == 0)
20580986ab12SMaksim Yevmenkin 			error = ng_btsocket_rfcomm_send_msc(pcb);
20590986ab12SMaksim Yevmenkin 
2060f2bb1caeSJulian Elischer 		if (error == 0) {
2061f2bb1caeSJulian Elischer 			pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTED;
2062f2bb1caeSJulian Elischer 			soisconnected(pcb->so);
20637c380856SMaksim Yevmenkin 		} else
20647c380856SMaksim Yevmenkin 			ng_btsocket_rfcomm_pcb_kill(pcb, error);
2065f2bb1caeSJulian Elischer 
2066f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
2067f2bb1caeSJulian Elischer 	} else
2068f2bb1caeSJulian Elischer 		/* Nobody is listen()ing on the requested DLCI */
2069f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_DM,dlci);
2070f2bb1caeSJulian Elischer 
2071f2bb1caeSJulian Elischer 	return (error);
2072f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_sabm */
2073f2bb1caeSJulian Elischer 
2074f2bb1caeSJulian Elischer /*
2075f2bb1caeSJulian Elischer  * Process RFCOMM DISC frame
2076f2bb1caeSJulian Elischer  */
2077f2bb1caeSJulian Elischer 
2078f2bb1caeSJulian Elischer static int
2079f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_disc(ng_btsocket_rfcomm_session_p s, int dlci)
2080f2bb1caeSJulian Elischer {
2081f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
2082f2bb1caeSJulian Elischer 	int				error = 0;
2083f2bb1caeSJulian Elischer 
2084f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
2085f2bb1caeSJulian Elischer 
2086f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
2087f2bb1caeSJulian Elischer "%s: Got DISC, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
2088f2bb1caeSJulian Elischer 		__func__, s->state, s->flags, s->mtu, dlci);
2089f2bb1caeSJulian Elischer 
2090f2bb1caeSJulian Elischer 	/* DLCI == 0 means close multiplexor channel */
2091f2bb1caeSJulian Elischer 	if (dlci == 0) {
2092f2bb1caeSJulian Elischer 		/* XXX FIXME assume that remote side will close the socket */
2093f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_command(s, RFCOMM_FRAME_UA, 0);
209448bd0712SMaksim Yevmenkin 		if (error == 0) {
209548bd0712SMaksim Yevmenkin 			if (s->state == NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING)
209648bd0712SMaksim Yevmenkin 				s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED; /* XXX */
2097f2bb1caeSJulian Elischer 			else
209848bd0712SMaksim Yevmenkin 				s->state = NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING;
209948bd0712SMaksim Yevmenkin 		} else
2100f2bb1caeSJulian Elischer 			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED; /* XXX */
2101f2bb1caeSJulian Elischer 
2102f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_session_clean(s);
2103f2bb1caeSJulian Elischer 	} else {
2104f2bb1caeSJulian Elischer 		pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2105f2bb1caeSJulian Elischer 		if (pcb != NULL) {
2106f2bb1caeSJulian Elischer 			int	err;
2107f2bb1caeSJulian Elischer 
2108f2bb1caeSJulian Elischer 			mtx_lock(&pcb->pcb_mtx);
2109f2bb1caeSJulian Elischer 
2110f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_INFO(
2111f2bb1caeSJulian Elischer "%s: Got DISC for dlci=%d, state=%d, flags=%#x\n",
2112f2bb1caeSJulian Elischer 				__func__, dlci, pcb->state, pcb->flags);
2113f2bb1caeSJulian Elischer 
2114f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_send_command(s,
2115f2bb1caeSJulian Elischer 					RFCOMM_FRAME_UA, dlci);
2116f2bb1caeSJulian Elischer 
2117f2bb1caeSJulian Elischer 			if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_CONNECTED)
2118f2bb1caeSJulian Elischer 				err = 0;
2119f2bb1caeSJulian Elischer 			else
2120f2bb1caeSJulian Elischer 				err = ECONNREFUSED;
2121f2bb1caeSJulian Elischer 
21227c380856SMaksim Yevmenkin 			ng_btsocket_rfcomm_pcb_kill(pcb, err);
2123f2bb1caeSJulian Elischer 
2124f2bb1caeSJulian Elischer 			mtx_unlock(&pcb->pcb_mtx);
2125f2bb1caeSJulian Elischer 		} else {
2126f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_WARN(
2127f2bb1caeSJulian Elischer "%s: Got DISC for non-existing dlci=%d\n", __func__, dlci);
2128f2bb1caeSJulian Elischer 
2129f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_send_command(s,
2130f2bb1caeSJulian Elischer 					RFCOMM_FRAME_DM, dlci);
2131f2bb1caeSJulian Elischer 		}
2132f2bb1caeSJulian Elischer 	}
2133f2bb1caeSJulian Elischer 
2134f2bb1caeSJulian Elischer 	return (error);
2135f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_disc */
2136f2bb1caeSJulian Elischer 
2137f2bb1caeSJulian Elischer /*
2138f2bb1caeSJulian Elischer  * Process RFCOMM UA frame
2139f2bb1caeSJulian Elischer  */
2140f2bb1caeSJulian Elischer 
2141f2bb1caeSJulian Elischer static int
2142f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_ua(ng_btsocket_rfcomm_session_p s, int dlci)
2143f2bb1caeSJulian Elischer {
2144f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
2145f2bb1caeSJulian Elischer 	int				error = 0;
2146f2bb1caeSJulian Elischer 
2147f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
2148f2bb1caeSJulian Elischer 
2149f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
2150f2bb1caeSJulian Elischer "%s: Got UA, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
2151f2bb1caeSJulian Elischer 		__func__, s->state, s->flags, s->mtu, dlci);
2152f2bb1caeSJulian Elischer 
2153f2bb1caeSJulian Elischer 	/* dlci == 0 means multiplexor channel */
2154f2bb1caeSJulian Elischer 	if (dlci == 0) {
2155f2bb1caeSJulian Elischer 		switch (s->state) {
2156f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
2157f2bb1caeSJulian Elischer 			s->state = NG_BTSOCKET_RFCOMM_SESSION_OPEN;
2158f2bb1caeSJulian Elischer 			ng_btsocket_rfcomm_connect_cfm(s);
2159f2bb1caeSJulian Elischer 			break;
2160f2bb1caeSJulian Elischer 
2161f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING:
2162f2bb1caeSJulian Elischer 			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
2163f2bb1caeSJulian Elischer 			ng_btsocket_rfcomm_session_clean(s);
2164f2bb1caeSJulian Elischer 			break;
2165f2bb1caeSJulian Elischer 
2166f2bb1caeSJulian Elischer 		default:
2167f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_WARN(
2168f2bb1caeSJulian Elischer "%s: Got UA for session in invalid state=%d(%d), flags=%#x, mtu=%d\n",
2169f2bb1caeSJulian Elischer 				__func__, s->state, INITIATOR(s), s->flags,
2170f2bb1caeSJulian Elischer 				s->mtu);
2171f2bb1caeSJulian Elischer 			error = ENOENT;
2172f2bb1caeSJulian Elischer 			break;
2173f2bb1caeSJulian Elischer 		}
2174f2bb1caeSJulian Elischer 
2175f2bb1caeSJulian Elischer 		return (error);
2176f2bb1caeSJulian Elischer 	}
2177f2bb1caeSJulian Elischer 
2178f2bb1caeSJulian Elischer 	/* Check if we have this DLCI */
2179f2bb1caeSJulian Elischer 	pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2180f2bb1caeSJulian Elischer 	if (pcb != NULL) {
2181f2bb1caeSJulian Elischer 		mtx_lock(&pcb->pcb_mtx);
2182f2bb1caeSJulian Elischer 
2183f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_INFO(
2184f2bb1caeSJulian Elischer "%s: Got UA for dlci=%d, state=%d, flags=%#x\n",
2185f2bb1caeSJulian Elischer 			__func__, dlci, pcb->state, pcb->flags);
2186f2bb1caeSJulian Elischer 
2187f2bb1caeSJulian Elischer 		switch (pcb->state) {
2188f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
2189f2bb1caeSJulian Elischer 			ng_btsocket_rfcomm_untimeout(pcb);
2190f2bb1caeSJulian Elischer 
2191f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_send_msc(pcb);
2192f2bb1caeSJulian Elischer 			if (error == 0) {
2193f2bb1caeSJulian Elischer 				pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTED;
2194f2bb1caeSJulian Elischer 				soisconnected(pcb->so);
2195f2bb1caeSJulian Elischer 			}
2196f2bb1caeSJulian Elischer 			break;
2197f2bb1caeSJulian Elischer 
2198f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
21997c380856SMaksim Yevmenkin 			ng_btsocket_rfcomm_pcb_kill(pcb, 0);
2200f2bb1caeSJulian Elischer 			break;
2201f2bb1caeSJulian Elischer 
2202f2bb1caeSJulian Elischer 		default:
2203f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_WARN(
2204f2bb1caeSJulian Elischer "%s: Got UA for dlci=%d in invalid state=%d, flags=%#x\n",
2205f2bb1caeSJulian Elischer 				__func__, dlci, pcb->state, pcb->flags);
2206f2bb1caeSJulian Elischer 			error = ENOENT;
2207f2bb1caeSJulian Elischer 			break;
2208f2bb1caeSJulian Elischer 		}
2209f2bb1caeSJulian Elischer 
2210f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
2211f2bb1caeSJulian Elischer 	} else {
2212f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_WARN(
2213f2bb1caeSJulian Elischer "%s: Got UA for non-existing dlci=%d\n", __func__, dlci);
2214f2bb1caeSJulian Elischer 
2215f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_DM,dlci);
2216f2bb1caeSJulian Elischer 	}
2217f2bb1caeSJulian Elischer 
2218f2bb1caeSJulian Elischer 	return (error);
2219f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_ua */
2220f2bb1caeSJulian Elischer 
2221f2bb1caeSJulian Elischer /*
2222f2bb1caeSJulian Elischer  * Process RFCOMM DM frame
2223f2bb1caeSJulian Elischer  */
2224f2bb1caeSJulian Elischer 
2225f2bb1caeSJulian Elischer static int
2226f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_dm(ng_btsocket_rfcomm_session_p s, int dlci)
2227f2bb1caeSJulian Elischer {
2228f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
2229f2bb1caeSJulian Elischer 	int				error;
2230f2bb1caeSJulian Elischer 
2231f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
2232f2bb1caeSJulian Elischer 
2233f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
2234f2bb1caeSJulian Elischer "%s: Got DM, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
2235f2bb1caeSJulian Elischer 		__func__, s->state, s->flags, s->mtu, dlci);
2236f2bb1caeSJulian Elischer 
2237f2bb1caeSJulian Elischer 	/* DLCI == 0 means multiplexor channel */
2238f2bb1caeSJulian Elischer 	if (dlci == 0) {
2239f2bb1caeSJulian Elischer 		/* Disconnect all dlc's on the session */
2240f2bb1caeSJulian Elischer 		s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
2241f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_session_clean(s);
2242f2bb1caeSJulian Elischer 	} else {
2243f2bb1caeSJulian Elischer 		pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2244f2bb1caeSJulian Elischer 		if (pcb != NULL) {
2245f2bb1caeSJulian Elischer 			mtx_lock(&pcb->pcb_mtx);
2246f2bb1caeSJulian Elischer 
2247f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_INFO(
2248f2bb1caeSJulian Elischer "%s: Got DM for dlci=%d, state=%d, flags=%#x\n",
2249f2bb1caeSJulian Elischer 				__func__, dlci, pcb->state, pcb->flags);
2250f2bb1caeSJulian Elischer 
2251f2bb1caeSJulian Elischer 			if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_CONNECTED)
2252f2bb1caeSJulian Elischer 				error = ECONNRESET;
2253f2bb1caeSJulian Elischer 			else
2254f2bb1caeSJulian Elischer 				error = ECONNREFUSED;
2255f2bb1caeSJulian Elischer 
22567c380856SMaksim Yevmenkin 			ng_btsocket_rfcomm_pcb_kill(pcb, error);
2257f2bb1caeSJulian Elischer 
2258f2bb1caeSJulian Elischer 			mtx_unlock(&pcb->pcb_mtx);
2259f2bb1caeSJulian Elischer 		} else
2260f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_WARN(
2261f2bb1caeSJulian Elischer "%s: Got DM for non-existing dlci=%d\n", __func__, dlci);
2262f2bb1caeSJulian Elischer 	}
2263f2bb1caeSJulian Elischer 
2264f2bb1caeSJulian Elischer 	return (0);
2265f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_dm */
2266f2bb1caeSJulian Elischer 
2267f2bb1caeSJulian Elischer /*
2268f2bb1caeSJulian Elischer  * Process RFCOMM UIH frame (data)
2269f2bb1caeSJulian Elischer  */
2270f2bb1caeSJulian Elischer 
2271f2bb1caeSJulian Elischer static int
2272f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_uih(ng_btsocket_rfcomm_session_p s, int dlci,
2273f2bb1caeSJulian Elischer 		int pf, struct mbuf *m0)
2274f2bb1caeSJulian Elischer {
2275f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
2276f2bb1caeSJulian Elischer 	int				error = 0;
2277f2bb1caeSJulian Elischer 
2278f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
2279f2bb1caeSJulian Elischer 
2280f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
2281f2bb1caeSJulian Elischer "%s: Got UIH, session state=%d, flags=%#x, mtu=%d, dlci=%d, pf=%d, len=%d\n",
2282f2bb1caeSJulian Elischer 		__func__, s->state, s->flags, s->mtu, dlci, pf,
2283f2bb1caeSJulian Elischer 		m0->m_pkthdr.len);
2284f2bb1caeSJulian Elischer 
2285f2bb1caeSJulian Elischer 	/* XXX should we do it here? Check for session flow control */
2286f2bb1caeSJulian Elischer 	if (s->flags & NG_BTSOCKET_RFCOMM_SESSION_LFC) {
2287f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_WARN(
2288f2bb1caeSJulian Elischer "%s: Got UIH with session flow control asserted, state=%d, flags=%#x\n",
2289f2bb1caeSJulian Elischer 			__func__, s->state, s->flags);
2290f2bb1caeSJulian Elischer 		goto drop;
2291f2bb1caeSJulian Elischer 	}
2292f2bb1caeSJulian Elischer 
2293f2bb1caeSJulian Elischer 	/* Check if we have this dlci */
2294f2bb1caeSJulian Elischer 	pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2295f2bb1caeSJulian Elischer 	if (pcb == NULL) {
2296f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_WARN(
2297f2bb1caeSJulian Elischer "%s: Got UIH for non-existing dlci=%d\n", __func__, dlci);
2298f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_DM,dlci);
2299f2bb1caeSJulian Elischer 		goto drop;
2300f2bb1caeSJulian Elischer 	}
2301f2bb1caeSJulian Elischer 
2302f2bb1caeSJulian Elischer 	mtx_lock(&pcb->pcb_mtx);
2303f2bb1caeSJulian Elischer 
2304f2bb1caeSJulian Elischer 	/* Check dlci state */
2305f2bb1caeSJulian Elischer 	if (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTED) {
2306f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_WARN(
2307f2bb1caeSJulian Elischer "%s: Got UIH for dlci=%d in invalid state=%d, flags=%#x\n",
2308f2bb1caeSJulian Elischer 			__func__, dlci, pcb->state, pcb->flags);
2309f2bb1caeSJulian Elischer 		error = EINVAL;
2310f2bb1caeSJulian Elischer 		goto drop1;
2311f2bb1caeSJulian Elischer 	}
2312f2bb1caeSJulian Elischer 
2313f2bb1caeSJulian Elischer 	/* Check dlci flow control */
2314f2bb1caeSJulian Elischer 	if (((pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) && pcb->rx_cred <= 0) ||
2315f2bb1caeSJulian Elischer 	     (pcb->lmodem & RFCOMM_MODEM_FC)) {
2316f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ERR(
2317f2bb1caeSJulian Elischer "%s: Got UIH for dlci=%d with asserted flow control, state=%d, " \
2318f2bb1caeSJulian Elischer "flags=%#x, rx_cred=%d, lmodem=%#x\n",
2319f2bb1caeSJulian Elischer 			__func__, dlci, pcb->state, pcb->flags,
2320f2bb1caeSJulian Elischer 			pcb->rx_cred, pcb->lmodem);
2321f2bb1caeSJulian Elischer 		goto drop1;
2322f2bb1caeSJulian Elischer 	}
2323f2bb1caeSJulian Elischer 
2324f2bb1caeSJulian Elischer 	/* Did we get any credits? */
2325f2bb1caeSJulian Elischer 	if ((pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) && pf) {
2326f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_INFO(
2327f2bb1caeSJulian Elischer "%s: Got %d more credits for dlci=%d, state=%d, flags=%#x, " \
2328f2bb1caeSJulian Elischer "rx_cred=%d, tx_cred=%d\n",
2329f2bb1caeSJulian Elischer 			__func__, *mtod(m0, u_int8_t *), dlci, pcb->state,
2330f2bb1caeSJulian Elischer 			pcb->flags, pcb->rx_cred, pcb->tx_cred);
2331f2bb1caeSJulian Elischer 
2332f2bb1caeSJulian Elischer 		pcb->tx_cred += *mtod(m0, u_int8_t *);
2333f2bb1caeSJulian Elischer 		m_adj(m0, 1);
2334f2bb1caeSJulian Elischer 
2335f2bb1caeSJulian Elischer 		/* Send more from the DLC. XXX check for errors? */
2336f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_pcb_send(pcb, ALOT);
2337f2bb1caeSJulian Elischer 	}
2338f2bb1caeSJulian Elischer 
2339f2bb1caeSJulian Elischer 	/* OK the of the rest of the mbuf is the data */
2340f2bb1caeSJulian Elischer 	if (m0->m_pkthdr.len > 0) {
2341f2bb1caeSJulian Elischer 		/* If we are using credit flow control decrease rx_cred here */
2342f2bb1caeSJulian Elischer 		if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) {
2343f2bb1caeSJulian Elischer 			/* Give remote peer more credits (if needed) */
2344f2bb1caeSJulian Elischer 			if (-- pcb->rx_cred <= RFCOMM_MAX_CREDITS / 2)
2345f2bb1caeSJulian Elischer 				ng_btsocket_rfcomm_send_credits(pcb);
2346f2bb1caeSJulian Elischer 			else
2347f2bb1caeSJulian Elischer 				NG_BTSOCKET_RFCOMM_INFO(
2348f2bb1caeSJulian Elischer "%s: Remote side still has credits, dlci=%d, state=%d, flags=%#x, " \
2349f2bb1caeSJulian Elischer "rx_cred=%d, tx_cred=%d\n",		__func__, dlci, pcb->state, pcb->flags,
2350f2bb1caeSJulian Elischer 					pcb->rx_cred, pcb->tx_cred);
2351f2bb1caeSJulian Elischer 		}
2352f2bb1caeSJulian Elischer 
2353f2bb1caeSJulian Elischer 		/* Check packet against mtu on dlci */
2354f2bb1caeSJulian Elischer 		if (m0->m_pkthdr.len > pcb->mtu) {
2355f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_ERR(
2356f2bb1caeSJulian Elischer "%s: Got oversized UIH for dlci=%d, state=%d, flags=%#x, mtu=%d, len=%d\n",
2357f2bb1caeSJulian Elischer 				__func__, dlci, pcb->state, pcb->flags,
2358f2bb1caeSJulian Elischer 				pcb->mtu, m0->m_pkthdr.len);
2359f2bb1caeSJulian Elischer 
2360f2bb1caeSJulian Elischer 			error = EMSGSIZE;
2361f2bb1caeSJulian Elischer 		} else if (m0->m_pkthdr.len > sbspace(&pcb->so->so_rcv)) {
2362f2bb1caeSJulian Elischer 
2363f2bb1caeSJulian Elischer 			/*
2364f2bb1caeSJulian Elischer 			 * This is really bad. Receive queue on socket does
2365f2bb1caeSJulian Elischer 			 * not have enough space for the packet. We do not
2366f2bb1caeSJulian Elischer 			 * have any other choice but drop the packet.
2367f2bb1caeSJulian Elischer 			 */
2368f2bb1caeSJulian Elischer 
2369f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_ERR(
2370f2bb1caeSJulian Elischer "%s: Not enough space in socket receive queue. Dropping UIH for dlci=%d, " \
2371f2bb1caeSJulian Elischer "state=%d, flags=%#x, len=%d, space=%ld\n",
2372f2bb1caeSJulian Elischer 				__func__, dlci, pcb->state, pcb->flags,
2373f2bb1caeSJulian Elischer 				m0->m_pkthdr.len, sbspace(&pcb->so->so_rcv));
2374f2bb1caeSJulian Elischer 
2375f2bb1caeSJulian Elischer 			error = ENOBUFS;
2376f2bb1caeSJulian Elischer 		} else {
2377f2bb1caeSJulian Elischer 			/* Append packet to the socket receive queue */
2378829fae90SGleb Smirnoff 			sbappend(&pcb->so->so_rcv, m0, 0);
2379f2bb1caeSJulian Elischer 			m0 = NULL;
2380f2bb1caeSJulian Elischer 
2381f2bb1caeSJulian Elischer 			sorwakeup(pcb->so);
2382f2bb1caeSJulian Elischer 		}
2383f2bb1caeSJulian Elischer 	}
2384f2bb1caeSJulian Elischer drop1:
2385f2bb1caeSJulian Elischer 	mtx_unlock(&pcb->pcb_mtx);
2386f2bb1caeSJulian Elischer drop:
2387f2bb1caeSJulian Elischer 	NG_FREE_M(m0); /* checks for != NULL */
2388f2bb1caeSJulian Elischer 
2389f2bb1caeSJulian Elischer 	return (error);
2390f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_uih */
2391f2bb1caeSJulian Elischer 
2392f2bb1caeSJulian Elischer /*
2393f2bb1caeSJulian Elischer  * Process RFCOMM MCC command (Multiplexor)
2394f2bb1caeSJulian Elischer  *
2395f2bb1caeSJulian Elischer  * From TS 07.10 spec
2396f2bb1caeSJulian Elischer  *
2397f2bb1caeSJulian Elischer  * "5.4.3.1 Information Data
2398f2bb1caeSJulian Elischer  *
2399f2bb1caeSJulian Elischer  *  ...The frames (UIH) sent by the initiating station have the C/R bit set
2400f2bb1caeSJulian Elischer  *  to 1 and those sent by the responding station have the C/R bit set to 0..."
2401f2bb1caeSJulian Elischer  *
2402f2bb1caeSJulian Elischer  * "5.4.6.2 Operating procedures
2403f2bb1caeSJulian Elischer  *
2404f2bb1caeSJulian Elischer  *  Messages always exist in pairs; a command message and a corresponding
2405f2bb1caeSJulian Elischer  *  response message. If the C/R bit is set to 1 the message is a command,
2406f2bb1caeSJulian Elischer  *  if it is set to 0 the message is a response...
2407f2bb1caeSJulian Elischer  *
2408f2bb1caeSJulian Elischer  *  ...
2409f2bb1caeSJulian Elischer  *
2410f2bb1caeSJulian Elischer  *  NOTE: Notice that when UIH frames are used to convey information on DLCI 0
2411f2bb1caeSJulian Elischer  *  there are at least two different fields that contain a C/R bit, and the
2412f2bb1caeSJulian Elischer  *  bits are set of different form. The C/R bit in the Type field shall be set
2413f2bb1caeSJulian Elischer  *  as it is stated above, while the C/R bit in the Address field (see subclause
2414f2bb1caeSJulian Elischer  *  5.2.1.2) shall be set as it is described in subclause 5.4.3.1."
2415f2bb1caeSJulian Elischer  */
2416f2bb1caeSJulian Elischer 
2417f2bb1caeSJulian Elischer static int
2418f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_mcc(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2419f2bb1caeSJulian Elischer {
2420f2bb1caeSJulian Elischer 	struct rfcomm_mcc_hdr	*hdr = NULL;
2421f2bb1caeSJulian Elischer 	u_int8_t		 cr, type, length;
2422f2bb1caeSJulian Elischer 
2423f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
2424f2bb1caeSJulian Elischer 
2425f2bb1caeSJulian Elischer 	/*
2426f2bb1caeSJulian Elischer 	 * We can access data directly in the first mbuf, because we have
2427f2bb1caeSJulian Elischer 	 * m_pullup()'ed mbuf chain in ng_btsocket_rfcomm_receive_frame().
2428f2bb1caeSJulian Elischer 	 * All MCC commands should fit into single mbuf (except probably TEST).
2429f2bb1caeSJulian Elischer 	 */
2430f2bb1caeSJulian Elischer 
2431f2bb1caeSJulian Elischer 	hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2432f2bb1caeSJulian Elischer 	cr = RFCOMM_CR(hdr->type);
2433f2bb1caeSJulian Elischer 	type = RFCOMM_MCC_TYPE(hdr->type);
2434f2bb1caeSJulian Elischer 	length = RFCOMM_MCC_LENGTH(hdr->length);
2435f2bb1caeSJulian Elischer 
2436f2bb1caeSJulian Elischer 	/* Check MCC frame length */
2437f2bb1caeSJulian Elischer 	if (sizeof(*hdr) + length != m0->m_pkthdr.len) {
2438f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ERR(
2439f2bb1caeSJulian Elischer "%s: Invalid MCC frame length=%d, len=%d\n",
2440f2bb1caeSJulian Elischer 			__func__, length, m0->m_pkthdr.len);
2441f2bb1caeSJulian Elischer 		NG_FREE_M(m0);
2442f2bb1caeSJulian Elischer 
2443f2bb1caeSJulian Elischer 		return (EMSGSIZE);
2444f2bb1caeSJulian Elischer 	}
2445f2bb1caeSJulian Elischer 
2446f2bb1caeSJulian Elischer 	switch (type) {
2447f2bb1caeSJulian Elischer 	case RFCOMM_MCC_TEST:
2448f2bb1caeSJulian Elischer 		return (ng_btsocket_rfcomm_receive_test(s, m0));
2449f2bb1caeSJulian Elischer 		/* NOT REACHED */
2450f2bb1caeSJulian Elischer 
2451f2bb1caeSJulian Elischer 	case RFCOMM_MCC_FCON:
2452f2bb1caeSJulian Elischer 	case RFCOMM_MCC_FCOFF:
2453f2bb1caeSJulian Elischer 		return (ng_btsocket_rfcomm_receive_fc(s, m0));
2454f2bb1caeSJulian Elischer 		/* NOT REACHED */
2455f2bb1caeSJulian Elischer 
2456f2bb1caeSJulian Elischer 	case RFCOMM_MCC_MSC:
2457f2bb1caeSJulian Elischer 		return (ng_btsocket_rfcomm_receive_msc(s, m0));
2458f2bb1caeSJulian Elischer 		/* NOT REACHED */
2459f2bb1caeSJulian Elischer 
2460f2bb1caeSJulian Elischer 	case RFCOMM_MCC_RPN:
2461f2bb1caeSJulian Elischer 		return (ng_btsocket_rfcomm_receive_rpn(s, m0));
2462f2bb1caeSJulian Elischer 		/* NOT REACHED */
2463f2bb1caeSJulian Elischer 
2464f2bb1caeSJulian Elischer 	case RFCOMM_MCC_RLS:
2465f2bb1caeSJulian Elischer 		return (ng_btsocket_rfcomm_receive_rls(s, m0));
2466f2bb1caeSJulian Elischer 		/* NOT REACHED */
2467f2bb1caeSJulian Elischer 
2468f2bb1caeSJulian Elischer 	case RFCOMM_MCC_PN:
2469f2bb1caeSJulian Elischer 		return (ng_btsocket_rfcomm_receive_pn(s, m0));
2470f2bb1caeSJulian Elischer 		/* NOT REACHED */
2471f2bb1caeSJulian Elischer 
2472f2bb1caeSJulian Elischer 	case RFCOMM_MCC_NSC:
2473f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ERR(
2474f2bb1caeSJulian Elischer "%s: Got MCC NSC, type=%#x, cr=%d, length=%d, session state=%d, flags=%#x, " \
2475f2bb1caeSJulian Elischer "mtu=%d, len=%d\n",	__func__, RFCOMM_MCC_TYPE(*((u_int8_t *)(hdr + 1))), cr,
2476f2bb1caeSJulian Elischer 			 length, s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2477f2bb1caeSJulian Elischer 		NG_FREE_M(m0);
2478f2bb1caeSJulian Elischer 		break;
2479f2bb1caeSJulian Elischer 
2480f2bb1caeSJulian Elischer 	default:
2481f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ERR(
2482f2bb1caeSJulian Elischer "%s: Got unknown MCC, type=%#x, cr=%d, length=%d, session state=%d, " \
2483f2bb1caeSJulian Elischer "flags=%#x, mtu=%d, len=%d\n",
2484f2bb1caeSJulian Elischer 			__func__, type, cr, length, s->state, s->flags,
2485f2bb1caeSJulian Elischer 			s->mtu, m0->m_pkthdr.len);
2486f2bb1caeSJulian Elischer 
2487f2bb1caeSJulian Elischer 		/* Reuse mbuf to send NSC */
2488f2bb1caeSJulian Elischer 		hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2489f2bb1caeSJulian Elischer 		m0->m_pkthdr.len = m0->m_len = sizeof(*hdr);
2490f2bb1caeSJulian Elischer 
2491f2bb1caeSJulian Elischer 		/* Create MCC NSC header */
2492f2bb1caeSJulian Elischer 		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_NSC);
2493f2bb1caeSJulian Elischer 		hdr->length = RFCOMM_MKLEN8(1);
2494f2bb1caeSJulian Elischer 
2495f2bb1caeSJulian Elischer 		/* Put back MCC command type we did not like */
2496f2bb1caeSJulian Elischer 		m0->m_data[m0->m_len] = RFCOMM_MKMCC_TYPE(cr, type);
2497f2bb1caeSJulian Elischer 		m0->m_pkthdr.len ++;
2498f2bb1caeSJulian Elischer 		m0->m_len ++;
2499f2bb1caeSJulian Elischer 
2500f2bb1caeSJulian Elischer 		/* Send UIH frame */
2501f2bb1caeSJulian Elischer 		return (ng_btsocket_rfcomm_send_uih(s,
2502f2bb1caeSJulian Elischer 				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0));
2503f2bb1caeSJulian Elischer 		/* NOT REACHED */
2504f2bb1caeSJulian Elischer 	}
2505f2bb1caeSJulian Elischer 
2506f2bb1caeSJulian Elischer 	return (0);
2507f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_mcc */
2508f2bb1caeSJulian Elischer 
2509f2bb1caeSJulian Elischer /*
2510f2bb1caeSJulian Elischer  * Receive RFCOMM TEST MCC command
2511f2bb1caeSJulian Elischer  */
2512f2bb1caeSJulian Elischer 
2513f2bb1caeSJulian Elischer static int
2514f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_test(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2515f2bb1caeSJulian Elischer {
2516f2bb1caeSJulian Elischer 	struct rfcomm_mcc_hdr	*hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2517f2bb1caeSJulian Elischer 	int			 error = 0;
2518f2bb1caeSJulian Elischer 
2519f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
2520f2bb1caeSJulian Elischer 
2521f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
2522f2bb1caeSJulian Elischer "%s: Got MCC TEST, cr=%d, length=%d, session state=%d, flags=%#x, mtu=%d, " \
2523f2bb1caeSJulian Elischer "len=%d\n",	__func__, RFCOMM_CR(hdr->type), RFCOMM_MCC_LENGTH(hdr->length),
2524f2bb1caeSJulian Elischer 		s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2525f2bb1caeSJulian Elischer 
2526f2bb1caeSJulian Elischer 	if (RFCOMM_CR(hdr->type)) {
2527f2bb1caeSJulian Elischer 		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_TEST);
2528f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_uih(s,
2529f2bb1caeSJulian Elischer 				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2530f2bb1caeSJulian Elischer 	} else
2531f2bb1caeSJulian Elischer 		NG_FREE_M(m0); /* XXX ignore response */
2532f2bb1caeSJulian Elischer 
2533f2bb1caeSJulian Elischer 	return (error);
2534f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_test */
2535f2bb1caeSJulian Elischer 
2536f2bb1caeSJulian Elischer /*
2537f2bb1caeSJulian Elischer  * Receive RFCOMM FCON/FCOFF MCC command
2538f2bb1caeSJulian Elischer  */
2539f2bb1caeSJulian Elischer 
2540f2bb1caeSJulian Elischer static int
2541f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_fc(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2542f2bb1caeSJulian Elischer {
2543f2bb1caeSJulian Elischer 	struct rfcomm_mcc_hdr	*hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2544f2bb1caeSJulian Elischer 	u_int8_t		 type = RFCOMM_MCC_TYPE(hdr->type);
2545f2bb1caeSJulian Elischer 	int			 error = 0;
2546f2bb1caeSJulian Elischer 
2547f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
2548f2bb1caeSJulian Elischer 
2549f2bb1caeSJulian Elischer 	/*
2550f2bb1caeSJulian Elischer 	 * Turn ON/OFF aggregate flow on the entire session. When remote peer
2551f2bb1caeSJulian Elischer 	 * asserted flow control no transmission shall occur except on dlci 0
2552f2bb1caeSJulian Elischer 	 * (control channel).
2553f2bb1caeSJulian Elischer 	 */
2554f2bb1caeSJulian Elischer 
2555f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
2556f2bb1caeSJulian Elischer "%s: Got MCC FC%s, cr=%d, length=%d, session state=%d, flags=%#x, mtu=%d, " \
2557f2bb1caeSJulian Elischer "len=%d\n",	__func__, (type == RFCOMM_MCC_FCON)? "ON" : "OFF",
2558f2bb1caeSJulian Elischer 		RFCOMM_CR(hdr->type), RFCOMM_MCC_LENGTH(hdr->length),
2559f2bb1caeSJulian Elischer 		s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2560f2bb1caeSJulian Elischer 
2561f2bb1caeSJulian Elischer 	if (RFCOMM_CR(hdr->type)) {
2562f2bb1caeSJulian Elischer 		if (type == RFCOMM_MCC_FCON)
2563f2bb1caeSJulian Elischer 			s->flags &= ~NG_BTSOCKET_RFCOMM_SESSION_RFC;
2564f2bb1caeSJulian Elischer 		else
2565f2bb1caeSJulian Elischer 			s->flags |= NG_BTSOCKET_RFCOMM_SESSION_RFC;
2566f2bb1caeSJulian Elischer 
2567f2bb1caeSJulian Elischer 		hdr->type = RFCOMM_MKMCC_TYPE(0, type);
2568f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_uih(s,
2569f2bb1caeSJulian Elischer 				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2570f2bb1caeSJulian Elischer 	} else
2571f2bb1caeSJulian Elischer 		NG_FREE_M(m0); /* XXX ignore response */
2572f2bb1caeSJulian Elischer 
2573f2bb1caeSJulian Elischer 	return (error);
2574f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_fc  */
2575f2bb1caeSJulian Elischer 
2576f2bb1caeSJulian Elischer /*
2577f2bb1caeSJulian Elischer  * Receive RFCOMM MSC MCC command
2578f2bb1caeSJulian Elischer  */
2579f2bb1caeSJulian Elischer 
2580f2bb1caeSJulian Elischer static int
2581f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_msc(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2582f2bb1caeSJulian Elischer {
2583f2bb1caeSJulian Elischer 	struct rfcomm_mcc_hdr		*hdr = mtod(m0, struct rfcomm_mcc_hdr*);
2584f2bb1caeSJulian Elischer 	struct rfcomm_mcc_msc		*msc = (struct rfcomm_mcc_msc *)(hdr+1);
2585f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_t	*pcb = NULL;
2586f2bb1caeSJulian Elischer 	int				 error = 0;
2587f2bb1caeSJulian Elischer 
2588f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
2589f2bb1caeSJulian Elischer 
2590f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
2591f2bb1caeSJulian Elischer "%s: Got MCC MSC, dlci=%d, cr=%d, length=%d, session state=%d, flags=%#x, " \
2592f2bb1caeSJulian Elischer "mtu=%d, len=%d\n",
2593f2bb1caeSJulian Elischer 		__func__,  RFCOMM_DLCI(msc->address), RFCOMM_CR(hdr->type),
2594f2bb1caeSJulian Elischer 		RFCOMM_MCC_LENGTH(hdr->length), s->state, s->flags,
2595f2bb1caeSJulian Elischer 		s->mtu, m0->m_pkthdr.len);
2596f2bb1caeSJulian Elischer 
2597f2bb1caeSJulian Elischer 	if (RFCOMM_CR(hdr->type)) {
2598f2bb1caeSJulian Elischer 		pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, RFCOMM_DLCI(msc->address));
2599f2bb1caeSJulian Elischer 		if (pcb == NULL) {
2600f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_WARN(
2601f2bb1caeSJulian Elischer "%s: Got MSC command for non-existing dlci=%d\n",
2602f2bb1caeSJulian Elischer 				__func__, RFCOMM_DLCI(msc->address));
2603f2bb1caeSJulian Elischer 			NG_FREE_M(m0);
2604f2bb1caeSJulian Elischer 
2605f2bb1caeSJulian Elischer 			return (ENOENT);
2606f2bb1caeSJulian Elischer 		}
2607f2bb1caeSJulian Elischer 
2608f2bb1caeSJulian Elischer 		mtx_lock(&pcb->pcb_mtx);
2609f2bb1caeSJulian Elischer 
2610f2bb1caeSJulian Elischer 		if (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTING &&
2611f2bb1caeSJulian Elischer 		    pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTED) {
2612f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_WARN(
2613f2bb1caeSJulian Elischer "%s: Got MSC on dlci=%d in invalid state=%d\n",
2614f2bb1caeSJulian Elischer 				__func__, RFCOMM_DLCI(msc->address),
2615f2bb1caeSJulian Elischer 				pcb->state);
2616f2bb1caeSJulian Elischer 
2617f2bb1caeSJulian Elischer 			mtx_unlock(&pcb->pcb_mtx);
2618f2bb1caeSJulian Elischer 			NG_FREE_M(m0);
2619f2bb1caeSJulian Elischer 
2620f2bb1caeSJulian Elischer 			return (EINVAL);
2621f2bb1caeSJulian Elischer 		}
2622f2bb1caeSJulian Elischer 
2623f2bb1caeSJulian Elischer 		pcb->rmodem = msc->modem; /* Update remote port signals */
2624f2bb1caeSJulian Elischer 
2625f2bb1caeSJulian Elischer 		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_MSC);
2626f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_uih(s,
2627f2bb1caeSJulian Elischer 				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2628f2bb1caeSJulian Elischer 
2629f2bb1caeSJulian Elischer #if 0 /* YYY */
2630f2bb1caeSJulian Elischer 		/* Send more data from DLC. XXX check for errors? */
2631f2bb1caeSJulian Elischer 		if (!(pcb->rmodem & RFCOMM_MODEM_FC) &&
2632f2bb1caeSJulian Elischer 		    !(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC))
2633f2bb1caeSJulian Elischer 			ng_btsocket_rfcomm_pcb_send(pcb, ALOT);
2634f2bb1caeSJulian Elischer #endif /* YYY */
2635f2bb1caeSJulian Elischer 
2636f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
2637f2bb1caeSJulian Elischer 	} else
2638f2bb1caeSJulian Elischer 		NG_FREE_M(m0); /* XXX ignore response */
2639f2bb1caeSJulian Elischer 
2640f2bb1caeSJulian Elischer 	return (error);
2641f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_msc */
2642f2bb1caeSJulian Elischer 
2643f2bb1caeSJulian Elischer /*
2644f2bb1caeSJulian Elischer  * Receive RFCOMM RPN MCC command
2645f2bb1caeSJulian Elischer  * XXX FIXME do we need htole16/le16toh for RPN param_mask?
2646f2bb1caeSJulian Elischer  */
2647f2bb1caeSJulian Elischer 
2648f2bb1caeSJulian Elischer static int
2649f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_rpn(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2650f2bb1caeSJulian Elischer {
2651f2bb1caeSJulian Elischer 	struct rfcomm_mcc_hdr	*hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2652f2bb1caeSJulian Elischer 	struct rfcomm_mcc_rpn	*rpn = (struct rfcomm_mcc_rpn *)(hdr + 1);
2653f2bb1caeSJulian Elischer 	int			 error = 0;
2654f2bb1caeSJulian Elischer 	u_int16_t		 param_mask;
2655f2bb1caeSJulian Elischer 	u_int8_t		 bit_rate, data_bits, stop_bits, parity,
2656f2bb1caeSJulian Elischer 				 flow_control, xon_char, xoff_char;
2657f2bb1caeSJulian Elischer 
2658f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
2659f2bb1caeSJulian Elischer 
2660f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
2661f2bb1caeSJulian Elischer "%s: Got MCC RPN, dlci=%d, cr=%d, length=%d, session state=%d, flags=%#x, " \
2662f2bb1caeSJulian Elischer "mtu=%d, len=%d\n",
2663f2bb1caeSJulian Elischer 		__func__, RFCOMM_DLCI(rpn->dlci), RFCOMM_CR(hdr->type),
2664f2bb1caeSJulian Elischer 		RFCOMM_MCC_LENGTH(hdr->length), s->state, s->flags,
2665f2bb1caeSJulian Elischer 		s->mtu, m0->m_pkthdr.len);
2666f2bb1caeSJulian Elischer 
2667f2bb1caeSJulian Elischer 	if (RFCOMM_CR(hdr->type)) {
2668f2bb1caeSJulian Elischer 		param_mask = RFCOMM_RPN_PM_ALL;
2669f2bb1caeSJulian Elischer 
2670f2bb1caeSJulian Elischer 		if (RFCOMM_MCC_LENGTH(hdr->length) == 1) {
2671f2bb1caeSJulian Elischer 			/* Request - return default setting */
2672f2bb1caeSJulian Elischer 			bit_rate = RFCOMM_RPN_BR_115200;
2673f2bb1caeSJulian Elischer 			data_bits = RFCOMM_RPN_DATA_8;
2674f2bb1caeSJulian Elischer 			stop_bits = RFCOMM_RPN_STOP_1;
2675f2bb1caeSJulian Elischer 			parity = RFCOMM_RPN_PARITY_NONE;
2676f2bb1caeSJulian Elischer 			flow_control = RFCOMM_RPN_FLOW_NONE;
2677f2bb1caeSJulian Elischer 			xon_char = RFCOMM_RPN_XON_CHAR;
2678f2bb1caeSJulian Elischer 			xoff_char = RFCOMM_RPN_XOFF_CHAR;
2679f2bb1caeSJulian Elischer                 } else {
2680f2bb1caeSJulian Elischer 			/*
2681f2bb1caeSJulian Elischer 			 * Ignore/accept bit_rate, 8 bits, 1 stop bit, no
2682f2bb1caeSJulian Elischer 			 * parity, no flow control lines, default XON/XOFF
2683f2bb1caeSJulian Elischer 			 * chars.
2684f2bb1caeSJulian Elischer 			 */
2685f2bb1caeSJulian Elischer 
2686f2bb1caeSJulian Elischer 			bit_rate = rpn->bit_rate;
2687f2bb1caeSJulian Elischer 			rpn->param_mask = le16toh(rpn->param_mask); /* XXX */
2688f2bb1caeSJulian Elischer 
2689f2bb1caeSJulian Elischer 			data_bits = RFCOMM_RPN_DATA_BITS(rpn->line_settings);
2690f2bb1caeSJulian Elischer 			if (rpn->param_mask & RFCOMM_RPN_PM_DATA &&
2691f2bb1caeSJulian Elischer 			    data_bits != RFCOMM_RPN_DATA_8) {
2692f2bb1caeSJulian Elischer 				data_bits = RFCOMM_RPN_DATA_8;
2693f2bb1caeSJulian Elischer 				param_mask ^= RFCOMM_RPN_PM_DATA;
2694f2bb1caeSJulian Elischer 			}
2695f2bb1caeSJulian Elischer 
2696f2bb1caeSJulian Elischer 			stop_bits = RFCOMM_RPN_STOP_BITS(rpn->line_settings);
2697f2bb1caeSJulian Elischer 			if (rpn->param_mask & RFCOMM_RPN_PM_STOP &&
2698f2bb1caeSJulian Elischer 			    stop_bits != RFCOMM_RPN_STOP_1) {
2699f2bb1caeSJulian Elischer 				stop_bits = RFCOMM_RPN_STOP_1;
2700f2bb1caeSJulian Elischer 				param_mask ^= RFCOMM_RPN_PM_STOP;
2701f2bb1caeSJulian Elischer 			}
2702f2bb1caeSJulian Elischer 
2703f2bb1caeSJulian Elischer 			parity = RFCOMM_RPN_PARITY(rpn->line_settings);
2704f2bb1caeSJulian Elischer 			if (rpn->param_mask & RFCOMM_RPN_PM_PARITY &&
2705f2bb1caeSJulian Elischer 			    parity != RFCOMM_RPN_PARITY_NONE) {
2706f2bb1caeSJulian Elischer 				parity = RFCOMM_RPN_PARITY_NONE;
2707f2bb1caeSJulian Elischer 				param_mask ^= RFCOMM_RPN_PM_PARITY;
2708f2bb1caeSJulian Elischer 			}
2709f2bb1caeSJulian Elischer 
2710f2bb1caeSJulian Elischer 			flow_control = rpn->flow_control;
2711f2bb1caeSJulian Elischer 			if (rpn->param_mask & RFCOMM_RPN_PM_FLOW &&
2712f2bb1caeSJulian Elischer 			    flow_control != RFCOMM_RPN_FLOW_NONE) {
2713f2bb1caeSJulian Elischer 				flow_control = RFCOMM_RPN_FLOW_NONE;
2714f2bb1caeSJulian Elischer 				param_mask ^= RFCOMM_RPN_PM_FLOW;
2715f2bb1caeSJulian Elischer 			}
2716f2bb1caeSJulian Elischer 
2717f2bb1caeSJulian Elischer 			xon_char = rpn->xon_char;
2718f2bb1caeSJulian Elischer 			if (rpn->param_mask & RFCOMM_RPN_PM_XON &&
2719f2bb1caeSJulian Elischer 			    xon_char != RFCOMM_RPN_XON_CHAR) {
2720f2bb1caeSJulian Elischer 				xon_char = RFCOMM_RPN_XON_CHAR;
2721f2bb1caeSJulian Elischer 				param_mask ^= RFCOMM_RPN_PM_XON;
2722f2bb1caeSJulian Elischer 			}
2723f2bb1caeSJulian Elischer 
2724f2bb1caeSJulian Elischer 			xoff_char = rpn->xoff_char;
2725f2bb1caeSJulian Elischer 			if (rpn->param_mask & RFCOMM_RPN_PM_XOFF &&
2726f2bb1caeSJulian Elischer 			    xoff_char != RFCOMM_RPN_XOFF_CHAR) {
2727f2bb1caeSJulian Elischer 				xoff_char = RFCOMM_RPN_XOFF_CHAR;
2728f2bb1caeSJulian Elischer 				param_mask ^= RFCOMM_RPN_PM_XOFF;
2729f2bb1caeSJulian Elischer 			}
2730f2bb1caeSJulian Elischer 		}
2731f2bb1caeSJulian Elischer 
2732f2bb1caeSJulian Elischer 		rpn->bit_rate = bit_rate;
2733f2bb1caeSJulian Elischer 		rpn->line_settings = RFCOMM_MKRPN_LINE_SETTINGS(data_bits,
2734f2bb1caeSJulian Elischer 						stop_bits, parity);
2735f2bb1caeSJulian Elischer 		rpn->flow_control = flow_control;
2736f2bb1caeSJulian Elischer 		rpn->xon_char = xon_char;
2737f2bb1caeSJulian Elischer 		rpn->xoff_char = xoff_char;
2738f2bb1caeSJulian Elischer 		rpn->param_mask = htole16(param_mask); /* XXX */
2739f2bb1caeSJulian Elischer 
2740f2bb1caeSJulian Elischer 		m0->m_pkthdr.len = m0->m_len = sizeof(*hdr) + sizeof(*rpn);
2741f2bb1caeSJulian Elischer 
2742f2bb1caeSJulian Elischer 		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_RPN);
2743f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_uih(s,
2744f2bb1caeSJulian Elischer 				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2745f2bb1caeSJulian Elischer 	} else
2746f2bb1caeSJulian Elischer 		NG_FREE_M(m0); /* XXX ignore response */
2747f2bb1caeSJulian Elischer 
2748f2bb1caeSJulian Elischer 	return (error);
2749f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_rpn */
2750f2bb1caeSJulian Elischer 
2751f2bb1caeSJulian Elischer /*
2752f2bb1caeSJulian Elischer  * Receive RFCOMM RLS MCC command
2753f2bb1caeSJulian Elischer  */
2754f2bb1caeSJulian Elischer 
2755f2bb1caeSJulian Elischer static int
2756f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_rls(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2757f2bb1caeSJulian Elischer {
2758f2bb1caeSJulian Elischer 	struct rfcomm_mcc_hdr	*hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2759f2bb1caeSJulian Elischer 	struct rfcomm_mcc_rls	*rls = (struct rfcomm_mcc_rls *)(hdr + 1);
2760f2bb1caeSJulian Elischer 	int			 error = 0;
2761f2bb1caeSJulian Elischer 
2762f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
2763f2bb1caeSJulian Elischer 
2764f2bb1caeSJulian Elischer 	/*
2765f2bb1caeSJulian Elischer 	 * XXX FIXME Do we have to do anything else here? Remote peer tries to
2766f2bb1caeSJulian Elischer 	 * tell us something about DLCI. Just report what we have received and
2767f2bb1caeSJulian Elischer 	 * return back received values as required by TS 07.10 spec.
2768f2bb1caeSJulian Elischer 	 */
2769f2bb1caeSJulian Elischer 
2770f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
2771f2bb1caeSJulian Elischer "%s: Got MCC RLS, dlci=%d, status=%#x, cr=%d, length=%d, session state=%d, " \
2772f2bb1caeSJulian Elischer "flags=%#x, mtu=%d, len=%d\n",
2773f2bb1caeSJulian Elischer 		__func__, RFCOMM_DLCI(rls->address), rls->status,
2774f2bb1caeSJulian Elischer 		RFCOMM_CR(hdr->type), RFCOMM_MCC_LENGTH(hdr->length),
2775f2bb1caeSJulian Elischer 		s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2776f2bb1caeSJulian Elischer 
2777f2bb1caeSJulian Elischer 	if (RFCOMM_CR(hdr->type)) {
2778f2bb1caeSJulian Elischer 		if (rls->status & 0x1)
2779f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_ERR(
2780f2bb1caeSJulian Elischer "%s: Got RLS dlci=%d, error=%#x\n", __func__, RFCOMM_DLCI(rls->address),
2781f2bb1caeSJulian Elischer 				rls->status >> 1);
2782f2bb1caeSJulian Elischer 
2783f2bb1caeSJulian Elischer 		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_RLS);
2784f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_uih(s,
2785f2bb1caeSJulian Elischer 				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2786f2bb1caeSJulian Elischer 	} else
2787f2bb1caeSJulian Elischer 		NG_FREE_M(m0); /* XXX ignore responses */
2788f2bb1caeSJulian Elischer 
2789f2bb1caeSJulian Elischer 	return (error);
2790f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_rls */
2791f2bb1caeSJulian Elischer 
2792f2bb1caeSJulian Elischer /*
2793f2bb1caeSJulian Elischer  * Receive RFCOMM PN MCC command
2794f2bb1caeSJulian Elischer  */
2795f2bb1caeSJulian Elischer 
2796f2bb1caeSJulian Elischer static int
2797f2bb1caeSJulian Elischer ng_btsocket_rfcomm_receive_pn(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2798f2bb1caeSJulian Elischer {
2799f2bb1caeSJulian Elischer 	struct rfcomm_mcc_hdr		*hdr = mtod(m0, struct rfcomm_mcc_hdr*);
2800f2bb1caeSJulian Elischer 	struct rfcomm_mcc_pn		*pn = (struct rfcomm_mcc_pn *)(hdr+1);
2801f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_t	*pcb = NULL;
2802f2bb1caeSJulian Elischer 	int				 error = 0;
2803f2bb1caeSJulian Elischer 
2804f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
2805f2bb1caeSJulian Elischer 
2806f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
2807f2bb1caeSJulian Elischer "%s: Got MCC PN, dlci=%d, cr=%d, length=%d, flow_control=%#x, priority=%d, " \
2808f2bb1caeSJulian Elischer "ack_timer=%d, mtu=%d, max_retrans=%d, credits=%d, session state=%d, " \
2809f2bb1caeSJulian Elischer "flags=%#x, session mtu=%d, len=%d\n",
2810f2bb1caeSJulian Elischer 		__func__, pn->dlci, RFCOMM_CR(hdr->type),
2811f2bb1caeSJulian Elischer 		RFCOMM_MCC_LENGTH(hdr->length), pn->flow_control, pn->priority,
2812f2bb1caeSJulian Elischer 		pn->ack_timer, le16toh(pn->mtu), pn->max_retrans, pn->credits,
2813f2bb1caeSJulian Elischer 		s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2814f2bb1caeSJulian Elischer 
2815f2bb1caeSJulian Elischer 	if (pn->dlci == 0) {
2816f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_ERR("%s: Zero dlci in MCC PN\n", __func__);
2817f2bb1caeSJulian Elischer 		NG_FREE_M(m0);
2818f2bb1caeSJulian Elischer 
2819f2bb1caeSJulian Elischer 		return (EINVAL);
2820f2bb1caeSJulian Elischer 	}
2821f2bb1caeSJulian Elischer 
2822f2bb1caeSJulian Elischer 	/* Check if we have this dlci */
2823f2bb1caeSJulian Elischer 	pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, pn->dlci);
2824f2bb1caeSJulian Elischer 	if (pcb != NULL) {
2825f2bb1caeSJulian Elischer 		mtx_lock(&pcb->pcb_mtx);
2826f2bb1caeSJulian Elischer 
2827f2bb1caeSJulian Elischer 		if (RFCOMM_CR(hdr->type)) {
2828f2bb1caeSJulian Elischer 			/* PN Request */
2829f2bb1caeSJulian Elischer 			ng_btsocket_rfcomm_set_pn(pcb, 1, pn->flow_control,
2830f2bb1caeSJulian Elischer 				pn->credits, pn->mtu);
2831f2bb1caeSJulian Elischer 
2832f2bb1caeSJulian Elischer 			if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) {
2833f2bb1caeSJulian Elischer 				pn->flow_control = 0xe0;
2834f2bb1caeSJulian Elischer 				pn->credits = RFCOMM_DEFAULT_CREDITS;
2835f2bb1caeSJulian Elischer 			} else {
2836f2bb1caeSJulian Elischer 				pn->flow_control = 0;
2837f2bb1caeSJulian Elischer 				pn->credits = 0;
2838f2bb1caeSJulian Elischer 			}
2839f2bb1caeSJulian Elischer 
2840f2bb1caeSJulian Elischer 			hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_PN);
2841f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_send_uih(s,
2842f2bb1caeSJulian Elischer 					RFCOMM_MKADDRESS(INITIATOR(s), 0),
2843f2bb1caeSJulian Elischer 					0, 0, m0);
2844f2bb1caeSJulian Elischer 		} else {
2845f2bb1caeSJulian Elischer 			/* PN Response - proceed with SABM. Timeout still set */
2846f2bb1caeSJulian Elischer 			if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_CONFIGURING) {
2847f2bb1caeSJulian Elischer 				ng_btsocket_rfcomm_set_pn(pcb, 0,
2848f2bb1caeSJulian Elischer 					pn->flow_control, pn->credits, pn->mtu);
2849f2bb1caeSJulian Elischer 
2850f2bb1caeSJulian Elischer 				pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTING;
2851f2bb1caeSJulian Elischer 				error = ng_btsocket_rfcomm_send_command(s,
2852f2bb1caeSJulian Elischer 						RFCOMM_FRAME_SABM, pn->dlci);
2853f2bb1caeSJulian Elischer 			} else
2854f2bb1caeSJulian Elischer 				NG_BTSOCKET_RFCOMM_WARN(
2855f2bb1caeSJulian Elischer "%s: Got PN response for dlci=%d in invalid state=%d\n",
2856f2bb1caeSJulian Elischer 					__func__, pn->dlci, pcb->state);
2857f2bb1caeSJulian Elischer 
2858f2bb1caeSJulian Elischer 			NG_FREE_M(m0);
2859f2bb1caeSJulian Elischer 		}
2860f2bb1caeSJulian Elischer 
2861f2bb1caeSJulian Elischer 		mtx_unlock(&pcb->pcb_mtx);
2862f2bb1caeSJulian Elischer 	} else if (RFCOMM_CR(hdr->type)) {
2863053359b7SPedro F. Giffuni 		/* PN request to non-existing dlci - incoming connection */
2864f2bb1caeSJulian Elischer 		pcb = ng_btsocket_rfcomm_connect_ind(s,
2865f2bb1caeSJulian Elischer 				RFCOMM_SRVCHANNEL(pn->dlci));
2866f2bb1caeSJulian Elischer 		if (pcb != NULL) {
2867f2bb1caeSJulian Elischer 			mtx_lock(&pcb->pcb_mtx);
2868f2bb1caeSJulian Elischer 
2869f2bb1caeSJulian Elischer 			pcb->dlci = pn->dlci;
2870f2bb1caeSJulian Elischer 
2871f2bb1caeSJulian Elischer 			ng_btsocket_rfcomm_set_pn(pcb, 1, pn->flow_control,
2872f2bb1caeSJulian Elischer 				pn->credits, pn->mtu);
2873f2bb1caeSJulian Elischer 
2874f2bb1caeSJulian Elischer 			if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) {
2875f2bb1caeSJulian Elischer 				pn->flow_control = 0xe0;
2876f2bb1caeSJulian Elischer 				pn->credits = RFCOMM_DEFAULT_CREDITS;
2877f2bb1caeSJulian Elischer 			} else {
2878f2bb1caeSJulian Elischer 				pn->flow_control = 0;
2879f2bb1caeSJulian Elischer 				pn->credits = 0;
2880f2bb1caeSJulian Elischer 			}
2881f2bb1caeSJulian Elischer 
2882f2bb1caeSJulian Elischer 			hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_PN);
2883f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_send_uih(s,
2884f2bb1caeSJulian Elischer 					RFCOMM_MKADDRESS(INITIATOR(s), 0),
2885f2bb1caeSJulian Elischer 					0, 0, m0);
2886f2bb1caeSJulian Elischer 
2887f2bb1caeSJulian Elischer 			if (error == 0) {
2888f2bb1caeSJulian Elischer 				ng_btsocket_rfcomm_timeout(pcb);
2889f2bb1caeSJulian Elischer 				pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTING;
2890f2bb1caeSJulian Elischer 				soisconnecting(pcb->so);
28917c380856SMaksim Yevmenkin 			} else
28927c380856SMaksim Yevmenkin 				ng_btsocket_rfcomm_pcb_kill(pcb, error);
2893f2bb1caeSJulian Elischer 
2894f2bb1caeSJulian Elischer 			mtx_unlock(&pcb->pcb_mtx);
2895f2bb1caeSJulian Elischer 		} else {
2896f2bb1caeSJulian Elischer 			/* Nobody is listen()ing on this channel */
2897f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_send_command(s,
2898f2bb1caeSJulian Elischer 					RFCOMM_FRAME_DM, pn->dlci);
2899f2bb1caeSJulian Elischer 			NG_FREE_M(m0);
2900f2bb1caeSJulian Elischer 		}
2901f2bb1caeSJulian Elischer 	} else
2902f2bb1caeSJulian Elischer 		NG_FREE_M(m0); /* XXX ignore response to non-existing dlci */
2903f2bb1caeSJulian Elischer 
2904f2bb1caeSJulian Elischer 	return (error);
2905f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_receive_pn */
2906f2bb1caeSJulian Elischer 
2907f2bb1caeSJulian Elischer /*
2908f2bb1caeSJulian Elischer  * Set PN parameters for dlci. Caller must hold pcb->pcb_mtx.
2909f2bb1caeSJulian Elischer  *
2910f2bb1caeSJulian Elischer  * From Bluetooth spec.
2911f2bb1caeSJulian Elischer  *
2912f2bb1caeSJulian Elischer  * "... The CL1 - CL4 field is completely redefined. (In TS07.10 this defines
2913f2bb1caeSJulian Elischer  *  the convergence layer to use, which is not applicable to RFCOMM. In RFCOMM,
2914f2bb1caeSJulian Elischer  *  in Bluetooth versions up to 1.0B, this field was forced to 0).
2915f2bb1caeSJulian Elischer  *
2916f2bb1caeSJulian Elischer  *  In the PN request sent prior to a DLC establishment, this field must contain
2917f2bb1caeSJulian Elischer  *  the value 15 (0xF), indicating support of credit based flow control in the
2918f2bb1caeSJulian Elischer  *  sender. See Table 5.3 below. If the PN response contains any other value
2919f2bb1caeSJulian Elischer  *  than 14 (0xE) in this field, it is inferred that the peer RFCOMM entity is
2920f2bb1caeSJulian Elischer  *  not supporting the credit based flow control feature. (This is only possible
2921f2bb1caeSJulian Elischer  *  if the peer RFCOMM implementation is only conforming to Bluetooth version
2922f2bb1caeSJulian Elischer  *  1.0B.) If a PN request is sent on an already open DLC, then this field must
2923f2bb1caeSJulian Elischer  *  contain the value zero; it is not possible to set initial credits  more
2924f2bb1caeSJulian Elischer  *  than once per DLC activation. A responding implementation must set this
2925f2bb1caeSJulian Elischer  *  field in the PN response to 14 (0xE), if (and only if) the value in the PN
2926f2bb1caeSJulian Elischer  *  request was 15..."
2927f2bb1caeSJulian Elischer  */
2928f2bb1caeSJulian Elischer 
2929f2bb1caeSJulian Elischer static void
2930f2bb1caeSJulian Elischer ng_btsocket_rfcomm_set_pn(ng_btsocket_rfcomm_pcb_p pcb, u_int8_t cr,
2931f2bb1caeSJulian Elischer 		u_int8_t flow_control, u_int8_t credits, u_int16_t mtu)
2932f2bb1caeSJulian Elischer {
2933f2bb1caeSJulian Elischer 	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
2934f2bb1caeSJulian Elischer 
2935f2bb1caeSJulian Elischer 	pcb->mtu = le16toh(mtu);
2936f2bb1caeSJulian Elischer 
2937f2bb1caeSJulian Elischer 	if (cr) {
2938f2bb1caeSJulian Elischer 		if (flow_control == 0xf0) {
2939f2bb1caeSJulian Elischer 			pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_CFC;
2940f2bb1caeSJulian Elischer 			pcb->tx_cred = credits;
2941f2bb1caeSJulian Elischer 		} else {
2942f2bb1caeSJulian Elischer 			pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_CFC;
2943f2bb1caeSJulian Elischer 			pcb->tx_cred = 0;
2944f2bb1caeSJulian Elischer 		}
2945f2bb1caeSJulian Elischer 	} else {
2946f2bb1caeSJulian Elischer 		if (flow_control == 0xe0) {
2947f2bb1caeSJulian Elischer 			pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_CFC;
2948f2bb1caeSJulian Elischer 			pcb->tx_cred = credits;
2949f2bb1caeSJulian Elischer 		} else {
2950f2bb1caeSJulian Elischer 			pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_CFC;
2951f2bb1caeSJulian Elischer 			pcb->tx_cred = 0;
2952f2bb1caeSJulian Elischer 		}
2953f2bb1caeSJulian Elischer 	}
2954f2bb1caeSJulian Elischer 
2955f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
2956f2bb1caeSJulian Elischer "%s: cr=%d, dlci=%d, state=%d, flags=%#x, mtu=%d, rx_cred=%d, tx_cred=%d\n",
2957f2bb1caeSJulian Elischer 		__func__, cr, pcb->dlci, pcb->state, pcb->flags, pcb->mtu,
2958f2bb1caeSJulian Elischer 		pcb->rx_cred, pcb->tx_cred);
2959f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_set_pn */
2960f2bb1caeSJulian Elischer 
2961f2bb1caeSJulian Elischer /*
2962f2bb1caeSJulian Elischer  * Send RFCOMM SABM/DISC/UA/DM frames. Caller must hold s->session_mtx
2963f2bb1caeSJulian Elischer  */
2964f2bb1caeSJulian Elischer 
2965f2bb1caeSJulian Elischer static int
2966f2bb1caeSJulian Elischer ng_btsocket_rfcomm_send_command(ng_btsocket_rfcomm_session_p s,
2967f2bb1caeSJulian Elischer 		u_int8_t type, u_int8_t dlci)
2968f2bb1caeSJulian Elischer {
2969f2bb1caeSJulian Elischer 	struct rfcomm_cmd_hdr	*hdr = NULL;
2970f2bb1caeSJulian Elischer 	struct mbuf		*m = NULL;
2971f2bb1caeSJulian Elischer 	int			 cr;
2972f2bb1caeSJulian Elischer 
2973f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
2974f2bb1caeSJulian Elischer 
2975f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
2976f2bb1caeSJulian Elischer "%s: Sending command type %#x, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
2977f2bb1caeSJulian Elischer 		__func__, type, s->state, s->flags, s->mtu, dlci);
2978f2bb1caeSJulian Elischer 
2979f2bb1caeSJulian Elischer 	switch (type) {
2980f2bb1caeSJulian Elischer 	case RFCOMM_FRAME_SABM:
2981f2bb1caeSJulian Elischer 	case RFCOMM_FRAME_DISC:
2982f2bb1caeSJulian Elischer 		cr = INITIATOR(s);
2983f2bb1caeSJulian Elischer 		break;
2984f2bb1caeSJulian Elischer 
2985f2bb1caeSJulian Elischer 	case RFCOMM_FRAME_UA:
2986f2bb1caeSJulian Elischer 	case RFCOMM_FRAME_DM:
2987f2bb1caeSJulian Elischer 		cr = !INITIATOR(s);
2988f2bb1caeSJulian Elischer 		break;
2989f2bb1caeSJulian Elischer 
2990f2bb1caeSJulian Elischer 	default:
2991f2bb1caeSJulian Elischer 		panic("%s: Invalid frame type=%#x\n", __func__, type);
2992f2bb1caeSJulian Elischer 		return (EINVAL);
2993f2bb1caeSJulian Elischer 		/* NOT REACHED */
2994f2bb1caeSJulian Elischer 	}
2995f2bb1caeSJulian Elischer 
2996eb1b1807SGleb Smirnoff 	MGETHDR(m, M_NOWAIT, MT_DATA);
2997f2bb1caeSJulian Elischer 	if (m == NULL)
2998f2bb1caeSJulian Elischer 		return (ENOBUFS);
2999f2bb1caeSJulian Elischer 
3000f2bb1caeSJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*hdr);
3001f2bb1caeSJulian Elischer 
3002f2bb1caeSJulian Elischer 	hdr = mtod(m, struct rfcomm_cmd_hdr *);
3003f2bb1caeSJulian Elischer 	hdr->address = RFCOMM_MKADDRESS(cr, dlci);
3004f2bb1caeSJulian Elischer 	hdr->control = RFCOMM_MKCONTROL(type, 1);
3005f2bb1caeSJulian Elischer 	hdr->length = RFCOMM_MKLEN8(0);
3006f2bb1caeSJulian Elischer 	hdr->fcs = ng_btsocket_rfcomm_fcs3((u_int8_t *) hdr);
3007f2bb1caeSJulian Elischer 
3008f2bb1caeSJulian Elischer 	NG_BT_MBUFQ_ENQUEUE(&s->outq, m);
3009f2bb1caeSJulian Elischer 
3010f2bb1caeSJulian Elischer 	return (0);
3011f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_send_command */
3012f2bb1caeSJulian Elischer 
3013f2bb1caeSJulian Elischer /*
3014f2bb1caeSJulian Elischer  * Send RFCOMM UIH frame. Caller must hold s->session_mtx
3015f2bb1caeSJulian Elischer  */
3016f2bb1caeSJulian Elischer 
3017f2bb1caeSJulian Elischer static int
3018f2bb1caeSJulian Elischer ng_btsocket_rfcomm_send_uih(ng_btsocket_rfcomm_session_p s, u_int8_t address,
3019f2bb1caeSJulian Elischer 		u_int8_t pf, u_int8_t credits, struct mbuf *data)
3020f2bb1caeSJulian Elischer {
3021f2bb1caeSJulian Elischer 	struct rfcomm_frame_hdr	*hdr = NULL;
3022f2bb1caeSJulian Elischer 	struct mbuf		*m = NULL, *mcrc = NULL;
3023f2bb1caeSJulian Elischer 	u_int16_t		 length;
3024f2bb1caeSJulian Elischer 
3025f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
3026f2bb1caeSJulian Elischer 
3027eb1b1807SGleb Smirnoff 	MGETHDR(m, M_NOWAIT, MT_DATA);
3028f2bb1caeSJulian Elischer 	if (m == NULL) {
3029f2bb1caeSJulian Elischer 		NG_FREE_M(data);
3030f2bb1caeSJulian Elischer 		return (ENOBUFS);
3031f2bb1caeSJulian Elischer 	}
3032f2bb1caeSJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*hdr);
3033f2bb1caeSJulian Elischer 
3034eb1b1807SGleb Smirnoff 	MGET(mcrc, M_NOWAIT, MT_DATA);
3035f2bb1caeSJulian Elischer 	if (mcrc == NULL) {
3036f2bb1caeSJulian Elischer 		NG_FREE_M(data);
3037f2bb1caeSJulian Elischer 		return (ENOBUFS);
3038f2bb1caeSJulian Elischer 	}
3039f2bb1caeSJulian Elischer 	mcrc->m_len = 1;
3040f2bb1caeSJulian Elischer 
3041f2bb1caeSJulian Elischer 	/* Fill UIH frame header */
3042f2bb1caeSJulian Elischer 	hdr = mtod(m, struct rfcomm_frame_hdr *);
3043f2bb1caeSJulian Elischer 	hdr->address = address;
3044f2bb1caeSJulian Elischer 	hdr->control = RFCOMM_MKCONTROL(RFCOMM_FRAME_UIH, pf);
3045f2bb1caeSJulian Elischer 
3046f2bb1caeSJulian Elischer 	/* Calculate FCS */
3047f2bb1caeSJulian Elischer 	mcrc->m_data[0] = ng_btsocket_rfcomm_fcs2((u_int8_t *) hdr);
3048f2bb1caeSJulian Elischer 
3049f2bb1caeSJulian Elischer 	/* Put length back */
3050f2bb1caeSJulian Elischer 	length = (data != NULL)? data->m_pkthdr.len : 0;
3051f2bb1caeSJulian Elischer 	if (length > 127) {
3052f2bb1caeSJulian Elischer 		u_int16_t	l = htole16(RFCOMM_MKLEN16(length));
3053f2bb1caeSJulian Elischer 
3054f2bb1caeSJulian Elischer 		bcopy(&l, &hdr->length, sizeof(l));
3055f2bb1caeSJulian Elischer 		m->m_pkthdr.len ++;
3056f2bb1caeSJulian Elischer 		m->m_len ++;
3057f2bb1caeSJulian Elischer 	} else
3058f2bb1caeSJulian Elischer 		hdr->length = RFCOMM_MKLEN8(length);
3059f2bb1caeSJulian Elischer 
3060f2bb1caeSJulian Elischer 	if (pf) {
3061f2bb1caeSJulian Elischer 		m->m_data[m->m_len] = credits;
3062f2bb1caeSJulian Elischer 		m->m_pkthdr.len ++;
3063f2bb1caeSJulian Elischer 		m->m_len ++;
3064f2bb1caeSJulian Elischer 	}
3065f2bb1caeSJulian Elischer 
3066f2bb1caeSJulian Elischer 	/* Add payload */
3067f2bb1caeSJulian Elischer 	if (data != NULL) {
3068f2bb1caeSJulian Elischer 		m_cat(m, data);
3069f2bb1caeSJulian Elischer 		m->m_pkthdr.len += length;
3070f2bb1caeSJulian Elischer 	}
3071f2bb1caeSJulian Elischer 
3072f2bb1caeSJulian Elischer 	/* Put FCS back */
3073f2bb1caeSJulian Elischer 	m_cat(m, mcrc);
3074f2bb1caeSJulian Elischer 	m->m_pkthdr.len ++;
3075f2bb1caeSJulian Elischer 
3076f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
3077f2bb1caeSJulian Elischer "%s: Sending UIH state=%d, flags=%#x, address=%d, length=%d, pf=%d, " \
3078f2bb1caeSJulian Elischer "credits=%d, len=%d\n",
3079f2bb1caeSJulian Elischer 		__func__, s->state, s->flags, address, length, pf, credits,
3080f2bb1caeSJulian Elischer 		m->m_pkthdr.len);
3081f2bb1caeSJulian Elischer 
3082f2bb1caeSJulian Elischer 	NG_BT_MBUFQ_ENQUEUE(&s->outq, m);
3083f2bb1caeSJulian Elischer 
3084f2bb1caeSJulian Elischer 	return (0);
3085f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_send_uih */
3086f2bb1caeSJulian Elischer 
3087f2bb1caeSJulian Elischer /*
3088f2bb1caeSJulian Elischer  * Send MSC request. Caller must hold pcb->pcb_mtx and pcb->session->session_mtx
3089f2bb1caeSJulian Elischer  */
3090f2bb1caeSJulian Elischer 
3091f2bb1caeSJulian Elischer static int
3092f2bb1caeSJulian Elischer ng_btsocket_rfcomm_send_msc(ng_btsocket_rfcomm_pcb_p pcb)
3093f2bb1caeSJulian Elischer {
3094f2bb1caeSJulian Elischer 	struct mbuf		*m = NULL;
3095f2bb1caeSJulian Elischer 	struct rfcomm_mcc_hdr	*hdr = NULL;
3096f2bb1caeSJulian Elischer 	struct rfcomm_mcc_msc	*msc = NULL;
3097f2bb1caeSJulian Elischer 
3098f2bb1caeSJulian Elischer 	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3099f2bb1caeSJulian Elischer 	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3100f2bb1caeSJulian Elischer 
3101eb1b1807SGleb Smirnoff 	MGETHDR(m, M_NOWAIT, MT_DATA);
3102f2bb1caeSJulian Elischer 	if (m == NULL)
3103f2bb1caeSJulian Elischer 		return (ENOBUFS);
3104f2bb1caeSJulian Elischer 
3105f2bb1caeSJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*hdr) + sizeof(*msc);
3106f2bb1caeSJulian Elischer 
3107f2bb1caeSJulian Elischer 	hdr = mtod(m, struct rfcomm_mcc_hdr *);
3108f2bb1caeSJulian Elischer 	msc = (struct rfcomm_mcc_msc *)(hdr + 1);
3109f2bb1caeSJulian Elischer 
3110f2bb1caeSJulian Elischer 	hdr->type = RFCOMM_MKMCC_TYPE(1, RFCOMM_MCC_MSC);
3111f2bb1caeSJulian Elischer 	hdr->length = RFCOMM_MKLEN8(sizeof(*msc));
3112f2bb1caeSJulian Elischer 
3113f2bb1caeSJulian Elischer 	msc->address = RFCOMM_MKADDRESS(1, pcb->dlci);
3114f2bb1caeSJulian Elischer 	msc->modem = pcb->lmodem;
3115f2bb1caeSJulian Elischer 
3116f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
3117f2bb1caeSJulian Elischer "%s: Sending MSC dlci=%d, state=%d, flags=%#x, address=%d, modem=%#x\n",
3118f2bb1caeSJulian Elischer 		__func__, pcb->dlci, pcb->state, pcb->flags, msc->address,
3119f2bb1caeSJulian Elischer 		msc->modem);
3120f2bb1caeSJulian Elischer 
3121f2bb1caeSJulian Elischer 	return (ng_btsocket_rfcomm_send_uih(pcb->session,
3122f2bb1caeSJulian Elischer 			RFCOMM_MKADDRESS(INITIATOR(pcb->session), 0), 0, 0, m));
3123f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_send_msc */
3124f2bb1caeSJulian Elischer 
3125f2bb1caeSJulian Elischer /*
3126f2bb1caeSJulian Elischer  * Send PN request. Caller must hold pcb->pcb_mtx and pcb->session->session_mtx
3127f2bb1caeSJulian Elischer  */
3128f2bb1caeSJulian Elischer 
3129f2bb1caeSJulian Elischer static int
3130f2bb1caeSJulian Elischer ng_btsocket_rfcomm_send_pn(ng_btsocket_rfcomm_pcb_p pcb)
3131f2bb1caeSJulian Elischer {
3132f2bb1caeSJulian Elischer 	struct mbuf		*m = NULL;
3133f2bb1caeSJulian Elischer 	struct rfcomm_mcc_hdr	*hdr = NULL;
3134f2bb1caeSJulian Elischer 	struct rfcomm_mcc_pn	*pn = NULL;
3135f2bb1caeSJulian Elischer 
3136f2bb1caeSJulian Elischer 	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3137f2bb1caeSJulian Elischer 	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3138f2bb1caeSJulian Elischer 
3139eb1b1807SGleb Smirnoff 	MGETHDR(m, M_NOWAIT, MT_DATA);
3140f2bb1caeSJulian Elischer 	if (m == NULL)
3141f2bb1caeSJulian Elischer 		return (ENOBUFS);
3142f2bb1caeSJulian Elischer 
3143f2bb1caeSJulian Elischer 	m->m_pkthdr.len = m->m_len = sizeof(*hdr) + sizeof(*pn);
3144f2bb1caeSJulian Elischer 
3145f2bb1caeSJulian Elischer 	hdr = mtod(m, struct rfcomm_mcc_hdr *);
3146f2bb1caeSJulian Elischer 	pn = (struct rfcomm_mcc_pn *)(hdr + 1);
3147f2bb1caeSJulian Elischer 
3148f2bb1caeSJulian Elischer 	hdr->type = RFCOMM_MKMCC_TYPE(1, RFCOMM_MCC_PN);
3149f2bb1caeSJulian Elischer 	hdr->length = RFCOMM_MKLEN8(sizeof(*pn));
3150f2bb1caeSJulian Elischer 
3151f2bb1caeSJulian Elischer 	pn->dlci = pcb->dlci;
31520986ab12SMaksim Yevmenkin 
31530986ab12SMaksim Yevmenkin 	/*
31540986ab12SMaksim Yevmenkin 	 * Set default DLCI priority as described in GSM 07.10
31550986ab12SMaksim Yevmenkin 	 * (ETSI TS 101 369) clause 5.6 page 42
31560986ab12SMaksim Yevmenkin 	 */
31570986ab12SMaksim Yevmenkin 
31580986ab12SMaksim Yevmenkin 	pn->priority = (pcb->dlci < 56)? (((pcb->dlci >> 3) << 3) + 7) : 61;
3159f2bb1caeSJulian Elischer 	pn->ack_timer = 0;
3160f2bb1caeSJulian Elischer 	pn->mtu = htole16(pcb->mtu);
3161f2bb1caeSJulian Elischer 	pn->max_retrans = 0;
3162f2bb1caeSJulian Elischer 
3163f2bb1caeSJulian Elischer 	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) {
3164f2bb1caeSJulian Elischer 		pn->flow_control = 0xf0;
3165f2bb1caeSJulian Elischer 		pn->credits = pcb->rx_cred;
3166f2bb1caeSJulian Elischer 	} else {
3167f2bb1caeSJulian Elischer 		pn->flow_control = 0;
3168f2bb1caeSJulian Elischer 		pn->credits = 0;
3169f2bb1caeSJulian Elischer 	}
3170f2bb1caeSJulian Elischer 
3171f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
3172f2bb1caeSJulian Elischer "%s: Sending PN dlci=%d, state=%d, flags=%#x, mtu=%d, flow_control=%#x, " \
3173f2bb1caeSJulian Elischer "credits=%d\n",	__func__, pcb->dlci, pcb->state, pcb->flags, pcb->mtu,
3174f2bb1caeSJulian Elischer 		pn->flow_control, pn->credits);
3175f2bb1caeSJulian Elischer 
3176f2bb1caeSJulian Elischer 	return (ng_btsocket_rfcomm_send_uih(pcb->session,
3177f2bb1caeSJulian Elischer 			RFCOMM_MKADDRESS(INITIATOR(pcb->session), 0), 0, 0, m));
3178f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_send_pn */
3179f2bb1caeSJulian Elischer 
3180f2bb1caeSJulian Elischer /*
3181f2bb1caeSJulian Elischer  * Calculate and send credits based on available space in receive buffer
3182f2bb1caeSJulian Elischer  */
3183f2bb1caeSJulian Elischer 
3184f2bb1caeSJulian Elischer static int
3185f2bb1caeSJulian Elischer ng_btsocket_rfcomm_send_credits(ng_btsocket_rfcomm_pcb_p pcb)
3186f2bb1caeSJulian Elischer {
3187f2bb1caeSJulian Elischer 	int		error = 0;
3188f2bb1caeSJulian Elischer 	u_int8_t	credits;
3189f2bb1caeSJulian Elischer 
3190f2bb1caeSJulian Elischer 	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3191f2bb1caeSJulian Elischer 	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3192f2bb1caeSJulian Elischer 
3193f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
3194f2bb1caeSJulian Elischer "%s: Sending more credits, dlci=%d, state=%d, flags=%#x, mtu=%d, " \
3195f2bb1caeSJulian Elischer "space=%ld, tx_cred=%d, rx_cred=%d\n",
3196f2bb1caeSJulian Elischer 		__func__, pcb->dlci, pcb->state, pcb->flags, pcb->mtu,
3197f2bb1caeSJulian Elischer 		sbspace(&pcb->so->so_rcv), pcb->tx_cred, pcb->rx_cred);
3198f2bb1caeSJulian Elischer 
3199f2bb1caeSJulian Elischer 	credits = sbspace(&pcb->so->so_rcv) / pcb->mtu;
3200f2bb1caeSJulian Elischer 	if (credits > 0) {
3201f2bb1caeSJulian Elischer 		if (pcb->rx_cred + credits > RFCOMM_MAX_CREDITS)
3202f2bb1caeSJulian Elischer 			credits = RFCOMM_MAX_CREDITS - pcb->rx_cred;
3203f2bb1caeSJulian Elischer 
3204f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_uih(
3205f2bb1caeSJulian Elischer 				pcb->session,
3206f2bb1caeSJulian Elischer 				RFCOMM_MKADDRESS(INITIATOR(pcb->session),
3207f2bb1caeSJulian Elischer 					pcb->dlci), 1, credits, NULL);
3208f2bb1caeSJulian Elischer 		if (error == 0) {
3209f2bb1caeSJulian Elischer 			pcb->rx_cred += credits;
3210f2bb1caeSJulian Elischer 
3211f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_INFO(
3212f2bb1caeSJulian Elischer "%s: Gave remote side %d more credits, dlci=%d, state=%d, flags=%#x, " \
3213f2bb1caeSJulian Elischer "rx_cred=%d, tx_cred=%d\n",	__func__, credits, pcb->dlci, pcb->state,
3214f2bb1caeSJulian Elischer 				pcb->flags, pcb->rx_cred, pcb->tx_cred);
3215f2bb1caeSJulian Elischer 		} else
3216f2bb1caeSJulian Elischer 			NG_BTSOCKET_RFCOMM_ERR(
3217f2bb1caeSJulian Elischer "%s: Could not send credits, error=%d, dlci=%d, state=%d, flags=%#x, " \
3218f2bb1caeSJulian Elischer "mtu=%d, space=%ld, tx_cred=%d, rx_cred=%d\n",
3219f2bb1caeSJulian Elischer 				__func__, error, pcb->dlci, pcb->state,
3220f2bb1caeSJulian Elischer 				pcb->flags, pcb->mtu, sbspace(&pcb->so->so_rcv),
3221f2bb1caeSJulian Elischer 				pcb->tx_cred, pcb->rx_cred);
3222f2bb1caeSJulian Elischer 	}
3223f2bb1caeSJulian Elischer 
3224f2bb1caeSJulian Elischer 	return (error);
3225f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_send_credits */
3226f2bb1caeSJulian Elischer 
3227f2bb1caeSJulian Elischer /*****************************************************************************
3228f2bb1caeSJulian Elischer  *****************************************************************************
3229f2bb1caeSJulian Elischer  **                              RFCOMM DLCs
3230f2bb1caeSJulian Elischer  *****************************************************************************
3231f2bb1caeSJulian Elischer  *****************************************************************************/
3232f2bb1caeSJulian Elischer 
3233f2bb1caeSJulian Elischer /*
3234f2bb1caeSJulian Elischer  * Send data from socket send buffer
3235f2bb1caeSJulian Elischer  * Caller must hold pcb->pcb_mtx and pcb->session->session_mtx
3236f2bb1caeSJulian Elischer  */
3237f2bb1caeSJulian Elischer 
3238f2bb1caeSJulian Elischer static int
3239f2bb1caeSJulian Elischer ng_btsocket_rfcomm_pcb_send(ng_btsocket_rfcomm_pcb_p pcb, int limit)
3240f2bb1caeSJulian Elischer {
3241f2bb1caeSJulian Elischer 	struct mbuf	*m = NULL;
3242f2bb1caeSJulian Elischer 	int		 sent, length, error;
3243f2bb1caeSJulian Elischer 
3244f2bb1caeSJulian Elischer 	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3245f2bb1caeSJulian Elischer 	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3246f2bb1caeSJulian Elischer 
3247f2bb1caeSJulian Elischer 	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC)
3248f2bb1caeSJulian Elischer 		limit = min(limit, pcb->tx_cred);
3249f2bb1caeSJulian Elischer 	else if (!(pcb->rmodem & RFCOMM_MODEM_FC))
3250f2bb1caeSJulian Elischer 		limit = min(limit, RFCOMM_MAX_CREDITS); /* XXX ??? */
3251f2bb1caeSJulian Elischer 	else
3252f2bb1caeSJulian Elischer 		limit = 0;
3253f2bb1caeSJulian Elischer 
3254f2bb1caeSJulian Elischer 	if (limit == 0) {
3255f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_INFO(
3256f2bb1caeSJulian Elischer "%s: Could not send - remote flow control asserted, dlci=%d, flags=%#x, " \
3257f2bb1caeSJulian Elischer "rmodem=%#x, tx_cred=%d\n",
3258f2bb1caeSJulian Elischer 			__func__, pcb->dlci, pcb->flags, pcb->rmodem,
3259f2bb1caeSJulian Elischer 			pcb->tx_cred);
3260f2bb1caeSJulian Elischer 
3261f2bb1caeSJulian Elischer 		return (0);
3262f2bb1caeSJulian Elischer 	}
3263f2bb1caeSJulian Elischer 
3264f2bb1caeSJulian Elischer 	for (error = 0, sent = 0; sent < limit; sent ++) {
3265cfa6009eSGleb Smirnoff 		length = min(pcb->mtu, sbavail(&pcb->so->so_snd));
3266f2bb1caeSJulian Elischer 		if (length == 0)
3267f2bb1caeSJulian Elischer 			break;
3268f2bb1caeSJulian Elischer 
3269f2bb1caeSJulian Elischer 		/* Get the chunk from the socket's send buffer */
3270f2bb1caeSJulian Elischer 		m = ng_btsocket_rfcomm_prepare_packet(&pcb->so->so_snd, length);
3271f2bb1caeSJulian Elischer 		if (m == NULL) {
3272f2bb1caeSJulian Elischer 			error = ENOBUFS;
3273f2bb1caeSJulian Elischer 			break;
3274f2bb1caeSJulian Elischer 		}
3275f2bb1caeSJulian Elischer 
3276f2bb1caeSJulian Elischer 		sbdrop(&pcb->so->so_snd, length);
3277f2bb1caeSJulian Elischer 
3278f2bb1caeSJulian Elischer 		error = ng_btsocket_rfcomm_send_uih(pcb->session,
3279f2bb1caeSJulian Elischer 				RFCOMM_MKADDRESS(INITIATOR(pcb->session),
3280f2bb1caeSJulian Elischer 					pcb->dlci), 0, 0, m);
3281f2bb1caeSJulian Elischer 		if (error != 0)
3282f2bb1caeSJulian Elischer 			break;
3283f2bb1caeSJulian Elischer 	}
3284f2bb1caeSJulian Elischer 
3285f2bb1caeSJulian Elischer 	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC)
3286f2bb1caeSJulian Elischer 		pcb->tx_cred -= sent;
3287f2bb1caeSJulian Elischer 
3288f2bb1caeSJulian Elischer 	if (error == 0 && sent > 0) {
3289f2bb1caeSJulian Elischer 		pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_SENDING;
3290f2bb1caeSJulian Elischer 		sowwakeup(pcb->so);
3291f2bb1caeSJulian Elischer 	}
3292f2bb1caeSJulian Elischer 
3293f2bb1caeSJulian Elischer 	return (error);
3294f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_pcb_send */
3295f2bb1caeSJulian Elischer 
3296f2bb1caeSJulian Elischer /*
3297f2bb1caeSJulian Elischer  * Unlink and disconnect DLC. If ng_btsocket_rfcomm_pcb_kill() returns
3298f2bb1caeSJulian Elischer  * non zero value than socket has no reference and has to be detached.
3299f2bb1caeSJulian Elischer  * Caller must hold pcb->pcb_mtx and pcb->session->session_mtx
3300f2bb1caeSJulian Elischer  */
3301f2bb1caeSJulian Elischer 
33027c380856SMaksim Yevmenkin static void
3303f2bb1caeSJulian Elischer ng_btsocket_rfcomm_pcb_kill(ng_btsocket_rfcomm_pcb_p pcb, int error)
3304f2bb1caeSJulian Elischer {
3305f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_session_p	s = pcb->session;
3306f2bb1caeSJulian Elischer 
3307f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
3308f2bb1caeSJulian Elischer "%s: Killing DLC, so=%p, dlci=%d, state=%d, flags=%#x, error=%d\n",
3309f2bb1caeSJulian Elischer 		__func__, pcb->so, pcb->dlci, pcb->state, pcb->flags, error);
3310f2bb1caeSJulian Elischer 
3311f2bb1caeSJulian Elischer 	if (pcb->session == NULL)
3312f2bb1caeSJulian Elischer 		panic("%s: DLC without session, pcb=%p, state=%d, flags=%#x\n",
3313f2bb1caeSJulian Elischer 			__func__, pcb, pcb->state, pcb->flags);
3314f2bb1caeSJulian Elischer 
331581c95c52SSam Leffler 	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
331681c95c52SSam Leffler 	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
331781c95c52SSam Leffler 
3318f2bb1caeSJulian Elischer 	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)
3319f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_untimeout(pcb);
3320f2bb1caeSJulian Elischer 
3321f2bb1caeSJulian Elischer 	/* Detach DLC from the session. Does not matter which state DLC in */
3322f2bb1caeSJulian Elischer 	LIST_REMOVE(pcb, session_next);
3323f2bb1caeSJulian Elischer 	pcb->session = NULL;
3324f2bb1caeSJulian Elischer 
3325f2bb1caeSJulian Elischer 	/* Change DLC state and wakeup all sleepers */
3326f2bb1caeSJulian Elischer 	pcb->state = NG_BTSOCKET_RFCOMM_DLC_CLOSED;
3327f2bb1caeSJulian Elischer 	pcb->so->so_error = error;
3328f2bb1caeSJulian Elischer 	soisdisconnected(pcb->so);
3329f2bb1caeSJulian Elischer 	wakeup(&pcb->state);
3330f2bb1caeSJulian Elischer 
3331f2bb1caeSJulian Elischer 	/* Check if we have any DLCs left on the session */
3332f2bb1caeSJulian Elischer 	if (LIST_EMPTY(&s->dlcs) && INITIATOR(s)) {
3333f2bb1caeSJulian Elischer 		NG_BTSOCKET_RFCOMM_INFO(
3334f2bb1caeSJulian Elischer "%s: Disconnecting session, state=%d, flags=%#x, mtu=%d\n",
3335f2bb1caeSJulian Elischer 			__func__, s->state, s->flags, s->mtu);
3336f2bb1caeSJulian Elischer 
3337f2bb1caeSJulian Elischer 		switch (s->state) {
3338f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_SESSION_CLOSED:
3339f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING:
3340f2bb1caeSJulian Elischer 			/*
3341f2bb1caeSJulian Elischer 			 * Do not have to do anything here. We can get here
3342f2bb1caeSJulian Elischer 			 * when L2CAP connection was terminated or we have
3343f2bb1caeSJulian Elischer 			 * received DISC on multiplexor channel
3344f2bb1caeSJulian Elischer 			 */
3345f2bb1caeSJulian Elischer 			break;
3346f2bb1caeSJulian Elischer 
3347f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_SESSION_OPEN:
3348f2bb1caeSJulian Elischer 			/* Send DISC on multiplexor channel */
3349f2bb1caeSJulian Elischer 			error = ng_btsocket_rfcomm_send_command(s,
3350f2bb1caeSJulian Elischer 					RFCOMM_FRAME_DISC, 0);
3351f2bb1caeSJulian Elischer 			if (error == 0) {
3352f2bb1caeSJulian Elischer 				s->state = NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING;
3353f2bb1caeSJulian Elischer 				break;
3354f2bb1caeSJulian Elischer 			}
3355f2bb1caeSJulian Elischer 			/* FALL THROUGH */
3356f2bb1caeSJulian Elischer 
3357f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_SESSION_CONNECTING:
3358f2bb1caeSJulian Elischer 		case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
3359f2bb1caeSJulian Elischer 			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
3360f2bb1caeSJulian Elischer 			break;
3361f2bb1caeSJulian Elischer 
3362f2bb1caeSJulian Elischer /*		case NG_BTSOCKET_RFCOMM_SESSION_LISTENING: */
3363f2bb1caeSJulian Elischer 		default:
3364f2bb1caeSJulian Elischer 			panic("%s: Invalid session state=%d, flags=%#x\n",
3365f2bb1caeSJulian Elischer 				__func__, s->state, s->flags);
3366f2bb1caeSJulian Elischer 			break;
3367f2bb1caeSJulian Elischer 		}
3368f2bb1caeSJulian Elischer 
3369f2bb1caeSJulian Elischer 		ng_btsocket_rfcomm_task_wakeup();
3370f2bb1caeSJulian Elischer 	}
3371f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_pcb_kill */
3372f2bb1caeSJulian Elischer 
3373f2bb1caeSJulian Elischer /*
3374f2bb1caeSJulian Elischer  * Look for given dlci for given RFCOMM session. Caller must hold s->session_mtx
3375f2bb1caeSJulian Elischer  */
3376f2bb1caeSJulian Elischer 
3377f2bb1caeSJulian Elischer static ng_btsocket_rfcomm_pcb_p
3378f2bb1caeSJulian Elischer ng_btsocket_rfcomm_pcb_by_dlci(ng_btsocket_rfcomm_session_p s, int dlci)
3379f2bb1caeSJulian Elischer {
3380f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
3381f2bb1caeSJulian Elischer 
3382f2bb1caeSJulian Elischer 	mtx_assert(&s->session_mtx, MA_OWNED);
3383f2bb1caeSJulian Elischer 
3384f2bb1caeSJulian Elischer 	LIST_FOREACH(pcb, &s->dlcs, session_next)
3385f2bb1caeSJulian Elischer 		if (pcb->dlci == dlci)
3386f2bb1caeSJulian Elischer 			break;
3387f2bb1caeSJulian Elischer 
3388f2bb1caeSJulian Elischer 	return (pcb);
3389f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_pcb_by_dlci */
3390f2bb1caeSJulian Elischer 
3391f2bb1caeSJulian Elischer /*
3392f2bb1caeSJulian Elischer  * Look for socket that listens on given src address and given channel
3393f2bb1caeSJulian Elischer  */
3394f2bb1caeSJulian Elischer 
3395f2bb1caeSJulian Elischer static ng_btsocket_rfcomm_pcb_p
3396f2bb1caeSJulian Elischer ng_btsocket_rfcomm_pcb_listener(bdaddr_p src, int channel)
3397f2bb1caeSJulian Elischer {
3398f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = NULL, pcb1 = NULL;
3399f2bb1caeSJulian Elischer 
3400f2bb1caeSJulian Elischer 	mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
3401f2bb1caeSJulian Elischer 
3402f2bb1caeSJulian Elischer 	LIST_FOREACH(pcb, &ng_btsocket_rfcomm_sockets, next) {
3403f2bb1caeSJulian Elischer 		if (pcb->channel != channel ||
3404f2bb1caeSJulian Elischer 		    !(pcb->so->so_options & SO_ACCEPTCONN))
3405f2bb1caeSJulian Elischer 			continue;
3406f2bb1caeSJulian Elischer 
3407f2bb1caeSJulian Elischer 		if (bcmp(&pcb->src, src, sizeof(*src)) == 0)
3408f2bb1caeSJulian Elischer 			break;
3409f2bb1caeSJulian Elischer 
3410f2bb1caeSJulian Elischer 		if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
3411f2bb1caeSJulian Elischer 			pcb1 = pcb;
3412f2bb1caeSJulian Elischer 	}
3413f2bb1caeSJulian Elischer 
3414f2bb1caeSJulian Elischer 	mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
3415f2bb1caeSJulian Elischer 
3416f2bb1caeSJulian Elischer 	return ((pcb != NULL)? pcb : pcb1);
3417f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_pcb_listener */
3418f2bb1caeSJulian Elischer 
3419f2bb1caeSJulian Elischer /*****************************************************************************
3420f2bb1caeSJulian Elischer  *****************************************************************************
3421f2bb1caeSJulian Elischer  **                              Misc. functions
3422f2bb1caeSJulian Elischer  *****************************************************************************
3423f2bb1caeSJulian Elischer  *****************************************************************************/
3424f2bb1caeSJulian Elischer 
3425f2bb1caeSJulian Elischer /*
3426f2bb1caeSJulian Elischer  *  Set timeout. Caller MUST hold pcb_mtx
3427f2bb1caeSJulian Elischer  */
3428f2bb1caeSJulian Elischer 
3429f2bb1caeSJulian Elischer static void
3430f2bb1caeSJulian Elischer ng_btsocket_rfcomm_timeout(ng_btsocket_rfcomm_pcb_p pcb)
3431f2bb1caeSJulian Elischer {
3432f2bb1caeSJulian Elischer 	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3433f2bb1caeSJulian Elischer 
3434f2bb1caeSJulian Elischer 	if (!(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)) {
3435f2bb1caeSJulian Elischer 		pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_TIMO;
3436f2bb1caeSJulian Elischer 		pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT;
34373d1592efSJohn Baldwin 		callout_reset(&pcb->timo, ng_btsocket_rfcomm_timo * hz,
34383d1592efSJohn Baldwin 		    ng_btsocket_rfcomm_process_timeout, pcb);
3439f2bb1caeSJulian Elischer 	} else
3440f2bb1caeSJulian Elischer 		panic("%s: Duplicated socket timeout?!\n", __func__);
3441f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_timeout */
3442f2bb1caeSJulian Elischer 
3443f2bb1caeSJulian Elischer /*
3444f2bb1caeSJulian Elischer  *  Unset pcb timeout. Caller MUST hold pcb_mtx
3445f2bb1caeSJulian Elischer  */
3446f2bb1caeSJulian Elischer 
3447f2bb1caeSJulian Elischer static void
3448f2bb1caeSJulian Elischer ng_btsocket_rfcomm_untimeout(ng_btsocket_rfcomm_pcb_p pcb)
3449f2bb1caeSJulian Elischer {
3450f2bb1caeSJulian Elischer 	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3451f2bb1caeSJulian Elischer 
3452f2bb1caeSJulian Elischer 	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO) {
34533d1592efSJohn Baldwin 		callout_stop(&pcb->timo);
3454f2bb1caeSJulian Elischer 		pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMO;
3455f2bb1caeSJulian Elischer 		pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT;
3456f2bb1caeSJulian Elischer 	} else
3457f2bb1caeSJulian Elischer 		panic("%s: No socket timeout?!\n", __func__);
3458f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_timeout */
3459f2bb1caeSJulian Elischer 
3460f2bb1caeSJulian Elischer /*
3461f2bb1caeSJulian Elischer  * Process pcb timeout
3462f2bb1caeSJulian Elischer  */
3463f2bb1caeSJulian Elischer 
3464f2bb1caeSJulian Elischer static void
3465f2bb1caeSJulian Elischer ng_btsocket_rfcomm_process_timeout(void *xpcb)
3466f2bb1caeSJulian Elischer {
3467f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_pcb_p	pcb = (ng_btsocket_rfcomm_pcb_p) xpcb;
3468f2bb1caeSJulian Elischer 
34693d1592efSJohn Baldwin 	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3470f2bb1caeSJulian Elischer 
3471f2bb1caeSJulian Elischer 	NG_BTSOCKET_RFCOMM_INFO(
3472f2bb1caeSJulian Elischer "%s: Timeout, so=%p, dlci=%d, state=%d, flags=%#x\n",
3473f2bb1caeSJulian Elischer 		__func__, pcb->so, pcb->dlci, pcb->state, pcb->flags);
3474f2bb1caeSJulian Elischer 
3475f2bb1caeSJulian Elischer 	pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMO;
3476f2bb1caeSJulian Elischer 	pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT;
3477f2bb1caeSJulian Elischer 
3478f2bb1caeSJulian Elischer 	switch (pcb->state) {
3479f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING:
3480f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
3481f2bb1caeSJulian Elischer 		pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING;
3482f2bb1caeSJulian Elischer 		break;
3483f2bb1caeSJulian Elischer 
3484f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
3485f2bb1caeSJulian Elischer 	case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
3486f2bb1caeSJulian Elischer 		break;
3487f2bb1caeSJulian Elischer 
3488f2bb1caeSJulian Elischer 	default:
3489f2bb1caeSJulian Elischer 		panic(
3490f2bb1caeSJulian Elischer "%s: DLC timeout in invalid state, dlci=%d, state=%d, flags=%#x\n",
3491f2bb1caeSJulian Elischer 			__func__, pcb->dlci, pcb->state, pcb->flags);
3492f2bb1caeSJulian Elischer 		break;
3493f2bb1caeSJulian Elischer 	}
3494f2bb1caeSJulian Elischer 
3495f2bb1caeSJulian Elischer 	ng_btsocket_rfcomm_task_wakeup();
3496f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_process_timeout */
3497f2bb1caeSJulian Elischer 
3498f2bb1caeSJulian Elischer /*
3499f2bb1caeSJulian Elischer  * Get up to length bytes from the socket buffer
3500f2bb1caeSJulian Elischer  */
3501f2bb1caeSJulian Elischer 
3502f2bb1caeSJulian Elischer static struct mbuf *
3503f2bb1caeSJulian Elischer ng_btsocket_rfcomm_prepare_packet(struct sockbuf *sb, int length)
3504f2bb1caeSJulian Elischer {
3505f2bb1caeSJulian Elischer 	struct mbuf	*top = NULL, *m = NULL, *n = NULL, *nextpkt = NULL;
3506f2bb1caeSJulian Elischer 	int		 mlen, noff, len;
3507f2bb1caeSJulian Elischer 
3508eb1b1807SGleb Smirnoff 	MGETHDR(top, M_NOWAIT, MT_DATA);
3509f2bb1caeSJulian Elischer 	if (top == NULL)
3510f2bb1caeSJulian Elischer 		return (NULL);
3511f2bb1caeSJulian Elischer 
3512f2bb1caeSJulian Elischer 	top->m_pkthdr.len = length;
3513f2bb1caeSJulian Elischer 	top->m_len = 0;
3514f2bb1caeSJulian Elischer 	mlen = MHLEN;
3515f2bb1caeSJulian Elischer 
3516f2bb1caeSJulian Elischer 	m = top;
3517f2bb1caeSJulian Elischer 	n = sb->sb_mb;
3518f2bb1caeSJulian Elischer 	nextpkt = n->m_nextpkt;
3519f2bb1caeSJulian Elischer 	noff = 0;
3520f2bb1caeSJulian Elischer 
3521f2bb1caeSJulian Elischer 	while (length > 0 && n != NULL) {
3522f2bb1caeSJulian Elischer 		len = min(mlen - m->m_len, n->m_len - noff);
3523f2bb1caeSJulian Elischer 		if (len > length)
3524f2bb1caeSJulian Elischer 			len = length;
3525f2bb1caeSJulian Elischer 
3526f2bb1caeSJulian Elischer 		bcopy(mtod(n, caddr_t)+noff, mtod(m, caddr_t)+m->m_len, len);
3527f2bb1caeSJulian Elischer 		m->m_len += len;
3528f2bb1caeSJulian Elischer 		noff += len;
3529f2bb1caeSJulian Elischer 		length -= len;
3530f2bb1caeSJulian Elischer 
3531f2bb1caeSJulian Elischer 		if (length > 0 && m->m_len == mlen) {
3532eb1b1807SGleb Smirnoff 			MGET(m->m_next, M_NOWAIT, MT_DATA);
3533f2bb1caeSJulian Elischer 			if (m->m_next == NULL) {
3534f2bb1caeSJulian Elischer 				NG_FREE_M(top);
3535f2bb1caeSJulian Elischer 				return (NULL);
3536f2bb1caeSJulian Elischer 			}
3537f2bb1caeSJulian Elischer 
3538f2bb1caeSJulian Elischer 			m = m->m_next;
3539f2bb1caeSJulian Elischer 			m->m_len = 0;
3540f2bb1caeSJulian Elischer 			mlen = MLEN;
3541f2bb1caeSJulian Elischer 		}
3542f2bb1caeSJulian Elischer 
3543f2bb1caeSJulian Elischer 		if (noff == n->m_len) {
3544f2bb1caeSJulian Elischer 			noff = 0;
3545f2bb1caeSJulian Elischer 			n = n->m_next;
3546f2bb1caeSJulian Elischer 
3547f2bb1caeSJulian Elischer 			if (n == NULL)
3548f2bb1caeSJulian Elischer 				n = nextpkt;
3549f2bb1caeSJulian Elischer 
3550f2bb1caeSJulian Elischer 			nextpkt = (n != NULL)? n->m_nextpkt : NULL;
3551f2bb1caeSJulian Elischer 		}
3552f2bb1caeSJulian Elischer 	}
3553f2bb1caeSJulian Elischer 
3554f2bb1caeSJulian Elischer 	if (length < 0)
3555f2bb1caeSJulian Elischer 		panic("%s: length=%d\n", __func__, length);
3556f2bb1caeSJulian Elischer 	if (length > 0 && n == NULL)
3557f2bb1caeSJulian Elischer 		panic("%s: bogus length=%d, n=%p\n", __func__, length, n);
3558f2bb1caeSJulian Elischer 
3559f2bb1caeSJulian Elischer 	return (top);
3560f2bb1caeSJulian Elischer } /* ng_btsocket_rfcomm_prepare_packet */
3561f2bb1caeSJulian Elischer 
3562