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