Deleted Added
full compact
30c30
< SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/dsp.c 170505 2007-06-10 15:46:34Z mjacob $");
---
> SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/dsp.c 170815 2007-06-16 03:37:28Z ariff $");
31a32,35
> static int dsp_mmap_allow_prot_exec = 0;
> SYSCTL_INT(_hw_snd, OID_AUTO, compat_linux_mmap, CTLFLAG_RW,
> &dsp_mmap_allow_prot_exec, 0, "linux mmap compatibility");
>
33a38,39
> int busy, simplex;
> TAILQ_ENTRY(dsp_cdevinfo) link;
36,37c42,44
< #define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch)
< #define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch)
---
> #define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch)
> #define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch)
> #define PCM_SIMPLEX(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->simplex)
39,42c46
< #define PCMDEV_ACQUIRE(x) do { \
< if ((x)->si_drv1 == NULL) \
< (x)->si_drv1 = x; \
< } while(0)
---
> #define DSP_CDEVINFO_CACHESIZE 8
44,47c48,49
< #define PCMDEV_RELEASE(x) do { \
< if ((x)->si_drv1 == x) \
< (x)->si_drv1 = NULL; \
< } while(0)
---
> #define DSP_REGISTERED(x, y) (PCM_REGISTERED(x) && \
> (y) != NULL && (y)->si_drv1 != NULL)
61d62
< .d_flags = D_NEEDGIANT,
98c99
< static u_int32_t
---
> static uint32_t
109c110
< dsp_set_flags(struct cdev *dev, u_int32_t flags)
---
> dsp_set_flags(struct cdev *dev, uint32_t flags)
121,122d121
< * set the priority if the device is simplex and one direction (only) is
< * specified.
126c125,126
< getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
---
> getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch,
> uint32_t prio)
129c129,130
< u_int32_t flags;
---
> struct pcm_channel *ch;
> uint32_t flags;
131,141c132,168
< d = dsp_get_info(dev);
< if (d == NULL)
< return -1;
< pcm_inprog(d, 1);
< pcm_lock(d);
< flags = dsp_get_flags(dev);
< KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
< ("getchns: read and write both prioritised"));
<
< if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
< flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
---
> if (PCM_SIMPLEX(dev) != 0) {
> d = dsp_get_info(dev);
> if (!PCM_REGISTERED(d))
> return (ENXIO);
> pcm_lock(d);
> PCM_WAIT(d);
> PCM_ACQUIRE(d);
> /*
> * Note: order is important -
> * pcm flags -> prio query flags -> wild guess
> */
> ch = NULL;
> flags = dsp_get_flags(dev);
> if (flags & SD_F_PRIO_WR) {
> ch = PCM_RDCH(dev);
> PCM_RDCH(dev) = NULL;
> } else if (flags & SD_F_PRIO_RD) {
> ch = PCM_WRCH(dev);
> PCM_WRCH(dev) = NULL;
> } else if (prio & SD_F_PRIO_WR) {
> ch = PCM_RDCH(dev);
> PCM_RDCH(dev) = NULL;
> flags |= SD_F_PRIO_WR;
> } else if (prio & SD_F_PRIO_RD) {
> ch = PCM_WRCH(dev);
> PCM_WRCH(dev) = NULL;
> flags |= SD_F_PRIO_RD;
> } else if (PCM_WRCH(dev) != NULL) {
> ch = PCM_RDCH(dev);
> PCM_RDCH(dev) = NULL;
> flags |= SD_F_PRIO_WR;
> } else if (PCM_RDCH(dev) != NULL) {
> ch = PCM_WRCH(dev);
> PCM_WRCH(dev) = NULL;
> flags |= SD_F_PRIO_RD;
> }
> PCM_SIMPLEX(dev) = 0;
142a170,176
> if (ch != NULL) {
> CHN_LOCK(ch);
> pcm_chnref(ch, -1);
> pcm_chnrelease(ch);
> }
> PCM_RELEASE(d);
> pcm_unlock(d);
147,156d180
< if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
< if (prio) {
< if (*rdch && flags & SD_F_PRIO_WR) {
< PCM_RDCH(dev) = NULL;
< *rdch = pcm_getfakechan(d);
< } else if (*wrch && flags & SD_F_PRIO_RD) {
< PCM_WRCH(dev) = NULL;
< *wrch = pcm_getfakechan(d);
< }
< }
158,162c182
< pcm_getfakechan(d)->flags |= CHN_F_BUSY;
< }
< pcm_unlock(d);
<
< if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
---
> if (*rdch != NULL && (prio & SD_F_PRIO_RD))
164c184
< if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
---
> if (*wrch != NULL && (prio & SD_F_PRIO_WR))
167c187
< return 0;
---
> return (0);
172c192,193
< relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
---
> relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch,
> uint32_t prio)
174,179c195
< struct snddev_info *d;
<
< d = dsp_get_info(dev);
< if (d == NULL)
< return;
< if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
---
> if (wrch != NULL && (prio & SD_F_PRIO_WR))
181c197
< if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
---
> if (rdch != NULL && (prio & SD_F_PRIO_RD))
183d198
< pcm_inprog(d, -1);
190c205,212
< KASSERT(dev != NULL && dev->si_drv1 == dev && rdch != wrch,
---
> struct snddev_info *d;
> struct dsp_cdevinfo *cdi;
> int simplex;
>
> d = dsp_get_info(dev);
>
> KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 == NULL &&
> rdch != wrch,
192,196c214,243
<
< dev->si_drv1 = malloc(sizeof(struct dsp_cdevinfo), M_DEVBUF,
< M_WAITOK | M_ZERO);
< PCM_RDCH(dev) = rdch;
< PCM_WRCH(dev) = wrch;
---
> PCM_BUSYASSERT(d);
> mtx_assert(d->lock, MA_OWNED);
>
> simplex = (dsp_get_flags(dev) & SD_F_SIMPLEX) ? 1 : 0;
>
> /*
> * Scan for free instance entry and put it into the end of list.
> * Create new one if necessary.
> */
> TAILQ_FOREACH(cdi, &d->dsp_cdevinfo_pool, link) {
> if (cdi->busy != 0)
> break;
> cdi->rdch = rdch;
> cdi->wrch = wrch;
> cdi->simplex = simplex;
> cdi->busy = 1;
> TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link);
> TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link);
> dev->si_drv1 = cdi;
> return;
> }
> pcm_unlock(d);
> cdi = malloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO);
> pcm_lock(d);
> cdi->rdch = rdch;
> cdi->wrch = wrch;
> cdi->simplex = simplex;
> cdi->busy = 1;
> TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link);
> dev->si_drv1 = cdi;
202c249,256
< KASSERT(dev != NULL && dev->si_drv1 != NULL &&
---
> struct snddev_info *d;
> struct dsp_cdevinfo *cdi, *tmp;
> uint32_t flags;
> int i;
>
> d = dsp_get_info(dev);
>
> KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 != NULL &&
204a259,260
> PCM_BUSYASSERT(d);
> mtx_assert(d->lock, MA_OWNED);
206c262
< free(dev->si_drv1, M_DEVBUF);
---
> cdi = dev->si_drv1;
207a264,298
> cdi->rdch = NULL;
> cdi->wrch = NULL;
> cdi->simplex = 0;
> cdi->busy = 0;
>
> /*
> * Once it is free, move it back to the beginning of list for
> * faster new entry allocation.
> */
> TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link);
> TAILQ_INSERT_HEAD(&d->dsp_cdevinfo_pool, cdi, link);
>
> /*
> * Scan the list, cache free entries up to DSP_CDEVINFO_CACHESIZE.
> * Reset simplex flags.
> */
> flags = dsp_get_flags(dev) & ~SD_F_PRIO_SET;
> i = DSP_CDEVINFO_CACHESIZE;
> TAILQ_FOREACH_SAFE(cdi, &d->dsp_cdevinfo_pool, link, tmp) {
> if (cdi->busy != 0) {
> if (cdi->simplex == 0) {
> if (cdi->rdch != NULL)
> flags |= SD_F_PRIO_RD;
> if (cdi->wrch != NULL)
> flags |= SD_F_PRIO_WR;
> }
> } else {
> if (i == 0) {
> TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link);
> free(cdi, M_DEVBUF);
> } else
> i--;
> }
> }
> dsp_set_flags(dev, flags);
209a301,335
> void
> dsp_cdevinfo_init(struct snddev_info *d)
> {
> struct dsp_cdevinfo *cdi;
> int i;
>
> KASSERT(d != NULL, ("NULL snddev_info"));
> PCM_BUSYASSERT(d);
> mtx_assert(d->lock, MA_NOTOWNED);
>
> TAILQ_INIT(&d->dsp_cdevinfo_pool);
> for (i = 0; i < DSP_CDEVINFO_CACHESIZE; i++) {
> cdi = malloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO);
> TAILQ_INSERT_HEAD(&d->dsp_cdevinfo_pool, cdi, link);
> }
> }
>
> void
> dsp_cdevinfo_flush(struct snddev_info *d)
> {
> struct dsp_cdevinfo *cdi, *tmp;
>
> KASSERT(d != NULL, ("NULL snddev_info"));
> PCM_BUSYASSERT(d);
> mtx_assert(d->lock, MA_NOTOWNED);
>
> cdi = TAILQ_FIRST(&d->dsp_cdevinfo_pool);
> while (cdi != NULL) {
> tmp = TAILQ_NEXT(cdi, link);
> free(cdi, M_DEVBUF);
> cdi = tmp;
> }
> TAILQ_INIT(&d->dsp_cdevinfo_pool);
> }
>
246a373,376
> { SND_DEV_DSPHW_CD, "dspcd", ".", 0, 0, 0,
> AFMT_S16_LE | AFMT_STEREO, 44100, DSP_CDEV_TYPE_RDWR },
> { SND_DEV_DSP_MMAP, "dsp_mmap", ".", 0, 0, 0,
> AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDWR },
248a379,394
> #define DSP_FIXUP_ERROR() do { \
> prio = dsp_get_flags(i_dev); \
> if (!DSP_F_VALID(flags)) \
> error = EINVAL; \
> if (!DSP_F_DUPLEX(flags) && \
> ((DSP_F_READ(flags) && d->reccount == 0) || \
> (DSP_F_WRITE(flags) && d->playcount == 0))) \
> error = ENOTSUP; \
> else if (!DSP_F_DUPLEX(flags) && (prio & SD_F_SIMPLEX) && \
> ((DSP_F_READ(flags) && (prio & SD_F_PRIO_WR)) || \
> (DSP_F_WRITE(flags) && (prio & SD_F_PRIO_RD)))) \
> error = EBUSY; \
> else if (DSP_REGISTERED(d, i_dev)) \
> error = EBUSY; \
> } while(0)
>
254,256c400,401
< uint32_t fmt, spd;
< int i, error, devtype;
< int wdevunit, rdevunit;
---
> uint32_t fmt, spd, prio;
> int i, error, rderror, wrerror, devtype, wdevunit, rdevunit;
260c405
< return ENODEV;
---
> return (ENODEV);
262d406
< /* This too.. */
264,265c408,409
< if (d == NULL)
< return EBADF;
---
> if (!PCM_REGISTERED(d))
> return (EBADF);
266a411,412
> PCM_GIANT_ENTER(d);
>
268a415
> PCM_WAIT(d);
275a423
> DSP_FIXUP_ERROR();
277c425,426
< return error;
---
> PCM_GIANT_EXIT(d);
> return (error);
280,288c429,430
< 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))
< error = ENOTSUP;
< else
< error = 0;
---
> error = 0;
> DSP_FIXUP_ERROR();
293c435,436
< return error;
---
> PCM_GIANT_EXIT(d);
> return (error);
297,298c440,442
< * Fake busy state by pointing si_drv1 to something else since
< * we have to give up locking somewhere during setup process.
---
> * That is just enough. Acquire and unlock pcm lock so
> * the other will just have to wait until we finish doing
> * everything.
300c444,445
< PCMDEV_ACQUIRE(i_dev);
---
> PCM_ACQUIRE(d);
> pcm_unlock(d);
320,322c465,467
< PCMDEV_RELEASE(i_dev);
< pcm_unlock(d);
< return ENOTSUP;
---
> PCM_RELEASE_QUICK(d);
> PCM_GIANT_EXIT(d);
> return (ENOTSUP);
338a484,485
> rderror = 0;
> wrerror = 0;
348,350c495,496
< pcm_unlock(d);
< error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid,
< rdevunit);
---
> rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC,
> td->td_proc->p_pid, rdevunit);
352c498
< if (error == 0 && (chn_reset(rdch, fmt) != 0 ||
---
> if (rderror == 0 && (chn_reset(rdch, fmt) != 0 ||
354c500
< error = ENODEV;
---
> rderror = ENXIO;
356c502
< if (error != 0) {
---
> if (rderror != 0) {
359,363c505,516
< pcm_lock(d);
< (void)snd_clone_release(i_dev);
< PCMDEV_RELEASE(i_dev);
< pcm_unlock(d);
< return error;
---
> if (!DSP_F_DUPLEX(flags)) {
> (void)snd_clone_release(i_dev);
> PCM_RELEASE_QUICK(d);
> PCM_GIANT_EXIT(d);
> return (rderror);
> }
> rdch = NULL;
> } else {
> if (flags & O_NONBLOCK)
> rdch->flags |= CHN_F_NBIO;
> pcm_chnref(rdch, 1);
> CHN_UNLOCK(rdch);
365,370d517
<
< if (flags & O_NONBLOCK)
< rdch->flags |= CHN_F_NBIO;
< pcm_chnref(rdch, 1);
< CHN_UNLOCK(rdch);
< pcm_lock(d);
375,377c522,523
< pcm_unlock(d);
< error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid,
< wdevunit);
---
> wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY,
> td->td_proc->p_pid, wdevunit);
379c525
< if (error == 0 && (chn_reset(wrch, fmt) != 0 ||
---
> if (wrerror == 0 && (chn_reset(wrch, fmt) != 0 ||
381c527
< error = ENODEV;
---
> wrerror = ENXIO;
383c529
< if (error != 0) {
---
> if (wrerror != 0) {
386,393c532,545
< if (rdch != NULL) {
< /*
< * Lock, deref and release previously
< * created record channel
< */
< CHN_LOCK(rdch);
< pcm_chnref(rdch, -1);
< pcm_chnrelease(rdch);
---
> if (!DSP_F_DUPLEX(flags)) {
> if (rdch != NULL) {
> /*
> * Lock, deref and release previously
> * created record channel
> */
> CHN_LOCK(rdch);
> pcm_chnref(rdch, -1);
> pcm_chnrelease(rdch);
> }
> (void)snd_clone_release(i_dev);
> PCM_RELEASE_QUICK(d);
> PCM_GIANT_EXIT(d);
> return (wrerror);
395,399c547,552
< pcm_lock(d);
< (void)snd_clone_release(i_dev);
< PCMDEV_RELEASE(i_dev);
< pcm_unlock(d);
< return error;
---
> wrch = NULL;
> } else {
> if (flags & O_NONBLOCK)
> wrch->flags |= CHN_F_NBIO;
> pcm_chnref(wrch, 1);
> CHN_UNLOCK(wrch);
400a554
> }
402,406c556,560
< if (flags & O_NONBLOCK)
< wrch->flags |= CHN_F_NBIO;
< pcm_chnref(wrch, 1);
< CHN_UNLOCK(wrch);
< pcm_lock(d);
---
> if (rdch == NULL && wrch == NULL) {
> (void)snd_clone_release(i_dev);
> PCM_RELEASE_QUICK(d);
> PCM_GIANT_EXIT(d);
> return ((wrerror != 0) ? wrerror : rderror);
408a563,564
> pcm_lock(d);
>
409a566,570
> * We're done. Allocate channels information for this cdev.
> */
> dsp_cdevinfo_alloc(i_dev, rdch, wrch);
>
> /*
413a575
> PCM_RELEASE(d);
416,420c578
< /*
< * We're done. Allocate and point si_drv1 to a real
< * allocated structure.
< */
< dsp_cdevinfo_alloc(i_dev, rdch, wrch);
---
> PCM_GIANT_LEAVE(d);
422c580
< return 0;
---
> return (0);
430c588
< int refs, sg_ids[2];
---
> int sg_ids, refs;
433,434c591,595
< if (d == NULL)
< return EBADF;
---
> if (!DSP_REGISTERED(d, i_dev))
> return (EBADF);
>
> PCM_GIANT_ENTER(d);
>
435a597,598
> PCM_WAIT(d);
>
439,444d601
< /*
< * Free_unr() may sleep, so store released syncgroup IDs until after
< * all locks are released.
< */
< sg_ids[0] = sg_ids[1] = 0;
<
446c603
< refs = 0;
---
> PCM_ACQUIRE(d);
447a605,606
>
> refs = 0;
459c618
< sg_ids[0] = chn_syncdestroy(rdch);
---
> sg_ids = chn_syncdestroy(rdch);
460a620,621
> if (sg_ids != 0)
> free_unr(pcmsg_unrhdr, sg_ids);
467a629
> PCM_RDCH(i_dev) = NULL;
474c636
< sg_ids[1] = chn_syncdestroy(wrch);
---
> sg_ids = chn_syncdestroy(wrch);
475a638,639
> if (sg_ids != 0)
> free_unr(pcmsg_unrhdr, sg_ids);
482a647
> PCM_WRCH(i_dev) = NULL;
486,489d650
< if (rdch)
< PCM_RDCH(i_dev) = NULL;
< if (wrch)
< PCM_WRCH(i_dev) = NULL;
495,498d655
< if (pcm_getfakechan(d))
< pcm_getfakechan(d)->flags = 0;
< /* What is this?!? */
< dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
508a666
> PCM_RELEASE(d);
513,516c671
< if (sg_ids[0])
< free_unr(pcmsg_unrhdr, sg_ids[0]);
< if (sg_ids[1])
< free_unr(pcmsg_unrhdr, sg_ids[1]);
---
> PCM_GIANT_LEAVE(d);
518c673
< return 0;
---
> return (0);
521,522c676,677
< static int
< dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
---
> static __inline int
> dsp_io_ops(struct cdev *i_dev, struct uio *buf)
524,525c679,683
< struct pcm_channel *rdch, *wrch;
< int ret;
---
> struct snddev_info *d;
> struct pcm_channel **ch, *rdch, *wrch;
> int (*chn_io)(struct pcm_channel *, struct uio *);
> int prio, ret;
> pid_t runpid;
527c685,687
< getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
---
> KASSERT(i_dev != NULL && buf != NULL &&
> (buf->uio_rw == UIO_READ || buf->uio_rw == UIO_WRITE),
> ("%s(): io train wreck!", __func__));
529,530c689,691
< if (rdch == NULL || !(rdch->flags & CHN_F_BUSY))
< return EBADF;
---
> d = dsp_get_info(i_dev);
> if (!DSP_REGISTERED(d, i_dev))
> return (EBADF);
532,534c693,708
< if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
< relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
< return EINVAL;
---
> PCM_GIANT_ENTER(d);
>
> switch (buf->uio_rw) {
> case UIO_READ:
> prio = SD_F_PRIO_RD;
> ch = &rdch;
> chn_io = chn_read;
> break;
> case UIO_WRITE:
> prio = SD_F_PRIO_WR;
> ch = &wrch;
> chn_io = chn_write;
> break;
> default:
> panic("invalid/corrupted uio direction: %d", buf->uio_rw);
> break;
536,539d709
< 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);
541,542c711,713
< return ret;
< }
---
> rdch = NULL;
> wrch = NULL;
> runpid = buf->uio_td->td_proc->p_pid;
544,548c715
< static int
< dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
< {
< struct pcm_channel *rdch, *wrch;
< int ret;
---
> getchns(i_dev, &rdch, &wrch, prio);
550c717,720
< getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
---
> if (*ch == NULL || !((*ch)->flags & CHN_F_BUSY)) {
> PCM_GIANT_EXIT(d);
> return (EBADF);
> }
552,557c722,729
< if (wrch == NULL || !(wrch->flags & CHN_F_BUSY))
< return EBADF;
<
< if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
< relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
< return EINVAL;
---
> if (((*ch)->flags & (CHN_F_MAPPED | CHN_F_DEAD)) ||
> (((*ch)->flags & CHN_F_RUNNING) && (*ch)->pid != runpid)) {
> relchns(i_dev, rdch, wrch, prio);
> PCM_GIANT_EXIT(d);
> return (EINVAL);
> } else if (!((*ch)->flags & CHN_F_RUNNING)) {
> (*ch)->flags |= CHN_F_RUNNING;
> (*ch)->pid = runpid;
559,560d730
< if (!(wrch->flags & CHN_F_RUNNING))
< wrch->flags |= CHN_F_RUNNING;
563,565c733,735
< * Chn_write() must give up channel lock in order to copy bytes from
< * userland, so up the "in progress" counter to make sure someone
< * else doesn't come along and muss up the buffer.
---
> * chn_read/write must give up channel lock in order to copy bytes
> * from/to userland, so up the "in progress" counter to make sure
> * someone else doesn't come along and muss up the buffer.
567,570c737,739
< ++wrch->inprog;
< ret = chn_write(wrch, buf);
< --wrch->inprog;
< cv_signal(&wrch->cv);
---
> ++(*ch)->inprog;
> ret = chn_io(*ch, buf);
> --(*ch)->inprog;
572c741
< relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
---
> CHN_BROADCAST(&(*ch)->cv);
574c743,747
< return ret;
---
> relchns(i_dev, rdch, wrch, prio);
>
> PCM_GIANT_LEAVE(d);
>
> return (ret);
577a751,762
> dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
> {
> return (dsp_io_ops(i_dev, buf));
> }
>
> static int
> dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
> {
> return (dsp_io_ops(i_dev, buf));
> }
>
> static int
582,584c767
< int kill;
< int ret = 0, *arg_i = (int *)arg, tmp;
< int xcmd;
---
> int *arg_i, ret, kill, tmp, xcmd;
585a769,776
> d = dsp_get_info(i_dev);
> if (!DSP_REGISTERED(d, i_dev))
> return (EBADF);
>
> PCM_GIANT_ENTER(d);
>
> arg_i = (int *)arg;
> ret = 0;
592,595d782
<
< d = dsp_get_info(i_dev);
< if (d == NULL)
< return EBADF;
600,603c787,797
< if (d->mixer_dev != NULL)
< return mixer_ioctl(d->mixer_dev, cmd, arg, -1, td);
< else
< return EBADF;
---
> if (d->mixer_dev != NULL) {
> PCM_ACQUIRE_QUICK(d);
> ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td,
> MIXER_CMD_DIRECT);
> PCM_RELEASE_QUICK(d);
> } else
> ret = EBADF;
>
> PCM_GIANT_EXIT(d);
>
> return (ret);
610a805
> PCM_ACQUIRE_QUICK(d);
624,625c819,821
<
< return ret;
---
> PCM_RELEASE_QUICK(d);
> PCM_GIANT_EXIT(d);
> return (ret);
637c833,834
< return EINVAL;
---
> PCM_GIANT_EXIT(d);
> return (EINVAL);
643a841,846
> if (wrch == NULL && rdch == NULL) {
> relchns(i_dev, rdch, wrch, 0);
> PCM_GIANT_EXIT(d);
> return (EINVAL);
> }
>
669a873
> PCM_ACQUIRE_QUICK(d);
681a886
> PCM_RELEASE_QUICK(d);
711a917
> PCM_ACQUIRE_QUICK(d);
737a944
> PCM_RELEASE_QUICK(d);
746a954
> pcm_lock(d);
770,771d977
< if (rdch)
< CHN_UNLOCK(rdch);
773a980,982
> if (rdch)
> CHN_UNLOCK(rdch);
> pcm_unlock(d);
853c1062
< break ;
---
> break;
856a1066
> PCM_ACQUIRE_QUICK(d);
866a1077
> PCM_RELEASE_QUICK(d);
892c1103,1104
< }
---
> } else
> ret = EINVAL;
897a1110
> PCM_ACQUIRE_QUICK(d);
910a1124
> PCM_RELEASE_QUICK(d);
928a1143
> PCM_ACQUIRE_QUICK(d);
941a1157
> PCM_RELEASE_QUICK(d);
949a1166
> PCM_ACQUIRE_QUICK(d);
962a1180
> PCM_RELEASE_QUICK(d);
994c1212
< break ;
---
> break;
998a1217
> PCM_ACQUIRE_QUICK(d);
1011a1231
> PCM_RELEASE_QUICK(d);
1024,1027c1244,1247
< u_int32_t fragln = (*arg_i) & 0x0000ffff;
< u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
< u_int32_t fragsz;
< u_int32_t r_maxfrags, r_fragsz;
---
> uint32_t fragln = (*arg_i) & 0x0000ffff;
> uint32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
> uint32_t fragsz;
> uint32_t r_maxfrags, r_fragsz;
1039a1260
> PCM_ACQUIRE_QUICK(d);
1059a1281
> PCM_RELEASE_QUICK(d);
1083c1305,1306
< }
---
> } else
> ret = EINVAL;
1101c1324,1325
< }
---
> } else
> ret = EINVAL;
1141a1366
> pcm_lock(d);
1144a1370
> pcm_unlock(d);
1223c1449,1450
< }
---
> } else
> ret = EINVAL;
1230a1458
> pcm_lock(d);
1232a1461
> pcm_unlock(d);
1256,1258c1485,1490
< if (d->mixer_dev != NULL)
< ret = mixer_ioctl(d->mixer_dev, xcmd, arg, -1, td);
< else
---
> if (d->mixer_dev != NULL) {
> PCM_ACQUIRE_QUICK(d);
> ret = mixer_ioctl_cmd(d->mixer_dev, xcmd, arg, -1, td,
> MIXER_CMD_DIRECT);
> PCM_RELEASE_QUICK(d);
> } else
1265,1267c1497,1502
< if (d->mixer_dev != NULL)
< ret = mixer_ioctl(d->mixer_dev, cmd, arg, -1, td);
< else
---
> if (d->mixer_dev != NULL) {
> PCM_ACQUIRE_QUICK(d);
> ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td,
> MIXER_CMD_DIRECT);
> PCM_RELEASE_QUICK(d);
> } else
1453a1689
> PCM_ACQUIRE_QUICK(d);
1454a1691
> PCM_RELEASE_QUICK(d);
1457a1695
> PCM_ACQUIRE_QUICK(d);
1458a1697
> PCM_RELEASE_QUICK(d);
1461a1701
> PCM_ACQUIRE_QUICK(d);
1462a1703
> PCM_RELEASE_QUICK(d);
1490a1732,1735
> /*
> * XXX Once implemented, revisit this for proper cv protection
> * (if necessary).
> */
1554a1800
>
1556c1802,1805
< return ret;
---
>
> PCM_GIANT_LEAVE(d);
>
> return (ret);
1562c1811,1812
< struct pcm_channel *wrch = NULL, *rdch = NULL;
---
> struct snddev_info *d;
> struct pcm_channel *wrch, *rdch;
1564a1815,1822
> d = dsp_get_info(i_dev);
> if (!DSP_REGISTERED(d, i_dev))
> return (EBADF);
>
> PCM_GIANT_ENTER(d);
>
> wrch = NULL;
> rdch = NULL;
1565a1824
>
1568c1827
< if (wrch) {
---
> if (wrch != NULL && !(wrch->flags & CHN_F_DEAD)) {
1573c1832,1833
< if (rdch) {
---
>
> if (rdch != NULL && !(rdch->flags & CHN_F_DEAD)) {
1577a1838
>
1580c1841,1843
< return ret;
---
> PCM_GIANT_LEAVE(d);
>
> return (ret);
1586c1849,1850
< struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
---
> struct snddev_info *d;
> struct pcm_channel *wrch, *rdch, *c;
1588,1589c1852,1861
< if (nprot & PROT_EXEC)
< return -1;
---
> /*
> * Reject PROT_EXEC by default. It just doesn't makes sense.
> * Unfortunately, we have to give up this one due to linux_mmap
> * changes.
> *
> * http://lists.freebsd.org/pipermail/freebsd-emulation/2007-June/003698.html
> *
> */
> if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec == 0)
> return (-1);
1590a1863,1868
> d = dsp_get_info(i_dev);
> if (!DSP_REGISTERED(d, i_dev))
> return (-1);
>
> PCM_GIANT_ENTER(d);
>
1592c1870
< #if 0
---
>
1594,1595c1872,1882
< * XXX the linux api uses the nprot to select read/write buffer
< * our vm system doesn't allow this, so force write buffer
---
> * XXX The linux api uses the nprot to select read/write buffer
> * our vm system doesn't allow this, so force write buffer.
> *
> * This is just a quack to fool full-duplex mmap, so that at
> * least playback _or_ recording works. If you really got the
> * urge to make _both_ work at the same time, avoid O_RDWR.
> * Just open each direction separately and mmap() it.
> *
> * Failure is not an option due to INVARIANTS check within
> * device_pager.c, which means, we have to give up one over
> * another.
1596a1884
> c = (wrch != NULL) ? wrch : rdch;
1598,1609c1886,1889
< if (wrch && (nprot & PROT_WRITE)) {
< c = wrch;
< } else if (rdch && (nprot & PROT_READ)) {
< c = rdch;
< } else {
< return -1;
< }
< #else
< c = wrch;
< #endif
<
< if (c == NULL) {
---
> if (c == NULL || (c->flags & CHN_F_MMAP_INVALID) ||
> offset >= sndbuf_getsize(c->bufsoft) ||
> (wrch != NULL && (wrch->flags & CHN_F_MMAP_INVALID)) ||
> (rdch != NULL && (rdch->flags & CHN_F_MMAP_INVALID))) {
1611c1891,1892
< return -1;
---
> PCM_GIANT_EXIT(d);
> return (-1);
1614,1617c1895,1899
< if (offset >= sndbuf_getsize(c->bufsoft)) {
< relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
< return -1;
< }
---
> /* XXX full-duplex quack. */
> if (wrch != NULL)
> wrch->flags |= CHN_F_MAPPED;
> if (rdch != NULL)
> rdch->flags |= CHN_F_MAPPED;
1619,1621d1900
< if (!(c->flags & CHN_F_MAPPED))
< c->flags |= CHN_F_MAPPED;
<
1625c1904,1906
< return 0;
---
> PCM_GIANT_LEAVE(d);
>
> return (0);
1709,1710c1990,1991
< for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])) &&
< unit == -1; i++) {
---
> for (i = 0; unit == -1 &&
> i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) {
1726c2007
< if (d == NULL || d->clones == NULL)
---
> if (!PCM_REGISTERED(d) || d->clones == NULL)
1728a2010,2011
> /* XXX Need Giant magic entry ??? */
>
1734a2018,2021
> PCM_WAIT(d);
> PCM_ACQUIRE(d);
> pcm_unlock(d);
>
1741c2028
< pcm_unlock(d);
---
> PCM_RELEASE_QUICK(d);
1791c2078
< pcm_unlock(d);
---
> PCM_RELEASE_QUICK(d);
1801d2087
< pcm_unlock(d);
1805d2090
< pcm_lock(d);
1808d2092
< pcm_unlock(d);
1809a2094,2095
> PCM_RELEASE_QUICK(d);
>
1899c2185
< int i, nchan, ret, *rates, minch, maxch;
---
> int i, nchan, *rates, minch, maxch;
1907,1908c2193,2194
< if ((ai->dev == -1) && (i_dev->si_devsw != &dsp_cdevsw))
< return EINVAL;
---
> if (ai->dev == -1 && i_dev->si_devsw != &dsp_cdevsw)
> return (EINVAL);
1913d2198
< ret = 0;
1923c2208
< if (d == NULL)
---
> if (!PCM_REGISTERED(d))
1925a2211,2212
> /* XXX Need Giant magic entry ??? */
>
1928d2214
< pcm_inprog(d, 1);
1935,1938c2221,2225
< if ((ch == PCM_RDCH(i_dev)) || /* record ch */
< (ch == PCM_WRCH(i_dev))) { /* playback ch */
< devname = i_dev->si_name;
< goto dspfound;
---
> if (DSP_REGISTERED(d, i_dev) &&
> (ch == PCM_RDCH(i_dev) || /* record ch */
> ch == PCM_WRCH(i_dev))) { /* playback ch */
> devname = dsp_unit2name(buf,
> sizeof(buf), ch->unit);
1943d2229
< goto dspfound;
1944a2231,2232
> if (devname != NULL)
> break;
1946,1948d2233
< /*
< * XXX I really doubt if this is correct.
< */
1952,1954c2237,2244
< pcm_unlock(d);
< pcm_inprog(d, -1);
< }
---
> if (devname != NULL) {
> /*
> * At this point, the following synchronization stuff
> * has happened:
> * - a specific PCM device is locked.
> * - a specific audio channel has been locked, so be
> * sure to unlock when exiting;
> */
1956,1957c2246
< /* Exhausted the search -- nothing is locked, so return. */
< return EINVAL;
---
> caps = chn_getcaps(ch);
1959,1964c2248,2252
< dspfound:
< /* Should've found the device, but something isn't right */
< if (devname == NULL) {
< ret = EINVAL;
< goto out;
< }
---
> /*
> * With all handles collected, zero out the user's
> * container and begin filling in its fields.
> */
> bzero((void *)ai, sizeof(oss_audioinfo));
1966,1973c2254,2255
< /*
< * At this point, the following synchronization stuff has happened:
< * - a specific PCM device is locked and its "in progress
< * operations" counter has been incremented, so be sure to unlock
< * and decrement when exiting;
< * - a specific audio channel has been locked, so be sure to unlock
< * when exiting;
< */
---
> ai->dev = nchan;
> strlcpy(ai->name, ch->name, sizeof(ai->name));
1975c2257,2260
< caps = chn_getcaps(ch);
---
> if ((ch->flags & CHN_F_BUSY) == 0)
> ai->busy = 0;
> else
> ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ;
1977,1981c2262,2269
< /*
< * With all handles collected, zero out the user's container and
< * begin filling in its fields.
< */
< bzero((void *)ai, sizeof(oss_audioinfo));
---
> /**
> * @note
> * @c cmd - OSSv4 docs: "Only supported under Linux at
> * this moment." Cop-out, I know, but I'll save
> * running around in the process table for later.
> * Is there a risk of leaking information?
> */
> ai->pid = ch->pid;
1983,1984c2271,2280
< ai->dev = nchan;
< strlcpy(ai->name, ch->name, sizeof(ai->name));
---
> /*
> * These flags stolen from SNDCTL_DSP_GETCAPS handler.
> * Note, however, that a single channel operates in
> * only one direction, so DSP_CAP_DUPLEX is out.
> */
> /**
> * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep
> * these in pcmchan::caps?
> */
> ai->caps = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
1986,1989c2282,2304
< if ((ch->flags & CHN_F_BUSY) == 0)
< ai->busy = 0;
< else
< ai->busy = (ch->direction == PCMDIR_PLAY) ? OPEN_WRITE : OPEN_READ;
---
> /*
> * Collect formats supported @b natively by the
> * device. Also determine min/max channels. (I.e.,
> * mono, stereo, or both?)
> *
> * If any channel is stereo, maxch = 2;
> * if all channels are stereo, minch = 2, too;
> * if any channel is mono, minch = 1;
> * and if all channels are mono, maxch = 1.
> */
> minch = 0;
> maxch = 0;
> fmts = 0;
> for (i = 0; caps->fmtlist[i]; i++) {
> fmts |= caps->fmtlist[i];
> if (caps->fmtlist[i] & AFMT_STEREO) {
> minch = (minch == 0) ? 2 : minch;
> maxch = 2;
> } else {
> minch = 1;
> maxch = (maxch == 0) ? 1 : maxch;
> }
> }
1991,1997c2306,2309
< /**
< * @note
< * @c cmd - OSSv4 docs: "Only supported under Linux at this moment."
< * Cop-out, I know, but I'll save running around in the process
< * table for later. Is there a risk of leaking information?
< */
< ai->pid = ch->pid;
---
> if (ch->direction == PCMDIR_PLAY)
> ai->oformats = fmts;
> else
> ai->iformats = fmts;
1999,2008c2311,2350
< /*
< * These flags stolen from SNDCTL_DSP_GETCAPS handler. Note, however,
< * that a single channel operates in only one direction, so
< * DSP_CAP_DUPLEX is out.
< */
< /**
< * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep these in
< * pcmchan::caps?
< */
< ai->caps = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
---
> /**
> * @note
> * @c magic - OSSv4 docs: "Reserved for internal use
> * by OSS."
> *
> * @par
> * @c card_number - OSSv4 docs: "Number of the sound
> * card where this device belongs or -1 if this
> * information is not available. Applications
> * should normally not use this field for any
> * purpose."
> */
> ai->card_number = -1;
> /**
> * @todo @c song_name - depends first on
> * SNDCTL_[GS]ETSONG @todo @c label - depends
> * on SNDCTL_[GS]ETLABEL
> * @todo @c port_number - routing information?
> */
> ai->port_number = -1;
> ai->mixer_dev = (d->mixer_dev != NULL) ? PCMUNIT(d->mixer_dev) : -1;
> /**
> * @note
> * @c real_device - OSSv4 docs: "Obsolete."
> */
> ai->real_device = -1;
> strlcpy(ai->devnode, devname, sizeof(ai->devnode));
> ai->enabled = device_is_attached(d->dev) ? 1 : 0;
> /**
> * @note
> * @c flags - OSSv4 docs: "Reserved for future use."
> *
> * @note
> * @c binding - OSSv4 docs: "Reserved for future use."
> *
> * @todo @c handle - haven't decided how to generate
> * this yet; bus, vendor, device IDs?
> */
> ai->min_rate = caps->minspeed;
> ai->max_rate = caps->maxspeed;
2010,2031c2352,2353
< /*
< * Collect formats supported @b natively by the device. Also
< * determine min/max channels. (I.e., mono, stereo, or both?)
< *
< * If any channel is stereo, maxch = 2;
< * if all channels are stereo, minch = 2, too;
< * if any channel is mono, minch = 1;
< * and if all channels are mono, maxch = 1.
< */
< minch = 0;
< maxch = 0;
< fmts = 0;
< for (i = 0; caps->fmtlist[i]; i++) {
< fmts |= caps->fmtlist[i];
< if (caps->fmtlist[i] & AFMT_STEREO) {
< minch = (minch == 0) ? 2 : minch;
< maxch = 2;
< } else {
< minch = 1;
< maxch = (maxch == 0) ? 1 : maxch;
< }
< }
---
> ai->min_channels = minch;
> ai->max_channels = maxch;
2033,2036c2355,2357
< if (ch->direction == PCMDIR_PLAY)
< ai->oformats = fmts;
< else
< ai->iformats = fmts;
---
> ai->nrates = chn_getrates(ch, &rates);
> if (ai->nrates > OSS_MAX_SAMPLE_RATES)
> ai->nrates = OSS_MAX_SAMPLE_RATES;
2038,2074c2359,2360
< /**
< * @note
< * @c magic - OSSv4 docs: "Reserved for internal use by OSS."
< *
< * @par
< * @c card_number - OSSv4 docs: "Number of the sound card where this
< * device belongs or -1 if this information is not available.
< * Applications should normally not use this field for any
< * purpose."
< */
< ai->card_number = -1;
< /**
< * @todo @c song_name - depends first on SNDCTL_[GS]ETSONG
< * @todo @c label - depends on SNDCTL_[GS]ETLABEL
< * @todo @c port_number - routing information?
< */
< ai->port_number = -1;
< ai->mixer_dev = (d->mixer_dev != NULL) ? PCMUNIT(d->mixer_dev) : -1;
< /**
< * @note
< * @c real_device - OSSv4 docs: "Obsolete."
< */
< ai->real_device = -1;
< strlcpy(ai->devnode, devname, sizeof(ai->devnode));
< ai->enabled = device_is_attached(d->dev) ? 1 : 0;
< /**
< * @note
< * @c flags - OSSv4 docs: "Reserved for future use."
< *
< * @note
< * @c binding - OSSv4 docs: "Reserved for future use."
< *
< * @todo @c handle - haven't decided how to generate this yet; bus,
< * vendor, device IDs?
< */
< ai->min_rate = caps->minspeed;
< ai->max_rate = caps->maxspeed;
---
> for (i = 0; i < ai->nrates; i++)
> ai->rates[i] = rates[i];
2076,2077c2362,2363
< ai->min_channels = minch;
< ai->max_channels = maxch;
---
> CHN_UNLOCK(ch);
> }
2079,2081c2365
< ai->nrates = chn_getrates(ch, &rates);
< if (ai->nrates > OSS_MAX_SAMPLE_RATES)
< ai->nrates = OSS_MAX_SAMPLE_RATES;
---
> pcm_unlock(d);
2083,2084c2367,2369
< for (i = 0; i < ai->nrates; i++)
< ai->rates[i] = rates[i];
---
> if (devname != NULL)
> return (0);
> }
2086,2091c2371,2372
< out:
< CHN_UNLOCK(ch);
< pcm_unlock(d);
< pcm_inprog(d, -1);
<
< return ret;
---
> /* Exhausted the search -- nothing is locked, so return. */
> return (EINVAL);
2262c2543
< return ret;
---
> return (ret);
2328c2609,2610
< ret = msleep(sm, &snd_pcm_syncgroups_mtx, PRIBIO | PCATCH, "pcmsgrp", timo);
---
> ret = msleep(sm, &snd_pcm_syncgroups_mtx,
> PRIBIO | PCATCH, "pcmsg", timo);
2366c2648
< return ret;
---
> return (ret);
2405c2687
< return EIO;
---
> return (EIO);
2425c2707
< return ret;
---
> return (ret);
2458c2740
< return EINVAL;
---
> return (EINVAL);
2481c2763
< return EINVAL;
---
> return (EINVAL);
2501c2783
< return EINVAL;
---
> return (EINVAL);
2529c2811
< return EINVAL;
---
> return (EINVAL);
2553c2835
< return EINVAL;
---
> return (EINVAL);
2578c2860
< return EINVAL;
---
> return (EINVAL);
2603c2885
< return EINVAL;
---
> return (EINVAL);
2632c2914
< return EINVAL;
---
> return (EINVAL);