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