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