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