xref: /dragonfly/sys/dev/sound/pcm/channel.c (revision 51871435)
1 /*-
2  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3  * Portions Copyright by Luigi Rizzo - 1997-99
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/dev/sound/pcm/channel.c,v 1.99.2.5 2007/05/13 20:53:39 ariff Exp $
28  */
29 
30 #include "use_isa.h"
31 
32 #include <dev/sound/pcm/sound.h>
33 #include <sys/vnode.h>		/* IO_NDELAY */
34 
35 #include "feeder_if.h"
36 
37 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.15 2008/01/05 13:34:22 corecode Exp $");
38 
39 #define MIN_CHUNK_SIZE 		256	/* for uiomove etc. */
40 #if 0
41 #define	DMA_ALIGN_THRESHOLD	4
42 #define	DMA_ALIGN_MASK		(~(DMA_ALIGN_THRESHOLD - 1))
43 #endif
44 
45 #define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED))
46 
47 /*
48 #define DEB(x) x
49 */
50 
51 static int chn_targetirqrate = 32;
52 TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate);
53 
54 static int
55 sysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS)
56 {
57 	int err, val;
58 
59 	val = chn_targetirqrate;
60 	err = sysctl_handle_int(oidp, &val, sizeof(val), req);
61 	if (val < 16 || val > 512)
62 		err = EINVAL;
63 	else
64 		chn_targetirqrate = val;
65 
66 	return err;
67 }
68 SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW,
69 	0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "");
70 static int report_soft_formats = 1;
71 SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
72 	&report_soft_formats, 1, "report software-emulated formats");
73 
74 static int chn_buildfeeder(struct pcm_channel *c);
75 
76 static void
77 chn_lockinit(struct pcm_channel *c, int dir)
78 {
79 	switch(dir) {
80 	case PCMDIR_PLAY:
81 		c->lock = snd_mtxcreate(c->name, "pcm play channel");
82 		break;
83 	case PCMDIR_REC:
84 		c->lock = snd_mtxcreate(c->name, "pcm record channel");
85 		break;
86 	case PCMDIR_VIRTUAL:
87 		c->lock = snd_mtxcreate(c->name, "pcm virtual play channel");
88 		break;
89 	case 0:
90 		c->lock = snd_mtxcreate(c->name, "pcm fake channel");
91 		break;
92 	}
93 }
94 
95 static void
96 chn_lockdestroy(struct pcm_channel *c)
97 {
98 	snd_mtxfree(c->lock);
99 }
100 
101 static int
102 chn_polltrigger(struct pcm_channel *c)
103 {
104 	struct snd_dbuf *bs = c->bufsoft;
105 	unsigned amt, lim;
106 
107 	CHN_LOCKASSERT(c);
108 	if (c->flags & CHN_F_MAPPED) {
109 		if (sndbuf_getprevblocks(bs) == 0)
110 			return 1;
111 		else
112 			return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0;
113 	} else {
114 		amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
115 #if 0
116 		lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
117 #endif
118 		lim = 1;
119 		return (amt >= lim)? 1 : 0;
120 	}
121 	return 0;
122 }
123 
124 static int
125 chn_pollreset(struct pcm_channel *c)
126 {
127 	struct snd_dbuf *bs = c->bufsoft;
128 
129 	CHN_LOCKASSERT(c);
130 	sndbuf_updateprevtotal(bs);
131 	return 1;
132 }
133 
134 static void
135 chn_wakeup(struct pcm_channel *c)
136 {
137 	struct snd_dbuf *bs = c->bufsoft;
138 	struct pcmchan_children *pce;
139 
140 	CHN_LOCKASSERT(c);
141 	if (SLIST_EMPTY(&c->children)) {
142 		/*if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))*/
143 		if (SLIST_FIRST(&sndbuf_getkq(bs)->ki_note) && chn_polltrigger(c)) {
144 			/*
145 			 * XXX
146 			 *
147 			 * We would call KNOTE() here, but as we
148 			 * are in interrupt context, we'd have to
149 			 * acquire the MP lock before.
150 			 * Instead, we'll queue a task in a software
151 			 * interrupt, which will run with the MP lock
152 			 * held.
153 			 *
154 			 * buffer.c:sndbuf_kqtask will then call
155 			 * KNOTE() from safer context.
156 			 */
157 			taskqueue_enqueue(taskqueue_swi, &bs->kqtask);
158 		}
159 	} else {
160 		SLIST_FOREACH(pce, &c->children, link) {
161 			CHN_LOCK(pce->channel);
162 			chn_wakeup(pce->channel);
163 			CHN_UNLOCK(pce->channel);
164 		}
165 	}
166 
167 	wakeup(bs);
168 }
169 
170 static int
171 chn_sleep(struct pcm_channel *c, char *str, int timeout)
172 {
173     	struct snd_dbuf *bs = c->bufsoft;
174 	int ret;
175 
176 	CHN_LOCKASSERT(c);
177 #ifdef USING_MUTEX
178 	ret = snd_mtxsleep(bs, c->lock, PCATCH, str, timeout);
179 #else
180 	ret = tsleep(bs, PRIBIO | PCATCH, str, timeout);
181 #endif
182 
183 	return ret;
184 }
185 
186 /*
187  * chn_dmaupdate() tracks the status of a dma transfer,
188  * updating pointers.
189  */
190 
191 static unsigned int
192 chn_dmaupdate(struct pcm_channel *c)
193 {
194 	struct snd_dbuf *b = c->bufhard;
195 	unsigned int delta, old, hwptr, amt;
196 
197 	KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0"));
198 	CHN_LOCKASSERT(c);
199 
200 	old = sndbuf_gethwptr(b);
201 	hwptr = chn_getptr(c);
202 	delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b);
203 	sndbuf_sethwptr(b, hwptr);
204 
205 	DEB(
206 	if (delta >= ((sndbuf_getsize(b) * 15) / 16)) {
207 		if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING)))
208 			device_printf(c->dev, "hwptr went backwards %d -> %d\n", old, hwptr);
209 	}
210 	);
211 
212 	if (c->direction == PCMDIR_PLAY) {
213 		amt = MIN(delta, sndbuf_getready(b));
214 		if (amt > 0)
215 			sndbuf_dispose(b, NULL, amt);
216 	} else {
217 		amt = MIN(delta, sndbuf_getfree(b));
218 		if (amt > 0)
219 		       sndbuf_acquire(b, NULL, amt);
220 	}
221 
222 	return delta;
223 }
224 
225 void
226 chn_wrupdate(struct pcm_channel *c)
227 {
228 	int ret;
229 
230 	CHN_LOCKASSERT(c);
231 	KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
232 
233 	if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || !(c->flags & CHN_F_TRIGGERED))
234 		return;
235 	chn_dmaupdate(c);
236 	ret = chn_wrfeed(c);
237 	/* tell the driver we've updated the primary buffer */
238 	chn_trigger(c, PCMTRIG_EMLDMAWR);
239 	DEB(if (ret)
240 		kprintf("chn_wrupdate: chn_wrfeed returned %d\n", ret);)
241 
242 }
243 
244 int
245 chn_wrfeed(struct pcm_channel *c)
246 {
247     	struct snd_dbuf *b = c->bufhard;
248     	struct snd_dbuf *bs = c->bufsoft;
249 	unsigned int ret, amt;
250 
251 	CHN_LOCKASSERT(c);
252 #if 0
253     	DEB(
254 	if (c->flags & CHN_F_CLOSING) {
255 		sndbuf_dump(b, "b", 0x02);
256 		sndbuf_dump(bs, "bs", 0x02);
257 	})
258 #endif
259 
260 	if (c->flags & CHN_F_MAPPED)
261 		sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
262 
263 	amt = sndbuf_getfree(b);
264 	KASSERT(amt <= sndbuf_getsize(bs),
265 	    ("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name,
266 	   amt, sndbuf_getsize(bs), c->flags));
267 
268 	ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
269 	/*
270 	 * Possible xruns. There should be no empty space left in buffer.
271 	 */
272 	if (sndbuf_getfree(b) > 0)
273 		c->xruns++;
274 
275 	if (ret == 0 && sndbuf_getfree(b) < amt)
276 		chn_wakeup(c);
277 
278 	return ret;
279 }
280 
281 static void
282 chn_wrintr(struct pcm_channel *c)
283 {
284 	int ret;
285 
286 	CHN_LOCKASSERT(c);
287 	/* update pointers in primary buffer */
288 	chn_dmaupdate(c);
289 	/* ...and feed from secondary to primary */
290 	ret = chn_wrfeed(c);
291 	/* tell the driver we've updated the primary buffer */
292 	chn_trigger(c, PCMTRIG_EMLDMAWR);
293 	DEB(if (ret)
294 		kprintf("chn_wrintr: chn_wrfeed returned %d\n", ret);)
295 }
296 
297 /*
298  * user write routine - uiomove data into secondary buffer, trigger if necessary
299  * if blocking, sleep, rinse and repeat.
300  *
301  * called externally, so must handle locking
302  */
303 
304 int
305 chn_write(struct pcm_channel *c, struct uio *buf, int ioflags)
306 {
307 	int ret, timeout, newsize, count, sz;
308 	int nbio;
309 	struct snd_dbuf *bs = c->bufsoft;
310 	void *off;
311 	int t, x,togo,p;
312 
313 	CHN_LOCKASSERT(c);
314 	/*
315 	 * XXX Certain applications attempt to write larger size
316 	 * of pcm data than c->blocksize2nd without blocking,
317 	 * resulting partial write. Expand the block size so that
318 	 * the write operation avoids blocking.
319 	 */
320 	nbio = (c->flags & CHN_F_NBIO) || (ioflags & IO_NDELAY);
321 	if (nbio && buf->uio_resid > (size_t)sndbuf_getblksz(bs)) {
322 		DEB(device_printf(c->dev, "broken app, nbio and tried to write %ld bytes with fragsz %d\n",
323 			buf->uio_resid, sndbuf_getblksz(bs)));
324 		newsize = 16;
325 		while (newsize < (int)szmin(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2))
326 			newsize <<= 1;
327 		chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize);
328 		DEB(device_printf(c->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs)));
329 	}
330 
331 	ret = 0;
332 	count = hz;
333 	while (!ret && (buf->uio_resid > 0) && (count > 0)) {
334 		sz = sndbuf_getfree(bs);
335 		if (sz == 0) {
336 			if (nbio)
337 				ret = EWOULDBLOCK;
338 			else {
339 				timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
340 				if (timeout < 1)
341 					timeout = 1;
342 				timeout = 1;
343 	   			ret = chn_sleep(c, "pcmwr", timeout);
344 				if (ret == EWOULDBLOCK) {
345 					count -= timeout;
346 					ret = 0;
347 				} else if (ret == 0)
348 					count = hz;
349 			}
350 		} else {
351 			sz = (int)szmin(sz, buf->uio_resid);
352 			KASSERT(sz > 0, ("confusion in chn_write"));
353 			/* kprintf("sz: %d\n", sz); */
354 
355 			/*
356 			 * The following assumes that the free space in
357 			 * the buffer can never be less around the
358 			 * unlock-uiomove-lock sequence.
359 			 */
360 			togo = sz;
361 			while (ret == 0 && togo> 0) {
362 				p = sndbuf_getfreeptr(bs);
363 				t = MIN(togo, sndbuf_getsize(bs) - p);
364 				off = sndbuf_getbufofs(bs, p);
365 				CHN_UNLOCK(c);
366 				ret = uiomove(off, t, buf);
367 				CHN_LOCK(c);
368 				togo -= t;
369 				x = sndbuf_acquire(bs, NULL, t);
370 			}
371 			ret = 0;
372 			if (ret == 0 && !(c->flags & CHN_F_TRIGGERED))
373 				chn_start(c, 0);
374 		}
375 	}
376 	/* kprintf("ret: %d left: %d\n", ret, buf->uio_resid); */
377 
378 	if (count <= 0) {
379 		c->flags |= CHN_F_DEAD;
380 		kprintf("%s: play interrupt timeout, channel dead\n", c->name);
381 	}
382 
383 	return ret;
384 }
385 
386 #if 0
387 static int
388 chn_rddump(struct pcm_channel *c, unsigned int cnt)
389 {
390     	struct snd_dbuf *b = c->bufhard;
391 
392 	CHN_LOCKASSERT(c);
393 #if 0
394 	static uint32_t kk = 0;
395 	printf("%u: dumping %d bytes\n", ++kk, cnt);
396 #endif
397 	c->xruns++;
398 	sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt);
399 	return sndbuf_dispose(b, NULL, cnt);
400 }
401 #endif
402 
403 /*
404  * Feed new data from the read buffer. Can be called in the bottom half.
405  */
406 int
407 chn_rdfeed(struct pcm_channel *c)
408 {
409     	struct snd_dbuf *b = c->bufhard;
410     	struct snd_dbuf *bs = c->bufsoft;
411 	unsigned int ret, amt;
412 
413 	CHN_LOCKASSERT(c);
414     	DEB(
415 	if (c->flags & CHN_F_CLOSING) {
416 		sndbuf_dump(b, "b", 0x02);
417 		sndbuf_dump(bs, "bs", 0x02);
418 	})
419 
420 #if 0
421 	amt = sndbuf_getready(b);
422 	if (sndbuf_getfree(bs) < amt) {
423 		c->xruns++;
424 		amt = sndbuf_getfree(bs);
425 	}
426 #endif
427 	amt = sndbuf_getfree(bs);
428 	ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0;
429 
430 	amt = sndbuf_getready(b);
431 	if (amt > 0) {
432 		c->xruns++;
433 		sndbuf_dispose(b, NULL, amt);
434 	}
435 
436 	chn_wakeup(c);
437 
438 	return ret;
439 }
440 
441 void
442 chn_rdupdate(struct pcm_channel *c)
443 {
444 	int ret;
445 
446 	CHN_LOCKASSERT(c);
447 	KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel"));
448 
449 	if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED))
450 		return;
451 	chn_trigger(c, PCMTRIG_EMLDMARD);
452 	chn_dmaupdate(c);
453 	ret = chn_rdfeed(c);
454 	DEB(if (ret)
455 		kprintf("chn_rdfeed: %d\n", ret);)
456 }
457 
458 /* read interrupt routine. Must be called with interrupts blocked. */
459 static void
460 chn_rdintr(struct pcm_channel *c)
461 {
462 	int ret;
463 
464 	CHN_LOCKASSERT(c);
465 	/* tell the driver to update the primary buffer if non-dma */
466 	chn_trigger(c, PCMTRIG_EMLDMARD);
467 	/* update pointers in primary buffer */
468 	chn_dmaupdate(c);
469 	/* ...and feed from primary to secondary */
470 	ret = chn_rdfeed(c);
471 }
472 
473 /*
474  * user read routine - trigger if necessary, uiomove data from secondary buffer
475  * if blocking, sleep, rinse and repeat.
476  *
477  * called externally, so must handle locking
478  */
479 
480 int
481 chn_read(struct pcm_channel *c, struct uio *buf, int ioflags)
482 {
483 	int		ret, timeout, sz, count;
484 	int nbio;
485 	struct snd_dbuf       *bs = c->bufsoft;
486 	void *off;
487 	int t, x,togo,p;
488 
489 	CHN_LOCKASSERT(c);
490 	nbio = (c->flags & CHN_F_NBIO) || (ioflags & IO_NDELAY);
491 	if (!(c->flags & CHN_F_TRIGGERED))
492 		chn_start(c, 0);
493 
494 	ret = 0;
495 	count = hz;
496 	while (!ret && (buf->uio_resid > 0) && (count > 0)) {
497 		sz = (int)szmin(buf->uio_resid, sndbuf_getready(bs));
498 
499 		if (sz > 0) {
500 			/*
501 			 * The following assumes that the free space in
502 			 * the buffer can never be less around the
503 			 * unlock-uiomove-lock sequence.
504 			 */
505 			togo = sz;
506 			while (ret == 0 && togo> 0) {
507 				p = sndbuf_getreadyptr(bs);
508 				t = MIN(togo, sndbuf_getsize(bs) - p);
509 				off = sndbuf_getbufofs(bs, p);
510 				CHN_UNLOCK(c);
511 				ret = uiomove(off, t, buf);
512 				CHN_LOCK(c);
513 				togo -= t;
514 				x = sndbuf_dispose(bs, NULL, t);
515 			}
516 			ret = 0;
517 		} else {
518 			if (nbio) {
519 				ret = EWOULDBLOCK;
520 			} else {
521 				timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
522 				if (timeout < 1)
523 					timeout = 1;
524 	   			ret = chn_sleep(c, "pcmrd", timeout);
525 				if (ret == EWOULDBLOCK) {
526 					count -= timeout;
527 					ret = 0;
528 				} else {
529 					count = hz;
530 				}
531 
532 			}
533 		}
534 	}
535 
536 	if (count <= 0) {
537 		c->flags |= CHN_F_DEAD;
538 		kprintf("%s: record interrupt timeout, channel dead\n", c->name);
539 	}
540 
541 	return ret;
542 }
543 
544 void
545 chn_intr(struct pcm_channel *c)
546 {
547 	CHN_LOCK(c);
548 	c->interrupts++;
549 	if (c->direction == PCMDIR_PLAY)
550 		chn_wrintr(c);
551 	else
552 		chn_rdintr(c);
553 	CHN_UNLOCK(c);
554 }
555 
556 u_int32_t
557 chn_start(struct pcm_channel *c, int force)
558 {
559 	u_int32_t i, j;
560 	struct snd_dbuf *b = c->bufhard;
561 	struct snd_dbuf *bs = c->bufsoft;
562 
563 	CHN_LOCKASSERT(c);
564 	/* if we're running, or if we're prevented from triggering, bail */
565 	if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force))
566 		return EINVAL;
567 
568 	i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs);
569 	j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b);
570 	if (force || (i >= j)) {
571 		c->flags |= CHN_F_TRIGGERED;
572 		/*
573 		 * if we're starting because a vchan started, don't feed any data
574 		 * or it becomes impossible to start vchans synchronised with the
575 		 * first one.  the hardbuf should be empty so we top it up with
576 		 * silence to give it something to chew.  the real data will be
577 		 * fed at the first irq.
578 		 */
579 		if (c->direction == PCMDIR_PLAY) {
580 			/*
581 			 * Reduce pops during playback startup.
582 			 */
583 			sndbuf_fillsilence(b);
584 			if (SLIST_EMPTY(&c->children))
585 				chn_wrfeed(c);
586 		}
587 		sndbuf_setrun(b, 1);
588 		c->xruns = 0;
589 	    	chn_trigger(c, PCMTRIG_START);
590 		return 0;
591 	}
592 
593 	return 0;
594 }
595 
596 void
597 chn_resetbuf(struct pcm_channel *c)
598 {
599 	struct snd_dbuf *b = c->bufhard;
600 	struct snd_dbuf *bs = c->bufsoft;
601 
602 	c->blocks = 0;
603 	sndbuf_reset(b);
604 	sndbuf_reset(bs);
605 }
606 
607 /*
608  * chn_sync waits until the space in the given channel goes above
609  * a threshold. The threshold is checked against fl or rl respectively.
610  * Assume that the condition can become true, do not check here...
611  */
612 int
613 chn_sync(struct pcm_channel *c, int threshold)
614 {
615     	u_long rdy;
616     	int ret;
617     	struct snd_dbuf *bs = c->bufsoft;
618 
619 	CHN_LOCKASSERT(c);
620 
621 	/* if we haven't yet started and nothing is buffered, else start*/
622 	if (!(c->flags & CHN_F_TRIGGERED)) {
623 		if (sndbuf_getready(bs) > 0) {
624 			ret = chn_start(c, 1);
625 			if (ret)
626 				return ret;
627 		} else {
628 			return 0;
629 		}
630 	}
631 
632 	for (;;) {
633 		rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
634 		if (rdy <= threshold) {
635 	    		ret = chn_sleep(c, "pcmsyn", 1);
636 	    		if (ret == ERESTART || ret == EINTR) {
637 				DEB(kprintf("chn_sync: tsleep returns %d\n", ret));
638 				return -1;
639 	    		}
640 		} else
641 			break;
642     	}
643     	return 0;
644 }
645 
646 /* called externally, handle locking */
647 int
648 chn_poll(struct pcm_channel *c, int ev, struct thread *td)
649 {
650 	int ret;
651 
652 	CHN_LOCKASSERT(c);
653     	if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED))
654 		chn_start(c, 1);
655 	ret = 0;
656 	if (chn_polltrigger(c) && chn_pollreset(c))
657 		ret = ev;
658 	return ret;
659 }
660 
661 /*
662  * chn_abort terminates a running dma transfer.  it may sleep up to 200ms.
663  * it returns the number of bytes that have not been transferred.
664  *
665  * called from: dsp_close, dsp_ioctl, with channel locked
666  */
667 int
668 chn_abort(struct pcm_channel *c)
669 {
670     	int missing = 0;
671     	struct snd_dbuf *b = c->bufhard;
672     	struct snd_dbuf *bs = c->bufsoft;
673 
674 	CHN_LOCKASSERT(c);
675 	if (!(c->flags & CHN_F_TRIGGERED))
676 		return 0;
677 	c->flags |= CHN_F_ABORTING;
678 
679 	c->flags &= ~CHN_F_TRIGGERED;
680 	/* kill the channel */
681 	chn_trigger(c, PCMTRIG_ABORT);
682 	sndbuf_setrun(b, 0);
683 	if (!(c->flags & CHN_F_VIRTUAL))
684 		chn_dmaupdate(c);
685     	missing = sndbuf_getready(bs) + sndbuf_getready(b);
686 
687 	c->flags &= ~CHN_F_ABORTING;
688 	return missing;
689 }
690 
691 /*
692  * this routine tries to flush the dma transfer. It is called
693  * on a close of a playback channel.
694  * first, if there is data in the buffer, but the dma has not yet
695  * begun, we need to start it.
696  * next, we wait for the play buffer to drain
697  * finally, we stop the dma.
698  *
699  * called from: dsp_close, not valid for record channels.
700  */
701 
702 int
703 chn_flush(struct pcm_channel *c)
704 {
705     	int ret, count, resid, resid_p;
706     	struct snd_dbuf *b = c->bufhard;
707     	struct snd_dbuf *bs = c->bufsoft;
708 
709 	CHN_LOCKASSERT(c);
710 	KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel"));
711     	DEB(kprintf("chn_flush: c->flags 0x%08x\n", c->flags));
712 
713 	/* if we haven't yet started and nothing is buffered, else start*/
714 	if (!(c->flags & CHN_F_TRIGGERED)) {
715 		if (sndbuf_getready(bs) > 0) {
716 			ret = chn_start(c, 1);
717 			if (ret)
718 				return ret;
719 		} else {
720 			return 0;
721 		}
722 	}
723 
724 	c->flags |= CHN_F_CLOSING;
725 	resid = sndbuf_getready(bs) + sndbuf_getready(b);
726 	resid_p = resid;
727 	count = 10;
728 	ret = 0;
729 	while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) {
730 		/* still pending output data. */
731 		ret = chn_sleep(c, "pcmflu", hz / 10);
732 		if (ret == EWOULDBLOCK)
733 			ret = 0;
734 		if (ret == 0) {
735 			resid = sndbuf_getready(bs) + sndbuf_getready(b);
736 			if (resid == resid_p)
737 				count--;
738 			if (resid > resid_p)
739 				DEB(printf("chn_flush: buffer length increasind %d -> %d\n", resid_p, resid));
740 			resid_p = resid;
741 		}
742    	}
743 	if (count == 0)
744 		DEB(kprintf("chn_flush: timeout, hw %d, sw %d\n",
745 			sndbuf_getready(b), sndbuf_getready(bs)));
746 
747 	c->flags &= ~CHN_F_TRIGGERED;
748 	/* kill the channel */
749 	chn_trigger(c, PCMTRIG_ABORT);
750 	sndbuf_setrun(b, 0);
751 
752     	c->flags &= ~CHN_F_CLOSING;
753     	return 0;
754 }
755 
756 int
757 fmtvalid(u_int32_t fmt, u_int32_t *fmtlist)
758 {
759 	int i;
760 
761 	for (i = 0; fmtlist[i]; i++)
762 		if (fmt == fmtlist[i])
763 			return 1;
764 	return 0;
765 }
766 
767 int
768 chn_reset(struct pcm_channel *c, u_int32_t fmt)
769 {
770 	int hwspd, r;
771 
772 	CHN_LOCKASSERT(c);
773 	c->flags &= CHN_F_RESET;
774 	c->interrupts = 0;
775 	c->xruns = 0;
776 
777 	r = CHANNEL_RESET(c->methods, c->devinfo);
778 	if (fmt != 0) {
779 #if 0
780 		hwspd = DSP_DEFAULT_SPEED;
781 		/* only do this on a record channel until feederbuilder works */
782 		if (c->direction == PCMDIR_REC)
783 			RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
784 		c->speed = hwspd;
785 #endif
786 		hwspd = chn_getcaps(c)->minspeed;
787 		c->speed = hwspd;
788 
789 		if (r == 0)
790 			r = chn_setformat(c, fmt);
791 		if (r == 0)
792 			r = chn_setspeed(c, hwspd);
793 #if 0
794 		if (r == 0)
795 			r = chn_setvolume(c, 100, 100);
796 #endif
797 	}
798 	if (r == 0)
799 		r = chn_setblocksize(c, 0, 0);
800 	if (r == 0) {
801 		chn_resetbuf(c);
802 		r = CHANNEL_RESETDONE(c->methods, c->devinfo);
803 	}
804 	return r;
805 }
806 
807 int
808 chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
809 {
810 	struct feeder_class *fc;
811 	struct snd_dbuf *b, *bs;
812 	int ret;
813 
814 	chn_lockinit(c, dir);
815 
816 	b = NULL;
817 	bs = NULL;
818 	c->devinfo = NULL;
819 	c->feeder = NULL;
820 
821 	ret = ENOMEM;
822 	b = sndbuf_create(c->dev, c->name, "primary", c);
823 	if (b == NULL)
824 		goto out;
825 	bs = sndbuf_create(c->dev, c->name, "secondary", c);
826 	if (bs == NULL)
827 		goto out;
828 
829 	CHN_LOCK(c);
830 
831 	ret = EINVAL;
832 	fc = feeder_getclass(NULL);
833 	if (fc == NULL)
834 		goto out;
835 	if (chn_addfeeder(c, fc, NULL))
836 		goto out;
837 
838 	/*
839 	 * XXX - sndbuf_setup() & sndbuf_resize() expect to be called
840 	 *	 with the channel unlocked because they are also called
841 	 *	 from driver methods that don't know about locking
842 	 */
843 	CHN_UNLOCK(c);
844 	sndbuf_setup(bs, NULL, 0);
845 	CHN_LOCK(c);
846 	c->bufhard = b;
847 	c->bufsoft = bs;
848 	c->flags = 0;
849 	c->feederflags = 0;
850 
851 	ret = ENODEV;
852 	CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() kmalloc() call */
853 	c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction);
854 	CHN_LOCK(c);
855 	if (c->devinfo == NULL)
856 		goto out;
857 
858 	ret = ENOMEM;
859 	if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0))
860 		goto out;
861 
862 	ret = chn_setdir(c, direction);
863 	if (ret)
864 		goto out;
865 
866 	ret = sndbuf_setfmt(b, AFMT_U8);
867 	if (ret)
868 		goto out;
869 
870 	ret = sndbuf_setfmt(bs, AFMT_U8);
871 	if (ret)
872 		goto out;
873 
874 	ret = chn_setvolume(c, 100, 100);
875 	if (ret)
876 		goto out;
877 
878 
879 out:
880 	CHN_UNLOCK(c);
881 	if (ret) {
882 		if (c->devinfo) {
883 			if (CHANNEL_FREE(c->methods, c->devinfo))
884 				sndbuf_free(b);
885 		}
886 		if (bs)
887 			sndbuf_destroy(bs);
888 		if (b)
889 			sndbuf_destroy(b);
890 		c->flags |= CHN_F_DEAD;
891 		chn_lockdestroy(c);
892 
893 		return ret;
894 	}
895 
896 	return 0;
897 }
898 
899 int
900 chn_kill(struct pcm_channel *c)
901 {
902     	struct snd_dbuf *b = c->bufhard;
903     	struct snd_dbuf *bs = c->bufsoft;
904 
905 	if (c->flags & CHN_F_TRIGGERED)
906 		chn_trigger(c, PCMTRIG_ABORT);
907 	while (chn_removefeeder(c) == 0);
908 	if (CHANNEL_FREE(c->methods, c->devinfo))
909 		sndbuf_free(b);
910 	c->flags |= CHN_F_DEAD;
911 	sndbuf_destroy(bs);
912 	sndbuf_destroy(b);
913 	chn_lockdestroy(c);
914 	return 0;
915 }
916 
917 int
918 chn_setdir(struct pcm_channel *c, int dir)
919 {
920 #if NISA > 0
921     	struct snd_dbuf *b = c->bufhard;
922 #endif
923 	int r;
924 
925 	CHN_LOCKASSERT(c);
926 	c->direction = dir;
927 	r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction);
928 #if NISA > 0
929 	if (!r && SND_DMA(b))
930 		sndbuf_dmasetdir(b, c->direction);
931 #endif
932 	return r;
933 }
934 
935 int
936 chn_setvolume(struct pcm_channel *c, int left, int right)
937 {
938 	CHN_LOCKASSERT(c);
939 	/* should add a feeder for volume changing if channel returns -1 */
940 	if (left > 100)
941 		left = 100;
942 	if (left < 0)
943 		left = 0;
944 	if (right > 100)
945 		right = 100;
946 	if (right < 0)
947 		right = 0;
948 	c->volume = left | (right << 8);
949 	return 0;
950 }
951 
952 static int
953 chn_tryspeed(struct pcm_channel *c, int speed)
954 {
955 	struct pcm_feeder *f;
956     	struct snd_dbuf *b = c->bufhard;
957     	struct snd_dbuf *bs = c->bufsoft;
958     	struct snd_dbuf *x;
959 	int r, delta;
960 
961 	CHN_LOCKASSERT(c);
962 	DEB(kprintf("setspeed, channel %s\n", c->name));
963 	DEB(kprintf("want speed %d, ", speed));
964 	if (speed <= 0)
965 		return EINVAL;
966 	if (CANCHANGE(c)) {
967 		r = 0;
968 		c->speed = speed;
969 		sndbuf_setspd(bs, speed);
970 		RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
971 		DEB(kprintf("try speed %d, ", speed));
972 		sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed));
973 		DEB(kprintf("got speed %d\n", sndbuf_getspd(b)));
974 
975 		delta = sndbuf_getspd(b) - sndbuf_getspd(bs);
976 		if (delta < 0)
977 			delta = -delta;
978 
979 		c->feederflags &= ~(1 << FEEDER_RATE);
980 		/*
981 		 * Used to be 500. It was too big!
982 		 */
983 		if (delta > 25)
984 			c->feederflags |= 1 << FEEDER_RATE;
985 		else
986 			sndbuf_setspd(bs, sndbuf_getspd(b));
987 
988 		r = chn_buildfeeder(c);
989 		DEB(kprintf("r = %d\n", r));
990 		if (r)
991 			goto out;
992 
993 		r = chn_setblocksize(c, 0, 0);
994 		if (r)
995 			goto out;
996 
997 		if (!(c->feederflags & (1 << FEEDER_RATE)))
998 			goto out;
999 
1000 		r = EINVAL;
1001 		f = chn_findfeeder(c, FEEDER_RATE);
1002 		DEB(kprintf("feedrate = %p\n", f));
1003 		if (f == NULL)
1004 			goto out;
1005 
1006 		x = (c->direction == PCMDIR_REC)? b : bs;
1007 		r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x));
1008 		DEB(kprintf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r));
1009 		if (r)
1010 			goto out;
1011 
1012 		x = (c->direction == PCMDIR_REC)? bs : b;
1013 		r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x));
1014 		DEB(kprintf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r));
1015 out:
1016 		if (!r)
1017 			r = CHANNEL_SETFORMAT(c->methods, c->devinfo,
1018 							sndbuf_getfmt(b));
1019 		if (!r)
1020 			sndbuf_setfmt(bs, c->format);
1021 		DEB(kprintf("setspeed done, r = %d\n", r));
1022 		return r;
1023 	} else
1024 		return EINVAL;
1025 }
1026 
1027 int
1028 chn_setspeed(struct pcm_channel *c, int speed)
1029 {
1030 	int r, oldspeed = c->speed;
1031 
1032 	r = chn_tryspeed(c, speed);
1033 	if (r) {
1034 		DEB(kprintf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
1035 		r = chn_tryspeed(c, oldspeed);
1036 	}
1037 	return r;
1038 }
1039 
1040 static int
1041 chn_tryformat(struct pcm_channel *c, u_int32_t fmt)
1042 {
1043 	struct snd_dbuf *b = c->bufhard;
1044 	struct snd_dbuf *bs = c->bufsoft;
1045 	int r;
1046 
1047 	CHN_LOCKASSERT(c);
1048 	if (CANCHANGE(c)) {
1049 		DEB(kprintf("want format %d\n", fmt));
1050 		c->format = fmt;
1051 		r = chn_buildfeeder(c);
1052 		if (r == 0) {
1053 			sndbuf_setfmt(bs, c->format);
1054 			chn_resetbuf(c);
1055 			r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b));
1056 			if (r == 0)
1057 				r = chn_tryspeed(c, c->speed);
1058 		}
1059 		return r;
1060 	} else
1061 		return EINVAL;
1062 }
1063 
1064 int
1065 chn_setformat(struct pcm_channel *c, u_int32_t fmt)
1066 {
1067 	u_int32_t oldfmt = c->format;
1068 	int r;
1069 
1070 	r = chn_tryformat(c, fmt);
1071 	if (r) {
1072 		DEB(kprintf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
1073 		chn_tryformat(c, oldfmt);
1074 	}
1075 	return r;
1076 }
1077 
1078 /*
1079  * given a bufsz value, round it to a power of 2 in the min-max range
1080  * XXX only works if min and max are powers of 2
1081  */
1082 static int
1083 round_bufsz(int bufsz, int min, int max)
1084 {
1085 	int tmp = min * 2;
1086 
1087 	KASSERT((min & (min-1)) == 0, ("min %d must be power of 2", min));
1088 	KASSERT((max & (max-1)) == 0, ("max %d must be power of 2", max));
1089 	while (tmp <= bufsz)
1090 		tmp <<= 1;
1091 	tmp >>= 1;
1092 	if (tmp > max)
1093 		tmp = max;
1094 	return tmp;
1095 }
1096 
1097 /*
1098  * set the channel's blocksize both for soft and hard buffers.
1099  *
1100  * blksz should be a power of 2 between 2**4 and 2**16 -- it is useful
1101  * that it has the same value for both bufsoft and bufhard.
1102  * blksz == -1 computes values according to a target irq rate.
1103  * blksz == 0 reuses previous values if available, otherwise
1104  * behaves as for -1
1105  *
1106  * blkcnt is set by the user, between 2 and (2**17)/blksz for bufsoft,
1107  * but should be a power of 2 for bufhard to simplify life to low
1108  * level drivers.
1109  * Note, for the rec channel a large blkcnt is ok,
1110  * but for the play channel we want blksz as small as possible to keep
1111  * the delay small, because routines in the write path always try to
1112  * keep bufhard full.
1113  *
1114  * Unless we have good reason to, use the values suggested by the caller.
1115  */
1116 int
1117 chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
1118 {
1119 	struct snd_dbuf *b = c->bufhard;
1120 	struct snd_dbuf *bs = c->bufsoft;
1121 	int irqhz, ret, maxsz, maxsize, reqblksz;
1122 
1123 	CHN_LOCKASSERT(c);
1124 	if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) {
1125 		KASSERT(sndbuf_getsize(bs) ==  0 ||
1126 		    sndbuf_getsize(bs) >= sndbuf_getsize(b),
1127 		    ("%s(%s): bufsoft size %d < bufhard size %d", __func__,
1128 		    c->name, sndbuf_getsize(bs), sndbuf_getsize(b)));
1129 		return EINVAL;
1130 	}
1131 	c->flags |= CHN_F_SETBLOCKSIZE;
1132 
1133 	ret = 0;
1134 	DEB(kprintf("%s(%d, %d)\n", __func__, blkcnt, blksz));
1135 	if (blksz == 0 || blksz == -1) { /* let the driver choose values */
1136 		if (blksz == -1)	/* delete previous values */
1137 			c->flags &= ~CHN_F_HAS_SIZE;
1138 		if (!(c->flags & CHN_F_HAS_SIZE)) { /* no previous value */
1139 			/*
1140 			 * compute a base blksz according to the target irq
1141 			 * rate, then round to a suitable power of 2
1142 			 * in the range 16.. 2^17/2.
1143 			 * Finally compute a suitable blkcnt.
1144 			 */
1145 			blksz = round_bufsz( (sndbuf_getbps(bs) *
1146 				sndbuf_getspd(bs)) / chn_targetirqrate,
1147 				16, CHN_2NDBUFMAXSIZE / 2);
1148 			blkcnt = CHN_2NDBUFMAXSIZE / blksz;
1149 		} else { /* use previously defined value */
1150 			blkcnt = sndbuf_getblkcnt(bs);
1151 			blksz = sndbuf_getblksz(bs);
1152 		}
1153 	} else {
1154 		/*
1155 		 * use supplied values if reasonable. Note that here we
1156 		 * might have blksz which is not a power of 2 if the
1157 		 * ioctl() to compute it allows such values.
1158 		 */
1159 		ret = EINVAL;
1160 		if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
1161 			goto out;
1162 		ret = 0;
1163 		c->flags |= CHN_F_HAS_SIZE;
1164 	}
1165 
1166 	reqblksz = blksz;
1167 	if (reqblksz < sndbuf_getbps(bs))
1168 		reqblksz = sndbuf_getbps(bs);
1169 	if (reqblksz % sndbuf_getbps(bs))
1170 		reqblksz -= reqblksz % sndbuf_getbps(bs);
1171 
1172 	/* adjust for different hw format/speed */
1173 	/*
1174 	 * Now compute the approx irq rate for the given (soft) blksz,
1175 	 * reduce to the acceptable range and compute a corresponding blksz
1176 	 * for the hard buffer. Then set the channel's blocksize and
1177 	 * corresponding hardbuf value. The number of blocks used should
1178 	 * be set by the device-specific routine. In fact, even the
1179 	 * call to sndbuf_setblksz() should not be here! XXX
1180 	 */
1181 
1182 	irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz;
1183 	RANGE(irqhz, 16, 512);
1184 
1185 	maxsz = sndbuf_getmaxsize(b);
1186 	if (maxsz == 0) /* virtual channels don't appear to allocate bufhard */
1187 		maxsz = CHN_2NDBUFMAXSIZE;
1188 	blksz = round_bufsz( (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz,
1189 			16, maxsz / 2);
1190 
1191 	/* Increase the size of bufsoft if before increasing bufhard. */
1192 	maxsize = sndbuf_getsize(b);
1193 	if (sndbuf_getsize(bs) > maxsize)
1194 		maxsize = sndbuf_getsize(bs);
1195 	if (reqblksz * blkcnt > maxsize)
1196 		maxsize = reqblksz * blkcnt;
1197 	if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
1198 		ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
1199 		if (ret)
1200 			goto out1;
1201 	}
1202 
1203 	CHN_UNLOCK(c);
1204 	sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
1205 	CHN_LOCK(c);
1206 
1207 	/* Decrease the size of bufsoft after decreasing bufhard. */
1208 	maxsize = sndbuf_getsize(b);
1209 	if (reqblksz * blkcnt > maxsize)
1210 		maxsize = reqblksz * blkcnt;
1211 	if (maxsize > sndbuf_getsize(bs))
1212 		kprintf("Danger! %s bufsoft size increasing from %d to %d after CHANNEL_SETBLOCKSIZE()\n",
1213 		    c->name, sndbuf_getsize(bs), maxsize);
1214 	if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
1215 		ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
1216 		if (ret)
1217 			goto out1;
1218 	}
1219 
1220 	chn_resetbuf(c);
1221 out1:
1222 	KASSERT(sndbuf_getsize(bs) ==  0 ||
1223 	    sndbuf_getsize(bs) >= sndbuf_getsize(b),
1224 	    ("%s(%s): bufsoft size %d < bufhard size %d, reqblksz=%d blksz=%d maxsize=%d blkcnt=%d",
1225 	    __func__, c->name, sndbuf_getsize(bs), sndbuf_getsize(b), reqblksz,
1226 	    blksz, maxsize, blkcnt));
1227 out:
1228 	c->flags &= ~CHN_F_SETBLOCKSIZE;
1229 #if 0
1230 	if (1) {
1231 		static uint32_t kk = 0;
1232 		printf("%u: b %d/%d/%d : (%d)%d/0x%0x | bs %d/%d/%d : (%d)%d/0x%0x\n", ++kk,
1233 			sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b),
1234 			sndbuf_getbps(b),
1235 			sndbuf_getspd(b), sndbuf_getfmt(b),
1236 			sndbuf_getsize(bs), sndbuf_getblksz(bs), sndbuf_getblkcnt(bs),
1237 			sndbuf_getbps(bs),
1238 			sndbuf_getspd(bs), sndbuf_getfmt(bs));
1239 		if (sndbuf_getsize(b) % sndbuf_getbps(b) ||
1240 				sndbuf_getblksz(b) % sndbuf_getbps(b) ||
1241 				sndbuf_getsize(bs) % sndbuf_getbps(bs) ||
1242 				sndbuf_getblksz(b) % sndbuf_getbps(b)) {
1243 			printf("%u: bps/blksz alignment screwed!\n", kk);
1244 		}
1245 	}
1246 #endif
1247 	return ret;
1248 }
1249 
1250 int
1251 chn_trigger(struct pcm_channel *c, int go)
1252 {
1253 #if NISA > 0
1254     	struct snd_dbuf *b = c->bufhard;
1255 #endif
1256 	int ret;
1257 
1258 	CHN_LOCKASSERT(c);
1259 #if NISA > 0
1260 	if (SND_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
1261 		sndbuf_dmabounce(b);
1262 #endif
1263 	ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
1264 
1265 	return ret;
1266 }
1267 
1268 int
1269 chn_getptr(struct pcm_channel *c)
1270 {
1271 #if 0
1272 	int hwptr;
1273 	int a = (1 << c->align) - 1;
1274 
1275 	CHN_LOCKASSERT(c);
1276 	hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
1277 	/* don't allow unaligned values in the hwa ptr */
1278 #if 1
1279 	hwptr &= ~a ; /* Apply channel align mask */
1280 #endif
1281 	hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
1282 	return hwptr;
1283 #endif
1284 	int hwptr;
1285 
1286 	CHN_LOCKASSERT(c);
1287 	hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
1288 	return (hwptr - (hwptr % sndbuf_getbps(c->bufhard)));
1289 }
1290 
1291 struct pcmchan_caps *
1292 chn_getcaps(struct pcm_channel *c)
1293 {
1294 	CHN_LOCKASSERT(c);
1295 	return CHANNEL_GETCAPS(c->methods, c->devinfo);
1296 }
1297 
1298 u_int32_t
1299 chn_getformats(struct pcm_channel *c)
1300 {
1301 	u_int32_t *fmtlist, fmts;
1302 	int i;
1303 
1304 	fmtlist = chn_getcaps(c)->fmtlist;
1305 	fmts = 0;
1306 	for (i = 0; fmtlist[i]; i++)
1307 		fmts |= fmtlist[i];
1308 
1309 	/* report software-supported formats */
1310 	if (report_soft_formats)
1311 		fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE|
1312 		    AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE|
1313 		    AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE|
1314 		    AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8;
1315 
1316 	return fmts;
1317 }
1318 
1319 static int
1320 chn_buildfeeder(struct pcm_channel *c)
1321 {
1322 	struct feeder_class *fc;
1323 	struct pcm_feederdesc desc;
1324 	u_int32_t tmp[2], type, flags, hwfmt, *fmtlist;
1325 	int err;
1326 
1327 	CHN_LOCKASSERT(c);
1328 	while (chn_removefeeder(c) == 0);
1329 	KASSERT((c->feeder == NULL), ("feeder chain not empty"));
1330 
1331 	c->align = sndbuf_getalign(c->bufsoft);
1332 
1333 	if (SLIST_EMPTY(&c->children)) {
1334 		fc = feeder_getclass(NULL);
1335 		KASSERT(fc != NULL, ("can't find root feeder"));
1336 
1337 		err = chn_addfeeder(c, fc, NULL);
1338 		if (err) {
1339 			DEB(kprintf("can't add root feeder, err %d\n", err));
1340 
1341 			return err;
1342 		}
1343 		c->feeder->desc->out = c->format;
1344 	} else {
1345 		if (c->flags & CHN_F_HAS_VCHAN) {
1346 			desc.type = FEEDER_MIXER;
1347 			desc.in = 0;
1348 		} else {
1349 			DEB(printf("can't decide which feeder type to use!\n"));
1350 			return EOPNOTSUPP;
1351 		}
1352 		desc.out = c->format;
1353 		desc.flags = 0;
1354 		fc = feeder_getclass(&desc);
1355 		if (fc == NULL) {
1356 			DEB(kprintf("can't find vchan feeder\n"));
1357 
1358 			return EOPNOTSUPP;
1359 		}
1360 
1361 		err = chn_addfeeder(c, fc, &desc);
1362 		if (err) {
1363 			DEB(kprintf("can't add vchan feeder, err %d\n", err));
1364 
1365 			return err;
1366 		}
1367 	}
1368 	c->feederflags &= ~(1 << FEEDER_VOLUME);
1369 	if (c->direction == PCMDIR_PLAY &&
1370 			!(c->flags & CHN_F_VIRTUAL) &&
1371 			c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTPCMVOL) &&
1372 			c->parentsnddev->mixer_dev)
1373 		c->feederflags |= 1 << FEEDER_VOLUME;
1374 	flags = c->feederflags;
1375 	fmtlist = chn_getcaps(c)->fmtlist;
1376 
1377 	DEB(kprintf("feederflags %x\n", flags));
1378 
1379 	for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) {
1380 		if (flags & (1 << type)) {
1381 			desc.type = type;
1382 			desc.in = 0;
1383 			desc.out = 0;
1384 			desc.flags = 0;
1385 			DEB(kprintf("find feeder type %d, ", type));
1386 			fc = feeder_getclass(&desc);
1387 			DEB(kprintf("got %p\n", fc));
1388 			if (fc == NULL) {
1389 				DEB(kprintf("can't find required feeder type %d\n", type));
1390 
1391 				return EOPNOTSUPP;
1392 			}
1393 
1394  			DEB(kprintf("build fmtchain from 0x%08x to 0x%08x: ", c->feeder->desc->out, fc->desc->in));
1395 			tmp[0] = fc->desc->in;
1396 			tmp[1] = 0;
1397 			if (chn_fmtchain(c, tmp) == 0) {
1398 				DEB(printf("failed\n"));
1399 
1400 				return ENODEV;
1401 			}
1402  			DEB(printf("ok\n"));
1403 
1404 			err = chn_addfeeder(c, fc, fc->desc);
1405 			if (err) {
1406 				DEB(kprintf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err));
1407 
1408 				return err;
1409 			}
1410 			DEB(kprintf("added feeder %p, output 0x%x\n", fc, c->feeder->desc->out));
1411 		}
1412 	}
1413 
1414  	if (c->direction == PCMDIR_REC) {
1415 	 	tmp[0] = c->format;
1416  		tmp[1] = 0;
1417  		hwfmt = chn_fmtchain(c, tmp);
1418  	} else
1419  		hwfmt = chn_fmtchain(c, fmtlist);
1420 
1421 	if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) {
1422 		DEB(printf("Invalid hardware format: 0x%08x\n", hwfmt));
1423 		return ENODEV;
1424 	}
1425 
1426 	sndbuf_setfmt(c->bufhard, hwfmt);
1427 
1428 	if ((flags & (1 << FEEDER_VOLUME))) {
1429 		struct dev_ioctl_args map;
1430 		u_int32_t parent = SOUND_MIXER_NONE;
1431 		int vol, left, right;
1432 
1433 		vol = 100 | (100 << 8);
1434 
1435 		CHN_UNLOCK(c);
1436 		/*
1437 		 * XXX This is ugly! The way mixer subs being so secretive
1438 		 * about its own internals force us to use this silly
1439 		 * monkey trick.
1440 		 */
1441 		map.a_head.a_dev = c->parentsnddev->mixer_dev;
1442 		map.a_cmd = MIXER_READ(SOUND_MIXER_PCM);
1443 		map.a_data = (caddr_t)&vol;
1444 		map.a_fflag = -1;
1445 		map.a_cred = NULL;
1446 		map.a_sysmsg = NULL;
1447 		if (mixer_ioctl(&map) != 0)
1448 			device_printf(c->dev, "Soft PCM Volume: Failed to read default value\n");
1449 		left = vol & 0x7f;
1450 		right = (vol >> 8) & 0x7f;
1451 		if (c->parentsnddev != NULL &&
1452 		    c->parentsnddev->mixer_dev != NULL &&
1453 		    c->parentsnddev->mixer_dev->si_drv1 != NULL)
1454 			parent = mix_getparent(
1455 			    c->parentsnddev->mixer_dev->si_drv1,
1456 			    SOUND_MIXER_PCM);
1457 		if (parent != SOUND_MIXER_NONE) {
1458 			vol = 100 | (100 << 8);
1459 			map.a_head.a_dev = c->parentsnddev->mixer_dev;
1460 			map.a_cmd = MIXER_READ(parent);
1461 			map.a_data = (caddr_t)&vol;
1462 			map.a_fflag = -1;
1463 			map.a_cred = NULL;
1464 			if (mixer_ioctl(&map) != 0)
1465 				device_printf(c->dev, "Soft Volume: Failed to read parent default value\n");
1466 			left = (left * (vol & 0x7f)) / 100;
1467 			right = (right * ((vol >> 8) & 0x7f)) / 100;
1468 		}
1469 
1470 		CHN_LOCK(c);
1471 		chn_setvolume(c, left, right);
1472 	}
1473 
1474 	return 0;
1475 }
1476 
1477 int
1478 chn_notify(struct pcm_channel *c, u_int32_t flags)
1479 {
1480 	struct pcmchan_children *pce;
1481 	struct pcm_channel *child;
1482 	int run;
1483 
1484 	CHN_LOCK(c);
1485 
1486 	if (SLIST_EMPTY(&c->children)) {
1487 		CHN_UNLOCK(c);
1488 		return ENODEV;
1489 	}
1490 
1491 	run = (c->flags & CHN_F_TRIGGERED)? 1 : 0;
1492 	/*
1493 	 * if the hwchan is running, we can't change its rate, format or
1494 	 * blocksize
1495 	 */
1496 	if (run)
1497 		flags &= CHN_N_VOLUME | CHN_N_TRIGGER;
1498 
1499 	if (flags & CHN_N_RATE) {
1500 		/*
1501 		 * we could do something here, like scan children and decide on
1502 		 * the most appropriate rate to mix at, but we don't for now
1503 		 */
1504 	}
1505 	if (flags & CHN_N_FORMAT) {
1506 		/*
1507 		 * we could do something here, like scan children and decide on
1508 		 * the most appropriate mixer feeder to use, but we don't for now
1509 		 */
1510 	}
1511 	if (flags & CHN_N_VOLUME) {
1512 		/*
1513 		 * we could do something here but we don't for now
1514 		 */
1515 	}
1516 	if (flags & CHN_N_BLOCKSIZE) {
1517 		int blksz;
1518 		/*
1519 		 * scan the children, find the lowest blocksize and use that
1520 		 * for the hard blocksize
1521 		 */
1522 		blksz = sndbuf_getmaxsize(c->bufhard) / 2;
1523 		SLIST_FOREACH(pce, &c->children, link) {
1524 			child = pce->channel;
1525 			CHN_LOCK(child);
1526 			if (sndbuf_getblksz(child->bufhard) < blksz)
1527 				blksz = sndbuf_getblksz(child->bufhard);
1528 			CHN_UNLOCK(child);
1529 		}
1530 		chn_setblocksize(c, 2, blksz);
1531 	}
1532 	if (flags & CHN_N_TRIGGER) {
1533 		int nrun;
1534 		/*
1535 		 * scan the children, and figure out if any are running
1536 		 * if so, we need to be running, otherwise we need to be stopped
1537 		 * if we aren't in our target sstate, move to it
1538 		 */
1539 		nrun = 0;
1540 		SLIST_FOREACH(pce, &c->children, link) {
1541 			child = pce->channel;
1542 			CHN_LOCK(child);
1543 			if (child->flags & CHN_F_TRIGGERED)
1544 				nrun = 1;
1545 			CHN_UNLOCK(child);
1546 		}
1547 		if (nrun && !run)
1548 			chn_start(c, 1);
1549 		if (!nrun && run)
1550 			chn_abort(c);
1551 	}
1552 	CHN_UNLOCK(c);
1553 	return 0;
1554 }
1555 
1556 void
1557 chn_lock(struct pcm_channel *c)
1558 {
1559 	CHN_LOCK(c);
1560 }
1561 
1562 void
1563 chn_unlock(struct pcm_channel *c)
1564 {
1565 	CHN_UNLOCK(c);
1566 }
1567