1/* $NetBSD: vaudio.c,v 1.2 2011/12/26 23:50:43 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2011 Jared D. McNeill <jmcneill@invisible.ca> 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: vaudio.c,v 1.2 2011/12/26 23:50:43 jmcneill Exp $"); 31 32#include <sys/param.h> 33#include <sys/proc.h> 34#include <sys/systm.h> 35#include <sys/device.h> 36#include <sys/audioio.h> 37 38#include <machine/mainbus.h> 39#include <machine/thunk.h> 40 41#include <dev/audio_if.h> 42#include <dev/auconv.h> 43 44static const struct audio_format vaudio_audio_formats[1] = { 45 { NULL, AUMODE_PLAY|AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16, 46 2, AUFMT_STEREO, 0, { 8000, 48000 } }, 47}; 48 49struct vaudio_stream { 50 struct vaudio_softc *st_softc; 51 void * st_sih; 52 callout_t st_callout; 53 void (*st_intr)(void *); 54 void * st_intrarg; 55 uint8_t * st_start; 56 uint8_t * st_end; 57 uint8_t * st_cur; 58 int st_blksize; 59 bool st_running; 60}; 61 62struct vaudio_softc { 63 device_t sc_dev; 64 void * sc_audiodev; 65 const char * sc_audiopath; 66 int sc_audiofd; 67 struct audio_encoding_set * sc_encodings; 68 audio_params_t sc_pparam; 69 audio_params_t sc_rparam; 70 kmutex_t sc_lock; 71 kmutex_t sc_intr_lock; 72 73 struct vaudio_stream sc_play; 74 struct vaudio_stream sc_record; 75}; 76 77static int vaudio_match(device_t, cfdata_t, void *); 78static void vaudio_attach(device_t, device_t, void *); 79static bool vaudio_shutdown(device_t, int); 80 81static void vaudio_intr(void *); 82static void vaudio_softintr_play(void *); 83static void vaudio_softintr_record(void *); 84 85static int vaudio_open(void *, int); 86static void vaudio_close(void *); 87static int vaudio_drain(void *); 88static int vaudio_query_encoding(void *, audio_encoding_t *); 89static int vaudio_set_params(void *, int, int, audio_params_t *, 90 audio_params_t *, stream_filter_list_t *, 91 stream_filter_list_t *); 92static int vaudio_commit_settings(void *); 93static int vaudio_trigger_output(void *, void *, void *, int, 94 void (*)(void *), void *, 95 const audio_params_t *); 96static int vaudio_trigger_input(void *, void *, void *, int, 97 void (*)(void *), void *, 98 const audio_params_t *); 99static int vaudio_halt_output(void *); 100static int vaudio_halt_input(void *); 101static int vaudio_getdev(void *, struct audio_device *); 102static int vaudio_set_port(void *, mixer_ctrl_t *); 103static int vaudio_get_port(void *, mixer_ctrl_t *); 104static int vaudio_query_devinfo(void *, mixer_devinfo_t *); 105static int vaudio_get_props(void *); 106static void vaudio_get_locks(void *, kmutex_t **, kmutex_t **); 107 108CFATTACH_DECL_NEW(vaudio, sizeof(struct vaudio_softc), 109 vaudio_match, vaudio_attach, NULL, NULL); 110 111static const struct audio_hw_if vaudio_hw_if = { 112 .open = vaudio_open, 113 .close = vaudio_close, 114 .drain = vaudio_drain, 115 .query_encoding = vaudio_query_encoding, 116 .set_params = vaudio_set_params, 117 .commit_settings = vaudio_commit_settings, 118 .halt_output = vaudio_halt_output, 119 .halt_input = vaudio_halt_input, 120 .getdev = vaudio_getdev, 121 .set_port = vaudio_set_port, 122 .get_port = vaudio_get_port, 123 .query_devinfo = vaudio_query_devinfo, 124 .get_props = vaudio_get_props, 125 .trigger_output = vaudio_trigger_output, 126 .trigger_input = vaudio_trigger_input, 127 .get_locks = vaudio_get_locks, 128}; 129 130static int 131vaudio_match(device_t parent, cfdata_t match, void *opaque) 132{ 133 struct thunkbus_attach_args *taa = opaque; 134 135 if (taa->taa_type != THUNKBUS_TYPE_VAUDIO) 136 return 0; 137 138 return 1; 139} 140 141static void 142vaudio_attach(device_t parent, device_t self, void *opaque) 143{ 144 struct vaudio_softc *sc = device_private(self); 145 struct thunkbus_attach_args *taa = opaque; 146 int error; 147 148 aprint_naive("\n"); 149 aprint_normal(": Virtual Audio (device = %s)\n", taa->u.vaudio.device); 150 151 sc->sc_dev = self; 152 153 pmf_device_register1(self, NULL, NULL, vaudio_shutdown); 154 155 sc->sc_audiopath = taa->u.vaudio.device; 156 sc->sc_audiofd = thunk_audio_open(sc->sc_audiopath); 157 if (sc->sc_audiofd == -1) { 158 aprint_error_dev(self, "couldn't open audio device: %d\n", 159 thunk_geterrno()); 160 return; 161 } 162 163 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 164 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO); 165 166 error = auconv_create_encodings(vaudio_audio_formats, 167 __arraycount(vaudio_audio_formats), &sc->sc_encodings); 168 if (error) { 169 aprint_error_dev(self, "couldn't create encodings\n"); 170 return; 171 } 172 173 sc->sc_play.st_softc = sc; 174 sc->sc_play.st_sih = softint_establish(SOFTINT_SERIAL|SOFTINT_MPSAFE, 175 vaudio_softintr_play, &sc->sc_play); 176 callout_init(&sc->sc_play.st_callout, CALLOUT_MPSAFE); 177 callout_setfunc(&sc->sc_play.st_callout, vaudio_intr, &sc->sc_play); 178 179 sc->sc_record.st_softc = sc; 180 sc->sc_record.st_sih = softint_establish(SOFTINT_SERIAL|SOFTINT_MPSAFE, 181 vaudio_softintr_record, &sc->sc_record); 182 callout_init(&sc->sc_record.st_callout, CALLOUT_MPSAFE); 183 callout_setfunc(&sc->sc_record.st_callout, vaudio_intr, &sc->sc_record); 184 185 sc->sc_audiodev = audio_attach_mi(&vaudio_hw_if, sc, self); 186} 187 188static bool 189vaudio_shutdown(device_t self, int flags) 190{ 191 struct vaudio_softc *sc = device_private(self); 192 193 if (sc->sc_audiofd != -1) 194 thunk_audio_close(sc->sc_audiofd); 195 196 return true; 197} 198 199static void 200vaudio_intr(void *opaque) 201{ 202 struct vaudio_stream *st = opaque; 203 204 softint_schedule(st->st_sih); 205} 206 207static void 208vaudio_softintr_play(void *opaque) 209{ 210 struct vaudio_stream *st = opaque; 211 struct vaudio_softc *sc = st->st_softc; 212 213 while (st->st_running) { 214 if (thunk_audio_pollout(sc->sc_audiofd) < st->st_blksize) 215 break; 216 thunk_audio_write(sc->sc_audiofd, st->st_cur, st->st_blksize); 217 mutex_spin_enter(&sc->sc_intr_lock); 218 st->st_intr(st->st_intrarg); 219 mutex_spin_exit(&sc->sc_intr_lock); 220 st->st_cur += st->st_blksize; 221 if (st->st_cur >= st->st_end) 222 st->st_cur = st->st_start; 223 } 224 225 if (st->st_running) { 226 callout_schedule(&st->st_callout, 1); 227 } 228} 229 230static void 231vaudio_softintr_record(void *opaque) 232{ 233 struct vaudio_stream *st = opaque; 234 struct vaudio_softc *sc = st->st_softc; 235 236 while (st->st_running) { 237 if (thunk_audio_pollin(sc->sc_audiofd) < st->st_blksize) 238 break; 239 thunk_audio_read(sc->sc_audiofd, st->st_cur, st->st_blksize); 240 mutex_spin_enter(&sc->sc_intr_lock); 241 st->st_intr(st->st_intrarg); 242 mutex_spin_exit(&sc->sc_intr_lock); 243 st->st_cur += st->st_blksize; 244 if (st->st_cur >= st->st_end) 245 st->st_cur = st->st_start; 246 } 247 248 if (st->st_running) { 249 callout_schedule(&st->st_callout, 1); 250 } 251} 252 253static int 254vaudio_open(void *opaque, int flags) 255{ 256 return 0; 257} 258 259static void 260vaudio_close(void *opaque) 261{ 262} 263 264static int 265vaudio_drain(void *opaque) 266{ 267 struct vaudio_softc *sc = opaque; 268 269 return thunk_audio_drain(sc->sc_audiofd); 270} 271 272static int 273vaudio_query_encoding(void *opaque, audio_encoding_t *enc) 274{ 275 struct vaudio_softc *sc = opaque; 276 277 return auconv_query_encoding(sc->sc_encodings, enc); 278} 279 280static int 281vaudio_set_params(void *opaque, int setmode, int usemode, 282 audio_params_t *play, audio_params_t *rec, 283 stream_filter_list_t *pfil, stream_filter_list_t *rfil) 284{ 285 struct vaudio_softc *sc = opaque; 286 audio_params_t *p; 287 stream_filter_list_t *fil; 288 int mode, index; 289 290 for (mode = AUMODE_RECORD; mode != -1; 291 mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { 292 if ((setmode & mode) == 0) 293 continue; 294 p = mode == AUMODE_PLAY ? play : rec; 295 fil = mode == AUMODE_PLAY ? pfil : rfil; 296 if (p == NULL) 297 continue; 298 299 index = auconv_set_converter(vaudio_audio_formats, 300 __arraycount(vaudio_audio_formats), mode, p, TRUE, fil); 301 if (index < 0) 302 return EINVAL; 303 if (fil->req_size > 0) 304 p = &fil->filters[0].param; 305 306 if (mode == AUMODE_PLAY) 307 sc->sc_pparam = *p; 308 else 309 sc->sc_rparam = *p; 310 } 311 312 return 0; 313} 314 315static int 316vaudio_commit_settings(void *opaque) 317{ 318 struct vaudio_softc *sc = opaque; 319 thunk_audio_config_t pconf, rconf; 320 321 memset(&pconf, 0, sizeof(pconf)); 322 pconf.sample_rate = sc->sc_pparam.sample_rate; 323 pconf.precision = sc->sc_pparam.precision; 324 pconf.validbits = sc->sc_pparam.validbits; 325 pconf.channels = sc->sc_pparam.channels; 326 327 memset(&rconf, 0, sizeof(rconf)); 328 rconf.sample_rate = sc->sc_rparam.sample_rate; 329 rconf.precision = sc->sc_rparam.precision; 330 rconf.validbits = sc->sc_rparam.validbits; 331 rconf.channels = sc->sc_rparam.channels; 332 333 return thunk_audio_config(sc->sc_audiofd, &pconf, &rconf); 334} 335 336static int 337vaudio_trigger_output(void *opaque, void *start, void *end, int blksize, 338 void (*intr)(void *), void *intrarg, const audio_params_t *param) 339{ 340 struct vaudio_softc *sc = opaque; 341 struct vaudio_stream *st = &sc->sc_play; 342 343 st->st_intr = intr; 344 st->st_intrarg = intrarg; 345 st->st_start = st->st_cur = start; 346 st->st_end = end; 347 st->st_blksize = blksize; 348 st->st_running = true; 349 callout_schedule(&st->st_callout, 1); 350 351 return 0; 352} 353 354static int 355vaudio_trigger_input(void *opaque, void *start, void *end, int blksize, 356 void (*intr)(void *), void *intrarg, const audio_params_t *param) 357{ 358 struct vaudio_softc *sc = opaque; 359 struct vaudio_stream *st = &sc->sc_record; 360 361 st->st_intr = intr; 362 st->st_intrarg = intrarg; 363 st->st_start = st->st_cur = start; 364 st->st_end = end; 365 st->st_blksize = blksize; 366 st->st_running = true; 367 callout_schedule(&st->st_callout, 1); 368 369 return 0; 370} 371 372static int 373vaudio_halt_output(void *opaque) 374{ 375 struct vaudio_softc *sc = opaque; 376 377 sc->sc_play.st_running = false; 378 callout_halt(&sc->sc_play.st_callout, NULL); 379 380 return 0; 381} 382 383static int 384vaudio_halt_input(void *opaque) 385{ 386 struct vaudio_softc *sc = opaque; 387 388 sc->sc_record.st_running = false; 389 callout_halt(&sc->sc_record.st_callout, NULL); 390 391 return 0; 392} 393 394static int 395vaudio_getdev(void *opaque, struct audio_device *adev) 396{ 397 struct vaudio_softc *sc = opaque; 398 399 sprintf(adev->name, "Virtual Audio"); 400 sprintf(adev->version, ""); 401 snprintf(adev->config, sizeof(adev->config), "%s", sc->sc_audiopath); 402 403 return 0; 404} 405 406static int 407vaudio_set_port(void *opaque, mixer_ctrl_t *mc) 408{ 409 return EINVAL; 410} 411 412static int 413vaudio_get_port(void *opaque, mixer_ctrl_t *mc) 414{ 415 return EINVAL; 416} 417 418static int 419vaudio_query_devinfo(void *opaque, mixer_devinfo_t *di) 420{ 421 return EINVAL; 422} 423 424static int 425vaudio_get_props(void *opaque) 426{ 427 /* NB: should query host audio driver for capabilities */ 428 return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE; 429} 430 431static void 432vaudio_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread) 433{ 434 struct vaudio_softc *sc = opaque; 435 436 *intr = &sc->sc_intr_lock; 437 *thread = &sc->sc_lock; 438} 439