1 /* $OpenBSD: sio_sun.c,v 1.30 2022/12/27 17:10:07 jmc Exp $ */ 2 /* 3 * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/ioctl.h> 20 #include <sys/audioio.h> 21 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <limits.h> 25 #include <poll.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "debug.h" 32 #include "sio_priv.h" 33 34 #define DEVPATH_PREFIX "/dev/audio" 35 #define DEVPATH_MAX (1 + \ 36 sizeof(DEVPATH_PREFIX) - 1 + \ 37 sizeof(int) * 3) 38 39 struct sio_sun_hdl { 40 struct sio_hdl sio; 41 int fd; 42 int filling; 43 unsigned int ibpf, obpf; /* bytes per frame */ 44 unsigned int ibytes, obytes; /* bytes the hw transferred */ 45 unsigned int ierr, oerr; /* frames the hw dropped */ 46 int idelta, odelta; /* position reported to client */ 47 }; 48 49 static void sio_sun_close(struct sio_hdl *); 50 static int sio_sun_start(struct sio_hdl *); 51 static int sio_sun_flush(struct sio_hdl *); 52 static int sio_sun_setpar(struct sio_hdl *, struct sio_par *); 53 static int sio_sun_getpar(struct sio_hdl *, struct sio_par *); 54 static int sio_sun_getcap(struct sio_hdl *, struct sio_cap *); 55 static size_t sio_sun_read(struct sio_hdl *, void *, size_t); 56 static size_t sio_sun_write(struct sio_hdl *, const void *, size_t); 57 static int sio_sun_nfds(struct sio_hdl *); 58 static int sio_sun_pollfd(struct sio_hdl *, struct pollfd *, int); 59 static int sio_sun_revents(struct sio_hdl *, struct pollfd *); 60 61 static struct sio_ops sio_sun_ops = { 62 sio_sun_close, 63 sio_sun_setpar, 64 sio_sun_getpar, 65 sio_sun_getcap, 66 sio_sun_write, 67 sio_sun_read, 68 sio_sun_start, 69 NULL, 70 sio_sun_flush, 71 sio_sun_nfds, 72 sio_sun_pollfd, 73 sio_sun_revents, 74 NULL, /* setvol */ 75 NULL, /* getvol */ 76 }; 77 78 static int 79 sio_sun_adjpar(struct sio_sun_hdl *hdl, struct audio_swpar *ap) 80 { 81 if (hdl->sio.eof) 82 return 0; 83 if (ioctl(hdl->fd, AUDIO_SETPAR, ap) == -1) { 84 DPERROR("AUDIO_SETPAR"); 85 hdl->sio.eof = 1; 86 return 0; 87 } 88 if (ioctl(hdl->fd, AUDIO_GETPAR, ap) == -1) { 89 DPERROR("AUDIO_GETPAR"); 90 hdl->sio.eof = 1; 91 return 0; 92 } 93 return 1; 94 } 95 96 /* 97 * try to set the device to the given parameters and check that the 98 * device can use them; return 1 on success, 0 on failure or error 99 */ 100 static int 101 sio_sun_testpar(struct sio_sun_hdl *hdl, struct sio_enc *enc, 102 unsigned int pchan, unsigned int rchan, unsigned int rate) 103 { 104 struct audio_swpar ap; 105 106 AUDIO_INITPAR(&ap); 107 if (enc != NULL) { 108 ap.sig = enc->sig; 109 ap.bits = enc->bits; 110 ap.bps = enc->bps; 111 if (ap.bps > 1) 112 ap.le = enc->le; 113 if (ap.bps * 8 > ap.bits) 114 ap.msb = enc->msb; 115 } 116 if (rate) 117 ap.rate = rate; 118 if (pchan && (hdl->sio.mode & SIO_PLAY)) 119 ap.pchan = pchan; 120 if (rchan && (hdl->sio.mode & SIO_REC)) 121 ap.rchan = rchan; 122 if (!sio_sun_adjpar(hdl, &ap)) 123 return 0; 124 if (pchan && ap.pchan != pchan) 125 return 0; 126 if (rchan && ap.rchan != rchan) 127 return 0; 128 if (rate && ap.rate != rate) 129 return 0; 130 if (enc) { 131 if (ap.sig != enc->sig) 132 return 0; 133 if (ap.bits != enc->bits) 134 return 0; 135 if (ap.bps != enc->bps) 136 return 0; 137 if (ap.bps > 1 && ap.le != enc->le) 138 return 0; 139 if (ap.bits < ap.bps * 8 && ap.msb != enc->msb) 140 return 0; 141 } 142 return 1; 143 } 144 145 /* 146 * guess device capabilities 147 */ 148 static int 149 sio_sun_getcap(struct sio_hdl *sh, struct sio_cap *cap) 150 { 151 static unsigned int chans[] = { 152 1, 2, 4, 6, 8, 10, 12 153 }; 154 static unsigned int rates[] = { 155 8000, 11025, 12000, 16000, 22050, 24000, 156 32000, 44100, 48000, 64000, 88200, 96000 157 }; 158 static unsigned int encs[] = { 159 8, 16, 24, 32 160 }; 161 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 162 struct audio_swpar savepar, ap; 163 unsigned int nconf = 0; 164 unsigned int enc_map = 0, rchan_map = 0, pchan_map = 0, rate_map; 165 unsigned int i, j, conf; 166 167 if (ioctl(hdl->fd, AUDIO_GETPAR, &savepar) == -1) { 168 DPERROR("AUDIO_GETPAR"); 169 hdl->sio.eof = 1; 170 return 0; 171 } 172 173 /* 174 * get a subset of supported encodings 175 */ 176 for (i = 0; i < sizeof(encs) / sizeof(encs[0]); i++) { 177 AUDIO_INITPAR(&ap); 178 ap.bits = encs[i]; 179 ap.sig = (ap.bits > 8) ? 1 : 0; 180 if (!sio_sun_adjpar(hdl, &ap)) 181 return 0; 182 if (ap.bits == encs[i]) { 183 cap->enc[i].sig = ap.sig; 184 cap->enc[i].bits = ap.bits; 185 cap->enc[i].le = ap.le; 186 cap->enc[i].bps = ap.bps; 187 cap->enc[i].msb = ap.msb; 188 enc_map |= 1 << i; 189 } 190 } 191 192 /* 193 * fill channels 194 * 195 * for now we're lucky: all kernel devices assume that the 196 * number of channels and the encoding are independent so we can 197 * use the current encoding and try various channels. 198 */ 199 if (hdl->sio.mode & SIO_PLAY) { 200 for (i = 0; i < sizeof(chans) / sizeof(chans[0]); i++) { 201 AUDIO_INITPAR(&ap); 202 ap.pchan = chans[i]; 203 if (!sio_sun_adjpar(hdl, &ap)) 204 return 0; 205 if (ap.pchan == chans[i]) { 206 cap->pchan[i] = chans[i]; 207 pchan_map |= (1 << i); 208 } 209 } 210 } 211 if (hdl->sio.mode & SIO_REC) { 212 for (i = 0; i < sizeof(chans) / sizeof(chans[0]); i++) { 213 AUDIO_INITPAR(&ap); 214 ap.pchan = chans[i]; 215 if (!sio_sun_adjpar(hdl, &ap)) 216 return 0; 217 if (ap.rchan == chans[i]) { 218 cap->rchan[i] = chans[i]; 219 rchan_map |= (1 << i); 220 } 221 } 222 } 223 224 /* 225 * fill rates 226 * 227 * rates are not independent from other parameters (eg. on 228 * uaudio devices), so certain rates may not be allowed with 229 * certain encodings. We have to check rates for all encodings 230 */ 231 for (j = 0; j < sizeof(encs) / sizeof(encs[0]); j++) { 232 rate_map = 0; 233 if ((enc_map & (1 << j)) == 0) 234 continue; 235 for (i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) { 236 if (sio_sun_testpar(hdl, 237 &cap->enc[j], 0, 0, rates[i])) { 238 cap->rate[i] = rates[i]; 239 rate_map |= (1 << i); 240 } 241 } 242 for (conf = 0; conf < nconf; conf++) { 243 if (cap->confs[conf].rate == rate_map) { 244 cap->confs[conf].enc |= (1 << j); 245 break; 246 } 247 } 248 if (conf == nconf) { 249 if (nconf == SIO_NCONF) 250 break; 251 cap->confs[nconf].enc = (1 << j); 252 cap->confs[nconf].pchan = pchan_map; 253 cap->confs[nconf].rchan = rchan_map; 254 cap->confs[nconf].rate = rate_map; 255 nconf++; 256 } 257 } 258 cap->nconf = nconf; 259 260 if (ioctl(hdl->fd, AUDIO_SETPAR, &savepar) == -1) { 261 DPERROR("AUDIO_SETPAR"); 262 hdl->sio.eof = 1; 263 return 0; 264 } 265 return 1; 266 } 267 268 int 269 sio_sun_getfd(const char *str, unsigned int mode, int nbio) 270 { 271 const char *p; 272 char path[DEVPATH_MAX]; 273 unsigned int devnum; 274 int fd, flags; 275 276 #ifdef DEBUG 277 _sndio_debug_init(); 278 #endif 279 p = _sndio_parsetype(str, "rsnd"); 280 if (p == NULL) { 281 DPRINTF("sio_sun_getfd: %s: \"rsnd\" expected\n", str); 282 return -1; 283 } 284 switch (*p) { 285 case '/': 286 p++; 287 break; 288 default: 289 DPRINTF("sio_sun_getfd: %s: '/' expected\n", str); 290 return -1; 291 } 292 p = _sndio_parsenum(p, &devnum, 255); 293 if (p == NULL || *p != '\0') { 294 DPRINTF("sio_sun_getfd: %s: number expected after '/'\n", str); 295 return -1; 296 } 297 snprintf(path, sizeof(path), DEVPATH_PREFIX "%u", devnum); 298 if (mode == (SIO_PLAY | SIO_REC)) 299 flags = O_RDWR; 300 else 301 flags = (mode & SIO_PLAY) ? O_WRONLY : O_RDONLY; 302 while ((fd = open(path, flags | O_NONBLOCK | O_CLOEXEC)) == -1) { 303 if (errno == EINTR) 304 continue; 305 DPERROR(path); 306 return -1; 307 } 308 return fd; 309 } 310 311 struct sio_hdl * 312 sio_sun_fdopen(int fd, unsigned int mode, int nbio) 313 { 314 struct sio_sun_hdl *hdl; 315 316 #ifdef DEBUG 317 _sndio_debug_init(); 318 #endif 319 hdl = malloc(sizeof(struct sio_sun_hdl)); 320 if (hdl == NULL) 321 return NULL; 322 _sio_create(&hdl->sio, &sio_sun_ops, mode, nbio); 323 324 /* 325 * pause the device 326 */ 327 if (ioctl(fd, AUDIO_STOP) == -1) { 328 DPERROR("AUDIO_STOP"); 329 free(hdl); 330 return NULL; 331 } 332 hdl->fd = fd; 333 hdl->filling = 0; 334 return (struct sio_hdl *)hdl; 335 } 336 337 struct sio_hdl * 338 _sio_sun_open(const char *str, unsigned int mode, int nbio) 339 { 340 struct sio_hdl *hdl; 341 int fd; 342 343 fd = sio_sun_getfd(str, mode, nbio); 344 if (fd == -1) 345 return NULL; 346 hdl = sio_sun_fdopen(fd, mode, nbio); 347 if (hdl != NULL) 348 return hdl; 349 while (close(fd) == -1 && errno == EINTR) 350 ; /* retry */ 351 return NULL; 352 } 353 354 static void 355 sio_sun_close(struct sio_hdl *sh) 356 { 357 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 358 359 while (close(hdl->fd) == -1 && errno == EINTR) 360 ; /* retry */ 361 free(hdl); 362 } 363 364 static int 365 sio_sun_start(struct sio_hdl *sh) 366 { 367 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 368 369 hdl->obpf = hdl->sio.par.pchan * hdl->sio.par.bps; 370 hdl->ibpf = hdl->sio.par.rchan * hdl->sio.par.bps; 371 hdl->ibytes = 0; 372 hdl->obytes = 0; 373 hdl->ierr = 0; 374 hdl->oerr = 0; 375 hdl->idelta = 0; 376 hdl->odelta = 0; 377 378 if (hdl->sio.mode & SIO_PLAY) { 379 /* 380 * keep the device paused and let sio_sun_pollfd() trigger the 381 * start later, to avoid buffer underruns 382 */ 383 hdl->filling = 1; 384 } else { 385 /* 386 * no play buffers to fill, start now! 387 */ 388 if (ioctl(hdl->fd, AUDIO_START) == -1) { 389 DPERROR("AUDIO_START"); 390 hdl->sio.eof = 1; 391 return 0; 392 } 393 _sio_onmove_cb(&hdl->sio, 0); 394 } 395 return 1; 396 } 397 398 static int 399 sio_sun_flush(struct sio_hdl *sh) 400 { 401 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 402 403 if (hdl->filling) { 404 hdl->filling = 0; 405 return 1; 406 } 407 if (ioctl(hdl->fd, AUDIO_STOP) == -1) { 408 DPERROR("AUDIO_STOP"); 409 hdl->sio.eof = 1; 410 return 0; 411 } 412 return 1; 413 } 414 415 static int 416 sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par) 417 { 418 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 419 struct audio_swpar ap; 420 421 AUDIO_INITPAR(&ap); 422 ap.sig = par->sig; 423 ap.le = par->le; 424 ap.bits = par->bits; 425 ap.bps = par->bps; 426 ap.msb = par->msb; 427 ap.rate = par->rate; 428 if (hdl->sio.mode & SIO_PLAY) 429 ap.pchan = par->pchan; 430 if (hdl->sio.mode & SIO_REC) 431 ap.rchan = par->rchan; 432 if (par->round != ~0U && par->appbufsz != ~0U) { 433 ap.round = par->round; 434 ap.nblks = par->appbufsz / par->round; 435 } else if (par->round != ~0U) { 436 ap.round = par->round; 437 ap.nblks = 2; 438 } else if (par->appbufsz != ~0U) { 439 ap.round = par->appbufsz / 2; 440 ap.nblks = 2; 441 } 442 if (ioctl(hdl->fd, AUDIO_SETPAR, &ap) == -1) { 443 DPERROR("AUDIO_SETPAR"); 444 hdl->sio.eof = 1; 445 return 0; 446 } 447 return 1; 448 } 449 450 static int 451 sio_sun_getpar(struct sio_hdl *sh, struct sio_par *par) 452 { 453 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 454 struct audio_swpar ap; 455 456 if (ioctl(hdl->fd, AUDIO_GETPAR, &ap) == -1) { 457 DPERROR("AUDIO_GETPAR"); 458 hdl->sio.eof = 1; 459 return 0; 460 } 461 par->sig = ap.sig; 462 par->le = ap.le; 463 par->bits = ap.bits; 464 par->bps = ap.bps; 465 par->msb = ap.msb; 466 par->rate = ap.rate; 467 par->pchan = ap.pchan; 468 par->rchan = ap.rchan; 469 par->round = ap.round; 470 par->appbufsz = par->bufsz = ap.nblks * ap.round; 471 par->xrun = SIO_IGNORE; 472 return 1; 473 } 474 475 static size_t 476 sio_sun_read(struct sio_hdl *sh, void *buf, size_t len) 477 { 478 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 479 ssize_t n; 480 481 while ((n = read(hdl->fd, buf, len)) == -1) { 482 if (errno == EINTR) 483 continue; 484 if (errno != EAGAIN) { 485 DPERROR("sio_sun_read: read"); 486 hdl->sio.eof = 1; 487 } 488 return 0; 489 } 490 if (n == 0) { 491 DPRINTF("sio_sun_read: eof\n"); 492 hdl->sio.eof = 1; 493 return 0; 494 } 495 return n; 496 } 497 498 static size_t 499 sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len) 500 { 501 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 502 const unsigned char *data = buf; 503 ssize_t n, todo; 504 505 todo = len; 506 while ((n = write(hdl->fd, data, todo)) == -1) { 507 if (errno == EINTR) 508 continue; 509 if (errno != EAGAIN) { 510 DPERROR("sio_sun_write: write"); 511 hdl->sio.eof = 1; 512 } 513 return 0; 514 } 515 return n; 516 } 517 518 static int 519 sio_sun_nfds(struct sio_hdl *hdl) 520 { 521 return 1; 522 } 523 524 static int 525 sio_sun_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events) 526 { 527 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 528 529 pfd->fd = hdl->fd; 530 pfd->events = events; 531 if (hdl->filling && hdl->sio.wused == hdl->sio.par.bufsz * 532 hdl->sio.par.pchan * hdl->sio.par.bps) { 533 hdl->filling = 0; 534 if (ioctl(hdl->fd, AUDIO_START) == -1) { 535 DPERROR("AUDIO_START"); 536 hdl->sio.eof = 1; 537 return 0; 538 } 539 _sio_onmove_cb(&hdl->sio, 0); 540 } 541 return 1; 542 } 543 544 int 545 sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd) 546 { 547 struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; 548 struct audio_pos ap; 549 int dierr = 0, doerr = 0, offset, delta; 550 int revents = pfd->revents; 551 552 if ((pfd->revents & POLLHUP) || 553 (pfd->revents & (POLLIN | POLLOUT)) == 0) 554 return pfd->revents; 555 if (ioctl(hdl->fd, AUDIO_GETPOS, &ap) == -1) { 556 DPERROR("sio_sun_revents: GETPOS"); 557 hdl->sio.eof = 1; 558 return POLLHUP; 559 } 560 if (hdl->sio.mode & SIO_PLAY) { 561 delta = (ap.play_pos - hdl->obytes) / hdl->obpf; 562 doerr = (ap.play_xrun - hdl->oerr) / hdl->obpf; 563 hdl->obytes = ap.play_pos; 564 hdl->oerr = ap.play_xrun; 565 hdl->odelta += delta; 566 if (!(hdl->sio.mode & SIO_REC)) { 567 hdl->idelta += delta; 568 dierr = doerr; 569 } 570 if (doerr > 0) 571 DPRINTFN(2, "play xrun %d\n", doerr); 572 } 573 if (hdl->sio.mode & SIO_REC) { 574 delta = (ap.rec_pos - hdl->ibytes) / hdl->ibpf; 575 dierr = (ap.rec_xrun - hdl->ierr) / hdl->ibpf; 576 hdl->ibytes = ap.rec_pos; 577 hdl->ierr = ap.rec_xrun; 578 hdl->idelta += delta; 579 if (!(hdl->sio.mode & SIO_PLAY)) { 580 hdl->odelta += delta; 581 doerr = dierr; 582 } 583 if (dierr > 0) 584 DPRINTFN(2, "rec xrun %d\n", dierr); 585 } 586 587 /* 588 * GETPOS reports positions including xruns, 589 * so we have to subtract to get the real position 590 */ 591 hdl->idelta -= dierr; 592 hdl->odelta -= doerr; 593 594 offset = doerr - dierr; 595 if (offset > 0) { 596 hdl->sio.rdrop += offset * hdl->ibpf; 597 hdl->idelta -= offset; 598 DPRINTFN(2, "will drop %d and pause %d\n", offset, doerr); 599 } else if (offset < 0) { 600 hdl->sio.wsil += -offset * hdl->obpf; 601 hdl->odelta -= -offset; 602 DPRINTFN(2, "will insert %d and pause %d\n", -offset, dierr); 603 } 604 605 delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta; 606 if (delta > 0) { 607 _sio_onmove_cb(&hdl->sio, delta); 608 hdl->idelta -= delta; 609 hdl->odelta -= delta; 610 } 611 return revents; 612 } 613