xref: /freebsd/sys/dev/sound/pcm/vchan.c (revision 2b743a9e9ddc6736208dc8ca1ce06ce64ad20a19)
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 	free(data, M_DEVBUF);
338 	return 0;
339 }
340 
341 static int
342 vchan_setformat(kobj_t obj, void *data, uint32_t format)
343 {
344 	struct vchinfo *ch = data;
345 	struct pcm_channel *parent = ch->parent;
346 	struct pcm_channel *channel = ch->channel;
347 
348 	ch->fmt = format;
349 	ch->bps = 1;
350 	ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0;
351 	if (ch->fmt & AFMT_16BIT)
352 		ch->bps <<= 1;
353 	else if (ch->fmt & AFMT_24BIT)
354 		ch->bps *= 3;
355 	else if (ch->fmt & AFMT_32BIT)
356 		ch->bps <<= 2;
357 	CHN_UNLOCK(channel);
358 	chn_notify(parent, CHN_N_FORMAT);
359 	CHN_LOCK(channel);
360 	sndbuf_setfmt(channel->bufsoft, format);
361 	return 0;
362 }
363 
364 static int
365 vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
366 {
367 	struct vchinfo *ch = data;
368 	struct pcm_channel *parent = ch->parent;
369 	struct pcm_channel *channel = ch->channel;
370 
371 	ch->spd = speed;
372 	CHN_UNLOCK(channel);
373 	CHN_LOCK(parent);
374 	speed = sndbuf_getspd(parent->bufsoft);
375 	CHN_UNLOCK(parent);
376 	CHN_LOCK(channel);
377 	return speed;
378 }
379 
380 static int
381 vchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
382 {
383 	struct vchinfo *ch = data;
384 	struct pcm_channel *channel = ch->channel;
385 	struct pcm_channel *parent = ch->parent;
386 	/* struct pcm_channel *channel = ch->channel; */
387 	int prate, crate;
388 
389 	ch->blksz = blocksize;
390 	/* CHN_UNLOCK(channel); */
391 	sndbuf_setblksz(channel->bufhard, blocksize);
392 	chn_notify(parent, CHN_N_BLOCKSIZE);
393 	CHN_LOCK(parent);
394 	/* CHN_LOCK(channel); */
395 
396 	crate = ch->spd * ch->bps;
397 	prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft);
398 	blocksize = sndbuf_getblksz(parent->bufsoft);
399 	CHN_UNLOCK(parent);
400 	blocksize *= prate;
401 	blocksize /= crate;
402 	blocksize += ch->bps;
403 	prate = 0;
404 	while (blocksize >> prate)
405 		prate++;
406 	blocksize = 1 << (prate - 1);
407 	blocksize -= blocksize % ch->bps;
408 	/* XXX screwed !@#$ */
409 	if (blocksize < ch->bps)
410 		blocksize = 4096 - (4096 % ch->bps);
411 
412 	return blocksize;
413 }
414 
415 static int
416 vchan_trigger(kobj_t obj, void *data, int go)
417 {
418 	struct vchinfo *ch = data;
419 	struct pcm_channel *parent = ch->parent;
420 	struct pcm_channel *channel = ch->channel;
421 
422 	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
423 		return 0;
424 
425 	ch->run = (go == PCMTRIG_START)? 1 : 0;
426 	CHN_UNLOCK(channel);
427 	chn_notify(parent, CHN_N_TRIGGER);
428 	CHN_LOCK(channel);
429 
430 	return 0;
431 }
432 
433 static struct pcmchan_caps *
434 vchan_getcaps(kobj_t obj, void *data)
435 {
436 	struct vchinfo *ch = data;
437 	uint32_t fmt;
438 
439 	ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft);
440 	ch->caps.maxspeed = ch->caps.minspeed;
441 	ch->caps.caps = 0;
442 	ch->fmts[1] = 0;
443 	fmt = sndbuf_getfmt(ch->parent->bufsoft);
444 	if (fmt != vchan_valid_format(fmt)) {
445 		device_printf(ch->parent->dev,
446 			    "%s: WARNING: invalid vchan format! (0x%08x)\n",
447 			    __func__, fmt);
448 		fmt = VCHAN_DEFAULT_AFMT;
449 	}
450 	ch->fmts[0] = fmt;
451 	ch->caps.fmtlist = ch->fmts;
452 
453 	return &ch->caps;
454 }
455 
456 static kobj_method_t vchan_methods[] = {
457 	KOBJMETHOD(channel_init,		vchan_init),
458 	KOBJMETHOD(channel_free,		vchan_free),
459 	KOBJMETHOD(channel_setformat,		vchan_setformat),
460 	KOBJMETHOD(channel_setspeed,		vchan_setspeed),
461 	KOBJMETHOD(channel_setblocksize,	vchan_setblocksize),
462 	KOBJMETHOD(channel_trigger,		vchan_trigger),
463 	KOBJMETHOD(channel_getcaps,		vchan_getcaps),
464 	{0, 0}
465 };
466 CHANNEL_DECLARE(vchan);
467 
468 /*
469  * On the fly vchan rate settings
470  */
471 #ifdef SND_DYNSYSCTL
472 static int
473 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
474 {
475 	struct snddev_info *d;
476 	struct snddev_channel *sce;
477 	struct pcm_channel *c, *ch = NULL, *fake;
478 	struct pcmchan_caps *caps;
479 	int err = 0;
480 	int newspd = 0;
481 
482 	d = oidp->oid_arg1;
483 	if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
484 		return EINVAL;
485 	if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
486 		pcm_inprog(d, -1);
487 		return EINPROGRESS;
488 	}
489 	SLIST_FOREACH(sce, &d->channels, link) {
490 		c = sce->channel;
491 		CHN_LOCK(c);
492 		if (c->direction == PCMDIR_PLAY) {
493 			if (c->flags & CHN_F_VIRTUAL) {
494 				/* Sanity check */
495 				if (ch != NULL && ch != c->parentchannel) {
496 					CHN_UNLOCK(c);
497 					pcm_inprog(d, -1);
498 					return EINVAL;
499 				}
500 				if (req->newptr != NULL &&
501 						(c->flags & CHN_F_BUSY)) {
502 					CHN_UNLOCK(c);
503 					pcm_inprog(d, -1);
504 					return EBUSY;
505 				}
506 			} else if (c->flags & CHN_F_HAS_VCHAN) {
507 				/* No way!! */
508 				if (ch != NULL) {
509 					CHN_UNLOCK(c);
510 					pcm_inprog(d, -1);
511 					return EINVAL;
512 				}
513 				ch = c;
514 				newspd = ch->speed;
515 			}
516 		}
517 		CHN_UNLOCK(c);
518 	}
519 	if (ch == NULL) {
520 		pcm_inprog(d, -1);
521 		return EINVAL;
522 	}
523 	err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
524 	if (err == 0 && req->newptr != NULL) {
525 		if (newspd < 1 || newspd < feeder_rate_min ||
526 				newspd > feeder_rate_max) {
527 			pcm_inprog(d, -1);
528 			return EINVAL;
529 		}
530 		CHN_LOCK(ch);
531 		if (feeder_rate_round) {
532 			caps = chn_getcaps(ch);
533 			if (caps == NULL || newspd < caps->minspeed ||
534 					newspd > caps->maxspeed) {
535 				CHN_UNLOCK(ch);
536 				pcm_inprog(d, -1);
537 				return EINVAL;
538 			}
539 		}
540 		if (newspd != ch->speed) {
541 			err = chn_setspeed(ch, newspd);
542 			/*
543 			 * Try to avoid FEEDER_RATE on parent channel if the
544 			 * requested value is not supported by the hardware.
545 			 */
546 			if (!err && feeder_rate_round &&
547 					(ch->feederflags & (1 << FEEDER_RATE))) {
548 				newspd = sndbuf_getspd(ch->bufhard);
549 				err = chn_setspeed(ch, newspd);
550 			}
551 			CHN_UNLOCK(ch);
552 			if (err == 0) {
553 				fake = pcm_getfakechan(d);
554 				if (fake != NULL) {
555 					CHN_LOCK(fake);
556 					fake->speed = newspd;
557 					CHN_UNLOCK(fake);
558 				}
559 			}
560 		} else
561 			CHN_UNLOCK(ch);
562 	}
563 	pcm_inprog(d, -1);
564 	return err;
565 }
566 
567 static int
568 sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
569 {
570 	struct snddev_info *d;
571 	struct snddev_channel *sce;
572 	struct pcm_channel *c, *ch = NULL, *fake;
573 	uint32_t newfmt, spd;
574 	char fmtstr[AFMTSTR_MAXSZ];
575 	int err = 0, i;
576 
577 	d = oidp->oid_arg1;
578 	if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
579 		return EINVAL;
580 	if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
581 		pcm_inprog(d, -1);
582 		return EINPROGRESS;
583 	}
584 	SLIST_FOREACH(sce, &d->channels, link) {
585 		c = sce->channel;
586 		CHN_LOCK(c);
587 		if (c->direction == PCMDIR_PLAY) {
588 			if (c->flags & CHN_F_VIRTUAL) {
589 				/* Sanity check */
590 				if (ch != NULL && ch != c->parentchannel) {
591 					CHN_UNLOCK(c);
592 					pcm_inprog(d, -1);
593 					return EINVAL;
594 				}
595 				if (req->newptr != NULL &&
596 						(c->flags & CHN_F_BUSY)) {
597 					CHN_UNLOCK(c);
598 					pcm_inprog(d, -1);
599 					return EBUSY;
600 				}
601 			} else if (c->flags & CHN_F_HAS_VCHAN) {
602 				/* No way!! */
603 				if (ch != NULL) {
604 					CHN_UNLOCK(c);
605 					pcm_inprog(d, -1);
606 					return EINVAL;
607 				}
608 				ch = c;
609 				if (ch->format != afmt2afmtstr(vchan_supported_fmts,
610 					    ch->format, fmtstr, sizeof(fmtstr),
611 					    AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) {
612 					strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, sizeof(fmtstr));
613 				}
614 			}
615 		}
616 		CHN_UNLOCK(c);
617 	}
618 	if (ch == NULL) {
619 		pcm_inprog(d, -1);
620 		return EINVAL;
621 	}
622 	err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
623 	if (err == 0 && req->newptr != NULL) {
624 		for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) {
625 			if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) {
626 				strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, sizeof(fmtstr));
627 				break;
628 			}
629 		}
630 		newfmt = vchan_valid_strformat(fmtstr);
631 		if (newfmt == 0) {
632 			pcm_inprog(d, -1);
633 			return EINVAL;
634 		}
635 		CHN_LOCK(ch);
636 		if (newfmt != ch->format) {
637 			/* Get channel speed, before chn_reset() screw it. */
638 			spd = ch->speed;
639 			err = chn_reset(ch, newfmt);
640 			if (err == 0)
641 				err = chn_setspeed(ch, spd);
642 			CHN_UNLOCK(ch);
643 			if (err == 0) {
644 				fake = pcm_getfakechan(d);
645 				if (fake != NULL) {
646 					CHN_LOCK(fake);
647 					fake->format = newfmt;
648 					CHN_UNLOCK(fake);
649 				}
650 			}
651 		} else
652 			CHN_UNLOCK(ch);
653 	}
654 	pcm_inprog(d, -1);
655 	return err;
656 }
657 #endif
658 
659 /* virtual channel interface */
660 
661 int
662 vchan_create(struct pcm_channel *parent)
663 {
664 	struct snddev_info *d = parent->parentsnddev;
665 	struct pcmchan_children *pce;
666 	struct pcm_channel *child, *fake;
667 	struct pcmchan_caps *parent_caps;
668 	uint32_t vchanfmt = 0;
669 	int err, first, speed = 0, r;
670 
671 	if (!(parent->flags & CHN_F_BUSY))
672 		return EBUSY;
673 
674 
675 	CHN_UNLOCK(parent);
676 
677 	pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
678 	if (!pce) {
679 		CHN_LOCK(parent);
680 		return ENOMEM;
681 	}
682 
683 	/* create a new playback channel */
684 	child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
685 	if (!child) {
686 		free(pce, M_DEVBUF);
687 		CHN_LOCK(parent);
688 		return ENODEV;
689 	}
690 	pce->channel = child;
691 
692 	/* add us to our grandparent's channel list */
693 	/*
694 	 * XXX maybe we shouldn't always add the dev_t
695  	 */
696 	err = pcm_chn_add(d, child);
697 	if (err) {
698 		pcm_chn_destroy(child);
699 		free(pce, M_DEVBUF);
700 		CHN_LOCK(parent);
701 		return err;
702 	}
703 
704 	CHN_LOCK(parent);
705 	/* add us to our parent channel's children */
706 	first = SLIST_EMPTY(&parent->children);
707 	SLIST_INSERT_HEAD(&parent->children, pce, link);
708 	parent->flags |= CHN_F_HAS_VCHAN;
709 
710 	if (first) {
711 		parent_caps = chn_getcaps(parent);
712 		if (parent_caps == NULL)
713 			err = EINVAL;
714 
715 		fake = pcm_getfakechan(d);
716 
717 		if (!err && fake != NULL) {
718 			/*
719 			 * Avoid querying kernel hint, use saved value
720 			 * from fake channel.
721 			 */
722 			CHN_UNLOCK(parent);
723 			CHN_LOCK(fake);
724 			speed = fake->speed;
725 			vchanfmt = fake->format;
726 			CHN_UNLOCK(fake);
727 			CHN_LOCK(parent);
728 		}
729 
730 		if (!err) {
731 			if (vchanfmt == 0) {
732 				const char *vfmt;
733 
734 				CHN_UNLOCK(parent);
735 				r = resource_string_value(device_get_name(parent->dev),
736 					device_get_unit(parent->dev),
737 					"vchanformat", &vfmt);
738 				CHN_LOCK(parent);
739 				if (r != 0)
740 					vfmt = NULL;
741 				if (vfmt != NULL) {
742 					vchanfmt = vchan_valid_strformat(vfmt);
743 					for (r = 0; vchanfmt == 0 &&
744 							vchan_fmtstralias[r].alias != NULL;
745 							r++) {
746 						if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) {
747 							vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr);
748 							break;
749 						}
750 					}
751 				}
752 				if (vchanfmt == 0)
753 					vchanfmt = VCHAN_DEFAULT_AFMT;
754 			}
755 			err = chn_reset(parent, vchanfmt);
756 		}
757 
758 		if (!err) {
759 			/*
760 			 * This is very sad. Few soundcards advertised as being
761 			 * able to do (insanely) higher/lower speed, but in
762 			 * reality, they simply can't. At least, we give user chance
763 			 * to set sane value via kernel hints or sysctl.
764 			 */
765 			if (speed < 1) {
766 				CHN_UNLOCK(parent);
767 				r = resource_int_value(device_get_name(parent->dev),
768 							device_get_unit(parent->dev),
769 								"vchanrate", &speed);
770 				CHN_LOCK(parent);
771 				if (r != 0) {
772 					/*
773 					 * No saved value from fake channel,
774 					 * no hint, NOTHING.
775 					 *
776 					 * Workaround for sb16 running
777 					 * poorly at 45k / 49k.
778 					 */
779 					switch (parent_caps->maxspeed) {
780 					case 45000:
781 					case 49000:
782 						speed = 44100;
783 						break;
784 					default:
785 						speed = VCHAN_DEFAULT_SPEED;
786 						if (speed > parent_caps->maxspeed)
787 							speed = parent_caps->maxspeed;
788 						break;
789 					}
790 					if (speed < parent_caps->minspeed)
791 						speed = parent_caps->minspeed;
792 				}
793 			}
794 
795 			if (feeder_rate_round) {
796 				/*
797 				 * Limit speed based on driver caps.
798 				 * This is supposed to help fixed rate, non-VRA
799 				 * AC97 cards, but.. (see below)
800 				 */
801 				if (speed < parent_caps->minspeed)
802 					speed = parent_caps->minspeed;
803 				if (speed > parent_caps->maxspeed)
804 					speed = parent_caps->maxspeed;
805 			}
806 
807 			/*
808 			 * We still need to limit the speed between
809 			 * feeder_rate_min <-> feeder_rate_max. This is
810 			 * just an escape goat if all of the above failed
811 			 * miserably.
812 			 */
813 			if (speed < feeder_rate_min)
814 				speed = feeder_rate_min;
815 			if (speed > feeder_rate_max)
816 				speed = feeder_rate_max;
817 
818 			err = chn_setspeed(parent, speed);
819 			/*
820 			 * Try to avoid FEEDER_RATE on parent channel if the
821 			 * requested value is not supported by the hardware.
822 			 */
823 			if (!err && feeder_rate_round &&
824 					(parent->feederflags & (1 << FEEDER_RATE))) {
825 				speed = sndbuf_getspd(parent->bufhard);
826 				err = chn_setspeed(parent, speed);
827 			}
828 
829 			if (!err && fake != NULL) {
830 				/*
831 				 * Save new value to fake channel.
832 				 */
833 				CHN_UNLOCK(parent);
834 				CHN_LOCK(fake);
835 				fake->speed = speed;
836 				fake->format = vchanfmt;
837 				CHN_UNLOCK(fake);
838 				CHN_LOCK(parent);
839 			}
840 		}
841 
842 		if (err) {
843 			SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
844 			parent->flags &= ~CHN_F_HAS_VCHAN;
845 			CHN_UNLOCK(parent);
846 			free(pce, M_DEVBUF);
847 			if (pcm_chn_remove(d, child) == 0)
848 				pcm_chn_destroy(child);
849 			CHN_LOCK(parent);
850 			return err;
851 		}
852 	}
853 
854 	return 0;
855 }
856 
857 int
858 vchan_destroy(struct pcm_channel *c)
859 {
860 	struct pcm_channel *parent = c->parentchannel;
861 	struct snddev_info *d = parent->parentsnddev;
862 	struct pcmchan_children *pce;
863 	struct snddev_channel *sce;
864 	uint32_t spd;
865 	int err;
866 
867 	CHN_LOCK(parent);
868 	if (!(parent->flags & CHN_F_BUSY)) {
869 		CHN_UNLOCK(parent);
870 		return EBUSY;
871 	}
872 	if (SLIST_EMPTY(&parent->children)) {
873 		CHN_UNLOCK(parent);
874 		return EINVAL;
875 	}
876 
877 	/* remove us from our parent's children list */
878 	SLIST_FOREACH(pce, &parent->children, link) {
879 		if (pce->channel == c)
880 			goto gotch;
881 	}
882 	CHN_UNLOCK(parent);
883 	return EINVAL;
884 gotch:
885 	SLIST_FOREACH(sce, &d->channels, link) {
886 		if (sce->channel == c) {
887 			if (sce->dsp_devt) {
888 				destroy_dev(sce->dsp_devt);
889 				sce->dsp_devt = NULL;
890 			}
891 			if (sce->dspW_devt) {
892 				destroy_dev(sce->dspW_devt);
893 				sce->dspW_devt = NULL;
894 			}
895 			if (sce->audio_devt) {
896 				destroy_dev(sce->audio_devt);
897 				sce->audio_devt = NULL;
898 			}
899 			if (sce->dspHW_devt) {
900 				destroy_dev(sce->dspHW_devt);
901 				sce->dspHW_devt = NULL;
902 			}
903 			d->devcount--;
904 			break;
905 		}
906 	}
907 	SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
908 	free(pce, M_DEVBUF);
909 
910 	if (SLIST_EMPTY(&parent->children)) {
911 		parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
912 		spd = parent->speed;
913 		if (chn_reset(parent, parent->format) == 0)
914 			chn_setspeed(parent, spd);
915 	}
916 
917 	/* remove us from our grandparent's channel list */
918 	err = pcm_chn_remove(d, c);
919 
920 	CHN_UNLOCK(parent);
921 	/* destroy ourselves */
922 	if (!err)
923 		err = pcm_chn_destroy(c);
924 
925 	return err;
926 }
927 
928 int
929 vchan_initsys(device_t dev)
930 {
931 #ifdef SND_DYNSYSCTL
932 	struct snddev_info *d;
933 
934 	d = device_get_softc(dev);
935 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
936 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
937 	    OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
938 	    sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
939 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
940 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
941 	    OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
942 	    sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate");
943 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
944 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
945 	    OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d),
946 	    sysctl_hw_snd_vchanformat, "A", "virtual channel format");
947 #endif
948 
949 	return 0;
950 }
951