1*82bfc72bSratchov /* $OpenBSD: sio_sun.c,v 1.2 2011/04/16 10:52:22 ratchov Exp $ */ 2b041ccb2Sratchov /* 3b041ccb2Sratchov * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> 4b041ccb2Sratchov * 5b041ccb2Sratchov * Permission to use, copy, modify, and distribute this software for any 6b041ccb2Sratchov * purpose with or without fee is hereby granted, provided that the above 7b041ccb2Sratchov * copyright notice and this permission notice appear in all copies. 8b041ccb2Sratchov * 9b041ccb2Sratchov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10b041ccb2Sratchov * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11b041ccb2Sratchov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12b041ccb2Sratchov * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13b041ccb2Sratchov * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14b041ccb2Sratchov * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15b041ccb2Sratchov * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16b041ccb2Sratchov */ 17b041ccb2Sratchov /* 18b041ccb2Sratchov * TODO: 19b041ccb2Sratchov * 20b041ccb2Sratchov * remove filling code from sio_sun_write() and create sio_sun_fill() 21b041ccb2Sratchov * 22b041ccb2Sratchov * allow block size to be set 23b041ccb2Sratchov * 24b041ccb2Sratchov * call hdl->cb_pos() from sio_sun_read() and sio_sun_write(), or better: 25b041ccb2Sratchov * implement generic blocking sio_read() and sio_write() with poll(2) 26b041ccb2Sratchov * and use non-blocking sio_ops only 27b041ccb2Sratchov */ 28b041ccb2Sratchov 29b041ccb2Sratchov #include <sys/types.h> 30b041ccb2Sratchov #include <sys/ioctl.h> 31b041ccb2Sratchov #include <sys/audioio.h> 32b041ccb2Sratchov #include <sys/stat.h> 33b041ccb2Sratchov 34b041ccb2Sratchov #include <errno.h> 35b041ccb2Sratchov #include <fcntl.h> 36b041ccb2Sratchov #include <limits.h> 37b041ccb2Sratchov #include <poll.h> 38b041ccb2Sratchov #include <stdio.h> 39b041ccb2Sratchov #include <stdlib.h> 40b041ccb2Sratchov #include <string.h> 41b041ccb2Sratchov #include <unistd.h> 42b041ccb2Sratchov 43*82bfc72bSratchov #include "debug.h" 44b041ccb2Sratchov #include "sio_priv.h" 45b041ccb2Sratchov 46b041ccb2Sratchov struct sio_sun_hdl { 47b041ccb2Sratchov struct sio_hdl sio; 48b041ccb2Sratchov int fd; 49b041ccb2Sratchov int filling; 50b041ccb2Sratchov unsigned ibpf, obpf; /* bytes per frame */ 51b041ccb2Sratchov unsigned ibytes, obytes; /* bytes the hw transfered */ 52b041ccb2Sratchov unsigned ierr, oerr; /* frames the hw dropped */ 53b041ccb2Sratchov int offset; /* frames play is ahead of record */ 54b041ccb2Sratchov int idelta, odelta; /* position reported to client */ 55b041ccb2Sratchov int mix_fd, mix_index; /* /dev/mixerN stuff */ 56b041ccb2Sratchov }; 57b041ccb2Sratchov 58b041ccb2Sratchov static void sio_sun_close(struct sio_hdl *); 59b041ccb2Sratchov static int sio_sun_start(struct sio_hdl *); 60b041ccb2Sratchov static int sio_sun_stop(struct sio_hdl *); 61b041ccb2Sratchov static int sio_sun_setpar(struct sio_hdl *, struct sio_par *); 62b041ccb2Sratchov static int sio_sun_getpar(struct sio_hdl *, struct sio_par *); 63b041ccb2Sratchov static int sio_sun_getcap(struct sio_hdl *, struct sio_cap *); 64b041ccb2Sratchov static size_t sio_sun_read(struct sio_hdl *, void *, size_t); 65b041ccb2Sratchov static size_t sio_sun_write(struct sio_hdl *, const void *, size_t); 66b041ccb2Sratchov static int sio_sun_nfds(struct sio_hdl *); 67b041ccb2Sratchov static int sio_sun_pollfd(struct sio_hdl *, struct pollfd *, int); 68b041ccb2Sratchov static int sio_sun_revents(struct sio_hdl *, struct pollfd *); 69b041ccb2Sratchov 70b041ccb2Sratchov static struct sio_ops sio_sun_ops = { 71b041ccb2Sratchov sio_sun_close, 72b041ccb2Sratchov sio_sun_setpar, 73b041ccb2Sratchov sio_sun_getpar, 74b041ccb2Sratchov sio_sun_getcap, 75b041ccb2Sratchov sio_sun_write, 76b041ccb2Sratchov sio_sun_read, 77b041ccb2Sratchov sio_sun_start, 78b041ccb2Sratchov sio_sun_stop, 79b041ccb2Sratchov sio_sun_nfds, 80b041ccb2Sratchov sio_sun_pollfd, 81b041ccb2Sratchov sio_sun_revents, 82b041ccb2Sratchov NULL, /* setvol */ 83b041ccb2Sratchov NULL, /* getvol */ 84b041ccb2Sratchov }; 85b041ccb2Sratchov 86b041ccb2Sratchov /* 87b041ccb2Sratchov * convert sun encoding to sio_par encoding 88b041ccb2Sratchov */ 89b041ccb2Sratchov static int 90b041ccb2Sratchov sio_sun_infotoenc(struct sio_sun_hdl *hdl, struct audio_prinfo *ai, struct sio_par *par) 91b041ccb2Sratchov { 92b041ccb2Sratchov par->msb = ai->msb; 93b041ccb2Sratchov par->bits = ai->precision; 94b041ccb2Sratchov par->bps = ai->bps; 95b041ccb2Sratchov switch (ai->encoding) { 96b041ccb2Sratchov case AUDIO_ENCODING_SLINEAR_LE: 97b041ccb2Sratchov par->le = 1; 98b041ccb2Sratchov par->sig = 1; 99b041ccb2Sratchov break; 100b041ccb2Sratchov case AUDIO_ENCODING_SLINEAR_BE: 101b041ccb2Sratchov par->le = 0; 102b041ccb2Sratchov par->sig = 1; 103b041ccb2Sratchov break; 104b041ccb2Sratchov case AUDIO_ENCODING_ULINEAR_LE: 105b041ccb2Sratchov par->le = 1; 106b041ccb2Sratchov par->sig = 0; 107b041ccb2Sratchov break; 108b041ccb2Sratchov case AUDIO_ENCODING_ULINEAR_BE: 109b041ccb2Sratchov par->le = 0; 110b041ccb2Sratchov par->sig = 0; 111b041ccb2Sratchov break; 112b041ccb2Sratchov case AUDIO_ENCODING_SLINEAR: 113b041ccb2Sratchov par->le = SIO_LE_NATIVE; 114b041ccb2Sratchov par->sig = 1; 115b041ccb2Sratchov break; 116b041ccb2Sratchov case AUDIO_ENCODING_ULINEAR: 117b041ccb2Sratchov par->le = SIO_LE_NATIVE; 118b041ccb2Sratchov par->sig = 0; 119b041ccb2Sratchov break; 120b041ccb2Sratchov default: 121b041ccb2Sratchov DPRINTF("sio_sun_infotoenc: unsupported encoding\n"); 122b041ccb2Sratchov hdl->sio.eof = 1; 123b041ccb2Sratchov return 0; 124b041ccb2Sratchov } 125b041ccb2Sratchov return 1; 126b041ccb2Sratchov } 127b041ccb2Sratchov 128b041ccb2Sratchov /* 129b041ccb2Sratchov * convert sio_par encoding to sun encoding 130b041ccb2Sratchov */ 131b041ccb2Sratchov static void 132b041ccb2Sratchov sio_sun_enctoinfo(struct sio_sun_hdl *hdl, unsigned *renc, struct sio_par *par) 133b041ccb2Sratchov { 134b041ccb2Sratchov if (par->le == ~0U && par->sig == ~0U) { 135b041ccb2Sratchov *renc = ~0U; 136b041ccb2Sratchov } else if (par->le == ~0U || par->sig == ~0U) { 137b041ccb2Sratchov *renc = AUDIO_ENCODING_SLINEAR; 138b041ccb2Sratchov } else if (par->le && par->sig) { 139b041ccb2Sratchov *renc = AUDIO_ENCODING_SLINEAR_LE; 140b041ccb2Sratchov } else if (!par->le && par->sig) { 141b041ccb2Sratchov *renc = AUDIO_ENCODING_SLINEAR_BE; 142b041ccb2Sratchov } else if (par->le && !par->sig) { 143b041ccb2Sratchov *renc = AUDIO_ENCODING_ULINEAR_LE; 144b041ccb2Sratchov } else { 145b041ccb2Sratchov *renc = AUDIO_ENCODING_ULINEAR_BE; 146b041ccb2Sratchov } 147b041ccb2Sratchov } 148b041ccb2Sratchov 149b041ccb2Sratchov /* 150b041ccb2Sratchov * try to set the device to the given parameters and check that the 151b041ccb2Sratchov * device can use them; return 1 on success, 0 on failure or error 152b041ccb2Sratchov */ 153b041ccb2Sratchov static int 154b041ccb2Sratchov sio_sun_tryinfo(struct sio_sun_hdl *hdl, struct sio_enc *enc, 155b041ccb2Sratchov unsigned pchan, unsigned rchan, unsigned rate) 156b041ccb2Sratchov { 157b041ccb2Sratchov struct audio_info aui; 158b041ccb2Sratchov struct audio_prinfo *pr; 159b041ccb2Sratchov 160b041ccb2Sratchov pr = (hdl->sio.mode & SIO_PLAY) ? &aui.play : &aui.record; 161b041ccb2Sratchov 162b041ccb2Sratchov AUDIO_INITINFO(&aui); 163b041ccb2Sratchov if (enc) { 164b041ccb2Sratchov if (enc->le && enc->sig) { 165b041ccb2Sratchov pr->encoding = AUDIO_ENCODING_SLINEAR_LE; 166b041ccb2Sratchov } else if (!enc->le && enc->sig) { 167b041ccb2Sratchov pr->encoding = AUDIO_ENCODING_SLINEAR_BE; 168b041ccb2Sratchov } else if (enc->le && !enc->sig) { 169b041ccb2Sratchov pr->encoding = AUDIO_ENCODING_ULINEAR_LE; 170b041ccb2Sratchov } else { 171b041ccb2Sratchov pr->encoding = AUDIO_ENCODING_ULINEAR_BE; 172b041ccb2Sratchov } 173b041ccb2Sratchov pr->precision = enc->bits; 174b041ccb2Sratchov } 175b041ccb2Sratchov if (rate) 176b041ccb2Sratchov pr->sample_rate = rate; 177b041ccb2Sratchov if ((hdl->sio.mode & (SIO_PLAY | SIO_REC)) == (SIO_PLAY | SIO_REC)) 178b041ccb2Sratchov aui.record = aui.play; 179b041ccb2Sratchov if (pchan && (hdl->sio.mode & SIO_PLAY)) 180b041ccb2Sratchov aui.play.channels = pchan; 181b041ccb2Sratchov if (rchan && (hdl->sio.mode & SIO_REC)) 182b041ccb2Sratchov aui.record.channels = rchan; 183b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { 184b041ccb2Sratchov if (errno == EINVAL) 185b041ccb2Sratchov return 0; 186b041ccb2Sratchov DPERROR("sio_sun_tryinfo: setinfo"); 187b041ccb2Sratchov hdl->sio.eof = 1; 188b041ccb2Sratchov return 0; 189b041ccb2Sratchov } 190b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { 191b041ccb2Sratchov DPERROR("sio_sun_tryinfo: getinfo"); 192b041ccb2Sratchov hdl->sio.eof = 1; 193b041ccb2Sratchov return 0; 194b041ccb2Sratchov } 195b041ccb2Sratchov if (pchan && aui.play.channels != pchan) 196b041ccb2Sratchov return 0; 197b041ccb2Sratchov if (rchan && aui.record.channels != rchan) 198b041ccb2Sratchov return 0; 199b041ccb2Sratchov if (rate) { 200b041ccb2Sratchov if ((hdl->sio.mode & SIO_PLAY) && 201b041ccb2Sratchov (aui.play.sample_rate != rate)) 202b041ccb2Sratchov return 0; 203b041ccb2Sratchov if ((hdl->sio.mode & SIO_REC) && 204b041ccb2Sratchov (aui.record.sample_rate != rate)) 205b041ccb2Sratchov return 0; 206b041ccb2Sratchov } 207b041ccb2Sratchov return 1; 208b041ccb2Sratchov } 209b041ccb2Sratchov 210b041ccb2Sratchov /* 211b041ccb2Sratchov * guess device capabilities 212b041ccb2Sratchov */ 213b041ccb2Sratchov static int 214b041ccb2Sratchov sio_sun_getcap(struct sio_hdl *sh, struct sio_cap *cap) 215b041ccb2Sratchov { 216b041ccb2Sratchov #define NCHANS (sizeof(chans) / sizeof(chans[0])) 217b041ccb2Sratchov #define NRATES (sizeof(rates) / sizeof(rates[0])) 218b041ccb2Sratchov static unsigned chans[] = { 219b041ccb2Sratchov 1, 2, 4, 6, 8, 10, 12 220b041ccb2Sratchov }; 221b041ccb2Sratchov static unsigned rates[] = { 222b041ccb2Sratchov 8000, 11025, 12000, 16000, 22050, 24000, 223b041ccb2Sratchov 32000, 44100, 48000, 64000, 88200, 96000 224b041ccb2Sratchov }; 225b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 226b041ccb2Sratchov struct sio_par savepar; 227b041ccb2Sratchov struct audio_encoding ae; 228b041ccb2Sratchov unsigned nenc = 0, nconf = 0; 229b041ccb2Sratchov unsigned enc_map = 0, rchan_map = 0, pchan_map = 0, rate_map; 230b041ccb2Sratchov unsigned i, j, conf; 231b041ccb2Sratchov 232b041ccb2Sratchov if (!sio_sun_getpar(&hdl->sio, &savepar)) 233b041ccb2Sratchov return 0; 234b041ccb2Sratchov 235b041ccb2Sratchov /* 236b041ccb2Sratchov * fill encoding list 237b041ccb2Sratchov */ 238b041ccb2Sratchov for (ae.index = 0; nenc < SIO_NENC; ae.index++) { 239b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_GETENC, &ae) < 0) { 240b041ccb2Sratchov if (errno == EINVAL) 241b041ccb2Sratchov break; 242b041ccb2Sratchov DPERROR("sio_sun_getcap: getenc"); 243b041ccb2Sratchov hdl->sio.eof = 1; 244b041ccb2Sratchov return 0; 245b041ccb2Sratchov } 246b041ccb2Sratchov if (ae.flags & AUDIO_ENCODINGFLAG_EMULATED) 247b041ccb2Sratchov continue; 248b041ccb2Sratchov if (ae.encoding == AUDIO_ENCODING_SLINEAR_LE) { 249b041ccb2Sratchov cap->enc[nenc].le = 1; 250b041ccb2Sratchov cap->enc[nenc].sig = 1; 251b041ccb2Sratchov } else if (ae.encoding == AUDIO_ENCODING_SLINEAR_BE) { 252b041ccb2Sratchov cap->enc[nenc].le = 0; 253b041ccb2Sratchov cap->enc[nenc].sig = 1; 254b041ccb2Sratchov } else if (ae.encoding == AUDIO_ENCODING_ULINEAR_LE) { 255b041ccb2Sratchov cap->enc[nenc].le = 1; 256b041ccb2Sratchov cap->enc[nenc].sig = 0; 257b041ccb2Sratchov } else if (ae.encoding == AUDIO_ENCODING_ULINEAR_BE) { 258b041ccb2Sratchov cap->enc[nenc].le = 0; 259b041ccb2Sratchov cap->enc[nenc].sig = 0; 260b041ccb2Sratchov } else if (ae.encoding == AUDIO_ENCODING_SLINEAR) { 261b041ccb2Sratchov cap->enc[nenc].le = SIO_LE_NATIVE; 262b041ccb2Sratchov cap->enc[nenc].sig = 1; 263b041ccb2Sratchov } else if (ae.encoding == AUDIO_ENCODING_ULINEAR) { 264b041ccb2Sratchov cap->enc[nenc].le = SIO_LE_NATIVE; 265b041ccb2Sratchov cap->enc[nenc].sig = 0; 266b041ccb2Sratchov } else { 267b041ccb2Sratchov /* unsipported encoding */ 268b041ccb2Sratchov continue; 269b041ccb2Sratchov } 270b041ccb2Sratchov cap->enc[nenc].bits = ae.precision; 271b041ccb2Sratchov cap->enc[nenc].bps = ae.bps; 272b041ccb2Sratchov cap->enc[nenc].msb = ae.msb; 273b041ccb2Sratchov enc_map |= (1 << nenc); 274b041ccb2Sratchov nenc++; 275b041ccb2Sratchov } 276b041ccb2Sratchov 277b041ccb2Sratchov /* 278b041ccb2Sratchov * fill channels 279b041ccb2Sratchov * 280b041ccb2Sratchov * for now we're lucky: all kernel devices assume that the 281b041ccb2Sratchov * number of channels and the encoding are independent so we can 282b041ccb2Sratchov * use the current encoding and try various channels. 283b041ccb2Sratchov */ 284b041ccb2Sratchov if (hdl->sio.mode & SIO_PLAY) { 285b041ccb2Sratchov memcpy(&cap->pchan, chans, NCHANS * sizeof(unsigned)); 286b041ccb2Sratchov for (i = 0; i < NCHANS; i++) { 287b041ccb2Sratchov if (sio_sun_tryinfo(hdl, NULL, chans[i], 0, 0)) 288b041ccb2Sratchov pchan_map |= (1 << i); 289b041ccb2Sratchov } 290b041ccb2Sratchov } 291b041ccb2Sratchov if (hdl->sio.mode & SIO_REC) { 292b041ccb2Sratchov memcpy(&cap->rchan, chans, NCHANS * sizeof(unsigned)); 293b041ccb2Sratchov for (i = 0; i < NCHANS; i++) { 294b041ccb2Sratchov if (sio_sun_tryinfo(hdl, NULL, 0, chans[i], 0)) 295b041ccb2Sratchov rchan_map |= (1 << i); 296b041ccb2Sratchov } 297b041ccb2Sratchov } 298b041ccb2Sratchov 299b041ccb2Sratchov /* 300b041ccb2Sratchov * fill rates 301b041ccb2Sratchov * 302b041ccb2Sratchov * rates are not independent from other parameters (eg. on 303b041ccb2Sratchov * uaudio devices), so certain rates may not be allowed with 304b041ccb2Sratchov * certain encodings. We have to check rates for all encodings 305b041ccb2Sratchov */ 306b041ccb2Sratchov memcpy(&cap->rate, rates, NRATES * sizeof(unsigned)); 307b041ccb2Sratchov for (j = 0; j < nenc; j++) { 308b041ccb2Sratchov rate_map = 0; 309b041ccb2Sratchov for (i = 0; i < NRATES; i++) { 310b041ccb2Sratchov if (sio_sun_tryinfo(hdl, &cap->enc[j], 0, 0, rates[i])) 311b041ccb2Sratchov rate_map |= (1 << i); 312b041ccb2Sratchov } 313b041ccb2Sratchov for (conf = 0; conf < nconf; conf++) { 314b041ccb2Sratchov if (cap->confs[conf].rate == rate_map) { 315b041ccb2Sratchov cap->confs[conf].enc |= (1 << j); 316b041ccb2Sratchov break; 317b041ccb2Sratchov } 318b041ccb2Sratchov } 319b041ccb2Sratchov if (conf == nconf) { 320b041ccb2Sratchov if (nconf == SIO_NCONF) 321b041ccb2Sratchov break; 322b041ccb2Sratchov cap->confs[nconf].enc = (1 << j); 323b041ccb2Sratchov cap->confs[nconf].pchan = pchan_map; 324b041ccb2Sratchov cap->confs[nconf].rchan = rchan_map; 325b041ccb2Sratchov cap->confs[nconf].rate = rate_map; 326b041ccb2Sratchov nconf++; 327b041ccb2Sratchov } 328b041ccb2Sratchov } 329b041ccb2Sratchov cap->nconf = nconf; 330b041ccb2Sratchov if (!sio_sun_setpar(&hdl->sio, &savepar)) 331b041ccb2Sratchov return 0; 332b041ccb2Sratchov return 1; 333b041ccb2Sratchov #undef NCHANS 334b041ccb2Sratchov #undef NRATES 335b041ccb2Sratchov } 336b041ccb2Sratchov 337b041ccb2Sratchov struct sio_hdl * 338b041ccb2Sratchov sio_sun_open(const char *str, unsigned mode, int nbio) 339b041ccb2Sratchov { 340b041ccb2Sratchov int fd, flags, fullduplex; 341b041ccb2Sratchov struct audio_info aui; 342b041ccb2Sratchov struct sio_sun_hdl *hdl; 343b041ccb2Sratchov struct sio_par par; 344b041ccb2Sratchov char path[PATH_MAX]; 345b041ccb2Sratchov 346b041ccb2Sratchov hdl = malloc(sizeof(struct sio_sun_hdl)); 347b041ccb2Sratchov if (hdl == NULL) 348b041ccb2Sratchov return NULL; 349b041ccb2Sratchov sio_create(&hdl->sio, &sio_sun_ops, mode, nbio); 350b041ccb2Sratchov 351b041ccb2Sratchov snprintf(path, sizeof(path), "/dev/audio%s", str); 352b041ccb2Sratchov if (mode == (SIO_PLAY | SIO_REC)) 353b041ccb2Sratchov flags = O_RDWR; 354b041ccb2Sratchov else 355b041ccb2Sratchov flags = (mode & SIO_PLAY) ? O_WRONLY : O_RDONLY; 356b041ccb2Sratchov 357b041ccb2Sratchov while ((fd = open(path, flags | O_NONBLOCK)) < 0) { 358b041ccb2Sratchov if (errno == EINTR) 359b041ccb2Sratchov continue; 360b041ccb2Sratchov DPERROR(path); 361b041ccb2Sratchov goto bad_free; 362b041ccb2Sratchov } 363b041ccb2Sratchov if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { 364b041ccb2Sratchov DPERROR("FD_CLOEXEC"); 365b041ccb2Sratchov goto bad_close; 366b041ccb2Sratchov } 367b041ccb2Sratchov 368b041ccb2Sratchov /* 369b041ccb2Sratchov * pause the device 370b041ccb2Sratchov */ 371b041ccb2Sratchov AUDIO_INITINFO(&aui); 372b041ccb2Sratchov if (mode & SIO_PLAY) 373b041ccb2Sratchov aui.play.pause = 1; 374b041ccb2Sratchov if (mode & SIO_REC) 375b041ccb2Sratchov aui.record.pause = 1; 376b041ccb2Sratchov if (ioctl(fd, AUDIO_SETINFO, &aui) < 0) { 377b041ccb2Sratchov DPERROR("sio_open_sun: setinfo"); 378b041ccb2Sratchov goto bad_close; 379b041ccb2Sratchov } 380b041ccb2Sratchov /* 381b041ccb2Sratchov * If both play and record are requested then 382b041ccb2Sratchov * set full duplex mode. 383b041ccb2Sratchov */ 384b041ccb2Sratchov if (mode == (SIO_PLAY | SIO_REC)) { 385b041ccb2Sratchov fullduplex = 1; 386b041ccb2Sratchov if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0) { 387b041ccb2Sratchov DPRINTF("sio_open_sun: %s: can't set full-duplex\n", path); 388b041ccb2Sratchov goto bad_close; 389b041ccb2Sratchov } 390b041ccb2Sratchov } 391b041ccb2Sratchov hdl->fd = fd; 392b041ccb2Sratchov 393b041ccb2Sratchov /* 394b041ccb2Sratchov * Default parameters may not be compatible with libsndio (eg. mulaw 395b041ccb2Sratchov * encodings, different playback and recording parameters, etc...), so 396b041ccb2Sratchov * set parameters to a random value. If the requested parameters are 397b041ccb2Sratchov * not supported by the device, then sio_setpar() will pick supported 398b041ccb2Sratchov * ones. 399b041ccb2Sratchov */ 400b041ccb2Sratchov sio_initpar(&par); 401b041ccb2Sratchov par.rate = 48000; 402b041ccb2Sratchov par.le = SIO_LE_NATIVE; 403b041ccb2Sratchov par.sig = 1; 404b041ccb2Sratchov par.bits = 16; 405b041ccb2Sratchov par.appbufsz = 1200; 406b041ccb2Sratchov if (!sio_setpar(&hdl->sio, &par)) 407b041ccb2Sratchov goto bad_close; 408b041ccb2Sratchov return (struct sio_hdl *)hdl; 409b041ccb2Sratchov bad_close: 410b041ccb2Sratchov while (close(fd) < 0 && errno == EINTR) 411b041ccb2Sratchov ; /* retry */ 412b041ccb2Sratchov bad_free: 413b041ccb2Sratchov free(hdl); 414b041ccb2Sratchov return NULL; 415b041ccb2Sratchov } 416b041ccb2Sratchov 417b041ccb2Sratchov static void 418b041ccb2Sratchov sio_sun_close(struct sio_hdl *sh) 419b041ccb2Sratchov { 420b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 421b041ccb2Sratchov 422b041ccb2Sratchov while (close(hdl->fd) < 0 && errno == EINTR) 423b041ccb2Sratchov ; /* retry */ 424b041ccb2Sratchov free(hdl); 425b041ccb2Sratchov } 426b041ccb2Sratchov 427b041ccb2Sratchov static int 428b041ccb2Sratchov sio_sun_start(struct sio_hdl *sh) 429b041ccb2Sratchov { 430b041ccb2Sratchov struct sio_par par; 431b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 432b041ccb2Sratchov struct audio_info aui; 433b041ccb2Sratchov 434b041ccb2Sratchov if (!sio_getpar(&hdl->sio, &par)) 435b041ccb2Sratchov return 0; 436b041ccb2Sratchov hdl->obpf = par.pchan * par.bps; 437b041ccb2Sratchov hdl->ibpf = par.rchan * par.bps; 438b041ccb2Sratchov hdl->ibytes = 0; 439b041ccb2Sratchov hdl->obytes = 0; 440b041ccb2Sratchov hdl->ierr = 0; 441b041ccb2Sratchov hdl->oerr = 0; 442b041ccb2Sratchov hdl->offset = 0; 443b041ccb2Sratchov hdl->idelta = 0; 444b041ccb2Sratchov hdl->odelta = 0; 445b041ccb2Sratchov 446b041ccb2Sratchov if (hdl->sio.mode & SIO_PLAY) { 447b041ccb2Sratchov /* 448b041ccb2Sratchov * keep the device paused and let sio_sun_write() trigger the 449b041ccb2Sratchov * start later, to avoid buffer underruns 450b041ccb2Sratchov */ 451b041ccb2Sratchov hdl->filling = 1; 452b041ccb2Sratchov } else { 453b041ccb2Sratchov /* 454b041ccb2Sratchov * no play buffers to fill, start now! 455b041ccb2Sratchov */ 456b041ccb2Sratchov AUDIO_INITINFO(&aui); 457b041ccb2Sratchov if (hdl->sio.mode & SIO_REC) 458b041ccb2Sratchov aui.record.pause = 0; 459b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { 460b041ccb2Sratchov DPERROR("sio_sun_start: setinfo"); 461b041ccb2Sratchov hdl->sio.eof = 1; 462b041ccb2Sratchov return 0; 463b041ccb2Sratchov } 464b041ccb2Sratchov hdl->filling = 0; 465b041ccb2Sratchov sio_onmove_cb(&hdl->sio, 0); 466b041ccb2Sratchov } 467b041ccb2Sratchov return 1; 468b041ccb2Sratchov } 469b041ccb2Sratchov 470b041ccb2Sratchov static int 471b041ccb2Sratchov sio_sun_stop(struct sio_hdl *sh) 472b041ccb2Sratchov { 473b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 474b041ccb2Sratchov struct audio_info aui; 475b041ccb2Sratchov int mode; 476b041ccb2Sratchov 477b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { 478b041ccb2Sratchov DPERROR("sio_sun_stop: getinfo"); 479b041ccb2Sratchov hdl->sio.eof = 1; 480b041ccb2Sratchov return 0; 481b041ccb2Sratchov } 482b041ccb2Sratchov mode = aui.mode; 483b041ccb2Sratchov 484b041ccb2Sratchov /* 485b041ccb2Sratchov * there's no way to drain the device without blocking, so just 486b041ccb2Sratchov * stop it until the kernel driver get fixed 487b041ccb2Sratchov */ 488b041ccb2Sratchov AUDIO_INITINFO(&aui); 489b041ccb2Sratchov aui.mode = 0; 490b041ccb2Sratchov if (hdl->sio.mode & SIO_PLAY) 491b041ccb2Sratchov aui.play.pause = 1; 492b041ccb2Sratchov if (hdl->sio.mode & SIO_REC) 493b041ccb2Sratchov aui.record.pause = 1; 494b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { 495b041ccb2Sratchov DPERROR("sio_sun_stop: setinfo1"); 496b041ccb2Sratchov hdl->sio.eof = 1; 497b041ccb2Sratchov return 0; 498b041ccb2Sratchov } 499b041ccb2Sratchov AUDIO_INITINFO(&aui); 500b041ccb2Sratchov aui.mode = mode; 501b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { 502b041ccb2Sratchov DPERROR("sio_sun_stop: setinfo2"); 503b041ccb2Sratchov hdl->sio.eof = 1; 504b041ccb2Sratchov return 0; 505b041ccb2Sratchov } 506b041ccb2Sratchov return 1; 507b041ccb2Sratchov } 508b041ccb2Sratchov 509b041ccb2Sratchov static int 510b041ccb2Sratchov sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par) 511b041ccb2Sratchov { 512b041ccb2Sratchov #define NRETRIES 8 513b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 514b041ccb2Sratchov struct audio_info aui; 515b041ccb2Sratchov unsigned i, infr, ibpf, onfr, obpf; 516b041ccb2Sratchov unsigned bufsz, round; 517b041ccb2Sratchov unsigned rate, req_rate, prec, enc; 518b041ccb2Sratchov 519b041ccb2Sratchov /* 520b041ccb2Sratchov * try to set parameters until the device accepts 521b041ccb2Sratchov * a common encoding and rate for play and record 522b041ccb2Sratchov */ 523b041ccb2Sratchov rate = par->rate; 524b041ccb2Sratchov prec = par->bits; 525b041ccb2Sratchov sio_sun_enctoinfo(hdl, &enc, par); 526b041ccb2Sratchov for (i = 0;; i++) { 527b041ccb2Sratchov if (i == NRETRIES) { 528b041ccb2Sratchov DPRINTF("sio_sun_setpar: couldn't set parameters\n"); 529b041ccb2Sratchov hdl->sio.eof = 1; 530b041ccb2Sratchov return 0; 531b041ccb2Sratchov } 532b041ccb2Sratchov AUDIO_INITINFO(&aui); 533b041ccb2Sratchov if (hdl->sio.mode & SIO_PLAY) { 534b041ccb2Sratchov aui.play.sample_rate = rate; 535b041ccb2Sratchov aui.play.precision = prec; 536b041ccb2Sratchov aui.play.encoding = enc; 537b041ccb2Sratchov aui.play.channels = par->pchan; 538b041ccb2Sratchov } 539b041ccb2Sratchov if (hdl->sio.mode & SIO_REC) { 540b041ccb2Sratchov aui.record.sample_rate = rate; 541b041ccb2Sratchov aui.record.precision = prec; 542b041ccb2Sratchov aui.record.encoding = enc; 543b041ccb2Sratchov aui.record.channels = par->rchan; 544b041ccb2Sratchov } 545b041ccb2Sratchov DPRINTF("sio_sun_setpar: %i: trying pars = %u/%u/%u\n", 546b041ccb2Sratchov i, rate, prec, enc); 547b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0 && errno != EINVAL) { 548b041ccb2Sratchov DPERROR("sio_sun_setpar: setinfo(pars)"); 549b041ccb2Sratchov hdl->sio.eof = 1; 550b041ccb2Sratchov return 0; 551b041ccb2Sratchov } 552b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { 553b041ccb2Sratchov DPERROR("sio_sun_setpar: getinfo(pars)"); 554b041ccb2Sratchov hdl->sio.eof = 1; 555b041ccb2Sratchov return 0; 556b041ccb2Sratchov } 557b041ccb2Sratchov enc = (hdl->sio.mode & SIO_REC) ? 558b041ccb2Sratchov aui.record.encoding : aui.play.encoding; 559b041ccb2Sratchov switch (enc) { 560b041ccb2Sratchov case AUDIO_ENCODING_SLINEAR_LE: 561b041ccb2Sratchov case AUDIO_ENCODING_SLINEAR_BE: 562b041ccb2Sratchov case AUDIO_ENCODING_ULINEAR_LE: 563b041ccb2Sratchov case AUDIO_ENCODING_ULINEAR_BE: 564b041ccb2Sratchov case AUDIO_ENCODING_SLINEAR: 565b041ccb2Sratchov case AUDIO_ENCODING_ULINEAR: 566b041ccb2Sratchov break; 567b041ccb2Sratchov default: 568b041ccb2Sratchov DPRINTF("sio_sun_setpar: couldn't set linear encoding\n"); 569b041ccb2Sratchov hdl->sio.eof = 1; 570b041ccb2Sratchov return 0; 571b041ccb2Sratchov } 572b041ccb2Sratchov if (hdl->sio.mode != (SIO_REC | SIO_PLAY)) 573b041ccb2Sratchov break; 574b041ccb2Sratchov if (aui.play.sample_rate == aui.record.sample_rate && 575b041ccb2Sratchov aui.play.precision == aui.record.precision && 576b041ccb2Sratchov aui.play.encoding == aui.record.encoding) 577b041ccb2Sratchov break; 578b041ccb2Sratchov if (i < NRETRIES / 2) { 579b041ccb2Sratchov rate = aui.play.sample_rate; 580b041ccb2Sratchov prec = aui.play.precision; 581b041ccb2Sratchov enc = aui.play.encoding; 582b041ccb2Sratchov } else { 583b041ccb2Sratchov rate = aui.record.sample_rate; 584b041ccb2Sratchov prec = aui.record.precision; 585b041ccb2Sratchov enc = aui.record.encoding; 586b041ccb2Sratchov } 587b041ccb2Sratchov } 588b041ccb2Sratchov 589b041ccb2Sratchov /* 590b041ccb2Sratchov * If the rate that the hardware is using is different than 591b041ccb2Sratchov * the requested rate, scale buffer sizes so they will be the 592b041ccb2Sratchov * same time duration as what was requested. This just gets 593b041ccb2Sratchov * the rates to use for scaling, that actual scaling is done 594b041ccb2Sratchov * later. 595b041ccb2Sratchov */ 596b041ccb2Sratchov rate = (hdl->sio.mode & SIO_REC) ? aui.record.sample_rate : 597b041ccb2Sratchov aui.play.sample_rate; 598b041ccb2Sratchov req_rate = rate; 599b041ccb2Sratchov if (par->rate && par->rate != ~0U) 600b041ccb2Sratchov req_rate = par->rate; 601b041ccb2Sratchov 602b041ccb2Sratchov /* 603b041ccb2Sratchov * if block size and buffer size are not both set then 604b041ccb2Sratchov * set the blocksize to half the buffer size 605b041ccb2Sratchov */ 606b041ccb2Sratchov bufsz = par->appbufsz; 607b041ccb2Sratchov round = par->round; 608b041ccb2Sratchov if (bufsz != ~0U) { 609b041ccb2Sratchov bufsz = bufsz * rate / req_rate; 610b041ccb2Sratchov if (round == ~0U) 611b041ccb2Sratchov round = (bufsz + 1) / 2; 612b041ccb2Sratchov else 613b041ccb2Sratchov round = round * rate / req_rate; 614b041ccb2Sratchov } else if (round != ~0U) { 615b041ccb2Sratchov round = round * rate / req_rate; 616b041ccb2Sratchov bufsz = round * 2; 617b041ccb2Sratchov } else 618b041ccb2Sratchov return 1; 619b041ccb2Sratchov 620b041ccb2Sratchov /* 621b041ccb2Sratchov * get the play/record frame size in bytes 622b041ccb2Sratchov */ 623b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { 624b041ccb2Sratchov DPERROR("sio_sun_setpar: GETINFO"); 625b041ccb2Sratchov hdl->sio.eof = 1; 626b041ccb2Sratchov return 0; 627b041ccb2Sratchov } 628b041ccb2Sratchov ibpf = (hdl->sio.mode & SIO_REC) ? 629b041ccb2Sratchov aui.record.channels * aui.record.bps : 1; 630b041ccb2Sratchov obpf = (hdl->sio.mode & SIO_PLAY) ? 631b041ccb2Sratchov aui.play.channels * aui.play.bps : 1; 632b041ccb2Sratchov 633b041ccb2Sratchov DPRINTF("sio_sun_setpar: bpf = (%u, %u)\n", ibpf, obpf); 634b041ccb2Sratchov 635b041ccb2Sratchov /* 636b041ccb2Sratchov * try to set parameters until the device accepts 637b041ccb2Sratchov * a common block size for play and record 638b041ccb2Sratchov */ 639b041ccb2Sratchov for (i = 0; i < NRETRIES; i++) { 640b041ccb2Sratchov AUDIO_INITINFO(&aui); 641b041ccb2Sratchov aui.hiwat = (bufsz + round - 1) / round; 642b041ccb2Sratchov aui.lowat = aui.hiwat; 643b041ccb2Sratchov if (hdl->sio.mode & SIO_REC) 644b041ccb2Sratchov aui.record.block_size = round * ibpf; 645b041ccb2Sratchov if (hdl->sio.mode & SIO_PLAY) 646b041ccb2Sratchov aui.play.block_size = round * obpf; 647b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { 648b041ccb2Sratchov DPERROR("sio_sun_setpar2: SETINFO"); 649b041ccb2Sratchov hdl->sio.eof = 1; 650b041ccb2Sratchov return 0; 651b041ccb2Sratchov } 652b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { 653b041ccb2Sratchov DPERROR("sio_sun_setpar2: GETINFO"); 654b041ccb2Sratchov hdl->sio.eof = 1; 655b041ccb2Sratchov return 0; 656b041ccb2Sratchov } 657b041ccb2Sratchov infr = aui.record.block_size / ibpf; 658b041ccb2Sratchov onfr = aui.play.block_size / obpf; 659b041ccb2Sratchov DPRINTF("sio_sun_setpar: %i: trying round = %u -> (%u, %u)\n", 660b041ccb2Sratchov i, round, infr, onfr); 661b041ccb2Sratchov 662b041ccb2Sratchov /* 663b041ccb2Sratchov * if half-duplex or both block sizes match, we're done 664b041ccb2Sratchov */ 665b041ccb2Sratchov if (hdl->sio.mode != (SIO_REC | SIO_PLAY) || infr == onfr) { 666b041ccb2Sratchov DPRINTF("sio_sun_setpar: blocksize ok\n"); 667b041ccb2Sratchov return 1; 668b041ccb2Sratchov } 669b041ccb2Sratchov 670b041ccb2Sratchov /* 671b041ccb2Sratchov * half of the retries, retry with the smaller value, 672b041ccb2Sratchov * then with the larger returned value 673b041ccb2Sratchov */ 674b041ccb2Sratchov if (i < NRETRIES / 2) 675b041ccb2Sratchov round = infr < onfr ? infr : onfr; 676b041ccb2Sratchov else 677b041ccb2Sratchov round = infr < onfr ? onfr : infr; 678b041ccb2Sratchov } 679b041ccb2Sratchov DPRINTF("sio_sun_setpar: couldn't find a working blocksize\n"); 680b041ccb2Sratchov hdl->sio.eof = 1; 681b041ccb2Sratchov return 0; 682b041ccb2Sratchov #undef NRETRIES 683b041ccb2Sratchov } 684b041ccb2Sratchov 685b041ccb2Sratchov static int 686b041ccb2Sratchov sio_sun_getpar(struct sio_hdl *sh, struct sio_par *par) 687b041ccb2Sratchov { 688b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 689b041ccb2Sratchov struct audio_info aui; 690b041ccb2Sratchov 691b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_GETINFO, &aui) < 0) { 692b041ccb2Sratchov DPERROR("sio_sun_getpar: getinfo"); 693b041ccb2Sratchov hdl->sio.eof = 1; 694b041ccb2Sratchov return 0; 695b041ccb2Sratchov } 696b041ccb2Sratchov if (hdl->sio.mode & SIO_PLAY) { 697b041ccb2Sratchov par->rate = aui.play.sample_rate; 698b041ccb2Sratchov if (!sio_sun_infotoenc(hdl, &aui.play, par)) 699b041ccb2Sratchov return 0; 700b041ccb2Sratchov } else if (hdl->sio.mode & SIO_REC) { 701b041ccb2Sratchov par->rate = aui.record.sample_rate; 702b041ccb2Sratchov if (!sio_sun_infotoenc(hdl, &aui.record, par)) 703b041ccb2Sratchov return 0; 704b041ccb2Sratchov } else 705b041ccb2Sratchov return 0; 706b041ccb2Sratchov par->pchan = (hdl->sio.mode & SIO_PLAY) ? 707b041ccb2Sratchov aui.play.channels : 0; 708b041ccb2Sratchov par->rchan = (hdl->sio.mode & SIO_REC) ? 709b041ccb2Sratchov aui.record.channels : 0; 710b041ccb2Sratchov par->round = (hdl->sio.mode & SIO_REC) ? 711b041ccb2Sratchov aui.record.block_size / (par->bps * par->rchan) : 712b041ccb2Sratchov aui.play.block_size / (par->bps * par->pchan); 713b041ccb2Sratchov par->appbufsz = aui.hiwat * par->round; 714b041ccb2Sratchov par->bufsz = par->appbufsz; 715b041ccb2Sratchov return 1; 716b041ccb2Sratchov } 717b041ccb2Sratchov 718b041ccb2Sratchov /* 719b041ccb2Sratchov * drop recorded samples to compensate xruns 720b041ccb2Sratchov */ 721b041ccb2Sratchov static int 722b041ccb2Sratchov sio_sun_rdrop(struct sio_sun_hdl *hdl) 723b041ccb2Sratchov { 724b041ccb2Sratchov #define DROP_NMAX 0x1000 725b041ccb2Sratchov static char dropbuf[DROP_NMAX]; 726b041ccb2Sratchov ssize_t n, todo; 727b041ccb2Sratchov 728b041ccb2Sratchov while (hdl->offset > 0) { 729b041ccb2Sratchov todo = hdl->offset * hdl->ibpf; 730b041ccb2Sratchov if (todo > DROP_NMAX) 731b041ccb2Sratchov todo = DROP_NMAX - DROP_NMAX % hdl->ibpf; 732b041ccb2Sratchov while ((n = read(hdl->fd, dropbuf, todo)) < 0) { 733b041ccb2Sratchov if (errno == EINTR) 734b041ccb2Sratchov continue; 735b041ccb2Sratchov if (errno != EAGAIN) { 736b041ccb2Sratchov DPERROR("sio_sun_rdrop: read"); 737b041ccb2Sratchov hdl->sio.eof = 1; 738b041ccb2Sratchov } 739b041ccb2Sratchov return 0; 740b041ccb2Sratchov } 741b041ccb2Sratchov if (n == 0) { 742b041ccb2Sratchov DPRINTF("sio_sun_rdrop: eof\n"); 743b041ccb2Sratchov hdl->sio.eof = 1; 744b041ccb2Sratchov return 0; 745b041ccb2Sratchov } 746b041ccb2Sratchov hdl->offset -= (int)n / (int)hdl->ibpf; 747b041ccb2Sratchov DPRINTF("sio_sun_rdrop: dropped %ld/%ld bytes\n", n, todo); 748b041ccb2Sratchov } 749b041ccb2Sratchov return 1; 750b041ccb2Sratchov } 751b041ccb2Sratchov 752b041ccb2Sratchov static size_t 753b041ccb2Sratchov sio_sun_read(struct sio_hdl *sh, void *buf, size_t len) 754b041ccb2Sratchov { 755b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 756b041ccb2Sratchov ssize_t n; 757b041ccb2Sratchov 758b041ccb2Sratchov if (!sio_sun_rdrop(hdl)) 759b041ccb2Sratchov return 0; 760b041ccb2Sratchov while ((n = read(hdl->fd, buf, len)) < 0) { 761b041ccb2Sratchov if (errno == EINTR) 762b041ccb2Sratchov continue; 763b041ccb2Sratchov if (errno != EAGAIN) { 764b041ccb2Sratchov DPERROR("sio_sun_read: read"); 765b041ccb2Sratchov hdl->sio.eof = 1; 766b041ccb2Sratchov } 767b041ccb2Sratchov return 0; 768b041ccb2Sratchov } 769b041ccb2Sratchov if (n == 0) { 770b041ccb2Sratchov DPRINTF("sio_sun_read: eof\n"); 771b041ccb2Sratchov hdl->sio.eof = 1; 772b041ccb2Sratchov return 0; 773b041ccb2Sratchov } 774b041ccb2Sratchov return n; 775b041ccb2Sratchov } 776b041ccb2Sratchov 777b041ccb2Sratchov static size_t 778b041ccb2Sratchov sio_sun_autostart(struct sio_sun_hdl *hdl) 779b041ccb2Sratchov { 780b041ccb2Sratchov struct audio_info aui; 781b041ccb2Sratchov struct pollfd pfd; 782b041ccb2Sratchov 783b041ccb2Sratchov pfd.fd = hdl->fd; 784b041ccb2Sratchov pfd.events = POLLOUT; 785b041ccb2Sratchov while (poll(&pfd, 1, 0) < 0) { 786b041ccb2Sratchov if (errno == EINTR) 787b041ccb2Sratchov continue; 788b041ccb2Sratchov DPERROR("sio_sun_autostart: poll"); 789b041ccb2Sratchov hdl->sio.eof = 1; 790b041ccb2Sratchov return 0; 791b041ccb2Sratchov } 792b041ccb2Sratchov if (!(pfd.revents & POLLOUT)) { 793b041ccb2Sratchov hdl->filling = 0; 794b041ccb2Sratchov AUDIO_INITINFO(&aui); 795b041ccb2Sratchov if (hdl->sio.mode & SIO_PLAY) 796b041ccb2Sratchov aui.play.pause = 0; 797b041ccb2Sratchov if (hdl->sio.mode & SIO_REC) 798b041ccb2Sratchov aui.record.pause = 0; 799b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0) { 800b041ccb2Sratchov DPERROR("sio_sun_autostart: setinfo"); 801b041ccb2Sratchov hdl->sio.eof = 1; 802b041ccb2Sratchov return 0; 803b041ccb2Sratchov } 804b041ccb2Sratchov sio_onmove_cb(&hdl->sio, 0); 805b041ccb2Sratchov } 806b041ccb2Sratchov return 1; 807b041ccb2Sratchov } 808b041ccb2Sratchov 809b041ccb2Sratchov /* 810b041ccb2Sratchov * insert silence to play to compensate xruns 811b041ccb2Sratchov */ 812b041ccb2Sratchov static int 813b041ccb2Sratchov sio_sun_wsil(struct sio_sun_hdl *hdl) 814b041ccb2Sratchov { 815b041ccb2Sratchov #define ZERO_NMAX 0x1000 816b041ccb2Sratchov static char zero[ZERO_NMAX]; 817b041ccb2Sratchov ssize_t n, todo; 818b041ccb2Sratchov 819b041ccb2Sratchov while (hdl->offset < 0) { 820b041ccb2Sratchov todo = (int)-hdl->offset * (int)hdl->obpf; 821b041ccb2Sratchov if (todo > ZERO_NMAX) 822b041ccb2Sratchov todo = ZERO_NMAX - ZERO_NMAX % hdl->obpf; 823b041ccb2Sratchov while ((n = write(hdl->fd, zero, todo)) < 0) { 824b041ccb2Sratchov if (errno == EINTR) 825b041ccb2Sratchov continue; 826b041ccb2Sratchov if (errno != EAGAIN) { 827b041ccb2Sratchov DPERROR("sio_sun_wsil: write"); 828b041ccb2Sratchov hdl->sio.eof = 1; 829b041ccb2Sratchov return 0; 830b041ccb2Sratchov } 831b041ccb2Sratchov return 0; 832b041ccb2Sratchov } 833b041ccb2Sratchov hdl->offset += (int)n / (int)hdl->obpf; 834b041ccb2Sratchov DPRINTF("sio_sun_wsil: inserted %ld/%ld bytes\n", n, todo); 835b041ccb2Sratchov } 836b041ccb2Sratchov return 1; 837b041ccb2Sratchov } 838b041ccb2Sratchov 839b041ccb2Sratchov 840b041ccb2Sratchov static size_t 841b041ccb2Sratchov sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len) 842b041ccb2Sratchov { 843b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 844b041ccb2Sratchov const unsigned char *data = buf; 845b041ccb2Sratchov ssize_t n, todo; 846b041ccb2Sratchov 847b041ccb2Sratchov if (!sio_sun_wsil(hdl)) 848b041ccb2Sratchov return 0; 849b041ccb2Sratchov todo = len; 850b041ccb2Sratchov while ((n = write(hdl->fd, data, todo)) < 0) { 851b041ccb2Sratchov if (errno == EINTR) 852b041ccb2Sratchov continue; 853b041ccb2Sratchov if (errno != EAGAIN) { 854b041ccb2Sratchov DPERROR("sio_sun_write: write"); 855b041ccb2Sratchov hdl->sio.eof = 1; 856b041ccb2Sratchov } 857b041ccb2Sratchov return 0; 858b041ccb2Sratchov } 859b041ccb2Sratchov if (hdl->filling) { 860b041ccb2Sratchov if (!sio_sun_autostart(hdl)) 861b041ccb2Sratchov return 0; 862b041ccb2Sratchov } 863b041ccb2Sratchov return n; 864b041ccb2Sratchov } 865b041ccb2Sratchov 866b041ccb2Sratchov static int 867b041ccb2Sratchov sio_sun_nfds(struct sio_hdl *hdl) 868b041ccb2Sratchov { 869b041ccb2Sratchov return 1; 870b041ccb2Sratchov } 871b041ccb2Sratchov 872b041ccb2Sratchov static int 873b041ccb2Sratchov sio_sun_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events) 874b041ccb2Sratchov { 875b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 876b041ccb2Sratchov 877b041ccb2Sratchov pfd->fd = hdl->fd; 878b041ccb2Sratchov pfd->events = events; 879b041ccb2Sratchov return 1; 880b041ccb2Sratchov } 881b041ccb2Sratchov 882b041ccb2Sratchov int 883b041ccb2Sratchov sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd) 884b041ccb2Sratchov { 885b041ccb2Sratchov struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 886b041ccb2Sratchov struct audio_offset ao; 887b041ccb2Sratchov int xrun, dmove, dierr = 0, doerr = 0, delta; 888b041ccb2Sratchov int revents = pfd->revents; 889b041ccb2Sratchov 890b041ccb2Sratchov if (!hdl->sio.started) 891b041ccb2Sratchov return pfd->revents; 892b041ccb2Sratchov if (hdl->sio.mode & SIO_PLAY) { 893b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_PERROR, &xrun) < 0) { 894b041ccb2Sratchov DPERROR("sio_sun_revents: PERROR"); 895b041ccb2Sratchov hdl->sio.eof = 1; 896b041ccb2Sratchov return POLLHUP; 897b041ccb2Sratchov } 898b041ccb2Sratchov doerr = xrun - hdl->oerr; 899b041ccb2Sratchov hdl->oerr = xrun; 900b041ccb2Sratchov if (!(hdl->sio.mode & SIO_REC)) 901b041ccb2Sratchov dierr = doerr; 902b041ccb2Sratchov } 903b041ccb2Sratchov if (hdl->sio.mode & SIO_REC) { 904b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_RERROR, &xrun) < 0) { 905b041ccb2Sratchov DPERROR("sio_sun_revents: RERROR"); 906b041ccb2Sratchov hdl->sio.eof = 1; 907b041ccb2Sratchov return POLLHUP; 908b041ccb2Sratchov } 909b041ccb2Sratchov dierr = xrun - hdl->ierr; 910b041ccb2Sratchov hdl->ierr = xrun; 911b041ccb2Sratchov if (!(hdl->sio.mode & SIO_PLAY)) 912b041ccb2Sratchov doerr = dierr; 913b041ccb2Sratchov } 914b041ccb2Sratchov hdl->offset += doerr - dierr; 915b041ccb2Sratchov dmove = dierr > doerr ? dierr : doerr; 916b041ccb2Sratchov hdl->idelta -= dmove; 917b041ccb2Sratchov hdl->odelta -= dmove; 918b041ccb2Sratchov 919b041ccb2Sratchov if ((revents & POLLOUT) && (hdl->sio.mode & SIO_PLAY)) { 920b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_GETOOFFS, &ao) < 0) { 921b041ccb2Sratchov DPERROR("sio_sun_revents: GETOOFFS"); 922b041ccb2Sratchov hdl->sio.eof = 1; 923b041ccb2Sratchov return POLLHUP; 924b041ccb2Sratchov } 925b041ccb2Sratchov delta = (ao.samples - hdl->obytes) / hdl->obpf; 926b041ccb2Sratchov hdl->obytes = ao.samples; 927b041ccb2Sratchov hdl->odelta += delta; 928b041ccb2Sratchov if (!(hdl->sio.mode & SIO_REC)) 929b041ccb2Sratchov hdl->idelta += delta; 930b041ccb2Sratchov } 931b041ccb2Sratchov if ((revents & POLLIN) && (hdl->sio.mode & SIO_REC)) { 932b041ccb2Sratchov if (ioctl(hdl->fd, AUDIO_GETIOFFS, &ao) < 0) { 933b041ccb2Sratchov DPERROR("sio_sun_revents: GETIOFFS"); 934b041ccb2Sratchov hdl->sio.eof = 1; 935b041ccb2Sratchov return POLLHUP; 936b041ccb2Sratchov } 937b041ccb2Sratchov delta = (ao.samples - hdl->ibytes) / hdl->ibpf; 938b041ccb2Sratchov hdl->ibytes = ao.samples; 939b041ccb2Sratchov hdl->idelta += delta; 940b041ccb2Sratchov if (!(hdl->sio.mode & SIO_PLAY)) 941b041ccb2Sratchov hdl->odelta += delta; 942b041ccb2Sratchov } 943b041ccb2Sratchov delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta; 944b041ccb2Sratchov if (delta > 0) { 945b041ccb2Sratchov sio_onmove_cb(&hdl->sio, delta); 946b041ccb2Sratchov hdl->idelta -= delta; 947b041ccb2Sratchov hdl->odelta -= delta; 948b041ccb2Sratchov } 949b041ccb2Sratchov 950b041ccb2Sratchov /* 951b041ccb2Sratchov * drop recorded samples or insert silence to play 952b041ccb2Sratchov * right now to adjust revents, and avoid busy loops 953b041ccb2Sratchov * programs 954b041ccb2Sratchov */ 955b041ccb2Sratchov if (hdl->filling) 956b041ccb2Sratchov revents |= POLLOUT; 957b041ccb2Sratchov if ((hdl->sio.mode & SIO_PLAY) && !sio_sun_wsil(hdl)) 958b041ccb2Sratchov revents &= ~POLLOUT; 959b041ccb2Sratchov if ((hdl->sio.mode & SIO_REC) && !sio_sun_rdrop(hdl)) 960b041ccb2Sratchov revents &= ~POLLIN; 961b041ccb2Sratchov return revents; 962b041ccb2Sratchov } 963