xref: /freebsd/sys/dev/sound/pcm/vchan.c (revision f856af0466c076beef4ea9b15d088e1119a945b8)
1 /*-
2  * Copyright (c) 2001 Cameron Grant <cg@FreeBSD.org>
3  * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * Almost entirely rewritten to add multi-format/channels mixing support.
28  *
29  */
30 
31 #include <dev/sound/pcm/sound.h>
32 #include <dev/sound/pcm/vchan.h>
33 #include "feeder_if.h"
34 
35 SND_DECLARE_FILE("$FreeBSD$");
36 
37 MALLOC_DEFINE(M_VCHANFEEDER, "vchanfeed", "pcm vchan feeder");
38 
39 /*
40  * Default speed / format
41  */
42 #define VCHAN_DEFAULT_SPEED	48000
43 #define VCHAN_DEFAULT_AFMT	(AFMT_S16_LE | AFMT_STEREO)
44 #define VCHAN_DEFAULT_STRFMT	"s16le"
45 
46 struct feed_vchan_info;
47 
48 typedef uint32_t (*feed_vchan_mixer)(struct feed_vchan_info *,
49 				uint8_t *, uint8_t *, uint32_t);
50 
51 struct feed_vchan_info {
52 	uint32_t bps, channels, zero_sample;
53 	feed_vchan_mixer mix;
54 };
55 
56 struct vchinfo {
57 	uint32_t spd, fmt, fmts[2], blksz, bps, run;
58 	struct pcm_channel *channel, *parent;
59 	struct pcmchan_caps caps;
60 };
61 
62 /* support everything (mono / stereo), except a-law / mu-law */
63 static struct afmtstr_table vchan_supported_fmts[] = {
64 	{    "u8", AFMT_U8     }, {    "s8", AFMT_S8     },
65 	{ "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE },
66 	{ "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE },
67 	{ "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE },
68 	{ "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE },
69 	{ "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE },
70 	{ "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE },
71 	{    NULL, 0           },
72 };
73 
74 /* alias table, shorter. */
75 static const struct {
76 	char *alias, *fmtstr;
77 } vchan_fmtstralias[] = {
78 	{  "8", "u8"    }, { "16", "s16le" },
79 	{ "24", "s24le" }, { "32", "s32le" },
80 	{ NULL, NULL    },
81 };
82 
83 #define vchan_valid_format(fmt) \
84 	afmt2afmtstr(vchan_supported_fmts, fmt, NULL, 0, 0, \
85 	AFMTSTR_STEREO_RETURN)
86 #define vchan_valid_strformat(strfmt) \
87 	afmtstr2afmt(vchan_supported_fmts, strfmt, AFMTSTR_STEREO_RETURN);
88 
89 /*
90  * Need specialized WRITE macros since 32bit might involved saturation
91  * if calculation is done within 32bit arithmetic.
92  */
93 #define VCHAN_PCM_WRITE_S8_NE(b8, val)		PCM_WRITE_S8(b8, val)
94 #define VCHAN_PCM_WRITE_S16_LE(b8, val)		PCM_WRITE_S16_LE(b8, val)
95 #define VCHAN_PCM_WRITE_S24_LE(b8, val)		PCM_WRITE_S24_LE(b8, val)
96 #define VCHAN_PCM_WRITE_S32_LE(b8, val)		_PCM_WRITE_S32_LE(b8, val)
97 #define VCHAN_PCM_WRITE_S16_BE(b8, val)		PCM_WRITE_S16_BE(b8, val)
98 #define VCHAN_PCM_WRITE_S24_BE(b8, val)		PCM_WRITE_S24_BE(b8, val)
99 #define VCHAN_PCM_WRITE_S32_BE(b8, val)		_PCM_WRITE_S32_BE(b8, val)
100 #define VCHAN_PCM_WRITE_U8_NE(b8, val)		PCM_WRITE_U8(b8, val)
101 #define VCHAN_PCM_WRITE_U16_LE(b8, val)		PCM_WRITE_U16_LE(b8, val)
102 #define VCHAN_PCM_WRITE_U24_LE(b8, val)		PCM_WRITE_U24_LE(b8, val)
103 #define VCHAN_PCM_WRITE_U32_LE(b8, val)		_PCM_WRITE_U32_LE(b8, val)
104 #define VCHAN_PCM_WRITE_U16_BE(b8, val)		PCM_WRITE_U16_BE(b8, val)
105 #define VCHAN_PCM_WRITE_U24_BE(b8, val)		PCM_WRITE_U24_BE(b8, val)
106 #define VCHAN_PCM_WRITE_U32_BE(b8, val)		_PCM_WRITE_U32_BE(b8, val)
107 
108 #define FEEDER_VCHAN_MIX(FMTBIT, VCHAN_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS)	\
109 static uint32_t									\
110 feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS(struct feed_vchan_info *info,		\
111 				uint8_t *to, uint8_t *tmp, uint32_t count)	\
112 {										\
113 	uint32_t bps;								\
114 	int32_t x, y;								\
115 	VCHAN_INTCAST z;							\
116 	int i;									\
117 										\
118 	bps = info->bps;							\
119 	i = count;								\
120 	tmp += i;								\
121 	to += i;								\
122 	while (i > 0) {								\
123 		tmp -= bps;							\
124 		to -= bps;							\
125 		i -= bps;							\
126 		x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(tmp);			\
127 		y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(to);			\
128 		z = (VCHAN_INTCAST)x + y;					\
129 		x = PCM_CLAMP_##SIGN##FMTBIT(z);				\
130 		VCHAN_PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(to, x);		\
131 	}									\
132 	return count;								\
133 }
134 
135 FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne)
136 FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le)
137 FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le)
138 FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le)
139 FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be)
140 FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be)
141 FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be)
142 /*  unsigned */
143 FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne)
144 FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le)
145 FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le)
146 FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le)
147 FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be)
148 FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be)
149 FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be)
150 
151 static int
152 feed_vchan_setup(struct pcm_feeder *f)
153 {
154 	struct feed_vchan_info *info = f->data;
155 	static const struct {
156 		uint32_t format;	/* pcm / audio format */
157 		uint32_t bps;		/* bytes-per-sample, regardless of
158 					   total channels */
159 		feed_vchan_mixer mix;
160 	} vchan_mix_tbl[] = {
161 		{ AFMT_S8, PCM_8_BPS, feed_vchan_mix_s8ne },
162 		{ AFMT_S16_LE, PCM_16_BPS, feed_vchan_mix_s16le },
163 		{ AFMT_S24_LE, PCM_24_BPS, feed_vchan_mix_s24le },
164 		{ AFMT_S32_LE, PCM_32_BPS, feed_vchan_mix_s32le },
165 		{ AFMT_S16_BE, PCM_16_BPS, feed_vchan_mix_s16be },
166 		{ AFMT_S24_BE, PCM_24_BPS, feed_vchan_mix_s24be },
167 		{ AFMT_S32_BE, PCM_32_BPS, feed_vchan_mix_s32be },
168 		/* unsigned */
169 		{ AFMT_U8, PCM_8_BPS, feed_vchan_mix_u8ne },
170 		{ AFMT_U16_LE, PCM_16_BPS, feed_vchan_mix_u16le },
171 		{ AFMT_U24_LE, PCM_24_BPS, feed_vchan_mix_u24le },
172 		{ AFMT_U32_LE, PCM_32_BPS, feed_vchan_mix_u32le },
173 		{ AFMT_U16_BE, PCM_16_BPS, feed_vchan_mix_u16be },
174 		{ AFMT_U24_BE, PCM_24_BPS, feed_vchan_mix_u24be },
175 		{ AFMT_U32_BE, PCM_32_BPS, feed_vchan_mix_u32be },
176 		{ 0, 0, NULL },
177 	};
178 	uint32_t i;
179 
180 	for (i = 0; i < sizeof(vchan_mix_tbl) / sizeof(*vchan_mix_tbl); i++) {
181 		if (vchan_mix_tbl[i].format == 0)
182 			return -1;
183 		if ((f->desc->out & ~AFMT_STEREO) == vchan_mix_tbl[i].format) {
184 			info->bps = vchan_mix_tbl[i].bps;
185 			info->mix = vchan_mix_tbl[i].mix;
186 			break;
187 		}
188 	}
189 
190 	info->channels = (f->desc->out & AFMT_STEREO) ? 2 : 1;
191 	info->zero_sample = (f->desc->out & AFMT_SIGNED) ? 0x00 : 0x80;
192 
193 	return 0;
194 }
195 
196 static int
197 feed_vchan_init(struct pcm_feeder *f)
198 {
199 	struct feed_vchan_info *info;
200 
201 	if (f->desc->out != f->desc->in)
202 		return EINVAL;
203 
204 	info = malloc(sizeof(*info), M_VCHANFEEDER, M_NOWAIT | M_ZERO);
205 	if (info == NULL)
206 		return ENOMEM;
207 	f->data = info;
208 	return feed_vchan_setup(f);
209 }
210 
211 static int
212 feed_vchan_free(struct pcm_feeder *f)
213 {
214 	struct feed_vchan_info *info = f->data;
215 
216 	if (info)
217 		free(info, M_VCHANFEEDER);
218 	f->data = NULL;
219 	return 0;
220 }
221 
222 static int
223 feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
224 						uint32_t count, void *source)
225 {
226 	struct feed_vchan_info *info = f->data;
227 	struct snd_dbuf *src = source;
228 	struct pcmchan_children *cce;
229 	struct pcm_channel *ch;
230 	uint32_t cnt, rcnt = 0, sz;
231 	uint8_t *tmp;
232 
233 	sz = sndbuf_getsize(src);
234 	if (sz < count)
235 		count = sz;
236 
237 	sz = info->bps * info->channels;
238 	count -= count % sz;
239 	if (count < sz)
240 		return 0;
241 
242 	/*
243 	 * we are going to use our source as a temporary buffer since it's
244 	 * got no other purpose.  we obtain our data by traversing the channel
245 	 * list of children and calling vchan_mix_* to mix count bytes from each
246 	 * into our destination buffer, b
247 	 */
248 	tmp = sndbuf_getbuf(src);
249 	memset(b, info->zero_sample, count);
250 	SLIST_FOREACH(cce, &c->children, link) {
251 		ch = cce->channel;
252 		CHN_LOCK(ch);
253 		if (ch->flags & CHN_F_TRIGGERED) {
254 			if (ch->flags & CHN_F_MAPPED)
255 				sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft));
256 			cnt = FEEDER_FEED(ch->feeder, ch, tmp, count, ch->bufsoft);
257 			cnt -= cnt % sz;
258 			cnt = info->mix(info, b, tmp, cnt);
259 			if (cnt > rcnt)
260 				rcnt = cnt;
261 		}
262 		CHN_UNLOCK(ch);
263 	}
264 
265 	if (++c->feedcount == 0)
266 		c->feedcount = 2;
267 
268 	return rcnt;
269 }
270 
271 static struct pcm_feederdesc feeder_vchan_desc[] = {
272 	{FEEDER_MIXER, AFMT_S8, AFMT_S8, 0},
273 	{FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0},
274 	{FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0},
275 	{FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0},
276 	{FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0},
277 	{FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0},
278 	{FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0},
279 	{FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
280 	{FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
281 	{FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0},
282 	{FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0},
283 	{FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
284 	{FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0},
285 	{FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0},
286 	/* unsigned */
287 	{FEEDER_MIXER, AFMT_U8, AFMT_U8, 0},
288 	{FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0},
289 	{FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0},
290 	{FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0},
291 	{FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0},
292 	{FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0},
293 	{FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0},
294 	{FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
295 	{FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
296 	{FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0},
297 	{FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0},
298 	{FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
299 	{FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0},
300 	{FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0},
301 	{0, 0, 0, 0},
302 };
303 static kobj_method_t feeder_vchan_methods[] = {
304 	KOBJMETHOD(feeder_init,		feed_vchan_init),
305 	KOBJMETHOD(feeder_free,		feed_vchan_free),
306 	KOBJMETHOD(feeder_feed,		feed_vchan),
307 	{0, 0}
308 };
309 FEEDER_DECLARE(feeder_vchan, 2, NULL);
310 
311 /************************************************************/
312 
313 static void *
314 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
315 {
316 	struct vchinfo *ch;
317 	struct pcm_channel *parent = devinfo;
318 
319 	KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction"));
320 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
321 	if (!ch)
322 		return NULL;
323 	ch->parent = parent;
324 	ch->channel = c;
325 	ch->fmt = AFMT_U8;
326 	ch->spd = DSP_DEFAULT_SPEED;
327 	ch->blksz = 2048;
328 
329 	c->flags |= CHN_F_VIRTUAL;
330 
331 	return ch;
332 }
333 
334 static int
335 vchan_free(kobj_t obj, void *data)
336 {
337 	return 0;
338 }
339 
340 static int
341 vchan_setformat(kobj_t obj, void *data, uint32_t format)
342 {
343 	struct vchinfo *ch = data;
344 	struct pcm_channel *parent = ch->parent;
345 	struct pcm_channel *channel = ch->channel;
346 
347 	ch->fmt = format;
348 	ch->bps = 1;
349 	ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0;
350 	if (ch->fmt & AFMT_16BIT)
351 		ch->bps <<= 1;
352 	else if (ch->fmt & AFMT_24BIT)
353 		ch->bps *= 3;
354 	else if (ch->fmt & AFMT_32BIT)
355 		ch->bps <<= 2;
356 	CHN_UNLOCK(channel);
357 	chn_notify(parent, CHN_N_FORMAT);
358 	CHN_LOCK(channel);
359 	sndbuf_setfmt(channel->bufsoft, format);
360 	return 0;
361 }
362 
363 static int
364 vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
365 {
366 	struct vchinfo *ch = data;
367 	struct pcm_channel *parent = ch->parent;
368 	struct pcm_channel *channel = ch->channel;
369 
370 	ch->spd = speed;
371 	CHN_UNLOCK(channel);
372 	CHN_LOCK(parent);
373 	speed = sndbuf_getspd(parent->bufsoft);
374 	CHN_UNLOCK(parent);
375 	CHN_LOCK(channel);
376 	return speed;
377 }
378 
379 static int
380 vchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
381 {
382 	struct vchinfo *ch = data;
383 	struct pcm_channel *channel = ch->channel;
384 	struct pcm_channel *parent = ch->parent;
385 	/* struct pcm_channel *channel = ch->channel; */
386 	int prate, crate;
387 
388 	ch->blksz = blocksize;
389 	/* CHN_UNLOCK(channel); */
390 	sndbuf_setblksz(channel->bufhard, blocksize);
391 	chn_notify(parent, CHN_N_BLOCKSIZE);
392 	CHN_LOCK(parent);
393 	/* CHN_LOCK(channel); */
394 
395 	crate = ch->spd * ch->bps;
396 	prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft);
397 	blocksize = sndbuf_getblksz(parent->bufsoft);
398 	CHN_UNLOCK(parent);
399 	blocksize *= prate;
400 	blocksize /= crate;
401 	blocksize += ch->bps;
402 	prate = 0;
403 	while (blocksize >> prate)
404 		prate++;
405 	blocksize = 1 << (prate - 1);
406 	blocksize -= blocksize % ch->bps;
407 	/* XXX screwed !@#$ */
408 	if (blocksize < ch->bps)
409 		blocksize = 4096 - (4096 % ch->bps);
410 
411 	return blocksize;
412 }
413 
414 static int
415 vchan_trigger(kobj_t obj, void *data, int go)
416 {
417 	struct vchinfo *ch = data;
418 	struct pcm_channel *parent = ch->parent;
419 	struct pcm_channel *channel = ch->channel;
420 
421 	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
422 		return 0;
423 
424 	ch->run = (go == PCMTRIG_START)? 1 : 0;
425 	CHN_UNLOCK(channel);
426 	chn_notify(parent, CHN_N_TRIGGER);
427 	CHN_LOCK(channel);
428 
429 	return 0;
430 }
431 
432 static struct pcmchan_caps *
433 vchan_getcaps(kobj_t obj, void *data)
434 {
435 	struct vchinfo *ch = data;
436 	uint32_t fmt;
437 
438 	ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft);
439 	ch->caps.maxspeed = ch->caps.minspeed;
440 	ch->caps.caps = 0;
441 	ch->fmts[1] = 0;
442 	fmt = sndbuf_getfmt(ch->parent->bufsoft);
443 	if (fmt != vchan_valid_format(fmt)) {
444 		device_printf(ch->parent->dev,
445 			    "%s: WARNING: invalid vchan format! (0x%08x)\n",
446 			    __func__, fmt);
447 		fmt = VCHAN_DEFAULT_AFMT;
448 	}
449 	ch->fmts[0] = fmt;
450 	ch->caps.fmtlist = ch->fmts;
451 
452 	return &ch->caps;
453 }
454 
455 static kobj_method_t vchan_methods[] = {
456 	KOBJMETHOD(channel_init,		vchan_init),
457 	KOBJMETHOD(channel_free,		vchan_free),
458 	KOBJMETHOD(channel_setformat,		vchan_setformat),
459 	KOBJMETHOD(channel_setspeed,		vchan_setspeed),
460 	KOBJMETHOD(channel_setblocksize,	vchan_setblocksize),
461 	KOBJMETHOD(channel_trigger,		vchan_trigger),
462 	KOBJMETHOD(channel_getcaps,		vchan_getcaps),
463 	{0, 0}
464 };
465 CHANNEL_DECLARE(vchan);
466 
467 /*
468  * On the fly vchan rate settings
469  */
470 #ifdef SND_DYNSYSCTL
471 static int
472 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
473 {
474 	struct snddev_info *d;
475 	struct snddev_channel *sce;
476 	struct pcm_channel *c, *ch = NULL, *fake;
477 	struct pcmchan_caps *caps;
478 	int err = 0;
479 	int newspd = 0;
480 
481 	d = oidp->oid_arg1;
482 	if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
483 		return EINVAL;
484 	if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
485 		pcm_inprog(d, -1);
486 		return EINPROGRESS;
487 	}
488 	SLIST_FOREACH(sce, &d->channels, link) {
489 		c = sce->channel;
490 		CHN_LOCK(c);
491 		if (c->direction == PCMDIR_PLAY) {
492 			if (c->flags & CHN_F_VIRTUAL) {
493 				/* Sanity check */
494 				if (ch != NULL && ch != c->parentchannel) {
495 					CHN_UNLOCK(c);
496 					pcm_inprog(d, -1);
497 					return EINVAL;
498 				}
499 				if (req->newptr != NULL &&
500 						(c->flags & CHN_F_BUSY)) {
501 					CHN_UNLOCK(c);
502 					pcm_inprog(d, -1);
503 					return EBUSY;
504 				}
505 			} else if (c->flags & CHN_F_HAS_VCHAN) {
506 				/* No way!! */
507 				if (ch != NULL) {
508 					CHN_UNLOCK(c);
509 					pcm_inprog(d, -1);
510 					return EINVAL;
511 				}
512 				ch = c;
513 				newspd = ch->speed;
514 			}
515 		}
516 		CHN_UNLOCK(c);
517 	}
518 	if (ch == NULL) {
519 		pcm_inprog(d, -1);
520 		return EINVAL;
521 	}
522 	err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
523 	if (err == 0 && req->newptr != NULL) {
524 		if (newspd < 1 || newspd < feeder_rate_min ||
525 				newspd > feeder_rate_max) {
526 			pcm_inprog(d, -1);
527 			return EINVAL;
528 		}
529 		CHN_LOCK(ch);
530 		if (feeder_rate_round) {
531 			caps = chn_getcaps(ch);
532 			if (caps == NULL || newspd < caps->minspeed ||
533 					newspd > caps->maxspeed) {
534 				CHN_UNLOCK(ch);
535 				pcm_inprog(d, -1);
536 				return EINVAL;
537 			}
538 		}
539 		if (newspd != ch->speed) {
540 			err = chn_setspeed(ch, newspd);
541 			/*
542 			 * Try to avoid FEEDER_RATE on parent channel if the
543 			 * requested value is not supported by the hardware.
544 			 */
545 			if (!err && feeder_rate_round &&
546 					(ch->feederflags & (1 << FEEDER_RATE))) {
547 				newspd = sndbuf_getspd(ch->bufhard);
548 				err = chn_setspeed(ch, newspd);
549 			}
550 			CHN_UNLOCK(ch);
551 			if (err == 0) {
552 				fake = pcm_getfakechan(d);
553 				if (fake != NULL) {
554 					CHN_LOCK(fake);
555 					fake->speed = newspd;
556 					CHN_UNLOCK(fake);
557 				}
558 			}
559 		} else
560 			CHN_UNLOCK(ch);
561 	}
562 	pcm_inprog(d, -1);
563 	return err;
564 }
565 
566 static int
567 sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
568 {
569 	struct snddev_info *d;
570 	struct snddev_channel *sce;
571 	struct pcm_channel *c, *ch = NULL, *fake;
572 	uint32_t newfmt, spd;
573 	char fmtstr[AFMTSTR_MAXSZ];
574 	int err = 0, i;
575 
576 	d = oidp->oid_arg1;
577 	if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
578 		return EINVAL;
579 	if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
580 		pcm_inprog(d, -1);
581 		return EINPROGRESS;
582 	}
583 	SLIST_FOREACH(sce, &d->channels, link) {
584 		c = sce->channel;
585 		CHN_LOCK(c);
586 		if (c->direction == PCMDIR_PLAY) {
587 			if (c->flags & CHN_F_VIRTUAL) {
588 				/* Sanity check */
589 				if (ch != NULL && ch != c->parentchannel) {
590 					CHN_UNLOCK(c);
591 					pcm_inprog(d, -1);
592 					return EINVAL;
593 				}
594 				if (req->newptr != NULL &&
595 						(c->flags & CHN_F_BUSY)) {
596 					CHN_UNLOCK(c);
597 					pcm_inprog(d, -1);
598 					return EBUSY;
599 				}
600 			} else if (c->flags & CHN_F_HAS_VCHAN) {
601 				/* No way!! */
602 				if (ch != NULL) {
603 					CHN_UNLOCK(c);
604 					pcm_inprog(d, -1);
605 					return EINVAL;
606 				}
607 				ch = c;
608 				if (ch->format != afmt2afmtstr(vchan_supported_fmts,
609 					    ch->format, fmtstr, sizeof(fmtstr),
610 					    AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) {
611 					strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, sizeof(fmtstr));
612 				}
613 			}
614 		}
615 		CHN_UNLOCK(c);
616 	}
617 	if (ch == NULL) {
618 		pcm_inprog(d, -1);
619 		return EINVAL;
620 	}
621 	err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
622 	if (err == 0 && req->newptr != NULL) {
623 		for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) {
624 			if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) {
625 				strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, sizeof(fmtstr));
626 				break;
627 			}
628 		}
629 		newfmt = vchan_valid_strformat(fmtstr);
630 		if (newfmt == 0) {
631 			pcm_inprog(d, -1);
632 			return EINVAL;
633 		}
634 		CHN_LOCK(ch);
635 		if (newfmt != ch->format) {
636 			/* Get channel speed, before chn_reset() screw it. */
637 			spd = ch->speed;
638 			err = chn_reset(ch, newfmt);
639 			if (err == 0)
640 				err = chn_setspeed(ch, spd);
641 			CHN_UNLOCK(ch);
642 			if (err == 0) {
643 				fake = pcm_getfakechan(d);
644 				if (fake != NULL) {
645 					CHN_LOCK(fake);
646 					fake->format = newfmt;
647 					CHN_UNLOCK(fake);
648 				}
649 			}
650 		} else
651 			CHN_UNLOCK(ch);
652 	}
653 	pcm_inprog(d, -1);
654 	return err;
655 }
656 #endif
657 
658 /* virtual channel interface */
659 
660 int
661 vchan_create(struct pcm_channel *parent)
662 {
663 	struct snddev_info *d = parent->parentsnddev;
664 	struct pcmchan_children *pce;
665 	struct pcm_channel *child, *fake;
666 	struct pcmchan_caps *parent_caps;
667 	uint32_t vchanfmt = 0;
668 	int err, first, speed = 0, r;
669 
670 	if (!(parent->flags & CHN_F_BUSY))
671 		return EBUSY;
672 
673 
674 	CHN_UNLOCK(parent);
675 
676 	pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
677 	if (!pce) {
678 		CHN_LOCK(parent);
679 		return ENOMEM;
680 	}
681 
682 	/* create a new playback channel */
683 	child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
684 	if (!child) {
685 		free(pce, M_DEVBUF);
686 		CHN_LOCK(parent);
687 		return ENODEV;
688 	}
689 	pce->channel = child;
690 
691 	/* add us to our grandparent's channel list */
692 	/*
693 	 * XXX maybe we shouldn't always add the dev_t
694  	 */
695 	err = pcm_chn_add(d, child);
696 	if (err) {
697 		pcm_chn_destroy(child);
698 		free(pce, M_DEVBUF);
699 		CHN_LOCK(parent);
700 		return err;
701 	}
702 
703 	CHN_LOCK(parent);
704 	/* add us to our parent channel's children */
705 	first = SLIST_EMPTY(&parent->children);
706 	SLIST_INSERT_HEAD(&parent->children, pce, link);
707 	parent->flags |= CHN_F_HAS_VCHAN;
708 
709 	if (first) {
710 		parent_caps = chn_getcaps(parent);
711 		if (parent_caps == NULL)
712 			err = EINVAL;
713 
714 		fake = pcm_getfakechan(d);
715 
716 		if (!err && fake != NULL) {
717 			/*
718 			 * Avoid querying kernel hint, use saved value
719 			 * from fake channel.
720 			 */
721 			CHN_UNLOCK(parent);
722 			CHN_LOCK(fake);
723 			speed = fake->speed;
724 			vchanfmt = fake->format;
725 			CHN_UNLOCK(fake);
726 			CHN_LOCK(parent);
727 		}
728 
729 		if (!err) {
730 			if (vchanfmt == 0) {
731 				const char *vfmt;
732 
733 				CHN_UNLOCK(parent);
734 				r = resource_string_value(device_get_name(parent->dev),
735 					device_get_unit(parent->dev),
736 					"vchanformat", &vfmt);
737 				CHN_LOCK(parent);
738 				if (r != 0)
739 					vfmt = NULL;
740 				if (vfmt != NULL) {
741 					vchanfmt = vchan_valid_strformat(vfmt);
742 					for (r = 0; vchanfmt == 0 &&
743 							vchan_fmtstralias[r].alias != NULL;
744 							r++) {
745 						if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) {
746 							vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr);
747 							break;
748 						}
749 					}
750 				}
751 				if (vchanfmt == 0)
752 					vchanfmt = VCHAN_DEFAULT_AFMT;
753 			}
754 			err = chn_reset(parent, vchanfmt);
755 		}
756 
757 		if (!err) {
758 			/*
759 			 * This is very sad. Few soundcards advertised as being
760 			 * able to do (insanely) higher/lower speed, but in
761 			 * reality, they simply can't. At least, we give user chance
762 			 * to set sane value via kernel hints or sysctl.
763 			 */
764 			if (speed < 1) {
765 				CHN_UNLOCK(parent);
766 				r = resource_int_value(device_get_name(parent->dev),
767 							device_get_unit(parent->dev),
768 								"vchanrate", &speed);
769 				CHN_LOCK(parent);
770 				if (r != 0) {
771 					/*
772 					 * No saved value from fake channel,
773 					 * no hint, NOTHING.
774 					 *
775 					 * Workaround for sb16 running
776 					 * poorly at 45k / 49k.
777 					 */
778 					switch (parent_caps->maxspeed) {
779 					case 45000:
780 					case 49000:
781 						speed = 44100;
782 						break;
783 					default:
784 						speed = VCHAN_DEFAULT_SPEED;
785 						if (speed > parent_caps->maxspeed)
786 							speed = parent_caps->maxspeed;
787 						break;
788 					}
789 					if (speed < parent_caps->minspeed)
790 						speed = parent_caps->minspeed;
791 				}
792 			}
793 
794 			if (feeder_rate_round) {
795 				/*
796 				 * Limit speed based on driver caps.
797 				 * This is supposed to help fixed rate, non-VRA
798 				 * AC97 cards, but.. (see below)
799 				 */
800 				if (speed < parent_caps->minspeed)
801 					speed = parent_caps->minspeed;
802 				if (speed > parent_caps->maxspeed)
803 					speed = parent_caps->maxspeed;
804 			}
805 
806 			/*
807 			 * We still need to limit the speed between
808 			 * feeder_rate_min <-> feeder_rate_max. This is
809 			 * just an escape goat if all of the above failed
810 			 * miserably.
811 			 */
812 			if (speed < feeder_rate_min)
813 				speed = feeder_rate_min;
814 			if (speed > feeder_rate_max)
815 				speed = feeder_rate_max;
816 
817 			err = chn_setspeed(parent, speed);
818 			/*
819 			 * Try to avoid FEEDER_RATE on parent channel if the
820 			 * requested value is not supported by the hardware.
821 			 */
822 			if (!err && feeder_rate_round &&
823 					(parent->feederflags & (1 << FEEDER_RATE))) {
824 				speed = sndbuf_getspd(parent->bufhard);
825 				err = chn_setspeed(parent, speed);
826 			}
827 
828 			if (!err && fake != NULL) {
829 				/*
830 				 * Save new value to fake channel.
831 				 */
832 				CHN_UNLOCK(parent);
833 				CHN_LOCK(fake);
834 				fake->speed = speed;
835 				fake->format = vchanfmt;
836 				CHN_UNLOCK(fake);
837 				CHN_LOCK(parent);
838 			}
839 		}
840 
841 		if (err) {
842 			SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
843 			parent->flags &= ~CHN_F_HAS_VCHAN;
844 			CHN_UNLOCK(parent);
845 			free(pce, M_DEVBUF);
846 			if (pcm_chn_remove(d, child) == 0)
847 				pcm_chn_destroy(child);
848 			CHN_LOCK(parent);
849 			return err;
850 		}
851 	}
852 
853 	return 0;
854 }
855 
856 int
857 vchan_destroy(struct pcm_channel *c)
858 {
859 	struct pcm_channel *parent = c->parentchannel;
860 	struct snddev_info *d = parent->parentsnddev;
861 	struct pcmchan_children *pce;
862 	struct snddev_channel *sce;
863 	uint32_t spd;
864 	int err;
865 
866 	CHN_LOCK(parent);
867 	if (!(parent->flags & CHN_F_BUSY)) {
868 		CHN_UNLOCK(parent);
869 		return EBUSY;
870 	}
871 	if (SLIST_EMPTY(&parent->children)) {
872 		CHN_UNLOCK(parent);
873 		return EINVAL;
874 	}
875 
876 	/* remove us from our parent's children list */
877 	SLIST_FOREACH(pce, &parent->children, link) {
878 		if (pce->channel == c)
879 			goto gotch;
880 	}
881 	CHN_UNLOCK(parent);
882 	return EINVAL;
883 gotch:
884 	SLIST_FOREACH(sce, &d->channels, link) {
885 		if (sce->channel == c) {
886 			if (sce->dsp_devt) {
887 				destroy_dev(sce->dsp_devt);
888 				sce->dsp_devt = NULL;
889 			}
890 			if (sce->dspW_devt) {
891 				destroy_dev(sce->dspW_devt);
892 				sce->dspW_devt = NULL;
893 			}
894 			if (sce->audio_devt) {
895 				destroy_dev(sce->audio_devt);
896 				sce->audio_devt = NULL;
897 			}
898 			if (sce->dspHW_devt) {
899 				destroy_dev(sce->dspHW_devt);
900 				sce->dspHW_devt = NULL;
901 			}
902 			d->devcount--;
903 			break;
904 		}
905 	}
906 	SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
907 	free(pce, M_DEVBUF);
908 
909 	if (SLIST_EMPTY(&parent->children)) {
910 		parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
911 		spd = parent->speed;
912 		if (chn_reset(parent, parent->format) == 0)
913 			chn_setspeed(parent, spd);
914 	}
915 
916 	/* remove us from our grandparent's channel list */
917 	err = pcm_chn_remove(d, c);
918 
919 	CHN_UNLOCK(parent);
920 	/* destroy ourselves */
921 	if (!err)
922 		err = pcm_chn_destroy(c);
923 
924 	return err;
925 }
926 
927 int
928 vchan_initsys(device_t dev)
929 {
930 #ifdef SND_DYNSYSCTL
931 	struct snddev_info *d;
932 
933 	d = device_get_softc(dev);
934 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
935 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
936 	    OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
937 	    sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
938 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
939 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
940 	    OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
941 	    sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate");
942 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
943 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
944 	    OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d),
945 	    sysctl_hw_snd_vchanformat, "A", "virtual channel format");
946 #endif
947 
948 	return 0;
949 }
950