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