vchan.c revision 170521
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
34SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/vchan.c 170521 2007-06-11 00:49:46Z ariff $");
35
36MALLOC_DEFINE(M_VCHANFEEDER, "vchanfeed", "pcm vchan feeder");
37
38typedef uint32_t (*feed_vchan_mixer)(uint8_t *, uint8_t *, uint32_t);
39
40struct 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 */
48static 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. */
60static 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)	\
94static uint32_t									\
95feed_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
120FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne)
121FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le)
122FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le)
123FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le)
124FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be)
125FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be)
126FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be)
127FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne)
128FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le)
129FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le)
130FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le)
131FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be)
132FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be)
133FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be)
134
135struct feed_vchan_info {
136	uint32_t format;
137	int bps;
138	feed_vchan_mixer mix;
139};
140
141static 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
162static int
163feed_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
184static __inline int
185feed_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		bs = ch->bufsoft;
226		cnt = sndbuf_getfree(bs);
227		if (!(ch->flags & CHN_F_TRIGGERED) ||
228		    cnt < sndbuf_getbps(bs)) {
229			CHN_UNLOCK(ch);
230			continue;
231		}
232		do {
233			cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf, cnt, b);
234			if (cnt != 0) {
235				sndbuf_acquire(bs, bs->tmpbuf, cnt);
236				cnt = sndbuf_getfree(bs);
237			}
238		} while (cnt != 0);
239		/*
240		 * Not entirely flushed out...
241		 */
242		if (b->rl != 0)
243			ch->xruns++;
244		CHN_UNLOCK(ch);
245		/*
246		 * Rewind buffer position for next virtual channel.
247		 */
248		b->rp = 0;
249		b->rl = rdy;
250	}
251
252	/*
253	 * Set ready pointer to indicate that our children are ready
254	 * to be woken up, also as an interrupt threshold progress
255	 * indicator.
256	 */
257	b->rl = 1;
258
259	/*
260	 * Return 0 to bail out early from sndbuf_feed() loop.
261	 * No need to increase feedcount counter since part of this
262	 * feeder chains already include feed_root().
263	 */
264	return (0);
265}
266
267static int
268feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
269    uint32_t count, void *source)
270{
271	struct feed_vchan_info *info;
272	struct snd_dbuf *src = source;
273	struct pcm_channel *ch;
274	uint32_t cnt, mcnt, rcnt, sz;
275	uint8_t *tmp;
276
277	if (c->direction == PCMDIR_REC)
278		return (feed_vchan_rec(c));
279
280	sz = sndbuf_getsize(src);
281	if (sz < count)
282		count = sz;
283
284	info = &feed_vchan_info_tbl[FVCHAN_INFOIDX((intptr_t)f->data)];
285	sz = info->bps * FVCHAN_CHANNELS((intptr_t)f->data);
286	count -= count % sz;
287	if (count < sz)
288		return (0);
289
290	/*
291	 * we are going to use our source as a temporary buffer since it's
292	 * got no other purpose.  we obtain our data by traversing the channel
293	 * list of children and calling vchan_mix_* to mix count bytes from
294	 * each into our destination buffer, b
295	 */
296	tmp = sndbuf_getbuf(src);
297	rcnt = 0;
298	mcnt = 0;
299
300	CHN_FOREACH(ch, c, children.busy) {
301		CHN_LOCK(ch);
302		if (!(ch->flags & CHN_F_TRIGGERED)) {
303			CHN_UNLOCK(ch);
304			continue;
305		}
306		if ((ch->flags & CHN_F_MAPPED) && !(ch->flags & CHN_F_CLOSING))
307			sndbuf_acquire(ch->bufsoft, NULL,
308			    sndbuf_getfree(ch->bufsoft));
309		if (rcnt == 0) {
310			rcnt = FEEDER_FEED(ch->feeder, ch, b, count,
311			    ch->bufsoft);
312			rcnt -= rcnt % sz;
313			mcnt = count - rcnt;
314		} else {
315			cnt = FEEDER_FEED(ch->feeder, ch, tmp, count,
316			    ch->bufsoft);
317			cnt -= cnt % sz;
318			if (cnt != 0) {
319				if (mcnt != 0) {
320					memset(b + rcnt,
321					    sndbuf_zerodata(f->desc->out),
322					    mcnt);
323					mcnt = 0;
324				}
325				cnt = info->mix(b, tmp, cnt);
326				if (cnt > rcnt)
327					rcnt = cnt;
328			}
329		}
330		CHN_UNLOCK(ch);
331	}
332
333	if (++c->feedcount == 0)
334		c->feedcount = 2;
335
336	return (rcnt);
337}
338
339static struct pcm_feederdesc feeder_vchan_desc[] = {
340	{FEEDER_MIXER, AFMT_S8, AFMT_S8, 0},
341	{FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0},
342	{FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0},
343	{FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0},
344	{FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0},
345	{FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0},
346	{FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0},
347	{FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0},
348	{FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
349	{FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0},
350	{FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0},
351	{FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0},
352	{FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0},
353	{FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0},
354	{FEEDER_MIXER, AFMT_U8, AFMT_U8, 0},
355	{FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0},
356	{FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0},
357	{FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0},
358	{FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0},
359	{FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0},
360	{FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0},
361	{FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0},
362	{FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0},
363	{FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0},
364	{FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0},
365	{FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0},
366	{FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0},
367	{FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0},
368	{0, 0, 0, 0},
369};
370static kobj_method_t feeder_vchan_methods[] = {
371	KOBJMETHOD(feeder_init,		feed_vchan_init),
372	KOBJMETHOD(feeder_feed,		feed_vchan),
373	{0, 0}
374};
375FEEDER_DECLARE(feeder_vchan, 2, NULL);
376
377/************************************************************/
378
379static void *
380vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
381    struct pcm_channel *c, int dir)
382{
383	struct vchinfo *ch;
384
385	KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC,
386	    ("vchan_init: bad direction"));
387	KASSERT(c != NULL && c->parentchannel != NULL,
388	    ("vchan_init: bad channels"));
389
390	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
391	ch->channel = c;
392	ch->trigger = PCMTRIG_STOP;
393
394	c->flags |= CHN_F_VIRTUAL;
395
396	return (ch);
397}
398
399static int
400vchan_free(kobj_t obj, void *data)
401{
402	free(data, M_DEVBUF);
403
404	return (0);
405}
406
407static int
408vchan_setformat(kobj_t obj, void *data, uint32_t format)
409{
410	struct vchinfo *ch = data;
411
412	if (fmtvalid(format, ch->fmtlist) == 0)
413		return (-1);
414
415	return (0);
416}
417
418static int
419vchan_setspeed(kobj_t obj, void *data, uint32_t speed)
420{
421	struct vchinfo *ch = data;
422	struct pcm_channel *p = ch->channel->parentchannel;
423
424	return (sndbuf_getspd(p->bufsoft));
425}
426
427static int
428vchan_trigger(kobj_t obj, void *data, int go)
429{
430	struct vchinfo *ch = data;
431	struct pcm_channel *c, *p;
432	int otrigger;
433
434	if (!PCMTRIG_COMMON(go) || go == ch->trigger)
435		return (0);
436
437	c = ch->channel;
438	p = c->parentchannel;
439	otrigger = ch->trigger;
440	ch->trigger = go;
441
442	CHN_UNLOCK(c);
443	CHN_LOCK(p);
444
445	switch (go) {
446	case PCMTRIG_START:
447		if (otrigger != PCMTRIG_START) {
448			CHN_INSERT_HEAD(p, c, children.busy);
449		}
450		break;
451	case PCMTRIG_STOP:
452	case PCMTRIG_ABORT:
453		if (otrigger == PCMTRIG_START) {
454			CHN_REMOVE(p, c, children.busy);
455		}
456		break;
457	default:
458		break;
459	}
460
461	CHN_UNLOCK(p);
462	chn_notify(p, CHN_N_TRIGGER);
463	CHN_LOCK(c);
464
465	return (0);
466}
467
468static struct pcmchan_caps *
469vchan_getcaps(kobj_t obj, void *data)
470{
471	struct vchinfo *ch = data;
472	struct pcm_channel *c, *p;
473	uint32_t fmt;
474
475	c = ch->channel;
476	p = c->parentchannel;
477	ch->caps.minspeed = sndbuf_getspd(p->bufsoft);
478	ch->caps.maxspeed = ch->caps.minspeed;
479	ch->caps.caps = 0;
480	ch->fmtlist[1] = 0;
481	fmt = sndbuf_getfmt(p->bufsoft);
482	if (fmt != vchan_valid_format(fmt)) {
483		device_printf(c->dev,
484			    "%s: WARNING: invalid vchan format! (0x%08x)\n",
485			    __func__, fmt);
486		fmt = VCHAN_DEFAULT_AFMT;
487	}
488	ch->fmtlist[0] = fmt;
489	ch->caps.fmtlist = ch->fmtlist;
490
491	return (&ch->caps);
492}
493
494static kobj_method_t vchan_methods[] = {
495	KOBJMETHOD(channel_init,		vchan_init),
496	KOBJMETHOD(channel_free,		vchan_free),
497	KOBJMETHOD(channel_setformat,		vchan_setformat),
498	KOBJMETHOD(channel_setspeed,		vchan_setspeed),
499	KOBJMETHOD(channel_trigger,		vchan_trigger),
500	KOBJMETHOD(channel_getcaps,		vchan_getcaps),
501	{0, 0}
502};
503CHANNEL_DECLARE(vchan);
504
505/*
506 * On the fly vchan rate settings
507 */
508#ifdef SND_DYNSYSCTL
509static int
510sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
511{
512	struct snddev_info *d;
513	struct pcm_channel *c, *ch = NULL;
514	struct pcmchan_caps *caps;
515	int vchancount, *vchanrate;
516	int direction;
517	int err = 0;
518	int newspd = 0;
519
520	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
521	if (d == NULL || !(d->flags & SD_F_AUTOVCHAN))
522		return (EINVAL);
523
524	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
525	case VCHAN_PLAY:
526		direction = PCMDIR_PLAY;
527		vchancount = d->pvchancount;
528		vchanrate = &d->pvchanrate;
529		break;
530	case VCHAN_REC:
531		direction = PCMDIR_REC;
532		vchancount = d->rvchancount;
533		vchanrate = &d->rvchanrate;
534		break;
535	default:
536		return (EINVAL);
537		break;
538	}
539
540	if (vchancount < 1)
541		return (EINVAL);
542	if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
543		pcm_inprog(d, -1);
544		return (EINPROGRESS);
545	}
546	CHN_FOREACH(c, d, channels.pcm) {
547		CHN_LOCK(c);
548		if (c->direction == direction) {
549			if (c->flags & CHN_F_VIRTUAL) {
550				/* Sanity check */
551				if (ch != NULL && ch != c->parentchannel) {
552					CHN_UNLOCK(c);
553					pcm_inprog(d, -1);
554					return (EINVAL);
555				}
556				if (req->newptr != NULL &&
557						(c->flags & CHN_F_BUSY)) {
558					CHN_UNLOCK(c);
559					pcm_inprog(d, -1);
560					return (EBUSY);
561				}
562			} else if (c->flags & CHN_F_HAS_VCHAN) {
563				/* No way!! */
564				if (ch != NULL) {
565					CHN_UNLOCK(c);
566					pcm_inprog(d, -1);
567					return (EINVAL);
568				}
569				ch = c;
570				newspd = ch->speed;
571			}
572		}
573		CHN_UNLOCK(c);
574	}
575	if (ch == NULL) {
576		pcm_inprog(d, -1);
577		return (EINVAL);
578	}
579	err = sysctl_handle_int(oidp, &newspd, 0, req);
580	if (err == 0 && req->newptr != NULL) {
581		if (newspd < 1 || newspd < feeder_rate_min ||
582		    newspd > feeder_rate_max) {
583			pcm_inprog(d, -1);
584			return (EINVAL);
585		}
586		CHN_LOCK(ch);
587		if (feeder_rate_round) {
588			caps = chn_getcaps(ch);
589			if (caps == NULL || newspd < caps->minspeed ||
590			    newspd > caps->maxspeed) {
591				CHN_UNLOCK(ch);
592				pcm_inprog(d, -1);
593				return (EINVAL);
594			}
595		}
596		if (newspd != ch->speed) {
597			err = chn_setspeed(ch, newspd);
598			/*
599			 * Try to avoid FEEDER_RATE on parent channel if the
600			 * requested value is not supported by the hardware.
601			 */
602			if (!err && feeder_rate_round &&
603			    (ch->feederflags & (1 << FEEDER_RATE))) {
604				newspd = sndbuf_getspd(ch->bufhard);
605				err = chn_setspeed(ch, newspd);
606			}
607			CHN_UNLOCK(ch);
608			if (err == 0) {
609				pcm_lock(d);
610				*vchanrate = newspd;
611				pcm_unlock(d);
612			}
613		} else
614			CHN_UNLOCK(ch);
615	}
616	pcm_inprog(d, -1);
617	return (err);
618}
619
620static int
621sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
622{
623	struct snddev_info *d;
624	struct pcm_channel *c, *ch = NULL;
625	uint32_t newfmt, spd;
626	int vchancount, *vchanformat;
627	int direction;
628	int err = 0, i;
629	char fmtstr[AFMTSTR_MAXSZ];
630
631	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
632	if (d == NULL || !(d->flags & SD_F_AUTOVCHAN))
633		return (EINVAL);
634
635	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
636	case VCHAN_PLAY:
637		direction = PCMDIR_PLAY;
638		vchancount = d->pvchancount;
639		vchanformat = &d->pvchanformat;
640		break;
641	case VCHAN_REC:
642		direction = PCMDIR_REC;
643		vchancount = d->rvchancount;
644		vchanformat = &d->rvchanformat;
645		break;
646	default:
647		return (EINVAL);
648		break;
649	}
650
651	if (vchancount < 1)
652		return (EINVAL);
653	if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
654		pcm_inprog(d, -1);
655		return (EINPROGRESS);
656	}
657	CHN_FOREACH(c, d, channels.pcm) {
658		CHN_LOCK(c);
659		if (c->direction == direction) {
660			if (c->flags & CHN_F_VIRTUAL) {
661				/* Sanity check */
662				if (ch != NULL && ch != c->parentchannel) {
663					CHN_UNLOCK(c);
664					pcm_inprog(d, -1);
665					return (EINVAL);
666				}
667				if (req->newptr != NULL &&
668						(c->flags & CHN_F_BUSY)) {
669					CHN_UNLOCK(c);
670					pcm_inprog(d, -1);
671					return (EBUSY);
672				}
673			} else if (c->flags & CHN_F_HAS_VCHAN) {
674				/* No way!! */
675				if (ch != NULL) {
676					CHN_UNLOCK(c);
677					pcm_inprog(d, -1);
678					return (EINVAL);
679				}
680				ch = c;
681				if (ch->format !=
682				    afmt2afmtstr(vchan_supported_fmts,
683				    ch->format, fmtstr, sizeof(fmtstr),
684				    AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) {
685					strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT,
686					    sizeof(fmtstr));
687				}
688			}
689		}
690		CHN_UNLOCK(c);
691	}
692	if (ch == NULL) {
693		pcm_inprog(d, -1);
694		return (EINVAL);
695	}
696	err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
697	if (err == 0 && req->newptr != NULL) {
698		for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) {
699			if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) {
700				strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr,
701				    sizeof(fmtstr));
702				break;
703			}
704		}
705		newfmt = vchan_valid_strformat(fmtstr);
706		if (newfmt == 0) {
707			pcm_inprog(d, -1);
708			return (EINVAL);
709		}
710		CHN_LOCK(ch);
711		if (newfmt != ch->format) {
712			/* Get channel speed, before chn_reset() screw it. */
713			spd = ch->speed;
714			err = chn_reset(ch, newfmt);
715			if (err == 0)
716				err = chn_setspeed(ch, spd);
717			CHN_UNLOCK(ch);
718			if (err == 0) {
719				pcm_lock(d);
720				*vchanformat = newfmt;
721				pcm_unlock(d);
722			}
723		} else
724			CHN_UNLOCK(ch);
725	}
726	pcm_inprog(d, -1);
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
738int
739vchan_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	if (!(parent->flags & CHN_F_BUSY))
749		return (EBUSY);
750
751	if (parent->direction == PCMDIR_PLAY) {
752		direction = PCMDIR_PLAY_VIRTUAL;
753		vchanfmt = d->pvchanformat;
754		speed = d->pvchanrate;
755	} else if (parent->direction == PCMDIR_REC) {
756		direction = PCMDIR_REC_VIRTUAL;
757		vchanfmt = d->rvchanformat;
758		speed = d->rvchanrate;
759	} else
760		return (EINVAL);
761	CHN_UNLOCK(parent);
762
763	/* create a new playback channel */
764	ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent);
765	if (ch == NULL) {
766		CHN_LOCK(parent);
767		return (ENODEV);
768	}
769
770	/* add us to our grandparent's channel list */
771	err = pcm_chn_add(d, ch);
772	if (err) {
773		pcm_chn_destroy(ch);
774		CHN_LOCK(parent);
775		return (err);
776	}
777
778	CHN_LOCK(parent);
779	/* add us to our parent channel's children */
780	first = CHN_EMPTY(parent, children);
781	after = NULL;
782	CHN_FOREACH(tmp, parent, children) {
783		if (CHN_CHAN(tmp) > CHN_CHAN(ch))
784			after = tmp;
785		else if (CHN_CHAN(tmp) < CHN_CHAN(ch))
786			break;
787	}
788	if (after != NULL) {
789		CHN_INSERT_AFTER(after, ch, children);
790	} else {
791		CHN_INSERT_HEAD(parent, ch, children);
792	}
793	parent->flags |= CHN_F_HAS_VCHAN;
794
795	if (first) {
796		parent_caps = chn_getcaps(parent);
797		if (parent_caps == NULL)
798			err = EINVAL;
799
800		if (!err) {
801			if (vchanfmt == 0) {
802				const char *vfmt;
803
804				CHN_UNLOCK(parent);
805				r = resource_string_value(
806				    device_get_name(parent->dev),
807				    device_get_unit(parent->dev),
808				    VCHAN_FMT_HINT(direction),
809				    &vfmt);
810				CHN_LOCK(parent);
811				if (r != 0)
812					vfmt = NULL;
813				if (vfmt != NULL) {
814					vchanfmt = vchan_valid_strformat(vfmt);
815					for (r = 0; vchanfmt == 0 &&
816					    vchan_fmtstralias[r].alias != NULL;
817					    r++) {
818						if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) {
819							vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr);
820							break;
821						}
822					}
823				}
824				if (vchanfmt == 0)
825					vchanfmt = VCHAN_DEFAULT_AFMT;
826			}
827			err = chn_reset(parent, vchanfmt);
828		}
829
830		if (!err) {
831			/*
832			 * This is very sad. Few soundcards advertised as being
833			 * able to do (insanely) higher/lower speed, but in
834			 * reality, they simply can't. At least, we give user chance
835			 * to set sane value via kernel hints or sysctl.
836			 */
837			if (speed < 1) {
838				CHN_UNLOCK(parent);
839				r = resource_int_value(
840				    device_get_name(parent->dev),
841				    device_get_unit(parent->dev),
842				    VCHAN_SPD_HINT(direction),
843				    &speed);
844				CHN_LOCK(parent);
845				if (r != 0) {
846					/*
847					 * No saved value, no hint, NOTHING.
848					 *
849					 * Workaround for sb16 running
850					 * poorly at 45k / 49k.
851					 */
852					switch (parent_caps->maxspeed) {
853					case 45000:
854					case 49000:
855						speed = 44100;
856						break;
857					default:
858						speed = VCHAN_DEFAULT_SPEED;
859						if (speed > parent_caps->maxspeed)
860							speed = parent_caps->maxspeed;
861						break;
862					}
863					if (speed < parent_caps->minspeed)
864						speed = parent_caps->minspeed;
865				}
866			}
867
868			if (feeder_rate_round) {
869				/*
870				 * Limit speed based on driver caps.
871				 * This is supposed to help fixed rate, non-VRA
872				 * AC97 cards, but.. (see below)
873				 */
874				if (speed < parent_caps->minspeed)
875					speed = parent_caps->minspeed;
876				if (speed > parent_caps->maxspeed)
877					speed = parent_caps->maxspeed;
878			}
879
880			/*
881			 * We still need to limit the speed between
882			 * feeder_rate_min <-> feeder_rate_max. This is
883			 * just an escape goat if all of the above failed
884			 * miserably.
885			 */
886			if (speed < feeder_rate_min)
887				speed = feeder_rate_min;
888			if (speed > feeder_rate_max)
889				speed = feeder_rate_max;
890
891			err = chn_setspeed(parent, speed);
892			/*
893			 * Try to avoid FEEDER_RATE on parent channel if the
894			 * requested value is not supported by the hardware.
895			 */
896			if (!err && feeder_rate_round &&
897			    (parent->feederflags & (1 << FEEDER_RATE))) {
898				speed = sndbuf_getspd(parent->bufhard);
899				err = chn_setspeed(parent, speed);
900			}
901
902			if (!err) {
903				/*
904				 * Save new value.
905				 */
906				CHN_UNLOCK(parent);
907				pcm_lock(d);
908				if (direction == PCMDIR_PLAY_VIRTUAL) {
909					d->pvchanformat = vchanfmt;
910					d->pvchanrate = speed;
911				} else {
912					d->rvchanformat = vchanfmt;
913					d->rvchanrate = speed;
914				}
915				pcm_unlock(d);
916				CHN_LOCK(parent);
917			}
918		}
919
920		if (err) {
921			CHN_REMOVE(parent, ch, children);
922			parent->flags &= ~CHN_F_HAS_VCHAN;
923			CHN_UNLOCK(parent);
924			pcm_lock(d);
925			if (pcm_chn_remove(d, ch) == 0) {
926				pcm_unlock(d);
927				pcm_chn_destroy(ch);
928			} else
929				pcm_unlock(d);
930			CHN_LOCK(parent);
931			return (err);
932		}
933	}
934
935	return (0);
936}
937
938int
939vchan_destroy(struct pcm_channel *c)
940{
941	struct pcm_channel *parent = c->parentchannel;
942	struct snddev_info *d = parent->parentsnddev;
943	uint32_t spd;
944	int err;
945
946	CHN_LOCK(parent);
947	if (!(parent->flags & CHN_F_BUSY)) {
948		CHN_UNLOCK(parent);
949		return (EBUSY);
950	}
951	if (CHN_EMPTY(parent, children)) {
952		CHN_UNLOCK(parent);
953		return (EINVAL);
954	}
955
956	/* remove us from our parent's children list */
957	CHN_REMOVE(parent, c, children);
958
959	if (CHN_EMPTY(parent, children)) {
960		parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
961		spd = parent->speed;
962		if (chn_reset(parent, parent->format) == 0)
963			chn_setspeed(parent, spd);
964	}
965
966	CHN_UNLOCK(parent);
967
968	/* remove us from our grandparent's channel list */
969	pcm_lock(d);
970	err = pcm_chn_remove(d, c);
971	pcm_unlock(d);
972
973	/* destroy ourselves */
974	if (!err)
975		err = pcm_chn_destroy(c);
976
977	return (err);
978}
979
980int
981vchan_initsys(device_t dev)
982{
983#ifdef SND_DYNSYSCTL
984	struct snddev_info *d;
985	int unit;
986
987	unit = device_get_unit(dev);
988	d = device_get_softc(dev);
989
990	/* Play */
991	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
992	    SYSCTL_CHILDREN(d->play_sysctl_tree),
993	    OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW,
994	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
995	    sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
996	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
997	    SYSCTL_CHILDREN(d->play_sysctl_tree),
998	    OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW,
999	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1000	    sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate");
1001	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1002	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1003	    OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW,
1004	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1005	    sysctl_hw_snd_vchanformat, "A", "virtual channel format");
1006	/* Rec */
1007	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1008	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1009	    OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW,
1010	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1011	    sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
1012	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1013	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1014	    OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW,
1015	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1016	    sysctl_hw_snd_vchanrate, "I", "virtual channel base speed/rate");
1017	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1018	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1019	    OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW,
1020	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1021	    sysctl_hw_snd_vchanformat, "A", "virtual channel format");
1022#endif
1023
1024	return (0);
1025}
1026