xref: /dragonfly/sys/dev/sound/pcm/channel.c (revision fe76c4fb)
1 /*
2  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3  * Portions Copyright by Luigi Rizzo - 1997-99
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/dev/sound/pcm/channel.c,v 1.19.2.19 2003/03/11 15:15:41 orion Exp $
28  * $DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.8 2006/06/13 08:12:02 dillon Exp $
29  */
30 
31 #include <dev/sound/pcm/sound.h>
32 #include <sys/vnode.h>
33 
34 #include "feeder_if.h"
35 
36 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.8 2006/06/13 08:12:02 dillon Exp $");
37 
38 #define MIN_CHUNK_SIZE 		256	/* for uiomove etc. */
39 #define	DMA_ALIGN_THRESHOLD	4
40 #define	DMA_ALIGN_MASK		(~(DMA_ALIGN_THRESHOLD - 1))
41 
42 #define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED))
43 
44 /*
45 #define DEB(x) x
46 */
47 
48 static int chn_targetirqrate = 32;
49 TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate);
50 
51 static int
52 sysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS)
53 {
54 	int err, val;
55 
56 	val = chn_targetirqrate;
57 	err = sysctl_handle_int(oidp, &val, sizeof(val), req);
58 	if (val < 16 || val > 512)
59 		err = EINVAL;
60 	else
61 		chn_targetirqrate = val;
62 
63 	return err;
64 }
65 SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW,
66 	0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "");
67 static int report_soft_formats = 1;
68 SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
69 	&report_soft_formats, 1, "report software-emulated formats");
70 
71 static int chn_buildfeeder(struct pcm_channel *c);
72 
73 static void
74 chn_lockinit(struct pcm_channel *c)
75 {
76 	c->lock = snd_mtxcreate(c->name, "pcm channel");
77 }
78 
79 static void
80 chn_lockdestroy(struct pcm_channel *c)
81 {
82 	snd_mtxfree(c->lock);
83 }
84 
85 static int
86 chn_polltrigger(struct pcm_channel *c)
87 {
88 	struct snd_dbuf *bs = c->bufsoft;
89 	unsigned amt, lim;
90 
91 	CHN_LOCKASSERT(c);
92 	if (c->flags & CHN_F_MAPPED) {
93 		if (sndbuf_getprevblocks(bs) == 0)
94 			return 1;
95 		else
96 			return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0;
97 	} else {
98 		amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
99 		lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
100 		lim = 1;
101 		return (amt >= lim)? 1 : 0;
102 	}
103 	return 0;
104 }
105 
106 static int
107 chn_pollreset(struct pcm_channel *c)
108 {
109 	struct snd_dbuf *bs = c->bufsoft;
110 
111 	CHN_LOCKASSERT(c);
112 	sndbuf_updateprevtotal(bs);
113 	return 1;
114 }
115 
116 static void
117 chn_wakeup(struct pcm_channel *c)
118 {
119     	struct snd_dbuf *bs = c->bufsoft;
120 
121 	CHN_LOCKASSERT(c);
122 	if (sndbuf_getsel(bs)->si_pid && chn_polltrigger(c))
123 		selwakeup(sndbuf_getsel(bs));
124 	wakeup(bs);
125 }
126 
127 static int
128 chn_sleep(struct pcm_channel *c, char *str, int timeout)
129 {
130     	struct snd_dbuf *bs = c->bufsoft;
131 	int ret;
132 
133 	CHN_LOCKASSERT(c);
134 #ifdef USING_MUTEX
135 	ret = msleep(bs, c->lock, PCATCH, str, timeout);
136 #else
137 	ret = tsleep(bs, PCATCH, str, timeout);
138 #endif
139 
140 	return ret;
141 }
142 
143 /*
144  * chn_dmaupdate() tracks the status of a dma transfer,
145  * updating pointers. It must be called from a critical section.
146  */
147 
148 static unsigned int
149 chn_dmaupdate(struct pcm_channel *c)
150 {
151 	struct snd_dbuf *b = c->bufhard;
152 	unsigned int delta, old, hwptr, amt;
153 
154 	KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0"));
155 	CHN_LOCKASSERT(c);
156 
157 	old = sndbuf_gethwptr(b);
158 	hwptr = chn_getptr(c);
159 	delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b);
160 	sndbuf_sethwptr(b, hwptr);
161 
162 	DEB(
163 	if (delta >= ((sndbuf_getsize(b) * 15) / 16)) {
164 		if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING)))
165 			device_printf(c->dev, "hwptr went backwards %d -> %d\n", old, hwptr);
166 	}
167 	);
168 
169 	if (c->direction == PCMDIR_PLAY) {
170 		amt = MIN(delta, sndbuf_getready(b));
171 		if (amt > 0)
172 			sndbuf_dispose(b, NULL, amt);
173 	} else {
174 		amt = MIN(delta, sndbuf_getfree(b));
175 		if (amt > 0)
176 		       sndbuf_acquire(b, NULL, amt);
177 	}
178 
179 	return delta;
180 }
181 
182 void
183 chn_wrupdate(struct pcm_channel *c)
184 {
185 	int ret;
186 
187 	CHN_LOCKASSERT(c);
188 	KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
189 
190 	if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || !(c->flags & CHN_F_TRIGGERED))
191 		return;
192 	chn_dmaupdate(c);
193 	ret = chn_wrfeed(c);
194 	/* tell the driver we've updated the primary buffer */
195 	chn_trigger(c, PCMTRIG_EMLDMAWR);
196 	DEB(if (ret)
197 		printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);)
198 
199 }
200 
201 int
202 chn_wrfeed(struct pcm_channel *c)
203 {
204     	struct snd_dbuf *b = c->bufhard;
205     	struct snd_dbuf *bs = c->bufsoft;
206 	unsigned int ret, amt;
207 
208 	CHN_LOCKASSERT(c);
209     	DEB(
210 	if (c->flags & CHN_F_CLOSING) {
211 		sndbuf_dump(b, "b", 0x02);
212 		sndbuf_dump(bs, "bs", 0x02);
213 	})
214 
215 	if (c->flags & CHN_F_MAPPED)
216 		sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
217 
218 	amt = sndbuf_getfree(b);
219 	if (sndbuf_getready(bs) < amt)
220 		c->xruns++;
221 
222 	ret = (amt > 0)? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
223 	if (ret == 0 && sndbuf_getfree(b) < amt)
224 		chn_wakeup(c);
225 
226 	return ret;
227 }
228 
229 static void
230 chn_wrintr(struct pcm_channel *c)
231 {
232 	int ret;
233 
234 	CHN_LOCKASSERT(c);
235 	/* update pointers in primary buffer */
236 	chn_dmaupdate(c);
237 	/* ...and feed from secondary to primary */
238 	ret = chn_wrfeed(c);
239 	/* tell the driver we've updated the primary buffer */
240 	chn_trigger(c, PCMTRIG_EMLDMAWR);
241 	DEB(if (ret)
242 		printf("chn_wrintr: chn_wrfeed returned %d\n", ret);)
243 }
244 
245 /*
246  * user write routine - uiomove data into secondary buffer, trigger if necessary
247  * if blocking, sleep, rinse and repeat.
248  *
249  * called externally, so must handle locking
250  */
251 
252 int
253 chn_write(struct pcm_channel *c, struct uio *buf, int ioflags)
254 {
255 	int ret, timeout, newsize, count, sz;
256 	int nbio;
257 	struct snd_dbuf *bs = c->bufsoft;
258 
259 	CHN_LOCKASSERT(c);
260 	nbio = (c->flags & CHN_F_NBIO) || (ioflags & IO_NDELAY);
261 	/*
262 	 * XXX Certain applications attempt to write larger size
263 	 * of pcm data than c->blocksize2nd without blocking,
264 	 * resulting partial write. Expand the block size so that
265 	 * the write operation avoids blocking.
266 	 */
267 	if (nbio && buf->uio_resid > sndbuf_getblksz(bs)) {
268 		DEB(device_printf(c->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n",
269 			buf->uio_resid, sndbuf_getblksz(bs)));
270 		newsize = 16;
271 		while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2))
272 			newsize <<= 1;
273 		chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize);
274 		DEB(device_printf(c->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs)));
275 	}
276 
277 	ret = 0;
278 	count = hz;
279 	while (!ret && (buf->uio_resid > 0) && (count > 0)) {
280 		sz = sndbuf_getfree(bs);
281 		if (sz == 0) {
282 			if (nbio) {
283 				ret = EWOULDBLOCK;
284 			} else {
285 				timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
286 				if (timeout < 1)
287 					timeout = 1;
288 				timeout = 1;
289 	   			ret = chn_sleep(c, "pcmwr", timeout);
290 				if (ret == EWOULDBLOCK) {
291 					count -= timeout;
292 					ret = 0;
293 				} else if (ret == 0)
294 					count = hz;
295 			}
296 		} else {
297 			sz = MIN(sz, buf->uio_resid);
298 			KASSERT(sz > 0, ("confusion in chn_write"));
299 			/* printf("sz: %d\n", sz); */
300 			ret = sndbuf_uiomove(bs, buf, sz);
301 			if (ret == 0 && !(c->flags & CHN_F_TRIGGERED))
302 				chn_start(c, 0);
303 		}
304 	}
305 	/* printf("ret: %d left: %d\n", ret, buf->uio_resid); */
306 
307 	if (count <= 0) {
308 		c->flags |= CHN_F_DEAD;
309 		printf("%s: play interrupt timeout, channel dead\n", c->name);
310 	}
311 
312 	return ret;
313 }
314 
315 static int
316 chn_rddump(struct pcm_channel *c, unsigned int cnt)
317 {
318     	struct snd_dbuf *b = c->bufhard;
319 
320 	CHN_LOCKASSERT(c);
321 	sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt);
322 	return sndbuf_dispose(b, NULL, cnt);
323 }
324 
325 /*
326  * Feed new data from the read buffer. Can be called in the bottom half.
327  * Hence must be called from a critical section.
328  */
329 int
330 chn_rdfeed(struct pcm_channel *c)
331 {
332     	struct snd_dbuf *b = c->bufhard;
333     	struct snd_dbuf *bs = c->bufsoft;
334 	unsigned int ret, amt;
335 
336 	CHN_LOCKASSERT(c);
337     	DEB(
338 	if (c->flags & CHN_F_CLOSING) {
339 		sndbuf_dump(b, "b", 0x02);
340 		sndbuf_dump(bs, "bs", 0x02);
341 	})
342 
343 	amt = sndbuf_getready(b);
344 	if (sndbuf_getfree(bs) < amt) {
345 		c->xruns++;
346 		amt = sndbuf_getfree(bs);
347 	}
348 	ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0;
349 
350 	amt = sndbuf_getready(b);
351 	if (amt > 0)
352 		chn_rddump(c, amt);
353 
354 	chn_wakeup(c);
355 
356 	return ret;
357 }
358 
359 void
360 chn_rdupdate(struct pcm_channel *c)
361 {
362 	int ret;
363 
364 	CHN_LOCKASSERT(c);
365 	KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel"));
366 
367 	if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED))
368 		return;
369 	chn_trigger(c, PCMTRIG_EMLDMARD);
370 	chn_dmaupdate(c);
371 	ret = chn_rdfeed(c);
372 	if (ret)
373 		printf("chn_rdfeed: %d\n", ret);
374 
375 }
376 
377 /* read interrupt routine. Must be called with interrupts blocked. */
378 static void
379 chn_rdintr(struct pcm_channel *c)
380 {
381 	int ret;
382 
383 	CHN_LOCKASSERT(c);
384 	/* tell the driver to update the primary buffer if non-dma */
385 	chn_trigger(c, PCMTRIG_EMLDMARD);
386 	/* update pointers in primary buffer */
387 	chn_dmaupdate(c);
388 	/* ...and feed from primary to secondary */
389 	ret = chn_rdfeed(c);
390 }
391 
392 /*
393  * user read routine - trigger if necessary, uiomove data from secondary buffer
394  * if blocking, sleep, rinse and repeat.
395  *
396  * called externally, so must handle locking
397  */
398 
399 int
400 chn_read(struct pcm_channel *c, struct uio *buf, int ioflags)
401 {
402 	int ret, timeout, sz, count;
403 	int nbio;
404 	struct snd_dbuf       *bs = c->bufsoft;
405 
406 	CHN_LOCKASSERT(c);
407 	if (!(c->flags & CHN_F_TRIGGERED))
408 		chn_start(c, 0);
409 
410 	nbio = (c->flags & CHN_F_NBIO) || (ioflags & IO_NDELAY);
411 	ret = 0;
412 	count = hz;
413 	while (!ret && (buf->uio_resid > 0) && (count > 0)) {
414 		sz = MIN(buf->uio_resid, sndbuf_getready(bs));
415 
416 		if (sz > 0) {
417 			ret = sndbuf_uiomove(bs, buf, sz);
418 		} else {
419 			if (nbio) {
420 				ret = EWOULDBLOCK;
421 			} else {
422 				timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
423 				if (timeout < 1)
424 					timeout = 1;
425 	   			ret = chn_sleep(c, "pcmrd", timeout);
426 				if (ret == EWOULDBLOCK) {
427 					count -= timeout;
428 					ret = 0;
429 				} else {
430 					count = hz;
431 				}
432 
433 			}
434 		}
435 	}
436 
437 	if (count <= 0) {
438 		c->flags |= CHN_F_DEAD;
439 		printf("%s: record interrupt timeout, channel dead\n", c->name);
440 	}
441 
442 	return ret;
443 }
444 
445 void
446 chn_intr(struct pcm_channel *c)
447 {
448 	CHN_LOCK(c);
449 	c->interrupts++;
450 	if (c->direction == PCMDIR_PLAY)
451 		chn_wrintr(c);
452 	else
453 		chn_rdintr(c);
454 	CHN_UNLOCK(c);
455 }
456 
457 u_int32_t
458 chn_start(struct pcm_channel *c, int force)
459 {
460 	u_int32_t i, j;
461 	struct snd_dbuf *b = c->bufhard;
462 	struct snd_dbuf *bs = c->bufsoft;
463 
464 	CHN_LOCKASSERT(c);
465 	/* if we're running, or if we're prevented from triggering, bail */
466 	if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force))
467 		return EINVAL;
468 
469 	i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs);
470 	j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b);
471 	if (force || (i >= j)) {
472 		c->flags |= CHN_F_TRIGGERED;
473 		/*
474 		 * if we're starting because a vchan started, don't feed any data
475 		 * or it becomes impossible to start vchans synchronised with the
476 		 * first one.  the hardbuf should be empty so we top it up with
477 		 * silence to give it something to chew.  the real data will be
478 		 * fed at the first irq.
479 		 */
480 		if (c->direction == PCMDIR_PLAY) {
481 			if (SLIST_EMPTY(&c->children))
482 				chn_wrfeed(c);
483 			else
484 				sndbuf_fillsilence(b);
485 		}
486 		sndbuf_setrun(b, 1);
487 		c->xruns = 0;
488 	    	chn_trigger(c, PCMTRIG_START);
489 		return 0;
490 	}
491 
492 	return 0;
493 }
494 
495 void
496 chn_resetbuf(struct pcm_channel *c)
497 {
498 	struct snd_dbuf *b = c->bufhard;
499 	struct snd_dbuf *bs = c->bufsoft;
500 
501 	c->blocks = 0;
502 	sndbuf_reset(b);
503 	sndbuf_reset(bs);
504 }
505 
506 /*
507  * chn_sync waits until the space in the given channel goes above
508  * a threshold. The threshold is checked against fl or rl respectively.
509  * Assume that the condition can become true, do not check here...
510  */
511 int
512 chn_sync(struct pcm_channel *c, int threshold)
513 {
514     	u_long rdy;
515     	int ret;
516     	struct snd_dbuf *bs = c->bufsoft;
517 
518 	CHN_LOCKASSERT(c);
519     	for (;;) {
520 		rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
521 		if (rdy <= threshold) {
522 	    		ret = chn_sleep(c, "pcmsyn", 1);
523 	    		if (ret == ERESTART || ret == EINTR) {
524 				DEB(printf("chn_sync: tsleep returns %d\n", ret));
525 				return -1;
526 	    		}
527 		} else
528 			break;
529     	}
530     	return 0;
531 }
532 
533 /* called externally, handle locking */
534 int
535 chn_poll(struct pcm_channel *c, int ev, struct proc *p)
536 {
537 	struct snd_dbuf *bs = c->bufsoft;
538 	int ret;
539 
540 	CHN_LOCKASSERT(c);
541     	if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED))
542 		chn_start(c, 1);
543 	ret = 0;
544 	if (chn_polltrigger(c) && chn_pollreset(c))
545 		ret = ev;
546 	else
547 		selrecord(p->p_thread, sndbuf_getsel(bs));
548 	return ret;
549 }
550 
551 /*
552  * chn_abort terminates a running dma transfer.  it may sleep up to 200ms.
553  * it returns the number of bytes that have not been transferred.
554  *
555  * called from: dsp_close, dsp_ioctl, with channel locked
556  */
557 int
558 chn_abort(struct pcm_channel *c)
559 {
560     	int missing = 0;
561     	struct snd_dbuf *b = c->bufhard;
562     	struct snd_dbuf *bs = c->bufsoft;
563 
564 	CHN_LOCKASSERT(c);
565 	if (!(c->flags & CHN_F_TRIGGERED))
566 		return 0;
567 	c->flags |= CHN_F_ABORTING;
568 
569 	c->flags &= ~CHN_F_TRIGGERED;
570 	/* kill the channel */
571 	chn_trigger(c, PCMTRIG_ABORT);
572 	sndbuf_setrun(b, 0);
573 	if (!(c->flags & CHN_F_VIRTUAL))
574 		chn_dmaupdate(c);
575     	missing = sndbuf_getready(bs) + sndbuf_getready(b);
576 
577 	c->flags &= ~CHN_F_ABORTING;
578 	return missing;
579 }
580 
581 /*
582  * this routine tries to flush the dma transfer. It is called
583  * on a close. We immediately abort any read DMA
584  * operation, and then wait for the play buffer to drain.
585  *
586  * called from: dsp_close
587  */
588 
589 int
590 chn_flush(struct pcm_channel *c)
591 {
592     	int ret, count, resid, resid_p;
593     	struct snd_dbuf *b = c->bufhard;
594     	struct snd_dbuf *bs = c->bufsoft;
595 
596 	CHN_LOCKASSERT(c);
597 	KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
598     	DEB(printf("chn_flush c->flags 0x%08x\n", c->flags));
599 	if (!(c->flags & CHN_F_TRIGGERED))
600 		return 0;
601 
602 	c->flags |= CHN_F_CLOSING;
603 	resid = sndbuf_getready(bs) + sndbuf_getready(b);
604 	resid_p = resid;
605 	count = 10;
606 	ret = 0;
607 	while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) {
608 		/* still pending output data. */
609 		ret = chn_sleep(c, "pcmflu", hz / 10);
610 		if (ret == EWOULDBLOCK)
611 			ret = 0;
612 		if (ret == 0) {
613 			resid = sndbuf_getready(bs) + sndbuf_getready(b);
614 			if (resid >= resid_p)
615 				count--;
616 			resid_p = resid;
617 		}
618    	}
619 	if (count == 0)
620 		DEB(printf("chn_flush: timeout\n"));
621 
622 	c->flags &= ~CHN_F_TRIGGERED;
623 	/* kill the channel */
624 	chn_trigger(c, PCMTRIG_ABORT);
625 	sndbuf_setrun(b, 0);
626 
627     	c->flags &= ~CHN_F_CLOSING;
628     	return 0;
629 }
630 
631 int
632 fmtvalid(u_int32_t fmt, u_int32_t *fmtlist)
633 {
634 	int i;
635 
636 	for (i = 0; fmtlist[i]; i++)
637 		if (fmt == fmtlist[i])
638 			return 1;
639 	return 0;
640 }
641 
642 int
643 chn_reset(struct pcm_channel *c, u_int32_t fmt)
644 {
645 	int hwspd, r;
646 
647 	CHN_LOCKASSERT(c);
648 	c->flags &= CHN_F_RESET;
649 	c->interrupts = 0;
650 	c->xruns = 0;
651 
652 	r = CHANNEL_RESET(c->methods, c->devinfo);
653 	if (fmt != 0) {
654 		hwspd = DSP_DEFAULT_SPEED;
655 		/* only do this on a record channel until feederbuilder works */
656 		if (c->direction == PCMDIR_REC)
657 			RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
658 		c->speed = hwspd;
659 
660 		if (r == 0)
661 			r = chn_setformat(c, fmt);
662 		if (r == 0)
663 			r = chn_setspeed(c, hwspd);
664 		if (r == 0)
665 			r = chn_setvolume(c, 100, 100);
666 	}
667 	if (r == 0)
668 		r = chn_setblocksize(c, 0, 0);
669 	if (r == 0) {
670 		chn_resetbuf(c);
671 		r = CHANNEL_RESETDONE(c->methods, c->devinfo);
672 	}
673 	return r;
674 }
675 
676 int
677 chn_init(struct pcm_channel *c, void *devinfo, int dir)
678 {
679 	struct feeder_class *fc;
680 	struct snd_dbuf *b, *bs;
681 	int ret;
682 
683 	chn_lockinit(c);
684 	CHN_LOCK(c);
685 
686 	b = NULL;
687 	bs = NULL;
688 	c->devinfo = NULL;
689 	c->feeder = NULL;
690 
691 	ret = EINVAL;
692 	fc = feeder_getclass(NULL);
693 	if (fc == NULL)
694 		goto out;
695 	if (chn_addfeeder(c, fc, NULL))
696 		goto out;
697 
698 	ret = ENOMEM;
699 	b = sndbuf_create(c->dev, c->name, "primary");
700 	if (b == NULL)
701 		goto out;
702 	bs = sndbuf_create(c->dev, c->name, "secondary");
703 	if (bs == NULL)
704 		goto out;
705 	sndbuf_setup(bs, NULL, 0);
706 	c->bufhard = b;
707 	c->bufsoft = bs;
708 	c->flags = 0;
709 	c->feederflags = 0;
710 
711 	ret = ENODEV;
712 	c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, dir);
713 	if (c->devinfo == NULL)
714 		goto out;
715 
716 	ret = ENOMEM;
717 	if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0))
718 		goto out;
719 
720 	ret = chn_setdir(c, dir);
721 	if (ret)
722 		goto out;
723 
724 	ret = sndbuf_setfmt(b, AFMT_U8);
725 	if (ret)
726 		goto out;
727 
728 	ret = sndbuf_setfmt(bs, AFMT_U8);
729 	if (ret)
730 		goto out;
731 
732 
733 out:
734 	if (ret) {
735 		if (c->devinfo) {
736 			if (CHANNEL_FREE(c->methods, c->devinfo))
737 				sndbuf_free(b);
738 		}
739 		if (bs)
740 			sndbuf_destroy(bs);
741 		if (b)
742 			sndbuf_destroy(b);
743 		c->flags |= CHN_F_DEAD;
744 		chn_lockdestroy(c);
745 
746 		return ret;
747 	}
748 
749 	CHN_UNLOCK(c);
750 	return 0;
751 }
752 
753 int
754 chn_kill(struct pcm_channel *c)
755 {
756     	struct snd_dbuf *b = c->bufhard;
757     	struct snd_dbuf *bs = c->bufsoft;
758 
759 	CHN_LOCK(c);
760 	if (c->flags & CHN_F_TRIGGERED)
761 		chn_trigger(c, PCMTRIG_ABORT);
762 	while (chn_removefeeder(c) == 0);
763 	if (CHANNEL_FREE(c->methods, c->devinfo))
764 		sndbuf_free(b);
765 	c->flags |= CHN_F_DEAD;
766 	sndbuf_destroy(bs);
767 	sndbuf_destroy(b);
768 	chn_lockdestroy(c);
769 	return 0;
770 }
771 
772 int
773 chn_setdir(struct pcm_channel *c, int dir)
774 {
775     	struct snd_dbuf *b = c->bufhard;
776 	int r;
777 
778 	CHN_LOCKASSERT(c);
779 	c->direction = dir;
780 	r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction);
781 	if (!r && ISA_DMA(b))
782 		sndbuf_isadmasetdir(b, c->direction);
783 	return r;
784 }
785 
786 int
787 chn_setvolume(struct pcm_channel *c, int left, int right)
788 {
789 	CHN_LOCKASSERT(c);
790 	/* could add a feeder for volume changing if channel returns -1 */
791 	c->volume = (left << 8) | right;
792 	return 0;
793 }
794 
795 static int
796 chn_tryspeed(struct pcm_channel *c, int speed)
797 {
798 	struct pcm_feeder *f;
799     	struct snd_dbuf *b = c->bufhard;
800     	struct snd_dbuf *bs = c->bufsoft;
801     	struct snd_dbuf *x;
802 	int r, delta;
803 
804 	CHN_LOCKASSERT(c);
805 	DEB(printf("setspeed, channel %s\n", c->name));
806 	DEB(printf("want speed %d, ", speed));
807 	if (speed <= 0)
808 		return EINVAL;
809 	if (CANCHANGE(c)) {
810 		r = 0;
811 		c->speed = speed;
812 		sndbuf_setspd(bs, speed);
813 		RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
814 		DEB(printf("try speed %d, ", speed));
815 		sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed));
816 		DEB(printf("got speed %d\n", sndbuf_getspd(b)));
817 
818 		delta = sndbuf_getspd(b) - sndbuf_getspd(bs);
819 		if (delta < 0)
820 			delta = -delta;
821 
822 		c->feederflags &= ~(1 << FEEDER_RATE);
823 		if (delta > 500)
824 			c->feederflags |= 1 << FEEDER_RATE;
825 		else
826 			sndbuf_setspd(bs, sndbuf_getspd(b));
827 
828 		r = chn_buildfeeder(c);
829 		DEB(printf("r = %d\n", r));
830 		if (r)
831 			goto out;
832 
833 		r = chn_setblocksize(c, 0, 0);
834 		if (r)
835 			goto out;
836 
837 		if (!(c->feederflags & (1 << FEEDER_RATE)))
838 			goto out;
839 
840 		r = EINVAL;
841 		f = chn_findfeeder(c, FEEDER_RATE);
842 		DEB(printf("feedrate = %p\n", f));
843 		if (f == NULL)
844 			goto out;
845 
846 		x = (c->direction == PCMDIR_REC)? b : bs;
847 		r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x));
848 		DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r));
849 		if (r)
850 			goto out;
851 
852 		x = (c->direction == PCMDIR_REC)? bs : b;
853 		r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x));
854 		DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r));
855 out:
856 		DEB(printf("setspeed done, r = %d\n", r));
857 		return r;
858 	} else
859 		return EINVAL;
860 }
861 
862 int
863 chn_setspeed(struct pcm_channel *c, int speed)
864 {
865 	int r, oldspeed = c->speed;
866 
867 	r = chn_tryspeed(c, speed);
868 	if (r) {
869 		DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
870 		r = chn_tryspeed(c, oldspeed);
871 	}
872 	return r;
873 }
874 
875 static int
876 chn_tryformat(struct pcm_channel *c, u_int32_t fmt)
877 {
878 	struct snd_dbuf *b = c->bufhard;
879 	struct snd_dbuf *bs = c->bufsoft;
880 	int r;
881 
882 	CHN_LOCKASSERT(c);
883 	if (CANCHANGE(c)) {
884 		DEB(printf("want format %d\n", fmt));
885 		c->format = fmt;
886 		r = chn_buildfeeder(c);
887 		if (r == 0) {
888 			sndbuf_setfmt(bs, c->format);
889 			chn_resetbuf(c);
890 			r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b));
891 			if (r == 0)
892 				r = chn_tryspeed(c, c->speed);
893 		}
894 		return r;
895 	} else
896 		return EINVAL;
897 }
898 
899 int
900 chn_setformat(struct pcm_channel *c, u_int32_t fmt)
901 {
902 	u_int32_t oldfmt = c->format;
903 	int r;
904 
905 	r = chn_tryformat(c, fmt);
906 	if (r) {
907 		DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
908 		chn_tryformat(c, oldfmt);
909 	}
910 	return r;
911 }
912 
913 int
914 chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
915 {
916 	struct snd_dbuf *b = c->bufhard;
917 	struct snd_dbuf *bs = c->bufsoft;
918 	int bufsz, irqhz, tmp, ret;
919 
920 	CHN_LOCKASSERT(c);
921 	if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED))
922 		return EINVAL;
923 
924 	ret = 0;
925 	DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz));
926 	if (blksz == 0 || blksz == -1) {
927 		if (blksz == -1)
928 			c->flags &= ~CHN_F_HAS_SIZE;
929 		if (!(c->flags & CHN_F_HAS_SIZE)) {
930 			blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / chn_targetirqrate;
931 	      		tmp = 32;
932 			while (tmp <= blksz)
933 				tmp <<= 1;
934 			tmp >>= 1;
935 			blksz = tmp;
936 			blkcnt = CHN_2NDBUFMAXSIZE / blksz;
937 
938 			RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
939 			RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz);
940 			DEB(printf("%s: defaulting to (%d, %d)\n", __func__, blkcnt, blksz));
941 		} else {
942 			blkcnt = sndbuf_getblkcnt(bs);
943 			blksz = sndbuf_getblksz(bs);
944 			DEB(printf("%s: updating (%d, %d)\n", __func__, blkcnt, blksz));
945 		}
946 	} else {
947 		ret = EINVAL;
948 		if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
949 			goto out;
950 		ret = 0;
951 		c->flags |= CHN_F_HAS_SIZE;
952 	}
953 
954 	bufsz = blkcnt * blksz;
955 
956 	ret = ENOMEM;
957 	if (sndbuf_remalloc(bs, blkcnt, blksz))
958 		goto out;
959 	ret = 0;
960 
961 	/* adjust for different hw format/speed */
962 	irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / sndbuf_getblksz(bs);
963 	DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __func__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz));
964 	RANGE(irqhz, 16, 512);
965 
966 	sndbuf_setblksz(b, (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz);
967 
968 	/* round down to 2^x */
969 	blksz = 32;
970 	while (blksz <= sndbuf_getblksz(b))
971 		blksz <<= 1;
972 	blksz >>= 1;
973 
974 	/* round down to fit hw buffer size */
975 	RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2);
976 	DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __func__, blksz, sndbuf_getmaxsize(b)));
977 
978 	sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
979 
980 	irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b);
981 	DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz));
982 
983 	chn_resetbuf(c);
984 out:
985 	return ret;
986 }
987 
988 int
989 chn_trigger(struct pcm_channel *c, int go)
990 {
991     	struct snd_dbuf *b = c->bufhard;
992 	int ret;
993 
994 	CHN_LOCKASSERT(c);
995 	if (ISA_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
996 		sndbuf_isadmabounce(b);
997 	ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
998 
999 	return ret;
1000 }
1001 
1002 int
1003 chn_getptr(struct pcm_channel *c)
1004 {
1005 	int hwptr;
1006 	int a = (1 << c->align) - 1;
1007 
1008 	CHN_LOCKASSERT(c);
1009 	hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
1010 	/* don't allow unaligned values in the hwa ptr */
1011 #if 1
1012 	hwptr &= ~a ; /* Apply channel align mask */
1013 #endif
1014 	hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
1015 	return hwptr;
1016 }
1017 
1018 struct pcmchan_caps *
1019 chn_getcaps(struct pcm_channel *c)
1020 {
1021 	CHN_LOCKASSERT(c);
1022 	return CHANNEL_GETCAPS(c->methods, c->devinfo);
1023 }
1024 
1025 u_int32_t
1026 chn_getformats(struct pcm_channel *c)
1027 {
1028 	u_int32_t *fmtlist, fmts;
1029 	int i;
1030 
1031 	fmtlist = chn_getcaps(c)->fmtlist;
1032 	fmts = 0;
1033 	for (i = 0; fmtlist[i]; i++)
1034 		fmts |= fmtlist[i];
1035 
1036 	/* report software-supported formats */
1037 	if (report_soft_formats)
1038 		fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U16_LE|AFMT_U16_BE|
1039 		    AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8;
1040 
1041 	return fmts;
1042 }
1043 
1044 static int
1045 chn_buildfeeder(struct pcm_channel *c)
1046 {
1047 	struct feeder_class *fc;
1048 	struct pcm_feederdesc desc;
1049 	u_int32_t tmp[2], type, flags, hwfmt;
1050 	int err;
1051 
1052 	CHN_LOCKASSERT(c);
1053 	while (chn_removefeeder(c) == 0);
1054 	KASSERT((c->feeder == NULL), ("feeder chain not empty"));
1055 
1056 	c->align = sndbuf_getalign(c->bufsoft);
1057 
1058 	if (SLIST_EMPTY(&c->children)) {
1059 		fc = feeder_getclass(NULL);
1060 		KASSERT(fc != NULL, ("can't find root feeder"));
1061 
1062 		err = chn_addfeeder(c, fc, NULL);
1063 		if (err) {
1064 			DEB(printf("can't add root feeder, err %d\n", err));
1065 
1066 			return err;
1067 		}
1068 		c->feeder->desc->out = c->format;
1069 	} else {
1070 		desc.type = FEEDER_MIXER;
1071 		desc.in = 0;
1072 		desc.out = c->format;
1073 		desc.flags = 0;
1074 		fc = feeder_getclass(&desc);
1075 		if (fc == NULL) {
1076 			DEB(printf("can't find vchan feeder\n"));
1077 
1078 			return EOPNOTSUPP;
1079 		}
1080 
1081 		err = chn_addfeeder(c, fc, &desc);
1082 		if (err) {
1083 			DEB(printf("can't add vchan feeder, err %d\n", err));
1084 
1085 			return err;
1086 		}
1087 	}
1088 	flags = c->feederflags;
1089 
1090 	DEB(printf("not mapped, feederflags %x\n", flags));
1091 
1092 	for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) {
1093 		if (flags & (1 << type)) {
1094 			desc.type = type;
1095 			desc.in = 0;
1096 			desc.out = 0;
1097 			desc.flags = 0;
1098 			DEB(printf("find feeder type %d, ", type));
1099 			fc = feeder_getclass(&desc);
1100 			DEB(printf("got %p\n", fc));
1101 			if (fc == NULL) {
1102 				DEB(printf("can't find required feeder type %d\n", type));
1103 
1104 				return EOPNOTSUPP;
1105 			}
1106 
1107 			if (c->feeder->desc->out != fc->desc->in) {
1108  				DEB(printf("build fmtchain from %x to %x: ", c->feeder->desc->out, fc->desc->in));
1109 				tmp[0] = fc->desc->in;
1110 				tmp[1] = 0;
1111 				if (chn_fmtchain(c, tmp) == 0) {
1112 					DEB(printf("failed\n"));
1113 
1114 					return ENODEV;
1115 				}
1116  				DEB(printf("ok\n"));
1117 			}
1118 
1119 			err = chn_addfeeder(c, fc, fc->desc);
1120 			if (err) {
1121 				DEB(printf("can't add feeder %p, output %x, err %d\n", fc, fc->desc->out, err));
1122 
1123 				return err;
1124 			}
1125 			DEB(printf("added feeder %p, output %x\n", fc, c->feeder->desc->out));
1126 		}
1127 	}
1128 
1129 	if (fmtvalid(c->feeder->desc->out, chn_getcaps(c)->fmtlist)) {
1130 		hwfmt = c->feeder->desc->out;
1131 	} else {
1132 		if (c->direction == PCMDIR_REC) {
1133 			tmp[0] = c->format;
1134 			tmp[1] = NULL;
1135 			hwfmt = chn_fmtchain(c, tmp);
1136 		} else {
1137 			hwfmt = chn_fmtchain(c, chn_getcaps(c)->fmtlist);
1138 		}
1139 	}
1140 
1141 	if (hwfmt == 0)
1142 		return ENODEV;
1143 
1144 	sndbuf_setfmt(c->bufhard, hwfmt);
1145 
1146 	return 0;
1147 }
1148 
1149 int
1150 chn_notify(struct pcm_channel *c, u_int32_t flags)
1151 {
1152 	struct pcmchan_children *pce;
1153 	struct pcm_channel *child;
1154 	int run;
1155 
1156 	if (SLIST_EMPTY(&c->children))
1157 		return ENODEV;
1158 
1159 	run = (c->flags & CHN_F_TRIGGERED)? 1 : 0;
1160 	/*
1161 	 * if the hwchan is running, we can't change its rate, format or
1162 	 * blocksize
1163 	 */
1164 	if (run)
1165 		flags &= CHN_N_VOLUME | CHN_N_TRIGGER;
1166 
1167 	if (flags & CHN_N_RATE) {
1168 		/*
1169 		 * we could do something here, like scan children and decide on
1170 		 * the most appropriate rate to mix at, but we don't for now
1171 		 */
1172 	}
1173 	if (flags & CHN_N_FORMAT) {
1174 		/*
1175 		 * we could do something here, like scan children and decide on
1176 		 * the most appropriate mixer feeder to use, but we don't for now
1177 		 */
1178 	}
1179 	if (flags & CHN_N_VOLUME) {
1180 		/*
1181 		 * we could do something here but we don't for now
1182 		 */
1183 	}
1184 	if (flags & CHN_N_BLOCKSIZE) {
1185 		int blksz;
1186 		/*
1187 		 * scan the children, find the lowest blocksize and use that
1188 		 * for the hard blocksize
1189 		 */
1190 		blksz = sndbuf_getmaxsize(c->bufhard) / 2;
1191 		SLIST_FOREACH(pce, &c->children, link) {
1192 			child = pce->channel;
1193 			if (sndbuf_getblksz(child->bufhard) < blksz)
1194 				blksz = sndbuf_getblksz(child->bufhard);
1195 		}
1196 		chn_setblocksize(c, 2, blksz);
1197 	}
1198 	if (flags & CHN_N_TRIGGER) {
1199 		int nrun;
1200 		/*
1201 		 * scan the children, and figure out if any are running
1202 		 * if so, we need to be running, otherwise we need to be stopped
1203 		 * if we aren't in our target sstate, move to it
1204 		 */
1205 		nrun = 0;
1206 		SLIST_FOREACH(pce, &c->children, link) {
1207 			child = pce->channel;
1208 			if (child->flags & CHN_F_TRIGGERED)
1209 				nrun = 1;
1210 		}
1211 		if (nrun && !run)
1212 			chn_start(c, 1);
1213 		if (!nrun && run)
1214 			chn_abort(c);
1215 	}
1216 	return 0;
1217 }
1218