xref: /freebsd/sys/dev/sound/pcm/channel.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
1 /*
2  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3  * Portions Copyright by Luigi Rizzo - 1997-99
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29 
30 #include <dev/sound/pcm/sound.h>
31 
32 #include "feeder_if.h"
33 
34 #define MIN_CHUNK_SIZE 		256	/* for uiomove etc. */
35 #define	DMA_ALIGN_THRESHOLD	4
36 #define	DMA_ALIGN_MASK		(~(DMA_ALIGN_THRESHOLD - 1))
37 
38 #define	MIN(x, y) (((x) < (y))? (x) : (y))
39 #define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED))
40 
41 /*
42 #define DEB(x) x
43 */
44 
45 static int chn_buildfeeder(struct pcm_channel *c);
46 
47 static void
48 chn_lockinit(struct pcm_channel *c)
49 {
50 	c->lock = snd_mtxcreate(c->name);
51 }
52 
53 static void
54 chn_lockdestroy(struct pcm_channel *c)
55 {
56 	snd_mtxfree(c->lock);
57 }
58 
59 static int
60 chn_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 
80 static int
81 chn_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 
90 static void
91 chn_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 
101 static int
102 chn_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 
122 static unsigned int
123 chn_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 
155 void
156 chn_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 
174 static int irqc = 0;
175 
176 int
177 chn_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 
201 static void
202 chn_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 
225 int
226 chn_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 		device_printf(c->parentsnddev->dev, "play interrupt timeout, channel dead\n");
280 	}
281 
282 	return ret;
283 }
284 
285 static int
286 chn_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  */
299 int
300 chn_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 
321 void
322 chn_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. */
340 static void
341 chn_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 
364 int
365 chn_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 		device_printf(c->parentsnddev->dev, "record interrupt timeout, channel dead\n");
400 	}
401 
402 	return ret;
403 }
404 
405 void
406 chn_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 
416 u_int32_t
417 chn_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 
452 void
453 chn_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  */
468 int
469 chn_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 */
491 int
492 chn_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  */
514 int
515 chn_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 
546 int
547 chn_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 
588 int
589 fmtvalid(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 
599 int
600 chn_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 
626 int
627 chn_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 
675 int
676 chn_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 
694 int
695 chn_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 
708 int
709 chn_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 
717 static int
718 chn_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));
774 out:
775 		DEB(printf("setspeed done, r = %d\n", r));
776 		return r;
777 	} else
778 		return EINVAL;
779 }
780 
781 int
782 chn_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 
794 static int
795 chn_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 
818 int
819 chn_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 
832 int
833 chn_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);
903 out:
904 	return ret;
905 }
906 
907 int
908 chn_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 
921 int
922 chn_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 
937 struct pcmchan_caps *
938 chn_getcaps(struct pcm_channel *c)
939 {
940 	CHN_LOCKASSERT(c);
941 	return CHANNEL_GETCAPS(c->methods, c->devinfo);
942 }
943 
944 u_int32_t
945 chn_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 
958 static int
959 chn_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 
1049 int
1050 chn_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