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