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