1 /* $NetBSD: vaudio.c,v 1.5 2019/05/08 13:40:16 isaki 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.5 2019/05/08 13:40:16 isaki 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/audio_if.h> 42 43 static const struct audio_format vaudio_audio_formats[1] = { 44 { 45 .mode = AUMODE_PLAY | AUMODE_RECORD, 46 .encoding = AUDIO_ENCODING_SLINEAR_LE, 47 .validbits = 16, 48 .precision = 16, 49 .channels = 2, 50 .channel_mask = AUFMT_STEREO, 51 .frequency_type = 0, 52 .frequency = { 8000, 48000 }, 53 }, 54 }; 55 #define VAUDIO_NFORMATS __arraycount(vaudio_audio_formats) 56 57 struct vaudio_stream { 58 struct vaudio_softc *st_softc; 59 void * st_sih; 60 callout_t st_callout; 61 void (*st_intr)(void *); 62 void * st_intrarg; 63 uint8_t * st_start; 64 uint8_t * st_end; 65 uint8_t * st_cur; 66 int st_blksize; 67 bool st_running; 68 }; 69 70 struct vaudio_softc { 71 device_t sc_dev; 72 void * sc_audiodev; 73 const char * sc_audiopath; 74 int sc_audiofd; 75 audio_params_t sc_pparam; 76 audio_params_t sc_rparam; 77 kmutex_t sc_lock; 78 kmutex_t sc_intr_lock; 79 80 struct vaudio_stream sc_play; 81 struct vaudio_stream sc_record; 82 }; 83 84 static int vaudio_match(device_t, cfdata_t, void *); 85 static void vaudio_attach(device_t, device_t, void *); 86 static bool vaudio_shutdown(device_t, int); 87 88 static void vaudio_intr(void *); 89 static void vaudio_softintr_play(void *); 90 static void vaudio_softintr_record(void *); 91 92 static int vaudio_query_format(void *, audio_format_query_t *); 93 static int vaudio_set_format(void *, int, const audio_params_t *, 94 const audio_params_t *, 95 audio_filter_reg_t *, audio_filter_reg_t *); 96 static int vaudio_commit_settings(void *); 97 static int vaudio_trigger_output(void *, void *, void *, int, 98 void (*)(void *), void *, 99 const audio_params_t *); 100 static int vaudio_trigger_input(void *, void *, void *, int, 101 void (*)(void *), void *, 102 const audio_params_t *); 103 static int vaudio_halt_output(void *); 104 static int vaudio_halt_input(void *); 105 static int vaudio_getdev(void *, struct audio_device *); 106 static int vaudio_set_port(void *, mixer_ctrl_t *); 107 static int vaudio_get_port(void *, mixer_ctrl_t *); 108 static int vaudio_query_devinfo(void *, mixer_devinfo_t *); 109 static int vaudio_get_props(void *); 110 static void vaudio_get_locks(void *, kmutex_t **, kmutex_t **); 111 112 CFATTACH_DECL_NEW(vaudio, sizeof(struct vaudio_softc), 113 vaudio_match, vaudio_attach, NULL, NULL); 114 115 static const struct audio_hw_if vaudio_hw_if = { 116 .query_format = vaudio_query_format, 117 .set_format = vaudio_set_format, 118 .commit_settings = vaudio_commit_settings, 119 .halt_output = vaudio_halt_output, 120 .halt_input = vaudio_halt_input, 121 .getdev = vaudio_getdev, 122 .set_port = vaudio_set_port, 123 .get_port = vaudio_get_port, 124 .query_devinfo = vaudio_query_devinfo, 125 .get_props = vaudio_get_props, 126 .trigger_output = vaudio_trigger_output, 127 .trigger_input = vaudio_trigger_input, 128 .get_locks = vaudio_get_locks, 129 }; 130 131 static int 132 vaudio_match(device_t parent, cfdata_t match, void *opaque) 133 { 134 struct thunkbus_attach_args *taa = opaque; 135 136 if (taa->taa_type != THUNKBUS_TYPE_VAUDIO) 137 return 0; 138 139 return 1; 140 } 141 142 static void 143 vaudio_attach(device_t parent, device_t self, void *opaque) 144 { 145 struct vaudio_softc *sc = device_private(self); 146 struct thunkbus_attach_args *taa = opaque; 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 sc->sc_play.st_softc = sc; 167 sc->sc_play.st_sih = softint_establish(SOFTINT_SERIAL|SOFTINT_MPSAFE, 168 vaudio_softintr_play, &sc->sc_play); 169 callout_init(&sc->sc_play.st_callout, CALLOUT_MPSAFE); 170 callout_setfunc(&sc->sc_play.st_callout, vaudio_intr, &sc->sc_play); 171 172 sc->sc_record.st_softc = sc; 173 sc->sc_record.st_sih = softint_establish(SOFTINT_SERIAL|SOFTINT_MPSAFE, 174 vaudio_softintr_record, &sc->sc_record); 175 callout_init(&sc->sc_record.st_callout, CALLOUT_MPSAFE); 176 callout_setfunc(&sc->sc_record.st_callout, vaudio_intr, &sc->sc_record); 177 178 sc->sc_audiodev = audio_attach_mi(&vaudio_hw_if, sc, self); 179 } 180 181 static bool 182 vaudio_shutdown(device_t self, int flags) 183 { 184 struct vaudio_softc *sc = device_private(self); 185 186 if (sc->sc_audiofd != -1) 187 thunk_audio_close(sc->sc_audiofd); 188 189 return true; 190 } 191 192 static void 193 vaudio_intr(void *opaque) 194 { 195 struct vaudio_stream *st = opaque; 196 197 softint_schedule(st->st_sih); 198 } 199 200 static void 201 vaudio_softintr_play(void *opaque) 202 { 203 struct vaudio_stream *st = opaque; 204 struct vaudio_softc *sc = st->st_softc; 205 206 while (st->st_running) { 207 if (thunk_audio_pollout(sc->sc_audiofd) < st->st_blksize) 208 break; 209 thunk_audio_write(sc->sc_audiofd, st->st_cur, st->st_blksize); 210 mutex_spin_enter(&sc->sc_intr_lock); 211 st->st_intr(st->st_intrarg); 212 mutex_spin_exit(&sc->sc_intr_lock); 213 st->st_cur += st->st_blksize; 214 if (st->st_cur >= st->st_end) 215 st->st_cur = st->st_start; 216 } 217 218 if (st->st_running) { 219 callout_schedule(&st->st_callout, 1); 220 } 221 } 222 223 static void 224 vaudio_softintr_record(void *opaque) 225 { 226 struct vaudio_stream *st = opaque; 227 struct vaudio_softc *sc = st->st_softc; 228 229 while (st->st_running) { 230 if (thunk_audio_pollin(sc->sc_audiofd) < st->st_blksize) 231 break; 232 thunk_audio_read(sc->sc_audiofd, st->st_cur, st->st_blksize); 233 mutex_spin_enter(&sc->sc_intr_lock); 234 st->st_intr(st->st_intrarg); 235 mutex_spin_exit(&sc->sc_intr_lock); 236 st->st_cur += st->st_blksize; 237 if (st->st_cur >= st->st_end) 238 st->st_cur = st->st_start; 239 } 240 241 if (st->st_running) { 242 callout_schedule(&st->st_callout, 1); 243 } 244 } 245 246 static int 247 vaudio_query_format(void *opaque, audio_format_query_t *afp) 248 { 249 250 return audio_query_format(vaudio_audio_formats, VAUDIO_NFORMATS, afp); 251 } 252 253 static int 254 vaudio_set_format(void *opaque, int setmode, 255 const audio_params_t *play, const audio_params_t *rec, 256 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) 257 { 258 struct vaudio_softc *sc = opaque; 259 260 if ((setmode & AUMODE_PLAY)) 261 sc->sc_pparam = *play; 262 if ((setmode & AUMODE_RECORD)) 263 sc->sc_rparam = *rec; 264 265 return 0; 266 } 267 268 static int 269 vaudio_commit_settings(void *opaque) 270 { 271 struct vaudio_softc *sc = opaque; 272 thunk_audio_config_t pconf, rconf; 273 274 memset(&pconf, 0, sizeof(pconf)); 275 pconf.sample_rate = sc->sc_pparam.sample_rate; 276 pconf.precision = sc->sc_pparam.precision; 277 pconf.validbits = sc->sc_pparam.validbits; 278 pconf.channels = sc->sc_pparam.channels; 279 280 memset(&rconf, 0, sizeof(rconf)); 281 rconf.sample_rate = sc->sc_rparam.sample_rate; 282 rconf.precision = sc->sc_rparam.precision; 283 rconf.validbits = sc->sc_rparam.validbits; 284 rconf.channels = sc->sc_rparam.channels; 285 286 return thunk_audio_config(sc->sc_audiofd, &pconf, &rconf); 287 } 288 289 static int 290 vaudio_trigger_output(void *opaque, void *start, void *end, int blksize, 291 void (*intr)(void *), void *intrarg, const audio_params_t *param) 292 { 293 struct vaudio_softc *sc = opaque; 294 struct vaudio_stream *st = &sc->sc_play; 295 296 st->st_intr = intr; 297 st->st_intrarg = intrarg; 298 st->st_start = st->st_cur = start; 299 st->st_end = end; 300 st->st_blksize = blksize; 301 st->st_running = true; 302 callout_schedule(&st->st_callout, 1); 303 304 return 0; 305 } 306 307 static int 308 vaudio_trigger_input(void *opaque, void *start, void *end, int blksize, 309 void (*intr)(void *), void *intrarg, const audio_params_t *param) 310 { 311 struct vaudio_softc *sc = opaque; 312 struct vaudio_stream *st = &sc->sc_record; 313 314 st->st_intr = intr; 315 st->st_intrarg = intrarg; 316 st->st_start = st->st_cur = start; 317 st->st_end = end; 318 st->st_blksize = blksize; 319 st->st_running = true; 320 callout_schedule(&st->st_callout, 1); 321 322 return 0; 323 } 324 325 static int 326 vaudio_halt_output(void *opaque) 327 { 328 struct vaudio_softc *sc = opaque; 329 330 sc->sc_play.st_running = false; 331 callout_halt(&sc->sc_play.st_callout, NULL); 332 333 return 0; 334 } 335 336 static int 337 vaudio_halt_input(void *opaque) 338 { 339 struct vaudio_softc *sc = opaque; 340 341 sc->sc_record.st_running = false; 342 callout_halt(&sc->sc_record.st_callout, NULL); 343 344 return 0; 345 } 346 347 static int 348 vaudio_getdev(void *opaque, struct audio_device *adev) 349 { 350 struct vaudio_softc *sc = opaque; 351 352 snprintf(adev->name, sizeof(adev->name), "Virtual Audio"); 353 adev->version[0] = '\0'; 354 snprintf(adev->config, sizeof(adev->config), "%s", sc->sc_audiopath); 355 356 return 0; 357 } 358 359 static int 360 vaudio_set_port(void *opaque, mixer_ctrl_t *mc) 361 { 362 return EINVAL; 363 } 364 365 static int 366 vaudio_get_port(void *opaque, mixer_ctrl_t *mc) 367 { 368 return EINVAL; 369 } 370 371 static int 372 vaudio_query_devinfo(void *opaque, mixer_devinfo_t *di) 373 { 374 return EINVAL; 375 } 376 377 static int 378 vaudio_get_props(void *opaque) 379 { 380 /* NB: should query host audio driver for capabilities */ 381 return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE; 382 } 383 384 static void 385 vaudio_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread) 386 { 387 struct vaudio_softc *sc = opaque; 388 389 *intr = &sc->sc_intr_lock; 390 *thread = &sc->sc_lock; 391 } 392