Annotation of src/usr.bin/sndiod/siofile.c, Revision 1.5
1.5 ! ratchov 1: /* $OpenBSD: siofile.c,v 1.4 2014/03/17 17:16:06 ratchov Exp $ */
1.1 ratchov 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:
1.2 ratchov 34: #define WATCHDOG_USEC 2000000 /* 2 seconds */
35:
1.3 ratchov 36: void dev_sio_onmove(void *, int);
37: void dev_sio_timeout(void *);
1.1 ratchov 38: int dev_sio_pollfd(void *, struct pollfd *);
39: int dev_sio_revents(void *, struct pollfd *);
40: void dev_sio_run(void *);
41: void dev_sio_hup(void *);
42:
43: struct 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:
52: void
53: dev_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:
1.2 ratchov 76: void
77: dev_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:
1.1 ratchov 86: /*
87: * open the device.
88: */
89: int
90: dev_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;
1.5 ! ratchov 135:
! 136: #ifdef DEBUG
! 137: /*
! 138: * We support any parameters combination exposed by the kernel,
! 139: * and we have no other choice than trusting the kernel for
! 140: * returning correct parameters. But let's check parameters
! 141: * early and nicely report kernel bugs rather than crashing
! 142: * later in memset(), malloc() or alike.
! 143: */
! 144:
! 145: if (par.bits > BITS_MAX) {
! 146: log_puts(d->path);
! 147: log_puts(": ");
! 148: log_putu(par.bits);
! 149: log_puts(": unsupported number of bits\n");
! 150: goto bad_close;
! 151: }
! 152: if (par.bps > SIO_BPS(BITS_MAX)) {
! 153: log_puts(d->path);
! 154: log_puts(": ");
! 155: log_putu(par.bits);
! 156: log_puts(": unsupported sample size\n");
! 157: goto bad_close;
! 158: }
! 159: if ((mode & SIO_PLAY) && par.pchan > NCHAN_MAX) {
! 160: log_puts(d->path);
! 161: log_puts(": ");
! 162: log_putu(par.pchan);
! 163: log_puts(": unsupported number of play channels\n");
! 164: goto bad_close;
! 165: }
! 166: if ((mode & SIO_REC) && par.rchan > NCHAN_MAX) {
! 167: log_puts(d->path);
! 168: log_puts(": ");
! 169: log_putu(par.rchan);
! 170: log_puts(": unsupported number of rec channels\n");
! 171: goto bad_close;
! 172: }
! 173: if (par.bufsz == 0 || par.bufsz > RATE_MAX) {
! 174: log_puts(d->path);
! 175: log_puts(": ");
! 176: log_putu(par.bufsz);
! 177: log_puts(": unsupported buffer size\n");
! 178: goto bad_close;
! 179: }
! 180: if (par.round == 0 || par.round > par.bufsz ||
! 181: par.bufsz % par.round != 0) {
! 182: log_puts(d->path);
! 183: log_puts(": ");
! 184: log_putu(par.round);
! 185: log_puts(": unsupported block size\n");
! 186: goto bad_close;
! 187: }
! 188: if (par.rate == 0 || par.rate > RATE_MAX) {
! 189: log_puts(d->path);
! 190: log_puts(": ");
! 191: log_putu(par.rate);
! 192: log_puts(": unsupported rate\n");
! 193: goto bad_close;
! 194: }
! 195: #endif
! 196:
1.1 ratchov 197: d->par.bits = par.bits;
198: d->par.bps = par.bps;
199: d->par.sig = par.sig;
200: d->par.le = par.le;
201: d->par.msb = par.msb;
202: if (mode & SIO_PLAY)
203: d->pchan = par.pchan;
204: if (mode & SIO_REC)
205: d->rchan = par.rchan;
206: d->bufsz = par.bufsz;
207: d->round = par.round;
208: d->rate = par.rate;
209: if (!(mode & MODE_PLAY))
210: d->mode &= ~(MODE_PLAY | MODE_MON);
211: if (!(mode & MODE_REC))
212: d->mode &= ~MODE_REC;
213: sio_onmove(d->sio.hdl, dev_sio_onmove, d);
214: d->sio.file = file_new(&dev_sio_ops, d, d->path, sio_nfds(d->sio.hdl));
1.2 ratchov 215: timo_set(&d->sio.watchdog, dev_sio_timeout, d);
1.1 ratchov 216: return 1;
217: bad_close:
218: sio_close(d->sio.hdl);
219: return 0;
220: }
221:
222: void
223: dev_sio_close(struct dev *d)
224: {
225: #ifdef DEBUG
226: if (log_level >= 3) {
227: dev_log(d);
228: log_puts(": closed\n");
229: }
230: #endif
231: file_del(d->sio.file);
232: sio_close(d->sio.hdl);
233: }
234:
235: void
236: dev_sio_start(struct dev *d)
237: {
238: if (!sio_start(d->sio.hdl)) {
239: if (log_level >= 1) {
240: dev_log(d);
241: log_puts(": failed to start device\n");
242: }
243: return;
244: }
245: if (d->mode & MODE_PLAY) {
246: d->sio.cstate = DEV_SIO_CYCLE;
247: d->sio.todo = 0;
248: } else {
249: d->sio.cstate = DEV_SIO_READ;
250: d->sio.todo = d->round * d->rchan * d->par.bps;
251: }
252: #ifdef DEBUG
253: d->sio.pused = 0;
254: d->sio.rused = 0;
255: d->sio.sum_utime = 0;
256: d->sio.sum_wtime = 0;
257: d->sio.wtime = file_wtime;
258: d->sio.utime = file_utime;
259: if (log_level >= 3) {
260: dev_log(d);
261: log_puts(": started\n");
262: }
263: #endif
1.2 ratchov 264: timo_add(&d->sio.watchdog, WATCHDOG_USEC);
1.1 ratchov 265: }
266:
267: void
268: dev_sio_stop(struct dev *d)
269: {
270: if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) {
271: if (log_level >= 1) {
272: dev_log(d);
273: log_puts(": failed to stop device\n");
274: }
275: return;
276: }
277: #ifdef DEBUG
278: if (log_level >= 3) {
279: dev_log(d);
280: log_puts(": stopped, load avg = ");
281: log_puti(d->sio.sum_utime / 1000);
282: log_puts(" / ");
283: log_puti(d->sio.sum_wtime / 1000);
284: log_puts("\n");
285: }
286: #endif
1.2 ratchov 287: timo_del(&d->sio.watchdog);
1.1 ratchov 288: }
289:
290: int
291: dev_sio_pollfd(void *arg, struct pollfd *pfd)
292: {
293: struct dev *d = arg;
294: int events;
295:
296: events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
297: return sio_pollfd(d->sio.hdl, pfd, events);
298: }
299:
300: int
301: dev_sio_revents(void *arg, struct pollfd *pfd)
302: {
303: struct dev *d = arg;
304: int events;
305:
306: events = sio_revents(d->sio.hdl, pfd);
307: #ifdef DEBUG
308: d->sio.events = events;
309: #endif
310: return events;
311: }
312:
313: void
314: dev_sio_run(void *arg)
315: {
316: struct dev *d = arg;
317: unsigned char *data, *base;
318: unsigned int n;
319:
320: /*
321: * sio_read() and sio_write() would block at the end of the
322: * cycle so we *must* return and restart poll()'ing. Otherwise
323: * we may trigger dev_cycle() which would make all clients
324: * underrun (ex, on a play-only device)
325: */
326: for (;;) {
327: if (d->pstate != DEV_RUN)
328: return;
329: switch (d->sio.cstate) {
330: case DEV_SIO_READ:
331: #ifdef DEBUG
332: if (!(d->sio.events & POLLIN)) {
333: dev_log(d);
334: log_puts(": recording, but POLLIN not set\n");
335: panic();
336: }
337: if (d->sio.todo == 0) {
338: dev_log(d);
339: log_puts(": can't read data\n");
340: panic();
341: }
342: if (d->prime > 0) {
343: dev_log(d);
344: log_puts(": unexpected data\n");
345: panic();
346: }
347: #endif
348: base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf;
349: data = base +
350: d->rchan * d->round * d->par.bps -
351: d->sio.todo;
352: n = sio_read(d->sio.hdl, data, d->sio.todo);
353: d->sio.todo -= n;
354: #ifdef DEBUG
355: if (n == 0 && data == base && !sio_eof(d->sio.hdl)) {
356: dev_log(d);
357: log_puts(": read blocked at cycle start\n");
358: }
359: if (log_level >= 4) {
360: dev_log(d);
361: log_puts(": read ");
362: log_putu(n);
363: log_puts(": bytes, todo ");
364: log_putu(d->sio.todo);
365: log_puts("/");
366: log_putu(d->round * d->rchan * d->par.bps);
367: log_puts("\n");
368: }
369: #endif
370: if (d->sio.todo > 0)
371: return;
372: #ifdef DEBUG
373: d->sio.rused -= d->round;
374: if (log_level >= 2) {
375: if (d->sio.rused >= d->round) {
376: dev_log(d);
377: log_puts(": rec hw xrun, rused = ");
378: log_puti(d->sio.rused);
379: log_puts("/");
380: log_puti(d->bufsz);
381: log_puts("\n");
382: }
383: if (d->sio.rused < 0 ||
384: d->sio.rused >= d->bufsz) {
385: dev_log(d);
386: log_puts(": out of bounds rused = ");
387: log_puti(d->sio.rused);
388: log_puts("/");
389: log_puti(d->bufsz);
390: log_puts("\n");
391: }
392: }
393: #endif
394: d->sio.cstate = DEV_SIO_CYCLE;
395: break;
396: case DEV_SIO_CYCLE:
1.2 ratchov 397: timo_del(&d->sio.watchdog);
398: timo_add(&d->sio.watchdog, WATCHDOG_USEC);
399:
1.1 ratchov 400: #ifdef DEBUG
401: /*
402: * check that we're called at cycle boundary:
403: * either after a recorded block, or when POLLOUT is
404: * raised
405: */
406: if (!((d->mode & MODE_REC) && d->prime == 0) &&
407: !(d->sio.events & POLLOUT)) {
408: dev_log(d);
409: log_puts(": cycle not at block boundary\n");
410: panic();
411: }
412: #endif
413: dev_cycle(d);
414: if (d->mode & MODE_PLAY) {
415: d->sio.cstate = DEV_SIO_WRITE;
416: d->sio.todo = d->round * d->pchan * d->par.bps;
417: break;
418: } else {
419: d->sio.cstate = DEV_SIO_READ;
420: d->sio.todo = d->round * d->rchan * d->par.bps;
421: return;
422: }
423: case DEV_SIO_WRITE:
424: #ifdef DEBUG
425: if (d->sio.todo == 0) {
426: dev_log(d);
427: log_puts(": can't write data\n");
428: panic();
429: }
430: #endif
431: base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d);
432: data = base +
433: d->pchan * d->round * d->par.bps -
434: d->sio.todo;
435: n = sio_write(d->sio.hdl, data, d->sio.todo);
436: d->sio.todo -= n;
437: #ifdef DEBUG
438: if (n == 0 && data == base && !sio_eof(d->sio.hdl)) {
439: dev_log(d);
440: log_puts(": write blocked at cycle start\n");
441: }
442: if (log_level >= 4) {
443: dev_log(d);
444: log_puts(": wrote ");
445: log_putu(n);
446: log_puts(" bytes, todo ");
447: log_putu(d->sio.todo);
448: log_puts("/");
449: log_putu(d->round * d->pchan * d->par.bps);
450: log_puts("\n");
451: }
452: #endif
453: if (d->sio.todo > 0)
454: return;
455: #ifdef DEBUG
456: d->sio.pused += d->round;
457: if (log_level >= 2) {
458: if (d->prime == 0 &&
459: d->sio.pused <= d->bufsz - d->round) {
460: dev_log(d);
461: log_puts(": play hw xrun, pused = ");
462: log_puti(d->sio.pused);
463: log_puts("/");
464: log_puti(d->bufsz);
465: log_puts("\n");
466: }
467: if (d->sio.pused < 0 ||
468: d->sio.pused > d->bufsz) {
469: /* device driver or libsndio bug */
470: dev_log(d);
471: log_puts(": out of bounds pused = ");
472: log_puti(d->sio.pused);
473: log_puts("/");
474: log_puti(d->bufsz);
475: log_puts("\n");
476: }
477: }
478: #endif
479: d->poffs += d->round;
1.4 ratchov 480: if (d->poffs == d->psize)
1.1 ratchov 481: d->poffs = 0;
482: if ((d->mode & MODE_REC) && d->prime == 0) {
483: d->sio.cstate = DEV_SIO_READ;
484: d->sio.todo = d->round * d->rchan * d->par.bps;
485: } else
486: d->sio.cstate = DEV_SIO_CYCLE;
487: return;
488: }
489: }
490: }
491:
492: void
493: dev_sio_hup(void *arg)
494: {
495: struct dev *d = arg;
496:
497: dev_close(d);
498: }