1/*
2 * ng_btsocket_sco.c
3 */
4
5/*-
6 * SPDX-License-Identifier: BSD-2-Clause
7 *
8 * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $Id: ng_btsocket_sco.c,v 1.2 2005/10/31 18:08:51 max Exp $
33 */
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/bitstring.h>
38#include <sys/domain.h>
39#include <sys/endian.h>
40#include <sys/errno.h>
41#include <sys/filedesc.h>
42#include <sys/ioccom.h>
43#include <sys/kernel.h>
44#include <sys/lock.h>
45#include <sys/malloc.h>
46#include <sys/mbuf.h>
47#include <sys/mutex.h>
48#include <sys/protosw.h>
49#include <sys/queue.h>
50#include <sys/socket.h>
51#include <sys/socketvar.h>
52#include <sys/sysctl.h>
53#include <sys/taskqueue.h>
54
55#include <net/vnet.h>
56
57#include <netgraph/ng_message.h>
58#include <netgraph/netgraph.h>
59#include <netgraph/bluetooth/include/ng_bluetooth.h>
60#include <netgraph/bluetooth/include/ng_hci.h>
61#include <netgraph/bluetooth/include/ng_l2cap.h>
62#include <netgraph/bluetooth/include/ng_btsocket.h>
63#include <netgraph/bluetooth/include/ng_btsocket_sco.h>
64
65/* MALLOC define */
66#ifdef NG_SEPARATE_MALLOC
67static MALLOC_DEFINE(M_NETGRAPH_BTSOCKET_SCO, "netgraph_btsocks_sco",
68		"Netgraph Bluetooth SCO sockets");
69#else
70#define M_NETGRAPH_BTSOCKET_SCO M_NETGRAPH
71#endif /* NG_SEPARATE_MALLOC */
72
73/* Netgraph node methods */
74static ng_constructor_t	ng_btsocket_sco_node_constructor;
75static ng_rcvmsg_t	ng_btsocket_sco_node_rcvmsg;
76static ng_shutdown_t	ng_btsocket_sco_node_shutdown;
77static ng_newhook_t	ng_btsocket_sco_node_newhook;
78static ng_connect_t	ng_btsocket_sco_node_connect;
79static ng_rcvdata_t	ng_btsocket_sco_node_rcvdata;
80static ng_disconnect_t	ng_btsocket_sco_node_disconnect;
81
82static void		ng_btsocket_sco_input   (void *, int);
83static void		ng_btsocket_sco_rtclean (void *, int);
84
85/* Netgraph type descriptor */
86static struct ng_type	typestruct = {
87	.version =	NG_ABI_VERSION,
88	.name =		NG_BTSOCKET_SCO_NODE_TYPE,
89	.constructor =	ng_btsocket_sco_node_constructor,
90	.rcvmsg =	ng_btsocket_sco_node_rcvmsg,
91	.shutdown =	ng_btsocket_sco_node_shutdown,
92	.newhook =	ng_btsocket_sco_node_newhook,
93	.connect =	ng_btsocket_sco_node_connect,
94	.rcvdata =	ng_btsocket_sco_node_rcvdata,
95	.disconnect =	ng_btsocket_sco_node_disconnect,
96};
97
98/* Globals */
99static u_int32_t				ng_btsocket_sco_debug_level;
100static node_p					ng_btsocket_sco_node;
101static struct ng_bt_itemq			ng_btsocket_sco_queue;
102static struct mtx				ng_btsocket_sco_queue_mtx;
103static struct task				ng_btsocket_sco_queue_task;
104static struct mtx				ng_btsocket_sco_sockets_mtx;
105static LIST_HEAD(, ng_btsocket_sco_pcb)		ng_btsocket_sco_sockets;
106static LIST_HEAD(, ng_btsocket_sco_rtentry)	ng_btsocket_sco_rt;
107static struct mtx				ng_btsocket_sco_rt_mtx;
108static struct task				ng_btsocket_sco_rt_task;
109static struct timeval				ng_btsocket_sco_lasttime;
110static int					ng_btsocket_sco_curpps;
111
112/* Sysctl tree */
113SYSCTL_DECL(_net_bluetooth_sco_sockets);
114static SYSCTL_NODE(_net_bluetooth_sco_sockets, OID_AUTO, seq,
115    CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
116    "Bluetooth SEQPACKET SCO sockets family");
117SYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, debug_level,
118	CTLFLAG_RW,
119	&ng_btsocket_sco_debug_level, NG_BTSOCKET_WARN_LEVEL,
120	"Bluetooth SEQPACKET SCO sockets debug level");
121SYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, queue_len,
122	CTLFLAG_RD,
123	&ng_btsocket_sco_queue.len, 0,
124	"Bluetooth SEQPACKET SCO sockets input queue length");
125SYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, queue_maxlen,
126	CTLFLAG_RD,
127	&ng_btsocket_sco_queue.maxlen, 0,
128	"Bluetooth SEQPACKET SCO sockets input queue max. length");
129SYSCTL_UINT(_net_bluetooth_sco_sockets_seq, OID_AUTO, queue_drops,
130	CTLFLAG_RD,
131	&ng_btsocket_sco_queue.drops, 0,
132	"Bluetooth SEQPACKET SCO sockets input queue drops");
133
134/* Debug */
135#define NG_BTSOCKET_SCO_INFO \
136	if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_INFO_LEVEL && \
137	    ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \
138		printf
139
140#define NG_BTSOCKET_SCO_WARN \
141	if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_WARN_LEVEL && \
142	    ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \
143		printf
144
145#define NG_BTSOCKET_SCO_ERR \
146	if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_ERR_LEVEL && \
147	    ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \
148		printf
149
150#define NG_BTSOCKET_SCO_ALERT \
151	if (ng_btsocket_sco_debug_level >= NG_BTSOCKET_ALERT_LEVEL && \
152	    ppsratecheck(&ng_btsocket_sco_lasttime, &ng_btsocket_sco_curpps, 1)) \
153		printf
154
155/*
156 * Netgraph message processing routines
157 */
158
159static int ng_btsocket_sco_process_lp_con_cfm
160	(struct ng_mesg *, ng_btsocket_sco_rtentry_p);
161static int ng_btsocket_sco_process_lp_con_ind
162	(struct ng_mesg *, ng_btsocket_sco_rtentry_p);
163static int ng_btsocket_sco_process_lp_discon_ind
164	(struct ng_mesg *, ng_btsocket_sco_rtentry_p);
165
166/*
167 * Send LP messages to the lower layer
168 */
169
170static int  ng_btsocket_sco_send_lp_con_req
171	(ng_btsocket_sco_pcb_p);
172static int  ng_btsocket_sco_send_lp_con_rsp
173	(ng_btsocket_sco_rtentry_p, bdaddr_p, int);
174static int  ng_btsocket_sco_send_lp_discon_req
175	(ng_btsocket_sco_pcb_p);
176
177static int ng_btsocket_sco_send2
178	(ng_btsocket_sco_pcb_p);
179
180/*
181 * Timeout processing routines
182 */
183
184static void ng_btsocket_sco_timeout         (ng_btsocket_sco_pcb_p);
185static void ng_btsocket_sco_untimeout       (ng_btsocket_sco_pcb_p);
186static void ng_btsocket_sco_process_timeout (void *);
187
188/*
189 * Other stuff
190 */
191
192static ng_btsocket_sco_pcb_p	ng_btsocket_sco_pcb_by_addr(bdaddr_p);
193static ng_btsocket_sco_pcb_p	ng_btsocket_sco_pcb_by_handle(bdaddr_p, int);
194static ng_btsocket_sco_pcb_p	ng_btsocket_sco_pcb_by_addrs(bdaddr_p, bdaddr_p);
195
196#define ng_btsocket_sco_wakeup_input_task() \
197	taskqueue_enqueue(taskqueue_swi, &ng_btsocket_sco_queue_task)
198
199#define ng_btsocket_sco_wakeup_route_task() \
200	taskqueue_enqueue(taskqueue_swi, &ng_btsocket_sco_rt_task)
201
202/*****************************************************************************
203 *****************************************************************************
204 **                        Netgraph node interface
205 *****************************************************************************
206 *****************************************************************************/
207
208/*
209 * Netgraph node constructor. Do not allow to create node of this type.
210 */
211
212static int
213ng_btsocket_sco_node_constructor(node_p node)
214{
215	return (EINVAL);
216} /* ng_btsocket_sco_node_constructor */
217
218/*
219 * Do local shutdown processing. Let old node go and create new fresh one.
220 */
221
222static int
223ng_btsocket_sco_node_shutdown(node_p node)
224{
225	int	error = 0;
226
227	NG_NODE_UNREF(node);
228
229	/* Create new node */
230	error = ng_make_node_common(&typestruct, &ng_btsocket_sco_node);
231	if (error != 0) {
232		NG_BTSOCKET_SCO_ALERT(
233"%s: Could not create Netgraph node, error=%d\n", __func__, error);
234
235		ng_btsocket_sco_node = NULL;
236
237		return (error);
238	}
239
240	error = ng_name_node(ng_btsocket_sco_node,
241				NG_BTSOCKET_SCO_NODE_TYPE);
242	if (error != 0) {
243		NG_BTSOCKET_SCO_ALERT(
244"%s: Could not name Netgraph node, error=%d\n", __func__, error);
245
246		NG_NODE_UNREF(ng_btsocket_sco_node);
247		ng_btsocket_sco_node = NULL;
248
249		return (error);
250	}
251
252	return (0);
253} /* ng_btsocket_sco_node_shutdown */
254
255/*
256 * We allow any hook to be connected to the node.
257 */
258
259static int
260ng_btsocket_sco_node_newhook(node_p node, hook_p hook, char const *name)
261{
262	return (0);
263} /* ng_btsocket_sco_node_newhook */
264
265/*
266 * Just say "YEP, that's OK by me!"
267 */
268
269static int
270ng_btsocket_sco_node_connect(hook_p hook)
271{
272	NG_HOOK_SET_PRIVATE(hook, NULL);
273	NG_HOOK_REF(hook); /* Keep extra reference to the hook */
274
275#if 0
276	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
277	NG_HOOK_FORCE_QUEUE(hook);
278#endif
279
280	return (0);
281} /* ng_btsocket_sco_node_connect */
282
283/*
284 * Hook disconnection. Schedule route cleanup task
285 */
286
287static int
288ng_btsocket_sco_node_disconnect(hook_p hook)
289{
290	/*
291	 * If hook has private information than we must have this hook in
292	 * the routing table and must schedule cleaning for the routing table.
293	 * Otherwise hook was connected but we never got "hook_info" message,
294	 * so we have never added this hook to the routing table and it save
295	 * to just delete it.
296	 */
297
298	if (NG_HOOK_PRIVATE(hook) != NULL)
299		return (ng_btsocket_sco_wakeup_route_task());
300
301	NG_HOOK_UNREF(hook); /* Remove extra reference */
302
303	return (0);
304} /* ng_btsocket_sco_node_disconnect */
305
306/*
307 * Process incoming messages
308 */
309
310static int
311ng_btsocket_sco_node_rcvmsg(node_p node, item_p item, hook_p hook)
312{
313	struct ng_mesg	*msg = NGI_MSG(item); /* item still has message */
314	int		 error = 0;
315
316	if (msg != NULL && msg->header.typecookie == NGM_HCI_COOKIE) {
317		mtx_lock(&ng_btsocket_sco_queue_mtx);
318		if (NG_BT_ITEMQ_FULL(&ng_btsocket_sco_queue)) {
319			NG_BTSOCKET_SCO_ERR(
320"%s: Input queue is full (msg)\n", __func__);
321
322			NG_BT_ITEMQ_DROP(&ng_btsocket_sco_queue);
323			NG_FREE_ITEM(item);
324			error = ENOBUFS;
325		} else {
326			if (hook != NULL) {
327				NG_HOOK_REF(hook);
328				NGI_SET_HOOK(item, hook);
329			}
330
331			NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_sco_queue, item);
332			error = ng_btsocket_sco_wakeup_input_task();
333		}
334		mtx_unlock(&ng_btsocket_sco_queue_mtx);
335	} else {
336		NG_FREE_ITEM(item);
337		error = EINVAL;
338	}
339
340	return (error);
341} /* ng_btsocket_sco_node_rcvmsg */
342
343/*
344 * Receive data on a hook
345 */
346
347static int
348ng_btsocket_sco_node_rcvdata(hook_p hook, item_p item)
349{
350	int	error = 0;
351
352	mtx_lock(&ng_btsocket_sco_queue_mtx);
353	if (NG_BT_ITEMQ_FULL(&ng_btsocket_sco_queue)) {
354		NG_BTSOCKET_SCO_ERR(
355"%s: Input queue is full (data)\n", __func__);
356
357		NG_BT_ITEMQ_DROP(&ng_btsocket_sco_queue);
358		NG_FREE_ITEM(item);
359		error = ENOBUFS;
360	} else {
361		NG_HOOK_REF(hook);
362		NGI_SET_HOOK(item, hook);
363
364		NG_BT_ITEMQ_ENQUEUE(&ng_btsocket_sco_queue, item);
365		error = ng_btsocket_sco_wakeup_input_task();
366	}
367	mtx_unlock(&ng_btsocket_sco_queue_mtx);
368
369	return (error);
370} /* ng_btsocket_sco_node_rcvdata */
371
372/*
373 * Process LP_ConnectCfm event from the lower layer protocol
374 */
375
376static int
377ng_btsocket_sco_process_lp_con_cfm(struct ng_mesg *msg,
378		ng_btsocket_sco_rtentry_p rt)
379{
380	ng_hci_lp_con_cfm_ep	*ep = NULL;
381	ng_btsocket_sco_pcb_t	*pcb = NULL;
382	int			 error = 0;
383
384	if (msg->header.arglen != sizeof(*ep))
385		return (EMSGSIZE);
386
387	ep = (ng_hci_lp_con_cfm_ep *)(msg->data);
388
389	mtx_lock(&ng_btsocket_sco_sockets_mtx);
390
391	/* Look for the socket with the token */
392	pcb = ng_btsocket_sco_pcb_by_addrs(&rt->src, &ep->bdaddr);
393	if (pcb == NULL) {
394		mtx_unlock(&ng_btsocket_sco_sockets_mtx);
395		return (ENOENT);
396	}
397
398	/* pcb is locked */
399
400	NG_BTSOCKET_SCO_INFO(
401"%s: Got LP_ConnectCfm response, src bdaddr=%x:%x:%x:%x:%x:%x, " \
402"dst bdaddr=%x:%x:%x:%x:%x:%x, status=%d, handle=%d, state=%d\n",
403		__func__,
404		pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
405		pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
406		pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
407		pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
408		ep->status, ep->con_handle, pcb->state);
409
410	if (pcb->state != NG_BTSOCKET_SCO_CONNECTING) {
411		mtx_unlock(&pcb->pcb_mtx);
412		mtx_unlock(&ng_btsocket_sco_sockets_mtx);
413
414		return (ENOENT);
415	}
416
417	ng_btsocket_sco_untimeout(pcb);
418
419	if (ep->status == 0) {
420		/*
421		 * Connection is open. Update connection handle and
422		 * socket state
423		 */
424
425		pcb->con_handle = ep->con_handle;
426		pcb->state = NG_BTSOCKET_SCO_OPEN;
427		soisconnected(pcb->so);
428	} else {
429		/*
430		 * We have failed to open connection, so disconnect the socket
431		 */
432
433		pcb->so->so_error = ECONNREFUSED; /* XXX convert status ??? */
434		pcb->state = NG_BTSOCKET_SCO_CLOSED;
435		soisdisconnected(pcb->so);
436	}
437
438	mtx_unlock(&pcb->pcb_mtx);
439	mtx_unlock(&ng_btsocket_sco_sockets_mtx);
440
441	return (error);
442} /* ng_btsocket_sco_process_lp_con_cfm */
443
444/*
445 * Process LP_ConnectInd indicator. Find socket that listens on address.
446 * Find exact or closest match.
447 */
448
449static int
450ng_btsocket_sco_process_lp_con_ind(struct ng_mesg *msg,
451		ng_btsocket_sco_rtentry_p rt)
452{
453	ng_hci_lp_con_ind_ep	*ep = NULL;
454	ng_btsocket_sco_pcb_t	*pcb = NULL, *pcb1 = NULL;
455	int			 error = 0;
456	u_int16_t		 status = 0;
457
458	if (msg->header.arglen != sizeof(*ep))
459		return (EMSGSIZE);
460
461	ep = (ng_hci_lp_con_ind_ep *)(msg->data);
462
463	NG_BTSOCKET_SCO_INFO(
464"%s: Got LP_ConnectInd indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \
465"dst bdaddr=%x:%x:%x:%x:%x:%x\n",
466		__func__,
467		rt->src.b[5], rt->src.b[4], rt->src.b[3],
468		rt->src.b[2], rt->src.b[1], rt->src.b[0],
469		ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
470		ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
471
472	mtx_lock(&ng_btsocket_sco_sockets_mtx);
473
474	pcb = ng_btsocket_sco_pcb_by_addr(&rt->src);
475	if (pcb != NULL) {
476		struct socket *so1;
477
478		/* pcb is locked */
479
480		CURVNET_SET(pcb->so->so_vnet);
481		so1 = sonewconn(pcb->so, 0);
482		CURVNET_RESTORE();
483
484		if (so1 == NULL) {
485			status = 0x0d; /* Rejected due to limited resources */
486			goto respond;
487		}
488
489		/*
490		 * If we got here than we have created new socket. So complete
491		 * connection. If we we listening on specific address then copy
492		 * source address from listening socket, otherwise copy source
493		 * address from hook's routing information.
494		 */
495
496		pcb1 = so2sco_pcb(so1);
497		KASSERT((pcb1 != NULL),
498("%s: pcb1 == NULL\n", __func__));
499
500 		mtx_lock(&pcb1->pcb_mtx);
501
502		if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src)) != 0)
503			bcopy(&pcb->src, &pcb1->src, sizeof(pcb1->src));
504		else
505			bcopy(&rt->src, &pcb1->src, sizeof(pcb1->src));
506
507		pcb1->flags &= ~NG_BTSOCKET_SCO_CLIENT;
508
509		bcopy(&ep->bdaddr, &pcb1->dst, sizeof(pcb1->dst));
510		pcb1->rt = rt;
511	} else
512		/* Nobody listens on requested BDADDR */
513		status = 0x1f; /* Unspecified Error */
514
515respond:
516	error = ng_btsocket_sco_send_lp_con_rsp(rt, &ep->bdaddr, status);
517	if (pcb1 != NULL) {
518		if (error != 0) {
519			pcb1->so->so_error = error;
520			pcb1->state = NG_BTSOCKET_SCO_CLOSED;
521			soisdisconnected(pcb1->so);
522		} else {
523			pcb1->state = NG_BTSOCKET_SCO_CONNECTING;
524			soisconnecting(pcb1->so);
525
526			ng_btsocket_sco_timeout(pcb1);
527		}
528
529		mtx_unlock(&pcb1->pcb_mtx);
530	}
531
532	if (pcb != NULL)
533		mtx_unlock(&pcb->pcb_mtx);
534
535	mtx_unlock(&ng_btsocket_sco_sockets_mtx);
536
537	return (error);
538} /* ng_btsocket_sco_process_lp_con_ind */
539
540/*
541 * Process LP_DisconnectInd indicator
542 */
543
544static int
545ng_btsocket_sco_process_lp_discon_ind(struct ng_mesg *msg,
546		ng_btsocket_sco_rtentry_p rt)
547{
548	ng_hci_lp_discon_ind_ep	*ep = NULL;
549	ng_btsocket_sco_pcb_t	*pcb = NULL;
550
551	/* Check message */
552	if (msg->header.arglen != sizeof(*ep))
553		return (EMSGSIZE);
554
555	ep = (ng_hci_lp_discon_ind_ep *)(msg->data);
556
557	mtx_lock(&ng_btsocket_sco_sockets_mtx);
558
559	/* Look for the socket with given channel ID */
560	pcb = ng_btsocket_sco_pcb_by_handle(&rt->src, ep->con_handle);
561	if (pcb == NULL) {
562		mtx_unlock(&ng_btsocket_sco_sockets_mtx);
563		return (0);
564	}
565
566	/*
567	 * Disconnect the socket. If there was any pending request we can
568	 * not do anything here anyway.
569	 */
570
571	/* pcb is locked */
572
573       	NG_BTSOCKET_SCO_INFO(
574"%s: Got LP_DisconnectInd indicator, src bdaddr=%x:%x:%x:%x:%x:%x, " \
575"dst bdaddr=%x:%x:%x:%x:%x:%x, handle=%d, state=%d\n",
576		__func__,
577		pcb->src.b[5], pcb->src.b[4], pcb->src.b[3],
578		pcb->src.b[2], pcb->src.b[1], pcb->src.b[0],
579		pcb->dst.b[5], pcb->dst.b[4], pcb->dst.b[3],
580		pcb->dst.b[2], pcb->dst.b[1], pcb->dst.b[0],
581		pcb->con_handle, pcb->state);
582
583	if (pcb->flags & NG_BTSOCKET_SCO_TIMO)
584		ng_btsocket_sco_untimeout(pcb);
585
586	pcb->state = NG_BTSOCKET_SCO_CLOSED;
587	soisdisconnected(pcb->so);
588
589	mtx_unlock(&pcb->pcb_mtx);
590	mtx_unlock(&ng_btsocket_sco_sockets_mtx);
591
592	return (0);
593} /* ng_btsocket_sco_process_lp_discon_ind */
594
595/*
596 * Send LP_ConnectReq request
597 */
598
599static int
600ng_btsocket_sco_send_lp_con_req(ng_btsocket_sco_pcb_p pcb)
601{
602	struct ng_mesg		*msg = NULL;
603	ng_hci_lp_con_req_ep	*ep = NULL;
604	int			 error = 0;
605
606	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
607
608	if (pcb->rt == NULL ||
609	    pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))
610		return (ENETDOWN);
611
612	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_REQ,
613		sizeof(*ep), M_NOWAIT);
614	if (msg == NULL)
615		return (ENOMEM);
616
617	ep = (ng_hci_lp_con_req_ep *)(msg->data);
618	ep->link_type = NG_HCI_LINK_SCO;
619	bcopy(&pcb->dst, &ep->bdaddr, sizeof(ep->bdaddr));
620
621	NG_SEND_MSG_HOOK(error, ng_btsocket_sco_node, msg, pcb->rt->hook, 0);
622
623	return (error);
624} /* ng_btsocket_sco_send_lp_con_req */
625
626/*
627 * Send LP_ConnectRsp response
628 */
629
630static int
631ng_btsocket_sco_send_lp_con_rsp(ng_btsocket_sco_rtentry_p rt, bdaddr_p dst, int status)
632{
633	struct ng_mesg		*msg = NULL;
634	ng_hci_lp_con_rsp_ep	*ep = NULL;
635	int			 error = 0;
636
637	if (rt == NULL || rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))
638		return (ENETDOWN);
639
640	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP,
641		sizeof(*ep), M_NOWAIT);
642	if (msg == NULL)
643		return (ENOMEM);
644
645	ep = (ng_hci_lp_con_rsp_ep *)(msg->data);
646	ep->status = status;
647	ep->link_type = NG_HCI_LINK_SCO;
648	bcopy(dst, &ep->bdaddr, sizeof(ep->bdaddr));
649
650	NG_SEND_MSG_HOOK(error, ng_btsocket_sco_node, msg, rt->hook, 0);
651
652	return (error);
653} /* ng_btsocket_sco_send_lp_con_rsp */
654
655/*
656 * Send LP_DisconReq request
657 */
658
659static int
660ng_btsocket_sco_send_lp_discon_req(ng_btsocket_sco_pcb_p pcb)
661{
662	struct ng_mesg		*msg = NULL;
663	ng_hci_lp_discon_req_ep	*ep = NULL;
664	int			 error = 0;
665
666	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
667
668	if (pcb->rt == NULL ||
669	    pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook))
670		return (ENETDOWN);
671
672	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_REQ,
673		sizeof(*ep), M_NOWAIT);
674	if (msg == NULL)
675		return (ENOMEM);
676
677	ep = (ng_hci_lp_discon_req_ep *)(msg->data);
678	ep->con_handle = pcb->con_handle;
679	ep->reason = 0x13; /* User Ended Connection */
680
681	NG_SEND_MSG_HOOK(error, ng_btsocket_sco_node, msg, pcb->rt->hook, 0);
682
683	return (error);
684} /* ng_btsocket_sco_send_lp_discon_req */
685
686/*****************************************************************************
687 *****************************************************************************
688 **                              Socket interface
689 *****************************************************************************
690 *****************************************************************************/
691
692/*
693 * SCO sockets data input routine
694 */
695
696static void
697ng_btsocket_sco_data_input(struct mbuf *m, hook_p hook)
698{
699	ng_hci_scodata_pkt_t		*hdr = NULL;
700	ng_btsocket_sco_pcb_t		*pcb = NULL;
701	ng_btsocket_sco_rtentry_t	*rt = NULL;
702	u_int16_t			 con_handle;
703
704	if (hook == NULL) {
705		NG_BTSOCKET_SCO_ALERT(
706"%s: Invalid source hook for SCO data packet\n", __func__);
707		goto drop;
708	}
709
710	rt = (ng_btsocket_sco_rtentry_t *) NG_HOOK_PRIVATE(hook);
711	if (rt == NULL) {
712		NG_BTSOCKET_SCO_ALERT(
713"%s: Could not find out source bdaddr for SCO data packet\n", __func__);
714		goto drop;
715	}
716
717	/* Make sure we can access header */
718	if (m->m_pkthdr.len < sizeof(*hdr)) {
719		NG_BTSOCKET_SCO_ERR(
720"%s: SCO data packet too small, len=%d\n", __func__, m->m_pkthdr.len);
721		goto drop;
722	}
723
724	if (m->m_len < sizeof(*hdr)) {
725		m = m_pullup(m, sizeof(*hdr));
726		if (m == NULL)
727			goto drop;
728	}
729
730	/* Strip SCO packet header and verify packet length */
731	hdr = mtod(m, ng_hci_scodata_pkt_t *);
732	m_adj(m, sizeof(*hdr));
733
734	if (hdr->length != m->m_pkthdr.len) {
735		NG_BTSOCKET_SCO_ERR(
736"%s: Bad SCO data packet length, len=%d, length=%d\n",
737			__func__, m->m_pkthdr.len, hdr->length);
738		goto drop;
739	}
740
741	/*
742	 * Now process packet
743	 */
744
745	con_handle = NG_HCI_CON_HANDLE(le16toh(hdr->con_handle));
746
747	NG_BTSOCKET_SCO_INFO(
748"%s: Received SCO data packet: src bdaddr=%x:%x:%x:%x:%x:%x, handle=%d, " \
749"length=%d\n",	__func__,
750		rt->src.b[5], rt->src.b[4], rt->src.b[3],
751		rt->src.b[2], rt->src.b[1], rt->src.b[0],
752		con_handle, hdr->length);
753
754	mtx_lock(&ng_btsocket_sco_sockets_mtx);
755
756	/* Find socket */
757	pcb = ng_btsocket_sco_pcb_by_handle(&rt->src, con_handle);
758	if (pcb == NULL) {
759		mtx_unlock(&ng_btsocket_sco_sockets_mtx);
760		goto drop;
761	}
762
763	/* pcb is locked */
764
765	if (pcb->state != NG_BTSOCKET_SCO_OPEN) {
766		NG_BTSOCKET_SCO_ERR(
767"%s: No connected socket found, src bdaddr=%x:%x:%x:%x:%x:%x, state=%d\n",
768			__func__,
769			rt->src.b[5], rt->src.b[4], rt->src.b[3],
770			rt->src.b[2], rt->src.b[1], rt->src.b[0],
771			pcb->state);
772
773		mtx_unlock(&pcb->pcb_mtx);
774		mtx_unlock(&ng_btsocket_sco_sockets_mtx);
775		goto drop;
776	}
777
778	/* Check if we have enough space in socket receive queue */
779	if (m->m_pkthdr.len > sbspace(&pcb->so->so_rcv)) {
780		NG_BTSOCKET_SCO_ERR(
781"%s: Not enough space in socket receive queue. Dropping SCO data packet, " \
782"src bdaddr=%x:%x:%x:%x:%x:%x, len=%d, space=%ld\n",
783			__func__,
784			rt->src.b[5], rt->src.b[4], rt->src.b[3],
785			rt->src.b[2], rt->src.b[1], rt->src.b[0],
786			m->m_pkthdr.len,
787			sbspace(&pcb->so->so_rcv));
788
789		mtx_unlock(&pcb->pcb_mtx);
790		mtx_unlock(&ng_btsocket_sco_sockets_mtx);
791		goto drop;
792	}
793
794	/* Append packet to the socket receive queue and wakeup */
795	sbappendrecord(&pcb->so->so_rcv, m);
796	m = NULL;
797
798	sorwakeup(pcb->so);
799
800	mtx_unlock(&pcb->pcb_mtx);
801	mtx_unlock(&ng_btsocket_sco_sockets_mtx);
802drop:
803	NG_FREE_M(m); /* checks for m != NULL */
804} /* ng_btsocket_sco_data_input */
805
806/*
807 * SCO sockets default message input routine
808 */
809
810static void
811ng_btsocket_sco_default_msg_input(struct ng_mesg *msg, hook_p hook)
812{
813	ng_btsocket_sco_rtentry_t	*rt = NULL;
814
815	if (hook == NULL || NG_HOOK_NOT_VALID(hook))
816		return;
817
818	rt = (ng_btsocket_sco_rtentry_t *) NG_HOOK_PRIVATE(hook);
819
820	switch (msg->header.cmd) {
821	case NGM_HCI_NODE_UP: {
822		ng_hci_node_up_ep	*ep = NULL;
823
824		if (msg->header.arglen != sizeof(*ep))
825			break;
826
827		ep = (ng_hci_node_up_ep *)(msg->data);
828		if (bcmp(&ep->bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
829			break;
830
831		if (rt == NULL) {
832			rt = malloc(sizeof(*rt),
833				M_NETGRAPH_BTSOCKET_SCO, M_NOWAIT|M_ZERO);
834			if (rt == NULL)
835				break;
836
837			NG_HOOK_SET_PRIVATE(hook, rt);
838
839			mtx_lock(&ng_btsocket_sco_rt_mtx);
840
841			LIST_INSERT_HEAD(&ng_btsocket_sco_rt, rt, next);
842		} else
843			mtx_lock(&ng_btsocket_sco_rt_mtx);
844
845		bcopy(&ep->bdaddr, &rt->src, sizeof(rt->src));
846		rt->pkt_size = (ep->pkt_size == 0)? 60 : ep->pkt_size;
847		rt->num_pkts = ep->num_pkts;
848		rt->hook = hook;
849
850		mtx_unlock(&ng_btsocket_sco_rt_mtx);
851
852		NG_BTSOCKET_SCO_INFO(
853"%s: Updating hook \"%s\", src bdaddr=%x:%x:%x:%x:%x:%x, pkt_size=%d, " \
854"num_pkts=%d\n",	__func__, NG_HOOK_NAME(hook),
855			rt->src.b[5], rt->src.b[4], rt->src.b[3],
856			rt->src.b[2], rt->src.b[1], rt->src.b[0],
857			rt->pkt_size, rt->num_pkts);
858		} break;
859
860	case NGM_HCI_SYNC_CON_QUEUE: {
861		ng_hci_sync_con_queue_ep	*ep = NULL;
862		ng_btsocket_sco_pcb_t		*pcb = NULL;
863
864		if (rt == NULL || msg->header.arglen != sizeof(*ep))
865			break;
866
867		ep = (ng_hci_sync_con_queue_ep *)(msg->data);
868
869		rt->pending -= ep->completed;
870		if (rt->pending < 0) {
871			NG_BTSOCKET_SCO_WARN(
872"%s: Pending packet counter is out of sync! bdaddr=%x:%x:%x:%x:%x:%x, " \
873"handle=%d, pending=%d, completed=%d\n",
874				__func__,
875				rt->src.b[5], rt->src.b[4], rt->src.b[3],
876				rt->src.b[2], rt->src.b[1], rt->src.b[0],
877				ep->con_handle, rt->pending,
878				ep->completed);
879
880			rt->pending = 0;
881		}
882
883		mtx_lock(&ng_btsocket_sco_sockets_mtx);
884
885		/* Find socket */
886		pcb = ng_btsocket_sco_pcb_by_handle(&rt->src, ep->con_handle);
887		if (pcb == NULL) {
888			mtx_unlock(&ng_btsocket_sco_sockets_mtx);
889			break;
890		}
891
892		/* pcb is locked */
893
894		/* Check state */
895		if (pcb->state == NG_BTSOCKET_SCO_OPEN) {
896			/* Remove timeout */
897			ng_btsocket_sco_untimeout(pcb);
898
899			/* Drop completed packets from the send queue */
900			for (; ep->completed > 0; ep->completed --)
901				sbdroprecord(&pcb->so->so_snd);
902
903			/* Send more if we have any */
904			if (sbavail(&pcb->so->so_snd) > 0)
905				if (ng_btsocket_sco_send2(pcb) == 0)
906					ng_btsocket_sco_timeout(pcb);
907
908			/* Wake up writers */
909			sowwakeup(pcb->so);
910		}
911
912		mtx_unlock(&pcb->pcb_mtx);
913		mtx_unlock(&ng_btsocket_sco_sockets_mtx);
914	} break;
915
916	default:
917		NG_BTSOCKET_SCO_WARN(
918"%s: Unknown message, cmd=%d\n", __func__, msg->header.cmd);
919		break;
920	}
921
922	NG_FREE_MSG(msg); /* Checks for msg != NULL */
923} /* ng_btsocket_sco_default_msg_input */
924
925/*
926 * SCO sockets LP message input routine
927 */
928
929static void
930ng_btsocket_sco_lp_msg_input(struct ng_mesg *msg, hook_p hook)
931{
932	ng_btsocket_sco_rtentry_p	 rt = NULL;
933
934	if (hook == NULL) {
935		NG_BTSOCKET_SCO_ALERT(
936"%s: Invalid source hook for LP message\n", __func__);
937		goto drop;
938	}
939
940	rt = (ng_btsocket_sco_rtentry_p) NG_HOOK_PRIVATE(hook);
941	if (rt == NULL) {
942		NG_BTSOCKET_SCO_ALERT(
943"%s: Could not find out source bdaddr for LP message\n", __func__);
944		goto drop;
945	}
946
947	switch (msg->header.cmd) {
948	case NGM_HCI_LP_CON_CFM: /* Connection Confirmation Event */
949		ng_btsocket_sco_process_lp_con_cfm(msg, rt);
950		break;
951
952	case NGM_HCI_LP_CON_IND: /* Connection Indication Event */
953		ng_btsocket_sco_process_lp_con_ind(msg, rt);
954		break;
955
956	case NGM_HCI_LP_DISCON_IND: /* Disconnection Indication Event */
957		ng_btsocket_sco_process_lp_discon_ind(msg, rt);
958		break;
959
960	/* XXX FIXME add other LP messages */
961
962	default:
963		NG_BTSOCKET_SCO_WARN(
964"%s: Unknown LP message, cmd=%d\n", __func__, msg->header.cmd);
965		break;
966	}
967drop:
968	NG_FREE_MSG(msg);
969} /* ng_btsocket_sco_lp_msg_input */
970
971/*
972 * SCO sockets input routine
973 */
974
975static void
976ng_btsocket_sco_input(void *context, int pending)
977{
978	item_p	item = NULL;
979	hook_p	hook = NULL;
980
981	for (;;) {
982		mtx_lock(&ng_btsocket_sco_queue_mtx);
983		NG_BT_ITEMQ_DEQUEUE(&ng_btsocket_sco_queue, item);
984		mtx_unlock(&ng_btsocket_sco_queue_mtx);
985
986		if (item == NULL)
987			break;
988
989		NGI_GET_HOOK(item, hook);
990		if (hook != NULL && NG_HOOK_NOT_VALID(hook))
991			goto drop;
992
993		switch(item->el_flags & NGQF_TYPE) {
994		case NGQF_DATA: {
995			struct mbuf     *m = NULL;
996
997			NGI_GET_M(item, m);
998			ng_btsocket_sco_data_input(m, hook);
999			} break;
1000
1001		case NGQF_MESG: {
1002			struct ng_mesg  *msg = NULL;
1003
1004			NGI_GET_MSG(item, msg);
1005
1006			switch (msg->header.cmd) {
1007			case NGM_HCI_LP_CON_CFM:
1008			case NGM_HCI_LP_CON_IND:
1009			case NGM_HCI_LP_DISCON_IND:
1010			/* XXX FIXME add other LP messages */
1011				ng_btsocket_sco_lp_msg_input(msg, hook);
1012				break;
1013
1014			default:
1015				ng_btsocket_sco_default_msg_input(msg, hook);
1016				break;
1017			}
1018			} break;
1019
1020		default:
1021			KASSERT(0,
1022("%s: invalid item type=%ld\n", __func__, (item->el_flags & NGQF_TYPE)));
1023			break;
1024		}
1025drop:
1026		if (hook != NULL)
1027			NG_HOOK_UNREF(hook);
1028
1029		NG_FREE_ITEM(item);
1030	}
1031} /* ng_btsocket_sco_input */
1032
1033/*
1034 * Route cleanup task. Gets scheduled when hook is disconnected. Here we
1035 * will find all sockets that use "invalid" hook and disconnect them.
1036 */
1037
1038static void
1039ng_btsocket_sco_rtclean(void *context, int pending)
1040{
1041	ng_btsocket_sco_pcb_p		pcb = NULL, pcb_next = NULL;
1042	ng_btsocket_sco_rtentry_p	rt = NULL;
1043
1044	/*
1045	 * First disconnect all sockets that use "invalid" hook
1046	 */
1047
1048	mtx_lock(&ng_btsocket_sco_sockets_mtx);
1049
1050	for(pcb = LIST_FIRST(&ng_btsocket_sco_sockets); pcb != NULL; ) {
1051		mtx_lock(&pcb->pcb_mtx);
1052		pcb_next = LIST_NEXT(pcb, next);
1053
1054		if (pcb->rt != NULL &&
1055		    pcb->rt->hook != NULL && NG_HOOK_NOT_VALID(pcb->rt->hook)) {
1056			if (pcb->flags & NG_BTSOCKET_SCO_TIMO)
1057				ng_btsocket_sco_untimeout(pcb);
1058
1059			pcb->rt = NULL;
1060			pcb->so->so_error = ENETDOWN;
1061			pcb->state = NG_BTSOCKET_SCO_CLOSED;
1062			soisdisconnected(pcb->so);
1063		}
1064
1065		mtx_unlock(&pcb->pcb_mtx);
1066		pcb = pcb_next;
1067	}
1068
1069	mtx_unlock(&ng_btsocket_sco_sockets_mtx);
1070
1071	/*
1072	 * Now cleanup routing table
1073	 */
1074
1075	mtx_lock(&ng_btsocket_sco_rt_mtx);
1076
1077	for (rt = LIST_FIRST(&ng_btsocket_sco_rt); rt != NULL; ) {
1078		ng_btsocket_sco_rtentry_p	rt_next = LIST_NEXT(rt, next);
1079
1080		if (rt->hook != NULL && NG_HOOK_NOT_VALID(rt->hook)) {
1081			LIST_REMOVE(rt, next);
1082
1083			NG_HOOK_SET_PRIVATE(rt->hook, NULL);
1084			NG_HOOK_UNREF(rt->hook); /* Remove extra reference */
1085
1086			bzero(rt, sizeof(*rt));
1087			free(rt, M_NETGRAPH_BTSOCKET_SCO);
1088		}
1089
1090		rt = rt_next;
1091	}
1092
1093	mtx_unlock(&ng_btsocket_sco_rt_mtx);
1094} /* ng_btsocket_sco_rtclean */
1095
1096/*
1097 * Initialize everything
1098 */
1099
1100static void
1101ng_btsocket_sco_init(void *arg __unused)
1102{
1103	int	error = 0;
1104
1105	ng_btsocket_sco_node = NULL;
1106	ng_btsocket_sco_debug_level = NG_BTSOCKET_WARN_LEVEL;
1107
1108	/* Register Netgraph node type */
1109	error = ng_newtype(&typestruct);
1110	if (error != 0) {
1111		NG_BTSOCKET_SCO_ALERT(
1112"%s: Could not register Netgraph node type, error=%d\n", __func__, error);
1113
1114                return;
1115	}
1116
1117	/* Create Netgrapg node */
1118	error = ng_make_node_common(&typestruct, &ng_btsocket_sco_node);
1119	if (error != 0) {
1120		NG_BTSOCKET_SCO_ALERT(
1121"%s: Could not create Netgraph node, error=%d\n", __func__, error);
1122
1123		ng_btsocket_sco_node = NULL;
1124
1125		return;
1126	}
1127
1128	error = ng_name_node(ng_btsocket_sco_node, NG_BTSOCKET_SCO_NODE_TYPE);
1129	if (error != 0) {
1130		NG_BTSOCKET_SCO_ALERT(
1131"%s: Could not name Netgraph node, error=%d\n", __func__, error);
1132
1133		NG_NODE_UNREF(ng_btsocket_sco_node);
1134		ng_btsocket_sco_node = NULL;
1135
1136		return;
1137	}
1138
1139	/* Create input queue */
1140	NG_BT_ITEMQ_INIT(&ng_btsocket_sco_queue, 300);
1141	mtx_init(&ng_btsocket_sco_queue_mtx,
1142		"btsocks_sco_queue_mtx", NULL, MTX_DEF);
1143	TASK_INIT(&ng_btsocket_sco_queue_task, 0,
1144		ng_btsocket_sco_input, NULL);
1145
1146	/* Create list of sockets */
1147	LIST_INIT(&ng_btsocket_sco_sockets);
1148	mtx_init(&ng_btsocket_sco_sockets_mtx,
1149		"btsocks_sco_sockets_mtx", NULL, MTX_DEF);
1150
1151	/* Routing table */
1152	LIST_INIT(&ng_btsocket_sco_rt);
1153	mtx_init(&ng_btsocket_sco_rt_mtx,
1154		"btsocks_sco_rt_mtx", NULL, MTX_DEF);
1155	TASK_INIT(&ng_btsocket_sco_rt_task, 0,
1156		ng_btsocket_sco_rtclean, NULL);
1157} /* ng_btsocket_sco_init */
1158SYSINIT(ng_btsocket_sco_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD,
1159    ng_btsocket_sco_init, NULL);
1160
1161/*
1162 * Abort connection on socket
1163 */
1164
1165void
1166ng_btsocket_sco_abort(struct socket *so)
1167{
1168	so->so_error = ECONNABORTED;
1169
1170	(void) ng_btsocket_sco_disconnect(so);
1171} /* ng_btsocket_sco_abort */
1172
1173void
1174ng_btsocket_sco_close(struct socket *so)
1175{
1176	(void) ng_btsocket_sco_disconnect(so);
1177} /* ng_btsocket_sco_close */
1178
1179/*
1180 * Create and attach new socket
1181 */
1182
1183int
1184ng_btsocket_sco_attach(struct socket *so, int proto, struct thread *td)
1185{
1186	ng_btsocket_sco_pcb_p	pcb = so2sco_pcb(so);
1187	int			error;
1188
1189	/* Check socket and protocol */
1190	if (ng_btsocket_sco_node == NULL)
1191		return (EPROTONOSUPPORT);
1192	if (so->so_type != SOCK_SEQPACKET)
1193		return (ESOCKTNOSUPPORT);
1194
1195#if 0 /* XXX sonewconn() calls "pru_attach" with proto == 0 */
1196	if (proto != 0)
1197		if (proto != BLUETOOTH_PROTO_SCO)
1198			return (EPROTONOSUPPORT);
1199#endif /* XXX */
1200
1201	if (pcb != NULL)
1202		return (EISCONN);
1203
1204	/* Reserve send and receive space if it is not reserved yet */
1205	if ((so->so_snd.sb_hiwat == 0) || (so->so_rcv.sb_hiwat == 0)) {
1206		error = soreserve(so, NG_BTSOCKET_SCO_SENDSPACE,
1207					NG_BTSOCKET_SCO_RECVSPACE);
1208		if (error != 0)
1209			return (error);
1210	}
1211
1212	/* Allocate the PCB */
1213        pcb = malloc(sizeof(*pcb),
1214		M_NETGRAPH_BTSOCKET_SCO, M_NOWAIT | M_ZERO);
1215        if (pcb == NULL)
1216                return (ENOMEM);
1217
1218	/* Link the PCB and the socket */
1219	so->so_pcb = (caddr_t) pcb;
1220	pcb->so = so;
1221	pcb->state = NG_BTSOCKET_SCO_CLOSED;
1222
1223	callout_init(&pcb->timo, 1);
1224
1225	/*
1226	 * Mark PCB mutex as DUPOK to prevent "duplicated lock of
1227	 * the same type" message. When accepting new SCO connection
1228	 * ng_btsocket_sco_process_lp_con_ind() holds both PCB mutexes
1229	 * for "old" (accepting) PCB and "new" (created) PCB.
1230	 */
1231
1232	mtx_init(&pcb->pcb_mtx, "btsocks_sco_pcb_mtx", NULL,
1233		MTX_DEF|MTX_DUPOK);
1234
1235	/*
1236	 * Add the PCB to the list
1237	 *
1238	 * XXX FIXME VERY IMPORTANT!
1239	 *
1240	 * This is totally FUBAR. We could get here in two cases:
1241	 *
1242	 * 1) When user calls socket()
1243	 * 2) When we need to accept new incoming connection and call
1244	 *    sonewconn()
1245	 *
1246	 * In the first case we must acquire ng_btsocket_sco_sockets_mtx.
1247	 * In the second case we hold ng_btsocket_sco_sockets_mtx already.
1248	 * So we now need to distinguish between these cases. From reading
1249	 * /sys/kern/uipc_socket2.c we can find out that sonewconn() calls
1250	 * pru_attach with proto == 0 and td == NULL. For now use this fact
1251	 * to figure out if we were called from socket() or from sonewconn().
1252	 */
1253
1254	if (td != NULL)
1255		mtx_lock(&ng_btsocket_sco_sockets_mtx);
1256	else
1257		mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED);
1258
1259	LIST_INSERT_HEAD(&ng_btsocket_sco_sockets, pcb, next);
1260
1261	if (td != NULL)
1262		mtx_unlock(&ng_btsocket_sco_sockets_mtx);
1263
1264        return (0);
1265} /* ng_btsocket_sco_attach */
1266
1267/*
1268 * Bind socket
1269 */
1270
1271int
1272ng_btsocket_sco_bind(struct socket *so, struct sockaddr *nam,
1273		struct thread *td)
1274{
1275	ng_btsocket_sco_pcb_t	*pcb = NULL;
1276	struct sockaddr_sco	*sa = (struct sockaddr_sco *) nam;
1277
1278	if (ng_btsocket_sco_node == NULL)
1279		return (EINVAL);
1280
1281	/* Verify address */
1282	if (sa == NULL)
1283		return (EINVAL);
1284	if (sa->sco_family != AF_BLUETOOTH)
1285		return (EAFNOSUPPORT);
1286	if (sa->sco_len != sizeof(*sa))
1287		return (EINVAL);
1288
1289	mtx_lock(&ng_btsocket_sco_sockets_mtx);
1290
1291	/*
1292	 * Check if other socket has this address already (look for exact
1293	 * match in bdaddr) and assign socket address if it's available.
1294	 */
1295
1296	if (bcmp(&sa->sco_bdaddr, NG_HCI_BDADDR_ANY, sizeof(sa->sco_bdaddr)) != 0) {
1297 		LIST_FOREACH(pcb, &ng_btsocket_sco_sockets, next) {
1298			mtx_lock(&pcb->pcb_mtx);
1299
1300			if (bcmp(&pcb->src, &sa->sco_bdaddr, sizeof(bdaddr_t)) == 0) {
1301				mtx_unlock(&pcb->pcb_mtx);
1302				mtx_unlock(&ng_btsocket_sco_sockets_mtx);
1303
1304				return (EADDRINUSE);
1305			}
1306
1307			mtx_unlock(&pcb->pcb_mtx);
1308		}
1309	}
1310
1311	pcb = so2sco_pcb(so);
1312	if (pcb == NULL) {
1313		mtx_unlock(&ng_btsocket_sco_sockets_mtx);
1314		return (EINVAL);
1315	}
1316
1317	mtx_lock(&pcb->pcb_mtx);
1318	bcopy(&sa->sco_bdaddr, &pcb->src, sizeof(pcb->src));
1319	mtx_unlock(&pcb->pcb_mtx);
1320
1321	mtx_unlock(&ng_btsocket_sco_sockets_mtx);
1322
1323	return (0);
1324} /* ng_btsocket_sco_bind */
1325
1326/*
1327 * Connect socket
1328 */
1329
1330int
1331ng_btsocket_sco_connect(struct socket *so, struct sockaddr *nam,
1332		struct thread *td)
1333{
1334	ng_btsocket_sco_pcb_t		*pcb = so2sco_pcb(so);
1335	struct sockaddr_sco		*sa = (struct sockaddr_sco *) nam;
1336	ng_btsocket_sco_rtentry_t	*rt = NULL;
1337	int				 have_src, error = 0;
1338
1339	/* Check socket */
1340	if (pcb == NULL)
1341		return (EINVAL);
1342	if (ng_btsocket_sco_node == NULL)
1343		return (EINVAL);
1344
1345	/* Verify address */
1346	if (sa == NULL)
1347		return (EINVAL);
1348	if (sa->sco_family != AF_BLUETOOTH)
1349		return (EAFNOSUPPORT);
1350	if (sa->sco_len != sizeof(*sa))
1351		return (EINVAL);
1352	if (bcmp(&sa->sco_bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
1353		return (EDESTADDRREQ);
1354
1355	/*
1356	 * Routing. Socket should be bound to some source address. The source
1357	 * address can be ANY. Destination address must be set and it must not
1358	 * be ANY. If source address is ANY then find first rtentry that has
1359	 * src != dst.
1360	 */
1361
1362	mtx_lock(&ng_btsocket_sco_rt_mtx);
1363	mtx_lock(&pcb->pcb_mtx);
1364
1365	if (pcb->state == NG_BTSOCKET_SCO_CONNECTING) {
1366		mtx_unlock(&pcb->pcb_mtx);
1367		mtx_unlock(&ng_btsocket_sco_rt_mtx);
1368
1369		return (EINPROGRESS);
1370	}
1371
1372	if (bcmp(&sa->sco_bdaddr, &pcb->src, sizeof(pcb->src)) == 0) {
1373		mtx_unlock(&pcb->pcb_mtx);
1374		mtx_unlock(&ng_btsocket_sco_rt_mtx);
1375
1376		return (EINVAL);
1377	}
1378
1379	/* Send destination address and PSM */
1380	bcopy(&sa->sco_bdaddr, &pcb->dst, sizeof(pcb->dst));
1381
1382	pcb->rt = NULL;
1383	have_src = bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(pcb->src));
1384
1385	LIST_FOREACH(rt, &ng_btsocket_sco_rt, next) {
1386		if (rt->hook == NULL || NG_HOOK_NOT_VALID(rt->hook))
1387			continue;
1388
1389		/* Match src and dst */
1390		if (have_src) {
1391			if (bcmp(&pcb->src, &rt->src, sizeof(rt->src)) == 0)
1392				break;
1393		} else {
1394			if (bcmp(&pcb->dst, &rt->src, sizeof(rt->src)) != 0)
1395				break;
1396		}
1397	}
1398
1399	if (rt != NULL) {
1400		pcb->rt = rt;
1401
1402		if (!have_src)
1403			bcopy(&rt->src, &pcb->src, sizeof(pcb->src));
1404	} else
1405		error = EHOSTUNREACH;
1406
1407	/*
1408	 * Send LP_Connect request
1409	 */
1410
1411	if (error == 0) {
1412		error = ng_btsocket_sco_send_lp_con_req(pcb);
1413		if (error == 0) {
1414			pcb->flags |= NG_BTSOCKET_SCO_CLIENT;
1415			pcb->state = NG_BTSOCKET_SCO_CONNECTING;
1416			soisconnecting(pcb->so);
1417
1418			ng_btsocket_sco_timeout(pcb);
1419		}
1420	}
1421
1422	mtx_unlock(&pcb->pcb_mtx);
1423	mtx_unlock(&ng_btsocket_sco_rt_mtx);
1424
1425	return (error);
1426} /* ng_btsocket_sco_connect */
1427
1428/*
1429 * Process ioctl's calls on socket
1430 */
1431
1432int
1433ng_btsocket_sco_control(struct socket *so, u_long cmd, void *data,
1434		struct ifnet *ifp, struct thread *td)
1435{
1436	return (EINVAL);
1437} /* ng_btsocket_sco_control */
1438
1439/*
1440 * Process getsockopt/setsockopt system calls
1441 */
1442
1443int
1444ng_btsocket_sco_ctloutput(struct socket *so, struct sockopt *sopt)
1445{
1446	ng_btsocket_sco_pcb_p	pcb = so2sco_pcb(so);
1447        int			error, tmp;
1448
1449	if (ng_btsocket_sco_node == NULL)
1450		return (EINVAL);
1451	if (pcb == NULL)
1452		return (EINVAL);
1453
1454	if (sopt->sopt_level != SOL_SCO)
1455		return (0);
1456
1457	mtx_lock(&pcb->pcb_mtx);
1458
1459	switch (sopt->sopt_dir) {
1460	case SOPT_GET:
1461		if (pcb->state != NG_BTSOCKET_SCO_OPEN) {
1462			error = ENOTCONN;
1463			break;
1464		}
1465
1466		switch (sopt->sopt_name) {
1467		case SO_SCO_MTU:
1468			tmp = pcb->rt->pkt_size;
1469			error = sooptcopyout(sopt, &tmp, sizeof(tmp));
1470			break;
1471
1472		case SO_SCO_CONNINFO:
1473			tmp = pcb->con_handle;
1474			error = sooptcopyout(sopt, &tmp, sizeof(tmp));
1475			break;
1476
1477		default:
1478			error = EINVAL;
1479			break;
1480		}
1481		break;
1482
1483	case SOPT_SET:
1484		error = ENOPROTOOPT;
1485		break;
1486
1487	default:
1488		error = EINVAL;
1489		break;
1490	}
1491
1492	mtx_unlock(&pcb->pcb_mtx);
1493
1494	return (error);
1495} /* ng_btsocket_sco_ctloutput */
1496
1497/*
1498 * Detach and destroy socket
1499 */
1500
1501void
1502ng_btsocket_sco_detach(struct socket *so)
1503{
1504	ng_btsocket_sco_pcb_p	pcb = so2sco_pcb(so);
1505
1506	KASSERT(pcb != NULL, ("ng_btsocket_sco_detach: pcb == NULL"));
1507
1508	if (ng_btsocket_sco_node == NULL)
1509		return;
1510
1511	mtx_lock(&ng_btsocket_sco_sockets_mtx);
1512	mtx_lock(&pcb->pcb_mtx);
1513
1514	if (pcb->flags & NG_BTSOCKET_SCO_TIMO)
1515		ng_btsocket_sco_untimeout(pcb);
1516
1517	if (pcb->state == NG_BTSOCKET_SCO_OPEN)
1518		ng_btsocket_sco_send_lp_discon_req(pcb);
1519
1520	pcb->state = NG_BTSOCKET_SCO_CLOSED;
1521
1522	LIST_REMOVE(pcb, next);
1523
1524	mtx_unlock(&pcb->pcb_mtx);
1525	mtx_unlock(&ng_btsocket_sco_sockets_mtx);
1526
1527	mtx_destroy(&pcb->pcb_mtx);
1528	bzero(pcb, sizeof(*pcb));
1529	free(pcb, M_NETGRAPH_BTSOCKET_SCO);
1530
1531	soisdisconnected(so);
1532	so->so_pcb = NULL;
1533} /* ng_btsocket_sco_detach */
1534
1535/*
1536 * Disconnect socket
1537 */
1538
1539int
1540ng_btsocket_sco_disconnect(struct socket *so)
1541{
1542	ng_btsocket_sco_pcb_p	pcb = so2sco_pcb(so);
1543
1544	if (pcb == NULL)
1545		return (EINVAL);
1546	if (ng_btsocket_sco_node == NULL)
1547		return (EINVAL);
1548
1549	mtx_lock(&pcb->pcb_mtx);
1550
1551	if (pcb->state == NG_BTSOCKET_SCO_DISCONNECTING) {
1552		mtx_unlock(&pcb->pcb_mtx);
1553
1554		return (EINPROGRESS);
1555	}
1556
1557	if (pcb->flags & NG_BTSOCKET_SCO_TIMO)
1558		ng_btsocket_sco_untimeout(pcb);
1559
1560	if (pcb->state == NG_BTSOCKET_SCO_OPEN) {
1561		ng_btsocket_sco_send_lp_discon_req(pcb);
1562
1563		pcb->state = NG_BTSOCKET_SCO_DISCONNECTING;
1564		soisdisconnecting(so);
1565
1566		ng_btsocket_sco_timeout(pcb);
1567	} else {
1568		pcb->state = NG_BTSOCKET_SCO_CLOSED;
1569		soisdisconnected(so);
1570	}
1571
1572	mtx_unlock(&pcb->pcb_mtx);
1573
1574	return (0);
1575} /* ng_btsocket_sco_disconnect */
1576
1577/*
1578 * Listen on socket
1579 */
1580
1581int
1582ng_btsocket_sco_listen(struct socket *so, int backlog, struct thread *td)
1583{
1584	ng_btsocket_sco_pcb_p	pcb = so2sco_pcb(so);
1585	int			error;
1586
1587	if (pcb == NULL)
1588		return (EINVAL);
1589	if (ng_btsocket_sco_node == NULL)
1590		return (EINVAL);
1591
1592	SOCK_LOCK(so);
1593	mtx_lock(&pcb->pcb_mtx);
1594
1595	error = solisten_proto_check(so);
1596	if (error != 0)
1597		goto out;
1598#if 0
1599	if (bcmp(&pcb->src, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0) {
1600		error = EDESTADDRREQ;
1601		goto out;
1602	}
1603#endif
1604	solisten_proto(so, backlog);
1605out:
1606	mtx_unlock(&pcb->pcb_mtx);
1607	SOCK_UNLOCK(so);
1608
1609	return (error);
1610} /* ng_btsocket_listen */
1611
1612/*
1613 * Return peer address for getpeername(2) or for accept(2).  For the latter
1614 * case no extra work to do here, socket must be connected and ready.
1615 */
1616int
1617ng_btsocket_sco_peeraddr(struct socket *so, struct sockaddr *sa)
1618{
1619	ng_btsocket_sco_pcb_p	pcb = so2sco_pcb(so);
1620	struct sockaddr_sco *sco = (struct sockaddr_sco *)sa;
1621
1622	if (pcb == NULL)
1623		return (EINVAL);
1624	if (ng_btsocket_sco_node == NULL)
1625		return (EINVAL);
1626
1627	*sco = (struct sockaddr_sco ){
1628		.sco_len = sizeof(struct sockaddr_sco),
1629		.sco_family = AF_BLUETOOTH,
1630	};
1631	mtx_lock(&pcb->pcb_mtx);
1632	bcopy(&pcb->dst, &sco->sco_bdaddr, sizeof(sco->sco_bdaddr));
1633	mtx_unlock(&pcb->pcb_mtx);
1634
1635	return (0);
1636}
1637
1638/*
1639 * Send data to socket
1640 */
1641
1642int
1643ng_btsocket_sco_send(struct socket *so, int flags, struct mbuf *m,
1644		struct sockaddr *nam, struct mbuf *control, struct thread *td)
1645{
1646	ng_btsocket_sco_pcb_t	*pcb = so2sco_pcb(so);
1647	int			 error = 0;
1648
1649	if (ng_btsocket_sco_node == NULL) {
1650		error = ENETDOWN;
1651		goto drop;
1652	}
1653
1654	/* Check socket and input */
1655	if (pcb == NULL || m == NULL || control != NULL) {
1656		error = EINVAL;
1657		goto drop;
1658	}
1659
1660	mtx_lock(&pcb->pcb_mtx);
1661
1662	/* Make sure socket is connected */
1663	if (pcb->state != NG_BTSOCKET_SCO_OPEN) {
1664		mtx_unlock(&pcb->pcb_mtx);
1665		error = ENOTCONN;
1666		goto drop;
1667	}
1668
1669	/* Check route */
1670	if (pcb->rt == NULL ||
1671	    pcb->rt->hook == NULL || NG_HOOK_NOT_VALID(pcb->rt->hook)) {
1672		mtx_unlock(&pcb->pcb_mtx);
1673		error = ENETDOWN;
1674		goto drop;
1675	}
1676
1677	/* Check packet size */
1678	if (m->m_pkthdr.len > pcb->rt->pkt_size) {
1679		NG_BTSOCKET_SCO_ERR(
1680"%s: Packet too big, len=%d, pkt_size=%d\n",
1681			__func__, m->m_pkthdr.len, pcb->rt->pkt_size);
1682
1683		mtx_unlock(&pcb->pcb_mtx);
1684		error = EMSGSIZE;
1685		goto drop;
1686	}
1687
1688	/*
1689	 * First put packet on socket send queue. Then check if we have
1690	 * pending timeout. If we do not have timeout then we must send
1691	 * packet and schedule timeout. Otherwise do nothing and wait for
1692	 * NGM_HCI_SYNC_CON_QUEUE message.
1693	 */
1694
1695	sbappendrecord(&pcb->so->so_snd, m);
1696	m = NULL;
1697
1698	if (!(pcb->flags & NG_BTSOCKET_SCO_TIMO)) {
1699		error = ng_btsocket_sco_send2(pcb);
1700		if (error == 0)
1701			ng_btsocket_sco_timeout(pcb);
1702		else
1703			sbdroprecord(&pcb->so->so_snd); /* XXX */
1704	}
1705
1706	mtx_unlock(&pcb->pcb_mtx);
1707drop:
1708	NG_FREE_M(m); /* checks for != NULL */
1709	NG_FREE_M(control);
1710
1711	return (error);
1712} /* ng_btsocket_sco_send */
1713
1714/*
1715 * Send first packet in the socket queue to the SCO layer
1716 */
1717
1718static int
1719ng_btsocket_sco_send2(ng_btsocket_sco_pcb_p pcb)
1720{
1721	struct  mbuf		*m = NULL;
1722	ng_hci_scodata_pkt_t	*hdr = NULL;
1723	int			 error = 0;
1724
1725	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
1726
1727	while (pcb->rt->pending < pcb->rt->num_pkts &&
1728	       sbavail(&pcb->so->so_snd) > 0) {
1729		/* Get a copy of the first packet on send queue */
1730		m = m_dup(pcb->so->so_snd.sb_mb, M_NOWAIT);
1731		if (m == NULL) {
1732			error = ENOBUFS;
1733			break;
1734		}
1735
1736		/* Create SCO packet header */
1737		M_PREPEND(m, sizeof(*hdr), M_NOWAIT);
1738		if (m != NULL)
1739			if (m->m_len < sizeof(*hdr))
1740				m = m_pullup(m, sizeof(*hdr));
1741
1742		if (m == NULL) {
1743			error = ENOBUFS;
1744			break;
1745		}
1746
1747		/* Fill in the header */
1748		hdr = mtod(m, ng_hci_scodata_pkt_t *);
1749		hdr->type = NG_HCI_SCO_DATA_PKT;
1750		hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE(pcb->con_handle, 0, 0));
1751		hdr->length = m->m_pkthdr.len - sizeof(*hdr);
1752
1753		/* Send packet */
1754		NG_SEND_DATA_ONLY(error, pcb->rt->hook, m);
1755		if (error != 0)
1756			break;
1757
1758		pcb->rt->pending ++;
1759	}
1760
1761	return ((pcb->rt->pending > 0)? 0 : error);
1762} /* ng_btsocket_sco_send2 */
1763
1764/*
1765 * Get socket address
1766 */
1767
1768int
1769ng_btsocket_sco_sockaddr(struct socket *so, struct sockaddr *sa)
1770{
1771	ng_btsocket_sco_pcb_p	pcb = so2sco_pcb(so);
1772	struct sockaddr_sco *sco = (struct sockaddr_sco *)sa;
1773
1774	if (pcb == NULL)
1775		return (EINVAL);
1776	if (ng_btsocket_sco_node == NULL)
1777		return (EINVAL);
1778
1779	*sco = (struct sockaddr_sco ){
1780		.sco_len = sizeof(struct sockaddr_sco),
1781		.sco_family = AF_BLUETOOTH,
1782	};
1783	mtx_lock(&pcb->pcb_mtx);
1784	bcopy(&pcb->src, &sco->sco_bdaddr, sizeof(sco->sco_bdaddr));
1785	mtx_unlock(&pcb->pcb_mtx);
1786
1787	return (0);
1788}
1789
1790/*****************************************************************************
1791 *****************************************************************************
1792 **                              Misc. functions
1793 *****************************************************************************
1794 *****************************************************************************/
1795
1796/*
1797 * Look for the socket that listens on given bdaddr.
1798 * Returns exact or close match (if any).
1799 * Caller must hold ng_btsocket_sco_sockets_mtx.
1800 * Returns with locked pcb.
1801 */
1802
1803static ng_btsocket_sco_pcb_p
1804ng_btsocket_sco_pcb_by_addr(bdaddr_p bdaddr)
1805{
1806	ng_btsocket_sco_pcb_p	p = NULL, p1 = NULL;
1807
1808	mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED);
1809
1810	LIST_FOREACH(p, &ng_btsocket_sco_sockets, next) {
1811		mtx_lock(&p->pcb_mtx);
1812
1813		if (p->so == NULL || !SOLISTENING(p->so)) {
1814			mtx_unlock(&p->pcb_mtx);
1815			continue;
1816		}
1817
1818		if (bcmp(&p->src, bdaddr, sizeof(p->src)) == 0)
1819			return (p); /* return with locked pcb */
1820
1821		if (bcmp(&p->src, NG_HCI_BDADDR_ANY, sizeof(p->src)) == 0)
1822			p1 = p;
1823
1824		mtx_unlock(&p->pcb_mtx);
1825	}
1826
1827	if (p1 != NULL)
1828		mtx_lock(&p1->pcb_mtx);
1829
1830	return (p1);
1831} /* ng_btsocket_sco_pcb_by_addr */
1832
1833/*
1834 * Look for the socket that assigned to given source address and handle.
1835 * Caller must hold ng_btsocket_sco_sockets_mtx.
1836 * Returns with locked pcb.
1837 */
1838
1839static ng_btsocket_sco_pcb_p
1840ng_btsocket_sco_pcb_by_handle(bdaddr_p src, int con_handle)
1841{
1842	ng_btsocket_sco_pcb_p	p = NULL;
1843
1844	mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED);
1845
1846	LIST_FOREACH(p, &ng_btsocket_sco_sockets, next) {
1847		mtx_lock(&p->pcb_mtx);
1848
1849		if (p->con_handle == con_handle &&
1850		    bcmp(src, &p->src, sizeof(p->src)) == 0)
1851			return (p); /* return with locked pcb */
1852
1853		mtx_unlock(&p->pcb_mtx);
1854	}
1855
1856	return (NULL);
1857} /* ng_btsocket_sco_pcb_by_handle */
1858
1859/*
1860 * Look for the socket in CONNECTING state with given source and destination
1861 * addresses. Caller must hold ng_btsocket_sco_sockets_mtx.
1862 * Returns with locked pcb.
1863 */
1864
1865static ng_btsocket_sco_pcb_p
1866ng_btsocket_sco_pcb_by_addrs(bdaddr_p src, bdaddr_p dst)
1867{
1868	ng_btsocket_sco_pcb_p	p = NULL;
1869
1870	mtx_assert(&ng_btsocket_sco_sockets_mtx, MA_OWNED);
1871
1872	LIST_FOREACH(p, &ng_btsocket_sco_sockets, next) {
1873		mtx_lock(&p->pcb_mtx);
1874
1875		if (p->state == NG_BTSOCKET_SCO_CONNECTING &&
1876		    bcmp(src, &p->src, sizeof(p->src)) == 0 &&
1877		    bcmp(dst, &p->dst, sizeof(p->dst)) == 0)
1878			return (p); /* return with locked pcb */
1879
1880		mtx_unlock(&p->pcb_mtx);
1881	}
1882
1883	return (NULL);
1884} /* ng_btsocket_sco_pcb_by_addrs */
1885
1886/*
1887 * Set timeout on socket
1888 */
1889
1890static void
1891ng_btsocket_sco_timeout(ng_btsocket_sco_pcb_p pcb)
1892{
1893	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
1894
1895	if (!(pcb->flags & NG_BTSOCKET_SCO_TIMO)) {
1896		pcb->flags |= NG_BTSOCKET_SCO_TIMO;
1897		callout_reset(&pcb->timo, bluetooth_sco_rtx_timeout(),
1898					ng_btsocket_sco_process_timeout, pcb);
1899	} else
1900		KASSERT(0,
1901("%s: Duplicated socket timeout?!\n", __func__));
1902} /* ng_btsocket_sco_timeout */
1903
1904/*
1905 * Unset timeout on socket
1906 */
1907
1908static void
1909ng_btsocket_sco_untimeout(ng_btsocket_sco_pcb_p pcb)
1910{
1911	mtx_assert(&pcb->pcb_mtx, MA_OWNED);
1912
1913	if (pcb->flags & NG_BTSOCKET_SCO_TIMO) {
1914		callout_stop(&pcb->timo);
1915		pcb->flags &= ~NG_BTSOCKET_SCO_TIMO;
1916	} else
1917		KASSERT(0,
1918("%s: No socket timeout?!\n", __func__));
1919} /* ng_btsocket_sco_untimeout */
1920
1921/*
1922 * Process timeout on socket
1923 */
1924
1925static void
1926ng_btsocket_sco_process_timeout(void *xpcb)
1927{
1928	ng_btsocket_sco_pcb_p	 pcb = (ng_btsocket_sco_pcb_p) xpcb;
1929
1930	mtx_lock(&pcb->pcb_mtx);
1931
1932	pcb->flags &= ~NG_BTSOCKET_SCO_TIMO;
1933	pcb->so->so_error = ETIMEDOUT;
1934
1935	switch (pcb->state) {
1936	case NG_BTSOCKET_SCO_CONNECTING:
1937		/* Connect timeout - close the socket */
1938		pcb->state = NG_BTSOCKET_SCO_CLOSED;
1939		soisdisconnected(pcb->so);
1940		break;
1941
1942	case NG_BTSOCKET_SCO_OPEN:
1943		/* Send timeout - did not get NGM_HCI_SYNC_CON_QUEUE */
1944		sbdroprecord(&pcb->so->so_snd);
1945		sowwakeup(pcb->so);
1946		/* XXX FIXME what to do with pcb->rt->pending??? */
1947		break;
1948
1949	case NG_BTSOCKET_SCO_DISCONNECTING:
1950		/* Disconnect timeout - disconnect the socket anyway */
1951		pcb->state = NG_BTSOCKET_SCO_CLOSED;
1952		soisdisconnected(pcb->so);
1953		break;
1954
1955	default:
1956		NG_BTSOCKET_SCO_ERR(
1957"%s: Invalid socket state=%d\n", __func__, pcb->state);
1958		break;
1959	}
1960
1961	mtx_unlock(&pcb->pcb_mtx);
1962} /* ng_btsocket_sco_process_timeout */
1963