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