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