--- sys/dev/sound/clone.c.orig Sun Jun 3 01:36:11 2007 +++ sys/dev/sound/clone.c Mon Jun 4 17:30:57 2007 @@ -775,7 +775,8 @@ * give up locking for further setup. Unit magic is set right here * to avoid collision with other contesting handler. */ - ce = malloc(sizeof(*ce), M_DEVBUF, M_NOWAIT | M_ZERO); + ce = malloc(sizeof(*ce), M_DEVBUF, + ((c->flags & SND_CLONE_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO); if (ce == NULL) { if (*unit != -1) return (NULL); --- sys/dev/sound/clone.h.orig Fri Jun 1 02:35:24 2007 +++ sys/dev/sound/clone.h Sun Jun 3 21:18:07 2007 @@ -56,6 +56,8 @@ * handler has been expired. * SND_CLONE_GC_REVOKE - Revoke clone invocation status which has been * expired instead of removing and freeing it. + * SND_CLONE_WAITOK - malloc() is allowed to sleep while allocating + * clone entry. */ #define SND_CLONE_ENABLE 0x00000001 #define SND_CLONE_GC_ENABLE 0x00000002 @@ -63,12 +65,14 @@ #define SND_CLONE_GC_LASTREF 0x00000008 #define SND_CLONE_GC_EXPIRED 0x00000010 #define SND_CLONE_GC_REVOKE 0x00000020 +#define SND_CLONE_WAITOK 0x80000000 #define SND_CLONE_GC_MASK (SND_CLONE_GC_ENABLE | \ SND_CLONE_GC_UNREF | \ SND_CLONE_GC_LASTREF | \ SND_CLONE_GC_EXPIRED | \ - SND_CLONE_GC_REVOKE) + SND_CLONE_GC_REVOKE | \ + SND_CLONE_WAITOK) #define SND_CLONE_MASK (SND_CLONE_ENABLE | SND_CLONE_GC_MASK) --- sys/dev/sound/pci/es137x.c.orig Thu Apr 19 02:26:39 2007 +++ sys/dev/sound/pci/es137x.c Mon Jun 4 16:16:56 2007 @@ -1480,14 +1480,14 @@ struct es_info *es; struct snddev_info *d; struct snd_mixer *m; - struct cdev *i_dev; device_t dev; uint32_t val, set; int recsrc, level, err; dev = oidp->oid_arg1; d = device_get_softc(dev); - if (d == NULL || d->mixer_dev == NULL || d->mixer_dev->si_drv1 == NULL) + if (!PCM_REGISTERED(d) || d->mixer_dev == NULL || + d->mixer_dev->si_drv1 == NULL) return (EINVAL); es = d->devinfo; if (es == NULL) @@ -1504,43 +1504,47 @@ return (EINVAL); if (val == set) return (0); - i_dev = d->mixer_dev; - if (mixer_ioctl(i_dev, 0, (caddr_t)&recsrc, 0, NULL) != EBADF) - return (EBUSY); - err = mixer_ioctl(i_dev, MIXER_READ(SOUND_MIXER_PCM), (caddr_t)&level, - -1, NULL); - if (!err) - err = mixer_ioctl(i_dev, MIXER_READ(SOUND_MIXER_RECSRC), - (caddr_t)&recsrc, -1, NULL); - if (err) - return (err); - if (level < 0) - return (EINVAL); + pcm_lock(d); + PCM_CV_WAIT(d); + PCM_CV_ACQUIRE(d); + pcm_unlock(d); + m = (d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 : NULL; + if (m == NULL) { + err = ENODEV; + goto es137x_single_pcm_mixer_out; + } + if (mixer_busy(m) != 0) { + err = EBUSY; + goto es137x_single_pcm_mixer_out; + } + level = mix_get(m, SOUND_MIXER_PCM); + recsrc = mix_getrecsrc(m); + if (level < 0 || recsrc < 0) { + err = ENXIO; + goto es137x_single_pcm_mixer_out; + } ES_LOCK(es); if (es->ctrl & (CTRL_ADC_EN | CTRL_DAC1_EN | CTRL_DAC2_EN)) { ES_UNLOCK(es); - return (EBUSY); + err = EBUSY; + goto es137x_single_pcm_mixer_out; } if (val) es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 1); else es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 0); ES_UNLOCK(es); - m = i_dev->si_drv1; if (!val) { - mix_setdevs(m, mix_getdevs(d->mixer_dev->si_drv1) | - (1 << SOUND_MIXER_SYNTH)); - mix_setrecdevs(m, mix_getrecdevs(d->mixer_dev->si_drv1) | - (1 << SOUND_MIXER_SYNTH)); - err = mixer_ioctl(i_dev, MIXER_WRITE(SOUND_MIXER_SYNTH), - (caddr_t)&level, -1, NULL); + mix_setdevs(m, mix_getdevs(m) | (1 << SOUND_MIXER_SYNTH)); + mix_setrecdevs(m, mix_getrecdevs(m) | (1 << SOUND_MIXER_SYNTH)); + err = mix_set(m, SOUND_MIXER_SYNTH, level & 0x7f, + (level >> 8) & 0x7f); } else { - err = mixer_ioctl(i_dev, MIXER_WRITE(SOUND_MIXER_SYNTH), - (caddr_t)&level, -1, NULL); - mix_setdevs(m, mix_getdevs(d->mixer_dev->si_drv1) & - ~(1 << SOUND_MIXER_SYNTH)); - mix_setrecdevs(m, mix_getrecdevs(d->mixer_dev->si_drv1) & + err = mix_set(m, SOUND_MIXER_SYNTH, level & 0x7f, + (level >> 8) & 0x7f); + mix_setdevs(m, mix_getdevs(m) & ~(1 << SOUND_MIXER_SYNTH)); + mix_setrecdevs(m, mix_getrecdevs(m) & ~(1 << SOUND_MIXER_SYNTH)); } if (!err) { @@ -1550,10 +1554,15 @@ else if (recsrc & (1 << SOUND_MIXER_SYNTH)) recsrc |= 1 << SOUND_MIXER_PCM; if (level != recsrc) - err = mixer_ioctl(i_dev, - MIXER_WRITE(SOUND_MIXER_RECSRC), - (caddr_t)&recsrc, -1, NULL); + err = mix_setrecsrc(m, recsrc); } + +es137x_single_pcm_mixer_out: + + pcm_lock(d); + PCM_CV_RELEASE(d); + pcm_unlock(d); + return (err); } --- sys/dev/sound/pcm/channel.c.orig Fri Jun 1 02:43:32 2007 +++ sys/dev/sound/pcm/channel.c Mon Jun 4 01:19:52 2007 @@ -39,20 +39,10 @@ #define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1)) #endif -#define CHN_STARTED(c) ((c)->flags & CHN_F_TRIGGERED) -#define CHN_STOPPED(c) (!CHN_STARTED(c)) -#define CHN_DIRSTR(c) (((c)->direction == PCMDIR_PLAY) ? \ - "PCMDIR_PLAY" : "PCMDIR_REC") - -#define BUF_PARENT(c, b) \ +#define CHN_BUF_PARENT(c, b) \ (((c) != NULL && (c)->parentchannel != NULL && \ (c)->parentchannel->bufhard != NULL) ? \ (c)->parentchannel->bufhard : (b)) - -#define CHN_TIMEOUT 5 -#define CHN_TIMEOUT_MIN 1 -#define CHN_TIMEOUT_MAX 10 - /* #define DEB(x) x */ @@ -653,7 +643,7 @@ } else { struct snd_dbuf *pb; - pb = BUF_PARENT(c, b); + pb = CHN_BUF_PARENT(c, b); i = sndbuf_xbytes(sndbuf_getready(bs), bs, pb); j = sndbuf_getbps(pb); } @@ -738,7 +728,7 @@ return 0; } - b = BUF_PARENT(c, c->bufhard); + b = CHN_BUF_PARENT(c, c->bufhard); minflush = threshold + sndbuf_xbytes(sndbuf_getready(b), b, bs); @@ -1501,7 +1491,7 @@ } if (c->parentchannel != NULL) { - pb = BUF_PARENT(c, NULL); + pb = CHN_BUF_PARENT(c, NULL); CHN_UNLOCK(c); chn_notify(c->parentchannel, CHN_N_BLOCKSIZE); CHN_LOCK(c); @@ -1891,6 +1881,7 @@ { struct feeder_class *fc; struct pcm_feederdesc desc; + struct snd_mixer *m; u_int32_t tmp[2], type, flags, hwfmt, *fmtlist; int err; char fmtstr[AFMTSTR_MAXSZ]; @@ -1942,18 +1933,25 @@ } else return EOPNOTSUPP; + /* XXX These are too much.. */ + if (c->parentsnddev != NULL && c->parentsnddev->mixer_dev != NULL && + c->parentsnddev->mixer_dev->si_drv1 != NULL) + m = c->parentsnddev->mixer_dev->si_drv1; + else + m = NULL; + c->feederflags &= ~(1 << FEEDER_VOLUME); - if (c->direction == PCMDIR_PLAY && - !(c->flags & CHN_F_VIRTUAL) && c->parentsnddev && - (c->parentsnddev->flags & SD_F_SOFTPCMVOL) && - c->parentsnddev->mixer_dev) + if (c->direction == PCMDIR_PLAY && !(c->flags & CHN_F_VIRTUAL) && m && + (c->parentsnddev->flags & SD_F_SOFTPCMVOL)) c->feederflags |= 1 << FEEDER_VOLUME; + if (!(c->flags & CHN_F_VIRTUAL) && c->parentsnddev && ((c->direction == PCMDIR_PLAY && (c->parentsnddev->flags & SD_F_PSWAPLR)) || (c->direction == PCMDIR_REC && (c->parentsnddev->flags & SD_F_RSWAPLR)))) c->feederflags |= 1 << FEEDER_SWAPLR; + flags = c->feederflags; fmtlist = chn_getcaps(c)->fmtlist; @@ -2066,34 +2064,27 @@ sndbuf_setfmt(c->bufhard, hwfmt); if ((flags & (1 << FEEDER_VOLUME))) { - u_int32_t parent = SOUND_MIXER_NONE; + u_int32_t parent; int vol, left, right; - vol = 100 | (100 << 8); - CHN_UNLOCK(c); - /* - * XXX This is ugly! The way mixer subs being so secretive - * about its own internals force us to use this silly - * monkey trick. - */ - if (mixer_ioctl(c->parentsnddev->mixer_dev, - MIXER_READ(SOUND_MIXER_PCM), (caddr_t)&vol, -1, NULL) != 0) - device_printf(c->dev, "Soft PCM Volume: Failed to read default value\n"); + vol = mix_get(m, SOUND_MIXER_PCM); + if (vol == -1) { + device_printf(c->dev, + "Soft PCM Volume: Failed to read default value\n"); + vol = 100 | (100 << 8); + } left = vol & 0x7f; right = (vol >> 8) & 0x7f; - if (c->parentsnddev != NULL && - c->parentsnddev->mixer_dev != NULL && - c->parentsnddev->mixer_dev->si_drv1 != NULL) - parent = mix_getparent( - c->parentsnddev->mixer_dev->si_drv1, - SOUND_MIXER_PCM); + parent = mix_getparent(m, SOUND_MIXER_PCM); if (parent != SOUND_MIXER_NONE) { - vol = 100 | (100 << 8); - if (mixer_ioctl(c->parentsnddev->mixer_dev, - MIXER_READ(parent), - (caddr_t)&vol, -1, NULL) != 0) - device_printf(c->dev, "Soft Volume: Failed to read parent default value\n"); + vol = mix_get(m, parent); + if (vol == -1) { + device_printf(c->dev, + "Soft Volume: Failed to read parent " + "default value\n"); + vol = 100 | (100 << 8); + } left = (left * (vol & 0x7f)) / 100; right = (right * ((vol >> 8) & 0x7f)) / 100; } --- sys/dev/sound/pcm/channel.h.orig Fri Jun 1 02:43:32 2007 +++ sys/dev/sound/pcm/channel.h Sun Jun 3 18:45:04 2007 @@ -331,6 +331,15 @@ #define CHN_LATENCY_PROFILE_MAX 1 #define CHN_LATENCY_PROFILE_DEFAULT CHN_LATENCY_PROFILE_MAX +#define CHN_STARTED(c) ((c)->flags & CHN_F_TRIGGERED) +#define CHN_STOPPED(c) (!CHN_STARTED(c)) +#define CHN_DIRSTR(c) (((c)->direction == PCMDIR_PLAY) ? \ + "PCMDIR_PLAY" : "PCMDIR_REC") + +#define CHN_TIMEOUT 5 +#define CHN_TIMEOUT_MIN 1 +#define CHN_TIMEOUT_MAX 10 + /* * This should be large enough to hold all pcm data between * tsleeps in chn_{read,write} at the highest sample rate. --- sys/dev/sound/pcm/dsp.c.orig Sun Jun 3 18:56:22 2007 +++ sys/dev/sound/pcm/dsp.c Mon Jun 4 17:45:13 2007 @@ -58,7 +58,6 @@ struct cdevsw dsp_cdevsw = { .d_version = D_VERSION, - .d_flags = D_NEEDGIANT, .d_open = dsp_open, .d_close = dsp_close, .d_read = dsp_read, @@ -252,20 +251,21 @@ struct pcm_channel *rdch, *wrch; struct snddev_info *d; uint32_t fmt, spd; - int i, error, devtype; - int wdevunit, rdevunit; + int i, error, devtype, wdevunit, rdevunit; /* Kind of impossible.. */ if (i_dev == NULL || td == NULL) - return ENODEV; + return (ENODEV); - /* This too.. */ d = dsp_get_info(i_dev); - if (d == NULL) - return EBADF; + if (!PCM_REGISTERED(d)) + return (EBADF); + + PCM_GIANT_ENTER(d); /* Lock snddev so nobody else can monkey with it. */ pcm_lock(d); + PCM_CV_WAIT(d); /* * Try to acquire cloned device before someone else pick it. @@ -274,15 +274,14 @@ error = snd_clone_acquire(i_dev); if (!(error == 0 || error == ENODEV)) { pcm_unlock(d); - return error; + goto dsp_open_out; } if (!DSP_F_VALID(flags)) error = EINVAL; else if (i_dev->si_drv1 != NULL) error = EBUSY; - else if (DSP_F_DUPLEX(flags) && - (dsp_get_flags(i_dev) & SD_F_SIMPLEX)) + else if (DSP_F_DUPLEX(flags) && (dsp_get_flags(i_dev) & SD_F_SIMPLEX)) error = ENOTSUP; else error = 0; @@ -290,7 +289,7 @@ if (error != 0) { (void)snd_clone_release(i_dev); pcm_unlock(d); - return error; + goto dsp_open_out; } /* @@ -319,7 +318,8 @@ (void)snd_clone_release(i_dev); PCMDEV_RELEASE(i_dev); pcm_unlock(d); - return ENOTSUP; + error = ENOTSUP; + goto dsp_open_out; } if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) wdevunit = dev2unit(i_dev); @@ -345,6 +345,7 @@ */ if (DSP_F_READ(flags)) { /* open for read */ + PCM_CV_ACQUIRE(d); pcm_unlock(d); error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, rdevunit); @@ -359,8 +360,9 @@ pcm_lock(d); (void)snd_clone_release(i_dev); PCMDEV_RELEASE(i_dev); + PCM_CV_RELEASE(d); pcm_unlock(d); - return error; + goto dsp_open_out; } if (flags & O_NONBLOCK) @@ -372,6 +374,7 @@ if (DSP_F_WRITE(flags)) { /* open for write */ + PCM_CV_ACQUIRE(d); pcm_unlock(d); error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, wdevunit); @@ -395,8 +398,9 @@ pcm_lock(d); (void)snd_clone_release(i_dev); PCMDEV_RELEASE(i_dev); + PCM_CV_RELEASE(d); pcm_unlock(d); - return error; + goto dsp_open_out; } if (flags & O_NONBLOCK) @@ -411,6 +415,7 @@ */ (void)snd_clone_ref(i_dev); + PCM_CV_RELEASE(d); pcm_unlock(d); /* @@ -419,7 +424,10 @@ */ dsp_cdevinfo_alloc(i_dev, rdch, wrch); - return 0; +dsp_open_out: + PCM_GIANT_EXIT(d); + + return (error); } static int @@ -427,12 +435,16 @@ { struct pcm_channel *rdch, *wrch; struct snddev_info *d; - int refs, sg_ids[2]; + int sg_ids[2], refs; d = dsp_get_info(i_dev); - if (d == NULL) - return EBADF; + if (!PCM_REGISTERED(d)) + return (EBADF); + + PCM_GIANT_ENTER(d); + pcm_lock(d); + PCM_CV_WAIT(d); rdch = PCM_RDCH(i_dev); wrch = PCM_WRCH(i_dev); @@ -444,6 +456,7 @@ if (rdch || wrch) { refs = 0; + PCM_CV_ACQUIRE(d); pcm_unlock(d); if (rdch) { /* @@ -506,6 +519,7 @@ (void)snd_clone_release(i_dev); (void)snd_clone_unref(i_dev); } + PCM_CV_RELEASE(d); } pcm_unlock(d); @@ -515,46 +529,78 @@ if (sg_ids[1]) free_unr(pcmsg_unrhdr, sg_ids[1]); - return 0; + PCM_GIANT_EXIT(d); + + return (0); } static int dsp_read(struct cdev *i_dev, struct uio *buf, int flag) { + struct snddev_info *d; struct pcm_channel *rdch, *wrch; int ret; + d = dsp_get_info(i_dev); + if (!PCM_REGISTERED(d)) + return (EBADF); + + PCM_GIANT_ENTER(d); + + rdch = NULL; + wrch = NULL; + getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD); - if (rdch == NULL || !(rdch->flags & CHN_F_BUSY)) - return EBADF; + if (rdch == NULL || !(rdch->flags & CHN_F_BUSY)) { + ret = EBADF; + goto dsp_read_out; + } if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) { relchns(i_dev, rdch, wrch, SD_F_PRIO_RD); - return EINVAL; + ret = EINVAL; + goto dsp_read_out; } if (!(rdch->flags & CHN_F_RUNNING)) rdch->flags |= CHN_F_RUNNING; ret = chn_read(rdch, buf); relchns(i_dev, rdch, wrch, SD_F_PRIO_RD); - return ret; +dsp_read_out: + PCM_GIANT_EXIT(d); + + return (ret); } static int dsp_write(struct cdev *i_dev, struct uio *buf, int flag) { + struct snddev_info *d; struct pcm_channel *rdch, *wrch; int ret; + d = dsp_get_info(i_dev); + if (!PCM_REGISTERED(d)) + return (EBADF); + + PCM_GIANT_ENTER(d); + + rdch = NULL; + wrch = NULL; + ret = 0; + getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR); - if (wrch == NULL || !(wrch->flags & CHN_F_BUSY)) - return EBADF; + if (wrch == NULL || !(wrch->flags & CHN_F_BUSY)) { + ret = EBADF; + goto dsp_write_out; + } if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) { relchns(i_dev, rdch, wrch, SD_F_PRIO_WR); - return EINVAL; + ret = EINVAL; + goto dsp_write_out; } if (!(wrch->flags & CHN_F_RUNNING)) wrch->flags |= CHN_F_RUNNING; @@ -571,36 +617,58 @@ relchns(i_dev, rdch, wrch, SD_F_PRIO_WR); - return ret; +dsp_write_out: + PCM_GIANT_EXIT(d); + + return (ret); } +#define DSP_IOCTL_CV_ACQUIRE(x) do { \ + pcm_lock(x); \ + PCM_CV_WAIT(x); \ + PCM_CV_ACQUIRE(x); \ + pcm_unlock(x); \ +} while(0) +#define DSP_IOCTL_CV_RELEASE(x) do { \ + pcm_lock(x); \ + PCM_CV_RELEASE(x); \ + pcm_unlock(x); \ +} while(0) + static int dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) { struct pcm_channel *chn, *rdch, *wrch; struct snddev_info *d; - int kill; - int ret = 0, *arg_i = (int *)arg, tmp; - int xcmd; + int *arg_i, ret, kill, tmp, xcmd; + + d = dsp_get_info(i_dev); + if (!PCM_REGISTERED(d)) + return (EBADF); + + PCM_GIANT_ENTER(d); + arg_i = (int *)arg; + ret = 0; xcmd = 0; /* * this is an evil hack to allow broken apps to perform mixer ioctls * on dsp devices. */ - - d = dsp_get_info(i_dev); - if (d == NULL) - return EBADF; if (IOCGROUP(cmd) == 'M') { /* * This is at least, a bug to bug compatible with OSS. */ - if (d->mixer_dev != NULL) - return mixer_ioctl(d->mixer_dev, cmd, arg, -1, td); - else - return EBADF; + if (d->mixer_dev != NULL) { + DSP_IOCTL_CV_ACQUIRE(d); + ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td, + MIXER_CMD_DIRECT); + DSP_IOCTL_CV_RELEASE(d); + } else + ret = EBADF; + + goto dsp_ioctl_out; } /* @@ -622,7 +690,7 @@ ret = EINVAL; } - return ret; + goto dsp_ioctl_out; } getchns(i_dev, &rdch, &wrch, 0); @@ -634,7 +702,8 @@ kill |= 2; if (kill == 3) { relchns(i_dev, rdch, wrch, 0); - return EINVAL; + ret = EINVAL; + goto dsp_ioctl_out; } if (kill & 1) wrch = NULL; @@ -667,6 +736,7 @@ p->play_size = 0; p->rec_size = 0; + DSP_IOCTL_CV_ACQUIRE(d); if (wrch) { CHN_LOCK(wrch); chn_setblocksize(wrch, 2, p->play_size); @@ -679,6 +749,7 @@ p->rec_size = sndbuf_getblksz(rdch->bufsoft); CHN_UNLOCK(rdch); } + DSP_IOCTL_CV_RELEASE(d); } break; case AIOGSIZE: /* get the current blocksize */ @@ -709,6 +780,7 @@ ret = EINVAL; break; } + DSP_IOCTL_CV_ACQUIRE(d); if (wrch) { CHN_LOCK(wrch); if (cmd == AIOSFMT && p->play_format != 0) { @@ -735,6 +807,7 @@ p->rec_rate = 0; p->rec_format = 0; } + DSP_IOCTL_CV_RELEASE(d); } break; @@ -767,10 +840,10 @@ p->mixers = 1; /* default: one mixer */ p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0; p->left = p->right = 100; - if (rdch) - CHN_UNLOCK(rdch); if (wrch) CHN_UNLOCK(wrch); + if (rdch) + CHN_UNLOCK(rdch); } break; @@ -850,10 +923,11 @@ *arg_i = 0; ret = EINVAL; } - break ; + break; case SNDCTL_DSP_SETBLKSIZE: RANGE(*arg_i, 16, 65536); + DSP_IOCTL_CV_ACQUIRE(d); if (wrch) { CHN_LOCK(wrch); chn_setblocksize(wrch, 2, *arg_i); @@ -864,6 +938,7 @@ chn_setblocksize(rdch, 2, *arg_i); CHN_UNLOCK(rdch); } + DSP_IOCTL_CV_RELEASE(d); break; case SNDCTL_DSP_RESET: @@ -895,6 +970,7 @@ case SNDCTL_DSP_SPEED: /* chn_setspeed may sleep */ tmp = 0; + DSP_IOCTL_CV_ACQUIRE(d); if (wrch) { CHN_LOCK(wrch); ret = chn_setspeed(wrch, *arg_i); @@ -908,6 +984,7 @@ tmp = rdch->speed; CHN_UNLOCK(rdch); } + DSP_IOCTL_CV_RELEASE(d); *arg_i = tmp; break; @@ -926,6 +1003,7 @@ case SNDCTL_DSP_STEREO: tmp = -1; *arg_i = (*arg_i)? AFMT_STEREO : 0; + DSP_IOCTL_CV_ACQUIRE(d); if (wrch) { CHN_LOCK(wrch); ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); @@ -939,6 +1017,7 @@ tmp = (rdch->format & AFMT_STEREO)? 1 : 0; CHN_UNLOCK(rdch); } + DSP_IOCTL_CV_RELEASE(d); *arg_i = tmp; break; @@ -947,6 +1026,7 @@ if (*arg_i != 0) { tmp = 0; *arg_i = (*arg_i != 1)? AFMT_STEREO : 0; + DSP_IOCTL_CV_ACQUIRE(d); if (wrch) { CHN_LOCK(wrch); ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); @@ -960,6 +1040,7 @@ tmp = (rdch->format & AFMT_STEREO)? 2 : 1; CHN_UNLOCK(rdch); } + DSP_IOCTL_CV_RELEASE(d); *arg_i = tmp; } else { chn = wrch ? wrch : rdch; @@ -991,11 +1072,12 @@ *arg_i = 0; ret = EINVAL; } - break ; + break; case SNDCTL_DSP_SETFMT: /* sets _one_ format */ if ((*arg_i != AFMT_QUERY)) { tmp = 0; + DSP_IOCTL_CV_ACQUIRE(d); if (wrch) { CHN_LOCK(wrch); ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO)); @@ -1009,6 +1091,7 @@ tmp = rdch->format & ~AFMT_STEREO; CHN_UNLOCK(rdch); } + DSP_IOCTL_CV_RELEASE(d); *arg_i = tmp; } else { chn = wrch ? wrch : rdch; @@ -1037,6 +1120,7 @@ maxfrags = CHN_2NDBUFMAXSIZE / fragsz; DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz)); + DSP_IOCTL_CV_ACQUIRE(d); if (rdch) { CHN_LOCK(rdch); ret = chn_setblocksize(rdch, maxfrags, fragsz); @@ -1057,6 +1141,7 @@ maxfrags = r_maxfrags; fragsz = r_fragsz; } + DSP_IOCTL_CV_RELEASE(d); fragln = 0; while (fragsz > 1) { @@ -1253,18 +1338,24 @@ if (xcmd == 0) xcmd = SOUND_MIXER_WRITE_PCM; - if (d->mixer_dev != NULL) - ret = mixer_ioctl(d->mixer_dev, xcmd, arg, -1, td); - else + if (d->mixer_dev != NULL) { + DSP_IOCTL_CV_ACQUIRE(d); + ret = mixer_ioctl_cmd(d->mixer_dev, xcmd, arg, -1, td, + MIXER_CMD_DIRECT); + DSP_IOCTL_CV_RELEASE(d); + } else ret = ENOTSUP; break; case SNDCTL_DSP_GET_RECSRC_NAMES: case SNDCTL_DSP_GET_RECSRC: case SNDCTL_DSP_SET_RECSRC: - if (d->mixer_dev != NULL) - ret = mixer_ioctl(d->mixer_dev, cmd, arg, -1, td); - else + if (d->mixer_dev != NULL) { + DSP_IOCTL_CV_ACQUIRE(d); + ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td, + MIXER_CMD_DIRECT); + DSP_IOCTL_CV_RELEASE(d); + } else ret = ENOTSUP; break; @@ -1451,15 +1542,21 @@ break; case SNDCTL_DSP_SYNCGROUP: + DSP_IOCTL_CV_ACQUIRE(d); ret = dsp_oss_syncgroup(wrch, rdch, (oss_syncgroup *)arg); + DSP_IOCTL_CV_RELEASE(d); break; case SNDCTL_DSP_SYNCSTART: + DSP_IOCTL_CV_ACQUIRE(d); ret = dsp_oss_syncstart(*arg_i); + DSP_IOCTL_CV_RELEASE(d); break; case SNDCTL_DSP_POLICY: + DSP_IOCTL_CV_ACQUIRE(d); ret = dsp_oss_policy(wrch, rdch, *arg_i); + DSP_IOCTL_CV_RELEASE(d); break; #ifdef OSSV4_EXPERIMENT @@ -1488,6 +1585,10 @@ } break; + /* + * XXX Once implemented, revisit this for proper cv protection + * (if necessary). + */ case SNDCTL_DSP_COOKEDMODE: ret = dsp_oss_cookedmode(wrch, rdch, *arg_i); break; @@ -1552,17 +1653,32 @@ ret = EINVAL; break; } + relchns(i_dev, rdch, wrch, 0); + +dsp_ioctl_out: + PCM_GIANT_EXIT(d); + return ret; } static int dsp_poll(struct cdev *i_dev, int events, struct thread *td) { - struct pcm_channel *wrch = NULL, *rdch = NULL; + struct snddev_info *d; + struct pcm_channel *wrch, *rdch; int ret, e; + d = dsp_get_info(i_dev); + if (!PCM_REGISTERED(d)) + return (EBADF); + + PCM_GIANT_ENTER(d); + + wrch = NULL; + rdch = NULL; ret = 0; + getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); if (wrch) { @@ -1577,17 +1693,32 @@ } relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); - return ret; + PCM_GIANT_EXIT(d); + + return (ret); } static int dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) { - struct pcm_channel *wrch = NULL, *rdch = NULL, *c; + struct snddev_info *d; + struct pcm_channel *wrch, *rdch, *c; + int ret; if (nprot & PROT_EXEC) - return -1; + return (-1); + + d = dsp_get_info(i_dev); + if (!PCM_REGISTERED(d)) + return (EBADF); + PCM_GIANT_ENTER(d); + + wrch = NULL; + rdch = NULL; + c = NULL; + ret = 0; + getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); #if 0 /* @@ -1608,12 +1739,14 @@ if (c == NULL) { relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); - return -1; + ret = -1; + goto dsp_mmap_out; } if (offset >= sndbuf_getsize(c->bufsoft)) { relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); - return -1; + ret = -1; + goto dsp_mmap_out; } if (!(c->flags & CHN_F_MAPPED)) @@ -1622,7 +1755,10 @@ *paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset)); relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); - return 0; +dsp_mmap_out: + PCM_GIANT_EXIT(d); + + return (ret); } #ifdef USING_DEVFS @@ -1723,7 +1859,7 @@ } d = devclass_get_softc(pcm_devclass, unit); - if (d == NULL || d->clones == NULL) + if (!PCM_REGISTERED(d) || d->clones == NULL) return; pcm_lock(d); @@ -1732,6 +1868,8 @@ return; } + PCM_CV_WAIT(d); + udcmask = snd_u2unit(unit) | snd_d2unit(devtype); if (devhw != 0) { @@ -1798,12 +1936,14 @@ snd_clone_setmaxunit(d->clones, tumax); if (ce != NULL) { udcmask |= snd_c2unit(cunit); + PCM_CV_ACQUIRE(d); pcm_unlock(d); *dev = make_dev(&dsp_cdevsw, unit2minor(udcmask), UID_ROOT, GID_WHEEL, 0666, "%s%d%s%d", devname, unit, devsep, cunit); pcm_lock(d); snd_clone_register(ce, *dev); + PCM_CV_RELEASE(d); } pcm_unlock(d); --- sys/dev/sound/pcm/mixer.c.orig Sun Jun 3 18:56:22 2007 +++ sys/dev/sound/pcm/mixer.c Mon Jun 4 18:08:19 2007 @@ -35,12 +35,12 @@ #define MIXER_NAMELEN 16 struct snd_mixer { KOBJ_FIELDS; - const char *type; void *devinfo; int busy; int hwvol_muted; int hwvol_mixer; int hwvol_step; + int type; device_t dev; u_int32_t hwvol_mute_level; u_int32_t devs; @@ -83,10 +83,10 @@ static d_open_t mixer_open; static d_close_t mixer_close; +static d_ioctl_t mixer_ioctl; static struct cdevsw mixer_cdevsw = { .d_version = D_VERSION, - .d_flags = D_NEEDGIANT, .d_open = mixer_open, .d_close = mixer_close, .d_ioctl = mixer_ioctl, @@ -126,17 +126,38 @@ } #endif +#define MIXER_SET_UNLOCK(x, y) do { \ + if ((y) != 0) \ + snd_mtxunlock((x)->lock); \ +} while(0) + +#define MIXER_SET_LOCK(x, y) do { \ + if ((y) != 0) \ + snd_mtxlock((x)->lock); \ +} while(0) + static int -mixer_set_softpcmvol(struct snd_mixer *mixer, struct snddev_info *d, - unsigned left, unsigned right) +mixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d, + unsigned left, unsigned right) { struct pcm_channel *c; - int locked; + int dropmtx, acquiremtx; + + if (!PCM_REGISTERED(d)) + return (EINVAL); + + if (mtx_owned(m->lock)) + dropmtx = 1; + else + dropmtx = 0; + + if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0) + acquiremtx = 0; + else + acquiremtx = 1; - locked = (mixer->lock != NULL && - mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0; - if (locked) - snd_mtxunlock(mixer->lock); + MIXER_SET_UNLOCK(m, dropmtx); + MIXER_SET_LOCK(d, acquiremtx); if (CHN_EMPTY(d, channels.pcm.busy)) { CHN_FOREACH(c, d, channels.pcm) { @@ -156,10 +177,10 @@ } } - if (locked) - snd_mtxlock(mixer->lock); + MIXER_SET_UNLOCK(d, acquiremtx); + MIXER_SET_LOCK(m, dropmtx); - return 0; + return (0); } static int @@ -169,7 +190,7 @@ unsigned l, r, tl, tr; u_int32_t parent = SOUND_MIXER_NONE, child = 0; u_int32_t realdev; - int i; + int i, dropmtx; if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || (0 == (m->devs & (1 << dev)))) @@ -183,6 +204,14 @@ if (d == NULL) return -1; + /* It is safe to drop this mutex due to Giant. */ + if (!(d->flags & SD_F_MPSAFE) && mtx_owned(m->lock) != 0) + dropmtx = 1; + else + dropmtx = 0; + + MIXER_SET_UNLOCK(m, dropmtx); + /* TODO: recursive handling */ parent = m->parent[dev]; if (parent >= SOUND_MIXER_NRDEVICES) @@ -194,10 +223,12 @@ tl = (l * (m->level[parent] & 0x00ff)) / 100; tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100; if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) - mixer_set_softpcmvol(m, d, tl, tr); + (void)mixer_set_softpcmvol(m, d, tl, tr); else if (realdev != SOUND_MIXER_NONE && - MIXER_SET(m, realdev, tl, tr) < 0) + MIXER_SET(m, realdev, tl, tr) < 0) { + MIXER_SET_LOCK(m, dropmtx); return -1; + } } else if (child != 0) { for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (!(child & (1 << i)) || m->parent[i] != dev) @@ -206,24 +237,30 @@ tl = (l * (m->level[i] & 0x00ff)) / 100; tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100; if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) - mixer_set_softpcmvol(m, d, tl, tr); + (void)mixer_set_softpcmvol(m, d, tl, tr); else if (realdev != SOUND_MIXER_NONE) MIXER_SET(m, realdev, tl, tr); } realdev = m->realdev[dev]; if (realdev != SOUND_MIXER_NONE && - MIXER_SET(m, realdev, l, r) < 0) + MIXER_SET(m, realdev, l, r) < 0) { + MIXER_SET_LOCK(m, dropmtx); return -1; + } } else { if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) - mixer_set_softpcmvol(m, d, l, r); + (void)mixer_set_softpcmvol(m, d, l, r); else if (realdev != SOUND_MIXER_NONE && - MIXER_SET(m, realdev, l, r) < 0) + MIXER_SET(m, realdev, l, r) < 0) { + MIXER_SET_LOCK(m, dropmtx); return -1; + } } m->level[dev] = l | (r << 8); + MIXER_SET_LOCK(m, dropmtx); + return 0; } @@ -232,16 +269,30 @@ { if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) return mixer->level[dev]; - else return -1; + else + return -1; } static int mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src) { + struct snddev_info *d; + int dropmtx; + + d = device_get_softc(mixer->dev); + if (d == NULL) + return -1; + if (!(d->flags & SD_F_MPSAFE) && mtx_owned(mixer->lock) != 0) + dropmtx = 1; + else + dropmtx = 0; src &= mixer->recdevs; if (src == 0) src = SOUND_MASK_MIC; + /* It is safe to drop this mutex due to Giant. */ + MIXER_SET_UNLOCK(mixer, dropmtx); mixer->recsrc = MIXER_SETRECSRC(mixer, src); + MIXER_SET_LOCK(mixer, dropmtx); return 0; } @@ -482,30 +533,92 @@ return m->devinfo; } -int -mixer_init(device_t dev, kobj_class_t cls, void *devinfo) +static struct snd_mixer * +mixer_obj_create(device_t dev, kobj_class_t cls, void *devinfo, + int type, const char *desc) { - struct snddev_info *snddev; struct snd_mixer *m; - u_int16_t v; - struct cdev *pdev; - int i, unit, devunit, val; + int i; + + KASSERT(dev != NULL && cls != NULL && devinfo != NULL, + ("%s(): NULL data dev=%p cls=%p devinfo=%p", + __func__, dev, cls, devinfo)); + KASSERT(type == MIXER_TYPE_PRIMARY || type == MIXER_TYPE_SECONDARY, + ("invalid mixer type=%d", type)); m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); - snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev)); - m->lock = snd_mtxcreate(m->name, "pcm mixer"); - m->type = cls->name; + snprintf(m->name, sizeof(m->name), "%s:mixer", + device_get_nameunit(dev)); + if (desc != NULL) { + strlcat(m->name, ":", sizeof(m->name)); + strlcat(m->name, desc, sizeof(m->name)); + } + m->lock = snd_mtxcreate(m->name, (type == MIXER_TYPE_PRIMARY) ? + "primary pcm mixer" : "secondary pcm mixer"); + m->type = type; m->devinfo = devinfo; m->busy = 0; m->dev = dev; - for (i = 0; i < 32; i++) { + for (i = 0; i < (sizeof(m->parent) / sizeof(m->parent[0])); i++) { m->parent[i] = SOUND_MIXER_NONE; m->child[i] = 0; m->realdev[i] = i; } - if (MIXER_INIT(m)) - goto bad; + if (MIXER_INIT(m)) { + snd_mtxlock(m->lock); + snd_mtxfree(m->lock); + kobj_delete((kobj_t)m, M_MIXER); + return (NULL); + } + + return (m); +} + +int +mixer_delete(struct snd_mixer *m) +{ + KASSERT(m != NULL, ("NULL snd_mixer")); + KASSERT(m->type == MIXER_TYPE_SECONDARY, + ("%s(): illegal mixer type=%d", __func__, m->type)); + + snd_mtxlock(m->lock); + + MIXER_UNINIT(m); + + snd_mtxfree(m->lock); + kobj_delete((kobj_t)m, M_MIXER); + + --mixer_count; + + return (0); +} + +struct snd_mixer * +mixer_create(device_t dev, kobj_class_t cls, void *devinfo, const char *desc) +{ + struct snd_mixer *m; + + m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_SECONDARY, desc); + + if (m != NULL) + ++mixer_count; + + return (m); +} + +int +mixer_init(device_t dev, kobj_class_t cls, void *devinfo) +{ + struct snddev_info *snddev; + struct snd_mixer *m; + u_int16_t v; + struct cdev *pdev; + int i, unit, devunit, val; + + m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_PRIMARY, NULL); + if (m == NULL) + return (-1); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { v = snd_mixerdefaults[i]; @@ -556,13 +669,7 @@ device_printf(dev, "Soft PCM mixer ENABLED\n"); } - return 0; - -bad: - snd_mtxlock(m->lock); - snd_mtxfree(m->lock); - kobj_delete((kobj_t)m, M_MIXER); - return -1; + return (0); } int @@ -577,7 +684,12 @@ pdev = mixer_get_devt(dev); if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL) return EBADF; + m = pdev->si_drv1; + KASSERT(m != NULL, ("NULL snd_mixer")); + KASSERT(m->type == MIXER_TYPE_PRIMARY, + ("%s(): illegal mixer type=%d", __func__, m->type)); + snd_mtxlock(m->lock); if (m->busy) { @@ -733,42 +845,170 @@ snd_mtxunlock(m->lock); } +int +mixer_busy(struct snd_mixer *m) +{ + KASSERT(m != NULL, ("NULL snd_mixer")); + + return (m->busy); +} + +int +mix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right) +{ + int ret; + + KASSERT(m != NULL, ("NULL snd_mixer")); + + snd_mtxlock(m->lock); + ret = mixer_set(m, dev, left | (right << 8)); + snd_mtxunlock(m->lock); + + return ((ret != 0) ? ENXIO : 0); +} + +int +mix_get(struct snd_mixer *m, u_int dev) +{ + int ret; + + KASSERT(m != NULL, ("NULL snd_mixer")); + + snd_mtxlock(m->lock); + ret = mixer_get(m, dev); + snd_mtxunlock(m->lock); + + return (ret); +} + +int +mix_setrecsrc(struct snd_mixer *m, u_int32_t src) +{ + int ret; + + KASSERT(m != NULL, ("NULL snd_mixer")); + + snd_mtxlock(m->lock); + ret = mixer_setrecsrc(m, src); + snd_mtxunlock(m->lock); + + return ((ret != 0) ? ENXIO : 0); +} + +u_int32_t +mix_getrecsrc(struct snd_mixer *m) +{ + u_int32_t ret; + + KASSERT(m != NULL, ("NULL snd_mixer")); + + snd_mtxlock(m->lock); + ret = mixer_getrecsrc(m); + snd_mtxunlock(m->lock); + + return (ret); +} + +int +mix_get_type(struct snd_mixer *m) +{ + KASSERT(m != NULL, ("NULL snd_mixer")); + + return (m->type); +} + /* ----------------------------------------------------------------------- */ static int mixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td) { + struct snddev_info *d; struct snd_mixer *m; + + if (i_dev == NULL || i_dev->si_drv1 == NULL) + return (EBADF); + m = i_dev->si_drv1; + d = device_get_softc(m->dev); + if (!PCM_REGISTERED(d)) + return (EBADF); + snd_mtxlock(m->lock); m->busy = 1; snd_mtxunlock(m->lock); - return 0; + + return (0); } static int mixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td) { + struct snddev_info *d; struct snd_mixer *m; + if (i_dev == NULL || i_dev->si_drv1 == NULL) + return (EBADF); + m = i_dev->si_drv1; + d = device_get_softc(m->dev); + if (!PCM_REGISTERED(d)) + return (EBADF); + snd_mtxlock(m->lock); if (!m->busy) { snd_mtxunlock(m->lock); - return EBADF; + return (EBADF); } m->busy = 0; snd_mtxunlock(m->lock); - return 0; + + return (0); +} + +static int +mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, + struct thread *td) +{ + struct snddev_info *d; + int ret; + + if (i_dev == NULL || i_dev->si_drv1 == NULL) + return (EBADF); + + d = device_get_softc(((struct snd_mixer *)i_dev->si_drv1)->dev); + if (!PCM_REGISTERED(d)) + return (EBADF); + + PCM_GIANT_ENTER(d); + + pcm_lock(d); + PCM_CV_WAIT(d); + PCM_CV_ACQUIRE(d); + pcm_unlock(d); + + ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td, MIXER_CMD_CDEV); + + pcm_lock(d); + PCM_CV_RELEASE(d); + pcm_unlock(d); + + PCM_GIANT_EXIT(d); + + return (ret); } +/* + * XXX Make sure you can guarantee concurrency safety before calling this + * function, be it through Giant, PCM_CV_*, etc ! + */ int -mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) +mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, + struct thread *td, int from) { struct snd_mixer *m; int ret, *arg_i = (int *)arg; @@ -780,7 +1020,7 @@ return EBADF; snd_mtxlock(m->lock); - if (mode != -1 && !m->busy) { + if (from == MIXER_CMD_CDEV && !m->busy) { snd_mtxunlock(m->lock); return EBADF; } @@ -867,7 +1107,7 @@ return; if (strcmp(name, "mixer") == 0) { d = devclass_get_softc(pcm_devclass, snd_unit); - if (d != NULL && d->mixer_dev != NULL) { + if (PCM_REGISTERED(d) && d->mixer_dev != NULL) { *dev = d->mixer_dev; dev_ref(*dev); } --- sys/dev/sound/pcm/mixer.h.orig Sat Sep 30 09:24:22 2006 +++ sys/dev/sound/pcm/mixer.h Mon Jun 4 15:41:09 2007 @@ -26,16 +26,27 @@ * $FreeBSD: src/sys/dev/sound/pcm/mixer.h,v 1.18 2006/09/30 01:24:22 ariff Exp $ */ +struct snd_mixer *mixer_create(device_t dev, kobj_class_t cls, void *devinfo, + const char *desc); +int mixer_delete(struct snd_mixer *m); int mixer_init(device_t dev, kobj_class_t cls, void *devinfo); int mixer_uninit(device_t dev); int mixer_reinit(device_t dev); -int mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td); +int mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td, int from); int mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi); int mixer_hwvol_init(device_t dev); void mixer_hwvol_mute(device_t dev); void mixer_hwvol_step(device_t dev, int left_step, int right_step); +int mixer_busy(struct snd_mixer *m); + +int mix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right); +int mix_get(struct snd_mixer *m, u_int dev); +int mix_setrecsrc(struct snd_mixer *m, u_int32_t src); +u_int32_t mix_getrecsrc(struct snd_mixer *m); +int mix_get_type(struct snd_mixer *m); + void mix_setdevs(struct snd_mixer *m, u_int32_t v); void mix_setrecdevs(struct snd_mixer *m, u_int32_t v); u_int32_t mix_getdevs(struct snd_mixer *m); @@ -47,6 +58,12 @@ void *mix_getdevinfo(struct snd_mixer *m); extern int mixer_count; + +#define MIXER_CMD_DIRECT 0 /* send command within driver */ +#define MIXER_CMD_CDEV 1 /* send command from cdev/ioctl */ + +#define MIXER_TYPE_PRIMARY 0 /* mixer_init() */ +#define MIXER_TYPE_SECONDARY 1 /* mixer_create() */ /* * this is a kludge to allow hiding of the struct snd_mixer definition --- sys/dev/sound/pcm/sndstat.c.orig Fri Jun 1 02:43:32 2007 +++ sys/dev/sound/pcm/sndstat.c Sun Jun 3 18:45:04 2007 @@ -26,6 +26,7 @@ #include #include +#include #ifdef USING_MUTEX #include #endif @@ -45,7 +46,6 @@ static struct cdevsw sndstat_cdevsw = { .d_version = D_VERSION, - .d_flags = D_NEEDGIANT, .d_open = sndstat_open, .d_close = sndstat_close, .d_read = sndstat_read, @@ -137,8 +137,6 @@ static int sndstat_open(struct cdev *i_dev, int flags, int mode, struct thread *td) { - int error; - if (sndstat_dev == NULL || i_dev != sndstat_dev) return EBADF; @@ -150,19 +148,13 @@ SNDSTAT_PID_SET(i_dev, td->td_proc->p_pid); mtx_unlock(&sndstat_lock); if (sbuf_new(&sndstat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) { - error = ENXIO; - goto out; - } - sndstat_bufptr = 0; - error = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM; -out: - if (error) { mtx_lock(&sndstat_lock); - SNDSTAT_FLUSH(); SNDSTAT_PID_SET(i_dev, 0); mtx_unlock(&sndstat_lock); + return ENXIO; } - return (error); + sndstat_bufptr = 0; + return 0; } static int @@ -201,6 +193,16 @@ } mtx_unlock(&sndstat_lock); + if (sndstat_bufptr == 0) { + err = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM; + if (err) { + mtx_lock(&sndstat_lock); + SNDSTAT_FLUSH(); + mtx_unlock(&sndstat_lock); + return err; + } + } + l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr); err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0; sndstat_bufptr += l; @@ -348,10 +350,11 @@ sndstat_prepare(struct sbuf *s) { struct sndstat_entry *ent; + struct snddev_info *d; int i, j; - sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit)\n", - (unsigned int)sizeof(intpcm_t) << 3); + sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit %d/%s)\n", + (u_int)sizeof(intpcm_t) << 3, SND_DRV_VERSION, MACHINE_ARCH); if (SLIST_EMPTY(&sndstat_devlist)) { sbuf_printf(s, "No devices installed.\n"); sbuf_finish(s); @@ -365,9 +368,13 @@ ent = sndstat_find(j, i); if (!ent) continue; + d = device_get_softc(ent->dev); + if (!PCM_REGISTERED(d)) + continue; sbuf_printf(s, "%s:", device_get_nameunit(ent->dev)); sbuf_printf(s, " <%s>", device_get_desc(ent->dev)); - sbuf_printf(s, " %s", ent->str); + sbuf_printf(s, " %s [%s]", ent->str, + (d->flags & SD_F_MPSAFE) ? "MPSAFE" : "GIANT"); if (ent->handler) ent->handler(s, ent->dev, snd_verbose); else --- sys/dev/sound/pcm/sound.c.orig Sun Jun 3 18:56:22 2007 +++ sys/dev/sound/pcm/sound.c Sun Jun 3 21:32:20 2007 @@ -131,12 +131,17 @@ int snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) { + struct snddev_info *d; #ifdef USING_MUTEX flags &= INTR_MPSAFE; flags |= INTR_TYPE_AV; #else flags = INTR_TYPE_AV; #endif + d = device_get_softc(dev); + if (d != NULL && (flags & INTR_MPSAFE)) + d->flags |= SD_F_MPSAFE; + return bus_setup_intr(dev, res, flags, #if __FreeBSD_version >= 700031 NULL, @@ -301,8 +306,9 @@ KASSERT(d != NULL && ch != NULL && (devunit == -1 || !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && (direction == PCMDIR_PLAY || direction == PCMDIR_REC), - ("%s() invalid d=%p ch=%p direction=%d pid=%d devunit=%d", + ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d", __func__, d, ch, direction, pid, devunit)); + KASSERT(d->flags & SD_F_BUSY, ("%s(): no cv protection!", __func__)); /* Double check again. */ if (devunit != -1) { @@ -444,7 +450,7 @@ error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); if (error == 0 && req->newptr != NULL) { d = devclass_get_softc(pcm_devclass, unit); - if (d == NULL || CHN_EMPTY(d, channels.pcm)) + if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) return EINVAL; snd_unit = unit; } @@ -472,9 +478,18 @@ for (i = 0; pcm_devclass != NULL && i < devclass_get_maxunit(pcm_devclass); i++) { d = devclass_get_softc(pcm_devclass, i); - if (d == NULL) + if (!PCM_REGISTERED(d)) continue; + pcm_inprog(d, 1); + pcm_lock(d); + PCM_CV_WAIT(d); + PCM_CV_ACQUIRE(d); + pcm_unlock(d); pcm_setmaxautovchans(d, v); + pcm_lock(d); + PCM_CV_RELEASE(d); + pcm_unlock(d); + pcm_inprog(d, -1); } } return (error); @@ -491,8 +506,7 @@ char *dirs, *devname, buf[CHN_NAMELEN]; KASSERT(num >= -1, ("invalid num=%d", num)); - - pcm_lock(d); + snd_mtxassert(d->lock); switch(dir) { case PCMDIR_PLAY: @@ -524,16 +538,13 @@ max = SND_MAXVCHANS; break; default: - pcm_unlock(d); return NULL; } chan = (num == -1) ? 0 : num; - if (*pnum >= max || chan >= max) { - pcm_unlock(d); + if (*pnum >= max || chan >= max) return NULL; - } rpnum = 0; @@ -551,7 +562,6 @@ if (chan >= max) { device_printf(d->dev, "chan=%d > %d\n", chan, max); - pcm_unlock(d); return NULL; } } @@ -562,7 +572,6 @@ device_printf(d->dev, "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", __func__, dirs, *pnum, rpnum); - pcm_unlock(d); return NULL; } @@ -577,6 +586,7 @@ } (*pnum)++; + pcm_unlock(d); ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); @@ -591,14 +601,13 @@ device_get_nameunit(ch->dev), dirs, devname); err = chn_init(ch, devinfo, dir, direction); + pcm_lock(d); if (err) { device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); kobj_delete(ch->methods, M_DEVBUF); free(ch, M_DEVBUF); - pcm_lock(d); (*pnum)--; - pcm_unlock(d); return NULL; } @@ -631,10 +640,9 @@ struct pcm_channel *tmp, *after; int num; - pcm_lock(d); - KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || ch->direction == PCMDIR_REC), ("Invalid pcm channel")); + snd_mtxassert(d->lock); after = NULL; tmp = NULL; @@ -671,7 +679,6 @@ } d->devcount++; - pcm_unlock(d); return 0; } @@ -681,6 +688,8 @@ { struct pcm_channel *tmp; + snd_mtxassert(d->lock); + tmp = NULL; CHN_FOREACH(tmp, d, channels.pcm) { @@ -719,9 +728,11 @@ struct pcm_channel *ch; int err; + pcm_lock(d); ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); if (!ch) { device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); + pcm_unlock(d); return ENODEV; } @@ -729,9 +740,10 @@ if (err) { device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); pcm_chn_destroy(ch); - return err; } + pcm_unlock(d); + return err; } @@ -766,6 +778,9 @@ (void)snd_clone_enable(d->clones); } + /* Done, we're ready.. */ + d->flags |= SD_F_REGISTERED; + pcm_unlock(d); return 0; @@ -834,7 +849,7 @@ int err; d = oidp->oid_arg1; - if (d == NULL || d->clones == NULL) + if (!PCM_REGISTERED(d) || d->clones == NULL) return (ENODEV); pcm_lock(d); @@ -843,10 +858,11 @@ err = sysctl_handle_int(oidp, (int *)(&flags), sizeof(flags), req); if (err == 0 && req->newptr != NULL) { - if ((flags & ~SND_CLONE_MASK)) + if ((flags & ~(SND_CLONE_MASK & ~SND_CLONE_WAITOK))) err = EINVAL; else { pcm_lock(d); + PCM_CV_WAIT(d); (void)snd_clone_setflags(d->clones, flags); pcm_unlock(d); } @@ -862,7 +878,7 @@ int err, deadline; d = oidp->oid_arg1; - if (d == NULL || d->clones == NULL) + if (!PCM_REGISTERED(d) || d->clones == NULL) return (ENODEV); pcm_lock(d); @@ -875,6 +891,7 @@ err = EINVAL; else { pcm_lock(d); + PCM_CV_WAIT(d); (void)snd_clone_setdeadline(d->clones, deadline); pcm_unlock(d); } @@ -890,7 +907,7 @@ int err, val; d = oidp->oid_arg1; - if (d == NULL || d->clones == NULL) + if (!PCM_REGISTERED(d) || d->clones == NULL) return (ENODEV); val = 0; @@ -898,6 +915,7 @@ if (err == 0 && req->newptr != NULL && val != 0) { pcm_lock(d); + PCM_CV_WAIT(d); (void)snd_clone_gc(d->clones); pcm_unlock(d); } @@ -918,9 +936,10 @@ for (i = 0; pcm_devclass != NULL && i < devclass_get_maxunit(pcm_devclass); i++) { d = devclass_get_softc(pcm_devclass, i); - if (d == NULL || d->clones == NULL) + if (!PCM_REGISTERED(d) || d->clones == NULL) continue; pcm_lock(d); + PCM_CV_WAIT(d); (void)snd_clone_gc(d->clones); pcm_unlock(d); } @@ -953,8 +972,9 @@ } d = device_get_softc(dev); + d->dev = dev; d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); - + PCM_CV_INIT(d); #if 0 /* * d->flags should be cleared by the allocator of the softc. @@ -963,7 +983,6 @@ */ d->flags = 0; #endif - d->dev = dev; d->devinfo = devinfo; d->devcount = 0; d->reccount = 0; @@ -1045,18 +1064,25 @@ } sndstat_register(dev, d->status, sndstat_prepare_pcm); + return 0; } int pcm_unregister(device_t dev) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d; struct pcm_channel *ch; struct thread *td; int i; td = curthread; + d = device_get_softc(dev); + + if (!PCM_ALIVE(d)) { + device_printf(dev, "unregister: device not configured\n"); + return ENXIO; + } if (sndstat_acquire(td) != 0) { device_printf(dev, "unregister: sndstat busy\n"); @@ -1064,7 +1090,8 @@ } pcm_lock(d); - if (d->inprog) { + + if (d->inprog || (d->flags & SD_F_BUSY)) { device_printf(dev, "unregister: operation in progress\n"); pcm_unlock(d); sndstat_release(td); @@ -1099,6 +1126,9 @@ return EBUSY; } + d->flags |= SD_F_DYING; + d->flags &= ~SD_F_REGISTERED; + if (d->clones != NULL) { snd_clone_destroy(d->clones); d->clones = NULL; @@ -1123,6 +1153,7 @@ chn_kill(d->fakechan); fkchan_kill(d->fakechan); + PCM_CV_DESTROY(d); pcm_unlock(d); snd_mtxfree(d->lock); sndstat_unregister(dev); @@ -1134,11 +1165,13 @@ */ for (i = 0; pcm_devclass != NULL && i < devclass_get_maxunit(pcm_devclass); i++) { - if (device_get_unit(dev) == i || - devclass_get_softc(pcm_devclass, i) == NULL) + if (device_get_unit(dev) == i) continue; - snd_unit = i; - break; + d = devclass_get_softc(pcm_devclass, i); + if (PCM_REGISTERED(d)) { + snd_unit = i; + break; + } } } @@ -1162,81 +1195,93 @@ return ENXIO; pcm_lock(d); - if (!CHN_EMPTY(d, channels.pcm)) { - sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", - d->playcount, d->pvchancount, - d->reccount, d->rvchancount, - (d->flags & SD_F_SIMPLEX)? "" : " duplex", + PCM_CV_WAIT(d); + + if (CHN_EMPTY(d, channels.pcm)) { + pcm_unlock(d); + sbuf_printf(s, " (mixer only)"); + return 0; + } + + PCM_CV_ACQUIRE(d); + pcm_unlock(d); + sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", + d->playcount, d->pvchancount, + d->reccount, d->rvchancount, + (d->flags & SD_F_SIMPLEX)? "" : " duplex", #ifdef USING_DEVFS - (device_get_unit(dev) == snd_unit)? " default" : "" + (device_get_unit(dev) == snd_unit)? " default" : "" #else - "" + "" #endif - ); - - if (verbose <= 1) { - pcm_unlock(d); - return 0; - } + ); - CHN_FOREACH(c, d, channels.pcm) { + if (verbose <= 1) { + pcm_lock(d); + PCM_CV_RELEASE(d); + pcm_unlock(d); + return 0; + } - KASSERT(c->bufhard != NULL && c->bufsoft != NULL, - ("hosed pcm channel setup")); + CHN_FOREACH(c, d, channels.pcm) { - sbuf_printf(s, "\n\t"); + KASSERT(c->bufhard != NULL && c->bufsoft != NULL, + ("hosed pcm channel setup")); - /* it would be better to indent child channels */ - sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); - sbuf_printf(s, "spd %d", c->speed); - if (c->speed != sndbuf_getspd(c->bufhard)) - sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); - sbuf_printf(s, ", fmt 0x%08x", c->format); - if (c->format != sndbuf_getfmt(c->bufhard)) - sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); - sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); - if (c->pid != -1) - sbuf_printf(s, ", pid %d", c->pid); - sbuf_printf(s, "\n\t"); - - sbuf_printf(s, "interrupts %d, ", c->interrupts); - if (c->direction == PCMDIR_REC) - sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); - else - sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); - sbuf_printf(s, "\n\t"); + sbuf_printf(s, "\n\t"); - sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); + /* it would be better to indent child channels */ + sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); + sbuf_printf(s, "spd %d", c->speed); + if (c->speed != sndbuf_getspd(c->bufhard)) + sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); + sbuf_printf(s, ", fmt 0x%08x", c->format); + if (c->format != sndbuf_getfmt(c->bufhard)) + sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); + sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); + if (c->pid != -1) + sbuf_printf(s, ", pid %d", c->pid); + sbuf_printf(s, "\n\t"); + + sbuf_printf(s, "interrupts %d, ", c->interrupts); + if (c->direction == PCMDIR_REC) + sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", + c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), + sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), + sndbuf_getblkcnt(c->bufhard), + sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), + sndbuf_getblkcnt(c->bufsoft)); + else + sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", + c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), + sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), + sndbuf_getblkcnt(c->bufhard), + sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), + sndbuf_getblkcnt(c->bufsoft)); + sbuf_printf(s, "\n\t"); + + sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); + sbuf_printf(s, " -> "); + f = c->feeder; + while (f->source != NULL) + f = f->source; + while (f != NULL) { + sbuf_printf(s, "%s", f->class->name); + if (f->desc->type == FEEDER_FMT) + sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); + if (f->desc->type == FEEDER_RATE) + sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); + if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || + f->desc->type == FEEDER_VOLUME) + sbuf_printf(s, "(0x%08x)", f->desc->out); sbuf_printf(s, " -> "); - f = c->feeder; - while (f->source != NULL) - f = f->source; - while (f != NULL) { - sbuf_printf(s, "%s", f->class->name); - if (f->desc->type == FEEDER_FMT) - sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); - if (f->desc->type == FEEDER_RATE) - sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); - if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || - f->desc->type == FEEDER_VOLUME) - sbuf_printf(s, "(0x%08x)", f->desc->out); - sbuf_printf(s, " -> "); - f = f->parent; - } - sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); + f = f->parent; } - } else - sbuf_printf(s, " (mixer only)"); + sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); + } + + pcm_lock(d); + PCM_CV_RELEASE(d); pcm_unlock(d); return 0; @@ -1250,40 +1295,58 @@ { struct snddev_info *d; int direction, vchancount; - int err, newcnt; + int err, cnt; d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); - if (d == NULL) - return EINVAL; + if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) + return (EINVAL); + + pcm_inprog(d, 1); + pcm_lock(d); + PCM_CV_WAIT(d); switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { case VCHAN_PLAY: - if (d->playcount < 1) - return ENODEV; direction = PCMDIR_PLAY; vchancount = d->pvchancount; + cnt = d->playcount; break; case VCHAN_REC: - if (d->reccount < 1) - return ENODEV; direction = PCMDIR_REC; vchancount = d->rvchancount; + cnt = d->reccount; break; default: - return EINVAL; + pcm_unlock(d); + pcm_inprog(d, -1); + return (EINVAL); break; } - newcnt = vchancount; - err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); + if (cnt < 1) { + pcm_unlock(d); + pcm_inprog(d, -1); + return (ENODEV); + } + + PCM_CV_ACQUIRE(d); + pcm_unlock(d); - if (err == 0 && req->newptr != NULL && vchancount != newcnt) { - if (newcnt < 0) - newcnt = 0; - if (newcnt > SND_MAXVCHANS) - newcnt = SND_MAXVCHANS; - err = pcm_setvchans(d, direction, newcnt, -1); + cnt = vchancount; + err = sysctl_handle_int(oidp, &cnt, sizeof(cnt), req); + + if (err == 0 && req->newptr != NULL && vchancount != cnt) { + if (cnt < 0) + cnt = 0; + if (cnt > SND_MAXVCHANS) + cnt = SND_MAXVCHANS; + err = pcm_setvchans(d, direction, cnt, -1); } + + pcm_lock(d); + PCM_CV_RELEASE(d); + pcm_unlock(d); + pcm_inprog(d, -1); return err; } --- sys/dev/sound/pcm/sound.h.orig Fri Jun 1 02:43:32 2007 +++ sys/dev/sound/pcm/sound.h Mon Jun 4 17:14:16 2007 @@ -138,12 +138,21 @@ #define SD_F_RSWAPLR 0x00000010 #define SD_F_DYING 0x00000020 #define SD_F_SUICIDE 0x00000040 +#define SD_F_BUSY 0x00000080 +#define SD_F_MPSAFE 0x00000100 +#define SD_F_REGISTERED 0x00000200 + #define SD_F_PRIO_RD 0x10000000 #define SD_F_PRIO_WR 0x20000000 #define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR) #define SD_F_DIR_SET 0x40000000 #define SD_F_TRANSIENT 0xf0000000 +#define PCM_ALIVE(x) ((x) != NULL && (x)->lock != NULL && \ + !((x)->flags & SD_F_DYING)) +#define PCM_REGISTERED(x) (PCM_ALIVE(x) && \ + ((x)->flags & SD_F_REGISTERED)) + /* many variables should be reduced to a range. Here define a macro */ #define RANGE(var, low, high) (var) = \ (((var)<(low))? (low) : ((var)>(high))? (high) : (var)) @@ -580,6 +589,7 @@ uint32_t rvchanrate, rvchanformat; struct sysctl_ctx_list play_sysctl_ctx, rec_sysctl_ctx; struct sysctl_oid *play_sysctl_tree, *rec_sysctl_tree; + struct cv cv; }; void sound_oss_sysinfo(oss_sysinfo *); @@ -591,6 +601,102 @@ void pcm_lock(struct snddev_info *d); void pcm_unlock(struct snddev_info *d); #endif + +/* + * For PCM_CV_[WAIT | ACQUIRE | RELEASE], be sure to surround these + * with pcm_lock/unlock() sequence, or I'll come to gnaw upon you! + */ + +#define PCM_CV_INIT(x) cv_init(&(x)->cv, device_get_nameunit((x)->dev)) +#define PCM_CV_DESTROY(x) cv_destroy(&(x)->cv) + +#ifdef SND_DIAGNOSTIC +#define PCM_CV_WAIT(x) do { \ + if (mtx_owned((x)->lock) == 0) \ + panic("%s(%d): Mutex not owned!", __func__, __LINE__); \ + while ((x)->flags & SD_F_BUSY) { \ + if (snd_verbose > 3) \ + device_printf((x)->dev, \ + "%s(%d): cv_wait() called.\n", \ + __func__, __LINE__); \ + cv_wait(&(x)->cv, (x)->lock); \ + } \ +} while(0) + +#define PCM_CV_ACQUIRE(x) do { \ + if (mtx_owned((x)->lock) == 0) \ + panic("%s(%d): Mutex not owned!", __func__, __LINE__); \ + if ((x)->flags & SD_F_BUSY) { \ + if (snd_verbose > 3) \ + device_printf((x)->dev, \ + "%s(%d): Trying to acquire BUSY cv!\n", \ + __func__, __LINE__); \ + } else \ + (x)->flags |= SD_F_BUSY; \ +} while(0) + +#define PCM_CV_RELEASE(x) do { \ + if (mtx_owned((x)->lock) == 0) \ + panic("%s(%d): Mutex not owned!", __func__, __LINE__); \ + if ((x)->flags & SD_F_BUSY) { \ + (x)->flags &= ~SD_F_BUSY; \ + cv_signal(&(x)->cv); \ + } else if (snd_verbose > 3) \ + device_printf((x)->dev, \ + "%s(%d): Releasing non-BUSY cv!\n", \ + __func__, __LINE__); \ +} while(0) + +#define PCM_GIANT_ENTER(x) do { \ + int _pcm_giant = 0; \ + if (mtx_owned(&Giant) != 0) \ + panic("%s(%d): Giant owned!", __func__, __LINE__); \ + if (!((x)->flags & SD_F_MPSAFE)) { \ + mtx_lock(&Giant); \ + _pcm_giant = 1; \ + } + +#define PCM_GIANT_EXIT(x) \ + if (!(_pcm_giant == 0 || _pcm_giant == 1)) \ + panic("%s(%d): _pcm_giant screwed!", \ + __func__, __LINE__); \ + if (((x)->flags & SD_F_MPSAFE) && \ + (_pcm_giant != 0 || mtx_owned(&Giant) != 0)) \ + panic("%s(%d): MPSAFE Giant?", __func__, __LINE__); \ + if (_pcm_giant != 0) { \ + if (mtx_owned(&Giant) == 0) \ + panic("%s(%d): Giant not owned!", \ + __func__, __LINE__); \ + mtx_unlock(&Giant); \ + } \ +} while(0) +#else /* SND_DIAGNOSTIC */ +#define PCM_CV_WAIT(x) do { \ + while ((x)->flags & SD_F_BUSY) \ + cv_wait(&(x)->cv, (x)->lock); \ +} while(0) + +#define PCM_CV_ACQUIRE(x) (x)->flags |= SD_F_BUSY + +#define PCM_CV_RELEASE(x) do { \ + if ((x)->flags & SD_F_BUSY) { \ + (x)->flags &= ~SD_F_BUSY; \ + cv_signal(&(x)->cv); \ + } \ +} while(0) + +#define PCM_GIANT_ENTER(x) do { \ + int _pcm_giant = 0; \ + if (!((x)->flags & SD_F_MPSAFE)) { \ + mtx_lock(&Giant); \ + _pcm_giant = 1; \ + } + +#define PCM_GIANT_EXIT(x) \ + if (_pcm_giant != 0) \ + mtx_unlock(&Giant); \ +} while(0) +#endif /* !SND_DIAGNOSTIC */ #ifdef KLD_MODULE #define PCM_KLDSTRING(a) ("kld " # a) --- sys/dev/sound/pcm/vchan.c.orig Sat Jun 2 22:00:44 2007 +++ sys/dev/sound/pcm/vchan.c Sun Jun 3 18:45:04 2007 @@ -513,15 +513,16 @@ struct snddev_info *d; struct pcm_channel *c, *ch = NULL; struct pcmchan_caps *caps; - int vchancount, *vchanrate; - int direction; - int err = 0; - int newspd = 0; + int *vchanrate, vchancount, direction, err, newspd; d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); - if (d == NULL || !(d->flags & SD_F_AUTOVCHAN)) + if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) return (EINVAL); + pcm_inprog(d, 1); + pcm_lock(d); + PCM_CV_WAIT(d); + switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { case VCHAN_PLAY: direction = PCMDIR_PLAY; @@ -534,16 +535,20 @@ vchanrate = &d->rvchanrate; break; default: + pcm_unlock(d); + pcm_inprog(d, -1); return (EINVAL); break; } - if (vchancount < 1) - return (EINVAL); - if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { + if (vchancount < 1) { + pcm_unlock(d); pcm_inprog(d, -1); - return (EINPROGRESS); + return (EINVAL); } + + newspd = 0; + CHN_FOREACH(c, d, channels.pcm) { CHN_LOCK(c); if (c->direction == direction) { @@ -551,19 +556,15 @@ /* Sanity check */ if (ch != NULL && ch != c->parentchannel) { CHN_UNLOCK(c); + pcm_unlock(d); pcm_inprog(d, -1); return (EINVAL); } - if (req->newptr != NULL && - (c->flags & CHN_F_BUSY)) { - CHN_UNLOCK(c); - pcm_inprog(d, -1); - return (EBUSY); - } } else if (c->flags & CHN_F_HAS_VCHAN) { /* No way!! */ if (ch != NULL) { CHN_UNLOCK(c); + pcm_unlock(d); pcm_inprog(d, -1); return (EINVAL); } @@ -574,13 +575,19 @@ CHN_UNLOCK(c); } if (ch == NULL) { + pcm_unlock(d); pcm_inprog(d, -1); return (EINVAL); } + PCM_CV_ACQUIRE(d); + pcm_unlock(d); err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req); if (err == 0 && req->newptr != NULL) { if (newspd < 1 || newspd < feeder_rate_min || newspd > feeder_rate_max) { + pcm_lock(d); + PCM_CV_RELEASE(d); + pcm_unlock(d); pcm_inprog(d, -1); return (EINVAL); } @@ -590,11 +597,14 @@ if (caps == NULL || newspd < caps->minspeed || newspd > caps->maxspeed) { CHN_UNLOCK(ch); + pcm_lock(d); + PCM_CV_RELEASE(d); + pcm_unlock(d); pcm_inprog(d, -1); return (EINVAL); } } - if (newspd != ch->speed) { + if (CHN_STOPPED(ch) && newspd != ch->speed) { err = chn_setspeed(ch, newspd); /* * Try to avoid FEEDER_RATE on parent channel if the @@ -614,7 +624,12 @@ } else CHN_UNLOCK(ch); } + + pcm_lock(d); + PCM_CV_RELEASE(d); + pcm_unlock(d); pcm_inprog(d, -1); + return (err); } @@ -624,15 +639,17 @@ struct snddev_info *d; struct pcm_channel *c, *ch = NULL; uint32_t newfmt, spd; - int vchancount, *vchanformat; - int direction; - int err = 0, i; + int *vchanformat, vchancount, direction, err, i; char fmtstr[AFMTSTR_MAXSZ]; d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); - if (d == NULL || !(d->flags & SD_F_AUTOVCHAN)) + if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) return (EINVAL); + pcm_inprog(d, 1); + pcm_lock(d); + PCM_CV_WAIT(d); + switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { case VCHAN_PLAY: direction = PCMDIR_PLAY; @@ -645,16 +662,18 @@ vchanformat = &d->rvchanformat; break; default: + pcm_unlock(d); + pcm_inprog(d, -1); return (EINVAL); break; } - if (vchancount < 1) - return (EINVAL); - if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { + if (vchancount < 1) { + pcm_unlock(d); pcm_inprog(d, -1); - return (EINPROGRESS); + return (EINVAL); } + CHN_FOREACH(c, d, channels.pcm) { CHN_LOCK(c); if (c->direction == direction) { @@ -662,19 +681,15 @@ /* Sanity check */ if (ch != NULL && ch != c->parentchannel) { CHN_UNLOCK(c); + pcm_unlock(d); pcm_inprog(d, -1); return (EINVAL); } - if (req->newptr != NULL && - (c->flags & CHN_F_BUSY)) { - CHN_UNLOCK(c); - pcm_inprog(d, -1); - return (EBUSY); - } } else if (c->flags & CHN_F_HAS_VCHAN) { /* No way!! */ if (ch != NULL) { CHN_UNLOCK(c); + pcm_unlock(d); pcm_inprog(d, -1); return (EINVAL); } @@ -691,9 +706,12 @@ CHN_UNLOCK(c); } if (ch == NULL) { + pcm_unlock(d); pcm_inprog(d, -1); return (EINVAL); } + PCM_CV_ACQUIRE(d); + pcm_unlock(d); err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); if (err == 0 && req->newptr != NULL) { for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) { @@ -705,11 +723,14 @@ } newfmt = vchan_valid_strformat(fmtstr); if (newfmt == 0) { + pcm_lock(d); + PCM_CV_RELEASE(d); + pcm_unlock(d); pcm_inprog(d, -1); return (EINVAL); } CHN_LOCK(ch); - if (newfmt != ch->format) { + if (CHN_STOPPED(ch) && newfmt != ch->format) { /* Get channel speed, before chn_reset() screw it. */ spd = ch->speed; err = chn_reset(ch, newfmt); @@ -724,7 +745,12 @@ } else CHN_UNLOCK(ch); } + + pcm_lock(d); + PCM_CV_RELEASE(d); + pcm_unlock(d); pcm_inprog(d, -1); + return (err); } #endif @@ -761,9 +787,12 @@ return (EINVAL); CHN_UNLOCK(parent); + pcm_lock(d); + /* create a new playback channel */ ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent); if (ch == NULL) { + pcm_unlock(d); CHN_LOCK(parent); return (ENODEV); } @@ -772,10 +801,13 @@ err = pcm_chn_add(d, ch); if (err) { pcm_chn_destroy(ch); + pcm_unlock(d); CHN_LOCK(parent); return (err); } + pcm_unlock(d); + CHN_LOCK(parent); /* add us to our parent channel's children */ first = CHN_EMPTY(parent, children); @@ -923,11 +955,9 @@ parent->flags &= ~CHN_F_HAS_VCHAN; CHN_UNLOCK(parent); pcm_lock(d); - if (pcm_chn_remove(d, ch) == 0) { - pcm_unlock(d); + if (pcm_chn_remove(d, ch) == 0) pcm_chn_destroy(ch); - } else - pcm_unlock(d); + pcm_unlock(d); CHN_LOCK(parent); return (err); } @@ -969,11 +999,12 @@ /* remove us from our grandparent's channel list */ pcm_lock(d); err = pcm_chn_remove(d, c); - pcm_unlock(d); /* destroy ourselves */ if (!err) err = pcm_chn_destroy(c); + + pcm_unlock(d); return (err); } --- sys/dev/sound/usb/uaudio.c.orig Fri Jun 1 02:43:33 2007 +++ sys/dev/sound/usb/uaudio.c Sun Jun 3 18:45:04 2007 @@ -4511,12 +4511,16 @@ if (!d) return ENXIO; - snd_mtxlock(d->lock); + pcm_lock(d); + PCM_CV_WAIT(d); + if (CHN_EMPTY(d, channels.pcm)) { + pcm_unlock(d); sbuf_printf(s, " (mixer only)"); - snd_mtxunlock(d->lock); return 0; } + PCM_CV_ACQUIRE(d); + pcm_unlock(d); sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", d->playcount, d->pvchancount, d->reccount, d->rvchancount, @@ -4533,7 +4537,9 @@ } if (verbose <= 1) { - snd_mtxunlock(d->lock); + pcm_lock(d); + PCM_CV_RELEASE(d); + pcm_unlock(d); return 0; } @@ -4591,7 +4597,10 @@ } sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); } - snd_mtxunlock(d->lock); + + pcm_lock(d); + PCM_CV_RELEASE(d); + pcm_unlock(d); return 0; } --- sys/dev/sound/version.h.orig Fri Jun 1 02:35:24 2007 +++ sys/dev/sound/version.h Sun Jun 3 19:05:21 2007 @@ -37,6 +37,6 @@ * Last 2 decimal places reserved for daily versioning, starting * with 0. */ -#define SND_DRV_VERSION 2007060100 +#define SND_DRV_VERSION 2007060400 #endif /* !_SND_VERSION_H_ */