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