siofile.c revision 1.4
1/*	$OpenBSD: siofile.c,v 1.4 2014/03/17 17:16:06 ratchov Exp $	*/
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
34#define WATCHDOG_USEC	2000000		/* 2 seconds */
35
36void dev_sio_onmove(void *, int);
37void dev_sio_timeout(void *);
38int dev_sio_pollfd(void *, struct pollfd *);
39int dev_sio_revents(void *, struct pollfd *);
40void dev_sio_run(void *);
41void dev_sio_hup(void *);
42
43struct 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
52void
53dev_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
76void
77dev_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
86/*
87 * open the device.
88 */
89int
90dev_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;
135	d->par.bits = par.bits;
136	d->par.bps = par.bps;
137	d->par.sig = par.sig;
138	d->par.le = par.le;
139	d->par.msb = par.msb;
140	if (mode & SIO_PLAY)
141		d->pchan = par.pchan;
142	if (mode & SIO_REC)
143		d->rchan = par.rchan;
144	d->bufsz = par.bufsz;
145	d->round = par.round;
146	d->rate = par.rate;
147	if (!(mode & MODE_PLAY))
148		d->mode &= ~(MODE_PLAY | MODE_MON);
149	if (!(mode & MODE_REC))
150		d->mode &= ~MODE_REC;
151	sio_onmove(d->sio.hdl, dev_sio_onmove, d);
152	d->sio.file = file_new(&dev_sio_ops, d, d->path, sio_nfds(d->sio.hdl));
153	timo_set(&d->sio.watchdog, dev_sio_timeout, d);
154	return 1;
155 bad_close:
156	sio_close(d->sio.hdl);
157	return 0;
158}
159
160void
161dev_sio_close(struct dev *d)
162{
163#ifdef DEBUG
164	if (log_level >= 3) {
165		dev_log(d);
166		log_puts(": closed\n");
167	}
168#endif
169	file_del(d->sio.file);
170	sio_close(d->sio.hdl);
171}
172
173void
174dev_sio_start(struct dev *d)
175{
176	if (!sio_start(d->sio.hdl)) {
177		if (log_level >= 1) {
178			dev_log(d);
179			log_puts(": failed to start device\n");
180		}
181		return;
182	}
183	if (d->mode & MODE_PLAY) {
184		d->sio.cstate = DEV_SIO_CYCLE;
185		d->sio.todo = 0;
186	} else {
187		d->sio.cstate = DEV_SIO_READ;
188		d->sio.todo = d->round * d->rchan * d->par.bps;
189	}
190#ifdef DEBUG
191	d->sio.pused = 0;
192	d->sio.rused = 0;
193	d->sio.sum_utime = 0;
194	d->sio.sum_wtime = 0;
195	d->sio.wtime = file_wtime;
196	d->sio.utime = file_utime;
197	if (log_level >= 3) {
198		dev_log(d);
199		log_puts(": started\n");
200	}
201#endif
202	timo_add(&d->sio.watchdog, WATCHDOG_USEC);
203}
204
205void
206dev_sio_stop(struct dev *d)
207{
208	if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) {
209		if (log_level >= 1) {
210			dev_log(d);
211			log_puts(": failed to stop device\n");
212		}
213		return;
214	}
215#ifdef DEBUG
216	if (log_level >= 3) {
217		dev_log(d);
218		log_puts(": stopped, load avg = ");
219		log_puti(d->sio.sum_utime / 1000);
220		log_puts(" / ");
221		log_puti(d->sio.sum_wtime / 1000);
222		log_puts("\n");
223	}
224#endif
225	timo_del(&d->sio.watchdog);
226}
227
228int
229dev_sio_pollfd(void *arg, struct pollfd *pfd)
230{
231	struct dev *d = arg;
232	int events;
233
234	events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
235	return sio_pollfd(d->sio.hdl, pfd, events);
236}
237
238int
239dev_sio_revents(void *arg, struct pollfd *pfd)
240{
241	struct dev *d = arg;
242	int events;
243
244	events = sio_revents(d->sio.hdl, pfd);
245#ifdef DEBUG
246	d->sio.events = events;
247#endif
248	return events;
249}
250
251void
252dev_sio_run(void *arg)
253{
254	struct dev *d = arg;
255	unsigned char *data, *base;
256	unsigned int n;
257
258	/*
259	 * sio_read() and sio_write() would block at the end of the
260	 * cycle so we *must* return and restart poll()'ing. Otherwise
261	 * we may trigger dev_cycle() which would make all clients
262	 * underrun (ex, on a play-only device)
263	 */
264	for (;;) {
265		if (d->pstate != DEV_RUN)
266			return;
267		switch (d->sio.cstate) {
268		case DEV_SIO_READ:
269#ifdef DEBUG
270			if (!(d->sio.events & POLLIN)) {
271				dev_log(d);
272				log_puts(": recording, but POLLIN not set\n");
273				panic();
274			}
275			if (d->sio.todo == 0) {
276				dev_log(d);
277				log_puts(": can't read data\n");
278				panic();
279			}
280			if (d->prime > 0) {
281				dev_log(d);
282				log_puts(": unexpected data\n");
283				panic();
284			}
285#endif
286			base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf;
287			data = base +
288			    d->rchan * d->round * d->par.bps -
289			    d->sio.todo;
290			n = sio_read(d->sio.hdl, data, d->sio.todo);
291			d->sio.todo -= n;
292#ifdef DEBUG
293			if (n == 0 && data == base && !sio_eof(d->sio.hdl)) {
294				dev_log(d);
295				log_puts(": read blocked at cycle start\n");
296			}
297			if (log_level >= 4) {
298				dev_log(d);
299				log_puts(": read ");
300				log_putu(n);
301				log_puts(": bytes, todo ");
302				log_putu(d->sio.todo);
303				log_puts("/");
304				log_putu(d->round * d->rchan * d->par.bps);
305				log_puts("\n");
306			}
307#endif
308			if (d->sio.todo > 0)
309				return;
310#ifdef DEBUG
311			d->sio.rused -= d->round;
312			if (log_level >= 2) {
313				if (d->sio.rused >= d->round) {
314					dev_log(d);
315					log_puts(": rec hw xrun, rused = ");
316					log_puti(d->sio.rused);
317					log_puts("/");
318					log_puti(d->bufsz);
319					log_puts("\n");
320				}
321				if (d->sio.rused < 0 ||
322				    d->sio.rused >= d->bufsz) {
323					dev_log(d);
324					log_puts(": out of bounds rused = ");
325					log_puti(d->sio.rused);
326					log_puts("/");
327					log_puti(d->bufsz);
328					log_puts("\n");
329				}
330			}
331#endif
332			d->sio.cstate = DEV_SIO_CYCLE;
333			break;
334		case DEV_SIO_CYCLE:
335			timo_del(&d->sio.watchdog);
336			timo_add(&d->sio.watchdog, WATCHDOG_USEC);
337
338#ifdef DEBUG
339			/*
340			 * check that we're called at cycle boundary:
341			 * either after a recorded block, or when POLLOUT is
342			 * raised
343			 */
344			if (!((d->mode & MODE_REC) && d->prime == 0) &&
345			    !(d->sio.events & POLLOUT)) {
346				dev_log(d);
347				log_puts(": cycle not at block boundary\n");
348				panic();
349			}
350#endif
351			dev_cycle(d);
352			if (d->mode & MODE_PLAY) {
353				d->sio.cstate = DEV_SIO_WRITE;
354				d->sio.todo = d->round * d->pchan * d->par.bps;
355				break;
356			} else {
357				d->sio.cstate = DEV_SIO_READ;
358				d->sio.todo = d->round * d->rchan * d->par.bps;
359				return;
360			}
361		case DEV_SIO_WRITE:
362#ifdef DEBUG
363			if (d->sio.todo == 0) {
364				dev_log(d);
365				log_puts(": can't write data\n");
366				panic();
367			}
368#endif
369			base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d);
370			data = base +
371			    d->pchan * d->round * d->par.bps -
372			    d->sio.todo;
373			n = sio_write(d->sio.hdl, data, d->sio.todo);
374			d->sio.todo -= n;
375#ifdef DEBUG
376			if (n == 0 && data == base && !sio_eof(d->sio.hdl)) {
377				dev_log(d);
378				log_puts(": write blocked at cycle start\n");
379			}
380			if (log_level >= 4) {
381				dev_log(d);
382				log_puts(": wrote ");
383				log_putu(n);
384				log_puts(" bytes, todo ");
385				log_putu(d->sio.todo);
386				log_puts("/");
387				log_putu(d->round * d->pchan * d->par.bps);
388				log_puts("\n");
389			}
390#endif
391			if (d->sio.todo > 0)
392				return;
393#ifdef DEBUG
394			d->sio.pused += d->round;
395			if (log_level >= 2) {
396				if (d->prime == 0 &&
397				    d->sio.pused <= d->bufsz - d->round) {
398					dev_log(d);
399					log_puts(": play hw xrun, pused = ");
400					log_puti(d->sio.pused);
401					log_puts("/");
402					log_puti(d->bufsz);
403					log_puts("\n");
404				}
405				if (d->sio.pused < 0 ||
406				    d->sio.pused > d->bufsz) {
407					/* device driver or libsndio bug */
408					dev_log(d);
409					log_puts(": out of bounds pused = ");
410					log_puti(d->sio.pused);
411					log_puts("/");
412					log_puti(d->bufsz);
413					log_puts("\n");
414				}
415			}
416#endif
417			d->poffs += d->round;
418			if (d->poffs == d->psize)
419				d->poffs = 0;
420			if ((d->mode & MODE_REC) && d->prime == 0) {
421				d->sio.cstate = DEV_SIO_READ;
422				d->sio.todo = d->round * d->rchan * d->par.bps;
423			} else
424				d->sio.cstate = DEV_SIO_CYCLE;
425			return;
426		}
427	}
428}
429
430void
431dev_sio_hup(void *arg)
432{
433	struct dev *d = arg;
434
435	dev_close(d);
436}
437