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