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