1 | /* $NetBSD: btsco.c,v 1.34 2015/07/10 22:03:12 nat Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2006 Itronix Inc. |
5 | * All rights reserved. |
6 | * |
7 | * Written by Iain Hibbert for Itronix Inc. |
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 | * 3. The name of Itronix Inc. may not be used to endorse |
18 | * or promote products derived from this software without specific |
19 | * prior written permission. |
20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY |
25 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
27 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
28 | * ON ANY THEORY OF LIABILITY, WHETHER IN |
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
31 | * POSSIBILITY OF SUCH DAMAGE. |
32 | */ |
33 | |
34 | #include <sys/cdefs.h> |
35 | __KERNEL_RCSID(0, "$NetBSD: btsco.c,v 1.34 2015/07/10 22:03:12 nat Exp $" ); |
36 | |
37 | #include <sys/param.h> |
38 | #include <sys/audioio.h> |
39 | #include <sys/conf.h> |
40 | #include <sys/device.h> |
41 | #include <sys/fcntl.h> |
42 | #include <sys/kernel.h> |
43 | #include <sys/queue.h> |
44 | #include <sys/kmem.h> |
45 | #include <sys/mbuf.h> |
46 | #include <sys/proc.h> |
47 | #include <sys/socketvar.h> |
48 | #include <sys/systm.h> |
49 | #include <sys/intr.h> |
50 | |
51 | #include <prop/proplib.h> |
52 | |
53 | #include <netbt/bluetooth.h> |
54 | #include <netbt/rfcomm.h> |
55 | #include <netbt/sco.h> |
56 | |
57 | #include <dev/audio_if.h> |
58 | #include <dev/auconv.h> |
59 | #include <dev/mulaw.h> |
60 | |
61 | #include <dev/bluetooth/btdev.h> |
62 | #include <dev/bluetooth/btsco.h> |
63 | |
64 | #undef DPRINTF |
65 | #undef DPRINTFN |
66 | |
67 | #ifdef BTSCO_DEBUG |
68 | int btsco_debug = BTSCO_DEBUG; |
69 | #define DPRINTF(...) do { \ |
70 | if (btsco_debug) { \ |
71 | printf("%s: ", __func__); \ |
72 | printf(__VA_ARGS__); \ |
73 | } \ |
74 | } while (/* CONSTCOND */0) |
75 | |
76 | #define DPRINTFN(n, ...) do { \ |
77 | if (btsco_debug > (n)) { \ |
78 | printf("%s: ", __func__); \ |
79 | printf(__VA_ARGS__); \ |
80 | } \ |
81 | } while (/* CONSTCOND */0) |
82 | #else |
83 | #define DPRINTF(...) |
84 | #define DPRINTFN(...) |
85 | #endif |
86 | |
87 | /***************************************************************************** |
88 | * |
89 | * Bluetooth SCO Audio device |
90 | */ |
91 | |
92 | /* btsco softc */ |
93 | struct btsco_softc { |
94 | uint16_t sc_flags; |
95 | const char *sc_name; /* our device_xname */ |
96 | |
97 | device_t sc_audio; /* MI audio device */ |
98 | void *sc_intr; /* interrupt cookie */ |
99 | kcondvar_t sc_connect; /* connect wait */ |
100 | kmutex_t sc_intr_lock; /* for audio */ |
101 | |
102 | /* Bluetooth */ |
103 | bdaddr_t sc_laddr; /* local address */ |
104 | bdaddr_t sc_raddr; /* remote address */ |
105 | uint16_t sc_state; /* link state */ |
106 | struct sco_pcb *sc_sco; /* SCO handle */ |
107 | struct sco_pcb *sc_sco_l; /* SCO listen handle */ |
108 | uint16_t sc_mtu; /* SCO mtu */ |
109 | uint8_t sc_channel; /* RFCOMM channel */ |
110 | int sc_err; /* stored error */ |
111 | |
112 | /* Receive */ |
113 | int sc_rx_want; /* bytes wanted */ |
114 | uint8_t *sc_rx_block; /* receive block */ |
115 | void (*sc_rx_intr)(void *); /* callback */ |
116 | void *sc_rx_intrarg; /* callback arg */ |
117 | struct mbuf *sc_rx_mbuf; /* leftover mbuf */ |
118 | |
119 | /* Transmit */ |
120 | int sc_tx_size; /* bytes to send */ |
121 | int sc_tx_pending; /* packets pending */ |
122 | uint8_t *sc_tx_block; /* transmit block */ |
123 | void (*sc_tx_intr)(void *); /* callback */ |
124 | void *sc_tx_intrarg; /* callback arg */ |
125 | void *sc_tx_buf; /* transmit buffer */ |
126 | int sc_tx_refcnt; /* buffer refcnt */ |
127 | |
128 | /* mixer data */ |
129 | int sc_vgs; /* speaker volume */ |
130 | int sc_vgm; /* mic volume */ |
131 | }; |
132 | |
133 | /* sc_state */ |
134 | #define BTSCO_CLOSED 0 |
135 | #define BTSCO_WAIT_CONNECT 1 |
136 | #define BTSCO_OPEN 2 |
137 | |
138 | /* sc_flags */ |
139 | #define BTSCO_LISTEN (1 << 1) |
140 | |
141 | /* autoconf(9) glue */ |
142 | static int btsco_match(device_t, cfdata_t, void *); |
143 | static void btsco_attach(device_t, device_t, void *); |
144 | static int btsco_detach(device_t, int); |
145 | |
146 | CFATTACH_DECL_NEW(btsco, sizeof(struct btsco_softc), |
147 | btsco_match, btsco_attach, btsco_detach, NULL); |
148 | |
149 | /* audio(9) glue */ |
150 | static int btsco_open(void *, int); |
151 | static void btsco_close(void *); |
152 | static int btsco_query_encoding(void *, struct audio_encoding *); |
153 | static int btsco_set_params(void *, int, int, audio_params_t *, audio_params_t *, |
154 | stream_filter_list_t *, stream_filter_list_t *); |
155 | static int btsco_round_blocksize(void *, int, int, const audio_params_t *); |
156 | static int btsco_start_output(void *, void *, int, void (*)(void *), void *); |
157 | static int btsco_start_input(void *, void *, int, void (*)(void *), void *); |
158 | static int btsco_halt_output(void *); |
159 | static int btsco_halt_input(void *); |
160 | static int btsco_getdev(void *, struct audio_device *); |
161 | static int btsco_setfd(void *, int); |
162 | static int btsco_set_port(void *, mixer_ctrl_t *); |
163 | static int btsco_get_port(void *, mixer_ctrl_t *); |
164 | static int btsco_query_devinfo(void *, mixer_devinfo_t *); |
165 | static void *btsco_allocm(void *, int, size_t); |
166 | static void btsco_freem(void *, void *, size_t); |
167 | static int btsco_get_props(void *); |
168 | static int btsco_dev_ioctl(void *, u_long, void *, int, struct lwp *); |
169 | static void btsco_get_locks(void *, kmutex_t **, kmutex_t **); |
170 | |
171 | static const struct audio_hw_if btsco_if = { |
172 | btsco_open, /* open */ |
173 | btsco_close, /* close */ |
174 | NULL, /* drain */ |
175 | btsco_query_encoding, /* query_encoding */ |
176 | btsco_set_params, /* set_params */ |
177 | btsco_round_blocksize, /* round_blocksize */ |
178 | NULL, /* commit_settings */ |
179 | NULL, /* init_output */ |
180 | NULL, /* init_input */ |
181 | btsco_start_output, /* start_output */ |
182 | btsco_start_input, /* start_input */ |
183 | btsco_halt_output, /* halt_output */ |
184 | btsco_halt_input, /* halt_input */ |
185 | NULL, /* speaker_ctl */ |
186 | btsco_getdev, /* getdev */ |
187 | btsco_setfd, /* setfd */ |
188 | btsco_set_port, /* set_port */ |
189 | btsco_get_port, /* get_port */ |
190 | btsco_query_devinfo, /* query_devinfo */ |
191 | btsco_allocm, /* allocm */ |
192 | btsco_freem, /* freem */ |
193 | NULL, /* round_buffersize */ |
194 | NULL, /* mappage */ |
195 | btsco_get_props, /* get_props */ |
196 | NULL, /* trigger_output */ |
197 | NULL, /* trigger_input */ |
198 | btsco_dev_ioctl, /* dev_ioctl */ |
199 | btsco_get_locks, /* get_locks */ |
200 | }; |
201 | |
202 | static const struct audio_device btsco_device = { |
203 | "Bluetooth Audio" , |
204 | "" , |
205 | "btsco" |
206 | }; |
207 | |
208 | /* Voice_Setting == 0x0060: 8000Hz, mono, 16-bit, slinear_le */ |
209 | static const struct audio_format btsco_format = { |
210 | NULL, /* driver_data */ |
211 | (AUMODE_PLAY | AUMODE_RECORD), /* mode */ |
212 | AUDIO_ENCODING_SLINEAR_LE, /* encoding */ |
213 | 16, /* validbits */ |
214 | 16, /* precision */ |
215 | 1, /* channels */ |
216 | AUFMT_MONAURAL, /* channel_mask */ |
217 | 1, /* frequency_type */ |
218 | { 8000 } /* frequency */ |
219 | }; |
220 | |
221 | /* bluetooth(9) glue for SCO */ |
222 | static void btsco_sco_connecting(void *); |
223 | static void btsco_sco_connected(void *); |
224 | static void btsco_sco_disconnected(void *, int); |
225 | static void *btsco_sco_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); |
226 | static void btsco_sco_complete(void *, int); |
227 | static void btsco_sco_linkmode(void *, int); |
228 | static void btsco_sco_input(void *, struct mbuf *); |
229 | |
230 | static const struct btproto btsco_sco_proto = { |
231 | btsco_sco_connecting, |
232 | btsco_sco_connected, |
233 | btsco_sco_disconnected, |
234 | btsco_sco_newconn, |
235 | btsco_sco_complete, |
236 | btsco_sco_linkmode, |
237 | btsco_sco_input, |
238 | }; |
239 | |
240 | |
241 | /***************************************************************************** |
242 | * |
243 | * btsco definitions |
244 | */ |
245 | |
246 | /* |
247 | * btsco mixer class |
248 | */ |
249 | #define BTSCO_VGS 0 |
250 | #define BTSCO_VGM 1 |
251 | #define BTSCO_INPUT_CLASS 2 |
252 | #define BTSCO_OUTPUT_CLASS 3 |
253 | |
254 | /* connect timeout */ |
255 | #define BTSCO_TIMEOUT (30 * hz) |
256 | |
257 | /* misc btsco functions */ |
258 | static void btsco_extfree(struct mbuf *, void *, size_t, void *); |
259 | static void btsco_intr(void *); |
260 | |
261 | |
262 | /***************************************************************************** |
263 | * |
264 | * btsco autoconf(9) routines |
265 | */ |
266 | |
267 | static int |
268 | btsco_match(device_t self, cfdata_t cfdata, void *aux) |
269 | { |
270 | prop_dictionary_t dict = aux; |
271 | prop_object_t obj; |
272 | |
273 | obj = prop_dictionary_get(dict, BTDEVservice); |
274 | if (prop_string_equals_cstring(obj, "HSET" )) |
275 | return 1; |
276 | |
277 | if (prop_string_equals_cstring(obj, "HF" )) |
278 | return 1; |
279 | |
280 | return 0; |
281 | } |
282 | |
283 | static void |
284 | btsco_attach(device_t parent, device_t self, void *aux) |
285 | { |
286 | struct btsco_softc *sc = device_private(self); |
287 | prop_dictionary_t dict = aux; |
288 | prop_object_t obj; |
289 | |
290 | /* |
291 | * Init softc |
292 | */ |
293 | sc->sc_vgs = 200; |
294 | sc->sc_vgm = 200; |
295 | sc->sc_state = BTSCO_CLOSED; |
296 | sc->sc_name = device_xname(self); |
297 | cv_init(&sc->sc_connect, "connect" ); |
298 | mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NONE); |
299 | |
300 | /* |
301 | * copy in our configuration info |
302 | */ |
303 | obj = prop_dictionary_get(dict, BTDEVladdr); |
304 | bdaddr_copy(&sc->sc_laddr, prop_data_data_nocopy(obj)); |
305 | |
306 | obj = prop_dictionary_get(dict, BTDEVraddr); |
307 | bdaddr_copy(&sc->sc_raddr, prop_data_data_nocopy(obj)); |
308 | |
309 | obj = prop_dictionary_get(dict, BTDEVservice); |
310 | if (prop_string_equals_cstring(obj, "HF" )) { |
311 | sc->sc_flags |= BTSCO_LISTEN; |
312 | aprint_verbose(" listen mode" ); |
313 | } |
314 | |
315 | obj = prop_dictionary_get(dict, BTSCOchannel); |
316 | if (prop_object_type(obj) != PROP_TYPE_NUMBER |
317 | || prop_number_integer_value(obj) < RFCOMM_CHANNEL_MIN |
318 | || prop_number_integer_value(obj) > RFCOMM_CHANNEL_MAX) { |
319 | aprint_error(" invalid %s" , BTSCOchannel); |
320 | return; |
321 | } |
322 | sc->sc_channel = prop_number_integer_value(obj); |
323 | |
324 | aprint_verbose(" channel %d" , sc->sc_channel); |
325 | aprint_normal("\n" ); |
326 | |
327 | DPRINTF("sc=%p\n" , sc); |
328 | |
329 | /* |
330 | * set up transmit interrupt |
331 | */ |
332 | sc->sc_intr = softint_establish(SOFTINT_NET, btsco_intr, sc); |
333 | if (sc->sc_intr == NULL) { |
334 | aprint_error_dev(self, "softint_establish failed\n" ); |
335 | return; |
336 | } |
337 | |
338 | /* |
339 | * attach audio device |
340 | */ |
341 | sc->sc_audio = audio_attach_mi(&btsco_if, sc, self); |
342 | if (sc->sc_audio == NULL) { |
343 | aprint_error_dev(self, "audio_attach_mi failed\n" ); |
344 | return; |
345 | } |
346 | |
347 | pmf_device_register(self, NULL, NULL); |
348 | } |
349 | |
350 | static int |
351 | btsco_detach(device_t self, int flags) |
352 | { |
353 | struct btsco_softc *sc = device_private(self); |
354 | |
355 | DPRINTF("sc=%p\n" , sc); |
356 | |
357 | pmf_device_deregister(self); |
358 | |
359 | mutex_enter(bt_lock); |
360 | if (sc->sc_sco != NULL) { |
361 | DPRINTF("sc_sco=%p\n" , sc->sc_sco); |
362 | sco_disconnect_pcb(sc->sc_sco, 0); |
363 | sco_detach_pcb(&sc->sc_sco); |
364 | sc->sc_sco = NULL; |
365 | } |
366 | |
367 | if (sc->sc_sco_l != NULL) { |
368 | DPRINTF("sc_sco_l=%p\n" , sc->sc_sco_l); |
369 | sco_detach_pcb(&sc->sc_sco_l); |
370 | sc->sc_sco_l = NULL; |
371 | } |
372 | mutex_exit(bt_lock); |
373 | |
374 | if (sc->sc_audio != NULL) { |
375 | DPRINTF("sc_audio=%p\n" , sc->sc_audio); |
376 | config_detach(sc->sc_audio, flags); |
377 | sc->sc_audio = NULL; |
378 | } |
379 | |
380 | if (sc->sc_intr != NULL) { |
381 | softint_disestablish(sc->sc_intr); |
382 | sc->sc_intr = NULL; |
383 | } |
384 | |
385 | if (sc->sc_rx_mbuf != NULL) { |
386 | m_freem(sc->sc_rx_mbuf); |
387 | sc->sc_rx_mbuf = NULL; |
388 | } |
389 | |
390 | if (sc->sc_tx_refcnt > 0) { |
391 | aprint_error_dev(self, "tx_refcnt=%d!\n" , sc->sc_tx_refcnt); |
392 | |
393 | if ((flags & DETACH_FORCE) == 0) |
394 | return EAGAIN; |
395 | } |
396 | |
397 | cv_destroy(&sc->sc_connect); |
398 | mutex_destroy(&sc->sc_intr_lock); |
399 | |
400 | return 0; |
401 | } |
402 | |
403 | /***************************************************************************** |
404 | * |
405 | * bluetooth(9) methods for SCO |
406 | * |
407 | * All these are called from Bluetooth Protocol code, in a soft |
408 | * interrupt context at IPL_SOFTNET. |
409 | */ |
410 | |
411 | static void |
412 | btsco_sco_connecting(void *arg) |
413 | { |
414 | /* struct btsco_softc *sc = arg; */ |
415 | |
416 | /* dont care */ |
417 | } |
418 | |
419 | static void |
420 | btsco_sco_connected(void *arg) |
421 | { |
422 | struct btsco_softc *sc = arg; |
423 | |
424 | DPRINTF("%s\n" , sc->sc_name); |
425 | |
426 | KASSERT(sc->sc_sco != NULL); |
427 | KASSERT(sc->sc_state == BTSCO_WAIT_CONNECT); |
428 | |
429 | /* |
430 | * If we are listening, no more need |
431 | */ |
432 | if (sc->sc_sco_l != NULL) |
433 | sco_detach_pcb(&sc->sc_sco_l); |
434 | |
435 | sc->sc_state = BTSCO_OPEN; |
436 | cv_broadcast(&sc->sc_connect); |
437 | } |
438 | |
439 | static void |
440 | btsco_sco_disconnected(void *arg, int err) |
441 | { |
442 | struct btsco_softc *sc = arg; |
443 | |
444 | DPRINTF("%s sc_state %d\n" , sc->sc_name, sc->sc_state); |
445 | |
446 | KASSERT(sc->sc_sco != NULL); |
447 | |
448 | sc->sc_err = err; |
449 | sco_detach_pcb(&sc->sc_sco); |
450 | |
451 | switch (sc->sc_state) { |
452 | case BTSCO_CLOSED: /* dont think this can happen */ |
453 | break; |
454 | |
455 | case BTSCO_WAIT_CONNECT: /* connect failed */ |
456 | cv_broadcast(&sc->sc_connect); |
457 | break; |
458 | |
459 | case BTSCO_OPEN: /* link lost */ |
460 | /* |
461 | * If IO is in progress, tell the audio driver that it |
462 | * has completed so that when it tries to send more, we |
463 | * can indicate an error. |
464 | */ |
465 | mutex_enter(&sc->sc_intr_lock); |
466 | if (sc->sc_tx_pending > 0) { |
467 | sc->sc_tx_pending = 0; |
468 | (*sc->sc_tx_intr)(sc->sc_tx_intrarg); |
469 | } |
470 | if (sc->sc_rx_want > 0) { |
471 | sc->sc_rx_want = 0; |
472 | (*sc->sc_rx_intr)(sc->sc_rx_intrarg); |
473 | } |
474 | mutex_exit(&sc->sc_intr_lock); |
475 | break; |
476 | |
477 | default: |
478 | UNKNOWN(sc->sc_state); |
479 | } |
480 | |
481 | sc->sc_state = BTSCO_CLOSED; |
482 | } |
483 | |
484 | static void * |
485 | btsco_sco_newconn(void *arg, struct sockaddr_bt *laddr, |
486 | struct sockaddr_bt *raddr) |
487 | { |
488 | struct btsco_softc *sc = arg; |
489 | |
490 | DPRINTF("%s\n" , sc->sc_name); |
491 | |
492 | if (bdaddr_same(&raddr->bt_bdaddr, &sc->sc_raddr) == 0 |
493 | || sc->sc_state != BTSCO_WAIT_CONNECT |
494 | || sc->sc_sco != NULL) |
495 | return NULL; |
496 | |
497 | sco_attach_pcb(&sc->sc_sco, &btsco_sco_proto, sc); |
498 | return sc->sc_sco; |
499 | } |
500 | |
501 | static void |
502 | btsco_sco_complete(void *arg, int count) |
503 | { |
504 | struct btsco_softc *sc = arg; |
505 | |
506 | DPRINTFN(10, "%s count %d\n" , sc->sc_name, count); |
507 | |
508 | mutex_enter(&sc->sc_intr_lock); |
509 | if (sc->sc_tx_pending > 0) { |
510 | sc->sc_tx_pending -= count; |
511 | if (sc->sc_tx_pending == 0) |
512 | (*sc->sc_tx_intr)(sc->sc_tx_intrarg); |
513 | } |
514 | mutex_exit(&sc->sc_intr_lock); |
515 | } |
516 | |
517 | static void |
518 | btsco_sco_linkmode(void *arg, int new) |
519 | { |
520 | /* struct btsco_softc *sc = arg; */ |
521 | |
522 | /* dont care */ |
523 | } |
524 | |
525 | static void |
526 | btsco_sco_input(void *arg, struct mbuf *m) |
527 | { |
528 | struct btsco_softc *sc = arg; |
529 | int len; |
530 | |
531 | DPRINTFN(10, "%s len=%d\n" , sc->sc_name, m->m_pkthdr.len); |
532 | |
533 | mutex_enter(&sc->sc_intr_lock); |
534 | if (sc->sc_rx_want == 0) { |
535 | m_freem(m); |
536 | } else { |
537 | KASSERT(sc->sc_rx_intr != NULL); |
538 | KASSERT(sc->sc_rx_block != NULL); |
539 | |
540 | len = MIN(sc->sc_rx_want, m->m_pkthdr.len); |
541 | m_copydata(m, 0, len, sc->sc_rx_block); |
542 | |
543 | sc->sc_rx_want -= len; |
544 | sc->sc_rx_block += len; |
545 | |
546 | if (len > m->m_pkthdr.len) { |
547 | if (sc->sc_rx_mbuf != NULL) |
548 | m_freem(sc->sc_rx_mbuf); |
549 | |
550 | m_adj(m, len); |
551 | sc->sc_rx_mbuf = m; |
552 | } else { |
553 | m_freem(m); |
554 | } |
555 | |
556 | if (sc->sc_rx_want == 0) |
557 | (*sc->sc_rx_intr)(sc->sc_rx_intrarg); |
558 | } |
559 | mutex_exit(&sc->sc_intr_lock); |
560 | } |
561 | |
562 | |
563 | /***************************************************************************** |
564 | * |
565 | * audio(9) methods |
566 | * |
567 | */ |
568 | |
569 | static int |
570 | btsco_open(void *hdl, int flags) |
571 | { |
572 | struct sockaddr_bt sa; |
573 | struct btsco_softc *sc = hdl; |
574 | struct sockopt sopt; |
575 | int err, timo; |
576 | |
577 | DPRINTF("%s flags 0x%x\n" , sc->sc_name, flags); |
578 | /* flags FREAD & FWRITE? */ |
579 | |
580 | if (sc->sc_sco != NULL || sc->sc_sco_l != NULL) |
581 | return EIO; |
582 | |
583 | KASSERT(mutex_owned(bt_lock)); |
584 | |
585 | memset(&sa, 0, sizeof(sa)); |
586 | sa.bt_len = sizeof(sa); |
587 | sa.bt_family = AF_BLUETOOTH; |
588 | bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); |
589 | |
590 | if (sc->sc_flags & BTSCO_LISTEN) { |
591 | err = sco_attach_pcb(&sc->sc_sco_l, &btsco_sco_proto, sc); |
592 | if (err) |
593 | goto done; |
594 | |
595 | err = sco_bind_pcb(sc->sc_sco_l, &sa); |
596 | if (err) { |
597 | sco_detach_pcb(&sc->sc_sco_l); |
598 | goto done; |
599 | } |
600 | |
601 | err = sco_listen_pcb(sc->sc_sco_l); |
602 | if (err) { |
603 | sco_detach_pcb(&sc->sc_sco_l); |
604 | goto done; |
605 | } |
606 | |
607 | timo = 0; /* no timeout */ |
608 | } else { |
609 | err = sco_attach_pcb(&sc->sc_sco, &btsco_sco_proto, sc); |
610 | if (err) |
611 | goto done; |
612 | |
613 | err = sco_bind_pcb(sc->sc_sco, &sa); |
614 | if (err) { |
615 | sco_detach_pcb(&sc->sc_sco); |
616 | goto done; |
617 | } |
618 | |
619 | bdaddr_copy(&sa.bt_bdaddr, &sc->sc_raddr); |
620 | err = sco_connect_pcb(sc->sc_sco, &sa); |
621 | if (err) { |
622 | sco_detach_pcb(&sc->sc_sco); |
623 | goto done; |
624 | } |
625 | |
626 | timo = BTSCO_TIMEOUT; |
627 | } |
628 | |
629 | sc->sc_state = BTSCO_WAIT_CONNECT; |
630 | while (err == 0 && sc->sc_state == BTSCO_WAIT_CONNECT) |
631 | err = cv_timedwait_sig(&sc->sc_connect, bt_lock, timo); |
632 | |
633 | switch (sc->sc_state) { |
634 | case BTSCO_CLOSED: /* disconnected */ |
635 | err = sc->sc_err; |
636 | |
637 | /* fall through to */ |
638 | case BTSCO_WAIT_CONNECT: /* error */ |
639 | if (sc->sc_sco != NULL) |
640 | sco_detach_pcb(&sc->sc_sco); |
641 | |
642 | if (sc->sc_sco_l != NULL) |
643 | sco_detach_pcb(&sc->sc_sco_l); |
644 | |
645 | break; |
646 | |
647 | case BTSCO_OPEN: /* hurrah */ |
648 | sockopt_init(&sopt, BTPROTO_SCO, SO_SCO_MTU, 0); |
649 | (void)sco_getopt(sc->sc_sco, &sopt); |
650 | (void)sockopt_get(&sopt, &sc->sc_mtu, sizeof(sc->sc_mtu)); |
651 | sockopt_destroy(&sopt); |
652 | break; |
653 | |
654 | default: |
655 | UNKNOWN(sc->sc_state); |
656 | break; |
657 | } |
658 | |
659 | done: |
660 | DPRINTF("done err=%d, sc_state=%d, sc_mtu=%d\n" , |
661 | err, sc->sc_state, sc->sc_mtu); |
662 | return err; |
663 | } |
664 | |
665 | static void |
666 | btsco_close(void *hdl) |
667 | { |
668 | struct btsco_softc *sc = hdl; |
669 | |
670 | DPRINTF("%s\n" , sc->sc_name); |
671 | |
672 | KASSERT(mutex_owned(bt_lock)); |
673 | |
674 | if (sc->sc_sco != NULL) { |
675 | sco_disconnect_pcb(sc->sc_sco, 0); |
676 | sco_detach_pcb(&sc->sc_sco); |
677 | } |
678 | |
679 | if (sc->sc_sco_l != NULL) { |
680 | sco_detach_pcb(&sc->sc_sco_l); |
681 | } |
682 | |
683 | if (sc->sc_rx_mbuf != NULL) { |
684 | m_freem(sc->sc_rx_mbuf); |
685 | sc->sc_rx_mbuf = NULL; |
686 | } |
687 | |
688 | sc->sc_rx_want = 0; |
689 | sc->sc_rx_block = NULL; |
690 | sc->sc_rx_intr = NULL; |
691 | sc->sc_rx_intrarg = NULL; |
692 | |
693 | sc->sc_tx_size = 0; |
694 | sc->sc_tx_block = NULL; |
695 | sc->sc_tx_pending = 0; |
696 | sc->sc_tx_intr = NULL; |
697 | sc->sc_tx_intrarg = NULL; |
698 | } |
699 | |
700 | static int |
701 | btsco_query_encoding(void *hdl, struct audio_encoding *ae) |
702 | { |
703 | /* struct btsco_softc *sc = hdl; */ |
704 | int err = 0; |
705 | |
706 | switch (ae->index) { |
707 | case 0: |
708 | strcpy(ae->name, AudioEslinear_le); |
709 | ae->encoding = AUDIO_ENCODING_SLINEAR_LE; |
710 | ae->precision = 16; |
711 | ae->flags = 0; |
712 | break; |
713 | |
714 | default: |
715 | err = EINVAL; |
716 | } |
717 | |
718 | return err; |
719 | } |
720 | |
721 | static int |
722 | btsco_set_params(void *hdl, int setmode, int usemode, |
723 | audio_params_t *play, audio_params_t *rec, |
724 | stream_filter_list_t *pfil, stream_filter_list_t *rfil) |
725 | { |
726 | /* struct btsco_softc *sc = hdl; */ |
727 | const struct audio_format *f; |
728 | int rv; |
729 | |
730 | DPRINTF("setmode 0x%x usemode 0x%x\n" , setmode, usemode); |
731 | DPRINTF("rate %d, precision %d, channels %d encoding %d\n" , |
732 | play->sample_rate, play->precision, play->channels, play->encoding); |
733 | |
734 | /* |
735 | * If we had a list of formats, we could check the HCI_Voice_Setting |
736 | * and select the appropriate one to use. Currently only one is |
737 | * supported: 0x0060 == 8000Hz, mono, 16-bit, slinear_le |
738 | */ |
739 | f = &btsco_format; |
740 | |
741 | if (setmode & AUMODE_PLAY) { |
742 | rv = auconv_set_converter(f, 1, AUMODE_PLAY, play, TRUE, pfil); |
743 | if (rv < 0) |
744 | return EINVAL; |
745 | } |
746 | |
747 | if (setmode & AUMODE_RECORD) { |
748 | rv = auconv_set_converter(f, 1, AUMODE_RECORD, rec, TRUE, rfil); |
749 | if (rv < 0) |
750 | return EINVAL; |
751 | } |
752 | |
753 | return 0; |
754 | } |
755 | |
756 | /* |
757 | * If we have an MTU value to use, round the blocksize to that. |
758 | */ |
759 | static int |
760 | btsco_round_blocksize(void *hdl, int bs, int mode, |
761 | const audio_params_t *param) |
762 | { |
763 | struct btsco_softc *sc = hdl; |
764 | |
765 | if (sc->sc_mtu > 0) { |
766 | bs = (bs / sc->sc_mtu) * sc->sc_mtu; |
767 | if (bs == 0) |
768 | bs = sc->sc_mtu; |
769 | } |
770 | |
771 | DPRINTF("%s mode=0x%x, bs=%d, sc_mtu=%d\n" , |
772 | sc->sc_name, mode, bs, sc->sc_mtu); |
773 | |
774 | return bs; |
775 | } |
776 | |
777 | /* |
778 | * Start Output |
779 | * |
780 | * We dont want to be calling the network stack with sc_intr_lock held |
781 | * so make a note of what is to be sent, and schedule an interrupt to |
782 | * bundle it up and queue it. |
783 | */ |
784 | static int |
785 | btsco_start_output(void *hdl, void *block, int blksize, |
786 | void (*intr)(void *), void *intrarg) |
787 | { |
788 | struct btsco_softc *sc = hdl; |
789 | |
790 | DPRINTFN(5, "%s blksize %d\n" , sc->sc_name, blksize); |
791 | |
792 | if (sc->sc_sco == NULL) |
793 | return ENOTCONN; /* connection lost */ |
794 | |
795 | sc->sc_tx_block = block; |
796 | sc->sc_tx_pending = 0; |
797 | sc->sc_tx_size = blksize; |
798 | sc->sc_tx_intr = intr; |
799 | sc->sc_tx_intrarg = intrarg; |
800 | |
801 | kpreempt_disable(); |
802 | softint_schedule(sc->sc_intr); |
803 | kpreempt_enable(); |
804 | return 0; |
805 | } |
806 | |
807 | /* |
808 | * Start Input |
809 | * |
810 | * When the SCO link is up, we are getting data in any case, so all we do |
811 | * is note what we want and where to put it and let the sco_input routine |
812 | * fill in the data. |
813 | * |
814 | * If there was any leftover data that didnt fit in the last block, retry |
815 | * it now. |
816 | */ |
817 | static int |
818 | btsco_start_input(void *hdl, void *block, int blksize, |
819 | void (*intr)(void *), void *intrarg) |
820 | { |
821 | struct btsco_softc *sc = hdl; |
822 | struct mbuf *m; |
823 | |
824 | DPRINTFN(5, "%s blksize %d\n" , sc->sc_name, blksize); |
825 | |
826 | if (sc->sc_sco == NULL) |
827 | return ENOTCONN; |
828 | |
829 | sc->sc_rx_want = blksize; |
830 | sc->sc_rx_block = block; |
831 | sc->sc_rx_intr = intr; |
832 | sc->sc_rx_intrarg = intrarg; |
833 | |
834 | if (sc->sc_rx_mbuf != NULL) { |
835 | m = sc->sc_rx_mbuf; |
836 | sc->sc_rx_mbuf = NULL; |
837 | btsco_sco_input(sc, m); |
838 | } |
839 | |
840 | return 0; |
841 | } |
842 | |
843 | /* |
844 | * Halt Output |
845 | * |
846 | * This doesnt really halt the output, but it will look |
847 | * that way to the audio driver. The current block will |
848 | * still be transmitted. |
849 | */ |
850 | static int |
851 | btsco_halt_output(void *hdl) |
852 | { |
853 | struct btsco_softc *sc = hdl; |
854 | |
855 | DPRINTFN(5, "%s\n" , sc->sc_name); |
856 | |
857 | sc->sc_tx_size = 0; |
858 | sc->sc_tx_block = NULL; |
859 | sc->sc_tx_pending = 0; |
860 | sc->sc_tx_intr = NULL; |
861 | sc->sc_tx_intrarg = NULL; |
862 | |
863 | return 0; |
864 | } |
865 | |
866 | /* |
867 | * Halt Input |
868 | * |
869 | * This doesnt really halt the input, but it will look |
870 | * that way to the audio driver. Incoming data will be |
871 | * discarded. |
872 | */ |
873 | static int |
874 | btsco_halt_input(void *hdl) |
875 | { |
876 | struct btsco_softc *sc = hdl; |
877 | |
878 | DPRINTFN(5, "%s\n" , sc->sc_name); |
879 | |
880 | sc->sc_rx_want = 0; |
881 | sc->sc_rx_block = NULL; |
882 | sc->sc_rx_intr = NULL; |
883 | sc->sc_rx_intrarg = NULL; |
884 | |
885 | if (sc->sc_rx_mbuf != NULL) { |
886 | m_freem(sc->sc_rx_mbuf); |
887 | sc->sc_rx_mbuf = NULL; |
888 | } |
889 | |
890 | return 0; |
891 | } |
892 | |
893 | static int |
894 | btsco_getdev(void *hdl, struct audio_device *ret) |
895 | { |
896 | |
897 | *ret = btsco_device; |
898 | return 0; |
899 | } |
900 | |
901 | static int |
902 | btsco_setfd(void *hdl, int fd) |
903 | { |
904 | DPRINTF("set %s duplex\n" , fd ? "full" : "half" ); |
905 | |
906 | return 0; |
907 | } |
908 | |
909 | static int |
910 | btsco_set_port(void *hdl, mixer_ctrl_t *mc) |
911 | { |
912 | struct btsco_softc *sc = hdl; |
913 | int err = 0; |
914 | |
915 | DPRINTF("%s dev %d type %d\n" , sc->sc_name, mc->dev, mc->type); |
916 | |
917 | switch (mc->dev) { |
918 | case BTSCO_VGS: |
919 | if (mc->type != AUDIO_MIXER_VALUE || |
920 | mc->un.value.num_channels != 1) { |
921 | err = EINVAL; |
922 | break; |
923 | } |
924 | |
925 | sc->sc_vgs = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; |
926 | break; |
927 | |
928 | case BTSCO_VGM: |
929 | if (mc->type != AUDIO_MIXER_VALUE || |
930 | mc->un.value.num_channels != 1) { |
931 | err = EINVAL; |
932 | break; |
933 | } |
934 | |
935 | sc->sc_vgm = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; |
936 | break; |
937 | |
938 | default: |
939 | err = EINVAL; |
940 | break; |
941 | } |
942 | |
943 | return err; |
944 | } |
945 | |
946 | static int |
947 | btsco_get_port(void *hdl, mixer_ctrl_t *mc) |
948 | { |
949 | struct btsco_softc *sc = hdl; |
950 | int err = 0; |
951 | |
952 | DPRINTF("%s dev %d\n" , sc->sc_name, mc->dev); |
953 | |
954 | switch (mc->dev) { |
955 | case BTSCO_VGS: |
956 | mc->type = AUDIO_MIXER_VALUE; |
957 | mc->un.value.num_channels = 1; |
958 | mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_vgs; |
959 | break; |
960 | |
961 | case BTSCO_VGM: |
962 | mc->type = AUDIO_MIXER_VALUE; |
963 | mc->un.value.num_channels = 1; |
964 | mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_vgm; |
965 | break; |
966 | |
967 | default: |
968 | err = EINVAL; |
969 | break; |
970 | } |
971 | |
972 | return err; |
973 | } |
974 | |
975 | static int |
976 | btsco_query_devinfo(void *hdl, mixer_devinfo_t *di) |
977 | { |
978 | /* struct btsco_softc *sc = hdl; */ |
979 | int err = 0; |
980 | |
981 | switch(di->index) { |
982 | case BTSCO_VGS: |
983 | di->mixer_class = BTSCO_INPUT_CLASS; |
984 | di->next = di->prev = AUDIO_MIXER_LAST; |
985 | strcpy(di->label.name, AudioNspeaker); |
986 | di->type = AUDIO_MIXER_VALUE; |
987 | strcpy(di->un.v.units.name, AudioNvolume); |
988 | di->un.v.num_channels = 1; |
989 | di->un.v.delta = BTSCO_DELTA; |
990 | break; |
991 | |
992 | case BTSCO_VGM: |
993 | di->mixer_class = BTSCO_INPUT_CLASS; |
994 | di->next = di->prev = AUDIO_MIXER_LAST; |
995 | strcpy(di->label.name, AudioNmicrophone); |
996 | di->type = AUDIO_MIXER_VALUE; |
997 | strcpy(di->un.v.units.name, AudioNvolume); |
998 | di->un.v.num_channels = 1; |
999 | di->un.v.delta = BTSCO_DELTA; |
1000 | break; |
1001 | |
1002 | case BTSCO_INPUT_CLASS: |
1003 | di->mixer_class = BTSCO_INPUT_CLASS; |
1004 | di->next = di->prev = AUDIO_MIXER_LAST; |
1005 | strcpy(di->label.name, AudioCinputs); |
1006 | di->type = AUDIO_MIXER_CLASS; |
1007 | break; |
1008 | |
1009 | default: |
1010 | err = ENXIO; |
1011 | break; |
1012 | } |
1013 | |
1014 | return err; |
1015 | } |
1016 | |
1017 | /* |
1018 | * Allocate Ring Buffers. |
1019 | */ |
1020 | static void * |
1021 | btsco_allocm(void *hdl, int direction, size_t size) |
1022 | { |
1023 | struct btsco_softc *sc = hdl; |
1024 | void *addr; |
1025 | |
1026 | DPRINTF("%s: size %d direction %d\n" , sc->sc_name, size, direction); |
1027 | |
1028 | addr = kmem_alloc(size, KM_SLEEP); |
1029 | |
1030 | if (addr != NULL && direction == AUMODE_PLAY) { |
1031 | sc->sc_tx_buf = addr; |
1032 | sc->sc_tx_refcnt = 0; |
1033 | } |
1034 | |
1035 | return addr; |
1036 | } |
1037 | |
1038 | /* |
1039 | * Free Ring Buffers. |
1040 | * |
1041 | * Because we used external memory for the tx mbufs, we dont |
1042 | * want to free the memory until all the mbufs are done with |
1043 | * |
1044 | * Just to be sure, dont free if something is still pending. |
1045 | * This would be a memory leak but at least there is a warning.. |
1046 | */ |
1047 | static void |
1048 | btsco_freem(void *hdl, void *addr, size_t size) |
1049 | { |
1050 | struct btsco_softc *sc = hdl; |
1051 | int count = hz / 2; |
1052 | |
1053 | if (addr == sc->sc_tx_buf) { |
1054 | DPRINTF("%s: tx_refcnt=%d\n" , sc->sc_name, sc->sc_tx_refcnt); |
1055 | |
1056 | sc->sc_tx_buf = NULL; |
1057 | |
1058 | while (sc->sc_tx_refcnt> 0 && count-- > 0) |
1059 | kpause("drain" , false, 1, NULL); |
1060 | |
1061 | if (sc->sc_tx_refcnt > 0) { |
1062 | aprint_error("%s: ring buffer unreleased!\n" , sc->sc_name); |
1063 | return; |
1064 | } |
1065 | } |
1066 | |
1067 | kmem_free(addr, size); |
1068 | } |
1069 | |
1070 | static int |
1071 | btsco_get_props(void *hdl) |
1072 | { |
1073 | |
1074 | return AUDIO_PROP_FULLDUPLEX; |
1075 | } |
1076 | |
1077 | static void |
1078 | btsco_get_locks(void *hdl, kmutex_t **intr, kmutex_t **thread) |
1079 | { |
1080 | struct btsco_softc *sc = hdl; |
1081 | |
1082 | *intr = &sc->sc_intr_lock; |
1083 | *thread = bt_lock; |
1084 | } |
1085 | |
1086 | /* |
1087 | * Handle private ioctl. We pass information out about how to talk |
1088 | * to the device and mixer. |
1089 | */ |
1090 | static int |
1091 | btsco_dev_ioctl(void *hdl, u_long cmd, void *addr, int flag, |
1092 | struct lwp *l) |
1093 | { |
1094 | struct btsco_softc *sc = hdl; |
1095 | struct btsco_info *bi = (struct btsco_info *)addr; |
1096 | int err = 0; |
1097 | |
1098 | DPRINTF("%s cmd 0x%lx flag %d\n" , sc->sc_name, cmd, flag); |
1099 | |
1100 | switch (cmd) { |
1101 | case BTSCO_GETINFO: |
1102 | memset(bi, 0, sizeof(*bi)); |
1103 | bdaddr_copy(&bi->laddr, &sc->sc_laddr); |
1104 | bdaddr_copy(&bi->raddr, &sc->sc_raddr); |
1105 | bi->channel = sc->sc_channel; |
1106 | bi->vgs = BTSCO_VGS; |
1107 | bi->vgm = BTSCO_VGM; |
1108 | break; |
1109 | |
1110 | default: |
1111 | err = EPASSTHROUGH; |
1112 | break; |
1113 | } |
1114 | |
1115 | return err; |
1116 | } |
1117 | |
1118 | |
1119 | /***************************************************************************** |
1120 | * |
1121 | * misc btsco functions |
1122 | * |
1123 | */ |
1124 | |
1125 | /* |
1126 | * Our transmit interrupt. This is triggered when a new block is to be |
1127 | * sent. We send mtu sized chunks of the block as mbufs with external |
1128 | * storage to sco_send_pcb() |
1129 | */ |
1130 | static void |
1131 | btsco_intr(void *arg) |
1132 | { |
1133 | struct btsco_softc *sc = arg; |
1134 | struct mbuf *m; |
1135 | uint8_t *block; |
1136 | int mlen, size; |
1137 | |
1138 | DPRINTFN(10, "%s block %p size %d\n" , |
1139 | sc->sc_name, sc->sc_tx_block, sc->sc_tx_size); |
1140 | |
1141 | if (sc->sc_sco == NULL) |
1142 | return; /* connection is lost */ |
1143 | |
1144 | block = sc->sc_tx_block; |
1145 | size = sc->sc_tx_size; |
1146 | sc->sc_tx_block = NULL; |
1147 | sc->sc_tx_size = 0; |
1148 | |
1149 | mutex_enter(bt_lock); |
1150 | while (size > 0) { |
1151 | MGETHDR(m, M_DONTWAIT, MT_DATA); |
1152 | if (m == NULL) |
1153 | break; |
1154 | |
1155 | mlen = MIN(sc->sc_mtu, size); |
1156 | |
1157 | /* I think M_DEVBUF is true but not relevant */ |
1158 | MEXTADD(m, block, mlen, M_DEVBUF, btsco_extfree, sc); |
1159 | if ((m->m_flags & M_EXT) == 0) { |
1160 | m_free(m); |
1161 | break; |
1162 | } |
1163 | sc->sc_tx_refcnt++; |
1164 | |
1165 | m->m_pkthdr.len = m->m_len = mlen; |
1166 | sc->sc_tx_pending++; |
1167 | |
1168 | if (sco_send_pcb(sc->sc_sco, m) > 0) { |
1169 | sc->sc_tx_pending--; |
1170 | break; |
1171 | } |
1172 | |
1173 | block += mlen; |
1174 | size -= mlen; |
1175 | } |
1176 | mutex_exit(bt_lock); |
1177 | } |
1178 | |
1179 | /* |
1180 | * Release the mbuf, we keep a reference count on the tx buffer so |
1181 | * that we dont release it before its free. |
1182 | */ |
1183 | static void |
1184 | btsco_extfree(struct mbuf *m, void *addr, size_t size, |
1185 | void *arg) |
1186 | { |
1187 | struct btsco_softc *sc = arg; |
1188 | |
1189 | if (m != NULL) |
1190 | pool_cache_put(mb_cache, m); |
1191 | |
1192 | sc->sc_tx_refcnt--; |
1193 | } |
1194 | |