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