56953923e3f8255a166e1c3acdc36831bafba24f
[dragonfly.git] / sys / netbt / sco_socket.c
1 /* $OpenBSD: sco_socket.c,v 1.1 2007/06/01 02:46:12 uwe Exp $ */
2 /* $NetBSD: sco_socket.c,v 1.9 2007/04/21 06:15:23 plunky Exp $ */
3
4 /*-
5  * Copyright (c) 2006 Itronix Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of Itronix Inc. may not be used to endorse
17  *    or promote products derived from this software without specific
18  *    prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 /* load symbolic names */
34 #ifdef BLUETOOTH_DEBUG
35 #define PRUREQUESTS
36 #define PRCOREQUESTS
37 #endif
38
39 #include <sys/param.h>
40 #include <sys/domain.h>
41 #include <sys/kernel.h>
42 #include <sys/mbuf.h>
43 #include <sys/proc.h>
44 #include <sys/protosw.h>
45 #include <sys/socket.h>
46 #include <sys/socketvar.h>
47 #include <sys/systm.h>
48 #include <sys/bus.h>
49
50 #include <netbt/bluetooth.h>
51 #include <netbt/hci.h>
52 #include <netbt/sco.h>
53
54 /*******************************************************************************
55  *
56  * SCO SOCK_SEQPACKET sockets - low latency audio data
57  */
58
59 static void sco_connecting(void *);
60 static void sco_connected(void *);
61 static void sco_disconnected(void *, int);
62 static void *sco_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *);
63 static void sco_complete(void *, int);
64 static void sco_linkmode(void *, int);
65 static void sco_input(void *, struct mbuf *);
66
67 static const struct btproto sco_proto = {
68         sco_connecting,
69         sco_connected,
70         sco_disconnected,
71         sco_newconn,
72         sco_complete,
73         sco_linkmode,
74         sco_input,
75 };
76
77 int sco_sendspace = 4096;
78 int sco_recvspace = 4096;
79
80 /*
81  * get/set socket options
82  */
83 int
84 sco_ctloutput(struct socket *so, struct sockopt *sopt)
85 {
86         struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
87         struct mbuf *m;
88         int err = 0;
89
90 #ifdef notyet                   /* XXX */
91         DPRINTFN(2, "req %s\n", prcorequests[req]);
92 #endif
93
94         if (pcb == NULL)
95                 return EINVAL;
96
97         if (sopt->sopt_level != BTPROTO_SCO)
98                 return ENOPROTOOPT;
99
100         switch(sopt->sopt_dir) {
101         case PRCO_GETOPT:
102                 m = m_get(MB_WAIT, MT_DATA);
103                 m->m_len = sco_getopt(pcb, sopt->sopt_name, mtod(m, uint8_t *));
104                 if (m->m_len == 0) {
105                         m_freem(m);
106                         m = NULL;
107                         err = ENOPROTOOPT;
108                 }
109                 /* *opt = m; */
110                 /* XXX There are possible memory leaks (Griffin) */
111                 soopt_from_kbuf(sopt, mtod(m, void *), m->m_len);
112                 break;
113
114         case PRCO_SETOPT:
115                 m = m_get(M_WAITOK, MT_DATA);
116                 KKASSERT(m != NULL);
117                 err = soopt_to_kbuf(sopt, mtod(m,void*), m->m_len, m->m_len); 
118
119                 if (m->m_len == 0) {
120                         m_freem(m);
121                         m = NULL;
122                         err = EIO;
123                 }
124
125                 err = sco_setopt(pcb, sopt->sopt_name, mtod(m, uint8_t *));
126                 m_freem(m);
127                 break;
128
129         default:
130                 err = ENOPROTOOPT;
131                 break;
132         }
133
134         return err;
135 }
136
137 /*****************************************************************************
138  *
139  *      SCO Protocol socket callbacks
140  *
141  */
142 static void
143 sco_connecting(void *arg)
144 {
145         struct socket *so = arg;
146
147         DPRINTF("Connecting\n");
148         soisconnecting(so);
149 }
150
151 static void
152 sco_connected(void *arg)
153 {
154         struct socket *so = arg;
155
156         DPRINTF("Connected\n");
157         soisconnected(so);
158 }
159
160 static void
161 sco_disconnected(void *arg, int err)
162 {
163         struct socket *so = arg;
164
165         DPRINTF("Disconnected (%d)\n", err);
166
167         so->so_error = err;
168         soisdisconnected(so);
169 }
170
171 static void *
172 sco_newconn(void *arg, struct sockaddr_bt *laddr,
173     struct sockaddr_bt *raddr)
174 {
175         struct socket *so = arg;
176
177         DPRINTF("New Connection\n");
178         so = sonewconn(so, 0);
179         if (so == NULL)
180                 return NULL;
181
182         soisconnecting(so);
183         return so->so_pcb;
184 }
185
186 static void
187 sco_complete(void *arg, int num)
188 {
189         struct socket *so = arg;
190
191         while (num-- > 0)
192                 sbdroprecord(&so->so_snd.sb);
193
194         sowwakeup(so);
195 }
196
197 static void
198 sco_linkmode(void *arg, int mode)
199 {
200 }
201
202 static void
203 sco_input(void *arg, struct mbuf *m)
204 {
205         struct socket *so = arg;
206
207         /*
208          * since this data is time sensitive, if the buffer
209          * is full we just dump data until the latest one
210          * will fit.
211          */
212
213         while (m->m_pkthdr.len > sbspace(&so->so_rcv))
214                 sbdroprecord(&so->so_rcv.sb);
215
216         DPRINTFN(10, "received %d bytes\n", m->m_pkthdr.len);
217
218         sbappendrecord(&so->so_rcv.sb, m);
219         sorwakeup(so);
220 }
221
222 /*
223  * Implementation of usrreqs.
224  */
225 static int
226 sco_sdetach(struct socket *so)
227 {
228         return sco_detach((struct sco_pcb **)&so->so_pcb);
229 }
230
231 static int
232 sco_sabort (struct socket *so)
233 {
234         struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
235
236         sco_disconnect(pcb, 0);
237         soisdisconnected(so);
238
239         return sco_sdetach(so);
240 }
241
242 static int
243 sco_sdisconnect (struct socket *so)
244 {
245         struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
246
247         soisdisconnecting(so);
248
249         return sco_disconnect(pcb, so->so_linger);
250 }
251
252 static int
253 sco_sattach (struct socket *so, int proto,
254     struct pru_attach_info *ai)
255 {
256         struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
257         int err=0;
258
259         if (pcb)
260                 return EINVAL;
261
262         err = soreserve(so, sco_sendspace, sco_recvspace,NULL);
263         if (err)
264                 return err;
265
266         return sco_attach((struct sco_pcb **)&so->so_pcb,
267             &sco_proto, so);
268 }
269
270 static int
271 sco_sbind (struct socket *so, struct sockaddr *nam,
272                                  struct thread *td)
273 {
274         struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
275         struct sockaddr_bt *sa;
276
277         KKASSERT(nam != NULL);
278         sa = (struct sockaddr_bt *)nam;
279
280         if (sa->bt_len != sizeof(struct sockaddr_bt))
281                 return EINVAL;
282
283         if (sa->bt_family != AF_BLUETOOTH)
284                 return EAFNOSUPPORT;
285
286         return sco_bind(pcb, sa);
287 }
288
289 static int
290 sco_sconnect (struct socket *so, struct sockaddr *nam,
291                                     struct thread *td)
292 {
293         struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
294         struct sockaddr_bt *sa;
295
296         KKASSERT(nam != NULL);
297         sa = (struct sockaddr_bt *)nam;
298
299         if (sa->bt_len != sizeof(struct sockaddr_bt))
300                 return EINVAL;
301
302         if (sa->bt_family != AF_BLUETOOTH)
303                 return EAFNOSUPPORT;
304
305         soisconnecting(so);
306         return sco_connect(pcb, sa);
307 }
308
309 static int
310 sco_speeraddr (struct socket *so, struct sockaddr **nam)
311 {
312         struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
313         struct sockaddr_bt *sa, ssa;
314         int e;
315
316         sa = &ssa;
317         bzero(sa, sizeof *sa);
318         sa->bt_len = sizeof(struct sockaddr_bt);
319         sa->bt_family = AF_BLUETOOTH;
320         e = sco_peeraddr(pcb, sa);
321         *nam = dup_sockaddr((struct sockaddr *)sa);
322
323         return (e);
324 }
325
326 static int
327 sco_ssockaddr (struct socket *so, struct sockaddr **nam)
328 {
329         struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
330         struct sockaddr_bt *sa, ssa;
331         int e;
332
333         sa = &ssa;
334         bzero(sa, sizeof *sa);
335         sa->bt_len = sizeof(struct sockaddr_bt);
336         sa->bt_family = AF_BLUETOOTH;
337         e = sco_sockaddr(pcb, sa);
338         *nam = dup_sockaddr((struct sockaddr *)sa);
339
340         return (e);
341 }
342
343 static int
344 sco_sshutdown (struct socket *so)
345 {
346         socantsendmore(so);
347         return 0;
348 }
349
350 static int
351 sco_ssend (struct socket *so, int flags, struct mbuf *m,
352     struct sockaddr *addr, struct mbuf *control, struct thread *td)
353 {
354         struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
355         struct mbuf *m0;
356         int err = 0;
357
358         KKASSERT(m != NULL);
359         if (m->m_pkthdr.len == 0)
360                 goto error;
361
362         if (m->m_pkthdr.len > pcb->sp_mtu) {
363                 err = EMSGSIZE;
364                 goto error;
365         }
366
367         m0 = m_copym(m, 0, M_COPYALL, MB_DONTWAIT);
368         if (m0 == NULL) {
369                 err = ENOMEM;
370                 goto error;
371         }
372
373         if (control) /* no use for that */
374                 m_freem(control);
375
376         sbappendrecord(&so->so_snd.sb, m);
377         return sco_send(pcb, m0);
378
379 error:
380         if (m) m_freem(m);
381         if (control) m_freem(control);
382         return err;
383 }
384
385 static int
386 sco_saccept(struct socket *so, struct sockaddr **nam)
387 {
388         struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
389         struct sockaddr_bt *sa, ssa;
390         int e;
391
392         sa = &ssa;
393         bzero(sa, sizeof *sa);
394         sa->bt_len = sizeof(struct sockaddr_bt);
395         sa->bt_family = AF_BLUETOOTH;
396         e = sco_peeraddr(pcb, sa);
397         *nam = dup_sockaddr((struct sockaddr *)sa);
398
399         return (e);
400 }
401
402 static int
403 sco_slisten(struct socket *so, struct thread *td)
404 {
405         struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
406         return sco_listen(pcb);
407 }
408
409
410 struct pr_usrreqs sco_usrreqs = {
411         .pru_abort = sco_sabort,
412         .pru_accept = sco_saccept,
413         .pru_attach = sco_sattach,
414         .pru_bind = sco_sbind,
415         .pru_connect = sco_sconnect,
416         .pru_connect2 = pru_connect2_notsupp,
417         .pru_control = pru_control_notsupp,
418         .pru_detach = sco_sdetach,
419         .pru_disconnect = sco_sdisconnect,
420         .pru_listen = sco_slisten,
421         .pru_peeraddr = sco_speeraddr,
422         .pru_rcvd = pru_rcvd_notsupp,
423         .pru_rcvoob = pru_rcvoob_notsupp,
424         .pru_send = sco_ssend,
425         .pru_sense = pru_sense_null,
426         .pru_shutdown = sco_sshutdown,
427         .pru_sockaddr = sco_ssockaddr,
428         .pru_sosend = sosend,
429         .pru_soreceive = soreceive
430 };