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