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