Annotation of src/usr.bin/sndiod/siofile.c, Revision 1.3
1.3 ! ratchov 1: /* $OpenBSD: siofile.c,v 1.2 2013/02/01 09:06:27 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;
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));
1.2 ratchov 153: timo_set(&d->sio.watchdog, dev_sio_timeout, d);
1.1 ratchov 154: return 1;
155: bad_close:
156: sio_close(d->sio.hdl);
157: return 0;
158: }
159:
160: void
161: dev_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:
173: void
174: dev_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
1.2 ratchov 202: timo_add(&d->sio.watchdog, WATCHDOG_USEC);
1.1 ratchov 203: }
204:
205: void
206: dev_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
1.2 ratchov 225: timo_del(&d->sio.watchdog);
1.1 ratchov 226: }
227:
228: int
229: dev_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:
238: int
239: dev_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:
251: void
252: dev_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:
1.2 ratchov 335: timo_del(&d->sio.watchdog);
336: timo_add(&d->sio.watchdog, WATCHDOG_USEC);
337:
1.1 ratchov 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->bufsz)
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:
430: void
431: dev_sio_hup(void *arg)
432: {
433: struct dev *d = arg;
434:
435: dev_close(d);
436: }