]>
Commit | Line | Data |
---|---|---|
663df1cc AR |
1 | /* |
2 | * SPDX-License-Identifier: ISC | |
3 | * | |
4 | * Copyright (c) 2019 Alexandre Ratchov <[email protected]> | |
5 | */ | |
6 | ||
7 | /* | |
8 | * TODO : | |
9 | * | |
10 | * Use a single device and open it in full-duplex rather than | |
11 | * opening it twice (once for playback once for recording). | |
12 | * | |
13 | * This is the only way to ensure that playback doesn't drift with respect | |
14 | * to recording, which is what guest systems expect. | |
15 | */ | |
16 | ||
17 | #include <poll.h> | |
18 | #include <sndio.h> | |
19 | #include "qemu/osdep.h" | |
20 | #include "qemu/main-loop.h" | |
21 | #include "audio.h" | |
22 | #include "trace.h" | |
23 | ||
24 | #define AUDIO_CAP "sndio" | |
25 | #include "audio_int.h" | |
26 | ||
27 | /* default latency in microseconds if no option is set */ | |
28 | #define SNDIO_LATENCY_US 50000 | |
29 | ||
30 | typedef struct SndioVoice { | |
31 | union { | |
32 | HWVoiceOut out; | |
33 | HWVoiceIn in; | |
34 | } hw; | |
35 | struct sio_par par; | |
36 | struct sio_hdl *hdl; | |
37 | struct pollfd *pfds; | |
38 | struct pollindex { | |
39 | struct SndioVoice *self; | |
40 | int index; | |
41 | } *pindexes; | |
42 | unsigned char *buf; | |
43 | size_t buf_size; | |
44 | size_t sndio_pos; | |
45 | size_t qemu_pos; | |
46 | unsigned int mode; | |
47 | unsigned int nfds; | |
48 | bool enabled; | |
49 | } SndioVoice; | |
50 | ||
51 | typedef struct SndioConf { | |
52 | const char *devname; | |
53 | unsigned int latency; | |
54 | } SndioConf; | |
55 | ||
56 | /* needed for forward reference */ | |
57 | static void sndio_poll_in(void *arg); | |
58 | static void sndio_poll_out(void *arg); | |
59 | ||
60 | /* | |
61 | * stop polling descriptors | |
62 | */ | |
63 | static void sndio_poll_clear(SndioVoice *self) | |
64 | { | |
65 | struct pollfd *pfd; | |
66 | int i; | |
67 | ||
68 | for (i = 0; i < self->nfds; i++) { | |
69 | pfd = &self->pfds[i]; | |
70 | qemu_set_fd_handler(pfd->fd, NULL, NULL, NULL); | |
71 | } | |
72 | ||
73 | self->nfds = 0; | |
74 | } | |
75 | ||
76 | /* | |
77 | * write data to the device until it blocks or | |
78 | * all of our buffered data is written | |
79 | */ | |
80 | static void sndio_write(SndioVoice *self) | |
81 | { | |
82 | size_t todo, n; | |
83 | ||
84 | todo = self->qemu_pos - self->sndio_pos; | |
85 | ||
86 | /* | |
87 | * transfer data to device, until it blocks | |
88 | */ | |
89 | while (todo > 0) { | |
90 | n = sio_write(self->hdl, self->buf + self->sndio_pos, todo); | |
91 | if (n == 0) { | |
92 | break; | |
93 | } | |
94 | self->sndio_pos += n; | |
95 | todo -= n; | |
96 | } | |
97 | ||
98 | if (self->sndio_pos == self->buf_size) { | |
99 | /* | |
100 | * we complete the block | |
101 | */ | |
102 | self->sndio_pos = 0; | |
103 | self->qemu_pos = 0; | |
104 | } | |
105 | } | |
106 | ||
107 | /* | |
108 | * read data from the device until it blocks or | |
109 | * there no room any longer | |
110 | */ | |
111 | static void sndio_read(SndioVoice *self) | |
112 | { | |
113 | size_t todo, n; | |
114 | ||
115 | todo = self->buf_size - self->sndio_pos; | |
116 | ||
117 | /* | |
118 | * transfer data from the device, until it blocks | |
119 | */ | |
120 | while (todo > 0) { | |
121 | n = sio_read(self->hdl, self->buf + self->sndio_pos, todo); | |
122 | if (n == 0) { | |
123 | break; | |
124 | } | |
125 | self->sndio_pos += n; | |
126 | todo -= n; | |
127 | } | |
128 | } | |
129 | ||
130 | /* | |
131 | * Set handlers for all descriptors libsndio needs to | |
132 | * poll | |
133 | */ | |
134 | static void sndio_poll_wait(SndioVoice *self) | |
135 | { | |
136 | struct pollfd *pfd; | |
137 | int events, i; | |
138 | ||
139 | events = 0; | |
140 | if (self->mode == SIO_PLAY) { | |
141 | if (self->sndio_pos < self->qemu_pos) { | |
142 | events |= POLLOUT; | |
143 | } | |
144 | } else { | |
145 | if (self->sndio_pos < self->buf_size) { | |
146 | events |= POLLIN; | |
147 | } | |
148 | } | |
149 | ||
150 | /* | |
151 | * fill the given array of descriptors with the events sndio | |
152 | * wants, they are different from our 'event' variable because | |
153 | * sndio may use descriptors internally. | |
154 | */ | |
155 | self->nfds = sio_pollfd(self->hdl, self->pfds, events); | |
156 | ||
157 | for (i = 0; i < self->nfds; i++) { | |
158 | pfd = &self->pfds[i]; | |
159 | if (pfd->fd < 0) { | |
160 | continue; | |
161 | } | |
162 | qemu_set_fd_handler(pfd->fd, | |
163 | (pfd->events & POLLIN) ? sndio_poll_in : NULL, | |
164 | (pfd->events & POLLOUT) ? sndio_poll_out : NULL, | |
165 | &self->pindexes[i]); | |
166 | pfd->revents = 0; | |
167 | } | |
168 | } | |
169 | ||
170 | /* | |
171 | * call-back called when one of the descriptors | |
172 | * became readable or writable | |
173 | */ | |
174 | static void sndio_poll_event(SndioVoice *self, int index, int event) | |
175 | { | |
176 | int revents; | |
177 | ||
178 | /* | |
179 | * ensure we're not called twice this cycle | |
180 | */ | |
181 | sndio_poll_clear(self); | |
182 | ||
183 | /* | |
184 | * make self->pfds[] look as we're returning from poll syscal, | |
185 | * this is how sio_revents expects events to be. | |
186 | */ | |
187 | self->pfds[index].revents = event; | |
188 | ||
189 | /* | |
190 | * tell sndio to handle events and return whether we can read or | |
191 | * write without blocking. | |
192 | */ | |
193 | revents = sio_revents(self->hdl, self->pfds); | |
194 | if (self->mode == SIO_PLAY) { | |
195 | if (revents & POLLOUT) { | |
196 | sndio_write(self); | |
197 | } | |
198 | ||
199 | if (self->qemu_pos < self->buf_size) { | |
200 | audio_run(self->hw.out.s, "sndio_out"); | |
201 | } | |
202 | } else { | |
203 | if (revents & POLLIN) { | |
204 | sndio_read(self); | |
205 | } | |
206 | ||
207 | if (self->qemu_pos < self->sndio_pos) { | |
208 | audio_run(self->hw.in.s, "sndio_in"); | |
209 | } | |
210 | } | |
211 | ||
212 | /* | |
213 | * audio_run() may have changed state | |
214 | */ | |
215 | if (self->enabled) { | |
216 | sndio_poll_wait(self); | |
217 | } | |
218 | } | |
219 | ||
220 | /* | |
221 | * return the upper limit of the amount of free play buffer space | |
222 | */ | |
223 | static size_t sndio_buffer_get_free(HWVoiceOut *hw) | |
224 | { | |
225 | SndioVoice *self = (SndioVoice *) hw; | |
226 | ||
227 | return self->buf_size - self->qemu_pos; | |
228 | } | |
229 | ||
230 | /* | |
231 | * return a buffer where data to play can be stored, | |
232 | * its size is stored in the location pointed by the size argument. | |
233 | */ | |
234 | static void *sndio_get_buffer_out(HWVoiceOut *hw, size_t *size) | |
235 | { | |
236 | SndioVoice *self = (SndioVoice *) hw; | |
237 | ||
238 | *size = self->buf_size - self->qemu_pos; | |
239 | return self->buf + self->qemu_pos; | |
240 | } | |
241 | ||
242 | /* | |
243 | * put back to sndio back-end a buffer returned by sndio_get_buffer_out() | |
244 | */ | |
245 | static size_t sndio_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) | |
246 | { | |
247 | SndioVoice *self = (SndioVoice *) hw; | |
248 | ||
249 | self->qemu_pos += size; | |
250 | sndio_poll_wait(self); | |
251 | return size; | |
252 | } | |
253 | ||
254 | /* | |
255 | * return a buffer from where recorded data is available, | |
256 | * its size is stored in the location pointed by the size argument. | |
257 | * it may not exceed the initial value of "*size". | |
258 | */ | |
259 | static void *sndio_get_buffer_in(HWVoiceIn *hw, size_t *size) | |
260 | { | |
261 | SndioVoice *self = (SndioVoice *) hw; | |
262 | size_t todo, max_todo; | |
263 | ||
264 | /* | |
265 | * unlike the get_buffer_out() method, get_buffer_in() | |
266 | * must return a buffer of at most the given size, see audio.c | |
267 | */ | |
268 | max_todo = *size; | |
269 | ||
270 | todo = self->sndio_pos - self->qemu_pos; | |
271 | if (todo > max_todo) { | |
272 | todo = max_todo; | |
273 | } | |
274 | ||
275 | *size = todo; | |
276 | return self->buf + self->qemu_pos; | |
277 | } | |
278 | ||
279 | /* | |
280 | * discard the given amount of recorded data | |
281 | */ | |
282 | static void sndio_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size) | |
283 | { | |
284 | SndioVoice *self = (SndioVoice *) hw; | |
285 | ||
286 | self->qemu_pos += size; | |
287 | if (self->qemu_pos == self->buf_size) { | |
288 | self->qemu_pos = 0; | |
289 | self->sndio_pos = 0; | |
290 | } | |
291 | sndio_poll_wait(self); | |
292 | } | |
293 | ||
294 | /* | |
295 | * call-back called when one of our descriptors becomes writable | |
296 | */ | |
297 | static void sndio_poll_out(void *arg) | |
298 | { | |
299 | struct pollindex *pindex = (struct pollindex *) arg; | |
300 | ||
301 | sndio_poll_event(pindex->self, pindex->index, POLLOUT); | |
302 | } | |
303 | ||
304 | /* | |
305 | * call-back called when one of our descriptors becomes readable | |
306 | */ | |
307 | static void sndio_poll_in(void *arg) | |
308 | { | |
309 | struct pollindex *pindex = (struct pollindex *) arg; | |
310 | ||
311 | sndio_poll_event(pindex->self, pindex->index, POLLIN); | |
312 | } | |
313 | ||
314 | static void sndio_fini(SndioVoice *self) | |
315 | { | |
316 | if (self->hdl) { | |
317 | sio_close(self->hdl); | |
318 | self->hdl = NULL; | |
319 | } | |
320 | ||
321 | g_free(self->pfds); | |
322 | g_free(self->pindexes); | |
323 | g_free(self->buf); | |
324 | } | |
325 | ||
326 | static int sndio_init(SndioVoice *self, | |
327 | struct audsettings *as, int mode, Audiodev *dev) | |
328 | { | |
329 | AudiodevSndioOptions *opts = &dev->u.sndio; | |
330 | unsigned long long latency; | |
331 | const char *dev_name; | |
332 | struct sio_par req; | |
333 | unsigned int nch; | |
334 | int i, nfds; | |
335 | ||
336 | dev_name = opts->has_dev ? opts->dev : SIO_DEVANY; | |
337 | latency = opts->has_latency ? opts->latency : SNDIO_LATENCY_US; | |
338 | ||
339 | /* open the device in non-blocking mode */ | |
340 | self->hdl = sio_open(dev_name, mode, 1); | |
341 | if (self->hdl == NULL) { | |
342 | dolog("failed to open device\n"); | |
343 | return -1; | |
344 | } | |
345 | ||
346 | self->mode = mode; | |
347 | ||
348 | sio_initpar(&req); | |
349 | ||
350 | switch (as->fmt) { | |
351 | case AUDIO_FORMAT_S8: | |
352 | req.bits = 8; | |
353 | req.sig = 1; | |
354 | break; | |
355 | case AUDIO_FORMAT_U8: | |
356 | req.bits = 8; | |
357 | req.sig = 0; | |
358 | break; | |
359 | case AUDIO_FORMAT_S16: | |
360 | req.bits = 16; | |
361 | req.sig = 1; | |
362 | break; | |
363 | case AUDIO_FORMAT_U16: | |
364 | req.bits = 16; | |
365 | req.sig = 0; | |
366 | break; | |
367 | case AUDIO_FORMAT_S32: | |
368 | req.bits = 32; | |
369 | req.sig = 1; | |
370 | break; | |
371 | case AUDIO_FORMAT_U32: | |
372 | req.bits = 32; | |
373 | req.sig = 0; | |
374 | break; | |
375 | default: | |
376 | dolog("unknown audio sample format\n"); | |
377 | return -1; | |
378 | } | |
379 | ||
380 | if (req.bits > 8) { | |
381 | req.le = as->endianness ? 0 : 1; | |
382 | } | |
383 | ||
384 | req.rate = as->freq; | |
385 | if (mode == SIO_PLAY) { | |
386 | req.pchan = as->nchannels; | |
387 | } else { | |
388 | req.rchan = as->nchannels; | |
389 | } | |
390 | ||
391 | /* set on-device buffer size */ | |
392 | req.appbufsz = req.rate * latency / 1000000; | |
393 | ||
394 | if (!sio_setpar(self->hdl, &req)) { | |
395 | dolog("failed set audio params\n"); | |
396 | goto fail; | |
397 | } | |
398 | ||
399 | if (!sio_getpar(self->hdl, &self->par)) { | |
400 | dolog("failed get audio params\n"); | |
401 | goto fail; | |
402 | } | |
403 | ||
404 | nch = (mode == SIO_PLAY) ? self->par.pchan : self->par.rchan; | |
405 | ||
406 | /* | |
407 | * With the default setup, sndio supports any combination of parameters | |
408 | * so these checks are mostly to catch configuration errors. | |
409 | */ | |
410 | if (self->par.bits != req.bits || self->par.bps != req.bits / 8 || | |
411 | self->par.sig != req.sig || (req.bits > 8 && self->par.le != req.le) || | |
412 | self->par.rate != as->freq || nch != as->nchannels) { | |
413 | dolog("unsupported audio params\n"); | |
414 | goto fail; | |
415 | } | |
416 | ||
417 | /* | |
418 | * we use one block as buffer size; this is how | |
419 | * transfers get well aligned | |
420 | */ | |
421 | self->buf_size = self->par.round * self->par.bps * nch; | |
422 | ||
423 | self->buf = g_malloc(self->buf_size); | |
424 | if (self->buf == NULL) { | |
425 | dolog("failed to allocate audio buffer\n"); | |
426 | goto fail; | |
427 | } | |
428 | ||
429 | nfds = sio_nfds(self->hdl); | |
430 | ||
431 | self->pfds = g_malloc_n(nfds, sizeof(struct pollfd)); | |
432 | if (self->pfds == NULL) { | |
433 | dolog("failed to allocate pollfd structures\n"); | |
434 | goto fail; | |
435 | } | |
436 | ||
437 | self->pindexes = g_malloc_n(nfds, sizeof(struct pollindex)); | |
438 | if (self->pindexes == NULL) { | |
439 | dolog("failed to allocate pollindex structures\n"); | |
440 | goto fail; | |
441 | } | |
442 | ||
443 | for (i = 0; i < nfds; i++) { | |
444 | self->pindexes[i].self = self; | |
445 | self->pindexes[i].index = i; | |
446 | } | |
447 | ||
448 | return 0; | |
449 | fail: | |
450 | sndio_fini(self); | |
451 | return -1; | |
452 | } | |
453 | ||
454 | static void sndio_enable(SndioVoice *self, bool enable) | |
455 | { | |
456 | if (enable) { | |
457 | sio_start(self->hdl); | |
458 | self->enabled = true; | |
459 | sndio_poll_wait(self); | |
460 | } else { | |
461 | self->enabled = false; | |
462 | sndio_poll_clear(self); | |
463 | sio_stop(self->hdl); | |
464 | } | |
465 | } | |
466 | ||
467 | static void sndio_enable_out(HWVoiceOut *hw, bool enable) | |
468 | { | |
469 | SndioVoice *self = (SndioVoice *) hw; | |
470 | ||
471 | sndio_enable(self, enable); | |
472 | } | |
473 | ||
474 | static void sndio_enable_in(HWVoiceIn *hw, bool enable) | |
475 | { | |
476 | SndioVoice *self = (SndioVoice *) hw; | |
477 | ||
478 | sndio_enable(self, enable); | |
479 | } | |
480 | ||
481 | static int sndio_init_out(HWVoiceOut *hw, struct audsettings *as, void *opaque) | |
482 | { | |
483 | SndioVoice *self = (SndioVoice *) hw; | |
484 | ||
485 | if (sndio_init(self, as, SIO_PLAY, opaque) == -1) { | |
486 | return -1; | |
487 | } | |
488 | ||
489 | audio_pcm_init_info(&hw->info, as); | |
490 | hw->samples = self->par.round; | |
491 | return 0; | |
492 | } | |
493 | ||
494 | static int sndio_init_in(HWVoiceIn *hw, struct audsettings *as, void *opaque) | |
495 | { | |
496 | SndioVoice *self = (SndioVoice *) hw; | |
497 | ||
498 | if (sndio_init(self, as, SIO_REC, opaque) == -1) { | |
499 | return -1; | |
500 | } | |
501 | ||
502 | audio_pcm_init_info(&hw->info, as); | |
503 | hw->samples = self->par.round; | |
504 | return 0; | |
505 | } | |
506 | ||
507 | static void sndio_fini_out(HWVoiceOut *hw) | |
508 | { | |
509 | SndioVoice *self = (SndioVoice *) hw; | |
510 | ||
511 | sndio_fini(self); | |
512 | } | |
513 | ||
514 | static void sndio_fini_in(HWVoiceIn *hw) | |
515 | { | |
516 | SndioVoice *self = (SndioVoice *) hw; | |
517 | ||
518 | sndio_fini(self); | |
519 | } | |
520 | ||
521 | static void *sndio_audio_init(Audiodev *dev) | |
522 | { | |
523 | assert(dev->driver == AUDIODEV_DRIVER_SNDIO); | |
524 | return dev; | |
525 | } | |
526 | ||
527 | static void sndio_audio_fini(void *opaque) | |
528 | { | |
529 | } | |
530 | ||
531 | static struct audio_pcm_ops sndio_pcm_ops = { | |
532 | .init_out = sndio_init_out, | |
533 | .fini_out = sndio_fini_out, | |
534 | .enable_out = sndio_enable_out, | |
535 | .write = audio_generic_write, | |
536 | .buffer_get_free = sndio_buffer_get_free, | |
537 | .get_buffer_out = sndio_get_buffer_out, | |
538 | .put_buffer_out = sndio_put_buffer_out, | |
539 | .init_in = sndio_init_in, | |
540 | .fini_in = sndio_fini_in, | |
541 | .read = audio_generic_read, | |
542 | .enable_in = sndio_enable_in, | |
543 | .get_buffer_in = sndio_get_buffer_in, | |
544 | .put_buffer_in = sndio_put_buffer_in, | |
545 | }; | |
546 | ||
547 | static struct audio_driver sndio_audio_driver = { | |
548 | .name = "sndio", | |
549 | .descr = "sndio https://sndio.org", | |
550 | .init = sndio_audio_init, | |
551 | .fini = sndio_audio_fini, | |
552 | .pcm_ops = &sndio_pcm_ops, | |
553 | .can_be_default = 1, | |
554 | .max_voices_out = INT_MAX, | |
555 | .max_voices_in = INT_MAX, | |
556 | .voice_size_out = sizeof(SndioVoice), | |
557 | .voice_size_in = sizeof(SndioVoice) | |
558 | }; | |
559 | ||
560 | static void register_audio_sndio(void) | |
561 | { | |
562 | audio_driver_register(&sndio_audio_driver); | |
563 | } | |
564 | ||
565 | type_init(register_audio_sndio); |