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