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