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