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