siofile.c revision 1.21
1/*	$OpenBSD: siofile.c,v 1.21 2020/06/18 05:11:13 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 dev_alt *n;
97	struct sio_hdl *hdl;
98	struct sioctl_hdl *ctlhdl;
99
100	for (n = d->alt_list; n != NULL; n = n->next) {
101		if (d->alt_num == n->idx)
102			continue;
103		hdl = fdpass_sio_open(d->num, n->idx, mode);
104		if (hdl != NULL) {
105			if (log_level >= 2) {
106				dev_log(d);
107				log_puts(": using ");
108				log_puts(n->name);
109				log_puts("\n");
110			}
111			ctlhdl = fdpass_sioctl_open(d->num, n->idx,
112			    SIOCTL_READ | SIOCTL_WRITE);
113			if (ctlhdl == NULL) {
114				if (log_level >= 1) {
115					dev_log(d);
116					log_puts(": no control device\n");
117				}
118			}
119			d->alt_num = n->idx;
120			*rctlhdl = ctlhdl;
121			return hdl;
122		}
123	}
124	return NULL;
125}
126
127/*
128 * open the device.
129 */
130int
131dev_sio_open(struct dev *d)
132{
133	struct sio_par par;
134	unsigned int mode = d->mode & (MODE_PLAY | MODE_REC);
135
136	d->sio.hdl = dev_sio_openlist(d, mode, &d->sioctl.hdl);
137	if (d->sio.hdl == NULL) {
138		if (mode != (SIO_PLAY | SIO_REC))
139			return 0;
140		d->sio.hdl = dev_sio_openlist(d, SIO_PLAY, &d->sioctl.hdl);
141		if (d->sio.hdl != NULL)
142			mode = SIO_PLAY;
143		else {
144			d->sio.hdl = dev_sio_openlist(d,
145			    SIO_REC, &d->sioctl.hdl);
146			if (d->sio.hdl != NULL)
147				mode = SIO_REC;
148			else
149				return 0;
150		}
151		if (log_level >= 1) {
152			log_puts("warning, device opened in ");
153			log_puts(mode == SIO_PLAY ? "play-only" : "rec-only");
154			log_puts(" mode\n");
155		}
156	}
157	sio_initpar(&par);
158	par.bits = d->par.bits;
159	par.bps = d->par.bps;
160	par.sig = d->par.sig;
161	par.le = d->par.le;
162	par.msb = d->par.msb;
163	if (mode & SIO_PLAY)
164		par.pchan = d->pchan;
165	if (mode & SIO_REC)
166		par.rchan = d->rchan;
167	if (d->bufsz)
168		par.appbufsz = d->bufsz;
169	if (d->round)
170		par.round = d->round;
171	if (d->rate)
172		par.rate = d->rate;
173	if (!sio_setpar(d->sio.hdl, &par))
174		goto bad_close;
175	if (!sio_getpar(d->sio.hdl, &par))
176		goto bad_close;
177
178#ifdef DEBUG
179	/*
180	 * We support any parameter combination exposed by the kernel,
181	 * and we have no other choice than trusting the kernel for
182	 * returning correct parameters. But let's check parameters
183	 * early and nicely report kernel bugs rather than crashing
184	 * later in memset(), malloc() or alike.
185	 */
186
187	if (par.bits > BITS_MAX) {
188		dev_log(d);
189		log_puts(": ");
190		log_putu(par.bits);
191		log_puts(": unsupported number of bits\n");
192		goto bad_close;
193	}
194	if (par.bps > SIO_BPS(BITS_MAX)) {
195		dev_log(d);
196		log_puts(": ");
197		log_putu(par.bps);
198		log_puts(": unsupported sample size\n");
199		goto bad_close;
200	}
201	if ((mode & SIO_PLAY) && par.pchan > NCHAN_MAX) {
202		dev_log(d);
203		log_puts(": ");
204		log_putu(par.pchan);
205		log_puts(": unsupported number of play channels\n");
206		goto bad_close;
207	}
208	if ((mode & SIO_REC) && par.rchan > NCHAN_MAX) {
209		dev_log(d);
210		log_puts(": ");
211		log_putu(par.rchan);
212		log_puts(": unsupported number of rec channels\n");
213		goto bad_close;
214	}
215	if (par.bufsz == 0 || par.bufsz > RATE_MAX) {
216		dev_log(d);
217		log_puts(": ");
218		log_putu(par.bufsz);
219		log_puts(": unsupported buffer size\n");
220		goto bad_close;
221	}
222	if (par.round == 0 || par.round > par.bufsz ||
223	    par.bufsz % par.round != 0) {
224		dev_log(d);
225		log_puts(": ");
226		log_putu(par.round);
227		log_puts(": unsupported block size\n");
228		goto bad_close;
229	}
230	if (par.rate == 0 || par.rate > RATE_MAX) {
231		dev_log(d);
232		log_puts(": ");
233		log_putu(par.rate);
234		log_puts(": unsupported rate\n");
235		goto bad_close;
236	}
237#endif
238
239	d->par.bits = par.bits;
240	d->par.bps = par.bps;
241	d->par.sig = par.sig;
242	d->par.le = par.le;
243	d->par.msb = par.msb;
244	if (mode & SIO_PLAY)
245		d->pchan = par.pchan;
246	if (mode & SIO_REC)
247		d->rchan = par.rchan;
248	d->bufsz = par.bufsz;
249	d->round = par.round;
250	d->rate = par.rate;
251	if (!(mode & MODE_PLAY))
252		d->mode &= ~(MODE_PLAY | MODE_MON);
253	if (!(mode & MODE_REC))
254		d->mode &= ~MODE_REC;
255	sio_onmove(d->sio.hdl, dev_sio_onmove, d);
256	d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(d->sio.hdl));
257	if (d->sioctl.hdl) {
258		d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix",
259		    sioctl_nfds(d->sioctl.hdl));
260	}
261	timo_set(&d->sio.watchdog, dev_sio_timeout, d);
262	dev_sioctl_open(d);
263	return 1;
264 bad_close:
265	sio_close(d->sio.hdl);
266	if (d->sioctl.hdl) {
267		sioctl_close(d->sioctl.hdl);
268		d->sioctl.hdl = NULL;
269	}
270	return 0;
271}
272
273/*
274 * Open an alternate device. Upon success and if the new device is
275 * compatible with the old one, close the old device and continue
276 * using the new one. The new device is not started.
277 */
278int
279dev_sio_reopen(struct dev *d)
280{
281	struct sioctl_hdl *ctlhdl;
282	struct sio_par par;
283	struct sio_hdl *hdl;
284
285	hdl = dev_sio_openlist(d, d->mode & (MODE_PLAY | MODE_REC), &ctlhdl);
286	if (hdl == NULL) {
287		if (log_level >= 1) {
288			dev_log(d);
289			log_puts(": couldn't open an alternate device\n");
290		}
291		return 0;
292	}
293
294	sio_initpar(&par);
295	par.bits = d->par.bits;
296	par.bps = d->par.bps;
297	par.sig = d->par.sig;
298	par.le = d->par.le;
299	par.msb = d->par.msb;
300	if (d->mode & SIO_PLAY)
301		par.pchan = d->pchan;
302	if (d->mode & SIO_REC)
303		par.rchan = d->rchan;
304	par.appbufsz = d->bufsz;
305	par.round = d->round;
306	par.rate = d->rate;
307	if (!sio_setpar(hdl, &par))
308		goto bad_close;
309	if (!sio_getpar(hdl, &par))
310		goto bad_close;
311
312	/* check if new parameters are compatible with old ones */
313	if (par.round != d->round || par.bufsz != d->bufsz ||
314	    par.rate != d->rate) {
315		if (log_level >= 1) {
316			dev_log(d);
317			log_puts(": alternate device not compatible\n");
318		}
319		goto bad_close;
320	}
321
322	/* close unused device */
323	timo_del(&d->sio.watchdog);
324	file_del(d->sio.file);
325	sio_close(d->sio.hdl);
326	if (d->sioctl.hdl) {
327		file_del(d->sioctl.file);
328		sioctl_close(d->sioctl.hdl);
329		d->sioctl.hdl = NULL;
330	}
331
332	/* update parameters */
333	d->par.bits = par.bits;
334	d->par.bps = par.bps;
335	d->par.sig = par.sig;
336	d->par.le = par.le;
337	d->par.msb = par.msb;
338	if (d->mode & SIO_PLAY)
339		d->pchan = par.pchan;
340	if (d->mode & SIO_REC)
341		d->rchan = par.rchan;
342
343	d->sio.hdl = hdl;
344	d->sioctl.hdl = ctlhdl;
345	d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(hdl));
346	if (d->sioctl.hdl) {
347		d->sioctl.file = file_new(&dev_sioctl_ops, d, "mix",
348		    sioctl_nfds(ctlhdl));
349	}
350	sio_onmove(hdl, dev_sio_onmove, d);
351	return 1;
352bad_close:
353	sio_close(hdl);
354	if (ctlhdl)
355		sioctl_close(ctlhdl);
356	return 0;
357}
358
359void
360dev_sio_close(struct dev *d)
361{
362	dev_sioctl_close(d);
363#ifdef DEBUG
364	if (log_level >= 3) {
365		dev_log(d);
366		log_puts(": closed\n");
367	}
368#endif
369	timo_del(&d->sio.watchdog);
370	file_del(d->sio.file);
371	sio_close(d->sio.hdl);
372	if (d->sioctl.hdl) {
373		file_del(d->sioctl.file);
374		sioctl_close(d->sioctl.hdl);
375		d->sioctl.hdl = NULL;
376	}
377	d->alt_num = -1;
378}
379
380void
381dev_sio_start(struct dev *d)
382{
383	if (!sio_start(d->sio.hdl)) {
384		if (log_level >= 1) {
385			dev_log(d);
386			log_puts(": failed to start device\n");
387		}
388		return;
389	}
390	if (d->mode & MODE_PLAY) {
391		d->sio.cstate = DEV_SIO_CYCLE;
392		d->sio.todo = 0;
393	} else {
394		d->sio.cstate = DEV_SIO_READ;
395		d->sio.todo = d->round * d->rchan * d->par.bps;
396	}
397#ifdef DEBUG
398	d->sio.pused = 0;
399	d->sio.rused = 0;
400	d->sio.sum_utime = 0;
401	d->sio.sum_wtime = 0;
402	d->sio.wtime = file_wtime;
403	d->sio.utime = file_utime;
404	if (log_level >= 3) {
405		dev_log(d);
406		log_puts(": started\n");
407	}
408#endif
409	timo_add(&d->sio.watchdog, WATCHDOG_USEC);
410}
411
412void
413dev_sio_stop(struct dev *d)
414{
415	if (!sio_eof(d->sio.hdl) && !sio_stop(d->sio.hdl)) {
416		if (log_level >= 1) {
417			dev_log(d);
418			log_puts(": failed to stop device\n");
419		}
420		return;
421	}
422#ifdef DEBUG
423	if (log_level >= 3) {
424		dev_log(d);
425		log_puts(": stopped, load avg = ");
426		log_puti(d->sio.sum_utime / 1000);
427		log_puts(" / ");
428		log_puti(d->sio.sum_wtime / 1000);
429		log_puts("\n");
430	}
431#endif
432	timo_del(&d->sio.watchdog);
433}
434
435int
436dev_sio_pollfd(void *arg, struct pollfd *pfd)
437{
438	struct dev *d = arg;
439	int events;
440
441	events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
442	return sio_pollfd(d->sio.hdl, pfd, events);
443}
444
445int
446dev_sio_revents(void *arg, struct pollfd *pfd)
447{
448	struct dev *d = arg;
449	int events;
450
451	events = sio_revents(d->sio.hdl, pfd);
452#ifdef DEBUG
453	d->sio.events = events;
454#endif
455	return events;
456}
457
458void
459dev_sio_run(void *arg)
460{
461	struct dev *d = arg;
462	unsigned char *data, *base;
463	unsigned int n;
464
465	/*
466	 * sio_read() and sio_write() would block at the end of the
467	 * cycle so we *must* return and restart poll()'ing. Otherwise
468	 * we may trigger dev_cycle() which would make all clients
469	 * underrun (ex, on a play-only device)
470	 */
471	for (;;) {
472		if (d->pstate != DEV_RUN)
473			return;
474		switch (d->sio.cstate) {
475		case DEV_SIO_READ:
476#ifdef DEBUG
477			if (!(d->sio.events & POLLIN)) {
478				dev_log(d);
479				log_puts(": recording, but POLLIN not set\n");
480				panic();
481			}
482			if (d->sio.todo == 0) {
483				dev_log(d);
484				log_puts(": can't read data\n");
485				panic();
486			}
487			if (d->prime > 0) {
488				dev_log(d);
489				log_puts(": unexpected data\n");
490				panic();
491			}
492#endif
493			base = d->decbuf ? d->decbuf : (unsigned char *)d->rbuf;
494			data = base +
495			    d->rchan * d->round * d->par.bps -
496			    d->sio.todo;
497			n = sio_read(d->sio.hdl, data, d->sio.todo);
498			d->sio.todo -= n;
499#ifdef DEBUG
500			if (log_level >= 4) {
501				dev_log(d);
502				log_puts(": read ");
503				log_putu(n);
504				log_puts(": bytes, todo ");
505				log_putu(d->sio.todo);
506				log_puts("/");
507				log_putu(d->round * d->rchan * d->par.bps);
508				log_puts("\n");
509			}
510#endif
511			if (d->sio.todo > 0)
512				return;
513#ifdef DEBUG
514			d->sio.rused -= d->round;
515			if (log_level >= 2) {
516				if (d->sio.rused >= d->round) {
517					dev_log(d);
518					log_puts(": rec hw xrun, rused = ");
519					log_puti(d->sio.rused);
520					log_puts("/");
521					log_puti(d->bufsz);
522					log_puts("\n");
523				}
524				if (d->sio.rused < 0 ||
525				    d->sio.rused >= d->bufsz) {
526					dev_log(d);
527					log_puts(": out of bounds rused = ");
528					log_puti(d->sio.rused);
529					log_puts("/");
530					log_puti(d->bufsz);
531					log_puts("\n");
532				}
533			}
534#endif
535			d->sio.cstate = DEV_SIO_CYCLE;
536			break;
537		case DEV_SIO_CYCLE:
538			timo_del(&d->sio.watchdog);
539			timo_add(&d->sio.watchdog, WATCHDOG_USEC);
540
541#ifdef DEBUG
542			/*
543			 * check that we're called at cycle boundary:
544			 * either after a recorded block, or when POLLOUT is
545			 * raised
546			 */
547			if (!((d->mode & MODE_REC) && d->prime == 0) &&
548			    !(d->sio.events & POLLOUT)) {
549				dev_log(d);
550				log_puts(": cycle not at block boundary\n");
551				panic();
552			}
553#endif
554			dev_cycle(d);
555			if (d->mode & MODE_PLAY) {
556				d->sio.cstate = DEV_SIO_WRITE;
557				d->sio.todo = d->round * d->pchan * d->par.bps;
558				break;
559			} else {
560				d->sio.cstate = DEV_SIO_READ;
561				d->sio.todo = d->round * d->rchan * d->par.bps;
562				return;
563			}
564		case DEV_SIO_WRITE:
565#ifdef DEBUG
566			if (d->sio.todo == 0) {
567				dev_log(d);
568				log_puts(": can't write data\n");
569				panic();
570			}
571#endif
572			base = d->encbuf ? d->encbuf : (unsigned char *)DEV_PBUF(d);
573			data = base +
574			    d->pchan * d->round * d->par.bps -
575			    d->sio.todo;
576			n = sio_write(d->sio.hdl, data, d->sio.todo);
577			d->sio.todo -= n;
578#ifdef DEBUG
579			if (log_level >= 4) {
580				dev_log(d);
581				log_puts(": wrote ");
582				log_putu(n);
583				log_puts(" bytes, todo ");
584				log_putu(d->sio.todo);
585				log_puts("/");
586				log_putu(d->round * d->pchan * d->par.bps);
587				log_puts("\n");
588			}
589#endif
590			if (d->sio.todo > 0)
591				return;
592#ifdef DEBUG
593			d->sio.pused += d->round;
594			if (log_level >= 2) {
595				if (d->prime == 0 &&
596				    d->sio.pused <= d->bufsz - d->round) {
597					dev_log(d);
598					log_puts(": play hw xrun, pused = ");
599					log_puti(d->sio.pused);
600					log_puts("/");
601					log_puti(d->bufsz);
602					log_puts("\n");
603				}
604				if (d->sio.pused < 0 ||
605				    d->sio.pused > d->bufsz) {
606					/* device driver or libsndio bug */
607					dev_log(d);
608					log_puts(": out of bounds pused = ");
609					log_puti(d->sio.pused);
610					log_puts("/");
611					log_puti(d->bufsz);
612					log_puts("\n");
613				}
614			}
615#endif
616			d->poffs += d->round;
617			if (d->poffs == d->psize)
618				d->poffs = 0;
619			if ((d->mode & MODE_REC) && d->prime == 0) {
620				d->sio.cstate = DEV_SIO_READ;
621				d->sio.todo = d->round * d->rchan * d->par.bps;
622			} else
623				d->sio.cstate = DEV_SIO_CYCLE;
624			return;
625		}
626	}
627}
628
629void
630dev_sio_hup(void *arg)
631{
632	struct dev *d = arg;
633
634#ifdef DEBUG
635	if (log_level >= 2) {
636		dev_log(d);
637		log_puts(": disconnected\n");
638	}
639#endif
640	if (!dev_reopen(d))
641		dev_abort(d);
642}
643