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