1139749Simp/*-
2193640Sariff * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
3193640Sariff * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
4193640Sariff * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
5193640Sariff * Portions Copyright (c) Luigi Rizzo <luigi@FreeBSD.org> - 1997-99
650724Scg * All rights reserved.
750724Scg *
850724Scg * Redistribution and use in source and binary forms, with or without
950724Scg * modification, are permitted provided that the following conditions
1050724Scg * are met:
1150724Scg * 1. Redistributions of source code must retain the above copyright
1250724Scg *    notice, this list of conditions and the following disclaimer.
1350724Scg * 2. Redistributions in binary form must reproduce the above copyright
1450724Scg *    notice, this list of conditions and the following disclaimer in the
1550724Scg *    documentation and/or other materials provided with the distribution.
1650724Scg *
1750724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1850724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1950724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2050724Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2150724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2250724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2350724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2450724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2550724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2650724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2750724Scg * SUCH DAMAGE.
2850724Scg */
2950724Scg
30147274Smarius#include "opt_isa.h"
31147274Smarius
32193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS
33193640Sariff#include "opt_snd.h"
34193640Sariff#endif
35193640Sariff
3653465Scg#include <dev/sound/pcm/sound.h>
37193640Sariff#include <dev/sound/pcm/vchan.h>
3850724Scg
3970134Scg#include "feeder_if.h"
4070134Scg
4182180ScgSND_DECLARE_FILE("$FreeBSD$");
4282180Scg
43164614Sariffint report_soft_formats = 1;
44164614SariffSYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
45164614Sariff	&report_soft_formats, 1, "report software-emulated formats");
4683613Scg
47193640Sariffint report_soft_matrix = 1;
48193640SariffSYSCTL_INT(_hw_snd, OID_AUTO, report_soft_matrix, CTLFLAG_RW,
49193640Sariff	&report_soft_matrix, 1, "report software-emulated channel matrixing");
50193640Sariff
51164614Sariffint chn_latency = CHN_LATENCY_DEFAULT;
52164614SariffTUNABLE_INT("hw.snd.latency", &chn_latency);
53164614Sariff
5483613Scgstatic int
55164614Sariffsysctl_hw_snd_latency(SYSCTL_HANDLER_ARGS)
5683613Scg{
5783613Scg	int err, val;
5883613Scg
59164614Sariff	val = chn_latency;
60170289Sdwmalone	err = sysctl_handle_int(oidp, &val, 0, req);
61168243Sariff	if (err != 0 || req->newptr == NULL)
62168243Sariff		return err;
63164614Sariff	if (val < CHN_LATENCY_MIN || val > CHN_LATENCY_MAX)
6483613Scg		err = EINVAL;
6583613Scg	else
66164614Sariff		chn_latency = val;
6783613Scg
6883613Scg	return err;
6983613Scg}
70164614SariffSYSCTL_PROC(_hw_snd, OID_AUTO, latency, CTLTYPE_INT | CTLFLAG_RW,
71164614Sariff	0, sizeof(int), sysctl_hw_snd_latency, "I",
72164614Sariff	"buffering latency (0=low ... 10=high)");
7383613Scg
74164614Sariffint chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT;
75164614SariffTUNABLE_INT("hw.snd.latency_profile", &chn_latency_profile);
76164614Sariff
77164614Sariffstatic int
78164614Sariffsysctl_hw_snd_latency_profile(SYSCTL_HANDLER_ARGS)
79164614Sariff{
80164614Sariff	int err, val;
81164614Sariff
82164614Sariff	val = chn_latency_profile;
83170289Sdwmalone	err = sysctl_handle_int(oidp, &val, 0, req);
84168243Sariff	if (err != 0 || req->newptr == NULL)
85168243Sariff		return err;
86164614Sariff	if (val < CHN_LATENCY_PROFILE_MIN || val > CHN_LATENCY_PROFILE_MAX)
87164614Sariff		err = EINVAL;
88164614Sariff	else
89164614Sariff		chn_latency_profile = val;
90164614Sariff
91164614Sariff	return err;
92164614Sariff}
93164614SariffSYSCTL_PROC(_hw_snd, OID_AUTO, latency_profile, CTLTYPE_INT | CTLFLAG_RW,
94164614Sariff	0, sizeof(int), sysctl_hw_snd_latency_profile, "I",
95164614Sariff	"buffering latency profile (0=aggresive 1=safe)");
96164614Sariff
97167645Sariffstatic int chn_timeout = CHN_TIMEOUT;
98168243SariffTUNABLE_INT("hw.snd.timeout", &chn_timeout);
99167645Sariff#ifdef SND_DEBUG
100167645Sariffstatic int
101167645Sariffsysctl_hw_snd_timeout(SYSCTL_HANDLER_ARGS)
102167645Sariff{
103167645Sariff	int err, val;
104167645Sariff
105167645Sariff	val = chn_timeout;
106170289Sdwmalone	err = sysctl_handle_int(oidp, &val, 0, req);
107168243Sariff	if (err != 0 || req->newptr == NULL)
108168243Sariff		return err;
109167645Sariff	if (val < CHN_TIMEOUT_MIN || val > CHN_TIMEOUT_MAX)
110167645Sariff		err = EINVAL;
111167645Sariff	else
112167645Sariff		chn_timeout = val;
113167645Sariff
114167645Sariff	return err;
115167645Sariff}
116167645SariffSYSCTL_PROC(_hw_snd, OID_AUTO, timeout, CTLTYPE_INT | CTLFLAG_RW,
117167645Sariff	0, sizeof(int), sysctl_hw_snd_timeout, "I",
118168243Sariff	"interrupt timeout (1 - 10) seconds");
119167645Sariff#endif
120167645Sariff
121193640Sariffstatic int chn_vpc_autoreset = 1;
122193640SariffTUNABLE_INT("hw.snd.vpc_autoreset", &chn_vpc_autoreset);
123193640SariffSYSCTL_INT(_hw_snd, OID_AUTO, vpc_autoreset, CTLFLAG_RW,
124193640Sariff	&chn_vpc_autoreset, 0, "automatically reset channels volume to 0db");
125193640Sariff
126193640Sariffstatic int chn_vol_0db_pcm = SND_VOL_0DB_PCM;
127214332SmavTUNABLE_INT("hw.snd.vpc_0db", &chn_vol_0db_pcm);
128193640Sariff
129193640Sariffstatic void
130193640Sariffchn_vpc_proc(int reset, int db)
131193640Sariff{
132193640Sariff	struct snddev_info *d;
133193640Sariff	struct pcm_channel *c;
134193640Sariff	int i;
135193640Sariff
136193640Sariff	for (i = 0; pcm_devclass != NULL &&
137193640Sariff	    i < devclass_get_maxunit(pcm_devclass); i++) {
138193640Sariff		d = devclass_get_softc(pcm_devclass, i);
139193640Sariff		if (!PCM_REGISTERED(d))
140193640Sariff			continue;
141193640Sariff		PCM_LOCK(d);
142193640Sariff		PCM_WAIT(d);
143193640Sariff		PCM_ACQUIRE(d);
144193640Sariff		CHN_FOREACH(c, d, channels.pcm) {
145193640Sariff			CHN_LOCK(c);
146193640Sariff			CHN_SETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_VOL_0DB, db);
147193640Sariff			if (reset != 0)
148193640Sariff				chn_vpc_reset(c, SND_VOL_C_PCM, 1);
149193640Sariff			CHN_UNLOCK(c);
150193640Sariff		}
151193640Sariff		PCM_RELEASE(d);
152193640Sariff		PCM_UNLOCK(d);
153193640Sariff	}
154193640Sariff}
155193640Sariff
156193640Sariffstatic int
157193640Sariffsysctl_hw_snd_vpc_0db(SYSCTL_HANDLER_ARGS)
158193640Sariff{
159193640Sariff	int err, val;
160193640Sariff
161193640Sariff	val = chn_vol_0db_pcm;
162193640Sariff	err = sysctl_handle_int(oidp, &val, 0, req);
163193640Sariff	if (err != 0 || req->newptr == NULL)
164193640Sariff		return (err);
165193640Sariff	if (val < SND_VOL_0DB_MIN || val > SND_VOL_0DB_MAX)
166193640Sariff		return (EINVAL);
167193640Sariff
168193640Sariff	chn_vol_0db_pcm = val;
169193640Sariff	chn_vpc_proc(0, val);
170193640Sariff
171193640Sariff	return (0);
172193640Sariff}
173193640SariffSYSCTL_PROC(_hw_snd, OID_AUTO, vpc_0db, CTLTYPE_INT | CTLFLAG_RW,
174193640Sariff	0, sizeof(int), sysctl_hw_snd_vpc_0db, "I",
175193640Sariff	"0db relative level");
176193640Sariff
177193640Sariffstatic int
178193640Sariffsysctl_hw_snd_vpc_reset(SYSCTL_HANDLER_ARGS)
179193640Sariff{
180193640Sariff	int err, val;
181193640Sariff
182193640Sariff	val = 0;
183193640Sariff	err = sysctl_handle_int(oidp, &val, 0, req);
184193640Sariff	if (err != 0 || req->newptr == NULL || val == 0)
185193640Sariff		return (err);
186193640Sariff
187193640Sariff	chn_vol_0db_pcm = SND_VOL_0DB_PCM;
188193640Sariff	chn_vpc_proc(1, SND_VOL_0DB_PCM);
189193640Sariff
190193640Sariff	return (0);
191193640Sariff}
192193640SariffSYSCTL_PROC(_hw_snd, OID_AUTO, vpc_reset, CTLTYPE_INT | CTLFLAG_RW,
193193640Sariff	0, sizeof(int), sysctl_hw_snd_vpc_reset, "I",
194193640Sariff	"reset volume on all channels");
195193640Sariff
196167645Sariffstatic int chn_usefrags = 0;
197167645SariffTUNABLE_INT("hw.snd.usefrags", &chn_usefrags);
198167645Sariffstatic int chn_syncdelay = -1;
199167645SariffTUNABLE_INT("hw.snd.syncdelay", &chn_syncdelay);
200167645Sariff#ifdef SND_DEBUG
201167645SariffSYSCTL_INT(_hw_snd, OID_AUTO, usefrags, CTLFLAG_RW,
202167645Sariff	&chn_usefrags, 1, "prefer setfragments() over setblocksize()");
203167645SariffSYSCTL_INT(_hw_snd, OID_AUTO, syncdelay, CTLFLAG_RW,
204167645Sariff	&chn_syncdelay, 1,
205167645Sariff	"append (0-1000) millisecond trailing buffer delay on each sync");
206167645Sariff#endif
207167645Sariff
208162588Snetchild/**
209162588Snetchild * @brief Channel sync group lock
210162588Snetchild *
211162588Snetchild * Clients should acquire this lock @b without holding any channel locks
212162588Snetchild * before touching syncgroups or the main syncgroup list.
213162588Snetchild */
214162588Snetchildstruct mtx snd_pcm_syncgroups_mtx;
215162588SnetchildMTX_SYSINIT(pcm_syncgroup, &snd_pcm_syncgroups_mtx, "PCM channel sync group lock", MTX_DEF);
216162588Snetchild/**
217162588Snetchild * @brief syncgroups' master list
218162588Snetchild *
219162588Snetchild * Each time a channel syncgroup is created, it's added to this list.  This
220162588Snetchild * list should only be accessed with @sa snd_pcm_syncgroups_mtx held.
221162588Snetchild *
222162588Snetchild * See SNDCTL_DSP_SYNCGROUP for more information.
223162588Snetchild */
224201145Santoinestruct pcm_synclist snd_pcm_syncgroups = SLIST_HEAD_INITIALIZER(snd_pcm_syncgroups);
225162588Snetchild
22674763Scgstatic void
227123156Smatkchn_lockinit(struct pcm_channel *c, int dir)
22874763Scg{
229170815Sariff	switch (dir) {
230126367Struckman	case PCMDIR_PLAY:
231126367Struckman		c->lock = snd_mtxcreate(c->name, "pcm play channel");
232170815Sariff		cv_init(&c->intr_cv, "pcmwr");
233126367Struckman		break;
234170161Sariff	case PCMDIR_PLAY_VIRTUAL:
235170161Sariff		c->lock = snd_mtxcreate(c->name, "pcm virtual play channel");
236170815Sariff		cv_init(&c->intr_cv, "pcmwrv");
237170161Sariff		break;
238126367Struckman	case PCMDIR_REC:
239126367Struckman		c->lock = snd_mtxcreate(c->name, "pcm record channel");
240170815Sariff		cv_init(&c->intr_cv, "pcmrd");
241126367Struckman		break;
242170161Sariff	case PCMDIR_REC_VIRTUAL:
243170161Sariff		c->lock = snd_mtxcreate(c->name, "pcm virtual record channel");
244170815Sariff		cv_init(&c->intr_cv, "pcmrdv");
245126367Struckman		break;
246193640Sariff	default:
247193640Sariff		panic("%s(): Invalid direction=%d", __func__, dir);
248126367Struckman		break;
249126367Struckman	}
250162588Snetchild
251170815Sariff	cv_init(&c->cv, "pcmchn");
25274763Scg}
25350724Scg
25474763Scgstatic void
25574763Scgchn_lockdestroy(struct pcm_channel *c)
25674763Scg{
257170815Sariff	CHN_LOCKASSERT(c);
258170815Sariff
259170815Sariff	CHN_BROADCAST(&c->cv);
260170815Sariff	CHN_BROADCAST(&c->intr_cv);
261170815Sariff
262170815Sariff	cv_destroy(&c->cv);
263170815Sariff	cv_destroy(&c->intr_cv);
264170815Sariff
26574763Scg	snd_mtxfree(c->lock);
26674763Scg}
26750724Scg
268162588Snetchild/**
269162588Snetchild * @brief Determine channel is ready for I/O
270162588Snetchild *
271162588Snetchild * @retval 1 = ready for I/O
272162588Snetchild * @retval 0 = not ready for I/O
273162588Snetchild */
27450724Scgstatic int
27574763Scgchn_polltrigger(struct pcm_channel *c)
27650724Scg{
27774763Scg	struct snd_dbuf *bs = c->bufsoft;
278193640Sariff	u_int delta;
27950724Scg
28074763Scg	CHN_LOCKASSERT(c);
281193640Sariff
282193640Sariff	if (c->flags & CHN_F_MMAP) {
283193640Sariff		if (sndbuf_getprevtotal(bs) < c->lw)
284193640Sariff			delta = c->lw;
28574797Scg		else
286193640Sariff			delta = sndbuf_gettotal(bs) - sndbuf_getprevtotal(bs);
28774763Scg	} else {
288193640Sariff		if (c->direction == PCMDIR_PLAY)
289193640Sariff			delta = sndbuf_getfree(bs);
290193640Sariff		else
291193640Sariff			delta = sndbuf_getready(bs);
29274763Scg	}
293193640Sariff
294193640Sariff	return ((delta < c->lw) ? 0 : 1);
29550724Scg}
29650724Scg
297193640Sariffstatic void
29874763Scgchn_pollreset(struct pcm_channel *c)
29950724Scg{
30050724Scg
30174763Scg	CHN_LOCKASSERT(c);
302193640Sariff	sndbuf_updateprevtotal(c->bufsoft);
30350724Scg}
30450724Scg
30550724Scgstatic void
30674763Scgchn_wakeup(struct pcm_channel *c)
30750724Scg{
308170815Sariff	struct snd_dbuf *bs;
309170161Sariff	struct pcm_channel *ch;
31050724Scg
31174763Scg	CHN_LOCKASSERT(c);
312170815Sariff
313170815Sariff	bs = c->bufsoft;
314170815Sariff
315170815Sariff	if (CHN_EMPTY(c, children.busy)) {
316125982Smatk		if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))
317126030Smatk			selwakeuppri(sndbuf_getsel(bs), PRIBIO);
318170815Sariff		if (c->flags & CHN_F_SLEEPING) {
319170815Sariff			/*
320170815Sariff			 * Ok, I can just panic it right here since it is
321170815Sariff			 * quite obvious that we never allow multiple waiters
322170815Sariff			 * from userland. I'm too generous...
323170815Sariff			 */
324170815Sariff			CHN_BROADCAST(&c->intr_cv);
325170161Sariff		}
326125982Smatk	} else {
327170161Sariff		CHN_FOREACH(ch, c, children.busy) {
328170161Sariff			CHN_LOCK(ch);
329170161Sariff			chn_wakeup(ch);
330170161Sariff			CHN_UNLOCK(ch);
331125982Smatk		}
332125982Smatk	}
33350724Scg}
33450724Scg
33574763Scgstatic int
336170815Sariffchn_sleep(struct pcm_channel *c, int timeout)
33754535Scg{
33874763Scg	int ret;
33954535Scg
34074763Scg	CHN_LOCKASSERT(c);
341170161Sariff
342170815Sariff	if (c->flags & CHN_F_DEAD)
343170815Sariff		return (EINVAL);
344170815Sariff
345170161Sariff	c->flags |= CHN_F_SLEEPING;
346170815Sariff	ret = cv_timedwait_sig(&c->intr_cv, c->lock, timeout);
347170161Sariff	c->flags &= ~CHN_F_SLEEPING;
34874763Scg
349170815Sariff	return ((c->flags & CHN_F_DEAD) ? EINVAL : ret);
35054535Scg}
35154535Scg
35254535Scg/*
35350724Scg * chn_dmaupdate() tracks the status of a dma transfer,
354150825Snetchild * updating pointers.
35550724Scg */
35654155Scg
35774763Scgstatic unsigned int
35874763Scgchn_dmaupdate(struct pcm_channel *c)
35950724Scg{
36074763Scg	struct snd_dbuf *b = c->bufhard;
36174763Scg	unsigned int delta, old, hwptr, amt;
36250724Scg
36377269Scg	KASSERT(sndbuf_getsize(b) > 0, ("bufsize == 0"));
36474763Scg	CHN_LOCKASSERT(c);
36588382Scg
36674763Scg	old = sndbuf_gethwptr(b);
36754155Scg	hwptr = chn_getptr(c);
36874763Scg	delta = (sndbuf_getsize(b) + hwptr - old) % sndbuf_getsize(b);
36974763Scg	sndbuf_sethwptr(b, hwptr);
37074763Scg
37172136Scg	if (c->direction == PCMDIR_PLAY) {
372167645Sariff		amt = min(delta, sndbuf_getready(b));
373193640Sariff		amt -= amt % sndbuf_getalign(b);
37474763Scg		if (amt > 0)
37574763Scg			sndbuf_dispose(b, NULL, amt);
37650724Scg	} else {
377167645Sariff		amt = min(delta, sndbuf_getfree(b));
378193640Sariff		amt -= amt % sndbuf_getalign(b);
37974763Scg		if (amt > 0)
38074763Scg		       sndbuf_acquire(b, NULL, amt);
38150724Scg	}
382167645Sariff	if (snd_verbose > 3 && CHN_STARTED(c) && delta == 0) {
383167645Sariff		device_printf(c->dev, "WARNING: %s DMA completion "
384164614Sariff			"too fast/slow ! hwptr=%u, old=%u "
385164614Sariff			"delta=%u amt=%u ready=%u free=%u\n",
386167645Sariff			CHN_DIRSTR(c), hwptr, old, delta, amt,
387164614Sariff			sndbuf_getready(b), sndbuf_getfree(b));
388164614Sariff	}
38974763Scg
39074763Scg	return delta;
39150724Scg}
39250724Scg
393193640Sariffstatic void
39474763Scgchn_wrfeed(struct pcm_channel *c)
39572136Scg{
39674763Scg    	struct snd_dbuf *b = c->bufhard;
39774763Scg    	struct snd_dbuf *bs = c->bufsoft;
398230845Smav	unsigned int amt, want, wasfree;
39972136Scg
40074763Scg	CHN_LOCKASSERT(c);
40172136Scg
402193640Sariff	if ((c->flags & CHN_F_MMAP) && !(c->flags & CHN_F_CLOSING))
40388382Scg		sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
40488382Scg
405230845Smav	wasfree = sndbuf_getfree(b);
406230845Smav	want = min(sndbuf_getsize(b),
407230845Smav	    imax(0, sndbuf_xbytes(sndbuf_getsize(bs), bs, b) -
408230845Smav	     sndbuf_getready(b)));
409230845Smav	amt = min(wasfree, want);
410193640Sariff	if (amt > 0)
411193640Sariff		sndbuf_feed(bs, b, c, c->feeder, amt);
41288382Scg
413153868Sariff	/*
414153868Sariff	 * Possible xruns. There should be no empty space left in buffer.
415153868Sariff	 */
416230845Smav	if (sndbuf_getready(b) < want)
417153868Sariff		c->xruns++;
418148606Snetchild
419230845Smav	if (sndbuf_getfree(b) < wasfree)
42074763Scg		chn_wakeup(c);
421193640Sariff}
42288382Scg
423193640Sariff#if 0
424193640Sariffstatic void
425193640Sariffchn_wrupdate(struct pcm_channel *c)
426193640Sariff{
427193640Sariff
428193640Sariff	CHN_LOCKASSERT(c);
429193640Sariff	KASSERT(c->direction == PCMDIR_PLAY, ("%s(): bad channel", __func__));
430193640Sariff
431193640Sariff	if ((c->flags & (CHN_F_MMAP | CHN_F_VIRTUAL)) || CHN_STOPPED(c))
432193640Sariff		return;
433193640Sariff	chn_dmaupdate(c);
434193640Sariff	chn_wrfeed(c);
435193640Sariff	/* tell the driver we've updated the primary buffer */
436193640Sariff	chn_trigger(c, PCMTRIG_EMLDMAWR);
43754535Scg}
438193640Sariff#endif
43954535Scg
44050724Scgstatic void
44174763Scgchn_wrintr(struct pcm_channel *c)
44250724Scg{
44350724Scg
44474763Scg	CHN_LOCKASSERT(c);
44574763Scg	/* update pointers in primary buffer */
44674797Scg	chn_dmaupdate(c);
44774763Scg	/* ...and feed from secondary to primary */
448193640Sariff	chn_wrfeed(c);
44974763Scg	/* tell the driver we've updated the primary buffer */
45074797Scg	chn_trigger(c, PCMTRIG_EMLDMAWR);
45150724Scg}
45250724Scg
45350724Scg/*
45482479Scg * user write routine - uiomove data into secondary buffer, trigger if necessary
45574763Scg * if blocking, sleep, rinse and repeat.
45650724Scg *
45774763Scg * called externally, so must handle locking
45850724Scg */
45950724Scg
46050724Scgint
46174763Scgchn_write(struct pcm_channel *c, struct uio *buf)
46250724Scg{
46374763Scg	struct snd_dbuf *bs = c->bufsoft;
464123013Smatk	void *off;
465167645Sariff	int ret, timeout, sz, t, p;
46650724Scg
46774763Scg	CHN_LOCKASSERT(c);
46855204Scg
46974763Scg	ret = 0;
470167645Sariff	timeout = chn_timeout * hz;
471162588Snetchild
472167645Sariff	while (ret == 0 && buf->uio_resid > 0) {
473167645Sariff		sz = min(buf->uio_resid, sndbuf_getfree(bs));
474167645Sariff		if (sz > 0) {
475123013Smatk			/*
476123013Smatk			 * The following assumes that the free space in
477123013Smatk			 * the buffer can never be less around the
478123013Smatk			 * unlock-uiomove-lock sequence.
479123013Smatk			 */
480167645Sariff			while (ret == 0 && sz > 0) {
481123013Smatk				p = sndbuf_getfreeptr(bs);
482167645Sariff				t = min(sz, sndbuf_getsize(bs) - p);
483123013Smatk				off = sndbuf_getbufofs(bs, p);
484123013Smatk				CHN_UNLOCK(c);
485123013Smatk				ret = uiomove(off, t, buf);
486123013Smatk				CHN_LOCK(c);
487167645Sariff				sz -= t;
488167645Sariff				sndbuf_acquire(bs, NULL, t);
489123013Smatk			}
490123013Smatk			ret = 0;
491174220Sariff			if (CHN_STOPPED(c) && !(c->flags & CHN_F_NOTRIGGER)) {
492170815Sariff				ret = chn_start(c, 0);
493170815Sariff				if (ret != 0)
494170815Sariff					c->flags |= CHN_F_DEAD;
495170815Sariff			}
496167645Sariff		} else if (c->flags & (CHN_F_NBIO | CHN_F_NOTRIGGER)) {
497167645Sariff			/**
498167645Sariff			 * @todo Evaluate whether EAGAIN is truly desirable.
499167645Sariff			 * 	 4Front drivers behave like this, but I'm
500167645Sariff			 * 	 not sure if it at all violates the "write
501167645Sariff			 * 	 should be allowed to block" model.
502167645Sariff			 *
503167645Sariff			 * 	 The idea is that, while set with CHN_F_NOTRIGGER,
504167645Sariff			 * 	 a channel isn't playing, *but* without this we
505167645Sariff			 * 	 end up with "interrupt timeout / channel dead".
506167645Sariff			 */
507167645Sariff			ret = EAGAIN;
508167645Sariff		} else {
509170815Sariff   			ret = chn_sleep(c, timeout);
510167645Sariff			if (ret == EAGAIN) {
511167645Sariff				ret = EINVAL;
512167645Sariff				c->flags |= CHN_F_DEAD;
513193640Sariff				device_printf(c->dev, "%s(): %s: "
514193640Sariff				    "play interrupt timeout, channel dead\n",
515193640Sariff				    __func__, c->name);
516167645Sariff			} else if (ret == ERESTART || ret == EINTR)
517167645Sariff				c->flags |= CHN_F_ABORTING;
51874763Scg		}
51974763Scg	}
52072136Scg
521170815Sariff	return (ret);
52250724Scg}
52350724Scg
52454535Scg/*
52582479Scg * Feed new data from the read buffer. Can be called in the bottom half.
52654535Scg */
527193640Sariffstatic void
52874763Scgchn_rdfeed(struct pcm_channel *c)
52954535Scg{
53074763Scg    	struct snd_dbuf *b = c->bufhard;
53174763Scg    	struct snd_dbuf *bs = c->bufsoft;
532193640Sariff	unsigned int amt;
53354535Scg
53474763Scg	CHN_LOCKASSERT(c);
53554535Scg
536193640Sariff	if (c->flags & CHN_F_MMAP)
537170815Sariff		sndbuf_dispose(bs, NULL, sndbuf_getready(bs));
538170815Sariff
539149953Snetchild	amt = sndbuf_getfree(bs);
540193640Sariff	if (amt > 0)
541193640Sariff		sndbuf_feed(b, bs, c, c->feeder, amt);
54274763Scg
54383081Scg	amt = sndbuf_getready(b);
544153952Sariff	if (amt > 0) {
545153952Sariff		c->xruns++;
546153952Sariff		sndbuf_dispose(b, NULL, amt);
547153952Sariff	}
54874763Scg
549168243Sariff	if (sndbuf_getready(bs) > 0)
550168243Sariff		chn_wakeup(c);
55154535Scg}
55254535Scg
553193640Sariff#if 0
554193640Sariffstatic void
55574763Scgchn_rdupdate(struct pcm_channel *c)
55654535Scg{
55754535Scg
55874763Scg	CHN_LOCKASSERT(c);
55974763Scg	KASSERT(c->direction == PCMDIR_REC, ("chn_rdupdate on bad channel"));
56054535Scg
561193640Sariff	if ((c->flags & (CHN_F_MMAP | CHN_F_VIRTUAL)) || CHN_STOPPED(c))
56274763Scg		return;
56374763Scg	chn_trigger(c, PCMTRIG_EMLDMARD);
56474763Scg	chn_dmaupdate(c);
565193640Sariff	chn_rdfeed(c);
56654535Scg}
567193640Sariff#endif
56854535Scg
56950724Scg/* read interrupt routine. Must be called with interrupts blocked. */
57050724Scgstatic void
57174763Scgchn_rdintr(struct pcm_channel *c)
57250724Scg{
57350724Scg
57474763Scg	CHN_LOCKASSERT(c);
57582479Scg	/* tell the driver to update the primary buffer if non-dma */
57674797Scg	chn_trigger(c, PCMTRIG_EMLDMARD);
57782492Scg	/* update pointers in primary buffer */
57874797Scg	chn_dmaupdate(c);
57974763Scg	/* ...and feed from primary to secondary */
580193640Sariff	chn_rdfeed(c);
58150724Scg}
58250724Scg
58350724Scg/*
58489689Scg * user read routine - trigger if necessary, uiomove data from secondary buffer
58574763Scg * if blocking, sleep, rinse and repeat.
58650724Scg *
58774763Scg * called externally, so must handle locking
58850724Scg */
58950724Scg
59050724Scgint
59174763Scgchn_read(struct pcm_channel *c, struct uio *buf)
59250724Scg{
593164614Sariff	struct snd_dbuf *bs = c->bufsoft;
594123013Smatk	void *off;
595167645Sariff	int ret, timeout, sz, t, p;
59650724Scg
59774763Scg	CHN_LOCKASSERT(c);
598167645Sariff
599174220Sariff	if (CHN_STOPPED(c) && !(c->flags & CHN_F_NOTRIGGER)) {
600170815Sariff		ret = chn_start(c, 0);
601170815Sariff		if (ret != 0) {
602170815Sariff			c->flags |= CHN_F_DEAD;
603170815Sariff			return (ret);
604170815Sariff		}
605170815Sariff	}
60655204Scg
60774763Scg	ret = 0;
608167645Sariff	timeout = chn_timeout * hz;
60955204Scg
610167645Sariff	while (ret == 0 && buf->uio_resid > 0) {
611167645Sariff		sz = min(buf->uio_resid, sndbuf_getready(bs));
61285406Scg		if (sz > 0) {
613123013Smatk			/*
614123013Smatk			 * The following assumes that the free space in
615123013Smatk			 * the buffer can never be less around the
616123013Smatk			 * unlock-uiomove-lock sequence.
617123013Smatk			 */
618167645Sariff			while (ret == 0 && sz > 0) {
619123013Smatk				p = sndbuf_getreadyptr(bs);
620167645Sariff				t = min(sz, sndbuf_getsize(bs) - p);
621123013Smatk				off = sndbuf_getbufofs(bs, p);
622123013Smatk				CHN_UNLOCK(c);
623123013Smatk				ret = uiomove(off, t, buf);
624123013Smatk				CHN_LOCK(c);
625167645Sariff				sz -= t;
626167645Sariff				sndbuf_dispose(bs, NULL, t);
627123013Smatk			}
628123013Smatk			ret = 0;
629167645Sariff		} else if (c->flags & (CHN_F_NBIO | CHN_F_NOTRIGGER))
630167645Sariff			ret = EAGAIN;
631167645Sariff		else {
632170815Sariff   			ret = chn_sleep(c, timeout);
633167645Sariff			if (ret == EAGAIN) {
634167645Sariff				ret = EINVAL;
635167645Sariff				c->flags |= CHN_F_DEAD;
636193640Sariff				device_printf(c->dev, "%s(): %s: "
637193640Sariff				    "record interrupt timeout, channel dead\n",
638193640Sariff				    __func__, c->name);
639167645Sariff			} else if (ret == ERESTART || ret == EINTR)
640167645Sariff				c->flags |= CHN_F_ABORTING;
64174763Scg		}
64274763Scg	}
64372136Scg
644170815Sariff	return (ret);
64550724Scg}
64650724Scg
64750724Scgvoid
648193640Sariffchn_intr_locked(struct pcm_channel *c)
64950724Scg{
650193640Sariff
651193640Sariff	CHN_LOCKASSERT(c);
652193640Sariff
65382479Scg	c->interrupts++;
654193640Sariff
65559324Scg	if (c->direction == PCMDIR_PLAY)
65659324Scg		chn_wrintr(c);
65759324Scg	else
65859324Scg		chn_rdintr(c);
659193640Sariff}
660193640Sariff
661193640Sariffvoid
662193640Sariffchn_intr(struct pcm_channel *c)
663193640Sariff{
664193640Sariff
665193640Sariff	if (CHN_LOCKOWNED(c)) {
666193640Sariff		chn_intr_locked(c);
667193640Sariff		return;
668184610Salfred	}
669193640Sariff
670193640Sariff	CHN_LOCK(c);
671193640Sariff	chn_intr_locked(c);
672193640Sariff	CHN_UNLOCK(c);
67350724Scg}
67450724Scg
67560961Scgu_int32_t
67674763Scgchn_start(struct pcm_channel *c, int force)
67760961Scg{
67882492Scg	u_int32_t i, j;
67974763Scg	struct snd_dbuf *b = c->bufhard;
68074763Scg	struct snd_dbuf *bs = c->bufsoft;
681170815Sariff	int err;
68260961Scg
68374763Scg	CHN_LOCKASSERT(c);
68474763Scg	/* if we're running, or if we're prevented from triggering, bail */
685167645Sariff	if (CHN_STARTED(c) || ((c->flags & CHN_F_NOTRIGGER) && !force))
686170815Sariff		return (EINVAL);
68774763Scg
688170815Sariff	err = 0;
689170815Sariff
690164614Sariff	if (force) {
691164614Sariff		i = 1;
692164614Sariff		j = 0;
693164614Sariff	} else {
694164614Sariff		if (c->direction == PCMDIR_REC) {
695164614Sariff			i = sndbuf_getfree(bs);
696167645Sariff			j = (i > 0) ? 1 : sndbuf_getready(b);
697164614Sariff		} else {
698167645Sariff			if (sndbuf_getfree(bs) == 0) {
699167645Sariff				i = 1;
700167645Sariff				j = 0;
701167645Sariff			} else {
702167645Sariff				struct snd_dbuf *pb;
703164614Sariff
704170815Sariff				pb = CHN_BUF_PARENT(c, b);
705167645Sariff				i = sndbuf_xbytes(sndbuf_getready(bs), bs, pb);
706193640Sariff				j = sndbuf_getalign(pb);
707167645Sariff			}
708164614Sariff		}
709170161Sariff		if (snd_verbose > 3 && CHN_EMPTY(c, children))
710193640Sariff			device_printf(c->dev, "%s(): %s (%s) threshold "
711193640Sariff			    "i=%d j=%d\n", __func__, CHN_DIRSTR(c),
712193640Sariff			    (c->flags & CHN_F_VIRTUAL) ? "virtual" :
713193640Sariff			    "hardware", i, j);
714164614Sariff	}
715164614Sariff
716164614Sariff	if (i >= j) {
71774763Scg		c->flags |= CHN_F_TRIGGERED;
71874763Scg		sndbuf_setrun(b, 1);
719193640Sariff		if (c->flags & CHN_F_CLOSING)
720193640Sariff			c->feedcount = 2;
721193640Sariff		else {
722193640Sariff			c->feedcount = 0;
723193640Sariff			c->interrupts = 0;
724193640Sariff			c->xruns = 0;
725193640Sariff		}
726193640Sariff		if (c->parentchannel == NULL) {
727193640Sariff			if (c->direction == PCMDIR_PLAY)
728230845Smav				sndbuf_fillsilence_rl(b,
729230845Smav				    sndbuf_xbytes(sndbuf_getsize(bs), bs, b));
730164614Sariff			if (snd_verbose > 3)
731193640Sariff				device_printf(c->dev,
732193640Sariff				    "%s(): %s starting! (%s/%s) "
733193640Sariff				    "(ready=%d force=%d i=%d j=%d "
734193640Sariff				    "intrtimeout=%u latency=%dms)\n",
735164614Sariff				    __func__,
736164614Sariff				    (c->flags & CHN_F_HAS_VCHAN) ?
737193640Sariff				    "VCHAN PARENT" : "HW", CHN_DIRSTR(c),
738164614Sariff				    (c->flags & CHN_F_CLOSING) ? "closing" :
739164614Sariff				    "running",
740164614Sariff				    sndbuf_getready(b),
741167645Sariff				    force, i, j, c->timeout,
742167645Sariff				    (sndbuf_getsize(b) * 1000) /
743193640Sariff				    (sndbuf_getalign(b) * sndbuf_getspd(b)));
744164614Sariff		}
745170815Sariff		err = chn_trigger(c, PCMTRIG_START);
74660961Scg	}
74774763Scg
748170815Sariff	return (err);
74960961Scg}
75060961Scg
75165340Scgvoid
75274763Scgchn_resetbuf(struct pcm_channel *c)
75350724Scg{
75474763Scg	struct snd_dbuf *b = c->bufhard;
75574763Scg	struct snd_dbuf *bs = c->bufsoft;
75650724Scg
75759246Scg	c->blocks = 0;
75870291Scg	sndbuf_reset(b);
75970291Scg	sndbuf_reset(bs);
76050724Scg}
76150724Scg
76250724Scg/*
76355204Scg * chn_sync waits until the space in the given channel goes above
76450724Scg * a threshold. The threshold is checked against fl or rl respectively.
76550724Scg * Assume that the condition can become true, do not check here...
76650724Scg */
76750724Scgint
76874763Scgchn_sync(struct pcm_channel *c, int threshold)
76950724Scg{
770164614Sariff    	struct snd_dbuf *b, *bs;
771167663Sariff	int ret, count, hcount, minflush, resid, residp, syncdelay, blksz;
772167663Sariff	u_int32_t cflag;
77350724Scg
77474763Scg	CHN_LOCKASSERT(c);
775119095Scg
776170815Sariff	if (c->direction != PCMDIR_PLAY)
777170815Sariff		return (EINVAL);
778170815Sariff
779167645Sariff	bs = c->bufsoft;
780167645Sariff
781167645Sariff	if ((c->flags & (CHN_F_DEAD | CHN_F_ABORTING)) ||
782167645Sariff	    (threshold < 1 && sndbuf_getready(bs) < 1))
783170815Sariff		return (0);
784164614Sariff
785119095Scg	/* if we haven't yet started and nothing is buffered, else start*/
786167645Sariff	if (CHN_STOPPED(c)) {
787167645Sariff		if (threshold > 0 || sndbuf_getready(bs) > 0) {
788119095Scg			ret = chn_start(c, 1);
789170815Sariff			if (ret != 0)
790170815Sariff				return (ret);
791164614Sariff		} else
792170815Sariff			return (0);
793164614Sariff	}
794164614Sariff
795170815Sariff	b = CHN_BUF_PARENT(c, c->bufhard);
796164614Sariff
797167645Sariff	minflush = threshold + sndbuf_xbytes(sndbuf_getready(b), b, bs);
798167645Sariff
799167645Sariff	syncdelay = chn_syncdelay;
800167645Sariff
801167645Sariff	if (syncdelay < 0 && (threshold > 0 || sndbuf_getready(bs) > 0))
802167645Sariff		minflush += sndbuf_xbytes(sndbuf_getsize(b), b, bs);
803167645Sariff
804167645Sariff	/*
805167645Sariff	 * Append (0-1000) millisecond trailing buffer (if needed)
806167645Sariff	 * for slower / high latency hardwares (notably USB audio)
807167645Sariff	 * to avoid audible truncation.
808167645Sariff	 */
809167645Sariff	if (syncdelay > 0)
810193640Sariff		minflush += (sndbuf_getalign(bs) * sndbuf_getspd(bs) *
811167645Sariff		    ((syncdelay > 1000) ? 1000 : syncdelay)) / 1000;
812167645Sariff
813193640Sariff	minflush -= minflush % sndbuf_getalign(bs);
814164614Sariff
815164614Sariff	if (minflush > 0) {
816164614Sariff		threshold = min(minflush, sndbuf_getfree(bs));
817164614Sariff		sndbuf_clear(bs, threshold);
818164614Sariff		sndbuf_acquire(bs, NULL, threshold);
819164614Sariff		minflush -= threshold;
820164614Sariff	}
821164614Sariff
822164614Sariff	resid = sndbuf_getready(bs);
823164614Sariff	residp = resid;
824167645Sariff	blksz = sndbuf_getblksz(b);
825167645Sariff	if (blksz < 1) {
826193640Sariff		device_printf(c->dev,
827193640Sariff		    "%s(): WARNING: blksz < 1 ! maxsize=%d [%d/%d/%d]\n",
828167645Sariff		    __func__, sndbuf_getmaxsize(b), sndbuf_getsize(b),
829167645Sariff		    sndbuf_getblksz(b), sndbuf_getblkcnt(b));
830167645Sariff		if (sndbuf_getblkcnt(b) > 0)
831167645Sariff			blksz = sndbuf_getsize(b) / sndbuf_getblkcnt(b);
832167645Sariff		if (blksz < 1)
833167645Sariff			blksz = 1;
834167645Sariff	}
835167645Sariff	count = sndbuf_xbytes(minflush + resid, bs, b) / blksz;
836164614Sariff	hcount = count;
837164614Sariff	ret = 0;
838164614Sariff
839167645Sariff	if (snd_verbose > 3)
840193640Sariff		device_printf(c->dev, "%s(): [begin] timeout=%d count=%d "
841167645Sariff		    "minflush=%d resid=%d\n", __func__, c->timeout, count,
842167645Sariff		    minflush, resid);
843167645Sariff
844167663Sariff	cflag = c->flags & CHN_F_CLOSING;
845167663Sariff	c->flags |= CHN_F_CLOSING;
846167645Sariff	while (count > 0 && (resid > 0 || minflush > 0)) {
847170815Sariff		ret = chn_sleep(c, c->timeout);
848164614Sariff    		if (ret == ERESTART || ret == EINTR) {
849164614Sariff			c->flags |= CHN_F_ABORTING;
850164614Sariff			break;
851170815Sariff		} else if (ret == 0 || ret == EAGAIN) {
852164614Sariff			resid = sndbuf_getready(bs);
853164614Sariff			if (resid == residp) {
854164614Sariff				--count;
855164614Sariff				if (snd_verbose > 3)
856193640Sariff					device_printf(c->dev,
857193640Sariff					    "%s(): [stalled] timeout=%d "
858164614Sariff					    "count=%d hcount=%d "
859164614Sariff					    "resid=%d minflush=%d\n",
860164614Sariff					    __func__, c->timeout, count,
861164614Sariff					    hcount, resid, minflush);
862164614Sariff			} else if (resid < residp && count < hcount) {
863164614Sariff				++count;
864164614Sariff				if (snd_verbose > 3)
865193640Sariff					device_printf(c->dev,
866193640Sariff					    "%s((): [resume] timeout=%d "
867164614Sariff					    "count=%d hcount=%d "
868164614Sariff					    "resid=%d minflush=%d\n",
869164614Sariff					    __func__, c->timeout, count,
870164614Sariff					    hcount, resid, minflush);
871164614Sariff			}
872164614Sariff			if (minflush > 0 && sndbuf_getfree(bs) > 0) {
873164614Sariff				threshold = min(minflush,
874164614Sariff				    sndbuf_getfree(bs));
875164614Sariff				sndbuf_clear(bs, threshold);
876164614Sariff				sndbuf_acquire(bs, NULL, threshold);
877164614Sariff				resid = sndbuf_getready(bs);
878164614Sariff				minflush -= threshold;
879164614Sariff			}
880164614Sariff			residp = resid;
881170815Sariff		} else
882170815Sariff			break;
883119095Scg	}
884167663Sariff	c->flags &= ~CHN_F_CLOSING;
885167663Sariff	c->flags |= cflag;
886119095Scg
887164614Sariff	if (snd_verbose > 3)
888193640Sariff		device_printf(c->dev,
889193640Sariff		    "%s(): timeout=%d count=%d hcount=%d resid=%d residp=%d "
890164614Sariff		    "minflush=%d ret=%d\n",
891164614Sariff		    __func__, c->timeout, count, hcount, resid, residp,
892164614Sariff		    minflush, ret);
893164614Sariff
894170815Sariff    	return (0);
89550724Scg}
89650724Scg
89774763Scg/* called externally, handle locking */
89850724Scgint
89983366Sjulianchn_poll(struct pcm_channel *c, int ev, struct thread *td)
90050724Scg{
90174763Scg	struct snd_dbuf *bs = c->bufsoft;
90255204Scg	int ret;
90355204Scg
90478214Scg	CHN_LOCKASSERT(c);
905193640Sariff
906193640Sariff    	if (!(c->flags & (CHN_F_MMAP | CHN_F_TRIGGERED))) {
907170815Sariff		ret = chn_start(c, 1);
908170815Sariff		if (ret != 0)
909170815Sariff			return (0);
910170815Sariff	}
911193640Sariff
91255204Scg	ret = 0;
913193640Sariff	if (chn_polltrigger(c)) {
914193640Sariff		chn_pollreset(c);
91555204Scg		ret = ev;
916193640Sariff	} else
91783805Sjhb		selrecord(td, sndbuf_getsel(bs));
918193640Sariff
919170815Sariff	return (ret);
92050724Scg}
92150724Scg
92250724Scg/*
92374763Scg * chn_abort terminates a running dma transfer.  it may sleep up to 200ms.
92474763Scg * it returns the number of bytes that have not been transferred.
92574763Scg *
92677269Scg * called from: dsp_close, dsp_ioctl, with channel locked
92750724Scg */
92850724Scgint
92974763Scgchn_abort(struct pcm_channel *c)
93050724Scg{
93178362Scg    	int missing = 0;
93274763Scg    	struct snd_dbuf *b = c->bufhard;
93374763Scg    	struct snd_dbuf *bs = c->bufsoft;
93450724Scg
93574763Scg	CHN_LOCKASSERT(c);
936167645Sariff	if (CHN_STOPPED(c))
93761642Scg		return 0;
93854792Scg	c->flags |= CHN_F_ABORTING;
93974763Scg
94072136Scg	c->flags &= ~CHN_F_TRIGGERED;
94174763Scg	/* kill the channel */
94254792Scg	chn_trigger(c, PCMTRIG_ABORT);
94374763Scg	sndbuf_setrun(b, 0);
94477882Scg	if (!(c->flags & CHN_F_VIRTUAL))
94577882Scg		chn_dmaupdate(c);
946167645Sariff    	missing = sndbuf_getready(bs);
94774763Scg
94874763Scg	c->flags &= ~CHN_F_ABORTING;
94974797Scg	return missing;
95050724Scg}
95150724Scg
95250724Scg/*
95350724Scg * this routine tries to flush the dma transfer. It is called
954119095Scg * on a close of a playback channel.
955119095Scg * first, if there is data in the buffer, but the dma has not yet
956119095Scg * begun, we need to start it.
957119095Scg * next, we wait for the play buffer to drain
958119095Scg * finally, we stop the dma.
95974763Scg *
960119095Scg * called from: dsp_close, not valid for record channels.
96150724Scg */
96250724Scg
96350724Scgint
96474763Scgchn_flush(struct pcm_channel *c)
96550724Scg{
96674763Scg    	struct snd_dbuf *b = c->bufhard;
96750724Scg
96874763Scg	CHN_LOCKASSERT(c);
969119095Scg	KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel"));
970119095Scg    	DEB(printf("chn_flush: c->flags 0x%08x\n", c->flags));
97168413Scg
97274797Scg	c->flags |= CHN_F_CLOSING;
973164614Sariff	chn_sync(c, 0);
97474763Scg	c->flags &= ~CHN_F_TRIGGERED;
97574763Scg	/* kill the channel */
97674763Scg	chn_trigger(c, PCMTRIG_ABORT);
97774763Scg	sndbuf_setrun(b, 0);
97874763Scg
97950724Scg    	c->flags &= ~CHN_F_CLOSING;
98050724Scg    	return 0;
98150724Scg}
98250724Scg
98350724Scgint
984193640Sariffsnd_fmtvalid(uint32_t fmt, uint32_t *fmtlist)
98566308Scg{
98666308Scg	int i;
98766308Scg
988193640Sariff	for (i = 0; fmtlist[i] != 0; i++) {
989193640Sariff		if (fmt == fmtlist[i] ||
990193640Sariff		    ((fmt & AFMT_PASSTHROUGH) &&
991193640Sariff		    (AFMT_ENCODING(fmt) & fmtlist[i])))
992193640Sariff			return (1);
993193640Sariff	}
994193640Sariff
995193640Sariff	return (0);
99666308Scg}
99766308Scg
998193640Sariffstatic const struct {
999193640Sariff	char *name, *alias1, *alias2;
1000193640Sariff	uint32_t afmt;
1001193640Sariff} afmt_tab[] = {
1002193640Sariff	{  "alaw",  NULL, NULL, AFMT_A_LAW  },
1003193640Sariff	{ "mulaw",  NULL, NULL, AFMT_MU_LAW },
1004193640Sariff	{    "u8",   "8", NULL, AFMT_U8     },
1005193640Sariff	{    "s8",  NULL, NULL, AFMT_S8     },
1006193640Sariff#if BYTE_ORDER == LITTLE_ENDIAN
1007193640Sariff	{ "s16le", "s16", "16", AFMT_S16_LE },
1008193640Sariff	{ "s16be",  NULL, NULL, AFMT_S16_BE },
1009193640Sariff#else
1010193640Sariff	{ "s16le",  NULL, NULL, AFMT_S16_LE },
1011193640Sariff	{ "s16be", "s16", "16", AFMT_S16_BE },
1012193640Sariff#endif
1013193640Sariff	{ "u16le",  NULL, NULL, AFMT_U16_LE },
1014193640Sariff	{ "u16be",  NULL, NULL, AFMT_U16_BE },
1015193640Sariff	{ "s24le",  NULL, NULL, AFMT_S24_LE },
1016193640Sariff	{ "s24be",  NULL, NULL, AFMT_S24_BE },
1017193640Sariff	{ "u24le",  NULL, NULL, AFMT_U24_LE },
1018193640Sariff	{ "u24be",  NULL, NULL, AFMT_U24_BE },
1019193640Sariff#if BYTE_ORDER == LITTLE_ENDIAN
1020193640Sariff	{ "s32le", "s32", "32", AFMT_S32_LE },
1021193640Sariff	{ "s32be",  NULL, NULL, AFMT_S32_BE },
1022193640Sariff#else
1023193640Sariff	{ "s32le",  NULL, NULL, AFMT_S32_LE },
1024193640Sariff	{ "s32be", "s32", "32", AFMT_S32_BE },
1025193640Sariff#endif
1026193640Sariff	{ "u32le",  NULL, NULL, AFMT_U32_LE },
1027193640Sariff	{ "u32be",  NULL, NULL, AFMT_U32_BE },
1028193640Sariff	{   "ac3",  NULL, NULL, AFMT_AC3    },
1029193640Sariff	{    NULL,  NULL, NULL, 0           }
1030164614Sariff};
1031164614Sariff
1032193640Sariffstatic const struct {
1033193640Sariff	char *name, *alias1, *alias2;
1034193640Sariff	int matrix_id;
1035193640Sariff} matrix_id_tab[] = {
1036193640Sariff	{ "1.0",  "1",   "mono", SND_CHN_MATRIX_1_0     },
1037193640Sariff	{ "2.0",  "2", "stereo", SND_CHN_MATRIX_2_0     },
1038193640Sariff	{ "2.1", NULL,     NULL, SND_CHN_MATRIX_2_1     },
1039193640Sariff	{ "3.0",  "3",     NULL, SND_CHN_MATRIX_3_0     },
1040243138Smav	{ "3.1", NULL,     NULL, SND_CHN_MATRIX_3_1     },
1041193640Sariff	{ "4.0",  "4",   "quad", SND_CHN_MATRIX_4_0     },
1042193640Sariff	{ "4.1", NULL,     NULL, SND_CHN_MATRIX_4_1     },
1043193640Sariff	{ "5.0",  "5",     NULL, SND_CHN_MATRIX_5_0     },
1044193640Sariff	{ "5.1",  "6",     NULL, SND_CHN_MATRIX_5_1     },
1045193640Sariff	{ "6.0", NULL,     NULL, SND_CHN_MATRIX_6_0     },
1046193640Sariff	{ "6.1",  "7",     NULL, SND_CHN_MATRIX_6_1     },
1047243138Smav	{ "7.0", NULL,     NULL, SND_CHN_MATRIX_7_0     },
1048193640Sariff	{ "7.1",  "8",     NULL, SND_CHN_MATRIX_7_1     },
1049193640Sariff	{  NULL, NULL,     NULL, SND_CHN_MATRIX_UNKNOWN }
1050193640Sariff};
1051164614Sariff
1052193640Sariffuint32_t
1053193640Sariffsnd_str2afmt(const char *req)
1054164614Sariff{
1055193640Sariff	uint32_t i, afmt;
1056193640Sariff	int matrix_id;
1057193640Sariff	char b1[8], b2[8];
1058164614Sariff
1059193640Sariff	i = sscanf(req, "%5[^:]:%6s", b1, b2);
1060164614Sariff
1061193640Sariff	if (i == 1) {
1062193640Sariff		if (strlen(req) != strlen(b1))
1063193640Sariff			return (0);
1064193640Sariff		strlcpy(b2, "2.0", sizeof(b2));
1065193640Sariff	} else if (i == 2) {
1066193640Sariff		if (strlen(req) != (strlen(b1) + 1 + strlen(b2)))
1067193640Sariff			return (0);
1068193640Sariff	} else
1069193640Sariff		return (0);
1070164614Sariff
1071193640Sariff	afmt = 0;
1072193640Sariff	matrix_id = SND_CHN_MATRIX_UNKNOWN;
1073164614Sariff
1074193640Sariff	for (i = 0; afmt == 0 && afmt_tab[i].name != NULL; i++) {
1075193640Sariff		if (strcasecmp(afmt_tab[i].name, b1) == 0 ||
1076193640Sariff		    (afmt_tab[i].alias1 != NULL &&
1077193640Sariff		    strcasecmp(afmt_tab[i].alias1, b1) == 0) ||
1078193640Sariff		    (afmt_tab[i].alias2 != NULL &&
1079193640Sariff		    strcasecmp(afmt_tab[i].alias2, b1) == 0)) {
1080193640Sariff			afmt = afmt_tab[i].afmt;
1081193640Sariff			strlcpy(b1, afmt_tab[i].name, sizeof(b1));
1082193640Sariff		}
1083193640Sariff	}
1084164614Sariff
1085193640Sariff	if (afmt == 0)
1086193640Sariff		return (0);
1087193640Sariff
1088193640Sariff	for (i = 0; matrix_id == SND_CHN_MATRIX_UNKNOWN &&
1089193640Sariff	    matrix_id_tab[i].name != NULL; i++) {
1090193640Sariff		if (strcmp(matrix_id_tab[i].name, b2) == 0 ||
1091193640Sariff		    (matrix_id_tab[i].alias1 != NULL &&
1092193640Sariff		    strcmp(matrix_id_tab[i].alias1, b2) == 0) ||
1093193640Sariff		    (matrix_id_tab[i].alias2 != NULL &&
1094193640Sariff		    strcasecmp(matrix_id_tab[i].alias2, b2) == 0)) {
1095193640Sariff			matrix_id = matrix_id_tab[i].matrix_id;
1096193640Sariff			strlcpy(b2, matrix_id_tab[i].name, sizeof(b2));
1097164614Sariff		}
1098164614Sariff	}
1099164614Sariff
1100193640Sariff	if (matrix_id == SND_CHN_MATRIX_UNKNOWN)
1101193640Sariff		return (0);
1102193640Sariff
1103193640Sariff#ifndef _KERNEL
1104193640Sariff	printf("Parse OK: '%s' -> '%s:%s' %d\n", req, b1, b2,
1105193640Sariff	    (int)(b2[0]) - '0' + (int)(b2[2]) - '0');
1106193640Sariff#endif
1107193640Sariff
1108193640Sariff	return (SND_FORMAT(afmt, b2[0] - '0' + b2[2] - '0', b2[2] - '0'));
1109164614Sariff}
1110164614Sariff
1111193640Sariffuint32_t
1112193640Sariffsnd_afmt2str(uint32_t afmt, char *buf, size_t len)
1113164614Sariff{
1114193640Sariff	uint32_t i, enc, ch, ext;
1115193640Sariff	char tmp[AFMTSTR_LEN];
1116164614Sariff
1117193640Sariff	if (buf == NULL || len < AFMTSTR_LEN)
1118193640Sariff		return (0);
1119164614Sariff
1120193640Sariff
1121193640Sariff	bzero(tmp, sizeof(tmp));
1122193640Sariff
1123193640Sariff	enc = AFMT_ENCODING(afmt);
1124193640Sariff	ch = AFMT_CHANNEL(afmt);
1125193640Sariff	ext = AFMT_EXTCHANNEL(afmt);
1126193640Sariff
1127193640Sariff	for (i = 0; afmt_tab[i].name != NULL; i++) {
1128193640Sariff		if (enc == afmt_tab[i].afmt) {
1129193640Sariff			strlcpy(tmp, afmt_tab[i].name, sizeof(tmp));
1130193640Sariff			strlcat(tmp, ":", sizeof(tmp));
1131164614Sariff			break;
1132193640Sariff		}
1133164614Sariff	}
1134164614Sariff
1135193640Sariff	if (strlen(tmp) == 0)
1136193640Sariff		return (0);
1137193640Sariff
1138193640Sariff	for (i = 0; matrix_id_tab[i].name != NULL; i++) {
1139193640Sariff		if (ch == (matrix_id_tab[i].name[0] - '0' +
1140193640Sariff		    matrix_id_tab[i].name[2] - '0') &&
1141193640Sariff		    ext == (matrix_id_tab[i].name[2] - '0')) {
1142193640Sariff			strlcat(tmp, matrix_id_tab[i].name, sizeof(tmp));
1143164614Sariff			break;
1144164614Sariff		}
1145164614Sariff	}
1146164614Sariff
1147193640Sariff	if (strlen(tmp) == 0)
1148193640Sariff		return (0);
1149193640Sariff
1150193640Sariff	strlcpy(buf, tmp, len);
1151193640Sariff
1152193640Sariff	return (snd_str2afmt(buf));
1153164614Sariff}
1154164614Sariff
1155164614Sariffint
1156193640Sariffchn_reset(struct pcm_channel *c, uint32_t fmt, uint32_t spd)
115750724Scg{
1158193640Sariff	int r;
115959246Scg
116074763Scg	CHN_LOCKASSERT(c);
1161164614Sariff	c->feedcount = 0;
116250724Scg	c->flags &= CHN_F_RESET;
116382479Scg	c->interrupts = 0;
1164164614Sariff	c->timeout = 1;
116582479Scg	c->xruns = 0;
116689687Scg
1167193640Sariff	c->flags |= (pcm_getflags(c->dev) & SD_F_BITPERFECT) ?
1168193640Sariff	    CHN_F_BITPERFECT : 0;
1169193640Sariff
117089687Scg	r = CHANNEL_RESET(c->methods, c->devinfo);
1171193640Sariff	if (r == 0 && fmt != 0 && spd != 0) {
1172193640Sariff		r = chn_setparam(c, fmt, spd);
1173193640Sariff		fmt = 0;
1174193640Sariff		spd = 0;
117559324Scg	}
1176193640Sariff	if (r == 0 && fmt != 0)
1177193640Sariff		r = chn_setformat(c, fmt);
1178193640Sariff	if (r == 0 && spd != 0)
1179193640Sariff		r = chn_setspeed(c, spd);
118089687Scg	if (r == 0)
1181164614Sariff		r = chn_setlatency(c, chn_latency);
118274763Scg	if (r == 0) {
118374797Scg		chn_resetbuf(c);
118489687Scg		r = CHANNEL_RESETDONE(c->methods, c->devinfo);
118550724Scg	}
118674763Scg	return r;
118750724Scg}
118850724Scg
118950724Scgint
1190126367Struckmanchn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
119150724Scg{
119270134Scg	struct feeder_class *fc;
119374763Scg	struct snd_dbuf *b, *bs;
1194193640Sariff	int i, ret;
119554535Scg
1196168243Sariff	if (chn_timeout < CHN_TIMEOUT_MIN || chn_timeout > CHN_TIMEOUT_MAX)
1197168243Sariff		chn_timeout = CHN_TIMEOUT;
1198168243Sariff
1199123156Smatk	chn_lockinit(c, dir);
120082479Scg
120189687Scg	b = NULL;
120289687Scg	bs = NULL;
1203170161Sariff	CHN_INIT(c, children);
1204170161Sariff	CHN_INIT(c, children.busy);
120589687Scg	c->devinfo = NULL;
120670134Scg	c->feeder = NULL;
1207164614Sariff	c->latency = -1;
1208164614Sariff	c->timeout = 1;
120989687Scg
1210125136Struckman	ret = ENOMEM;
1211125136Struckman	b = sndbuf_create(c->dev, c->name, "primary", c);
1212125136Struckman	if (b == NULL)
1213125136Struckman		goto out;
1214125136Struckman	bs = sndbuf_create(c->dev, c->name, "secondary", c);
1215125136Struckman	if (bs == NULL)
1216125136Struckman		goto out;
1217125136Struckman
1218125136Struckman	CHN_LOCK(c);
1219125136Struckman
122089687Scg	ret = EINVAL;
122170134Scg	fc = feeder_getclass(NULL);
122270134Scg	if (fc == NULL)
122389687Scg		goto out;
122470134Scg	if (chn_addfeeder(c, fc, NULL))
122589687Scg		goto out;
122664881Scg
1227125136Struckman	/*
1228125136Struckman	 * XXX - sndbuf_setup() & sndbuf_resize() expect to be called
1229125136Struckman	 *	 with the channel unlocked because they are also called
1230125136Struckman	 *	 from driver methods that don't know about locking
1231125136Struckman	 */
1232125136Struckman	CHN_UNLOCK(c);
123374763Scg	sndbuf_setup(bs, NULL, 0);
1234125136Struckman	CHN_LOCK(c);
123574763Scg	c->bufhard = b;
123674763Scg	c->bufsoft = bs;
123750724Scg	c->flags = 0;
123866308Scg	c->feederflags = 0;
1239162588Snetchild	c->sm = NULL;
1240193640Sariff	c->format = SND_FORMAT(AFMT_U8, 1, 0);
1241193640Sariff	c->speed = DSP_DEFAULT_SPEED;
124289687Scg
1243193640Sariff	c->matrix = *feeder_matrix_id_map(SND_CHN_MATRIX_1_0);
1244193640Sariff	c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL;
1245193640Sariff
1246193640Sariff	for (i = 0; i < SND_CHN_T_MAX; i++) {
1247193640Sariff		c->volume[SND_VOL_C_MASTER][i] = SND_VOL_0DB_MASTER;
1248193640Sariff	}
1249193640Sariff
1250193640Sariff	c->volume[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB] = SND_VOL_0DB_MASTER;
1251193640Sariff	c->volume[SND_VOL_C_PCM][SND_CHN_T_VOL_0DB] = chn_vol_0db_pcm;
1252193640Sariff
1253193640Sariff	chn_vpc_reset(c, SND_VOL_C_PCM, 1);
1254193640Sariff
125589687Scg	ret = ENODEV;
1256125136Struckman	CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */
1257126367Struckman	c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction);
1258125136Struckman	CHN_LOCK(c);
125989687Scg	if (c->devinfo == NULL)
126089687Scg		goto out;
126189687Scg
126289687Scg	ret = ENOMEM;
126389687Scg	if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0))
126489687Scg		goto out;
126589687Scg
1266193640Sariff	ret = 0;
1267193640Sariff	c->direction = direction;
126889687Scg
1269193640Sariff	sndbuf_setfmt(b, c->format);
1270193640Sariff	sndbuf_setspd(b, c->speed);
1271193640Sariff	sndbuf_setfmt(bs, c->format);
1272193640Sariff	sndbuf_setspd(bs, c->speed);
127389687Scg
1274162588Snetchild	/**
1275162588Snetchild	 * @todo Should this be moved somewhere else?  The primary buffer
1276162588Snetchild	 * 	 is allocated by the driver or via DMA map setup, and tmpbuf
1277162588Snetchild	 * 	 seems to only come into existence in sndbuf_resize().
1278162588Snetchild	 */
1279162588Snetchild	if (c->direction == PCMDIR_PLAY) {
1280162588Snetchild		bs->sl = sndbuf_getmaxsize(bs);
1281162588Snetchild		bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_NOWAIT);
1282162588Snetchild		if (bs->shadbuf == NULL) {
1283162588Snetchild			ret = ENOMEM;
1284162588Snetchild			goto out;
1285162588Snetchild		}
1286162588Snetchild	}
128789687Scg
128889687Scgout:
1289125136Struckman	CHN_UNLOCK(c);
129089687Scg	if (ret) {
129189687Scg		if (c->devinfo) {
129289687Scg			if (CHANNEL_FREE(c->methods, c->devinfo))
129389687Scg				sndbuf_free(b);
129489687Scg		}
129589687Scg		if (bs)
129689687Scg			sndbuf_destroy(bs);
129789687Scg		if (b)
129889687Scg			sndbuf_destroy(b);
1299170815Sariff		CHN_LOCK(c);
130089687Scg		c->flags |= CHN_F_DEAD;
130189687Scg		chn_lockdestroy(c);
130289687Scg
130389687Scg		return ret;
130474763Scg	}
130555204Scg
130650724Scg	return 0;
130750724Scg}
130850724Scg
130950724Scgint
131074763Scgchn_kill(struct pcm_channel *c)
131165340Scg{
131274763Scg    	struct snd_dbuf *b = c->bufhard;
131374763Scg    	struct snd_dbuf *bs = c->bufsoft;
131474763Scg
1315170161Sariff	if (CHN_STARTED(c)) {
1316170161Sariff		CHN_LOCK(c);
131765644Scg		chn_trigger(c, PCMTRIG_ABORT);
1318170161Sariff		CHN_UNLOCK(c);
1319170161Sariff	}
1320170161Sariff	while (chn_removefeeder(c) == 0)
1321170161Sariff		;
132270134Scg	if (CHANNEL_FREE(c->methods, c->devinfo))
132389770Scg		sndbuf_free(b);
132474763Scg	sndbuf_destroy(bs);
132574763Scg	sndbuf_destroy(b);
1326170815Sariff	CHN_LOCK(c);
1327170815Sariff	c->flags |= CHN_F_DEAD;
132874763Scg	chn_lockdestroy(c);
1329170815Sariff
1330170815Sariff	return (0);
133165340Scg}
133265340Scg
1333193640Sariff/* XXX Obsolete. Use *_matrix() variant instead. */
133465340Scgint
1335193640Sariffchn_setvolume(struct pcm_channel *c, int left, int right)
133650724Scg{
1337193640Sariff	int ret;
133855204Scg
1339193640Sariff	ret = chn_setvolume_matrix(c, SND_VOL_C_MASTER, SND_CHN_T_FL, left);
1340193640Sariff	ret |= chn_setvolume_matrix(c, SND_VOL_C_MASTER, SND_CHN_T_FR,
1341193640Sariff	    right) << 8;
1342193640Sariff
1343193640Sariff	return (ret);
1344193640Sariff}
1345193640Sariff
1346193640Sariffint
1347193640Sariffchn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right,
1348193640Sariff    int center)
1349193640Sariff{
1350193640Sariff	int i, ret;
1351193640Sariff
1352193640Sariff	ret = 0;
1353193640Sariff
1354193640Sariff	for (i = 0; i < SND_CHN_T_MAX; i++) {
1355193640Sariff		if ((1 << i) & SND_CHN_LEFT_MASK)
1356193640Sariff			ret |= chn_setvolume_matrix(c, vc, i, left);
1357193640Sariff		else if ((1 << i) & SND_CHN_RIGHT_MASK)
1358193640Sariff			ret |= chn_setvolume_matrix(c, vc, i, right) << 8;
1359193640Sariff		else
1360193640Sariff			ret |= chn_setvolume_matrix(c, vc, i, center) << 16;
1361193640Sariff	}
1362193640Sariff
1363193640Sariff	return (ret);
1364193640Sariff}
1365193640Sariff
1366193640Sariffint
1367193640Sariffchn_setvolume_matrix(struct pcm_channel *c, int vc, int vt, int val)
1368193640Sariff{
1369193640Sariff	int i;
1370193640Sariff
1371193640Sariff	KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX &&
1372193640Sariff	    (vc == SND_VOL_C_MASTER || (vc & 1)) &&
1373193640Sariff	    (vt == SND_CHN_T_VOL_0DB || (vt >= SND_CHN_T_BEGIN &&
1374193640Sariff	    vt <= SND_CHN_T_END)) && (vt != SND_CHN_T_VOL_0DB ||
1375193640Sariff	    (val >= SND_VOL_0DB_MIN && val <= SND_VOL_0DB_MAX)),
1376193640Sariff	    ("%s(): invalid volume matrix c=%p vc=%d vt=%d val=%d",
1377193640Sariff	    __func__, c, vc, vt, val));
137874763Scg	CHN_LOCKASSERT(c);
1379193640Sariff
1380193640Sariff	if (val < 0)
1381193640Sariff		val = 0;
1382193640Sariff	if (val > 100)
1383193640Sariff		val = 100;
1384193640Sariff
1385193640Sariff	c->volume[vc][vt] = val;
1386193640Sariff
1387193640Sariff	/*
1388193640Sariff	 * Do relative calculation here and store it into class + 1
1389193640Sariff	 * to ease the job of feeder_volume.
1390193640Sariff	 */
1391193640Sariff	if (vc == SND_VOL_C_MASTER) {
1392193640Sariff		for (vc = SND_VOL_C_BEGIN; vc <= SND_VOL_C_END;
1393193640Sariff		    vc += SND_VOL_C_STEP)
1394193640Sariff			c->volume[SND_VOL_C_VAL(vc)][vt] =
1395193640Sariff			    SND_VOL_CALC_VAL(c->volume, vc, vt);
1396193640Sariff	} else if (vc & 1) {
1397193640Sariff		if (vt == SND_CHN_T_VOL_0DB)
1398193640Sariff			for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END;
1399193640Sariff			    i += SND_CHN_T_STEP) {
1400193640Sariff				c->volume[SND_VOL_C_VAL(vc)][i] =
1401193640Sariff				    SND_VOL_CALC_VAL(c->volume, vc, i);
1402193640Sariff			}
1403193640Sariff		else
1404193640Sariff			c->volume[SND_VOL_C_VAL(vc)][vt] =
1405193640Sariff			    SND_VOL_CALC_VAL(c->volume, vc, vt);
1406193640Sariff	}
1407193640Sariff
1408193640Sariff	return (val);
140950724Scg}
141050724Scg
141150724Scgint
1412193640Sariffchn_getvolume_matrix(struct pcm_channel *c, int vc, int vt)
141350724Scg{
1414193640Sariff	KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX &&
1415193640Sariff	    (vt == SND_CHN_T_VOL_0DB ||
1416193640Sariff	    (vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)),
1417193640Sariff	    ("%s(): invalid volume matrix c=%p vc=%d vt=%d",
1418193640Sariff	    __func__, c, vc, vt));
141974763Scg	CHN_LOCKASSERT(c);
1420193640Sariff
1421193640Sariff	return (c->volume[vc][vt]);
142250724Scg}
142350724Scg
1424193640Sariffstruct pcmchan_matrix *
1425193640Sariffchn_getmatrix(struct pcm_channel *c)
1426193640Sariff{
1427193640Sariff
1428193640Sariff	KASSERT(c != NULL, ("%s(): NULL channel", __func__));
1429193640Sariff	CHN_LOCKASSERT(c);
1430193640Sariff
1431193640Sariff	if (!(c->format & AFMT_CONVERTIBLE))
1432193640Sariff		return (NULL);
1433193640Sariff
1434193640Sariff	return (&c->matrix);
1435193640Sariff}
1436193640Sariff
1437193640Sariffint
1438193640Sariffchn_setmatrix(struct pcm_channel *c, struct pcmchan_matrix *m)
1439193640Sariff{
1440193640Sariff
1441193640Sariff	KASSERT(c != NULL && m != NULL,
1442193640Sariff	    ("%s(): NULL channel or matrix", __func__));
1443193640Sariff	CHN_LOCKASSERT(c);
1444193640Sariff
1445193640Sariff	if (!(c->format & AFMT_CONVERTIBLE))
1446193640Sariff		return (EINVAL);
1447193640Sariff
1448193640Sariff	c->matrix = *m;
1449193640Sariff	c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL;
1450193640Sariff
1451193640Sariff	return (chn_setformat(c, SND_FORMAT(c->format, m->channels, m->ext)));
1452193640Sariff}
1453193640Sariff
1454193640Sariff/*
1455193640Sariff * XXX chn_oss_* exists for the sake of compatibility.
1456193640Sariff */
1457193640Sariffint
1458193640Sariffchn_oss_getorder(struct pcm_channel *c, unsigned long long *map)
1459193640Sariff{
1460193640Sariff
1461193640Sariff	KASSERT(c != NULL && map != NULL,
1462193640Sariff	    ("%s(): NULL channel or map", __func__));
1463193640Sariff	CHN_LOCKASSERT(c);
1464193640Sariff
1465193640Sariff	if (!(c->format & AFMT_CONVERTIBLE))
1466193640Sariff		return (EINVAL);
1467193640Sariff
1468193640Sariff	return (feeder_matrix_oss_get_channel_order(&c->matrix, map));
1469193640Sariff}
1470193640Sariff
1471193640Sariffint
1472193640Sariffchn_oss_setorder(struct pcm_channel *c, unsigned long long *map)
1473193640Sariff{
1474193640Sariff	struct pcmchan_matrix m;
1475193640Sariff	int ret;
1476193640Sariff
1477193640Sariff	KASSERT(c != NULL && map != NULL,
1478193640Sariff	    ("%s(): NULL channel or map", __func__));
1479193640Sariff	CHN_LOCKASSERT(c);
1480193640Sariff
1481193640Sariff	if (!(c->format & AFMT_CONVERTIBLE))
1482193640Sariff		return (EINVAL);
1483193640Sariff
1484193640Sariff	m = c->matrix;
1485193640Sariff	ret = feeder_matrix_oss_set_channel_order(&m, map);
1486193640Sariff	if (ret != 0)
1487193640Sariff		return (ret);
1488193640Sariff
1489193640Sariff	return (chn_setmatrix(c, &m));
1490193640Sariff}
1491193640Sariff
1492193640Sariff#define SND_CHN_OSS_FRONT	(SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR)
1493193640Sariff#define SND_CHN_OSS_SURR	(SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR)
1494193640Sariff#define SND_CHN_OSS_CENTER_LFE	(SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF)
1495193640Sariff#define SND_CHN_OSS_REAR	(SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR)
1496193640Sariff
1497193640Sariffint
1498193640Sariffchn_oss_getmask(struct pcm_channel *c, uint32_t *retmask)
1499193640Sariff{
1500193640Sariff	struct pcmchan_matrix *m;
1501193640Sariff	struct pcmchan_caps *caps;
1502193640Sariff	uint32_t i, format;
1503193640Sariff
1504193640Sariff	KASSERT(c != NULL && retmask != NULL,
1505193640Sariff	    ("%s(): NULL channel or retmask", __func__));
1506193640Sariff	CHN_LOCKASSERT(c);
1507193640Sariff
1508193640Sariff	caps = chn_getcaps(c);
1509193640Sariff	if (caps == NULL || caps->fmtlist == NULL)
1510193640Sariff		return (ENODEV);
1511193640Sariff
1512193640Sariff	for (i = 0; caps->fmtlist[i] != 0; i++) {
1513193640Sariff		format = caps->fmtlist[i];
1514193640Sariff		if (!(format & AFMT_CONVERTIBLE)) {
1515193640Sariff			*retmask |= DSP_BIND_SPDIF;
1516193640Sariff			continue;
1517193640Sariff		}
1518193640Sariff		m = CHANNEL_GETMATRIX(c->methods, c->devinfo, format);
1519193640Sariff		if (m == NULL)
1520193640Sariff			continue;
1521193640Sariff		if (m->mask & SND_CHN_OSS_FRONT)
1522193640Sariff			*retmask |= DSP_BIND_FRONT;
1523193640Sariff		if (m->mask & SND_CHN_OSS_SURR)
1524193640Sariff			*retmask |= DSP_BIND_SURR;
1525193640Sariff		if (m->mask & SND_CHN_OSS_CENTER_LFE)
1526193640Sariff			*retmask |= DSP_BIND_CENTER_LFE;
1527193640Sariff		if (m->mask & SND_CHN_OSS_REAR)
1528193640Sariff			*retmask |= DSP_BIND_REAR;
1529193640Sariff	}
1530193640Sariff
1531193640Sariff	/* report software-supported binding mask */
1532193640Sariff	if (!CHN_BITPERFECT(c) && report_soft_matrix)
1533193640Sariff		*retmask |= DSP_BIND_FRONT | DSP_BIND_SURR |
1534193640Sariff		    DSP_BIND_CENTER_LFE | DSP_BIND_REAR;
1535193640Sariff
1536193640Sariff	return (0);
1537193640Sariff}
1538193640Sariff
1539193640Sariffvoid
1540193640Sariffchn_vpc_reset(struct pcm_channel *c, int vc, int force)
1541193640Sariff{
1542193640Sariff	int i;
1543193640Sariff
1544193640Sariff	KASSERT(c != NULL && vc >= SND_VOL_C_BEGIN && vc <= SND_VOL_C_END,
1545193640Sariff	    ("%s(): invalid reset c=%p vc=%d", __func__, c, vc));
1546193640Sariff	CHN_LOCKASSERT(c);
1547193640Sariff
1548193640Sariff	if (force == 0 && chn_vpc_autoreset == 0)
1549193640Sariff		return;
1550193640Sariff
1551193640Sariff	for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END; i += SND_CHN_T_STEP)
1552193640Sariff		CHN_SETVOLUME(c, vc, i, c->volume[vc][SND_CHN_T_VOL_0DB]);
1553193640Sariff}
1554193640Sariff
1555164614Sariffstatic u_int32_t
1556164614Sariffround_pow2(u_int32_t v)
1557164614Sariff{
1558164614Sariff	u_int32_t ret;
1559164614Sariff
1560164614Sariff	if (v < 2)
1561164614Sariff		v = 2;
1562164614Sariff	ret = 0;
1563164614Sariff	while (v >> ret)
1564164614Sariff		ret++;
1565164614Sariff	ret = 1 << (ret - 1);
1566164614Sariff	while (ret < v)
1567164614Sariff		ret <<= 1;
1568164614Sariff	return ret;
1569164614Sariff}
1570164614Sariff
1571167645Sariffstatic u_int32_t
1572167645Sariffround_blksz(u_int32_t v, int round)
1573167645Sariff{
1574167645Sariff	u_int32_t ret, tmp;
1575167645Sariff
1576167645Sariff	if (round < 1)
1577167645Sariff		round = 1;
1578167645Sariff
1579167645Sariff	ret = min(round_pow2(v), CHN_2NDBUFMAXSIZE >> 1);
1580167645Sariff
1581167645Sariff	if (ret > v && (ret >> 1) > 0 && (ret >> 1) >= ((v * 3) >> 2))
1582167645Sariff		ret >>= 1;
1583167645Sariff
1584167645Sariff	tmp = ret - (ret % round);
1585167645Sariff	while (tmp < 16 || tmp < round) {
1586167645Sariff		ret <<= 1;
1587167645Sariff		tmp = ret - (ret % round);
1588167645Sariff	}
1589167645Sariff
1590167645Sariff	return ret;
1591167645Sariff}
1592167645Sariff
1593164614Sariff/*
1594164614Sariff * 4Front call it DSP Policy, while we call it "Latency Profile". The idea
1595164614Sariff * is to keep 2nd buffer short so that it doesn't cause long queue during
1596164614Sariff * buffer transfer.
1597164614Sariff *
1598164614Sariff *    Latency reference table for 48khz stereo 16bit: (PLAY)
1599164614Sariff *
1600164614Sariff *      +---------+------------+-----------+------------+
1601164614Sariff *      | Latency | Blockcount | Blocksize | Buffersize |
1602164614Sariff *      +---------+------------+-----------+------------+
1603164614Sariff *      |     0   |       2    |   64      |    128     |
1604164614Sariff *      +---------+------------+-----------+------------+
1605164614Sariff *      |     1   |       4    |   128     |    512     |
1606164614Sariff *      +---------+------------+-----------+------------+
1607164614Sariff *      |     2   |       8    |   512     |    4096    |
1608164614Sariff *      +---------+------------+-----------+------------+
1609164614Sariff *      |     3   |      16    |   512     |    8192    |
1610164614Sariff *      +---------+------------+-----------+------------+
1611164614Sariff *      |     4   |      32    |   512     |    16384   |
1612164614Sariff *      +---------+------------+-----------+------------+
1613164614Sariff *      |     5   |      32    |   1024    |    32768   |
1614164614Sariff *      +---------+------------+-----------+------------+
1615164614Sariff *      |     6   |      16    |   2048    |    32768   |
1616164614Sariff *      +---------+------------+-----------+------------+
1617164614Sariff *      |     7   |       8    |   4096    |    32768   |
1618164614Sariff *      +---------+------------+-----------+------------+
1619164614Sariff *      |     8   |       4    |   8192    |    32768   |
1620164614Sariff *      +---------+------------+-----------+------------+
1621164614Sariff *      |     9   |       2    |   16384   |    32768   |
1622164614Sariff *      +---------+------------+-----------+------------+
1623164614Sariff *      |    10   |       2    |   32768   |    65536   |
1624164614Sariff *      +---------+------------+-----------+------------+
1625164614Sariff *
1626164614Sariff * Recording need a different reference table. All we care is
1627164614Sariff * gobbling up everything within reasonable buffering threshold.
1628164614Sariff *
1629164614Sariff *    Latency reference table for 48khz stereo 16bit: (REC)
1630164614Sariff *
1631164614Sariff *      +---------+------------+-----------+------------+
1632164614Sariff *      | Latency | Blockcount | Blocksize | Buffersize |
1633164614Sariff *      +---------+------------+-----------+------------+
1634164614Sariff *      |     0   |     512    |   32      |    16384   |
1635164614Sariff *      +---------+------------+-----------+------------+
1636164614Sariff *      |     1   |     256    |   64      |    16384   |
1637164614Sariff *      +---------+------------+-----------+------------+
1638164614Sariff *      |     2   |     128    |   128     |    16384   |
1639164614Sariff *      +---------+------------+-----------+------------+
1640164614Sariff *      |     3   |      64    |   256     |    16384   |
1641164614Sariff *      +---------+------------+-----------+------------+
1642164614Sariff *      |     4   |      32    |   512     |    16384   |
1643164614Sariff *      +---------+------------+-----------+------------+
1644164614Sariff *      |     5   |      32    |   1024    |    32768   |
1645164614Sariff *      +---------+------------+-----------+------------+
1646164614Sariff *      |     6   |      16    |   2048    |    32768   |
1647164614Sariff *      +---------+------------+-----------+------------+
1648164614Sariff *      |     7   |       8    |   4096    |    32768   |
1649164614Sariff *      +---------+------------+-----------+------------+
1650164614Sariff *      |     8   |       4    |   8192    |    32768   |
1651164614Sariff *      +---------+------------+-----------+------------+
1652164614Sariff *      |     9   |       2    |   16384   |    32768   |
1653164614Sariff *      +---------+------------+-----------+------------+
1654164614Sariff *      |    10   |       2    |   32768   |    65536   |
1655164614Sariff *      +---------+------------+-----------+------------+
1656164614Sariff *
1657164614Sariff * Calculations for other data rate are entirely based on these reference
1658164614Sariff * tables. For normal operation, Latency 5 seems give the best, well
1659164614Sariff * balanced performance for typical workload. Anything below 5 will
1660164614Sariff * eat up CPU to keep up with increasing context switches because of
1661164614Sariff * shorter buffer space and usually require the application to handle it
1662164614Sariff * aggresively through possibly real time programming technique.
1663164614Sariff *
1664164614Sariff */
1665164614Sariff#define CHN_LATENCY_PBLKCNT_REF				\
1666164614Sariff	{{1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1},		\
1667164614Sariff	{1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 1}}
1668164614Sariff#define CHN_LATENCY_PBUFSZ_REF				\
1669164614Sariff	{{7, 9, 12, 13, 14, 15, 15, 15, 15, 15, 16},	\
1670164614Sariff	{11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 17}}
1671164614Sariff
1672164614Sariff#define CHN_LATENCY_RBLKCNT_REF				\
1673164614Sariff	{{9, 8, 7, 6, 5, 5, 4, 3, 2, 1, 1},		\
1674164614Sariff	{9, 8, 7, 6, 5, 5, 4, 3, 2, 1, 1}}
1675164614Sariff#define CHN_LATENCY_RBUFSZ_REF				\
1676164614Sariff	{{14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16},	\
1677164614Sariff	{15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17}}
1678164614Sariff
1679164614Sariff#define CHN_LATENCY_DATA_REF	192000 /* 48khz stereo 16bit ~ 48000 x 2 x 2 */
1680164614Sariff
168172457Scgstatic int
1682164614Sariffchn_calclatency(int dir, int latency, int bps, u_int32_t datarate,
1683164614Sariff				u_int32_t max, int *rblksz, int *rblkcnt)
1684164614Sariff{
1685167645Sariff	static int pblkcnts[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] =
1686164614Sariff	    CHN_LATENCY_PBLKCNT_REF;
1687167645Sariff	static int  pbufszs[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] =
1688164614Sariff	    CHN_LATENCY_PBUFSZ_REF;
1689167645Sariff	static int rblkcnts[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] =
1690164614Sariff	    CHN_LATENCY_RBLKCNT_REF;
1691167645Sariff	static int  rbufszs[CHN_LATENCY_PROFILE_MAX + 1][CHN_LATENCY_MAX + 1] =
1692164614Sariff	    CHN_LATENCY_RBUFSZ_REF;
1693167645Sariff	u_int32_t bufsz;
1694167645Sariff	int lprofile, blksz, blkcnt;
1695164614Sariff
1696167645Sariff	if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX ||
1697164614Sariff	    bps < 1 || datarate < 1 ||
1698164614Sariff	    !(dir == PCMDIR_PLAY || dir == PCMDIR_REC)) {
1699164614Sariff		if (rblksz != NULL)
1700164614Sariff			*rblksz = CHN_2NDBUFMAXSIZE >> 1;
1701164614Sariff		if (rblkcnt != NULL)
1702164614Sariff			*rblkcnt = 2;
1703193640Sariff		printf("%s(): FAILED dir=%d latency=%d bps=%d "
1704164614Sariff		    "datarate=%u max=%u\n",
1705164614Sariff		    __func__, dir, latency, bps, datarate, max);
1706164614Sariff		return CHN_2NDBUFMAXSIZE;
1707164614Sariff	}
1708164614Sariff
1709167645Sariff	lprofile = chn_latency_profile;
1710167645Sariff
1711164614Sariff	if (dir == PCMDIR_PLAY) {
1712167645Sariff		blkcnt = pblkcnts[lprofile][latency];
1713167645Sariff		bufsz = pbufszs[lprofile][latency];
1714164614Sariff	} else {
1715167645Sariff		blkcnt = rblkcnts[lprofile][latency];
1716167645Sariff		bufsz = rbufszs[lprofile][latency];
1717164614Sariff	}
1718167645Sariff
1719167645Sariff	bufsz = round_pow2(snd_xbytes(1 << bufsz, CHN_LATENCY_DATA_REF,
1720167645Sariff	    datarate));
1721164614Sariff	if (bufsz > max)
1722164614Sariff		bufsz = max;
1723167645Sariff	blksz = round_blksz(bufsz >> blkcnt, bps);
1724167645Sariff
1725164614Sariff	if (rblksz != NULL)
1726164614Sariff		*rblksz = blksz;
1727164614Sariff	if (rblkcnt != NULL)
1728164614Sariff		*rblkcnt = 1 << blkcnt;
1729164614Sariff
1730164614Sariff	return blksz << blkcnt;
1731164614Sariff}
1732164614Sariff
1733164614Sariffstatic int
1734164614Sariffchn_resizebuf(struct pcm_channel *c, int latency,
1735164614Sariff					int blkcnt, int blksz)
1736164614Sariff{
1737164614Sariff	struct snd_dbuf *b, *bs, *pb;
1738230845Smav	int sblksz, sblkcnt, hblksz, hblkcnt, limit = 0, nsblksz, nsblkcnt;
1739164614Sariff	int ret;
1740164614Sariff
1741164614Sariff	CHN_LOCKASSERT(c);
1742164614Sariff
1743193640Sariff	if ((c->flags & (CHN_F_MMAP | CHN_F_TRIGGERED)) ||
1744164614Sariff	    !(c->direction == PCMDIR_PLAY || c->direction == PCMDIR_REC))
1745164614Sariff		return EINVAL;
1746164614Sariff
1747164614Sariff	if (latency == -1) {
1748164614Sariff		c->latency = -1;
1749164614Sariff		latency = chn_latency;
1750164614Sariff	} else if (latency == -2) {
1751164614Sariff		latency = c->latency;
1752164614Sariff		if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX)
1753164614Sariff			latency = chn_latency;
1754164614Sariff	} else if (latency < CHN_LATENCY_MIN || latency > CHN_LATENCY_MAX)
1755164614Sariff		return EINVAL;
1756164614Sariff	else {
1757164614Sariff		c->latency = latency;
1758164614Sariff	}
1759164614Sariff
1760164614Sariff	bs = c->bufsoft;
1761164614Sariff	b = c->bufhard;
1762164614Sariff
1763164614Sariff	if (!(blksz == 0 || blkcnt == -1) &&
1764193640Sariff	    (blksz < 16 || blksz < sndbuf_getalign(bs) || blkcnt < 2 ||
1765164614Sariff	    (blksz * blkcnt) > CHN_2NDBUFMAXSIZE))
1766164614Sariff		return EINVAL;
1767164614Sariff
1768193640Sariff	chn_calclatency(c->direction, latency, sndbuf_getalign(bs),
1769193640Sariff	    sndbuf_getalign(bs) * sndbuf_getspd(bs), CHN_2NDBUFMAXSIZE,
1770164614Sariff	    &sblksz, &sblkcnt);
1771164614Sariff
1772164614Sariff	if (blksz == 0 || blkcnt == -1) {
1773164614Sariff		if (blkcnt == -1)
1774164614Sariff			c->flags &= ~CHN_F_HAS_SIZE;
1775164614Sariff		if (c->flags & CHN_F_HAS_SIZE) {
1776164614Sariff			blksz = sndbuf_getblksz(bs);
1777164614Sariff			blkcnt = sndbuf_getblkcnt(bs);
1778164614Sariff		}
1779164614Sariff	} else
1780164614Sariff		c->flags |= CHN_F_HAS_SIZE;
1781164614Sariff
1782164614Sariff	if (c->flags & CHN_F_HAS_SIZE) {
1783164614Sariff		/*
1784164614Sariff		 * The application has requested their own blksz/blkcnt.
1785164614Sariff		 * Just obey with it, and let them toast alone. We can
1786164614Sariff		 * clamp it to the nearest latency profile, but that would
1787164614Sariff		 * defeat the purpose of having custom control. The least
1788164614Sariff		 * we can do is round it to the nearest ^2 and align it.
1789164614Sariff		 */
1790193640Sariff		sblksz = round_blksz(blksz, sndbuf_getalign(bs));
1791167645Sariff		sblkcnt = round_pow2(blkcnt);
1792164614Sariff	}
1793164614Sariff
1794164614Sariff	if (c->parentchannel != NULL) {
1795230845Smav		pb = c->parentchannel->bufsoft;
1796164614Sariff		CHN_UNLOCK(c);
1797170815Sariff		CHN_LOCK(c->parentchannel);
1798164614Sariff		chn_notify(c->parentchannel, CHN_N_BLOCKSIZE);
1799170815Sariff		CHN_UNLOCK(c->parentchannel);
1800164614Sariff		CHN_LOCK(c);
1801230845Smav		if (c->direction == PCMDIR_PLAY) {
1802230845Smav			limit = (pb != NULL) ?
1803230845Smav			    sndbuf_xbytes(sndbuf_getsize(pb), pb, bs) : 0;
1804230845Smav		} else {
1805230845Smav			limit = (pb != NULL) ?
1806230845Smav			    sndbuf_xbytes(sndbuf_getblksz(pb), pb, bs) * 2 : 0;
1807230845Smav		}
1808164614Sariff	} else {
1809167645Sariff		hblkcnt = 2;
1810164614Sariff		if (c->flags & CHN_F_HAS_SIZE) {
1811167645Sariff			hblksz = round_blksz(sndbuf_xbytes(sblksz, bs, b),
1812193640Sariff			    sndbuf_getalign(b));
1813167645Sariff			hblkcnt = round_pow2(sndbuf_getblkcnt(bs));
1814164614Sariff		} else
1815164614Sariff			chn_calclatency(c->direction, latency,
1816193640Sariff			    sndbuf_getalign(b),
1817193640Sariff			    sndbuf_getalign(b) * sndbuf_getspd(b),
1818167645Sariff			    CHN_2NDBUFMAXSIZE, &hblksz, &hblkcnt);
1819164614Sariff
1820164614Sariff		if ((hblksz << 1) > sndbuf_getmaxsize(b))
1821167645Sariff			hblksz = round_blksz(sndbuf_getmaxsize(b) >> 1,
1822193640Sariff			    sndbuf_getalign(b));
1823167645Sariff
1824167645Sariff		while ((hblksz * hblkcnt) > sndbuf_getmaxsize(b)) {
1825167645Sariff			if (hblkcnt < 4)
1826167645Sariff				hblksz >>= 1;
1827167645Sariff			else
1828167645Sariff				hblkcnt >>= 1;
1829167645Sariff		}
1830167645Sariff
1831193640Sariff		hblksz -= hblksz % sndbuf_getalign(b);
1832164614Sariff
1833164614Sariff#if 0
1834164614Sariff		hblksz = sndbuf_getmaxsize(b) >> 1;
1835193640Sariff		hblksz -= hblksz % sndbuf_getalign(b);
1836167645Sariff		hblkcnt = 2;
1837164614Sariff#endif
1838167645Sariff
1839164614Sariff		CHN_UNLOCK(c);
1840167645Sariff		if (chn_usefrags == 0 ||
1841167645Sariff		    CHANNEL_SETFRAGMENTS(c->methods, c->devinfo,
1842193640Sariff		    hblksz, hblkcnt) != 0)
1843167645Sariff			sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods,
1844167645Sariff			    c->devinfo, hblksz));
1845164614Sariff		CHN_LOCK(c);
1846164614Sariff
1847170161Sariff		if (!CHN_EMPTY(c, children)) {
1848230845Smav			nsblksz = round_blksz(
1849230845Smav			    sndbuf_xbytes(sndbuf_getblksz(b), b, bs),
1850193640Sariff			    sndbuf_getalign(bs));
1851230845Smav			nsblkcnt = sndbuf_getblkcnt(b);
1852230845Smav			if (c->direction == PCMDIR_PLAY) {
1853230845Smav				do {
1854230845Smav					nsblkcnt--;
1855230845Smav				} while (nsblkcnt >= 2 &&
1856230845Smav				    nsblksz * nsblkcnt >= sblksz * sblkcnt);
1857230845Smav				nsblkcnt++;
1858230845Smav			}
1859230845Smav			sblksz = nsblksz;
1860230845Smav			sblkcnt = nsblkcnt;
1861164614Sariff			limit = 0;
1862230845Smav		} else
1863230845Smav			limit = sndbuf_xbytes(sndbuf_getblksz(b), b, bs) * 2;
1864164614Sariff	}
1865164614Sariff
1866164614Sariff	if (limit > CHN_2NDBUFMAXSIZE)
1867164614Sariff		limit = CHN_2NDBUFMAXSIZE;
1868164614Sariff
1869167645Sariff#if 0
1870167645Sariff	while (limit > 0 && (sblksz * sblkcnt) > limit) {
1871167645Sariff		if (sblkcnt < 4)
1872164614Sariff			break;
1873167645Sariff		sblkcnt >>= 1;
1874164614Sariff	}
1875167645Sariff#endif
1876164614Sariff
1877167645Sariff	while ((sblksz * sblkcnt) < limit)
1878167645Sariff		sblkcnt <<= 1;
1879167645Sariff
1880167645Sariff	while ((sblksz * sblkcnt) > CHN_2NDBUFMAXSIZE) {
1881167645Sariff		if (sblkcnt < 4)
1882167645Sariff			sblksz >>= 1;
1883167645Sariff		else
1884167645Sariff			sblkcnt >>= 1;
1885167645Sariff	}
1886167645Sariff
1887193640Sariff	sblksz -= sblksz % sndbuf_getalign(bs);
1888167645Sariff
1889164614Sariff	if (sndbuf_getblkcnt(bs) != sblkcnt || sndbuf_getblksz(bs) != sblksz ||
1890164614Sariff	    sndbuf_getsize(bs) != (sblkcnt * sblksz)) {
1891164614Sariff		ret = sndbuf_remalloc(bs, sblkcnt, sblksz);
1892164614Sariff		if (ret != 0) {
1893193640Sariff			device_printf(c->dev, "%s(): Failed: %d %d\n",
1894193640Sariff			    __func__, sblkcnt, sblksz);
1895164614Sariff			return ret;
1896164614Sariff		}
1897164614Sariff	}
1898164614Sariff
1899164614Sariff	/*
1900230845Smav	 * Interrupt timeout
1901230845Smav	 */
1902230845Smav	c->timeout = ((u_int64_t)hz * sndbuf_getsize(bs)) /
1903230845Smav	    ((u_int64_t)sndbuf_getspd(bs) * sndbuf_getalign(bs));
1904230845Smav	if (c->parentchannel != NULL)
1905230845Smav		c->timeout = min(c->timeout, c->parentchannel->timeout);
1906230845Smav	if (c->timeout < 1)
1907230845Smav		c->timeout = 1;
1908230845Smav
1909230845Smav	/*
1910164614Sariff	 * OSSv4 docs: "By default OSS will set the low water level equal
1911164614Sariff	 * to the fragment size which is optimal in most cases."
1912164614Sariff	 */
1913164614Sariff	c->lw = sndbuf_getblksz(bs);
1914164614Sariff	chn_resetbuf(c);
1915164614Sariff
1916164614Sariff	if (snd_verbose > 3)
1917193640Sariff		device_printf(c->dev, "%s(): %s (%s) timeout=%u "
1918164614Sariff		    "b[%d/%d/%d] bs[%d/%d/%d] limit=%d\n",
1919167645Sariff		    __func__, CHN_DIRSTR(c),
1920164614Sariff		    (c->flags & CHN_F_VIRTUAL) ? "virtual" : "hardware",
1921164614Sariff		    c->timeout,
1922164614Sariff		    sndbuf_getsize(b), sndbuf_getblksz(b),
1923164614Sariff		    sndbuf_getblkcnt(b),
1924164614Sariff		    sndbuf_getsize(bs), sndbuf_getblksz(bs),
1925164614Sariff		    sndbuf_getblkcnt(bs), limit);
1926164614Sariff
1927164614Sariff	return 0;
1928164614Sariff}
1929164614Sariff
1930164614Sariffint
1931164614Sariffchn_setlatency(struct pcm_channel *c, int latency)
1932164614Sariff{
1933164614Sariff	CHN_LOCKASSERT(c);
1934164614Sariff	/* Destroy blksz/blkcnt, enforce latency profile. */
1935164614Sariff	return chn_resizebuf(c, latency, -1, 0);
1936164614Sariff}
1937164614Sariff
1938164614Sariffint
1939164614Sariffchn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
1940164614Sariff{
1941164614Sariff	CHN_LOCKASSERT(c);
1942164614Sariff	/* Destroy latency profile, enforce blksz/blkcnt */
1943164614Sariff	return chn_resizebuf(c, -1, blkcnt, blksz);
1944164614Sariff}
1945164614Sariff
1946193640Sariffint
1947193640Sariffchn_setparam(struct pcm_channel *c, uint32_t format, uint32_t speed)
194850724Scg{
1949193640Sariff	struct pcmchan_caps *caps;
1950193640Sariff	uint32_t hwspeed, delta;
1951193640Sariff	int ret;
195266308Scg
195374763Scg	CHN_LOCKASSERT(c);
195470291Scg
1955193640Sariff	if (speed < 1 || format == 0 || CHN_STARTED(c))
1956193640Sariff		return (EINVAL);
195770291Scg
1958193640Sariff	c->format = format;
1959193640Sariff	c->speed = speed;
196070291Scg
1961193640Sariff	caps = chn_getcaps(c);
196270291Scg
1963193640Sariff	hwspeed = speed;
1964193640Sariff	RANGE(hwspeed, caps->minspeed, caps->maxspeed);
196570291Scg
1966193640Sariff	sndbuf_setspd(c->bufhard, CHANNEL_SETSPEED(c->methods, c->devinfo,
1967193640Sariff	    hwspeed));
1968193640Sariff	hwspeed = sndbuf_getspd(c->bufhard);
196970291Scg
1970193640Sariff	delta = (hwspeed > speed) ? (hwspeed - speed) : (speed - hwspeed);
197170291Scg
1972193640Sariff	if (delta <= feeder_rate_round)
1973193640Sariff		c->speed = hwspeed;
1974193640Sariff
1975193640Sariff	ret = feeder_chain(c);
1976193640Sariff
1977193640Sariff	if (ret == 0)
1978193640Sariff		ret = CHANNEL_SETFORMAT(c->methods, c->devinfo,
1979193640Sariff		    sndbuf_getfmt(c->bufhard));
1980193640Sariff
1981193640Sariff	if (ret == 0)
1982193640Sariff		ret = chn_resizebuf(c, -2, 0, 0);
1983193640Sariff
1984193640Sariff	return (ret);
198550724Scg}
198650724Scg
198750724Scgint
1988193640Sariffchn_setspeed(struct pcm_channel *c, uint32_t speed)
198950724Scg{
1990193640Sariff	uint32_t oldformat, oldspeed, format;
1991193640Sariff	int ret;
199272457Scg
1993193640Sariff#if 0
1994193640Sariff	/* XXX force 48k */
1995193640Sariff	if (c->format & AFMT_PASSTHROUGH)
1996193640Sariff		speed = AFMT_PASSTHROUGH_RATE;
1997193640Sariff#endif
1998193640Sariff
1999193640Sariff	oldformat = c->format;
2000193640Sariff	oldspeed = c->speed;
2001193640Sariff	format = oldformat;
2002193640Sariff
2003193640Sariff	ret = chn_setparam(c, format, speed);
2004193640Sariff	if (ret != 0) {
2005164614Sariff		if (snd_verbose > 3)
2006193640Sariff			device_printf(c->dev,
2007193640Sariff			    "%s(): Setting speed %d failed, "
2008193640Sariff			    "falling back to %d\n",
2009193640Sariff			    __func__, speed, oldspeed);
2010193640Sariff		chn_setparam(c, c->format, oldspeed);
201172457Scg	}
2012193640Sariff
2013193640Sariff	return (ret);
201472457Scg}
201572457Scg
2016193640Sariffint
2017193640Sariffchn_setformat(struct pcm_channel *c, uint32_t format)
201872457Scg{
2019193640Sariff	uint32_t oldformat, oldspeed, speed;
2020193640Sariff	int ret;
202160961Scg
2022193640Sariff	/* XXX force stereo */
2023230537Smav	if ((format & AFMT_PASSTHROUGH) && AFMT_CHANNEL(format) < 2) {
2024193640Sariff		format = SND_FORMAT(format, AFMT_PASSTHROUGH_CHANNEL,
2025193640Sariff		    AFMT_PASSTHROUGH_EXTCHANNEL);
2026230537Smav	}
2027193640Sariff
2028193640Sariff	oldformat = c->format;
2029193640Sariff	oldspeed = c->speed;
2030193640Sariff	speed = oldspeed;
2031193640Sariff
2032193640Sariff	ret = chn_setparam(c, format, speed);
2033193640Sariff	if (ret != 0) {
2034193640Sariff		if (snd_verbose > 3)
2035193640Sariff			device_printf(c->dev,
2036193640Sariff			    "%s(): Format change 0x%08x failed, "
2037193640Sariff			    "falling back to 0x%08x\n",
2038193640Sariff			    __func__, format, oldformat);
2039193640Sariff		chn_setparam(c, oldformat, oldspeed);
2040193640Sariff	}
2041193640Sariff
2042193640Sariff	return (ret);
204350724Scg}
204450724Scg
2045193640Sariffvoid
2046193640Sariffchn_syncstate(struct pcm_channel *c)
204772457Scg{
2048193640Sariff	struct snddev_info *d;
2049193640Sariff	struct snd_mixer *m;
205072457Scg
2051193640Sariff	d = (c != NULL) ? c->parentsnddev : NULL;
2052193640Sariff	m = (d != NULL && d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 :
2053193640Sariff	    NULL;
2054193640Sariff
2055193640Sariff	if (d == NULL || m == NULL)
2056193640Sariff		return;
2057193640Sariff
2058193640Sariff	CHN_LOCKASSERT(c);
2059193640Sariff
2060193640Sariff	if (c->feederflags & (1 << FEEDER_VOLUME)) {
2061193640Sariff		uint32_t parent;
2062193640Sariff		int vol, pvol, left, right, center;
2063193640Sariff
2064193640Sariff		if (c->direction == PCMDIR_PLAY &&
2065193640Sariff		    (d->flags & SD_F_SOFTPCMVOL)) {
2066193640Sariff			/* CHN_UNLOCK(c); */
2067193640Sariff			vol = mix_get(m, SOUND_MIXER_PCM);
2068193640Sariff			parent = mix_getparent(m, SOUND_MIXER_PCM);
2069193640Sariff			if (parent != SOUND_MIXER_NONE)
2070193640Sariff				pvol = mix_get(m, parent);
2071193640Sariff			else
2072193640Sariff				pvol = 100 | (100 << 8);
2073193640Sariff			/* CHN_LOCK(c); */
2074193640Sariff		} else {
2075193640Sariff			vol = 100 | (100 << 8);
2076193640Sariff			pvol = vol;
2077193640Sariff		}
2078193640Sariff
2079193640Sariff		if (vol == -1) {
2080193640Sariff			device_printf(c->dev,
2081193640Sariff			    "Soft PCM Volume: Failed to read pcm "
2082193640Sariff			    "default value\n");
2083193640Sariff			vol = 100 | (100 << 8);
2084193640Sariff		}
2085193640Sariff
2086193640Sariff		if (pvol == -1) {
2087193640Sariff			device_printf(c->dev,
2088193640Sariff			    "Soft PCM Volume: Failed to read parent "
2089193640Sariff			    "default value\n");
2090193640Sariff			pvol = 100 | (100 << 8);
2091193640Sariff		}
2092193640Sariff
2093193640Sariff		left = ((vol & 0x7f) * (pvol & 0x7f)) / 100;
2094193640Sariff		right = (((vol >> 8) & 0x7f) * ((pvol >> 8) & 0x7f)) / 100;
2095193640Sariff		center = (left + right) >> 1;
2096193640Sariff
2097193640Sariff		chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right, center);
209872457Scg	}
2099193640Sariff
2100193640Sariff	if (c->feederflags & (1 << FEEDER_EQ)) {
2101193640Sariff		struct pcm_feeder *f;
2102193640Sariff		int treble, bass, state;
2103193640Sariff
2104193640Sariff		/* CHN_UNLOCK(c); */
2105193640Sariff		treble = mix_get(m, SOUND_MIXER_TREBLE);
2106193640Sariff		bass = mix_get(m, SOUND_MIXER_BASS);
2107193640Sariff		/* CHN_LOCK(c); */
2108193640Sariff
2109193640Sariff		if (treble == -1)
2110193640Sariff			treble = 50;
2111193640Sariff		else
2112193640Sariff			treble = ((treble & 0x7f) +
2113193640Sariff			    ((treble >> 8) & 0x7f)) >> 1;
2114193640Sariff
2115193640Sariff		if (bass == -1)
2116193640Sariff			bass = 50;
2117193640Sariff		else
2118193640Sariff			bass = ((bass & 0x7f) + ((bass >> 8) & 0x7f)) >> 1;
2119193640Sariff
2120193640Sariff		f = chn_findfeeder(c, FEEDER_EQ);
2121193640Sariff		if (f != NULL) {
2122193640Sariff			if (FEEDER_SET(f, FEEDEQ_TREBLE, treble) != 0)
2123193640Sariff				device_printf(c->dev,
2124193640Sariff				    "EQ: Failed to set treble -- %d\n",
2125193640Sariff				    treble);
2126193640Sariff			if (FEEDER_SET(f, FEEDEQ_BASS, bass) != 0)
2127193640Sariff				device_printf(c->dev,
2128193640Sariff				    "EQ: Failed to set bass -- %d\n",
2129193640Sariff				    bass);
2130193640Sariff			if (FEEDER_SET(f, FEEDEQ_PREAMP, d->eqpreamp) != 0)
2131193640Sariff				device_printf(c->dev,
2132193640Sariff				    "EQ: Failed to set preamp -- %d\n",
2133193640Sariff				    d->eqpreamp);
2134193640Sariff			if (d->flags & SD_F_EQ_BYPASSED)
2135193640Sariff				state = FEEDEQ_BYPASS;
2136193640Sariff			else if (d->flags & SD_F_EQ_ENABLED)
2137193640Sariff				state = FEEDEQ_ENABLE;
2138193640Sariff			else
2139193640Sariff				state = FEEDEQ_DISABLE;
2140193640Sariff			if (FEEDER_SET(f, FEEDEQ_STATE, state) != 0)
2141193640Sariff				device_printf(c->dev,
2142193640Sariff				    "EQ: Failed to set state -- %d\n", state);
2143193640Sariff		}
2144193640Sariff	}
214572457Scg}
214672457Scg
214772457Scgint
214874763Scgchn_trigger(struct pcm_channel *c, int go)
214950724Scg{
2150147274Smarius#ifdef DEV_ISA
215174763Scg    	struct snd_dbuf *b = c->bufhard;
2152147274Smarius#endif
2153170161Sariff	struct snddev_info *d = c->parentsnddev;
215474763Scg	int ret;
215574763Scg
215674763Scg	CHN_LOCKASSERT(c);
2157147274Smarius#ifdef DEV_ISA
2158110499Snyan	if (SND_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD))
2159110499Snyan		sndbuf_dmabounce(b);
2160147274Smarius#endif
2161170815Sariff	if (!PCMTRIG_COMMON(go))
2162170815Sariff		return (CHANNEL_TRIGGER(c->methods, c->devinfo, go));
2163170815Sariff
2164170815Sariff	if (go == c->trigger)
2165170521Sariff		return (0);
2166170161Sariff
216774763Scg	ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go);
2168170815Sariff	if (ret != 0)
2169170815Sariff		return (ret);
217074763Scg
2171170815Sariff	switch (go) {
2172170815Sariff	case PCMTRIG_START:
2173170815Sariff		if (snd_verbose > 3)
2174170815Sariff			device_printf(c->dev,
2175170815Sariff			    "%s() %s: calling go=0x%08x , "
2176170815Sariff			    "prev=0x%08x\n", __func__, c->name, go,
2177170815Sariff			    c->trigger);
2178170815Sariff		if (c->trigger != PCMTRIG_START) {
2179170815Sariff			c->trigger = go;
2180170815Sariff			CHN_UNLOCK(c);
2181193640Sariff			PCM_LOCK(d);
2182170815Sariff			CHN_INSERT_HEAD(d, c, channels.pcm.busy);
2183193640Sariff			PCM_UNLOCK(d);
2184170815Sariff			CHN_LOCK(c);
2185193640Sariff			chn_syncstate(c);
2186170161Sariff		}
2187170815Sariff		break;
2188170815Sariff	case PCMTRIG_STOP:
2189170815Sariff	case PCMTRIG_ABORT:
2190170815Sariff		if (snd_verbose > 3)
2191170815Sariff			device_printf(c->dev,
2192170815Sariff			    "%s() %s: calling go=0x%08x , "
2193170815Sariff			    "prev=0x%08x\n", __func__, c->name, go,
2194170815Sariff			    c->trigger);
2195170815Sariff		if (c->trigger == PCMTRIG_START) {
2196170815Sariff			c->trigger = go;
2197170815Sariff			CHN_UNLOCK(c);
2198193640Sariff			PCM_LOCK(d);
2199170815Sariff			CHN_REMOVE(d, c, channels.pcm.busy);
2200193640Sariff			PCM_UNLOCK(d);
2201170815Sariff			CHN_LOCK(c);
2202170815Sariff		}
2203170815Sariff		break;
2204170815Sariff	default:
2205170815Sariff		break;
2206170161Sariff	}
2207170161Sariff
2208170815Sariff	return (0);
220950724Scg}
221050724Scg
2211162588Snetchild/**
2212162588Snetchild * @brief Queries sound driver for sample-aligned hardware buffer pointer index
2213162588Snetchild *
2214162588Snetchild * This function obtains the hardware pointer location, then aligns it to
2215162588Snetchild * the current bytes-per-sample value before returning.  (E.g., a channel
2216162588Snetchild * running in 16 bit stereo mode would require 4 bytes per sample, so a
2217162588Snetchild * hwptr value ranging from 32-35 would be returned as 32.)
2218162588Snetchild *
2219162588Snetchild * @param c	PCM channel context
2220162588Snetchild * @returns 	sample-aligned hardware buffer pointer index
2221162588Snetchild */
222250724Scgint
222374763Scgchn_getptr(struct pcm_channel *c)
222450724Scg{
222554155Scg	int hwptr;
222654155Scg
222774763Scg	CHN_LOCKASSERT(c);
2228167645Sariff	hwptr = (CHN_STARTED(c)) ? CHANNEL_GETPTR(c->methods, c->devinfo) : 0;
2229193640Sariff	return (hwptr - (hwptr % sndbuf_getalign(c->bufhard)));
223050724Scg}
223150724Scg
223274763Scgstruct pcmchan_caps *
223374763Scgchn_getcaps(struct pcm_channel *c)
223450724Scg{
223574763Scg	CHN_LOCKASSERT(c);
223670134Scg	return CHANNEL_GETCAPS(c->methods, c->devinfo);
223750724Scg}
223864881Scg
223964881Scgu_int32_t
224074763Scgchn_getformats(struct pcm_channel *c)
224164881Scg{
224264881Scg	u_int32_t *fmtlist, fmts;
224364881Scg	int i;
224464881Scg
224564881Scg	fmtlist = chn_getcaps(c)->fmtlist;
224664881Scg	fmts = 0;
224764881Scg	for (i = 0; fmtlist[i]; i++)
224864881Scg		fmts |= fmtlist[i];
224964881Scg
225091186Sdes	/* report software-supported formats */
2251193640Sariff	if (!CHN_BITPERFECT(c) && report_soft_formats)
2252193640Sariff		fmts |= AFMT_CONVERTIBLE;
225391186Sdes
2254193640Sariff	return (AFMT_ENCODING(fmts));
225564881Scg}
225664881Scg
225777269Scgint
225877269Scgchn_notify(struct pcm_channel *c, u_int32_t flags)
225977269Scg{
2260193640Sariff	struct pcm_channel *ch;
2261193640Sariff	struct pcmchan_caps *caps;
2262193640Sariff	uint32_t bestformat, bestspeed, besthwformat, *vchanformat, *vchanrate;
2263193640Sariff	uint32_t vpflags;
2264193640Sariff	int dirty, err, run, nrun;
226577269Scg
2266170815Sariff	CHN_LOCKASSERT(c);
2267125136Struckman
2268170815Sariff	if (CHN_EMPTY(c, children))
2269170815Sariff		return (ENODEV);
227077269Scg
2271170815Sariff	err = 0;
2272170815Sariff
227377882Scg	/*
2274170815Sariff	 * If the hwchan is running, we can't change its rate, format or
227577882Scg	 * blocksize
227677882Scg	 */
2277170815Sariff	run = (CHN_STARTED(c)) ? 1 : 0;
227877882Scg	if (run)
227977882Scg		flags &= CHN_N_VOLUME | CHN_N_TRIGGER;
228077882Scg
228177269Scg	if (flags & CHN_N_RATE) {
2282193640Sariff		/*
2283193640Sariff		 * XXX I'll make good use of this someday.
2284193640Sariff		 *     However this is currently being superseded by
2285193640Sariff		 *     the availability of CHN_F_VCHAN_DYNAMIC.
2286193640Sariff		 */
228777269Scg	}
2288193640Sariff
228977269Scg	if (flags & CHN_N_FORMAT) {
2290193640Sariff		/*
2291193640Sariff		 * XXX I'll make good use of this someday.
2292193640Sariff		 *     However this is currently being superseded by
2293193640Sariff		 *     the availability of CHN_F_VCHAN_DYNAMIC.
2294193640Sariff		 */
229577269Scg	}
2296193640Sariff
229777269Scg	if (flags & CHN_N_VOLUME) {
2298193640Sariff		/*
2299193640Sariff		 * XXX I'll make good use of this someday, though
2300193640Sariff		 *     soft volume control is currently pretty much
2301193640Sariff		 *     integrated.
2302193640Sariff		 */
230377269Scg	}
2304193640Sariff
230577269Scg	if (flags & CHN_N_BLOCKSIZE) {
230677269Scg		/*
2307164614Sariff		 * Set to default latency profile
230877269Scg		 */
2309164614Sariff		chn_setlatency(c, chn_latency);
231077269Scg	}
2311193640Sariff
2312193640Sariff	if ((flags & CHN_N_TRIGGER) && !(c->flags & CHN_F_VCHAN_DYNAMIC)) {
2313170161Sariff		nrun = CHN_EMPTY(c, children.busy) ? 0 : 1;
231477882Scg		if (nrun && !run)
2315170815Sariff			err = chn_start(c, 1);
231677882Scg		if (!nrun && run)
231777269Scg			chn_abort(c);
2318193640Sariff		flags &= ~CHN_N_TRIGGER;
231977269Scg	}
2320170815Sariff
2321193640Sariff	if (flags & CHN_N_TRIGGER) {
2322193640Sariff		if (c->direction == PCMDIR_PLAY) {
2323193640Sariff			vchanformat = &c->parentsnddev->pvchanformat;
2324193640Sariff			vchanrate = &c->parentsnddev->pvchanrate;
2325193640Sariff		} else {
2326193640Sariff			vchanformat = &c->parentsnddev->rvchanformat;
2327193640Sariff			vchanrate = &c->parentsnddev->rvchanrate;
2328193640Sariff		}
2329193640Sariff
2330193640Sariff		/* Dynamic Virtual Channel */
2331193640Sariff		if (!(c->flags & CHN_F_VCHAN_ADAPTIVE)) {
2332193640Sariff			bestformat = *vchanformat;
2333193640Sariff			bestspeed = *vchanrate;
2334193640Sariff		} else {
2335193640Sariff			bestformat = 0;
2336193640Sariff			bestspeed = 0;
2337193640Sariff		}
2338193640Sariff
2339193640Sariff		besthwformat = 0;
2340193640Sariff		nrun = 0;
2341193640Sariff		caps = chn_getcaps(c);
2342193640Sariff		dirty = 0;
2343193640Sariff		vpflags = 0;
2344193640Sariff
2345193640Sariff		CHN_FOREACH(ch, c, children.busy) {
2346193640Sariff			CHN_LOCK(ch);
2347193640Sariff			if ((ch->format & AFMT_PASSTHROUGH) &&
2348193640Sariff			    snd_fmtvalid(ch->format, caps->fmtlist)) {
2349193640Sariff				bestformat = ch->format;
2350193640Sariff				bestspeed = ch->speed;
2351193640Sariff				CHN_UNLOCK(ch);
2352193640Sariff				vpflags = CHN_F_PASSTHROUGH;
2353193640Sariff				nrun++;
2354193640Sariff				break;
2355193640Sariff			}
2356193640Sariff			if ((ch->flags & CHN_F_EXCLUSIVE) && vpflags == 0) {
2357193640Sariff				if (c->flags & CHN_F_VCHAN_ADAPTIVE) {
2358193640Sariff					bestspeed = ch->speed;
2359193640Sariff					RANGE(bestspeed, caps->minspeed,
2360193640Sariff					    caps->maxspeed);
2361193640Sariff					besthwformat = snd_fmtbest(ch->format,
2362193640Sariff					    caps->fmtlist);
2363193640Sariff					if (besthwformat != 0)
2364193640Sariff						bestformat = besthwformat;
2365193640Sariff				}
2366193640Sariff				CHN_UNLOCK(ch);
2367193640Sariff				vpflags = CHN_F_EXCLUSIVE;
2368193640Sariff				nrun++;
2369193640Sariff				continue;
2370193640Sariff			}
2371193640Sariff			if (!(c->flags & CHN_F_VCHAN_ADAPTIVE) ||
2372193640Sariff			    vpflags != 0) {
2373193640Sariff				CHN_UNLOCK(ch);
2374193640Sariff				nrun++;
2375193640Sariff				continue;
2376193640Sariff			}
2377193640Sariff			if (ch->speed > bestspeed) {
2378193640Sariff				bestspeed = ch->speed;
2379193640Sariff				RANGE(bestspeed, caps->minspeed,
2380193640Sariff				    caps->maxspeed);
2381193640Sariff			}
2382193640Sariff			besthwformat = snd_fmtbest(ch->format, caps->fmtlist);
2383193640Sariff			if (!(besthwformat & AFMT_VCHAN)) {
2384193640Sariff				CHN_UNLOCK(ch);
2385193640Sariff				nrun++;
2386193640Sariff				continue;
2387193640Sariff			}
2388193640Sariff			if (AFMT_CHANNEL(besthwformat) >
2389193640Sariff			    AFMT_CHANNEL(bestformat))
2390193640Sariff				bestformat = besthwformat;
2391193640Sariff			else if (AFMT_CHANNEL(besthwformat) ==
2392193640Sariff			    AFMT_CHANNEL(bestformat) &&
2393193640Sariff			    AFMT_BIT(besthwformat) > AFMT_BIT(bestformat))
2394193640Sariff				bestformat = besthwformat;
2395193640Sariff			CHN_UNLOCK(ch);
2396193640Sariff			nrun++;
2397193640Sariff		}
2398193640Sariff
2399193640Sariff		if (bestformat == 0)
2400193640Sariff			bestformat = c->format;
2401193640Sariff		if (bestspeed == 0)
2402193640Sariff			bestspeed = c->speed;
2403193640Sariff
2404193640Sariff		if (bestformat != c->format || bestspeed != c->speed)
2405193640Sariff			dirty = 1;
2406193640Sariff
2407193640Sariff		c->flags &= ~(CHN_F_PASSTHROUGH | CHN_F_EXCLUSIVE);
2408193640Sariff		c->flags |= vpflags;
2409193640Sariff
2410193640Sariff		if (nrun && !run) {
2411193640Sariff			if (dirty) {
2412193640Sariff				bestspeed = CHANNEL_SETSPEED(c->methods,
2413193640Sariff				    c->devinfo, bestspeed);
2414193640Sariff				err = chn_reset(c, bestformat, bestspeed);
2415193640Sariff			}
2416193640Sariff			if (err == 0 && dirty) {
2417193640Sariff				CHN_FOREACH(ch, c, children.busy) {
2418193640Sariff					CHN_LOCK(ch);
2419193640Sariff					if (VCHAN_SYNC_REQUIRED(ch))
2420193640Sariff						vchan_sync(ch);
2421193640Sariff					CHN_UNLOCK(ch);
2422193640Sariff				}
2423193640Sariff			}
2424193640Sariff			if (err == 0) {
2425193640Sariff				if (dirty)
2426193640Sariff					c->flags |= CHN_F_DIRTY;
2427193640Sariff				err = chn_start(c, 1);
2428193640Sariff			}
2429193640Sariff		}
2430193640Sariff
2431193640Sariff		if (nrun && run && dirty) {
2432193640Sariff			chn_abort(c);
2433193640Sariff			bestspeed = CHANNEL_SETSPEED(c->methods, c->devinfo,
2434193640Sariff			    bestspeed);
2435193640Sariff			err = chn_reset(c, bestformat, bestspeed);
2436193640Sariff			if (err == 0) {
2437193640Sariff				CHN_FOREACH(ch, c, children.busy) {
2438193640Sariff					CHN_LOCK(ch);
2439193640Sariff					if (VCHAN_SYNC_REQUIRED(ch))
2440193640Sariff						vchan_sync(ch);
2441193640Sariff					CHN_UNLOCK(ch);
2442193640Sariff				}
2443193640Sariff			}
2444193640Sariff			if (err == 0) {
2445193640Sariff				c->flags |= CHN_F_DIRTY;
2446193640Sariff				err = chn_start(c, 1);
2447193640Sariff			}
2448193640Sariff		}
2449193640Sariff
2450193640Sariff		if (err == 0 && !(bestformat & AFMT_PASSTHROUGH) &&
2451193640Sariff		    (bestformat & AFMT_VCHAN)) {
2452193640Sariff			*vchanformat = bestformat;
2453193640Sariff			*vchanrate = bestspeed;
2454193640Sariff		}
2455193640Sariff
2456193640Sariff		if (!nrun && run) {
2457193640Sariff			c->flags &= ~(CHN_F_PASSTHROUGH | CHN_F_EXCLUSIVE);
2458193640Sariff			bestformat = *vchanformat;
2459193640Sariff			bestspeed = *vchanrate;
2460193640Sariff			chn_abort(c);
2461193640Sariff			if (c->format != bestformat || c->speed != bestspeed)
2462193640Sariff				chn_reset(c, bestformat, bestspeed);
2463193640Sariff		}
2464193640Sariff	}
2465193640Sariff
2466170815Sariff	return (err);
246777269Scg}
2468125136Struckman
2469162588Snetchild/**
2470162588Snetchild * @brief Fetch array of supported discrete sample rates
2471162588Snetchild *
2472162588Snetchild * Wrapper for CHANNEL_GETRATES.  Please see channel_if.m:getrates() for
2473162588Snetchild * detailed information.
2474162588Snetchild *
2475162588Snetchild * @note If the operation isn't supported, this function will just return 0
2476162588Snetchild *       (no rates in the array), and *rates will be set to NULL.  Callers
2477162588Snetchild *       should examine rates @b only if this function returns non-zero.
2478162588Snetchild *
2479162588Snetchild * @param c	pcm channel to examine
2480162588Snetchild * @param rates	pointer to array of integers; rate table will be recorded here
2481162588Snetchild *
2482162588Snetchild * @return number of rates in the array pointed to be @c rates
2483162588Snetchild */
2484162588Snetchildint
2485162588Snetchildchn_getrates(struct pcm_channel *c, int **rates)
2486162588Snetchild{
2487162588Snetchild	KASSERT(rates != NULL, ("rates is null"));
2488162588Snetchild	CHN_LOCKASSERT(c);
2489162588Snetchild	return CHANNEL_GETRATES(c->methods, c->devinfo, rates);
2490162588Snetchild}
2491162588Snetchild
2492162588Snetchild/**
2493162588Snetchild * @brief Remove channel from a sync group, if there is one.
2494162588Snetchild *
2495162588Snetchild * This function is initially intended for the following conditions:
2496162588Snetchild *   - Starting a syncgroup (@c SNDCTL_DSP_SYNCSTART ioctl)
2497162588Snetchild *   - Closing a device.  (A channel can't be destroyed if it's still in use.)
2498162588Snetchild *
2499162588Snetchild * @note Before calling this function, the syncgroup list mutex must be
2500162588Snetchild * held.  (Consider pcm_channel::sm protected by the SG list mutex
2501162588Snetchild * whether @c c is locked or not.)
2502162588Snetchild *
2503162588Snetchild * @param c	channel device to be started or closed
2504162588Snetchild * @returns	If this channel was the only member of a group, the group ID
2505162588Snetchild * 		is returned to the caller so that the caller can release it
2506162588Snetchild * 		via free_unr() after giving up the syncgroup lock.  Else it
2507162588Snetchild * 		returns 0.
2508162588Snetchild */
2509162588Snetchildint
2510162588Snetchildchn_syncdestroy(struct pcm_channel *c)
2511162588Snetchild{
2512162588Snetchild	struct pcmchan_syncmember *sm;
2513162588Snetchild	struct pcmchan_syncgroup *sg;
2514162588Snetchild	int sg_id;
2515162588Snetchild
2516162588Snetchild	sg_id = 0;
2517162588Snetchild
2518162588Snetchild	PCM_SG_LOCKASSERT(MA_OWNED);
2519162588Snetchild
2520162588Snetchild	if (c->sm != NULL) {
2521162588Snetchild		sm = c->sm;
2522162588Snetchild		sg = sm->parent;
2523162588Snetchild		c->sm = NULL;
2524162588Snetchild
2525162588Snetchild		KASSERT(sg != NULL, ("syncmember has null parent"));
2526162588Snetchild
2527162588Snetchild		SLIST_REMOVE(&sg->members, sm, pcmchan_syncmember, link);
2528162588Snetchild		free(sm, M_DEVBUF);
2529162588Snetchild
2530162588Snetchild		if (SLIST_EMPTY(&sg->members)) {
2531162588Snetchild			SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link);
2532162588Snetchild			sg_id = sg->id;
2533162588Snetchild			free(sg, M_DEVBUF);
2534162588Snetchild		}
2535162588Snetchild	}
2536162588Snetchild
2537162588Snetchild	return sg_id;
2538162588Snetchild}
2539162588Snetchild
2540162588Snetchild#ifdef OSSV4_EXPERIMENT
2541162588Snetchildint
2542162588Snetchildchn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak)
2543162588Snetchild{
2544162588Snetchild	CHN_LOCKASSERT(c);
2545162588Snetchild	return CHANNEL_GETPEAKS(c->methods, c->devinfo, lpeak, rpeak);
2546162588Snetchild}
2547162588Snetchild#endif
2548