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