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