vchan.c revision 167678
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 167678 2007-03-18 18:26:40Z 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
46typedef uint32_t (*feed_vchan_mixer)(uint8_t *, uint8_t *, uint32_t);
47
48struct 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 */
55static 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. */
67static 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)	\
101static uint32_t									\
102feed_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
127FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne)
128FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le)
129FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le)
130FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le)
131FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be)
132FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be)
133FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be)
134FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne)
135FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le)
136FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le)
137FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le)
138FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be)
139FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be)
140FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be)
141
142struct feed_vchan_info {
143	uint32_t format;
144	int bps;
145	feed_vchan_mixer mix;
146};
147
148static 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
169static int
170feed_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
191static int
192feed_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
262static 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};
293static kobj_method_t feeder_vchan_methods[] = {
294	KOBJMETHOD(feeder_init,		feed_vchan_init),
295	KOBJMETHOD(feeder_feed,		feed_vchan),
296	{0, 0}
297};
298FEEDER_DECLARE(feeder_vchan, 2, NULL);
299
300/************************************************************/
301
302static void *
303vchan_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
321static int
322vchan_free(kobj_t obj, void *data)
323{
324	free(data, M_DEVBUF);
325	return 0;
326}
327
328static int
329vchan_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
351static int
352vchan_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
367static int
368vchan_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
402static int
403vchan_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
420static struct pcmchan_caps *
421vchan_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
443static 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};
453CHANNEL_DECLARE(vchan);
454
455/*
456 * On the fly vchan rate settings
457 */
458#ifdef SND_DYNSYSCTL
459static int
460sysctl_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
554static int
555sysctl_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
648int
649vchan_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
840int
841vchan_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;
867gotch:
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
911int
912vchan_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