vchan.c revision 170206
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 170206 2007-06-02 13:07:44Z joel $");
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 (!(go == PCMTRIG_START || go == PCMTRIG_STOP ||
435	    go == PCMTRIG_ABORT) || go == ch->trigger)
436		return (0);
437
438	c = ch->channel;
439	p = c->parentchannel;
440	otrigger = ch->trigger;
441	ch->trigger = go;
442
443	CHN_UNLOCK(c);
444	CHN_LOCK(p);
445
446	switch (go) {
447	case PCMTRIG_START:
448		if (otrigger != PCMTRIG_START) {
449			CHN_INSERT_HEAD(p, c, children.busy);
450		}
451		break;
452	case PCMTRIG_STOP:
453	case PCMTRIG_ABORT:
454		if (otrigger == PCMTRIG_START) {
455			CHN_REMOVE(p, c, children.busy);
456		}
457		break;
458	default:
459		break;
460	}
461
462	CHN_UNLOCK(p);
463	chn_notify(p, CHN_N_TRIGGER);
464	CHN_LOCK(c);
465
466	return (0);
467}
468
469static struct pcmchan_caps *
470vchan_getcaps(kobj_t obj, void *data)
471{
472	struct vchinfo *ch = data;
473	struct pcm_channel *c, *p;
474	uint32_t fmt;
475
476	c = ch->channel;
477	p = c->parentchannel;
478	ch->caps.minspeed = sndbuf_getspd(p->bufsoft);
479	ch->caps.maxspeed = ch->caps.minspeed;
480	ch->caps.caps = 0;
481	ch->fmtlist[1] = 0;
482	fmt = sndbuf_getfmt(p->bufsoft);
483	if (fmt != vchan_valid_format(fmt)) {
484		device_printf(c->dev,
485			    "%s: WARNING: invalid vchan format! (0x%08x)\n",
486			    __func__, fmt);
487		fmt = VCHAN_DEFAULT_AFMT;
488	}
489	ch->fmtlist[0] = fmt;
490	ch->caps.fmtlist = ch->fmtlist;
491
492	return (&ch->caps);
493}
494
495static kobj_method_t vchan_methods[] = {
496	KOBJMETHOD(channel_init,		vchan_init),
497	KOBJMETHOD(channel_free,		vchan_free),
498	KOBJMETHOD(channel_setformat,		vchan_setformat),
499	KOBJMETHOD(channel_setspeed,		vchan_setspeed),
500	KOBJMETHOD(channel_trigger,		vchan_trigger),
501	KOBJMETHOD(channel_getcaps,		vchan_getcaps),
502	{0, 0}
503};
504CHANNEL_DECLARE(vchan);
505
506/*
507 * On the fly vchan rate settings
508 */
509#ifdef SND_DYNSYSCTL
510static int
511sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
512{
513	struct snddev_info *d;
514	struct pcm_channel *c, *ch = NULL;
515	struct pcmchan_caps *caps;
516	int vchancount, *vchanrate;
517	int direction;
518	int err = 0;
519	int newspd = 0;
520
521	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
522	if (d == NULL || !(d->flags & SD_F_AUTOVCHAN))
523		return (EINVAL);
524
525	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
526	case VCHAN_PLAY:
527		direction = PCMDIR_PLAY;
528		vchancount = d->pvchancount;
529		vchanrate = &d->pvchanrate;
530		break;
531	case VCHAN_REC:
532		direction = PCMDIR_REC;
533		vchancount = d->rvchancount;
534		vchanrate = &d->rvchanrate;
535		break;
536	default:
537		return (EINVAL);
538		break;
539	}
540
541	if (vchancount < 1)
542		return (EINVAL);
543	if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
544		pcm_inprog(d, -1);
545		return (EINPROGRESS);
546	}
547	CHN_FOREACH(c, d, channels.pcm) {
548		CHN_LOCK(c);
549		if (c->direction == direction) {
550			if (c->flags & CHN_F_VIRTUAL) {
551				/* Sanity check */
552				if (ch != NULL && ch != c->parentchannel) {
553					CHN_UNLOCK(c);
554					pcm_inprog(d, -1);
555					return (EINVAL);
556				}
557				if (req->newptr != NULL &&
558						(c->flags & CHN_F_BUSY)) {
559					CHN_UNLOCK(c);
560					pcm_inprog(d, -1);
561					return (EBUSY);
562				}
563			} else if (c->flags & CHN_F_HAS_VCHAN) {
564				/* No way!! */
565				if (ch != NULL) {
566					CHN_UNLOCK(c);
567					pcm_inprog(d, -1);
568					return (EINVAL);
569				}
570				ch = c;
571				newspd = ch->speed;
572			}
573		}
574		CHN_UNLOCK(c);
575	}
576	if (ch == NULL) {
577		pcm_inprog(d, -1);
578		return (EINVAL);
579	}
580	err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
581	if (err == 0 && req->newptr != NULL) {
582		if (newspd < 1 || newspd < feeder_rate_min ||
583		    newspd > feeder_rate_max) {
584			pcm_inprog(d, -1);
585			return (EINVAL);
586		}
587		CHN_LOCK(ch);
588		if (feeder_rate_round) {
589			caps = chn_getcaps(ch);
590			if (caps == NULL || newspd < caps->minspeed ||
591			    newspd > caps->maxspeed) {
592				CHN_UNLOCK(ch);
593				pcm_inprog(d, -1);
594				return (EINVAL);
595			}
596		}
597		if (newspd != ch->speed) {
598			err = chn_setspeed(ch, newspd);
599			/*
600			 * Try to avoid FEEDER_RATE on parent channel if the
601			 * requested value is not supported by the hardware.
602			 */
603			if (!err && feeder_rate_round &&
604			    (ch->feederflags & (1 << FEEDER_RATE))) {
605				newspd = sndbuf_getspd(ch->bufhard);
606				err = chn_setspeed(ch, newspd);
607			}
608			CHN_UNLOCK(ch);
609			if (err == 0) {
610				pcm_lock(d);
611				*vchanrate = newspd;
612				pcm_unlock(d);
613			}
614		} else
615			CHN_UNLOCK(ch);
616	}
617	pcm_inprog(d, -1);
618	return (err);
619}
620
621static int
622sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS)
623{
624	struct snddev_info *d;
625	struct pcm_channel *c, *ch = NULL;
626	uint32_t newfmt, spd;
627	int vchancount, *vchanformat;
628	int direction;
629	int err = 0, i;
630	char fmtstr[AFMTSTR_MAXSZ];
631
632	d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
633	if (d == NULL || !(d->flags & SD_F_AUTOVCHAN))
634		return (EINVAL);
635
636	switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
637	case VCHAN_PLAY:
638		direction = PCMDIR_PLAY;
639		vchancount = d->pvchancount;
640		vchanformat = &d->pvchanformat;
641		break;
642	case VCHAN_REC:
643		direction = PCMDIR_REC;
644		vchancount = d->rvchancount;
645		vchanformat = &d->rvchanformat;
646		break;
647	default:
648		return (EINVAL);
649		break;
650	}
651
652	if (vchancount < 1)
653		return (EINVAL);
654	if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) {
655		pcm_inprog(d, -1);
656		return (EINPROGRESS);
657	}
658	CHN_FOREACH(c, d, channels.pcm) {
659		CHN_LOCK(c);
660		if (c->direction == direction) {
661			if (c->flags & CHN_F_VIRTUAL) {
662				/* Sanity check */
663				if (ch != NULL && ch != c->parentchannel) {
664					CHN_UNLOCK(c);
665					pcm_inprog(d, -1);
666					return (EINVAL);
667				}
668				if (req->newptr != NULL &&
669						(c->flags & CHN_F_BUSY)) {
670					CHN_UNLOCK(c);
671					pcm_inprog(d, -1);
672					return (EBUSY);
673				}
674			} else if (c->flags & CHN_F_HAS_VCHAN) {
675				/* No way!! */
676				if (ch != NULL) {
677					CHN_UNLOCK(c);
678					pcm_inprog(d, -1);
679					return (EINVAL);
680				}
681				ch = c;
682				if (ch->format !=
683				    afmt2afmtstr(vchan_supported_fmts,
684				    ch->format, fmtstr, sizeof(fmtstr),
685				    AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) {
686					strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT,
687					    sizeof(fmtstr));
688				}
689			}
690		}
691		CHN_UNLOCK(c);
692	}
693	if (ch == NULL) {
694		pcm_inprog(d, -1);
695		return (EINVAL);
696	}
697	err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req);
698	if (err == 0 && req->newptr != NULL) {
699		for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) {
700			if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) {
701				strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr,
702				    sizeof(fmtstr));
703				break;
704			}
705		}
706		newfmt = vchan_valid_strformat(fmtstr);
707		if (newfmt == 0) {
708			pcm_inprog(d, -1);
709			return (EINVAL);
710		}
711		CHN_LOCK(ch);
712		if (newfmt != ch->format) {
713			/* Get channel speed, before chn_reset() screw it. */
714			spd = ch->speed;
715			err = chn_reset(ch, newfmt);
716			if (err == 0)
717				err = chn_setspeed(ch, spd);
718			CHN_UNLOCK(ch);
719			if (err == 0) {
720				pcm_lock(d);
721				*vchanformat = newfmt;
722				pcm_unlock(d);
723			}
724		} else
725			CHN_UNLOCK(ch);
726	}
727	pcm_inprog(d, -1);
728	return (err);
729}
730#endif
731
732/* virtual channel interface */
733
734#define VCHAN_FMT_HINT(x)	((x) == PCMDIR_PLAY_VIRTUAL) ?		\
735				"play.vchanformat" : "rec.vchanformat"
736#define VCHAN_SPD_HINT(x)	((x) == PCMDIR_PLAY_VIRTUAL) ?		\
737				"play.vchanrate" : "rec.vchanrate"
738
739int
740vchan_create(struct pcm_channel *parent, int num)
741{
742	struct snddev_info *d = parent->parentsnddev;
743	struct pcm_channel *ch, *tmp, *after;
744	struct pcmchan_caps *parent_caps;
745	uint32_t vchanfmt;
746	int err, first, speed, r;
747	int direction;
748
749	if (!(parent->flags & CHN_F_BUSY))
750		return (EBUSY);
751
752	if (parent->direction == PCMDIR_PLAY) {
753		direction = PCMDIR_PLAY_VIRTUAL;
754		vchanfmt = d->pvchanformat;
755		speed = d->pvchanrate;
756	} else if (parent->direction == PCMDIR_REC) {
757		direction = PCMDIR_REC_VIRTUAL;
758		vchanfmt = d->rvchanformat;
759		speed = d->rvchanrate;
760	} else
761		return (EINVAL);
762	CHN_UNLOCK(parent);
763
764	/* create a new playback channel */
765	ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent);
766	if (ch == NULL) {
767		CHN_LOCK(parent);
768		return (ENODEV);
769	}
770
771	/* add us to our grandparent's channel list */
772	err = pcm_chn_add(d, ch);
773	if (err) {
774		pcm_chn_destroy(ch);
775		CHN_LOCK(parent);
776		return (err);
777	}
778
779	CHN_LOCK(parent);
780	/* add us to our parent channel's children */
781	first = CHN_EMPTY(parent, children);
782	after = NULL;
783	CHN_FOREACH(tmp, parent, children) {
784		if (CHN_CHAN(tmp) > CHN_CHAN(ch))
785			after = tmp;
786		else if (CHN_CHAN(tmp) < CHN_CHAN(ch))
787			break;
788	}
789	if (after != NULL) {
790		CHN_INSERT_AFTER(after, ch, children);
791	} else {
792		CHN_INSERT_HEAD(parent, ch, children);
793	}
794	parent->flags |= CHN_F_HAS_VCHAN;
795
796	if (first) {
797		parent_caps = chn_getcaps(parent);
798		if (parent_caps == NULL)
799			err = EINVAL;
800
801		if (!err) {
802			if (vchanfmt == 0) {
803				const char *vfmt;
804
805				CHN_UNLOCK(parent);
806				r = resource_string_value(
807				    device_get_name(parent->dev),
808				    device_get_unit(parent->dev),
809				    VCHAN_FMT_HINT(direction),
810				    &vfmt);
811				CHN_LOCK(parent);
812				if (r != 0)
813					vfmt = NULL;
814				if (vfmt != NULL) {
815					vchanfmt = vchan_valid_strformat(vfmt);
816					for (r = 0; vchanfmt == 0 &&
817					    vchan_fmtstralias[r].alias != NULL;
818					    r++) {
819						if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) {
820							vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr);
821							break;
822						}
823					}
824				}
825				if (vchanfmt == 0)
826					vchanfmt = VCHAN_DEFAULT_AFMT;
827			}
828			err = chn_reset(parent, vchanfmt);
829		}
830
831		if (!err) {
832			/*
833			 * This is very sad. Few soundcards advertised as being
834			 * able to do (insanely) higher/lower speed, but in
835			 * reality, they simply can't. At least, we give user chance
836			 * to set sane value via kernel hints or sysctl.
837			 */
838			if (speed < 1) {
839				CHN_UNLOCK(parent);
840				r = resource_int_value(
841				    device_get_name(parent->dev),
842				    device_get_unit(parent->dev),
843				    VCHAN_SPD_HINT(direction),
844				    &speed);
845				CHN_LOCK(parent);
846				if (r != 0) {
847					/*
848					 * No saved value, no hint, NOTHING.
849					 *
850					 * Workaround for sb16 running
851					 * poorly at 45k / 49k.
852					 */
853					switch (parent_caps->maxspeed) {
854					case 45000:
855					case 49000:
856						speed = 44100;
857						break;
858					default:
859						speed = VCHAN_DEFAULT_SPEED;
860						if (speed > parent_caps->maxspeed)
861							speed = parent_caps->maxspeed;
862						break;
863					}
864					if (speed < parent_caps->minspeed)
865						speed = parent_caps->minspeed;
866				}
867			}
868
869			if (feeder_rate_round) {
870				/*
871				 * Limit speed based on driver caps.
872				 * This is supposed to help fixed rate, non-VRA
873				 * AC97 cards, but.. (see below)
874				 */
875				if (speed < parent_caps->minspeed)
876					speed = parent_caps->minspeed;
877				if (speed > parent_caps->maxspeed)
878					speed = parent_caps->maxspeed;
879			}
880
881			/*
882			 * We still need to limit the speed between
883			 * feeder_rate_min <-> feeder_rate_max. This is
884			 * just an escape goat if all of the above failed
885			 * miserably.
886			 */
887			if (speed < feeder_rate_min)
888				speed = feeder_rate_min;
889			if (speed > feeder_rate_max)
890				speed = feeder_rate_max;
891
892			err = chn_setspeed(parent, speed);
893			/*
894			 * Try to avoid FEEDER_RATE on parent channel if the
895			 * requested value is not supported by the hardware.
896			 */
897			if (!err && feeder_rate_round &&
898			    (parent->feederflags & (1 << FEEDER_RATE))) {
899				speed = sndbuf_getspd(parent->bufhard);
900				err = chn_setspeed(parent, speed);
901			}
902
903			if (!err) {
904				/*
905				 * Save new value.
906				 */
907				CHN_UNLOCK(parent);
908				pcm_lock(d);
909				if (direction == PCMDIR_PLAY_VIRTUAL) {
910					d->pvchanformat = vchanfmt;
911					d->pvchanrate = speed;
912				} else {
913					d->rvchanformat = vchanfmt;
914					d->rvchanrate = speed;
915				}
916				pcm_unlock(d);
917				CHN_LOCK(parent);
918			}
919		}
920
921		if (err) {
922			CHN_REMOVE(parent, ch, children);
923			parent->flags &= ~CHN_F_HAS_VCHAN;
924			CHN_UNLOCK(parent);
925			pcm_lock(d);
926			if (pcm_chn_remove(d, ch) == 0) {
927				pcm_unlock(d);
928				pcm_chn_destroy(ch);
929			} else
930				pcm_unlock(d);
931			CHN_LOCK(parent);
932			return (err);
933		}
934	}
935
936	return (0);
937}
938
939int
940vchan_destroy(struct pcm_channel *c)
941{
942	struct pcm_channel *parent = c->parentchannel;
943	struct snddev_info *d = parent->parentsnddev;
944	uint32_t spd;
945	int err;
946
947	CHN_LOCK(parent);
948	if (!(parent->flags & CHN_F_BUSY)) {
949		CHN_UNLOCK(parent);
950		return (EBUSY);
951	}
952	if (CHN_EMPTY(parent, children)) {
953		CHN_UNLOCK(parent);
954		return (EINVAL);
955	}
956
957	/* remove us from our parent's children list */
958	CHN_REMOVE(parent, c, children);
959
960	if (CHN_EMPTY(parent, children)) {
961		parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN);
962		spd = parent->speed;
963		if (chn_reset(parent, parent->format) == 0)
964			chn_setspeed(parent, spd);
965	}
966
967	CHN_UNLOCK(parent);
968
969	/* remove us from our grandparent's channel list */
970	pcm_lock(d);
971	err = pcm_chn_remove(d, c);
972	pcm_unlock(d);
973
974	/* destroy ourselves */
975	if (!err)
976		err = pcm_chn_destroy(c);
977
978	return (err);
979}
980
981int
982vchan_initsys(device_t dev)
983{
984#ifdef SND_DYNSYSCTL
985	struct snddev_info *d;
986	int unit;
987
988	unit = device_get_unit(dev);
989	d = device_get_softc(dev);
990
991	/* Play */
992	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
993	    SYSCTL_CHILDREN(d->play_sysctl_tree),
994	    OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW,
995	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
996	    sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
997	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
998	    SYSCTL_CHILDREN(d->play_sysctl_tree),
999	    OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW,
1000	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1001	    sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate");
1002	SYSCTL_ADD_PROC(&d->play_sysctl_ctx,
1003	    SYSCTL_CHILDREN(d->play_sysctl_tree),
1004	    OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW,
1005	    VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE,
1006	    sysctl_hw_snd_vchanformat, "A", "virtual channel format");
1007	/* Rec */
1008	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1009	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1010	    OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW,
1011	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1012	    sysctl_hw_snd_vchans, "I", "total allocated virtual channel");
1013	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1014	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1015	    OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW,
1016	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1017	    sysctl_hw_snd_vchanrate, "I", "virtual channel base speed/rate");
1018	SYSCTL_ADD_PROC(&d->rec_sysctl_ctx,
1019	    SYSCTL_CHILDREN(d->rec_sysctl_tree),
1020	    OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW,
1021	    VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE,
1022	    sysctl_hw_snd_vchanformat, "A", "virtual channel format");
1023#endif
1024
1025	return (0);
1026}
1027