1 /* $OpenBSD: siofile.c,v 1.24 2021/03/03 10:00:27 ratchov Exp $ */ 2 /* 3 * Copyright (c) 2008-2012 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 #include <sys/time.h> 18 #include <sys/types.h> 19 20 #include <poll.h> 21 #include <sndio.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 26 #include "abuf.h" 27 #include "defs.h" 28 #include "dev.h" 29 #include "dev_sioctl.h" 30 #include "dsp.h" 31 #include "fdpass.h" 32 #include "file.h" 33 #include "siofile.h" 34 #include "utils.h" 35 36 #define WATCHDOG_USEC 4000000 /* 4 seconds */ 37 38 void dev_sio_onmove(void *, int); 39 void dev_sio_timeout(void *); 40 int dev_sio_pollfd(void *, struct pollfd *); 41 int dev_sio_revents(void *, struct pollfd *); 42 void dev_sio_run(void *); 43 void dev_sio_hup(void *); 44 45 extern struct fileops dev_sioctl_ops; 46 47 struct fileops dev_sio_ops = { 48 "sio", 49 dev_sio_pollfd, 50 dev_sio_revents, 51 dev_sio_run, 52 dev_sio_run, 53 dev_sio_hup 54 }; 55 56 void 57 dev_sio_onmove(void *arg, int delta) 58 { 59 struct dev *d = arg; 60 61 #ifdef DEBUG 62 if (log_level >= 4) { 63 dev_log(d); 64 log_puts(": tick, delta = "); 65 log_puti(delta); 66 log_puts("\n"); 67 } 68 d->sio.sum_utime += file_utime - d->sio.utime; 69 d->sio.sum_wtime += file_wtime - d->sio.wtime; 70 d->sio.wtime = file_wtime; 71 d->sio.utime = file_utime; 72 if (d->mode & MODE_PLAY) 73 d->sio.pused -= delta; 74 if (d->mode & MODE_REC) 75 d->sio.rused += delta; 76 #endif 77 dev_onmove(d, delta); 78 } 79 80 void 81 dev_sio_timeout(void *arg) 82 { 83 struct dev *d = arg; 84 85 dev_log(d); 86 log_puts(": watchdog timeout\n"); 87 dev_abort(d); 88 } 89 90 static int 91 dev_sio_openalt(struct dev *d, struct dev_alt *n, 92 struct sio_hdl **rhdl, struct sioctl_hdl **rctlhdl, unsigned int *rmode) 93 { 94 struct sio_hdl *hdl; 95 struct sioctl_hdl *ctlhdl; 96 unsigned int mode = d->reqmode & (MODE_PLAY | MODE_REC); 97 98 hdl = fdpass_sio_open(d->num, n->idx, mode); 99 if (hdl == NULL) { 100 if (mode != (SIO_PLAY | SIO_REC)) 101 return 0; 102 hdl = fdpass_sio_open(d->num, n->idx, SIO_PLAY); 103 if (hdl != NULL) 104 mode = SIO_PLAY; 105 else { 106 hdl = fdpass_sio_open(d->num, n->idx, SIO_REC); 107 if (hdl != NULL) 108 mode = SIO_REC; 109 else 110 return 0; 111 } 112 if (log_level >= 1) { 113 log_puts("warning, device opened in "); 114 log_puts(mode == SIO_PLAY ? "play-only" : "rec-only"); 115 log_puts(" mode\n"); 116 } 117 } 118 119 ctlhdl = fdpass_sioctl_open(d->num, n->idx, SIOCTL_READ | SIOCTL_WRITE); 120 if (ctlhdl == NULL) { 121 if (log_level >= 1) { 122 dev_log(d); 123 log_puts(": no control device\n"); 124 } 125 } 126 127 *rhdl = hdl; 128 *rctlhdl = ctlhdl; 129 *rmode = mode; 130 return 1; 131 } 132 133 /* 134 * open the device using one of the provided paths 135 */ 136 static int 137 dev_sio_openlist(struct dev *d, 138 struct sio_hdl **rhdl, struct sioctl_hdl **rctlhdl, unsigned int *rmode) 139 { 140 struct dev_alt *n; 141 struct ctl *c; 142 int val; 143 144 for (n = d->alt_list; n != NULL; n = n->next) { 145 if (d->alt_num == n->idx) 146 continue; 147 if (log_level >= 2) { 148 dev_log(d); 149 log_puts(": trying "); 150 log_puts(n->name); 151 log_puts("\n"); 152 } 153 if (dev_sio_openalt(d, n, rhdl, rctlhdl, rmode)) { 154 if (log_level >= 2) { 155 dev_log(d); 156 log_puts(": using "); 157 log_puts(n->name); 158 log_puts("\n"); 159 } 160 d->alt_num = n->idx; 161 for (c = ctl_list; c != NULL; c = c->next) { 162 if (!ctl_match(c, CTL_DEV_ALT, d, NULL)) 163 continue; 164 val = c->u.dev_alt.idx == n->idx; 165 if (c->curval == val) 166 continue; 167 c->curval = val; 168 if (val) 169 c->val_mask = ~0U; 170 } 171 return 1; 172 } 173 } 174 return 0; 175 } 176 177 /* 178 * open the device. 179 */ 180 int 181 dev_sio_open(struct dev *d) 182 { 183 struct sio_par par; 184 185 if (!dev_sio_openlist(d, &d->sio.hdl, &d->sioctl.hdl, &d->mode)) 186 return 0; 187 188 sio_initpar(&par); 189 par.bits = d->par.bits; 190 par.bps = d->par.bps; 191 par.sig = d->par.sig; 192 par.le = d->par.le; 193 par.msb = d->par.msb; 194 if (d->mode & SIO_PLAY) 195 par.pchan = d->pchan; 196 if (d->mode & SIO_REC) 197 par.rchan = d->rchan; 198 if (d->bufsz) 199 par.appbufsz = d->bufsz; 200 if (d->round) 201 par.round = d->round; 202 if (d->rate) 203 par.rate = d->rate; 204 if (!sio_setpar(d->sio.hdl, &par)) 205 goto bad_close; 206 if (!sio_getpar(d->sio.hdl, &par)) 207 goto bad_close; 208 209 #ifdef DEBUG 210 /* 211 * We support any parameter combination exposed by the kernel, 212 * and we have no other choice than trusting the kernel for 213 * returning correct parameters. But let's check parameters 214 * early and nicely report kernel bugs rather than crashing 215 * later in memset(), malloc() or alike. 216 */ 217 218 if (par.bits > BITS_MAX) { 219 dev_log(d); 220 log_puts(": "); 221 log_putu(par.bits); 222 log_puts(": unsupported number of bits\n"); 223 goto bad_close; 224 } 225 if (par.bps > SIO_BPS(BITS_MAX)) { 226 dev_log(d); 227 log_puts(": "); 228 log_putu(par.bps); 229 log_puts(": unsupported sample size\n"); 230 goto bad_close; 231 } 232 if ((d->mode & SIO_PLAY) && par.pchan > NCHAN_MAX) { 233 dev_log(d); 234 log_puts(": "); 235 log_putu(par.pchan); 236 log_puts(": unsupported number of play channels\n"); 237 goto bad_close; 238 } 239 if ((d->mode & SIO_REC) && par.rchan > NCHAN_MAX) { 240 dev_log(d); 241 log_puts(": "); 242 log_putu(par.rchan); 243 log_puts(": unsupported number of rec channels\n"); 244 goto bad_close; 245 } 246 if (par.bufsz == 0 || par.bufsz > RATE_MAX) { 247 dev_log(d); 248 log_puts(": "); 249 log_putu(par.bufsz); 250 log_puts(": unsupported buffer size\n"); 251 goto bad_close; 252 } 253 if (par.round == 0 || par.round > par.bufsz || 254 par.bufsz % par.round != 0) { 255 dev_log(d); 256 log_puts(": "); 257 log_putu(par.round); 258 log_puts(": unsupported block size\n"); 259 goto bad_close; 260 } 261 if (par.rate == 0 || par.rate > RATE_MAX) { 262 dev_log(d); 263 log_puts(": "); 264 log_putu(par.rate); 265 log_puts(": unsupported rate\n"); 266 goto bad_close; 267 } 268 #endif 269 270 d->par.bits = par.bits; 271 d->par.bps = par.bps; 272 d->par.sig = par.sig; 273 d->par.le = par.le; 274 d->par.msb = par.msb; 275 if (d->mode & SIO_PLAY) 276 d->pchan = par.pchan; 277 if (d->mode & SIO_REC) 278 d->rchan = par.rchan; 279 d->bufsz = par.bufsz; 280 d->round = par.round; 281 d->rate = par.rate; 282 if (d->mode & MODE_PLAY) 283 d->mode |= MODE_MON; 284 sio_onmove(d->sio.hdl, dev_sio_onmove, d); 285 d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(d->sio.hdl)); 286 if (d->sioctl.hdl) { 287 d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix", 288 sioctl_nfds(d->sioctl.hdl)); 289 } 290 timo_set(&d->sio.watchdog, dev_sio_timeout, d); 291 dev_sioctl_open(d); 292 return 1; 293 bad_close: 294 sio_close(d->sio.hdl); 295 if (d->sioctl.hdl) { 296 sioctl_close(d->sioctl.hdl); 297 d->sioctl.hdl = NULL; 298 } 299 return 0; 300 } 301 302 /* 303 * Open an alternate device. Upon success and if the new device is 304 * compatible with the old one, close the old device and continue 305 * using the new one. The new device is not started. 306 */ 307 int 308 dev_sio_reopen(struct dev *d) 309 { 310 struct sio_par par; 311 struct sio_hdl *hdl; 312 struct sioctl_hdl *ctlhdl; 313 unsigned int mode; 314 315 if (!dev_sio_openlist(d, &hdl, &ctlhdl, &mode)) 316 return 0; 317 318 sio_initpar(&par); 319 par.bits = d->par.bits; 320 par.bps = d->par.bps; 321 par.sig = d->par.sig; 322 par.le = d->par.le; 323 par.msb = d->par.msb; 324 if (mode & SIO_PLAY) 325 par.pchan = d->reqpchan; 326 if (mode & SIO_REC) 327 par.rchan = d->reqrchan; 328 par.appbufsz = d->bufsz; 329 par.round = d->round; 330 par.rate = d->rate; 331 if (!sio_setpar(hdl, &par)) 332 goto bad_close; 333 if (!sio_getpar(hdl, &par)) 334 goto bad_close; 335 336 /* check if new parameters are compatible with old ones */ 337 if (par.round != d->round || par.bufsz != d->bufsz || 338 par.rate != d->rate) { 339 if (log_level >= 1) { 340 dev_log(d); 341 log_puts(": alternate device not compatible\n"); 342 } 343 goto bad_close; 344 } 345 346 /* close unused device */ 347 timo_del(&d->sio.watchdog); 348 file_del(d->sio.file); 349 sio_close(d->sio.hdl); 350 if (d->sioctl.hdl) { 351 file_del(d->sioctl.file); 352 sioctl_close(d->sioctl.hdl); 353 d->sioctl.hdl = NULL; 354 } 355 356 /* update parameters */ 357 d->mode = mode; 358 d->par.bits = par.bits; 359 d->par.bps = par.bps; 360 d->par.sig = par.sig; 361 d->par.le = par.le; 362 d->par.msb = par.msb; 363 if (d->mode & SIO_PLAY) 364 d->pchan = par.pchan; 365 if (d->mode & SIO_REC) 366 d->rchan = par.rchan; 367 368 d->sio.hdl = hdl; 369 d->sioctl.hdl = ctlhdl; 370 d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(hdl)); 371 if (d->sioctl.hdl) { 372 d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix", 373 sioctl_nfds(ctlhdl)); 374 } 375 sio_onmove(hdl, dev_sio_onmove, d); 376 return 1; 377 bad_close: 378 sio_close(hdl); 379 if (ctlhdl) 380 sioctl_close(ctlhdl); 381 return 0; 382 } 383 384 void 385 dev_sio_close(struct dev *d) 386 { 387 dev_sioctl_close(d); 388 #ifdef DEBUG 389 if (log_level >= 3) { 390 dev_log(d); 391 log_puts(": closed\n"); 392 } 393 #endif 394 timo_del(&d->sio.watchdog); 395 file_del(d->sio.file); 396 sio_close(d->sio.hdl); 397 if (d->sioctl.hdl) { 398 file_del(d->sioctl.file); 399 sioctl_close(d->sioctl.hdl); 400 d->sioctl.hdl = NULL; 401 } 402 d->alt_num = -1; 403 } 404 405 void 406 dev_sio_start(struct dev *d) 407 { 408 if (!sio_start(d->sio.hdl)) { 409 if (log_level >= 1) { 410 dev_log(d); 411 log_puts(": failed to start device\n"); 412 } 413 return; 414 } 415 if (d->mode & MODE_PLAY) { 416 d->sio.cstate = DEV_SIO_CYCLE; 417 d->sio.todo = 0; 418 } else { 419 d->sio.cstate = DEV_SIO_READ; 420 d->sio.todo = d->round * d->rchan * d->par.bps; 421 } 422 #ifdef DEBUG 423 d->sio.pused = 0; 424 d->sio.rused = 0; 425 d->sio.sum_utime = 0; 426 d->sio.sum_wtime = 0; 427 d->sio.wtime = file_wtime; 428 d->sio.utime = file_utime; 429 if (log_level >= 3) { 430 dev_log(d); 431 log_puts(": started\n"); 432 } 433 #endif 434 timo_add(&d->sio.watchdog, WATCHDOG_USEC); 435 } 436 437 void 438 dev_sio_stop(struct dev *d) 439 { 440 if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) { 441 if (log_level >= 1) { 442 dev_log(d); 443 log_puts(": failed to stop device\n"); 444 } 445 return; 446 } 447 #ifdef DEBUG 448 if (log_level >= 3) { 449 dev_log(d); 450 log_puts(": stopped, load avg = "); 451 log_puti(d->sio.sum_utime / 1000); 452 log_puts(" / "); 453 log_puti(d->sio.sum_wtime / 1000); 454 log_puts("\n"); 455 } 456 #endif 457 timo_del(&d->sio.watchdog); 458 } 459 460 int 461 dev_sio_pollfd(void *arg, struct pollfd *pfd) 462 { 463 struct dev *d = arg; 464 int events; 465 466 events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT; 467 return sio_pollfd(d->sio.hdl, pfd, events); 468 } 469 470 int 471 dev_sio_revents(void *arg, struct pollfd *pfd) 472 { 473 struct dev *d = arg; 474 int events; 475 476 events = sio_revents(d->sio.hdl, pfd); 477 #ifdef DEBUG 478 d->sio.events = events; 479 #endif 480 return events; 481 } 482 483 void 484 dev_sio_run(void *arg) 485 { 486 struct dev *d = arg; 487 unsigned char *data, *base; 488 unsigned int n; 489 490 /* 491 * sio_read() and sio_write() would block at the end of the 492 * cycle so we *must* return and restart poll()'ing. Otherwise 493 * we may trigger dev_cycle() which would make all clients 494 * underrun (ex, on a play-only device) 495 */ 496 for (;;) { 497 if (d->pstate != DEV_RUN) 498 return; 499 switch (d->sio.cstate) { 500 case DEV_SIO_READ: 501 #ifdef DEBUG 502 if (!(d->sio.events & POLLIN)) { 503 dev_log(d); 504 log_puts(": recording, but POLLIN not set\n"); 505 panic(); 506 } 507 if (d->sio.todo == 0) { 508 dev_log(d); 509 log_puts(": can't read data\n"); 510 panic(); 511 } 512 if (d->prime > 0) { 513 dev_log(d); 514 log_puts(": unexpected data\n"); 515 panic(); 516 } 517 #endif 518 base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf; 519 data = base + 520 d->rchan * d->round * d->par.bps - 521 d->sio.todo; 522 n = sio_read(d->sio.hdl, data, d->sio.todo); 523 d->sio.todo -= n; 524 #ifdef DEBUG 525 if (log_level >= 4) { 526 dev_log(d); 527 log_puts(": read "); 528 log_putu(n); 529 log_puts(": bytes, todo "); 530 log_putu(d->sio.todo); 531 log_puts("/"); 532 log_putu(d->round * d->rchan * d->par.bps); 533 log_puts("\n"); 534 } 535 #endif 536 if (d->sio.todo > 0) 537 return; 538 #ifdef DEBUG 539 d->sio.rused -= d->round; 540 if (log_level >= 2) { 541 if (d->sio.rused >= d->round) { 542 dev_log(d); 543 log_puts(": rec hw xrun, rused = "); 544 log_puti(d->sio.rused); 545 log_puts("/"); 546 log_puti(d->bufsz); 547 log_puts("\n"); 548 } 549 if (d->sio.rused < 0 || 550 d->sio.rused >= d->bufsz) { 551 dev_log(d); 552 log_puts(": out of bounds rused = "); 553 log_puti(d->sio.rused); 554 log_puts("/"); 555 log_puti(d->bufsz); 556 log_puts("\n"); 557 } 558 } 559 #endif 560 d->sio.cstate = DEV_SIO_CYCLE; 561 break; 562 case DEV_SIO_CYCLE: 563 timo_del(&d->sio.watchdog); 564 timo_add(&d->sio.watchdog, WATCHDOG_USEC); 565 566 #ifdef DEBUG 567 /* 568 * check that we're called at cycle boundary: 569 * either after a recorded block, or when POLLOUT is 570 * raised 571 */ 572 if (!((d->mode & MODE_REC) && d->prime == 0) && 573 !(d->sio.events & POLLOUT)) { 574 dev_log(d); 575 log_puts(": cycle not at block boundary\n"); 576 panic(); 577 } 578 #endif 579 dev_cycle(d); 580 if (d->mode & MODE_PLAY) { 581 d->sio.cstate = DEV_SIO_WRITE; 582 d->sio.todo = d->round * d->pchan * d->par.bps; 583 break; 584 } else { 585 d->sio.cstate = DEV_SIO_READ; 586 d->sio.todo = d->round * d->rchan * d->par.bps; 587 return; 588 } 589 case DEV_SIO_WRITE: 590 #ifdef DEBUG 591 if (d->sio.todo == 0) { 592 dev_log(d); 593 log_puts(": can't write data\n"); 594 panic(); 595 } 596 #endif 597 base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d); 598 data = base + 599 d->pchan * d->round * d->par.bps - 600 d->sio.todo; 601 n = sio_write(d->sio.hdl, data, d->sio.todo); 602 d->sio.todo -= n; 603 #ifdef DEBUG 604 if (log_level >= 4) { 605 dev_log(d); 606 log_puts(": wrote "); 607 log_putu(n); 608 log_puts(" bytes, todo "); 609 log_putu(d->sio.todo); 610 log_puts("/"); 611 log_putu(d->round * d->pchan * d->par.bps); 612 log_puts("\n"); 613 } 614 #endif 615 if (d->sio.todo > 0) 616 return; 617 #ifdef DEBUG 618 d->sio.pused += d->round; 619 if (log_level >= 2) { 620 if (d->prime == 0 && 621 d->sio.pused <= d->bufsz - d->round) { 622 dev_log(d); 623 log_puts(": play hw xrun, pused = "); 624 log_puti(d->sio.pused); 625 log_puts("/"); 626 log_puti(d->bufsz); 627 log_puts("\n"); 628 } 629 if (d->sio.pused < 0 || 630 d->sio.pused > d->bufsz) { 631 /* device driver or libsndio bug */ 632 dev_log(d); 633 log_puts(": out of bounds pused = "); 634 log_puti(d->sio.pused); 635 log_puts("/"); 636 log_puti(d->bufsz); 637 log_puts("\n"); 638 } 639 } 640 #endif 641 d->poffs += d->round; 642 if (d->poffs == d->psize) 643 d->poffs = 0; 644 if ((d->mode & MODE_REC) && d->prime == 0) { 645 d->sio.cstate = DEV_SIO_READ; 646 d->sio.todo = d->round * d->rchan * d->par.bps; 647 } else 648 d->sio.cstate = DEV_SIO_CYCLE; 649 return; 650 } 651 } 652 } 653 654 void 655 dev_sio_hup(void *arg) 656 { 657 struct dev *d = arg; 658 659 #ifdef DEBUG 660 if (log_level >= 2) { 661 dev_log(d); 662 log_puts(": disconnected\n"); 663 } 664 #endif 665 if (!dev_reopen(d)) 666 dev_abort(d); 667 } 668