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