siofile.c revision 1.20
1/*	$OpenBSD: siofile.c,v 1.20 2020/06/12 15:40:18 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 "dev_sioctl.h"
30#include "dsp.h"
31#include "fdpass.h"
32#include "file.h"
33#include "siofile.h"
34#include "utils.h"
35
36#define WATCHDOG_USEC	4000000		/* 4 seconds */
37
38void dev_sio_onmove(void *, int);
39void dev_sio_timeout(void *);
40int dev_sio_pollfd(void *, struct pollfd *);
41int dev_sio_revents(void *, struct pollfd *);
42void dev_sio_run(void *);
43void dev_sio_hup(void *);
44
45extern struct fileops dev_sioctl_ops;
46
47struct 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
56void
57dev_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
80void
81dev_sio_timeout(void *arg)
82{
83	struct dev *d = arg;
84
85	dev_log(d);
86	log_puts(": watchdog timeout\n");
87	dev_abort(d);
88}
89
90/*
91 * open the device using one of the provided paths
92 */
93static struct sio_hdl *
94dev_sio_openlist(struct dev *d, unsigned int mode, struct sioctl_hdl **rctlhdl)
95{
96	struct name *n;
97	struct sio_hdl *hdl;
98	struct sioctl_hdl *ctlhdl;
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			}
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;
123			return hdl;
124		}
125		n = n->next;
126		idx++;
127	}
128	return NULL;
129}
130
131/*
132 * open the device.
133 */
134int
135dev_sio_open(struct dev *d)
136{
137	struct sio_par par;
138	unsigned int mode = d->mode & (MODE_PLAY | MODE_REC);
139
140	d->sio.hdl = dev_sio_openlist(d, mode, &d->sioctl.hdl);
141	if (d->sio.hdl == NULL) {
142		if (mode != (SIO_PLAY | SIO_REC))
143			return 0;
144		d->sio.hdl = dev_sio_openlist(d, SIO_PLAY, &d->sioctl.hdl);
145		if (d->sio.hdl != NULL)
146			mode = SIO_PLAY;
147		else {
148			d->sio.hdl = dev_sio_openlist(d,
149			    SIO_REC, &d->sioctl.hdl);
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;
181
182#ifdef DEBUG
183	/*
184	 * We support any parameter combination exposed by the kernel,
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) {
192		dev_log(d);
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)) {
199		dev_log(d);
200		log_puts(": ");
201		log_putu(par.bps);
202		log_puts(": unsupported sample size\n");
203		goto bad_close;
204	}
205	if ((mode & SIO_PLAY) && par.pchan > NCHAN_MAX) {
206		dev_log(d);
207		log_puts(": ");
208		log_putu(par.pchan);
209		log_puts(": unsupported number of play channels\n");
210		goto bad_close;
211	}
212	if ((mode & SIO_REC) && par.rchan > NCHAN_MAX) {
213		dev_log(d);
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) {
220		dev_log(d);
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) {
228		dev_log(d);
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) {
235		dev_log(d);
236		log_puts(": ");
237		log_putu(par.rate);
238		log_puts(": unsupported rate\n");
239		goto bad_close;
240	}
241#endif
242
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);
260	d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(d->sio.hdl));
261	if (d->sioctl.hdl) {
262		d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix",
263		    sioctl_nfds(d->sioctl.hdl));
264	}
265	timo_set(&d->sio.watchdog, dev_sio_timeout, d);
266	dev_sioctl_open(d);
267	return 1;
268 bad_close:
269	sio_close(d->sio.hdl);
270	if (d->sioctl.hdl) {
271		sioctl_close(d->sioctl.hdl);
272		d->sioctl.hdl = NULL;
273	}
274	return 0;
275}
276
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 */
282int
283dev_sio_reopen(struct dev *d)
284{
285	struct sioctl_hdl *ctlhdl;
286	struct sio_par par;
287	struct sio_hdl *hdl;
288
289	hdl = dev_sio_openlist(d, d->mode & (MODE_PLAY | MODE_REC), &ctlhdl);
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);
330	if (d->sioctl.hdl) {
331		file_del(d->sioctl.file);
332		sioctl_close(d->sioctl.hdl);
333		d->sioctl.hdl = NULL;
334	}
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;
348	d->sioctl.hdl = ctlhdl;
349	d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(hdl));
350	if (d->sioctl.hdl) {
351		d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix",
352		    sioctl_nfds(ctlhdl));
353	}
354	sio_onmove(hdl, dev_sio_onmove, d);
355	return 1;
356bad_close:
357	sio_close(hdl);
358	if (ctlhdl)
359		sioctl_close(ctlhdl);
360	return 0;
361}
362
363void
364dev_sio_close(struct dev *d)
365{
366	dev_sioctl_close(d);
367#ifdef DEBUG
368	if (log_level >= 3) {
369		dev_log(d);
370		log_puts(": closed\n");
371	}
372#endif
373	timo_del(&d->sio.watchdog);
374	file_del(d->sio.file);
375	sio_close(d->sio.hdl);
376	if (d->sioctl.hdl) {
377		file_del(d->sioctl.file);
378		sioctl_close(d->sioctl.hdl);
379		d->sioctl.hdl = NULL;
380	}
381}
382
383void
384dev_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
412	timo_add(&d->sio.watchdog, WATCHDOG_USEC);
413}
414
415void
416dev_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
435	timo_del(&d->sio.watchdog);
436}
437
438int
439dev_sio_pollfd(void *arg, struct pollfd *pfd)
440{
441	struct dev *d = arg;
442	int events;
443
444	events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
445	return sio_pollfd(d->sio.hdl, pfd, events);
446}
447
448int
449dev_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
461void
462dev_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:
541			timo_del(&d->sio.watchdog);
542			timo_add(&d->sio.watchdog, WATCHDOG_USEC);
543
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;
620			if (d->poffs == d->psize)
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
632void
633dev_sio_hup(void *arg)
634{
635	struct dev *d = arg;
636
637#ifdef DEBUG
638	if (log_level >= 2) {
639		dev_log(d);
640		log_puts(": disconnected\n");
641	}
642#endif
643	if (!dev_reopen(d))
644		dev_abort(d);
645}
646