Annotation of src/usr.bin/aucat/siofile.c, Revision 1.9
1.9 ! ratchov 1: /* $OpenBSD: siofile.c,v 1.8 2011/10/12 07:20:04 ratchov Exp $ */
1.1 ratchov 2: /*
3: * Copyright (c) 2008 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:
18: #include <sys/time.h>
19: #include <sys/types.h>
20:
21: #include <poll.h>
22: #include <sndio.h>
23: #include <stdio.h>
24: #include <stdlib.h>
25: #include <string.h>
26:
27: #include "aparams.h"
28: #include "aproc.h"
1.4 ratchov 29: #include "abuf.h"
1.1 ratchov 30: #include "conf.h"
31: #include "dev.h"
32: #include "file.h"
33: #include "siofile.h"
34: #ifdef DEBUG
35: #include "dbg.h"
36: #endif
37:
38: struct siofile {
39: struct file file;
40: struct sio_hdl *hdl;
1.4 ratchov 41: unsigned wtickets, wbpf;
42: unsigned rtickets, rbpf;
43: unsigned bufsz;
1.1 ratchov 44: int started;
1.9 ! ratchov 45: void (*onmove)(void *, int);
! 46: void *arg;
1.7 ratchov 47: #ifdef DEBUG
48: long long wtime, utime;
49: #endif
1.1 ratchov 50: };
51:
52: void siofile_close(struct file *);
53: unsigned siofile_read(struct file *, unsigned char *, unsigned);
54: unsigned siofile_write(struct file *, unsigned char *, unsigned);
1.9 ! ratchov 55: void siofile_start(struct file *, void (*)(void *, int), void *);
1.1 ratchov 56: void siofile_stop(struct file *);
57: int siofile_nfds(struct file *);
58: int siofile_pollfd(struct file *, struct pollfd *, int);
59: int siofile_revents(struct file *, struct pollfd *);
60:
61: struct fileops siofile_ops = {
62: "sio",
63: sizeof(struct siofile),
64: siofile_close,
65: siofile_read,
66: siofile_write,
67: siofile_start,
68: siofile_stop,
69: siofile_nfds,
70: siofile_pollfd,
71: siofile_revents
72: };
73:
1.4 ratchov 74: int wsio_out(struct aproc *, struct abuf *);
75: int rsio_in(struct aproc *, struct abuf *);
76:
77: struct aproc_ops rsio_ops = {
78: "rsio",
79: rsio_in,
80: rfile_out,
81: rfile_eof,
82: rfile_hup,
83: NULL, /* newin */
84: NULL, /* newout */
85: aproc_ipos,
86: aproc_opos,
87: rfile_done
88: };
89:
90: struct aproc_ops wsio_ops = {
91: "wsio",
92: wfile_in,
93: wsio_out,
94: wfile_eof,
95: wfile_hup,
96: NULL, /* newin */
97: NULL, /* newout */
98: aproc_ipos,
99: aproc_opos,
100: wfile_done
101: };
102:
103: struct aproc *
104: rsio_new(struct file *f)
105: {
106: struct aproc *p;
107:
108: p = aproc_new(&rsio_ops, f->name);
109: p->u.io.file = f;
110: p->u.io.partial = 0;
111: f->rproc = p;
112: return p;
113: }
114:
115: struct aproc *
116: wsio_new(struct file *f)
117: {
118: struct aproc *p;
119:
120: p = aproc_new(&wsio_ops, f->name);
121: p->u.io.file = f;
122: p->u.io.partial = 0;
123: f->wproc = p;
124: return p;
125: }
126:
127: int
128: wsio_out(struct aproc *p, struct abuf *obuf)
129: {
130: struct siofile *f = (struct siofile *)p->u.io.file;
131:
132: if (f->wtickets == 0) {
133: #ifdef DEBUG
134: if (debug_level >= 4) {
135: file_dbg(&f->file);
136: dbg_puts(": no more write tickets\n");
137: }
138: #endif
139: f->file.state &= ~FILE_WOK;
140: return 0;
141: }
142: return wfile_out(p, obuf);
143: }
144:
145: int
146: rsio_in(struct aproc *p, struct abuf *ibuf)
147: {
148: struct siofile *f = (struct siofile *)p->u.io.file;
149:
150: if (f->rtickets == 0) {
151: #ifdef DEBUG
152: if (debug_level >= 4) {
153: file_dbg(&f->file);
154: dbg_puts(": no more read tickets\n");
155: }
156: #endif
157: f->file.state &= ~FILE_ROK;
158: return 0;
159: }
160: return rfile_in(p, ibuf);
161: }
162:
1.1 ratchov 163: void
164: siofile_cb(void *addr, int delta)
165: {
166: struct siofile *f = (struct siofile *)addr;
167: struct aproc *p;
168:
169: #ifdef DEBUG
170: if (delta < 0 || delta > (60 * RATE_MAX)) {
1.4 ratchov 171: file_dbg(&f->file);
1.1 ratchov 172: dbg_puts(": ");
173: dbg_puti(delta);
174: dbg_puts(": bogus sndio delta");
175: dbg_panic();
176: }
1.4 ratchov 177: if (debug_level >= 4) {
178: file_dbg(&f->file);
179: dbg_puts(": tick, delta = ");
180: dbg_puti(delta);
1.7 ratchov 181: dbg_puts(", load = ");
182: dbg_puti((file_utime - f->utime) / 1000);
183: dbg_puts(" + ");
184: dbg_puti((file_wtime - f->wtime) / 1000);
1.4 ratchov 185: dbg_puts("\n");
186: }
1.7 ratchov 187: f->wtime = file_wtime;
188: f->utime = file_utime;
1.1 ratchov 189: #endif
190: if (delta != 0) {
191: p = f->file.wproc;
192: if (p && p->ops->opos)
193: p->ops->opos(p, NULL, delta);
194: p = f->file.rproc;
195: if (p && p->ops->ipos)
196: p->ops->ipos(p, NULL, delta);
197: }
1.9 ! ratchov 198: if (f->onmove)
! 199: f->onmove(f->arg, delta);
1.4 ratchov 200: f->wtickets += delta * f->wbpf;
201: f->rtickets += delta * f->rbpf;
1.1 ratchov 202: }
203:
204: /*
205: * Open the device.
206: */
207: struct siofile *
1.5 ratchov 208: siofile_new(struct fileops *ops, char *path, unsigned *rmode,
1.1 ratchov 209: struct aparams *ipar, struct aparams *opar,
210: unsigned *bufsz, unsigned *round)
211: {
1.6 ratchov 212: char *siopath;
1.1 ratchov 213: struct sio_par par;
214: struct sio_hdl *hdl;
215: struct siofile *f;
1.5 ratchov 216: unsigned mode = *rmode;
1.1 ratchov 217:
1.6 ratchov 218: siopath = (strcmp(path, "default") == 0) ? NULL : path;
219: hdl = sio_open(siopath, mode, 1);
1.5 ratchov 220: if (hdl == NULL) {
221: if (mode != (SIO_PLAY | SIO_REC))
222: return NULL;
1.6 ratchov 223: hdl = sio_open(siopath, SIO_PLAY, 1);
1.5 ratchov 224: if (hdl != NULL)
225: mode = SIO_PLAY;
226: else {
1.6 ratchov 227: hdl = sio_open(siopath, SIO_REC, 1);
1.5 ratchov 228: if (hdl != NULL)
229: mode = SIO_REC;
230: else
231: return NULL;
232: }
233: #ifdef DEBUG
234: if (debug_level >= 1) {
235: dbg_puts("warning, device opened in ");
236: dbg_puts(mode == SIO_PLAY ? "play-only" : "rec-only");
237: dbg_puts(" mode\n");
238: }
239: #endif
240: }
241:
1.1 ratchov 242: sio_initpar(&par);
1.4 ratchov 243: if (mode & SIO_REC) {
1.1 ratchov 244: par.bits = ipar->bits;
245: par.bps = ipar->bps;
246: par.sig = ipar->sig;
247: par.le = ipar->le;
248: par.msb = ipar->msb;
249: par.rate = ipar->rate;
1.8 ratchov 250: par.rchan = ipar->cmax + 1;
1.1 ratchov 251: } else {
252: par.bits = opar->bits;
253: par.bps = opar->bps;
254: par.sig = opar->sig;
255: par.le = opar->le;
256: par.msb = opar->msb;
257: par.rate = opar->rate;
258: }
1.4 ratchov 259: if (mode & SIO_PLAY)
1.8 ratchov 260: par.pchan = opar->cmax + 1;
261: if (*bufsz)
262: par.appbufsz = *bufsz;
263: if (*round)
264: par.round = *round;
1.1 ratchov 265: if (!sio_setpar(hdl, &par))
266: goto bad_close;
267: if (!sio_getpar(hdl, &par))
268: goto bad_close;
1.4 ratchov 269: if (mode & SIO_REC) {
1.1 ratchov 270: ipar->bits = par.bits;
271: ipar->bps = par.bps;
272: ipar->sig = par.sig;
273: ipar->le = par.le;
274: ipar->msb = par.msb;
275: ipar->rate = par.rate;
1.8 ratchov 276: ipar->cmin = 0;
277: ipar->cmax = par.rchan - 1;
1.1 ratchov 278: }
1.4 ratchov 279: if (mode & SIO_PLAY) {
1.1 ratchov 280: opar->bits = par.bits;
281: opar->bps = par.bps;
282: opar->sig = par.sig;
283: opar->le = par.le;
284: opar->msb = par.msb;
285: opar->rate = par.rate;
1.8 ratchov 286: opar->cmin = 0;
287: opar->cmax = par.pchan - 1;
1.1 ratchov 288: }
1.5 ratchov 289: *rmode = mode;
1.1 ratchov 290: *bufsz = par.bufsz;
291: *round = par.round;
292: f = (struct siofile *)file_new(ops, path, sio_nfds(hdl));
293: if (f == NULL)
294: goto bad_close;
295: f->hdl = hdl;
296: f->started = 0;
1.4 ratchov 297: f->wtickets = 0;
298: f->rtickets = 0;
299: f->wbpf = par.pchan * par.bps;
300: f->rbpf = par.rchan * par.bps;
301: f->bufsz = par.bufsz;
1.1 ratchov 302: sio_onmove(f->hdl, siofile_cb, f);
303: return f;
304: bad_close:
305: sio_close(hdl);
306: return NULL;
307: }
308:
309: void
1.9 ! ratchov 310: siofile_start(struct file *file, void (*cb)(void *, int), void *arg)
1.1 ratchov 311: {
312: struct siofile *f = (struct siofile *)file;
313:
314: if (!sio_start(f->hdl)) {
315: #ifdef DEBUG
316: dbg_puts(f->file.name);
317: dbg_puts(": failed to start device\n");
318: #endif
319: file_close(file);
320: return;
321: }
322: f->started = 1;
1.4 ratchov 323: f->wtickets = f->bufsz * f->wbpf;
324: f->rtickets = 0;
1.1 ratchov 325: #ifdef DEBUG
1.7 ratchov 326: f->wtime = file_wtime;
327: f->utime = file_utime;
1.1 ratchov 328: if (debug_level >= 3) {
329: file_dbg(&f->file);
330: dbg_puts(": started\n");
331: }
332: #endif
1.9 ! ratchov 333: f->onmove = cb;
! 334: f->arg = arg;
1.1 ratchov 335: }
336:
337: void
338: siofile_stop(struct file *file)
339: {
340: struct siofile *f = (struct siofile *)file;
341:
342: f->started = 0;
1.9 ! ratchov 343: f->onmove = NULL;
1.1 ratchov 344: if (!sio_eof(f->hdl) && !sio_stop(f->hdl)) {
345: #ifdef DEBUG
346: dbg_puts(f->file.name);
347: dbg_puts(": failed to stop device\n");
348: #endif
349: file_close(file);
350: return;
351: }
352: #ifdef DEBUG
353: if (debug_level >= 3) {
354: file_dbg(&f->file);
355: dbg_puts(": stopped\n");
356: }
357: #endif
358: }
359:
360: unsigned
361: siofile_read(struct file *file, unsigned char *data, unsigned count)
362: {
363: struct siofile *f = (struct siofile *)file;
364: unsigned n;
365:
1.4 ratchov 366: #ifdef DEBUG
367: if (f->rtickets == 0) {
368: file_dbg(&f->file);
369: dbg_puts(": called with no read tickets\n");
370: }
371: #endif
372: if (count > f->rtickets)
373: count = f->rtickets;
1.1 ratchov 374: n = f->started ? sio_read(f->hdl, data, count) : 0;
375: if (n == 0) {
376: f->file.state &= ~FILE_ROK;
377: if (sio_eof(f->hdl)) {
378: #ifdef DEBUG
379: dbg_puts(f->file.name);
380: dbg_puts(": failed to read from device\n");
381: #endif
382: file_eof(&f->file);
383: } else {
384: #ifdef DEBUG
385: if (debug_level >= 4) {
386: file_dbg(&f->file);
387: dbg_puts(": reading blocked\n");
388: }
389: #endif
390: }
391: return 0;
1.4 ratchov 392: } else {
393: f->rtickets -= n;
394: if (f->rtickets == 0) {
395: f->file.state &= ~FILE_ROK;
396: #ifdef DEBUG
397: if (debug_level >= 4) {
398: file_dbg(&f->file);
399: dbg_puts(": read tickets exhausted\n");
400: }
401: #endif
402: }
1.1 ratchov 403: }
404: return n;
405:
406: }
407:
408: unsigned
409: siofile_write(struct file *file, unsigned char *data, unsigned count)
410: {
411: struct siofile *f = (struct siofile *)file;
412: unsigned n;
413:
1.4 ratchov 414: #ifdef DEBUG
415: if (f->wtickets == 0) {
416: file_dbg(&f->file);
417: dbg_puts(": called with no write tickets\n");
418: }
419: #endif
420: if (count > f->wtickets)
421: count = f->wtickets;
1.1 ratchov 422: n = f->started ? sio_write(f->hdl, data, count) : 0;
423: if (n == 0) {
424: f->file.state &= ~FILE_WOK;
425: if (sio_eof(f->hdl)) {
426: #ifdef DEBUG
427: dbg_puts(f->file.name);
428: dbg_puts(": failed to write on device\n");
429: #endif
430: file_hup(&f->file);
431: } else {
432: #ifdef DEBUG
433: if (debug_level >= 4) {
434: file_dbg(&f->file);
435: dbg_puts(": writing blocked\n");
436: }
437: #endif
438: }
439: return 0;
1.4 ratchov 440: } else {
441: f->wtickets -= n;
442: if (f->wtickets == 0) {
443: f->file.state &= ~FILE_WOK;
444: #ifdef DEBUG
445: if (debug_level >= 4) {
446: file_dbg(&f->file);
447: dbg_puts(": write tickets exhausted\n");
448: }
449: #endif
450: }
1.1 ratchov 451: }
452: return n;
453: }
454:
455: int
456: siofile_nfds(struct file *file)
457: {
458: return sio_nfds(((struct siofile *)file)->hdl);
459: }
460:
461: int
462: siofile_pollfd(struct file *file, struct pollfd *pfd, int events)
463: {
464: struct siofile *f = (struct siofile *)file;
465:
466: if (!f->started)
467: events &= ~(POLLIN | POLLOUT);
468: return sio_pollfd(((struct siofile *)file)->hdl, pfd, events);
469: }
470:
471: int
472: siofile_revents(struct file *file, struct pollfd *pfd)
473: {
474: return sio_revents(((struct siofile *)file)->hdl, pfd);
475: }
476:
477: void
478: siofile_close(struct file *file)
479: {
480: struct siofile *f = (struct siofile *)file;
481:
482: if (f->started)
483: siofile_stop(&f->file);
484: return sio_close(((struct siofile *)file)->hdl);
485: }