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