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