1 | /* $NetBSD: sco_socket.c,v 1.37 2015/05/02 17:18:03 rtr 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.37 2015/05/02 17:18:03 rtr 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 | static int |
82 | sco_attach(struct socket *so, int proto) |
83 | { |
84 | int error; |
85 | |
86 | KASSERT(so->so_pcb == NULL); |
87 | |
88 | if (so->so_lock == NULL) { |
89 | mutex_obj_hold(bt_lock); |
90 | so->so_lock = bt_lock; |
91 | solock(so); |
92 | } |
93 | KASSERT(solocked(so)); |
94 | |
95 | error = soreserve(so, sco_sendspace, sco_recvspace); |
96 | if (error) { |
97 | return error; |
98 | } |
99 | return sco_attach_pcb((struct sco_pcb **)&so->so_pcb, &sco_proto, so); |
100 | } |
101 | |
102 | static void |
103 | sco_detach(struct socket *so) |
104 | { |
105 | KASSERT(so->so_pcb != NULL); |
106 | sco_detach_pcb((struct sco_pcb **)&so->so_pcb); |
107 | KASSERT(so->so_pcb == NULL); |
108 | } |
109 | |
110 | static int |
111 | sco_accept(struct socket *so, struct sockaddr *nam) |
112 | { |
113 | struct sco_pcb *pcb = so->so_pcb; |
114 | |
115 | KASSERT(solocked(so)); |
116 | KASSERT(nam != NULL); |
117 | |
118 | if (pcb == NULL) |
119 | return EINVAL; |
120 | |
121 | return sco_peeraddr_pcb(pcb, (struct sockaddr_bt *)nam); |
122 | } |
123 | |
124 | static int |
125 | sco_bind(struct socket *so, struct sockaddr *nam, struct lwp *l) |
126 | { |
127 | struct sco_pcb *pcb = so->so_pcb; |
128 | struct sockaddr_bt *sa = (struct sockaddr_bt *)nam; |
129 | |
130 | KASSERT(solocked(so)); |
131 | KASSERT(nam != NULL); |
132 | |
133 | if (pcb == NULL) |
134 | return EINVAL; |
135 | |
136 | if (sa->bt_len != sizeof(struct sockaddr_bt)) |
137 | return EINVAL; |
138 | |
139 | if (sa->bt_family != AF_BLUETOOTH) |
140 | return EAFNOSUPPORT; |
141 | |
142 | return sco_bind_pcb(pcb, sa); |
143 | } |
144 | |
145 | static int |
146 | sco_listen(struct socket *so, struct lwp *l) |
147 | { |
148 | struct sco_pcb *pcb = so->so_pcb; |
149 | |
150 | KASSERT(solocked(so)); |
151 | |
152 | if (pcb == NULL) |
153 | return EINVAL; |
154 | |
155 | return sco_listen_pcb(pcb); |
156 | } |
157 | |
158 | static int |
159 | sco_connect(struct socket *so, struct sockaddr *nam, struct lwp *l) |
160 | { |
161 | struct sco_pcb *pcb = so->so_pcb; |
162 | struct sockaddr_bt *sa = (struct sockaddr_bt *)nam; |
163 | |
164 | KASSERT(solocked(so)); |
165 | KASSERT(nam != NULL); |
166 | |
167 | if (pcb == NULL) |
168 | return EINVAL; |
169 | |
170 | if (sa->bt_len != sizeof(struct sockaddr_bt)) |
171 | return EINVAL; |
172 | |
173 | if (sa->bt_family != AF_BLUETOOTH) |
174 | return EAFNOSUPPORT; |
175 | |
176 | soisconnecting(so); |
177 | return sco_connect_pcb(pcb, sa); |
178 | } |
179 | |
180 | static int |
181 | sco_connect2(struct socket *so, struct socket *so2) |
182 | { |
183 | struct sco_pcb *pcb = so->so_pcb; |
184 | |
185 | KASSERT(solocked(so)); |
186 | |
187 | if (pcb == NULL) |
188 | return EINVAL; |
189 | |
190 | return EOPNOTSUPP; |
191 | } |
192 | |
193 | static int |
194 | sco_disconnect(struct socket *so) |
195 | { |
196 | struct sco_pcb *pcb = so->so_pcb; |
197 | |
198 | KASSERT(solocked(so)); |
199 | |
200 | if (pcb == NULL) |
201 | return EINVAL; |
202 | |
203 | soisdisconnecting(so); |
204 | return sco_disconnect_pcb(pcb, so->so_linger); |
205 | } |
206 | |
207 | static int |
208 | sco_shutdown(struct socket *so) |
209 | { |
210 | KASSERT(solocked(so)); |
211 | |
212 | socantsendmore(so); |
213 | return 0; |
214 | } |
215 | |
216 | static int |
217 | sco_abort(struct socket *so) |
218 | { |
219 | struct sco_pcb *pcb = so->so_pcb; |
220 | |
221 | KASSERT(solocked(so)); |
222 | |
223 | if (pcb == NULL) |
224 | return EINVAL; |
225 | |
226 | sco_disconnect_pcb(pcb, 0); |
227 | soisdisconnected(so); |
228 | sco_detach(so); |
229 | return 0; |
230 | } |
231 | |
232 | static int |
233 | sco_ioctl(struct socket *so, u_long cmd, void *nam, struct ifnet *ifp) |
234 | { |
235 | return EOPNOTSUPP; |
236 | } |
237 | |
238 | static int |
239 | sco_stat(struct socket *so, struct stat *ub) |
240 | { |
241 | KASSERT(solocked(so)); |
242 | |
243 | return 0; |
244 | } |
245 | |
246 | static int |
247 | sco_peeraddr(struct socket *so, struct sockaddr *nam) |
248 | { |
249 | struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb; |
250 | |
251 | KASSERT(solocked(so)); |
252 | KASSERT(pcb != NULL); |
253 | KASSERT(nam != NULL); |
254 | |
255 | return sco_peeraddr_pcb(pcb, (struct sockaddr_bt *)nam); |
256 | } |
257 | |
258 | static int |
259 | sco_sockaddr(struct socket *so, struct sockaddr *nam) |
260 | { |
261 | struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb; |
262 | |
263 | KASSERT(solocked(so)); |
264 | KASSERT(pcb != NULL); |
265 | KASSERT(nam != NULL); |
266 | |
267 | return sco_sockaddr_pcb(pcb, (struct sockaddr_bt *)nam); |
268 | } |
269 | |
270 | static int |
271 | sco_rcvd(struct socket *so, int flags, struct lwp *l) |
272 | { |
273 | KASSERT(solocked(so)); |
274 | |
275 | return EOPNOTSUPP; |
276 | } |
277 | |
278 | static int |
279 | sco_recvoob(struct socket *so, struct mbuf *m, int flags) |
280 | { |
281 | KASSERT(solocked(so)); |
282 | |
283 | return EOPNOTSUPP; |
284 | } |
285 | |
286 | static int |
287 | sco_send(struct socket *so, struct mbuf *m, struct sockaddr *nam, |
288 | struct mbuf *control, struct lwp *l) |
289 | { |
290 | struct sco_pcb *pcb = so->so_pcb; |
291 | int err = 0; |
292 | struct mbuf *m0; |
293 | |
294 | KASSERT(solocked(so)); |
295 | KASSERT(m != NULL); |
296 | |
297 | if (control) /* no use for that */ |
298 | m_freem(control); |
299 | |
300 | if (pcb == NULL) { |
301 | err = EINVAL; |
302 | goto release; |
303 | } |
304 | |
305 | if (m->m_pkthdr.len == 0) |
306 | goto release; |
307 | |
308 | if (m->m_pkthdr.len > pcb->sp_mtu) { |
309 | err = EMSGSIZE; |
310 | goto release; |
311 | } |
312 | |
313 | m0 = m_copypacket(m, M_DONTWAIT); |
314 | if (m0 == NULL) { |
315 | err = ENOMEM; |
316 | goto release; |
317 | } |
318 | |
319 | sbappendrecord(&so->so_snd, m); |
320 | return sco_send_pcb(pcb, m0); |
321 | |
322 | release: |
323 | m_freem(m); |
324 | return err; |
325 | } |
326 | |
327 | static int |
328 | sco_sendoob(struct socket *so, struct mbuf *m, struct mbuf *control) |
329 | { |
330 | KASSERT(solocked(so)); |
331 | |
332 | if (m) |
333 | m_freem(m); |
334 | if (control) |
335 | m_freem(control); |
336 | |
337 | return EOPNOTSUPP; |
338 | } |
339 | |
340 | static int |
341 | sco_purgeif(struct socket *so, struct ifnet *ifp) |
342 | { |
343 | |
344 | return EOPNOTSUPP; |
345 | } |
346 | |
347 | /* |
348 | * get/set socket options |
349 | */ |
350 | int |
351 | sco_ctloutput(int req, struct socket *so, struct sockopt *sopt) |
352 | { |
353 | struct sco_pcb *pcb = (struct sco_pcb *)so->so_pcb; |
354 | int err = 0; |
355 | |
356 | DPRINTFN(2, "req %s\n" , prcorequests[req]); |
357 | |
358 | if (pcb == NULL) |
359 | return EINVAL; |
360 | |
361 | if (sopt->sopt_level != BTPROTO_SCO) |
362 | return ENOPROTOOPT; |
363 | |
364 | switch(req) { |
365 | case PRCO_GETOPT: |
366 | err = sco_getopt(pcb, sopt); |
367 | break; |
368 | |
369 | case PRCO_SETOPT: |
370 | err = sco_setopt(pcb, sopt); |
371 | break; |
372 | |
373 | default: |
374 | err = ENOPROTOOPT; |
375 | break; |
376 | } |
377 | |
378 | return err; |
379 | } |
380 | |
381 | /***************************************************************************** |
382 | * |
383 | * SCO Protocol socket callbacks |
384 | * |
385 | */ |
386 | static void |
387 | sco_connecting(void *arg) |
388 | { |
389 | struct socket *so = arg; |
390 | |
391 | DPRINTF("Connecting\n" ); |
392 | soisconnecting(so); |
393 | } |
394 | |
395 | static void |
396 | sco_connected(void *arg) |
397 | { |
398 | struct socket *so = arg; |
399 | |
400 | DPRINTF("Connected\n" ); |
401 | soisconnected(so); |
402 | } |
403 | |
404 | static void |
405 | sco_disconnected(void *arg, int err) |
406 | { |
407 | struct socket *so = arg; |
408 | |
409 | DPRINTF("Disconnected (%d)\n" , err); |
410 | |
411 | so->so_error = err; |
412 | soisdisconnected(so); |
413 | } |
414 | |
415 | static void * |
416 | sco_newconn(void *arg, struct sockaddr_bt *laddr, |
417 | struct sockaddr_bt *raddr) |
418 | { |
419 | struct socket *so = arg; |
420 | |
421 | DPRINTF("New Connection\n" ); |
422 | so = sonewconn(so, false); |
423 | if (so == NULL) |
424 | return NULL; |
425 | |
426 | soisconnecting(so); |
427 | return so->so_pcb; |
428 | } |
429 | |
430 | static void |
431 | sco_complete(void *arg, int num) |
432 | { |
433 | struct socket *so = arg; |
434 | |
435 | while (num-- > 0) |
436 | sbdroprecord(&so->so_snd); |
437 | |
438 | sowwakeup(so); |
439 | } |
440 | |
441 | static void |
442 | sco_linkmode(void *arg, int mode) |
443 | { |
444 | } |
445 | |
446 | static void |
447 | sco_input(void *arg, struct mbuf *m) |
448 | { |
449 | struct socket *so = arg; |
450 | |
451 | /* |
452 | * since this data is time sensitive, if the buffer |
453 | * is full we just dump data until the latest one |
454 | * will fit. |
455 | */ |
456 | |
457 | while (m->m_pkthdr.len > sbspace(&so->so_rcv)) |
458 | sbdroprecord(&so->so_rcv); |
459 | |
460 | DPRINTFN(10, "received %d bytes\n" , m->m_pkthdr.len); |
461 | |
462 | sbappendrecord(&so->so_rcv, m); |
463 | sorwakeup(so); |
464 | } |
465 | |
466 | PR_WRAP_USRREQS(sco) |
467 | |
468 | #define sco_attach sco_attach_wrapper |
469 | #define sco_detach sco_detach_wrapper |
470 | #define sco_accept sco_accept_wrapper |
471 | #define sco_bind sco_bind_wrapper |
472 | #define sco_listen sco_listen_wrapper |
473 | #define sco_connect sco_connect_wrapper |
474 | #define sco_connect2 sco_connect2_wrapper |
475 | #define sco_disconnect sco_disconnect_wrapper |
476 | #define sco_shutdown sco_shutdown_wrapper |
477 | #define sco_abort sco_abort_wrapper |
478 | #define sco_ioctl sco_ioctl_wrapper |
479 | #define sco_stat sco_stat_wrapper |
480 | #define sco_peeraddr sco_peeraddr_wrapper |
481 | #define sco_sockaddr sco_sockaddr_wrapper |
482 | #define sco_rcvd sco_rcvd_wrapper |
483 | #define sco_recvoob sco_recvoob_wrapper |
484 | #define sco_send sco_send_wrapper |
485 | #define sco_sendoob sco_sendoob_wrapper |
486 | #define sco_purgeif sco_purgeif_wrapper |
487 | |
488 | const struct pr_usrreqs sco_usrreqs = { |
489 | .pr_attach = sco_attach, |
490 | .pr_detach = sco_detach, |
491 | .pr_accept = sco_accept, |
492 | .pr_bind = sco_bind, |
493 | .pr_listen = sco_listen, |
494 | .pr_connect = sco_connect, |
495 | .pr_connect2 = sco_connect2, |
496 | .pr_disconnect = sco_disconnect, |
497 | .pr_shutdown = sco_shutdown, |
498 | .pr_abort = sco_abort, |
499 | .pr_ioctl = sco_ioctl, |
500 | .pr_stat = sco_stat, |
501 | .pr_peeraddr = sco_peeraddr, |
502 | .pr_sockaddr = sco_sockaddr, |
503 | .pr_rcvd = sco_rcvd, |
504 | .pr_recvoob = sco_recvoob, |
505 | .pr_send = sco_send, |
506 | .pr_sendoob = sco_sendoob, |
507 | .pr_purgeif = sco_purgeif, |
508 | }; |
509 | |