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