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