channel.c revision 149953
10Sstevel@tonic-gate/*-
20Sstevel@tonic-gate * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
30Sstevel@tonic-gate * Portions Copyright by Luigi Rizzo - 1997-99
40Sstevel@tonic-gate * All rights reserved.
51544Seschrock *
61544Seschrock * Redistribution and use in source and binary forms, with or without
70Sstevel@tonic-gate * modification, are permitted provided that the following conditions
80Sstevel@tonic-gate * are met:
90Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright
100Sstevel@tonic-gate *    notice, this list of conditions and the following disclaimer.
110Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright
120Sstevel@tonic-gate *    notice, this list of conditions and the following disclaimer in the
130Sstevel@tonic-gate *    documentation and/or other materials provided with the distribution.
140Sstevel@tonic-gate *
150Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
160Sstevel@tonic-gate * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
170Sstevel@tonic-gate * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
180Sstevel@tonic-gate * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
190Sstevel@tonic-gate * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
200Sstevel@tonic-gate * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
216336Sbholler * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
220Sstevel@tonic-gate * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
239053Sjonathan.chew@sun.com * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
240Sstevel@tonic-gate * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
250Sstevel@tonic-gate * SUCH DAMAGE.
260Sstevel@tonic-gate */
270Sstevel@tonic-gate
280Sstevel@tonic-gate#include "opt_isa.h"
290Sstevel@tonic-gate
300Sstevel@tonic-gate#include <dev/sound/pcm/sound.h>
310Sstevel@tonic-gate
320Sstevel@tonic-gate#include "feeder_if.h"
330Sstevel@tonic-gate
340Sstevel@tonic-gateSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/channel.c 149953 2005-09-10 18:10:31Z netchild $");
350Sstevel@tonic-gate
360Sstevel@tonic-gate#define MIN_CHUNK_SIZE 		256	/* for uiomove etc. */
370Sstevel@tonic-gate#define	DMA_ALIGN_THRESHOLD	4
385648Ssetje#define	DMA_ALIGN_MASK		(~(DMA_ALIGN_THRESHOLD - 1))
390Sstevel@tonic-gate
400Sstevel@tonic-gate#define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED))
415648Ssetje
420Sstevel@tonic-gate/*
430Sstevel@tonic-gate#define DEB(x) x
440Sstevel@tonic-gate*/
450Sstevel@tonic-gate
460Sstevel@tonic-gatestatic int chn_targetirqrate = 32;
470Sstevel@tonic-gateTUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate);
486671Sjjc
496671Sjjcstatic int
506671Sjjcsysctl_hw_snd_targetirqrate(SYSCTL_HANDLER_ARGS)
519053Sjonathan.chew@sun.com{
529053Sjonathan.chew@sun.com	int err, val;
5312004Sjiang.liu@intel.com
549053Sjonathan.chew@sun.com	val = chn_targetirqrate;
556671Sjjc	err = sysctl_handle_int(oidp, &val, sizeof(val), req);
566671Sjjc	if (val < 16 || val > 512)
570Sstevel@tonic-gate		err = EINVAL;
580Sstevel@tonic-gate	else
590Sstevel@tonic-gate		chn_targetirqrate = val;
600Sstevel@tonic-gate
610Sstevel@tonic-gate	return err;
620Sstevel@tonic-gate}
630Sstevel@tonic-gateSYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW,
640Sstevel@tonic-gate	0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "");
650Sstevel@tonic-gatestatic int report_soft_formats = 1;
660Sstevel@tonic-gateSYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
670Sstevel@tonic-gate	&report_soft_formats, 1, "report software-emulated formats");
680Sstevel@tonic-gate
690Sstevel@tonic-gatestatic int chn_buildfeeder(struct pcm_channel *c);
700Sstevel@tonic-gate
710Sstevel@tonic-gatestatic void
720Sstevel@tonic-gatechn_lockinit(struct pcm_channel *c, int dir)
730Sstevel@tonic-gate{
740Sstevel@tonic-gate	switch(dir) {
750Sstevel@tonic-gate	case PCMDIR_PLAY:
760Sstevel@tonic-gate		c->lock = snd_mtxcreate(c->name, "pcm play channel");
770Sstevel@tonic-gate		break;
780Sstevel@tonic-gate	case PCMDIR_REC:
790Sstevel@tonic-gate		c->lock = snd_mtxcreate(c->name, "pcm record channel");
800Sstevel@tonic-gate		break;
819940SVikram.Hegde@Sun.COM	case PCMDIR_VIRTUAL:
820Sstevel@tonic-gate		c->lock = snd_mtxcreate(c->name, "pcm virtual play channel");
830Sstevel@tonic-gate		break;
840Sstevel@tonic-gate	case 0:
850Sstevel@tonic-gate		c->lock = snd_mtxcreate(c->name, "pcm fake channel");
860Sstevel@tonic-gate		break;
870Sstevel@tonic-gate	}
880Sstevel@tonic-gate}
890Sstevel@tonic-gate
900Sstevel@tonic-gatestatic void
910Sstevel@tonic-gatechn_lockdestroy(struct pcm_channel *c)
920Sstevel@tonic-gate{
930Sstevel@tonic-gate	snd_mtxfree(c->lock);
940Sstevel@tonic-gate}
950Sstevel@tonic-gate
960Sstevel@tonic-gatestatic int
970Sstevel@tonic-gatechn_polltrigger(struct pcm_channel *c)
980Sstevel@tonic-gate{
990Sstevel@tonic-gate	struct snd_dbuf *bs = c->bufsoft;
1000Sstevel@tonic-gate	unsigned amt, lim;
1010Sstevel@tonic-gate
1020Sstevel@tonic-gate	CHN_LOCKASSERT(c);
1030Sstevel@tonic-gate	if (c->flags & CHN_F_MAPPED) {
1040Sstevel@tonic-gate		if (sndbuf_getprevblocks(bs) == 0)
1050Sstevel@tonic-gate			return 1;
1060Sstevel@tonic-gate		else
1070Sstevel@tonic-gate			return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0;
1080Sstevel@tonic-gate	} else {
1090Sstevel@tonic-gate		amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
1100Sstevel@tonic-gate#if 0
1110Sstevel@tonic-gate		lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
1120Sstevel@tonic-gate#endif
1130Sstevel@tonic-gate		lim = 1;
1140Sstevel@tonic-gate		return (amt >= lim)? 1 : 0;
1150Sstevel@tonic-gate	}
1160Sstevel@tonic-gate	return 0;
1170Sstevel@tonic-gate}
1180Sstevel@tonic-gate
1190Sstevel@tonic-gatestatic int
1200Sstevel@tonic-gatechn_pollreset(struct pcm_channel *c)
1210Sstevel@tonic-gate{
1225648Ssetje	struct snd_dbuf *bs = c->bufsoft;
1230Sstevel@tonic-gate
1240Sstevel@tonic-gate	CHN_LOCKASSERT(c);
1250Sstevel@tonic-gate	sndbuf_updateprevtotal(bs);
1260Sstevel@tonic-gate	return 1;
1275648Ssetje}
1280Sstevel@tonic-gate
1290Sstevel@tonic-gatestatic void
1300Sstevel@tonic-gatechn_wakeup(struct pcm_channel *c)
1310Sstevel@tonic-gate{
1320Sstevel@tonic-gate	struct snd_dbuf *bs = c->bufsoft;
1330Sstevel@tonic-gate	struct pcmchan_children *pce;
1340Sstevel@tonic-gate
1350Sstevel@tonic-gate	CHN_LOCKASSERT(c);
1360Sstevel@tonic-gate	if (SLIST_EMPTY(&c->children)) {
1370Sstevel@tonic-gate		if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))
1385648Ssetje			selwakeuppri(sndbuf_getsel(bs), PRIBIO);
1390Sstevel@tonic-gate	} else {
1400Sstevel@tonic-gate		SLIST_FOREACH(pce, &c->children, link) {
1410Sstevel@tonic-gate			CHN_LOCK(pce->channel);
1420Sstevel@tonic-gate			chn_wakeup(pce->channel);
1430Sstevel@tonic-gate			CHN_UNLOCK(pce->channel);
1440Sstevel@tonic-gate		}
1450Sstevel@tonic-gate	}
1460Sstevel@tonic-gate
1470Sstevel@tonic-gate	wakeup(bs);
1480Sstevel@tonic-gate}
1490Sstevel@tonic-gate
1500Sstevel@tonic-gatestatic int
1510Sstevel@tonic-gatechn_sleep(struct pcm_channel *c, char *str, int timeout)
1520Sstevel@tonic-gate{
1530Sstevel@tonic-gate    	struct snd_dbuf *bs = c->bufsoft;
1540Sstevel@tonic-gate	int ret;
1550Sstevel@tonic-gate
1560Sstevel@tonic-gate	CHN_LOCKASSERT(c);
1570Sstevel@tonic-gate#ifdef USING_MUTEX
1580Sstevel@tonic-gate	ret = msleep(bs, c->lock, PRIBIO | PCATCH, str, timeout);
1590Sstevel@tonic-gate#else
1600Sstevel@tonic-gate	ret = tsleep(bs, PRIBIO | PCATCH, str, timeout);
1610Sstevel@tonic-gate#endif
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate	return ret;
1640Sstevel@tonic-gate}
1650Sstevel@tonic-gate
1660Sstevel@tonic-gate/*
1670Sstevel@tonic-gate * chn_dmaupdate() tracks the status of a dma transfer,
1680Sstevel@tonic-gate * updating pointers. It must be called at spltty().
1690Sstevel@tonic-gate */
1700Sstevel@tonic-gate
1710Sstevel@tonic-gatestatic unsigned int
1720Sstevel@tonic-gatechn_dmaupdate(struct pcm_channel *c)
1730Sstevel@tonic-gate{
1740Sstevel@tonic-gate	struct snd_dbuf *b = c->bufhard;
1750Sstevel@tonic-gate	unsigned int delta, old, hwptr, amt;
1760Sstevel@tonic-gate
1770Sstevel@tonic-gate	KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0"));
1780Sstevel@tonic-gate	CHN_LOCKASSERT(c);
1790Sstevel@tonic-gate
1800Sstevel@tonic-gate	old = sndbuf_gethwptr(b);
1810Sstevel@tonic-gate	hwptr = chn_getptr(c);
1820Sstevel@tonic-gate	delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b);
1830Sstevel@tonic-gate	sndbuf_sethwptr(b, hwptr);
1840Sstevel@tonic-gate
1850Sstevel@tonic-gate	DEB(
1860Sstevel@tonic-gate	if (delta >= ((sndbuf_getsize(b) * 15) / 16)) {
1870Sstevel@tonic-gate		if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING)))
1880Sstevel@tonic-gate			device_printf(c->dev, "hwptr went backwards %d -> %d\n", old, hwptr);
1890Sstevel@tonic-gate	}
1900Sstevel@tonic-gate	);
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate	if (c->direction == PCMDIR_PLAY) {
1930Sstevel@tonic-gate		amt = MIN(delta, sndbuf_getready(b));
1940Sstevel@tonic-gate		if (amt > 0)
1950Sstevel@tonic-gate			sndbuf_dispose(b, NULL, amt);
1960Sstevel@tonic-gate	} else {
1970Sstevel@tonic-gate		amt = MIN(delta, sndbuf_getfree(b));
1980Sstevel@tonic-gate		if (amt > 0)
1998215SVikram.Hegde@Sun.COM		       sndbuf_acquire(b, NULL, amt);
2008215SVikram.Hegde@Sun.COM	}
2010Sstevel@tonic-gate
2020Sstevel@tonic-gate	return delta;
2030Sstevel@tonic-gate}
2040Sstevel@tonic-gate
2050Sstevel@tonic-gatevoid
2060Sstevel@tonic-gatechn_wrupdate(struct pcm_channel *c)
2070Sstevel@tonic-gate{
2080Sstevel@tonic-gate	int ret;
2090Sstevel@tonic-gate
2100Sstevel@tonic-gate	CHN_LOCKASSERT(c);
2110Sstevel@tonic-gate	KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel"));
2120Sstevel@tonic-gate
2130Sstevel@tonic-gate	if ((c->flags & (CHN_F_MAPPED | CHN_F_VIRTUAL)) || !(c->flags & CHN_F_TRIGGERED))
2140Sstevel@tonic-gate		return;
2150Sstevel@tonic-gate	chn_dmaupdate(c);
2160Sstevel@tonic-gate	ret = chn_wrfeed(c);
2170Sstevel@tonic-gate	/* tell the driver we've updated the primary buffer */
2180Sstevel@tonic-gate	chn_trigger(c, PCMTRIG_EMLDMAWR);
2190Sstevel@tonic-gate	DEB(if (ret)
2200Sstevel@tonic-gate		printf("chn_wrupdate: chn_wrfeed returned %d\n", ret);)
2210Sstevel@tonic-gate
2227656SSherry.Moore@Sun.COM}
2233446Smrj
2240Sstevel@tonic-gateint
2250Sstevel@tonic-gatechn_wrfeed(struct pcm_channel *c)
2260Sstevel@tonic-gate{
2275648Ssetje    	struct snd_dbuf *b = c->bufhard;
2280Sstevel@tonic-gate    	struct snd_dbuf *bs = c->bufsoft;
2290Sstevel@tonic-gate	unsigned int ret, amt;
2303446Smrj
2313446Smrj	CHN_LOCKASSERT(c);
2323446Smrj#if 0
2335648Ssetje    	DEB(
2343446Smrj	if (c->flags & CHN_F_CLOSING) {
2356336Sbholler		sndbuf_dump(b, "b", 0x02);
2363446Smrj		sndbuf_dump(bs, "bs", 0x02);
2375648Ssetje	})
2386336Sbholler#endif
2396336Sbholler
2406336Sbholler	if (c->flags & CHN_F_MAPPED)
2413446Smrj		sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
2423446Smrj
2439053Sjonathan.chew@sun.com	amt = sndbuf_getfree(b);
2449053Sjonathan.chew@sun.com	KASSERT(amt <= sndbuf_getsize(bs),
2453446Smrj	    ("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name,
2463446Smrj	   amt, sndbuf_getsize(bs), c->flags));
2473446Smrj
2483446Smrj	if (SLIST_EMPTY(&c->children)) {
2493446Smrj		/*
2503446Smrj		 * Hardware channel
2515648Ssetje		 */
2525648Ssetje		if (sndbuf_getready(bs) < amt)
2533446Smrj			c->xruns++;
2540Sstevel@tonic-gate		ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC;
2550Sstevel@tonic-gate	} else {
2560Sstevel@tonic-gate		/*
2570Sstevel@tonic-gate		 * vchan
2580Sstevel@tonic-gate		 */
2590Sstevel@tonic-gate		if (amt > 0) {
2600Sstevel@tonic-gate			ret = sndbuf_feed(bs, b, c, c->feeder, amt);
261			/*
262			 * Possible vchan xruns. There should be no empty space
263			 * left in buffer.
264			 */
265			if (sndbuf_getfree(b) > 0)
266				c->xruns++;
267		} else
268			ret = ENOSPC;
269	}
270
271	if (ret == 0 && sndbuf_getfree(b) < amt)
272		chn_wakeup(c);
273
274	return ret;
275}
276
277static void
278chn_wrintr(struct pcm_channel *c)
279{
280	int ret;
281
282	CHN_LOCKASSERT(c);
283	/* update pointers in primary buffer */
284	chn_dmaupdate(c);
285	/* ...and feed from secondary to primary */
286	ret = chn_wrfeed(c);
287	/* tell the driver we've updated the primary buffer */
288	chn_trigger(c, PCMTRIG_EMLDMAWR);
289	DEB(if (ret)
290		printf("chn_wrintr: chn_wrfeed returned %d\n", ret);)
291}
292
293/*
294 * user write routine - uiomove data into secondary buffer, trigger if necessary
295 * if blocking, sleep, rinse and repeat.
296 *
297 * called externally, so must handle locking
298 */
299
300int
301chn_write(struct pcm_channel *c, struct uio *buf)
302{
303	int ret, timeout, newsize, count, sz;
304	struct snd_dbuf *bs = c->bufsoft;
305	void *off;
306	int t, x,togo,p;
307
308	CHN_LOCKASSERT(c);
309	/*
310	 * XXX Certain applications attempt to write larger size
311	 * of pcm data than c->blocksize2nd without blocking,
312	 * resulting partial write. Expand the block size so that
313	 * the write operation avoids blocking.
314	 */
315	if ((c->flags & CHN_F_NBIO) && buf->uio_resid > sndbuf_getblksz(bs)) {
316		DEB(device_printf(c->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n",
317			buf->uio_resid, sndbuf_getblksz(bs)));
318		newsize = 16;
319		while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2))
320			newsize <<= 1;
321		chn_setblocksize(c, sndbuf_getblkcnt(bs), newsize);
322		DEB(device_printf(c->dev, "frags reset to %d x %d\n", sndbuf_getblkcnt(bs), sndbuf_getblksz(bs)));
323	}
324
325	ret = 0;
326	count = hz;
327	while (!ret && (buf->uio_resid > 0) && (count > 0)) {
328		sz = sndbuf_getfree(bs);
329		if (sz == 0) {
330			if (c->flags & CHN_F_NBIO)
331				ret = EWOULDBLOCK;
332			else {
333				timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
334				if (timeout < 1)
335					timeout = 1;
336				timeout = 1;
337	   			ret = chn_sleep(c, "pcmwr", timeout);
338				if (ret == EWOULDBLOCK) {
339					count -= timeout;
340					ret = 0;
341				} else if (ret == 0)
342					count = hz;
343			}
344		} else {
345			sz = MIN(sz, buf->uio_resid);
346			KASSERT(sz > 0, ("confusion in chn_write"));
347			/* printf("sz: %d\n", sz); */
348
349			/*
350			 * The following assumes that the free space in
351			 * the buffer can never be less around the
352			 * unlock-uiomove-lock sequence.
353			 */
354			togo = sz;
355			while (ret == 0 && togo> 0) {
356				p = sndbuf_getfreeptr(bs);
357				t = MIN(togo, sndbuf_getsize(bs) - p);
358				off = sndbuf_getbufofs(bs, p);
359				CHN_UNLOCK(c);
360				ret = uiomove(off, t, buf);
361				CHN_LOCK(c);
362				togo -= t;
363				x = sndbuf_acquire(bs, NULL, t);
364			}
365			ret = 0;
366			if (ret == 0 && !(c->flags & CHN_F_TRIGGERED))
367				chn_start(c, 0);
368		}
369	}
370	/* printf("ret: %d left: %d\n", ret, buf->uio_resid); */
371
372	if (count <= 0) {
373		c->flags |= CHN_F_DEAD;
374		printf("%s: play interrupt timeout, channel dead\n", c->name);
375	}
376
377	return ret;
378}
379
380static int
381chn_rddump(struct pcm_channel *c, unsigned int cnt)
382{
383    	struct snd_dbuf *b = c->bufhard;
384
385	CHN_LOCKASSERT(c);
386#if 0
387	static uint32_t kk = 0;
388	printf("%u: dumping %d bytes\n", ++kk, cnt);
389#endif
390	c->xruns++;
391	sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt);
392	return sndbuf_dispose(b, NULL, cnt);
393}
394
395/*
396 * Feed new data from the read buffer. Can be called in the bottom half.
397 * Hence must be called at spltty.
398 */
399int
400chn_rdfeed(struct pcm_channel *c)
401{
402    	struct snd_dbuf *b = c->bufhard;
403    	struct snd_dbuf *bs = c->bufsoft;
404	unsigned int ret, amt;
405
406	CHN_LOCKASSERT(c);
407    	DEB(
408	if (c->flags & CHN_F_CLOSING) {
409		sndbuf_dump(b, "b", 0x02);
410		sndbuf_dump(bs, "bs", 0x02);
411	})
412
413#if 0
414	amt = sndbuf_getready(b);
415	if (sndbuf_getfree(bs) < amt) {
416		c->xruns++;
417		amt = sndbuf_getfree(bs);
418	}
419#endif
420	amt = sndbuf_getfree(bs);
421	if (amt < sndbuf_getready(b))
422		c->xruns++;
423	ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0;
424
425	amt = sndbuf_getready(b);
426	if (amt > 0)
427		chn_rddump(c, amt);
428
429	chn_wakeup(c);
430
431	return ret;
432}
433
434void
435chn_rdupdate(struct pcm_channel *c)
436{
437	int ret;
438
439	CHN_LOCKASSERT(c);
440	KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel"));
441
442	if ((c->flags & CHN_F_MAPPED) || !(c->flags & CHN_F_TRIGGERED))
443		return;
444	chn_trigger(c, PCMTRIG_EMLDMARD);
445	chn_dmaupdate(c);
446	ret = chn_rdfeed(c);
447	if (ret)
448		printf("chn_rdfeed: %d\n", ret);
449
450}
451
452/* read interrupt routine. Must be called with interrupts blocked. */
453static void
454chn_rdintr(struct pcm_channel *c)
455{
456	int ret;
457
458	CHN_LOCKASSERT(c);
459	/* tell the driver to update the primary buffer if non-dma */
460	chn_trigger(c, PCMTRIG_EMLDMARD);
461	/* update pointers in primary buffer */
462	chn_dmaupdate(c);
463	/* ...and feed from primary to secondary */
464	ret = chn_rdfeed(c);
465}
466
467/*
468 * user read routine - trigger if necessary, uiomove data from secondary buffer
469 * if blocking, sleep, rinse and repeat.
470 *
471 * called externally, so must handle locking
472 */
473
474int
475chn_read(struct pcm_channel *c, struct uio *buf)
476{
477	int		ret, timeout, sz, count;
478	struct snd_dbuf       *bs = c->bufsoft;
479	void *off;
480	int t, x,togo,p;
481
482	CHN_LOCKASSERT(c);
483	if (!(c->flags & CHN_F_TRIGGERED))
484		chn_start(c, 0);
485
486	ret = 0;
487	count = hz;
488	while (!ret && (buf->uio_resid > 0) && (count > 0)) {
489		sz = MIN(buf->uio_resid, sndbuf_getready(bs));
490
491		if (sz > 0) {
492			/*
493			 * The following assumes that the free space in
494			 * the buffer can never be less around the
495			 * unlock-uiomove-lock sequence.
496			 */
497			togo = sz;
498			while (ret == 0 && togo> 0) {
499				p = sndbuf_getreadyptr(bs);
500				t = MIN(togo, sndbuf_getsize(bs) - p);
501				off = sndbuf_getbufofs(bs, p);
502				CHN_UNLOCK(c);
503				ret = uiomove(off, t, buf);
504				CHN_LOCK(c);
505				togo -= t;
506				x = sndbuf_dispose(bs, NULL, t);
507			}
508			ret = 0;
509		} else {
510			if (c->flags & CHN_F_NBIO) {
511				ret = EWOULDBLOCK;
512			} else {
513				timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
514				if (timeout < 1)
515					timeout = 1;
516	   			ret = chn_sleep(c, "pcmrd", timeout);
517				if (ret == EWOULDBLOCK) {
518					count -= timeout;
519					ret = 0;
520				} else {
521					count = hz;
522				}
523
524			}
525		}
526	}
527
528	if (count <= 0) {
529		c->flags |= CHN_F_DEAD;
530		printf("%s: record interrupt timeout, channel dead\n", c->name);
531	}
532
533	return ret;
534}
535
536void
537chn_intr(struct pcm_channel *c)
538{
539	CHN_LOCK(c);
540	c->interrupts++;
541	if (c->direction == PCMDIR_PLAY)
542		chn_wrintr(c);
543	else
544		chn_rdintr(c);
545	CHN_UNLOCK(c);
546}
547
548u_int32_t
549chn_start(struct pcm_channel *c, int force)
550{
551	u_int32_t i, j;
552	struct snd_dbuf *b = c->bufhard;
553	struct snd_dbuf *bs = c->bufsoft;
554
555	CHN_LOCKASSERT(c);
556	/* if we're running, or if we're prevented from triggering, bail */
557	if ((c->flags & CHN_F_TRIGGERED) || ((c->flags & CHN_F_NOTRIGGER) && !force))
558		return EINVAL;
559
560	i = (c->direction == PCMDIR_PLAY)? sndbuf_getready(bs) : sndbuf_getfree(bs);
561	j = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(b) : sndbuf_getready(b);
562	if (force || (i >= j)) {
563		c->flags |= CHN_F_TRIGGERED;
564		/*
565		 * if we're starting because a vchan started, don't feed any data
566		 * or it becomes impossible to start vchans synchronised with the
567		 * first one.  the hardbuf should be empty so we top it up with
568		 * silence to give it something to chew.  the real data will be
569		 * fed at the first irq.
570		 */
571		if (c->direction == PCMDIR_PLAY) {
572			/*
573			 * Reduce pops during playback startup.
574			 */
575			sndbuf_fillsilence(b);
576			if (SLIST_EMPTY(&c->children))
577				chn_wrfeed(c);
578		}
579		sndbuf_setrun(b, 1);
580		c->xruns = 0;
581	    	chn_trigger(c, PCMTRIG_START);
582		return 0;
583	}
584
585	return 0;
586}
587
588void
589chn_resetbuf(struct pcm_channel *c)
590{
591	struct snd_dbuf *b = c->bufhard;
592	struct snd_dbuf *bs = c->bufsoft;
593
594	c->blocks = 0;
595	sndbuf_reset(b);
596	sndbuf_reset(bs);
597}
598
599/*
600 * chn_sync waits until the space in the given channel goes above
601 * a threshold. The threshold is checked against fl or rl respectively.
602 * Assume that the condition can become true, do not check here...
603 */
604int
605chn_sync(struct pcm_channel *c, int threshold)
606{
607    	u_long rdy;
608    	int ret;
609    	struct snd_dbuf *bs = c->bufsoft;
610
611	CHN_LOCKASSERT(c);
612
613	/* if we haven't yet started and nothing is buffered, else start*/
614	if (!(c->flags & CHN_F_TRIGGERED)) {
615		if (sndbuf_getready(bs) > 0) {
616			ret = chn_start(c, 1);
617			if (ret)
618				return ret;
619		} else {
620			return 0;
621		}
622	}
623
624	for (;;) {
625		rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs);
626		if (rdy <= threshold) {
627	    		ret = chn_sleep(c, "pcmsyn", 1);
628	    		if (ret == ERESTART || ret == EINTR) {
629				DEB(printf("chn_sync: tsleep returns %d\n", ret));
630				return -1;
631	    		}
632		} else
633			break;
634    	}
635    	return 0;
636}
637
638/* called externally, handle locking */
639int
640chn_poll(struct pcm_channel *c, int ev, struct thread *td)
641{
642	struct snd_dbuf *bs = c->bufsoft;
643	int ret;
644
645	CHN_LOCKASSERT(c);
646    	if (!(c->flags & CHN_F_MAPPED) && !(c->flags & CHN_F_TRIGGERED))
647		chn_start(c, 1);
648	ret = 0;
649	if (chn_polltrigger(c) && chn_pollreset(c))
650		ret = ev;
651	else
652		selrecord(td, sndbuf_getsel(bs));
653	return ret;
654}
655
656/*
657 * chn_abort terminates a running dma transfer.  it may sleep up to 200ms.
658 * it returns the number of bytes that have not been transferred.
659 *
660 * called from: dsp_close, dsp_ioctl, with channel locked
661 */
662int
663chn_abort(struct pcm_channel *c)
664{
665    	int missing = 0;
666    	struct snd_dbuf *b = c->bufhard;
667    	struct snd_dbuf *bs = c->bufsoft;
668
669	CHN_LOCKASSERT(c);
670	if (!(c->flags & CHN_F_TRIGGERED))
671		return 0;
672	c->flags |= CHN_F_ABORTING;
673
674	c->flags &= ~CHN_F_TRIGGERED;
675	/* kill the channel */
676	chn_trigger(c, PCMTRIG_ABORT);
677	sndbuf_setrun(b, 0);
678	if (!(c->flags & CHN_F_VIRTUAL))
679		chn_dmaupdate(c);
680    	missing = sndbuf_getready(bs) + sndbuf_getready(b);
681
682	c->flags &= ~CHN_F_ABORTING;
683	return missing;
684}
685
686/*
687 * this routine tries to flush the dma transfer. It is called
688 * on a close of a playback channel.
689 * first, if there is data in the buffer, but the dma has not yet
690 * begun, we need to start it.
691 * next, we wait for the play buffer to drain
692 * finally, we stop the dma.
693 *
694 * called from: dsp_close, not valid for record channels.
695 */
696
697int
698chn_flush(struct pcm_channel *c)
699{
700    	int ret, count, resid, resid_p;
701    	struct snd_dbuf *b = c->bufhard;
702    	struct snd_dbuf *bs = c->bufsoft;
703
704	CHN_LOCKASSERT(c);
705	KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel"));
706    	DEB(printf("chn_flush: c->flags 0x%08x\n", c->flags));
707
708	/* if we haven't yet started and nothing is buffered, else start*/
709	if (!(c->flags & CHN_F_TRIGGERED)) {
710		if (sndbuf_getready(bs) > 0) {
711			ret = chn_start(c, 1);
712			if (ret)
713				return ret;
714		} else {
715			return 0;
716		}
717	}
718
719	c->flags |= CHN_F_CLOSING;
720	resid = sndbuf_getready(bs) + sndbuf_getready(b);
721	resid_p = resid;
722	count = 10;
723	ret = 0;
724	while ((count > 0) && (resid > sndbuf_getsize(b)) && (ret == 0)) {
725		/* still pending output data. */
726		ret = chn_sleep(c, "pcmflu", hz / 10);
727		if (ret == EWOULDBLOCK)
728			ret = 0;
729		if (ret == 0) {
730			resid = sndbuf_getready(bs) + sndbuf_getready(b);
731			if (resid == resid_p)
732				count--;
733			if (resid > resid_p)
734				DEB(printf("chn_flush: buffer length increasind %d -> %d\n", resid_p, resid));
735			resid_p = resid;
736		}
737   	}
738	if (count == 0)
739		DEB(printf("chn_flush: timeout, hw %d, sw %d\n",
740			sndbuf_getready(b), sndbuf_getready(bs)));
741
742	c->flags &= ~CHN_F_TRIGGERED;
743	/* kill the channel */
744	chn_trigger(c, PCMTRIG_ABORT);
745	sndbuf_setrun(b, 0);
746
747    	c->flags &= ~CHN_F_CLOSING;
748    	return 0;
749}
750
751int
752fmtvalid(u_int32_t fmt, u_int32_t *fmtlist)
753{
754	int i;
755
756	for (i = 0; fmtlist[i]; i++)
757		if (fmt == fmtlist[i])
758			return 1;
759	return 0;
760}
761
762int
763chn_reset(struct pcm_channel *c, u_int32_t fmt)
764{
765	int hwspd, r;
766
767	CHN_LOCKASSERT(c);
768	c->flags &= CHN_F_RESET;
769	c->interrupts = 0;
770	c->xruns = 0;
771
772	r = CHANNEL_RESET(c->methods, c->devinfo);
773	if (fmt != 0) {
774#if 0
775		hwspd = DSP_DEFAULT_SPEED;
776		/* only do this on a record channel until feederbuilder works */
777		if (c->direction == PCMDIR_REC)
778			RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
779		c->speed = hwspd;
780#endif
781		hwspd = chn_getcaps(c)->minspeed;
782		c->speed = hwspd;
783
784		if (r == 0)
785			r = chn_setformat(c, fmt);
786		if (r == 0)
787			r = chn_setspeed(c, hwspd);
788		if (r == 0)
789			r = chn_setvolume(c, 100, 100);
790	}
791	if (r == 0)
792		r = chn_setblocksize(c, 0, 0);
793	if (r == 0) {
794		chn_resetbuf(c);
795		r = CHANNEL_RESETDONE(c->methods, c->devinfo);
796	}
797	return r;
798}
799
800int
801chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
802{
803	struct feeder_class *fc;
804	struct snd_dbuf *b, *bs;
805	int ret;
806
807	chn_lockinit(c, dir);
808
809	b = NULL;
810	bs = NULL;
811	c->devinfo = NULL;
812	c->feeder = NULL;
813
814	ret = ENOMEM;
815	b = sndbuf_create(c->dev, c->name, "primary", c);
816	if (b == NULL)
817		goto out;
818	bs = sndbuf_create(c->dev, c->name, "secondary", c);
819	if (bs == NULL)
820		goto out;
821
822	CHN_LOCK(c);
823
824	ret = EINVAL;
825	fc = feeder_getclass(NULL);
826	if (fc == NULL)
827		goto out;
828	if (chn_addfeeder(c, fc, NULL))
829		goto out;
830
831	/*
832	 * XXX - sndbuf_setup() & sndbuf_resize() expect to be called
833	 *	 with the channel unlocked because they are also called
834	 *	 from driver methods that don't know about locking
835	 */
836	CHN_UNLOCK(c);
837	sndbuf_setup(bs, NULL, 0);
838	CHN_LOCK(c);
839	c->bufhard = b;
840	c->bufsoft = bs;
841	c->flags = 0;
842	c->feederflags = 0;
843
844	ret = ENODEV;
845	CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */
846	c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction);
847	CHN_LOCK(c);
848	if (c->devinfo == NULL)
849		goto out;
850
851	ret = ENOMEM;
852	if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0))
853		goto out;
854
855	ret = chn_setdir(c, direction);
856	if (ret)
857		goto out;
858
859	ret = sndbuf_setfmt(b, AFMT_U8);
860	if (ret)
861		goto out;
862
863	ret = sndbuf_setfmt(bs, AFMT_U8);
864	if (ret)
865		goto out;
866
867
868out:
869	CHN_UNLOCK(c);
870	if (ret) {
871		if (c->devinfo) {
872			if (CHANNEL_FREE(c->methods, c->devinfo))
873				sndbuf_free(b);
874		}
875		if (bs)
876			sndbuf_destroy(bs);
877		if (b)
878			sndbuf_destroy(b);
879		c->flags |= CHN_F_DEAD;
880		chn_lockdestroy(c);
881
882		return ret;
883	}
884
885	return 0;
886}
887
888int
889chn_kill(struct pcm_channel *c)
890{
891    	struct snd_dbuf *b = c->bufhard;
892    	struct snd_dbuf *bs = c->bufsoft;
893
894	if (c->flags & CHN_F_TRIGGERED)
895		chn_trigger(c, PCMTRIG_ABORT);
896	while (chn_removefeeder(c) == 0);
897	if (CHANNEL_FREE(c->methods, c->devinfo))
898		sndbuf_free(b);
899	c->flags |= CHN_F_DEAD;
900	sndbuf_destroy(bs);
901	sndbuf_destroy(b);
902	chn_lockdestroy(c);
903	return 0;
904}
905
906int
907chn_setdir(struct pcm_channel *c, int dir)
908{
909#ifdef DEV_ISA
910    	struct snd_dbuf *b = c->bufhard;
911#endif
912	int r;
913
914	CHN_LOCKASSERT(c);
915	c->direction = dir;
916	r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction);
917#ifdef DEV_ISA
918	if (!r && SND_DMA(b))
919		sndbuf_dmasetdir(b, c->direction);
920#endif
921	return r;
922}
923
924int
925chn_setvolume(struct pcm_channel *c, int left, int right)
926{
927	CHN_LOCKASSERT(c);
928	/* should add a feeder for volume changing if channel returns -1 */
929	c->volume = (left << 8) | right;
930	return 0;
931}
932
933static int
934chn_tryspeed(struct pcm_channel *c, int speed)
935{
936	struct pcm_feeder *f;
937    	struct snd_dbuf *b = c->bufhard;
938    	struct snd_dbuf *bs = c->bufsoft;
939    	struct snd_dbuf *x;
940	int r, delta;
941
942	CHN_LOCKASSERT(c);
943	DEB(printf("setspeed, channel %s\n", c->name));
944	DEB(printf("want speed %d, ", speed));
945	if (speed <= 0)
946		return EINVAL;
947	if (CANCHANGE(c)) {
948		r = 0;
949		c->speed = speed;
950		sndbuf_setspd(bs, speed);
951		RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed);
952		DEB(printf("try speed %d, ", speed));
953		sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed));
954		DEB(printf("got speed %d\n", sndbuf_getspd(b)));
955
956		delta = sndbuf_getspd(b) - sndbuf_getspd(bs);
957		if (delta < 0)
958			delta = -delta;
959
960		c->feederflags &= ~(1 << FEEDER_RATE);
961		/*
962		 * Used to be 500. It was too big!
963		 */
964		if (delta > 25)
965			c->feederflags |= 1 << FEEDER_RATE;
966		else
967			sndbuf_setspd(bs, sndbuf_getspd(b));
968
969		r = chn_buildfeeder(c);
970		DEB(printf("r = %d\n", r));
971		if (r)
972			goto out;
973
974		r = chn_setblocksize(c, 0, 0);
975		if (r)
976			goto out;
977
978		if (!(c->feederflags & (1 << FEEDER_RATE)))
979			goto out;
980
981		r = EINVAL;
982		f = chn_findfeeder(c, FEEDER_RATE);
983		DEB(printf("feedrate = %p\n", f));
984		if (f == NULL)
985			goto out;
986
987		x = (c->direction == PCMDIR_REC)? b : bs;
988		r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x));
989		DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r));
990		if (r)
991			goto out;
992
993		x = (c->direction == PCMDIR_REC)? bs : b;
994		r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x));
995		DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r));
996out:
997		if (!r)
998			r = CHANNEL_SETFORMAT(c->methods, c->devinfo,
999							sndbuf_getfmt(b));
1000		if (!r)
1001			sndbuf_setfmt(bs, c->format);
1002		DEB(printf("setspeed done, r = %d\n", r));
1003		return r;
1004	} else
1005		return EINVAL;
1006}
1007
1008int
1009chn_setspeed(struct pcm_channel *c, int speed)
1010{
1011	int r, oldspeed = c->speed;
1012
1013	r = chn_tryspeed(c, speed);
1014	if (r) {
1015		DEB(printf("Failed to set speed %d falling back to %d\n", speed, oldspeed));
1016		r = chn_tryspeed(c, oldspeed);
1017	}
1018	return r;
1019}
1020
1021static int
1022chn_tryformat(struct pcm_channel *c, u_int32_t fmt)
1023{
1024	struct snd_dbuf *b = c->bufhard;
1025	struct snd_dbuf *bs = c->bufsoft;
1026	int r;
1027
1028	CHN_LOCKASSERT(c);
1029	if (CANCHANGE(c)) {
1030		DEB(printf("want format %d\n", fmt));
1031		c->format = fmt;
1032		r = chn_buildfeeder(c);
1033		if (r == 0) {
1034			sndbuf_setfmt(bs, c->format);
1035			chn_resetbuf(c);
1036			r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b));
1037			if (r == 0)
1038				r = chn_tryspeed(c, c->speed);
1039		}
1040		return r;
1041	} else
1042		return EINVAL;
1043}
1044
1045int
1046chn_setformat(struct pcm_channel *c, u_int32_t fmt)
1047{
1048	u_int32_t oldfmt = c->format;
1049	int r;
1050
1051	r = chn_tryformat(c, fmt);
1052	if (r) {
1053		DEB(printf("Format change %d failed, reverting to %d\n", fmt, oldfmt));
1054		chn_tryformat(c, oldfmt);
1055	}
1056	return r;
1057}
1058
1059int
1060chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
1061{
1062	struct snd_dbuf *b = c->bufhard;
1063	struct snd_dbuf *bs = c->bufsoft;
1064	int irqhz, tmp, ret, maxsize, reqblksz, tmpblksz;
1065
1066	CHN_LOCKASSERT(c);
1067	if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) {
1068		KASSERT(sndbuf_getsize(bs) ==  0 ||
1069		    sndbuf_getsize(bs) >= sndbuf_getsize(b),
1070		    ("%s(%s): bufsoft size %d < bufhard size %d", __func__,
1071		    c->name, sndbuf_getsize(bs), sndbuf_getsize(b)));
1072		return EINVAL;
1073	}
1074	c->flags |= CHN_F_SETBLOCKSIZE;
1075
1076	ret = 0;
1077	DEB(printf("%s(%d, %d)\n", __func__, blkcnt, blksz));
1078	if (blksz == 0 || blksz == -1) {
1079		if (blksz == -1)
1080			c->flags &= ~CHN_F_HAS_SIZE;
1081		if (!(c->flags & CHN_F_HAS_SIZE)) {
1082			blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / chn_targetirqrate;
1083	      		tmp = 32;
1084			while (tmp <= blksz)
1085				tmp <<= 1;
1086			tmp >>= 1;
1087			blksz = tmp;
1088			blkcnt = CHN_2NDBUFMAXSIZE / blksz;
1089
1090			RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
1091 			RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz);
1092			DEB(printf("%s: defaulting to (%d, %d)\n", __func__, blkcnt, blksz));
1093		} else {
1094			blkcnt = sndbuf_getblkcnt(bs);
1095			blksz = sndbuf_getblksz(bs);
1096			DEB(printf("%s: updating (%d, %d)\n", __func__, blkcnt, blksz));
1097		}
1098	} else {
1099		ret = EINVAL;
1100		if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
1101			goto out;
1102		ret = 0;
1103		c->flags |= CHN_F_HAS_SIZE;
1104	}
1105
1106	reqblksz = blksz;
1107	if (reqblksz < sndbuf_getbps(bs))
1108		reqblksz = sndbuf_getbps(bs);
1109	if (reqblksz % sndbuf_getbps(bs))
1110		reqblksz -= reqblksz % sndbuf_getbps(bs);
1111
1112	/* adjust for different hw format/speed */
1113	irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz;
1114	DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __func__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz));
1115	RANGE(irqhz, 16, 512);
1116
1117	tmpblksz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz;
1118
1119	/* round down to 2^x */
1120	blksz = 32;
1121	while (blksz <= tmpblksz)
1122		blksz <<= 1;
1123	blksz >>= 1;
1124
1125	/* round down to fit hw buffer size */
1126	if (sndbuf_getmaxsize(b) > 0)
1127		RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2);
1128	else
1129		/* virtual channels don't appear to allocate bufhard */
1130		RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2);
1131	DEB(printf("%s: hard blksz requested %d (maxsize %d), ", __func__, blksz, sndbuf_getmaxsize(b)));
1132
1133	/* Increase the size of bufsoft if before increasing bufhard. */
1134	maxsize = sndbuf_getsize(b);
1135	if (sndbuf_getsize(bs) > maxsize)
1136		maxsize = sndbuf_getsize(bs);
1137	if (reqblksz * blkcnt > maxsize)
1138		maxsize = reqblksz * blkcnt;
1139	if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
1140		ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
1141		if (ret)
1142			goto out1;
1143	}
1144
1145	CHN_UNLOCK(c);
1146	sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz));
1147	CHN_LOCK(c);
1148
1149	/* Decrease the size of bufsoft after decreasing bufhard. */
1150	maxsize = sndbuf_getsize(b);
1151	if (reqblksz * blkcnt > maxsize)
1152		maxsize = reqblksz * blkcnt;
1153	if (maxsize > sndbuf_getsize(bs))
1154		printf("Danger! %s bufsoft size increasing from %d to %d after CHANNEL_SETBLOCKSIZE()\n",
1155		    c->name, sndbuf_getsize(bs), maxsize);
1156	if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) {
1157		ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz);
1158		if (ret)
1159			goto out1;
1160	}
1161
1162	irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b);
1163	DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz));
1164
1165	chn_resetbuf(c);
1166out1:
1167	KASSERT(sndbuf_getsize(bs) ==  0 ||
1168	    sndbuf_getsize(bs) >= sndbuf_getsize(b),
1169	    ("%s(%s): bufsoft size %d < bufhard size %d, reqblksz=%d blksz=%d maxsize=%d blkcnt=%d",
1170	    __func__, c->name, sndbuf_getsize(bs), sndbuf_getsize(b), reqblksz,
1171	    blksz, maxsize, blkcnt));
1172out:
1173	c->flags &= ~CHN_F_SETBLOCKSIZE;
1174#if 0
1175	if (1) {
1176		static uint32_t kk = 0;
1177		printf("%u: b %d/%d/%d : (%d)%d/0x%0x | bs %d/%d/%d : (%d)%d/0x%0x\n", ++kk,
1178			sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b),
1179			sndbuf_getbps(b),
1180			sndbuf_getspd(b), sndbuf_getfmt(b),
1181			sndbuf_getsize(bs), sndbuf_getblksz(bs), sndbuf_getblkcnt(bs),
1182			sndbuf_getbps(bs),
1183			sndbuf_getspd(bs), sndbuf_getfmt(bs));
1184		if (sndbuf_getsize(b) % sndbuf_getbps(b) ||
1185				sndbuf_getblksz(b) % sndbuf_getbps(b) ||
1186				sndbuf_getsize(bs) % sndbuf_getbps(bs) ||
1187				sndbuf_getblksz(b) % sndbuf_getbps(b)) {
1188			printf("%u: bps/blksz alignment screwed!\n", kk);
1189		}
1190	}
1191#endif
1192	return ret;
1193}
1194
1195int
1196chn_trigger(struct pcm_channel *c, int go)
1197{
1198#ifdef DEV_ISA
1199    	struct snd_dbuf *b = c->bufhard;
1200#endif
1201	int ret;
1202
1203	CHN_LOCKASSERT(c);
1204#ifdef DEV_ISA
1205	if (SND_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
1206		sndbuf_dmabounce(b);
1207#endif
1208	ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
1209
1210	return ret;
1211}
1212
1213int
1214chn_getptr(struct pcm_channel *c)
1215{
1216	int hwptr;
1217	int a = (1 << c->align) - 1;
1218
1219	CHN_LOCKASSERT(c);
1220	hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
1221	/* don't allow unaligned values in the hwa ptr */
1222#if 1
1223	hwptr &= ~a ; /* Apply channel align mask */
1224#endif
1225	hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
1226	return hwptr;
1227}
1228
1229struct pcmchan_caps *
1230chn_getcaps(struct pcm_channel *c)
1231{
1232	CHN_LOCKASSERT(c);
1233	return CHANNEL_GETCAPS(c->methods, c->devinfo);
1234}
1235
1236u_int32_t
1237chn_getformats(struct pcm_channel *c)
1238{
1239	u_int32_t *fmtlist, fmts;
1240	int i;
1241
1242	fmtlist = chn_getcaps(c)->fmtlist;
1243	fmts = 0;
1244	for (i = 0; fmtlist[i]; i++)
1245		fmts |= fmtlist[i];
1246
1247	/* report software-supported formats */
1248	if (report_soft_formats)
1249		fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE|
1250		    AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE|
1251		    AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE|
1252		    AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8;
1253
1254	return fmts;
1255}
1256
1257static int
1258chn_buildfeeder(struct pcm_channel *c)
1259{
1260	struct feeder_class *fc;
1261	struct pcm_feederdesc desc;
1262	u_int32_t tmp[2], type, flags, hwfmt, *fmtlist;
1263	int err;
1264
1265	CHN_LOCKASSERT(c);
1266	while (chn_removefeeder(c) == 0);
1267	KASSERT((c->feeder == NULL), ("feeder chain not empty"));
1268
1269	c->align = sndbuf_getalign(c->bufsoft);
1270
1271	if (SLIST_EMPTY(&c->children)) {
1272		fc = feeder_getclass(NULL);
1273		KASSERT(fc != NULL, ("can't find root feeder"));
1274
1275		err = chn_addfeeder(c, fc, NULL);
1276		if (err) {
1277			DEB(printf("can't add root feeder, err %d\n", err));
1278
1279			return err;
1280		}
1281		c->feeder->desc->out = c->format;
1282	} else {
1283		if (c->flags & CHN_F_HAS_VCHAN) {
1284			desc.type = FEEDER_MIXER;
1285			desc.in = 0;
1286		} else {
1287			DEB(printf("can't decide which feeder type to use!\n"));
1288			return EOPNOTSUPP;
1289		}
1290		desc.out = c->format;
1291		desc.flags = 0;
1292		fc = feeder_getclass(&desc);
1293		if (fc == NULL) {
1294			DEB(printf("can't find vchan feeder\n"));
1295
1296			return EOPNOTSUPP;
1297		}
1298
1299		err = chn_addfeeder(c, fc, &desc);
1300		if (err) {
1301			DEB(printf("can't add vchan feeder, err %d\n", err));
1302
1303			return err;
1304		}
1305	}
1306	flags = c->feederflags;
1307	fmtlist = chn_getcaps(c)->fmtlist;
1308
1309	DEB(printf("feederflags %x\n", flags));
1310
1311	for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) {
1312		if (flags & (1 << type)) {
1313			desc.type = type;
1314			desc.in = 0;
1315			desc.out = 0;
1316			desc.flags = 0;
1317			DEB(printf("find feeder type %d, ", type));
1318			fc = feeder_getclass(&desc);
1319			DEB(printf("got %p\n", fc));
1320			if (fc == NULL) {
1321				DEB(printf("can't find required feeder type %d\n", type));
1322
1323				return EOPNOTSUPP;
1324			}
1325
1326			if ((type == FEEDER_RATE &&
1327					!fmtvalid(fc->desc->in, fmtlist))
1328					|| c->feeder->desc->out != fc->desc->in) {
1329 				DEB(printf("build fmtchain from 0x%x to 0x%x: ", c->feeder->desc->out, fc->desc->in));
1330				tmp[0] = fc->desc->in;
1331				tmp[1] = 0;
1332				if (chn_fmtchain(c, tmp) == 0) {
1333					DEB(printf("failed\n"));
1334
1335					return ENODEV;
1336				}
1337 				DEB(printf("ok\n"));
1338			}
1339
1340			err = chn_addfeeder(c, fc, fc->desc);
1341			if (err) {
1342				DEB(printf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err));
1343
1344				return err;
1345			}
1346			DEB(printf("added feeder %p, output 0x%x\n", fc, c->feeder->desc->out));
1347		}
1348	}
1349
1350	if (fmtvalid(c->feeder->desc->out, fmtlist)
1351			&& !(c->direction == PCMDIR_REC &&
1352				c->format != c->feeder->desc->out))
1353		hwfmt = c->feeder->desc->out;
1354	else {
1355		if (c->direction == PCMDIR_REC) {
1356			tmp[0] = c->format;
1357			tmp[1] = 0;
1358			hwfmt = chn_fmtchain(c, tmp);
1359		} else
1360			hwfmt = chn_fmtchain(c, fmtlist);
1361	}
1362
1363	if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) {
1364		DEB(printf("Invalid hardware format: 0x%x\n", hwfmt));
1365		return ENODEV;
1366	}
1367
1368	sndbuf_setfmt(c->bufhard, hwfmt);
1369
1370	return 0;
1371}
1372
1373int
1374chn_notify(struct pcm_channel *c, u_int32_t flags)
1375{
1376	struct pcmchan_children *pce;
1377	struct pcm_channel *child;
1378	int run;
1379
1380	CHN_LOCK(c);
1381
1382	if (SLIST_EMPTY(&c->children)) {
1383		CHN_UNLOCK(c);
1384		return ENODEV;
1385	}
1386
1387	run = (c->flags & CHN_F_TRIGGERED)? 1 : 0;
1388	/*
1389	 * if the hwchan is running, we can't change its rate, format or
1390	 * blocksize
1391	 */
1392	if (run)
1393		flags &= CHN_N_VOLUME | CHN_N_TRIGGER;
1394
1395	if (flags & CHN_N_RATE) {
1396		/*
1397		 * we could do something here, like scan children and decide on
1398		 * the most appropriate rate to mix at, but we don't for now
1399		 */
1400	}
1401	if (flags & CHN_N_FORMAT) {
1402		/*
1403		 * we could do something here, like scan children and decide on
1404		 * the most appropriate mixer feeder to use, but we don't for now
1405		 */
1406	}
1407	if (flags & CHN_N_VOLUME) {
1408		/*
1409		 * we could do something here but we don't for now
1410		 */
1411	}
1412	if (flags & CHN_N_BLOCKSIZE) {
1413		int blksz;
1414		/*
1415		 * scan the children, find the lowest blocksize and use that
1416		 * for the hard blocksize
1417		 */
1418		blksz = sndbuf_getmaxsize(c->bufhard) / 2;
1419		SLIST_FOREACH(pce, &c->children, link) {
1420			child = pce->channel;
1421			CHN_LOCK(child);
1422			if (sndbuf_getblksz(child->bufhard) < blksz)
1423				blksz = sndbuf_getblksz(child->bufhard);
1424			CHN_UNLOCK(child);
1425		}
1426		chn_setblocksize(c, 2, blksz);
1427	}
1428	if (flags & CHN_N_TRIGGER) {
1429		int nrun;
1430		/*
1431		 * scan the children, and figure out if any are running
1432		 * if so, we need to be running, otherwise we need to be stopped
1433		 * if we aren't in our target sstate, move to it
1434		 */
1435		nrun = 0;
1436		SLIST_FOREACH(pce, &c->children, link) {
1437			child = pce->channel;
1438			CHN_LOCK(child);
1439			if (child->flags & CHN_F_TRIGGERED)
1440				nrun = 1;
1441			CHN_UNLOCK(child);
1442		}
1443		if (nrun && !run)
1444			chn_start(c, 1);
1445		if (!nrun && run)
1446			chn_abort(c);
1447	}
1448	CHN_UNLOCK(c);
1449	return 0;
1450}
1451
1452void
1453chn_lock(struct pcm_channel *c)
1454{
1455	CHN_LOCK(c);
1456}
1457
1458void
1459chn_unlock(struct pcm_channel *c)
1460{
1461	CHN_UNLOCK(c);
1462}
1463