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