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