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