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