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