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