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