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