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