siofile.c revision 1.12
1/*	$OpenBSD: siofile.c,v 1.12 2016/05/25 10:24:24 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 "fdpass.h"
31#include "file.h"
32#include "siofile.h"
33#include "utils.h"
34
35#define WATCHDOG_USEC	4000000		/* 4 seconds */
36
37void dev_sio_onmove(void *, int);
38void dev_sio_timeout(void *);
39int dev_sio_pollfd(void *, struct pollfd *);
40int dev_sio_revents(void *, struct pollfd *);
41void dev_sio_run(void *);
42void dev_sio_hup(void *);
43
44struct fileops dev_sio_ops = {
45	"sio",
46	dev_sio_pollfd,
47	dev_sio_revents,
48	dev_sio_run,
49	dev_sio_run,
50	dev_sio_hup
51};
52
53void
54dev_sio_onmove(void *arg, int delta)
55{
56	struct dev *d = arg;
57
58#ifdef DEBUG
59	if (log_level >= 4) {
60		dev_log(d);
61		log_puts(": tick, delta = ");
62		log_puti(delta);
63		log_puts("\n");
64	}
65	d->sio.sum_utime += file_utime - d->sio.utime;
66	d->sio.sum_wtime += file_wtime - d->sio.wtime;
67	d->sio.wtime = file_wtime;
68	d->sio.utime = file_utime;
69	if (d->mode & MODE_PLAY)
70		d->sio.pused -= delta;
71	if (d->mode & MODE_REC)
72		d->sio.rused += delta;
73#endif
74	dev_onmove(d, delta);
75}
76
77void
78dev_sio_timeout(void *arg)
79{
80	struct dev *d = arg;
81
82	dev_log(d);
83	log_puts(": watchdog timeout\n");
84	dev_close(d);
85}
86
87/*
88 * open the device.
89 */
90int
91dev_sio_open(struct dev *d)
92{
93	struct sio_par par;
94	unsigned int mode = d->mode & (MODE_PLAY | MODE_REC);
95
96	d->sio.hdl = fdpass_sio_open(d->num, mode);
97	if (d->sio.hdl == NULL) {
98		if (mode != (SIO_PLAY | SIO_REC))
99			return 0;
100		d->sio.hdl = fdpass_sio_open(d->num, SIO_PLAY);
101		if (d->sio.hdl != NULL)
102			mode = SIO_PLAY;
103		else {
104			d->sio.hdl = fdpass_sio_open(d->num, SIO_REC);
105			if (d->sio.hdl != NULL)
106				mode = SIO_REC;
107			else
108				return 0;
109		}
110		if (log_level >= 1) {
111			log_puts("warning, device opened in ");
112			log_puts(mode == SIO_PLAY ? "play-only" : "rec-only");
113			log_puts(" mode\n");
114		}
115	}
116	sio_initpar(&par);
117	par.bits = d->par.bits;
118	par.bps = d->par.bps;
119	par.sig = d->par.sig;
120	par.le = d->par.le;
121	par.msb = d->par.msb;
122	if (mode & SIO_PLAY)
123		par.pchan = d->pchan;
124	if (mode & SIO_REC)
125		par.rchan = d->rchan;
126	if (d->bufsz)
127		par.appbufsz = d->bufsz;
128	if (d->round)
129		par.round = d->round;
130	if (d->rate)
131		par.rate = d->rate;
132	if (!sio_setpar(d->sio.hdl, &par))
133		goto bad_close;
134	if (!sio_getpar(d->sio.hdl, &par))
135		goto bad_close;
136
137#ifdef DEBUG
138	/*
139	 * We support any parameters combination exposed by the kernel,
140	 * and we have no other choice than trusting the kernel for
141	 * returning correct parameters. But let's check parameters
142	 * early and nicely report kernel bugs rather than crashing
143	 * later in memset(), malloc() or alike.
144	 */
145
146	if (par.bits > BITS_MAX) {
147		log_puts(d->path);
148		log_puts(": ");
149		log_putu(par.bits);
150		log_puts(": unsupported number of bits\n");
151		goto bad_close;
152	}
153	if (par.bps > SIO_BPS(BITS_MAX)) {
154		log_puts(d->path);
155		log_puts(": ");
156		log_putu(par.bits);
157		log_puts(": unsupported sample size\n");
158		goto bad_close;
159	}
160	if ((mode & SIO_PLAY) && par.pchan > NCHAN_MAX) {
161		log_puts(d->path);
162		log_puts(": ");
163		log_putu(par.pchan);
164		log_puts(": unsupported number of play channels\n");
165		goto bad_close;
166	}
167	if ((mode & SIO_REC) && par.rchan > NCHAN_MAX) {
168		log_puts(d->path);
169		log_puts(": ");
170		log_putu(par.rchan);
171		log_puts(": unsupported number of rec channels\n");
172		goto bad_close;
173	}
174	if (par.bufsz == 0 || par.bufsz > RATE_MAX) {
175		log_puts(d->path);
176		log_puts(": ");
177		log_putu(par.bufsz);
178		log_puts(": unsupported buffer size\n");
179		goto bad_close;
180	}
181	if (par.round == 0 || par.round > par.bufsz ||
182	    par.bufsz % par.round != 0) {
183		log_puts(d->path);
184		log_puts(": ");
185		log_putu(par.round);
186		log_puts(": unsupported block size\n");
187		goto bad_close;
188	}
189	if (par.rate == 0 || par.rate > RATE_MAX) {
190		log_puts(d->path);
191		log_puts(": ");
192		log_putu(par.rate);
193		log_puts(": unsupported rate\n");
194		goto bad_close;
195	}
196#endif
197
198	d->par.bits = par.bits;
199	d->par.bps = par.bps;
200	d->par.sig = par.sig;
201	d->par.le = par.le;
202	d->par.msb = par.msb;
203	if (mode & SIO_PLAY)
204		d->pchan = par.pchan;
205	if (mode & SIO_REC)
206		d->rchan = par.rchan;
207	d->bufsz = par.bufsz;
208	d->round = par.round;
209	d->rate = par.rate;
210	if (!(mode & MODE_PLAY))
211		d->mode &= ~(MODE_PLAY | MODE_MON);
212	if (!(mode & MODE_REC))
213		d->mode &= ~MODE_REC;
214	sio_onmove(d->sio.hdl, dev_sio_onmove, d);
215	d->sio.file = file_new(&dev_sio_ops, d, d->path, sio_nfds(d->sio.hdl));
216	timo_set(&d->sio.watchdog, dev_sio_timeout, d);
217	return 1;
218 bad_close:
219	sio_close(d->sio.hdl);
220	return 0;
221}
222
223void
224dev_sio_close(struct dev *d)
225{
226#ifdef DEBUG
227	if (log_level >= 3) {
228		dev_log(d);
229		log_puts(": closed\n");
230	}
231#endif
232	timo_del(&d->sio.watchdog);
233	file_del(d->sio.file);
234	sio_close(d->sio.hdl);
235}
236
237void
238dev_sio_start(struct dev *d)
239{
240	if (!sio_start(d->sio.hdl)) {
241		if (log_level >= 1) {
242			dev_log(d);
243			log_puts(": failed to start device\n");
244		}
245		return;
246	}
247	if (d->mode & MODE_PLAY) {
248		d->sio.cstate = DEV_SIO_CYCLE;
249		d->sio.todo = 0;
250	} else {
251		d->sio.cstate = DEV_SIO_READ;
252		d->sio.todo = d->round * d->rchan * d->par.bps;
253	}
254#ifdef DEBUG
255	d->sio.pused = 0;
256	d->sio.rused = 0;
257	d->sio.sum_utime = 0;
258	d->sio.sum_wtime = 0;
259	d->sio.wtime = file_wtime;
260	d->sio.utime = file_utime;
261	if (log_level >= 3) {
262		dev_log(d);
263		log_puts(": started\n");
264	}
265#endif
266	timo_add(&d->sio.watchdog, WATCHDOG_USEC);
267}
268
269void
270dev_sio_stop(struct dev *d)
271{
272	if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) {
273		if (log_level >= 1) {
274			dev_log(d);
275			log_puts(": failed to stop device\n");
276		}
277		return;
278	}
279#ifdef DEBUG
280	if (log_level >= 3) {
281		dev_log(d);
282		log_puts(": stopped, load avg = ");
283		log_puti(d->sio.sum_utime / 1000);
284		log_puts(" / ");
285		log_puti(d->sio.sum_wtime / 1000);
286		log_puts("\n");
287	}
288#endif
289	timo_del(&d->sio.watchdog);
290}
291
292int
293dev_sio_pollfd(void *arg, struct pollfd *pfd)
294{
295	struct dev *d = arg;
296	int events;
297
298	events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
299	return sio_pollfd(d->sio.hdl, pfd, events);
300}
301
302int
303dev_sio_revents(void *arg, struct pollfd *pfd)
304{
305	struct dev *d = arg;
306	int events;
307
308	events = sio_revents(d->sio.hdl, pfd);
309#ifdef DEBUG
310	d->sio.events = events;
311#endif
312	return events;
313}
314
315void
316dev_sio_run(void *arg)
317{
318	struct dev *d = arg;
319	unsigned char *data, *base;
320	unsigned int n;
321
322	/*
323	 * sio_read() and sio_write() would block at the end of the
324	 * cycle so we *must* return and restart poll()'ing. Otherwise
325	 * we may trigger dev_cycle() which would make all clients
326	 * underrun (ex, on a play-only device)
327	 */
328	for (;;) {
329		if (d->pstate != DEV_RUN)
330			return;
331		switch (d->sio.cstate) {
332		case DEV_SIO_READ:
333#ifdef DEBUG
334			if (!(d->sio.events & POLLIN)) {
335				dev_log(d);
336				log_puts(": recording, but POLLIN not set\n");
337				panic();
338			}
339			if (d->sio.todo == 0) {
340				dev_log(d);
341				log_puts(": can't read data\n");
342				panic();
343			}
344			if (d->prime > 0) {
345				dev_log(d);
346				log_puts(": unexpected data\n");
347				panic();
348			}
349#endif
350			base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf;
351			data = base +
352			    d->rchan * d->round * d->par.bps -
353			    d->sio.todo;
354			n = sio_read(d->sio.hdl, data, d->sio.todo);
355			d->sio.todo -= n;
356#ifdef DEBUG
357			if (log_level >= 4) {
358				dev_log(d);
359				log_puts(": read ");
360				log_putu(n);
361				log_puts(": bytes, todo ");
362				log_putu(d->sio.todo);
363				log_puts("/");
364				log_putu(d->round * d->rchan * d->par.bps);
365				log_puts("\n");
366			}
367#endif
368			if (d->sio.todo > 0)
369				return;
370#ifdef DEBUG
371			d->sio.rused -= d->round;
372			if (log_level >= 2) {
373				if (d->sio.rused >= d->round) {
374					dev_log(d);
375					log_puts(": rec hw xrun, rused = ");
376					log_puti(d->sio.rused);
377					log_puts("/");
378					log_puti(d->bufsz);
379					log_puts("\n");
380				}
381				if (d->sio.rused < 0 ||
382				    d->sio.rused >= d->bufsz) {
383					dev_log(d);
384					log_puts(": out of bounds rused = ");
385					log_puti(d->sio.rused);
386					log_puts("/");
387					log_puti(d->bufsz);
388					log_puts("\n");
389				}
390			}
391#endif
392			d->sio.cstate = DEV_SIO_CYCLE;
393			break;
394		case DEV_SIO_CYCLE:
395			timo_del(&d->sio.watchdog);
396			timo_add(&d->sio.watchdog, WATCHDOG_USEC);
397
398#ifdef DEBUG
399			/*
400			 * check that we're called at cycle boundary:
401			 * either after a recorded block, or when POLLOUT is
402			 * raised
403			 */
404			if (!((d->mode & MODE_REC) && d->prime == 0) &&
405			    !(d->sio.events & POLLOUT)) {
406				dev_log(d);
407				log_puts(": cycle not at block boundary\n");
408				panic();
409			}
410#endif
411			dev_cycle(d);
412			if (d->mode & MODE_PLAY) {
413				d->sio.cstate = DEV_SIO_WRITE;
414				d->sio.todo = d->round * d->pchan * d->par.bps;
415				break;
416			} else {
417				d->sio.cstate = DEV_SIO_READ;
418				d->sio.todo = d->round * d->rchan * d->par.bps;
419				return;
420			}
421		case DEV_SIO_WRITE:
422#ifdef DEBUG
423			if (d->sio.todo == 0) {
424				dev_log(d);
425				log_puts(": can't write data\n");
426				panic();
427			}
428#endif
429			base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d);
430			data = base +
431			    d->pchan * d->round * d->par.bps -
432			    d->sio.todo;
433			n = sio_write(d->sio.hdl, data, d->sio.todo);
434			d->sio.todo -= n;
435#ifdef DEBUG
436			if (log_level >= 4) {
437				dev_log(d);
438				log_puts(": wrote ");
439				log_putu(n);
440				log_puts(" bytes, todo ");
441				log_putu(d->sio.todo);
442				log_puts("/");
443				log_putu(d->round * d->pchan * d->par.bps);
444				log_puts("\n");
445			}
446#endif
447			if (d->sio.todo > 0)
448				return;
449#ifdef DEBUG
450			d->sio.pused += d->round;
451			if (log_level >= 2) {
452				if (d->prime == 0 &&
453				    d->sio.pused <= d->bufsz - d->round) {
454					dev_log(d);
455					log_puts(": play hw xrun, pused = ");
456					log_puti(d->sio.pused);
457					log_puts("/");
458					log_puti(d->bufsz);
459					log_puts("\n");
460				}
461				if (d->sio.pused < 0 ||
462				    d->sio.pused > d->bufsz) {
463					/* device driver or libsndio bug */
464					dev_log(d);
465					log_puts(": out of bounds pused = ");
466					log_puti(d->sio.pused);
467					log_puts("/");
468					log_puti(d->bufsz);
469					log_puts("\n");
470				}
471			}
472#endif
473			d->poffs += d->round;
474			if (d->poffs == d->psize)
475				d->poffs = 0;
476			if ((d->mode & MODE_REC) && d->prime == 0) {
477				d->sio.cstate = DEV_SIO_READ;
478				d->sio.todo = d->round * d->rchan * d->par.bps;
479			} else
480				d->sio.cstate = DEV_SIO_CYCLE;
481			return;
482		}
483	}
484}
485
486void
487dev_sio_hup(void *arg)
488{
489	struct dev *d = arg;
490
491#ifdef DEBUG
492	if (log_level >= 2) {
493		dev_log(d);
494		log_puts(": disconnected\n");
495	}
496#endif
497	dev_close(d);
498}
499