sco_socket.c revision 1.9 1 /* $NetBSD: sco_socket.c,v 1.9 2007/04/21 06:15:23 plunky Exp $ */
2
3 /*-
4 * Copyright (c) 2006 Itronix Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of Itronix Inc. may not be used to endorse
16 * or promote products derived from this software without specific
17 * prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: sco_socket.c,v 1.9 2007/04/21 06:15:23 plunky Exp $");
34
35 /* load symbolic names */
36 #ifdef BLUETOOTH_DEBUG
37 #define PRUREQUESTS
38 #define PRCOREQUESTS
39 #endif
40
41 #include <sys/param.h>
42 #include <sys/domain.h>
43 #include <sys/kernel.h>
44 #include <sys/mbuf.h>
45 #include <sys/proc.h>
46 #include <sys/protosw.h>
47 #include <sys/socket.h>
48 #include <sys/socketvar.h>
49 #include <sys/systm.h>
50
51 #include <netbt/bluetooth.h>
52 #include <netbt/hci.h>
53 #include <netbt/sco.h>
54
55 /*******************************************************************************
56 *
57 * SCO SOCK_SEQPACKET sockets - low latency audio data
58 */
59
60 static void sco_connecting(void *);
61 static void sco_connected(void *);
62 static void sco_disconnected(void *, int);
63 static void *sco_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *);
64 static void sco_complete(void *, int);
65 static void sco_linkmode(void *, int);
66 static void sco_input(void *, struct mbuf *);
67
68 static const struct btproto sco_proto = {
69 sco_connecting,
70 sco_connected,
71 sco_disconnected,
72 sco_newconn,
73 sco_complete,
74 sco_linkmode,
75 sco_input,
76 };
77
78 int sco_sendspace = 4096;
79 int sco_recvspace = 4096;
80
81 /*
82 * User Request.
83 * up is socket
84 * m is either
85 * optional mbuf chain containing message
86 * ioctl command (PRU_CONTROL)
87 * nam is either
88 * optional mbuf chain containing an address
89 * ioctl data (PRU_CONTROL)
90 * optionally, protocol number (PRU_ATTACH)
91 * ctl is optional mbuf chain containing socket options
92 * l is pointer to process requesting action (if any)
93 *
94 * we are responsible for disposing of m and ctl if
95 * they are mbuf chains
96 */
97 int
98 sco_usrreq(struct socket *up, int req, struct mbuf *m,
99 struct mbuf *nam, struct mbuf *ctl, struct lwp *l)
100 {
101 struct sco_pcb *pcb = (struct sco_pcb *)up->so_pcb;
102 struct sockaddr_bt *sa;
103 struct mbuf *m0;
104 int err = 0;
105
106 DPRINTFN(2, "%s\n", prurequests[req]);
107
108 switch(req) {
109 case PRU_CONTROL:
110 return EOPNOTSUPP;
111
112 case PRU_PURGEIF:
113 return EOPNOTSUPP;
114
115 case PRU_ATTACH:
116 if (pcb)
117 return EINVAL;
118
119 err = soreserve(up, sco_sendspace, sco_recvspace);
120 if (err)
121 return err;
122
123 return sco_attach((struct sco_pcb **)&up->so_pcb,
124 &sco_proto, up);
125 }
126
127 /* anything after here *requires* a pcb */
128 if (pcb == NULL) {
129 err = EINVAL;
130 goto release;
131 }
132
133 switch(req) {
134 case PRU_DISCONNECT:
135 soisdisconnecting(up);
136 return sco_disconnect(pcb, up->so_linger);
137
138 case PRU_ABORT:
139 sco_disconnect(pcb, 0);
140 soisdisconnected(up);
141 /* fall through to */
142 case PRU_DETACH:
143 return sco_detach((struct sco_pcb **)&up->so_pcb);
144
145 case PRU_BIND:
146 KASSERT(nam != NULL);
147 sa = mtod(nam, struct sockaddr_bt *);
148
149 if (sa->bt_len != sizeof(struct sockaddr_bt))
150 return EINVAL;
151
152 if (sa->bt_family != AF_BLUETOOTH)
153 return EAFNOSUPPORT;
154
155 return sco_bind(pcb, sa);
156
157 case PRU_CONNECT:
158 KASSERT(nam != NULL);
159 sa = mtod(nam, struct sockaddr_bt *);
160
161 if (sa->bt_len != sizeof(struct sockaddr_bt))
162 return EINVAL;
163
164 if (sa->bt_family != AF_BLUETOOTH)
165 return EAFNOSUPPORT;
166
167 soisconnecting(up);
168 return sco_connect(pcb, sa);
169
170 case PRU_PEERADDR:
171 KASSERT(nam != NULL);
172 sa = mtod(nam, struct sockaddr_bt *);
173 nam->m_len = sizeof(struct sockaddr_bt);
174 return sco_peeraddr(pcb, sa);
175
176 case PRU_SOCKADDR:
177 KASSERT(nam != NULL);
178 sa = mtod(nam, struct sockaddr_bt *);
179 nam->m_len = sizeof(struct sockaddr_bt);
180 return sco_sockaddr(pcb, sa);
181
182 case PRU_SHUTDOWN:
183 socantsendmore(up);
184 break;
185
186 case PRU_SEND:
187 KASSERT(m != NULL);
188 if (m->m_pkthdr.len == 0)
189 break;
190
191 if (m->m_pkthdr.len > pcb->sp_mtu) {
192 err = EMSGSIZE;
193 break;
194 }
195
196 m0 = m_copypacket(m, M_DONTWAIT);
197 if (m0 == NULL) {
198 err = ENOMEM;
199 break;
200 }
201
202 if (ctl) /* no use for that */
203 m_freem(ctl);
204
205 sbappendrecord(&up->so_snd, m);
206 return sco_send(pcb, m0);
207
208 case PRU_SENSE:
209 return 0; /* (no sense - Doh!) */
210
211 case PRU_RCVD:
212 case PRU_RCVOOB:
213 return EOPNOTSUPP; /* (no release) */
214
215 case PRU_LISTEN:
216 return sco_listen(pcb);
217
218 case PRU_ACCEPT:
219 KASSERT(nam != NULL);
220 sa = mtod(nam, struct sockaddr_bt *);
221 nam->m_len = sizeof(struct sockaddr_bt);
222 return sco_peeraddr(pcb, sa);
223
224 case PRU_CONNECT2:
225 case PRU_SENDOOB:
226 case PRU_FASTTIMO:
227 case PRU_SLOWTIMO:
228 case PRU_PROTORCV:
229 case PRU_PROTOSEND:
230 err = EOPNOTSUPP;
231 break;
232
233 default:
234 UNKNOWN(req);
235 err = EOPNOTSUPP;
236 break;
237 }
238
239 release:
240 if (m) m_freem(m);
241 if (ctl) m_freem(ctl);
242 return err;
243 }
244
245 /*
246 * get/set socket options
247 */
248 int
249 sco_ctloutput(int req, struct socket *so, int level,
250 int optname, struct mbuf **opt)
251 {
252 struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb;
253 struct mbuf *m;
254 int err = 0;
255
256 DPRINTFN(2, "req %s\n", prcorequests[req]);
257
258 if (pcb == NULL)
259 return EINVAL;
260
261 if (level != BTPROTO_SCO)
262 return ENOPROTOOPT;
263
264 switch(req) {
265 case PRCO_GETOPT:
266 m = m_get(M_WAIT, MT_SOOPTS);
267 m->m_len = sco_getopt(pcb, optname, mtod(m, uint8_t *));
268 if (m->m_len == 0) {
269 m_freem(m);
270 m = NULL;
271 err = ENOPROTOOPT;
272 }
273 *opt = m;
274 break;
275
276 case PRCO_SETOPT:
277 m = *opt;
278 KASSERT(m != NULL);
279 err = sco_setopt(pcb, optname, mtod(m, uint8_t *));
280 m_freem(m);
281 break;
282
283 default:
284 err = ENOPROTOOPT;
285 break;
286 }
287
288 return err;
289 }
290
291 /*****************************************************************************
292 *
293 * SCO Protocol socket callbacks
294 *
295 */
296 static void
297 sco_connecting(void *arg)
298 {
299 struct socket *so = arg;
300
301 DPRINTF("Connecting\n");
302 soisconnecting(so);
303 }
304
305 static void
306 sco_connected(void *arg)
307 {
308 struct socket *so = arg;
309
310 DPRINTF("Connected\n");
311 soisconnected(so);
312 }
313
314 static void
315 sco_disconnected(void *arg, int err)
316 {
317 struct socket *so = arg;
318
319 DPRINTF("Disconnected (%d)\n", err);
320
321 so->so_error = err;
322 soisdisconnected(so);
323 }
324
325 static void *
326 sco_newconn(void *arg, struct sockaddr_bt *laddr,
327 struct sockaddr_bt *raddr)
328 {
329 struct socket *so = arg;
330
331 DPRINTF("New Connection\n");
332 so = sonewconn(so, 0);
333 if (so == NULL)
334 return NULL;
335
336 soisconnecting(so);
337 return so->so_pcb;
338 }
339
340 static void
341 sco_complete(void *arg, int num)
342 {
343 struct socket *so = arg;
344
345 while (num-- > 0)
346 sbdroprecord(&so->so_snd);
347
348 sowwakeup(so);
349 }
350
351 static void
352 sco_linkmode(void *arg, int mode)
353 {
354 }
355
356 static void
357 sco_input(void *arg, struct mbuf *m)
358 {
359 struct socket *so = arg;
360
361 /*
362 * since this data is time sensitive, if the buffer
363 * is full we just dump data until the latest one
364 * will fit.
365 */
366
367 while (m->m_pkthdr.len > sbspace(&so->so_rcv))
368 sbdroprecord(&so->so_rcv);
369
370 DPRINTFN(10, "received %d bytes\n", m->m_pkthdr.len);
371
372 sbappendrecord(&so->so_rcv, m);
373 sorwakeup(so);
374 }
375