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
69227293Sedstatic 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);
206227309Sedstatic 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{
331268061Strociny
332268061Strociny	/* Skip initialization of globals for non-default instances. */
333268061Strociny	if (!IS_DEFAULT_VNET(curvnet))
334268061Strociny		return;
335268061Strociny
336114878Sjulian	ng_btsocket_rfcomm_debug_level = NG_BTSOCKET_WARN_LEVEL;
337114878Sjulian	ng_btsocket_rfcomm_timo = 60;
338114878Sjulian
339114878Sjulian	/* RFCOMM task */
340114878Sjulian	TASK_INIT(&ng_btsocket_rfcomm_task, 0,
341114878Sjulian		ng_btsocket_rfcomm_sessions_task, NULL);
342114878Sjulian
343114878Sjulian	/* RFCOMM sessions list */
344114878Sjulian	LIST_INIT(&ng_btsocket_rfcomm_sessions);
345114878Sjulian	mtx_init(&ng_btsocket_rfcomm_sessions_mtx,
346114878Sjulian		"btsocks_rfcomm_sessions_mtx", NULL, MTX_DEF);
347114878Sjulian
348114878Sjulian	/* RFCOMM sockets list */
349114878Sjulian	LIST_INIT(&ng_btsocket_rfcomm_sockets);
350114878Sjulian	mtx_init(&ng_btsocket_rfcomm_sockets_mtx,
351114878Sjulian		"btsocks_rfcomm_sockets_mtx", NULL, MTX_DEF);
352114878Sjulian} /* ng_btsocket_rfcomm_init */
353114878Sjulian
354114878Sjulian/*
355114878Sjulian * Abort connection on socket
356114878Sjulian */
357114878Sjulian
358157366Srwatsonvoid
359114878Sjulianng_btsocket_rfcomm_abort(struct socket *so)
360114878Sjulian{
361160549Srwatson
362114878Sjulian	so->so_error = ECONNABORTED;
363160549Srwatson	(void)ng_btsocket_rfcomm_disconnect(so);
364114878Sjulian} /* ng_btsocket_rfcomm_abort */
365114878Sjulian
366160549Srwatsonvoid
367160549Srwatsonng_btsocket_rfcomm_close(struct socket *so)
368160549Srwatson{
369160549Srwatson
370160549Srwatson	(void)ng_btsocket_rfcomm_disconnect(so);
371160549Srwatson} /* ng_btsocket_rfcomm_close */
372160549Srwatson
373114878Sjulian/*
374114878Sjulian * Accept connection on socket. Nothing to do here, socket must be connected
375114878Sjulian * and ready, so just return peer address and be done with it.
376114878Sjulian */
377114878Sjulian
378114878Sjulianint
379114878Sjulianng_btsocket_rfcomm_accept(struct socket *so, struct sockaddr **nam)
380114878Sjulian{
381114878Sjulian	return (ng_btsocket_rfcomm_peeraddr(so, nam));
382114878Sjulian} /* ng_btsocket_rfcomm_accept */
383114878Sjulian
384114878Sjulian/*
385114878Sjulian * Create and attach new socket
386114878Sjulian */
387114878Sjulian
388114878Sjulianint
389114878Sjulianng_btsocket_rfcomm_attach(struct socket *so, int proto, struct thread *td)
390114878Sjulian{
391114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
392114878Sjulian	int				error;
393114878Sjulian
394114878Sjulian	/* Check socket and protocol */
395114878Sjulian	if (so->so_type != SOCK_STREAM)
396114878Sjulian		return (ESOCKTNOSUPPORT);
397114878Sjulian
398114878Sjulian#if 0 /* XXX sonewconn() calls "pru_attach" with proto == 0 */
399114878Sjulian	if (proto != 0)
400114878Sjulian		if (proto != BLUETOOTH_PROTO_RFCOMM)
401114878Sjulian			return (EPROTONOSUPPORT);
402114878Sjulian#endif /* XXX */
403114878Sjulian
404114878Sjulian	if (pcb != NULL)
405114878Sjulian		return (EISCONN);
406114878Sjulian
407114878Sjulian	/* Reserve send and receive space if it is not reserved yet */
408114878Sjulian	if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) {
409114878Sjulian		error = soreserve(so, NG_BTSOCKET_RFCOMM_SENDSPACE,
410114878Sjulian					NG_BTSOCKET_RFCOMM_RECVSPACE);
411114878Sjulian		if (error != 0)
412114878Sjulian			return (error);
413114878Sjulian	}
414114878Sjulian
415114878Sjulian	/* Allocate the PCB */
416184205Sdes        pcb = malloc(sizeof(*pcb),
417114878Sjulian		M_NETGRAPH_BTSOCKET_RFCOMM, M_NOWAIT | M_ZERO);
418114878Sjulian        if (pcb == NULL)
419114878Sjulian                return (ENOMEM);
420114878Sjulian
421114878Sjulian	/* Link the PCB and the socket */
422114878Sjulian	so->so_pcb = (caddr_t) pcb;
423114878Sjulian	pcb->so = so;
424114878Sjulian
425114878Sjulian	/* Initialize PCB */
426114878Sjulian	pcb->state = NG_BTSOCKET_RFCOMM_DLC_CLOSED;
427114878Sjulian	pcb->flags = NG_BTSOCKET_RFCOMM_DLC_CFC;
428114878Sjulian
429114878Sjulian	pcb->lmodem =
430114878Sjulian	pcb->rmodem = (RFCOMM_MODEM_RTC | RFCOMM_MODEM_RTR | RFCOMM_MODEM_DV);
431114878Sjulian
432114878Sjulian	pcb->mtu = RFCOMM_DEFAULT_MTU;
433114878Sjulian	pcb->tx_cred = 0;
434114878Sjulian	pcb->rx_cred = RFCOMM_DEFAULT_CREDITS;
435114878Sjulian
436114878Sjulian	mtx_init(&pcb->pcb_mtx, "btsocks_rfcomm_pcb_mtx", NULL, MTX_DEF);
437114878Sjulian	callout_handle_init(&pcb->timo);
438114878Sjulian
439114878Sjulian	/* Add the PCB to the list */
440114878Sjulian	mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
441114878Sjulian	LIST_INSERT_HEAD(&ng_btsocket_rfcomm_sockets, pcb, next);
442114878Sjulian	mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
443114878Sjulian
444114878Sjulian        return (0);
445114878Sjulian} /* ng_btsocket_rfcomm_attach */
446114878Sjulian
447114878Sjulian/*
448114878Sjulian * Bind socket
449114878Sjulian */
450114878Sjulian
451114878Sjulianint
452114878Sjulianng_btsocket_rfcomm_bind(struct socket *so, struct sockaddr *nam,
453114878Sjulian		struct thread *td)
454114878Sjulian{
455173151Semax	ng_btsocket_rfcomm_pcb_t	*pcb = so2rfcomm_pcb(so), *pcb1;
456114878Sjulian	struct sockaddr_rfcomm		*sa = (struct sockaddr_rfcomm *) nam;
457114878Sjulian
458114878Sjulian	if (pcb == NULL)
459114878Sjulian		return (EINVAL);
460114878Sjulian
461114878Sjulian	/* Verify address */
462114878Sjulian	if (sa == NULL)
463114878Sjulian		return (EINVAL);
464114878Sjulian	if (sa->rfcomm_family != AF_BLUETOOTH)
465114878Sjulian		return (EAFNOSUPPORT);
466114878Sjulian	if (sa->rfcomm_len != sizeof(*sa))
467114878Sjulian		return (EINVAL);
468114878Sjulian	if (sa->rfcomm_channel > 30)
469114878Sjulian		return (EINVAL);
470114878Sjulian
471173151Semax	mtx_lock(&pcb->pcb_mtx);
472173151Semax
473173151Semax	if (sa->rfcomm_channel != 0) {
474173151Semax		mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
475173151Semax
476173151Semax		LIST_FOREACH(pcb1, &ng_btsocket_rfcomm_sockets, next) {
477173151Semax			if (pcb1->channel == sa->rfcomm_channel &&
478173151Semax			    bcmp(&pcb1->src, &sa->rfcomm_bdaddr,
479173151Semax					sizeof(pcb1->src)) == 0) {
480173151Semax				mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
481173151Semax				mtx_unlock(&pcb->pcb_mtx);
482173151Semax
483173151Semax				return (EADDRINUSE);
484173151Semax			}
485173151Semax		}
486173151Semax
487173151Semax		mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
488173151Semax	}
489173151Semax
490114878Sjulian	bcopy(&sa->rfcomm_bdaddr, &pcb->src, sizeof(pcb->src));
491114878Sjulian	pcb->channel = sa->rfcomm_channel;
492114878Sjulian
493173151Semax	mtx_unlock(&pcb->pcb_mtx);
494173151Semax
495114878Sjulian	return (0);
496114878Sjulian} /* ng_btsocket_rfcomm_bind */
497114878Sjulian
498114878Sjulian/*
499114878Sjulian * Connect socket
500114878Sjulian */
501114878Sjulian
502114878Sjulianint
503114878Sjulianng_btsocket_rfcomm_connect(struct socket *so, struct sockaddr *nam,
504114878Sjulian		struct thread *td)
505114878Sjulian{
506114878Sjulian	ng_btsocket_rfcomm_pcb_t	*pcb = so2rfcomm_pcb(so);
507114878Sjulian	struct sockaddr_rfcomm		*sa = (struct sockaddr_rfcomm *) nam;
508114878Sjulian	ng_btsocket_rfcomm_session_t	*s = NULL;
509114878Sjulian	struct socket			*l2so = NULL;
510114878Sjulian	int				 dlci, error = 0;
511114878Sjulian
512114878Sjulian	if (pcb == NULL)
513114878Sjulian		return (EINVAL);
514114878Sjulian
515114878Sjulian	/* Verify address */
516114878Sjulian	if (sa == NULL)
517114878Sjulian		return (EINVAL);
518114878Sjulian	if (sa->rfcomm_family != AF_BLUETOOTH)
519114878Sjulian		return (EAFNOSUPPORT);
520114878Sjulian	if (sa->rfcomm_len != sizeof(*sa))
521114878Sjulian		return (EINVAL);
522114878Sjulian	if (sa->rfcomm_channel > 30)
523114878Sjulian		return (EINVAL);
524114878Sjulian	if (sa->rfcomm_channel == 0 ||
525114878Sjulian	    bcmp(&sa->rfcomm_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
526114878Sjulian		return (EDESTADDRREQ);
527114878Sjulian
528114878Sjulian	/*
529188452Semax	 * Note that we will not check for errors in socreate() because
530188452Semax	 * if we failed to create L2CAP socket at this point we still
531188452Semax	 * might have already open session.
532114878Sjulian	 */
533114878Sjulian
534114878Sjulian	error = socreate(PF_BLUETOOTH, &l2so, SOCK_SEQPACKET,
535114878Sjulian			BLUETOOTH_PROTO_L2CAP, td->td_ucred, td);
536114878Sjulian
537114878Sjulian	/*
538114878Sjulian	 * Look for session between "pcb->src" and "sa->rfcomm_bdaddr" (dst)
539114878Sjulian	 */
540114878Sjulian
541114878Sjulian	mtx_lock(&ng_btsocket_rfcomm_sessions_mtx);
542114878Sjulian
543114878Sjulian	s = ng_btsocket_rfcomm_session_by_addr(&pcb->src, &sa->rfcomm_bdaddr);
544114878Sjulian	if (s == NULL) {
545114878Sjulian		/*
546114878Sjulian		 * We need to create new RFCOMM session. Check if we have L2CAP
547114878Sjulian		 * socket. If l2so == NULL then error has the error code from
548114878Sjulian		 * socreate()
549114878Sjulian		 */
550114878Sjulian
551114878Sjulian		if (l2so == NULL) {
552114878Sjulian			mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
553114878Sjulian			return (error);
554114878Sjulian		}
555114878Sjulian
556114878Sjulian		error = ng_btsocket_rfcomm_session_create(&s, l2so,
557114878Sjulian				&pcb->src, &sa->rfcomm_bdaddr, td);
558114878Sjulian		if (error != 0) {
559114878Sjulian			mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
560114878Sjulian			soclose(l2so);
561114878Sjulian
562114878Sjulian			return (error);
563114878Sjulian		}
564114878Sjulian	} else if (l2so != NULL)
565114878Sjulian		soclose(l2so); /* we don't need new L2CAP socket */
566114878Sjulian
567114878Sjulian	/*
568218909Sbrucec	 * Check if we already have the same DLCI the same session
569114878Sjulian	 */
570114878Sjulian
571114878Sjulian	mtx_lock(&s->session_mtx);
572114878Sjulian	mtx_lock(&pcb->pcb_mtx);
573114878Sjulian
574114878Sjulian	dlci = RFCOMM_MKDLCI(!INITIATOR(s), sa->rfcomm_channel);
575114878Sjulian
576114878Sjulian	if (ng_btsocket_rfcomm_pcb_by_dlci(s, dlci) != NULL) {
577114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
578114878Sjulian		mtx_unlock(&s->session_mtx);
579114878Sjulian		mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
580114878Sjulian
581114878Sjulian		return (EBUSY);
582114878Sjulian	}
583114878Sjulian
584114878Sjulian	/*
585114878Sjulian	 * Check session state and if its not acceptable then refuse connection
586114878Sjulian	 */
587114878Sjulian
588114878Sjulian	switch (s->state) {
589114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_CONNECTING:
590114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
591114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_OPEN:
592114878Sjulian		/*
593114878Sjulian		 * Update destination address and channel and attach
594114878Sjulian		 * DLC to the session
595114878Sjulian		 */
596114878Sjulian
597114878Sjulian		bcopy(&sa->rfcomm_bdaddr, &pcb->dst, sizeof(pcb->dst));
598114878Sjulian		pcb->channel = sa->rfcomm_channel;
599114878Sjulian		pcb->dlci = dlci;
600114878Sjulian
601114878Sjulian		LIST_INSERT_HEAD(&s->dlcs, pcb, session_next);
602114878Sjulian		pcb->session = s;
603114878Sjulian
604114878Sjulian		ng_btsocket_rfcomm_timeout(pcb);
605114878Sjulian		soisconnecting(pcb->so);
606114878Sjulian
607114878Sjulian		if (s->state == NG_BTSOCKET_RFCOMM_SESSION_OPEN) {
608114878Sjulian			pcb->mtu = s->mtu;
609114878Sjulian			bcopy(&so2l2cap_pcb(s->l2so)->src, &pcb->src,
610114878Sjulian				sizeof(pcb->src));
611114878Sjulian
612114878Sjulian			pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONFIGURING;
613114878Sjulian
614114878Sjulian			error = ng_btsocket_rfcomm_send_pn(pcb);
615114878Sjulian			if (error == 0)
616114878Sjulian				error = ng_btsocket_rfcomm_task_wakeup();
617114878Sjulian		} else
618114878Sjulian			pcb->state = NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT;
619114878Sjulian		break;
620114878Sjulian
621114878Sjulian	default:
622114878Sjulian		error = ECONNRESET;
623114878Sjulian		break;
624114878Sjulian	}
625114878Sjulian
626114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
627114878Sjulian	mtx_unlock(&s->session_mtx);
628114878Sjulian	mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
629114878Sjulian
630114878Sjulian	return (error);
631114878Sjulian} /* ng_btsocket_rfcomm_connect */
632114878Sjulian
633114878Sjulian/*
634114878Sjulian * Process ioctl's calls on socket.
635114878Sjulian * XXX FIXME this should provide interface to the RFCOMM multiplexor channel
636114878Sjulian */
637114878Sjulian
638114878Sjulianint
639114878Sjulianng_btsocket_rfcomm_control(struct socket *so, u_long cmd, caddr_t data,
640114878Sjulian		struct ifnet *ifp, struct thread *td)
641114878Sjulian{
642114878Sjulian	return (EINVAL);
643114878Sjulian} /* ng_btsocket_rfcomm_control */
644114878Sjulian
645114878Sjulian/*
646114878Sjulian * Process getsockopt/setsockopt system calls
647114878Sjulian */
648114878Sjulian
649114878Sjulianint
650114878Sjulianng_btsocket_rfcomm_ctloutput(struct socket *so, struct sockopt *sopt)
651114878Sjulian{
652114878Sjulian	ng_btsocket_rfcomm_pcb_p		pcb = so2rfcomm_pcb(so);
653114878Sjulian	struct ng_btsocket_rfcomm_fc_info	fcinfo;
654114878Sjulian	int					error = 0;
655114878Sjulian
656114878Sjulian	if (pcb == NULL)
657114878Sjulian		return (EINVAL);
658114878Sjulian	if (sopt->sopt_level != SOL_RFCOMM)
659114878Sjulian		return (0);
660114878Sjulian
661114878Sjulian	mtx_lock(&pcb->pcb_mtx);
662114878Sjulian
663114878Sjulian	switch (sopt->sopt_dir) {
664114878Sjulian	case SOPT_GET:
665114878Sjulian		switch (sopt->sopt_name) {
666114878Sjulian		case SO_RFCOMM_MTU:
667114878Sjulian			error = sooptcopyout(sopt, &pcb->mtu, sizeof(pcb->mtu));
668114878Sjulian			break;
669114878Sjulian
670114878Sjulian		case SO_RFCOMM_FC_INFO:
671114878Sjulian			fcinfo.lmodem = pcb->lmodem;
672114878Sjulian			fcinfo.rmodem = pcb->rmodem;
673114878Sjulian			fcinfo.tx_cred = pcb->tx_cred;
674114878Sjulian			fcinfo.rx_cred = pcb->rx_cred;
675114878Sjulian			fcinfo.cfc = (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC)?
676114878Sjulian				1 : 0;
677114878Sjulian			fcinfo.reserved = 0;
678114878Sjulian
679114878Sjulian			error = sooptcopyout(sopt, &fcinfo, sizeof(fcinfo));
680114878Sjulian			break;
681114878Sjulian
682114878Sjulian		default:
683114878Sjulian			error = ENOPROTOOPT;
684114878Sjulian			break;
685114878Sjulian		}
686114878Sjulian		break;
687114878Sjulian
688114878Sjulian	case SOPT_SET:
689114878Sjulian		switch (sopt->sopt_name) {
690114878Sjulian		default:
691114878Sjulian			error = ENOPROTOOPT;
692114878Sjulian			break;
693114878Sjulian		}
694114878Sjulian		break;
695114878Sjulian
696114878Sjulian	default:
697114878Sjulian		error = EINVAL;
698114878Sjulian		break;
699114878Sjulian	}
700114878Sjulian
701114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
702114878Sjulian
703114878Sjulian	return (error);
704114878Sjulian} /* ng_btsocket_rfcomm_ctloutput */
705114878Sjulian
706114878Sjulian/*
707114878Sjulian * Detach and destroy socket
708114878Sjulian */
709114878Sjulian
710157370Srwatsonvoid
711114878Sjulianng_btsocket_rfcomm_detach(struct socket *so)
712114878Sjulian{
713114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
714114878Sjulian
715157370Srwatson	KASSERT(pcb != NULL, ("ng_btsocket_rfcomm_detach: pcb == NULL"));
716114878Sjulian
717114878Sjulian	mtx_lock(&pcb->pcb_mtx);
718114878Sjulian
719114878Sjulian	switch (pcb->state) {
720114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
721114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING:
722114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
723114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONNECTED:
724114878Sjulian		/* XXX What to do with pending request? */
725114878Sjulian		if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)
726114878Sjulian			ng_btsocket_rfcomm_untimeout(pcb);
727114878Sjulian
728114878Sjulian		if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT)
729114878Sjulian			pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_DETACHED;
730114878Sjulian		else
731114878Sjulian			pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING;
732114878Sjulian
733114878Sjulian		ng_btsocket_rfcomm_task_wakeup();
734114878Sjulian		break;
735114878Sjulian
736114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
737114878Sjulian		ng_btsocket_rfcomm_task_wakeup();
738114878Sjulian		break;
739114878Sjulian	}
740114878Sjulian
741114878Sjulian	while (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CLOSED)
742114878Sjulian		msleep(&pcb->state, &pcb->pcb_mtx, PZERO, "rf_det", 0);
743114878Sjulian
744114878Sjulian	if (pcb->session != NULL)
745114878Sjulian		panic("%s: pcb->session != NULL\n", __func__);
746114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)
747114878Sjulian		panic("%s: timeout on closed DLC, flags=%#x\n",
748114878Sjulian			__func__, pcb->flags);
749114878Sjulian
750114878Sjulian	mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
751114878Sjulian	LIST_REMOVE(pcb, next);
752114878Sjulian	mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
753114878Sjulian
754114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
755114878Sjulian
756114878Sjulian	mtx_destroy(&pcb->pcb_mtx);
757114878Sjulian	bzero(pcb, sizeof(*pcb));
758184205Sdes	free(pcb, M_NETGRAPH_BTSOCKET_RFCOMM);
759114878Sjulian
760114878Sjulian	soisdisconnected(so);
761114878Sjulian	so->so_pcb = NULL;
762114878Sjulian} /* ng_btsocket_rfcomm_detach */
763114878Sjulian
764114878Sjulian/*
765114878Sjulian * Disconnect socket
766114878Sjulian */
767114878Sjulian
768114878Sjulianint
769114878Sjulianng_btsocket_rfcomm_disconnect(struct socket *so)
770114878Sjulian{
771114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
772114878Sjulian
773114878Sjulian	if (pcb == NULL)
774114878Sjulian		return (EINVAL);
775114878Sjulian
776114878Sjulian	mtx_lock(&pcb->pcb_mtx);
777114878Sjulian
778114878Sjulian	if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING) {
779114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
780114878Sjulian		return (EINPROGRESS);
781114878Sjulian	}
782114878Sjulian
783114878Sjulian	/* XXX What to do with pending request? */
784114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)
785114878Sjulian		ng_btsocket_rfcomm_untimeout(pcb);
786114878Sjulian
787114878Sjulian	switch (pcb->state) {
788114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING: /* XXX can we get here? */
789114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONNECTING: /* XXX can we get here? */
790114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONNECTED:
791114878Sjulian
792114878Sjulian		/*
793114878Sjulian		 * Just change DLC state and enqueue RFCOMM task. It will
794114878Sjulian		 * queue and send DISC on the DLC.
795114878Sjulian		 */
796114878Sjulian
797114878Sjulian		pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING;
798114878Sjulian		soisdisconnecting(so);
799114878Sjulian
800114878Sjulian		ng_btsocket_rfcomm_task_wakeup();
801114878Sjulian		break;
802114878Sjulian
803161623Semax	case NG_BTSOCKET_RFCOMM_DLC_CLOSED:
804161623Semax	case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
805161623Semax		break;
806161623Semax
807114878Sjulian	default:
808114878Sjulian		panic("%s: Invalid DLC state=%d, flags=%#x\n",
809114878Sjulian			__func__, pcb->state, pcb->flags);
810114878Sjulian		break;
811114878Sjulian	}
812114878Sjulian
813114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
814114878Sjulian
815114878Sjulian	return (0);
816114878Sjulian} /* ng_btsocket_rfcomm_disconnect */
817114878Sjulian
818114878Sjulian/*
819114878Sjulian * Listen on socket. First call to listen() will create listening RFCOMM session
820114878Sjulian */
821114878Sjulian
822114878Sjulianint
823151888Srwatsonng_btsocket_rfcomm_listen(struct socket *so, int backlog, struct thread *td)
824114878Sjulian{
825173151Semax	ng_btsocket_rfcomm_pcb_p	 pcb = so2rfcomm_pcb(so), pcb1;
826114878Sjulian	ng_btsocket_rfcomm_session_p	 s = NULL;
827114878Sjulian	struct socket			*l2so = NULL;
828173151Semax	int				 error, socreate_error, usedchannels;
829114878Sjulian
830114878Sjulian	if (pcb == NULL)
831114878Sjulian		return (EINVAL);
832173151Semax	if (pcb->channel > 30)
833171937Semax		return (EADDRNOTAVAIL);
834114878Sjulian
835173151Semax	usedchannels = 0;
836173151Semax
837173151Semax	mtx_lock(&pcb->pcb_mtx);
838173151Semax
839173151Semax	if (pcb->channel == 0) {
840173151Semax		mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
841173151Semax
842173151Semax		LIST_FOREACH(pcb1, &ng_btsocket_rfcomm_sockets, next)
843173151Semax			if (pcb1->channel != 0 &&
844173151Semax			    bcmp(&pcb1->src, &pcb->src, sizeof(pcb->src)) == 0)
845173151Semax				usedchannels |= (1 << (pcb1->channel - 1));
846173151Semax
847173151Semax		for (pcb->channel = 30; pcb->channel > 0; pcb->channel --)
848173151Semax			if (!(usedchannels & (1 << (pcb->channel - 1))))
849173151Semax				break;
850173151Semax
851173151Semax		if (pcb->channel == 0) {
852173151Semax			mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
853173151Semax			mtx_unlock(&pcb->pcb_mtx);
854173151Semax
855173151Semax			return (EADDRNOTAVAIL);
856173151Semax		}
857173151Semax
858173151Semax		mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
859173151Semax	}
860173151Semax
861173151Semax	mtx_unlock(&pcb->pcb_mtx);
862173151Semax
863114878Sjulian	/*
864188452Semax	 * Note that we will not check for errors in socreate() because
865188452Semax	 * if we failed to create L2CAP socket at this point we still
866188452Semax	 * might have already open session.
867114878Sjulian	 */
868114878Sjulian
869142190Srwatson	socreate_error = socreate(PF_BLUETOOTH, &l2so, SOCK_SEQPACKET,
870114878Sjulian			BLUETOOTH_PROTO_L2CAP, td->td_ucred, td);
871114878Sjulian
872142190Srwatson	/*
873142190Srwatson	 * Transition the socket and session into the LISTENING state.  Check
874142190Srwatson	 * for collisions first, as there can only be one.
875114878Sjulian	 */
876114878Sjulian	mtx_lock(&ng_btsocket_rfcomm_sessions_mtx);
877142190Srwatson	SOCK_LOCK(so);
878142190Srwatson	error = solisten_proto_check(so);
879161623Semax	SOCK_UNLOCK(so);
880142190Srwatson	if (error != 0)
881142190Srwatson		goto out;
882114878Sjulian
883114878Sjulian	LIST_FOREACH(s, &ng_btsocket_rfcomm_sessions, next)
884114878Sjulian		if (s->state == NG_BTSOCKET_RFCOMM_SESSION_LISTENING)
885114878Sjulian			break;
886114878Sjulian
887114878Sjulian	if (s == NULL) {
888114878Sjulian		/*
889114878Sjulian		 * We need to create default RFCOMM session. Check if we have
890114878Sjulian		 * L2CAP socket. If l2so == NULL then error has the error code
891114878Sjulian		 * from socreate()
892114878Sjulian		 */
893114878Sjulian		if (l2so == NULL) {
894142190Srwatson			error = socreate_error;
895142190Srwatson			goto out;
896114878Sjulian		}
897114878Sjulian
898114878Sjulian		/*
899114878Sjulian		 * Create default listen RFCOMM session. The default RFCOMM
900114878Sjulian		 * session will listen on ANY address.
901114878Sjulian		 *
902114878Sjulian		 * XXX FIXME Note that currently there is no way to adjust MTU
903114878Sjulian		 * for the default session.
904114878Sjulian		 */
905114878Sjulian		error = ng_btsocket_rfcomm_session_create(&s, l2so,
906114878Sjulian					NG_HCI_BDADDR_ANY, NULL, td);
907142190Srwatson		if (error != 0)
908142190Srwatson			goto out;
909142190Srwatson		l2so = NULL;
910142190Srwatson	}
911161623Semax	SOCK_LOCK(so);
912151888Srwatson	solisten_proto(so, backlog);
913161623Semax	SOCK_UNLOCK(so);
914142190Srwatsonout:
915114878Sjulian	mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
916142190Srwatson	/*
917142190Srwatson	 * If we still have an l2so reference here, it's unneeded, so release
918142190Srwatson	 * it.
919142190Srwatson	 */
920142190Srwatson	if (l2so != NULL)
921142190Srwatson		soclose(l2so);
922142190Srwatson	return (error);
923114878Sjulian} /* ng_btsocket_listen */
924114878Sjulian
925114878Sjulian/*
926114878Sjulian * Get peer address
927114878Sjulian */
928114878Sjulian
929114878Sjulianint
930114878Sjulianng_btsocket_rfcomm_peeraddr(struct socket *so, struct sockaddr **nam)
931114878Sjulian{
932114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
933114878Sjulian	struct sockaddr_rfcomm		sa;
934114878Sjulian
935114878Sjulian	if (pcb == NULL)
936114878Sjulian		return (EINVAL);
937114878Sjulian
938114878Sjulian	bcopy(&pcb->dst, &sa.rfcomm_bdaddr, sizeof(sa.rfcomm_bdaddr));
939114878Sjulian	sa.rfcomm_channel = pcb->channel;
940114878Sjulian	sa.rfcomm_len = sizeof(sa);
941114878Sjulian	sa.rfcomm_family = AF_BLUETOOTH;
942114878Sjulian
943126425Srwatson	*nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT);
944114878Sjulian
945114878Sjulian	return ((*nam == NULL)? ENOMEM : 0);
946114878Sjulian} /* ng_btsocket_rfcomm_peeraddr */
947114878Sjulian
948114878Sjulian/*
949114878Sjulian * Send data to socket
950114878Sjulian */
951114878Sjulian
952114878Sjulianint
953114878Sjulianng_btsocket_rfcomm_send(struct socket *so, int flags, struct mbuf *m,
954114878Sjulian		struct sockaddr *nam, struct mbuf *control, struct thread *td)
955114878Sjulian{
956114878Sjulian	ng_btsocket_rfcomm_pcb_t	*pcb = so2rfcomm_pcb(so);
957114878Sjulian	int				 error = 0;
958114878Sjulian
959114878Sjulian	/* Check socket and input */
960114878Sjulian	if (pcb == NULL || m == NULL || control != NULL) {
961114878Sjulian		error = EINVAL;
962114878Sjulian		goto drop;
963114878Sjulian	}
964114878Sjulian
965114878Sjulian	mtx_lock(&pcb->pcb_mtx);
966114878Sjulian
967114878Sjulian	/* Make sure DLC is connected */
968114878Sjulian	if (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTED) {
969114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
970114878Sjulian		error = ENOTCONN;
971114878Sjulian		goto drop;
972114878Sjulian	}
973114878Sjulian
974114878Sjulian	/* Put the packet on the socket's send queue and wakeup RFCOMM task */
975114878Sjulian	sbappend(&pcb->so->so_snd, m);
976114878Sjulian	m = NULL;
977114878Sjulian
978114878Sjulian	if (!(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_SENDING)) {
979114878Sjulian		pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_SENDING;
980114878Sjulian		error = ng_btsocket_rfcomm_task_wakeup();
981114878Sjulian	}
982114878Sjulian
983114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
984114878Sjuliandrop:
985114878Sjulian	NG_FREE_M(m); /* checks for != NULL */
986114878Sjulian	NG_FREE_M(control);
987114878Sjulian
988114878Sjulian	return (error);
989114878Sjulian} /* ng_btsocket_rfcomm_send */
990114878Sjulian
991114878Sjulian/*
992114878Sjulian * Get socket address
993114878Sjulian */
994114878Sjulian
995114878Sjulianint
996114878Sjulianng_btsocket_rfcomm_sockaddr(struct socket *so, struct sockaddr **nam)
997114878Sjulian{
998114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = so2rfcomm_pcb(so);
999114878Sjulian	struct sockaddr_rfcomm		sa;
1000114878Sjulian
1001114878Sjulian	if (pcb == NULL)
1002114878Sjulian		return (EINVAL);
1003114878Sjulian
1004114878Sjulian	bcopy(&pcb->src, &sa.rfcomm_bdaddr, sizeof(sa.rfcomm_bdaddr));
1005114878Sjulian	sa.rfcomm_channel = pcb->channel;
1006114878Sjulian	sa.rfcomm_len = sizeof(sa);
1007114878Sjulian	sa.rfcomm_family = AF_BLUETOOTH;
1008114878Sjulian
1009126425Srwatson	*nam = sodupsockaddr((struct sockaddr *) &sa, M_NOWAIT);
1010114878Sjulian
1011114878Sjulian	return ((*nam == NULL)? ENOMEM : 0);
1012114878Sjulian} /* ng_btsocket_rfcomm_sockaddr */
1013114878Sjulian
1014114878Sjulian/*
1015114878Sjulian * Upcall function for L2CAP sockets. Enqueue RFCOMM task.
1016114878Sjulian */
1017114878Sjulian
1018193272Sjhbstatic int
1019114878Sjulianng_btsocket_rfcomm_upcall(struct socket *so, void *arg, int waitflag)
1020114878Sjulian{
1021114878Sjulian	int	error;
1022114878Sjulian
1023114878Sjulian	if (so == NULL)
1024114878Sjulian		panic("%s: so == NULL\n", __func__);
1025114878Sjulian
1026114878Sjulian	if ((error = ng_btsocket_rfcomm_task_wakeup()) != 0)
1027114878Sjulian		NG_BTSOCKET_RFCOMM_ALERT(
1028114878Sjulian"%s: Could not enqueue RFCOMM task, error=%d\n", __func__, error);
1029193272Sjhb	return (SU_OK);
1030114878Sjulian} /* ng_btsocket_rfcomm_upcall */
1031114878Sjulian
1032114878Sjulian/*
1033114878Sjulian * RFCOMM task. Will handle all RFCOMM sessions in one pass.
1034114878Sjulian * XXX FIXME does not scale very well
1035114878Sjulian */
1036114878Sjulian
1037114878Sjulianstatic void
1038114878Sjulianng_btsocket_rfcomm_sessions_task(void *ctx, int pending)
1039114878Sjulian{
1040114878Sjulian	ng_btsocket_rfcomm_session_p	s = NULL, s_next = NULL;
1041114878Sjulian
1042114878Sjulian	mtx_lock(&ng_btsocket_rfcomm_sessions_mtx);
1043114878Sjulian
1044114878Sjulian	for (s = LIST_FIRST(&ng_btsocket_rfcomm_sessions); s != NULL; ) {
1045114878Sjulian		mtx_lock(&s->session_mtx);
1046114878Sjulian		s_next = LIST_NEXT(s, next);
1047114878Sjulian
1048114878Sjulian		ng_btsocket_rfcomm_session_task(s);
1049114878Sjulian
1050114878Sjulian		if (s->state == NG_BTSOCKET_RFCOMM_SESSION_CLOSED) {
1051114878Sjulian			/* Unlink and clean the session */
1052114878Sjulian			LIST_REMOVE(s, next);
1053114878Sjulian
1054114878Sjulian			NG_BT_MBUFQ_DRAIN(&s->outq);
1055114878Sjulian			if (!LIST_EMPTY(&s->dlcs))
1056114878Sjulian				panic("%s: DLC list is not empty\n", __func__);
1057114878Sjulian
1058114878Sjulian			/* Close L2CAP socket */
1059130653Srwatson			SOCKBUF_LOCK(&s->l2so->so_rcv);
1060193272Sjhb			soupcall_clear(s->l2so, SO_RCV);
1061130653Srwatson			SOCKBUF_UNLOCK(&s->l2so->so_rcv);
1062130653Srwatson			SOCKBUF_LOCK(&s->l2so->so_snd);
1063193272Sjhb			soupcall_clear(s->l2so, SO_SND);
1064130653Srwatson			SOCKBUF_UNLOCK(&s->l2so->so_snd);
1065114878Sjulian			soclose(s->l2so);
1066114878Sjulian
1067114878Sjulian			mtx_unlock(&s->session_mtx);
1068114878Sjulian
1069114878Sjulian			mtx_destroy(&s->session_mtx);
1070114878Sjulian			bzero(s, sizeof(*s));
1071184205Sdes			free(s, M_NETGRAPH_BTSOCKET_RFCOMM);
1072114878Sjulian		} else
1073114878Sjulian			mtx_unlock(&s->session_mtx);
1074114878Sjulian
1075114878Sjulian		s = s_next;
1076114878Sjulian	}
1077114878Sjulian
1078114878Sjulian	mtx_unlock(&ng_btsocket_rfcomm_sessions_mtx);
1079114878Sjulian} /* ng_btsocket_rfcomm_sessions_task */
1080114878Sjulian
1081114878Sjulian/*
1082114878Sjulian * Process RFCOMM session. Will handle all RFCOMM sockets in one pass.
1083114878Sjulian */
1084114878Sjulian
1085114878Sjulianstatic void
1086114878Sjulianng_btsocket_rfcomm_session_task(ng_btsocket_rfcomm_session_p s)
1087114878Sjulian{
1088114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1089114878Sjulian
1090130480Srwatson	if (s->l2so->so_rcv.sb_state & SBS_CANTRCVMORE) {
1091114878Sjulian		NG_BTSOCKET_RFCOMM_INFO(
1092114878Sjulian"%s: L2CAP connection has been terminated, so=%p, so_state=%#x, so_count=%d, " \
1093114878Sjulian"state=%d, flags=%#x\n", __func__, s->l2so, s->l2so->so_state,
1094114878Sjulian			s->l2so->so_count, s->state, s->flags);
1095114878Sjulian
1096114878Sjulian		s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1097114878Sjulian		ng_btsocket_rfcomm_session_clean(s);
1098114878Sjulian	}
1099114878Sjulian
1100114878Sjulian	/* Now process upcall */
1101114878Sjulian	switch (s->state) {
1102114878Sjulian	/* Try to accept new L2CAP connection(s) */
1103114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_LISTENING:
1104114878Sjulian		while (ng_btsocket_rfcomm_session_accept(s) == 0)
1105114878Sjulian			;
1106114878Sjulian		break;
1107114878Sjulian
1108114878Sjulian	/* Process the results of the L2CAP connect */
1109114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_CONNECTING:
1110114878Sjulian		ng_btsocket_rfcomm_session_process_pcb(s);
1111114878Sjulian
1112114878Sjulian		if (ng_btsocket_rfcomm_session_connect(s) != 0) {
1113114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1114114878Sjulian			ng_btsocket_rfcomm_session_clean(s);
1115114878Sjulian		}
1116114878Sjulian		break;
1117114878Sjulian
1118114878Sjulian	/* Try to receive/send more data */
1119114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
1120114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_OPEN:
1121114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING:
1122114878Sjulian		ng_btsocket_rfcomm_session_process_pcb(s);
1123114878Sjulian
1124114878Sjulian		if (ng_btsocket_rfcomm_session_receive(s) != 0) {
1125114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1126114878Sjulian			ng_btsocket_rfcomm_session_clean(s);
1127114878Sjulian		} else if (ng_btsocket_rfcomm_session_send(s) != 0) {
1128114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1129114878Sjulian			ng_btsocket_rfcomm_session_clean(s);
1130114878Sjulian		}
1131114878Sjulian		break;
1132114878Sjulian
1133114878Sjulian	case NG_BTSOCKET_RFCOMM_SESSION_CLOSED:
1134114878Sjulian		break;
1135114878Sjulian
1136114878Sjulian	default:
1137114878Sjulian		panic("%s: Invalid session state=%d, flags=%#x\n",
1138114878Sjulian			__func__, s->state, s->flags);
1139114878Sjulian		break;
1140114878Sjulian	}
1141114878Sjulian} /* ng_btsocket_rfcomm_session_task */
1142114878Sjulian
1143114878Sjulian/*
1144114878Sjulian * Process RFCOMM connection indicator. Caller must hold s->session_mtx
1145114878Sjulian */
1146114878Sjulian
1147114878Sjulianstatic ng_btsocket_rfcomm_pcb_p
1148114878Sjulianng_btsocket_rfcomm_connect_ind(ng_btsocket_rfcomm_session_p s, int channel)
1149114878Sjulian{
1150114878Sjulian	ng_btsocket_rfcomm_pcb_p	 pcb = NULL, pcb1 = NULL;
1151114878Sjulian	ng_btsocket_l2cap_pcb_p		 l2pcb = NULL;
1152114878Sjulian	struct socket			*so1 = NULL;
1153114878Sjulian
1154114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1155114878Sjulian
1156114878Sjulian	/*
1157114878Sjulian	 * Try to find RFCOMM socket that listens on given source address
1158114878Sjulian	 * and channel. This will return the best possible match.
1159114878Sjulian	 */
1160114878Sjulian
1161114878Sjulian	l2pcb = so2l2cap_pcb(s->l2so);
1162114878Sjulian	pcb = ng_btsocket_rfcomm_pcb_listener(&l2pcb->src, channel);
1163114878Sjulian	if (pcb == NULL)
1164114878Sjulian		return (NULL);
1165114878Sjulian
1166114878Sjulian	/*
1167114878Sjulian	 * Check the pending connections queue and if we have space then
1168114878Sjulian	 * create new socket and set proper source and destination address,
1169114878Sjulian	 * and channel.
1170114878Sjulian	 */
1171114878Sjulian
1172114878Sjulian	mtx_lock(&pcb->pcb_mtx);
1173114878Sjulian
1174218757Sbz	if (pcb->so->so_qlen <= pcb->so->so_qlimit) {
1175218757Sbz		CURVNET_SET(pcb->so->so_vnet);
1176114878Sjulian		so1 = sonewconn(pcb->so, 0);
1177218757Sbz		CURVNET_RESTORE();
1178218757Sbz	}
1179114878Sjulian
1180114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
1181114878Sjulian
1182114878Sjulian	if (so1 == NULL)
1183114878Sjulian		return (NULL);
1184114878Sjulian
1185114878Sjulian	/*
1186114878Sjulian	 * If we got here than we have created new socket. So complete the
1187114878Sjulian	 * connection. Set source and destination address from the session.
1188114878Sjulian	 */
1189114878Sjulian
1190114878Sjulian	pcb1 = so2rfcomm_pcb(so1);
1191114878Sjulian	if (pcb1 == NULL)
1192114878Sjulian		panic("%s: pcb1 == NULL\n", __func__);
1193114878Sjulian
1194114878Sjulian	mtx_lock(&pcb1->pcb_mtx);
1195114878Sjulian
1196114878Sjulian	bcopy(&l2pcb->src, &pcb1->src, sizeof(pcb1->src));
1197114878Sjulian	bcopy(&l2pcb->dst, &pcb1->dst, sizeof(pcb1->dst));
1198114878Sjulian	pcb1->channel = channel;
1199114878Sjulian
1200114878Sjulian	/* Link new DLC to the session. We already hold s->session_mtx */
1201114878Sjulian	LIST_INSERT_HEAD(&s->dlcs, pcb1, session_next);
1202114878Sjulian	pcb1->session = s;
1203114878Sjulian
1204114878Sjulian	mtx_unlock(&pcb1->pcb_mtx);
1205114878Sjulian
1206114878Sjulian	return (pcb1);
1207114878Sjulian} /* ng_btsocket_rfcomm_connect_ind */
1208114878Sjulian
1209114878Sjulian/*
1210114878Sjulian * Process RFCOMM connect confirmation. Caller must hold s->session_mtx.
1211114878Sjulian */
1212114878Sjulian
1213114878Sjulianstatic void
1214114878Sjulianng_btsocket_rfcomm_connect_cfm(ng_btsocket_rfcomm_session_p s)
1215114878Sjulian{
1216161623Semax	ng_btsocket_rfcomm_pcb_p	pcb = NULL, pcb_next = NULL;
1217161623Semax	int				error;
1218114878Sjulian
1219114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1220114878Sjulian
1221114878Sjulian	/*
1222114878Sjulian	 * Wake up all waiting sockets and send PN request for each of them.
1223114878Sjulian	 * Note that timeout already been set in ng_btsocket_rfcomm_connect()
1224114878Sjulian	 *
1225114878Sjulian	 * Note: cannot use LIST_FOREACH because ng_btsocket_rfcomm_pcb_kill
1226114878Sjulian	 * will unlink DLC from the session
1227114878Sjulian	 */
1228114878Sjulian
1229114878Sjulian	for (pcb = LIST_FIRST(&s->dlcs); pcb != NULL; ) {
1230114878Sjulian		mtx_lock(&pcb->pcb_mtx);
1231114878Sjulian		pcb_next = LIST_NEXT(pcb, session_next);
1232114878Sjulian
1233114878Sjulian		if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT) {
1234114878Sjulian			pcb->mtu = s->mtu;
1235114878Sjulian			bcopy(&so2l2cap_pcb(s->l2so)->src, &pcb->src,
1236114878Sjulian				sizeof(pcb->src));
1237114878Sjulian
1238114878Sjulian			error = ng_btsocket_rfcomm_send_pn(pcb);
1239114878Sjulian			if (error == 0)
1240114878Sjulian				pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONFIGURING;
1241161623Semax			else
1242161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, error);
1243114878Sjulian		}
1244114878Sjulian
1245114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
1246114878Sjulian		pcb = pcb_next;
1247114878Sjulian	}
1248114878Sjulian} /* ng_btsocket_rfcomm_connect_cfm */
1249114878Sjulian
1250114878Sjulian/*****************************************************************************
1251114878Sjulian *****************************************************************************
1252114878Sjulian **                              RFCOMM sessions
1253114878Sjulian *****************************************************************************
1254114878Sjulian *****************************************************************************/
1255114878Sjulian
1256114878Sjulian/*
1257114878Sjulian * Create new RFCOMM session. That function WILL NOT take ownership over l2so.
1258114878Sjulian * Caller MUST free l2so if function failed.
1259114878Sjulian */
1260114878Sjulian
1261114878Sjulianstatic int
1262114878Sjulianng_btsocket_rfcomm_session_create(ng_btsocket_rfcomm_session_p *sp,
1263114878Sjulian		struct socket *l2so, bdaddr_p src, bdaddr_p dst,
1264114878Sjulian		struct thread *td)
1265114878Sjulian{
1266114878Sjulian	ng_btsocket_rfcomm_session_p	s = NULL;
1267114878Sjulian	struct sockaddr_l2cap		l2sa;
1268114878Sjulian	struct sockopt			l2sopt;
1269161579Semax	int				error;
1270161579Semax	u_int16_t			mtu;
1271114878Sjulian
1272114878Sjulian	mtx_assert(&ng_btsocket_rfcomm_sessions_mtx, MA_OWNED);
1273114878Sjulian
1274114878Sjulian	/* Allocate the RFCOMM session */
1275184205Sdes        s = malloc(sizeof(*s),
1276114878Sjulian		M_NETGRAPH_BTSOCKET_RFCOMM, M_NOWAIT | M_ZERO);
1277114878Sjulian        if (s == NULL)
1278114878Sjulian                return (ENOMEM);
1279114878Sjulian
1280114878Sjulian	/* Set defaults */
1281114878Sjulian	s->mtu = RFCOMM_DEFAULT_MTU;
1282114878Sjulian	s->flags = 0;
1283114878Sjulian	s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
1284114878Sjulian	NG_BT_MBUFQ_INIT(&s->outq, ifqmaxlen);
1285114878Sjulian
1286114878Sjulian	/*
1287114878Sjulian	 * XXX Mark session mutex as DUPOK to prevent "duplicated lock of
1288114878Sjulian	 * the same type" message. When accepting new L2CAP connection
1289114878Sjulian	 * ng_btsocket_rfcomm_session_accept() holds both session mutexes
1290114878Sjulian	 * for "old" (accepting) session and "new" (created) session.
1291114878Sjulian	 */
1292114878Sjulian
1293114878Sjulian	mtx_init(&s->session_mtx, "btsocks_rfcomm_session_mtx", NULL,
1294114878Sjulian		MTX_DEF|MTX_DUPOK);
1295114878Sjulian
1296114878Sjulian	LIST_INIT(&s->dlcs);
1297114878Sjulian
1298114878Sjulian	/* Prepare L2CAP socket */
1299130653Srwatson	SOCKBUF_LOCK(&l2so->so_rcv);
1300193272Sjhb	soupcall_set(l2so, SO_RCV, ng_btsocket_rfcomm_upcall, NULL);
1301130653Srwatson	SOCKBUF_UNLOCK(&l2so->so_rcv);
1302130653Srwatson	SOCKBUF_LOCK(&l2so->so_snd);
1303193272Sjhb	soupcall_set(l2so, SO_SND, ng_btsocket_rfcomm_upcall, NULL);
1304130653Srwatson	SOCKBUF_UNLOCK(&l2so->so_snd);
1305114878Sjulian	l2so->so_state |= SS_NBIO;
1306114878Sjulian	s->l2so = l2so;
1307114878Sjulian
1308114878Sjulian	mtx_lock(&s->session_mtx);
1309114878Sjulian
1310114878Sjulian	/*
1311114878Sjulian	 * "src" == NULL and "dst" == NULL means just create session.
1312114878Sjulian	 * caller must do the rest
1313114878Sjulian	 */
1314114878Sjulian
1315114878Sjulian	if (src == NULL && dst == NULL)
1316114878Sjulian		goto done;
1317114878Sjulian
1318114878Sjulian	/*
1319114878Sjulian	 * Set incoming MTU on L2CAP socket. It is RFCOMM session default MTU
1320114878Sjulian	 * plus 5 bytes: RFCOMM frame header, one extra byte for length and one
1321114878Sjulian	 * extra byte for credits.
1322114878Sjulian	 */
1323114878Sjulian
1324114878Sjulian	mtu = s->mtu + sizeof(struct rfcomm_frame_hdr) + 1 + 1;
1325114878Sjulian
1326114878Sjulian	l2sopt.sopt_dir = SOPT_SET;
1327114878Sjulian	l2sopt.sopt_level = SOL_L2CAP;
1328114878Sjulian	l2sopt.sopt_name = SO_L2CAP_IMTU;
1329114878Sjulian	l2sopt.sopt_val = (void *) &mtu;
1330114878Sjulian	l2sopt.sopt_valsize = sizeof(mtu);
1331114878Sjulian	l2sopt.sopt_td = NULL;
1332114878Sjulian
1333114878Sjulian	error = sosetopt(s->l2so, &l2sopt);
1334114878Sjulian	if (error != 0)
1335114878Sjulian		goto bad;
1336114878Sjulian
1337114878Sjulian	/* Bind socket to "src" address */
1338114878Sjulian	l2sa.l2cap_len = sizeof(l2sa);
1339114878Sjulian	l2sa.l2cap_family = AF_BLUETOOTH;
1340114878Sjulian	l2sa.l2cap_psm = (dst == NULL)? htole16(NG_L2CAP_PSM_RFCOMM) : 0;
1341114878Sjulian	bcopy(src, &l2sa.l2cap_bdaddr, sizeof(l2sa.l2cap_bdaddr));
1342114878Sjulian
1343114878Sjulian	error = sobind(s->l2so, (struct sockaddr *) &l2sa, td);
1344114878Sjulian	if (error != 0)
1345114878Sjulian		goto bad;
1346114878Sjulian
1347114878Sjulian	/* If "dst" is not NULL then initiate connect(), otherwise listen() */
1348114878Sjulian	if (dst == NULL) {
1349114878Sjulian		s->flags = 0;
1350114878Sjulian		s->state = NG_BTSOCKET_RFCOMM_SESSION_LISTENING;
1351114878Sjulian
1352114878Sjulian		error = solisten(s->l2so, 10, td);
1353114878Sjulian		if (error != 0)
1354114878Sjulian			goto bad;
1355114878Sjulian	} else {
1356114878Sjulian		s->flags = NG_BTSOCKET_RFCOMM_SESSION_INITIATOR;
1357114878Sjulian		s->state = NG_BTSOCKET_RFCOMM_SESSION_CONNECTING;
1358114878Sjulian
1359114878Sjulian		l2sa.l2cap_len = sizeof(l2sa);
1360114878Sjulian		l2sa.l2cap_family = AF_BLUETOOTH;
1361114878Sjulian		l2sa.l2cap_psm = htole16(NG_L2CAP_PSM_RFCOMM);
1362114878Sjulian	        bcopy(dst, &l2sa.l2cap_bdaddr, sizeof(l2sa.l2cap_bdaddr));
1363114878Sjulian
1364114878Sjulian		error = soconnect(s->l2so, (struct sockaddr *) &l2sa, td);
1365114878Sjulian		if (error != 0)
1366114878Sjulian			goto bad;
1367114878Sjulian	}
1368114878Sjulian
1369114878Sjuliandone:
1370114878Sjulian	LIST_INSERT_HEAD(&ng_btsocket_rfcomm_sessions, s, next);
1371114878Sjulian	*sp = s;
1372114878Sjulian
1373114878Sjulian	mtx_unlock(&s->session_mtx);
1374114878Sjulian
1375114878Sjulian	return (0);
1376114878Sjulian
1377114878Sjulianbad:
1378114878Sjulian	mtx_unlock(&s->session_mtx);
1379114878Sjulian
1380114878Sjulian	/* Return L2CAP socket back to its original state */
1381130653Srwatson	SOCKBUF_LOCK(&l2so->so_rcv);
1382193272Sjhb	soupcall_clear(s->l2so, SO_RCV);
1383130670Srwatson	SOCKBUF_UNLOCK(&l2so->so_rcv);
1384130653Srwatson	SOCKBUF_LOCK(&l2so->so_snd);
1385193272Sjhb	soupcall_clear(s->l2so, SO_SND);
1386130670Srwatson	SOCKBUF_UNLOCK(&l2so->so_snd);
1387114878Sjulian	l2so->so_state &= ~SS_NBIO;
1388114878Sjulian
1389114878Sjulian	mtx_destroy(&s->session_mtx);
1390114878Sjulian	bzero(s, sizeof(*s));
1391184205Sdes	free(s, M_NETGRAPH_BTSOCKET_RFCOMM);
1392114878Sjulian
1393114878Sjulian	return (error);
1394114878Sjulian} /* ng_btsocket_rfcomm_session_create */
1395114878Sjulian
1396114878Sjulian/*
1397114878Sjulian * Process accept() on RFCOMM session
1398114878Sjulian * XXX FIXME locking for "l2so"?
1399114878Sjulian */
1400114878Sjulian
1401114878Sjulianstatic int
1402114878Sjulianng_btsocket_rfcomm_session_accept(ng_btsocket_rfcomm_session_p s0)
1403114878Sjulian{
1404114878Sjulian	struct socket			*l2so = NULL;
1405114878Sjulian	struct sockaddr_l2cap		*l2sa = NULL;
1406114878Sjulian	ng_btsocket_l2cap_pcb_t		*l2pcb = NULL;
1407114878Sjulian	ng_btsocket_rfcomm_session_p	 s = NULL;
1408114878Sjulian	int				 error = 0;
1409114878Sjulian
1410114878Sjulian	mtx_assert(&ng_btsocket_rfcomm_sessions_mtx, MA_OWNED);
1411114878Sjulian	mtx_assert(&s0->session_mtx, MA_OWNED);
1412114878Sjulian
1413114878Sjulian	/* Check if there is a complete L2CAP connection in the queue */
1414114878Sjulian	if ((error = s0->l2so->so_error) != 0) {
1415114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
1416114878Sjulian"%s: Could not accept connection on L2CAP socket, error=%d\n", __func__, error);
1417114878Sjulian		s0->l2so->so_error = 0;
1418114878Sjulian
1419114878Sjulian		return (error);
1420114878Sjulian	}
1421114878Sjulian
1422129979Srwatson	ACCEPT_LOCK();
1423114878Sjulian	if (TAILQ_EMPTY(&s0->l2so->so_comp)) {
1424129979Srwatson		ACCEPT_UNLOCK();
1425130480Srwatson		if (s0->l2so->so_rcv.sb_state & SBS_CANTRCVMORE)
1426114878Sjulian			return (ECONNABORTED);
1427114878Sjulian		return (EWOULDBLOCK);
1428114878Sjulian	}
1429114878Sjulian
1430114878Sjulian	/* Accept incoming L2CAP connection */
1431114878Sjulian	l2so = TAILQ_FIRST(&s0->l2so->so_comp);
1432114878Sjulian	if (l2so == NULL)
1433114878Sjulian		panic("%s: l2so == NULL\n", __func__);
1434114878Sjulian
1435114878Sjulian	TAILQ_REMOVE(&s0->l2so->so_comp, l2so, so_list);
1436114878Sjulian	s0->l2so->so_qlen --;
1437129979Srwatson	l2so->so_qstate &= ~SQ_COMP;
1438129979Srwatson	l2so->so_head = NULL;
1439130387Srwatson	SOCK_LOCK(l2so);
1440114878Sjulian	soref(l2so);
1441114878Sjulian	l2so->so_state |= SS_NBIO;
1442130387Srwatson	SOCK_UNLOCK(l2so);
1443129979Srwatson	ACCEPT_UNLOCK();
1444114878Sjulian
1445114878Sjulian	error = soaccept(l2so, (struct sockaddr **) &l2sa);
1446114878Sjulian	if (error != 0) {
1447114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
1448114878Sjulian"%s: soaccept() on L2CAP socket failed, error=%d\n", __func__, error);
1449114878Sjulian		soclose(l2so);
1450114878Sjulian
1451114878Sjulian		return (error);
1452114878Sjulian	}
1453114878Sjulian
1454114878Sjulian	/*
1455114878Sjulian	 * Check if there is already active RFCOMM session between two devices.
1456114878Sjulian	 * If so then close L2CAP connection. We only support one RFCOMM session
1457114878Sjulian	 * between each pair of devices. Note that here we assume session in any
1458114878Sjulian	 * state. The session even could be in the middle of disconnecting.
1459114878Sjulian	 */
1460114878Sjulian
1461114878Sjulian	l2pcb = so2l2cap_pcb(l2so);
1462114878Sjulian	s = ng_btsocket_rfcomm_session_by_addr(&l2pcb->src, &l2pcb->dst);
1463114878Sjulian	if (s == NULL) {
1464114878Sjulian		/* Create a new RFCOMM session */
1465114878Sjulian		error = ng_btsocket_rfcomm_session_create(&s, l2so, NULL, NULL,
1466114878Sjulian				curthread /* XXX */);
1467114878Sjulian		if (error == 0) {
1468114878Sjulian			mtx_lock(&s->session_mtx);
1469114878Sjulian
1470114878Sjulian			s->flags = 0;
1471114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CONNECTED;
1472114878Sjulian
1473114878Sjulian			/*
1474114878Sjulian			 * Adjust MTU on incomming connection. Reserve 5 bytes:
1475114878Sjulian			 * RFCOMM frame header, one extra byte for length and
1476114878Sjulian			 * one extra byte for credits.
1477114878Sjulian			 */
1478114878Sjulian
1479114878Sjulian			s->mtu = min(l2pcb->imtu, l2pcb->omtu) -
1480114878Sjulian					sizeof(struct rfcomm_frame_hdr) - 1 - 1;
1481114878Sjulian
1482114878Sjulian			mtx_unlock(&s->session_mtx);
1483114878Sjulian		} else {
1484114878Sjulian			NG_BTSOCKET_RFCOMM_ALERT(
1485114878Sjulian"%s: Failed to create new RFCOMM session, error=%d\n", __func__, error);
1486114878Sjulian
1487114878Sjulian			soclose(l2so);
1488114878Sjulian		}
1489114878Sjulian	} else {
1490114878Sjulian		NG_BTSOCKET_RFCOMM_WARN(
1491114878Sjulian"%s: Rejecting duplicating RFCOMM session between src=%x:%x:%x:%x:%x:%x and " \
1492114878Sjulian"dst=%x:%x:%x:%x:%x:%x, state=%d, flags=%#x\n",	__func__,
1493114878Sjulian			l2pcb->src.b[5], l2pcb->src.b[4], l2pcb->src.b[3],
1494114878Sjulian			l2pcb->src.b[2], l2pcb->src.b[1], l2pcb->src.b[0],
1495114878Sjulian			l2pcb->dst.b[5], l2pcb->dst.b[4], l2pcb->dst.b[3],
1496114878Sjulian			l2pcb->dst.b[2], l2pcb->dst.b[1], l2pcb->dst.b[0],
1497114878Sjulian			s->state, s->flags);
1498114878Sjulian
1499114878Sjulian		error = EBUSY;
1500114878Sjulian		soclose(l2so);
1501114878Sjulian	}
1502114878Sjulian
1503114878Sjulian	return (error);
1504114878Sjulian} /* ng_btsocket_rfcomm_session_accept */
1505114878Sjulian
1506114878Sjulian/*
1507114878Sjulian * Process connect() on RFCOMM session
1508114878Sjulian * XXX FIXME locking for "l2so"?
1509114878Sjulian */
1510114878Sjulian
1511114878Sjulianstatic int
1512114878Sjulianng_btsocket_rfcomm_session_connect(ng_btsocket_rfcomm_session_p s)
1513114878Sjulian{
1514114878Sjulian	ng_btsocket_l2cap_pcb_p	l2pcb = so2l2cap_pcb(s->l2so);
1515114878Sjulian	int			error;
1516114878Sjulian
1517114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1518114878Sjulian
1519114878Sjulian	/* First check if connection has failed */
1520114878Sjulian	if ((error = s->l2so->so_error) != 0) {
1521114878Sjulian		s->l2so->so_error = 0;
1522114878Sjulian
1523114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
1524114878Sjulian"%s: Could not connect RFCOMM session, error=%d, state=%d, flags=%#x\n",
1525114878Sjulian			__func__, error, s->state, s->flags);
1526114878Sjulian
1527114878Sjulian		return (error);
1528114878Sjulian	}
1529114878Sjulian
1530114878Sjulian	/* Is connection still in progress? */
1531114878Sjulian	if (s->l2so->so_state & SS_ISCONNECTING)
1532114878Sjulian		return (0);
1533114878Sjulian
1534114878Sjulian	/*
1535114878Sjulian	 * If we got here then we are connected. Send SABM on DLCI 0 to
1536114878Sjulian	 * open multiplexor channel.
1537114878Sjulian	 */
1538114878Sjulian
1539114878Sjulian	if (error == 0) {
1540114878Sjulian		s->state = NG_BTSOCKET_RFCOMM_SESSION_CONNECTED;
1541114878Sjulian
1542114878Sjulian		/*
1543114878Sjulian		 * Adjust MTU on outgoing connection. Reserve 5 bytes: RFCOMM
1544114878Sjulian		 * frame header, one extra byte for length and one extra byte
1545114878Sjulian		 * for credits.
1546114878Sjulian		 */
1547114878Sjulian
1548114878Sjulian		s->mtu = min(l2pcb->imtu, l2pcb->omtu) -
1549114878Sjulian				sizeof(struct rfcomm_frame_hdr) - 1 - 1;
1550114878Sjulian
1551114878Sjulian		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_SABM,0);
1552114878Sjulian		if (error == 0)
1553114878Sjulian			error = ng_btsocket_rfcomm_task_wakeup();
1554114878Sjulian	}
1555114878Sjulian
1556114878Sjulian	return (error);
1557114878Sjulian}/* ng_btsocket_rfcomm_session_connect */
1558114878Sjulian
1559114878Sjulian/*
1560114878Sjulian * Receive data on RFCOMM session
1561114878Sjulian * XXX FIXME locking for "l2so"?
1562114878Sjulian */
1563114878Sjulian
1564114878Sjulianstatic int
1565114878Sjulianng_btsocket_rfcomm_session_receive(ng_btsocket_rfcomm_session_p s)
1566114878Sjulian{
1567114878Sjulian	struct mbuf	*m = NULL;
1568114878Sjulian	struct uio	 uio;
1569114878Sjulian	int		 more, flags, error;
1570114878Sjulian
1571114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1572114878Sjulian
1573114878Sjulian	/* Can we read from the L2CAP socket? */
1574114878Sjulian	if (!soreadable(s->l2so))
1575114878Sjulian		return (0);
1576114878Sjulian
1577114878Sjulian	/* First check for error on L2CAP socket */
1578114878Sjulian	if ((error = s->l2so->so_error) != 0) {
1579114878Sjulian		s->l2so->so_error = 0;
1580114878Sjulian
1581114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
1582114878Sjulian"%s: Could not receive data from L2CAP socket, error=%d, state=%d, flags=%#x\n",
1583114878Sjulian			__func__, error, s->state, s->flags);
1584114878Sjulian
1585114878Sjulian		return (error);
1586114878Sjulian	}
1587114878Sjulian
1588114878Sjulian	/*
1589114878Sjulian	 * Read all packets from the L2CAP socket.
1590114878Sjulian	 * XXX FIXME/VERIFY is that correct? For now use m->m_nextpkt as
1591114878Sjulian	 * indication that there is more packets on the socket's buffer.
1592114878Sjulian	 * Also what should we use in uio.uio_resid?
1593114878Sjulian	 * May be s->mtu + sizeof(struct rfcomm_frame_hdr) + 1 + 1?
1594114878Sjulian	 */
1595114878Sjulian
1596114878Sjulian	for (more = 1; more; ) {
1597114878Sjulian		/* Try to get next packet from socket */
1598114878Sjulian		bzero(&uio, sizeof(uio));
1599114878Sjulian/*		uio.uio_td = NULL; */
1600114878Sjulian		uio.uio_resid = 1000000000;
1601114878Sjulian		flags = MSG_DONTWAIT;
1602114878Sjulian
1603114878Sjulian		m = NULL;
1604160619Srwatson		error = soreceive(s->l2so, NULL, &uio, &m,
1605160619Srwatson		    (struct mbuf **) NULL, &flags);
1606114878Sjulian		if (error != 0) {
1607114878Sjulian			if (error == EWOULDBLOCK)
1608114878Sjulian				return (0); /* XXX can happen? */
1609114878Sjulian
1610114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
1611114878Sjulian"%s: Could not receive data from L2CAP socket, error=%d\n", __func__, error);
1612114878Sjulian
1613114878Sjulian			return (error);
1614114878Sjulian		}
1615114878Sjulian
1616114878Sjulian		more = (m->m_nextpkt != NULL);
1617114878Sjulian		m->m_nextpkt = NULL;
1618114878Sjulian
1619114878Sjulian		ng_btsocket_rfcomm_receive_frame(s, m);
1620114878Sjulian	}
1621114878Sjulian
1622114878Sjulian	return (0);
1623114878Sjulian} /* ng_btsocket_rfcomm_session_receive */
1624114878Sjulian
1625114878Sjulian/*
1626114878Sjulian * Send data on RFCOMM session
1627114878Sjulian * XXX FIXME locking for "l2so"?
1628114878Sjulian */
1629114878Sjulian
1630114878Sjulianstatic int
1631114878Sjulianng_btsocket_rfcomm_session_send(ng_btsocket_rfcomm_session_p s)
1632114878Sjulian{
1633114878Sjulian	struct mbuf	*m = NULL;
1634114878Sjulian	int		 error;
1635114878Sjulian
1636114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1637114878Sjulian
1638114878Sjulian	/* Send as much as we can from the session queue */
1639114878Sjulian	while (sowriteable(s->l2so)) {
1640114878Sjulian		/* Check if socket still OK */
1641114878Sjulian		if ((error = s->l2so->so_error) != 0) {
1642114878Sjulian			s->l2so->so_error = 0;
1643114878Sjulian
1644114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
1645114878Sjulian"%s: Detected error=%d on L2CAP socket, state=%d, flags=%#x\n",
1646114878Sjulian				__func__, error, s->state, s->flags);
1647114878Sjulian
1648114878Sjulian			return (error);
1649114878Sjulian		}
1650114878Sjulian
1651114878Sjulian		NG_BT_MBUFQ_DEQUEUE(&s->outq, m);
1652114878Sjulian		if (m == NULL)
1653114878Sjulian			return (0); /* we are done */
1654114878Sjulian
1655114878Sjulian		/* Call send function on the L2CAP socket */
1656170972Semax		error = (*s->l2so->so_proto->pr_usrreqs->pru_send)(s->l2so,
1657170972Semax				0, m, NULL, NULL, curthread /* XXX */);
1658114878Sjulian		if (error != 0) {
1659114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
1660114878Sjulian"%s: Could not send data to L2CAP socket, error=%d\n", __func__, error);
1661114878Sjulian
1662114878Sjulian			return (error);
1663114878Sjulian		}
1664114878Sjulian	}
1665114878Sjulian
1666114878Sjulian	return (0);
1667114878Sjulian} /* ng_btsocket_rfcomm_session_send */
1668114878Sjulian
1669114878Sjulian/*
1670114878Sjulian * Close and disconnect all DLCs for the given session. Caller must hold
1671114878Sjulian * s->sesson_mtx. Will wakeup session.
1672114878Sjulian */
1673114878Sjulian
1674114878Sjulianstatic void
1675114878Sjulianng_btsocket_rfcomm_session_clean(ng_btsocket_rfcomm_session_p s)
1676114878Sjulian{
1677161623Semax	ng_btsocket_rfcomm_pcb_p	pcb = NULL, pcb_next = NULL;
1678161623Semax	int				error;
1679114878Sjulian
1680114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1681114878Sjulian
1682114878Sjulian	/*
1683114878Sjulian	 * Note: cannot use LIST_FOREACH because ng_btsocket_rfcomm_pcb_kill
1684114878Sjulian	 * will unlink DLC from the session
1685114878Sjulian	 */
1686114878Sjulian
1687114878Sjulian	for (pcb = LIST_FIRST(&s->dlcs); pcb != NULL; ) {
1688114878Sjulian		mtx_lock(&pcb->pcb_mtx);
1689114878Sjulian		pcb_next = LIST_NEXT(pcb, session_next);
1690114878Sjulian
1691114878Sjulian		NG_BTSOCKET_RFCOMM_INFO(
1692114878Sjulian"%s: Disconnecting dlci=%d, state=%d, flags=%#x\n",
1693114878Sjulian			__func__, pcb->dlci, pcb->state, pcb->flags);
1694114878Sjulian
1695114878Sjulian		if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_CONNECTED)
1696114878Sjulian			error = ECONNRESET;
1697114878Sjulian		else
1698114878Sjulian			error = ECONNREFUSED;
1699114878Sjulian
1700161623Semax		ng_btsocket_rfcomm_pcb_kill(pcb, error);
1701114878Sjulian
1702114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
1703114878Sjulian		pcb = pcb_next;
1704114878Sjulian	}
1705114878Sjulian} /* ng_btsocket_rfcomm_session_clean */
1706114878Sjulian
1707114878Sjulian/*
1708114878Sjulian * Process all DLCs on the session. Caller MUST hold s->session_mtx.
1709114878Sjulian */
1710114878Sjulian
1711114878Sjulianstatic void
1712114878Sjulianng_btsocket_rfcomm_session_process_pcb(ng_btsocket_rfcomm_session_p s)
1713114878Sjulian{
1714161623Semax	ng_btsocket_rfcomm_pcb_p	pcb = NULL, pcb_next = NULL;
1715161623Semax	int				error;
1716114878Sjulian
1717114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1718114878Sjulian
1719114878Sjulian	/*
1720114878Sjulian	 * Note: cannot use LIST_FOREACH because ng_btsocket_rfcomm_pcb_kill
1721114878Sjulian	 * will unlink DLC from the session
1722114878Sjulian	 */
1723114878Sjulian
1724114878Sjulian	for (pcb = LIST_FIRST(&s->dlcs); pcb != NULL; ) {
1725114878Sjulian		mtx_lock(&pcb->pcb_mtx);
1726114878Sjulian		pcb_next = LIST_NEXT(pcb, session_next);
1727114878Sjulian
1728114878Sjulian		switch (pcb->state) {
1729114878Sjulian
1730114878Sjulian		/*
1731114878Sjulian		 * If DLC in W4_CONNECT state then we should check for both
1732114878Sjulian		 * timeout and detach.
1733114878Sjulian		 */
1734114878Sjulian
1735114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
1736161623Semax			if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_DETACHED)
1737161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, 0);
1738161623Semax			else if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT)
1739161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, ETIMEDOUT);
1740114878Sjulian			break;
1741114878Sjulian
1742114878Sjulian		/*
1743114878Sjulian		 * If DLC in CONFIGURING or CONNECTING state then we only
1744114878Sjulian		 * should check for timeout. If detach() was called then
1745114878Sjulian		 * DLC will be moved into DISCONNECTING state.
1746114878Sjulian		 */
1747114878Sjulian
1748114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING:
1749114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
1750114878Sjulian			if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT)
1751161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, ETIMEDOUT);
1752114878Sjulian			break;
1753114878Sjulian
1754114878Sjulian		/*
1755114878Sjulian		 * If DLC in CONNECTED state then we need to send data (if any)
1756114878Sjulian		 * from the socket's send queue. Note that we will send data
1757114878Sjulian		 * from either all sockets or none. This may overload session's
1758114878Sjulian		 * outgoing queue (but we do not check for that).
1759114878Sjulian		 *
1760114878Sjulian 		 * XXX FIXME need scheduler for RFCOMM sockets
1761114878Sjulian		 */
1762114878Sjulian
1763114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_CONNECTED:
1764114878Sjulian			error = ng_btsocket_rfcomm_pcb_send(pcb, ALOT);
1765114878Sjulian			if (error != 0)
1766161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, error);
1767114878Sjulian			break;
1768114878Sjulian
1769114878Sjulian		/*
1770114878Sjulian		 * If DLC in DISCONNECTING state then we must send DISC frame.
1771114878Sjulian		 * Note that if DLC has timeout set then we do not need to
1772114878Sjulian		 * resend DISC frame.
1773114878Sjulian		 *
1774114878Sjulian		 * XXX FIXME need to drain all data from the socket's queue
1775114878Sjulian		 * if LINGER option was set
1776114878Sjulian		 */
1777114878Sjulian
1778114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
1779114878Sjulian			if (!(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)) {
1780114878Sjulian				error = ng_btsocket_rfcomm_send_command(
1781114878Sjulian						pcb->session, RFCOMM_FRAME_DISC,
1782114878Sjulian						pcb->dlci);
1783114878Sjulian				if (error == 0)
1784114878Sjulian					ng_btsocket_rfcomm_timeout(pcb);
1785161623Semax				else
1786161623Semax					ng_btsocket_rfcomm_pcb_kill(pcb, error);
1787114878Sjulian			} else if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT)
1788161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, ETIMEDOUT);
1789114878Sjulian			break;
1790114878Sjulian
1791114878Sjulian/*		case NG_BTSOCKET_RFCOMM_DLC_CLOSED: */
1792114878Sjulian		default:
1793114878Sjulian			panic("%s: Invalid DLC state=%d, flags=%#x\n",
1794114878Sjulian				__func__, pcb->state, pcb->flags);
1795114878Sjulian			break;
1796114878Sjulian		}
1797114878Sjulian
1798114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
1799114878Sjulian		pcb = pcb_next;
1800114878Sjulian	}
1801114878Sjulian} /* ng_btsocket_rfcomm_session_process_pcb */
1802114878Sjulian
1803114878Sjulian/*
1804114878Sjulian * Find RFCOMM session between "src" and "dst".
1805114878Sjulian * Caller MUST hold ng_btsocket_rfcomm_sessions_mtx.
1806114878Sjulian */
1807114878Sjulian
1808114878Sjulianstatic ng_btsocket_rfcomm_session_p
1809114878Sjulianng_btsocket_rfcomm_session_by_addr(bdaddr_p src, bdaddr_p dst)
1810114878Sjulian{
1811114878Sjulian	ng_btsocket_rfcomm_session_p	s = NULL;
1812114878Sjulian	ng_btsocket_l2cap_pcb_p		l2pcb = NULL;
1813114878Sjulian	int				any_src;
1814114878Sjulian
1815114878Sjulian	mtx_assert(&ng_btsocket_rfcomm_sessions_mtx, MA_OWNED);
1816114878Sjulian
1817114878Sjulian	any_src = (bcmp(src, NG_HCI_BDADDR_ANY, sizeof(*src)) == 0);
1818114878Sjulian
1819114878Sjulian	LIST_FOREACH(s, &ng_btsocket_rfcomm_sessions, next) {
1820114878Sjulian		l2pcb = so2l2cap_pcb(s->l2so);
1821114878Sjulian
1822114878Sjulian		if ((any_src || bcmp(&l2pcb->src, src, sizeof(*src)) == 0) &&
1823114878Sjulian		    bcmp(&l2pcb->dst, dst, sizeof(*dst)) == 0)
1824114878Sjulian			break;
1825114878Sjulian	}
1826114878Sjulian
1827114878Sjulian	return (s);
1828114878Sjulian} /* ng_btsocket_rfcomm_session_by_addr */
1829114878Sjulian
1830114878Sjulian/*****************************************************************************
1831114878Sjulian *****************************************************************************
1832114878Sjulian **                                  RFCOMM
1833114878Sjulian *****************************************************************************
1834114878Sjulian *****************************************************************************/
1835114878Sjulian
1836114878Sjulian/*
1837114878Sjulian * Process incoming RFCOMM frame. Caller must hold s->session_mtx.
1838114878Sjulian * XXX FIXME check frame length
1839114878Sjulian */
1840114878Sjulian
1841114878Sjulianstatic int
1842114878Sjulianng_btsocket_rfcomm_receive_frame(ng_btsocket_rfcomm_session_p s,
1843114878Sjulian		struct mbuf *m0)
1844114878Sjulian{
1845114878Sjulian	struct rfcomm_frame_hdr	*hdr = NULL;
1846114878Sjulian	struct mbuf		*m = NULL;
1847114878Sjulian	u_int16_t		 length;
1848114878Sjulian	u_int8_t		 dlci, type;
1849114878Sjulian	int			 error = 0;
1850114878Sjulian
1851114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1852114878Sjulian
1853114878Sjulian	/* Pullup as much as we can into first mbuf (for direct access) */
1854114878Sjulian	length = min(m0->m_pkthdr.len, MHLEN);
1855114878Sjulian	if (m0->m_len < length) {
1856114878Sjulian		if ((m0 = m_pullup(m0, length)) == NULL) {
1857114878Sjulian			NG_BTSOCKET_RFCOMM_ALERT(
1858114878Sjulian"%s: m_pullup(%d) failed\n", __func__, length);
1859114878Sjulian
1860114878Sjulian			return (ENOBUFS);
1861114878Sjulian		}
1862114878Sjulian	}
1863114878Sjulian
1864114878Sjulian	hdr = mtod(m0, struct rfcomm_frame_hdr *);
1865114878Sjulian	dlci = RFCOMM_DLCI(hdr->address);
1866114878Sjulian	type = RFCOMM_TYPE(hdr->control);
1867114878Sjulian
1868114878Sjulian	/* Test EA bit in length. If not set then we have 2 bytes of length */
1869114878Sjulian	if (!RFCOMM_EA(hdr->length)) {
1870114878Sjulian		bcopy(&hdr->length, &length, sizeof(length));
1871144721Semax		length = le16toh(length) >> 1;
1872114878Sjulian		m_adj(m0, sizeof(*hdr) + 1);
1873114878Sjulian	} else {
1874114878Sjulian		length = hdr->length >> 1;
1875114878Sjulian		m_adj(m0, sizeof(*hdr));
1876114878Sjulian	}
1877114878Sjulian
1878114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
1879114878Sjulian"%s: Got frame type=%#x, dlci=%d, length=%d, cr=%d, pf=%d, len=%d\n",
1880114878Sjulian		__func__, type, dlci, length, RFCOMM_CR(hdr->address),
1881114878Sjulian		RFCOMM_PF(hdr->control), m0->m_pkthdr.len);
1882114878Sjulian
1883114878Sjulian	/*
1884114878Sjulian	 * Get FCS (the last byte in the frame)
1885114878Sjulian	 * XXX this will not work if mbuf chain ends with empty mbuf.
1886114878Sjulian	 * XXX let's hope it never happens :)
1887114878Sjulian	 */
1888114878Sjulian
1889114878Sjulian	for (m = m0; m->m_next != NULL; m = m->m_next)
1890114878Sjulian		;
1891114878Sjulian	if (m->m_len <= 0)
1892114878Sjulian		panic("%s: Empty mbuf at the end of the chain, len=%d\n",
1893114878Sjulian			__func__, m->m_len);
1894114878Sjulian
1895114878Sjulian	/*
1896114878Sjulian	 * Check FCS. We only need to calculate FCS on first 2 or 3 bytes
1897114878Sjulian	 * and already m_pullup'ed mbuf chain, so it should be safe.
1898114878Sjulian	 */
1899114878Sjulian
1900114878Sjulian	if (ng_btsocket_rfcomm_check_fcs((u_int8_t *) hdr, type, m->m_data[m->m_len - 1])) {
1901114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
1902114878Sjulian"%s: Invalid RFCOMM packet. Bad checksum\n", __func__);
1903114878Sjulian		NG_FREE_M(m0);
1904114878Sjulian
1905114878Sjulian		return (EINVAL);
1906114878Sjulian	}
1907114878Sjulian
1908114878Sjulian	m_adj(m0, -1); /* Trim FCS byte */
1909114878Sjulian
1910114878Sjulian	/*
1911114878Sjulian	 * Process RFCOMM frame.
1912114878Sjulian	 *
1913114878Sjulian	 * From TS 07.10 spec
1914114878Sjulian	 *
1915114878Sjulian	 * "... In the case where a SABM or DISC command with the P bit set
1916114878Sjulian	 * to 0 is received then the received frame shall be discarded..."
1917114878Sjulian 	 *
1918114878Sjulian	 * "... If a unsolicited DM response is received then the frame shall
1919114878Sjulian	 * be processed irrespective of the P/F setting... "
1920114878Sjulian	 *
1921114878Sjulian	 * "... The station may transmit response frames with the F bit set
1922114878Sjulian	 * to 0 at any opportunity on an asynchronous basis. However, in the
1923114878Sjulian	 * case where a UA response is received with the F bit set to 0 then
1924114878Sjulian	 * the received frame shall be discarded..."
1925114878Sjulian	 *
1926114878Sjulian	 * From Bluetooth spec
1927114878Sjulian	 *
1928114878Sjulian	 * "... When credit based flow control is being used, the meaning of
1929114878Sjulian	 * the P/F bit in the control field of the RFCOMM header is redefined
1930114878Sjulian	 * for UIH frames..."
1931114878Sjulian	 */
1932114878Sjulian
1933114878Sjulian	switch (type) {
1934114878Sjulian	case RFCOMM_FRAME_SABM:
1935114878Sjulian		if (RFCOMM_PF(hdr->control))
1936114878Sjulian			error = ng_btsocket_rfcomm_receive_sabm(s, dlci);
1937114878Sjulian		break;
1938114878Sjulian
1939114878Sjulian	case RFCOMM_FRAME_DISC:
1940114878Sjulian		if (RFCOMM_PF(hdr->control))
1941114878Sjulian			error = ng_btsocket_rfcomm_receive_disc(s, dlci);
1942114878Sjulian		break;
1943114878Sjulian
1944114878Sjulian	case RFCOMM_FRAME_UA:
1945114878Sjulian		if (RFCOMM_PF(hdr->control))
1946114878Sjulian			error = ng_btsocket_rfcomm_receive_ua(s, dlci);
1947114878Sjulian		break;
1948114878Sjulian
1949114878Sjulian	case RFCOMM_FRAME_DM:
1950114878Sjulian		error = ng_btsocket_rfcomm_receive_dm(s, dlci);
1951114878Sjulian		break;
1952114878Sjulian
1953114878Sjulian	case RFCOMM_FRAME_UIH:
1954114878Sjulian		if (dlci == 0)
1955114878Sjulian			error = ng_btsocket_rfcomm_receive_mcc(s, m0);
1956114878Sjulian		else
1957114878Sjulian			error = ng_btsocket_rfcomm_receive_uih(s, dlci,
1958114878Sjulian					RFCOMM_PF(hdr->control), m0);
1959114878Sjulian
1960114878Sjulian		return (error);
1961114878Sjulian		/* NOT REACHED */
1962114878Sjulian
1963114878Sjulian	default:
1964114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
1965114878Sjulian"%s: Invalid RFCOMM packet. Unknown type=%#x\n", __func__, type);
1966114878Sjulian		error = EINVAL;
1967114878Sjulian		break;
1968114878Sjulian	}
1969114878Sjulian
1970114878Sjulian	NG_FREE_M(m0);
1971114878Sjulian
1972114878Sjulian	return (error);
1973114878Sjulian} /* ng_btsocket_rfcomm_receive_frame */
1974114878Sjulian
1975114878Sjulian/*
1976114878Sjulian * Process RFCOMM SABM frame
1977114878Sjulian */
1978114878Sjulian
1979114878Sjulianstatic int
1980114878Sjulianng_btsocket_rfcomm_receive_sabm(ng_btsocket_rfcomm_session_p s, int dlci)
1981114878Sjulian{
1982161623Semax	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
1983161623Semax	int				error = 0;
1984114878Sjulian
1985114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
1986114878Sjulian
1987114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
1988114878Sjulian"%s: Got SABM, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
1989114878Sjulian		__func__, s->state, s->flags, s->mtu, dlci);
1990114878Sjulian
1991114878Sjulian	/* DLCI == 0 means open multiplexor channel */
1992114878Sjulian	if (dlci == 0) {
1993114878Sjulian		switch (s->state) {
1994114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
1995114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_OPEN:
1996114878Sjulian			error = ng_btsocket_rfcomm_send_command(s,
1997114878Sjulian					RFCOMM_FRAME_UA, dlci);
1998114878Sjulian			if (error == 0) {
1999114878Sjulian				s->state = NG_BTSOCKET_RFCOMM_SESSION_OPEN;
2000114878Sjulian				ng_btsocket_rfcomm_connect_cfm(s);
2001114878Sjulian			} else {
2002114878Sjulian				s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
2003114878Sjulian				ng_btsocket_rfcomm_session_clean(s);
2004114878Sjulian			}
2005114878Sjulian			break;
2006114878Sjulian
2007114878Sjulian		default:
2008114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2009114878Sjulian"%s: Got SABM for session in invalid state state=%d, flags=%#x\n",
2010114878Sjulian				__func__, s->state, s->flags);
2011114878Sjulian			error = EINVAL;
2012114878Sjulian			break;
2013114878Sjulian		}
2014114878Sjulian
2015114878Sjulian		return (error);
2016114878Sjulian	}
2017114878Sjulian
2018114878Sjulian	/* Make sure multiplexor channel is open */
2019114878Sjulian	if (s->state != NG_BTSOCKET_RFCOMM_SESSION_OPEN) {
2020114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
2021114878Sjulian"%s: Got SABM for dlci=%d with mulitplexor channel closed, state=%d, " \
2022114878Sjulian"flags=%#x\n",		__func__, dlci, s->state, s->flags);
2023114878Sjulian
2024114878Sjulian		return (EINVAL);
2025114878Sjulian	}
2026114878Sjulian
2027114878Sjulian	/*
2028114878Sjulian	 * Check if we have this DLCI. This might happen when remote
2029114878Sjulian	 * peer uses PN command before actual open (SABM) happens.
2030114878Sjulian	 */
2031114878Sjulian
2032114878Sjulian	pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2033114878Sjulian	if (pcb != NULL) {
2034114878Sjulian		mtx_lock(&pcb->pcb_mtx);
2035114878Sjulian
2036114878Sjulian		if (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTING) {
2037114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
2038114878Sjulian"%s: Got SABM for dlci=%d in invalid state=%d, flags=%#x\n",
2039114878Sjulian				__func__, dlci, pcb->state, pcb->flags);
2040114878Sjulian			mtx_unlock(&pcb->pcb_mtx);
2041114878Sjulian
2042114878Sjulian			return (ENOENT);
2043114878Sjulian		}
2044114878Sjulian
2045114878Sjulian		ng_btsocket_rfcomm_untimeout(pcb);
2046114878Sjulian
2047114878Sjulian		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_UA,dlci);
2048114878Sjulian		if (error == 0)
2049114878Sjulian			error = ng_btsocket_rfcomm_send_msc(pcb);
2050114878Sjulian
2051114878Sjulian		if (error == 0) {
2052114878Sjulian			pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTED;
2053114878Sjulian			soisconnected(pcb->so);
2054161623Semax		} else
2055161623Semax			ng_btsocket_rfcomm_pcb_kill(pcb, error);
2056114878Sjulian
2057114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
2058114878Sjulian
2059114878Sjulian		return (error);
2060114878Sjulian	}
2061114878Sjulian
2062114878Sjulian	/*
2063114878Sjulian	 * We do not have requested DLCI, so it must be an incoming connection
2064114878Sjulian	 * with default parameters. Try to accept it.
2065114878Sjulian	 */
2066114878Sjulian
2067114878Sjulian	pcb = ng_btsocket_rfcomm_connect_ind(s, RFCOMM_SRVCHANNEL(dlci));
2068114878Sjulian	if (pcb != NULL) {
2069114878Sjulian		mtx_lock(&pcb->pcb_mtx);
2070114878Sjulian
2071114878Sjulian		pcb->dlci = dlci;
2072114878Sjulian
2073114878Sjulian		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_UA,dlci);
2074121054Semax		if (error == 0)
2075121054Semax			error = ng_btsocket_rfcomm_send_msc(pcb);
2076121054Semax
2077114878Sjulian		if (error == 0) {
2078114878Sjulian			pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTED;
2079114878Sjulian			soisconnected(pcb->so);
2080161623Semax		} else
2081161623Semax			ng_btsocket_rfcomm_pcb_kill(pcb, error);
2082114878Sjulian
2083114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
2084114878Sjulian	} else
2085114878Sjulian		/* Nobody is listen()ing on the requested DLCI */
2086114878Sjulian		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_DM,dlci);
2087114878Sjulian
2088114878Sjulian	return (error);
2089114878Sjulian} /* ng_btsocket_rfcomm_receive_sabm */
2090114878Sjulian
2091114878Sjulian/*
2092114878Sjulian * Process RFCOMM DISC frame
2093114878Sjulian */
2094114878Sjulian
2095114878Sjulianstatic int
2096114878Sjulianng_btsocket_rfcomm_receive_disc(ng_btsocket_rfcomm_session_p s, int dlci)
2097114878Sjulian{
2098114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
2099114878Sjulian	int				error = 0;
2100114878Sjulian
2101114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2102114878Sjulian
2103114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2104114878Sjulian"%s: Got DISC, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
2105114878Sjulian		__func__, s->state, s->flags, s->mtu, dlci);
2106114878Sjulian
2107114878Sjulian	/* DLCI == 0 means close multiplexor channel */
2108114878Sjulian	if (dlci == 0) {
2109114878Sjulian		/* XXX FIXME assume that remote side will close the socket */
2110114878Sjulian		error = ng_btsocket_rfcomm_send_command(s, RFCOMM_FRAME_UA, 0);
2111128591Semax		if (error == 0) {
2112128591Semax			if (s->state == NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING)
2113128591Semax				s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED; /* XXX */
2114128591Semax			else
2115128591Semax				s->state = NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING;
2116128591Semax		} else
2117114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED; /* XXX */
2118114878Sjulian
2119114878Sjulian		ng_btsocket_rfcomm_session_clean(s);
2120114878Sjulian	} else {
2121114878Sjulian		pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2122114878Sjulian		if (pcb != NULL) {
2123161623Semax			int	err;
2124114878Sjulian
2125114878Sjulian			mtx_lock(&pcb->pcb_mtx);
2126114878Sjulian
2127114878Sjulian			NG_BTSOCKET_RFCOMM_INFO(
2128114878Sjulian"%s: Got DISC for dlci=%d, state=%d, flags=%#x\n",
2129114878Sjulian				__func__, dlci, pcb->state, pcb->flags);
2130114878Sjulian
2131114878Sjulian			error = ng_btsocket_rfcomm_send_command(s,
2132114878Sjulian					RFCOMM_FRAME_UA, dlci);
2133114878Sjulian
2134114878Sjulian			if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_CONNECTED)
2135114878Sjulian				err = 0;
2136114878Sjulian			else
2137114878Sjulian				err = ECONNREFUSED;
2138114878Sjulian
2139161623Semax			ng_btsocket_rfcomm_pcb_kill(pcb, err);
2140114878Sjulian
2141114878Sjulian			mtx_unlock(&pcb->pcb_mtx);
2142114878Sjulian		} else {
2143114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2144114878Sjulian"%s: Got DISC for non-existing dlci=%d\n", __func__, dlci);
2145114878Sjulian
2146114878Sjulian			error = ng_btsocket_rfcomm_send_command(s,
2147114878Sjulian					RFCOMM_FRAME_DM, dlci);
2148114878Sjulian		}
2149114878Sjulian	}
2150114878Sjulian
2151114878Sjulian	return (error);
2152114878Sjulian} /* ng_btsocket_rfcomm_receive_disc */
2153114878Sjulian
2154114878Sjulian/*
2155114878Sjulian * Process RFCOMM UA frame
2156114878Sjulian */
2157114878Sjulian
2158114878Sjulianstatic int
2159114878Sjulianng_btsocket_rfcomm_receive_ua(ng_btsocket_rfcomm_session_p s, int dlci)
2160114878Sjulian{
2161114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
2162114878Sjulian	int				error = 0;
2163114878Sjulian
2164114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2165114878Sjulian
2166114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2167114878Sjulian"%s: Got UA, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
2168114878Sjulian		__func__, s->state, s->flags, s->mtu, dlci);
2169114878Sjulian
2170114878Sjulian	/* dlci == 0 means multiplexor channel */
2171114878Sjulian	if (dlci == 0) {
2172114878Sjulian		switch (s->state) {
2173114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
2174114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_OPEN;
2175114878Sjulian			ng_btsocket_rfcomm_connect_cfm(s);
2176114878Sjulian			break;
2177114878Sjulian
2178114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING:
2179114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
2180114878Sjulian			ng_btsocket_rfcomm_session_clean(s);
2181114878Sjulian			break;
2182114878Sjulian
2183114878Sjulian		default:
2184114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2185114878Sjulian"%s: Got UA for session in invalid state=%d(%d), flags=%#x, mtu=%d\n",
2186114878Sjulian				__func__, s->state, INITIATOR(s), s->flags,
2187114878Sjulian				s->mtu);
2188114878Sjulian			error = ENOENT;
2189114878Sjulian			break;
2190114878Sjulian		}
2191114878Sjulian
2192114878Sjulian		return (error);
2193114878Sjulian	}
2194114878Sjulian
2195114878Sjulian	/* Check if we have this DLCI */
2196114878Sjulian	pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2197114878Sjulian	if (pcb != NULL) {
2198114878Sjulian		mtx_lock(&pcb->pcb_mtx);
2199114878Sjulian
2200114878Sjulian		NG_BTSOCKET_RFCOMM_INFO(
2201114878Sjulian"%s: Got UA for dlci=%d, state=%d, flags=%#x\n",
2202114878Sjulian			__func__, dlci, pcb->state, pcb->flags);
2203114878Sjulian
2204114878Sjulian		switch (pcb->state) {
2205114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
2206114878Sjulian			ng_btsocket_rfcomm_untimeout(pcb);
2207114878Sjulian
2208114878Sjulian			error = ng_btsocket_rfcomm_send_msc(pcb);
2209114878Sjulian			if (error == 0) {
2210114878Sjulian				pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTED;
2211114878Sjulian				soisconnected(pcb->so);
2212114878Sjulian			}
2213114878Sjulian			break;
2214114878Sjulian
2215114878Sjulian		case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
2216161623Semax			ng_btsocket_rfcomm_pcb_kill(pcb, 0);
2217114878Sjulian			break;
2218114878Sjulian
2219114878Sjulian		default:
2220114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2221114878Sjulian"%s: Got UA for dlci=%d in invalid state=%d, flags=%#x\n",
2222114878Sjulian				__func__, dlci, pcb->state, pcb->flags);
2223114878Sjulian			error = ENOENT;
2224114878Sjulian			break;
2225114878Sjulian		}
2226114878Sjulian
2227114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
2228114878Sjulian	} else {
2229114878Sjulian		NG_BTSOCKET_RFCOMM_WARN(
2230114878Sjulian"%s: Got UA for non-existing dlci=%d\n", __func__, dlci);
2231114878Sjulian
2232114878Sjulian		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_DM,dlci);
2233114878Sjulian	}
2234114878Sjulian
2235114878Sjulian	return (error);
2236114878Sjulian} /* ng_btsocket_rfcomm_receive_ua */
2237114878Sjulian
2238114878Sjulian/*
2239114878Sjulian * Process RFCOMM DM frame
2240114878Sjulian */
2241114878Sjulian
2242114878Sjulianstatic int
2243114878Sjulianng_btsocket_rfcomm_receive_dm(ng_btsocket_rfcomm_session_p s, int dlci)
2244114878Sjulian{
2245114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
2246114878Sjulian	int				error;
2247114878Sjulian
2248114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2249114878Sjulian
2250114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2251114878Sjulian"%s: Got DM, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
2252114878Sjulian		__func__, s->state, s->flags, s->mtu, dlci);
2253114878Sjulian
2254114878Sjulian	/* DLCI == 0 means multiplexor channel */
2255114878Sjulian	if (dlci == 0) {
2256114878Sjulian		/* Disconnect all dlc's on the session */
2257114878Sjulian		s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
2258114878Sjulian		ng_btsocket_rfcomm_session_clean(s);
2259114878Sjulian	} else {
2260114878Sjulian		pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2261114878Sjulian		if (pcb != NULL) {
2262114878Sjulian			mtx_lock(&pcb->pcb_mtx);
2263114878Sjulian
2264114878Sjulian			NG_BTSOCKET_RFCOMM_INFO(
2265114878Sjulian"%s: Got DM for dlci=%d, state=%d, flags=%#x\n",
2266114878Sjulian				__func__, dlci, pcb->state, pcb->flags);
2267114878Sjulian
2268114878Sjulian			if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_CONNECTED)
2269114878Sjulian				error = ECONNRESET;
2270114878Sjulian			else
2271114878Sjulian				error = ECONNREFUSED;
2272114878Sjulian
2273161623Semax			ng_btsocket_rfcomm_pcb_kill(pcb, error);
2274114878Sjulian
2275114878Sjulian			mtx_unlock(&pcb->pcb_mtx);
2276114878Sjulian		} else
2277114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2278114878Sjulian"%s: Got DM for non-existing dlci=%d\n", __func__, dlci);
2279114878Sjulian	}
2280114878Sjulian
2281114878Sjulian	return (0);
2282114878Sjulian} /* ng_btsocket_rfcomm_receive_dm */
2283114878Sjulian
2284114878Sjulian/*
2285114878Sjulian * Process RFCOMM UIH frame (data)
2286114878Sjulian */
2287114878Sjulian
2288114878Sjulianstatic int
2289114878Sjulianng_btsocket_rfcomm_receive_uih(ng_btsocket_rfcomm_session_p s, int dlci,
2290114878Sjulian		int pf, struct mbuf *m0)
2291114878Sjulian{
2292114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
2293114878Sjulian	int				error = 0;
2294114878Sjulian
2295114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2296114878Sjulian
2297114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2298114878Sjulian"%s: Got UIH, session state=%d, flags=%#x, mtu=%d, dlci=%d, pf=%d, len=%d\n",
2299114878Sjulian		__func__, s->state, s->flags, s->mtu, dlci, pf,
2300114878Sjulian		m0->m_pkthdr.len);
2301114878Sjulian
2302114878Sjulian	/* XXX should we do it here? Check for session flow control */
2303114878Sjulian	if (s->flags & NG_BTSOCKET_RFCOMM_SESSION_LFC) {
2304114878Sjulian		NG_BTSOCKET_RFCOMM_WARN(
2305114878Sjulian"%s: Got UIH with session flow control asserted, state=%d, flags=%#x\n",
2306114878Sjulian			__func__, s->state, s->flags);
2307114878Sjulian		goto drop;
2308114878Sjulian	}
2309114878Sjulian
2310114878Sjulian	/* Check if we have this dlci */
2311114878Sjulian	pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, dlci);
2312114878Sjulian	if (pcb == NULL) {
2313114878Sjulian		NG_BTSOCKET_RFCOMM_WARN(
2314114878Sjulian"%s: Got UIH for non-existing dlci=%d\n", __func__, dlci);
2315114878Sjulian		error = ng_btsocket_rfcomm_send_command(s,RFCOMM_FRAME_DM,dlci);
2316114878Sjulian		goto drop;
2317114878Sjulian	}
2318114878Sjulian
2319114878Sjulian	mtx_lock(&pcb->pcb_mtx);
2320114878Sjulian
2321114878Sjulian	/* Check dlci state */
2322114878Sjulian	if (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTED) {
2323114878Sjulian		NG_BTSOCKET_RFCOMM_WARN(
2324114878Sjulian"%s: Got UIH for dlci=%d in invalid state=%d, flags=%#x\n",
2325114878Sjulian			__func__, dlci, pcb->state, pcb->flags);
2326114878Sjulian		error = EINVAL;
2327114878Sjulian		goto drop1;
2328114878Sjulian	}
2329114878Sjulian
2330114878Sjulian	/* Check dlci flow control */
2331114878Sjulian	if (((pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) && pcb->rx_cred <= 0) ||
2332114878Sjulian	     (pcb->lmodem & RFCOMM_MODEM_FC)) {
2333114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
2334114878Sjulian"%s: Got UIH for dlci=%d with asserted flow control, state=%d, " \
2335114878Sjulian"flags=%#x, rx_cred=%d, lmodem=%#x\n",
2336114878Sjulian			__func__, dlci, pcb->state, pcb->flags,
2337114878Sjulian			pcb->rx_cred, pcb->lmodem);
2338114878Sjulian		goto drop1;
2339114878Sjulian	}
2340114878Sjulian
2341114878Sjulian	/* Did we get any credits? */
2342114878Sjulian	if ((pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) && pf) {
2343114878Sjulian		NG_BTSOCKET_RFCOMM_INFO(
2344114878Sjulian"%s: Got %d more credits for dlci=%d, state=%d, flags=%#x, " \
2345114878Sjulian"rx_cred=%d, tx_cred=%d\n",
2346114878Sjulian			__func__, *mtod(m0, u_int8_t *), dlci, pcb->state,
2347114878Sjulian			pcb->flags, pcb->rx_cred, pcb->tx_cred);
2348114878Sjulian
2349114878Sjulian		pcb->tx_cred += *mtod(m0, u_int8_t *);
2350114878Sjulian		m_adj(m0, 1);
2351114878Sjulian
2352114878Sjulian		/* Send more from the DLC. XXX check for errors? */
2353114878Sjulian		ng_btsocket_rfcomm_pcb_send(pcb, ALOT);
2354114878Sjulian	}
2355114878Sjulian
2356114878Sjulian	/* OK the of the rest of the mbuf is the data */
2357114878Sjulian	if (m0->m_pkthdr.len > 0) {
2358114878Sjulian		/* If we are using credit flow control decrease rx_cred here */
2359114878Sjulian		if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) {
2360114878Sjulian			/* Give remote peer more credits (if needed) */
2361114878Sjulian			if (-- pcb->rx_cred <= RFCOMM_MAX_CREDITS / 2)
2362114878Sjulian				ng_btsocket_rfcomm_send_credits(pcb);
2363114878Sjulian			else
2364114878Sjulian				NG_BTSOCKET_RFCOMM_INFO(
2365114878Sjulian"%s: Remote side still has credits, dlci=%d, state=%d, flags=%#x, " \
2366114878Sjulian"rx_cred=%d, tx_cred=%d\n",		__func__, dlci, pcb->state, pcb->flags,
2367114878Sjulian					pcb->rx_cred, pcb->tx_cred);
2368114878Sjulian		}
2369114878Sjulian
2370114878Sjulian		/* Check packet against mtu on dlci */
2371114878Sjulian		if (m0->m_pkthdr.len > pcb->mtu) {
2372114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
2373114878Sjulian"%s: Got oversized UIH for dlci=%d, state=%d, flags=%#x, mtu=%d, len=%d\n",
2374114878Sjulian				__func__, dlci, pcb->state, pcb->flags,
2375114878Sjulian				pcb->mtu, m0->m_pkthdr.len);
2376114878Sjulian
2377114878Sjulian			error = EMSGSIZE;
2378114878Sjulian		} else if (m0->m_pkthdr.len > sbspace(&pcb->so->so_rcv)) {
2379114878Sjulian
2380114878Sjulian			/*
2381114878Sjulian			 * This is really bad. Receive queue on socket does
2382114878Sjulian			 * not have enough space for the packet. We do not
2383114878Sjulian			 * have any other choice but drop the packet.
2384114878Sjulian			 */
2385114878Sjulian
2386114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
2387114878Sjulian"%s: Not enough space in socket receive queue. Dropping UIH for dlci=%d, " \
2388114878Sjulian"state=%d, flags=%#x, len=%d, space=%ld\n",
2389114878Sjulian				__func__, dlci, pcb->state, pcb->flags,
2390114878Sjulian				m0->m_pkthdr.len, sbspace(&pcb->so->so_rcv));
2391114878Sjulian
2392114878Sjulian			error = ENOBUFS;
2393114878Sjulian		} else {
2394114878Sjulian			/* Append packet to the socket receive queue */
2395114878Sjulian			sbappend(&pcb->so->so_rcv, m0);
2396114878Sjulian			m0 = NULL;
2397114878Sjulian
2398114878Sjulian			sorwakeup(pcb->so);
2399114878Sjulian		}
2400114878Sjulian	}
2401114878Sjuliandrop1:
2402114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
2403114878Sjuliandrop:
2404114878Sjulian	NG_FREE_M(m0); /* checks for != NULL */
2405114878Sjulian
2406114878Sjulian	return (error);
2407114878Sjulian} /* ng_btsocket_rfcomm_receive_uih */
2408114878Sjulian
2409114878Sjulian/*
2410114878Sjulian * Process RFCOMM MCC command (Multiplexor)
2411114878Sjulian *
2412114878Sjulian * From TS 07.10 spec
2413114878Sjulian *
2414114878Sjulian * "5.4.3.1 Information Data
2415114878Sjulian *
2416114878Sjulian *  ...The frames (UIH) sent by the initiating station have the C/R bit set
2417114878Sjulian *  to 1 and those sent by the responding station have the C/R bit set to 0..."
2418114878Sjulian *
2419114878Sjulian * "5.4.6.2 Operating procedures
2420114878Sjulian *
2421114878Sjulian *  Messages always exist in pairs; a command message and a corresponding
2422114878Sjulian *  response message. If the C/R bit is set to 1 the message is a command,
2423114878Sjulian *  if it is set to 0 the message is a response...
2424114878Sjulian *
2425114878Sjulian *  ...
2426114878Sjulian *
2427114878Sjulian *  NOTE: Notice that when UIH frames are used to convey information on DLCI 0
2428114878Sjulian *  there are at least two different fields that contain a C/R bit, and the
2429114878Sjulian *  bits are set of different form. The C/R bit in the Type field shall be set
2430114878Sjulian *  as it is stated above, while the C/R bit in the Address field (see subclause
2431114878Sjulian *  5.2.1.2) shall be set as it is described in subclause 5.4.3.1."
2432114878Sjulian */
2433114878Sjulian
2434114878Sjulianstatic int
2435114878Sjulianng_btsocket_rfcomm_receive_mcc(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2436114878Sjulian{
2437114878Sjulian	struct rfcomm_mcc_hdr	*hdr = NULL;
2438114878Sjulian	u_int8_t		 cr, type, length;
2439114878Sjulian
2440114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2441114878Sjulian
2442114878Sjulian	/*
2443114878Sjulian	 * We can access data directly in the first mbuf, because we have
2444114878Sjulian	 * m_pullup()'ed mbuf chain in ng_btsocket_rfcomm_receive_frame().
2445114878Sjulian	 * All MCC commands should fit into single mbuf (except probably TEST).
2446114878Sjulian	 */
2447114878Sjulian
2448114878Sjulian	hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2449114878Sjulian	cr = RFCOMM_CR(hdr->type);
2450114878Sjulian	type = RFCOMM_MCC_TYPE(hdr->type);
2451114878Sjulian	length = RFCOMM_MCC_LENGTH(hdr->length);
2452114878Sjulian
2453114878Sjulian	/* Check MCC frame length */
2454114878Sjulian	if (sizeof(*hdr) + length != m0->m_pkthdr.len) {
2455114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
2456114878Sjulian"%s: Invalid MCC frame length=%d, len=%d\n",
2457114878Sjulian			__func__, length, m0->m_pkthdr.len);
2458114878Sjulian		NG_FREE_M(m0);
2459114878Sjulian
2460114878Sjulian		return (EMSGSIZE);
2461114878Sjulian	}
2462114878Sjulian
2463114878Sjulian	switch (type) {
2464114878Sjulian	case RFCOMM_MCC_TEST:
2465114878Sjulian		return (ng_btsocket_rfcomm_receive_test(s, m0));
2466114878Sjulian		/* NOT REACHED */
2467114878Sjulian
2468114878Sjulian	case RFCOMM_MCC_FCON:
2469114878Sjulian	case RFCOMM_MCC_FCOFF:
2470114878Sjulian		return (ng_btsocket_rfcomm_receive_fc(s, m0));
2471114878Sjulian		/* NOT REACHED */
2472114878Sjulian
2473114878Sjulian	case RFCOMM_MCC_MSC:
2474114878Sjulian		return (ng_btsocket_rfcomm_receive_msc(s, m0));
2475114878Sjulian		/* NOT REACHED */
2476114878Sjulian
2477114878Sjulian	case RFCOMM_MCC_RPN:
2478114878Sjulian		return (ng_btsocket_rfcomm_receive_rpn(s, m0));
2479114878Sjulian		/* NOT REACHED */
2480114878Sjulian
2481114878Sjulian	case RFCOMM_MCC_RLS:
2482114878Sjulian		return (ng_btsocket_rfcomm_receive_rls(s, m0));
2483114878Sjulian		/* NOT REACHED */
2484114878Sjulian
2485114878Sjulian	case RFCOMM_MCC_PN:
2486114878Sjulian		return (ng_btsocket_rfcomm_receive_pn(s, m0));
2487114878Sjulian		/* NOT REACHED */
2488114878Sjulian
2489114878Sjulian	case RFCOMM_MCC_NSC:
2490114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
2491114878Sjulian"%s: Got MCC NSC, type=%#x, cr=%d, length=%d, session state=%d, flags=%#x, " \
2492114878Sjulian"mtu=%d, len=%d\n",	__func__, RFCOMM_MCC_TYPE(*((u_int8_t *)(hdr + 1))), cr,
2493114878Sjulian			 length, s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2494114878Sjulian		NG_FREE_M(m0);
2495114878Sjulian		break;
2496114878Sjulian
2497114878Sjulian	default:
2498114878Sjulian		NG_BTSOCKET_RFCOMM_ERR(
2499114878Sjulian"%s: Got unknown MCC, type=%#x, cr=%d, length=%d, session state=%d, " \
2500114878Sjulian"flags=%#x, mtu=%d, len=%d\n",
2501114878Sjulian			__func__, type, cr, length, s->state, s->flags,
2502114878Sjulian			s->mtu, m0->m_pkthdr.len);
2503114878Sjulian
2504114878Sjulian		/* Reuse mbuf to send NSC */
2505114878Sjulian		hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2506114878Sjulian		m0->m_pkthdr.len = m0->m_len = sizeof(*hdr);
2507114878Sjulian
2508114878Sjulian		/* Create MCC NSC header */
2509114878Sjulian		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_NSC);
2510114878Sjulian		hdr->length = RFCOMM_MKLEN8(1);
2511114878Sjulian
2512114878Sjulian		/* Put back MCC command type we did not like */
2513114878Sjulian		m0->m_data[m0->m_len] = RFCOMM_MKMCC_TYPE(cr, type);
2514114878Sjulian		m0->m_pkthdr.len ++;
2515114878Sjulian		m0->m_len ++;
2516114878Sjulian
2517114878Sjulian		/* Send UIH frame */
2518114878Sjulian		return (ng_btsocket_rfcomm_send_uih(s,
2519114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0));
2520114878Sjulian		/* NOT REACHED */
2521114878Sjulian	}
2522114878Sjulian
2523114878Sjulian	return (0);
2524114878Sjulian} /* ng_btsocket_rfcomm_receive_mcc */
2525114878Sjulian
2526114878Sjulian/*
2527114878Sjulian * Receive RFCOMM TEST MCC command
2528114878Sjulian */
2529114878Sjulian
2530114878Sjulianstatic int
2531114878Sjulianng_btsocket_rfcomm_receive_test(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2532114878Sjulian{
2533114878Sjulian	struct rfcomm_mcc_hdr	*hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2534114878Sjulian	int			 error = 0;
2535114878Sjulian
2536114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2537114878Sjulian
2538114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2539114878Sjulian"%s: Got MCC TEST, cr=%d, length=%d, session state=%d, flags=%#x, mtu=%d, " \
2540114878Sjulian"len=%d\n",	__func__, RFCOMM_CR(hdr->type), RFCOMM_MCC_LENGTH(hdr->length),
2541114878Sjulian		s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2542114878Sjulian
2543114878Sjulian	if (RFCOMM_CR(hdr->type)) {
2544114878Sjulian		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_TEST);
2545114878Sjulian		error = ng_btsocket_rfcomm_send_uih(s,
2546114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2547114878Sjulian	} else
2548114878Sjulian		NG_FREE_M(m0); /* XXX ignore response */
2549114878Sjulian
2550114878Sjulian	return (error);
2551114878Sjulian} /* ng_btsocket_rfcomm_receive_test */
2552114878Sjulian
2553114878Sjulian/*
2554114878Sjulian * Receive RFCOMM FCON/FCOFF MCC command
2555114878Sjulian */
2556114878Sjulian
2557114878Sjulianstatic int
2558114878Sjulianng_btsocket_rfcomm_receive_fc(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2559114878Sjulian{
2560114878Sjulian	struct rfcomm_mcc_hdr	*hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2561114878Sjulian	u_int8_t		 type = RFCOMM_MCC_TYPE(hdr->type);
2562114878Sjulian	int			 error = 0;
2563114878Sjulian
2564114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2565114878Sjulian
2566114878Sjulian	/*
2567114878Sjulian	 * Turn ON/OFF aggregate flow on the entire session. When remote peer
2568114878Sjulian	 * asserted flow control no transmission shall occur except on dlci 0
2569114878Sjulian	 * (control channel).
2570114878Sjulian	 */
2571114878Sjulian
2572114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2573114878Sjulian"%s: Got MCC FC%s, cr=%d, length=%d, session state=%d, flags=%#x, mtu=%d, " \
2574114878Sjulian"len=%d\n",	__func__, (type == RFCOMM_MCC_FCON)? "ON" : "OFF",
2575114878Sjulian		RFCOMM_CR(hdr->type), RFCOMM_MCC_LENGTH(hdr->length),
2576114878Sjulian		s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2577114878Sjulian
2578114878Sjulian	if (RFCOMM_CR(hdr->type)) {
2579114878Sjulian		if (type == RFCOMM_MCC_FCON)
2580114878Sjulian			s->flags &= ~NG_BTSOCKET_RFCOMM_SESSION_RFC;
2581114878Sjulian		else
2582114878Sjulian			s->flags |= NG_BTSOCKET_RFCOMM_SESSION_RFC;
2583114878Sjulian
2584114878Sjulian		hdr->type = RFCOMM_MKMCC_TYPE(0, type);
2585114878Sjulian		error = ng_btsocket_rfcomm_send_uih(s,
2586114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2587114878Sjulian	} else
2588114878Sjulian		NG_FREE_M(m0); /* XXX ignore response */
2589114878Sjulian
2590114878Sjulian	return (error);
2591114878Sjulian} /* ng_btsocket_rfcomm_receive_fc  */
2592114878Sjulian
2593114878Sjulian/*
2594114878Sjulian * Receive RFCOMM MSC MCC command
2595114878Sjulian */
2596114878Sjulian
2597114878Sjulianstatic int
2598114878Sjulianng_btsocket_rfcomm_receive_msc(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2599114878Sjulian{
2600114878Sjulian	struct rfcomm_mcc_hdr		*hdr = mtod(m0, struct rfcomm_mcc_hdr*);
2601114878Sjulian	struct rfcomm_mcc_msc		*msc = (struct rfcomm_mcc_msc *)(hdr+1);
2602114878Sjulian	ng_btsocket_rfcomm_pcb_t	*pcb = NULL;
2603114878Sjulian	int				 error = 0;
2604114878Sjulian
2605114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2606114878Sjulian
2607114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2608114878Sjulian"%s: Got MCC MSC, dlci=%d, cr=%d, length=%d, session state=%d, flags=%#x, " \
2609114878Sjulian"mtu=%d, len=%d\n",
2610114878Sjulian		__func__,  RFCOMM_DLCI(msc->address), RFCOMM_CR(hdr->type),
2611114878Sjulian		RFCOMM_MCC_LENGTH(hdr->length), s->state, s->flags,
2612114878Sjulian		s->mtu, m0->m_pkthdr.len);
2613114878Sjulian
2614114878Sjulian	if (RFCOMM_CR(hdr->type)) {
2615114878Sjulian		pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, RFCOMM_DLCI(msc->address));
2616114878Sjulian		if (pcb == NULL) {
2617114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2618114878Sjulian"%s: Got MSC command for non-existing dlci=%d\n",
2619114878Sjulian				__func__, RFCOMM_DLCI(msc->address));
2620114878Sjulian			NG_FREE_M(m0);
2621114878Sjulian
2622114878Sjulian			return (ENOENT);
2623114878Sjulian		}
2624114878Sjulian
2625114878Sjulian		mtx_lock(&pcb->pcb_mtx);
2626114878Sjulian
2627114878Sjulian		if (pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTING &&
2628114878Sjulian		    pcb->state != NG_BTSOCKET_RFCOMM_DLC_CONNECTED) {
2629114878Sjulian			NG_BTSOCKET_RFCOMM_WARN(
2630114878Sjulian"%s: Got MSC on dlci=%d in invalid state=%d\n",
2631114878Sjulian				__func__, RFCOMM_DLCI(msc->address),
2632114878Sjulian				pcb->state);
2633114878Sjulian
2634114878Sjulian			mtx_unlock(&pcb->pcb_mtx);
2635114878Sjulian			NG_FREE_M(m0);
2636114878Sjulian
2637114878Sjulian			return (EINVAL);
2638114878Sjulian		}
2639114878Sjulian
2640114878Sjulian		pcb->rmodem = msc->modem; /* Update remote port signals */
2641114878Sjulian
2642114878Sjulian		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_MSC);
2643114878Sjulian		error = ng_btsocket_rfcomm_send_uih(s,
2644114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2645114878Sjulian
2646114878Sjulian#if 0 /* YYY */
2647114878Sjulian		/* Send more data from DLC. XXX check for errors? */
2648114878Sjulian		if (!(pcb->rmodem & RFCOMM_MODEM_FC) &&
2649114878Sjulian		    !(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC))
2650114878Sjulian			ng_btsocket_rfcomm_pcb_send(pcb, ALOT);
2651114878Sjulian#endif /* YYY */
2652114878Sjulian
2653114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
2654114878Sjulian	} else
2655114878Sjulian		NG_FREE_M(m0); /* XXX ignore response */
2656114878Sjulian
2657114878Sjulian	return (error);
2658114878Sjulian} /* ng_btsocket_rfcomm_receive_msc */
2659114878Sjulian
2660114878Sjulian/*
2661114878Sjulian * Receive RFCOMM RPN MCC command
2662114878Sjulian * XXX FIXME do we need htole16/le16toh for RPN param_mask?
2663114878Sjulian */
2664114878Sjulian
2665114878Sjulianstatic int
2666114878Sjulianng_btsocket_rfcomm_receive_rpn(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2667114878Sjulian{
2668114878Sjulian	struct rfcomm_mcc_hdr	*hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2669114878Sjulian	struct rfcomm_mcc_rpn	*rpn = (struct rfcomm_mcc_rpn *)(hdr + 1);
2670114878Sjulian	int			 error = 0;
2671114878Sjulian	u_int16_t		 param_mask;
2672114878Sjulian	u_int8_t		 bit_rate, data_bits, stop_bits, parity,
2673114878Sjulian				 flow_control, xon_char, xoff_char;
2674114878Sjulian
2675114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2676114878Sjulian
2677114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2678114878Sjulian"%s: Got MCC RPN, dlci=%d, cr=%d, length=%d, session state=%d, flags=%#x, " \
2679114878Sjulian"mtu=%d, len=%d\n",
2680114878Sjulian		__func__, RFCOMM_DLCI(rpn->dlci), RFCOMM_CR(hdr->type),
2681114878Sjulian		RFCOMM_MCC_LENGTH(hdr->length), s->state, s->flags,
2682114878Sjulian		s->mtu, m0->m_pkthdr.len);
2683114878Sjulian
2684114878Sjulian	if (RFCOMM_CR(hdr->type)) {
2685114878Sjulian		param_mask = RFCOMM_RPN_PM_ALL;
2686114878Sjulian
2687114878Sjulian		if (RFCOMM_MCC_LENGTH(hdr->length) == 1) {
2688114878Sjulian			/* Request - return default setting */
2689114878Sjulian			bit_rate = RFCOMM_RPN_BR_115200;
2690114878Sjulian			data_bits = RFCOMM_RPN_DATA_8;
2691114878Sjulian			stop_bits = RFCOMM_RPN_STOP_1;
2692114878Sjulian			parity = RFCOMM_RPN_PARITY_NONE;
2693114878Sjulian			flow_control = RFCOMM_RPN_FLOW_NONE;
2694114878Sjulian			xon_char = RFCOMM_RPN_XON_CHAR;
2695114878Sjulian			xoff_char = RFCOMM_RPN_XOFF_CHAR;
2696114878Sjulian                } else {
2697114878Sjulian			/*
2698114878Sjulian			 * Ignore/accept bit_rate, 8 bits, 1 stop bit, no
2699114878Sjulian			 * parity, no flow control lines, default XON/XOFF
2700114878Sjulian			 * chars.
2701114878Sjulian			 */
2702114878Sjulian
2703114878Sjulian			bit_rate = rpn->bit_rate;
2704114878Sjulian			rpn->param_mask = le16toh(rpn->param_mask); /* XXX */
2705114878Sjulian
2706114878Sjulian			data_bits = RFCOMM_RPN_DATA_BITS(rpn->line_settings);
2707114878Sjulian			if (rpn->param_mask & RFCOMM_RPN_PM_DATA &&
2708114878Sjulian			    data_bits != RFCOMM_RPN_DATA_8) {
2709114878Sjulian				data_bits = RFCOMM_RPN_DATA_8;
2710114878Sjulian				param_mask ^= RFCOMM_RPN_PM_DATA;
2711114878Sjulian			}
2712114878Sjulian
2713114878Sjulian			stop_bits = RFCOMM_RPN_STOP_BITS(rpn->line_settings);
2714114878Sjulian			if (rpn->param_mask & RFCOMM_RPN_PM_STOP &&
2715114878Sjulian			    stop_bits != RFCOMM_RPN_STOP_1) {
2716114878Sjulian				stop_bits = RFCOMM_RPN_STOP_1;
2717114878Sjulian				param_mask ^= RFCOMM_RPN_PM_STOP;
2718114878Sjulian			}
2719114878Sjulian
2720114878Sjulian			parity = RFCOMM_RPN_PARITY(rpn->line_settings);
2721114878Sjulian			if (rpn->param_mask & RFCOMM_RPN_PM_PARITY &&
2722114878Sjulian			    parity != RFCOMM_RPN_PARITY_NONE) {
2723114878Sjulian				parity = RFCOMM_RPN_PARITY_NONE;
2724114878Sjulian				param_mask ^= RFCOMM_RPN_PM_PARITY;
2725114878Sjulian			}
2726114878Sjulian
2727114878Sjulian			flow_control = rpn->flow_control;
2728114878Sjulian			if (rpn->param_mask & RFCOMM_RPN_PM_FLOW &&
2729114878Sjulian			    flow_control != RFCOMM_RPN_FLOW_NONE) {
2730114878Sjulian				flow_control = RFCOMM_RPN_FLOW_NONE;
2731114878Sjulian				param_mask ^= RFCOMM_RPN_PM_FLOW;
2732114878Sjulian			}
2733114878Sjulian
2734114878Sjulian			xon_char = rpn->xon_char;
2735114878Sjulian			if (rpn->param_mask & RFCOMM_RPN_PM_XON &&
2736114878Sjulian			    xon_char != RFCOMM_RPN_XON_CHAR) {
2737114878Sjulian				xon_char = RFCOMM_RPN_XON_CHAR;
2738114878Sjulian				param_mask ^= RFCOMM_RPN_PM_XON;
2739114878Sjulian			}
2740114878Sjulian
2741114878Sjulian			xoff_char = rpn->xoff_char;
2742114878Sjulian			if (rpn->param_mask & RFCOMM_RPN_PM_XOFF &&
2743114878Sjulian			    xoff_char != RFCOMM_RPN_XOFF_CHAR) {
2744114878Sjulian				xoff_char = RFCOMM_RPN_XOFF_CHAR;
2745114878Sjulian				param_mask ^= RFCOMM_RPN_PM_XOFF;
2746114878Sjulian			}
2747114878Sjulian		}
2748114878Sjulian
2749114878Sjulian		rpn->bit_rate = bit_rate;
2750114878Sjulian		rpn->line_settings = RFCOMM_MKRPN_LINE_SETTINGS(data_bits,
2751114878Sjulian						stop_bits, parity);
2752114878Sjulian		rpn->flow_control = flow_control;
2753114878Sjulian		rpn->xon_char = xon_char;
2754114878Sjulian		rpn->xoff_char = xoff_char;
2755114878Sjulian		rpn->param_mask = htole16(param_mask); /* XXX */
2756114878Sjulian
2757114878Sjulian		m0->m_pkthdr.len = m0->m_len = sizeof(*hdr) + sizeof(*rpn);
2758114878Sjulian
2759114878Sjulian		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_RPN);
2760114878Sjulian		error = ng_btsocket_rfcomm_send_uih(s,
2761114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2762114878Sjulian	} else
2763114878Sjulian		NG_FREE_M(m0); /* XXX ignore response */
2764114878Sjulian
2765114878Sjulian	return (error);
2766114878Sjulian} /* ng_btsocket_rfcomm_receive_rpn */
2767114878Sjulian
2768114878Sjulian/*
2769114878Sjulian * Receive RFCOMM RLS MCC command
2770114878Sjulian */
2771114878Sjulian
2772114878Sjulianstatic int
2773114878Sjulianng_btsocket_rfcomm_receive_rls(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2774114878Sjulian{
2775114878Sjulian	struct rfcomm_mcc_hdr	*hdr = mtod(m0, struct rfcomm_mcc_hdr *);
2776114878Sjulian	struct rfcomm_mcc_rls	*rls = (struct rfcomm_mcc_rls *)(hdr + 1);
2777114878Sjulian	int			 error = 0;
2778114878Sjulian
2779114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2780114878Sjulian
2781114878Sjulian	/*
2782114878Sjulian	 * XXX FIXME Do we have to do anything else here? Remote peer tries to
2783114878Sjulian	 * tell us something about DLCI. Just report what we have received and
2784114878Sjulian	 * return back received values as required by TS 07.10 spec.
2785114878Sjulian	 */
2786114878Sjulian
2787114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2788114878Sjulian"%s: Got MCC RLS, dlci=%d, status=%#x, cr=%d, length=%d, session state=%d, " \
2789114878Sjulian"flags=%#x, mtu=%d, len=%d\n",
2790114878Sjulian		__func__, RFCOMM_DLCI(rls->address), rls->status,
2791114878Sjulian		RFCOMM_CR(hdr->type), RFCOMM_MCC_LENGTH(hdr->length),
2792114878Sjulian		s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2793114878Sjulian
2794114878Sjulian	if (RFCOMM_CR(hdr->type)) {
2795114878Sjulian		if (rls->status & 0x1)
2796114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
2797114878Sjulian"%s: Got RLS dlci=%d, error=%#x\n", __func__, RFCOMM_DLCI(rls->address),
2798114878Sjulian				rls->status >> 1);
2799114878Sjulian
2800114878Sjulian		hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_RLS);
2801114878Sjulian		error = ng_btsocket_rfcomm_send_uih(s,
2802114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(s), 0), 0, 0, m0);
2803114878Sjulian	} else
2804114878Sjulian		NG_FREE_M(m0); /* XXX ignore responses */
2805114878Sjulian
2806114878Sjulian	return (error);
2807114878Sjulian} /* ng_btsocket_rfcomm_receive_rls */
2808114878Sjulian
2809114878Sjulian/*
2810114878Sjulian * Receive RFCOMM PN MCC command
2811114878Sjulian */
2812114878Sjulian
2813114878Sjulianstatic int
2814114878Sjulianng_btsocket_rfcomm_receive_pn(ng_btsocket_rfcomm_session_p s, struct mbuf *m0)
2815114878Sjulian{
2816114878Sjulian	struct rfcomm_mcc_hdr		*hdr = mtod(m0, struct rfcomm_mcc_hdr*);
2817114878Sjulian	struct rfcomm_mcc_pn		*pn = (struct rfcomm_mcc_pn *)(hdr+1);
2818114878Sjulian	ng_btsocket_rfcomm_pcb_t	*pcb = NULL;
2819114878Sjulian	int				 error = 0;
2820114878Sjulian
2821114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2822114878Sjulian
2823114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2824114878Sjulian"%s: Got MCC PN, dlci=%d, cr=%d, length=%d, flow_control=%#x, priority=%d, " \
2825114878Sjulian"ack_timer=%d, mtu=%d, max_retrans=%d, credits=%d, session state=%d, " \
2826114878Sjulian"flags=%#x, session mtu=%d, len=%d\n",
2827114878Sjulian		__func__, pn->dlci, RFCOMM_CR(hdr->type),
2828114878Sjulian		RFCOMM_MCC_LENGTH(hdr->length), pn->flow_control, pn->priority,
2829114878Sjulian		pn->ack_timer, le16toh(pn->mtu), pn->max_retrans, pn->credits,
2830114878Sjulian		s->state, s->flags, s->mtu, m0->m_pkthdr.len);
2831114878Sjulian
2832114878Sjulian	if (pn->dlci == 0) {
2833114878Sjulian		NG_BTSOCKET_RFCOMM_ERR("%s: Zero dlci in MCC PN\n", __func__);
2834114878Sjulian		NG_FREE_M(m0);
2835114878Sjulian
2836114878Sjulian		return (EINVAL);
2837114878Sjulian	}
2838114878Sjulian
2839114878Sjulian	/* Check if we have this dlci */
2840114878Sjulian	pcb = ng_btsocket_rfcomm_pcb_by_dlci(s, pn->dlci);
2841114878Sjulian	if (pcb != NULL) {
2842114878Sjulian		mtx_lock(&pcb->pcb_mtx);
2843114878Sjulian
2844114878Sjulian		if (RFCOMM_CR(hdr->type)) {
2845114878Sjulian			/* PN Request */
2846114878Sjulian			ng_btsocket_rfcomm_set_pn(pcb, 1, pn->flow_control,
2847114878Sjulian				pn->credits, pn->mtu);
2848114878Sjulian
2849114878Sjulian			if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) {
2850114878Sjulian				pn->flow_control = 0xe0;
2851114878Sjulian				pn->credits = RFCOMM_DEFAULT_CREDITS;
2852114878Sjulian			} else {
2853114878Sjulian				pn->flow_control = 0;
2854114878Sjulian				pn->credits = 0;
2855114878Sjulian			}
2856114878Sjulian
2857114878Sjulian			hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_PN);
2858114878Sjulian			error = ng_btsocket_rfcomm_send_uih(s,
2859114878Sjulian					RFCOMM_MKADDRESS(INITIATOR(s), 0),
2860114878Sjulian					0, 0, m0);
2861114878Sjulian		} else {
2862114878Sjulian			/* PN Response - proceed with SABM. Timeout still set */
2863114878Sjulian			if (pcb->state == NG_BTSOCKET_RFCOMM_DLC_CONFIGURING) {
2864114878Sjulian				ng_btsocket_rfcomm_set_pn(pcb, 0,
2865114878Sjulian					pn->flow_control, pn->credits, pn->mtu);
2866114878Sjulian
2867114878Sjulian				pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTING;
2868114878Sjulian				error = ng_btsocket_rfcomm_send_command(s,
2869114878Sjulian						RFCOMM_FRAME_SABM, pn->dlci);
2870114878Sjulian			} else
2871114878Sjulian				NG_BTSOCKET_RFCOMM_WARN(
2872114878Sjulian"%s: Got PN response for dlci=%d in invalid state=%d\n",
2873114878Sjulian					__func__, pn->dlci, pcb->state);
2874114878Sjulian
2875114878Sjulian			NG_FREE_M(m0);
2876114878Sjulian		}
2877114878Sjulian
2878114878Sjulian		mtx_unlock(&pcb->pcb_mtx);
2879114878Sjulian	} else if (RFCOMM_CR(hdr->type)) {
2880114878Sjulian		/* PN request to non-existing dlci - incomming connection */
2881114878Sjulian		pcb = ng_btsocket_rfcomm_connect_ind(s,
2882114878Sjulian				RFCOMM_SRVCHANNEL(pn->dlci));
2883114878Sjulian		if (pcb != NULL) {
2884114878Sjulian			mtx_lock(&pcb->pcb_mtx);
2885114878Sjulian
2886114878Sjulian			pcb->dlci = pn->dlci;
2887114878Sjulian
2888114878Sjulian			ng_btsocket_rfcomm_set_pn(pcb, 1, pn->flow_control,
2889114878Sjulian				pn->credits, pn->mtu);
2890114878Sjulian
2891114878Sjulian			if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) {
2892114878Sjulian				pn->flow_control = 0xe0;
2893114878Sjulian				pn->credits = RFCOMM_DEFAULT_CREDITS;
2894114878Sjulian			} else {
2895114878Sjulian				pn->flow_control = 0;
2896114878Sjulian				pn->credits = 0;
2897114878Sjulian			}
2898114878Sjulian
2899114878Sjulian			hdr->type = RFCOMM_MKMCC_TYPE(0, RFCOMM_MCC_PN);
2900114878Sjulian			error = ng_btsocket_rfcomm_send_uih(s,
2901114878Sjulian					RFCOMM_MKADDRESS(INITIATOR(s), 0),
2902114878Sjulian					0, 0, m0);
2903114878Sjulian
2904114878Sjulian			if (error == 0) {
2905114878Sjulian				ng_btsocket_rfcomm_timeout(pcb);
2906114878Sjulian				pcb->state = NG_BTSOCKET_RFCOMM_DLC_CONNECTING;
2907114878Sjulian				soisconnecting(pcb->so);
2908161623Semax			} else
2909161623Semax				ng_btsocket_rfcomm_pcb_kill(pcb, error);
2910114878Sjulian
2911114878Sjulian			mtx_unlock(&pcb->pcb_mtx);
2912114878Sjulian		} else {
2913114878Sjulian			/* Nobody is listen()ing on this channel */
2914114878Sjulian			error = ng_btsocket_rfcomm_send_command(s,
2915114878Sjulian					RFCOMM_FRAME_DM, pn->dlci);
2916114878Sjulian			NG_FREE_M(m0);
2917114878Sjulian		}
2918114878Sjulian	} else
2919114878Sjulian		NG_FREE_M(m0); /* XXX ignore response to non-existing dlci */
2920114878Sjulian
2921114878Sjulian	return (error);
2922114878Sjulian} /* ng_btsocket_rfcomm_receive_pn */
2923114878Sjulian
2924114878Sjulian/*
2925114878Sjulian * Set PN parameters for dlci. Caller must hold pcb->pcb_mtx.
2926114878Sjulian *
2927114878Sjulian * From Bluetooth spec.
2928114878Sjulian *
2929114878Sjulian * "... The CL1 - CL4 field is completely redefined. (In TS07.10 this defines
2930114878Sjulian *  the convergence layer to use, which is not applicable to RFCOMM. In RFCOMM,
2931114878Sjulian *  in Bluetooth versions up to 1.0B, this field was forced to 0).
2932114878Sjulian *
2933114878Sjulian *  In the PN request sent prior to a DLC establishment, this field must contain
2934114878Sjulian *  the value 15 (0xF), indicating support of credit based flow control in the
2935114878Sjulian *  sender. See Table 5.3 below. If the PN response contains any other value
2936114878Sjulian *  than 14 (0xE) in this field, it is inferred that the peer RFCOMM entity is
2937114878Sjulian *  not supporting the credit based flow control feature. (This is only possible
2938114878Sjulian *  if the peer RFCOMM implementation is only conforming to Bluetooth version
2939114878Sjulian *  1.0B.) If a PN request is sent on an already open DLC, then this field must
2940114878Sjulian *  contain the value zero; it is not possible to set initial credits  more
2941114878Sjulian *  than once per DLC activation. A responding implementation must set this
2942114878Sjulian *  field in the PN response to 14 (0xE), if (and only if) the value in the PN
2943114878Sjulian *  request was 15..."
2944114878Sjulian */
2945114878Sjulian
2946114878Sjulianstatic void
2947114878Sjulianng_btsocket_rfcomm_set_pn(ng_btsocket_rfcomm_pcb_p pcb, u_int8_t cr,
2948114878Sjulian		u_int8_t flow_control, u_int8_t credits, u_int16_t mtu)
2949114878Sjulian{
2950114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
2951114878Sjulian
2952114878Sjulian	pcb->mtu = le16toh(mtu);
2953114878Sjulian
2954114878Sjulian	if (cr) {
2955114878Sjulian		if (flow_control == 0xf0) {
2956114878Sjulian			pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_CFC;
2957114878Sjulian			pcb->tx_cred = credits;
2958114878Sjulian		} else {
2959114878Sjulian			pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_CFC;
2960114878Sjulian			pcb->tx_cred = 0;
2961114878Sjulian		}
2962114878Sjulian	} else {
2963114878Sjulian		if (flow_control == 0xe0) {
2964114878Sjulian			pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_CFC;
2965114878Sjulian			pcb->tx_cred = credits;
2966114878Sjulian		} else {
2967114878Sjulian			pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_CFC;
2968114878Sjulian			pcb->tx_cred = 0;
2969114878Sjulian		}
2970114878Sjulian	}
2971114878Sjulian
2972114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2973114878Sjulian"%s: cr=%d, dlci=%d, state=%d, flags=%#x, mtu=%d, rx_cred=%d, tx_cred=%d\n",
2974114878Sjulian		__func__, cr, pcb->dlci, pcb->state, pcb->flags, pcb->mtu,
2975114878Sjulian		pcb->rx_cred, pcb->tx_cred);
2976114878Sjulian} /* ng_btsocket_rfcomm_set_pn */
2977114878Sjulian
2978114878Sjulian/*
2979114878Sjulian * Send RFCOMM SABM/DISC/UA/DM frames. Caller must hold s->session_mtx
2980114878Sjulian */
2981114878Sjulian
2982114878Sjulianstatic int
2983114878Sjulianng_btsocket_rfcomm_send_command(ng_btsocket_rfcomm_session_p s,
2984114878Sjulian		u_int8_t type, u_int8_t dlci)
2985114878Sjulian{
2986114878Sjulian	struct rfcomm_cmd_hdr	*hdr = NULL;
2987114878Sjulian	struct mbuf		*m = NULL;
2988114878Sjulian	int			 cr;
2989114878Sjulian
2990114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
2991114878Sjulian
2992114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
2993114878Sjulian"%s: Sending command type %#x, session state=%d, flags=%#x, mtu=%d, dlci=%d\n",
2994114878Sjulian		__func__, type, s->state, s->flags, s->mtu, dlci);
2995114878Sjulian
2996114878Sjulian	switch (type) {
2997114878Sjulian	case RFCOMM_FRAME_SABM:
2998114878Sjulian	case RFCOMM_FRAME_DISC:
2999114878Sjulian		cr = INITIATOR(s);
3000114878Sjulian		break;
3001114878Sjulian
3002114878Sjulian	case RFCOMM_FRAME_UA:
3003114878Sjulian	case RFCOMM_FRAME_DM:
3004114878Sjulian		cr = !INITIATOR(s);
3005114878Sjulian		break;
3006114878Sjulian
3007114878Sjulian	default:
3008114878Sjulian		panic("%s: Invalid frame type=%#x\n", __func__, type);
3009114878Sjulian		return (EINVAL);
3010114878Sjulian		/* NOT REACHED */
3011114878Sjulian	}
3012114878Sjulian
3013243882Sglebius	MGETHDR(m, M_NOWAIT, MT_DATA);
3014114878Sjulian	if (m == NULL)
3015114878Sjulian		return (ENOBUFS);
3016114878Sjulian
3017114878Sjulian	m->m_pkthdr.len = m->m_len = sizeof(*hdr);
3018114878Sjulian
3019114878Sjulian	hdr = mtod(m, struct rfcomm_cmd_hdr *);
3020114878Sjulian	hdr->address = RFCOMM_MKADDRESS(cr, dlci);
3021114878Sjulian	hdr->control = RFCOMM_MKCONTROL(type, 1);
3022114878Sjulian	hdr->length = RFCOMM_MKLEN8(0);
3023114878Sjulian	hdr->fcs = ng_btsocket_rfcomm_fcs3((u_int8_t *) hdr);
3024114878Sjulian
3025114878Sjulian	NG_BT_MBUFQ_ENQUEUE(&s->outq, m);
3026114878Sjulian
3027114878Sjulian	return (0);
3028114878Sjulian} /* ng_btsocket_rfcomm_send_command */
3029114878Sjulian
3030114878Sjulian/*
3031114878Sjulian * Send RFCOMM UIH frame. Caller must hold s->session_mtx
3032114878Sjulian */
3033114878Sjulian
3034114878Sjulianstatic int
3035114878Sjulianng_btsocket_rfcomm_send_uih(ng_btsocket_rfcomm_session_p s, u_int8_t address,
3036114878Sjulian		u_int8_t pf, u_int8_t credits, struct mbuf *data)
3037114878Sjulian{
3038114878Sjulian	struct rfcomm_frame_hdr	*hdr = NULL;
3039114878Sjulian	struct mbuf		*m = NULL, *mcrc = NULL;
3040114878Sjulian	u_int16_t		 length;
3041114878Sjulian
3042114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
3043114878Sjulian
3044243882Sglebius	MGETHDR(m, M_NOWAIT, MT_DATA);
3045114878Sjulian	if (m == NULL) {
3046114878Sjulian		NG_FREE_M(data);
3047114878Sjulian		return (ENOBUFS);
3048114878Sjulian	}
3049114878Sjulian	m->m_pkthdr.len = m->m_len = sizeof(*hdr);
3050114878Sjulian
3051243882Sglebius	MGET(mcrc, M_NOWAIT, MT_DATA);
3052114878Sjulian	if (mcrc == NULL) {
3053114878Sjulian		NG_FREE_M(data);
3054114878Sjulian		return (ENOBUFS);
3055114878Sjulian	}
3056114878Sjulian	mcrc->m_len = 1;
3057114878Sjulian
3058114878Sjulian	/* Fill UIH frame header */
3059114878Sjulian	hdr = mtod(m, struct rfcomm_frame_hdr *);
3060114878Sjulian	hdr->address = address;
3061114878Sjulian	hdr->control = RFCOMM_MKCONTROL(RFCOMM_FRAME_UIH, pf);
3062114878Sjulian
3063114878Sjulian	/* Calculate FCS */
3064114878Sjulian	mcrc->m_data[0] = ng_btsocket_rfcomm_fcs2((u_int8_t *) hdr);
3065114878Sjulian
3066114878Sjulian	/* Put length back */
3067114878Sjulian	length = (data != NULL)? data->m_pkthdr.len : 0;
3068114878Sjulian	if (length > 127) {
3069114878Sjulian		u_int16_t	l = htole16(RFCOMM_MKLEN16(length));
3070114878Sjulian
3071114878Sjulian		bcopy(&l, &hdr->length, sizeof(l));
3072114878Sjulian		m->m_pkthdr.len ++;
3073114878Sjulian		m->m_len ++;
3074114878Sjulian	} else
3075114878Sjulian		hdr->length = RFCOMM_MKLEN8(length);
3076114878Sjulian
3077114878Sjulian	if (pf) {
3078114878Sjulian		m->m_data[m->m_len] = credits;
3079114878Sjulian		m->m_pkthdr.len ++;
3080114878Sjulian		m->m_len ++;
3081114878Sjulian	}
3082114878Sjulian
3083114878Sjulian	/* Add payload */
3084114878Sjulian	if (data != NULL) {
3085114878Sjulian		m_cat(m, data);
3086114878Sjulian		m->m_pkthdr.len += length;
3087114878Sjulian	}
3088114878Sjulian
3089114878Sjulian	/* Put FCS back */
3090114878Sjulian	m_cat(m, mcrc);
3091114878Sjulian	m->m_pkthdr.len ++;
3092114878Sjulian
3093114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
3094114878Sjulian"%s: Sending UIH state=%d, flags=%#x, address=%d, length=%d, pf=%d, " \
3095114878Sjulian"credits=%d, len=%d\n",
3096114878Sjulian		__func__, s->state, s->flags, address, length, pf, credits,
3097114878Sjulian		m->m_pkthdr.len);
3098114878Sjulian
3099114878Sjulian	NG_BT_MBUFQ_ENQUEUE(&s->outq, m);
3100114878Sjulian
3101114878Sjulian	return (0);
3102114878Sjulian} /* ng_btsocket_rfcomm_send_uih */
3103114878Sjulian
3104114878Sjulian/*
3105114878Sjulian * Send MSC request. Caller must hold pcb->pcb_mtx and pcb->session->session_mtx
3106114878Sjulian */
3107114878Sjulian
3108114878Sjulianstatic int
3109114878Sjulianng_btsocket_rfcomm_send_msc(ng_btsocket_rfcomm_pcb_p pcb)
3110114878Sjulian{
3111114878Sjulian	struct mbuf		*m = NULL;
3112114878Sjulian	struct rfcomm_mcc_hdr	*hdr = NULL;
3113114878Sjulian	struct rfcomm_mcc_msc	*msc = NULL;
3114114878Sjulian
3115114878Sjulian	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3116114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3117114878Sjulian
3118243882Sglebius	MGETHDR(m, M_NOWAIT, MT_DATA);
3119114878Sjulian	if (m == NULL)
3120114878Sjulian		return (ENOBUFS);
3121114878Sjulian
3122114878Sjulian	m->m_pkthdr.len = m->m_len = sizeof(*hdr) + sizeof(*msc);
3123114878Sjulian
3124114878Sjulian	hdr = mtod(m, struct rfcomm_mcc_hdr *);
3125114878Sjulian	msc = (struct rfcomm_mcc_msc *)(hdr + 1);
3126114878Sjulian
3127114878Sjulian	hdr->type = RFCOMM_MKMCC_TYPE(1, RFCOMM_MCC_MSC);
3128114878Sjulian	hdr->length = RFCOMM_MKLEN8(sizeof(*msc));
3129114878Sjulian
3130114878Sjulian	msc->address = RFCOMM_MKADDRESS(1, pcb->dlci);
3131114878Sjulian	msc->modem = pcb->lmodem;
3132114878Sjulian
3133114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
3134114878Sjulian"%s: Sending MSC dlci=%d, state=%d, flags=%#x, address=%d, modem=%#x\n",
3135114878Sjulian		__func__, pcb->dlci, pcb->state, pcb->flags, msc->address,
3136114878Sjulian		msc->modem);
3137114878Sjulian
3138114878Sjulian	return (ng_btsocket_rfcomm_send_uih(pcb->session,
3139114878Sjulian			RFCOMM_MKADDRESS(INITIATOR(pcb->session), 0), 0, 0, m));
3140114878Sjulian} /* ng_btsocket_rfcomm_send_msc */
3141114878Sjulian
3142114878Sjulian/*
3143114878Sjulian * Send PN request. Caller must hold pcb->pcb_mtx and pcb->session->session_mtx
3144114878Sjulian */
3145114878Sjulian
3146114878Sjulianstatic int
3147114878Sjulianng_btsocket_rfcomm_send_pn(ng_btsocket_rfcomm_pcb_p pcb)
3148114878Sjulian{
3149114878Sjulian	struct mbuf		*m = NULL;
3150114878Sjulian	struct rfcomm_mcc_hdr	*hdr = NULL;
3151114878Sjulian	struct rfcomm_mcc_pn	*pn = NULL;
3152114878Sjulian
3153114878Sjulian	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3154114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3155114878Sjulian
3156243882Sglebius	MGETHDR(m, M_NOWAIT, MT_DATA);
3157114878Sjulian	if (m == NULL)
3158114878Sjulian		return (ENOBUFS);
3159114878Sjulian
3160114878Sjulian	m->m_pkthdr.len = m->m_len = sizeof(*hdr) + sizeof(*pn);
3161114878Sjulian
3162114878Sjulian	hdr = mtod(m, struct rfcomm_mcc_hdr *);
3163114878Sjulian	pn = (struct rfcomm_mcc_pn *)(hdr + 1);
3164114878Sjulian
3165114878Sjulian	hdr->type = RFCOMM_MKMCC_TYPE(1, RFCOMM_MCC_PN);
3166114878Sjulian	hdr->length = RFCOMM_MKLEN8(sizeof(*pn));
3167114878Sjulian
3168114878Sjulian	pn->dlci = pcb->dlci;
3169121054Semax
3170121054Semax	/*
3171121054Semax	 * Set default DLCI priority as described in GSM 07.10
3172121054Semax	 * (ETSI TS 101 369) clause 5.6 page 42
3173121054Semax	 */
3174121054Semax
3175121054Semax	pn->priority = (pcb->dlci < 56)? (((pcb->dlci >> 3) << 3) + 7) : 61;
3176114878Sjulian	pn->ack_timer = 0;
3177114878Sjulian	pn->mtu = htole16(pcb->mtu);
3178114878Sjulian	pn->max_retrans = 0;
3179114878Sjulian
3180114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC) {
3181114878Sjulian		pn->flow_control = 0xf0;
3182114878Sjulian		pn->credits = pcb->rx_cred;
3183114878Sjulian	} else {
3184114878Sjulian		pn->flow_control = 0;
3185114878Sjulian		pn->credits = 0;
3186114878Sjulian	}
3187114878Sjulian
3188114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
3189114878Sjulian"%s: Sending PN dlci=%d, state=%d, flags=%#x, mtu=%d, flow_control=%#x, " \
3190114878Sjulian"credits=%d\n",	__func__, pcb->dlci, pcb->state, pcb->flags, pcb->mtu,
3191114878Sjulian		pn->flow_control, pn->credits);
3192114878Sjulian
3193114878Sjulian	return (ng_btsocket_rfcomm_send_uih(pcb->session,
3194114878Sjulian			RFCOMM_MKADDRESS(INITIATOR(pcb->session), 0), 0, 0, m));
3195114878Sjulian} /* ng_btsocket_rfcomm_send_pn */
3196114878Sjulian
3197114878Sjulian/*
3198114878Sjulian * Calculate and send credits based on available space in receive buffer
3199114878Sjulian */
3200114878Sjulian
3201114878Sjulianstatic int
3202114878Sjulianng_btsocket_rfcomm_send_credits(ng_btsocket_rfcomm_pcb_p pcb)
3203114878Sjulian{
3204114878Sjulian	int		error = 0;
3205114878Sjulian	u_int8_t	credits;
3206114878Sjulian
3207114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3208114878Sjulian	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3209114878Sjulian
3210114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
3211114878Sjulian"%s: Sending more credits, dlci=%d, state=%d, flags=%#x, mtu=%d, " \
3212114878Sjulian"space=%ld, tx_cred=%d, rx_cred=%d\n",
3213114878Sjulian		__func__, pcb->dlci, pcb->state, pcb->flags, pcb->mtu,
3214114878Sjulian		sbspace(&pcb->so->so_rcv), pcb->tx_cred, pcb->rx_cred);
3215114878Sjulian
3216114878Sjulian	credits = sbspace(&pcb->so->so_rcv) / pcb->mtu;
3217114878Sjulian	if (credits > 0) {
3218114878Sjulian		if (pcb->rx_cred + credits > RFCOMM_MAX_CREDITS)
3219114878Sjulian			credits = RFCOMM_MAX_CREDITS - pcb->rx_cred;
3220114878Sjulian
3221114878Sjulian		error = ng_btsocket_rfcomm_send_uih(
3222114878Sjulian				pcb->session,
3223114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(pcb->session),
3224114878Sjulian					pcb->dlci), 1, credits, NULL);
3225114878Sjulian		if (error == 0) {
3226114878Sjulian			pcb->rx_cred += credits;
3227114878Sjulian
3228114878Sjulian			NG_BTSOCKET_RFCOMM_INFO(
3229114878Sjulian"%s: Gave remote side %d more credits, dlci=%d, state=%d, flags=%#x, " \
3230114878Sjulian"rx_cred=%d, tx_cred=%d\n",	__func__, credits, pcb->dlci, pcb->state,
3231114878Sjulian				pcb->flags, pcb->rx_cred, pcb->tx_cred);
3232114878Sjulian		} else
3233114878Sjulian			NG_BTSOCKET_RFCOMM_ERR(
3234114878Sjulian"%s: Could not send credits, error=%d, dlci=%d, state=%d, flags=%#x, " \
3235114878Sjulian"mtu=%d, space=%ld, tx_cred=%d, rx_cred=%d\n",
3236114878Sjulian				__func__, error, pcb->dlci, pcb->state,
3237114878Sjulian				pcb->flags, pcb->mtu, sbspace(&pcb->so->so_rcv),
3238114878Sjulian				pcb->tx_cred, pcb->rx_cred);
3239114878Sjulian	}
3240114878Sjulian
3241114878Sjulian	return (error);
3242114878Sjulian} /* ng_btsocket_rfcomm_send_credits */
3243114878Sjulian
3244114878Sjulian/*****************************************************************************
3245114878Sjulian *****************************************************************************
3246114878Sjulian **                              RFCOMM DLCs
3247114878Sjulian *****************************************************************************
3248114878Sjulian *****************************************************************************/
3249114878Sjulian
3250114878Sjulian/*
3251114878Sjulian * Send data from socket send buffer
3252114878Sjulian * Caller must hold pcb->pcb_mtx and pcb->session->session_mtx
3253114878Sjulian */
3254114878Sjulian
3255114878Sjulianstatic int
3256114878Sjulianng_btsocket_rfcomm_pcb_send(ng_btsocket_rfcomm_pcb_p pcb, int limit)
3257114878Sjulian{
3258114878Sjulian	struct mbuf	*m = NULL;
3259114878Sjulian	int		 sent, length, error;
3260114878Sjulian
3261114878Sjulian	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3262114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3263114878Sjulian
3264114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC)
3265114878Sjulian		limit = min(limit, pcb->tx_cred);
3266114878Sjulian	else if (!(pcb->rmodem & RFCOMM_MODEM_FC))
3267114878Sjulian		limit = min(limit, RFCOMM_MAX_CREDITS); /* XXX ??? */
3268114878Sjulian	else
3269114878Sjulian		limit = 0;
3270114878Sjulian
3271114878Sjulian	if (limit == 0) {
3272114878Sjulian		NG_BTSOCKET_RFCOMM_INFO(
3273114878Sjulian"%s: Could not send - remote flow control asserted, dlci=%d, flags=%#x, " \
3274114878Sjulian"rmodem=%#x, tx_cred=%d\n",
3275114878Sjulian			__func__, pcb->dlci, pcb->flags, pcb->rmodem,
3276114878Sjulian			pcb->tx_cred);
3277114878Sjulian
3278114878Sjulian		return (0);
3279114878Sjulian	}
3280114878Sjulian
3281114878Sjulian	for (error = 0, sent = 0; sent < limit; sent ++) {
3282114878Sjulian		length = min(pcb->mtu, pcb->so->so_snd.sb_cc);
3283114878Sjulian		if (length == 0)
3284114878Sjulian			break;
3285114878Sjulian
3286114878Sjulian		/* Get the chunk from the socket's send buffer */
3287114878Sjulian		m = ng_btsocket_rfcomm_prepare_packet(&pcb->so->so_snd, length);
3288114878Sjulian		if (m == NULL) {
3289114878Sjulian			error = ENOBUFS;
3290114878Sjulian			break;
3291114878Sjulian		}
3292114878Sjulian
3293114878Sjulian		sbdrop(&pcb->so->so_snd, length);
3294114878Sjulian
3295114878Sjulian		error = ng_btsocket_rfcomm_send_uih(pcb->session,
3296114878Sjulian				RFCOMM_MKADDRESS(INITIATOR(pcb->session),
3297114878Sjulian					pcb->dlci), 0, 0, m);
3298114878Sjulian		if (error != 0)
3299114878Sjulian			break;
3300114878Sjulian	}
3301114878Sjulian
3302114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_CFC)
3303114878Sjulian		pcb->tx_cred -= sent;
3304114878Sjulian
3305114878Sjulian	if (error == 0 && sent > 0) {
3306114878Sjulian		pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_SENDING;
3307114878Sjulian		sowwakeup(pcb->so);
3308114878Sjulian	}
3309114878Sjulian
3310114878Sjulian	return (error);
3311114878Sjulian} /* ng_btsocket_rfcomm_pcb_send */
3312114878Sjulian
3313114878Sjulian/*
3314114878Sjulian * Unlink and disconnect DLC. If ng_btsocket_rfcomm_pcb_kill() returns
3315114878Sjulian * non zero value than socket has no reference and has to be detached.
3316114878Sjulian * Caller must hold pcb->pcb_mtx and pcb->session->session_mtx
3317114878Sjulian */
3318114878Sjulian
3319161623Semaxstatic void
3320114878Sjulianng_btsocket_rfcomm_pcb_kill(ng_btsocket_rfcomm_pcb_p pcb, int error)
3321114878Sjulian{
3322114878Sjulian	ng_btsocket_rfcomm_session_p	s = pcb->session;
3323114878Sjulian
3324114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
3325114878Sjulian"%s: Killing DLC, so=%p, dlci=%d, state=%d, flags=%#x, error=%d\n",
3326114878Sjulian		__func__, pcb->so, pcb->dlci, pcb->state, pcb->flags, error);
3327114878Sjulian
3328114878Sjulian	if (pcb->session == NULL)
3329114878Sjulian		panic("%s: DLC without session, pcb=%p, state=%d, flags=%#x\n",
3330114878Sjulian			__func__, pcb, pcb->state, pcb->flags);
3331114878Sjulian
3332142542Ssam	mtx_assert(&pcb->session->session_mtx, MA_OWNED);
3333142542Ssam	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3334142542Ssam
3335114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)
3336114878Sjulian		ng_btsocket_rfcomm_untimeout(pcb);
3337114878Sjulian
3338114878Sjulian	/* Detach DLC from the session. Does not matter which state DLC in */
3339114878Sjulian	LIST_REMOVE(pcb, session_next);
3340114878Sjulian	pcb->session = NULL;
3341114878Sjulian
3342114878Sjulian	/* Change DLC state and wakeup all sleepers */
3343114878Sjulian	pcb->state = NG_BTSOCKET_RFCOMM_DLC_CLOSED;
3344114878Sjulian	pcb->so->so_error = error;
3345114878Sjulian	soisdisconnected(pcb->so);
3346114878Sjulian	wakeup(&pcb->state);
3347114878Sjulian
3348114878Sjulian	/* Check if we have any DLCs left on the session */
3349114878Sjulian	if (LIST_EMPTY(&s->dlcs) && INITIATOR(s)) {
3350114878Sjulian		NG_BTSOCKET_RFCOMM_INFO(
3351114878Sjulian"%s: Disconnecting session, state=%d, flags=%#x, mtu=%d\n",
3352114878Sjulian			__func__, s->state, s->flags, s->mtu);
3353114878Sjulian
3354114878Sjulian		switch (s->state) {
3355114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_CLOSED:
3356114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING:
3357114878Sjulian			/*
3358114878Sjulian			 * Do not have to do anything here. We can get here
3359114878Sjulian			 * when L2CAP connection was terminated or we have
3360114878Sjulian			 * received DISC on multiplexor channel
3361114878Sjulian			 */
3362114878Sjulian			break;
3363114878Sjulian
3364114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_OPEN:
3365114878Sjulian			/* Send DISC on multiplexor channel */
3366114878Sjulian			error = ng_btsocket_rfcomm_send_command(s,
3367114878Sjulian					RFCOMM_FRAME_DISC, 0);
3368114878Sjulian			if (error == 0) {
3369114878Sjulian				s->state = NG_BTSOCKET_RFCOMM_SESSION_DISCONNECTING;
3370114878Sjulian				break;
3371114878Sjulian			}
3372114878Sjulian			/* FALL THROUGH */
3373114878Sjulian
3374114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_CONNECTING:
3375114878Sjulian		case NG_BTSOCKET_RFCOMM_SESSION_CONNECTED:
3376114878Sjulian			s->state = NG_BTSOCKET_RFCOMM_SESSION_CLOSED;
3377114878Sjulian			break;
3378114878Sjulian
3379114878Sjulian/*		case NG_BTSOCKET_RFCOMM_SESSION_LISTENING: */
3380114878Sjulian		default:
3381114878Sjulian			panic("%s: Invalid session state=%d, flags=%#x\n",
3382114878Sjulian				__func__, s->state, s->flags);
3383114878Sjulian			break;
3384114878Sjulian		}
3385114878Sjulian
3386114878Sjulian		ng_btsocket_rfcomm_task_wakeup();
3387114878Sjulian	}
3388114878Sjulian} /* ng_btsocket_rfcomm_pcb_kill */
3389114878Sjulian
3390114878Sjulian/*
3391114878Sjulian * Look for given dlci for given RFCOMM session. Caller must hold s->session_mtx
3392114878Sjulian */
3393114878Sjulian
3394114878Sjulianstatic ng_btsocket_rfcomm_pcb_p
3395114878Sjulianng_btsocket_rfcomm_pcb_by_dlci(ng_btsocket_rfcomm_session_p s, int dlci)
3396114878Sjulian{
3397114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = NULL;
3398114878Sjulian
3399114878Sjulian	mtx_assert(&s->session_mtx, MA_OWNED);
3400114878Sjulian
3401114878Sjulian	LIST_FOREACH(pcb, &s->dlcs, session_next)
3402114878Sjulian		if (pcb->dlci == dlci)
3403114878Sjulian			break;
3404114878Sjulian
3405114878Sjulian	return (pcb);
3406114878Sjulian} /* ng_btsocket_rfcomm_pcb_by_dlci */
3407114878Sjulian
3408114878Sjulian/*
3409114878Sjulian * Look for socket that listens on given src address and given channel
3410114878Sjulian */
3411114878Sjulian
3412114878Sjulianstatic ng_btsocket_rfcomm_pcb_p
3413114878Sjulianng_btsocket_rfcomm_pcb_listener(bdaddr_p src, int channel)
3414114878Sjulian{
3415114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = NULL, pcb1 = NULL;
3416114878Sjulian
3417114878Sjulian	mtx_lock(&ng_btsocket_rfcomm_sockets_mtx);
3418114878Sjulian
3419114878Sjulian	LIST_FOREACH(pcb, &ng_btsocket_rfcomm_sockets, next) {
3420114878Sjulian		if (pcb->channel != channel ||
3421114878Sjulian		    !(pcb->so->so_options & SO_ACCEPTCONN))
3422114878Sjulian			continue;
3423114878Sjulian
3424114878Sjulian		if (bcmp(&pcb->src, src, sizeof(*src)) == 0)
3425114878Sjulian			break;
3426114878Sjulian
3427114878Sjulian		if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
3428114878Sjulian			pcb1 = pcb;
3429114878Sjulian	}
3430114878Sjulian
3431114878Sjulian	mtx_unlock(&ng_btsocket_rfcomm_sockets_mtx);
3432114878Sjulian
3433114878Sjulian	return ((pcb != NULL)? pcb : pcb1);
3434114878Sjulian} /* ng_btsocket_rfcomm_pcb_listener */
3435114878Sjulian
3436114878Sjulian/*****************************************************************************
3437114878Sjulian *****************************************************************************
3438114878Sjulian **                              Misc. functions
3439114878Sjulian *****************************************************************************
3440114878Sjulian *****************************************************************************/
3441114878Sjulian
3442114878Sjulian/*
3443114878Sjulian *  Set timeout. Caller MUST hold pcb_mtx
3444114878Sjulian */
3445114878Sjulian
3446114878Sjulianstatic void
3447114878Sjulianng_btsocket_rfcomm_timeout(ng_btsocket_rfcomm_pcb_p pcb)
3448114878Sjulian{
3449114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3450114878Sjulian
3451114878Sjulian	if (!(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO)) {
3452114878Sjulian		pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_TIMO;
3453114878Sjulian		pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT;
3454114878Sjulian		pcb->timo = timeout(ng_btsocket_rfcomm_process_timeout, pcb,
3455114878Sjulian					ng_btsocket_rfcomm_timo * hz);
3456114878Sjulian	} else
3457114878Sjulian		panic("%s: Duplicated socket timeout?!\n", __func__);
3458114878Sjulian} /* ng_btsocket_rfcomm_timeout */
3459114878Sjulian
3460114878Sjulian/*
3461114878Sjulian *  Unset pcb timeout. Caller MUST hold pcb_mtx
3462114878Sjulian */
3463114878Sjulian
3464114878Sjulianstatic void
3465114878Sjulianng_btsocket_rfcomm_untimeout(ng_btsocket_rfcomm_pcb_p pcb)
3466114878Sjulian{
3467114878Sjulian	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
3468114878Sjulian
3469114878Sjulian	if (pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO) {
3470114878Sjulian		untimeout(ng_btsocket_rfcomm_process_timeout, pcb, pcb->timo);
3471114878Sjulian		pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMO;
3472114878Sjulian		pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT;
3473114878Sjulian	} else
3474114878Sjulian		panic("%s: No socket timeout?!\n", __func__);
3475114878Sjulian} /* ng_btsocket_rfcomm_timeout */
3476114878Sjulian
3477114878Sjulian/*
3478114878Sjulian * Process pcb timeout
3479114878Sjulian */
3480114878Sjulian
3481114878Sjulianstatic void
3482114878Sjulianng_btsocket_rfcomm_process_timeout(void *xpcb)
3483114878Sjulian{
3484114878Sjulian	ng_btsocket_rfcomm_pcb_p	pcb = (ng_btsocket_rfcomm_pcb_p) xpcb;
3485114878Sjulian
3486114878Sjulian	mtx_lock(&pcb->pcb_mtx);
3487114878Sjulian
3488114878Sjulian	NG_BTSOCKET_RFCOMM_INFO(
3489114878Sjulian"%s: Timeout, so=%p, dlci=%d, state=%d, flags=%#x\n",
3490114878Sjulian		__func__, pcb->so, pcb->dlci, pcb->state, pcb->flags);
3491114878Sjulian
3492114878Sjulian	pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMO;
3493114878Sjulian	pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT;
3494114878Sjulian
3495114878Sjulian	switch (pcb->state) {
3496114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING:
3497114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
3498114878Sjulian		pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING;
3499114878Sjulian		break;
3500114878Sjulian
3501114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
3502114878Sjulian	case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
3503114878Sjulian		break;
3504114878Sjulian
3505114878Sjulian	default:
3506114878Sjulian		panic(
3507114878Sjulian"%s: DLC timeout in invalid state, dlci=%d, state=%d, flags=%#x\n",
3508114878Sjulian			__func__, pcb->dlci, pcb->state, pcb->flags);
3509114878Sjulian		break;
3510114878Sjulian	}
3511114878Sjulian
3512114878Sjulian	ng_btsocket_rfcomm_task_wakeup();
3513114878Sjulian
3514114878Sjulian	mtx_unlock(&pcb->pcb_mtx);
3515114878Sjulian} /* ng_btsocket_rfcomm_process_timeout */
3516114878Sjulian
3517114878Sjulian/*
3518114878Sjulian * Get up to length bytes from the socket buffer
3519114878Sjulian */
3520114878Sjulian
3521114878Sjulianstatic struct mbuf *
3522114878Sjulianng_btsocket_rfcomm_prepare_packet(struct sockbuf *sb, int length)
3523114878Sjulian{
3524114878Sjulian	struct mbuf	*top = NULL, *m = NULL, *n = NULL, *nextpkt = NULL;
3525114878Sjulian	int		 mlen, noff, len;
3526114878Sjulian
3527243882Sglebius	MGETHDR(top, M_NOWAIT, MT_DATA);
3528114878Sjulian	if (top == NULL)
3529114878Sjulian		return (NULL);
3530114878Sjulian
3531114878Sjulian	top->m_pkthdr.len = length;
3532114878Sjulian	top->m_len = 0;
3533114878Sjulian	mlen = MHLEN;
3534114878Sjulian
3535114878Sjulian	m = top;
3536114878Sjulian	n = sb->sb_mb;
3537114878Sjulian	nextpkt = n->m_nextpkt;
3538114878Sjulian	noff = 0;
3539114878Sjulian
3540114878Sjulian	while (length > 0 && n != NULL) {
3541114878Sjulian		len = min(mlen - m->m_len, n->m_len - noff);
3542114878Sjulian		if (len > length)
3543114878Sjulian			len = length;
3544114878Sjulian
3545114878Sjulian		bcopy(mtod(n, caddr_t)+noff, mtod(m, caddr_t)+m->m_len, len);
3546114878Sjulian		m->m_len += len;
3547114878Sjulian		noff += len;
3548114878Sjulian		length -= len;
3549114878Sjulian
3550114878Sjulian		if (length > 0 && m->m_len == mlen) {
3551243882Sglebius			MGET(m->m_next, M_NOWAIT, MT_DATA);
3552114878Sjulian			if (m->m_next == NULL) {
3553114878Sjulian				NG_FREE_M(top);
3554114878Sjulian				return (NULL);
3555114878Sjulian			}
3556114878Sjulian
3557114878Sjulian			m = m->m_next;
3558114878Sjulian			m->m_len = 0;
3559114878Sjulian			mlen = MLEN;
3560114878Sjulian		}
3561114878Sjulian
3562114878Sjulian		if (noff == n->m_len) {
3563114878Sjulian			noff = 0;
3564114878Sjulian			n = n->m_next;
3565114878Sjulian
3566114878Sjulian			if (n == NULL)
3567114878Sjulian				n = nextpkt;
3568114878Sjulian
3569114878Sjulian			nextpkt = (n != NULL)? n->m_nextpkt : NULL;
3570114878Sjulian		}
3571114878Sjulian	}
3572114878Sjulian
3573114878Sjulian	if (length < 0)
3574114878Sjulian		panic("%s: length=%d\n", __func__, length);
3575114878Sjulian	if (length > 0 && n == NULL)
3576114878Sjulian		panic("%s: bogus length=%d, n=%p\n", __func__, length, n);
3577114878Sjulian
3578114878Sjulian	return (top);
3579114878Sjulian} /* ng_btsocket_rfcomm_prepare_packet */
3580114878Sjulian
3581