1/*- 2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 3 * (C) 1997 Luigi Rizzo 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: --- 21 unchanged lines hidden (view full) --- 30#include <dev/sound/pcm/vchan.h> 31#include <dev/sound/pcm/dsp.h> 32#include <dev/sound/version.h> 33#include <sys/limits.h> 34#include <sys/sysctl.h> 35 36#include "feeder_if.h" 37 |
38SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 170815 2007-06-16 03:37:28Z ariff $"); |
39 40devclass_t pcm_devclass; 41 42int pcm_veto_load = 1; 43 44#ifdef USING_DEVFS 45int snd_unit = 0; 46TUNABLE_INT("hw.snd.default_unit", &snd_unit); --- 79 unchanged lines hidden (view full) --- 126 127 mtx_unlock(mtx); 128#endif 129} 130*/ 131int 132snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 133{ |
134 struct snddev_info *d; |
135#ifdef USING_MUTEX 136 flags &= INTR_MPSAFE; 137 flags |= INTR_TYPE_AV; 138#else 139 flags = INTR_TYPE_AV; 140#endif |
141 d = device_get_softc(dev); 142 if (d != NULL && (flags & INTR_MPSAFE)) 143 d->flags |= SD_F_MPSAFE; 144 |
145 return bus_setup_intr(dev, res, flags, 146#if __FreeBSD_version >= 700031 147 NULL, 148#endif 149 hand, param, cookiep); 150} 151 152#ifndef PCM_DEBUG_MTX --- 16 unchanged lines hidden (view full) --- 169 return d->fakechan; 170} 171 172static void 173pcm_clonereset(struct snddev_info *d) 174{ 175 int cmax; 176 |
177 PCM_BUSYASSERT(d); |
178 179 cmax = d->playcount + d->reccount - 1; 180 if (d->pvchancount > 0) 181 cmax += MAX(d->pvchancount, snd_maxautovchans) - 1; 182 if (d->rvchancount > 0) 183 cmax += MAX(d->rvchancount, snd_maxautovchans) - 1; 184 if (cmax > PCMMAXCLONE) 185 cmax = PCMMAXCLONE; 186 (void)snd_clone_gc(d->clones); 187 (void)snd_clone_setmaxunit(d->clones, cmax); 188} 189 190static int 191pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) 192{ 193 struct pcm_channel *c, *ch, *nch; 194 int err, vcnt; 195 |
196 PCM_BUSYASSERT(d); |
197 |
198 if ((direction == PCMDIR_PLAY && d->playcount < 1) || |
199 (direction == PCMDIR_REC && d->reccount < 1)) 200 return (ENODEV); |
201 |
202 if (!(d->flags & SD_F_AUTOVCHAN)) 203 return (EINVAL); |
204 |
205 if (newcnt < 0 || newcnt > SND_MAXVCHANS) 206 return (E2BIG); |
207 208 if (direction == PCMDIR_PLAY) 209 vcnt = d->pvchancount; 210 else if (direction == PCMDIR_REC) 211 vcnt = d->rvchancount; |
212 else 213 return (EINVAL); |
214 215 if (newcnt > vcnt) { 216 KASSERT(num == -1 || 217 (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), 218 ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", 219 num, newcnt, vcnt)); 220 /* add new vchans - find a parent channel first */ |
221 ch = NULL; |
222 CHN_FOREACH(c, d, channels.pcm) { 223 CHN_LOCK(c); 224 if (c->direction == direction && 225 ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && |
226 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 227 ch = c; 228 break; 229 } |
230 CHN_UNLOCK(c); 231 } |
232 if (ch == NULL) 233 return (EBUSY); 234 ch->flags |= CHN_F_BUSY; 235 err = 0; |
236 while (err == 0 && newcnt > vcnt) { |
237 err = vchan_create(ch, num); |
238 if (err == 0) 239 vcnt++; 240 else if (err == E2BIG && newcnt > vcnt) 241 device_printf(d->dev, 242 "%s: err=%d Maximum channel reached.\n", 243 __func__, err); 244 } 245 if (vcnt == 0) |
246 ch->flags &= ~CHN_F_BUSY; 247 CHN_UNLOCK(ch); 248 if (err != 0) 249 return (err); 250 else 251 pcm_clonereset(d); |
252 } else if (newcnt < vcnt) { 253 KASSERT(num == -1, 254 ("bogus vchan_destroy() request num=%d", num)); 255 CHN_FOREACH(c, d, channels.pcm) { 256 CHN_LOCK(c); 257 if (c->direction != direction || 258 CHN_EMPTY(c, children) || 259 !(c->flags & CHN_F_HAS_VCHAN)) { --- 6 unchanged lines hidden (view full) --- 266 CHN_UNLOCK(ch); 267 CHN_UNLOCK(c); 268 err = vchan_destroy(ch); 269 CHN_LOCK(c); 270 if (err == 0) 271 vcnt--; 272 } else 273 CHN_UNLOCK(ch); |
274 if (vcnt == newcnt) |
275 break; |
276 } 277 CHN_UNLOCK(c); 278 break; 279 } |
280 pcm_clonereset(d); |
281 } 282 |
283 return (0); |
284} 285 286/* return error status and a locked channel */ 287int 288pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 289 pid_t pid, int devunit) 290{ 291 struct pcm_channel *c; 292 int err, vchancount; 293 294 KASSERT(d != NULL && ch != NULL && (devunit == -1 || 295 !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && 296 (direction == PCMDIR_PLAY || direction == PCMDIR_REC), |
297 ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d", |
298 __func__, d, ch, direction, pid, devunit)); |
299 PCM_BUSYASSERT(d); |
300 301 /* Double check again. */ 302 if (devunit != -1) { 303 switch (snd_unit2d(devunit)) { 304 case SND_DEV_DSPHW_PLAY: 305 case SND_DEV_DSPHW_VPLAY: 306 if (direction != PCMDIR_PLAY) 307 return (EOPNOTSUPP); --- 7 unchanged lines hidden (view full) --- 315 if (!(direction == PCMDIR_PLAY || 316 direction == PCMDIR_REC)) 317 return (EOPNOTSUPP); 318 break; 319 } 320 } 321 322retry_chnalloc: |
323 err = EOPNOTSUPP; |
324 /* scan for a free channel */ 325 CHN_FOREACH(c, d, channels.pcm) { 326 CHN_LOCK(c); 327 if (c->direction == direction && !(c->flags & CHN_F_BUSY) && 328 (devunit == -1 || devunit == -2 || c->unit == devunit)) { 329 c->flags |= CHN_F_BUSY; 330 c->pid = pid; 331 *ch = c; --- 8 unchanged lines hidden (view full) --- 340 CHN_UNLOCK(c); 341 return (err); 342 } else if ((devunit == -1 || devunit == -2) && 343 c->direction == direction && (c->flags & CHN_F_BUSY)) 344 err = EBUSY; 345 CHN_UNLOCK(c); 346 } 347 |
348 if (devunit == -2) 349 return (err); 350 |
351 /* no channel available */ |
352 if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || 353 snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { |
354 if (direction == PCMDIR_PLAY) 355 vchancount = d->pvchancount; 356 else 357 vchancount = d->rvchancount; 358 if (!(vchancount > 0 && vchancount < snd_maxautovchans) && 359 (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) 360 return (err); 361 err = pcm_setvchans(d, direction, vchancount + 1, --- 7 unchanged lines hidden (view full) --- 369 370 return (err); 371} 372 373/* release a locked channel and unlock it */ 374int 375pcm_chnrelease(struct pcm_channel *c) 376{ |
377 PCM_BUSYASSERT(c->parentsnddev); |
378 CHN_LOCKASSERT(c); |
379 |
380 c->flags &= ~CHN_F_BUSY; 381 c->pid = -1; 382 CHN_UNLOCK(c); |
383 384 return (0); |
385} 386 387int 388pcm_chnref(struct pcm_channel *c, int ref) 389{ |
390 PCM_BUSYASSERT(c->parentsnddev); |
391 CHN_LOCKASSERT(c); |
392 |
393 c->refcount += ref; |
394 395 return (c->refcount); |
396} 397 398int 399pcm_inprog(struct snddev_info *d, int delta) 400{ |
401 snd_mtxassert(d->lock); |
402 |
403 d->inprog += delta; |
404 405 return (d->inprog); |
406} 407 408static void 409pcm_setmaxautovchans(struct snddev_info *d, int num) 410{ |
411 PCM_BUSYASSERT(d); 412 |
413 if (num < 0) 414 return; 415 416 if (num >= 0 && d->pvchancount > num) 417 (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); 418 else if (num > 0 && d->pvchancount == 0) 419 (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); 420 421 if (num >= 0 && d->rvchancount > num) 422 (void)pcm_setvchans(d, PCMDIR_REC, num, -1); 423 else if (num > 0 && d->rvchancount == 0) 424 (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); 425 |
426 pcm_clonereset(d); |
427} 428 429#ifdef USING_DEVFS 430static int 431sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 432{ 433 struct snddev_info *d; 434 int error, unit; 435 436 unit = snd_unit; 437 error = sysctl_handle_int(oidp, &unit, 0, req); 438 if (error == 0 && req->newptr != NULL) { 439 d = devclass_get_softc(pcm_devclass, unit); |
440 if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) |
441 return EINVAL; 442 snd_unit = unit; 443 } 444 return (error); 445} 446/* XXX: do we need a way to let the user change the default unit? */ 447SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW, 448 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device"); --- 11 unchanged lines hidden (view full) --- 460 if (v < 0) 461 v = 0; 462 if (v > SND_MAXVCHANS) 463 v = SND_MAXVCHANS; 464 snd_maxautovchans = v; 465 for (i = 0; pcm_devclass != NULL && 466 i < devclass_get_maxunit(pcm_devclass); i++) { 467 d = devclass_get_softc(pcm_devclass, i); |
468 if (!PCM_REGISTERED(d)) |
469 continue; |
470 PCM_ACQUIRE_QUICK(d); |
471 pcm_setmaxautovchans(d, v); |
472 PCM_RELEASE_QUICK(d); |
473 } 474 } 475 return (error); 476} 477SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 478 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 479 480struct pcm_channel * 481pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo) 482{ 483 struct pcm_channel *ch; 484 int direction, err, rpnum, *pnum, max; 485 int udc, device, chan; 486 char *dirs, *devname, buf[CHN_NAMELEN]; 487 |
488 PCM_BUSYASSERT(d); 489 snd_mtxassert(d->lock); |
490 KASSERT(num >= -1, ("invalid num=%d", num)); 491 |
492 |
493 switch (dir) { |
494 case PCMDIR_PLAY: 495 dirs = "play"; 496 direction = PCMDIR_PLAY; 497 pnum = &d->playcount; 498 device = SND_DEV_DSPHW_PLAY; 499 max = SND_MAXHWCHAN; 500 break; 501 case PCMDIR_PLAY_VIRTUAL: --- 13 unchanged lines hidden (view full) --- 515 case PCMDIR_REC_VIRTUAL: 516 dirs = "virtual"; 517 direction = PCMDIR_REC; 518 pnum = &d->rvchancount; 519 device = SND_DEV_DSPHW_VREC; 520 max = SND_MAXVCHANS; 521 break; 522 default: |
523 return (NULL); |
524 } 525 526 chan = (num == -1) ? 0 : num; 527 |
528 if (*pnum >= max || chan >= max) 529 return (NULL); |
530 531 rpnum = 0; 532 533 CHN_FOREACH(ch, d, channels.pcm) { 534 if (CHN_DEV(ch) != device) 535 continue; 536 if (chan == CHN_CHAN(ch)) { 537 if (num != -1) { 538 device_printf(d->dev, 539 "channel num=%d allocated!\n", chan); |
540 return (NULL); |
541 } 542 chan++; 543 if (chan >= max) { 544 device_printf(d->dev, 545 "chan=%d > %d\n", chan, max); |
546 return (NULL); |
547 } 548 } 549 rpnum++; 550 } 551 552 if (*pnum != rpnum) { 553 device_printf(d->dev, 554 "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", 555 __func__, dirs, *pnum, rpnum); |
556 return (NULL); |
557 } 558 559 udc = snd_mkunit(device_get_unit(d->dev), device, chan); 560 devname = dsp_unit2name(buf, sizeof(buf), udc); 561 562 if (devname == NULL) { 563 device_printf(d->dev, 564 "Failed to query device name udc=0x%08x\n", udc); |
565 return (NULL); |
566 } 567 |
568 pcm_unlock(d); |
569 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 570 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); 571 ch->unit = udc; 572 ch->pid = -1; 573 ch->parentsnddev = d; 574 ch->parentchannel = parent; 575 ch->dev = d->dev; 576 ch->trigger = PCMTRIG_STOP; 577 snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", 578 device_get_nameunit(ch->dev), dirs, devname); 579 580 err = chn_init(ch, devinfo, dir, direction); |
581 pcm_lock(d); |
582 if (err) { 583 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", 584 ch->name, err); 585 kobj_delete(ch->methods, M_DEVBUF); 586 free(ch, M_DEVBUF); |
587 return (NULL); |
588 } 589 |
590 return (ch); |
591} 592 593int 594pcm_chn_destroy(struct pcm_channel *ch) 595{ 596 struct snddev_info *d; 597 int err; 598 599 d = ch->parentsnddev; |
600 PCM_BUSYASSERT(d); 601 |
602 err = chn_kill(ch); 603 if (err) { |
604 device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n", 605 ch->name, err); 606 return (err); |
607 } 608 609 kobj_delete(ch->methods, M_DEVBUF); 610 free(ch, M_DEVBUF); 611 |
612 return (0); |
613} 614 615int 616pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 617{ 618 struct pcm_channel *tmp, *after; 619 int num; 620 |
621 PCM_BUSYASSERT(d); 622 snd_mtxassert(d->lock); |
623 KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 624 ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 625 626 after = NULL; 627 tmp = NULL; 628 num = 0; 629 630 /* 631 * Look for possible device collision. 632 */ 633 CHN_FOREACH(tmp, d, channels.pcm) { 634 if (tmp->unit == ch->unit) { 635 device_printf(d->dev, "%s(): Device collision " 636 "old=%p new=%p devunit=0x%08x\n", 637 __func__, tmp, ch, ch->unit); |
638 return (ENODEV); |
639 } 640 if (CHN_DEV(tmp) < CHN_DEV(ch)) { 641 if (num == 0) 642 after = tmp; 643 continue; 644 } else if (CHN_DEV(tmp) > CHN_DEV(ch)) 645 break; 646 num++; --- 4 unchanged lines hidden (view full) --- 651 } 652 653 if (after != NULL) { 654 CHN_INSERT_AFTER(after, ch, channels.pcm); 655 } else { 656 CHN_INSERT_HEAD(d, ch, channels.pcm); 657 } 658 |
659 switch (CHN_DEV(ch)) { 660 case SND_DEV_DSPHW_PLAY: 661 d->playcount++; 662 break; 663 case SND_DEV_DSPHW_VPLAY: 664 d->pvchancount++; 665 break; 666 case SND_DEV_DSPHW_REC: 667 d->reccount++; 668 break; 669 case SND_DEV_DSPHW_VREC: 670 d->rvchancount++; 671 break; 672 default: 673 break; 674 } 675 |
676 d->devcount++; |
677 |
678 return (0); |
679} 680 681int 682pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 683{ 684 struct pcm_channel *tmp; 685 |
686 PCM_BUSYASSERT(d); 687 snd_mtxassert(d->lock); 688 |
689 tmp = NULL; 690 691 CHN_FOREACH(tmp, d, channels.pcm) { 692 if (tmp == ch) 693 break; 694 } 695 696 if (tmp != ch) |
697 return (EINVAL); |
698 699 CHN_REMOVE(d, ch, channels.pcm); |
700 |
701 switch (CHN_DEV(ch)) { 702 case SND_DEV_DSPHW_PLAY: 703 d->playcount--; 704 break; 705 case SND_DEV_DSPHW_VPLAY: 706 d->pvchancount--; 707 break; 708 case SND_DEV_DSPHW_REC: 709 d->reccount--; 710 break; 711 case SND_DEV_DSPHW_VREC: 712 d->rvchancount--; 713 break; 714 default: 715 break; 716 } 717 |
718 d->devcount--; 719 720 return (0); |
721} 722 723int 724pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 725{ 726 struct snddev_info *d = device_get_softc(dev); 727 struct pcm_channel *ch; 728 int err; 729 |
730 PCM_BUSYASSERT(d); 731 732 pcm_lock(d); |
733 ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); 734 if (!ch) { |
735 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", 736 cls->name, dir, devinfo); 737 pcm_unlock(d); 738 return (ENODEV); |
739 } 740 741 err = pcm_chn_add(d, ch); |
742 pcm_unlock(d); |
743 if (err) { |
744 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", 745 ch->name, err); |
746 pcm_chn_destroy(ch); |
747 } 748 |
749 return (err); |
750} 751 752static int 753pcm_killchan(device_t dev) 754{ 755 struct snddev_info *d = device_get_softc(dev); 756 struct pcm_channel *ch; |
757 int error; |
758 |
759 PCM_BUSYASSERT(d); 760 |
761 ch = CHN_FIRST(d, channels.pcm); 762 |
763 pcm_lock(d); |
764 error = pcm_chn_remove(d, ch); |
765 pcm_unlock(d); |
766 if (error) 767 return (error); 768 return (pcm_chn_destroy(ch)); 769} 770 771int 772pcm_setstatus(device_t dev, char *str) 773{ 774 struct snddev_info *d = device_get_softc(dev); 775 |
776 PCM_BUSYASSERT(d); |
777 |
778 if (d->playcount == 0 || d->reccount == 0) 779 d->flags |= SD_F_SIMPLEX; |
780 |
781 if ((d->playcount > 0 || d->reccount > 0) && 782 !(d->flags & SD_F_AUTOVCHAN)) { 783 d->flags |= SD_F_AUTOVCHAN; 784 vchan_initsys(dev); 785 } 786 787 pcm_setmaxautovchans(d, snd_maxautovchans); 788 |
789 strlcpy(d->status, str, SND_STATUSLEN); 790 |
791 pcm_lock(d); 792 |
793 /* Last stage, enable cloning. */ |
794 if (d->clones != NULL) |
795 (void)snd_clone_enable(d->clones); |
796 |
797 /* Done, we're ready.. */ 798 d->flags |= SD_F_REGISTERED; 799 800 PCM_RELEASE(d); 801 |
802 pcm_unlock(d); 803 |
804 return (0); |
805} 806 807uint32_t 808pcm_getflags(device_t dev) 809{ 810 struct snddev_info *d = device_get_softc(dev); 811 812 return d->flags; --- 49 unchanged lines hidden (view full) --- 862static int 863sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS) 864{ 865 struct snddev_info *d; 866 uint32_t flags; 867 int err; 868 869 d = oidp->oid_arg1; |
870 if (!PCM_REGISTERED(d) || d->clones == NULL) |
871 return (ENODEV); 872 |
873 PCM_ACQUIRE_QUICK(d); 874 |
875 flags = snd_clone_getflags(d->clones); |
876 err = sysctl_handle_int(oidp, &flags, 0, req); 877 878 if (err == 0 && req->newptr != NULL) { |
879 if (flags & ~SND_CLONE_MASK) |
880 err = EINVAL; |
881 else |
882 (void)snd_clone_setflags(d->clones, flags); |
883 } 884 |
885 PCM_RELEASE_QUICK(d); 886 |
887 return (err); 888} 889 890static int 891sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS) 892{ 893 struct snddev_info *d; 894 int err, deadline; 895 896 d = oidp->oid_arg1; |
897 if (!PCM_REGISTERED(d) || d->clones == NULL) |
898 return (ENODEV); 899 |
900 PCM_ACQUIRE_QUICK(d); 901 |
902 deadline = snd_clone_getdeadline(d->clones); |
903 err = sysctl_handle_int(oidp, &deadline, 0, req); 904 905 if (err == 0 && req->newptr != NULL) { 906 if (deadline < 0) 907 err = EINVAL; |
908 else |
909 (void)snd_clone_setdeadline(d->clones, deadline); |
910 } 911 |
912 PCM_RELEASE_QUICK(d); 913 |
914 return (err); 915} 916 917static int 918sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS) 919{ 920 struct snddev_info *d; 921 int err, val; 922 923 d = oidp->oid_arg1; |
924 if (!PCM_REGISTERED(d) || d->clones == NULL) |
925 return (ENODEV); 926 927 val = 0; 928 err = sysctl_handle_int(oidp, &val, 0, req); 929 930 if (err == 0 && req->newptr != NULL && val != 0) { |
931 PCM_ACQUIRE_QUICK(d); 932 val = snd_clone_gc(d->clones); 933 PCM_RELEASE_QUICK(d); 934 if (bootverbose != 0 || snd_verbose > 3) 935 device_printf(d->dev, "clone gc: pruned=%d\n", val); |
936 } 937 938 return (err); 939} 940 941static int 942sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS) 943{ 944 struct snddev_info *d; 945 int i, err, val; 946 947 val = 0; 948 err = sysctl_handle_int(oidp, &val, 0, req); 949 950 if (err == 0 && req->newptr != NULL && val != 0) { 951 for (i = 0; pcm_devclass != NULL && 952 i < devclass_get_maxunit(pcm_devclass); i++) { 953 d = devclass_get_softc(pcm_devclass, i); |
954 if (!PCM_REGISTERED(d) || d->clones == NULL) |
955 continue; |
956 PCM_ACQUIRE_QUICK(d); 957 val = snd_clone_gc(d->clones); 958 PCM_RELEASE_QUICK(d); 959 if (bootverbose != 0 || snd_verbose > 3) 960 device_printf(d->dev, "clone gc: pruned=%d\n", 961 val); |
962 } 963 } 964 965 return (err); 966} 967SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW, 968 0, sizeof(int), sysctl_hw_snd_clone_gc, "I", 969 "global clone garbage collector"); --- 14 unchanged lines hidden (view full) --- 984 device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n", 985 device_get_unit(dev), PCMMAXUNIT); 986 device_printf(dev, 987 "Use 'hw.snd.maxunit' tunable to raise the limit.\n"); 988 return ENODEV; 989 } 990 991 d = device_get_softc(dev); |
992 d->dev = dev; |
993 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); |
994 cv_init(&d->cv, device_get_nameunit(dev)); 995 PCM_ACQUIRE_QUICK(d); 996 dsp_cdevinfo_init(d); |
997#if 0 998 /* 999 * d->flags should be cleared by the allocator of the softc. 1000 * We cannot clear this field here because several devices set 1001 * this flag before calling pcm_register(). 1002 */ 1003 d->flags = 0; 1004#endif |
1005 d->devinfo = devinfo; 1006 d->devcount = 0; 1007 d->reccount = 0; 1008 d->playcount = 0; 1009 d->pvchancount = 0; 1010 d->rvchancount = 0; 1011 d->pvchanrate = 0; 1012 d->pvchanformat = 0; 1013 d->rvchanrate = 0; 1014 d->rvchanformat = 0; 1015 d->inprog = 0; 1016 1017 /* 1018 * Create clone manager, disabled by default. Cloning will be 1019 * enabled during final stage of driver iniialization through 1020 * pcm_setstatus(). 1021 */ |
1022 d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE, 1023 SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK | |
1024 SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF | 1025 SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED); 1026 1027 if (bootverbose != 0 || snd_verbose > 3) { |
1028 device_printf(dev, 1029 "clone manager: deadline=%dms flags=0x%08x\n", 1030 snd_clone_getdeadline(d->clones), 1031 snd_clone_getflags(d->clones)); |
1032 } 1033 1034 CHN_INIT(d, channels.pcm); 1035 CHN_INIT(d, channels.pcm.busy); 1036 |
1037 /* XXX This is incorrect, but lets play along for now. */ |
1038 if ((numplay == 0 || numrec == 0) && numplay != numrec) 1039 d->flags |= SD_F_SIMPLEX; 1040 1041 d->fakechan = fkchan_setup(dev); 1042 chn_init(d->fakechan, NULL, 0, 0); 1043 1044#ifdef SND_DYNSYSCTL 1045 sysctl_ctx_init(&d->play_sysctl_ctx); --- 22 unchanged lines hidden (view full) --- 1068 "clone expiration deadline (ms)"); 1069 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1070 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1071 "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 1072 sysctl_dev_pcm_clone_gc, "I", 1073 "clone garbage collector"); 1074#endif 1075#endif |
1076 |
1077 if (numplay > 0 || numrec > 0) { 1078 d->flags |= SD_F_AUTOVCHAN; 1079 vchan_initsys(dev); 1080 } 1081 1082 sndstat_register(dev, d->status, sndstat_prepare_pcm); |
1083 |
1084 return 0; 1085} 1086 1087int 1088pcm_unregister(device_t dev) 1089{ |
1090 struct snddev_info *d; |
1091 struct pcm_channel *ch; 1092 struct thread *td; 1093 int i; 1094 1095 td = curthread; |
1096 d = device_get_softc(dev); |
1097 |
1098 if (!PCM_ALIVE(d)) { 1099 device_printf(dev, "unregister: device not configured\n"); 1100 return (0); 1101 } 1102 |
1103 if (sndstat_acquire(td) != 0) { 1104 device_printf(dev, "unregister: sndstat busy\n"); |
1105 return (EBUSY); |
1106 } 1107 1108 pcm_lock(d); |
1109 PCM_WAIT(d); 1110 1111 if (d->inprog != 0) { |
1112 device_printf(dev, "unregister: operation in progress\n"); 1113 pcm_unlock(d); 1114 sndstat_release(td); |
1115 return (EBUSY); |
1116 } 1117 |
1118 PCM_ACQUIRE(d); 1119 pcm_unlock(d); 1120 |
1121 CHN_FOREACH(ch, d, channels.pcm) { |
1122 CHN_LOCK(ch); |
1123 if (ch->refcount > 0) { |
1124 device_printf(dev, 1125 "unregister: channel %s busy (pid %d)\n", 1126 ch->name, ch->pid); 1127 CHN_UNLOCK(ch); 1128 PCM_RELEASE_QUICK(d); |
1129 sndstat_release(td); |
1130 return (EBUSY); |
1131 } |
1132 CHN_UNLOCK(ch); |
1133 } 1134 1135 if (d->clones != NULL) { 1136 if (snd_clone_busy(d->clones) != 0) { 1137 device_printf(dev, "unregister: clone busy\n"); |
1138 PCM_RELEASE_QUICK(d); |
1139 sndstat_release(td); |
1140 return (EBUSY); 1141 } else { 1142 pcm_lock(d); |
1143 (void)snd_clone_disable(d->clones); |
1144 pcm_unlock(d); 1145 } |
1146 } 1147 1148 if (mixer_uninit(dev) == EBUSY) { 1149 device_printf(dev, "unregister: mixer busy\n"); |
1150 pcm_lock(d); |
1151 if (d->clones != NULL) 1152 (void)snd_clone_enable(d->clones); |
1153 PCM_RELEASE(d); |
1154 pcm_unlock(d); 1155 sndstat_release(td); |
1156 return (EBUSY); |
1157 } 1158 |
1159 pcm_lock(d); 1160 d->flags |= SD_F_DYING; 1161 d->flags &= ~SD_F_REGISTERED; 1162 pcm_unlock(d); 1163 1164 /* 1165 * No lock being held, so this thing can be flushed without 1166 * stucking into devdrn oblivion. 1167 */ |
1168 if (d->clones != NULL) { 1169 snd_clone_destroy(d->clones); 1170 d->clones = NULL; 1171 } 1172 |
1173#ifdef SND_DYNSYSCTL 1174 if (d->play_sysctl_tree != NULL) { 1175 sysctl_ctx_free(&d->play_sysctl_ctx); 1176 d->play_sysctl_tree = NULL; 1177 } 1178 if (d->rec_sysctl_tree != NULL) { 1179 sysctl_ctx_free(&d->rec_sysctl_ctx); 1180 d->rec_sysctl_tree = NULL; 1181 } 1182#endif 1183 1184 while (!CHN_EMPTY(d, channels.pcm)) 1185 pcm_killchan(dev); 1186 1187 chn_kill(d->fakechan); 1188 fkchan_kill(d->fakechan); 1189 |
1190 dsp_cdevinfo_flush(d); 1191 1192 pcm_lock(d); 1193 PCM_RELEASE(d); 1194 cv_destroy(&d->cv); |
1195 pcm_unlock(d); 1196 snd_mtxfree(d->lock); 1197 sndstat_unregister(dev); 1198 sndstat_release(td); 1199 1200 if (snd_unit == device_get_unit(dev)) { 1201 /* 1202 * Reassign default unit to the next available dev. 1203 */ 1204 for (i = 0; pcm_devclass != NULL && 1205 i < devclass_get_maxunit(pcm_devclass); i++) { |
1206 if (device_get_unit(dev) == i) |
1207 continue; |
1208 d = devclass_get_softc(pcm_devclass, i); 1209 if (PCM_REGISTERED(d)) { 1210 snd_unit = i; 1211 break; 1212 } |
1213 } 1214 } 1215 |
1216 return (0); |
1217} 1218 1219/************************************************************************/ 1220 1221static int 1222sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 1223{ 1224 struct snddev_info *d; 1225 struct pcm_channel *c; 1226 struct pcm_feeder *f; 1227 1228 if (verbose < 1) 1229 return 0; 1230 1231 d = device_get_softc(dev); 1232 if (!d) 1233 return ENXIO; 1234 |
1235 PCM_BUSYASSERT(d); 1236 1237 if (CHN_EMPTY(d, channels.pcm)) { 1238 sbuf_printf(s, " (mixer only)"); 1239 return 0; 1240 } 1241 1242 sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", 1243 d->playcount, d->pvchancount, 1244 d->reccount, d->rvchancount, 1245 (d->flags & SD_F_SIMPLEX)? "" : " duplex", |
1246#ifdef USING_DEVFS |
1247 (device_get_unit(dev) == snd_unit)? " default" : "" |
1248#else |
1249 "" |
1250#endif |
1251 ); |
1252 |
1253 if (verbose <= 1) 1254 return 0; |
1255 |
1256 CHN_FOREACH(c, d, channels.pcm) { |
1257 |
1258 KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 1259 ("hosed pcm channel setup")); |
1260 |
1261 sbuf_printf(s, "\n\t"); |
1262 |
1263 /* it would be better to indent child channels */ 1264 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 1265 sbuf_printf(s, "spd %d", c->speed); 1266 if (c->speed != sndbuf_getspd(c->bufhard)) 1267 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 1268 sbuf_printf(s, ", fmt 0x%08x", c->format); 1269 if (c->format != sndbuf_getfmt(c->bufhard)) 1270 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 1271 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 1272 if (c->pid != -1) 1273 sbuf_printf(s, ", pid %d", c->pid); 1274 sbuf_printf(s, "\n\t"); |
1275 |
1276 sbuf_printf(s, "interrupts %d, ", c->interrupts); 1277 if (c->direction == PCMDIR_REC) 1278 sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 1279 c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), 1280 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1281 sndbuf_getblkcnt(c->bufhard), 1282 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1283 sndbuf_getblkcnt(c->bufsoft)); 1284 else 1285 sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", 1286 c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), 1287 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1288 sndbuf_getblkcnt(c->bufhard), 1289 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1290 sndbuf_getblkcnt(c->bufsoft)); 1291 sbuf_printf(s, "\n\t"); |
1292 |
1293 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 1294 sbuf_printf(s, " -> "); 1295 f = c->feeder; 1296 while (f->source != NULL) 1297 f = f->source; 1298 while (f != NULL) { 1299 sbuf_printf(s, "%s", f->class->name); 1300 if (f->desc->type == FEEDER_FMT) 1301 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 1302 if (f->desc->type == FEEDER_RATE) 1303 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 1304 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || 1305 f->desc->type == FEEDER_VOLUME) 1306 sbuf_printf(s, "(0x%08x)", f->desc->out); |
1307 sbuf_printf(s, " -> "); |
1308 f = f->parent; |
1309 } |
1310 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 1311 } |
1312 1313 return 0; 1314} 1315 1316/************************************************************************/ 1317 1318#ifdef SND_DYNSYSCTL 1319int 1320sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 1321{ 1322 struct snddev_info *d; 1323 int direction, vchancount; |
1324 int err, cnt; |
1325 1326 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); |
1327 if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 1328 return (EINVAL); |
1329 |
1330 pcm_lock(d); 1331 PCM_WAIT(d); 1332 |
1333 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 1334 case VCHAN_PLAY: |
1335 direction = PCMDIR_PLAY; 1336 vchancount = d->pvchancount; |
1337 cnt = d->playcount; |
1338 break; 1339 case VCHAN_REC: |
1340 direction = PCMDIR_REC; 1341 vchancount = d->rvchancount; |
1342 cnt = d->reccount; |
1343 break; 1344 default: |
1345 pcm_unlock(d); 1346 return (EINVAL); |
1347 break; 1348 } 1349 |
1350 if (cnt < 1) { 1351 pcm_unlock(d); 1352 return (ENODEV); 1353 } |
1354 |
1355 PCM_ACQUIRE(d); 1356 pcm_unlock(d); 1357 1358 cnt = vchancount; 1359 err = sysctl_handle_int(oidp, &cnt, 0, req); 1360 1361 if (err == 0 && req->newptr != NULL && vchancount != cnt) { 1362 if (cnt < 0) 1363 cnt = 0; 1364 if (cnt > SND_MAXVCHANS) 1365 cnt = SND_MAXVCHANS; 1366 err = pcm_setvchans(d, direction, cnt, -1); |
1367 } 1368 |
1369 PCM_RELEASE_QUICK(d); 1370 |
1371 return err; 1372} 1373#endif 1374 1375/************************************************************************/ 1376 1377/** 1378 * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. --- 31 unchanged lines hidden (view full) --- 1410 1411 /* 1412 * Iterate over PCM devices and their channels, gathering up data 1413 * for the numaudios, ncards, and openedaudio fields. 1414 */ 1415 si->numaudios = 0; 1416 bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 1417 |
1418 j = 0; |
1419 |
1420 for (i = 0; pcm_devclass != NULL && 1421 i < devclass_get_maxunit(pcm_devclass); i++) { 1422 d = devclass_get_softc(pcm_devclass, i); 1423 if (!PCM_REGISTERED(d)) 1424 continue; |
1425 |
1426 /* XXX Need Giant magic entry ??? */ |
1427 |
1428 /* See note in function's docblock */ 1429 mtx_assert(d->lock, MA_NOTOWNED); 1430 pcm_lock(d); |
1431 |
1432 si->numaudios += d->devcount; 1433 ++ncards; |
1434 |
1435 CHN_FOREACH(c, d, channels.pcm) { 1436 mtx_assert(c->lock, MA_NOTOWNED); 1437 CHN_LOCK(c); 1438 if (c->flags & CHN_F_BUSY) 1439 si->openedaudio[j / intnbits] |= 1440 (1 << (j % intnbits)); 1441 CHN_UNLOCK(c); 1442 j++; |
1443 } |
1444 1445 pcm_unlock(d); |
1446 } 1447 1448 si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 1449 /** 1450 * @todo Collect num{midis,timers}. 1451 * 1452 * Need access to sound/midi/midi.c::midistat_lock in order 1453 * to safely touch midi_devices and get a head count of, well, --- 64 unchanged lines hidden --- |