xref: /freebsd/sys/dev/sound/pcm/channel.c (revision 262e143bd46171a6415a5b28af260a5efa2a3db8)
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 "opt_isa.h"
29 
30 #include <dev/sound/pcm/sound.h>
31 
32 #include "feeder_if.h"
33 
34 SND_DECLARE_FILE("$FreeBSD$");
35 
36 #define MIN_CHUNK_SIZE 		256	/* for uiomove etc. */
37 #define	DMA_ALIGN_THRESHOLD	4
38 #define	DMA_ALIGN_MASK		(~(DMA_ALIGN_THRESHOLD - 1))
39 
40 #define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED))
41 
42 /*
43 #define DEB(x) x
44 */
45 
46 static int chn_targetirqrate = 32;
47 TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate);
48 
49 static int
50 sysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS)
51 {
52 	int err, val;
53 
54 	val = chn_targetirqrate;
55 	err = sysctl_handle_int(oidp, &val, sizeof(val), req);
56 	if (val < 16 || val > 512)
57 		err = EINVAL;
58 	else
59 		chn_targetirqrate = val;
60 
61 	return err;
62 }
63 SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW,
64 	0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "");
65 static int report_soft_formats = 1;
66 SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
67 	&report_soft_formats, 1, "report software-emulated formats");
68 
69 static int chn_buildfeeder(struct pcm_channel *c);
70 
71 static void
72 chn_lockinit(struct pcm_channel *c, int dir)
73 {
74 	switch(dir) {
75 	case PCMDIR_PLAY:
76 		c->lock = snd_mtxcreate(c->name, "pcm play channel");
77 		break;
78 	case PCMDIR_REC:
79 		c->lock = snd_mtxcreate(c->name, "pcm record channel");
80 		break;
81 	case PCMDIR_VIRTUAL:
82 		c->lock = snd_mtxcreate(c->name, "pcm virtual play channel");
83 		break;
84 	case 0:
85 		c->lock = snd_mtxcreate(c->name, "pcm fake channel");
86 		break;
87 	}
88 }
89 
90 static void
91 chn_lockdestroy(struct pcm_channel *c)
92 {
93 	snd_mtxfree(c->lock);
94 }
95 
96 static int
97 chn_polltrigger(struct pcm_channel *c)
98 {
99 	struct snd_dbuf *bs = c->bufsoft;
100 	unsigned amt, lim;
101 
102 	CHN_LOCKASSERT(c);
103 	if (c->flags & CHN_F_MAPPED) {
104 		if (sndbuf_getprevblocks(bs) == 0)
105 			return 1;
106 		else
107 			return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0;
108 	} else {
109 		amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
110 #if 0
111 		lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
112 #endif
113 		lim = 1;
114 		return (amt >= lim)? 1 : 0;
115 	}
116 	return 0;
117 }
118 
119 static int
120 chn_pollreset(struct pcm_channel *c)
121 {
122 	struct snd_dbuf *bs = c->bufsoft;
123 
124 	CHN_LOCKASSERT(c);
125 	sndbuf_updateprevtotal(bs);
126 	return 1;
127 }
128 
129 static void
130 chn_wakeup(struct pcm_channel *c)
131 {
132 	struct snd_dbuf *bs = c->bufsoft;
133 	struct pcmchan_children *pce;
134 
135 	CHN_LOCKASSERT(c);
136 	if (SLIST_EMPTY(&c->children)) {
137 		if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))
138 			selwakeuppri(sndbuf_getsel(bs), PRIBIO);
139 	} else {
140 		SLIST_FOREACH(pce, &c->children, link) {
141 			CHN_LOCK(pce->channel);
142 			chn_wakeup(pce->channel);
143 			CHN_UNLOCK(pce->channel);
144 		}
145 	}
146 
147 	wakeup(bs);
148 }
149 
150 static int
151 chn_sleep(struct pcm_channel *c, char *str, int timeout)
152 {
153     	struct snd_dbuf *bs = c->bufsoft;
154 	int ret;
155 
156 	CHN_LOCKASSERT(c);
157 #ifdef USING_MUTEX
158 	ret = msleep(bs, c->lock, PRIBIO | PCATCH, str, timeout);
159 #else
160 	ret = tsleep(bs, PRIBIO | PCATCH, str, timeout);
161 #endif
162 
163 	return ret;
164 }
165 
166 /*
167  * chn_dmaupdate() tracks the status of a dma transfer,
168  * updating pointers.
169  */
170 
171 static unsigned int
172 chn_dmaupdate(struct pcm_channel *c)
173 {
174 	struct snd_dbuf *b = c->bufhard;
175 	unsigned int delta, old, hwptr, amt;
176 
177 	KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0"));
178 	CHN_LOCKASSERT(c);
179 
180 	old = sndbuf_gethwptr(b);
181 	hwptr = chn_getptr(c);
182 	delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b);
183 	sndbuf_sethwptr(b, hwptr);
184 
185 	DEB(
186 	if (delta >= ((sndbuf_getsize(b) * 15) / 16)) {
187 		if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING)))
188 			device_printf(c->dev, "hwptr went backwards %d -> %d\n", old, hwptr);
189 	}
190 	);
191 
192 	if (c->direction == PCMDIR_PLAY) {
193 		amt = MIN(delta, sndbuf_getready(b));
194 		if (amt > 0)
195 			sndbuf_dispose(b, NULL, amt);
196 	} else {
197 		amt = MIN(delta, sndbuf_getfree(b));
198 		if (amt > 0)
199 		       sndbuf_acquire(b, NULL, amt);
200 	}
201 
202 	return delta;
203 }
204 
205 void
206 chn_wrupdate(struct pcm_channel *c)
207 {
208 	int ret;
209 
210 	CHN_LOCKASSERT(c);
211 	KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
212 
213 	if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || !(c->flags & CHN_F_TRIGGERED))
214 		return;
215 	chn_dmaupdate(c);
216 	ret = chn_wrfeed(c);
217 	/* tell the driver we've updated the primary buffer */
218 	chn_trigger(c, PCMTRIG_EMLDMAWR);
219 	DEB(if (ret)
220 		printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);)
221 
222 }
223 
224 int
225 chn_wrfeed(struct pcm_channel *c)
226 {
227     	struct snd_dbuf *b = c->bufhard;
228     	struct snd_dbuf *bs = c->bufsoft;
229 	unsigned int ret, amt;
230 
231 	CHN_LOCKASSERT(c);
232 #if 0
233     	DEB(
234 	if (c->flags & CHN_F_CLOSING) {
235 		sndbuf_dump(b, "b", 0x02);
236 		sndbuf_dump(bs, "bs", 0x02);
237 	})
238 #endif
239 
240 	if (c->flags & CHN_F_MAPPED)
241 		sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
242 
243 	amt = sndbuf_getfree(b);
244 	KASSERT(amt <= sndbuf_getsize(bs),
245 	    ("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name,
246 	   amt, sndbuf_getsize(bs), c->flags));
247 
248 	if (SLIST_EMPTY(&c->children)) {
249 		/*
250 		 * Hardware channel
251 		 */
252 		if (sndbuf_getready(bs) < amt)
253 			c->xruns++;
254 		ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
255 	} else {
256 		/*
257 		 * vchan
258 		 */
259 		if (amt > 0) {
260 			ret = sndbuf_feed(bs, b, c, c->feeder, amt);
261 			/*
262 			 * Possible vchan xruns. There should be no empty space
263 			 * left in buffer.
264 			 */
265 			if (sndbuf_getfree(b) > 0)
266 				c->xruns++;
267 		} else
268 			ret = ENOSPC;
269 	}
270 
271 	if (ret == 0 && sndbuf_getfree(b) < amt)
272 		chn_wakeup(c);
273 
274 	return ret;
275 }
276 
277 static void
278 chn_wrintr(struct pcm_channel *c)
279 {
280 	int ret;
281 
282 	CHN_LOCKASSERT(c);
283 	/* update pointers in primary buffer */
284 	chn_dmaupdate(c);
285 	/* ...and feed from secondary to primary */
286 	ret = chn_wrfeed(c);
287 	/* tell the driver we've updated the primary buffer */
288 	chn_trigger(c, PCMTRIG_EMLDMAWR);
289 	DEB(if (ret)
290 		printf("chn_wrintr: chn_wrfeed returned %d\n", ret);)
291 }
292 
293 /*
294  * user write routine - uiomove data into secondary buffer, trigger if necessary
295  * if blocking, sleep, rinse and repeat.
296  *
297  * called externally, so must handle locking
298  */
299 
300 int
301 chn_write(struct pcm_channel *c, struct uio *buf)
302 {
303 	int ret, timeout, newsize, count, sz;
304 	struct snd_dbuf *bs = c->bufsoft;
305 	void *off;
306 	int t, x,togo,p;
307 
308 	CHN_LOCKASSERT(c);
309 	/*
310 	 * XXX Certain applications attempt to write larger size
311 	 * of pcm data than c->blocksize2nd without blocking,
312 	 * resulting partial write. Expand the block size so that
313 	 * the write operation avoids blocking.
314 	 */
315 	if ((c->flags & CHN_F_NBIO) && buf->uio_resid > sndbuf_getblksz(bs)) {
316 		DEB(device_printf(c->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n",
317 			buf->uio_resid, sndbuf_getblksz(bs)));
318 		newsize = 16;
319 		while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2))
320 			newsize <<= 1;
321 		chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize);
322 		DEB(device_printf(c->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs)));
323 	}
324 
325 	ret = 0;
326 	count = hz;
327 	while (!ret && (buf->uio_resid > 0) && (count > 0)) {
328 		sz = sndbuf_getfree(bs);
329 		if (sz == 0) {
330 			if (c->flags & CHN_F_NBIO)
331 				ret = EWOULDBLOCK;
332 			else {
333 				timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
334 				if (timeout < 1)
335 					timeout = 1;
336 				timeout = 1;
337 	   			ret = chn_sleep(c, "pcmwr", timeout);
338 				if (ret == EWOULDBLOCK) {
339 					count -= timeout;
340 					ret = 0;
341 				} else if (ret == 0)
342 					count = hz;
343 			}
344 		} else {
345 			sz = MIN(sz, buf->uio_resid);
346 			KASSERT(sz > 0, ("confusion in chn_write"));
347 			/* printf("sz: %d\n", sz); */
348 
349 			/*
350 			 * The following assumes that the free space in
351 			 * the buffer can never be less around the
352 			 * unlock-uiomove-lock sequence.
353 			 */
354 			togo = sz;
355 			while (ret == 0 && togo> 0) {
356 				p = sndbuf_getfreeptr(bs);
357 				t = MIN(togo, sndbuf_getsize(bs) - p);
358 				off = sndbuf_getbufofs(bs, p);
359 				CHN_UNLOCK(c);
360 				ret = uiomove(off, t, buf);
361 				CHN_LOCK(c);
362 				togo -= t;
363 				x = sndbuf_acquire(bs, NULL, t);
364 			}
365 			ret = 0;
366 			if (ret == 0 && !(c->flags & CHN_F_TRIGGERED))
367 				chn_start(c, 0);
368 		}
369 	}
370 	/* printf("ret: %d left: %d\n", ret, buf->uio_resid); */
371 
372 	if (count <= 0) {
373 		c->flags |= CHN_F_DEAD;
374 		printf("%s: play interrupt timeout, channel dead\n", c->name);
375 	}
376 
377 	return ret;
378 }
379 
380 static int
381 chn_rddump(struct pcm_channel *c, unsigned int cnt)
382 {
383     	struct snd_dbuf *b = c->bufhard;
384 
385 	CHN_LOCKASSERT(c);
386 #if 0
387 	static uint32_t kk = 0;
388 	printf("%u: dumping %d bytes\n", ++kk, cnt);
389 #endif
390 	c->xruns++;
391 	sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt);
392 	return sndbuf_dispose(b, NULL, cnt);
393 }
394 
395 /*
396  * Feed new data from the read buffer. Can be called in the bottom half.
397  */
398 int
399 chn_rdfeed(struct pcm_channel *c)
400 {
401     	struct snd_dbuf *b = c->bufhard;
402     	struct snd_dbuf *bs = c->bufsoft;
403 	unsigned int ret, amt;
404 
405 	CHN_LOCKASSERT(c);
406     	DEB(
407 	if (c->flags & CHN_F_CLOSING) {
408 		sndbuf_dump(b, "b", 0x02);
409 		sndbuf_dump(bs, "bs", 0x02);
410 	})
411 
412 #if 0
413 	amt = sndbuf_getready(b);
414 	if (sndbuf_getfree(bs) < amt) {
415 		c->xruns++;
416 		amt = sndbuf_getfree(bs);
417 	}
418 #endif
419 	amt = sndbuf_getfree(bs);
420 	if (amt < sndbuf_getready(b))
421 		c->xruns++;
422 	ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0;
423 
424 	amt = sndbuf_getready(b);
425 	if (amt > 0)
426 		chn_rddump(c, amt);
427 
428 	chn_wakeup(c);
429 
430 	return ret;
431 }
432 
433 void
434 chn_rdupdate(struct pcm_channel *c)
435 {
436 	int ret;
437 
438 	CHN_LOCKASSERT(c);
439 	KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel"));
440 
441 	if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED))
442 		return;
443 	chn_trigger(c, PCMTRIG_EMLDMARD);
444 	chn_dmaupdate(c);
445 	ret = chn_rdfeed(c);
446 	if (ret)
447 		printf("chn_rdfeed: %d\n", ret);
448 
449 }
450 
451 /* read interrupt routine. Must be called with interrupts blocked. */
452 static void
453 chn_rdintr(struct pcm_channel *c)
454 {
455 	int ret;
456 
457 	CHN_LOCKASSERT(c);
458 	/* tell the driver to update the primary buffer if non-dma */
459 	chn_trigger(c, PCMTRIG_EMLDMARD);
460 	/* update pointers in primary buffer */
461 	chn_dmaupdate(c);
462 	/* ...and feed from primary to secondary */
463 	ret = chn_rdfeed(c);
464 }
465 
466 /*
467  * user read routine - trigger if necessary, uiomove data from secondary buffer
468  * if blocking, sleep, rinse and repeat.
469  *
470  * called externally, so must handle locking
471  */
472 
473 int
474 chn_read(struct pcm_channel *c, struct uio *buf)
475 {
476 	int		ret, timeout, sz, count;
477 	struct snd_dbuf       *bs = c->bufsoft;
478 	void *off;
479 	int t, x,togo,p;
480 
481 	CHN_LOCKASSERT(c);
482 	if (!(c->flags & CHN_F_TRIGGERED))
483 		chn_start(c, 0);
484 
485 	ret = 0;
486 	count = hz;
487 	while (!ret && (buf->uio_resid > 0) && (count > 0)) {
488 		sz = MIN(buf->uio_resid, sndbuf_getready(bs));
489 
490 		if (sz > 0) {
491 			/*
492 			 * The following assumes that the free space in
493 			 * the buffer can never be less around the
494 			 * unlock-uiomove-lock sequence.
495 			 */
496 			togo = sz;
497 			while (ret == 0 && togo> 0) {
498 				p = sndbuf_getreadyptr(bs);
499 				t = MIN(togo, sndbuf_getsize(bs) - p);
500 				off = sndbuf_getbufofs(bs, p);
501 				CHN_UNLOCK(c);
502 				ret = uiomove(off, t, buf);
503 				CHN_LOCK(c);
504 				togo -= t;
505 				x = sndbuf_dispose(bs, NULL, t);
506 			}
507 			ret = 0;
508 		} else {
509 			if (c->flags & CHN_F_NBIO) {
510 				ret = EWOULDBLOCK;
511 			} else {
512 				timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
513 				if (timeout < 1)
514 					timeout = 1;
515 	   			ret = chn_sleep(c, "pcmrd", timeout);
516 				if (ret == EWOULDBLOCK) {
517 					count -= timeout;
518 					ret = 0;
519 				} else {
520 					count = hz;
521 				}
522 
523 			}
524 		}
525 	}
526 
527 	if (count <= 0) {
528 		c->flags |= CHN_F_DEAD;
529 		printf("%s: record interrupt timeout, channel dead\n", c->name);
530 	}
531 
532 	return ret;
533 }
534 
535 void
536 chn_intr(struct pcm_channel *c)
537 {
538 	CHN_LOCK(c);
539 	c->interrupts++;
540 	if (c->direction == PCMDIR_PLAY)
541 		chn_wrintr(c);
542 	else
543 		chn_rdintr(c);
544 	CHN_UNLOCK(c);
545 }
546 
547 u_int32_t
548 chn_start(struct pcm_channel *c, int force)
549 {
550 	u_int32_t i, j;
551 	struct snd_dbuf *b = c->bufhard;
552 	struct snd_dbuf *bs = c->bufsoft;
553 
554 	CHN_LOCKASSERT(c);
555 	/* if we're running, or if we're prevented from triggering, bail */
556 	if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force))
557 		return EINVAL;
558 
559 	i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs);
560 	j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b);
561 	if (force || (i >= j)) {
562 		c->flags |= CHN_F_TRIGGERED;
563 		/*
564 		 * if we're starting because a vchan started, don't feed any data
565 		 * or it becomes impossible to start vchans synchronised with the
566 		 * first one.  the hardbuf should be empty so we top it up with
567 		 * silence to give it something to chew.  the real data will be
568 		 * fed at the first irq.
569 		 */
570 		if (c->direction == PCMDIR_PLAY) {
571 			/*
572 			 * Reduce pops during playback startup.
573 			 */
574 			sndbuf_fillsilence(b);
575 			if (SLIST_EMPTY(&c->children))
576 				chn_wrfeed(c);
577 		}
578 		sndbuf_setrun(b, 1);
579 		c->xruns = 0;
580 	    	chn_trigger(c, PCMTRIG_START);
581 		return 0;
582 	}
583 
584 	return 0;
585 }
586 
587 void
588 chn_resetbuf(struct pcm_channel *c)
589 {
590 	struct snd_dbuf *b = c->bufhard;
591 	struct snd_dbuf *bs = c->bufsoft;
592 
593 	c->blocks = 0;
594 	sndbuf_reset(b);
595 	sndbuf_reset(bs);
596 }
597 
598 /*
599  * chn_sync waits until the space in the given channel goes above
600  * a threshold. The threshold is checked against fl or rl respectively.
601  * Assume that the condition can become true, do not check here...
602  */
603 int
604 chn_sync(struct pcm_channel *c, int threshold)
605 {
606     	u_long rdy;
607     	int ret;
608     	struct snd_dbuf *bs = c->bufsoft;
609 
610 	CHN_LOCKASSERT(c);
611 
612 	/* if we haven't yet started and nothing is buffered, else start*/
613 	if (!(c->flags & CHN_F_TRIGGERED)) {
614 		if (sndbuf_getready(bs) > 0) {
615 			ret = chn_start(c, 1);
616 			if (ret)
617 				return ret;
618 		} else {
619 			return 0;
620 		}
621 	}
622 
623 	for (;;) {
624 		rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
625 		if (rdy <= threshold) {
626 	    		ret = chn_sleep(c, "pcmsyn", 1);
627 	    		if (ret == ERESTART || ret == EINTR) {
628 				DEB(printf("chn_sync: tsleep returns %d\n", ret));
629 				return -1;
630 	    		}
631 		} else
632 			break;
633     	}
634     	return 0;
635 }
636 
637 /* called externally, handle locking */
638 int
639 chn_poll(struct pcm_channel *c, int ev, struct thread *td)
640 {
641 	struct snd_dbuf *bs = c->bufsoft;
642 	int ret;
643 
644 	CHN_LOCKASSERT(c);
645     	if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED))
646 		chn_start(c, 1);
647 	ret = 0;
648 	if (chn_polltrigger(c) && chn_pollreset(c))
649 		ret = ev;
650 	else
651 		selrecord(td, sndbuf_getsel(bs));
652 	return ret;
653 }
654 
655 /*
656  * chn_abort terminates a running dma transfer.  it may sleep up to 200ms.
657  * it returns the number of bytes that have not been transferred.
658  *
659  * called from: dsp_close, dsp_ioctl, with channel locked
660  */
661 int
662 chn_abort(struct pcm_channel *c)
663 {
664     	int missing = 0;
665     	struct snd_dbuf *b = c->bufhard;
666     	struct snd_dbuf *bs = c->bufsoft;
667 
668 	CHN_LOCKASSERT(c);
669 	if (!(c->flags & CHN_F_TRIGGERED))
670 		return 0;
671 	c->flags |= CHN_F_ABORTING;
672 
673 	c->flags &= ~CHN_F_TRIGGERED;
674 	/* kill the channel */
675 	chn_trigger(c, PCMTRIG_ABORT);
676 	sndbuf_setrun(b, 0);
677 	if (!(c->flags & CHN_F_VIRTUAL))
678 		chn_dmaupdate(c);
679     	missing = sndbuf_getready(bs) + sndbuf_getready(b);
680 
681 	c->flags &= ~CHN_F_ABORTING;
682 	return missing;
683 }
684 
685 /*
686  * this routine tries to flush the dma transfer. It is called
687  * on a close of a playback channel.
688  * first, if there is data in the buffer, but the dma has not yet
689  * begun, we need to start it.
690  * next, we wait for the play buffer to drain
691  * finally, we stop the dma.
692  *
693  * called from: dsp_close, not valid for record channels.
694  */
695 
696 int
697 chn_flush(struct pcm_channel *c)
698 {
699     	int ret, count, resid, resid_p;
700     	struct snd_dbuf *b = c->bufhard;
701     	struct snd_dbuf *bs = c->bufsoft;
702 
703 	CHN_LOCKASSERT(c);
704 	KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel"));
705     	DEB(printf("chn_flush: c->flags 0x%08x\n", c->flags));
706 
707 	/* if we haven't yet started and nothing is buffered, else start*/
708 	if (!(c->flags & CHN_F_TRIGGERED)) {
709 		if (sndbuf_getready(bs) > 0) {
710 			ret = chn_start(c, 1);
711 			if (ret)
712 				return ret;
713 		} else {
714 			return 0;
715 		}
716 	}
717 
718 	c->flags |= CHN_F_CLOSING;
719 	resid = sndbuf_getready(bs) + sndbuf_getready(b);
720 	resid_p = resid;
721 	count = 10;
722 	ret = 0;
723 	while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) {
724 		/* still pending output data. */
725 		ret = chn_sleep(c, "pcmflu", hz / 10);
726 		if (ret == EWOULDBLOCK)
727 			ret = 0;
728 		if (ret == 0) {
729 			resid = sndbuf_getready(bs) + sndbuf_getready(b);
730 			if (resid == resid_p)
731 				count--;
732 			if (resid > resid_p)
733 				DEB(printf("chn_flush: buffer length increasind %d -> %d\n", resid_p, resid));
734 			resid_p = resid;
735 		}
736    	}
737 	if (count == 0)
738 		DEB(printf("chn_flush: timeout, hw %d, sw %d\n",
739 			sndbuf_getready(b), sndbuf_getready(bs)));
740 
741 	c->flags &= ~CHN_F_TRIGGERED;
742 	/* kill the channel */
743 	chn_trigger(c, PCMTRIG_ABORT);
744 	sndbuf_setrun(b, 0);
745 
746     	c->flags &= ~CHN_F_CLOSING;
747     	return 0;
748 }
749 
750 int
751 fmtvalid(u_int32_t fmt, u_int32_t *fmtlist)
752 {
753 	int i;
754 
755 	for (i = 0; fmtlist[i]; i++)
756 		if (fmt == fmtlist[i])
757 			return 1;
758 	return 0;
759 }
760 
761 int
762 chn_reset(struct pcm_channel *c, u_int32_t fmt)
763 {
764 	int hwspd, r;
765 
766 	CHN_LOCKASSERT(c);
767 	c->flags &= CHN_F_RESET;
768 	c->interrupts = 0;
769 	c->xruns = 0;
770 
771 	r = CHANNEL_RESET(c->methods, c->devinfo);
772 	if (fmt != 0) {
773 #if 0
774 		hwspd = DSP_DEFAULT_SPEED;
775 		/* only do this on a record channel until feederbuilder works */
776 		if (c->direction == PCMDIR_REC)
777 			RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
778 		c->speed = hwspd;
779 #endif
780 		hwspd = chn_getcaps(c)->minspeed;
781 		c->speed = hwspd;
782 
783 		if (r == 0)
784 			r = chn_setformat(c, fmt);
785 		if (r == 0)
786 			r = chn_setspeed(c, hwspd);
787 #if 0
788 		if (r == 0)
789 			r = chn_setvolume(c, 100, 100);
790 #endif
791 	}
792 	if (r == 0)
793 		r = chn_setblocksize(c, 0, 0);
794 	if (r == 0) {
795 		chn_resetbuf(c);
796 		r = CHANNEL_RESETDONE(c->methods, c->devinfo);
797 	}
798 	return r;
799 }
800 
801 int
802 chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
803 {
804 	struct feeder_class *fc;
805 	struct snd_dbuf *b, *bs;
806 	int ret;
807 
808 	chn_lockinit(c, dir);
809 
810 	b = NULL;
811 	bs = NULL;
812 	c->devinfo = NULL;
813 	c->feeder = NULL;
814 
815 	ret = ENOMEM;
816 	b = sndbuf_create(c->dev, c->name, "primary", c);
817 	if (b == NULL)
818 		goto out;
819 	bs = sndbuf_create(c->dev, c->name, "secondary", c);
820 	if (bs == NULL)
821 		goto out;
822 
823 	CHN_LOCK(c);
824 
825 	ret = EINVAL;
826 	fc = feeder_getclass(NULL);
827 	if (fc == NULL)
828 		goto out;
829 	if (chn_addfeeder(c, fc, NULL))
830 		goto out;
831 
832 	/*
833 	 * XXX - sndbuf_setup() & sndbuf_resize() expect to be called
834 	 *	 with the channel unlocked because they are also called
835 	 *	 from driver methods that don't know about locking
836 	 */
837 	CHN_UNLOCK(c);
838 	sndbuf_setup(bs, NULL, 0);
839 	CHN_LOCK(c);
840 	c->bufhard = b;
841 	c->bufsoft = bs;
842 	c->flags = 0;
843 	c->feederflags = 0;
844 
845 	ret = ENODEV;
846 	CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */
847 	c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction);
848 	CHN_LOCK(c);
849 	if (c->devinfo == NULL)
850 		goto out;
851 
852 	ret = ENOMEM;
853 	if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0))
854 		goto out;
855 
856 	ret = chn_setdir(c, direction);
857 	if (ret)
858 		goto out;
859 
860 	ret = sndbuf_setfmt(b, AFMT_U8);
861 	if (ret)
862 		goto out;
863 
864 	ret = sndbuf_setfmt(bs, AFMT_U8);
865 	if (ret)
866 		goto out;
867 
868 
869 out:
870 	CHN_UNLOCK(c);
871 	if (ret) {
872 		if (c->devinfo) {
873 			if (CHANNEL_FREE(c->methods, c->devinfo))
874 				sndbuf_free(b);
875 		}
876 		if (bs)
877 			sndbuf_destroy(bs);
878 		if (b)
879 			sndbuf_destroy(b);
880 		c->flags |= CHN_F_DEAD;
881 		chn_lockdestroy(c);
882 
883 		return ret;
884 	}
885 
886 	return 0;
887 }
888 
889 int
890 chn_kill(struct pcm_channel *c)
891 {
892     	struct snd_dbuf *b = c->bufhard;
893     	struct snd_dbuf *bs = c->bufsoft;
894 
895 	if (c->flags & CHN_F_TRIGGERED)
896 		chn_trigger(c, PCMTRIG_ABORT);
897 	while (chn_removefeeder(c) == 0);
898 	if (CHANNEL_FREE(c->methods, c->devinfo))
899 		sndbuf_free(b);
900 	c->flags |= CHN_F_DEAD;
901 	sndbuf_destroy(bs);
902 	sndbuf_destroy(b);
903 	chn_lockdestroy(c);
904 	return 0;
905 }
906 
907 int
908 chn_setdir(struct pcm_channel *c, int dir)
909 {
910 #ifdef DEV_ISA
911     	struct snd_dbuf *b = c->bufhard;
912 #endif
913 	int r;
914 
915 	CHN_LOCKASSERT(c);
916 	c->direction = dir;
917 	r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction);
918 #ifdef DEV_ISA
919 	if (!r && SND_DMA(b))
920 		sndbuf_dmasetdir(b, c->direction);
921 #endif
922 	return r;
923 }
924 
925 int
926 chn_setvolume(struct pcm_channel *c, int left, int right)
927 {
928 	CHN_LOCKASSERT(c);
929 	/* should add a feeder for volume changing if channel returns -1 */
930 	if (left > 100)
931 		left = 100;
932 	if (left < 0)
933 		left = 0;
934 	if (right > 100)
935 		right = 100;
936 	if (right < 0)
937 		right = 0;
938 	c->volume = left | (right << 8);
939 	return 0;
940 }
941 
942 static int
943 chn_tryspeed(struct pcm_channel *c, int speed)
944 {
945 	struct pcm_feeder *f;
946     	struct snd_dbuf *b = c->bufhard;
947     	struct snd_dbuf *bs = c->bufsoft;
948     	struct snd_dbuf *x;
949 	int r, delta;
950 
951 	CHN_LOCKASSERT(c);
952 	DEB(printf("setspeed, channel %s\n", c->name));
953 	DEB(printf("want speed %d, ", speed));
954 	if (speed <= 0)
955 		return EINVAL;
956 	if (CANCHANGE(c)) {
957 		r = 0;
958 		c->speed = speed;
959 		sndbuf_setspd(bs, speed);
960 		RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
961 		DEB(printf("try speed %d, ", speed));
962 		sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed));
963 		DEB(printf("got speed %d\n", sndbuf_getspd(b)));
964 
965 		delta = sndbuf_getspd(b) - sndbuf_getspd(bs);
966 		if (delta < 0)
967 			delta = -delta;
968 
969 		c->feederflags &= ~(1 << FEEDER_RATE);
970 		/*
971 		 * Used to be 500. It was too big!
972 		 */
973 		if (delta > 25)
974 			c->feederflags |= 1 << FEEDER_RATE;
975 		else
976 			sndbuf_setspd(bs, sndbuf_getspd(b));
977 
978 		r = chn_buildfeeder(c);
979 		DEB(printf("r = %d\n", r));
980 		if (r)
981 			goto out;
982 
983 		r = chn_setblocksize(c, 0, 0);
984 		if (r)
985 			goto out;
986 
987 		if (!(c->feederflags & (1 << FEEDER_RATE)))
988 			goto out;
989 
990 		r = EINVAL;
991 		f = chn_findfeeder(c, FEEDER_RATE);
992 		DEB(printf("feedrate = %p\n", f));
993 		if (f == NULL)
994 			goto out;
995 
996 		x = (c->direction == PCMDIR_REC)? b : bs;
997 		r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x));
998 		DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r));
999 		if (r)
1000 			goto out;
1001 
1002 		x = (c->direction == PCMDIR_REC)? bs : b;
1003 		r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x));
1004 		DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r));
1005 out:
1006 		if (!r)
1007 			r = CHANNEL_SETFORMAT(c->methods, c->devinfo,
1008 							sndbuf_getfmt(b));
1009 		if (!r)
1010 			sndbuf_setfmt(bs, c->format);
1011 		DEB(printf("setspeed done, r = %d\n", r));
1012 		return r;
1013 	} else
1014 		return EINVAL;
1015 }
1016 
1017 int
1018 chn_setspeed(struct pcm_channel *c, int speed)
1019 {
1020 	int r, oldspeed = c->speed;
1021 
1022 	r = chn_tryspeed(c, speed);
1023 	if (r) {
1024 		DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
1025 		r = chn_tryspeed(c, oldspeed);
1026 	}
1027 	return r;
1028 }
1029 
1030 static int
1031 chn_tryformat(struct pcm_channel *c, u_int32_t fmt)
1032 {
1033 	struct snd_dbuf *b = c->bufhard;
1034 	struct snd_dbuf *bs = c->bufsoft;
1035 	int r;
1036 
1037 	CHN_LOCKASSERT(c);
1038 	if (CANCHANGE(c)) {
1039 		DEB(printf("want format %d\n", fmt));
1040 		c->format = fmt;
1041 		r = chn_buildfeeder(c);
1042 		if (r == 0) {
1043 			sndbuf_setfmt(bs, c->format);
1044 			chn_resetbuf(c);
1045 			r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b));
1046 			if (r == 0)
1047 				r = chn_tryspeed(c, c->speed);
1048 		}
1049 		return r;
1050 	} else
1051 		return EINVAL;
1052 }
1053 
1054 int
1055 chn_setformat(struct pcm_channel *c, u_int32_t fmt)
1056 {
1057 	u_int32_t oldfmt = c->format;
1058 	int r;
1059 
1060 	r = chn_tryformat(c, fmt);
1061 	if (r) {
1062 		DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
1063 		chn_tryformat(c, oldfmt);
1064 	}
1065 	return r;
1066 }
1067 
1068 /*
1069  * given a bufsz value, round it to a power of 2 in the min-max range
1070  * XXX only works if min and max are powers of 2
1071  */
1072 static int
1073 round_bufsz(int bufsz, int min, int max)
1074 {
1075 	int tmp = min * 2;
1076 
1077 	KASSERT((min & (min-1)) == 0, ("min %d must be power of 2\n", min));
1078 	KASSERT((max & (max-1)) == 0, ("max %d must be power of 2\n", max));
1079 	while (tmp <= bufsz)
1080 		tmp <<= 1;
1081 	tmp >>= 1;
1082 	if (tmp > max)
1083 		tmp = max;
1084 	return tmp;
1085 }
1086 
1087 /*
1088  * set the channel's blocksize both for soft and hard buffers.
1089  *
1090  * blksz should be a power of 2 between 2**4 and 2**16 -- it is useful
1091  * that it has the same value for both bufsoft and bufhard.
1092  * blksz == -1 computes values according to a target irq rate.
1093  * blksz == 0 reuses previous values if available, otherwise
1094  * behaves as for -1
1095  *
1096  * blkcnt is set by the user, between 2 and (2**17)/blksz for bufsoft,
1097  * but should be a power of 2 for bufhard to simplify life to low
1098  * level drivers.
1099  * Note, for the rec channel a large blkcnt is ok,
1100  * but for the play channel we want blksz as small as possible to keep
1101  * the delay small, because routines in the write path always try to
1102  * keep bufhard full.
1103  *
1104  * Unless we have good reason to, use the values suggested by the caller.
1105  */
1106 int
1107 chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
1108 {
1109 	struct snd_dbuf *b = c->bufhard;
1110 	struct snd_dbuf *bs = c->bufsoft;
1111 	int irqhz, ret, maxsz, maxsize, reqblksz;
1112 
1113 	CHN_LOCKASSERT(c);
1114 	if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) {
1115 		KASSERT(sndbuf_getsize(bs) ==  0 ||
1116 		    sndbuf_getsize(bs) >= sndbuf_getsize(b),
1117 		    ("%s(%s): bufsoft size %d < bufhard size %d", __func__,
1118 		    c->name, sndbuf_getsize(bs), sndbuf_getsize(b)));
1119 		return EINVAL;
1120 	}
1121 	c->flags |= CHN_F_SETBLOCKSIZE;
1122 
1123 	ret = 0;
1124 	DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz));
1125 	if (blksz == 0 || blksz == -1) { /* let the driver choose values */
1126 		if (blksz == -1)	/* delete previous values */
1127 			c->flags &= ~CHN_F_HAS_SIZE;
1128 		if (!(c->flags & CHN_F_HAS_SIZE)) { /* no previous value */
1129 			/*
1130 			 * compute a base blksz according to the target irq
1131 			 * rate, then round to a suitable power of 2
1132 			 * in the range 16.. 2^17/2.
1133 			 * Finally compute a suitable blkcnt.
1134 			 */
1135 			blksz = round_bufsz( (sndbuf_getbps(bs) *
1136 				sndbuf_getspd(bs)) / chn_targetirqrate,
1137 				16, CHN_2NDBUFMAXSIZE / 2);
1138 			blkcnt = CHN_2NDBUFMAXSIZE / blksz;
1139 		} else { /* use previously defined value */
1140 			blkcnt = sndbuf_getblkcnt(bs);
1141 			blksz = sndbuf_getblksz(bs);
1142 		}
1143 	} else {
1144 		/*
1145 		 * use supplied values if reasonable. Note that here we
1146 		 * might have blksz which is not a power of 2 if the
1147 		 * ioctl() to compute it allows such values.
1148 		 */
1149 		ret = EINVAL;
1150 		if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
1151 			goto out;
1152 		ret = 0;
1153 		c->flags |= CHN_F_HAS_SIZE;
1154 	}
1155 
1156 	reqblksz = blksz;
1157 	if (reqblksz < sndbuf_getbps(bs))
1158 		reqblksz = sndbuf_getbps(bs);
1159 	if (reqblksz % sndbuf_getbps(bs))
1160 		reqblksz -= reqblksz % sndbuf_getbps(bs);
1161 
1162 	/* adjust for different hw format/speed */
1163 	/*
1164 	 * Now compute the approx irq rate for the given (soft) blksz,
1165 	 * reduce to the acceptable range and compute a corresponding blksz
1166 	 * for the hard buffer. Then set the channel's blocksize and
1167 	 * corresponding hardbuf value. The number of blocks used should
1168 	 * be set by the device-specific routine. In fact, even the
1169 	 * call to sndbuf_setblksz() should not be here! XXX
1170 	 */
1171 
1172 	irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz;
1173 	RANGE(irqhz, 16, 512);
1174 
1175 	maxsz = sndbuf_getmaxsize(b);
1176 	if (maxsz == 0) /* virtual channels don't appear to allocate bufhard */
1177 		maxsz = CHN_2NDBUFMAXSIZE;
1178 	blksz = round_bufsz( (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz,
1179 			16, maxsz / 2);
1180 
1181 	/* Increase the size of bufsoft if before increasing bufhard. */
1182 	maxsize = sndbuf_getsize(b);
1183 	if (sndbuf_getsize(bs) > maxsize)
1184 		maxsize = sndbuf_getsize(bs);
1185 	if (reqblksz * blkcnt > maxsize)
1186 		maxsize = reqblksz * blkcnt;
1187 	if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
1188 		ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
1189 		if (ret)
1190 			goto out1;
1191 	}
1192 
1193 	CHN_UNLOCK(c);
1194 	sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
1195 	CHN_LOCK(c);
1196 
1197 	/* Decrease the size of bufsoft after decreasing bufhard. */
1198 	maxsize = sndbuf_getsize(b);
1199 	if (reqblksz * blkcnt > maxsize)
1200 		maxsize = reqblksz * blkcnt;
1201 	if (maxsize > sndbuf_getsize(bs))
1202 		printf("Danger! %s bufsoft size increasing from %d to %d after CHANNEL_SETBLOCKSIZE()\n",
1203 		    c->name, sndbuf_getsize(bs), maxsize);
1204 	if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
1205 		ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
1206 		if (ret)
1207 			goto out1;
1208 	}
1209 
1210 	chn_resetbuf(c);
1211 out1:
1212 	KASSERT(sndbuf_getsize(bs) ==  0 ||
1213 	    sndbuf_getsize(bs) >= sndbuf_getsize(b),
1214 	    ("%s(%s): bufsoft size %d < bufhard size %d, reqblksz=%d blksz=%d maxsize=%d blkcnt=%d",
1215 	    __func__, c->name, sndbuf_getsize(bs), sndbuf_getsize(b), reqblksz,
1216 	    blksz, maxsize, blkcnt));
1217 out:
1218 	c->flags &= ~CHN_F_SETBLOCKSIZE;
1219 #if 0
1220 	if (1) {
1221 		static uint32_t kk = 0;
1222 		printf("%u: b %d/%d/%d : (%d)%d/0x%0x | bs %d/%d/%d : (%d)%d/0x%0x\n", ++kk,
1223 			sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b),
1224 			sndbuf_getbps(b),
1225 			sndbuf_getspd(b), sndbuf_getfmt(b),
1226 			sndbuf_getsize(bs), sndbuf_getblksz(bs), sndbuf_getblkcnt(bs),
1227 			sndbuf_getbps(bs),
1228 			sndbuf_getspd(bs), sndbuf_getfmt(bs));
1229 		if (sndbuf_getsize(b) % sndbuf_getbps(b) ||
1230 				sndbuf_getblksz(b) % sndbuf_getbps(b) ||
1231 				sndbuf_getsize(bs) % sndbuf_getbps(bs) ||
1232 				sndbuf_getblksz(b) % sndbuf_getbps(b)) {
1233 			printf("%u: bps/blksz alignment screwed!\n", kk);
1234 		}
1235 	}
1236 #endif
1237 	return ret;
1238 }
1239 
1240 int
1241 chn_trigger(struct pcm_channel *c, int go)
1242 {
1243 #ifdef DEV_ISA
1244     	struct snd_dbuf *b = c->bufhard;
1245 #endif
1246 	int ret;
1247 
1248 	CHN_LOCKASSERT(c);
1249 #ifdef DEV_ISA
1250 	if (SND_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
1251 		sndbuf_dmabounce(b);
1252 #endif
1253 	ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
1254 
1255 	return ret;
1256 }
1257 
1258 int
1259 chn_getptr(struct pcm_channel *c)
1260 {
1261 	int hwptr;
1262 	int a = (1 << c->align) - 1;
1263 
1264 	CHN_LOCKASSERT(c);
1265 	hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
1266 	/* don't allow unaligned values in the hwa ptr */
1267 #if 1
1268 	hwptr &= ~a ; /* Apply channel align mask */
1269 #endif
1270 	hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
1271 	return hwptr;
1272 }
1273 
1274 struct pcmchan_caps *
1275 chn_getcaps(struct pcm_channel *c)
1276 {
1277 	CHN_LOCKASSERT(c);
1278 	return CHANNEL_GETCAPS(c->methods, c->devinfo);
1279 }
1280 
1281 u_int32_t
1282 chn_getformats(struct pcm_channel *c)
1283 {
1284 	u_int32_t *fmtlist, fmts;
1285 	int i;
1286 
1287 	fmtlist = chn_getcaps(c)->fmtlist;
1288 	fmts = 0;
1289 	for (i = 0; fmtlist[i]; i++)
1290 		fmts |= fmtlist[i];
1291 
1292 	/* report software-supported formats */
1293 	if (report_soft_formats)
1294 		fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE|
1295 		    AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE|
1296 		    AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE|
1297 		    AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8;
1298 
1299 	return fmts;
1300 }
1301 
1302 static int
1303 chn_buildfeeder(struct pcm_channel *c)
1304 {
1305 	struct feeder_class *fc;
1306 	struct pcm_feederdesc desc;
1307 	u_int32_t tmp[2], type, flags, hwfmt, *fmtlist;
1308 	int err;
1309 
1310 	CHN_LOCKASSERT(c);
1311 	while (chn_removefeeder(c) == 0);
1312 	KASSERT((c->feeder == NULL), ("feeder chain not empty"));
1313 
1314 	c->align = sndbuf_getalign(c->bufsoft);
1315 
1316 	if (SLIST_EMPTY(&c->children)) {
1317 		fc = feeder_getclass(NULL);
1318 		KASSERT(fc != NULL, ("can't find root feeder"));
1319 
1320 		err = chn_addfeeder(c, fc, NULL);
1321 		if (err) {
1322 			DEB(printf("can't add root feeder, err %d\n", err));
1323 
1324 			return err;
1325 		}
1326 		c->feeder->desc->out = c->format;
1327 	} else {
1328 		if (c->flags & CHN_F_HAS_VCHAN) {
1329 			desc.type = FEEDER_MIXER;
1330 			desc.in = 0;
1331 		} else {
1332 			DEB(printf("can't decide which feeder type to use!\n"));
1333 			return EOPNOTSUPP;
1334 		}
1335 		desc.out = c->format;
1336 		desc.flags = 0;
1337 		fc = feeder_getclass(&desc);
1338 		if (fc == NULL) {
1339 			DEB(printf("can't find vchan feeder\n"));
1340 
1341 			return EOPNOTSUPP;
1342 		}
1343 
1344 		err = chn_addfeeder(c, fc, &desc);
1345 		if (err) {
1346 			DEB(printf("can't add vchan feeder, err %d\n", err));
1347 
1348 			return err;
1349 		}
1350 	}
1351 	c->feederflags &= ~(1 << FEEDER_VOLUME);
1352 	if (c->direction == PCMDIR_PLAY &&
1353 			!(c->flags & CHN_F_VIRTUAL) &&
1354 			c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTVOL) &&
1355 			c->parentsnddev->mixer_dev)
1356 		c->feederflags |= 1 << FEEDER_VOLUME;
1357 	flags = c->feederflags;
1358 	fmtlist = chn_getcaps(c)->fmtlist;
1359 
1360 	DEB(printf("feederflags %x\n", flags));
1361 
1362 	for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) {
1363 		if (flags & (1 << type)) {
1364 			desc.type = type;
1365 			desc.in = 0;
1366 			desc.out = 0;
1367 			desc.flags = 0;
1368 			DEB(printf("find feeder type %d, ", type));
1369 			fc = feeder_getclass(&desc);
1370 			DEB(printf("got %p\n", fc));
1371 			if (fc == NULL) {
1372 				DEB(printf("can't find required feeder type %d\n", type));
1373 
1374 				return EOPNOTSUPP;
1375 			}
1376 
1377 			if ((type == FEEDER_RATE &&
1378 					!fmtvalid(fc->desc->in, fmtlist))
1379 					|| c->feeder->desc->out != fc->desc->in) {
1380  				DEB(printf("build fmtchain from 0x%x to 0x%x: ", c->feeder->desc->out, fc->desc->in));
1381 				tmp[0] = fc->desc->in;
1382 				tmp[1] = 0;
1383 				if (chn_fmtchain(c, tmp) == 0) {
1384 					DEB(printf("failed\n"));
1385 
1386 					return ENODEV;
1387 				}
1388  				DEB(printf("ok\n"));
1389 			}
1390 
1391 			err = chn_addfeeder(c, fc, fc->desc);
1392 			if (err) {
1393 				DEB(printf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err));
1394 
1395 				return err;
1396 			}
1397 			DEB(printf("added feeder %p, output 0x%x\n", fc, c->feeder->desc->out));
1398 		}
1399 	}
1400 
1401 	if (fmtvalid(c->feeder->desc->out, fmtlist)
1402 			&& !(c->direction == PCMDIR_REC &&
1403 				c->format != c->feeder->desc->out))
1404 		hwfmt = c->feeder->desc->out;
1405 	else {
1406 		if (c->direction == PCMDIR_REC) {
1407 			tmp[0] = c->format;
1408 			tmp[1] = 0;
1409 			hwfmt = chn_fmtchain(c, tmp);
1410 		} else
1411 			hwfmt = chn_fmtchain(c, fmtlist);
1412 	}
1413 
1414 	if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) {
1415 		DEB(printf("Invalid hardware format: 0x%x\n", hwfmt));
1416 		return ENODEV;
1417 	}
1418 
1419 	sndbuf_setfmt(c->bufhard, hwfmt);
1420 
1421 	if ((flags & (1 << FEEDER_VOLUME))) {
1422 		int vol = 100 | (100 << 8);
1423 
1424 		CHN_UNLOCK(c);
1425 		/*
1426 		 * XXX This is ugly! The way mixer subs being so secretive
1427 		 * about its own internals force us to use this silly
1428 		 * monkey trick.
1429 		 */
1430 		if (mixer_ioctl(c->parentsnddev->mixer_dev,
1431 				MIXER_READ(SOUND_MIXER_PCM), (caddr_t)&vol, -1, NULL) != 0)
1432 			device_printf(c->dev, "Soft Volume: Failed to read default value\n");
1433 		CHN_LOCK(c);
1434 		chn_setvolume(c, vol & 0x7f, (vol >> 8) & 0x7f);
1435 	}
1436 
1437 	return 0;
1438 }
1439 
1440 int
1441 chn_notify(struct pcm_channel *c, u_int32_t flags)
1442 {
1443 	struct pcmchan_children *pce;
1444 	struct pcm_channel *child;
1445 	int run;
1446 
1447 	CHN_LOCK(c);
1448 
1449 	if (SLIST_EMPTY(&c->children)) {
1450 		CHN_UNLOCK(c);
1451 		return ENODEV;
1452 	}
1453 
1454 	run = (c->flags & CHN_F_TRIGGERED)? 1 : 0;
1455 	/*
1456 	 * if the hwchan is running, we can't change its rate, format or
1457 	 * blocksize
1458 	 */
1459 	if (run)
1460 		flags &= CHN_N_VOLUME | CHN_N_TRIGGER;
1461 
1462 	if (flags & CHN_N_RATE) {
1463 		/*
1464 		 * we could do something here, like scan children and decide on
1465 		 * the most appropriate rate to mix at, but we don't for now
1466 		 */
1467 	}
1468 	if (flags & CHN_N_FORMAT) {
1469 		/*
1470 		 * we could do something here, like scan children and decide on
1471 		 * the most appropriate mixer feeder to use, but we don't for now
1472 		 */
1473 	}
1474 	if (flags & CHN_N_VOLUME) {
1475 		/*
1476 		 * we could do something here but we don't for now
1477 		 */
1478 	}
1479 	if (flags & CHN_N_BLOCKSIZE) {
1480 		int blksz;
1481 		/*
1482 		 * scan the children, find the lowest blocksize and use that
1483 		 * for the hard blocksize
1484 		 */
1485 		blksz = sndbuf_getmaxsize(c->bufhard) / 2;
1486 		SLIST_FOREACH(pce, &c->children, link) {
1487 			child = pce->channel;
1488 			CHN_LOCK(child);
1489 			if (sndbuf_getblksz(child->bufhard) < blksz)
1490 				blksz = sndbuf_getblksz(child->bufhard);
1491 			CHN_UNLOCK(child);
1492 		}
1493 		chn_setblocksize(c, 2, blksz);
1494 	}
1495 	if (flags & CHN_N_TRIGGER) {
1496 		int nrun;
1497 		/*
1498 		 * scan the children, and figure out if any are running
1499 		 * if so, we need to be running, otherwise we need to be stopped
1500 		 * if we aren't in our target sstate, move to it
1501 		 */
1502 		nrun = 0;
1503 		SLIST_FOREACH(pce, &c->children, link) {
1504 			child = pce->channel;
1505 			CHN_LOCK(child);
1506 			if (child->flags & CHN_F_TRIGGERED)
1507 				nrun = 1;
1508 			CHN_UNLOCK(child);
1509 		}
1510 		if (nrun && !run)
1511 			chn_start(c, 1);
1512 		if (!nrun && run)
1513 			chn_abort(c);
1514 	}
1515 	CHN_UNLOCK(c);
1516 	return 0;
1517 }
1518 
1519 void
1520 chn_lock(struct pcm_channel *c)
1521 {
1522 	CHN_LOCK(c);
1523 }
1524 
1525 void
1526 chn_unlock(struct pcm_channel *c)
1527 {
1528 	CHN_UNLOCK(c);
1529 }
1530