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