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