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