Deleted Added
sdiff udiff text old ( 170289 ) new ( 170815 )
full compact
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 170289 2007-06-04 18:25:08Z dwmalone $");
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#ifdef USING_MUTEX
135 flags &= INTR_MPSAFE;
136 flags |= INTR_TYPE_AV;
137#else
138 flags = INTR_TYPE_AV;
139#endif
140 return bus_setup_intr(dev, res, flags,
141#if __FreeBSD_version >= 700031
142 NULL,
143#endif
144 hand, param, cookiep);
145}
146
147#ifndef PCM_DEBUG_MTX

--- 16 unchanged lines hidden (view full) ---

164 return d->fakechan;
165}
166
167static void
168pcm_clonereset(struct snddev_info *d)
169{
170 int cmax;
171
172 snd_mtxassert(d->lock);
173
174 cmax = d->playcount + d->reccount - 1;
175 if (d->pvchancount > 0)
176 cmax += MAX(d->pvchancount, snd_maxautovchans) - 1;
177 if (d->rvchancount > 0)
178 cmax += MAX(d->rvchancount, snd_maxautovchans) - 1;
179 if (cmax > PCMMAXCLONE)
180 cmax = PCMMAXCLONE;
181 (void)snd_clone_gc(d->clones);
182 (void)snd_clone_setmaxunit(d->clones, cmax);
183}
184
185static int
186pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
187{
188 struct pcm_channel *c, *ch, *nch;
189 int err, vcnt;
190
191 err = 0;
192
193 pcm_inprog(d, 1);
194
195 if ((direction == PCMDIR_PLAY && d->playcount < 1) ||
196 (direction == PCMDIR_REC && d->reccount < 1)) {
197 err = ENODEV;
198 goto pcm_setvchans_out;
199 }
200
201 if (!(d->flags & SD_F_AUTOVCHAN)) {
202 err = EINVAL;
203 goto pcm_setvchans_out;
204 }
205
206 if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
207 err = E2BIG;
208 goto pcm_setvchans_out;
209 }
210
211 if (direction == PCMDIR_PLAY)
212 vcnt = d->pvchancount;
213 else if (direction == PCMDIR_REC)
214 vcnt = d->rvchancount;
215 else {
216 err = EINVAL;
217 goto pcm_setvchans_out;
218 }
219
220 if (newcnt > vcnt) {
221 KASSERT(num == -1 ||
222 (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt),
223 ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
224 num, newcnt, vcnt));
225 /* add new vchans - find a parent channel first */
226 CHN_FOREACH(c, d, channels.pcm) {
227 CHN_LOCK(c);
228 if (c->direction == direction &&
229 ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 &&
230 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)))))
231 goto pcm_setvchans_addok;
232 CHN_UNLOCK(c);
233 }
234 err = EBUSY;
235 goto pcm_setvchans_out;
236pcm_setvchans_addok:
237 c->flags |= CHN_F_BUSY;
238 while (err == 0 && newcnt > vcnt) {
239 err = vchan_create(c, num);
240 if (err == 0)
241 vcnt++;
242 else if (err == E2BIG && newcnt > vcnt)
243 device_printf(d->dev,
244 "%s: err=%d Maximum channel reached.\n",
245 __func__, err);
246 }
247 if (vcnt == 0)
248 c->flags &= ~CHN_F_BUSY;
249 CHN_UNLOCK(c);
250 pcm_lock(d);
251 pcm_clonereset(d);
252 pcm_unlock(d);
253 } else if (newcnt < vcnt) {
254 KASSERT(num == -1,
255 ("bogus vchan_destroy() request num=%d", num));
256 CHN_FOREACH(c, d, channels.pcm) {
257 CHN_LOCK(c);
258 if (c->direction != direction ||
259 CHN_EMPTY(c, children) ||
260 !(c->flags & CHN_F_HAS_VCHAN)) {

--- 6 unchanged lines hidden (view full) ---

267 CHN_UNLOCK(ch);
268 CHN_UNLOCK(c);
269 err = vchan_destroy(ch);
270 CHN_LOCK(c);
271 if (err == 0)
272 vcnt--;
273 } else
274 CHN_UNLOCK(ch);
275 if (vcnt == newcnt) {
276 err = 0;
277 break;
278 }
279 }
280 CHN_UNLOCK(c);
281 break;
282 }
283 pcm_lock(d);
284 pcm_clonereset(d);
285 pcm_unlock(d);
286 }
287
288pcm_setvchans_out:
289 pcm_inprog(d, -1);
290 return err;
291}
292
293/* return error status and a locked channel */
294int
295pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
296 pid_t pid, int devunit)
297{
298 struct pcm_channel *c;
299 int err, vchancount;
300
301 KASSERT(d != NULL && ch != NULL && (devunit == -1 ||
302 !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) &&
303 (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
304 ("%s() invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
305 __func__, d, ch, direction, pid, devunit));
306
307 /* Double check again. */
308 if (devunit != -1) {
309 switch (snd_unit2d(devunit)) {
310 case SND_DEV_DSPHW_PLAY:
311 case SND_DEV_DSPHW_VPLAY:
312 if (direction != PCMDIR_PLAY)
313 return (EOPNOTSUPP);

--- 7 unchanged lines hidden (view full) ---

321 if (!(direction == PCMDIR_PLAY ||
322 direction == PCMDIR_REC))
323 return (EOPNOTSUPP);
324 break;
325 }
326 }
327
328retry_chnalloc:
329 err = ENODEV;
330 /* scan for a free channel */
331 CHN_FOREACH(c, d, channels.pcm) {
332 CHN_LOCK(c);
333 if (c->direction == direction && !(c->flags & CHN_F_BUSY) &&
334 (devunit == -1 || devunit == -2 || c->unit == devunit)) {
335 c->flags |= CHN_F_BUSY;
336 c->pid = pid;
337 *ch = c;

--- 8 unchanged lines hidden (view full) ---

346 CHN_UNLOCK(c);
347 return (err);
348 } else if ((devunit == -1 || devunit == -2) &&
349 c->direction == direction && (c->flags & CHN_F_BUSY))
350 err = EBUSY;
351 CHN_UNLOCK(c);
352 }
353
354 /* no channel available */
355 if (devunit == -1 || (devunit != -2 &&
356 (snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY ||
357 snd_unit2d(devunit) == SND_DEV_DSPHW_VREC))) {
358 if (direction == PCMDIR_PLAY)
359 vchancount = d->pvchancount;
360 else
361 vchancount = d->rvchancount;
362 if (!(vchancount > 0 && vchancount < snd_maxautovchans) &&
363 (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans))
364 return (err);
365 err = pcm_setvchans(d, direction, vchancount + 1,

--- 7 unchanged lines hidden (view full) ---

373
374 return (err);
375}
376
377/* release a locked channel and unlock it */
378int
379pcm_chnrelease(struct pcm_channel *c)
380{
381 CHN_LOCKASSERT(c);
382 c->flags &= ~CHN_F_BUSY;
383 c->pid = -1;
384 CHN_UNLOCK(c);
385 return 0;
386}
387
388int
389pcm_chnref(struct pcm_channel *c, int ref)
390{
391 int r;
392
393 CHN_LOCKASSERT(c);
394 c->refcount += ref;
395 r = c->refcount;
396 return r;
397}
398
399int
400pcm_inprog(struct snddev_info *d, int delta)
401{
402 int r;
403
404 if (delta == 0)
405 return d->inprog;
406
407 /* backtrace(); */
408 pcm_lock(d);
409 d->inprog += delta;
410 r = d->inprog;
411 pcm_unlock(d);
412 return r;
413}
414
415static void
416pcm_setmaxautovchans(struct snddev_info *d, int num)
417{
418 if (num < 0)
419 return;
420
421 if (num >= 0 && d->pvchancount > num)
422 (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1);
423 else if (num > 0 && d->pvchancount == 0)
424 (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1);
425
426 if (num >= 0 && d->rvchancount > num)
427 (void)pcm_setvchans(d, PCMDIR_REC, num, -1);
428 else if (num > 0 && d->rvchancount == 0)
429 (void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
430
431 pcm_lock(d);
432 pcm_clonereset(d);
433 pcm_unlock(d);
434}
435
436#ifdef USING_DEVFS
437static int
438sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
439{
440 struct snddev_info *d;
441 int error, unit;
442
443 unit = snd_unit;
444 error = sysctl_handle_int(oidp, &unit, 0, req);
445 if (error == 0 && req->newptr != NULL) {
446 d = devclass_get_softc(pcm_devclass, unit);
447 if (d == NULL || CHN_EMPTY(d, channels.pcm))
448 return EINVAL;
449 snd_unit = unit;
450 }
451 return (error);
452}
453/* XXX: do we need a way to let the user change the default unit? */
454SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
455 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device");

--- 11 unchanged lines hidden (view full) ---

467 if (v < 0)
468 v = 0;
469 if (v > SND_MAXVCHANS)
470 v = SND_MAXVCHANS;
471 snd_maxautovchans = v;
472 for (i = 0; pcm_devclass != NULL &&
473 i < devclass_get_maxunit(pcm_devclass); i++) {
474 d = devclass_get_softc(pcm_devclass, i);
475 if (d == NULL)
476 continue;
477 pcm_setmaxautovchans(d, v);
478 }
479 }
480 return (error);
481}
482SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
483 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel");
484
485struct pcm_channel *
486pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo)
487{
488 struct pcm_channel *ch;
489 int direction, err, rpnum, *pnum, max;
490 int udc, device, chan;
491 char *dirs, *devname, buf[CHN_NAMELEN];
492
493 KASSERT(num >= -1, ("invalid num=%d", num));
494
495 pcm_lock(d);
496
497 switch(dir) {
498 case PCMDIR_PLAY:
499 dirs = "play";
500 direction = PCMDIR_PLAY;
501 pnum = &d->playcount;
502 device = SND_DEV_DSPHW_PLAY;
503 max = SND_MAXHWCHAN;
504 break;
505 case PCMDIR_PLAY_VIRTUAL:

--- 13 unchanged lines hidden (view full) ---

519 case PCMDIR_REC_VIRTUAL:
520 dirs = "virtual";
521 direction = PCMDIR_REC;
522 pnum = &d->rvchancount;
523 device = SND_DEV_DSPHW_VREC;
524 max = SND_MAXVCHANS;
525 break;
526 default:
527 pcm_unlock(d);
528 return NULL;
529 }
530
531 chan = (num == -1) ? 0 : num;
532
533 if (*pnum >= max || chan >= max) {
534 pcm_unlock(d);
535 return NULL;
536 }
537
538 rpnum = 0;
539
540 CHN_FOREACH(ch, d, channels.pcm) {
541 if (CHN_DEV(ch) != device)
542 continue;
543 if (chan == CHN_CHAN(ch)) {
544 if (num != -1) {
545 device_printf(d->dev,
546 "channel num=%d allocated!\n", chan);
547 pcm_unlock(d);
548 return NULL;
549 }
550 chan++;
551 if (chan >= max) {
552 device_printf(d->dev,
553 "chan=%d > %d\n", chan, max);
554 pcm_unlock(d);
555 return NULL;
556 }
557 }
558 rpnum++;
559 }
560
561 if (*pnum != rpnum) {
562 device_printf(d->dev,
563 "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
564 __func__, dirs, *pnum, rpnum);
565 pcm_unlock(d);
566 return NULL;
567 }
568
569 udc = snd_mkunit(device_get_unit(d->dev), device, chan);
570 devname = dsp_unit2name(buf, sizeof(buf), udc);
571
572 if (devname == NULL) {
573 device_printf(d->dev,
574 "Failed to query device name udc=0x%08x\n", udc);
575 pcm_unlock(d);
576 return NULL;
577 }
578
579 (*pnum)++;
580 pcm_unlock(d);
581
582 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
583 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO);
584 ch->unit = udc;
585 ch->pid = -1;
586 ch->parentsnddev = d;
587 ch->parentchannel = parent;
588 ch->dev = d->dev;
589 ch->trigger = PCMTRIG_STOP;
590 snprintf(ch->name, sizeof(ch->name), "%s:%s:%s",
591 device_get_nameunit(ch->dev), dirs, devname);
592
593 err = chn_init(ch, devinfo, dir, direction);
594 if (err) {
595 device_printf(d->dev, "chn_init(%s) failed: err = %d\n",
596 ch->name, err);
597 kobj_delete(ch->methods, M_DEVBUF);
598 free(ch, M_DEVBUF);
599 pcm_lock(d);
600 (*pnum)--;
601 pcm_unlock(d);
602
603 return NULL;
604 }
605
606 return ch;
607}
608
609int
610pcm_chn_destroy(struct pcm_channel *ch)
611{
612 struct snddev_info *d;
613 int err;
614
615 d = ch->parentsnddev;
616 err = chn_kill(ch);
617 if (err) {
618 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
619 return err;
620 }
621
622 kobj_delete(ch->methods, M_DEVBUF);
623 free(ch, M_DEVBUF);
624
625 return 0;
626}
627
628int
629pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
630{
631 struct pcm_channel *tmp, *after;
632 int num;
633
634 pcm_lock(d);
635
636 KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY ||
637 ch->direction == PCMDIR_REC), ("Invalid pcm channel"));
638
639 after = NULL;
640 tmp = NULL;
641 num = 0;
642
643 /*
644 * Look for possible device collision.
645 */
646 CHN_FOREACH(tmp, d, channels.pcm) {
647 if (tmp->unit == ch->unit) {
648 device_printf(d->dev, "%s(): Device collision "
649 "old=%p new=%p devunit=0x%08x\n",
650 __func__, tmp, ch, ch->unit);
651 pcm_unlock(d);
652 return ENODEV;
653 }
654 if (CHN_DEV(tmp) < CHN_DEV(ch)) {
655 if (num == 0)
656 after = tmp;
657 continue;
658 } else if (CHN_DEV(tmp) > CHN_DEV(ch))
659 break;
660 num++;

--- 4 unchanged lines hidden (view full) ---

665 }
666
667 if (after != NULL) {
668 CHN_INSERT_AFTER(after, ch, channels.pcm);
669 } else {
670 CHN_INSERT_HEAD(d, ch, channels.pcm);
671 }
672
673 d->devcount++;
674 pcm_unlock(d);
675
676 return 0;
677}
678
679int
680pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
681{
682 struct pcm_channel *tmp;
683
684 tmp = NULL;
685
686 CHN_FOREACH(tmp, d, channels.pcm) {
687 if (tmp == ch)
688 break;
689 }
690
691 if (tmp != ch)
692 return EINVAL;
693
694 CHN_REMOVE(d, ch, channels.pcm);
695 switch (CHN_DEV(ch)) {
696 case SND_DEV_DSPHW_PLAY:
697 d->playcount--;
698 break;
699 case SND_DEV_DSPHW_VPLAY:
700 d->pvchancount--;
701 break;
702 case SND_DEV_DSPHW_REC:
703 d->reccount--;
704 break;
705 case SND_DEV_DSPHW_VREC:
706 d->rvchancount--;
707 break;
708 default:
709 break;
710 }
711
712 return 0;
713}
714
715int
716pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
717{
718 struct snddev_info *d = device_get_softc(dev);
719 struct pcm_channel *ch;
720 int err;
721
722 ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo);
723 if (!ch) {
724 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
725 return ENODEV;
726 }
727
728 err = pcm_chn_add(d, ch);
729 if (err) {
730 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
731 pcm_chn_destroy(ch);
732 return err;
733 }
734
735 return err;
736}
737
738static int
739pcm_killchan(device_t dev)
740{
741 struct snddev_info *d = device_get_softc(dev);
742 struct pcm_channel *ch;
743 int error = 0;
744
745 ch = CHN_FIRST(d, channels.pcm);
746
747 error = pcm_chn_remove(d, ch);
748 if (error)
749 return (error);
750 return (pcm_chn_destroy(ch));
751}
752
753int
754pcm_setstatus(device_t dev, char *str)
755{
756 struct snddev_info *d = device_get_softc(dev);
757
758 pcm_setmaxautovchans(d, snd_maxautovchans);
759
760 pcm_lock(d);
761
762 strlcpy(d->status, str, SND_STATUSLEN);
763
764 /* Last stage, enable cloning. */
765 if (d->clones != NULL) {
766 (void)snd_clone_enable(d->clones);
767 }
768
769 pcm_unlock(d);
770
771 return 0;
772}
773
774uint32_t
775pcm_getflags(device_t dev)
776{
777 struct snddev_info *d = device_get_softc(dev);
778
779 return d->flags;

--- 49 unchanged lines hidden (view full) ---

829static int
830sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS)
831{
832 struct snddev_info *d;
833 uint32_t flags;
834 int err;
835
836 d = oidp->oid_arg1;
837 if (d == NULL || d->clones == NULL)
838 return (ENODEV);
839
840 pcm_lock(d);
841 flags = snd_clone_getflags(d->clones);
842 pcm_unlock(d);
843 err = sysctl_handle_int(oidp, &flags, 0, req);
844
845 if (err == 0 && req->newptr != NULL) {
846 if ((flags & ~SND_CLONE_MASK))
847 err = EINVAL;
848 else {
849 pcm_lock(d);
850 (void)snd_clone_setflags(d->clones, flags);
851 pcm_unlock(d);
852 }
853 }
854
855 return (err);
856}
857
858static int
859sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS)
860{
861 struct snddev_info *d;
862 int err, deadline;
863
864 d = oidp->oid_arg1;
865 if (d == NULL || d->clones == NULL)
866 return (ENODEV);
867
868 pcm_lock(d);
869 deadline = snd_clone_getdeadline(d->clones);
870 pcm_unlock(d);
871 err = sysctl_handle_int(oidp, &deadline, 0, req);
872
873 if (err == 0 && req->newptr != NULL) {
874 if (deadline < 0)
875 err = EINVAL;
876 else {
877 pcm_lock(d);
878 (void)snd_clone_setdeadline(d->clones, deadline);
879 pcm_unlock(d);
880 }
881 }
882
883 return (err);
884}
885
886static int
887sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS)
888{
889 struct snddev_info *d;
890 int err, val;
891
892 d = oidp->oid_arg1;
893 if (d == NULL || d->clones == NULL)
894 return (ENODEV);
895
896 val = 0;
897 err = sysctl_handle_int(oidp, &val, 0, req);
898
899 if (err == 0 && req->newptr != NULL && val != 0) {
900 pcm_lock(d);
901 (void)snd_clone_gc(d->clones);
902 pcm_unlock(d);
903 }
904
905 return (err);
906}
907
908static int
909sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS)
910{
911 struct snddev_info *d;
912 int i, err, val;
913
914 val = 0;
915 err = sysctl_handle_int(oidp, &val, 0, req);
916
917 if (err == 0 && req->newptr != NULL && val != 0) {
918 for (i = 0; pcm_devclass != NULL &&
919 i < devclass_get_maxunit(pcm_devclass); i++) {
920 d = devclass_get_softc(pcm_devclass, i);
921 if (d == NULL || d->clones == NULL)
922 continue;
923 pcm_lock(d);
924 (void)snd_clone_gc(d->clones);
925 pcm_unlock(d);
926 }
927 }
928
929 return (err);
930}
931SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW,
932 0, sizeof(int), sysctl_hw_snd_clone_gc, "I",
933 "global clone garbage collector");

--- 14 unchanged lines hidden (view full) ---

948 device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n",
949 device_get_unit(dev), PCMMAXUNIT);
950 device_printf(dev,
951 "Use 'hw.snd.maxunit' tunable to raise the limit.\n");
952 return ENODEV;
953 }
954
955 d = device_get_softc(dev);
956 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
957
958#if 0
959 /*
960 * d->flags should be cleared by the allocator of the softc.
961 * We cannot clear this field here because several devices set
962 * this flag before calling pcm_register().
963 */
964 d->flags = 0;
965#endif
966 d->dev = dev;
967 d->devinfo = devinfo;
968 d->devcount = 0;
969 d->reccount = 0;
970 d->playcount = 0;
971 d->pvchancount = 0;
972 d->rvchancount = 0;
973 d->pvchanrate = 0;
974 d->pvchanformat = 0;
975 d->rvchanrate = 0;
976 d->rvchanformat = 0;
977 d->inprog = 0;
978
979 /*
980 * Create clone manager, disabled by default. Cloning will be
981 * enabled during final stage of driver iniialization through
982 * pcm_setstatus().
983 */
984 d->clones = snd_clone_create(
985#ifdef SND_DIAGNOSTIC
986 d->lock,
987#endif
988 SND_U_MASK | SND_D_MASK, PCMMAXCLONE, SND_CLONE_DEADLINE_DEFAULT,
989 SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF |
990 SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED);
991
992 if (bootverbose != 0 || snd_verbose > 3) {
993 pcm_lock(d);
994 device_printf(dev,
995 "clone manager: deadline=%dms flags=0x%08x\n",
996 snd_clone_getdeadline(d->clones),
997 snd_clone_getflags(d->clones));
998 pcm_unlock(d);
999 }
1000
1001 CHN_INIT(d, channels.pcm);
1002 CHN_INIT(d, channels.pcm.busy);
1003
1004 if ((numplay == 0 || numrec == 0) && numplay != numrec)
1005 d->flags |= SD_F_SIMPLEX;
1006
1007 d->fakechan = fkchan_setup(dev);
1008 chn_init(d->fakechan, NULL, 0, 0);
1009
1010#ifdef SND_DYNSYSCTL
1011 sysctl_ctx_init(&d->play_sysctl_ctx);

--- 22 unchanged lines hidden (view full) ---

1034 "clone expiration deadline (ms)");
1035 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
1036 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
1037 "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
1038 sysctl_dev_pcm_clone_gc, "I",
1039 "clone garbage collector");
1040#endif
1041#endif
1042 if (numplay > 0 || numrec > 0) {
1043 d->flags |= SD_F_AUTOVCHAN;
1044 vchan_initsys(dev);
1045 }
1046
1047 sndstat_register(dev, d->status, sndstat_prepare_pcm);
1048 return 0;
1049}
1050
1051int
1052pcm_unregister(device_t dev)
1053{
1054 struct snddev_info *d = device_get_softc(dev);
1055 struct pcm_channel *ch;
1056 struct thread *td;
1057 int i;
1058
1059 td = curthread;
1060
1061 if (sndstat_acquire(td) != 0) {
1062 device_printf(dev, "unregister: sndstat busy\n");
1063 return EBUSY;
1064 }
1065
1066 pcm_lock(d);
1067 if (d->inprog) {
1068 device_printf(dev, "unregister: operation in progress\n");
1069 pcm_unlock(d);
1070 sndstat_release(td);
1071 return EBUSY;
1072 }
1073
1074 CHN_FOREACH(ch, d, channels.pcm) {
1075 if (ch->refcount > 0) {
1076 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
1077 pcm_unlock(d);
1078 sndstat_release(td);
1079 return EBUSY;
1080 }
1081 }
1082
1083 if (d->clones != NULL) {
1084 if (snd_clone_busy(d->clones) != 0) {
1085 device_printf(dev, "unregister: clone busy\n");
1086 pcm_unlock(d);
1087 sndstat_release(td);
1088 return EBUSY;
1089 } else
1090 (void)snd_clone_disable(d->clones);
1091 }
1092
1093 if (mixer_uninit(dev) == EBUSY) {
1094 device_printf(dev, "unregister: mixer busy\n");
1095 if (d->clones != NULL)
1096 (void)snd_clone_enable(d->clones);
1097 pcm_unlock(d);
1098 sndstat_release(td);
1099 return EBUSY;
1100 }
1101
1102 if (d->clones != NULL) {
1103 snd_clone_destroy(d->clones);
1104 d->clones = NULL;
1105 }
1106
1107 d->devcount = 0;
1108
1109#ifdef SND_DYNSYSCTL
1110 if (d->play_sysctl_tree != NULL) {
1111 sysctl_ctx_free(&d->play_sysctl_ctx);
1112 d->play_sysctl_tree = NULL;
1113 }
1114 if (d->rec_sysctl_tree != NULL) {
1115 sysctl_ctx_free(&d->rec_sysctl_ctx);
1116 d->rec_sysctl_tree = NULL;
1117 }
1118#endif
1119
1120 while (!CHN_EMPTY(d, channels.pcm))
1121 pcm_killchan(dev);
1122
1123 chn_kill(d->fakechan);
1124 fkchan_kill(d->fakechan);
1125
1126 pcm_unlock(d);
1127 snd_mtxfree(d->lock);
1128 sndstat_unregister(dev);
1129 sndstat_release(td);
1130
1131 if (snd_unit == device_get_unit(dev)) {
1132 /*
1133 * Reassign default unit to the next available dev.
1134 */
1135 for (i = 0; pcm_devclass != NULL &&
1136 i < devclass_get_maxunit(pcm_devclass); i++) {
1137 if (device_get_unit(dev) == i ||
1138 devclass_get_softc(pcm_devclass, i) == NULL)
1139 continue;
1140 snd_unit = i;
1141 break;
1142 }
1143 }
1144
1145 return 0;
1146}
1147
1148/************************************************************************/
1149
1150static int
1151sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
1152{
1153 struct snddev_info *d;
1154 struct pcm_channel *c;
1155 struct pcm_feeder *f;
1156
1157 if (verbose < 1)
1158 return 0;
1159
1160 d = device_get_softc(dev);
1161 if (!d)
1162 return ENXIO;
1163
1164 pcm_lock(d);
1165 if (!CHN_EMPTY(d, channels.pcm)) {
1166 sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)",
1167 d->playcount, d->pvchancount,
1168 d->reccount, d->rvchancount,
1169 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
1170#ifdef USING_DEVFS
1171 (device_get_unit(dev) == snd_unit)? " default" : ""
1172#else
1173 ""
1174#endif
1175 );
1176
1177 if (verbose <= 1) {
1178 pcm_unlock(d);
1179 return 0;
1180 }
1181
1182 CHN_FOREACH(c, d, channels.pcm) {
1183
1184 KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1185 ("hosed pcm channel setup"));
1186
1187 sbuf_printf(s, "\n\t");
1188
1189 /* it would be better to indent child channels */
1190 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
1191 sbuf_printf(s, "spd %d", c->speed);
1192 if (c->speed != sndbuf_getspd(c->bufhard))
1193 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
1194 sbuf_printf(s, ", fmt 0x%08x", c->format);
1195 if (c->format != sndbuf_getfmt(c->bufhard))
1196 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1197 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
1198 if (c->pid != -1)
1199 sbuf_printf(s, ", pid %d", c->pid);
1200 sbuf_printf(s, "\n\t");
1201
1202 sbuf_printf(s, "interrupts %d, ", c->interrupts);
1203 if (c->direction == PCMDIR_REC)
1204 sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1205 c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1206 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1207 sndbuf_getblkcnt(c->bufhard),
1208 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1209 sndbuf_getblkcnt(c->bufsoft));
1210 else
1211 sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1212 c->xruns, c->feedcount, sndbuf_getready(c->bufsoft),
1213 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1214 sndbuf_getblkcnt(c->bufhard),
1215 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1216 sndbuf_getblkcnt(c->bufsoft));
1217 sbuf_printf(s, "\n\t");
1218
1219 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
1220 sbuf_printf(s, " -> ");
1221 f = c->feeder;
1222 while (f->source != NULL)
1223 f = f->source;
1224 while (f != NULL) {
1225 sbuf_printf(s, "%s", f->class->name);
1226 if (f->desc->type == FEEDER_FMT)
1227 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
1228 if (f->desc->type == FEEDER_RATE)
1229 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1230 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1231 f->desc->type == FEEDER_VOLUME)
1232 sbuf_printf(s, "(0x%08x)", f->desc->out);
1233 sbuf_printf(s, " -> ");
1234 f = f->parent;
1235 }
1236 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
1237 }
1238 } else
1239 sbuf_printf(s, " (mixer only)");
1240 pcm_unlock(d);
1241
1242 return 0;
1243}
1244
1245/************************************************************************/
1246
1247#ifdef SND_DYNSYSCTL
1248int
1249sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
1250{
1251 struct snddev_info *d;
1252 int direction, vchancount;
1253 int err, newcnt;
1254
1255 d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1));
1256 if (d == NULL)
1257 return EINVAL;
1258
1259 switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) {
1260 case VCHAN_PLAY:
1261 if (d->playcount < 1)
1262 return ENODEV;
1263 direction = PCMDIR_PLAY;
1264 vchancount = d->pvchancount;
1265 break;
1266 case VCHAN_REC:
1267 if (d->reccount < 1)
1268 return ENODEV;
1269 direction = PCMDIR_REC;
1270 vchancount = d->rvchancount;
1271 break;
1272 default:
1273 return EINVAL;
1274 break;
1275 }
1276
1277 newcnt = vchancount;
1278 err = sysctl_handle_int(oidp, &newcnt, 0, req);
1279
1280 if (err == 0 && req->newptr != NULL && vchancount != newcnt) {
1281 if (newcnt < 0)
1282 newcnt = 0;
1283 if (newcnt > SND_MAXVCHANS)
1284 newcnt = SND_MAXVCHANS;
1285 err = pcm_setvchans(d, direction, newcnt, -1);
1286 }
1287
1288 return err;
1289}
1290#endif
1291
1292/************************************************************************/
1293
1294/**
1295 * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl.

--- 31 unchanged lines hidden (view full) ---

1327
1328 /*
1329 * Iterate over PCM devices and their channels, gathering up data
1330 * for the numaudios, ncards, and openedaudio fields.
1331 */
1332 si->numaudios = 0;
1333 bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
1334
1335 if (pcm_devclass != NULL) {
1336 j = 0;
1337
1338 for (i = 0; pcm_devclass != NULL &&
1339 i < devclass_get_maxunit(pcm_devclass); i++) {
1340 d = devclass_get_softc(pcm_devclass, i);
1341 if (!d)
1342 continue;
1343
1344 /* See note in function's docblock */
1345 mtx_assert(d->lock, MA_NOTOWNED);
1346 /* Increment device's "operations in progress" */
1347 pcm_inprog(d, 1);
1348 pcm_lock(d);
1349
1350 si->numaudios += d->devcount;
1351 ++ncards;
1352
1353 CHN_FOREACH(c, d, channels.pcm) {
1354 mtx_assert(c->lock, MA_NOTOWNED);
1355 CHN_LOCK(c);
1356 if (c->flags & CHN_F_BUSY)
1357 si->openedaudio[j / intnbits] |=
1358 (1 << (j % intnbits));
1359 CHN_UNLOCK(c);
1360 j++;
1361 }
1362
1363 pcm_unlock(d);
1364 pcm_inprog(d, -1);
1365 }
1366 }
1367
1368 si->numsynths = 0; /* OSSv4 docs: this field is obsolete */
1369 /**
1370 * @todo Collect num{midis,timers}.
1371 *
1372 * Need access to sound/midi/midi.c::midistat_lock in order
1373 * to safely touch midi_devices and get a head count of, well,

--- 64 unchanged lines hidden ---