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 17*2ca10faeSMarkus 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 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 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 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 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 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 521663df1ccSAlexandre Ratchov static void *sndio_audio_init(Audiodev *dev) 522663df1ccSAlexandre Ratchov { 523663df1ccSAlexandre Ratchov assert(dev->driver == AUDIODEV_DRIVER_SNDIO); 524663df1ccSAlexandre Ratchov return dev; 525663df1ccSAlexandre Ratchov } 526663df1ccSAlexandre Ratchov 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 .can_be_default = 1, 554663df1ccSAlexandre Ratchov .max_voices_out = INT_MAX, 555663df1ccSAlexandre Ratchov .max_voices_in = INT_MAX, 556663df1ccSAlexandre Ratchov .voice_size_out = sizeof(SndioVoice), 557663df1ccSAlexandre Ratchov .voice_size_in = sizeof(SndioVoice) 558663df1ccSAlexandre Ratchov }; 559663df1ccSAlexandre Ratchov 560663df1ccSAlexandre Ratchov static void register_audio_sndio(void) 561663df1ccSAlexandre Ratchov { 562663df1ccSAlexandre Ratchov audio_driver_register(&sndio_audio_driver); 563663df1ccSAlexandre Ratchov } 564663df1ccSAlexandre Ratchov 565663df1ccSAlexandre Ratchov type_init(register_audio_sndio); 566