channel.c revision 78362
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 78362 2001-06-16 21:25:10Z 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 | CHN_F_VIRTUAL)) || !(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_LOCKASSERT(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	return ret;
502}
503
504/*
505 * chn_abort terminates a running dma transfer.  it may sleep up to 200ms.
506 * it returns the number of bytes that have not been transferred.
507 *
508 * called from: dsp_close, dsp_ioctl, with channel locked
509 */
510int
511chn_abort(struct pcm_channel *c)
512{
513    	int missing = 0;
514    	struct snd_dbuf *b = c->bufhard;
515    	struct snd_dbuf *bs = c->bufsoft;
516
517	CHN_LOCKASSERT(c);
518	if (!(c->flags & CHN_F_TRIGGERED))
519		return 0;
520	c->flags |= CHN_F_ABORTING;
521
522	c->flags &= ~CHN_F_TRIGGERED;
523	/* kill the channel */
524	chn_trigger(c, PCMTRIG_ABORT);
525	sndbuf_setrun(b, 0);
526	if (!(c->flags & CHN_F_VIRTUAL))
527		chn_dmaupdate(c);
528    	missing = sndbuf_getready(bs) + sndbuf_getready(b);
529
530	c->flags &= ~CHN_F_ABORTING;
531	return missing;
532}
533
534/*
535 * this routine tries to flush the dma transfer. It is called
536 * on a close. We immediately abort any read DMA
537 * operation, and then wait for the play bufhard to drain.
538 *
539 * called from: dsp_close
540 */
541
542int
543chn_flush(struct pcm_channel *c)
544{
545    	int ret, count, resid, resid_p;
546    	struct snd_dbuf *b = c->bufhard;
547    	struct snd_dbuf *bs = c->bufsoft;
548
549	CHN_LOCKASSERT(c);
550	KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
551    	DEB(printf("chn_flush c->flags 0x%08x\n", c->flags));
552	if (!(c->flags & CHN_F_TRIGGERED))
553		return 0;
554
555	c->flags |= CHN_F_CLOSING;
556	resid = sndbuf_getready(bs) + sndbuf_getready(b);
557	resid_p = resid;
558	count = 10;
559	ret = 0;
560	while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) {
561		/* still pending output data. */
562		ret = chn_sleep(c, "pcmflu", hz / 10);
563		if (ret == EWOULDBLOCK)
564			ret = 0;
565		if (ret == 0) {
566			resid = sndbuf_getready(bs) + sndbuf_getready(b);
567			if (resid >= resid_p)
568				count--;
569			resid_p = resid;
570		}
571   	}
572	if (count == 0)
573		DEB(printf("chn_flush: timeout\n"));
574
575	c->flags &= ~CHN_F_TRIGGERED;
576	/* kill the channel */
577	chn_trigger(c, PCMTRIG_ABORT);
578	sndbuf_setrun(b, 0);
579
580    	c->flags &= ~CHN_F_CLOSING;
581    	return 0;
582}
583
584int
585fmtvalid(u_int32_t fmt, u_int32_t *fmtlist)
586{
587	int i;
588
589	for (i = 0; fmtlist[i]; i++)
590		if (fmt == fmtlist[i])
591			return 1;
592	return 0;
593}
594
595int
596chn_reset(struct pcm_channel *c, u_int32_t fmt)
597{
598	int hwspd, r = 0;
599
600	CHN_LOCKASSERT(c);
601	c->flags &= CHN_F_RESET;
602	CHANNEL_RESET(c->methods, c->devinfo);
603	if (fmt) {
604		hwspd = DSP_DEFAULT_SPEED;
605		RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
606		c->speed = hwspd;
607
608		r = chn_setformat(c, fmt);
609		if (r == 0)
610			r = chn_setspeed(c, hwspd);
611		if (r == 0)
612			r = chn_setvolume(c, 100, 100);
613	}
614	r = chn_setblocksize(c, 0, 0);
615	if (r == 0) {
616		chn_resetbuf(c);
617		CHANNEL_RESETDONE(c->methods, c->devinfo);
618	}
619	return r;
620}
621
622int
623chn_init(struct pcm_channel *c, void *devinfo, int dir)
624{
625	struct feeder_class *fc;
626	struct snd_dbuf *b, *bs;
627
628	chn_lockinit(c);
629	CHN_LOCK(c);
630	/* Initialize the hardware and DMA bufhard first. */
631	c->feeder = NULL;
632	fc = feeder_getclass(NULL);
633	if (fc == NULL)
634		return EINVAL;
635	if (chn_addfeeder(c, fc, NULL))
636		return EINVAL;
637
638	b = sndbuf_create(c->name, "primary");
639	if (b == NULL)
640		return ENOMEM;
641	bs = sndbuf_create(c->name, "secondary");
642	if (bs == NULL) {
643		sndbuf_destroy(b);
644		return ENOMEM;
645	}
646	sndbuf_setup(bs, NULL, 0);
647	c->bufhard = b;
648	c->bufsoft = bs;
649	c->flags = 0;
650	c->feederflags = 0;
651	c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, dir);
652	if (c->devinfo == NULL) {
653		sndbuf_destroy(bs);
654		sndbuf_destroy(b);
655		return ENODEV;
656	}
657	if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) {
658		sndbuf_destroy(bs);
659		sndbuf_destroy(b);
660		return ENOMEM;
661	}
662	chn_setdir(c, dir);
663
664	/* And the secondary bufhard. */
665	sndbuf_setfmt(b, AFMT_U8);
666	sndbuf_setfmt(bs, AFMT_U8);
667	CHN_UNLOCK(c);
668	return 0;
669}
670
671int
672chn_kill(struct pcm_channel *c)
673{
674    	struct snd_dbuf *b = c->bufhard;
675    	struct snd_dbuf *bs = c->bufsoft;
676
677	CHN_LOCK(c);
678	if (c->flags & CHN_F_TRIGGERED)
679		chn_trigger(c, PCMTRIG_ABORT);
680	while (chn_removefeeder(c) == 0);
681	if (CHANNEL_FREE(c->methods, c->devinfo))
682		sndbuf_free(c->bufhard);
683	c->flags |= CHN_F_DEAD;
684	sndbuf_destroy(bs);
685	sndbuf_destroy(b);
686	chn_lockdestroy(c);
687	return 0;
688}
689
690int
691chn_setdir(struct pcm_channel *c, int dir)
692{
693    	struct snd_dbuf *b = c->bufhard;
694	int r;
695
696	CHN_LOCKASSERT(c);
697	c->direction = dir;
698	r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction);
699	if (!r && ISA_DMA(b))
700		sndbuf_isadmasetdir(b, c->direction);
701	return r;
702}
703
704int
705chn_setvolume(struct pcm_channel *c, int left, int right)
706{
707	CHN_LOCKASSERT(c);
708	/* could add a feeder for volume changing if channel returns -1 */
709	c->volume = (left << 8) | right;
710	return 0;
711}
712
713static int
714chn_tryspeed(struct pcm_channel *c, int speed)
715{
716	struct pcm_feeder *f;
717    	struct snd_dbuf *b = c->bufhard;
718    	struct snd_dbuf *bs = c->bufsoft;
719	int r, delta;
720
721	CHN_LOCKASSERT(c);
722	DEB(printf("setspeed, channel %s\n", c->name));
723	DEB(printf("want speed %d, ", speed));
724	if (speed <= 0)
725		return EINVAL;
726	if (CANCHANGE(c)) {
727		r = 0;
728		c->speed = speed;
729		sndbuf_setspd(bs, speed);
730		RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
731		DEB(printf("try speed %d, ", speed));
732		sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed));
733		DEB(printf("got speed %d\n", sndbuf_getspd(b)));
734
735		delta = sndbuf_getspd(b) - sndbuf_getspd(bs);
736		if (delta < 0)
737			delta = -delta;
738
739		c->feederflags &= ~(1 << FEEDER_RATE);
740		if (delta > 500)
741			c->feederflags |= 1 << FEEDER_RATE;
742		else
743			sndbuf_setspd(bs, sndbuf_getspd(b));
744
745		r = chn_buildfeeder(c);
746		DEB(printf("r = %d\n", r));
747		if (r)
748			goto out;
749
750		r = chn_setblocksize(c, 0, 0);
751		if (r)
752			goto out;
753
754		if (!(c->feederflags & (1 << FEEDER_RATE)))
755			goto out;
756
757		r = EINVAL;
758		f = chn_findfeeder(c, FEEDER_RATE);
759		DEB(printf("feedrate = %p\n", f));
760		if (f == NULL)
761			goto out;
762
763		r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(bs));
764		DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(bs), r));
765		if (r)
766			goto out;
767
768		r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(b));
769		DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(b), r));
770out:
771		DEB(printf("setspeed done, r = %d\n", r));
772		return r;
773	} else
774		return EINVAL;
775}
776
777int
778chn_setspeed(struct pcm_channel *c, int speed)
779{
780	int r, oldspeed = c->speed;
781
782	r = chn_tryspeed(c, speed);
783	if (r) {
784		DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
785		chn_tryspeed(c, oldspeed);
786	}
787	return r;
788}
789
790static int
791chn_tryformat(struct pcm_channel *c, u_int32_t fmt)
792{
793	struct snd_dbuf *b = c->bufhard;
794	struct snd_dbuf *bs = c->bufsoft;
795	int r;
796
797	CHN_LOCKASSERT(c);
798	if (CANCHANGE(c)) {
799		DEB(printf("want format %d\n", fmt));
800		c->format = fmt;
801		r = chn_buildfeeder(c);
802		if (r == 0) {
803			sndbuf_setfmt(b, c->feeder->desc->out);
804			sndbuf_setfmt(bs, fmt);
805			chn_resetbuf(c);
806			CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b));
807			r = chn_tryspeed(c, c->speed);
808		}
809		return r;
810	} else
811		return EINVAL;
812}
813
814int
815chn_setformat(struct pcm_channel *c, u_int32_t fmt)
816{
817	u_int32_t oldfmt = c->format;
818	int r;
819
820	r = chn_tryformat(c, fmt);
821	if (r) {
822		DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
823		chn_tryformat(c, oldfmt);
824	}
825	return r;
826}
827
828int
829chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
830{
831	struct snd_dbuf *b = c->bufhard;
832	struct snd_dbuf *bs = c->bufsoft;
833	int bufsz, irqhz, tmp, ret;
834
835	CHN_LOCKASSERT(c);
836	if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED))
837		return EINVAL;
838
839	ret = 0;
840	DEB(printf("%s(%d, %d)\n", __FUNCTION__, blkcnt, blksz));
841	if (blksz == 0 || blksz == -1) {
842		if (blksz == -1)
843			c->flags &= ~CHN_F_HAS_SIZE;
844		if (!(c->flags & CHN_F_HAS_SIZE)) {
845			blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / CHN_DEFAULT_HZ;
846	      		tmp = 32;
847			while (tmp <= blksz)
848				tmp <<= 1;
849			tmp >>= 1;
850			blksz = tmp;
851			blkcnt = CHN_2NDBUFMAXSIZE / blksz;
852
853			RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
854			RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz);
855			DEB(printf("%s: defaulting to (%d, %d)\n", __FUNCTION__, blkcnt, blksz));
856		} else {
857			blkcnt = sndbuf_getblkcnt(bs);
858			blksz = sndbuf_getblksz(bs);
859			DEB(printf("%s: updating (%d, %d)\n", __FUNCTION__, blkcnt, blksz));
860		}
861	} else {
862		ret = EINVAL;
863		if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
864			goto out;
865		ret = 0;
866		c->flags |= CHN_F_HAS_SIZE;
867	}
868
869	bufsz = blkcnt * blksz;
870
871	ret = ENOMEM;
872	if (sndbuf_remalloc(bs, blkcnt, blksz))
873		goto out;
874	ret = 0;
875
876	/* adjust for different hw format/speed */
877	irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / sndbuf_getblksz(bs);
878	DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __FUNCTION__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz));
879	RANGE(irqhz, 16, 512);
880
881	sndbuf_setblksz(b, (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz);
882
883	/* round down to 2^x */
884	blksz = 32;
885	while (blksz <= sndbuf_getblksz(b))
886		blksz <<= 1;
887	blksz >>= 1;
888
889	/* round down to fit hw bufhard size */
890	RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2);
891	DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __FUNCTION__, blksz, sndbuf_getmaxsize(b)));
892
893	sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
894
895	irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b);
896	DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz));
897
898	chn_resetbuf(c);
899out:
900	return ret;
901}
902
903int
904chn_trigger(struct pcm_channel *c, int go)
905{
906    	struct snd_dbuf *b = c->bufhard;
907	int ret;
908
909	CHN_LOCKASSERT(c);
910	if (ISA_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
911		sndbuf_isadmabounce(b);
912	ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
913
914	return ret;
915}
916
917int
918chn_getptr(struct pcm_channel *c)
919{
920	int hwptr;
921	int a = (1 << c->align) - 1;
922
923	CHN_LOCKASSERT(c);
924	hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
925	/* don't allow unaligned values in the hwa ptr */
926#if 1
927	hwptr &= ~a ; /* Apply channel align mask */
928#endif
929	hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
930	return hwptr;
931}
932
933struct pcmchan_caps *
934chn_getcaps(struct pcm_channel *c)
935{
936	CHN_LOCKASSERT(c);
937	return CHANNEL_GETCAPS(c->methods, c->devinfo);
938}
939
940u_int32_t
941chn_getformats(struct pcm_channel *c)
942{
943	u_int32_t *fmtlist, fmts;
944	int i;
945
946	fmtlist = chn_getcaps(c)->fmtlist;
947	fmts = 0;
948	for (i = 0; fmtlist[i]; i++)
949		fmts |= fmtlist[i];
950
951	return fmts;
952}
953
954static int
955chn_buildfeeder(struct pcm_channel *c)
956{
957	struct feeder_class *fc;
958	struct pcm_feederdesc desc;
959	u_int32_t tmp[2], type, flags;
960
961	CHN_LOCKASSERT(c);
962	while (chn_removefeeder(c) == 0);
963	KASSERT((c->feeder == NULL), ("feeder chain not empty"));
964
965	c->align = sndbuf_getalign(c->bufsoft);
966
967	if (SLIST_EMPTY(&c->children)) {
968		fc = feeder_getclass(NULL);
969		if (fc == NULL) {
970			DEB(printf("can't find root feeder\n"));
971			return EINVAL;
972		}
973		if (chn_addfeeder(c, fc, NULL)) {
974			DEB(printf("can't add root feeder\n"));
975			return EINVAL;
976		}
977		c->feeder->desc->out = c->format;
978	} else {
979		desc.type = FEEDER_MIXER;
980		desc.in = 0;
981		desc.out = c->format;
982		desc.flags = 0;
983		fc = feeder_getclass(&desc);
984		if (fc == NULL) {
985			DEB(printf("can't find vchan feeder\n"));
986			return EINVAL;
987		}
988		if (chn_addfeeder(c, fc, &desc)) {
989			DEB(printf("can't add vchan feeder\n"));
990			return EINVAL;
991		}
992	}
993	flags = c->feederflags;
994
995	if ((c->flags & CHN_F_MAPPED) && (flags != 0)) {
996		DEB(printf("can't build feeder chain on mapped channel\n"));
997		return EINVAL;
998	}
999	DEB(printf("not mapped, flags %x\n", flags));
1000
1001	for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) {
1002		if (flags & (1 << type)) {
1003			desc.type = type;
1004			desc.in = 0;
1005			desc.out = 0;
1006			desc.flags = 0;
1007			DEB(printf("find feeder type %d, ", type));
1008			fc = feeder_getclass(&desc);
1009			DEB(printf("got %p\n", fc));
1010			if (fc == NULL) {
1011				DEB(printf("can't find required feeder type %d\n", type));
1012				return EINVAL;
1013			}
1014
1015			if (c->feeder->desc->out != fc->desc->in) {
1016 				DEB(printf("build fmtchain from %x to %x: ", c->feeder->desc->out, fc->desc->in));
1017				tmp[0] = fc->desc->in;
1018				tmp[1] = 0;
1019				if (chn_fmtchain(c, tmp) == 0) {
1020					DEB(printf("failed\n"));
1021					return EINVAL;
1022				}
1023 				DEB(printf("ok\n"));
1024			}
1025
1026			if (chn_addfeeder(c, fc, fc->desc)) {
1027				DEB(printf("can't add feeder %p, output %x\n", fc, fc->desc->out));
1028				return EINVAL;
1029			}
1030			DEB(printf("added feeder %p, output %x\n", fc, c->feeder->desc->out));
1031		}
1032	}
1033
1034	if (!fmtvalid(c->feeder->desc->out, chn_getcaps(c)->fmtlist)) {
1035		if (chn_fmtchain(c, chn_getcaps(c)->fmtlist) == 0) {
1036			DEB(printf("can't build fmtchain from %x\n", c->feeder->desc->out));
1037			return EINVAL;
1038		}
1039		DEB(printf("built fmtchain from %x\n", c->feeder->desc->out));
1040	}
1041
1042	return 0;
1043}
1044
1045int
1046chn_notify(struct pcm_channel *c, u_int32_t flags)
1047{
1048	struct pcmchan_children *pce;
1049	struct pcm_channel *child;
1050	int run;
1051
1052	if (SLIST_EMPTY(&c->children))
1053		return ENODEV;
1054
1055	run = (c->flags & CHN_F_TRIGGERED)? 1 : 0;
1056	/*
1057	 * if the hwchan is running, we can't change its rate, format or
1058	 * blocksize
1059	 */
1060	if (run)
1061		flags &= CHN_N_VOLUME | CHN_N_TRIGGER;
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 nrun;
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		nrun = 0;
1102		SLIST_FOREACH(pce, &c->children, link) {
1103			child = pce->channel;
1104			if (child->flags & CHN_F_TRIGGERED)
1105				nrun = 1;
1106		}
1107		if (nrun && !run)
1108			chn_start(c, 1);
1109		if (!nrun && run)
1110			chn_abort(c);
1111	}
1112	return 0;
1113}
1114