vchan.c revision 164614
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
35SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/vchan.c 164614 2006-11-26 12:24:06Z ariff $");
36
37MALLOC_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
46struct feed_vchan_info;
47
48typedef uint32_t (*feed_vchan_mixer)(struct feed_vchan_info *,
49				uint8_t *, uint8_t *, uint32_t);
50
51struct feed_vchan_info {
52	uint32_t bps, channels, zero_sample;
53	feed_vchan_mixer mix;
54};
55
56struct 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 */
63static 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. */
75static 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)	\
109static uint32_t									\
110feed_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
135FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne)
136FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le)
137FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le)
138FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le)
139FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be)
140FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be)
141FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be)
142/*  unsigned */
143FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne)
144FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le)
145FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le)
146FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le)
147FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be)
148FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be)
149FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be)
150
151static int
152feed_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
196static int
197feed_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
211static int
212feed_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
222static int
223feed_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
271static 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};
303static 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};
309FEEDER_DECLARE(feeder_vchan, 2, NULL);
310
311/************************************************************/
312
313static void *
314vchan_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
334static int
335vchan_free(kobj_t obj, void *data)
336{
337	return 0;
338}
339
340static int
341vchan_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
363static int
364vchan_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
379static int
380vchan_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
414static int
415vchan_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
432static struct pcmchan_caps *
433vchan_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
455static 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};
465CHANNEL_DECLARE(vchan);
466
467/*
468 * On the fly vchan rate settings
469 */
470#ifdef SND_DYNSYSCTL
471static int
472sysctl_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
566static int
567sysctl_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
660int
661vchan_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
856int
857vchan_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;
883gotch:
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
927int
928vchan_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