siofile.c revision 1.4
1/* $OpenBSD: siofile.c,v 1.4 2014/03/17 17:16:06 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 "dsp.h" 30#include "file.h" 31#include "siofile.h" 32#include "utils.h" 33 34#define WATCHDOG_USEC 2000000 /* 2 seconds */ 35 36void dev_sio_onmove(void *, int); 37void dev_sio_timeout(void *); 38int dev_sio_pollfd(void *, struct pollfd *); 39int dev_sio_revents(void *, struct pollfd *); 40void dev_sio_run(void *); 41void dev_sio_hup(void *); 42 43struct fileops dev_sio_ops = { 44 "sio", 45 dev_sio_pollfd, 46 dev_sio_revents, 47 dev_sio_run, 48 dev_sio_run, 49 dev_sio_hup 50}; 51 52void 53dev_sio_onmove(void *arg, int delta) 54{ 55 struct dev *d = arg; 56 57#ifdef DEBUG 58 if (log_level >= 4) { 59 dev_log(d); 60 log_puts(": tick, delta = "); 61 log_puti(delta); 62 log_puts("\n"); 63 } 64 d->sio.sum_utime += file_utime - d->sio.utime; 65 d->sio.sum_wtime += file_wtime - d->sio.wtime; 66 d->sio.wtime = file_wtime; 67 d->sio.utime = file_utime; 68 if (d->mode & MODE_PLAY) 69 d->sio.pused -= delta; 70 if (d->mode & MODE_REC) 71 d->sio.rused += delta; 72#endif 73 dev_onmove(d, delta); 74} 75 76void 77dev_sio_timeout(void *arg) 78{ 79 struct dev *d = arg; 80 81 dev_log(d); 82 log_puts(": watchdog timeout\n"); 83 dev_close(d); 84} 85 86/* 87 * open the device. 88 */ 89int 90dev_sio_open(struct dev *d) 91{ 92 struct sio_par par; 93 unsigned int mode = d->mode & (MODE_PLAY | MODE_REC); 94 95 d->sio.hdl = sio_open(d->path, mode, 1); 96 if (d->sio.hdl == NULL) { 97 if (mode != (SIO_PLAY | SIO_REC)) 98 return 0; 99 d->sio.hdl = sio_open(d->path, SIO_PLAY, 1); 100 if (d->sio.hdl != NULL) 101 mode = SIO_PLAY; 102 else { 103 d->sio.hdl = sio_open(d->path, SIO_REC, 1); 104 if (d->sio.hdl != NULL) 105 mode = SIO_REC; 106 else 107 return 0; 108 } 109 if (log_level >= 1) { 110 log_puts("warning, device opened in "); 111 log_puts(mode == SIO_PLAY ? "play-only" : "rec-only"); 112 log_puts(" mode\n"); 113 } 114 } 115 sio_initpar(&par); 116 par.bits = d->par.bits; 117 par.bps = d->par.bps; 118 par.sig = d->par.sig; 119 par.le = d->par.le; 120 par.msb = d->par.msb; 121 if (mode & SIO_PLAY) 122 par.pchan = d->pchan; 123 if (mode & SIO_REC) 124 par.rchan = d->rchan; 125 if (d->bufsz) 126 par.appbufsz = d->bufsz; 127 if (d->round) 128 par.round = d->round; 129 if (d->rate) 130 par.rate = d->rate; 131 if (!sio_setpar(d->sio.hdl, &par)) 132 goto bad_close; 133 if (!sio_getpar(d->sio.hdl, &par)) 134 goto bad_close; 135 d->par.bits = par.bits; 136 d->par.bps = par.bps; 137 d->par.sig = par.sig; 138 d->par.le = par.le; 139 d->par.msb = par.msb; 140 if (mode & SIO_PLAY) 141 d->pchan = par.pchan; 142 if (mode & SIO_REC) 143 d->rchan = par.rchan; 144 d->bufsz = par.bufsz; 145 d->round = par.round; 146 d->rate = par.rate; 147 if (!(mode & MODE_PLAY)) 148 d->mode &= ~(MODE_PLAY | MODE_MON); 149 if (!(mode & MODE_REC)) 150 d->mode &= ~MODE_REC; 151 sio_onmove(d->sio.hdl, dev_sio_onmove, d); 152 d->sio.file = file_new(&dev_sio_ops, d, d->path, sio_nfds(d->sio.hdl)); 153 timo_set(&d->sio.watchdog, dev_sio_timeout, d); 154 return 1; 155 bad_close: 156 sio_close(d->sio.hdl); 157 return 0; 158} 159 160void 161dev_sio_close(struct dev *d) 162{ 163#ifdef DEBUG 164 if (log_level >= 3) { 165 dev_log(d); 166 log_puts(": closed\n"); 167 } 168#endif 169 file_del(d->sio.file); 170 sio_close(d->sio.hdl); 171} 172 173void 174dev_sio_start(struct dev *d) 175{ 176 if (!sio_start(d->sio.hdl)) { 177 if (log_level >= 1) { 178 dev_log(d); 179 log_puts(": failed to start device\n"); 180 } 181 return; 182 } 183 if (d->mode & MODE_PLAY) { 184 d->sio.cstate = DEV_SIO_CYCLE; 185 d->sio.todo = 0; 186 } else { 187 d->sio.cstate = DEV_SIO_READ; 188 d->sio.todo = d->round * d->rchan * d->par.bps; 189 } 190#ifdef DEBUG 191 d->sio.pused = 0; 192 d->sio.rused = 0; 193 d->sio.sum_utime = 0; 194 d->sio.sum_wtime = 0; 195 d->sio.wtime = file_wtime; 196 d->sio.utime = file_utime; 197 if (log_level >= 3) { 198 dev_log(d); 199 log_puts(": started\n"); 200 } 201#endif 202 timo_add(&d->sio.watchdog, WATCHDOG_USEC); 203} 204 205void 206dev_sio_stop(struct dev *d) 207{ 208 if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) { 209 if (log_level >= 1) { 210 dev_log(d); 211 log_puts(": failed to stop device\n"); 212 } 213 return; 214 } 215#ifdef DEBUG 216 if (log_level >= 3) { 217 dev_log(d); 218 log_puts(": stopped, load avg = "); 219 log_puti(d->sio.sum_utime / 1000); 220 log_puts(" / "); 221 log_puti(d->sio.sum_wtime / 1000); 222 log_puts("\n"); 223 } 224#endif 225 timo_del(&d->sio.watchdog); 226} 227 228int 229dev_sio_pollfd(void *arg, struct pollfd *pfd) 230{ 231 struct dev *d = arg; 232 int events; 233 234 events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT; 235 return sio_pollfd(d->sio.hdl, pfd, events); 236} 237 238int 239dev_sio_revents(void *arg, struct pollfd *pfd) 240{ 241 struct dev *d = arg; 242 int events; 243 244 events = sio_revents(d->sio.hdl, pfd); 245#ifdef DEBUG 246 d->sio.events = events; 247#endif 248 return events; 249} 250 251void 252dev_sio_run(void *arg) 253{ 254 struct dev *d = arg; 255 unsigned char *data, *base; 256 unsigned int n; 257 258 /* 259 * sio_read() and sio_write() would block at the end of the 260 * cycle so we *must* return and restart poll()'ing. Otherwise 261 * we may trigger dev_cycle() which would make all clients 262 * underrun (ex, on a play-only device) 263 */ 264 for (;;) { 265 if (d->pstate != DEV_RUN) 266 return; 267 switch (d->sio.cstate) { 268 case DEV_SIO_READ: 269#ifdef DEBUG 270 if (!(d->sio.events & POLLIN)) { 271 dev_log(d); 272 log_puts(": recording, but POLLIN not set\n"); 273 panic(); 274 } 275 if (d->sio.todo == 0) { 276 dev_log(d); 277 log_puts(": can't read data\n"); 278 panic(); 279 } 280 if (d->prime > 0) { 281 dev_log(d); 282 log_puts(": unexpected data\n"); 283 panic(); 284 } 285#endif 286 base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf; 287 data = base + 288 d->rchan * d->round * d->par.bps - 289 d->sio.todo; 290 n = sio_read(d->sio.hdl, data, d->sio.todo); 291 d->sio.todo -= n; 292#ifdef DEBUG 293 if (n == 0 && data == base && !sio_eof(d->sio.hdl)) { 294 dev_log(d); 295 log_puts(": read blocked at cycle start\n"); 296 } 297 if (log_level >= 4) { 298 dev_log(d); 299 log_puts(": read "); 300 log_putu(n); 301 log_puts(": bytes, todo "); 302 log_putu(d->sio.todo); 303 log_puts("/"); 304 log_putu(d->round * d->rchan * d->par.bps); 305 log_puts("\n"); 306 } 307#endif 308 if (d->sio.todo > 0) 309 return; 310#ifdef DEBUG 311 d->sio.rused -= d->round; 312 if (log_level >= 2) { 313 if (d->sio.rused >= d->round) { 314 dev_log(d); 315 log_puts(": rec hw xrun, rused = "); 316 log_puti(d->sio.rused); 317 log_puts("/"); 318 log_puti(d->bufsz); 319 log_puts("\n"); 320 } 321 if (d->sio.rused < 0 || 322 d->sio.rused >= d->bufsz) { 323 dev_log(d); 324 log_puts(": out of bounds rused = "); 325 log_puti(d->sio.rused); 326 log_puts("/"); 327 log_puti(d->bufsz); 328 log_puts("\n"); 329 } 330 } 331#endif 332 d->sio.cstate = DEV_SIO_CYCLE; 333 break; 334 case DEV_SIO_CYCLE: 335 timo_del(&d->sio.watchdog); 336 timo_add(&d->sio.watchdog, WATCHDOG_USEC); 337 338#ifdef DEBUG 339 /* 340 * check that we're called at cycle boundary: 341 * either after a recorded block, or when POLLOUT is 342 * raised 343 */ 344 if (!((d->mode & MODE_REC) && d->prime == 0) && 345 !(d->sio.events & POLLOUT)) { 346 dev_log(d); 347 log_puts(": cycle not at block boundary\n"); 348 panic(); 349 } 350#endif 351 dev_cycle(d); 352 if (d->mode & MODE_PLAY) { 353 d->sio.cstate = DEV_SIO_WRITE; 354 d->sio.todo = d->round * d->pchan * d->par.bps; 355 break; 356 } else { 357 d->sio.cstate = DEV_SIO_READ; 358 d->sio.todo = d->round * d->rchan * d->par.bps; 359 return; 360 } 361 case DEV_SIO_WRITE: 362#ifdef DEBUG 363 if (d->sio.todo == 0) { 364 dev_log(d); 365 log_puts(": can't write data\n"); 366 panic(); 367 } 368#endif 369 base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d); 370 data = base + 371 d->pchan * d->round * d->par.bps - 372 d->sio.todo; 373 n = sio_write(d->sio.hdl, data, d->sio.todo); 374 d->sio.todo -= n; 375#ifdef DEBUG 376 if (n == 0 && data == base && !sio_eof(d->sio.hdl)) { 377 dev_log(d); 378 log_puts(": write blocked at cycle start\n"); 379 } 380 if (log_level >= 4) { 381 dev_log(d); 382 log_puts(": wrote "); 383 log_putu(n); 384 log_puts(" bytes, todo "); 385 log_putu(d->sio.todo); 386 log_puts("/"); 387 log_putu(d->round * d->pchan * d->par.bps); 388 log_puts("\n"); 389 } 390#endif 391 if (d->sio.todo > 0) 392 return; 393#ifdef DEBUG 394 d->sio.pused += d->round; 395 if (log_level >= 2) { 396 if (d->prime == 0 && 397 d->sio.pused <= d->bufsz - d->round) { 398 dev_log(d); 399 log_puts(": play hw xrun, pused = "); 400 log_puti(d->sio.pused); 401 log_puts("/"); 402 log_puti(d->bufsz); 403 log_puts("\n"); 404 } 405 if (d->sio.pused < 0 || 406 d->sio.pused > d->bufsz) { 407 /* device driver or libsndio bug */ 408 dev_log(d); 409 log_puts(": out of bounds pused = "); 410 log_puti(d->sio.pused); 411 log_puts("/"); 412 log_puti(d->bufsz); 413 log_puts("\n"); 414 } 415 } 416#endif 417 d->poffs += d->round; 418 if (d->poffs == d->psize) 419 d->poffs = 0; 420 if ((d->mode & MODE_REC) && d->prime == 0) { 421 d->sio.cstate = DEV_SIO_READ; 422 d->sio.todo = d->round * d->rchan * d->par.bps; 423 } else 424 d->sio.cstate = DEV_SIO_CYCLE; 425 return; 426 } 427 } 428} 429 430void 431dev_sio_hup(void *arg) 432{ 433 struct dev *d = arg; 434 435 dev_close(d); 436} 437