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