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.7 2007/06/16 19:48:05 hasso 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.7 2007/06/16 19:48:05 hasso 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 if (!ch) 165 return NULL; 166 ch->parent = parent; 167 ch->channel = c; 168 ch->fmt = AFMT_U8; 169 ch->spd = DSP_DEFAULT_SPEED; 170 ch->blksz = 2048; 171 172 c->flags |= CHN_F_VIRTUAL; 173 174 return ch; 175 } 176 177 static int 178 vchan_free(kobj_t obj, void *data) 179 { 180 kfree(data, M_DEVBUF); 181 return 0; 182 } 183 184 static int 185 vchan_setformat(kobj_t obj, void *data, u_int32_t format) 186 { 187 struct vchinfo *ch = data; 188 struct pcm_channel *parent = ch->parent; 189 struct pcm_channel *channel = ch->channel; 190 191 ch->fmt = format; 192 ch->bps = 1; 193 ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0; 194 if (ch->fmt & AFMT_16BIT) 195 ch->bps <<= 1; 196 else if (ch->fmt & AFMT_24BIT) 197 ch->bps *= 3; 198 else if (ch->fmt & AFMT_32BIT) 199 ch->bps <<= 2; 200 CHN_UNLOCK(channel); 201 chn_notify(parent, CHN_N_FORMAT); 202 CHN_LOCK(channel); 203 sndbuf_setfmt(channel->bufsoft, format); 204 return 0; 205 } 206 207 static int 208 vchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 209 { 210 struct vchinfo *ch = data; 211 struct pcm_channel *parent = ch->parent; 212 struct pcm_channel *channel = ch->channel; 213 214 ch->spd = speed; 215 CHN_UNLOCK(channel); 216 CHN_LOCK(parent); 217 speed = sndbuf_getspd(parent->bufsoft); 218 CHN_UNLOCK(parent); 219 CHN_LOCK(channel); 220 return speed; 221 } 222 223 static int 224 vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 225 { 226 struct vchinfo *ch = data; 227 struct pcm_channel *channel = ch->channel; 228 struct pcm_channel *parent = ch->parent; 229 /* struct pcm_channel *channel = ch->channel; */ 230 int prate, crate; 231 232 ch->blksz = blocksize; 233 /* CHN_UNLOCK(channel); */ 234 sndbuf_setblksz(channel->bufhard, blocksize); 235 chn_notify(parent, CHN_N_BLOCKSIZE); 236 CHN_LOCK(parent); 237 /* CHN_LOCK(channel); */ 238 239 crate = ch->spd * ch->bps; 240 prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft); 241 blocksize = sndbuf_getblksz(parent->bufsoft); 242 CHN_UNLOCK(parent); 243 blocksize *= prate; 244 blocksize /= crate; 245 246 return blocksize; 247 } 248 249 static int 250 vchan_trigger(kobj_t obj, void *data, int go) 251 { 252 struct vchinfo *ch = data; 253 struct pcm_channel *parent = ch->parent; 254 struct pcm_channel *channel = ch->channel; 255 256 if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 257 return 0; 258 259 ch->run = (go == PCMTRIG_START)? 1 : 0; 260 CHN_UNLOCK(channel); 261 chn_notify(parent, CHN_N_TRIGGER); 262 CHN_LOCK(channel); 263 264 return 0; 265 } 266 267 static struct pcmchan_caps * 268 vchan_getcaps(kobj_t obj, void *data) 269 { 270 struct vchinfo *ch = data; 271 272 ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft); 273 ch->caps.maxspeed = ch->caps.minspeed; 274 ch->caps.fmtlist = vchan_fmt; 275 ch->caps.caps = 0; 276 277 return &ch->caps; 278 } 279 280 static kobj_method_t vchan_methods[] = { 281 KOBJMETHOD(channel_init, vchan_init), 282 KOBJMETHOD(channel_free, vchan_free), 283 KOBJMETHOD(channel_setformat, vchan_setformat), 284 KOBJMETHOD(channel_setspeed, vchan_setspeed), 285 KOBJMETHOD(channel_setblocksize, vchan_setblocksize), 286 KOBJMETHOD(channel_trigger, vchan_trigger), 287 KOBJMETHOD(channel_getcaps, vchan_getcaps), 288 { 0, 0 } 289 }; 290 CHANNEL_DECLARE(vchan); 291 292 #if 0 293 /* 294 * On the fly vchan rate settings 295 */ 296 #ifdef SND_DYNSYSCTL 297 static int 298 sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) 299 { 300 struct snddev_info *d; 301 struct snddev_channel *sce; 302 struct pcm_channel *c, *ch = NULL, *fake; 303 struct pcmchan_caps *caps; 304 int err = 0; 305 int newspd = 0; 306 307 d = oidp->oid_arg1; 308 if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) 309 return EINVAL; 310 if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { 311 pcm_inprog(d, -1); 312 return EINPROGRESS; 313 } 314 SLIST_FOREACH(sce, &d->channels, link) { 315 c = sce->channel; 316 CHN_LOCK(c); 317 if (c->direction == PCMDIR_PLAY) { 318 if (c->flags & CHN_F_VIRTUAL) { 319 /* Sanity check */ 320 if (ch != NULL && ch != c->parentchannel) { 321 CHN_UNLOCK(c); 322 pcm_inprog(d, -1); 323 return EINVAL; 324 } 325 if (req->newptr != NULL && 326 (c->flags & CHN_F_BUSY)) { 327 CHN_UNLOCK(c); 328 pcm_inprog(d, -1); 329 return EBUSY; 330 } 331 } else if (c->flags & CHN_F_HAS_VCHAN) { 332 /* No way!! */ 333 if (ch != NULL) { 334 CHN_UNLOCK(c); 335 pcm_inprog(d, -1); 336 return EINVAL; 337 } 338 ch = c; 339 newspd = ch->speed; 340 } 341 } 342 CHN_UNLOCK(c); 343 } 344 if (ch == NULL) { 345 pcm_inprog(d, -1); 346 return EINVAL; 347 } 348 err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req); 349 if (err == 0 && req->newptr != NULL) { 350 if (newspd < 1 || newspd < feeder_rate_ratemin || 351 newspd > feeder_rate_ratemax) { 352 pcm_inprog(d, -1); 353 return EINVAL; 354 } 355 CHN_LOCK(ch); 356 caps = chn_getcaps(ch); 357 if (caps == NULL || newspd < caps->minspeed || 358 newspd > caps->maxspeed) { 359 CHN_UNLOCK(ch); 360 pcm_inprog(d, -1); 361 return EINVAL; 362 } 363 if (newspd != ch->speed) { 364 err = chn_setspeed(ch, newspd); 365 /* 366 * Try to avoid FEEDER_RATE on parent channel if the 367 * requested value is not supported by the hardware. 368 */ 369 if (!err && (ch->feederflags & (1 << FEEDER_RATE))) { 370 newspd = sndbuf_getspd(ch->bufhard); 371 err = chn_setspeed(ch, newspd); 372 } 373 CHN_UNLOCK(ch); 374 if (err == 0) { 375 fake = pcm_getfakechan(d); 376 if (fake != NULL) { 377 CHN_LOCK(fake); 378 fake->speed = newspd; 379 CHN_UNLOCK(fake); 380 } 381 } 382 } else 383 CHN_UNLOCK(ch); 384 } 385 pcm_inprog(d, -1); 386 return err; 387 } 388 #endif 389 #endif 390 391 /* virtual channel interface */ 392 393 int 394 vchan_create(struct pcm_channel *parent) 395 { 396 struct snddev_info *d = parent->parentsnddev; 397 struct pcmchan_children *pce; 398 struct pcm_channel *child, *fake; 399 struct pcmchan_caps *parent_caps; 400 int err, first, speed = 0; 401 402 if (!(parent->flags & CHN_F_BUSY)) 403 return EBUSY; 404 405 406 CHN_UNLOCK(parent); 407 408 pce = kmalloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); 409 if (!pce) { 410 CHN_LOCK(parent); 411 return ENOMEM; 412 } 413 414 /* create a new playback channel */ 415 child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); 416 if (!child) { 417 kfree(pce, M_DEVBUF); 418 CHN_LOCK(parent); 419 return ENODEV; 420 } 421 pce->channel = child; 422 423 /* add us to our grandparent's channel list */ 424 /* 425 * XXX maybe we shouldn't always add the dev_t 426 */ 427 err = pcm_chn_add(d, child); 428 if (err) { 429 pcm_chn_destroy(child); 430 kfree(pce, M_DEVBUF); 431 CHN_LOCK(parent); 432 return err; 433 } 434 435 CHN_LOCK(parent); 436 /* add us to our parent channel's children */ 437 first = SLIST_EMPTY(&parent->children); 438 SLIST_INSERT_HEAD(&parent->children, pce, link); 439 parent->flags |= CHN_F_HAS_VCHAN; 440 441 if (first) { 442 parent_caps = chn_getcaps(parent); 443 if (parent_caps == NULL) 444 err = EINVAL; 445 446 if (!err) 447 err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE); 448 449 if (!err) { 450 fake = pcm_getfakechan(d); 451 if (fake != NULL) { 452 /* 453 * Avoid querying kernel hint, use saved value 454 * from fake channel. 455 */ 456 CHN_UNLOCK(parent); 457 CHN_LOCK(fake); 458 speed = fake->speed; 459 CHN_UNLOCK(fake); 460 CHN_LOCK(parent); 461 } 462 463 /* 464 * This is very sad. Few soundcards advertised as being 465 * able to do (insanely) higher/lower speed, but in 466 * reality, they simply can't. At least, we give user chance 467 * to set sane value via kernel hints or sysctl. 468 */ 469 if (speed < 1) { 470 int r; 471 CHN_UNLOCK(parent); 472 r = resource_int_value(device_get_name(parent->dev), 473 device_get_unit(parent->dev), 474 "vchanrate", &speed); 475 CHN_LOCK(parent); 476 if (r != 0) { 477 /* 478 * Workaround for sb16 running 479 * poorly at 45k / 49k. 480 */ 481 switch (parent_caps->maxspeed) { 482 case 45000: 483 case 49000: 484 speed = 44100; 485 break; 486 default: 487 speed = VCHAN_DEFAULT_SPEED; 488 break; 489 } 490 } 491 } 492 493 /* 494 * Limit speed based on driver caps. 495 * This is supposed to help fixed rate, non-VRA 496 * AC97 cards, but.. (see below) 497 */ 498 if (speed < parent_caps->minspeed) 499 speed = parent_caps->minspeed; 500 if (speed > parent_caps->maxspeed) 501 speed = parent_caps->maxspeed; 502 503 /* 504 * We still need to limit the speed between 505 * feeder_rate_ratemin <-> feeder_rate_ratemax. This is 506 * just an escape goat if all of the above failed 507 * miserably. 508 */ 509 if (speed < feeder_rate_ratemin) 510 speed = feeder_rate_ratemin; 511 if (speed > feeder_rate_ratemax) 512 speed = feeder_rate_ratemax; 513 514 err = chn_setspeed(parent, speed); 515 /* 516 * Try to avoid FEEDER_RATE on parent channel if the 517 * requested value is not supported by the hardware. 518 */ 519 if (!err && (parent->feederflags & (1 << FEEDER_RATE))) { 520 speed = sndbuf_getspd(parent->bufhard); 521 err = chn_setspeed(parent, speed); 522 } 523 524 if (!err && fake != NULL) { 525 /* 526 * Save new value to fake channel. 527 */ 528 CHN_UNLOCK(parent); 529 CHN_LOCK(fake); 530 fake->speed = speed; 531 CHN_UNLOCK(fake); 532 CHN_LOCK(parent); 533 } 534 } 535 536 if (err) { 537 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 538 parent->flags &= ~CHN_F_HAS_VCHAN; 539 CHN_UNLOCK(parent); 540 kfree(pce, M_DEVBUF); 541 if (pcm_chn_remove(d, child) == 0) 542 pcm_chn_destroy(child); 543 CHN_LOCK(parent); 544 return err; 545 } 546 } 547 548 return 0; 549 } 550 551 int 552 vchan_destroy(struct pcm_channel *c) 553 { 554 struct pcm_channel *parent = c->parentchannel; 555 struct snddev_info *d = parent->parentsnddev; 556 struct pcmchan_children *pce; 557 struct snddev_channel *sce; 558 uint32_t spd; 559 int err; 560 561 CHN_LOCK(parent); 562 if (!(parent->flags & CHN_F_BUSY)) { 563 CHN_UNLOCK(parent); 564 return EBUSY; 565 } 566 if (SLIST_EMPTY(&parent->children)) { 567 CHN_UNLOCK(parent); 568 return EINVAL; 569 } 570 571 /* remove us from our parent's children list */ 572 SLIST_FOREACH(pce, &parent->children, link) { 573 if (pce->channel == c) 574 goto gotch; 575 } 576 CHN_UNLOCK(parent); 577 return EINVAL; 578 gotch: 579 SLIST_FOREACH(sce, &d->channels, link) { 580 if (sce->channel == c) { 581 if (sce->dsp_devt) { 582 destroy_dev(sce->dsp_devt); 583 sce->dsp_devt = NULL; 584 } 585 if (sce->dspW_devt) { 586 destroy_dev(sce->dspW_devt); 587 sce->dspW_devt = NULL; 588 } 589 if (sce->audio_devt) { 590 destroy_dev(sce->audio_devt); 591 sce->audio_devt = NULL; 592 } 593 if (sce->dspr_devt) { 594 destroy_dev(sce->dspr_devt); 595 sce->dspr_devt = NULL; 596 } 597 d->devcount--; 598 break; 599 } 600 } 601 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 602 kfree(pce, M_DEVBUF); 603 604 if (SLIST_EMPTY(&parent->children)) { 605 parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); 606 spd = parent->speed; 607 if (chn_reset(parent, parent->format) == 0) 608 chn_setspeed(parent, spd); 609 } 610 611 /* remove us from our grandparent's channel list */ 612 err = pcm_chn_remove(d, c); 613 614 CHN_UNLOCK(parent); 615 /* destroy ourselves */ 616 if (!err) 617 err = pcm_chn_destroy(c); 618 619 return err; 620 } 621 622 int 623 vchan_initsys(device_t dev) 624 { 625 #ifdef SND_DYNSYSCTL 626 struct snddev_info *d; 627 628 d = device_get_softc(dev); 629 SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 630 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 631 sysctl_hw_snd_vchans, "I", ""); 632 #if 0 633 SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 634 OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 635 sysctl_hw_snd_vchanrate, "I", ""); 636 #endif 637 #endif 638 639 return 0; 640 } 641