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