sound.c revision 82492
1162856Sdes/* 276259Sgreen * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 376259Sgreen * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) 476259Sgreen * All rights reserved. 576259Sgreen * 676259Sgreen * Redistribution and use in source and binary forms, with or without 776259Sgreen * modification, are permitted provided that the following conditions 876259Sgreen * are met: 976259Sgreen * 1. Redistributions of source code must retain the above copyright 1076259Sgreen * notice, this list of conditions and the following disclaimer. 1176259Sgreen * 2. Redistributions in binary form must reproduce the above copyright 1276259Sgreen * notice, this list of conditions and the following disclaimer in the 1376259Sgreen * documentation and/or other materials provided with the distribution. 1476259Sgreen * 15162856Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16103134Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1798937Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18255767Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1976259Sgreen * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2098937Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2198937Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22103134Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2376259Sgreen * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <dev/sound/pcm/sound.h> 29#include <dev/sound/pcm/vchan.h> 30#include <sys/sysctl.h> 31 32#include "feeder_if.h" 33 34SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 82492 2001-08-29 09:17:43Z cg $"); 35 36struct snddev_channel { 37 SLIST_ENTRY(snddev_channel) link; 38 struct pcm_channel *channel; 39}; 40 41struct snddev_info { 42 SLIST_HEAD(, snddev_channel) channels; 43 struct pcm_channel *fakechan; 44 unsigned devcount, chancount, vchancount; 45 unsigned flags; 46 int inprog; 47 void *devinfo; 48 device_t dev; 49 char status[SND_STATUSLEN]; 50 struct sysctl_ctx_list sysctl_tree; 51 struct sysctl_oid *sysctl_tree_top; 52 void *lock; 53}; 54 55devclass_t pcm_devclass; 56 57#ifdef USING_DEVFS 58int snd_unit = 0; 59TUNABLE_INT("hw.snd.unit", &snd_unit); 60#endif 61 62int snd_maxautovchans = 0; 63TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 64 65SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 66 67static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 68 69struct sysctl_ctx_list * 70snd_sysctl_tree(device_t dev) 71{ 72 struct snddev_info *d = device_get_softc(dev); 73 74 return &d->sysctl_tree; 75} 76 77struct sysctl_oid * 78snd_sysctl_tree_top(device_t dev) 79{ 80 struct snddev_info *d = device_get_softc(dev); 81 82 return d->sysctl_tree_top; 83} 84 85void * 86snd_mtxcreate(const char *desc) 87{ 88#ifdef USING_MUTEX 89 struct mtx *m; 90 91 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 92 if (m == NULL) 93 return NULL; 94 mtx_init(m, desc, MTX_RECURSE); 95 return m; 96#else 97 return (void *)0xcafebabe; 98#endif 99} 100 101void 102snd_mtxfree(void *m) 103{ 104#ifdef USING_MUTEX 105 struct mtx *mtx = m; 106 107 mtx_assert(mtx, MA_OWNED); 108 mtx_destroy(mtx); 109 free(mtx, M_DEVBUF); 110#endif 111} 112 113void 114snd_mtxassert(void *m) 115{ 116#ifdef USING_MUTEX 117#ifdef INVARIANTS 118 struct mtx *mtx = m; 119 120 mtx_assert(mtx, MA_OWNED); 121#endif 122#endif 123} 124 125void 126snd_mtxlock(void *m) 127{ 128#ifdef USING_MUTEX 129 struct mtx *mtx = m; 130 131 mtx_lock(mtx); 132#endif 133} 134 135void 136snd_mtxunlock(void *m) 137{ 138#ifdef USING_MUTEX 139 struct mtx *mtx = m; 140 141 mtx_unlock(mtx); 142#endif 143} 144 145int 146snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 147{ 148#ifdef USING_MUTEX 149 flags &= INTR_MPSAFE; 150 flags |= INTR_TYPE_AV; 151#else 152 flags = INTR_TYPE_AV; 153#endif 154 return bus_setup_intr(dev, res, flags, hand, param, cookiep); 155} 156 157void 158pcm_lock(struct snddev_info *d) 159{ 160 snd_mtxlock(d->lock); 161} 162 163void 164pcm_unlock(struct snddev_info *d) 165{ 166 snd_mtxunlock(d->lock); 167} 168 169struct pcm_channel * 170pcm_getfakechan(struct snddev_info *d) 171{ 172 return d->fakechan; 173} 174 175/* return a locked channel */ 176struct pcm_channel * 177pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid) 178{ 179 struct pcm_channel *c; 180 struct snddev_channel *sce; 181 int err; 182 183 snd_mtxassert(d->lock); 184 185 /* scan for a free channel */ 186 SLIST_FOREACH(sce, &d->channels, link) { 187 c = sce->channel; 188 CHN_LOCK(c); 189 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 190 c->flags |= CHN_F_BUSY; 191 c->pid = pid; 192 return c; 193 } 194 CHN_UNLOCK(c); 195 } 196 197 /* no channel available */ 198 if (direction == PCMDIR_PLAY) { 199 if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { 200 /* try to create a vchan */ 201 SLIST_FOREACH(sce, &d->channels, link) { 202 c = sce->channel; 203 if (!SLIST_EMPTY(&c->children)) { 204 err = vchan_create(c); 205 if (!err) 206 return pcm_chnalloc(d, direction, pid); 207 else 208 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 209 } 210 } 211 } 212 } 213 214 return NULL; 215} 216 217/* release a locked channel and unlock it */ 218int 219pcm_chnrelease(struct pcm_channel *c) 220{ 221 CHN_LOCKASSERT(c); 222 c->flags &= ~CHN_F_BUSY; 223 c->pid = -1; 224 CHN_UNLOCK(c); 225 return 0; 226} 227 228int 229pcm_chnref(struct pcm_channel *c, int ref) 230{ 231 int r; 232 233 CHN_LOCKASSERT(c); 234 c->refcount += ref; 235 r = c->refcount; 236 return r; 237} 238 239int 240pcm_inprog(struct snddev_info *d, int delta) 241{ 242 d->inprog += delta; 243 return d->inprog; 244} 245 246static void 247pcm_setmaxautovchans(struct snddev_info *d, int num) 248{ 249 struct pcm_channel *c; 250 struct snddev_channel *sce; 251 int err, done; 252 253 if (num > 0 && d->vchancount == 0) { 254 SLIST_FOREACH(sce, &d->channels, link) { 255 c = sce->channel; 256 if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { 257 c->flags |= CHN_F_BUSY; 258 err = vchan_create(c); 259 if (err) { 260 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 261 c->flags &= ~CHN_F_BUSY; 262 } 263 return; 264 } 265 } 266 } 267 if (num == 0 && d->vchancount > 0) { 268 done = 0; 269 while (!done) { 270 done = 1; 271 SLIST_FOREACH(sce, &d->channels, link) { 272 c = sce->channel; 273 if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { 274 done = 0; 275 err = vchan_destroy(c); 276 if (err) 277 device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); 278 goto restart; 279 } 280 } 281restart: 282 } 283 } 284} 285 286#ifdef USING_DEVFS 287static int 288sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 289{ 290 struct snddev_info *d; 291 int error, unit; 292 293 unit = snd_unit; 294 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 295 if (error == 0 && req->newptr != NULL) { 296 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 297 return EINVAL; 298 d = devclass_get_softc(pcm_devclass, unit); 299 if (d == NULL || SLIST_EMPTY(&d->channels)) 300 return EINVAL; 301 snd_unit = unit; 302 } 303 return (error); 304} 305SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 306 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 307#endif 308 309static int 310sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 311{ 312 struct snddev_info *d; 313 int i, v, error; 314 315 v = snd_maxautovchans; 316 error = sysctl_handle_int(oidp, &v, sizeof(v), req); 317 if (error == 0 && req->newptr != NULL) { 318 if (v < 0 || v >= SND_MAXVCHANS) 319 return EINVAL; 320 if (v != snd_maxautovchans) { 321 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 322 d = devclass_get_softc(pcm_devclass, i); 323 if (!d) 324 continue; 325 pcm_setmaxautovchans(d, v); 326 } 327 } 328 snd_maxautovchans = v; 329 } 330 return (error); 331} 332SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 333 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 334 335struct pcm_channel * 336pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 337{ 338 struct pcm_channel *ch; 339 char *dirs; 340 int err; 341 342 switch(dir) { 343 case PCMDIR_PLAY: 344 dirs = "play"; 345 break; 346 case PCMDIR_REC: 347 dirs = "record"; 348 break; 349 case PCMDIR_VIRTUAL: 350 dirs = "virtual"; 351 dir = PCMDIR_PLAY; 352 break; 353 default: 354 return NULL; 355 } 356 357 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 358 if (!ch) 359 return NULL; 360 361 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 362 if (!ch->methods) { 363 free(ch, M_DEVBUF); 364 return NULL; 365 } 366 367 ch->pid = -1; 368 ch->parentsnddev = d; 369 ch->parentchannel = parent; 370 snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs); 371 372 err = chn_init(ch, devinfo, dir); 373 if (err) { 374 device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err); 375 kobj_delete(ch->methods, M_DEVBUF); 376 free(ch, M_DEVBUF); 377 return NULL; 378 } 379 380 return ch; 381} 382 383int 384pcm_chn_destroy(struct pcm_channel *ch) 385{ 386 int err; 387 388 err = chn_kill(ch); 389 if (err) { 390 device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err); 391 return err; 392 } 393 394 kobj_delete(ch->methods, M_DEVBUF); 395 free(ch, M_DEVBUF); 396 397 return 0; 398} 399 400int 401pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 402{ 403 struct snddev_channel *sce, *tmp, *after; 404 int unit = device_get_unit(d->dev); 405 406 snd_mtxlock(d->lock); 407 408 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 409 if (!sce) { 410 snd_mtxunlock(d->lock); 411 return ENOMEM; 412 } 413 414 sce->channel = ch; 415 if (SLIST_EMPTY(&d->channels)) { 416 SLIST_INSERT_HEAD(&d->channels, sce, link); 417 } else { 418 after = NULL; 419 SLIST_FOREACH(tmp, &d->channels, link) { 420 after = tmp; 421 } 422 SLIST_INSERT_AFTER(after, sce, link); 423 } 424 425 if (mkdev) 426 dsp_register(unit, d->devcount++); 427 d->chancount++; 428 if (ch->flags & CHN_F_VIRTUAL) 429 d->vchancount++; 430 431 snd_mtxunlock(d->lock); 432 433 return 0; 434} 435 436int 437pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 438{ 439 struct snddev_channel *sce; 440 int unit = device_get_unit(d->dev); 441 442 snd_mtxlock(d->lock); 443 SLIST_FOREACH(sce, &d->channels, link) { 444 if (sce->channel == ch) 445 goto gotit; 446 } 447 snd_mtxunlock(d->lock); 448 return EINVAL; 449gotit: 450 if (ch->flags & CHN_F_VIRTUAL) 451 d->vchancount--; 452 d->chancount--; 453 SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 454 free(sce, M_DEVBUF); 455 456 if (rmdev) 457 dsp_unregister(unit, --d->devcount); 458 snd_mtxunlock(d->lock); 459 460 return 0; 461} 462 463int 464pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 465{ 466 struct snddev_info *d = device_get_softc(dev); 467 struct pcm_channel *ch; 468 int err; 469 470 ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 471 if (!ch) { 472 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 473 return ENODEV; 474 } 475 476 err = pcm_chn_add(d, ch, 1); 477 if (err) { 478 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 479 pcm_chn_destroy(ch); 480 return err; 481 } 482 483 if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) { 484 ch->flags |= CHN_F_BUSY; 485 err = vchan_create(ch); 486 if (err) { 487 device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 488 ch->flags &= ~CHN_F_BUSY; 489 } 490 } 491 492 return err; 493} 494 495static int 496pcm_killchan(device_t dev) 497{ 498 struct snddev_info *d = device_get_softc(dev); 499 struct snddev_channel *sce; 500 501 snd_mtxlock(d->lock); 502 sce = SLIST_FIRST(&d->channels); 503 snd_mtxunlock(d->lock); 504 505 return pcm_chn_remove(d, sce->channel, 1); 506} 507 508int 509pcm_setstatus(device_t dev, char *str) 510{ 511 struct snddev_info *d = device_get_softc(dev); 512 513 snd_mtxlock(d->lock); 514 strncpy(d->status, str, SND_STATUSLEN); 515 snd_mtxunlock(d->lock); 516 return 0; 517} 518 519u_int32_t 520pcm_getflags(device_t dev) 521{ 522 struct snddev_info *d = device_get_softc(dev); 523 524 return d->flags; 525} 526 527void 528pcm_setflags(device_t dev, u_int32_t val) 529{ 530 struct snddev_info *d = device_get_softc(dev); 531 532 d->flags = val; 533} 534 535void * 536pcm_getdevinfo(device_t dev) 537{ 538 struct snddev_info *d = device_get_softc(dev); 539 540 return d->devinfo; 541} 542 543int 544pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 545{ 546 struct snddev_info *d = device_get_softc(dev); 547 548 d->lock = snd_mtxcreate(device_get_nameunit(dev)); 549 snd_mtxlock(d->lock); 550 551 d->flags = 0; 552 d->dev = dev; 553 d->devinfo = devinfo; 554 d->devcount = 0; 555 d->chancount = 0; 556 d->vchancount = 0; 557 d->inprog = 0; 558 559 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 560 d->flags |= SD_F_SIMPLEX; 561 562 d->fakechan = fkchan_setup(dev); 563 chn_init(d->fakechan, NULL, 0); 564 565#ifdef SND_DYNSYSCTL 566 sysctl_ctx_init(&d->sysctl_tree); 567 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 568 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 569 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 570 if (d->sysctl_tree_top == NULL) { 571 sysctl_ctx_free(&d->sysctl_tree); 572 goto no; 573 } 574#endif 575 if (numplay > 0) 576 vchan_initsys(dev); 577 if (numplay == 1) 578 d->flags |= SD_F_AUTOVCHAN; 579 580 snd_mtxunlock(d->lock); 581 sndstat_register(dev, d->status, sndstat_prepare_pcm); 582 return 0; 583no: 584 snd_mtxfree(d->lock); 585 return ENXIO; 586} 587 588int 589pcm_unregister(device_t dev) 590{ 591 struct snddev_info *d = device_get_softc(dev); 592 struct snddev_channel *sce; 593 594 snd_mtxlock(d->lock); 595 if (d->inprog) { 596 device_printf(dev, "unregister: operation in progress"); 597 snd_mtxunlock(d->lock); 598 return EBUSY; 599 } 600 SLIST_FOREACH(sce, &d->channels, link) { 601 if (sce->channel->refcount > 0) { 602 device_printf(dev, "unregister: channel busy"); 603 snd_mtxunlock(d->lock); 604 return EBUSY; 605 } 606 } 607 if (mixer_uninit(dev)) { 608 device_printf(dev, "unregister: mixer busy"); 609 snd_mtxunlock(d->lock); 610 return EBUSY; 611 } 612 613#ifdef SND_DYNSYSCTL 614 d->sysctl_tree_top = NULL; 615 sysctl_ctx_free(&d->sysctl_tree); 616#endif 617 while (!SLIST_EMPTY(&d->channels)) 618 pcm_killchan(dev); 619 620 chn_kill(d->fakechan); 621 fkchan_kill(d->fakechan); 622 623 snd_mtxfree(d->lock); 624 return 0; 625} 626 627/************************************************************************/ 628 629static int 630sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 631{ 632 struct snddev_info *d; 633 struct snddev_channel *sce; 634 struct pcm_channel *c; 635 struct pcm_feeder *f; 636 char *fsep; 637 int pc, rc, vc; 638 639 if (verbose < 1) 640 return 0; 641 642 d = device_get_softc(dev); 643 if (!d) 644 return ENXIO; 645 646 snd_mtxlock(d->lock); 647 if (!SLIST_EMPTY(&d->channels)) { 648 pc = rc = vc = 0; 649 SLIST_FOREACH(sce, &d->channels, link) { 650 c = sce->channel; 651 if (c->direction == PCMDIR_PLAY) { 652 if (c->flags & CHN_F_VIRTUAL) 653 vc++; 654 else 655 pc++; 656 } else 657 rc++; 658 } 659 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", pc, rc, vc, 660 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 661#ifdef USING_DEVFS 662 (device_get_unit(dev) == snd_unit)? " default" : "" 663#else 664 "" 665#endif 666 ); 667 if (verbose <= 1) 668 goto skipverbose; 669 SLIST_FOREACH(sce, &d->channels, link) { 670 c = sce->channel; 671 sbuf_printf(s, "\n\t"); 672 673 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 674 sbuf_printf(s, "speed %d, format %08x, flags %08x", c->speed, c->format, c->flags); 675 if (c->pid != -1) 676 sbuf_printf(s, ", pid %d", c->pid); 677 sbuf_printf(s, "\n\t"); 678 if (c->bufhard != NULL && c->bufsoft != NULL) { 679 sbuf_printf(s, "interrupts %d, ", c->interrupts); 680 if (c->direction == PCMDIR_REC) 681 sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 682 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 683 else 684 sbuf_printf(s, "underruns %d, ready %d", 685 c->xruns, sndbuf_getready(c->bufsoft)); 686 sbuf_printf(s, "\n\t"); 687 } 688 fsep = (c->direction == PCMDIR_REC)? " -> " : " <- "; 689 sbuf_printf(s, "{hardware}%s", fsep); 690 f = c->feeder; 691 while (f) { 692 sbuf_printf(s, "%s", f->class->name); 693 if (f->desc->type == FEEDER_FMT) 694 sbuf_printf(s, "(%08x%s%08x)", f->desc->out, fsep, f->desc->in); 695 if (f->desc->type == FEEDER_RATE) 696 sbuf_printf(s, "(%d%s%d)", FEEDER_GET(f, FEEDRATE_DST), fsep, FEEDER_GET(f, FEEDRATE_SRC)); 697 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 698 sbuf_printf(s, "(%08x)", f->desc->out); 699 sbuf_printf(s, "%s", fsep); 700 f = f->source; 701 } 702 sbuf_printf(s, "{userland}"); 703 } 704skipverbose: 705 } else 706 sbuf_printf(s, " (mixer only)"); 707 snd_mtxunlock(d->lock); 708 709 return 0; 710} 711 712/************************************************************************/ 713 714#ifdef SND_DYNSYSCTL 715int 716sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 717{ 718 struct snddev_info *d; 719 struct snddev_channel *sce; 720 struct pcm_channel *c; 721 int err, oldcnt, newcnt, cnt; 722 723 d = oidp->oid_arg1; 724 725 pcm_lock(d); 726 cnt = 0; 727 SLIST_FOREACH(sce, &d->channels, link) { 728 c = sce->channel; 729 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) 730 cnt++; 731 } 732 oldcnt = cnt; 733 newcnt = cnt; 734 735 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 736 if (err == 0 && req->newptr != NULL) { 737 if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 738 pcm_unlock(d); 739 return EINVAL; 740 } 741 742 if (newcnt > cnt) { 743 /* add new vchans - find a parent channel first */ 744 SLIST_FOREACH(sce, &d->channels, link) { 745 c = sce->channel; 746 /* not a candidate if not a play channel */ 747 if (c->direction != PCMDIR_PLAY) 748 goto addskip; 749 /* not a candidate if a virtual channel */ 750 if (c->flags & CHN_F_VIRTUAL) 751 goto addskip; 752 /* not a candidate if it's in use */ 753 if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 754 goto addskip; 755 /* 756 * if we get here we're a nonvirtual play channel, and either 757 * 1) not busy 758 * 2) busy with children, not directly open 759 * 760 * thus we can add children 761 */ 762 goto addok; 763addskip: 764 } 765 pcm_unlock(d); 766 return EBUSY; 767addok: 768 c->flags |= CHN_F_BUSY; 769 while (err == 0 && newcnt > cnt) { 770 err = vchan_create(c); 771 if (err == 0) 772 cnt++; 773 } 774 if (SLIST_EMPTY(&c->children)) 775 c->flags &= ~CHN_F_BUSY; 776 } else if (newcnt < cnt) { 777 while (err == 0 && newcnt < cnt) { 778 SLIST_FOREACH(sce, &d->channels, link) { 779 c = sce->channel; 780 if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 781 goto remok; 782 } 783 pcm_unlock(d); 784 return EINVAL; 785remok: 786 err = vchan_destroy(c); 787 if (err == 0) 788 cnt--; 789 } 790 } 791 } 792 793 pcm_unlock(d); 794 return err; 795} 796#endif 797 798/************************************************************************/ 799 800static moduledata_t sndpcm_mod = { 801 "snd_pcm", 802 NULL, 803 NULL 804}; 805DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 806MODULE_VERSION(snd_pcm, PCM_MODVER); 807