1114878Sjulian/*
2114878Sjulian * ng_btsocket_rfcomm.c
3139823Simp */
4139823Simp
5139823Simp/*-
6114878Sjulian * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7114878Sjulian * All rights reserved.
8114878Sjulian *
9114878Sjulian * Redistribution and use in source and binary forms, with or without
10114878Sjulian * modification, are permitted provided that the following conditions
11114878Sjulian * are met:
12114878Sjulian * 1. Redistributions of source code must retain the above copyright
13114878Sjulian *    notice, this list of conditions and the following disclaimer.
14114878Sjulian * 2. Redistributions in binary form must reproduce the above copyright
15114878Sjulian *    notice, this list of conditions and the following disclaimer in the
16114878Sjulian *    documentation and/or other materials provided with the distribution.
17114878Sjulian *
18114878Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19114878Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20114878Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21114878Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22114878Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23114878Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24114878Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25114878Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26114878Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27114878Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28114878Sjulian * SUCH DAMAGE.
29114878Sjulian *
30121054Semax * $Id: ng_btsocket_rfcomm.c,v 1.28 2003/09/14 23:29:06 max Exp $
31114878Sjulian * $FreeBSD$
32114878Sjulian */
33114878Sjulian
34114878Sjulian#include <sys/param.h>
35114878Sjulian#include <sys/systm.h>
36121054Semax#include <sys/bitstring.h>
37114878Sjulian#include <sys/domain.h>
38114878Sjulian#include <sys/endian.h>
39114878Sjulian#include <sys/errno.h>
40114878Sjulian#include <sys/filedesc.h>
41114878Sjulian#include <sys/ioccom.h>
42114878Sjulian#include <sys/kernel.h>
43114878Sjulian#include <sys/lock.h>
44114878Sjulian#include <sys/malloc.h>
45114878Sjulian#include <sys/mbuf.h>
46114878Sjulian#include <sys/mutex.h>
47114878Sjulian#include <sys/proc.h>
48114878Sjulian#include <sys/protosw.h>
49114878Sjulian#include <sys/queue.h>
50114878Sjulian#include <sys/socket.h>
51114878Sjulian#include <sys/socketvar.h>
52114878Sjulian#include <sys/sysctl.h>
53114878Sjulian#include <sys/taskqueue.h>
54114878Sjulian#include <sys/uio.h>
55218757Sbz
56218757Sbz#include <net/vnet.h>
57218757Sbz
58114878Sjulian#include <netgraph/ng_message.h>
59114878Sjulian#include <netgraph/netgraph.h>
60128688Semax#include <netgraph/bluetooth/include/ng_bluetooth.h>
61128688Semax#include <netgraph/bluetooth/include/ng_hci.h>
62128688Semax#include <netgraph/bluetooth/include/ng_l2cap.h>
63128688Semax#include <netgraph/bluetooth/include/ng_btsocket.h>
64128688Semax#include <netgraph/bluetooth/include/ng_btsocket_l2cap.h>
65128688Semax#include <netgraph/bluetooth/include/ng_btsocket_rfcomm.h>
66114878Sjulian
67114878Sjulian/* MALLOC define */
68114878Sjulian#ifdef NG_SEPARATE_MALLOC
69249132Smavstatic MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_RFCOMM, "netgraph_btsocks_rfcomm",
70114878Sjulian		"Netgraph Bluetooth RFCOMM sockets");
71114878Sjulian#else
72114878Sjulian#define M_NETGRAPH_BTSOCKET_RFCOMM M_NETGRAPH
73114878Sjulian#endif /* NG_SEPARATE_MALLOC */
74114878Sjulian
75114878Sjulian/* Debug */
76114878Sjulian#define NG_BTSOCKET_RFCOMM_INFO \
77181093Semax	if (ng_btsocket_rfcomm_debug_level >= NG_BTSOCKET_INFO_LEVEL && \
78181093Semax	    ppsratecheck(&ng_btsocket_rfcomm_lasttime, &ng_btsocket_rfcomm_curpps, 1)) \
79114878Sjulian		printf
80114878Sjulian
81114878Sjulian#define NG_BTSOCKET_RFCOMM_WARN \
82181093Semax	if (ng_btsocket_rfcomm_debug_level >= NG_BTSOCKET_WARN_LEVEL && \
83181093Semax	    ppsratecheck(&ng_btsocket_rfcomm_lasttime, &ng_btsocket_rfcomm_curpps, 1)) \
84114878Sjulian		printf
85114878Sjulian
86114878Sjulian#define NG_BTSOCKET_RFCOMM_ERR \
87181093Semax	if (ng_btsocket_rfcomm_debug_level >= NG_BTSOCKET_ERR_LEVEL && \
88181093Semax	    ppsratecheck(&ng_btsocket_rfcomm_lasttime, &ng_btsocket_rfcomm_curpps, 1)) \
89114878Sjulian		printf
90114878Sjulian
91114878Sjulian#define NG_BTSOCKET_RFCOMM_ALERT \
92181093Semax	if (ng_btsocket_rfcomm_debug_level >= NG_BTSOCKET_ALERT_LEVEL && \
93181093Semax	    ppsratecheck(&ng_btsocket_rfcomm_lasttime, &ng_btsocket_rfcomm_curpps, 1)) \
94114878Sjulian		printf
95114878Sjulian
96114878Sjulian#define	ALOT	0x7fff
97114878Sjulian
98114878Sjulian/* Local prototypes */
99193272Sjhbstatic int ng_btsocket_rfcomm_upcall
100114878Sjulian	(struct socket *so, void *arg, int waitflag);
101114878Sjulianstatic void ng_btsocket_rfcomm_sessions_task
102114878Sjulian	(void *ctx, int pending);
103114878Sjulianstatic void ng_btsocket_rfcomm_session_task
104114878Sjulian	(ng_btsocket_rfcomm_session_p s);
105114878Sjulian#define ng_btsocket_rfcomm_task_wakeup() \
106114878Sjulian	taskqueue_enqueue(taskqueue_swi_giant, &ng_btsocket_rfcomm_task)
107114878Sjulian
108114878Sjulianstatic ng_btsocket_rfcomm_pcb_p ng_btsocket_rfcomm_connect_ind
109114878Sjulian	(ng_btsocket_rfcomm_session_p s, int channel);
110114878Sjulianstatic void ng_btsocket_rfcomm_connect_cfm
111114878Sjulian	(ng_btsocket_rfcomm_session_p s);
112114878Sjulian
113114878Sjulianstatic int ng_btsocket_rfcomm_session_create
114114878Sjulian	(ng_btsocket_rfcomm_session_p *sp, struct socket *l2so,
115114878Sjulian	 bdaddr_p src, bdaddr_p dst, struct thread *td);
116114878Sjulianstatic int ng_btsocket_rfcomm_session_accept
117114878Sjulian	(ng_btsocket_rfcomm_session_p s0);
118114878Sjulianstatic int ng_btsocket_rfcomm_session_connect
119114878Sjulian	(ng_btsocket_rfcomm_session_p s);
120114878Sjulianstatic int ng_btsocket_rfcomm_session_receive
121114878Sjulian	(ng_btsocket_rfcomm_session_p s);
122114878Sjulianstatic int ng_btsocket_rfcomm_session_send
123114878Sjulian	(ng_btsocket_rfcomm_session_p s);
124114878Sjulianstatic void ng_btsocket_rfcomm_session_clean
125114878Sjulian	(ng_btsocket_rfcomm_session_p s);
126114878Sjulianstatic void ng_btsocket_rfcomm_session_process_pcb
127114878Sjulian	(ng_btsocket_rfcomm_session_p s);
128114878Sjulianstatic ng_btsocket_rfcomm_session_p ng_btsocket_rfcomm_session_by_addr
129114878Sjulian	(bdaddr_p src, bdaddr_p dst);
130114878Sjulian
131114878Sjulianstatic int ng_btsocket_rfcomm_receive_frame
132114878Sjulian	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
133114878Sjulianstatic int ng_btsocket_rfcomm_receive_sabm
134114878Sjulian	(ng_btsocket_rfcomm_session_p s, int dlci);
135114878Sjulianstatic int ng_btsocket_rfcomm_receive_disc
136114878Sjulian	(ng_btsocket_rfcomm_session_p s, int dlci);
137114878Sjulianstatic int ng_btsocket_rfcomm_receive_ua
138114878Sjulian	(ng_btsocket_rfcomm_session_p s, int dlci);
139114878Sjulianstatic int ng_btsocket_rfcomm_receive_dm
140114878Sjulian	(ng_btsocket_rfcomm_session_p s, int dlci);
141114878Sjulianstatic int ng_btsocket_rfcomm_receive_uih
142114878Sjulian	(ng_btsocket_rfcomm_session_p s, int dlci, int pf, struct mbuf *m0);
143114878Sjulianstatic int ng_btsocket_rfcomm_receive_mcc
144114878Sjulian	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
145114878Sjulianstatic int ng_btsocket_rfcomm_receive_test
146114878Sjulian	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
147114878Sjulianstatic int ng_btsocket_rfcomm_receive_fc
148114878Sjulian	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
149114878Sjulianstatic int ng_btsocket_rfcomm_receive_msc
150114878Sjulian	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
151114878Sjulianstatic int ng_btsocket_rfcomm_receive_rpn
152114878Sjulian	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
153114878Sjulianstatic int ng_btsocket_rfcomm_receive_rls
154114878Sjulian	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
155114878Sjulianstatic int ng_btsocket_rfcomm_receive_pn
156114878Sjulian	(ng_btsocket_rfcomm_session_p s, struct mbuf *m0);
157114878Sjulianstatic void ng_btsocket_rfcomm_set_pn
158114878Sjulian	(ng_btsocket_rfcomm_pcb_p pcb, u_int8_t cr, u_int8_t flow_control,
159114878Sjulian	 u_int8_t credits, u_int16_t mtu);
160114878Sjulian
161114878Sjulianstatic int ng_btsocket_rfcomm_send_command
162114878Sjulian	(ng_btsocket_rfcomm_session_p s, u_int8_t type, u_int8_t dlci);
163114878Sjulianstatic int ng_btsocket_rfcomm_send_uih
164114878Sjulian	(ng_btsocket_rfcomm_session_p s, u_int8_t address, u_int8_t pf,
165114878Sjulian	 u_int8_t credits, struct mbuf *data);
166114878Sjulianstatic int ng_btsocket_rfcomm_send_msc
167114878Sjulian	(ng_btsocket_rfcomm_pcb_p pcb);
168114878Sjulianstatic int ng_btsocket_rfcomm_send_pn
169114878Sjulian	(ng_btsocket_rfcomm_pcb_p pcb);
170114878Sjulianstatic int ng_btsocket_rfcomm_send_credits
171114878Sjulian	(ng_btsocket_rfcomm_pcb_p pcb);
172114878Sjulian
173114878Sjulianstatic int ng_btsocket_rfcomm_pcb_send
174114878Sjulian	(ng_btsocket_rfcomm_pcb_p pcb, int limit);
175161623Semaxstatic void ng_btsocket_rfcomm_pcb_kill
176114878Sjulian	(ng_btsocket_rfcomm_pcb_p pcb, int error);
177114878Sjulianstatic ng_btsocket_rfcomm_pcb_p ng_btsocket_rfcomm_pcb_by_dlci
178114878Sjulian	(ng_btsocket_rfcomm_session_p s, int dlci);
179114878Sjulianstatic ng_btsocket_rfcomm_pcb_p ng_btsocket_rfcomm_pcb_listener
180114878Sjulian	(bdaddr_p src, int channel);
181114878Sjulian
182114878Sjulianstatic void ng_btsocket_rfcomm_timeout
183114878Sjulian	(ng_btsocket_rfcomm_pcb_p pcb);
184114878Sjulianstatic void ng_btsocket_rfcomm_untimeout
185114878Sjulian	(ng_btsocket_rfcomm_pcb_p pcb);
186114878Sjulianstatic void ng_btsocket_rfcomm_process_timeout
187114878Sjulian	(void *xpcb);
188114878Sjulian
189114878Sjulianstatic struct mbuf * ng_btsocket_rfcomm_prepare_packet
190114878Sjulian	(struct sockbuf *sb, int length);
191114878Sjulian
192114878Sjulian/* Globals */
193114878Sjulianextern int					ifqmaxlen;
194114878Sjulianstatic u_int32_t				ng_btsocket_rfcomm_debug_level;
195114878Sjulianstatic u_int32_t				ng_btsocket_rfcomm_timo;
196114878Sjulianstruct task					ng_btsocket_rfcomm_task;
197114878Sjulianstatic LIST_HEAD(, ng_btsocket_rfcomm_session)	ng_btsocket_rfcomm_sessions;
198114878Sjulianstatic struct mtx				ng_btsocket_rfcomm_sessions_mtx;
199114878Sjulianstatic LIST_HEAD(, ng_btsocket_rfcomm_pcb)	ng_btsocket_rfcomm_sockets;
200114878Sjulianstatic struct mtx				ng_btsocket_rfcomm_sockets_mtx;
201181093Semaxstatic struct timeval				ng_btsocket_rfcomm_lasttime;
202181093Semaxstatic int					ng_btsocket_rfcomm_curpps;
203114878Sjulian
204114878Sjulian/* Sysctl tree */
205114878SjulianSYSCTL_DECL(_net_bluetooth_rfcomm_sockets);
206248085Smariusstatic SYSCTL_NODE(_net_bluetooth_rfcomm_sockets, OID_AUTO, stream, CTLFLAG_RW,
207114878Sjulian	0, "Bluetooth STREAM RFCOMM sockets family");
208217320SmdfSYSCTL_UINT(_net_bluetooth_rfcomm_sockets_stream, OID_AUTO, debug_level,
209114878Sjulian	CTLFLAG_RW,
210114878Sjulian	&ng_btsocket_rfcomm_debug_level, NG_BTSOCKET_INFO_LEVEL,
211114878Sjulian	"Bluetooth STREAM RFCOMM sockets debug level");
212217320SmdfSYSCTL_UINT(_net_bluetooth_rfcomm_sockets_stream, OID_AUTO, timeout,
213114878Sjulian	CTLFLAG_RW,
214114878Sjulian	&ng_btsocket_rfcomm_timo, 60,
215114878Sjulian	"Bluetooth STREAM RFCOMM sockets timeout");
216114878Sjulian
217114878Sjulian/*****************************************************************************
218114878Sjulian *****************************************************************************
219114878Sjulian **                              RFCOMM CRC
220114878Sjulian *****************************************************************************
221114878Sjulian *****************************************************************************/
222114878Sjulian
223114878Sjulianstatic u_int8_t	ng_btsocket_rfcomm_crc_table[256] = {
224114878Sjulian	0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
225114878Sjulian	0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
226114878Sjulian	0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
227114878Sjulian	0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
228114878Sjulian
229114878Sjulian	0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
230114878Sjulian	0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
231114878Sjulian	0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
232114878Sjulian	0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
233114878Sjulian
234114878Sjulian	0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
235114878Sjulian	0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
236114878Sjulian	0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
237114878Sjulian	0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
238114878Sjulian
239114878Sjulian	0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
240114878Sjulian	0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
241114878Sjulian	0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
242114878Sjulian	0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
243114878Sjulian
244114878Sjulian	0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
245114878Sjulian	0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
246114878Sjulian	0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
247114878Sjulian	0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
248114878Sjulian
249114878Sjulian	0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
250114878Sjulian	0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
251114878Sjulian	0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
252114878Sjulian	0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
253114878Sjulian
254114878Sjulian	0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
255114878Sjulian	0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
256114878Sjulian	0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
257114878Sjulian	0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
258114878Sjulian
259114878Sjulian	0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
260114878Sjulian	0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
261114878Sjulian	0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
262114878Sjulian	0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
263114878Sjulian};
264114878Sjulian
265114878Sjulian/* CRC */
266114878Sjulianstatic u_int8_t
267114878Sjulianng_btsocket_rfcomm_crc(u_int8_t *data, int length)
268114878Sjulian{
269114878Sjulian	u_int8_t	crc = 0xff;
270114878Sjulian
271114878Sjulian	while (length --)
272114878Sjulian		crc = ng_btsocket_rfcomm_crc_table[crc ^ *data++];
273114878Sjulian
274114878Sjulian	return (crc);
275114878Sjulian} /* ng_btsocket_rfcomm_crc */
276114878Sjulian
277114878Sjulian/* FCS on 2 bytes */
278114878Sjulianstatic u_int8_t
279114878Sjulianng_btsocket_rfcomm_fcs2(u_int8_t *data)
280114878Sjulian{
281114878Sjulian	return (0xff - ng_btsocket_rfcomm_crc(data, 2));
282114878Sjulian} /* ng_btsocket_rfcomm_fcs2 */
283114878Sjulian
284114878Sjulian/* FCS on 3 bytes */
285114878Sjulianstatic u_int8_t
286114878Sjulianng_btsocket_rfcomm_fcs3(u_int8_t *data)
287114878Sjulian{
288114878Sjulian	return (0xff - ng_btsocket_rfcomm_crc(data, 3));
289114878Sjulian} /* ng_btsocket_rfcomm_fcs3 */
290114878Sjulian
291114878Sjulian/*
292114878Sjulian * Check FCS
293114878Sjulian *
294114878Sjulian * From Bluetooth spec
295114878Sjulian *
296114878Sjulian * "... In 07.10, the frame check sequence (FCS) is calculated on different
297114878Sjulian * sets of fields for different frame types. These are the fields that the
298114878Sjulian * FCS are calculated on:
299114878Sjulian *
300114878Sjulian * For SABM, DISC, UA, DM frames: on Address, Control and length field.
301114878Sjulian * For UIH frames: on Address and Control field.
302114878Sjulian *
303114878Sjulian * (This is stated here for clarification, and to set the standard for RFCOMM;
304114878Sjulian * the fields included in FCS calculation have actually changed in version
305114878Sjulian * 7.0.0 of TS 07.10, but RFCOMM will not change the FCS calculation scheme
306114878Sjulian * from the one above.) ..."
307114878Sjulian */
308114878Sjulian
309114878Sjulianstatic int
310114878Sjulianng_btsocket_rfcomm_check_fcs(u_int8_t *data, int type, u_int8_t fcs)
311114878Sjulian{
312114878Sjulian	if (type != RFCOMM_FRAME_UIH)
313114878Sjulian		return (ng_btsocket_rfcomm_fcs3(data) != fcs);
314114878Sjulian
315114878Sjulian	return (ng_btsocket_rfcomm_fcs2(data) != fcs);
316114878Sjulian} /* ng_btsocket_rfcomm_check_fcs */
317114878Sjulian
318114878Sjulian/*****************************************************************************
319114878Sjulian *****************************************************************************
320114878Sjulian **                              Socket interface
321114878Sjulian *****************************************************************************
322114878Sjulian *****************************************************************************/
323114878Sjulian
324114878Sjulian/*
325114878Sjulian * Initialize everything
326114878Sjulian */
327114878Sjulian
328114878Sjulianvoid
329114878Sjulianng_btsocket_rfcomm_init(void)
330114878Sjulian{
331114878Sjulian	ng_btsocket_rfcomm_debug_level = NG_BTSOCKET_WARN_LEVEL;
332114878Sjulian	ng_btsocket_rfcomm_timo = 60;
333114878Sjulian
334114878Sjulian	/* RFCOMM task */
335114878Sjulian	TASK_INIT(&ng_btsocket_rfcomm_task, 0,
336114878Sjulian		ng_btsocket_rfcomm_sessions_task, NULL);
337114878Sjulian
338114878Sjulian	/* RFCOMM sessions list */
339114878Sjulian	LIST_INIT(&ng_btsocket_rfcomm_sessions);
340114878Sjulian	mtx_init(&ng_btsocket_rfcomm_sessions_mtx,
341114878Sjulian		"btsocks_rfcomm_sessions_mtx", NULL, MTX_DEF);
342114878Sjulian
343114878Sjulian	/* RFCOMM sockets list */
344114878Sjulian	LIST_INIT(&ng_btsocket_rfcomm_sockets);
345114878Sjulian	mtx_init(&ng_btsocket_rfcomm_sockets_mtx,
346114878Sjulian		"btsocks_rfcomm_sockets_mtx", NULL, MTX_DEF);
347114878Sjulian} /* ng_btsocket_rfcomm_init */
348114878Sjulian
349114878Sjulian/*
350114878Sjulian * Abort connection on socket
351114878Sjulian */
352114878Sjulian
353157366Srwatsonvoid
354114878Sjulianng_btsocket_rfcomm_abort(struct socket *so)
355114878Sjulian{
356160549Srwatson
357114878Sjulian	so->so_error = ECONNABORTED;
358160549Srwatson	(void)ng_btsocket_rfcomm_disconnect(so);
359114878Sjulian} /* ng_btsocket_rfcomm_abort */
360114878Sjulian
361160549Srwatsonvoid
362160549Srwatsonng_btsocket_rfcomm_close(struct socket *so)
363160549Srwatson{
364160549Srwatson
365160549Srwatson	(void)ng_btsocket_rfcomm_disconnect(so);
366160549Srwatson} /* ng_btsocket_rfcomm_close */
367160549Srwatson
368114878Sjulian/*
369114878Sjulian * Accept connection on socket. Nothing to do here, socket must be connected
370114878Sjulian * and ready, so just return peer address and be done with it.
371114878Sjulian */
372114878Sjulian
373114878Sjulianint
374114878Sjulianng_btsocket_rfcomm_accept(struct socket *so, struct sockaddr **nam)
375114878Sjulian{
376114878Sjulian	return (ng_btsocket_rfcomm_peeraddr(so, nam));
377114878Sjulian} /* ng_btsocket_rfcomm_accept */
378114878Sjulian
379114878Sjulian/*
380114878Sjulian * Create and attach new socket
381114878Sjulian */
382114878Sjulian
383114878Sjulianint
384114878Sjulianng_btsocket_rfcomm_attach(struct socket *so, int proto, struct thread *td)
385114878Sjulian{
386114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
387114878Sjulian	int				error;
388114878Sjulian
389114878Sjulian	/* Check socket and protocol */
390114878Sjulian	if (so->so_type != SOCK_STREAM)
391114878Sjulian		return (ESOCKTNOSUPPORT);
392114878Sjulian
393114878Sjulian#if 0 /* XXX sonewconn() calls "pru_attach" with proto == 0 */
394114878Sjulian	if (proto != 0)
395114878Sjulian		if (proto != BLUETOOTH_PROTO_RFCOMM)
396114878Sjulian			return (EPROTONOSUPPORT);
397114878Sjulian#endif /* XXX */
398114878Sjulian
399114878Sjulian	if (pcb != NULL)
400114878Sjulian		return (EISCONN);
401114878Sjulian
402114878Sjulian	/* Reserve send and receive space if it is not reserved yet */
403114878Sjulian	if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) {
404114878Sjulian		error = soreserve(so, NG_BTSOCKET_RFCOMM_SENDSPACE,
405114878Sjulian					NG_BTSOCKET_RFCOMM_RECVSPACE);
406114878Sjulian		if (error != 0)
407114878Sjulian			return (error);
408114878Sjulian	}
409114878Sjulian
410114878Sjulian	/* Allocate the PCB */
411184205Sdes        pcb = malloc(sizeof(*pcb),
412114878Sjulian		M_NETGRAPH_BTSOCKET_RFCOMM, M_NOWAIT | M_ZERO);
413114878Sjulian        if (pcb == NULL)
414114878Sjulian                return (ENOMEM);
415114878Sjulian
416114878Sjulian	/* Link the PCB and the socket */
417114878Sjulian	so->so_pcb = (caddr_t) pcb;
418114878Sjulian	pcb->so = so;
419114878Sjulian
420114878Sjulian	/* Initialize PCB */
421114878Sjulian	pcb->state = NG_BTSOCKET_RFCOMM_DLC_CLOSED;
422114878Sjulian	pcb->flags = NG_BTSOCKET_RFCOMM_DLC_CFC;
423114878Sjulian
424114878Sjulian	pcb->lmodem =
425114878Sjulian	pcb->rmodem = (RFCOMM_MODEM_RTC | RFCOMM_MODEM_RTR | RFCOMM_MODEM_DV);
426114878Sjulian
427114878Sjulian	pcb->mtu = RFCOMM_DEFAULT_MTU;
428114878Sjulian	pcb->tx_cred = 0;
429114878Sjulian	pcb->rx_cred = RFCOMM_DEFAULT_CREDITS;
430114878Sjulian
431114878Sjulian	mtx_init(&pcb->pcb_mtx, "btsocks_rfcomm_pcb_mtx", NULL, MTX_DEF);
432114878Sjulian	callout_handle_init(&pcb->timo);
433114878Sjulian
434114878Sjulian	/* Add the PCB to the list */
435114878Sjulian	mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
436114878Sjulian	LIST_INSERT_HEAD(&ng_btsocket_rfcomm_sockets, pcb, next);
437114878Sjulian	mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
438114878Sjulian
439114878Sjulian        return (0);
440114878Sjulian} /* ng_btsocket_rfcomm_attach */
441114878Sjulian
442114878Sjulian/*
443114878Sjulian * Bind socket
444114878Sjulian */
445114878Sjulian
446114878Sjulianint
447114878Sjulianng_btsocket_rfcomm_bind(struct socket *so, struct sockaddr *nam,
448114878Sjulian		struct thread *td)
449114878Sjulian{
450173151Semax	ng_btsocket_rfcomm_pcb_t	*pcb = so2rfcomm_pcb(so), *pcb1;
451114878Sjulian	struct sockaddr_rfcomm		*sa = (struct sockaddr_rfcomm *) nam;
452114878Sjulian
453114878Sjulian	if (pcb == NULL)
454114878Sjulian		return (EINVAL);
455114878Sjulian
456114878Sjulian	/* Verify address */
457114878Sjulian	if (sa == NULL)
458114878Sjulian		return (EINVAL);
459114878Sjulian	if (sa->rfcomm_family != AF_BLUETOOTH)
460114878Sjulian		return (EAFNOSUPPORT);
461114878Sjulian	if (sa->rfcomm_len != sizeof(*sa))
462114878Sjulian		return (EINVAL);
463114878Sjulian	if (sa->rfcomm_channel > 30)
464114878Sjulian		return (EINVAL);
465114878Sjulian
466173151Semax	mtx_lock(&pcb->pcb_mtx);
467173151Semax
468173151Semax	if (sa->rfcomm_channel != 0) {
469173151Semax		mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
470173151Semax
471173151Semax		LIST_FOREACH(pcb1, &ng_btsocket_rfcomm_sockets, next) {
472173151Semax			if (pcb1->channel == sa->rfcomm_channel &&
473173151Semax			    bcmp(&pcb1->src, &sa->rfcomm_bdaddr,
474173151Semax					sizeof(pcb1->src)) == 0) {
475173151Semax				mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
476173151Semax				mtx_unlock(&pcb->pcb_mtx);
477173151Semax
478173151Semax				return (EADDRINUSE);
479173151Semax			}
480173151Semax		}
481173151Semax
482173151Semax		mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
483173151Semax	}
484173151Semax
485114878Sjulian	bcopy(&sa->rfcomm_bdaddr, &pcb->src, sizeof(pcb->src));
486114878Sjulian	pcb->channel = sa->rfcomm_channel;
487114878Sjulian
488173151Semax	mtx_unlock(&pcb->pcb_mtx);
489173151Semax
490114878Sjulian	return (0);
491114878Sjulian} /* ng_btsocket_rfcomm_bind */
492114878Sjulian
493114878Sjulian/*
494114878Sjulian * Connect socket
495114878Sjulian */
496114878Sjulian
497114878Sjulianint
498114878Sjulianng_btsocket_rfcomm_connect(struct socket *so, struct sockaddr *nam,
499114878Sjulian		struct thread *td)
500114878Sjulian{
501114878Sjulian	ng_btsocket_rfcomm_pcb_t	*pcb = so2rfcomm_pcb(so);
502114878Sjulian	struct sockaddr_rfcomm		*sa = (struct sockaddr_rfcomm *) nam;
503114878Sjulian	ng_btsocket_rfcomm_session_t	*s = NULL;
504114878Sjulian	struct socket			*l2so = NULL;
505114878Sjulian	int				 dlci, error = 0;
506114878Sjulian
507114878Sjulian	if (pcb == NULL)
508114878Sjulian		return (EINVAL);
509114878Sjulian
510114878Sjulian	/* Verify address */
511114878Sjulian	if (sa == NULL)
512114878Sjulian		return (EINVAL);
513114878Sjulian	if (sa->rfcomm_family != AF_BLUETOOTH)
514114878Sjulian		return (EAFNOSUPPORT);
515114878Sjulian	if (sa->rfcomm_len != sizeof(*sa))
516114878Sjulian		return (EINVAL);
517114878Sjulian	if (sa->rfcomm_channel > 30)
518114878Sjulian		return (EINVAL);
519114878Sjulian	if (sa->rfcomm_channel == 0 ||
520114878Sjulian	    bcmp(&sa->rfcomm_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
521114878Sjulian		return (EDESTADDRREQ);
522114878Sjulian
523114878Sjulian	/*
524188452Semax	 * Note that we will not check for errors in socreate() because
525188452Semax	 * if we failed to create L2CAP socket at this point we still
526188452Semax	 * might have already open session.
527114878Sjulian	 */
528114878Sjulian
529114878Sjulian	error = socreate(PF_BLUETOOTH, &l2so, SOCK_SEQPACKET,
530114878Sjulian			BLUETOOTH_PROTO_L2CAP, td->td_ucred, td);
531114878Sjulian
532114878Sjulian	/*
533114878Sjulian	 * Look for session between "pcb->src" and "sa->rfcomm_bdaddr" (dst)
534114878Sjulian	 */
535114878Sjulian
536114878Sjulian	mtx_lock(&ng_btsocket_rfcomm_sessions_mtx);
537114878Sjulian
538114878Sjulian	s = ng_btsocket_rfcomm_session_by_addr(&pcb->src, &sa->rfcomm_bdaddr);
539114878Sjulian	if (s == NULL) {
540114878Sjulian		/*
541114878Sjulian		 * We need to create new RFCOMM session. Check if we have L2CAP
542114878Sjulian		 * socket. If l2so == NULL then error has the error code from
543114878Sjulian		 * socreate()
544114878Sjulian		 */
545114878Sjulian
546114878Sjulian		if (l2so == NULL) {
547114878Sjulian			mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
548114878Sjulian			return (error);
549114878Sjulian		}
550114878Sjulian
551114878Sjulian		error = ng_btsocket_rfcomm_session_create(&s, l2so,
552114878Sjulian				&pcb->src, &sa->rfcomm_bdaddr, td);
553114878Sjulian		if (error != 0) {
554114878Sjulian			mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
555114878Sjulian			soclose(l2so);
556114878Sjulian
557114878Sjulian			return (error);
558114878Sjulian		}
559114878Sjulian	} else if (l2so != NULL)
560114878Sjulian		soclose(l2so); /* we don't need new L2CAP socket */
561114878Sjulian
562114878Sjulian	/*
563218909Sbrucec	 * Check if we already have the same DLCI the same session
564114878Sjulian	 */
565114878Sjulian
566114878Sjulian	mtx_lock(&s->session_mtx);
567114878Sjulian	mtx_lock(&pcb->pcb_mtx);
568114878Sjulian
569114878Sjulian	dlci = RFCOMM_MKDLCI(!INITIATOR(s), sa->rfcomm_channel);
570114878Sjulian
571114878Sjulian	if (ng_btsocket_rfcomm_pcb_by_dlci(s, dlci) != NULL) {
572114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
573114878Sjulian		mtx_unlock(&s->session_mtx);
574114878Sjulian		mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
575114878Sjulian
576114878Sjulian		return (EBUSY);
577114878Sjulian	}
578114878Sjulian
579114878Sjulian	/*
580114878Sjulian	 * Check session state and if its not acceptable then refuse connection
581114878Sjulian	 */
582114878Sjulian
583114878Sjulian	switch (s->state) {
584114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_CONNECTING:
585114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
586114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_OPEN:
587114878Sjulian		/*
588114878Sjulian		 * Update destination address and channel and attach
589114878Sjulian		 * DLC to the session
590114878Sjulian		 */
591114878Sjulian
592114878Sjulian		bcopy(&sa->rfcomm_bdaddr, &pcb->dst, sizeof(pcb->dst));
593114878Sjulian		pcb->channel = sa->rfcomm_channel;
594114878Sjulian		pcb->dlci = dlci;
595114878Sjulian
596114878Sjulian		LIST_INSERT_HEAD(&s->dlcs, pcb, session_next);
597114878Sjulian		pcb->session = s;
598114878Sjulian
599114878Sjulian		ng_btsocket_rfcomm_timeout(pcb);
600114878Sjulian		soisconnecting(pcb->so);
601114878Sjulian
602114878Sjulian		if (s->state == NG_BTSOCKET_RFCOMM_SESSION_OPEN) {
603114878Sjulian			pcb->mtu = s->mtu;
604114878Sjulian			bcopy(&so2l2cap_pcb(s->l2so)->src, &pcb->src,
605114878Sjulian				sizeof(pcb->src));
606114878Sjulian
607114878Sjulian			pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONFIGURING;
608114878Sjulian
609114878Sjulian			error = ng_btsocket_rfcomm_send_pn(pcb);
610114878Sjulian			if (error == 0)
611114878Sjulian				error = ng_btsocket_rfcomm_task_wakeup();
612114878Sjulian		} else
613114878Sjulian			pcb->state = NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT;
614114878Sjulian		break;
615114878Sjulian
616114878Sjulian	default:
617114878Sjulian		error = ECONNRESET;
618114878Sjulian		break;
619114878Sjulian	}
620114878Sjulian
621114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
622114878Sjulian	mtx_unlock(&s->session_mtx);
623114878Sjulian	mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
624114878Sjulian
625114878Sjulian	return (error);
626114878Sjulian} /* ng_btsocket_rfcomm_connect */
627114878Sjulian
628114878Sjulian/*
629114878Sjulian * Process ioctl's calls on socket.
630114878Sjulian * XXX FIXME this should provide interface to the RFCOMM multiplexor channel
631114878Sjulian */
632114878Sjulian
633114878Sjulianint
634114878Sjulianng_btsocket_rfcomm_control(struct socket *so, u_long cmd, caddr_t data,
635114878Sjulian		struct ifnet *ifp, struct thread *td)
636114878Sjulian{
637114878Sjulian	return (EINVAL);
638114878Sjulian} /* ng_btsocket_rfcomm_control */
639114878Sjulian
640114878Sjulian/*
641114878Sjulian * Process getsockopt/setsockopt system calls
642114878Sjulian */
643114878Sjulian
644114878Sjulianint
645114878Sjulianng_btsocket_rfcomm_ctloutput(struct socket *so, struct sockopt *sopt)
646114878Sjulian{
647114878Sjulian	ng_btsocket_rfcomm_pcb_p		pcb = so2rfcomm_pcb(so);
648114878Sjulian	struct ng_btsocket_rfcomm_fc_info	fcinfo;
649114878Sjulian	int					error = 0;
650114878Sjulian
651114878Sjulian	if (pcb == NULL)
652114878Sjulian		return (EINVAL);
653114878Sjulian	if (sopt->sopt_level != SOL_RFCOMM)
654114878Sjulian		return (0);
655114878Sjulian
656114878Sjulian	mtx_lock(&pcb->pcb_mtx);
657114878Sjulian
658114878Sjulian	switch (sopt->sopt_dir) {
659114878Sjulian	case SOPT_GET:
660114878Sjulian		switch (sopt->sopt_name) {
661114878Sjulian		case SO_RFCOMM_MTU:
662114878Sjulian			error = sooptcopyout(sopt, &pcb->mtu, sizeof(pcb->mtu));
663114878Sjulian			break;
664114878Sjulian
665114878Sjulian		case SO_RFCOMM_FC_INFO:
666114878Sjulian			fcinfo.lmodem = pcb->lmodem;
667114878Sjulian			fcinfo.rmodem = pcb->rmodem;
668114878Sjulian			fcinfo.tx_cred = pcb->tx_cred;
669114878Sjulian			fcinfo.rx_cred = pcb->rx_cred;
670114878Sjulian			fcinfo.cfc = (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC)?
671114878Sjulian				1 : 0;
672114878Sjulian			fcinfo.reserved = 0;
673114878Sjulian
674114878Sjulian			error = sooptcopyout(sopt, &fcinfo, sizeof(fcinfo));
675114878Sjulian			break;
676114878Sjulian
677114878Sjulian		default:
678114878Sjulian			error = ENOPROTOOPT;
679114878Sjulian			break;
680114878Sjulian		}
681114878Sjulian		break;
682114878Sjulian
683114878Sjulian	case SOPT_SET:
684114878Sjulian		switch (sopt->sopt_name) {
685114878Sjulian		default:
686114878Sjulian			error = ENOPROTOOPT;
687114878Sjulian			break;
688114878Sjulian		}
689114878Sjulian		break;
690114878Sjulian
691114878Sjulian	default:
692114878Sjulian		error = EINVAL;
693114878Sjulian		break;
694114878Sjulian	}
695114878Sjulian
696114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
697114878Sjulian
698114878Sjulian	return (error);
699114878Sjulian} /* ng_btsocket_rfcomm_ctloutput */
700114878Sjulian
701114878Sjulian/*
702114878Sjulian * Detach and destroy socket
703114878Sjulian */
704114878Sjulian
705157370Srwatsonvoid
706114878Sjulianng_btsocket_rfcomm_detach(struct socket *so)
707114878Sjulian{
708114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
709114878Sjulian
710157370Srwatson	KASSERT(pcb != NULL, ("ng_btsocket_rfcomm_detach: pcb == NULL"));
711114878Sjulian
712114878Sjulian	mtx_lock(&pcb->pcb_mtx);
713114878Sjulian
714114878Sjulian	switch (pcb->state) {
715114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
716114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING:
717114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
718114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONNECTED:
719114878Sjulian		/* XXX What to do with pending request? */
720114878Sjulian		if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)
721114878Sjulian			ng_btsocket_rfcomm_untimeout(pcb);
722114878Sjulian
723114878Sjulian		if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT)
724114878Sjulian			pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_DETACHED;
725114878Sjulian		else
726114878Sjulian			pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING;
727114878Sjulian
728114878Sjulian		ng_btsocket_rfcomm_task_wakeup();
729114878Sjulian		break;
730114878Sjulian
731114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
732114878Sjulian		ng_btsocket_rfcomm_task_wakeup();
733114878Sjulian		break;
734114878Sjulian	}
735114878Sjulian
736114878Sjulian	while (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CLOSED)
737114878Sjulian		msleep(&pcb->state, &pcb->pcb_mtx, PZERO, "rf_det", 0);
738114878Sjulian
739114878Sjulian	if (pcb->session != NULL)
740114878Sjulian		panic("%s: pcb->session != NULL\n", __func__);
741114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)
742114878Sjulian		panic("%s: timeout on closed DLC, flags=%#x\n",
743114878Sjulian			__func__, pcb->flags);
744114878Sjulian
745114878Sjulian	mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
746114878Sjulian	LIST_REMOVE(pcb, next);
747114878Sjulian	mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
748114878Sjulian
749114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
750114878Sjulian
751114878Sjulian	mtx_destroy(&pcb->pcb_mtx);
752114878Sjulian	bzero(pcb, sizeof(*pcb));
753184205Sdes	free(pcb, M_NETGRAPH_BTSOCKET_RFCOMM);
754114878Sjulian
755114878Sjulian	soisdisconnected(so);
756114878Sjulian	so->so_pcb = NULL;
757114878Sjulian} /* ng_btsocket_rfcomm_detach */
758114878Sjulian
759114878Sjulian/*
760114878Sjulian * Disconnect socket
761114878Sjulian */
762114878Sjulian
763114878Sjulianint
764114878Sjulianng_btsocket_rfcomm_disconnect(struct socket *so)
765114878Sjulian{
766114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
767114878Sjulian
768114878Sjulian	if (pcb == NULL)
769114878Sjulian		return (EINVAL);
770114878Sjulian
771114878Sjulian	mtx_lock(&pcb->pcb_mtx);
772114878Sjulian
773114878Sjulian	if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING) {
774114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
775114878Sjulian		return (EINPROGRESS);
776114878Sjulian	}
777114878Sjulian
778114878Sjulian	/* XXX What to do with pending request? */
779114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)
780114878Sjulian		ng_btsocket_rfcomm_untimeout(pcb);
781114878Sjulian
782114878Sjulian	switch (pcb->state) {
783114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING: /* XXX can we get here? */
784114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONNECTING: /* XXX can we get here? */
785114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONNECTED:
786114878Sjulian
787114878Sjulian		/*
788114878Sjulian		 * Just change DLC state and enqueue RFCOMM task. It will
789114878Sjulian		 * queue and send DISC on the DLC.
790114878Sjulian		 */
791114878Sjulian
792114878Sjulian		pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING;
793114878Sjulian		soisdisconnecting(so);
794114878Sjulian
795114878Sjulian		ng_btsocket_rfcomm_task_wakeup();
796114878Sjulian		break;
797114878Sjulian
798161623Semax	case NG_BTSOCKET_RFCOMM_DLC_CLOSED:
799161623Semax	case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
800161623Semax		break;
801161623Semax
802114878Sjulian	default:
803114878Sjulian		panic("%s: Invalid DLC state=%d, flags=%#x\n",
804114878Sjulian			__func__, pcb->state, pcb->flags);
805114878Sjulian		break;
806114878Sjulian	}
807114878Sjulian
808114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
809114878Sjulian
810114878Sjulian	return (0);
811114878Sjulian} /* ng_btsocket_rfcomm_disconnect */
812114878Sjulian
813114878Sjulian/*
814114878Sjulian * Listen on socket. First call to listen() will create listening RFCOMM session
815114878Sjulian */
816114878Sjulian
817114878Sjulianint
818151888Srwatsonng_btsocket_rfcomm_listen(struct socket *so, int backlog, struct thread *td)
819114878Sjulian{
820173151Semax	ng_btsocket_rfcomm_pcb_p	 pcb = so2rfcomm_pcb(so), pcb1;
821114878Sjulian	ng_btsocket_rfcomm_session_p	 s = NULL;
822114878Sjulian	struct socket			*l2so = NULL;
823173151Semax	int				 error, socreate_error, usedchannels;
824114878Sjulian
825114878Sjulian	if (pcb == NULL)
826114878Sjulian		return (EINVAL);
827173151Semax	if (pcb->channel > 30)
828171937Semax		return (EADDRNOTAVAIL);
829114878Sjulian
830173151Semax	usedchannels = 0;
831173151Semax
832173151Semax	mtx_lock(&pcb->pcb_mtx);
833173151Semax
834173151Semax	if (pcb->channel == 0) {
835173151Semax		mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
836173151Semax
837173151Semax		LIST_FOREACH(pcb1, &ng_btsocket_rfcomm_sockets, next)
838173151Semax			if (pcb1->channel != 0 &&
839173151Semax			    bcmp(&pcb1->src, &pcb->src, sizeof(pcb->src)) == 0)
840173151Semax				usedchannels |= (1 << (pcb1->channel - 1));
841173151Semax
842173151Semax		for (pcb->channel = 30; pcb->channel > 0; pcb->channel --)
843173151Semax			if (!(usedchannels & (1 << (pcb->channel - 1))))
844173151Semax				break;
845173151Semax
846173151Semax		if (pcb->channel == 0) {
847173151Semax			mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
848173151Semax			mtx_unlock(&pcb->pcb_mtx);
849173151Semax
850173151Semax			return (EADDRNOTAVAIL);
851173151Semax		}
852173151Semax
853173151Semax		mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
854173151Semax	}
855173151Semax
856173151Semax	mtx_unlock(&pcb->pcb_mtx);
857173151Semax
858114878Sjulian	/*
859188452Semax	 * Note that we will not check for errors in socreate() because
860188452Semax	 * if we failed to create L2CAP socket at this point we still
861188452Semax	 * might have already open session.
862114878Sjulian	 */
863114878Sjulian
864142190Srwatson	socreate_error = socreate(PF_BLUETOOTH, &l2so, SOCK_SEQPACKET,
865114878Sjulian			BLUETOOTH_PROTO_L2CAP, td->td_ucred, td);
866114878Sjulian
867142190Srwatson	/*
868142190Srwatson	 * Transition the socket and session into the LISTENING state.  Check
869142190Srwatson	 * for collisions first, as there can only be one.
870114878Sjulian	 */
871114878Sjulian	mtx_lock(&ng_btsocket_rfcomm_sessions_mtx);
872142190Srwatson	SOCK_LOCK(so);
873142190Srwatson	error = solisten_proto_check(so);
874161623Semax	SOCK_UNLOCK(so);
875142190Srwatson	if (error != 0)
876142190Srwatson		goto out;
877114878Sjulian
878114878Sjulian	LIST_FOREACH(s, &ng_btsocket_rfcomm_sessions, next)
879114878Sjulian		if (s->state == NG_BTSOCKET_RFCOMM_SESSION_LISTENING)
880114878Sjulian			break;
881114878Sjulian
882114878Sjulian	if (s == NULL) {
883114878Sjulian		/*
884114878Sjulian		 * We need to create default RFCOMM session. Check if we have
885114878Sjulian		 * L2CAP socket. If l2so == NULL then error has the error code
886114878Sjulian		 * from socreate()
887114878Sjulian		 */
888114878Sjulian		if (l2so == NULL) {
889142190Srwatson			error = socreate_error;
890142190Srwatson			goto out;
891114878Sjulian		}
892114878Sjulian
893114878Sjulian		/*
894114878Sjulian		 * Create default listen RFCOMM session. The default RFCOMM
895114878Sjulian		 * session will listen on ANY address.
896114878Sjulian		 *
897114878Sjulian		 * XXX FIXME Note that currently there is no way to adjust MTU
898114878Sjulian		 * for the default session.
899114878Sjulian		 */
900114878Sjulian		error = ng_btsocket_rfcomm_session_create(&s, l2so,
901114878Sjulian					NG_HCI_BDADDR_ANY, NULL, td);
902142190Srwatson		if (error != 0)
903142190Srwatson			goto out;
904142190Srwatson		l2so = NULL;
905142190Srwatson	}
906161623Semax	SOCK_LOCK(so);
907151888Srwatson	solisten_proto(so, backlog);
908161623Semax	SOCK_UNLOCK(so);
909142190Srwatsonout:
910114878Sjulian	mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
911142190Srwatson	/*
912142190Srwatson	 * If we still have an l2so reference here, it's unneeded, so release
913142190Srwatson	 * it.
914142190Srwatson	 */
915142190Srwatson	if (l2so != NULL)
916142190Srwatson		soclose(l2so);
917142190Srwatson	return (error);
918114878Sjulian} /* ng_btsocket_listen */
919114878Sjulian
920114878Sjulian/*
921114878Sjulian * Get peer address
922114878Sjulian */
923114878Sjulian
924114878Sjulianint
925114878Sjulianng_btsocket_rfcomm_peeraddr(struct socket *so, struct sockaddr **nam)
926114878Sjulian{
927114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
928114878Sjulian	struct sockaddr_rfcomm		sa;
929114878Sjulian
930114878Sjulian	if (pcb == NULL)
931114878Sjulian		return (EINVAL);
932114878Sjulian
933114878Sjulian	bcopy(&pcb->dst, &sa.rfcomm_bdaddr, sizeof(sa.rfcomm_bdaddr));
934114878Sjulian	sa.rfcomm_channel = pcb->channel;
935114878Sjulian	sa.rfcomm_len = sizeof(sa);
936114878Sjulian	sa.rfcomm_family = AF_BLUETOOTH;
937114878Sjulian
938126425Srwatson	*nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT);
939114878Sjulian
940114878Sjulian	return ((*nam == NULL)? ENOMEM : 0);
941114878Sjulian} /* ng_btsocket_rfcomm_peeraddr */
942114878Sjulian
943114878Sjulian/*
944114878Sjulian * Send data to socket
945114878Sjulian */
946114878Sjulian
947114878Sjulianint
948114878Sjulianng_btsocket_rfcomm_send(struct socket *so, int flags, struct mbuf *m,
949114878Sjulian		struct sockaddr *nam, struct mbuf *control, struct thread *td)
950114878Sjulian{
951114878Sjulian	ng_btsocket_rfcomm_pcb_t	*pcb = so2rfcomm_pcb(so);
952114878Sjulian	int				 error = 0;
953114878Sjulian
954114878Sjulian	/* Check socket and input */
955114878Sjulian	if (pcb == NULL || m == NULL || control != NULL) {
956114878Sjulian		error = EINVAL;
957114878Sjulian		goto drop;
958114878Sjulian	}
959114878Sjulian
960114878Sjulian	mtx_lock(&pcb->pcb_mtx);
961114878Sjulian
962114878Sjulian	/* Make sure DLC is connected */
963114878Sjulian	if (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTED) {
964114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
965114878Sjulian		error = ENOTCONN;
966114878Sjulian		goto drop;
967114878Sjulian	}
968114878Sjulian
969114878Sjulian	/* Put the packet on the socket's send queue and wakeup RFCOMM task */
970114878Sjulian	sbappend(&pcb->so->so_snd, m);
971114878Sjulian	m = NULL;
972114878Sjulian
973114878Sjulian	if (!(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_SENDING)) {
974114878Sjulian		pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_SENDING;
975114878Sjulian		error = ng_btsocket_rfcomm_task_wakeup();
976114878Sjulian	}
977114878Sjulian
978114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
979114878Sjuliandrop:
980114878Sjulian	NG_FREE_M(m); /* checks for != NULL */
981114878Sjulian	NG_FREE_M(control);
982114878Sjulian
983114878Sjulian	return (error);
984114878Sjulian} /* ng_btsocket_rfcomm_send */
985114878Sjulian
986114878Sjulian/*
987114878Sjulian * Get socket address
988114878Sjulian */
989114878Sjulian
990114878Sjulianint
991114878Sjulianng_btsocket_rfcomm_sockaddr(struct socket *so, struct sockaddr **nam)
992114878Sjulian{
993114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
994114878Sjulian	struct sockaddr_rfcomm		sa;
995114878Sjulian
996114878Sjulian	if (pcb == NULL)
997114878Sjulian		return (EINVAL);
998114878Sjulian
999114878Sjulian	bcopy(&pcb->src, &sa.rfcomm_bdaddr, sizeof(sa.rfcomm_bdaddr));
1000114878Sjulian	sa.rfcomm_channel = pcb->channel;
1001114878Sjulian	sa.rfcomm_len = sizeof(sa);
1002114878Sjulian	sa.rfcomm_family = AF_BLUETOOTH;
1003114878Sjulian
1004126425Srwatson	*nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT);
1005114878Sjulian
1006114878Sjulian	return ((*nam == NULL)? ENOMEM : 0);
1007114878Sjulian} /* ng_btsocket_rfcomm_sockaddr */
1008114878Sjulian
1009114878Sjulian/*
1010114878Sjulian * Upcall function for L2CAP sockets. Enqueue RFCOMM task.
1011114878Sjulian */
1012114878Sjulian
1013193272Sjhbstatic int
1014114878Sjulianng_btsocket_rfcomm_upcall(struct socket *so, void *arg, int waitflag)
1015114878Sjulian{
1016114878Sjulian	int	error;
1017114878Sjulian
1018114878Sjulian	if (so == NULL)
1019114878Sjulian		panic("%s: so == NULL\n", __func__);
1020114878Sjulian
1021114878Sjulian	if ((error = ng_btsocket_rfcomm_task_wakeup()) != 0)
1022114878Sjulian		NG_BTSOCKET_RFCOMM_ALERT(
1023114878Sjulian"%s: Could not enqueue RFCOMM task, error=%d\n", __func__, error);
1024193272Sjhb	return (SU_OK);
1025114878Sjulian} /* ng_btsocket_rfcomm_upcall */
1026114878Sjulian
1027114878Sjulian/*
1028114878Sjulian * RFCOMM task. Will handle all RFCOMM sessions in one pass.
1029114878Sjulian * XXX FIXME does not scale very well
1030114878Sjulian */
1031114878Sjulian
1032114878Sjulianstatic void
1033114878Sjulianng_btsocket_rfcomm_sessions_task(void *ctx, int pending)
1034114878Sjulian{
1035114878Sjulian	ng_btsocket_rfcomm_session_p	s = NULL, s_next = NULL;
1036114878Sjulian
1037114878Sjulian	mtx_lock(&ng_btsocket_rfcomm_sessions_mtx);
1038114878Sjulian
1039114878Sjulian	for (s = LIST_FIRST(&ng_btsocket_rfcomm_sessions); s != NULL; ) {
1040114878Sjulian		mtx_lock(&s->session_mtx);
1041114878Sjulian		s_next = LIST_NEXT(s, next);
1042114878Sjulian
1043114878Sjulian		ng_btsocket_rfcomm_session_task(s);
1044114878Sjulian
1045114878Sjulian		if (s->state == NG_BTSOCKET_RFCOMM_SESSION_CLOSED) {
1046114878Sjulian			/* Unlink and clean the session */
1047114878Sjulian			LIST_REMOVE(s, next);
1048114878Sjulian
1049114878Sjulian			NG_BT_MBUFQ_DRAIN(&s->outq);
1050114878Sjulian			if (!LIST_EMPTY(&s->dlcs))
1051114878Sjulian				panic("%s: DLC list is not empty\n", __func__);
1052114878Sjulian
1053114878Sjulian			/* Close L2CAP socket */
1054130653Srwatson			SOCKBUF_LOCK(&s->l2so->so_rcv);
1055193272Sjhb			soupcall_clear(s->l2so, SO_RCV);
1056130653Srwatson			SOCKBUF_UNLOCK(&s->l2so->so_rcv);
1057130653Srwatson			SOCKBUF_LOCK(&s->l2so->so_snd);
1058193272Sjhb			soupcall_clear(s->l2so, SO_SND);
1059130653Srwatson			SOCKBUF_UNLOCK(&s->l2so->so_snd);
1060114878Sjulian			soclose(s->l2so);
1061114878Sjulian
1062114878Sjulian			mtx_unlock(&s->session_mtx);
1063114878Sjulian
1064114878Sjulian			mtx_destroy(&s->session_mtx);
1065114878Sjulian			bzero(s, sizeof(*s));
1066184205Sdes			free(s, M_NETGRAPH_BTSOCKET_RFCOMM);
1067114878Sjulian		} else
1068114878Sjulian			mtx_unlock(&s->session_mtx);
1069114878Sjulian
1070114878Sjulian		s = s_next;
1071114878Sjulian	}
1072114878Sjulian
1073114878Sjulian	mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
1074114878Sjulian} /* ng_btsocket_rfcomm_sessions_task */
1075114878Sjulian
1076114878Sjulian/*
1077114878Sjulian * Process RFCOMM session. Will handle all RFCOMM sockets in one pass.
1078114878Sjulian */
1079114878Sjulian
1080114878Sjulianstatic void
1081114878Sjulianng_btsocket_rfcomm_session_task(ng_btsocket_rfcomm_session_p s)
1082114878Sjulian{
1083114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1084114878Sjulian
1085130480Srwatson	if (s->l2so->so_rcv.sb_state & SBS_CANTRCVMORE) {
1086114878Sjulian		NG_BTSOCKET_RFCOMM_INFO(
1087114878Sjulian"%s: L2CAP connection has been terminated, so=%p, so_state=%#x, so_count=%d, " \
1088114878Sjulian"state=%d, flags=%#x\n", __func__, s->l2so, s->l2so->so_state,
1089114878Sjulian			s->l2so->so_count, s->state, s->flags);
1090114878Sjulian
1091114878Sjulian		s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1092114878Sjulian		ng_btsocket_rfcomm_session_clean(s);
1093114878Sjulian	}
1094114878Sjulian
1095114878Sjulian	/* Now process upcall */
1096114878Sjulian	switch (s->state) {
1097114878Sjulian	/* Try to accept new L2CAP connection(s) */
1098114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_LISTENING:
1099114878Sjulian		while (ng_btsocket_rfcomm_session_accept(s) == 0)
1100114878Sjulian			;
1101114878Sjulian		break;
1102114878Sjulian
1103114878Sjulian	/* Process the results of the L2CAP connect */
1104114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_CONNECTING:
1105114878Sjulian		ng_btsocket_rfcomm_session_process_pcb(s);
1106114878Sjulian
1107114878Sjulian		if (ng_btsocket_rfcomm_session_connect(s) != 0) {
1108114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1109114878Sjulian			ng_btsocket_rfcomm_session_clean(s);
1110114878Sjulian		}
1111114878Sjulian		break;
1112114878Sjulian
1113114878Sjulian	/* Try to receive/send more data */
1114114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
1115114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_OPEN:
1116114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING:
1117114878Sjulian		ng_btsocket_rfcomm_session_process_pcb(s);
1118114878Sjulian
1119114878Sjulian		if (ng_btsocket_rfcomm_session_receive(s) != 0) {
1120114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1121114878Sjulian			ng_btsocket_rfcomm_session_clean(s);
1122114878Sjulian		} else if (ng_btsocket_rfcomm_session_send(s) != 0) {
1123114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1124114878Sjulian			ng_btsocket_rfcomm_session_clean(s);
1125114878Sjulian		}
1126114878Sjulian		break;
1127114878Sjulian
1128114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_CLOSED:
1129114878Sjulian		break;
1130114878Sjulian
1131114878Sjulian	default:
1132114878Sjulian		panic("%s: Invalid session state=%d, flags=%#x\n",
1133114878Sjulian			__func__, s->state, s->flags);
1134114878Sjulian		break;
1135114878Sjulian	}
1136114878Sjulian} /* ng_btsocket_rfcomm_session_task */
1137114878Sjulian
1138114878Sjulian/*
1139114878Sjulian * Process RFCOMM connection indicator. Caller must hold s->session_mtx
1140114878Sjulian */
1141114878Sjulian
1142114878Sjulianstatic ng_btsocket_rfcomm_pcb_p
1143114878Sjulianng_btsocket_rfcomm_connect_ind(ng_btsocket_rfcomm_session_p s, int channel)
1144114878Sjulian{
1145114878Sjulian	ng_btsocket_rfcomm_pcb_p	 pcb = NULL, pcb1 = NULL;
1146114878Sjulian	ng_btsocket_l2cap_pcb_p		 l2pcb = NULL;
1147114878Sjulian	struct socket			*so1 = NULL;
1148114878Sjulian
1149114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1150114878Sjulian
1151114878Sjulian	/*
1152114878Sjulian	 * Try to find RFCOMM socket that listens on given source address
1153114878Sjulian	 * and channel. This will return the best possible match.
1154114878Sjulian	 */
1155114878Sjulian
1156114878Sjulian	l2pcb = so2l2cap_pcb(s->l2so);
1157114878Sjulian	pcb = ng_btsocket_rfcomm_pcb_listener(&l2pcb->src, channel);
1158114878Sjulian	if (pcb == NULL)
1159114878Sjulian		return (NULL);
1160114878Sjulian
1161114878Sjulian	/*
1162114878Sjulian	 * Check the pending connections queue and if we have space then
1163114878Sjulian	 * create new socket and set proper source and destination address,
1164114878Sjulian	 * and channel.
1165114878Sjulian	 */
1166114878Sjulian
1167114878Sjulian	mtx_lock(&pcb->pcb_mtx);
1168114878Sjulian
1169218757Sbz	if (pcb->so->so_qlen <= pcb->so->so_qlimit) {
1170218757Sbz		CURVNET_SET(pcb->so->so_vnet);
1171114878Sjulian		so1 = sonewconn(pcb->so, 0);
1172218757Sbz		CURVNET_RESTORE();
1173218757Sbz	}
1174114878Sjulian
1175114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
1176114878Sjulian
1177114878Sjulian	if (so1 == NULL)
1178114878Sjulian		return (NULL);
1179114878Sjulian
1180114878Sjulian	/*
1181114878Sjulian	 * If we got here than we have created new socket. So complete the
1182114878Sjulian	 * connection. Set source and destination address from the session.
1183114878Sjulian	 */
1184114878Sjulian
1185114878Sjulian	pcb1 = so2rfcomm_pcb(so1);
1186114878Sjulian	if (pcb1 == NULL)
1187114878Sjulian		panic("%s: pcb1 == NULL\n", __func__);
1188114878Sjulian
1189114878Sjulian	mtx_lock(&pcb1->pcb_mtx);
1190114878Sjulian
1191114878Sjulian	bcopy(&l2pcb->src, &pcb1->src, sizeof(pcb1->src));
1192114878Sjulian	bcopy(&l2pcb->dst, &pcb1->dst, sizeof(pcb1->dst));
1193114878Sjulian	pcb1->channel = channel;
1194114878Sjulian
1195114878Sjulian	/* Link new DLC to the session. We already hold s->session_mtx */
1196114878Sjulian	LIST_INSERT_HEAD(&s->dlcs, pcb1, session_next);
1197114878Sjulian	pcb1->session = s;
1198114878Sjulian
1199114878Sjulian	mtx_unlock(&pcb1->pcb_mtx);
1200114878Sjulian
1201114878Sjulian	return (pcb1);
1202114878Sjulian} /* ng_btsocket_rfcomm_connect_ind */
1203114878Sjulian
1204114878Sjulian/*
1205114878Sjulian * Process RFCOMM connect confirmation. Caller must hold s->session_mtx.
1206114878Sjulian */
1207114878Sjulian
1208114878Sjulianstatic void
1209114878Sjulianng_btsocket_rfcomm_connect_cfm(ng_btsocket_rfcomm_session_p s)
1210114878Sjulian{
1211161623Semax	ng_btsocket_rfcomm_pcb_p	pcb = NULL, pcb_next = NULL;
1212161623Semax	int				error;
1213114878Sjulian
1214114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1215114878Sjulian
1216114878Sjulian	/*
1217114878Sjulian	 * Wake up all waiting sockets and send PN request for each of them.
1218114878Sjulian	 * Note that timeout already been set in ng_btsocket_rfcomm_connect()
1219114878Sjulian	 *
1220114878Sjulian	 * Note: cannot use LIST_FOREACH because ng_btsocket_rfcomm_pcb_kill
1221114878Sjulian	 * will unlink DLC from the session
1222114878Sjulian	 */
1223114878Sjulian
1224114878Sjulian	for (pcb = LIST_FIRST(&s->dlcs); pcb != NULL; ) {
1225114878Sjulian		mtx_lock(&pcb->pcb_mtx);
1226114878Sjulian		pcb_next = LIST_NEXT(pcb, session_next);
1227114878Sjulian
1228114878Sjulian		if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT) {
1229114878Sjulian			pcb->mtu = s->mtu;
1230114878Sjulian			bcopy(&so2l2cap_pcb(s->l2so)->src, &pcb->src,
1231114878Sjulian				sizeof(pcb->src));
1232114878Sjulian
1233114878Sjulian			error = ng_btsocket_rfcomm_send_pn(pcb);
1234114878Sjulian			if (error == 0)
1235114878Sjulian				pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONFIGURING;
1236161623Semax			else
1237161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, error);
1238114878Sjulian		}
1239114878Sjulian
1240114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
1241114878Sjulian		pcb = pcb_next;
1242114878Sjulian	}
1243114878Sjulian} /* ng_btsocket_rfcomm_connect_cfm */
1244114878Sjulian
1245114878Sjulian/*****************************************************************************
1246114878Sjulian *****************************************************************************
1247114878Sjulian **                              RFCOMM sessions
1248114878Sjulian *****************************************************************************
1249114878Sjulian *****************************************************************************/
1250114878Sjulian
1251114878Sjulian/*
1252114878Sjulian * Create new RFCOMM session. That function WILL NOT take ownership over l2so.
1253114878Sjulian * Caller MUST free l2so if function failed.
1254114878Sjulian */
1255114878Sjulian
1256114878Sjulianstatic int
1257114878Sjulianng_btsocket_rfcomm_session_create(ng_btsocket_rfcomm_session_p *sp,
1258114878Sjulian		struct socket *l2so, bdaddr_p src, bdaddr_p dst,
1259114878Sjulian		struct thread *td)
1260114878Sjulian{
1261114878Sjulian	ng_btsocket_rfcomm_session_p	s = NULL;
1262114878Sjulian	struct sockaddr_l2cap		l2sa;
1263114878Sjulian	struct sockopt			l2sopt;
1264161579Semax	int				error;
1265161579Semax	u_int16_t			mtu;
1266114878Sjulian
1267114878Sjulian	mtx_assert(&ng_btsocket_rfcomm_sessions_mtx, MA_OWNED);
1268114878Sjulian
1269114878Sjulian	/* Allocate the RFCOMM session */
1270184205Sdes        s = malloc(sizeof(*s),
1271114878Sjulian		M_NETGRAPH_BTSOCKET_RFCOMM, M_NOWAIT | M_ZERO);
1272114878Sjulian        if (s == NULL)
1273114878Sjulian                return (ENOMEM);
1274114878Sjulian
1275114878Sjulian	/* Set defaults */
1276114878Sjulian	s->mtu = RFCOMM_DEFAULT_MTU;
1277114878Sjulian	s->flags = 0;
1278114878Sjulian	s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1279114878Sjulian	NG_BT_MBUFQ_INIT(&s->outq, ifqmaxlen);
1280114878Sjulian
1281114878Sjulian	/*
1282114878Sjulian	 * XXX Mark session mutex as DUPOK to prevent "duplicated lock of
1283114878Sjulian	 * the same type" message. When accepting new L2CAP connection
1284114878Sjulian	 * ng_btsocket_rfcomm_session_accept() holds both session mutexes
1285114878Sjulian	 * for "old" (accepting) session and "new" (created) session.
1286114878Sjulian	 */
1287114878Sjulian
1288114878Sjulian	mtx_init(&s->session_mtx, "btsocks_rfcomm_session_mtx", NULL,
1289114878Sjulian		MTX_DEF|MTX_DUPOK);
1290114878Sjulian
1291114878Sjulian	LIST_INIT(&s->dlcs);
1292114878Sjulian
1293114878Sjulian	/* Prepare L2CAP socket */
1294130653Srwatson	SOCKBUF_LOCK(&l2so->so_rcv);
1295193272Sjhb	soupcall_set(l2so, SO_RCV, ng_btsocket_rfcomm_upcall, NULL);
1296130653Srwatson	SOCKBUF_UNLOCK(&l2so->so_rcv);
1297130653Srwatson	SOCKBUF_LOCK(&l2so->so_snd);
1298193272Sjhb	soupcall_set(l2so, SO_SND, ng_btsocket_rfcomm_upcall, NULL);
1299130653Srwatson	SOCKBUF_UNLOCK(&l2so->so_snd);
1300114878Sjulian	l2so->so_state |= SS_NBIO;
1301114878Sjulian	s->l2so = l2so;
1302114878Sjulian
1303114878Sjulian	mtx_lock(&s->session_mtx);
1304114878Sjulian
1305114878Sjulian	/*
1306114878Sjulian	 * "src" == NULL and "dst" == NULL means just create session.
1307114878Sjulian	 * caller must do the rest
1308114878Sjulian	 */
1309114878Sjulian
1310114878Sjulian	if (src == NULL && dst == NULL)
1311114878Sjulian		goto done;
1312114878Sjulian
1313114878Sjulian	/*
1314114878Sjulian	 * Set incoming MTU on L2CAP socket. It is RFCOMM session default MTU
1315114878Sjulian	 * plus 5 bytes: RFCOMM frame header, one extra byte for length and one
1316114878Sjulian	 * extra byte for credits.
1317114878Sjulian	 */
1318114878Sjulian
1319114878Sjulian	mtu = s->mtu + sizeof(struct rfcomm_frame_hdr) + 1 + 1;
1320114878Sjulian
1321114878Sjulian	l2sopt.sopt_dir = SOPT_SET;
1322114878Sjulian	l2sopt.sopt_level = SOL_L2CAP;
1323114878Sjulian	l2sopt.sopt_name = SO_L2CAP_IMTU;
1324114878Sjulian	l2sopt.sopt_val = (void *) &mtu;
1325114878Sjulian	l2sopt.sopt_valsize = sizeof(mtu);
1326114878Sjulian	l2sopt.sopt_td = NULL;
1327114878Sjulian
1328114878Sjulian	error = sosetopt(s->l2so, &l2sopt);
1329114878Sjulian	if (error != 0)
1330114878Sjulian		goto bad;
1331114878Sjulian
1332114878Sjulian	/* Bind socket to "src" address */
1333114878Sjulian	l2sa.l2cap_len = sizeof(l2sa);
1334114878Sjulian	l2sa.l2cap_family = AF_BLUETOOTH;
1335114878Sjulian	l2sa.l2cap_psm = (dst == NULL)? htole16(NG_L2CAP_PSM_RFCOMM) : 0;
1336114878Sjulian	bcopy(src, &l2sa.l2cap_bdaddr, sizeof(l2sa.l2cap_bdaddr));
1337114878Sjulian
1338114878Sjulian	error = sobind(s->l2so, (struct sockaddr *) &l2sa, td);
1339114878Sjulian	if (error != 0)
1340114878Sjulian		goto bad;
1341114878Sjulian
1342114878Sjulian	/* If "dst" is not NULL then initiate connect(), otherwise listen() */
1343114878Sjulian	if (dst == NULL) {
1344114878Sjulian		s->flags = 0;
1345114878Sjulian		s->state = NG_BTSOCKET_RFCOMM_SESSION_LISTENING;
1346114878Sjulian
1347114878Sjulian		error = solisten(s->l2so, 10, td);
1348114878Sjulian		if (error != 0)
1349114878Sjulian			goto bad;
1350114878Sjulian	} else {
1351114878Sjulian		s->flags = NG_BTSOCKET_RFCOMM_SESSION_INITIATOR;
1352114878Sjulian		s->state = NG_BTSOCKET_RFCOMM_SESSION_CONNECTING;
1353114878Sjulian
1354114878Sjulian		l2sa.l2cap_len = sizeof(l2sa);
1355114878Sjulian		l2sa.l2cap_family = AF_BLUETOOTH;
1356114878Sjulian		l2sa.l2cap_psm = htole16(NG_L2CAP_PSM_RFCOMM);
1357114878Sjulian	        bcopy(dst, &l2sa.l2cap_bdaddr, sizeof(l2sa.l2cap_bdaddr));
1358114878Sjulian
1359114878Sjulian		error = soconnect(s->l2so, (struct sockaddr *) &l2sa, td);
1360114878Sjulian		if (error != 0)
1361114878Sjulian			goto bad;
1362114878Sjulian	}
1363114878Sjulian
1364114878Sjuliandone:
1365114878Sjulian	LIST_INSERT_HEAD(&ng_btsocket_rfcomm_sessions, s, next);
1366114878Sjulian	*sp = s;
1367114878Sjulian
1368114878Sjulian	mtx_unlock(&s->session_mtx);
1369114878Sjulian
1370114878Sjulian	return (0);
1371114878Sjulian
1372114878Sjulianbad:
1373114878Sjulian	mtx_unlock(&s->session_mtx);
1374114878Sjulian
1375114878Sjulian	/* Return L2CAP socket back to its original state */
1376130653Srwatson	SOCKBUF_LOCK(&l2so->so_rcv);
1377193272Sjhb	soupcall_clear(s->l2so, SO_RCV);
1378130670Srwatson	SOCKBUF_UNLOCK(&l2so->so_rcv);
1379130653Srwatson	SOCKBUF_LOCK(&l2so->so_snd);
1380193272Sjhb	soupcall_clear(s->l2so, SO_SND);
1381130670Srwatson	SOCKBUF_UNLOCK(&l2so->so_snd);
1382114878Sjulian	l2so->so_state &= ~SS_NBIO;
1383114878Sjulian
1384114878Sjulian	mtx_destroy(&s->session_mtx);
1385114878Sjulian	bzero(s, sizeof(*s));
1386184205Sdes	free(s, M_NETGRAPH_BTSOCKET_RFCOMM);
1387114878Sjulian
1388114878Sjulian	return (error);
1389114878Sjulian} /* ng_btsocket_rfcomm_session_create */
1390114878Sjulian
1391114878Sjulian/*
1392114878Sjulian * Process accept() on RFCOMM session
1393114878Sjulian * XXX FIXME locking for "l2so"?
1394114878Sjulian */
1395114878Sjulian
1396114878Sjulianstatic int
1397114878Sjulianng_btsocket_rfcomm_session_accept(ng_btsocket_rfcomm_session_p s0)
1398114878Sjulian{
1399114878Sjulian	struct socket			*l2so = NULL;
1400114878Sjulian	struct sockaddr_l2cap		*l2sa = NULL;
1401114878Sjulian	ng_btsocket_l2cap_pcb_t		*l2pcb = NULL;
1402114878Sjulian	ng_btsocket_rfcomm_session_p	 s = NULL;
1403114878Sjulian	int				 error = 0;
1404114878Sjulian
1405114878Sjulian	mtx_assert(&ng_btsocket_rfcomm_sessions_mtx, MA_OWNED);
1406114878Sjulian	mtx_assert(&s0->session_mtx, MA_OWNED);
1407114878Sjulian
1408114878Sjulian	/* Check if there is a complete L2CAP connection in the queue */
1409114878Sjulian	if ((error = s0->l2so->so_error) != 0) {
1410114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
1411114878Sjulian"%s: Could not accept connection on L2CAP socket, error=%d\n", __func__, error);
1412114878Sjulian		s0->l2so->so_error = 0;
1413114878Sjulian
1414114878Sjulian		return (error);
1415114878Sjulian	}
1416114878Sjulian
1417129979Srwatson	ACCEPT_LOCK();
1418114878Sjulian	if (TAILQ_EMPTY(&s0->l2so->so_comp)) {
1419129979Srwatson		ACCEPT_UNLOCK();
1420130480Srwatson		if (s0->l2so->so_rcv.sb_state & SBS_CANTRCVMORE)
1421114878Sjulian			return (ECONNABORTED);
1422114878Sjulian		return (EWOULDBLOCK);
1423114878Sjulian	}
1424114878Sjulian
1425114878Sjulian	/* Accept incoming L2CAP connection */
1426114878Sjulian	l2so = TAILQ_FIRST(&s0->l2so->so_comp);
1427114878Sjulian	if (l2so == NULL)
1428114878Sjulian		panic("%s: l2so == NULL\n", __func__);
1429114878Sjulian
1430114878Sjulian	TAILQ_REMOVE(&s0->l2so->so_comp, l2so, so_list);
1431114878Sjulian	s0->l2so->so_qlen --;
1432129979Srwatson	l2so->so_qstate &= ~SQ_COMP;
1433129979Srwatson	l2so->so_head = NULL;
1434130387Srwatson	SOCK_LOCK(l2so);
1435114878Sjulian	soref(l2so);
1436114878Sjulian	l2so->so_state |= SS_NBIO;
1437130387Srwatson	SOCK_UNLOCK(l2so);
1438129979Srwatson	ACCEPT_UNLOCK();
1439114878Sjulian
1440114878Sjulian	error = soaccept(l2so, (struct sockaddr **) &l2sa);
1441114878Sjulian	if (error != 0) {
1442114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
1443114878Sjulian"%s: soaccept() on L2CAP socket failed, error=%d\n", __func__, error);
1444114878Sjulian		soclose(l2so);
1445114878Sjulian
1446114878Sjulian		return (error);
1447114878Sjulian	}
1448114878Sjulian
1449114878Sjulian	/*
1450114878Sjulian	 * Check if there is already active RFCOMM session between two devices.
1451114878Sjulian	 * If so then close L2CAP connection. We only support one RFCOMM session
1452114878Sjulian	 * between each pair of devices. Note that here we assume session in any
1453114878Sjulian	 * state. The session even could be in the middle of disconnecting.
1454114878Sjulian	 */
1455114878Sjulian
1456114878Sjulian	l2pcb = so2l2cap_pcb(l2so);
1457114878Sjulian	s = ng_btsocket_rfcomm_session_by_addr(&l2pcb->src, &l2pcb->dst);
1458114878Sjulian	if (s == NULL) {
1459114878Sjulian		/* Create a new RFCOMM session */
1460114878Sjulian		error = ng_btsocket_rfcomm_session_create(&s, l2so, NULL, NULL,
1461114878Sjulian				curthread /* XXX */);
1462114878Sjulian		if (error == 0) {
1463114878Sjulian			mtx_lock(&s->session_mtx);
1464114878Sjulian
1465114878Sjulian			s->flags = 0;
1466114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CONNECTED;
1467114878Sjulian
1468114878Sjulian			/*
1469114878Sjulian			 * Adjust MTU on incomming connection. Reserve 5 bytes:
1470114878Sjulian			 * RFCOMM frame header, one extra byte for length and
1471114878Sjulian			 * one extra byte for credits.
1472114878Sjulian			 */
1473114878Sjulian
1474114878Sjulian			s->mtu = min(l2pcb->imtu, l2pcb->omtu) -
1475114878Sjulian					sizeof(struct rfcomm_frame_hdr) - 1 - 1;
1476114878Sjulian
1477114878Sjulian			mtx_unlock(&s->session_mtx);
1478114878Sjulian		} else {
1479114878Sjulian			NG_BTSOCKET_RFCOMM_ALERT(
1480114878Sjulian"%s: Failed to create new RFCOMM session, error=%d\n", __func__, error);
1481114878Sjulian
1482114878Sjulian			soclose(l2so);
1483114878Sjulian		}
1484114878Sjulian	} else {
1485114878Sjulian		NG_BTSOCKET_RFCOMM_WARN(
1486114878Sjulian"%s: Rejecting duplicating RFCOMM session between src=%x:%x:%x:%x:%x:%x and " \
1487114878Sjulian"dst=%x:%x:%x:%x:%x:%x, state=%d, flags=%#x\n",	__func__,
1488114878Sjulian			l2pcb->src.b[5], l2pcb->src.b[4], l2pcb->src.b[3],
1489114878Sjulian			l2pcb->src.b[2], l2pcb->src.b[1], l2pcb->src.b[0],
1490114878Sjulian			l2pcb->dst.b[5], l2pcb->dst.b[4], l2pcb->dst.b[3],
1491114878Sjulian			l2pcb->dst.b[2], l2pcb->dst.b[1], l2pcb->dst.b[0],
1492114878Sjulian			s->state, s->flags);
1493114878Sjulian
1494114878Sjulian		error = EBUSY;
1495114878Sjulian		soclose(l2so);
1496114878Sjulian	}
1497114878Sjulian
1498114878Sjulian	return (error);
1499114878Sjulian} /* ng_btsocket_rfcomm_session_accept */
1500114878Sjulian
1501114878Sjulian/*
1502114878Sjulian * Process connect() on RFCOMM session
1503114878Sjulian * XXX FIXME locking for "l2so"?
1504114878Sjulian */
1505114878Sjulian
1506114878Sjulianstatic int
1507114878Sjulianng_btsocket_rfcomm_session_connect(ng_btsocket_rfcomm_session_p s)
1508114878Sjulian{
1509114878Sjulian	ng_btsocket_l2cap_pcb_p	l2pcb = so2l2cap_pcb(s->l2so);
1510114878Sjulian	int			error;
1511114878Sjulian
1512114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1513114878Sjulian
1514114878Sjulian	/* First check if connection has failed */
1515114878Sjulian	if ((error = s->l2so->so_error) != 0) {
1516114878Sjulian		s->l2so->so_error = 0;
1517114878Sjulian
1518114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
1519114878Sjulian"%s: Could not connect RFCOMM session, error=%d, state=%d, flags=%#x\n",
1520114878Sjulian			__func__, error, s->state, s->flags);
1521114878Sjulian
1522114878Sjulian		return (error);
1523114878Sjulian	}
1524114878Sjulian
1525114878Sjulian	/* Is connection still in progress? */
1526114878Sjulian	if (s->l2so->so_state & SS_ISCONNECTING)
1527114878Sjulian		return (0);
1528114878Sjulian
1529114878Sjulian	/*
1530114878Sjulian	 * If we got here then we are connected. Send SABM on DLCI 0 to
1531114878Sjulian	 * open multiplexor channel.
1532114878Sjulian	 */
1533114878Sjulian
1534114878Sjulian	if (error == 0) {
1535114878Sjulian		s->state = NG_BTSOCKET_RFCOMM_SESSION_CONNECTED;
1536114878Sjulian
1537114878Sjulian		/*
1538114878Sjulian		 * Adjust MTU on outgoing connection. Reserve 5 bytes: RFCOMM
1539114878Sjulian		 * frame header, one extra byte for length and one extra byte
1540114878Sjulian		 * for credits.
1541114878Sjulian		 */
1542114878Sjulian
1543114878Sjulian		s->mtu = min(l2pcb->imtu, l2pcb->omtu) -
1544114878Sjulian				sizeof(struct rfcomm_frame_hdr) - 1 - 1;
1545114878Sjulian
1546114878Sjulian		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_SABM,0);
1547114878Sjulian		if (error == 0)
1548114878Sjulian			error = ng_btsocket_rfcomm_task_wakeup();
1549114878Sjulian	}
1550114878Sjulian
1551114878Sjulian	return (error);
1552114878Sjulian}/* ng_btsocket_rfcomm_session_connect */
1553114878Sjulian
1554114878Sjulian/*
1555114878Sjulian * Receive data on RFCOMM session
1556114878Sjulian * XXX FIXME locking for "l2so"?
1557114878Sjulian */
1558114878Sjulian
1559114878Sjulianstatic int
1560114878Sjulianng_btsocket_rfcomm_session_receive(ng_btsocket_rfcomm_session_p s)
1561114878Sjulian{
1562114878Sjulian	struct mbuf	*m = NULL;
1563114878Sjulian	struct uio	 uio;
1564114878Sjulian	int		 more, flags, error;
1565114878Sjulian
1566114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1567114878Sjulian
1568114878Sjulian	/* Can we read from the L2CAP socket? */
1569114878Sjulian	if (!soreadable(s->l2so))
1570114878Sjulian		return (0);
1571114878Sjulian
1572114878Sjulian	/* First check for error on L2CAP socket */
1573114878Sjulian	if ((error = s->l2so->so_error) != 0) {
1574114878Sjulian		s->l2so->so_error = 0;
1575114878Sjulian
1576114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
1577114878Sjulian"%s: Could not receive data from L2CAP socket, error=%d, state=%d, flags=%#x\n",
1578114878Sjulian			__func__, error, s->state, s->flags);
1579114878Sjulian
1580114878Sjulian		return (error);
1581114878Sjulian	}
1582114878Sjulian
1583114878Sjulian	/*
1584114878Sjulian	 * Read all packets from the L2CAP socket.
1585114878Sjulian	 * XXX FIXME/VERIFY is that correct? For now use m->m_nextpkt as
1586114878Sjulian	 * indication that there is more packets on the socket's buffer.
1587114878Sjulian	 * Also what should we use in uio.uio_resid?
1588114878Sjulian	 * May be s->mtu + sizeof(struct rfcomm_frame_hdr) + 1 + 1?
1589114878Sjulian	 */
1590114878Sjulian
1591114878Sjulian	for (more = 1; more; ) {
1592114878Sjulian		/* Try to get next packet from socket */
1593114878Sjulian		bzero(&uio, sizeof(uio));
1594114878Sjulian/*		uio.uio_td = NULL; */
1595114878Sjulian		uio.uio_resid = 1000000000;
1596114878Sjulian		flags = MSG_DONTWAIT;
1597114878Sjulian
1598114878Sjulian		m = NULL;
1599160619Srwatson		error = soreceive(s->l2so, NULL, &uio, &m,
1600160619Srwatson		    (struct mbuf **) NULL, &flags);
1601114878Sjulian		if (error != 0) {
1602114878Sjulian			if (error == EWOULDBLOCK)
1603114878Sjulian				return (0); /* XXX can happen? */
1604114878Sjulian
1605114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
1606114878Sjulian"%s: Could not receive data from L2CAP socket, error=%d\n", __func__, error);
1607114878Sjulian
1608114878Sjulian			return (error);
1609114878Sjulian		}
1610114878Sjulian
1611114878Sjulian		more = (m->m_nextpkt != NULL);
1612114878Sjulian		m->m_nextpkt = NULL;
1613114878Sjulian
1614114878Sjulian		ng_btsocket_rfcomm_receive_frame(s, m);
1615114878Sjulian	}
1616114878Sjulian
1617114878Sjulian	return (0);
1618114878Sjulian} /* ng_btsocket_rfcomm_session_receive */
1619114878Sjulian
1620114878Sjulian/*
1621114878Sjulian * Send data on RFCOMM session
1622114878Sjulian * XXX FIXME locking for "l2so"?
1623114878Sjulian */
1624114878Sjulian
1625114878Sjulianstatic int
1626114878Sjulianng_btsocket_rfcomm_session_send(ng_btsocket_rfcomm_session_p s)
1627114878Sjulian{
1628114878Sjulian	struct mbuf	*m = NULL;
1629114878Sjulian	int		 error;
1630114878Sjulian
1631114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1632114878Sjulian
1633114878Sjulian	/* Send as much as we can from the session queue */
1634114878Sjulian	while (sowriteable(s->l2so)) {
1635114878Sjulian		/* Check if socket still OK */
1636114878Sjulian		if ((error = s->l2so->so_error) != 0) {
1637114878Sjulian			s->l2so->so_error = 0;
1638114878Sjulian
1639114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
1640114878Sjulian"%s: Detected error=%d on L2CAP socket, state=%d, flags=%#x\n",
1641114878Sjulian				__func__, error, s->state, s->flags);
1642114878Sjulian
1643114878Sjulian			return (error);
1644114878Sjulian		}
1645114878Sjulian
1646114878Sjulian		NG_BT_MBUFQ_DEQUEUE(&s->outq, m);
1647114878Sjulian		if (m == NULL)
1648114878Sjulian			return (0); /* we are done */
1649114878Sjulian
1650114878Sjulian		/* Call send function on the L2CAP socket */
1651170972Semax		error = (*s->l2so->so_proto->pr_usrreqs->pru_send)(s->l2so,
1652170972Semax				0, m, NULL, NULL, curthread /* XXX */);
1653114878Sjulian		if (error != 0) {
1654114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
1655114878Sjulian"%s: Could not send data to L2CAP socket, error=%d\n", __func__, error);
1656114878Sjulian
1657114878Sjulian			return (error);
1658114878Sjulian		}
1659114878Sjulian	}
1660114878Sjulian
1661114878Sjulian	return (0);
1662114878Sjulian} /* ng_btsocket_rfcomm_session_send */
1663114878Sjulian
1664114878Sjulian/*
1665114878Sjulian * Close and disconnect all DLCs for the given session. Caller must hold
1666114878Sjulian * s->sesson_mtx. Will wakeup session.
1667114878Sjulian */
1668114878Sjulian
1669114878Sjulianstatic void
1670114878Sjulianng_btsocket_rfcomm_session_clean(ng_btsocket_rfcomm_session_p s)
1671114878Sjulian{
1672161623Semax	ng_btsocket_rfcomm_pcb_p	pcb = NULL, pcb_next = NULL;
1673161623Semax	int				error;
1674114878Sjulian
1675114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1676114878Sjulian
1677114878Sjulian	/*
1678114878Sjulian	 * Note: cannot use LIST_FOREACH because ng_btsocket_rfcomm_pcb_kill
1679114878Sjulian	 * will unlink DLC from the session
1680114878Sjulian	 */
1681114878Sjulian
1682114878Sjulian	for (pcb = LIST_FIRST(&s->dlcs); pcb != NULL; ) {
1683114878Sjulian		mtx_lock(&pcb->pcb_mtx);
1684114878Sjulian		pcb_next = LIST_NEXT(pcb, session_next);
1685114878Sjulian
1686114878Sjulian		NG_BTSOCKET_RFCOMM_INFO(
1687114878Sjulian"%s: Disconnecting dlci=%d, state=%d, flags=%#x\n",
1688114878Sjulian			__func__, pcb->dlci, pcb->state, pcb->flags);
1689114878Sjulian
1690114878Sjulian		if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_CONNECTED)
1691114878Sjulian			error = ECONNRESET;
1692114878Sjulian		else
1693114878Sjulian			error = ECONNREFUSED;
1694114878Sjulian
1695161623Semax		ng_btsocket_rfcomm_pcb_kill(pcb, error);
1696114878Sjulian
1697114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
1698114878Sjulian		pcb = pcb_next;
1699114878Sjulian	}
1700114878Sjulian} /* ng_btsocket_rfcomm_session_clean */
1701114878Sjulian
1702114878Sjulian/*
1703114878Sjulian * Process all DLCs on the session. Caller MUST hold s->session_mtx.
1704114878Sjulian */
1705114878Sjulian
1706114878Sjulianstatic void
1707114878Sjulianng_btsocket_rfcomm_session_process_pcb(ng_btsocket_rfcomm_session_p s)
1708114878Sjulian{
1709161623Semax	ng_btsocket_rfcomm_pcb_p	pcb = NULL, pcb_next = NULL;
1710161623Semax	int				error;
1711114878Sjulian
1712114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1713114878Sjulian
1714114878Sjulian	/*
1715114878Sjulian	 * Note: cannot use LIST_FOREACH because ng_btsocket_rfcomm_pcb_kill
1716114878Sjulian	 * will unlink DLC from the session
1717114878Sjulian	 */
1718114878Sjulian
1719114878Sjulian	for (pcb = LIST_FIRST(&s->dlcs); pcb != NULL; ) {
1720114878Sjulian		mtx_lock(&pcb->pcb_mtx);
1721114878Sjulian		pcb_next = LIST_NEXT(pcb, session_next);
1722114878Sjulian
1723114878Sjulian		switch (pcb->state) {
1724114878Sjulian
1725114878Sjulian		/*
1726114878Sjulian		 * If DLC in W4_CONNECT state then we should check for both
1727114878Sjulian		 * timeout and detach.
1728114878Sjulian		 */
1729114878Sjulian
1730114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
1731161623Semax			if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_DETACHED)
1732161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, 0);
1733161623Semax			else if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT)
1734161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, ETIMEDOUT);
1735114878Sjulian			break;
1736114878Sjulian
1737114878Sjulian		/*
1738114878Sjulian		 * If DLC in CONFIGURING or CONNECTING state then we only
1739114878Sjulian		 * should check for timeout. If detach() was called then
1740114878Sjulian		 * DLC will be moved into DISCONNECTING state.
1741114878Sjulian		 */
1742114878Sjulian
1743114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING:
1744114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
1745114878Sjulian			if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT)
1746161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, ETIMEDOUT);
1747114878Sjulian			break;
1748114878Sjulian
1749114878Sjulian		/*
1750114878Sjulian		 * If DLC in CONNECTED state then we need to send data (if any)
1751114878Sjulian		 * from the socket's send queue. Note that we will send data
1752114878Sjulian		 * from either all sockets or none. This may overload session's
1753114878Sjulian		 * outgoing queue (but we do not check for that).
1754114878Sjulian		 *
1755114878Sjulian 		 * XXX FIXME need scheduler for RFCOMM sockets
1756114878Sjulian		 */
1757114878Sjulian
1758114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_CONNECTED:
1759114878Sjulian			error = ng_btsocket_rfcomm_pcb_send(pcb, ALOT);
1760114878Sjulian			if (error != 0)
1761161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, error);
1762114878Sjulian			break;
1763114878Sjulian
1764114878Sjulian		/*
1765114878Sjulian		 * If DLC in DISCONNECTING state then we must send DISC frame.
1766114878Sjulian		 * Note that if DLC has timeout set then we do not need to
1767114878Sjulian		 * resend DISC frame.
1768114878Sjulian		 *
1769114878Sjulian		 * XXX FIXME need to drain all data from the socket's queue
1770114878Sjulian		 * if LINGER option was set
1771114878Sjulian		 */
1772114878Sjulian
1773114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
1774114878Sjulian			if (!(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)) {
1775114878Sjulian				error = ng_btsocket_rfcomm_send_command(
1776114878Sjulian						pcb->session, RFCOMM_FRAME_DISC,
1777114878Sjulian						pcb->dlci);
1778114878Sjulian				if (error == 0)
1779114878Sjulian					ng_btsocket_rfcomm_timeout(pcb);
1780161623Semax				else
1781161623Semax					ng_btsocket_rfcomm_pcb_kill(pcb, error);
1782114878Sjulian			} else if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT)
1783161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, ETIMEDOUT);
1784114878Sjulian			break;
1785114878Sjulian
1786114878Sjulian/*		case NG_BTSOCKET_RFCOMM_DLC_CLOSED: */
1787114878Sjulian		default:
1788114878Sjulian			panic("%s: Invalid DLC state=%d, flags=%#x\n",
1789114878Sjulian				__func__, pcb->state, pcb->flags);
1790114878Sjulian			break;
1791114878Sjulian		}
1792114878Sjulian
1793114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
1794114878Sjulian		pcb = pcb_next;
1795114878Sjulian	}
1796114878Sjulian} /* ng_btsocket_rfcomm_session_process_pcb */
1797114878Sjulian
1798114878Sjulian/*
1799114878Sjulian * Find RFCOMM session between "src" and "dst".
1800114878Sjulian * Caller MUST hold ng_btsocket_rfcomm_sessions_mtx.
1801114878Sjulian */
1802114878Sjulian
1803114878Sjulianstatic ng_btsocket_rfcomm_session_p
1804114878Sjulianng_btsocket_rfcomm_session_by_addr(bdaddr_p src, bdaddr_p dst)
1805114878Sjulian{
1806114878Sjulian	ng_btsocket_rfcomm_session_p	s = NULL;
1807114878Sjulian	ng_btsocket_l2cap_pcb_p		l2pcb = NULL;
1808114878Sjulian	int				any_src;
1809114878Sjulian
1810114878Sjulian	mtx_assert(&ng_btsocket_rfcomm_sessions_mtx, MA_OWNED);
1811114878Sjulian
1812114878Sjulian	any_src = (bcmp(src, NG_HCI_BDADDR_ANY, sizeof(*src)) == 0);
1813114878Sjulian
1814114878Sjulian	LIST_FOREACH(s, &ng_btsocket_rfcomm_sessions, next) {
1815114878Sjulian		l2pcb = so2l2cap_pcb(s->l2so);
1816114878Sjulian
1817114878Sjulian		if ((any_src || bcmp(&l2pcb->src, src, sizeof(*src)) == 0) &&
1818114878Sjulian		    bcmp(&l2pcb->dst, dst, sizeof(*dst)) == 0)
1819114878Sjulian			break;
1820114878Sjulian	}
1821114878Sjulian
1822114878Sjulian	return (s);
1823114878Sjulian} /* ng_btsocket_rfcomm_session_by_addr */
1824114878Sjulian
1825114878Sjulian/*****************************************************************************
1826114878Sjulian *****************************************************************************
1827114878Sjulian **                                  RFCOMM
1828114878Sjulian *****************************************************************************
1829114878Sjulian *****************************************************************************/
1830114878Sjulian
1831114878Sjulian/*
1832114878Sjulian * Process incoming RFCOMM frame. Caller must hold s->session_mtx.
1833114878Sjulian * XXX FIXME check frame length
1834114878Sjulian */
1835114878Sjulian
1836114878Sjulianstatic int
1837114878Sjulianng_btsocket_rfcomm_receive_frame(ng_btsocket_rfcomm_session_p s,
1838114878Sjulian		struct mbuf *m0)
1839114878Sjulian{
1840114878Sjulian	struct rfcomm_frame_hdr	*hdr = NULL;
1841114878Sjulian	struct mbuf		*m = NULL;
1842114878Sjulian	u_int16_t		 length;
1843114878Sjulian	u_int8_t		 dlci, type;
1844114878Sjulian	int			 error = 0;
1845114878Sjulian
1846114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1847114878Sjulian
1848114878Sjulian	/* Pullup as much as we can into first mbuf (for direct access) */
1849114878Sjulian	length = min(m0->m_pkthdr.len, MHLEN);
1850114878Sjulian	if (m0->m_len < length) {
1851114878Sjulian		if ((m0 = m_pullup(m0, length)) == NULL) {
1852114878Sjulian			NG_BTSOCKET_RFCOMM_ALERT(
1853114878Sjulian"%s: m_pullup(%d) failed\n", __func__, length);
1854114878Sjulian
1855114878Sjulian			return (ENOBUFS);
1856114878Sjulian		}
1857114878Sjulian	}
1858114878Sjulian
1859114878Sjulian	hdr = mtod(m0, struct rfcomm_frame_hdr *);
1860114878Sjulian	dlci = RFCOMM_DLCI(hdr->address);
1861114878Sjulian	type = RFCOMM_TYPE(hdr->control);
1862114878Sjulian
1863114878Sjulian	/* Test EA bit in length. If not set then we have 2 bytes of length */
1864114878Sjulian	if (!RFCOMM_EA(hdr->length)) {
1865114878Sjulian		bcopy(&hdr->length, &length, sizeof(length));
1866144721Semax		length = le16toh(length) >> 1;
1867114878Sjulian		m_adj(m0, sizeof(*hdr) + 1);
1868114878Sjulian	} else {
1869114878Sjulian		length = hdr->length >> 1;
1870114878Sjulian		m_adj(m0, sizeof(*hdr));
1871114878Sjulian	}
1872114878Sjulian
1873114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
1874114878Sjulian"%s: Got frame type=%#x, dlci=%d, length=%d, cr=%d, pf=%d, len=%d\n",
1875114878Sjulian		__func__, type, dlci, length, RFCOMM_CR(hdr->address),
1876114878Sjulian		RFCOMM_PF(hdr->control), m0->m_pkthdr.len);
1877114878Sjulian
1878114878Sjulian	/*
1879114878Sjulian	 * Get FCS (the last byte in the frame)
1880114878Sjulian	 * XXX this will not work if mbuf chain ends with empty mbuf.
1881114878Sjulian	 * XXX let's hope it never happens :)
1882114878Sjulian	 */
1883114878Sjulian
1884114878Sjulian	for (m = m0; m->m_next != NULL; m = m->m_next)
1885114878Sjulian		;
1886114878Sjulian	if (m->m_len <= 0)
1887114878Sjulian		panic("%s: Empty mbuf at the end of the chain, len=%d\n",
1888114878Sjulian			__func__, m->m_len);
1889114878Sjulian
1890114878Sjulian	/*
1891114878Sjulian	 * Check FCS. We only need to calculate FCS on first 2 or 3 bytes
1892114878Sjulian	 * and already m_pullup'ed mbuf chain, so it should be safe.
1893114878Sjulian	 */
1894114878Sjulian
1895114878Sjulian	if (ng_btsocket_rfcomm_check_fcs((u_int8_t *) hdr, type, m->m_data[m->m_len - 1])) {
1896114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
1897114878Sjulian"%s: Invalid RFCOMM packet. Bad checksum\n", __func__);
1898114878Sjulian		NG_FREE_M(m0);
1899114878Sjulian
1900114878Sjulian		return (EINVAL);
1901114878Sjulian	}
1902114878Sjulian
1903114878Sjulian	m_adj(m0, -1); /* Trim FCS byte */
1904114878Sjulian
1905114878Sjulian	/*
1906114878Sjulian	 * Process RFCOMM frame.
1907114878Sjulian	 *
1908114878Sjulian	 * From TS 07.10 spec
1909114878Sjulian	 *
1910114878Sjulian	 * "... In the case where a SABM or DISC command with the P bit set
1911114878Sjulian	 * to 0 is received then the received frame shall be discarded..."
1912114878Sjulian 	 *
1913114878Sjulian	 * "... If a unsolicited DM response is received then the frame shall
1914114878Sjulian	 * be processed irrespective of the P/F setting... "
1915114878Sjulian	 *
1916114878Sjulian	 * "... The station may transmit response frames with the F bit set
1917114878Sjulian	 * to 0 at any opportunity on an asynchronous basis. However, in the
1918114878Sjulian	 * case where a UA response is received with the F bit set to 0 then
1919114878Sjulian	 * the received frame shall be discarded..."
1920114878Sjulian	 *
1921114878Sjulian	 * From Bluetooth spec
1922114878Sjulian	 *
1923114878Sjulian	 * "... When credit based flow control is being used, the meaning of
1924114878Sjulian	 * the P/F bit in the control field of the RFCOMM header is redefined
1925114878Sjulian	 * for UIH frames..."
1926114878Sjulian	 */
1927114878Sjulian
1928114878Sjulian	switch (type) {
1929114878Sjulian	case RFCOMM_FRAME_SABM:
1930114878Sjulian		if (RFCOMM_PF(hdr->control))
1931114878Sjulian			error = ng_btsocket_rfcomm_receive_sabm(s, dlci);
1932114878Sjulian		break;
1933114878Sjulian
1934114878Sjulian	case RFCOMM_FRAME_DISC:
1935114878Sjulian		if (RFCOMM_PF(hdr->control))
1936114878Sjulian			error = ng_btsocket_rfcomm_receive_disc(s, dlci);
1937114878Sjulian		break;
1938114878Sjulian
1939114878Sjulian	case RFCOMM_FRAME_UA:
1940114878Sjulian		if (RFCOMM_PF(hdr->control))
1941114878Sjulian			error = ng_btsocket_rfcomm_receive_ua(s, dlci);
1942114878Sjulian		break;
1943114878Sjulian
1944114878Sjulian	case RFCOMM_FRAME_DM:
1945114878Sjulian		error = ng_btsocket_rfcomm_receive_dm(s, dlci);
1946114878Sjulian		break;
1947114878Sjulian
1948114878Sjulian	case RFCOMM_FRAME_UIH:
1949114878Sjulian		if (dlci == 0)
1950114878Sjulian			error = ng_btsocket_rfcomm_receive_mcc(s, m0);
1951114878Sjulian		else
1952114878Sjulian			error = ng_btsocket_rfcomm_receive_uih(s, dlci,
1953114878Sjulian					RFCOMM_PF(hdr->control), m0);
1954114878Sjulian
1955114878Sjulian		return (error);
1956114878Sjulian		/* NOT REACHED */
1957114878Sjulian
1958114878Sjulian	default:
1959114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
1960114878Sjulian"%s: Invalid RFCOMM packet. Unknown type=%#x\n", __func__, type);
1961114878Sjulian		error = EINVAL;
1962114878Sjulian		break;
1963114878Sjulian	}
1964114878Sjulian
1965114878Sjulian	NG_FREE_M(m0);
1966114878Sjulian
1967114878Sjulian	return (error);
1968114878Sjulian} /* ng_btsocket_rfcomm_receive_frame */
1969114878Sjulian
1970114878Sjulian/*
1971114878Sjulian * Process RFCOMM SABM frame
1972114878Sjulian */
1973114878Sjulian
1974114878Sjulianstatic int
1975114878Sjulianng_btsocket_rfcomm_receive_sabm(ng_btsocket_rfcomm_session_p s, int dlci)
1976114878Sjulian{
1977161623Semax	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
1978161623Semax	int				error = 0;
1979114878Sjulian
1980114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1981114878Sjulian
1982114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
1983114878Sjulian"%s: Got SABM, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
1984114878Sjulian		__func__, s->state, s->flags, s->mtu, dlci);
1985114878Sjulian
1986114878Sjulian	/* DLCI == 0 means open multiplexor channel */
1987114878Sjulian	if (dlci == 0) {
1988114878Sjulian		switch (s->state) {
1989114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
1990114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_OPEN:
1991114878Sjulian			error = ng_btsocket_rfcomm_send_command(s,
1992114878Sjulian					RFCOMM_FRAME_UA, dlci);
1993114878Sjulian			if (error == 0) {
1994114878Sjulian				s->state = NG_BTSOCKET_RFCOMM_SESSION_OPEN;
1995114878Sjulian				ng_btsocket_rfcomm_connect_cfm(s);
1996114878Sjulian			} else {
1997114878Sjulian				s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1998114878Sjulian				ng_btsocket_rfcomm_session_clean(s);
1999114878Sjulian			}
2000114878Sjulian			break;
2001114878Sjulian
2002114878Sjulian		default:
2003114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2004114878Sjulian"%s: Got SABM for session in invalid state state=%d, flags=%#x\n",
2005114878Sjulian				__func__, s->state, s->flags);
2006114878Sjulian			error = EINVAL;
2007114878Sjulian			break;
2008114878Sjulian		}
2009114878Sjulian
2010114878Sjulian		return (error);
2011114878Sjulian	}
2012114878Sjulian
2013114878Sjulian	/* Make sure multiplexor channel is open */
2014114878Sjulian	if (s->state != NG_BTSOCKET_RFCOMM_SESSION_OPEN) {
2015114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
2016114878Sjulian"%s: Got SABM for dlci=%d with mulitplexor channel closed, state=%d, " \
2017114878Sjulian"flags=%#x\n",		__func__, dlci, s->state, s->flags);
2018114878Sjulian
2019114878Sjulian		return (EINVAL);
2020114878Sjulian	}
2021114878Sjulian
2022114878Sjulian	/*
2023114878Sjulian	 * Check if we have this DLCI. This might happen when remote
2024114878Sjulian	 * peer uses PN command before actual open (SABM) happens.
2025114878Sjulian	 */
2026114878Sjulian
2027114878Sjulian	pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2028114878Sjulian	if (pcb != NULL) {
2029114878Sjulian		mtx_lock(&pcb->pcb_mtx);
2030114878Sjulian
2031114878Sjulian		if (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTING) {
2032114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
2033114878Sjulian"%s: Got SABM for dlci=%d in invalid state=%d, flags=%#x\n",
2034114878Sjulian				__func__, dlci, pcb->state, pcb->flags);
2035114878Sjulian			mtx_unlock(&pcb->pcb_mtx);
2036114878Sjulian
2037114878Sjulian			return (ENOENT);
2038114878Sjulian		}
2039114878Sjulian
2040114878Sjulian		ng_btsocket_rfcomm_untimeout(pcb);
2041114878Sjulian
2042114878Sjulian		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_UA,dlci);
2043114878Sjulian		if (error == 0)
2044114878Sjulian			error = ng_btsocket_rfcomm_send_msc(pcb);
2045114878Sjulian
2046114878Sjulian		if (error == 0) {
2047114878Sjulian			pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTED;
2048114878Sjulian			soisconnected(pcb->so);
2049161623Semax		} else
2050161623Semax			ng_btsocket_rfcomm_pcb_kill(pcb, error);
2051114878Sjulian
2052114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
2053114878Sjulian
2054114878Sjulian		return (error);
2055114878Sjulian	}
2056114878Sjulian
2057114878Sjulian	/*
2058114878Sjulian	 * We do not have requested DLCI, so it must be an incoming connection
2059114878Sjulian	 * with default parameters. Try to accept it.
2060114878Sjulian	 */
2061114878Sjulian
2062114878Sjulian	pcb = ng_btsocket_rfcomm_connect_ind(s, RFCOMM_SRVCHANNEL(dlci));
2063114878Sjulian	if (pcb != NULL) {
2064114878Sjulian		mtx_lock(&pcb->pcb_mtx);
2065114878Sjulian
2066114878Sjulian		pcb->dlci = dlci;
2067114878Sjulian
2068114878Sjulian		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_UA,dlci);
2069121054Semax		if (error == 0)
2070121054Semax			error = ng_btsocket_rfcomm_send_msc(pcb);
2071121054Semax
2072114878Sjulian		if (error == 0) {
2073114878Sjulian			pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTED;
2074114878Sjulian			soisconnected(pcb->so);
2075161623Semax		} else
2076161623Semax			ng_btsocket_rfcomm_pcb_kill(pcb, error);
2077114878Sjulian
2078114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
2079114878Sjulian	} else
2080114878Sjulian		/* Nobody is listen()ing on the requested DLCI */
2081114878Sjulian		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_DM,dlci);
2082114878Sjulian
2083114878Sjulian	return (error);
2084114878Sjulian} /* ng_btsocket_rfcomm_receive_sabm */
2085114878Sjulian
2086114878Sjulian/*
2087114878Sjulian * Process RFCOMM DISC frame
2088114878Sjulian */
2089114878Sjulian
2090114878Sjulianstatic int
2091114878Sjulianng_btsocket_rfcomm_receive_disc(ng_btsocket_rfcomm_session_p s, int dlci)
2092114878Sjulian{
2093114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
2094114878Sjulian	int				error = 0;
2095114878Sjulian
2096114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2097114878Sjulian
2098114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2099114878Sjulian"%s: Got DISC, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
2100114878Sjulian		__func__, s->state, s->flags, s->mtu, dlci);
2101114878Sjulian
2102114878Sjulian	/* DLCI == 0 means close multiplexor channel */
2103114878Sjulian	if (dlci == 0) {
2104114878Sjulian		/* XXX FIXME assume that remote side will close the socket */
2105114878Sjulian		error = ng_btsocket_rfcomm_send_command(s, RFCOMM_FRAME_UA, 0);
2106128591Semax		if (error == 0) {
2107128591Semax			if (s->state == NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING)
2108128591Semax				s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED; /* XXX */
2109128591Semax			else
2110128591Semax				s->state = NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING;
2111128591Semax		} else
2112114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED; /* XXX */
2113114878Sjulian
2114114878Sjulian		ng_btsocket_rfcomm_session_clean(s);
2115114878Sjulian	} else {
2116114878Sjulian		pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2117114878Sjulian		if (pcb != NULL) {
2118161623Semax			int	err;
2119114878Sjulian
2120114878Sjulian			mtx_lock(&pcb->pcb_mtx);
2121114878Sjulian
2122114878Sjulian			NG_BTSOCKET_RFCOMM_INFO(
2123114878Sjulian"%s: Got DISC for dlci=%d, state=%d, flags=%#x\n",
2124114878Sjulian				__func__, dlci, pcb->state, pcb->flags);
2125114878Sjulian
2126114878Sjulian			error = ng_btsocket_rfcomm_send_command(s,
2127114878Sjulian					RFCOMM_FRAME_UA, dlci);
2128114878Sjulian
2129114878Sjulian			if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_CONNECTED)
2130114878Sjulian				err = 0;
2131114878Sjulian			else
2132114878Sjulian				err = ECONNREFUSED;
2133114878Sjulian
2134161623Semax			ng_btsocket_rfcomm_pcb_kill(pcb, err);
2135114878Sjulian
2136114878Sjulian			mtx_unlock(&pcb->pcb_mtx);
2137114878Sjulian		} else {
2138114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2139114878Sjulian"%s: Got DISC for non-existing dlci=%d\n", __func__, dlci);
2140114878Sjulian
2141114878Sjulian			error = ng_btsocket_rfcomm_send_command(s,
2142114878Sjulian					RFCOMM_FRAME_DM, dlci);
2143114878Sjulian		}
2144114878Sjulian	}
2145114878Sjulian
2146114878Sjulian	return (error);
2147114878Sjulian} /* ng_btsocket_rfcomm_receive_disc */
2148114878Sjulian
2149114878Sjulian/*
2150114878Sjulian * Process RFCOMM UA frame
2151114878Sjulian */
2152114878Sjulian
2153114878Sjulianstatic int
2154114878Sjulianng_btsocket_rfcomm_receive_ua(ng_btsocket_rfcomm_session_p s, int dlci)
2155114878Sjulian{
2156114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
2157114878Sjulian	int				error = 0;
2158114878Sjulian
2159114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2160114878Sjulian
2161114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2162114878Sjulian"%s: Got UA, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
2163114878Sjulian		__func__, s->state, s->flags, s->mtu, dlci);
2164114878Sjulian
2165114878Sjulian	/* dlci == 0 means multiplexor channel */
2166114878Sjulian	if (dlci == 0) {
2167114878Sjulian		switch (s->state) {
2168114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
2169114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_OPEN;
2170114878Sjulian			ng_btsocket_rfcomm_connect_cfm(s);
2171114878Sjulian			break;
2172114878Sjulian
2173114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING:
2174114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
2175114878Sjulian			ng_btsocket_rfcomm_session_clean(s);
2176114878Sjulian			break;
2177114878Sjulian
2178114878Sjulian		default:
2179114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2180114878Sjulian"%s: Got UA for session in invalid state=%d(%d), flags=%#x, mtu=%d\n",
2181114878Sjulian				__func__, s->state, INITIATOR(s), s->flags,
2182114878Sjulian				s->mtu);
2183114878Sjulian			error = ENOENT;
2184114878Sjulian			break;
2185114878Sjulian		}
2186114878Sjulian
2187114878Sjulian		return (error);
2188114878Sjulian	}
2189114878Sjulian
2190114878Sjulian	/* Check if we have this DLCI */
2191114878Sjulian	pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2192114878Sjulian	if (pcb != NULL) {
2193114878Sjulian		mtx_lock(&pcb->pcb_mtx);
2194114878Sjulian
2195114878Sjulian		NG_BTSOCKET_RFCOMM_INFO(
2196114878Sjulian"%s: Got UA for dlci=%d, state=%d, flags=%#x\n",
2197114878Sjulian			__func__, dlci, pcb->state, pcb->flags);
2198114878Sjulian
2199114878Sjulian		switch (pcb->state) {
2200114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
2201114878Sjulian			ng_btsocket_rfcomm_untimeout(pcb);
2202114878Sjulian
2203114878Sjulian			error = ng_btsocket_rfcomm_send_msc(pcb);
2204114878Sjulian			if (error == 0) {
2205114878Sjulian				pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTED;
2206114878Sjulian				soisconnected(pcb->so);
2207114878Sjulian			}
2208114878Sjulian			break;
2209114878Sjulian
2210114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
2211161623Semax			ng_btsocket_rfcomm_pcb_kill(pcb, 0);
2212114878Sjulian			break;
2213114878Sjulian
2214114878Sjulian		default:
2215114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2216114878Sjulian"%s: Got UA for dlci=%d in invalid state=%d, flags=%#x\n",
2217114878Sjulian				__func__, dlci, pcb->state, pcb->flags);
2218114878Sjulian			error = ENOENT;
2219114878Sjulian			break;
2220114878Sjulian		}
2221114878Sjulian
2222114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
2223114878Sjulian	} else {
2224114878Sjulian		NG_BTSOCKET_RFCOMM_WARN(
2225114878Sjulian"%s: Got UA for non-existing dlci=%d\n", __func__, dlci);
2226114878Sjulian
2227114878Sjulian		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_DM,dlci);
2228114878Sjulian	}
2229114878Sjulian
2230114878Sjulian	return (error);
2231114878Sjulian} /* ng_btsocket_rfcomm_receive_ua */
2232114878Sjulian
2233114878Sjulian/*
2234114878Sjulian * Process RFCOMM DM frame
2235114878Sjulian */
2236114878Sjulian
2237114878Sjulianstatic int
2238114878Sjulianng_btsocket_rfcomm_receive_dm(ng_btsocket_rfcomm_session_p s, int dlci)
2239114878Sjulian{
2240114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
2241114878Sjulian	int				error;
2242114878Sjulian
2243114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2244114878Sjulian
2245114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2246114878Sjulian"%s: Got DM, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
2247114878Sjulian		__func__, s->state, s->flags, s->mtu, dlci);
2248114878Sjulian
2249114878Sjulian	/* DLCI == 0 means multiplexor channel */
2250114878Sjulian	if (dlci == 0) {
2251114878Sjulian		/* Disconnect all dlc's on the session */
2252114878Sjulian		s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
2253114878Sjulian		ng_btsocket_rfcomm_session_clean(s);
2254114878Sjulian	} else {
2255114878Sjulian		pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2256114878Sjulian		if (pcb != NULL) {
2257114878Sjulian			mtx_lock(&pcb->pcb_mtx);
2258114878Sjulian
2259114878Sjulian			NG_BTSOCKET_RFCOMM_INFO(
2260114878Sjulian"%s: Got DM for dlci=%d, state=%d, flags=%#x\n",
2261114878Sjulian				__func__, dlci, pcb->state, pcb->flags);
2262114878Sjulian
2263114878Sjulian			if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_CONNECTED)
2264114878Sjulian				error = ECONNRESET;
2265114878Sjulian			else
2266114878Sjulian				error = ECONNREFUSED;
2267114878Sjulian
2268161623Semax			ng_btsocket_rfcomm_pcb_kill(pcb, error);
2269114878Sjulian
2270114878Sjulian			mtx_unlock(&pcb->pcb_mtx);
2271114878Sjulian		} else
2272114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2273114878Sjulian"%s: Got DM for non-existing dlci=%d\n", __func__, dlci);
2274114878Sjulian	}
2275114878Sjulian
2276114878Sjulian	return (0);
2277114878Sjulian} /* ng_btsocket_rfcomm_receive_dm */
2278114878Sjulian
2279114878Sjulian/*
2280114878Sjulian * Process RFCOMM UIH frame (data)
2281114878Sjulian */
2282114878Sjulian
2283114878Sjulianstatic int
2284114878Sjulianng_btsocket_rfcomm_receive_uih(ng_btsocket_rfcomm_session_p s, int dlci,
2285114878Sjulian		int pf, struct mbuf *m0)
2286114878Sjulian{
2287114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
2288114878Sjulian	int				error = 0;
2289114878Sjulian
2290114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2291114878Sjulian
2292114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2293114878Sjulian"%s: Got UIH, session state=%d, flags=%#x, mtu=%d, dlci=%d, pf=%d, len=%d\n",
2294114878Sjulian		__func__, s->state, s->flags, s->mtu, dlci, pf,
2295114878Sjulian		m0->m_pkthdr.len);
2296114878Sjulian
2297114878Sjulian	/* XXX should we do it here? Check for session flow control */
2298114878Sjulian	if (s->flags & NG_BTSOCKET_RFCOMM_SESSION_LFC) {
2299114878Sjulian		NG_BTSOCKET_RFCOMM_WARN(
2300114878Sjulian"%s: Got UIH with session flow control asserted, state=%d, flags=%#x\n",
2301114878Sjulian			__func__, s->state, s->flags);
2302114878Sjulian		goto drop;
2303114878Sjulian	}
2304114878Sjulian
2305114878Sjulian	/* Check if we have this dlci */
2306114878Sjulian	pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2307114878Sjulian	if (pcb == NULL) {
2308114878Sjulian		NG_BTSOCKET_RFCOMM_WARN(
2309114878Sjulian"%s: Got UIH for non-existing dlci=%d\n", __func__, dlci);
2310114878Sjulian		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_DM,dlci);
2311114878Sjulian		goto drop;
2312114878Sjulian	}
2313114878Sjulian
2314114878Sjulian	mtx_lock(&pcb->pcb_mtx);
2315114878Sjulian
2316114878Sjulian	/* Check dlci state */
2317114878Sjulian	if (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTED) {
2318114878Sjulian		NG_BTSOCKET_RFCOMM_WARN(
2319114878Sjulian"%s: Got UIH for dlci=%d in invalid state=%d, flags=%#x\n",
2320114878Sjulian			__func__, dlci, pcb->state, pcb->flags);
2321114878Sjulian		error = EINVAL;
2322114878Sjulian		goto drop1;
2323114878Sjulian	}
2324114878Sjulian
2325114878Sjulian	/* Check dlci flow control */
2326114878Sjulian	if (((pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) && pcb->rx_cred <= 0) ||
2327114878Sjulian	     (pcb->lmodem & RFCOMM_MODEM_FC)) {
2328114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
2329114878Sjulian"%s: Got UIH for dlci=%d with asserted flow control, state=%d, " \
2330114878Sjulian"flags=%#x, rx_cred=%d, lmodem=%#x\n",
2331114878Sjulian			__func__, dlci, pcb->state, pcb->flags,
2332114878Sjulian			pcb->rx_cred, pcb->lmodem);
2333114878Sjulian		goto drop1;
2334114878Sjulian	}
2335114878Sjulian
2336114878Sjulian	/* Did we get any credits? */
2337114878Sjulian	if ((pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) && pf) {
2338114878Sjulian		NG_BTSOCKET_RFCOMM_INFO(
2339114878Sjulian"%s: Got %d more credits for dlci=%d, state=%d, flags=%#x, " \
2340114878Sjulian"rx_cred=%d, tx_cred=%d\n",
2341114878Sjulian			__func__, *mtod(m0, u_int8_t *), dlci, pcb->state,
2342114878Sjulian			pcb->flags, pcb->rx_cred, pcb->tx_cred);
2343114878Sjulian
2344114878Sjulian		pcb->tx_cred += *mtod(m0, u_int8_t *);
2345114878Sjulian		m_adj(m0, 1);
2346114878Sjulian
2347114878Sjulian		/* Send more from the DLC. XXX check for errors? */
2348114878Sjulian		ng_btsocket_rfcomm_pcb_send(pcb, ALOT);
2349114878Sjulian	}
2350114878Sjulian
2351114878Sjulian	/* OK the of the rest of the mbuf is the data */
2352114878Sjulian	if (m0->m_pkthdr.len > 0) {
2353114878Sjulian		/* If we are using credit flow control decrease rx_cred here */
2354114878Sjulian		if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) {
2355114878Sjulian			/* Give remote peer more credits (if needed) */
2356114878Sjulian			if (-- pcb->rx_cred <= RFCOMM_MAX_CREDITS / 2)
2357114878Sjulian				ng_btsocket_rfcomm_send_credits(pcb);
2358114878Sjulian			else
2359114878Sjulian				NG_BTSOCKET_RFCOMM_INFO(
2360114878Sjulian"%s: Remote side still has credits, dlci=%d, state=%d, flags=%#x, " \
2361114878Sjulian"rx_cred=%d, tx_cred=%d\n",		__func__, dlci, pcb->state, pcb->flags,
2362114878Sjulian					pcb->rx_cred, pcb->tx_cred);
2363114878Sjulian		}
2364114878Sjulian
2365114878Sjulian		/* Check packet against mtu on dlci */
2366114878Sjulian		if (m0->m_pkthdr.len > pcb->mtu) {
2367114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
2368114878Sjulian"%s: Got oversized UIH for dlci=%d, state=%d, flags=%#x, mtu=%d, len=%d\n",
2369114878Sjulian				__func__, dlci, pcb->state, pcb->flags,
2370114878Sjulian				pcb->mtu, m0->m_pkthdr.len);
2371114878Sjulian
2372114878Sjulian			error = EMSGSIZE;
2373114878Sjulian		} else if (m0->m_pkthdr.len > sbspace(&pcb->so->so_rcv)) {
2374114878Sjulian
2375114878Sjulian			/*
2376114878Sjulian			 * This is really bad. Receive queue on socket does
2377114878Sjulian			 * not have enough space for the packet. We do not
2378114878Sjulian			 * have any other choice but drop the packet.
2379114878Sjulian			 */
2380114878Sjulian
2381114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
2382114878Sjulian"%s: Not enough space in socket receive queue. Dropping UIH for dlci=%d, " \
2383114878Sjulian"state=%d, flags=%#x, len=%d, space=%ld\n",
2384114878Sjulian				__func__, dlci, pcb->state, pcb->flags,
2385114878Sjulian				m0->m_pkthdr.len, sbspace(&pcb->so->so_rcv));
2386114878Sjulian
2387114878Sjulian			error = ENOBUFS;
2388114878Sjulian		} else {
2389114878Sjulian			/* Append packet to the socket receive queue */
2390114878Sjulian			sbappend(&pcb->so->so_rcv, m0);
2391114878Sjulian			m0 = NULL;
2392114878Sjulian
2393114878Sjulian			sorwakeup(pcb->so);
2394114878Sjulian		}
2395114878Sjulian	}
2396114878Sjuliandrop1:
2397114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
2398114878Sjuliandrop:
2399114878Sjulian	NG_FREE_M(m0); /* checks for != NULL */
2400114878Sjulian
2401114878Sjulian	return (error);
2402114878Sjulian} /* ng_btsocket_rfcomm_receive_uih */
2403114878Sjulian
2404114878Sjulian/*
2405114878Sjulian * Process RFCOMM MCC command (Multiplexor)
2406114878Sjulian *
2407114878Sjulian * From TS 07.10 spec
2408114878Sjulian *
2409114878Sjulian * "5.4.3.1 Information Data
2410114878Sjulian *
2411114878Sjulian *  ...The frames (UIH) sent by the initiating station have the C/R bit set
2412114878Sjulian *  to 1 and those sent by the responding station have the C/R bit set to 0..."
2413114878Sjulian *
2414114878Sjulian * "5.4.6.2 Operating procedures
2415114878Sjulian *
2416114878Sjulian *  Messages always exist in pairs; a command message and a corresponding
2417114878Sjulian *  response message. If the C/R bit is set to 1 the message is a command,
2418114878Sjulian *  if it is set to 0 the message is a response...
2419114878Sjulian *
2420114878Sjulian *  ...
2421114878Sjulian *
2422114878Sjulian *  NOTE: Notice that when UIH frames are used to convey information on DLCI 0
2423114878Sjulian *  there are at least two different fields that contain a C/R bit, and the
2424114878Sjulian *  bits are set of different form. The C/R bit in the Type field shall be set
2425114878Sjulian *  as it is stated above, while the C/R bit in the Address field (see subclause
2426114878Sjulian *  5.2.1.2) shall be set as it is described in subclause 5.4.3.1."
2427114878Sjulian */
2428114878Sjulian
2429114878Sjulianstatic int
2430114878Sjulianng_btsocket_rfcomm_receive_mcc(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2431114878Sjulian{
2432114878Sjulian	struct rfcomm_mcc_hdr	*hdr = NULL;
2433114878Sjulian	u_int8_t		 cr, type, length;
2434114878Sjulian
2435114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2436114878Sjulian
2437114878Sjulian	/*
2438114878Sjulian	 * We can access data directly in the first mbuf, because we have
2439114878Sjulian	 * m_pullup()'ed mbuf chain in ng_btsocket_rfcomm_receive_frame().
2440114878Sjulian	 * All MCC commands should fit into single mbuf (except probably TEST).
2441114878Sjulian	 */
2442114878Sjulian
2443114878Sjulian	hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2444114878Sjulian	cr = RFCOMM_CR(hdr->type);
2445114878Sjulian	type = RFCOMM_MCC_TYPE(hdr->type);
2446114878Sjulian	length = RFCOMM_MCC_LENGTH(hdr->length);
2447114878Sjulian
2448114878Sjulian	/* Check MCC frame length */
2449114878Sjulian	if (sizeof(*hdr) + length != m0->m_pkthdr.len) {
2450114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
2451114878Sjulian"%s: Invalid MCC frame length=%d, len=%d\n",
2452114878Sjulian			__func__, length, m0->m_pkthdr.len);
2453114878Sjulian		NG_FREE_M(m0);
2454114878Sjulian
2455114878Sjulian		return (EMSGSIZE);
2456114878Sjulian	}
2457114878Sjulian
2458114878Sjulian	switch (type) {
2459114878Sjulian	case RFCOMM_MCC_TEST:
2460114878Sjulian		return (ng_btsocket_rfcomm_receive_test(s, m0));
2461114878Sjulian		/* NOT REACHED */
2462114878Sjulian
2463114878Sjulian	case RFCOMM_MCC_FCON:
2464114878Sjulian	case RFCOMM_MCC_FCOFF:
2465114878Sjulian		return (ng_btsocket_rfcomm_receive_fc(s, m0));
2466114878Sjulian		/* NOT REACHED */
2467114878Sjulian
2468114878Sjulian	case RFCOMM_MCC_MSC:
2469114878Sjulian		return (ng_btsocket_rfcomm_receive_msc(s, m0));
2470114878Sjulian		/* NOT REACHED */
2471114878Sjulian
2472114878Sjulian	case RFCOMM_MCC_RPN:
2473114878Sjulian		return (ng_btsocket_rfcomm_receive_rpn(s, m0));
2474114878Sjulian		/* NOT REACHED */
2475114878Sjulian
2476114878Sjulian	case RFCOMM_MCC_RLS:
2477114878Sjulian		return (ng_btsocket_rfcomm_receive_rls(s, m0));
2478114878Sjulian		/* NOT REACHED */
2479114878Sjulian
2480114878Sjulian	case RFCOMM_MCC_PN:
2481114878Sjulian		return (ng_btsocket_rfcomm_receive_pn(s, m0));
2482114878Sjulian		/* NOT REACHED */
2483114878Sjulian
2484114878Sjulian	case RFCOMM_MCC_NSC:
2485114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
2486114878Sjulian"%s: Got MCC NSC, type=%#x, cr=%d, length=%d, session state=%d, flags=%#x, " \
2487114878Sjulian"mtu=%d, len=%d\n",	__func__, RFCOMM_MCC_TYPE(*((u_int8_t *)(hdr + 1))), cr,
2488114878Sjulian			 length, s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2489114878Sjulian		NG_FREE_M(m0);
2490114878Sjulian		break;
2491114878Sjulian
2492114878Sjulian	default:
2493114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
2494114878Sjulian"%s: Got unknown MCC, type=%#x, cr=%d, length=%d, session state=%d, " \
2495114878Sjulian"flags=%#x, mtu=%d, len=%d\n",
2496114878Sjulian			__func__, type, cr, length, s->state, s->flags,
2497114878Sjulian			s->mtu, m0->m_pkthdr.len);
2498114878Sjulian
2499114878Sjulian		/* Reuse mbuf to send NSC */
2500114878Sjulian		hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2501114878Sjulian		m0->m_pkthdr.len = m0->m_len = sizeof(*hdr);
2502114878Sjulian
2503114878Sjulian		/* Create MCC NSC header */
2504114878Sjulian		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_NSC);
2505114878Sjulian		hdr->length = RFCOMM_MKLEN8(1);
2506114878Sjulian
2507114878Sjulian		/* Put back MCC command type we did not like */
2508114878Sjulian		m0->m_data[m0->m_len] = RFCOMM_MKMCC_TYPE(cr, type);
2509114878Sjulian		m0->m_pkthdr.len ++;
2510114878Sjulian		m0->m_len ++;
2511114878Sjulian
2512114878Sjulian		/* Send UIH frame */
2513114878Sjulian		return (ng_btsocket_rfcomm_send_uih(s,
2514114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0));
2515114878Sjulian		/* NOT REACHED */
2516114878Sjulian	}
2517114878Sjulian
2518114878Sjulian	return (0);
2519114878Sjulian} /* ng_btsocket_rfcomm_receive_mcc */
2520114878Sjulian
2521114878Sjulian/*
2522114878Sjulian * Receive RFCOMM TEST MCC command
2523114878Sjulian */
2524114878Sjulian
2525114878Sjulianstatic int
2526114878Sjulianng_btsocket_rfcomm_receive_test(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2527114878Sjulian{
2528114878Sjulian	struct rfcomm_mcc_hdr	*hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2529114878Sjulian	int			 error = 0;
2530114878Sjulian
2531114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2532114878Sjulian
2533114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2534114878Sjulian"%s: Got MCC TEST, cr=%d, length=%d, session state=%d, flags=%#x, mtu=%d, " \
2535114878Sjulian"len=%d\n",	__func__, RFCOMM_CR(hdr->type), RFCOMM_MCC_LENGTH(hdr->length),
2536114878Sjulian		s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2537114878Sjulian
2538114878Sjulian	if (RFCOMM_CR(hdr->type)) {
2539114878Sjulian		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_TEST);
2540114878Sjulian		error = ng_btsocket_rfcomm_send_uih(s,
2541114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2542114878Sjulian	} else
2543114878Sjulian		NG_FREE_M(m0); /* XXX ignore response */
2544114878Sjulian
2545114878Sjulian	return (error);
2546114878Sjulian} /* ng_btsocket_rfcomm_receive_test */
2547114878Sjulian
2548114878Sjulian/*
2549114878Sjulian * Receive RFCOMM FCON/FCOFF MCC command
2550114878Sjulian */
2551114878Sjulian
2552114878Sjulianstatic int
2553114878Sjulianng_btsocket_rfcomm_receive_fc(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2554114878Sjulian{
2555114878Sjulian	struct rfcomm_mcc_hdr	*hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2556114878Sjulian	u_int8_t		 type = RFCOMM_MCC_TYPE(hdr->type);
2557114878Sjulian	int			 error = 0;
2558114878Sjulian
2559114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2560114878Sjulian
2561114878Sjulian	/*
2562114878Sjulian	 * Turn ON/OFF aggregate flow on the entire session. When remote peer
2563114878Sjulian	 * asserted flow control no transmission shall occur except on dlci 0
2564114878Sjulian	 * (control channel).
2565114878Sjulian	 */
2566114878Sjulian
2567114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2568114878Sjulian"%s: Got MCC FC%s, cr=%d, length=%d, session state=%d, flags=%#x, mtu=%d, " \
2569114878Sjulian"len=%d\n",	__func__, (type == RFCOMM_MCC_FCON)? "ON" : "OFF",
2570114878Sjulian		RFCOMM_CR(hdr->type), RFCOMM_MCC_LENGTH(hdr->length),
2571114878Sjulian		s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2572114878Sjulian
2573114878Sjulian	if (RFCOMM_CR(hdr->type)) {
2574114878Sjulian		if (type == RFCOMM_MCC_FCON)
2575114878Sjulian			s->flags &= ~NG_BTSOCKET_RFCOMM_SESSION_RFC;
2576114878Sjulian		else
2577114878Sjulian			s->flags |= NG_BTSOCKET_RFCOMM_SESSION_RFC;
2578114878Sjulian
2579114878Sjulian		hdr->type = RFCOMM_MKMCC_TYPE(0, type);
2580114878Sjulian		error = ng_btsocket_rfcomm_send_uih(s,
2581114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2582114878Sjulian	} else
2583114878Sjulian		NG_FREE_M(m0); /* XXX ignore response */
2584114878Sjulian
2585114878Sjulian	return (error);
2586114878Sjulian} /* ng_btsocket_rfcomm_receive_fc  */
2587114878Sjulian
2588114878Sjulian/*
2589114878Sjulian * Receive RFCOMM MSC MCC command
2590114878Sjulian */
2591114878Sjulian
2592114878Sjulianstatic int
2593114878Sjulianng_btsocket_rfcomm_receive_msc(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2594114878Sjulian{
2595114878Sjulian	struct rfcomm_mcc_hdr		*hdr = mtod(m0, struct rfcomm_mcc_hdr*);
2596114878Sjulian	struct rfcomm_mcc_msc		*msc = (struct rfcomm_mcc_msc *)(hdr+1);
2597114878Sjulian	ng_btsocket_rfcomm_pcb_t	*pcb = NULL;
2598114878Sjulian	int				 error = 0;
2599114878Sjulian
2600114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2601114878Sjulian
2602114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2603114878Sjulian"%s: Got MCC MSC, dlci=%d, cr=%d, length=%d, session state=%d, flags=%#x, " \
2604114878Sjulian"mtu=%d, len=%d\n",
2605114878Sjulian		__func__,  RFCOMM_DLCI(msc->address), RFCOMM_CR(hdr->type),
2606114878Sjulian		RFCOMM_MCC_LENGTH(hdr->length), s->state, s->flags,
2607114878Sjulian		s->mtu, m0->m_pkthdr.len);
2608114878Sjulian
2609114878Sjulian	if (RFCOMM_CR(hdr->type)) {
2610114878Sjulian		pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, RFCOMM_DLCI(msc->address));
2611114878Sjulian		if (pcb == NULL) {
2612114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2613114878Sjulian"%s: Got MSC command for non-existing dlci=%d\n",
2614114878Sjulian				__func__, RFCOMM_DLCI(msc->address));
2615114878Sjulian			NG_FREE_M(m0);
2616114878Sjulian
2617114878Sjulian			return (ENOENT);
2618114878Sjulian		}
2619114878Sjulian
2620114878Sjulian		mtx_lock(&pcb->pcb_mtx);
2621114878Sjulian
2622114878Sjulian		if (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTING &&
2623114878Sjulian		    pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTED) {
2624114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2625114878Sjulian"%s: Got MSC on dlci=%d in invalid state=%d\n",
2626114878Sjulian				__func__, RFCOMM_DLCI(msc->address),
2627114878Sjulian				pcb->state);
2628114878Sjulian
2629114878Sjulian			mtx_unlock(&pcb->pcb_mtx);
2630114878Sjulian			NG_FREE_M(m0);
2631114878Sjulian
2632114878Sjulian			return (EINVAL);
2633114878Sjulian		}
2634114878Sjulian
2635114878Sjulian		pcb->rmodem = msc->modem; /* Update remote port signals */
2636114878Sjulian
2637114878Sjulian		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_MSC);
2638114878Sjulian		error = ng_btsocket_rfcomm_send_uih(s,
2639114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2640114878Sjulian
2641114878Sjulian#if 0 /* YYY */
2642114878Sjulian		/* Send more data from DLC. XXX check for errors? */
2643114878Sjulian		if (!(pcb->rmodem & RFCOMM_MODEM_FC) &&
2644114878Sjulian		    !(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC))
2645114878Sjulian			ng_btsocket_rfcomm_pcb_send(pcb, ALOT);
2646114878Sjulian#endif /* YYY */
2647114878Sjulian
2648114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
2649114878Sjulian	} else
2650114878Sjulian		NG_FREE_M(m0); /* XXX ignore response */
2651114878Sjulian
2652114878Sjulian	return (error);
2653114878Sjulian} /* ng_btsocket_rfcomm_receive_msc */
2654114878Sjulian
2655114878Sjulian/*
2656114878Sjulian * Receive RFCOMM RPN MCC command
2657114878Sjulian * XXX FIXME do we need htole16/le16toh for RPN param_mask?
2658114878Sjulian */
2659114878Sjulian
2660114878Sjulianstatic int
2661114878Sjulianng_btsocket_rfcomm_receive_rpn(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2662114878Sjulian{
2663114878Sjulian	struct rfcomm_mcc_hdr	*hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2664114878Sjulian	struct rfcomm_mcc_rpn	*rpn = (struct rfcomm_mcc_rpn *)(hdr + 1);
2665114878Sjulian	int			 error = 0;
2666114878Sjulian	u_int16_t		 param_mask;
2667114878Sjulian	u_int8_t		 bit_rate, data_bits, stop_bits, parity,
2668114878Sjulian				 flow_control, xon_char, xoff_char;
2669114878Sjulian
2670114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2671114878Sjulian
2672114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2673114878Sjulian"%s: Got MCC RPN, dlci=%d, cr=%d, length=%d, session state=%d, flags=%#x, " \
2674114878Sjulian"mtu=%d, len=%d\n",
2675114878Sjulian		__func__, RFCOMM_DLCI(rpn->dlci), RFCOMM_CR(hdr->type),
2676114878Sjulian		RFCOMM_MCC_LENGTH(hdr->length), s->state, s->flags,
2677114878Sjulian		s->mtu, m0->m_pkthdr.len);
2678114878Sjulian
2679114878Sjulian	if (RFCOMM_CR(hdr->type)) {
2680114878Sjulian		param_mask = RFCOMM_RPN_PM_ALL;
2681114878Sjulian
2682114878Sjulian		if (RFCOMM_MCC_LENGTH(hdr->length) == 1) {
2683114878Sjulian			/* Request - return default setting */
2684114878Sjulian			bit_rate = RFCOMM_RPN_BR_115200;
2685114878Sjulian			data_bits = RFCOMM_RPN_DATA_8;
2686114878Sjulian			stop_bits = RFCOMM_RPN_STOP_1;
2687114878Sjulian			parity = RFCOMM_RPN_PARITY_NONE;
2688114878Sjulian			flow_control = RFCOMM_RPN_FLOW_NONE;
2689114878Sjulian			xon_char = RFCOMM_RPN_XON_CHAR;
2690114878Sjulian			xoff_char = RFCOMM_RPN_XOFF_CHAR;
2691114878Sjulian                } else {
2692114878Sjulian			/*
2693114878Sjulian			 * Ignore/accept bit_rate, 8 bits, 1 stop bit, no
2694114878Sjulian			 * parity, no flow control lines, default XON/XOFF
2695114878Sjulian			 * chars.
2696114878Sjulian			 */
2697114878Sjulian
2698114878Sjulian			bit_rate = rpn->bit_rate;
2699114878Sjulian			rpn->param_mask = le16toh(rpn->param_mask); /* XXX */
2700114878Sjulian
2701114878Sjulian			data_bits = RFCOMM_RPN_DATA_BITS(rpn->line_settings);
2702114878Sjulian			if (rpn->param_mask & RFCOMM_RPN_PM_DATA &&
2703114878Sjulian			    data_bits != RFCOMM_RPN_DATA_8) {
2704114878Sjulian				data_bits = RFCOMM_RPN_DATA_8;
2705114878Sjulian				param_mask ^= RFCOMM_RPN_PM_DATA;
2706114878Sjulian			}
2707114878Sjulian
2708114878Sjulian			stop_bits = RFCOMM_RPN_STOP_BITS(rpn->line_settings);
2709114878Sjulian			if (rpn->param_mask & RFCOMM_RPN_PM_STOP &&
2710114878Sjulian			    stop_bits != RFCOMM_RPN_STOP_1) {
2711114878Sjulian				stop_bits = RFCOMM_RPN_STOP_1;
2712114878Sjulian				param_mask ^= RFCOMM_RPN_PM_STOP;
2713114878Sjulian			}
2714114878Sjulian
2715114878Sjulian			parity = RFCOMM_RPN_PARITY(rpn->line_settings);
2716114878Sjulian			if (rpn->param_mask & RFCOMM_RPN_PM_PARITY &&
2717114878Sjulian			    parity != RFCOMM_RPN_PARITY_NONE) {
2718114878Sjulian				parity = RFCOMM_RPN_PARITY_NONE;
2719114878Sjulian				param_mask ^= RFCOMM_RPN_PM_PARITY;
2720114878Sjulian			}
2721114878Sjulian
2722114878Sjulian			flow_control = rpn->flow_control;
2723114878Sjulian			if (rpn->param_mask & RFCOMM_RPN_PM_FLOW &&
2724114878Sjulian			    flow_control != RFCOMM_RPN_FLOW_NONE) {
2725114878Sjulian				flow_control = RFCOMM_RPN_FLOW_NONE;
2726114878Sjulian				param_mask ^= RFCOMM_RPN_PM_FLOW;
2727114878Sjulian			}
2728114878Sjulian
2729114878Sjulian			xon_char = rpn->xon_char;
2730114878Sjulian			if (rpn->param_mask & RFCOMM_RPN_PM_XON &&
2731114878Sjulian			    xon_char != RFCOMM_RPN_XON_CHAR) {
2732114878Sjulian				xon_char = RFCOMM_RPN_XON_CHAR;
2733114878Sjulian				param_mask ^= RFCOMM_RPN_PM_XON;
2734114878Sjulian			}
2735114878Sjulian
2736114878Sjulian			xoff_char = rpn->xoff_char;
2737114878Sjulian			if (rpn->param_mask & RFCOMM_RPN_PM_XOFF &&
2738114878Sjulian			    xoff_char != RFCOMM_RPN_XOFF_CHAR) {
2739114878Sjulian				xoff_char = RFCOMM_RPN_XOFF_CHAR;
2740114878Sjulian				param_mask ^= RFCOMM_RPN_PM_XOFF;
2741114878Sjulian			}
2742114878Sjulian		}
2743114878Sjulian
2744114878Sjulian		rpn->bit_rate = bit_rate;
2745114878Sjulian		rpn->line_settings = RFCOMM_MKRPN_LINE_SETTINGS(data_bits,
2746114878Sjulian						stop_bits, parity);
2747114878Sjulian		rpn->flow_control = flow_control;
2748114878Sjulian		rpn->xon_char = xon_char;
2749114878Sjulian		rpn->xoff_char = xoff_char;
2750114878Sjulian		rpn->param_mask = htole16(param_mask); /* XXX */
2751114878Sjulian
2752114878Sjulian		m0->m_pkthdr.len = m0->m_len = sizeof(*hdr) + sizeof(*rpn);
2753114878Sjulian
2754114878Sjulian		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_RPN);
2755114878Sjulian		error = ng_btsocket_rfcomm_send_uih(s,
2756114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2757114878Sjulian	} else
2758114878Sjulian		NG_FREE_M(m0); /* XXX ignore response */
2759114878Sjulian
2760114878Sjulian	return (error);
2761114878Sjulian} /* ng_btsocket_rfcomm_receive_rpn */
2762114878Sjulian
2763114878Sjulian/*
2764114878Sjulian * Receive RFCOMM RLS MCC command
2765114878Sjulian */
2766114878Sjulian
2767114878Sjulianstatic int
2768114878Sjulianng_btsocket_rfcomm_receive_rls(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2769114878Sjulian{
2770114878Sjulian	struct rfcomm_mcc_hdr	*hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2771114878Sjulian	struct rfcomm_mcc_rls	*rls = (struct rfcomm_mcc_rls *)(hdr + 1);
2772114878Sjulian	int			 error = 0;
2773114878Sjulian
2774114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2775114878Sjulian
2776114878Sjulian	/*
2777114878Sjulian	 * XXX FIXME Do we have to do anything else here? Remote peer tries to
2778114878Sjulian	 * tell us something about DLCI. Just report what we have received and
2779114878Sjulian	 * return back received values as required by TS 07.10 spec.
2780114878Sjulian	 */
2781114878Sjulian
2782114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2783114878Sjulian"%s: Got MCC RLS, dlci=%d, status=%#x, cr=%d, length=%d, session state=%d, " \
2784114878Sjulian"flags=%#x, mtu=%d, len=%d\n",
2785114878Sjulian		__func__, RFCOMM_DLCI(rls->address), rls->status,
2786114878Sjulian		RFCOMM_CR(hdr->type), RFCOMM_MCC_LENGTH(hdr->length),
2787114878Sjulian		s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2788114878Sjulian
2789114878Sjulian	if (RFCOMM_CR(hdr->type)) {
2790114878Sjulian		if (rls->status & 0x1)
2791114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
2792114878Sjulian"%s: Got RLS dlci=%d, error=%#x\n", __func__, RFCOMM_DLCI(rls->address),
2793114878Sjulian				rls->status >> 1);
2794114878Sjulian
2795114878Sjulian		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_RLS);
2796114878Sjulian		error = ng_btsocket_rfcomm_send_uih(s,
2797114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2798114878Sjulian	} else
2799114878Sjulian		NG_FREE_M(m0); /* XXX ignore responses */
2800114878Sjulian
2801114878Sjulian	return (error);
2802114878Sjulian} /* ng_btsocket_rfcomm_receive_rls */
2803114878Sjulian
2804114878Sjulian/*
2805114878Sjulian * Receive RFCOMM PN MCC command
2806114878Sjulian */
2807114878Sjulian
2808114878Sjulianstatic int
2809114878Sjulianng_btsocket_rfcomm_receive_pn(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2810114878Sjulian{
2811114878Sjulian	struct rfcomm_mcc_hdr		*hdr = mtod(m0, struct rfcomm_mcc_hdr*);
2812114878Sjulian	struct rfcomm_mcc_pn		*pn = (struct rfcomm_mcc_pn *)(hdr+1);
2813114878Sjulian	ng_btsocket_rfcomm_pcb_t	*pcb = NULL;
2814114878Sjulian	int				 error = 0;
2815114878Sjulian
2816114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2817114878Sjulian
2818114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2819114878Sjulian"%s: Got MCC PN, dlci=%d, cr=%d, length=%d, flow_control=%#x, priority=%d, " \
2820114878Sjulian"ack_timer=%d, mtu=%d, max_retrans=%d, credits=%d, session state=%d, " \
2821114878Sjulian"flags=%#x, session mtu=%d, len=%d\n",
2822114878Sjulian		__func__, pn->dlci, RFCOMM_CR(hdr->type),
2823114878Sjulian		RFCOMM_MCC_LENGTH(hdr->length), pn->flow_control, pn->priority,
2824114878Sjulian		pn->ack_timer, le16toh(pn->mtu), pn->max_retrans, pn->credits,
2825114878Sjulian		s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2826114878Sjulian
2827114878Sjulian	if (pn->dlci == 0) {
2828114878Sjulian		NG_BTSOCKET_RFCOMM_ERR("%s: Zero dlci in MCC PN\n", __func__);
2829114878Sjulian		NG_FREE_M(m0);
2830114878Sjulian
2831114878Sjulian		return (EINVAL);
2832114878Sjulian	}
2833114878Sjulian
2834114878Sjulian	/* Check if we have this dlci */
2835114878Sjulian	pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, pn->dlci);
2836114878Sjulian	if (pcb != NULL) {
2837114878Sjulian		mtx_lock(&pcb->pcb_mtx);
2838114878Sjulian
2839114878Sjulian		if (RFCOMM_CR(hdr->type)) {
2840114878Sjulian			/* PN Request */
2841114878Sjulian			ng_btsocket_rfcomm_set_pn(pcb, 1, pn->flow_control,
2842114878Sjulian				pn->credits, pn->mtu);
2843114878Sjulian
2844114878Sjulian			if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) {
2845114878Sjulian				pn->flow_control = 0xe0;
2846114878Sjulian				pn->credits = RFCOMM_DEFAULT_CREDITS;
2847114878Sjulian			} else {
2848114878Sjulian				pn->flow_control = 0;
2849114878Sjulian				pn->credits = 0;
2850114878Sjulian			}
2851114878Sjulian
2852114878Sjulian			hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_PN);
2853114878Sjulian			error = ng_btsocket_rfcomm_send_uih(s,
2854114878Sjulian					RFCOMM_MKADDRESS(INITIATOR(s), 0),
2855114878Sjulian					0, 0, m0);
2856114878Sjulian		} else {
2857114878Sjulian			/* PN Response - proceed with SABM. Timeout still set */
2858114878Sjulian			if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_CONFIGURING) {
2859114878Sjulian				ng_btsocket_rfcomm_set_pn(pcb, 0,
2860114878Sjulian					pn->flow_control, pn->credits, pn->mtu);
2861114878Sjulian
2862114878Sjulian				pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTING;
2863114878Sjulian				error = ng_btsocket_rfcomm_send_command(s,
2864114878Sjulian						RFCOMM_FRAME_SABM, pn->dlci);
2865114878Sjulian			} else
2866114878Sjulian				NG_BTSOCKET_RFCOMM_WARN(
2867114878Sjulian"%s: Got PN response for dlci=%d in invalid state=%d\n",
2868114878Sjulian					__func__, pn->dlci, pcb->state);
2869114878Sjulian
2870114878Sjulian			NG_FREE_M(m0);
2871114878Sjulian		}
2872114878Sjulian
2873114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
2874114878Sjulian	} else if (RFCOMM_CR(hdr->type)) {
2875114878Sjulian		/* PN request to non-existing dlci - incomming connection */
2876114878Sjulian		pcb = ng_btsocket_rfcomm_connect_ind(s,
2877114878Sjulian				RFCOMM_SRVCHANNEL(pn->dlci));
2878114878Sjulian		if (pcb != NULL) {
2879114878Sjulian			mtx_lock(&pcb->pcb_mtx);
2880114878Sjulian
2881114878Sjulian			pcb->dlci = pn->dlci;
2882114878Sjulian
2883114878Sjulian			ng_btsocket_rfcomm_set_pn(pcb, 1, pn->flow_control,
2884114878Sjulian				pn->credits, pn->mtu);
2885114878Sjulian
2886114878Sjulian			if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) {
2887114878Sjulian				pn->flow_control = 0xe0;
2888114878Sjulian				pn->credits = RFCOMM_DEFAULT_CREDITS;
2889114878Sjulian			} else {
2890114878Sjulian				pn->flow_control = 0;
2891114878Sjulian				pn->credits = 0;
2892114878Sjulian			}
2893114878Sjulian
2894114878Sjulian			hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_PN);
2895114878Sjulian			error = ng_btsocket_rfcomm_send_uih(s,
2896114878Sjulian					RFCOMM_MKADDRESS(INITIATOR(s), 0),
2897114878Sjulian					0, 0, m0);
2898114878Sjulian
2899114878Sjulian			if (error == 0) {
2900114878Sjulian				ng_btsocket_rfcomm_timeout(pcb);
2901114878Sjulian				pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTING;
2902114878Sjulian				soisconnecting(pcb->so);
2903161623Semax			} else
2904161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, error);
2905114878Sjulian
2906114878Sjulian			mtx_unlock(&pcb->pcb_mtx);
2907114878Sjulian		} else {
2908114878Sjulian			/* Nobody is listen()ing on this channel */
2909114878Sjulian			error = ng_btsocket_rfcomm_send_command(s,
2910114878Sjulian					RFCOMM_FRAME_DM, pn->dlci);
2911114878Sjulian			NG_FREE_M(m0);
2912114878Sjulian		}
2913114878Sjulian	} else
2914114878Sjulian		NG_FREE_M(m0); /* XXX ignore response to non-existing dlci */
2915114878Sjulian
2916114878Sjulian	return (error);
2917114878Sjulian} /* ng_btsocket_rfcomm_receive_pn */
2918114878Sjulian
2919114878Sjulian/*
2920114878Sjulian * Set PN parameters for dlci. Caller must hold pcb->pcb_mtx.
2921114878Sjulian *
2922114878Sjulian * From Bluetooth spec.
2923114878Sjulian *
2924114878Sjulian * "... The CL1 - CL4 field is completely redefined. (In TS07.10 this defines
2925114878Sjulian *  the convergence layer to use, which is not applicable to RFCOMM. In RFCOMM,
2926114878Sjulian *  in Bluetooth versions up to 1.0B, this field was forced to 0).
2927114878Sjulian *
2928114878Sjulian *  In the PN request sent prior to a DLC establishment, this field must contain
2929114878Sjulian *  the value 15 (0xF), indicating support of credit based flow control in the
2930114878Sjulian *  sender. See Table 5.3 below. If the PN response contains any other value
2931114878Sjulian *  than 14 (0xE) in this field, it is inferred that the peer RFCOMM entity is
2932114878Sjulian *  not supporting the credit based flow control feature. (This is only possible
2933114878Sjulian *  if the peer RFCOMM implementation is only conforming to Bluetooth version
2934114878Sjulian *  1.0B.) If a PN request is sent on an already open DLC, then this field must
2935114878Sjulian *  contain the value zero; it is not possible to set initial credits  more
2936114878Sjulian *  than once per DLC activation. A responding implementation must set this
2937114878Sjulian *  field in the PN response to 14 (0xE), if (and only if) the value in the PN
2938114878Sjulian *  request was 15..."
2939114878Sjulian */
2940114878Sjulian
2941114878Sjulianstatic void
2942114878Sjulianng_btsocket_rfcomm_set_pn(ng_btsocket_rfcomm_pcb_p pcb, u_int8_t cr,
2943114878Sjulian		u_int8_t flow_control, u_int8_t credits, u_int16_t mtu)
2944114878Sjulian{
2945114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
2946114878Sjulian
2947114878Sjulian	pcb->mtu = le16toh(mtu);
2948114878Sjulian
2949114878Sjulian	if (cr) {
2950114878Sjulian		if (flow_control == 0xf0) {
2951114878Sjulian			pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_CFC;
2952114878Sjulian			pcb->tx_cred = credits;
2953114878Sjulian		} else {
2954114878Sjulian			pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_CFC;
2955114878Sjulian			pcb->tx_cred = 0;
2956114878Sjulian		}
2957114878Sjulian	} else {
2958114878Sjulian		if (flow_control == 0xe0) {
2959114878Sjulian			pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_CFC;
2960114878Sjulian			pcb->tx_cred = credits;
2961114878Sjulian		} else {
2962114878Sjulian			pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_CFC;
2963114878Sjulian			pcb->tx_cred = 0;
2964114878Sjulian		}
2965114878Sjulian	}
2966114878Sjulian
2967114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2968114878Sjulian"%s: cr=%d, dlci=%d, state=%d, flags=%#x, mtu=%d, rx_cred=%d, tx_cred=%d\n",
2969114878Sjulian		__func__, cr, pcb->dlci, pcb->state, pcb->flags, pcb->mtu,
2970114878Sjulian		pcb->rx_cred, pcb->tx_cred);
2971114878Sjulian} /* ng_btsocket_rfcomm_set_pn */
2972114878Sjulian
2973114878Sjulian/*
2974114878Sjulian * Send RFCOMM SABM/DISC/UA/DM frames. Caller must hold s->session_mtx
2975114878Sjulian */
2976114878Sjulian
2977114878Sjulianstatic int
2978114878Sjulianng_btsocket_rfcomm_send_command(ng_btsocket_rfcomm_session_p s,
2979114878Sjulian		u_int8_t type, u_int8_t dlci)
2980114878Sjulian{
2981114878Sjulian	struct rfcomm_cmd_hdr	*hdr = NULL;
2982114878Sjulian	struct mbuf		*m = NULL;
2983114878Sjulian	int			 cr;
2984114878Sjulian
2985114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2986114878Sjulian
2987114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2988114878Sjulian"%s: Sending command type %#x, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
2989114878Sjulian		__func__, type, s->state, s->flags, s->mtu, dlci);
2990114878Sjulian
2991114878Sjulian	switch (type) {
2992114878Sjulian	case RFCOMM_FRAME_SABM:
2993114878Sjulian	case RFCOMM_FRAME_DISC:
2994114878Sjulian		cr = INITIATOR(s);
2995114878Sjulian		break;
2996114878Sjulian
2997114878Sjulian	case RFCOMM_FRAME_UA:
2998114878Sjulian	case RFCOMM_FRAME_DM:
2999114878Sjulian		cr = !INITIATOR(s);
3000114878Sjulian		break;
3001114878Sjulian
3002114878Sjulian	default:
3003114878Sjulian		panic("%s: Invalid frame type=%#x\n", __func__, type);
3004114878Sjulian		return (EINVAL);
3005114878Sjulian		/* NOT REACHED */
3006114878Sjulian	}
3007114878Sjulian
3008114878Sjulian	MGETHDR(m, M_DONTWAIT, MT_DATA);
3009114878Sjulian	if (m == NULL)
3010114878Sjulian		return (ENOBUFS);
3011114878Sjulian
3012114878Sjulian	m->m_pkthdr.len = m->m_len = sizeof(*hdr);
3013114878Sjulian
3014114878Sjulian	hdr = mtod(m, struct rfcomm_cmd_hdr *);
3015114878Sjulian	hdr->address = RFCOMM_MKADDRESS(cr, dlci);
3016114878Sjulian	hdr->control = RFCOMM_MKCONTROL(type, 1);
3017114878Sjulian	hdr->length = RFCOMM_MKLEN8(0);
3018114878Sjulian	hdr->fcs = ng_btsocket_rfcomm_fcs3((u_int8_t *) hdr);
3019114878Sjulian
3020114878Sjulian	NG_BT_MBUFQ_ENQUEUE(&s->outq, m);
3021114878Sjulian
3022114878Sjulian	return (0);
3023114878Sjulian} /* ng_btsocket_rfcomm_send_command */
3024114878Sjulian
3025114878Sjulian/*
3026114878Sjulian * Send RFCOMM UIH frame. Caller must hold s->session_mtx
3027114878Sjulian */
3028114878Sjulian
3029114878Sjulianstatic int
3030114878Sjulianng_btsocket_rfcomm_send_uih(ng_btsocket_rfcomm_session_p s, u_int8_t address,
3031114878Sjulian		u_int8_t pf, u_int8_t credits, struct mbuf *data)
3032114878Sjulian{
3033114878Sjulian	struct rfcomm_frame_hdr	*hdr = NULL;
3034114878Sjulian	struct mbuf		*m = NULL, *mcrc = NULL;
3035114878Sjulian	u_int16_t		 length;
3036114878Sjulian
3037114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
3038114878Sjulian
3039114878Sjulian	MGETHDR(m, M_DONTWAIT, MT_DATA);
3040114878Sjulian	if (m == NULL) {
3041114878Sjulian		NG_FREE_M(data);
3042114878Sjulian		return (ENOBUFS);
3043114878Sjulian	}
3044114878Sjulian	m->m_pkthdr.len = m->m_len = sizeof(*hdr);
3045114878Sjulian
3046114878Sjulian	MGET(mcrc, M_DONTWAIT, MT_DATA);
3047114878Sjulian	if (mcrc == NULL) {
3048114878Sjulian		NG_FREE_M(data);
3049114878Sjulian		return (ENOBUFS);
3050114878Sjulian	}
3051114878Sjulian	mcrc->m_len = 1;
3052114878Sjulian
3053114878Sjulian	/* Fill UIH frame header */
3054114878Sjulian	hdr = mtod(m, struct rfcomm_frame_hdr *);
3055114878Sjulian	hdr->address = address;
3056114878Sjulian	hdr->control = RFCOMM_MKCONTROL(RFCOMM_FRAME_UIH, pf);
3057114878Sjulian
3058114878Sjulian	/* Calculate FCS */
3059114878Sjulian	mcrc->m_data[0] = ng_btsocket_rfcomm_fcs2((u_int8_t *) hdr);
3060114878Sjulian
3061114878Sjulian	/* Put length back */
3062114878Sjulian	length = (data != NULL)? data->m_pkthdr.len : 0;
3063114878Sjulian	if (length > 127) {
3064114878Sjulian		u_int16_t	l = htole16(RFCOMM_MKLEN16(length));
3065114878Sjulian
3066114878Sjulian		bcopy(&l, &hdr->length, sizeof(l));
3067114878Sjulian		m->m_pkthdr.len ++;
3068114878Sjulian		m->m_len ++;
3069114878Sjulian	} else
3070114878Sjulian		hdr->length = RFCOMM_MKLEN8(length);
3071114878Sjulian
3072114878Sjulian	if (pf) {
3073114878Sjulian		m->m_data[m->m_len] = credits;
3074114878Sjulian		m->m_pkthdr.len ++;
3075114878Sjulian		m->m_len ++;
3076114878Sjulian	}
3077114878Sjulian
3078114878Sjulian	/* Add payload */
3079114878Sjulian	if (data != NULL) {
3080114878Sjulian		m_cat(m, data);
3081114878Sjulian		m->m_pkthdr.len += length;
3082114878Sjulian	}
3083114878Sjulian
3084114878Sjulian	/* Put FCS back */
3085114878Sjulian	m_cat(m, mcrc);
3086114878Sjulian	m->m_pkthdr.len ++;
3087114878Sjulian
3088114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
3089114878Sjulian"%s: Sending UIH state=%d, flags=%#x, address=%d, length=%d, pf=%d, " \
3090114878Sjulian"credits=%d, len=%d\n",
3091114878Sjulian		__func__, s->state, s->flags, address, length, pf, credits,
3092114878Sjulian		m->m_pkthdr.len);
3093114878Sjulian
3094114878Sjulian	NG_BT_MBUFQ_ENQUEUE(&s->outq, m);
3095114878Sjulian
3096114878Sjulian	return (0);
3097114878Sjulian} /* ng_btsocket_rfcomm_send_uih */
3098114878Sjulian
3099114878Sjulian/*
3100114878Sjulian * Send MSC request. Caller must hold pcb->pcb_mtx and pcb->session->session_mtx
3101114878Sjulian */
3102114878Sjulian
3103114878Sjulianstatic int
3104114878Sjulianng_btsocket_rfcomm_send_msc(ng_btsocket_rfcomm_pcb_p pcb)
3105114878Sjulian{
3106114878Sjulian	struct mbuf		*m = NULL;
3107114878Sjulian	struct rfcomm_mcc_hdr	*hdr = NULL;
3108114878Sjulian	struct rfcomm_mcc_msc	*msc = NULL;
3109114878Sjulian
3110114878Sjulian	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3111114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3112114878Sjulian
3113114878Sjulian	MGETHDR(m, M_DONTWAIT, MT_DATA);
3114114878Sjulian	if (m == NULL)
3115114878Sjulian		return (ENOBUFS);
3116114878Sjulian
3117114878Sjulian	m->m_pkthdr.len = m->m_len = sizeof(*hdr) + sizeof(*msc);
3118114878Sjulian
3119114878Sjulian	hdr = mtod(m, struct rfcomm_mcc_hdr *);
3120114878Sjulian	msc = (struct rfcomm_mcc_msc *)(hdr + 1);
3121114878Sjulian
3122114878Sjulian	hdr->type = RFCOMM_MKMCC_TYPE(1, RFCOMM_MCC_MSC);
3123114878Sjulian	hdr->length = RFCOMM_MKLEN8(sizeof(*msc));
3124114878Sjulian
3125114878Sjulian	msc->address = RFCOMM_MKADDRESS(1, pcb->dlci);
3126114878Sjulian	msc->modem = pcb->lmodem;
3127114878Sjulian
3128114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
3129114878Sjulian"%s: Sending MSC dlci=%d, state=%d, flags=%#x, address=%d, modem=%#x\n",
3130114878Sjulian		__func__, pcb->dlci, pcb->state, pcb->flags, msc->address,
3131114878Sjulian		msc->modem);
3132114878Sjulian
3133114878Sjulian	return (ng_btsocket_rfcomm_send_uih(pcb->session,
3134114878Sjulian			RFCOMM_MKADDRESS(INITIATOR(pcb->session), 0), 0, 0, m));
3135114878Sjulian} /* ng_btsocket_rfcomm_send_msc */
3136114878Sjulian
3137114878Sjulian/*
3138114878Sjulian * Send PN request. Caller must hold pcb->pcb_mtx and pcb->session->session_mtx
3139114878Sjulian */
3140114878Sjulian
3141114878Sjulianstatic int
3142114878Sjulianng_btsocket_rfcomm_send_pn(ng_btsocket_rfcomm_pcb_p pcb)
3143114878Sjulian{
3144114878Sjulian	struct mbuf		*m = NULL;
3145114878Sjulian	struct rfcomm_mcc_hdr	*hdr = NULL;
3146114878Sjulian	struct rfcomm_mcc_pn	*pn = NULL;
3147114878Sjulian
3148114878Sjulian	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3149114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3150114878Sjulian
3151114878Sjulian	MGETHDR(m, M_DONTWAIT, MT_DATA);
3152114878Sjulian	if (m == NULL)
3153114878Sjulian		return (ENOBUFS);
3154114878Sjulian
3155114878Sjulian	m->m_pkthdr.len = m->m_len = sizeof(*hdr) + sizeof(*pn);
3156114878Sjulian
3157114878Sjulian	hdr = mtod(m, struct rfcomm_mcc_hdr *);
3158114878Sjulian	pn = (struct rfcomm_mcc_pn *)(hdr + 1);
3159114878Sjulian
3160114878Sjulian	hdr->type = RFCOMM_MKMCC_TYPE(1, RFCOMM_MCC_PN);
3161114878Sjulian	hdr->length = RFCOMM_MKLEN8(sizeof(*pn));
3162114878Sjulian
3163114878Sjulian	pn->dlci = pcb->dlci;
3164121054Semax
3165121054Semax	/*
3166121054Semax	 * Set default DLCI priority as described in GSM 07.10
3167121054Semax	 * (ETSI TS 101 369) clause 5.6 page 42
3168121054Semax	 */
3169121054Semax
3170121054Semax	pn->priority = (pcb->dlci < 56)? (((pcb->dlci >> 3) << 3) + 7) : 61;
3171114878Sjulian	pn->ack_timer = 0;
3172114878Sjulian	pn->mtu = htole16(pcb->mtu);
3173114878Sjulian	pn->max_retrans = 0;
3174114878Sjulian
3175114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) {
3176114878Sjulian		pn->flow_control = 0xf0;
3177114878Sjulian		pn->credits = pcb->rx_cred;
3178114878Sjulian	} else {
3179114878Sjulian		pn->flow_control = 0;
3180114878Sjulian		pn->credits = 0;
3181114878Sjulian	}
3182114878Sjulian
3183114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
3184114878Sjulian"%s: Sending PN dlci=%d, state=%d, flags=%#x, mtu=%d, flow_control=%#x, " \
3185114878Sjulian"credits=%d\n",	__func__, pcb->dlci, pcb->state, pcb->flags, pcb->mtu,
3186114878Sjulian		pn->flow_control, pn->credits);
3187114878Sjulian
3188114878Sjulian	return (ng_btsocket_rfcomm_send_uih(pcb->session,
3189114878Sjulian			RFCOMM_MKADDRESS(INITIATOR(pcb->session), 0), 0, 0, m));
3190114878Sjulian} /* ng_btsocket_rfcomm_send_pn */
3191114878Sjulian
3192114878Sjulian/*
3193114878Sjulian * Calculate and send credits based on available space in receive buffer
3194114878Sjulian */
3195114878Sjulian
3196114878Sjulianstatic int
3197114878Sjulianng_btsocket_rfcomm_send_credits(ng_btsocket_rfcomm_pcb_p pcb)
3198114878Sjulian{
3199114878Sjulian	int		error = 0;
3200114878Sjulian	u_int8_t	credits;
3201114878Sjulian
3202114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3203114878Sjulian	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3204114878Sjulian
3205114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
3206114878Sjulian"%s: Sending more credits, dlci=%d, state=%d, flags=%#x, mtu=%d, " \
3207114878Sjulian"space=%ld, tx_cred=%d, rx_cred=%d\n",
3208114878Sjulian		__func__, pcb->dlci, pcb->state, pcb->flags, pcb->mtu,
3209114878Sjulian		sbspace(&pcb->so->so_rcv), pcb->tx_cred, pcb->rx_cred);
3210114878Sjulian
3211114878Sjulian	credits = sbspace(&pcb->so->so_rcv) / pcb->mtu;
3212114878Sjulian	if (credits > 0) {
3213114878Sjulian		if (pcb->rx_cred + credits > RFCOMM_MAX_CREDITS)
3214114878Sjulian			credits = RFCOMM_MAX_CREDITS - pcb->rx_cred;
3215114878Sjulian
3216114878Sjulian		error = ng_btsocket_rfcomm_send_uih(
3217114878Sjulian				pcb->session,
3218114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(pcb->session),
3219114878Sjulian					pcb->dlci), 1, credits, NULL);
3220114878Sjulian		if (error == 0) {
3221114878Sjulian			pcb->rx_cred += credits;
3222114878Sjulian
3223114878Sjulian			NG_BTSOCKET_RFCOMM_INFO(
3224114878Sjulian"%s: Gave remote side %d more credits, dlci=%d, state=%d, flags=%#x, " \
3225114878Sjulian"rx_cred=%d, tx_cred=%d\n",	__func__, credits, pcb->dlci, pcb->state,
3226114878Sjulian				pcb->flags, pcb->rx_cred, pcb->tx_cred);
3227114878Sjulian		} else
3228114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
3229114878Sjulian"%s: Could not send credits, error=%d, dlci=%d, state=%d, flags=%#x, " \
3230114878Sjulian"mtu=%d, space=%ld, tx_cred=%d, rx_cred=%d\n",
3231114878Sjulian				__func__, error, pcb->dlci, pcb->state,
3232114878Sjulian				pcb->flags, pcb->mtu, sbspace(&pcb->so->so_rcv),
3233114878Sjulian				pcb->tx_cred, pcb->rx_cred);
3234114878Sjulian	}
3235114878Sjulian
3236114878Sjulian	return (error);
3237114878Sjulian} /* ng_btsocket_rfcomm_send_credits */
3238114878Sjulian
3239114878Sjulian/*****************************************************************************
3240114878Sjulian *****************************************************************************
3241114878Sjulian **                              RFCOMM DLCs
3242114878Sjulian *****************************************************************************
3243114878Sjulian *****************************************************************************/
3244114878Sjulian
3245114878Sjulian/*
3246114878Sjulian * Send data from socket send buffer
3247114878Sjulian * Caller must hold pcb->pcb_mtx and pcb->session->session_mtx
3248114878Sjulian */
3249114878Sjulian
3250114878Sjulianstatic int
3251114878Sjulianng_btsocket_rfcomm_pcb_send(ng_btsocket_rfcomm_pcb_p pcb, int limit)
3252114878Sjulian{
3253114878Sjulian	struct mbuf	*m = NULL;
3254114878Sjulian	int		 sent, length, error;
3255114878Sjulian
3256114878Sjulian	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3257114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3258114878Sjulian
3259114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC)
3260114878Sjulian		limit = min(limit, pcb->tx_cred);
3261114878Sjulian	else if (!(pcb->rmodem & RFCOMM_MODEM_FC))
3262114878Sjulian		limit = min(limit, RFCOMM_MAX_CREDITS); /* XXX ??? */
3263114878Sjulian	else
3264114878Sjulian		limit = 0;
3265114878Sjulian
3266114878Sjulian	if (limit == 0) {
3267114878Sjulian		NG_BTSOCKET_RFCOMM_INFO(
3268114878Sjulian"%s: Could not send - remote flow control asserted, dlci=%d, flags=%#x, " \
3269114878Sjulian"rmodem=%#x, tx_cred=%d\n",
3270114878Sjulian			__func__, pcb->dlci, pcb->flags, pcb->rmodem,
3271114878Sjulian			pcb->tx_cred);
3272114878Sjulian
3273114878Sjulian		return (0);
3274114878Sjulian	}
3275114878Sjulian
3276114878Sjulian	for (error = 0, sent = 0; sent < limit; sent ++) {
3277114878Sjulian		length = min(pcb->mtu, pcb->so->so_snd.sb_cc);
3278114878Sjulian		if (length == 0)
3279114878Sjulian			break;
3280114878Sjulian
3281114878Sjulian		/* Get the chunk from the socket's send buffer */
3282114878Sjulian		m = ng_btsocket_rfcomm_prepare_packet(&pcb->so->so_snd, length);
3283114878Sjulian		if (m == NULL) {
3284114878Sjulian			error = ENOBUFS;
3285114878Sjulian			break;
3286114878Sjulian		}
3287114878Sjulian
3288114878Sjulian		sbdrop(&pcb->so->so_snd, length);
3289114878Sjulian
3290114878Sjulian		error = ng_btsocket_rfcomm_send_uih(pcb->session,
3291114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(pcb->session),
3292114878Sjulian					pcb->dlci), 0, 0, m);
3293114878Sjulian		if (error != 0)
3294114878Sjulian			break;
3295114878Sjulian	}
3296114878Sjulian
3297114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC)
3298114878Sjulian		pcb->tx_cred -= sent;
3299114878Sjulian
3300114878Sjulian	if (error == 0 && sent > 0) {
3301114878Sjulian		pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_SENDING;
3302114878Sjulian		sowwakeup(pcb->so);
3303114878Sjulian	}
3304114878Sjulian
3305114878Sjulian	return (error);
3306114878Sjulian} /* ng_btsocket_rfcomm_pcb_send */
3307114878Sjulian
3308114878Sjulian/*
3309114878Sjulian * Unlink and disconnect DLC. If ng_btsocket_rfcomm_pcb_kill() returns
3310114878Sjulian * non zero value than socket has no reference and has to be detached.
3311114878Sjulian * Caller must hold pcb->pcb_mtx and pcb->session->session_mtx
3312114878Sjulian */
3313114878Sjulian
3314161623Semaxstatic void
3315114878Sjulianng_btsocket_rfcomm_pcb_kill(ng_btsocket_rfcomm_pcb_p pcb, int error)
3316114878Sjulian{
3317114878Sjulian	ng_btsocket_rfcomm_session_p	s = pcb->session;
3318114878Sjulian
3319114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
3320114878Sjulian"%s: Killing DLC, so=%p, dlci=%d, state=%d, flags=%#x, error=%d\n",
3321114878Sjulian		__func__, pcb->so, pcb->dlci, pcb->state, pcb->flags, error);
3322114878Sjulian
3323114878Sjulian	if (pcb->session == NULL)
3324114878Sjulian		panic("%s: DLC without session, pcb=%p, state=%d, flags=%#x\n",
3325114878Sjulian			__func__, pcb, pcb->state, pcb->flags);
3326114878Sjulian
3327142542Ssam	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3328142542Ssam	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3329142542Ssam
3330114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)
3331114878Sjulian		ng_btsocket_rfcomm_untimeout(pcb);
3332114878Sjulian
3333114878Sjulian	/* Detach DLC from the session. Does not matter which state DLC in */
3334114878Sjulian	LIST_REMOVE(pcb, session_next);
3335114878Sjulian	pcb->session = NULL;
3336114878Sjulian
3337114878Sjulian	/* Change DLC state and wakeup all sleepers */
3338114878Sjulian	pcb->state = NG_BTSOCKET_RFCOMM_DLC_CLOSED;
3339114878Sjulian	pcb->so->so_error = error;
3340114878Sjulian	soisdisconnected(pcb->so);
3341114878Sjulian	wakeup(&pcb->state);
3342114878Sjulian
3343114878Sjulian	/* Check if we have any DLCs left on the session */
3344114878Sjulian	if (LIST_EMPTY(&s->dlcs) && INITIATOR(s)) {
3345114878Sjulian		NG_BTSOCKET_RFCOMM_INFO(
3346114878Sjulian"%s: Disconnecting session, state=%d, flags=%#x, mtu=%d\n",
3347114878Sjulian			__func__, s->state, s->flags, s->mtu);
3348114878Sjulian
3349114878Sjulian		switch (s->state) {
3350114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_CLOSED:
3351114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING:
3352114878Sjulian			/*
3353114878Sjulian			 * Do not have to do anything here. We can get here
3354114878Sjulian			 * when L2CAP connection was terminated or we have
3355114878Sjulian			 * received DISC on multiplexor channel
3356114878Sjulian			 */
3357114878Sjulian			break;
3358114878Sjulian
3359114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_OPEN:
3360114878Sjulian			/* Send DISC on multiplexor channel */
3361114878Sjulian			error = ng_btsocket_rfcomm_send_command(s,
3362114878Sjulian					RFCOMM_FRAME_DISC, 0);
3363114878Sjulian			if (error == 0) {
3364114878Sjulian				s->state = NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING;
3365114878Sjulian				break;
3366114878Sjulian			}
3367114878Sjulian			/* FALL THROUGH */
3368114878Sjulian
3369114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_CONNECTING:
3370114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
3371114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
3372114878Sjulian			break;
3373114878Sjulian
3374114878Sjulian/*		case NG_BTSOCKET_RFCOMM_SESSION_LISTENING: */
3375114878Sjulian		default:
3376114878Sjulian			panic("%s: Invalid session state=%d, flags=%#x\n",
3377114878Sjulian				__func__, s->state, s->flags);
3378114878Sjulian			break;
3379114878Sjulian		}
3380114878Sjulian
3381114878Sjulian		ng_btsocket_rfcomm_task_wakeup();
3382114878Sjulian	}
3383114878Sjulian} /* ng_btsocket_rfcomm_pcb_kill */
3384114878Sjulian
3385114878Sjulian/*
3386114878Sjulian * Look for given dlci for given RFCOMM session. Caller must hold s->session_mtx
3387114878Sjulian */
3388114878Sjulian
3389114878Sjulianstatic ng_btsocket_rfcomm_pcb_p
3390114878Sjulianng_btsocket_rfcomm_pcb_by_dlci(ng_btsocket_rfcomm_session_p s, int dlci)
3391114878Sjulian{
3392114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
3393114878Sjulian
3394114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
3395114878Sjulian
3396114878Sjulian	LIST_FOREACH(pcb, &s->dlcs, session_next)
3397114878Sjulian		if (pcb->dlci == dlci)
3398114878Sjulian			break;
3399114878Sjulian
3400114878Sjulian	return (pcb);
3401114878Sjulian} /* ng_btsocket_rfcomm_pcb_by_dlci */
3402114878Sjulian
3403114878Sjulian/*
3404114878Sjulian * Look for socket that listens on given src address and given channel
3405114878Sjulian */
3406114878Sjulian
3407114878Sjulianstatic ng_btsocket_rfcomm_pcb_p
3408114878Sjulianng_btsocket_rfcomm_pcb_listener(bdaddr_p src, int channel)
3409114878Sjulian{
3410114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = NULL, pcb1 = NULL;
3411114878Sjulian
3412114878Sjulian	mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
3413114878Sjulian
3414114878Sjulian	LIST_FOREACH(pcb, &ng_btsocket_rfcomm_sockets, next) {
3415114878Sjulian		if (pcb->channel != channel ||
3416114878Sjulian		    !(pcb->so->so_options & SO_ACCEPTCONN))
3417114878Sjulian			continue;
3418114878Sjulian
3419114878Sjulian		if (bcmp(&pcb->src, src, sizeof(*src)) == 0)
3420114878Sjulian			break;
3421114878Sjulian
3422114878Sjulian		if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
3423114878Sjulian			pcb1 = pcb;
3424114878Sjulian	}
3425114878Sjulian
3426114878Sjulian	mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
3427114878Sjulian
3428114878Sjulian	return ((pcb != NULL)? pcb : pcb1);
3429114878Sjulian} /* ng_btsocket_rfcomm_pcb_listener */
3430114878Sjulian
3431114878Sjulian/*****************************************************************************
3432114878Sjulian *****************************************************************************
3433114878Sjulian **                              Misc. functions
3434114878Sjulian *****************************************************************************
3435114878Sjulian *****************************************************************************/
3436114878Sjulian
3437114878Sjulian/*
3438114878Sjulian *  Set timeout. Caller MUST hold pcb_mtx
3439114878Sjulian */
3440114878Sjulian
3441114878Sjulianstatic void
3442114878Sjulianng_btsocket_rfcomm_timeout(ng_btsocket_rfcomm_pcb_p pcb)
3443114878Sjulian{
3444114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3445114878Sjulian
3446114878Sjulian	if (!(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)) {
3447114878Sjulian		pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_TIMO;
3448114878Sjulian		pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT;
3449114878Sjulian		pcb->timo = timeout(ng_btsocket_rfcomm_process_timeout, pcb,
3450114878Sjulian					ng_btsocket_rfcomm_timo * hz);
3451114878Sjulian	} else
3452114878Sjulian		panic("%s: Duplicated socket timeout?!\n", __func__);
3453114878Sjulian} /* ng_btsocket_rfcomm_timeout */
3454114878Sjulian
3455114878Sjulian/*
3456114878Sjulian *  Unset pcb timeout. Caller MUST hold pcb_mtx
3457114878Sjulian */
3458114878Sjulian
3459114878Sjulianstatic void
3460114878Sjulianng_btsocket_rfcomm_untimeout(ng_btsocket_rfcomm_pcb_p pcb)
3461114878Sjulian{
3462114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3463114878Sjulian
3464114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO) {
3465114878Sjulian		untimeout(ng_btsocket_rfcomm_process_timeout, pcb, pcb->timo);
3466114878Sjulian		pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMO;
3467114878Sjulian		pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT;
3468114878Sjulian	} else
3469114878Sjulian		panic("%s: No socket timeout?!\n", __func__);
3470114878Sjulian} /* ng_btsocket_rfcomm_timeout */
3471114878Sjulian
3472114878Sjulian/*
3473114878Sjulian * Process pcb timeout
3474114878Sjulian */
3475114878Sjulian
3476114878Sjulianstatic void
3477114878Sjulianng_btsocket_rfcomm_process_timeout(void *xpcb)
3478114878Sjulian{
3479114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = (ng_btsocket_rfcomm_pcb_p) xpcb;
3480114878Sjulian
3481114878Sjulian	mtx_lock(&pcb->pcb_mtx);
3482114878Sjulian
3483114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
3484114878Sjulian"%s: Timeout, so=%p, dlci=%d, state=%d, flags=%#x\n",
3485114878Sjulian		__func__, pcb->so, pcb->dlci, pcb->state, pcb->flags);
3486114878Sjulian
3487114878Sjulian	pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMO;
3488114878Sjulian	pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT;
3489114878Sjulian
3490114878Sjulian	switch (pcb->state) {
3491114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING:
3492114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
3493114878Sjulian		pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING;
3494114878Sjulian		break;
3495114878Sjulian
3496114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
3497114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
3498114878Sjulian		break;
3499114878Sjulian
3500114878Sjulian	default:
3501114878Sjulian		panic(
3502114878Sjulian"%s: DLC timeout in invalid state, dlci=%d, state=%d, flags=%#x\n",
3503114878Sjulian			__func__, pcb->dlci, pcb->state, pcb->flags);
3504114878Sjulian		break;
3505114878Sjulian	}
3506114878Sjulian
3507114878Sjulian	ng_btsocket_rfcomm_task_wakeup();
3508114878Sjulian
3509114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
3510114878Sjulian} /* ng_btsocket_rfcomm_process_timeout */
3511114878Sjulian
3512114878Sjulian/*
3513114878Sjulian * Get up to length bytes from the socket buffer
3514114878Sjulian */
3515114878Sjulian
3516114878Sjulianstatic struct mbuf *
3517114878Sjulianng_btsocket_rfcomm_prepare_packet(struct sockbuf *sb, int length)
3518114878Sjulian{
3519114878Sjulian	struct mbuf	*top = NULL, *m = NULL, *n = NULL, *nextpkt = NULL;
3520114878Sjulian	int		 mlen, noff, len;
3521114878Sjulian
3522114878Sjulian	MGETHDR(top, M_DONTWAIT, MT_DATA);
3523114878Sjulian	if (top == NULL)
3524114878Sjulian		return (NULL);
3525114878Sjulian
3526114878Sjulian	top->m_pkthdr.len = length;
3527114878Sjulian	top->m_len = 0;
3528114878Sjulian	mlen = MHLEN;
3529114878Sjulian
3530114878Sjulian	m = top;
3531114878Sjulian	n = sb->sb_mb;
3532114878Sjulian	nextpkt = n->m_nextpkt;
3533114878Sjulian	noff = 0;
3534114878Sjulian
3535114878Sjulian	while (length > 0 && n != NULL) {
3536114878Sjulian		len = min(mlen - m->m_len, n->m_len - noff);
3537114878Sjulian		if (len > length)
3538114878Sjulian			len = length;
3539114878Sjulian
3540114878Sjulian		bcopy(mtod(n, caddr_t)+noff, mtod(m, caddr_t)+m->m_len, len);
3541114878Sjulian		m->m_len += len;
3542114878Sjulian		noff += len;
3543114878Sjulian		length -= len;
3544114878Sjulian
3545114878Sjulian		if (length > 0 && m->m_len == mlen) {
3546114878Sjulian			MGET(m->m_next, M_DONTWAIT, MT_DATA);
3547114878Sjulian			if (m->m_next == NULL) {
3548114878Sjulian				NG_FREE_M(top);
3549114878Sjulian				return (NULL);
3550114878Sjulian			}
3551114878Sjulian
3552114878Sjulian			m = m->m_next;
3553114878Sjulian			m->m_len = 0;
3554114878Sjulian			mlen = MLEN;
3555114878Sjulian		}
3556114878Sjulian
3557114878Sjulian		if (noff == n->m_len) {
3558114878Sjulian			noff = 0;
3559114878Sjulian			n = n->m_next;
3560114878Sjulian
3561114878Sjulian			if (n == NULL)
3562114878Sjulian				n = nextpkt;
3563114878Sjulian
3564114878Sjulian			nextpkt = (n != NULL)? n->m_nextpkt : NULL;
3565114878Sjulian		}
3566114878Sjulian	}
3567114878Sjulian
3568114878Sjulian	if (length < 0)
3569114878Sjulian		panic("%s: length=%d\n", __func__, length);
3570114878Sjulian	if (length > 0 && n == NULL)
3571114878Sjulian		panic("%s: bogus length=%d, n=%p\n", __func__, length, n);
3572114878Sjulian
3573114878Sjulian	return (top);
3574114878Sjulian} /* ng_btsocket_rfcomm_prepare_packet */
3575114878Sjulian
3576