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: releng/7.3/sys/dev/sound/pcm/vchan.c 170815 2007-06-16 03:37:28Z ariff $");
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 }
Cache object: 7ef230ae31434726cf3ff30db29227cb
|