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