1 /*- 2 * Copyright (c) 2001 Cameron Grant <cg@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/sys/dev/sound/pcm/vchan.c,v 1.17.2.5 2007/02/04 06:17:14 ariff Exp $ 27 */ 28 29 #include <dev/sound/pcm/sound.h> 30 #include <dev/sound/pcm/vchan.h> 31 #include "feeder_if.h" 32 33 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.8 2008/01/06 16:55:51 swildner Exp $"); 34 35 /* 36 * Default speed 37 */ 38 #define VCHAN_DEFAULT_SPEED 48000 39 40 extern int feeder_rate_ratemin; 41 extern int feeder_rate_ratemax; 42 43 struct vchinfo { 44 u_int32_t spd, fmt, blksz, bps, run; 45 struct pcm_channel *channel, *parent; 46 struct pcmchan_caps caps; 47 }; 48 49 static u_int32_t vchan_fmt[] = { 50 AFMT_STEREO | AFMT_S16_LE, 51 0 52 }; 53 54 /* 55 * These values are: (1.022**v-1)*65536/(1.022**100-1) 56 * 57 * Inspired by inverting opensound (BSDL'd)'s mix_cvt. 58 */ 59 static int log_mix_cvt[101] = { 60 0, 184, 373, 565, 762, 964, 1170, 1380, 1595, 1814, 2039, 2268, 2503, 2742, 61 2987, 3238, 3493, 3755, 4022, 4295, 4574, 4859, 5151, 5449, 5753, 6064, 6382, 62 6707, 7039, 7379, 7726, 8080, 8442, 8813, 9191, 9578, 9973, 10377, 10790, 63 11212, 11643, 12084, 12534, 12995, 13465, 13946, 14437, 14940, 15453, 15977, 64 16513, 17061, 17621, 18193, 18778, 19376, 19987, 20611, 21249, 21901, 22567, 65 23248, 23944, 24656, 25383, 26126, 26885, 27661, 28454, 29265, 30093, 30940, 66 31805, 32689, 33593, 34517, 35461, 36425, 37411, 38419, 39449, 40501, 41577, 67 42676, 43799, 44947, 46121, 47320, 48546, 49798, 51078, 52387, 53724, 55090, 68 56487, 57914, 59373, 60864, 62387, 63944, 65536 69 }; 70 71 static int 72 vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count, int volume) 73 { 74 /* 75 * to is the output buffer, tmp is the input buffer 76 * count is the number of 16bit samples to mix 77 * volume is in range 0-100, (left | (right << 8)) 78 */ 79 int i; 80 int x; 81 int scale[2]; 82 int doscale; 83 84 scale[0] = log_mix_cvt[volume & 0xff]; 85 scale[1] = log_mix_cvt[(volume >> 8) & 0xff]; 86 doscale = volume != (100 | (100 << 8)); 87 88 for(i = 0; i < count; i++) { 89 x = to[i]; 90 if (doscale) 91 x += ((int)tmp[i] * scale[i % 2]) >> 16; 92 else 93 x += tmp[i]; 94 if (x < -32768) { 95 /* kprintf("%d + %d = %d (u)\n", to[i], tmp[i], x); */ 96 x = -32768; 97 } 98 if (x > 32767) { 99 /* kprintf("%d + %d = %d (o)\n", to[i], tmp[i], x); */ 100 x = 32767; 101 } 102 to[i] = x & 0x0000ffff; 103 } 104 return 0; 105 } 106 107 static int 108 feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) 109 { 110 /* we're going to abuse things a bit */ 111 struct snd_dbuf *src = source; 112 struct pcmchan_children *cce; 113 struct pcm_channel *ch; 114 uint32_t sz; 115 int16_t *tmp, *dst; 116 unsigned int cnt, rcnt = 0; 117 118 #if 0 119 if (sndbuf_getsize(src) < count) 120 panic("feed_vchan_s16(%s): tmp buffer size %d < count %d, flags = 0x%x", 121 c->name, sndbuf_getsize(src), count, c->flags); 122 #endif 123 sz = sndbuf_getsize(src); 124 if (sz < count) 125 count = sz; 126 count &= ~1; 127 if (count < 2) 128 return 0; 129 bzero(b, count); 130 131 /* 132 * we are going to use our source as a temporary buffer since it's 133 * got no other purpose. we obtain our data by traversing the channel 134 * list of children and calling vchan_mix_* to mix count bytes from each 135 * into our destination buffer, b 136 */ 137 dst = (int16_t *)b; 138 tmp = (int16_t *)sndbuf_getbuf(src); 139 bzero(tmp, count); 140 SLIST_FOREACH(cce, &c->children, link) { 141 ch = cce->channel; 142 CHN_LOCK(ch); 143 if (ch->flags & CHN_F_TRIGGERED) { 144 if (ch->flags & CHN_F_MAPPED) 145 sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft)); 146 cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft); 147 vchan_mix_s16(dst, tmp, cnt >> 1, ch->volume); 148 if (cnt > rcnt) 149 rcnt = cnt; 150 } 151 CHN_UNLOCK(ch); 152 } 153 154 return rcnt & ~1; 155 } 156 157 static struct pcm_feederdesc feeder_vchan_s16_desc[] = { 158 {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 159 {0}, 160 }; 161 static kobj_method_t feeder_vchan_s16_methods[] = { 162 KOBJMETHOD(feeder_feed, feed_vchan_s16), 163 KOBJMETHOD_END 164 }; 165 FEEDER_DECLARE(feeder_vchan_s16, 2, NULL); 166 167 /************************************************************/ 168 169 static void * 170 vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 171 { 172 struct vchinfo *ch; 173 struct pcm_channel *parent = devinfo; 174 175 KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction")); 176 ch = kmalloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 177 ch->parent = parent; 178 ch->channel = c; 179 ch->fmt = AFMT_U8; 180 ch->spd = DSP_DEFAULT_SPEED; 181 ch->blksz = 2048; 182 183 c->flags |= CHN_F_VIRTUAL; 184 185 return ch; 186 } 187 188 static int 189 vchan_free(kobj_t obj, void *data) 190 { 191 kfree(data, M_DEVBUF); 192 return 0; 193 } 194 195 static int 196 vchan_setformat(kobj_t obj, void *data, u_int32_t format) 197 { 198 struct vchinfo *ch = data; 199 struct pcm_channel *parent = ch->parent; 200 struct pcm_channel *channel = ch->channel; 201 202 ch->fmt = format; 203 ch->bps = 1; 204 ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0; 205 if (ch->fmt & AFMT_16BIT) 206 ch->bps <<= 1; 207 else if (ch->fmt & AFMT_24BIT) 208 ch->bps *= 3; 209 else if (ch->fmt & AFMT_32BIT) 210 ch->bps <<= 2; 211 CHN_UNLOCK(channel); 212 chn_notify(parent, CHN_N_FORMAT); 213 CHN_LOCK(channel); 214 sndbuf_setfmt(channel->bufsoft, format); 215 return 0; 216 } 217 218 static int 219 vchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 220 { 221 struct vchinfo *ch = data; 222 struct pcm_channel *parent = ch->parent; 223 struct pcm_channel *channel = ch->channel; 224 225 ch->spd = speed; 226 CHN_UNLOCK(channel); 227 CHN_LOCK(parent); 228 speed = sndbuf_getspd(parent->bufsoft); 229 CHN_UNLOCK(parent); 230 CHN_LOCK(channel); 231 return speed; 232 } 233 234 static int 235 vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 236 { 237 struct vchinfo *ch = data; 238 struct pcm_channel *channel = ch->channel; 239 struct pcm_channel *parent = ch->parent; 240 /* struct pcm_channel *channel = ch->channel; */ 241 int prate, crate; 242 243 ch->blksz = blocksize; 244 /* CHN_UNLOCK(channel); */ 245 sndbuf_setblksz(channel->bufhard, blocksize); 246 chn_notify(parent, CHN_N_BLOCKSIZE); 247 CHN_LOCK(parent); 248 /* CHN_LOCK(channel); */ 249 250 crate = ch->spd * ch->bps; 251 prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft); 252 blocksize = sndbuf_getblksz(parent->bufsoft); 253 CHN_UNLOCK(parent); 254 blocksize *= prate; 255 blocksize /= crate; 256 257 return blocksize; 258 } 259 260 static int 261 vchan_trigger(kobj_t obj, void *data, int go) 262 { 263 struct vchinfo *ch = data; 264 struct pcm_channel *parent = ch->parent; 265 struct pcm_channel *channel = ch->channel; 266 267 if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 268 return 0; 269 270 ch->run = (go == PCMTRIG_START)? 1 : 0; 271 CHN_UNLOCK(channel); 272 chn_notify(parent, CHN_N_TRIGGER); 273 CHN_LOCK(channel); 274 275 return 0; 276 } 277 278 static struct pcmchan_caps * 279 vchan_getcaps(kobj_t obj, void *data) 280 { 281 struct vchinfo *ch = data; 282 283 ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft); 284 ch->caps.maxspeed = ch->caps.minspeed; 285 ch->caps.fmtlist = vchan_fmt; 286 ch->caps.caps = 0; 287 288 return &ch->caps; 289 } 290 291 static kobj_method_t vchan_methods[] = { 292 KOBJMETHOD(channel_init, vchan_init), 293 KOBJMETHOD(channel_free, vchan_free), 294 KOBJMETHOD(channel_setformat, vchan_setformat), 295 KOBJMETHOD(channel_setspeed, vchan_setspeed), 296 KOBJMETHOD(channel_setblocksize, vchan_setblocksize), 297 KOBJMETHOD(channel_trigger, vchan_trigger), 298 KOBJMETHOD(channel_getcaps, vchan_getcaps), 299 KOBJMETHOD_END 300 }; 301 CHANNEL_DECLARE(vchan); 302 303 #if 0 304 /* 305 * On the fly vchan rate settings 306 */ 307 #ifdef SND_DYNSYSCTL 308 static int 309 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) 310 { 311 struct snddev_info *d; 312 struct snddev_channel *sce; 313 struct pcm_channel *c, *ch = NULL, *fake; 314 struct pcmchan_caps *caps; 315 int err = 0; 316 int newspd = 0; 317 318 d = oidp->oid_arg1; 319 if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) 320 return EINVAL; 321 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { 322 pcm_inprog(d, -1); 323 return EINPROGRESS; 324 } 325 SLIST_FOREACH(sce, &d->channels, link) { 326 c = sce->channel; 327 CHN_LOCK(c); 328 if (c->direction == PCMDIR_PLAY) { 329 if (c->flags & CHN_F_VIRTUAL) { 330 /* Sanity check */ 331 if (ch != NULL && ch != c->parentchannel) { 332 CHN_UNLOCK(c); 333 pcm_inprog(d, -1); 334 return EINVAL; 335 } 336 if (req->newptr != NULL && 337 (c->flags & CHN_F_BUSY)) { 338 CHN_UNLOCK(c); 339 pcm_inprog(d, -1); 340 return EBUSY; 341 } 342 } else if (c->flags & CHN_F_HAS_VCHAN) { 343 /* No way!! */ 344 if (ch != NULL) { 345 CHN_UNLOCK(c); 346 pcm_inprog(d, -1); 347 return EINVAL; 348 } 349 ch = c; 350 newspd = ch->speed; 351 } 352 } 353 CHN_UNLOCK(c); 354 } 355 if (ch == NULL) { 356 pcm_inprog(d, -1); 357 return EINVAL; 358 } 359 err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req); 360 if (err == 0 && req->newptr != NULL) { 361 if (newspd < 1 || newspd < feeder_rate_ratemin || 362 newspd > feeder_rate_ratemax) { 363 pcm_inprog(d, -1); 364 return EINVAL; 365 } 366 CHN_LOCK(ch); 367 caps = chn_getcaps(ch); 368 if (caps == NULL || newspd < caps->minspeed || 369 newspd > caps->maxspeed) { 370 CHN_UNLOCK(ch); 371 pcm_inprog(d, -1); 372 return EINVAL; 373 } 374 if (newspd != ch->speed) { 375 err = chn_setspeed(ch, newspd); 376 /* 377 * Try to avoid FEEDER_RATE on parent channel if the 378 * requested value is not supported by the hardware. 379 */ 380 if (!err && (ch->feederflags & (1 << FEEDER_RATE))) { 381 newspd = sndbuf_getspd(ch->bufhard); 382 err = chn_setspeed(ch, newspd); 383 } 384 CHN_UNLOCK(ch); 385 if (err == 0) { 386 fake = pcm_getfakechan(d); 387 if (fake != NULL) { 388 CHN_LOCK(fake); 389 fake->speed = newspd; 390 CHN_UNLOCK(fake); 391 } 392 } 393 } else 394 CHN_UNLOCK(ch); 395 } 396 pcm_inprog(d, -1); 397 return err; 398 } 399 #endif 400 #endif 401 402 /* virtual channel interface */ 403 404 int 405 vchan_create(struct pcm_channel *parent) 406 { 407 struct snddev_info *d = parent->parentsnddev; 408 struct pcmchan_children *pce; 409 struct pcm_channel *child, *fake; 410 struct pcmchan_caps *parent_caps; 411 int err, first, speed = 0; 412 413 if (!(parent->flags & CHN_F_BUSY)) 414 return EBUSY; 415 416 417 CHN_UNLOCK(parent); 418 419 pce = kmalloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); 420 421 /* create a new playback channel */ 422 child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); 423 if (!child) { 424 kfree(pce, M_DEVBUF); 425 CHN_LOCK(parent); 426 return ENODEV; 427 } 428 pce->channel = child; 429 430 /* add us to our grandparent's channel list */ 431 /* 432 * XXX maybe we shouldn't always add the dev_t 433 */ 434 err = pcm_chn_add(d, child); 435 if (err) { 436 pcm_chn_destroy(child); 437 kfree(pce, M_DEVBUF); 438 CHN_LOCK(parent); 439 return err; 440 } 441 442 CHN_LOCK(parent); 443 /* add us to our parent channel's children */ 444 first = SLIST_EMPTY(&parent->children); 445 SLIST_INSERT_HEAD(&parent->children, pce, link); 446 parent->flags |= CHN_F_HAS_VCHAN; 447 448 if (first) { 449 parent_caps = chn_getcaps(parent); 450 if (parent_caps == NULL) 451 err = EINVAL; 452 453 if (!err) 454 err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE); 455 456 if (!err) { 457 fake = pcm_getfakechan(d); 458 if (fake != NULL) { 459 /* 460 * Avoid querying kernel hint, use saved value 461 * from fake channel. 462 */ 463 CHN_UNLOCK(parent); 464 CHN_LOCK(fake); 465 speed = fake->speed; 466 CHN_UNLOCK(fake); 467 CHN_LOCK(parent); 468 } 469 470 /* 471 * This is very sad. Few soundcards advertised as being 472 * able to do (insanely) higher/lower speed, but in 473 * reality, they simply can't. At least, we give user chance 474 * to set sane value via kernel hints or sysctl. 475 */ 476 if (speed < 1) { 477 int r; 478 CHN_UNLOCK(parent); 479 r = resource_int_value(device_get_name(parent->dev), 480 device_get_unit(parent->dev), 481 "vchanrate", &speed); 482 CHN_LOCK(parent); 483 if (r != 0) { 484 /* 485 * Workaround for sb16 running 486 * poorly at 45k / 49k. 487 */ 488 switch (parent_caps->maxspeed) { 489 case 45000: 490 case 49000: 491 speed = 44100; 492 break; 493 default: 494 speed = VCHAN_DEFAULT_SPEED; 495 break; 496 } 497 } 498 } 499 500 /* 501 * Limit speed based on driver caps. 502 * This is supposed to help fixed rate, non-VRA 503 * AC97 cards, but.. (see below) 504 */ 505 if (speed < parent_caps->minspeed) 506 speed = parent_caps->minspeed; 507 if (speed > parent_caps->maxspeed) 508 speed = parent_caps->maxspeed; 509 510 /* 511 * We still need to limit the speed between 512 * feeder_rate_ratemin <-> feeder_rate_ratemax. This is 513 * just an escape goat if all of the above failed 514 * miserably. 515 */ 516 if (speed < feeder_rate_ratemin) 517 speed = feeder_rate_ratemin; 518 if (speed > feeder_rate_ratemax) 519 speed = feeder_rate_ratemax; 520 521 err = chn_setspeed(parent, speed); 522 /* 523 * Try to avoid FEEDER_RATE on parent channel if the 524 * requested value is not supported by the hardware. 525 */ 526 if (!err && (parent->feederflags & (1 << FEEDER_RATE))) { 527 speed = sndbuf_getspd(parent->bufhard); 528 err = chn_setspeed(parent, speed); 529 } 530 531 if (!err && fake != NULL) { 532 /* 533 * Save new value to fake channel. 534 */ 535 CHN_UNLOCK(parent); 536 CHN_LOCK(fake); 537 fake->speed = speed; 538 CHN_UNLOCK(fake); 539 CHN_LOCK(parent); 540 } 541 } 542 543 if (err) { 544 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 545 parent->flags &= ~CHN_F_HAS_VCHAN; 546 CHN_UNLOCK(parent); 547 kfree(pce, M_DEVBUF); 548 if (pcm_chn_remove(d, child) == 0) 549 pcm_chn_destroy(child); 550 CHN_LOCK(parent); 551 return err; 552 } 553 } 554 555 return 0; 556 } 557 558 int 559 vchan_destroy(struct pcm_channel *c) 560 { 561 struct pcm_channel *parent = c->parentchannel; 562 struct snddev_info *d = parent->parentsnddev; 563 struct pcmchan_children *pce; 564 struct snddev_channel *sce; 565 uint32_t spd; 566 int err; 567 568 CHN_LOCK(parent); 569 if (!(parent->flags & CHN_F_BUSY)) { 570 CHN_UNLOCK(parent); 571 return EBUSY; 572 } 573 if (SLIST_EMPTY(&parent->children)) { 574 CHN_UNLOCK(parent); 575 return EINVAL; 576 } 577 578 /* remove us from our parent's children list */ 579 SLIST_FOREACH(pce, &parent->children, link) { 580 if (pce->channel == c) 581 goto gotch; 582 } 583 CHN_UNLOCK(parent); 584 return EINVAL; 585 gotch: 586 SLIST_FOREACH(sce, &d->channels, link) { 587 if (sce->channel == c) { 588 if (sce->dsp_dev) { 589 destroy_dev(sce->dsp_dev); 590 sce->dsp_dev = NULL; 591 } 592 d->devcount--; 593 break; 594 } 595 } 596 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 597 kfree(pce, M_DEVBUF); 598 599 if (SLIST_EMPTY(&parent->children)) { 600 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 601 spd = parent->speed; 602 if (chn_reset(parent, parent->format) == 0) 603 chn_setspeed(parent, spd); 604 } 605 606 /* remove us from our grandparent's channel list */ 607 err = pcm_chn_remove(d, c); 608 609 CHN_UNLOCK(parent); 610 /* destroy ourselves */ 611 if (!err) 612 err = pcm_chn_destroy(c); 613 614 return err; 615 } 616 617 int 618 vchan_initsys(device_t dev) 619 { 620 #ifdef SND_DYNSYSCTL 621 struct snddev_info *d; 622 623 d = device_get_softc(dev); 624 SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 625 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 626 sysctl_hw_snd_vchans, "I", ""); 627 #if 0 628 SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 629 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 630 sysctl_hw_snd_vchanrate, "I", ""); 631 #endif 632 #endif 633 634 return 0; 635 } 636