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