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