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