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