Annotation of src/usr.bin/sndiod/siofile.c, Revision 1.24
1.24 ! ratchov 1: /* $OpenBSD: siofile.c,v 1.23 2021/03/02 12:15:46 edd 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"
1.18 ratchov 29: #include "dev_sioctl.h"
1.1 ratchov 30: #include "dsp.h"
1.10 ratchov 31: #include "fdpass.h"
1.1 ratchov 32: #include "file.h"
33: #include "siofile.h"
34: #include "utils.h"
35:
1.9 ratchov 36: #define WATCHDOG_USEC 4000000 /* 4 seconds */
1.2 ratchov 37:
1.3 ratchov 38: void dev_sio_onmove(void *, int);
39: void dev_sio_timeout(void *);
1.1 ratchov 40: int dev_sio_pollfd(void *, struct pollfd *);
41: int dev_sio_revents(void *, struct pollfd *);
42: void dev_sio_run(void *);
43: void dev_sio_hup(void *);
44:
1.19 ratchov 45: extern struct fileops dev_sioctl_ops;
46:
1.1 ratchov 47: struct fileops dev_sio_ops = {
48: "sio",
49: dev_sio_pollfd,
50: dev_sio_revents,
51: dev_sio_run,
52: dev_sio_run,
53: dev_sio_hup
54: };
55:
56: void
57: dev_sio_onmove(void *arg, int delta)
58: {
59: struct dev *d = arg;
60:
61: #ifdef DEBUG
62: if (log_level >= 4) {
63: dev_log(d);
64: log_puts(": tick, delta = ");
65: log_puti(delta);
66: log_puts("\n");
67: }
68: d->sio.sum_utime += file_utime - d->sio.utime;
69: d->sio.sum_wtime += file_wtime - d->sio.wtime;
70: d->sio.wtime = file_wtime;
71: d->sio.utime = file_utime;
72: if (d->mode & MODE_PLAY)
73: d->sio.pused -= delta;
74: if (d->mode & MODE_REC)
75: d->sio.rused += delta;
76: #endif
77: dev_onmove(d, delta);
78: }
79:
1.2 ratchov 80: void
81: dev_sio_timeout(void *arg)
82: {
83: struct dev *d = arg;
84:
85: dev_log(d);
86: log_puts(": watchdog timeout\n");
1.20 ratchov 87: dev_abort(d);
1.2 ratchov 88: }
89:
1.23 edd 90: static int
91: dev_sio_openalt(struct dev *d, struct dev_alt *n,
92: struct sio_hdl **rhdl, struct sioctl_hdl **rctlhdl, unsigned int *rmode)
93: {
94: struct sio_hdl *hdl;
95: struct sioctl_hdl *ctlhdl;
96: unsigned int mode = d->reqmode & (MODE_PLAY | MODE_REC);
97:
98: hdl = fdpass_sio_open(d->num, n->idx, mode);
99: if (hdl == NULL) {
100: if (mode != (SIO_PLAY | SIO_REC))
101: return 0;
102: hdl = fdpass_sio_open(d->num, n->idx, SIO_PLAY);
103: if (hdl != NULL)
104: mode = SIO_PLAY;
105: else {
106: hdl = fdpass_sio_open(d->num, n->idx, SIO_REC);
107: if (hdl != NULL)
108: mode = SIO_REC;
109: else
110: return 0;
111: }
112: if (log_level >= 1) {
113: log_puts("warning, device opened in ");
114: log_puts(mode == SIO_PLAY ? "play-only" : "rec-only");
115: log_puts(" mode\n");
116: }
117: }
118:
119: ctlhdl = fdpass_sioctl_open(d->num, n->idx, SIOCTL_READ | SIOCTL_WRITE);
120: if (ctlhdl == NULL) {
121: if (log_level >= 1) {
122: dev_log(d);
123: log_puts(": no control device\n");
124: }
125: }
126:
127: *rhdl = hdl;
128: *rctlhdl = ctlhdl;
129: *rmode = mode;
130: return 1;
131: }
132:
1.1 ratchov 133: /*
1.17 ratchov 134: * open the device using one of the provided paths
135: */
1.23 edd 136: static int
137: dev_sio_openlist(struct dev *d,
138: struct sio_hdl **rhdl, struct sioctl_hdl **rctlhdl, unsigned int *rmode)
1.17 ratchov 139: {
1.21 ratchov 140: struct dev_alt *n;
1.22 ratchov 141: struct ctl *c;
142: int val;
1.17 ratchov 143:
1.21 ratchov 144: for (n = d->alt_list; n != NULL; n = n->next) {
145: if (d->alt_num == n->idx)
146: continue;
1.23 edd 147: if (log_level >= 2) {
148: dev_log(d);
149: log_puts(": trying ");
150: log_puts(n->name);
151: log_puts("\n");
152: }
153: if (dev_sio_openalt(d, n, rhdl, rctlhdl, rmode)) {
1.17 ratchov 154: if (log_level >= 2) {
155: dev_log(d);
156: log_puts(": using ");
1.21 ratchov 157: log_puts(n->name);
1.17 ratchov 158: log_puts("\n");
159: }
1.21 ratchov 160: d->alt_num = n->idx;
1.24 ! ratchov 161: for (c = ctl_list; c != NULL; c = c->next) {
! 162: if (!ctl_match(c, CTL_DEV_ALT, d, NULL))
1.22 ratchov 163: continue;
1.24 ! ratchov 164: val = c->u.dev_alt.idx == n->idx;
1.22 ratchov 165: if (c->curval == val)
166: continue;
167: c->curval = val;
168: if (val)
169: c->val_mask = ~0U;
170: }
1.23 edd 171: return 1;
1.17 ratchov 172: }
173: }
1.23 edd 174: return 0;
1.17 ratchov 175: }
176:
177: /*
1.1 ratchov 178: * open the device.
179: */
180: int
181: dev_sio_open(struct dev *d)
182: {
183: struct sio_par par;
184:
1.23 edd 185: if (!dev_sio_openlist(d, &d->sio.hdl, &d->sioctl.hdl, &d->mode))
186: return 0;
187:
1.1 ratchov 188: sio_initpar(&par);
189: par.bits = d->par.bits;
190: par.bps = d->par.bps;
191: par.sig = d->par.sig;
192: par.le = d->par.le;
193: par.msb = d->par.msb;
1.23 edd 194: if (d->mode & SIO_PLAY)
1.1 ratchov 195: par.pchan = d->pchan;
1.23 edd 196: if (d->mode & SIO_REC)
1.1 ratchov 197: par.rchan = d->rchan;
198: if (d->bufsz)
199: par.appbufsz = d->bufsz;
200: if (d->round)
201: par.round = d->round;
202: if (d->rate)
203: par.rate = d->rate;
204: if (!sio_setpar(d->sio.hdl, &par))
205: goto bad_close;
206: if (!sio_getpar(d->sio.hdl, &par))
207: goto bad_close;
1.5 ratchov 208:
209: #ifdef DEBUG
210: /*
1.13 ratchov 211: * We support any parameter combination exposed by the kernel,
1.5 ratchov 212: * and we have no other choice than trusting the kernel for
213: * returning correct parameters. But let's check parameters
214: * early and nicely report kernel bugs rather than crashing
215: * later in memset(), malloc() or alike.
216: */
217:
218: if (par.bits > BITS_MAX) {
1.15 ratchov 219: dev_log(d);
1.5 ratchov 220: log_puts(": ");
221: log_putu(par.bits);
222: log_puts(": unsupported number of bits\n");
223: goto bad_close;
224: }
225: if (par.bps > SIO_BPS(BITS_MAX)) {
1.15 ratchov 226: dev_log(d);
1.5 ratchov 227: log_puts(": ");
1.14 ratchov 228: log_putu(par.bps);
1.5 ratchov 229: log_puts(": unsupported sample size\n");
230: goto bad_close;
231: }
1.23 edd 232: if ((d->mode & SIO_PLAY) && par.pchan > NCHAN_MAX) {
1.15 ratchov 233: dev_log(d);
1.5 ratchov 234: log_puts(": ");
235: log_putu(par.pchan);
236: log_puts(": unsupported number of play channels\n");
237: goto bad_close;
1.11 ratchov 238: }
1.23 edd 239: if ((d->mode & SIO_REC) && par.rchan > NCHAN_MAX) {
1.15 ratchov 240: dev_log(d);
1.5 ratchov 241: log_puts(": ");
242: log_putu(par.rchan);
243: log_puts(": unsupported number of rec channels\n");
244: goto bad_close;
245: }
246: if (par.bufsz == 0 || par.bufsz > RATE_MAX) {
1.15 ratchov 247: dev_log(d);
1.5 ratchov 248: log_puts(": ");
249: log_putu(par.bufsz);
250: log_puts(": unsupported buffer size\n");
251: goto bad_close;
252: }
253: if (par.round == 0 || par.round > par.bufsz ||
254: par.bufsz % par.round != 0) {
1.15 ratchov 255: dev_log(d);
1.5 ratchov 256: log_puts(": ");
257: log_putu(par.round);
258: log_puts(": unsupported block size\n");
259: goto bad_close;
260: }
261: if (par.rate == 0 || par.rate > RATE_MAX) {
1.15 ratchov 262: dev_log(d);
1.5 ratchov 263: log_puts(": ");
264: log_putu(par.rate);
265: log_puts(": unsupported rate\n");
266: goto bad_close;
267: }
268: #endif
269:
1.1 ratchov 270: d->par.bits = par.bits;
271: d->par.bps = par.bps;
272: d->par.sig = par.sig;
273: d->par.le = par.le;
274: d->par.msb = par.msb;
1.23 edd 275: if (d->mode & SIO_PLAY)
1.1 ratchov 276: d->pchan = par.pchan;
1.23 edd 277: if (d->mode & SIO_REC)
1.1 ratchov 278: d->rchan = par.rchan;
279: d->bufsz = par.bufsz;
280: d->round = par.round;
281: d->rate = par.rate;
1.23 edd 282: if (d->mode & MODE_PLAY)
283: d->mode |= MODE_MON;
1.1 ratchov 284: sio_onmove(d->sio.hdl, dev_sio_onmove, d);
1.16 ratchov 285: d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(d->sio.hdl));
1.19 ratchov 286: if (d->sioctl.hdl) {
287: d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix",
288: sioctl_nfds(d->sioctl.hdl));
289: }
1.2 ratchov 290: timo_set(&d->sio.watchdog, dev_sio_timeout, d);
1.18 ratchov 291: dev_sioctl_open(d);
1.1 ratchov 292: return 1;
293: bad_close:
294: sio_close(d->sio.hdl);
1.18 ratchov 295: if (d->sioctl.hdl) {
296: sioctl_close(d->sioctl.hdl);
297: d->sioctl.hdl = NULL;
298: }
1.1 ratchov 299: return 0;
300: }
301:
1.16 ratchov 302: /*
303: * Open an alternate device. Upon success and if the new device is
304: * compatible with the old one, close the old device and continue
305: * using the new one. The new device is not started.
306: */
307: int
308: dev_sio_reopen(struct dev *d)
309: {
310: struct sio_par par;
311: struct sio_hdl *hdl;
1.23 edd 312: struct sioctl_hdl *ctlhdl;
313: unsigned int mode;
1.16 ratchov 314:
1.23 edd 315: if (!dev_sio_openlist(d, &hdl, &ctlhdl, &mode))
1.16 ratchov 316: return 0;
317:
318: sio_initpar(&par);
319: par.bits = d->par.bits;
320: par.bps = d->par.bps;
321: par.sig = d->par.sig;
322: par.le = d->par.le;
323: par.msb = d->par.msb;
1.23 edd 324: if (mode & SIO_PLAY)
325: par.pchan = d->reqpchan;
326: if (mode & SIO_REC)
327: par.rchan = d->reqrchan;
1.16 ratchov 328: par.appbufsz = d->bufsz;
329: par.round = d->round;
330: par.rate = d->rate;
331: if (!sio_setpar(hdl, &par))
332: goto bad_close;
333: if (!sio_getpar(hdl, &par))
334: goto bad_close;
335:
336: /* check if new parameters are compatible with old ones */
337: if (par.round != d->round || par.bufsz != d->bufsz ||
338: par.rate != d->rate) {
339: if (log_level >= 1) {
340: dev_log(d);
341: log_puts(": alternate device not compatible\n");
342: }
343: goto bad_close;
344: }
345:
346: /* close unused device */
347: timo_del(&d->sio.watchdog);
348: file_del(d->sio.file);
349: sio_close(d->sio.hdl);
1.18 ratchov 350: if (d->sioctl.hdl) {
1.19 ratchov 351: file_del(d->sioctl.file);
1.18 ratchov 352: sioctl_close(d->sioctl.hdl);
353: d->sioctl.hdl = NULL;
354: }
1.16 ratchov 355:
356: /* update parameters */
1.23 edd 357: d->mode = mode;
1.16 ratchov 358: d->par.bits = par.bits;
359: d->par.bps = par.bps;
360: d->par.sig = par.sig;
361: d->par.le = par.le;
362: d->par.msb = par.msb;
363: if (d->mode & SIO_PLAY)
364: d->pchan = par.pchan;
365: if (d->mode & SIO_REC)
366: d->rchan = par.rchan;
367:
368: d->sio.hdl = hdl;
1.18 ratchov 369: d->sioctl.hdl = ctlhdl;
1.16 ratchov 370: d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(hdl));
1.19 ratchov 371: if (d->sioctl.hdl) {
372: d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix",
373: sioctl_nfds(ctlhdl));
374: }
1.16 ratchov 375: sio_onmove(hdl, dev_sio_onmove, d);
376: return 1;
377: bad_close:
378: sio_close(hdl);
1.18 ratchov 379: if (ctlhdl)
380: sioctl_close(ctlhdl);
1.16 ratchov 381: return 0;
382: }
383:
1.1 ratchov 384: void
385: dev_sio_close(struct dev *d)
386: {
1.18 ratchov 387: dev_sioctl_close(d);
1.1 ratchov 388: #ifdef DEBUG
389: if (log_level >= 3) {
390: dev_log(d);
391: log_puts(": closed\n");
392: }
393: #endif
1.7 ratchov 394: timo_del(&d->sio.watchdog);
1.1 ratchov 395: file_del(d->sio.file);
1.11 ratchov 396: sio_close(d->sio.hdl);
1.18 ratchov 397: if (d->sioctl.hdl) {
1.19 ratchov 398: file_del(d->sioctl.file);
1.18 ratchov 399: sioctl_close(d->sioctl.hdl);
400: d->sioctl.hdl = NULL;
401: }
1.21 ratchov 402: d->alt_num = -1;
1.1 ratchov 403: }
404:
405: void
406: dev_sio_start(struct dev *d)
407: {
408: if (!sio_start(d->sio.hdl)) {
409: if (log_level >= 1) {
410: dev_log(d);
411: log_puts(": failed to start device\n");
412: }
413: return;
414: }
415: if (d->mode & MODE_PLAY) {
416: d->sio.cstate = DEV_SIO_CYCLE;
417: d->sio.todo = 0;
418: } else {
419: d->sio.cstate = DEV_SIO_READ;
420: d->sio.todo = d->round * d->rchan * d->par.bps;
421: }
422: #ifdef DEBUG
423: d->sio.pused = 0;
424: d->sio.rused = 0;
425: d->sio.sum_utime = 0;
426: d->sio.sum_wtime = 0;
427: d->sio.wtime = file_wtime;
428: d->sio.utime = file_utime;
429: if (log_level >= 3) {
430: dev_log(d);
431: log_puts(": started\n");
432: }
433: #endif
1.2 ratchov 434: timo_add(&d->sio.watchdog, WATCHDOG_USEC);
1.1 ratchov 435: }
436:
437: void
438: dev_sio_stop(struct dev *d)
439: {
440: if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) {
441: if (log_level >= 1) {
442: dev_log(d);
443: log_puts(": failed to stop device\n");
444: }
445: return;
446: }
447: #ifdef DEBUG
448: if (log_level >= 3) {
449: dev_log(d);
450: log_puts(": stopped, load avg = ");
451: log_puti(d->sio.sum_utime / 1000);
452: log_puts(" / ");
453: log_puti(d->sio.sum_wtime / 1000);
454: log_puts("\n");
455: }
456: #endif
1.2 ratchov 457: timo_del(&d->sio.watchdog);
1.1 ratchov 458: }
459:
460: int
461: dev_sio_pollfd(void *arg, struct pollfd *pfd)
462: {
463: struct dev *d = arg;
464: int events;
1.11 ratchov 465:
1.1 ratchov 466: events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
467: return sio_pollfd(d->sio.hdl, pfd, events);
468: }
469:
470: int
471: dev_sio_revents(void *arg, struct pollfd *pfd)
472: {
473: struct dev *d = arg;
474: int events;
475:
476: events = sio_revents(d->sio.hdl, pfd);
477: #ifdef DEBUG
478: d->sio.events = events;
479: #endif
480: return events;
481: }
482:
483: void
484: dev_sio_run(void *arg)
485: {
486: struct dev *d = arg;
487: unsigned char *data, *base;
488: unsigned int n;
489:
490: /*
491: * sio_read() and sio_write() would block at the end of the
492: * cycle so we *must* return and restart poll()'ing. Otherwise
493: * we may trigger dev_cycle() which would make all clients
494: * underrun (ex, on a play-only device)
495: */
496: for (;;) {
497: if (d->pstate != DEV_RUN)
498: return;
499: switch (d->sio.cstate) {
500: case DEV_SIO_READ:
501: #ifdef DEBUG
502: if (!(d->sio.events & POLLIN)) {
503: dev_log(d);
504: log_puts(": recording, but POLLIN not set\n");
505: panic();
506: }
507: if (d->sio.todo == 0) {
508: dev_log(d);
509: log_puts(": can't read data\n");
510: panic();
511: }
512: if (d->prime > 0) {
513: dev_log(d);
514: log_puts(": unexpected data\n");
515: panic();
516: }
517: #endif
518: base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf;
519: data = base +
520: d->rchan * d->round * d->par.bps -
521: d->sio.todo;
522: n = sio_read(d->sio.hdl, data, d->sio.todo);
523: d->sio.todo -= n;
524: #ifdef DEBUG
525: if (log_level >= 4) {
526: dev_log(d);
527: log_puts(": read ");
528: log_putu(n);
529: log_puts(": bytes, todo ");
530: log_putu(d->sio.todo);
531: log_puts("/");
532: log_putu(d->round * d->rchan * d->par.bps);
533: log_puts("\n");
534: }
535: #endif
536: if (d->sio.todo > 0)
537: return;
538: #ifdef DEBUG
539: d->sio.rused -= d->round;
540: if (log_level >= 2) {
541: if (d->sio.rused >= d->round) {
542: dev_log(d);
543: log_puts(": rec hw xrun, rused = ");
544: log_puti(d->sio.rused);
545: log_puts("/");
546: log_puti(d->bufsz);
547: log_puts("\n");
548: }
549: if (d->sio.rused < 0 ||
550: d->sio.rused >= d->bufsz) {
551: dev_log(d);
552: log_puts(": out of bounds rused = ");
553: log_puti(d->sio.rused);
554: log_puts("/");
555: log_puti(d->bufsz);
556: log_puts("\n");
557: }
558: }
559: #endif
560: d->sio.cstate = DEV_SIO_CYCLE;
561: break;
562: case DEV_SIO_CYCLE:
1.2 ratchov 563: timo_del(&d->sio.watchdog);
564: timo_add(&d->sio.watchdog, WATCHDOG_USEC);
565:
1.1 ratchov 566: #ifdef DEBUG
567: /*
568: * check that we're called at cycle boundary:
569: * either after a recorded block, or when POLLOUT is
570: * raised
571: */
572: if (!((d->mode & MODE_REC) && d->prime == 0) &&
573: !(d->sio.events & POLLOUT)) {
574: dev_log(d);
575: log_puts(": cycle not at block boundary\n");
576: panic();
577: }
578: #endif
579: dev_cycle(d);
580: if (d->mode & MODE_PLAY) {
581: d->sio.cstate = DEV_SIO_WRITE;
582: d->sio.todo = d->round * d->pchan * d->par.bps;
583: break;
584: } else {
585: d->sio.cstate = DEV_SIO_READ;
586: d->sio.todo = d->round * d->rchan * d->par.bps;
587: return;
588: }
589: case DEV_SIO_WRITE:
590: #ifdef DEBUG
591: if (d->sio.todo == 0) {
592: dev_log(d);
593: log_puts(": can't write data\n");
594: panic();
595: }
596: #endif
597: base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d);
598: data = base +
599: d->pchan * d->round * d->par.bps -
600: d->sio.todo;
601: n = sio_write(d->sio.hdl, data, d->sio.todo);
602: d->sio.todo -= n;
603: #ifdef DEBUG
604: if (log_level >= 4) {
605: dev_log(d);
606: log_puts(": wrote ");
607: log_putu(n);
608: log_puts(" bytes, todo ");
609: log_putu(d->sio.todo);
610: log_puts("/");
611: log_putu(d->round * d->pchan * d->par.bps);
612: log_puts("\n");
613: }
614: #endif
615: if (d->sio.todo > 0)
616: return;
617: #ifdef DEBUG
618: d->sio.pused += d->round;
619: if (log_level >= 2) {
620: if (d->prime == 0 &&
621: d->sio.pused <= d->bufsz - d->round) {
622: dev_log(d);
623: log_puts(": play hw xrun, pused = ");
624: log_puti(d->sio.pused);
625: log_puts("/");
626: log_puti(d->bufsz);
627: log_puts("\n");
628: }
629: if (d->sio.pused < 0 ||
630: d->sio.pused > d->bufsz) {
631: /* device driver or libsndio bug */
632: dev_log(d);
633: log_puts(": out of bounds pused = ");
634: log_puti(d->sio.pused);
635: log_puts("/");
636: log_puti(d->bufsz);
637: log_puts("\n");
638: }
639: }
640: #endif
641: d->poffs += d->round;
1.4 ratchov 642: if (d->poffs == d->psize)
1.1 ratchov 643: d->poffs = 0;
644: if ((d->mode & MODE_REC) && d->prime == 0) {
645: d->sio.cstate = DEV_SIO_READ;
646: d->sio.todo = d->round * d->rchan * d->par.bps;
647: } else
648: d->sio.cstate = DEV_SIO_CYCLE;
649: return;
650: }
651: }
652: }
653:
654: void
655: dev_sio_hup(void *arg)
656: {
657: struct dev *d = arg;
658:
1.8 ratchov 659: #ifdef DEBUG
660: if (log_level >= 2) {
661: dev_log(d);
662: log_puts(": disconnected\n");
663: }
664: #endif
1.16 ratchov 665: if (!dev_reopen(d))
1.20 ratchov 666: dev_abort(d);
1.1 ratchov 667: }