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