xref: /openbmc/qemu/audio/sndioaudio.c (revision 69a80279)
1663df1ccSAlexandre Ratchov /*
2663df1ccSAlexandre Ratchov  * SPDX-License-Identifier: ISC
3663df1ccSAlexandre Ratchov  *
4663df1ccSAlexandre Ratchov  * Copyright (c) 2019 Alexandre Ratchov <alex@caoua.org>
5663df1ccSAlexandre Ratchov  */
6663df1ccSAlexandre Ratchov 
7663df1ccSAlexandre Ratchov /*
8663df1ccSAlexandre Ratchov  * TODO :
9663df1ccSAlexandre Ratchov  *
10663df1ccSAlexandre Ratchov  * Use a single device and open it in full-duplex rather than
11663df1ccSAlexandre Ratchov  * opening it twice (once for playback once for recording).
12663df1ccSAlexandre Ratchov  *
13663df1ccSAlexandre Ratchov  * This is the only way to ensure that playback doesn't drift with respect
14663df1ccSAlexandre Ratchov  * to recording, which is what guest systems expect.
15663df1ccSAlexandre Ratchov  */
16663df1ccSAlexandre Ratchov 
172ca10faeSMarkus Armbruster #include "qemu/osdep.h"
18663df1ccSAlexandre Ratchov #include <poll.h>
19663df1ccSAlexandre Ratchov #include <sndio.h>
20663df1ccSAlexandre Ratchov #include "qemu/main-loop.h"
21663df1ccSAlexandre Ratchov #include "audio.h"
22663df1ccSAlexandre Ratchov #include "trace.h"
23663df1ccSAlexandre Ratchov 
24663df1ccSAlexandre Ratchov #define AUDIO_CAP "sndio"
25663df1ccSAlexandre Ratchov #include "audio_int.h"
26663df1ccSAlexandre Ratchov 
27663df1ccSAlexandre Ratchov /* default latency in microseconds if no option is set */
28663df1ccSAlexandre Ratchov #define SNDIO_LATENCY_US   50000
29663df1ccSAlexandre Ratchov 
30663df1ccSAlexandre Ratchov typedef struct SndioVoice {
31663df1ccSAlexandre Ratchov     union {
32663df1ccSAlexandre Ratchov         HWVoiceOut out;
33663df1ccSAlexandre Ratchov         HWVoiceIn in;
34663df1ccSAlexandre Ratchov     } hw;
35663df1ccSAlexandre Ratchov     struct sio_par par;
36663df1ccSAlexandre Ratchov     struct sio_hdl *hdl;
37663df1ccSAlexandre Ratchov     struct pollfd *pfds;
38663df1ccSAlexandre Ratchov     struct pollindex {
39663df1ccSAlexandre Ratchov         struct SndioVoice *self;
40663df1ccSAlexandre Ratchov         int index;
41663df1ccSAlexandre Ratchov     } *pindexes;
42663df1ccSAlexandre Ratchov     unsigned char *buf;
43663df1ccSAlexandre Ratchov     size_t buf_size;
44663df1ccSAlexandre Ratchov     size_t sndio_pos;
45663df1ccSAlexandre Ratchov     size_t qemu_pos;
46663df1ccSAlexandre Ratchov     unsigned int mode;
47663df1ccSAlexandre Ratchov     unsigned int nfds;
48663df1ccSAlexandre Ratchov     bool enabled;
49663df1ccSAlexandre Ratchov } SndioVoice;
50663df1ccSAlexandre Ratchov 
51663df1ccSAlexandre Ratchov typedef struct SndioConf {
52663df1ccSAlexandre Ratchov     const char *devname;
53663df1ccSAlexandre Ratchov     unsigned int latency;
54663df1ccSAlexandre Ratchov } SndioConf;
55663df1ccSAlexandre Ratchov 
56663df1ccSAlexandre Ratchov /* needed for forward reference */
57663df1ccSAlexandre Ratchov static void sndio_poll_in(void *arg);
58663df1ccSAlexandre Ratchov static void sndio_poll_out(void *arg);
59663df1ccSAlexandre Ratchov 
60663df1ccSAlexandre Ratchov /*
61663df1ccSAlexandre Ratchov  * stop polling descriptors
62663df1ccSAlexandre Ratchov  */
sndio_poll_clear(SndioVoice * self)63663df1ccSAlexandre Ratchov static void sndio_poll_clear(SndioVoice *self)
64663df1ccSAlexandre Ratchov {
65663df1ccSAlexandre Ratchov     struct pollfd *pfd;
66663df1ccSAlexandre Ratchov     int i;
67663df1ccSAlexandre Ratchov 
68663df1ccSAlexandre Ratchov     for (i = 0; i < self->nfds; i++) {
69663df1ccSAlexandre Ratchov         pfd = &self->pfds[i];
70663df1ccSAlexandre Ratchov         qemu_set_fd_handler(pfd->fd, NULL, NULL, NULL);
71663df1ccSAlexandre Ratchov     }
72663df1ccSAlexandre Ratchov 
73663df1ccSAlexandre Ratchov     self->nfds = 0;
74663df1ccSAlexandre Ratchov }
75663df1ccSAlexandre Ratchov 
76663df1ccSAlexandre Ratchov /*
77663df1ccSAlexandre Ratchov  * write data to the device until it blocks or
78663df1ccSAlexandre Ratchov  * all of our buffered data is written
79663df1ccSAlexandre Ratchov  */
sndio_write(SndioVoice * self)80663df1ccSAlexandre Ratchov static void sndio_write(SndioVoice *self)
81663df1ccSAlexandre Ratchov {
82663df1ccSAlexandre Ratchov     size_t todo, n;
83663df1ccSAlexandre Ratchov 
84663df1ccSAlexandre Ratchov     todo = self->qemu_pos - self->sndio_pos;
85663df1ccSAlexandre Ratchov 
86663df1ccSAlexandre Ratchov     /*
87663df1ccSAlexandre Ratchov      * transfer data to device, until it blocks
88663df1ccSAlexandre Ratchov      */
89663df1ccSAlexandre Ratchov     while (todo > 0) {
90663df1ccSAlexandre Ratchov         n = sio_write(self->hdl, self->buf + self->sndio_pos, todo);
91663df1ccSAlexandre Ratchov         if (n == 0) {
92663df1ccSAlexandre Ratchov             break;
93663df1ccSAlexandre Ratchov         }
94663df1ccSAlexandre Ratchov         self->sndio_pos += n;
95663df1ccSAlexandre Ratchov         todo -= n;
96663df1ccSAlexandre Ratchov     }
97663df1ccSAlexandre Ratchov 
98663df1ccSAlexandre Ratchov     if (self->sndio_pos == self->buf_size) {
99663df1ccSAlexandre Ratchov         /*
100663df1ccSAlexandre Ratchov          * we complete the block
101663df1ccSAlexandre Ratchov          */
102663df1ccSAlexandre Ratchov         self->sndio_pos = 0;
103663df1ccSAlexandre Ratchov         self->qemu_pos = 0;
104663df1ccSAlexandre Ratchov     }
105663df1ccSAlexandre Ratchov }
106663df1ccSAlexandre Ratchov 
107663df1ccSAlexandre Ratchov /*
108663df1ccSAlexandre Ratchov  * read data from the device until it blocks or
109663df1ccSAlexandre Ratchov  * there no room any longer
110663df1ccSAlexandre Ratchov  */
sndio_read(SndioVoice * self)111663df1ccSAlexandre Ratchov static void sndio_read(SndioVoice *self)
112663df1ccSAlexandre Ratchov {
113663df1ccSAlexandre Ratchov     size_t todo, n;
114663df1ccSAlexandre Ratchov 
115663df1ccSAlexandre Ratchov     todo = self->buf_size - self->sndio_pos;
116663df1ccSAlexandre Ratchov 
117663df1ccSAlexandre Ratchov     /*
118663df1ccSAlexandre Ratchov      * transfer data from the device, until it blocks
119663df1ccSAlexandre Ratchov      */
120663df1ccSAlexandre Ratchov     while (todo > 0) {
121663df1ccSAlexandre Ratchov         n = sio_read(self->hdl, self->buf + self->sndio_pos, todo);
122663df1ccSAlexandre Ratchov         if (n == 0) {
123663df1ccSAlexandre Ratchov             break;
124663df1ccSAlexandre Ratchov         }
125663df1ccSAlexandre Ratchov         self->sndio_pos += n;
126663df1ccSAlexandre Ratchov         todo -= n;
127663df1ccSAlexandre Ratchov     }
128663df1ccSAlexandre Ratchov }
129663df1ccSAlexandre Ratchov 
130663df1ccSAlexandre Ratchov /*
131663df1ccSAlexandre Ratchov  * Set handlers for all descriptors libsndio needs to
132663df1ccSAlexandre Ratchov  * poll
133663df1ccSAlexandre Ratchov  */
sndio_poll_wait(SndioVoice * self)134663df1ccSAlexandre Ratchov static void sndio_poll_wait(SndioVoice *self)
135663df1ccSAlexandre Ratchov {
136663df1ccSAlexandre Ratchov     struct pollfd *pfd;
137663df1ccSAlexandre Ratchov     int events, i;
138663df1ccSAlexandre Ratchov 
139663df1ccSAlexandre Ratchov     events = 0;
140663df1ccSAlexandre Ratchov     if (self->mode == SIO_PLAY) {
141663df1ccSAlexandre Ratchov         if (self->sndio_pos < self->qemu_pos) {
142663df1ccSAlexandre Ratchov             events |= POLLOUT;
143663df1ccSAlexandre Ratchov         }
144663df1ccSAlexandre Ratchov     } else {
145663df1ccSAlexandre Ratchov         if (self->sndio_pos < self->buf_size) {
146663df1ccSAlexandre Ratchov             events |= POLLIN;
147663df1ccSAlexandre Ratchov         }
148663df1ccSAlexandre Ratchov     }
149663df1ccSAlexandre Ratchov 
150663df1ccSAlexandre Ratchov     /*
151663df1ccSAlexandre Ratchov      * fill the given array of descriptors with the events sndio
152663df1ccSAlexandre Ratchov      * wants, they are different from our 'event' variable because
153663df1ccSAlexandre Ratchov      * sndio may use descriptors internally.
154663df1ccSAlexandre Ratchov      */
155663df1ccSAlexandre Ratchov     self->nfds = sio_pollfd(self->hdl, self->pfds, events);
156663df1ccSAlexandre Ratchov 
157663df1ccSAlexandre Ratchov     for (i = 0; i < self->nfds; i++) {
158663df1ccSAlexandre Ratchov         pfd = &self->pfds[i];
159663df1ccSAlexandre Ratchov         if (pfd->fd < 0) {
160663df1ccSAlexandre Ratchov             continue;
161663df1ccSAlexandre Ratchov         }
162663df1ccSAlexandre Ratchov         qemu_set_fd_handler(pfd->fd,
163663df1ccSAlexandre Ratchov             (pfd->events & POLLIN) ? sndio_poll_in : NULL,
164663df1ccSAlexandre Ratchov             (pfd->events & POLLOUT) ? sndio_poll_out : NULL,
165663df1ccSAlexandre Ratchov             &self->pindexes[i]);
166663df1ccSAlexandre Ratchov         pfd->revents = 0;
167663df1ccSAlexandre Ratchov     }
168663df1ccSAlexandre Ratchov }
169663df1ccSAlexandre Ratchov 
170663df1ccSAlexandre Ratchov /*
171663df1ccSAlexandre Ratchov  * call-back called when one of the descriptors
172663df1ccSAlexandre Ratchov  * became readable or writable
173663df1ccSAlexandre Ratchov  */
sndio_poll_event(SndioVoice * self,int index,int event)174663df1ccSAlexandre Ratchov static void sndio_poll_event(SndioVoice *self, int index, int event)
175663df1ccSAlexandre Ratchov {
176663df1ccSAlexandre Ratchov     int revents;
177663df1ccSAlexandre Ratchov 
178663df1ccSAlexandre Ratchov     /*
179663df1ccSAlexandre Ratchov      * ensure we're not called twice this cycle
180663df1ccSAlexandre Ratchov      */
181663df1ccSAlexandre Ratchov     sndio_poll_clear(self);
182663df1ccSAlexandre Ratchov 
183663df1ccSAlexandre Ratchov     /*
184663df1ccSAlexandre Ratchov      * make self->pfds[] look as we're returning from poll syscal,
185663df1ccSAlexandre Ratchov      * this is how sio_revents expects events to be.
186663df1ccSAlexandre Ratchov      */
187663df1ccSAlexandre Ratchov     self->pfds[index].revents = event;
188663df1ccSAlexandre Ratchov 
189663df1ccSAlexandre Ratchov     /*
190663df1ccSAlexandre Ratchov      * tell sndio to handle events and return whether we can read or
191663df1ccSAlexandre Ratchov      * write without blocking.
192663df1ccSAlexandre Ratchov      */
193663df1ccSAlexandre Ratchov     revents = sio_revents(self->hdl, self->pfds);
194663df1ccSAlexandre Ratchov     if (self->mode == SIO_PLAY) {
195663df1ccSAlexandre Ratchov         if (revents & POLLOUT) {
196663df1ccSAlexandre Ratchov             sndio_write(self);
197663df1ccSAlexandre Ratchov         }
198663df1ccSAlexandre Ratchov 
199663df1ccSAlexandre Ratchov         if (self->qemu_pos < self->buf_size) {
200663df1ccSAlexandre Ratchov             audio_run(self->hw.out.s, "sndio_out");
201663df1ccSAlexandre Ratchov         }
202663df1ccSAlexandre Ratchov     } else {
203663df1ccSAlexandre Ratchov         if (revents & POLLIN) {
204663df1ccSAlexandre Ratchov             sndio_read(self);
205663df1ccSAlexandre Ratchov         }
206663df1ccSAlexandre Ratchov 
207663df1ccSAlexandre Ratchov         if (self->qemu_pos < self->sndio_pos) {
208663df1ccSAlexandre Ratchov             audio_run(self->hw.in.s, "sndio_in");
209663df1ccSAlexandre Ratchov         }
210663df1ccSAlexandre Ratchov     }
211663df1ccSAlexandre Ratchov 
212663df1ccSAlexandre Ratchov     /*
213663df1ccSAlexandre Ratchov      * audio_run() may have changed state
214663df1ccSAlexandre Ratchov      */
215663df1ccSAlexandre Ratchov     if (self->enabled) {
216663df1ccSAlexandre Ratchov         sndio_poll_wait(self);
217663df1ccSAlexandre Ratchov     }
218663df1ccSAlexandre Ratchov }
219663df1ccSAlexandre Ratchov 
220663df1ccSAlexandre Ratchov /*
221663df1ccSAlexandre Ratchov  * return the upper limit of the amount of free play buffer space
222663df1ccSAlexandre Ratchov  */
sndio_buffer_get_free(HWVoiceOut * hw)223663df1ccSAlexandre Ratchov static size_t sndio_buffer_get_free(HWVoiceOut *hw)
224663df1ccSAlexandre Ratchov {
225663df1ccSAlexandre Ratchov     SndioVoice *self = (SndioVoice *) hw;
226663df1ccSAlexandre Ratchov 
227663df1ccSAlexandre Ratchov     return self->buf_size - self->qemu_pos;
228663df1ccSAlexandre Ratchov }
229663df1ccSAlexandre Ratchov 
230663df1ccSAlexandre Ratchov /*
231663df1ccSAlexandre Ratchov  * return a buffer where data to play can be stored,
232663df1ccSAlexandre Ratchov  * its size is stored in the location pointed by the size argument.
233663df1ccSAlexandre Ratchov  */
sndio_get_buffer_out(HWVoiceOut * hw,size_t * size)234663df1ccSAlexandre Ratchov static void *sndio_get_buffer_out(HWVoiceOut *hw, size_t *size)
235663df1ccSAlexandre Ratchov {
236663df1ccSAlexandre Ratchov     SndioVoice *self = (SndioVoice *) hw;
237663df1ccSAlexandre Ratchov 
238663df1ccSAlexandre Ratchov     *size = self->buf_size - self->qemu_pos;
239663df1ccSAlexandre Ratchov     return self->buf + self->qemu_pos;
240663df1ccSAlexandre Ratchov }
241663df1ccSAlexandre Ratchov 
242663df1ccSAlexandre Ratchov /*
243663df1ccSAlexandre Ratchov  * put back to sndio back-end a buffer returned by sndio_get_buffer_out()
244663df1ccSAlexandre Ratchov  */
sndio_put_buffer_out(HWVoiceOut * hw,void * buf,size_t size)245663df1ccSAlexandre Ratchov static size_t sndio_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
246663df1ccSAlexandre Ratchov {
247663df1ccSAlexandre Ratchov     SndioVoice *self = (SndioVoice *) hw;
248663df1ccSAlexandre Ratchov 
249663df1ccSAlexandre Ratchov     self->qemu_pos += size;
250663df1ccSAlexandre Ratchov     sndio_poll_wait(self);
251663df1ccSAlexandre Ratchov     return size;
252663df1ccSAlexandre Ratchov }
253663df1ccSAlexandre Ratchov 
254663df1ccSAlexandre Ratchov /*
255663df1ccSAlexandre Ratchov  * return a buffer from where recorded data is available,
256663df1ccSAlexandre Ratchov  * its size is stored in the location pointed by the size argument.
257663df1ccSAlexandre Ratchov  * it may not exceed the initial value of "*size".
258663df1ccSAlexandre Ratchov  */
sndio_get_buffer_in(HWVoiceIn * hw,size_t * size)259663df1ccSAlexandre Ratchov static void *sndio_get_buffer_in(HWVoiceIn *hw, size_t *size)
260663df1ccSAlexandre Ratchov {
261663df1ccSAlexandre Ratchov     SndioVoice *self = (SndioVoice *) hw;
262663df1ccSAlexandre Ratchov     size_t todo, max_todo;
263663df1ccSAlexandre Ratchov 
264663df1ccSAlexandre Ratchov     /*
265663df1ccSAlexandre Ratchov      * unlike the get_buffer_out() method, get_buffer_in()
266663df1ccSAlexandre Ratchov      * must return a buffer of at most the given size, see audio.c
267663df1ccSAlexandre Ratchov      */
268663df1ccSAlexandre Ratchov     max_todo = *size;
269663df1ccSAlexandre Ratchov 
270663df1ccSAlexandre Ratchov     todo = self->sndio_pos - self->qemu_pos;
271663df1ccSAlexandre Ratchov     if (todo > max_todo) {
272663df1ccSAlexandre Ratchov         todo = max_todo;
273663df1ccSAlexandre Ratchov     }
274663df1ccSAlexandre Ratchov 
275663df1ccSAlexandre Ratchov     *size = todo;
276663df1ccSAlexandre Ratchov     return self->buf + self->qemu_pos;
277663df1ccSAlexandre Ratchov }
278663df1ccSAlexandre Ratchov 
279663df1ccSAlexandre Ratchov /*
280663df1ccSAlexandre Ratchov  * discard the given amount of recorded data
281663df1ccSAlexandre Ratchov  */
sndio_put_buffer_in(HWVoiceIn * hw,void * buf,size_t size)282663df1ccSAlexandre Ratchov static void sndio_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
283663df1ccSAlexandre Ratchov {
284663df1ccSAlexandre Ratchov     SndioVoice *self = (SndioVoice *) hw;
285663df1ccSAlexandre Ratchov 
286663df1ccSAlexandre Ratchov     self->qemu_pos += size;
287663df1ccSAlexandre Ratchov     if (self->qemu_pos == self->buf_size) {
288663df1ccSAlexandre Ratchov         self->qemu_pos = 0;
289663df1ccSAlexandre Ratchov         self->sndio_pos = 0;
290663df1ccSAlexandre Ratchov     }
291663df1ccSAlexandre Ratchov     sndio_poll_wait(self);
292663df1ccSAlexandre Ratchov }
293663df1ccSAlexandre Ratchov 
294663df1ccSAlexandre Ratchov /*
295663df1ccSAlexandre Ratchov  * call-back called when one of our descriptors becomes writable
296663df1ccSAlexandre Ratchov  */
sndio_poll_out(void * arg)297663df1ccSAlexandre Ratchov static void sndio_poll_out(void *arg)
298663df1ccSAlexandre Ratchov {
299663df1ccSAlexandre Ratchov     struct pollindex *pindex = (struct pollindex *) arg;
300663df1ccSAlexandre Ratchov 
301663df1ccSAlexandre Ratchov     sndio_poll_event(pindex->self, pindex->index, POLLOUT);
302663df1ccSAlexandre Ratchov }
303663df1ccSAlexandre Ratchov 
304663df1ccSAlexandre Ratchov /*
305663df1ccSAlexandre Ratchov  * call-back called when one of our descriptors becomes readable
306663df1ccSAlexandre Ratchov  */
sndio_poll_in(void * arg)307663df1ccSAlexandre Ratchov static void sndio_poll_in(void *arg)
308663df1ccSAlexandre Ratchov {
309663df1ccSAlexandre Ratchov     struct pollindex *pindex = (struct pollindex *) arg;
310663df1ccSAlexandre Ratchov 
311663df1ccSAlexandre Ratchov     sndio_poll_event(pindex->self, pindex->index, POLLIN);
312663df1ccSAlexandre Ratchov }
313663df1ccSAlexandre Ratchov 
sndio_fini(SndioVoice * self)314663df1ccSAlexandre Ratchov static void sndio_fini(SndioVoice *self)
315663df1ccSAlexandre Ratchov {
316663df1ccSAlexandre Ratchov     if (self->hdl) {
317663df1ccSAlexandre Ratchov         sio_close(self->hdl);
318663df1ccSAlexandre Ratchov         self->hdl = NULL;
319663df1ccSAlexandre Ratchov     }
320663df1ccSAlexandre Ratchov 
321663df1ccSAlexandre Ratchov     g_free(self->pfds);
322663df1ccSAlexandre Ratchov     g_free(self->pindexes);
323663df1ccSAlexandre Ratchov     g_free(self->buf);
324663df1ccSAlexandre Ratchov }
325663df1ccSAlexandre Ratchov 
sndio_init(SndioVoice * self,struct audsettings * as,int mode,Audiodev * dev)326663df1ccSAlexandre Ratchov static int sndio_init(SndioVoice *self,
327663df1ccSAlexandre Ratchov                       struct audsettings *as, int mode, Audiodev *dev)
328663df1ccSAlexandre Ratchov {
329663df1ccSAlexandre Ratchov     AudiodevSndioOptions *opts = &dev->u.sndio;
330663df1ccSAlexandre Ratchov     unsigned long long latency;
331663df1ccSAlexandre Ratchov     const char *dev_name;
332663df1ccSAlexandre Ratchov     struct sio_par req;
333663df1ccSAlexandre Ratchov     unsigned int nch;
334663df1ccSAlexandre Ratchov     int i, nfds;
335663df1ccSAlexandre Ratchov 
336ceb19c8fSMarkus Armbruster     dev_name = opts->dev ?: SIO_DEVANY;
337663df1ccSAlexandre Ratchov     latency = opts->has_latency ? opts->latency : SNDIO_LATENCY_US;
338663df1ccSAlexandre Ratchov 
339663df1ccSAlexandre Ratchov     /* open the device in non-blocking mode */
340663df1ccSAlexandre Ratchov     self->hdl = sio_open(dev_name, mode, 1);
341663df1ccSAlexandre Ratchov     if (self->hdl == NULL) {
342663df1ccSAlexandre Ratchov         dolog("failed to open device\n");
343663df1ccSAlexandre Ratchov         return -1;
344663df1ccSAlexandre Ratchov     }
345663df1ccSAlexandre Ratchov 
346663df1ccSAlexandre Ratchov     self->mode = mode;
347663df1ccSAlexandre Ratchov 
348663df1ccSAlexandre Ratchov     sio_initpar(&req);
349663df1ccSAlexandre Ratchov 
350663df1ccSAlexandre Ratchov     switch (as->fmt) {
351663df1ccSAlexandre Ratchov     case AUDIO_FORMAT_S8:
352663df1ccSAlexandre Ratchov         req.bits = 8;
353663df1ccSAlexandre Ratchov         req.sig = 1;
354663df1ccSAlexandre Ratchov         break;
355663df1ccSAlexandre Ratchov     case AUDIO_FORMAT_U8:
356663df1ccSAlexandre Ratchov         req.bits = 8;
357663df1ccSAlexandre Ratchov         req.sig = 0;
358663df1ccSAlexandre Ratchov         break;
359663df1ccSAlexandre Ratchov     case AUDIO_FORMAT_S16:
360663df1ccSAlexandre Ratchov         req.bits = 16;
361663df1ccSAlexandre Ratchov         req.sig = 1;
362663df1ccSAlexandre Ratchov         break;
363663df1ccSAlexandre Ratchov     case AUDIO_FORMAT_U16:
364663df1ccSAlexandre Ratchov         req.bits = 16;
365663df1ccSAlexandre Ratchov         req.sig = 0;
366663df1ccSAlexandre Ratchov         break;
367663df1ccSAlexandre Ratchov     case AUDIO_FORMAT_S32:
368663df1ccSAlexandre Ratchov         req.bits = 32;
369663df1ccSAlexandre Ratchov         req.sig = 1;
370663df1ccSAlexandre Ratchov         break;
371663df1ccSAlexandre Ratchov     case AUDIO_FORMAT_U32:
372663df1ccSAlexandre Ratchov         req.bits = 32;
373663df1ccSAlexandre Ratchov         req.sig = 0;
374663df1ccSAlexandre Ratchov         break;
375663df1ccSAlexandre Ratchov     default:
376663df1ccSAlexandre Ratchov         dolog("unknown audio sample format\n");
377663df1ccSAlexandre Ratchov         return -1;
378663df1ccSAlexandre Ratchov     }
379663df1ccSAlexandre Ratchov 
380663df1ccSAlexandre Ratchov     if (req.bits > 8) {
381663df1ccSAlexandre Ratchov         req.le = as->endianness ? 0 : 1;
382663df1ccSAlexandre Ratchov     }
383663df1ccSAlexandre Ratchov 
384663df1ccSAlexandre Ratchov     req.rate = as->freq;
385663df1ccSAlexandre Ratchov     if (mode == SIO_PLAY) {
386663df1ccSAlexandre Ratchov         req.pchan = as->nchannels;
387663df1ccSAlexandre Ratchov     } else {
388663df1ccSAlexandre Ratchov         req.rchan = as->nchannels;
389663df1ccSAlexandre Ratchov     }
390663df1ccSAlexandre Ratchov 
391663df1ccSAlexandre Ratchov     /* set on-device buffer size */
392663df1ccSAlexandre Ratchov     req.appbufsz = req.rate * latency / 1000000;
393663df1ccSAlexandre Ratchov 
394663df1ccSAlexandre Ratchov     if (!sio_setpar(self->hdl, &req)) {
395663df1ccSAlexandre Ratchov         dolog("failed set audio params\n");
396663df1ccSAlexandre Ratchov         goto fail;
397663df1ccSAlexandre Ratchov     }
398663df1ccSAlexandre Ratchov 
399663df1ccSAlexandre Ratchov     if (!sio_getpar(self->hdl, &self->par)) {
400663df1ccSAlexandre Ratchov         dolog("failed get audio params\n");
401663df1ccSAlexandre Ratchov         goto fail;
402663df1ccSAlexandre Ratchov     }
403663df1ccSAlexandre Ratchov 
404663df1ccSAlexandre Ratchov     nch = (mode == SIO_PLAY) ? self->par.pchan : self->par.rchan;
405663df1ccSAlexandre Ratchov 
406663df1ccSAlexandre Ratchov     /*
407663df1ccSAlexandre Ratchov      * With the default setup, sndio supports any combination of parameters
408663df1ccSAlexandre Ratchov      * so these checks are mostly to catch configuration errors.
409663df1ccSAlexandre Ratchov      */
410663df1ccSAlexandre Ratchov     if (self->par.bits != req.bits || self->par.bps != req.bits / 8 ||
411663df1ccSAlexandre Ratchov         self->par.sig != req.sig || (req.bits > 8 && self->par.le != req.le) ||
412663df1ccSAlexandre Ratchov         self->par.rate != as->freq || nch != as->nchannels) {
413663df1ccSAlexandre Ratchov         dolog("unsupported audio params\n");
414663df1ccSAlexandre Ratchov         goto fail;
415663df1ccSAlexandre Ratchov     }
416663df1ccSAlexandre Ratchov 
417663df1ccSAlexandre Ratchov     /*
418663df1ccSAlexandre Ratchov      * we use one block as buffer size; this is how
419663df1ccSAlexandre Ratchov      * transfers get well aligned
420663df1ccSAlexandre Ratchov      */
421663df1ccSAlexandre Ratchov     self->buf_size = self->par.round * self->par.bps * nch;
422663df1ccSAlexandre Ratchov 
423663df1ccSAlexandre Ratchov     self->buf = g_malloc(self->buf_size);
424663df1ccSAlexandre Ratchov     if (self->buf == NULL) {
425663df1ccSAlexandre Ratchov         dolog("failed to allocate audio buffer\n");
426663df1ccSAlexandre Ratchov         goto fail;
427663df1ccSAlexandre Ratchov     }
428663df1ccSAlexandre Ratchov 
429663df1ccSAlexandre Ratchov     nfds = sio_nfds(self->hdl);
430663df1ccSAlexandre Ratchov 
431663df1ccSAlexandre Ratchov     self->pfds = g_malloc_n(nfds, sizeof(struct pollfd));
432663df1ccSAlexandre Ratchov     if (self->pfds == NULL) {
433663df1ccSAlexandre Ratchov         dolog("failed to allocate pollfd structures\n");
434663df1ccSAlexandre Ratchov         goto fail;
435663df1ccSAlexandre Ratchov     }
436663df1ccSAlexandre Ratchov 
437663df1ccSAlexandre Ratchov     self->pindexes = g_malloc_n(nfds, sizeof(struct pollindex));
438663df1ccSAlexandre Ratchov     if (self->pindexes == NULL) {
439663df1ccSAlexandre Ratchov         dolog("failed to allocate pollindex structures\n");
440663df1ccSAlexandre Ratchov         goto fail;
441663df1ccSAlexandre Ratchov     }
442663df1ccSAlexandre Ratchov 
443663df1ccSAlexandre Ratchov     for (i = 0; i < nfds; i++) {
444663df1ccSAlexandre Ratchov         self->pindexes[i].self = self;
445663df1ccSAlexandre Ratchov         self->pindexes[i].index = i;
446663df1ccSAlexandre Ratchov     }
447663df1ccSAlexandre Ratchov 
448663df1ccSAlexandre Ratchov     return 0;
449663df1ccSAlexandre Ratchov fail:
450663df1ccSAlexandre Ratchov     sndio_fini(self);
451663df1ccSAlexandre Ratchov     return -1;
452663df1ccSAlexandre Ratchov }
453663df1ccSAlexandre Ratchov 
sndio_enable(SndioVoice * self,bool enable)454663df1ccSAlexandre Ratchov static void sndio_enable(SndioVoice *self, bool enable)
455663df1ccSAlexandre Ratchov {
456663df1ccSAlexandre Ratchov     if (enable) {
457663df1ccSAlexandre Ratchov         sio_start(self->hdl);
458663df1ccSAlexandre Ratchov         self->enabled = true;
459663df1ccSAlexandre Ratchov         sndio_poll_wait(self);
460663df1ccSAlexandre Ratchov     } else {
461663df1ccSAlexandre Ratchov         self->enabled = false;
462663df1ccSAlexandre Ratchov         sndio_poll_clear(self);
463663df1ccSAlexandre Ratchov         sio_stop(self->hdl);
464663df1ccSAlexandre Ratchov     }
465663df1ccSAlexandre Ratchov }
466663df1ccSAlexandre Ratchov 
sndio_enable_out(HWVoiceOut * hw,bool enable)467663df1ccSAlexandre Ratchov static void sndio_enable_out(HWVoiceOut *hw, bool enable)
468663df1ccSAlexandre Ratchov {
469663df1ccSAlexandre Ratchov     SndioVoice *self = (SndioVoice *) hw;
470663df1ccSAlexandre Ratchov 
471663df1ccSAlexandre Ratchov     sndio_enable(self, enable);
472663df1ccSAlexandre Ratchov }
473663df1ccSAlexandre Ratchov 
sndio_enable_in(HWVoiceIn * hw,bool enable)474663df1ccSAlexandre Ratchov static void sndio_enable_in(HWVoiceIn *hw, bool enable)
475663df1ccSAlexandre Ratchov {
476663df1ccSAlexandre Ratchov     SndioVoice *self = (SndioVoice *) hw;
477663df1ccSAlexandre Ratchov 
478663df1ccSAlexandre Ratchov     sndio_enable(self, enable);
479663df1ccSAlexandre Ratchov }
480663df1ccSAlexandre Ratchov 
sndio_init_out(HWVoiceOut * hw,struct audsettings * as,void * opaque)481663df1ccSAlexandre Ratchov static int sndio_init_out(HWVoiceOut *hw, struct audsettings *as, void *opaque)
482663df1ccSAlexandre Ratchov {
483663df1ccSAlexandre Ratchov     SndioVoice *self = (SndioVoice *) hw;
484663df1ccSAlexandre Ratchov 
485663df1ccSAlexandre Ratchov     if (sndio_init(self, as, SIO_PLAY, opaque) == -1) {
486663df1ccSAlexandre Ratchov         return -1;
487663df1ccSAlexandre Ratchov     }
488663df1ccSAlexandre Ratchov 
489663df1ccSAlexandre Ratchov     audio_pcm_init_info(&hw->info, as);
490663df1ccSAlexandre Ratchov     hw->samples = self->par.round;
491663df1ccSAlexandre Ratchov     return 0;
492663df1ccSAlexandre Ratchov }
493663df1ccSAlexandre Ratchov 
sndio_init_in(HWVoiceIn * hw,struct audsettings * as,void * opaque)494663df1ccSAlexandre Ratchov static int sndio_init_in(HWVoiceIn *hw, struct audsettings *as, void *opaque)
495663df1ccSAlexandre Ratchov {
496663df1ccSAlexandre Ratchov     SndioVoice *self = (SndioVoice *) hw;
497663df1ccSAlexandre Ratchov 
498663df1ccSAlexandre Ratchov     if (sndio_init(self, as, SIO_REC, opaque) == -1) {
499663df1ccSAlexandre Ratchov         return -1;
500663df1ccSAlexandre Ratchov     }
501663df1ccSAlexandre Ratchov 
502663df1ccSAlexandre Ratchov     audio_pcm_init_info(&hw->info, as);
503663df1ccSAlexandre Ratchov     hw->samples = self->par.round;
504663df1ccSAlexandre Ratchov     return 0;
505663df1ccSAlexandre Ratchov }
506663df1ccSAlexandre Ratchov 
sndio_fini_out(HWVoiceOut * hw)507663df1ccSAlexandre Ratchov static void sndio_fini_out(HWVoiceOut *hw)
508663df1ccSAlexandre Ratchov {
509663df1ccSAlexandre Ratchov     SndioVoice *self = (SndioVoice *) hw;
510663df1ccSAlexandre Ratchov 
511663df1ccSAlexandre Ratchov     sndio_fini(self);
512663df1ccSAlexandre Ratchov }
513663df1ccSAlexandre Ratchov 
sndio_fini_in(HWVoiceIn * hw)514663df1ccSAlexandre Ratchov static void sndio_fini_in(HWVoiceIn *hw)
515663df1ccSAlexandre Ratchov {
516663df1ccSAlexandre Ratchov     SndioVoice *self = (SndioVoice *) hw;
517663df1ccSAlexandre Ratchov 
518663df1ccSAlexandre Ratchov     sndio_fini(self);
519663df1ccSAlexandre Ratchov }
520663df1ccSAlexandre Ratchov 
sndio_audio_init(Audiodev * dev,Error ** errp)521*f6061733SPaolo Bonzini static void *sndio_audio_init(Audiodev *dev, Error **errp)
522663df1ccSAlexandre Ratchov {
523663df1ccSAlexandre Ratchov     assert(dev->driver == AUDIODEV_DRIVER_SNDIO);
524663df1ccSAlexandre Ratchov     return dev;
525663df1ccSAlexandre Ratchov }
526663df1ccSAlexandre Ratchov 
sndio_audio_fini(void * opaque)527663df1ccSAlexandre Ratchov static void sndio_audio_fini(void *opaque)
528663df1ccSAlexandre Ratchov {
529663df1ccSAlexandre Ratchov }
530663df1ccSAlexandre Ratchov 
531663df1ccSAlexandre Ratchov static struct audio_pcm_ops sndio_pcm_ops = {
532663df1ccSAlexandre Ratchov     .init_out        = sndio_init_out,
533663df1ccSAlexandre Ratchov     .fini_out        = sndio_fini_out,
534663df1ccSAlexandre Ratchov     .enable_out      = sndio_enable_out,
535663df1ccSAlexandre Ratchov     .write           = audio_generic_write,
536663df1ccSAlexandre Ratchov     .buffer_get_free = sndio_buffer_get_free,
537663df1ccSAlexandre Ratchov     .get_buffer_out  = sndio_get_buffer_out,
538663df1ccSAlexandre Ratchov     .put_buffer_out  = sndio_put_buffer_out,
539663df1ccSAlexandre Ratchov     .init_in         = sndio_init_in,
540663df1ccSAlexandre Ratchov     .fini_in         = sndio_fini_in,
541663df1ccSAlexandre Ratchov     .read            = audio_generic_read,
542663df1ccSAlexandre Ratchov     .enable_in       = sndio_enable_in,
543663df1ccSAlexandre Ratchov     .get_buffer_in   = sndio_get_buffer_in,
544663df1ccSAlexandre Ratchov     .put_buffer_in   = sndio_put_buffer_in,
545663df1ccSAlexandre Ratchov };
546663df1ccSAlexandre Ratchov 
547663df1ccSAlexandre Ratchov static struct audio_driver sndio_audio_driver = {
548663df1ccSAlexandre Ratchov     .name           = "sndio",
549663df1ccSAlexandre Ratchov     .descr          = "sndio https://sndio.org",
550663df1ccSAlexandre Ratchov     .init           = sndio_audio_init,
551663df1ccSAlexandre Ratchov     .fini           = sndio_audio_fini,
552663df1ccSAlexandre Ratchov     .pcm_ops        = &sndio_pcm_ops,
553663df1ccSAlexandre Ratchov     .max_voices_out = INT_MAX,
554663df1ccSAlexandre Ratchov     .max_voices_in  = INT_MAX,
555663df1ccSAlexandre Ratchov     .voice_size_out = sizeof(SndioVoice),
556663df1ccSAlexandre Ratchov     .voice_size_in  = sizeof(SndioVoice)
557663df1ccSAlexandre Ratchov };
558663df1ccSAlexandre Ratchov 
register_audio_sndio(void)559663df1ccSAlexandre Ratchov static void register_audio_sndio(void)
560663df1ccSAlexandre Ratchov {
561663df1ccSAlexandre Ratchov     audio_driver_register(&sndio_audio_driver);
562663df1ccSAlexandre Ratchov }
563663df1ccSAlexandre Ratchov 
564663df1ccSAlexandre Ratchov type_init(register_audio_sndio);
565