channel.c revision 152425
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
34SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/channel.c 152425 2005-11-14 18:21:23Z ariff $");
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
46static int chn_targetirqrate = 32;
47TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate);
48
49static int
50sysctl_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}
63SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW,
64	0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "");
65static int report_soft_formats = 1;
66SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
67	&report_soft_formats, 1, "report software-emulated formats");
68
69static int chn_buildfeeder(struct pcm_channel *c);
70
71static void
72chn_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
90static void
91chn_lockdestroy(struct pcm_channel *c)
92{
93	snd_mtxfree(c->lock);
94}
95
96static int
97chn_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
119static int
120chn_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
129static void
130chn_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
150static int
151chn_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
171static unsigned int
172chn_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
205void
206chn_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
224int
225chn_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
277static void
278chn_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
300int
301chn_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
380static int
381chn_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 */
398int
399chn_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
433void
434chn_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. */
452static void
453chn_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
473int
474chn_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
535void
536chn_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
547u_int32_t
548chn_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
587void
588chn_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 */
603int
604chn_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 */
638int
639chn_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 */
661int
662chn_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
696int
697chn_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
750int
751fmtvalid(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
761int
762chn_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
801int
802chn_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
869out:
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
889int
890chn_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
907int
908chn_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
925int
926chn_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
942static int
943chn_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));
1005out:
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
1017int
1018chn_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
1030static int
1031chn_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
1054int
1055chn_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 */
1072static int
1073round_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 */
1106int
1107chn_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);
1211out1:
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));
1217out:
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
1240int
1241chn_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
1258int
1259chn_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
1274struct pcmchan_caps *
1275chn_getcaps(struct pcm_channel *c)
1276{
1277	CHN_LOCKASSERT(c);
1278	return CHANNEL_GETCAPS(c->methods, c->devinfo);
1279}
1280
1281u_int32_t
1282chn_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
1302static int
1303chn_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
1440int
1441chn_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
1519void
1520chn_lock(struct pcm_channel *c)
1521{
1522	CHN_LOCK(c);
1523}
1524
1525void
1526chn_unlock(struct pcm_channel *c)
1527{
1528	CHN_UNLOCK(c);
1529}
1530