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