Annotation of src/usr.bin/sndiod/siofile.c, Revision 1.8
1.8 ! ratchov 1: /* $OpenBSD: siofile.c,v 1.7 2015/07/24 08:43:04 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
1.7 ratchov 231: timo_del(&d->sio.watchdog);
1.1 ratchov 232: file_del(d->sio.file);
1.6 ratchov 233: sio_close(d->sio.hdl);
1.1 ratchov 234: }
235:
236: void
237: dev_sio_start(struct dev *d)
238: {
239: if (!sio_start(d->sio.hdl)) {
240: if (log_level >= 1) {
241: dev_log(d);
242: log_puts(": failed to start device\n");
243: }
244: return;
245: }
246: if (d->mode & MODE_PLAY) {
247: d->sio.cstate = DEV_SIO_CYCLE;
248: d->sio.todo = 0;
249: } else {
250: d->sio.cstate = DEV_SIO_READ;
251: d->sio.todo = d->round * d->rchan * d->par.bps;
252: }
253: #ifdef DEBUG
254: d->sio.pused = 0;
255: d->sio.rused = 0;
256: d->sio.sum_utime = 0;
257: d->sio.sum_wtime = 0;
258: d->sio.wtime = file_wtime;
259: d->sio.utime = file_utime;
260: if (log_level >= 3) {
261: dev_log(d);
262: log_puts(": started\n");
263: }
264: #endif
1.2 ratchov 265: timo_add(&d->sio.watchdog, WATCHDOG_USEC);
1.1 ratchov 266: }
267:
268: void
269: dev_sio_stop(struct dev *d)
270: {
271: if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) {
272: if (log_level >= 1) {
273: dev_log(d);
274: log_puts(": failed to stop device\n");
275: }
276: return;
277: }
278: #ifdef DEBUG
279: if (log_level >= 3) {
280: dev_log(d);
281: log_puts(": stopped, load avg = ");
282: log_puti(d->sio.sum_utime / 1000);
283: log_puts(" / ");
284: log_puti(d->sio.sum_wtime / 1000);
285: log_puts("\n");
286: }
287: #endif
1.2 ratchov 288: timo_del(&d->sio.watchdog);
1.1 ratchov 289: }
290:
291: int
292: dev_sio_pollfd(void *arg, struct pollfd *pfd)
293: {
294: struct dev *d = arg;
295: int events;
296:
297: events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
298: return sio_pollfd(d->sio.hdl, pfd, events);
299: }
300:
301: int
302: dev_sio_revents(void *arg, struct pollfd *pfd)
303: {
304: struct dev *d = arg;
305: int events;
306:
307: events = sio_revents(d->sio.hdl, pfd);
308: #ifdef DEBUG
309: d->sio.events = events;
310: #endif
311: return events;
312: }
313:
314: void
315: dev_sio_run(void *arg)
316: {
317: struct dev *d = arg;
318: unsigned char *data, *base;
319: unsigned int n;
320:
321: /*
322: * sio_read() and sio_write() would block at the end of the
323: * cycle so we *must* return and restart poll()'ing. Otherwise
324: * we may trigger dev_cycle() which would make all clients
325: * underrun (ex, on a play-only device)
326: */
327: for (;;) {
328: if (d->pstate != DEV_RUN)
329: return;
330: switch (d->sio.cstate) {
331: case DEV_SIO_READ:
332: #ifdef DEBUG
333: if (!(d->sio.events & POLLIN)) {
334: dev_log(d);
335: log_puts(": recording, but POLLIN not set\n");
336: panic();
337: }
338: if (d->sio.todo == 0) {
339: dev_log(d);
340: log_puts(": can't read data\n");
341: panic();
342: }
343: if (d->prime > 0) {
344: dev_log(d);
345: log_puts(": unexpected data\n");
346: panic();
347: }
348: #endif
349: base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf;
350: data = base +
351: d->rchan * d->round * d->par.bps -
352: d->sio.todo;
353: n = sio_read(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(": read blocked at cycle start\n");
359: }
360: if (log_level >= 4) {
361: dev_log(d);
362: log_puts(": read ");
363: log_putu(n);
364: log_puts(": bytes, todo ");
365: log_putu(d->sio.todo);
366: log_puts("/");
367: log_putu(d->round * d->rchan * d->par.bps);
368: log_puts("\n");
369: }
370: #endif
371: if (d->sio.todo > 0)
372: return;
373: #ifdef DEBUG
374: d->sio.rused -= d->round;
375: if (log_level >= 2) {
376: if (d->sio.rused >= d->round) {
377: dev_log(d);
378: log_puts(": rec hw xrun, rused = ");
379: log_puti(d->sio.rused);
380: log_puts("/");
381: log_puti(d->bufsz);
382: log_puts("\n");
383: }
384: if (d->sio.rused < 0 ||
385: d->sio.rused >= d->bufsz) {
386: dev_log(d);
387: log_puts(": out of bounds rused = ");
388: log_puti(d->sio.rused);
389: log_puts("/");
390: log_puti(d->bufsz);
391: log_puts("\n");
392: }
393: }
394: #endif
395: d->sio.cstate = DEV_SIO_CYCLE;
396: break;
397: case DEV_SIO_CYCLE:
1.2 ratchov 398: timo_del(&d->sio.watchdog);
399: timo_add(&d->sio.watchdog, WATCHDOG_USEC);
400:
1.1 ratchov 401: #ifdef DEBUG
402: /*
403: * check that we're called at cycle boundary:
404: * either after a recorded block, or when POLLOUT is
405: * raised
406: */
407: if (!((d->mode & MODE_REC) && d->prime == 0) &&
408: !(d->sio.events & POLLOUT)) {
409: dev_log(d);
410: log_puts(": cycle not at block boundary\n");
411: panic();
412: }
413: #endif
414: dev_cycle(d);
415: if (d->mode & MODE_PLAY) {
416: d->sio.cstate = DEV_SIO_WRITE;
417: d->sio.todo = d->round * d->pchan * d->par.bps;
418: break;
419: } else {
420: d->sio.cstate = DEV_SIO_READ;
421: d->sio.todo = d->round * d->rchan * d->par.bps;
422: return;
423: }
424: case DEV_SIO_WRITE:
425: #ifdef DEBUG
426: if (d->sio.todo == 0) {
427: dev_log(d);
428: log_puts(": can't write data\n");
429: panic();
430: }
431: #endif
432: base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d);
433: data = base +
434: d->pchan * d->round * d->par.bps -
435: d->sio.todo;
436: n = sio_write(d->sio.hdl, data, d->sio.todo);
437: d->sio.todo -= n;
438: #ifdef DEBUG
439: if (n == 0 && data == base && !sio_eof(d->sio.hdl)) {
440: dev_log(d);
441: log_puts(": write blocked at cycle start\n");
442: }
443: if (log_level >= 4) {
444: dev_log(d);
445: log_puts(": wrote ");
446: log_putu(n);
447: log_puts(" bytes, todo ");
448: log_putu(d->sio.todo);
449: log_puts("/");
450: log_putu(d->round * d->pchan * d->par.bps);
451: log_puts("\n");
452: }
453: #endif
454: if (d->sio.todo > 0)
455: return;
456: #ifdef DEBUG
457: d->sio.pused += d->round;
458: if (log_level >= 2) {
459: if (d->prime == 0 &&
460: d->sio.pused <= d->bufsz - d->round) {
461: dev_log(d);
462: log_puts(": play hw xrun, pused = ");
463: log_puti(d->sio.pused);
464: log_puts("/");
465: log_puti(d->bufsz);
466: log_puts("\n");
467: }
468: if (d->sio.pused < 0 ||
469: d->sio.pused > d->bufsz) {
470: /* device driver or libsndio bug */
471: dev_log(d);
472: log_puts(": out of bounds pused = ");
473: log_puti(d->sio.pused);
474: log_puts("/");
475: log_puti(d->bufsz);
476: log_puts("\n");
477: }
478: }
479: #endif
480: d->poffs += d->round;
1.4 ratchov 481: if (d->poffs == d->psize)
1.1 ratchov 482: d->poffs = 0;
483: if ((d->mode & MODE_REC) && d->prime == 0) {
484: d->sio.cstate = DEV_SIO_READ;
485: d->sio.todo = d->round * d->rchan * d->par.bps;
486: } else
487: d->sio.cstate = DEV_SIO_CYCLE;
488: return;
489: }
490: }
491: }
492:
493: void
494: dev_sio_hup(void *arg)
495: {
496: struct dev *d = arg;
497:
1.8 ! ratchov 498: #ifdef DEBUG
! 499: if (log_level >= 2) {
! 500: dev_log(d);
! 501: log_puts(": disconnected\n");
! 502: }
! 503: #endif
1.1 ratchov 504: dev_close(d);
505: }