vaudio.c revision 1.4.32.1 1 /* $NetBSD: vaudio.c,v 1.4.32.1 2019/04/21 05:11:22 isaki Exp $ */
2
3 /*-
4 * Copyright (c) 2011 Jared D. McNeill <jmcneill (at) 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.4.32.1 2019/04/21 05:11:22 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_if.h>
42 #include <dev/auconv.h>
43
44 static const struct audio_format vaudio_audio_formats[1] = {
45 {
46 .mode = AUMODE_PLAY | AUMODE_RECORD,
47 .encoding = AUDIO_ENCODING_SLINEAR_LE,
48 .validbits = 16,
49 .precision = 16,
50 .channels = 2,
51 .channel_mask = AUFMT_STEREO,
52 .frequency_type = 0,
53 .frequency = { 8000, 48000 },
54 },
55 };
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 struct audio_encoding_set * sc_encodings;
76 audio_params_t sc_pparam;
77 audio_params_t sc_rparam;
78 kmutex_t sc_lock;
79 kmutex_t sc_intr_lock;
80
81 struct vaudio_stream sc_play;
82 struct vaudio_stream sc_record;
83 };
84
85 static int vaudio_match(device_t, cfdata_t, void *);
86 static void vaudio_attach(device_t, device_t, void *);
87 static bool vaudio_shutdown(device_t, int);
88
89 static void vaudio_intr(void *);
90 static void vaudio_softintr_play(void *);
91 static void vaudio_softintr_record(void *);
92
93 static int vaudio_open(void *, int);
94 static void vaudio_close(void *);
95 static int vaudio_drain(void *);
96 static int vaudio_query_encoding(void *, audio_encoding_t *);
97 static int vaudio_set_params(void *, int, int, audio_params_t *,
98 audio_params_t *, stream_filter_list_t *,
99 stream_filter_list_t *);
100 static int vaudio_commit_settings(void *);
101 static int vaudio_trigger_output(void *, void *, void *, int,
102 void (*)(void *), void *,
103 const audio_params_t *);
104 static int vaudio_trigger_input(void *, void *, void *, int,
105 void (*)(void *), void *,
106 const audio_params_t *);
107 static int vaudio_halt_output(void *);
108 static int vaudio_halt_input(void *);
109 static int vaudio_getdev(void *, struct audio_device *);
110 static int vaudio_set_port(void *, mixer_ctrl_t *);
111 static int vaudio_get_port(void *, mixer_ctrl_t *);
112 static int vaudio_query_devinfo(void *, mixer_devinfo_t *);
113 static int vaudio_get_props(void *);
114 static void vaudio_get_locks(void *, kmutex_t **, kmutex_t **);
115
116 CFATTACH_DECL_NEW(vaudio, sizeof(struct vaudio_softc),
117 vaudio_match, vaudio_attach, NULL, NULL);
118
119 static const struct audio_hw_if vaudio_hw_if = {
120 .open = vaudio_open,
121 .close = vaudio_close,
122 .drain = vaudio_drain,
123 .query_encoding = vaudio_query_encoding,
124 .set_params = vaudio_set_params,
125 .commit_settings = vaudio_commit_settings,
126 .halt_output = vaudio_halt_output,
127 .halt_input = vaudio_halt_input,
128 .getdev = vaudio_getdev,
129 .set_port = vaudio_set_port,
130 .get_port = vaudio_get_port,
131 .query_devinfo = vaudio_query_devinfo,
132 .get_props = vaudio_get_props,
133 .trigger_output = vaudio_trigger_output,
134 .trigger_input = vaudio_trigger_input,
135 .get_locks = vaudio_get_locks,
136 };
137
138 static int
139 vaudio_match(device_t parent, cfdata_t match, void *opaque)
140 {
141 struct thunkbus_attach_args *taa = opaque;
142
143 if (taa->taa_type != THUNKBUS_TYPE_VAUDIO)
144 return 0;
145
146 return 1;
147 }
148
149 static void
150 vaudio_attach(device_t parent, device_t self, void *opaque)
151 {
152 struct vaudio_softc *sc = device_private(self);
153 struct thunkbus_attach_args *taa = opaque;
154 int error;
155
156 aprint_naive("\n");
157 aprint_normal(": Virtual Audio (device = %s)\n", taa->u.vaudio.device);
158
159 sc->sc_dev = self;
160
161 pmf_device_register1(self, NULL, NULL, vaudio_shutdown);
162
163 sc->sc_audiopath = taa->u.vaudio.device;
164 sc->sc_audiofd = thunk_audio_open(sc->sc_audiopath);
165 if (sc->sc_audiofd == -1) {
166 aprint_error_dev(self, "couldn't open audio device: %d\n",
167 thunk_geterrno());
168 return;
169 }
170
171 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
172 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);
173
174 error = auconv_create_encodings(vaudio_audio_formats,
175 __arraycount(vaudio_audio_formats), &sc->sc_encodings);
176 if (error) {
177 aprint_error_dev(self, "couldn't create encodings\n");
178 return;
179 }
180
181 sc->sc_play.st_softc = sc;
182 sc->sc_play.st_sih = softint_establish(SOFTINT_SERIAL|SOFTINT_MPSAFE,
183 vaudio_softintr_play, &sc->sc_play);
184 callout_init(&sc->sc_play.st_callout, CALLOUT_MPSAFE);
185 callout_setfunc(&sc->sc_play.st_callout, vaudio_intr, &sc->sc_play);
186
187 sc->sc_record.st_softc = sc;
188 sc->sc_record.st_sih = softint_establish(SOFTINT_SERIAL|SOFTINT_MPSAFE,
189 vaudio_softintr_record, &sc->sc_record);
190 callout_init(&sc->sc_record.st_callout, CALLOUT_MPSAFE);
191 callout_setfunc(&sc->sc_record.st_callout, vaudio_intr, &sc->sc_record);
192
193 sc->sc_audiodev = audio_attach_mi(&vaudio_hw_if, sc, self);
194 }
195
196 static bool
197 vaudio_shutdown(device_t self, int flags)
198 {
199 struct vaudio_softc *sc = device_private(self);
200
201 if (sc->sc_audiofd != -1)
202 thunk_audio_close(sc->sc_audiofd);
203
204 return true;
205 }
206
207 static void
208 vaudio_intr(void *opaque)
209 {
210 struct vaudio_stream *st = opaque;
211
212 softint_schedule(st->st_sih);
213 }
214
215 static void
216 vaudio_softintr_play(void *opaque)
217 {
218 struct vaudio_stream *st = opaque;
219 struct vaudio_softc *sc = st->st_softc;
220
221 while (st->st_running) {
222 if (thunk_audio_pollout(sc->sc_audiofd) < st->st_blksize)
223 break;
224 thunk_audio_write(sc->sc_audiofd, st->st_cur, st->st_blksize);
225 mutex_spin_enter(&sc->sc_intr_lock);
226 st->st_intr(st->st_intrarg);
227 mutex_spin_exit(&sc->sc_intr_lock);
228 st->st_cur += st->st_blksize;
229 if (st->st_cur >= st->st_end)
230 st->st_cur = st->st_start;
231 }
232
233 if (st->st_running) {
234 callout_schedule(&st->st_callout, 1);
235 }
236 }
237
238 static void
239 vaudio_softintr_record(void *opaque)
240 {
241 struct vaudio_stream *st = opaque;
242 struct vaudio_softc *sc = st->st_softc;
243
244 while (st->st_running) {
245 if (thunk_audio_pollin(sc->sc_audiofd) < st->st_blksize)
246 break;
247 thunk_audio_read(sc->sc_audiofd, st->st_cur, st->st_blksize);
248 mutex_spin_enter(&sc->sc_intr_lock);
249 st->st_intr(st->st_intrarg);
250 mutex_spin_exit(&sc->sc_intr_lock);
251 st->st_cur += st->st_blksize;
252 if (st->st_cur >= st->st_end)
253 st->st_cur = st->st_start;
254 }
255
256 if (st->st_running) {
257 callout_schedule(&st->st_callout, 1);
258 }
259 }
260
261 static int
262 vaudio_open(void *opaque, int flags)
263 {
264 return 0;
265 }
266
267 static void
268 vaudio_close(void *opaque)
269 {
270 }
271
272 static int
273 vaudio_drain(void *opaque)
274 {
275 struct vaudio_softc *sc = opaque;
276
277 return thunk_audio_drain(sc->sc_audiofd);
278 }
279
280 static int
281 vaudio_query_encoding(void *opaque, audio_encoding_t *enc)
282 {
283 struct vaudio_softc *sc = opaque;
284
285 return auconv_query_encoding(sc->sc_encodings, enc);
286 }
287
288 static int
289 vaudio_set_params(void *opaque, int setmode, int usemode,
290 audio_params_t *play, audio_params_t *rec,
291 stream_filter_list_t *pfil, stream_filter_list_t *rfil)
292 {
293 struct vaudio_softc *sc = opaque;
294 audio_params_t *p;
295 stream_filter_list_t *fil;
296 int mode, index;
297
298 for (mode = AUMODE_RECORD; mode != -1;
299 mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
300 if ((setmode & mode) == 0)
301 continue;
302 p = mode == AUMODE_PLAY ? play : rec;
303 fil = mode == AUMODE_PLAY ? pfil : rfil;
304 if (p == NULL)
305 continue;
306
307 index = auconv_set_converter(vaudio_audio_formats,
308 __arraycount(vaudio_audio_formats), mode, p, TRUE, fil);
309 if (index < 0)
310 return EINVAL;
311 if (fil->req_size > 0)
312 p = &fil->filters[0].param;
313
314 if (mode == AUMODE_PLAY)
315 sc->sc_pparam = *p;
316 else
317 sc->sc_rparam = *p;
318 }
319
320 return 0;
321 }
322
323 static int
324 vaudio_commit_settings(void *opaque)
325 {
326 struct vaudio_softc *sc = opaque;
327 thunk_audio_config_t pconf, rconf;
328
329 memset(&pconf, 0, sizeof(pconf));
330 pconf.sample_rate = sc->sc_pparam.sample_rate;
331 pconf.precision = sc->sc_pparam.precision;
332 pconf.validbits = sc->sc_pparam.validbits;
333 pconf.channels = sc->sc_pparam.channels;
334
335 memset(&rconf, 0, sizeof(rconf));
336 rconf.sample_rate = sc->sc_rparam.sample_rate;
337 rconf.precision = sc->sc_rparam.precision;
338 rconf.validbits = sc->sc_rparam.validbits;
339 rconf.channels = sc->sc_rparam.channels;
340
341 return thunk_audio_config(sc->sc_audiofd, &pconf, &rconf);
342 }
343
344 static int
345 vaudio_trigger_output(void *opaque, void *start, void *end, int blksize,
346 void (*intr)(void *), void *intrarg, const audio_params_t *param)
347 {
348 struct vaudio_softc *sc = opaque;
349 struct vaudio_stream *st = &sc->sc_play;
350
351 st->st_intr = intr;
352 st->st_intrarg = intrarg;
353 st->st_start = st->st_cur = start;
354 st->st_end = end;
355 st->st_blksize = blksize;
356 st->st_running = true;
357 callout_schedule(&st->st_callout, 1);
358
359 return 0;
360 }
361
362 static int
363 vaudio_trigger_input(void *opaque, void *start, void *end, int blksize,
364 void (*intr)(void *), void *intrarg, const audio_params_t *param)
365 {
366 struct vaudio_softc *sc = opaque;
367 struct vaudio_stream *st = &sc->sc_record;
368
369 st->st_intr = intr;
370 st->st_intrarg = intrarg;
371 st->st_start = st->st_cur = start;
372 st->st_end = end;
373 st->st_blksize = blksize;
374 st->st_running = true;
375 callout_schedule(&st->st_callout, 1);
376
377 return 0;
378 }
379
380 static int
381 vaudio_halt_output(void *opaque)
382 {
383 struct vaudio_softc *sc = opaque;
384
385 sc->sc_play.st_running = false;
386 callout_halt(&sc->sc_play.st_callout, NULL);
387
388 return 0;
389 }
390
391 static int
392 vaudio_halt_input(void *opaque)
393 {
394 struct vaudio_softc *sc = opaque;
395
396 sc->sc_record.st_running = false;
397 callout_halt(&sc->sc_record.st_callout, NULL);
398
399 return 0;
400 }
401
402 static int
403 vaudio_getdev(void *opaque, struct audio_device *adev)
404 {
405 struct vaudio_softc *sc = opaque;
406
407 snprintf(adev->name, sizeof(adev->name), "Virtual Audio");
408 adev->version[0] = '\0';
409 snprintf(adev->config, sizeof(adev->config), "%s", sc->sc_audiopath);
410
411 return 0;
412 }
413
414 static int
415 vaudio_set_port(void *opaque, mixer_ctrl_t *mc)
416 {
417 return EINVAL;
418 }
419
420 static int
421 vaudio_get_port(void *opaque, mixer_ctrl_t *mc)
422 {
423 return EINVAL;
424 }
425
426 static int
427 vaudio_query_devinfo(void *opaque, mixer_devinfo_t *di)
428 {
429 return EINVAL;
430 }
431
432 static int
433 vaudio_get_props(void *opaque)
434 {
435 /* NB: should query host audio driver for capabilities */
436 return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE;
437 }
438
439 static void
440 vaudio_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
441 {
442 struct vaudio_softc *sc = opaque;
443
444 *intr = &sc->sc_intr_lock;
445 *thread = &sc->sc_lock;
446 }
447