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