diff options
Diffstat (limited to 'sys/dev/sound/pcm/dsp.c')
-rw-r--r-- | sys/dev/sound/pcm/dsp.c | 855 |
1 files changed, 635 insertions, 220 deletions
diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c index 56c1aaf..66faef1 100644 --- a/sys/dev/sound/pcm/dsp.c +++ b/sys/dev/sound/pcm/dsp.c @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> + * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> + * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 + * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,23 +26,31 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + #include <dev/sound/pcm/sound.h> #include <sys/ctype.h> +#include <sys/sysent.h> SND_DECLARE_FILE("$FreeBSD$"); 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"); + &dsp_mmap_allow_prot_exec, 0, + "linux mmap compatibility (-1=force disable 0=auto 1=force enable)"); struct dsp_cdevinfo { struct pcm_channel *rdch, *wrch; + struct pcm_channel *volch; int busy, simplex; TAILQ_ENTRY(dsp_cdevinfo) link; }; #define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch) #define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch) +#define PCM_VOLCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->volch) #define PCM_SIMPLEX(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->simplex) #define DSP_CDEVINFO_CACHESIZE 8 @@ -70,19 +80,18 @@ struct cdevsw dsp_cdevsw = { .d_name = "dsp", }; -#ifdef USING_DEVFS static eventhandler_tag dsp_ehtag = NULL; static int dsp_umax = -1; static int dsp_cmax = -1; -#endif static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group); static int dsp_oss_syncstart(int sg_id); static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy); -#ifdef OSSV4_EXPERIMENT static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled); static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); +static int dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, int *mask); +#ifdef OSSV4_EXPERIMENT static int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); static int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); static int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song); @@ -133,7 +142,7 @@ getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, d = dsp_get_info(dev); if (!PCM_REGISTERED(d)) return (ENXIO); - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); PCM_ACQUIRE(d); /* @@ -173,7 +182,7 @@ getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, pcm_chnrelease(ch); } PCM_RELEASE(d); - pcm_unlock(d); + PCM_UNLOCK(d); } *rdch = PCM_RDCH(dev); @@ -200,7 +209,8 @@ relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, static void dsp_cdevinfo_alloc(struct cdev *dev, - struct pcm_channel *rdch, struct pcm_channel *wrch) + struct pcm_channel *rdch, struct pcm_channel *wrch, + struct pcm_channel *volch) { struct snddev_info *d; struct dsp_cdevinfo *cdi; @@ -209,10 +219,10 @@ dsp_cdevinfo_alloc(struct cdev *dev, d = dsp_get_info(dev); KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 == NULL && - rdch != wrch, + ((rdch == NULL && wrch == NULL) || rdch != wrch), ("bogus %s(), what are you trying to accomplish here?", __func__)); PCM_BUSYASSERT(d); - mtx_assert(d->lock, MA_OWNED); + PCM_LOCKASSERT(d); simplex = (dsp_get_flags(dev) & SD_F_SIMPLEX) ? 1 : 0; @@ -225,6 +235,7 @@ dsp_cdevinfo_alloc(struct cdev *dev, break; cdi->rdch = rdch; cdi->wrch = wrch; + cdi->volch = volch; cdi->simplex = simplex; cdi->busy = 1; TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link); @@ -232,11 +243,12 @@ dsp_cdevinfo_alloc(struct cdev *dev, dev->si_drv1 = cdi; return; } - pcm_unlock(d); + PCM_UNLOCK(d); cdi = malloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO); - pcm_lock(d); + PCM_LOCK(d); cdi->rdch = rdch; cdi->wrch = wrch; + cdi->volch = volch; cdi->simplex = simplex; cdi->busy = 1; TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link); @@ -254,15 +266,17 @@ dsp_cdevinfo_free(struct cdev *dev) d = dsp_get_info(dev); KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 != NULL && - PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL, + PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL && + PCM_VOLCH(dev) == NULL, ("bogus %s(), what are you trying to accomplish here?", __func__)); PCM_BUSYASSERT(d); - mtx_assert(d->lock, MA_OWNED); + PCM_LOCKASSERT(d); cdi = dev->si_drv1; dev->si_drv1 = NULL; cdi->rdch = NULL; cdi->wrch = NULL; + cdi->volch = NULL; cdi->simplex = 0; cdi->busy = 0; @@ -306,7 +320,7 @@ dsp_cdevinfo_init(struct snddev_info *d) KASSERT(d != NULL, ("NULL snddev_info")); PCM_BUSYASSERT(d); - mtx_assert(d->lock, MA_NOTOWNED); + PCM_UNLOCKASSERT(d); TAILQ_INIT(&d->dsp_cdevinfo_pool); for (i = 0; i < DSP_CDEVINFO_CACHESIZE; i++) { @@ -322,7 +336,7 @@ dsp_cdevinfo_flush(struct snddev_info *d) KASSERT(d != NULL, ("NULL snddev_info")); PCM_BUSYASSERT(d); - mtx_assert(d->lock, MA_NOTOWNED); + PCM_UNLOCKASSERT(d); cdi = TAILQ_FIRST(&d->dsp_cdevinfo_pool); while (cdi != NULL) { @@ -337,7 +351,13 @@ dsp_cdevinfo_flush(struct snddev_info *d) enum { DSP_CDEV_TYPE_RDONLY, /* simplex read-only (record) */ DSP_CDEV_TYPE_WRONLY, /* simplex write-only (play) */ - DSP_CDEV_TYPE_RDWR, /* duplex read, write, or both */ + DSP_CDEV_TYPE_RDWR /* duplex read, write, or both */ +}; + +enum { + DSP_CDEV_VOLCTL_NONE, + DSP_CDEV_VOLCTL_READ, + DSP_CDEV_VOLCTL_WRITE }; #define DSP_F_VALID(x) ((x) & (FREAD | FWRITE)) @@ -350,30 +370,49 @@ static const struct { int type; char *name; char *sep; + char *alias; int use_sep; int hw; int max; + int volctl; uint32_t fmt, spd; int query; } dsp_cdevs[] = { - { SND_DEV_DSP, "dsp", ".", 0, 0, 0, - AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_AUDIO, "audio", ".", 0, 0, 0, - AFMT_MU_LAW, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSP16, "dspW", ".", 0, 0, 0, - AFMT_S16_LE, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSPHW_PLAY, "dsp", ".p", 1, 1, SND_MAXHWCHAN, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, - { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", 1, 1, SND_MAXVCHANS, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, - { SND_DEV_DSPHW_REC, "dsp", ".r", 1, 1, SND_MAXHWCHAN, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, - { SND_DEV_DSPHW_VREC, "dsp", ".vr", 1, 1, SND_MAXVCHANS, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, - { 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 }, + { SND_DEV_DSP, "dsp", ".", NULL, 0, 0, 0, 0, + SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_AUDIO, "audio", ".", NULL, 0, 0, 0, 0, + SND_FORMAT(AFMT_MU_LAW, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP16, "dspW", ".", NULL, 0, 0, 0, 0, + SND_FORMAT(AFMT_S16_LE, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSPHW_PLAY, "dsp", ".p", NULL, 1, 1, SND_MAXHWCHAN, 1, + SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY }, + { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", NULL, 1, 1, SND_MAXVCHANS, 1, + SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY }, + { SND_DEV_DSPHW_REC, "dsp", ".r", NULL, 1, 1, SND_MAXHWCHAN, 1, + SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY }, + { SND_DEV_DSPHW_VREC, "dsp", ".vr", NULL, 1, 1, SND_MAXVCHANS, 1, + SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY }, + { SND_DEV_DSPHW_CD, "dspcd", ".", NULL, 0, 0, 0, 0, + SND_FORMAT(AFMT_S16_LE, 2, 0), 44100, DSP_CDEV_TYPE_RDWR }, + /* Low priority, OSSv4 aliases. */ + { SND_DEV_DSP, "dsp_ac3", ".", "dsp", 0, 0, 0, 0, + SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_mmap", ".", "dsp", 0, 0, 0, 0, + SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_multich", ".", "dsp", 0, 0, 0, 0, + SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_spdifout", ".", "dsp", 0, 0, 0, 0, + SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_spdifin", ".", "dsp", 0, 0, 0, 0, + SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, }; #define DSP_FIXUP_ERROR() do { \ @@ -390,14 +429,14 @@ static const struct { error = EBUSY; \ else if (DSP_REGISTERED(d, i_dev)) \ error = EBUSY; \ -} while(0) +} while (0) static int dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) { struct pcm_channel *rdch, *wrch; struct snddev_info *d; - uint32_t fmt, spd, prio; + uint32_t fmt, spd, prio, volctl; int i, error, rderror, wrerror, devtype, wdevunit, rdevunit; /* Kind of impossible.. */ @@ -411,7 +450,7 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) PCM_GIANT_ENTER(d); /* Lock snddev so nobody else can monkey with it. */ - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); /* @@ -421,7 +460,7 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) error = snd_clone_acquire(i_dev); if (!(error == 0 || error == ENODEV)) { DSP_FIXUP_ERROR(); - pcm_unlock(d); + PCM_UNLOCK(d); PCM_GIANT_EXIT(d); return (error); } @@ -431,7 +470,7 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) if (error != 0) { (void)snd_clone_release(i_dev); - pcm_unlock(d); + PCM_UNLOCK(d); PCM_GIANT_EXIT(d); return (error); } @@ -442,34 +481,53 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) * everything. */ PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); devtype = PCMDEV(i_dev); wdevunit = -1; rdevunit = -1; fmt = 0; spd = 0; + volctl = DSP_CDEV_VOLCTL_NONE; for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { - if (devtype != dsp_cdevs[i].type) + if (devtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) continue; - if (DSP_F_SIMPLEX(flags) && - ((dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY && - DSP_F_READ(flags)) || - (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY && - DSP_F_WRITE(flags)))) { - /* - * simplex, opposite direction? Please be gone.. - */ - (void)snd_clone_release(i_dev); - PCM_RELEASE_QUICK(d); - PCM_GIANT_EXIT(d); - return (ENOTSUP); - } - if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) + /* + * Volume control only valid for DSPHW devices, + * and it must be opened in opposite direction be it + * simplex or duplex. Anything else will be handled + * as usual. + */ + if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) { + if (dsp_cdevs[i].volctl != 0 && + DSP_F_READ(flags)) { + volctl = DSP_CDEV_VOLCTL_WRITE; + flags &= ~FREAD; + flags |= FWRITE; + } + if (DSP_F_READ(flags)) { + (void)snd_clone_release(i_dev); + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (ENOTSUP); + } wdevunit = dev2unit(i_dev); - else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) + } else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) { + if (dsp_cdevs[i].volctl != 0 && + DSP_F_WRITE(flags)) { + volctl = DSP_CDEV_VOLCTL_READ; + flags &= ~FWRITE; + flags |= FREAD; + } + if (DSP_F_WRITE(flags)) { + (void)snd_clone_release(i_dev); + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (ENOTSUP); + } rdevunit = dev2unit(i_dev); + } fmt = dsp_cdevs[i].fmt; spd = dsp_cdevs[i].spd; break; @@ -493,12 +551,14 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) if (DSP_F_READ(flags)) { /* open for read */ rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC, - td->td_proc->p_pid, rdevunit); + td->td_proc->p_pid, td->td_proc->p_comm, rdevunit); - if (rderror == 0 && (chn_reset(rdch, fmt) != 0 || - (chn_setspeed(rdch, spd) != 0))) + if (rderror == 0 && chn_reset(rdch, fmt, spd) != 0) rderror = ENXIO; + if (volctl == DSP_CDEV_VOLCTL_READ) + rderror = 0; + if (rderror != 0) { if (rdch != NULL) pcm_chnrelease(rdch); @@ -509,10 +569,19 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) return (rderror); } rdch = NULL; + } else if (volctl == DSP_CDEV_VOLCTL_READ) { + if (rdch != NULL) { + pcm_chnref(rdch, 1); + pcm_chnrelease(rdch); + } } else { if (flags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO; + if (flags & O_EXCL) + rdch->flags |= CHN_F_EXCLUSIVE; pcm_chnref(rdch, 1); + if (volctl == DSP_CDEV_VOLCTL_NONE) + chn_vpc_reset(rdch, SND_VOL_C_PCM, 0); CHN_UNLOCK(rdch); } } @@ -520,12 +589,14 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) if (DSP_F_WRITE(flags)) { /* open for write */ wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, - td->td_proc->p_pid, wdevunit); + td->td_proc->p_pid, td->td_proc->p_comm, wdevunit); - if (wrerror == 0 && (chn_reset(wrch, fmt) != 0 || - (chn_setspeed(wrch, spd) != 0))) + if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0) wrerror = ENXIO; + if (volctl == DSP_CDEV_VOLCTL_WRITE) + wrerror = 0; + if (wrerror != 0) { if (wrch != NULL) pcm_chnrelease(wrch); @@ -545,27 +616,58 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) return (wrerror); } wrch = NULL; + } else if (volctl == DSP_CDEV_VOLCTL_WRITE) { + if (wrch != NULL) { + pcm_chnref(wrch, 1); + pcm_chnrelease(wrch); + } } else { if (flags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO; + if (flags & O_EXCL) + wrch->flags |= CHN_F_EXCLUSIVE; pcm_chnref(wrch, 1); + if (volctl == DSP_CDEV_VOLCTL_NONE) + chn_vpc_reset(wrch, SND_VOL_C_PCM, 0); CHN_UNLOCK(wrch); } } - if (rdch == NULL && wrch == NULL) { - (void)snd_clone_release(i_dev); - PCM_RELEASE_QUICK(d); - PCM_GIANT_EXIT(d); - return ((wrerror != 0) ? wrerror : rderror); - } - pcm_lock(d); + PCM_LOCK(d); /* * We're done. Allocate channels information for this cdev. */ - dsp_cdevinfo_alloc(i_dev, rdch, wrch); + switch (volctl) { + case DSP_CDEV_VOLCTL_READ: + KASSERT(wrch == NULL, ("wrch=%p not null!", wrch)); + dsp_cdevinfo_alloc(i_dev, NULL, NULL, rdch); + break; + case DSP_CDEV_VOLCTL_WRITE: + KASSERT(rdch == NULL, ("rdch=%p not null!", rdch)); + dsp_cdevinfo_alloc(i_dev, NULL, NULL, wrch); + break; + case DSP_CDEV_VOLCTL_NONE: + default: + if (wrch == NULL && rdch == NULL) { + (void)snd_clone_release(i_dev); + PCM_RELEASE(d); + PCM_UNLOCK(d); + PCM_GIANT_EXIT(d); + if (wrerror != 0) + return (wrerror); + if (rderror != 0) + return (rderror); + return (EINVAL); + } + dsp_cdevinfo_alloc(i_dev, rdch, wrch, NULL); + if (rdch != NULL) + CHN_INSERT_HEAD(d, rdch, channels.pcm.opened); + if (wrch != NULL) + CHN_INSERT_HEAD(d, wrch, channels.pcm.opened); + break; + } /* * Increase clone refcount for its automatic garbage collector. @@ -573,7 +675,7 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) (void)snd_clone_ref(i_dev); PCM_RELEASE(d); - pcm_unlock(d); + PCM_UNLOCK(d); PCM_GIANT_LEAVE(d); @@ -583,9 +685,9 @@ dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td) static int dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) { - struct pcm_channel *rdch, *wrch; + struct pcm_channel *rdch, *wrch, *volch; struct snddev_info *d; - int sg_ids, refs; + int sg_ids, rdref, wdref; d = dsp_get_info(i_dev); if (!DSP_REGISTERED(d, i_dev)) @@ -593,26 +695,51 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) PCM_GIANT_ENTER(d); - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); + PCM_ACQUIRE(d); rdch = PCM_RDCH(i_dev); wrch = PCM_WRCH(i_dev); + volch = PCM_VOLCH(i_dev); - if (rdch || wrch) { - PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_RDCH(i_dev) = NULL; + PCM_WRCH(i_dev) = NULL; + PCM_VOLCH(i_dev) = NULL; - refs = 0; - if (rdch) { + rdref = -1; + wdref = -1; + + if (volch != NULL) { + if (volch == rdch) + rdref--; + else if (volch == wrch) + wdref--; + else { + CHN_LOCK(volch); + pcm_chnref(volch, -1); + CHN_UNLOCK(volch); + } + } + + if (rdch != NULL) + CHN_REMOVE(d, rdch, channels.pcm.opened); + if (wrch != NULL) + CHN_REMOVE(d, wrch, channels.pcm.opened); + + if (rdch != NULL || wrch != NULL) { + PCM_UNLOCK(d); + if (rdch != NULL) { /* * The channel itself need not be locked because: - * a) Adding a channel to a syncgroup happens only in dsp_ioctl(), - * which cannot run concurrently to dsp_close(). - * b) The syncmember pointer (sm) is protected by the global - * syncgroup list lock. - * c) A channel can't just disappear, invalidating pointers, - * unless it's closed/dereferenced first. + * a) Adding a channel to a syncgroup happens only + * in dsp_ioctl(), which cannot run concurrently + * to dsp_close(). + * b) The syncmember pointer (sm) is protected by + * the global syncgroup list lock. + * c) A channel can't just disappear, invalidating + * pointers, unless it's closed/dereferenced + * first. */ PCM_SG_LOCK(); sg_ids = chn_syncdestroy(rdch); @@ -621,14 +748,14 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) free_unr(pcmsg_unrhdr, sg_ids); CHN_LOCK(rdch); - refs += pcm_chnref(rdch, -1); + pcm_chnref(rdch, rdref); chn_abort(rdch); /* won't sleep */ - rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); - chn_reset(rdch, 0); + rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP | + CHN_F_DEAD | CHN_F_EXCLUSIVE); + chn_reset(rdch, 0, 0); pcm_chnrelease(rdch); - PCM_RDCH(i_dev) = NULL; } - if (wrch) { + if (wrch != NULL) { /* * Please see block above. */ @@ -639,41 +766,34 @@ dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) free_unr(pcmsg_unrhdr, sg_ids); CHN_LOCK(wrch); - refs += pcm_chnref(wrch, -1); + pcm_chnref(wrch, wdref); chn_flush(wrch); /* may sleep */ - wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); - chn_reset(wrch, 0); + wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MMAP | + CHN_F_DEAD | CHN_F_EXCLUSIVE); + chn_reset(wrch, 0, 0); pcm_chnrelease(wrch); - PCM_WRCH(i_dev) = NULL; } + PCM_LOCK(d); + } - pcm_lock(d); - /* - * If there are no more references, release the channels. - */ - if (refs == 0 && PCM_RDCH(i_dev) == NULL && - PCM_WRCH(i_dev) == NULL) { - dsp_cdevinfo_free(i_dev); - /* - * Release clone busy state and unref it - * so the automatic garbage collector will - * get the hint and do the remaining cleanup - * process. - */ - (void)snd_clone_release(i_dev); + dsp_cdevinfo_free(i_dev); + /* + * Release clone busy state and unref it so the automatic + * garbage collector will get the hint and do the remaining + * cleanup process. + */ + (void)snd_clone_release(i_dev); - /* - * destroy_dev() might sleep, so release pcm lock - * here and rely on pcm cv serialization. - */ - pcm_unlock(d); - (void)snd_clone_unref(i_dev); - pcm_lock(d); - } - PCM_RELEASE(d); - } + /* + * destroy_dev() might sleep, so release pcm lock + * here and rely on pcm cv serialization. + */ + PCM_UNLOCK(d); + (void)snd_clone_unref(i_dev); + PCM_LOCK(d); - pcm_unlock(d); + PCM_RELEASE(d); + PCM_UNLOCK(d); PCM_GIANT_LEAVE(d); @@ -726,7 +846,7 @@ dsp_io_ops(struct cdev *i_dev, struct uio *buf) return (EBADF); } - if (((*ch)->flags & (CHN_F_MAPPED | CHN_F_DEAD)) || + if (((*ch)->flags & (CHN_F_MMAP | CHN_F_DEAD)) || (((*ch)->flags & CHN_F_RUNNING) && (*ch)->pid != runpid)) { relchns(i_dev, rdch, wrch, prio); PCM_GIANT_EXIT(d); @@ -767,11 +887,175 @@ dsp_write(struct cdev *i_dev, struct uio *buf, int flag) } static int -dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) +dsp_get_volume_channel(struct cdev *dev, struct pcm_channel **volch) +{ + struct snddev_info *d; + struct pcm_channel *c; + int unit; + + KASSERT(dev != NULL && volch != NULL, + ("%s(): NULL query dev=%p volch=%p", __func__, dev, volch)); + + d = dsp_get_info(dev); + if (!PCM_REGISTERED(d)) { + *volch = NULL; + return (EINVAL); + } + + PCM_UNLOCKASSERT(d); + + *volch = NULL; + + c = PCM_VOLCH(dev); + if (c != NULL) { + if (!(c->feederflags & (1 << FEEDER_VOLUME))) + return (-1); + *volch = c; + return (0); + } + + PCM_LOCK(d); + PCM_WAIT(d); + PCM_ACQUIRE(d); + + unit = dev2unit(dev); + + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + if (c->unit != unit) { + CHN_UNLOCK(c); + continue; + } + *volch = c; + pcm_chnref(c, 1); + PCM_VOLCH(dev) = c; + CHN_UNLOCK(c); + PCM_RELEASE(d); + PCM_UNLOCK(d); + return ((c->feederflags & (1 << FEEDER_VOLUME)) ? 0 : -1); + } + + PCM_RELEASE(d); + PCM_UNLOCK(d); + + return (EINVAL); +} + +static int +dsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd, + caddr_t arg) +{ + struct snddev_info *d; + struct pcm_channel *rdch, *wrch; + int j, devtype, ret; + + d = dsp_get_info(dev); + if (!PCM_REGISTERED(d) || !(dsp_get_flags(dev) & SD_F_VPC)) + return (-1); + + PCM_UNLOCKASSERT(d); + + j = cmd & 0xff; + + rdch = PCM_RDCH(dev); + wrch = PCM_WRCH(dev); + + /* No specific channel, look into cache */ + if (volch == NULL) + volch = PCM_VOLCH(dev); + + /* Look harder */ + if (volch == NULL) { + if (j == SOUND_MIXER_RECLEV && rdch != NULL) + volch = rdch; + else if (j == SOUND_MIXER_PCM && wrch != NULL) + volch = wrch; + } + + devtype = PCMDEV(dev); + + /* Look super harder */ + if (volch == NULL && + (devtype == SND_DEV_DSPHW_PLAY || devtype == SND_DEV_DSPHW_VPLAY || + devtype == SND_DEV_DSPHW_REC || devtype == SND_DEV_DSPHW_VREC)) { + ret = dsp_get_volume_channel(dev, &volch); + if (ret != 0) + return (ret); + if (volch == NULL) + return (EINVAL); + } + + /* Final validation */ + if (volch != NULL) { + CHN_LOCK(volch); + if (!(volch->feederflags & (1 << FEEDER_VOLUME))) { + CHN_UNLOCK(volch); + return (-1); + } + if (volch->direction == PCMDIR_PLAY) + wrch = volch; + else + rdch = volch; + } + + ret = EINVAL; + + if (volch != NULL && + ((j == SOUND_MIXER_PCM && volch->direction == PCMDIR_PLAY) || + (j == SOUND_MIXER_RECLEV && volch->direction == PCMDIR_REC))) { + if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { + int left, right, center; + + left = *(int *)arg & 0x7f; + right = ((*(int *)arg) >> 8) & 0x7f; + center = (left + right) >> 1; + chn_setvolume_multi(volch, SND_VOL_C_PCM, left, right, + center); + } else if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = CHN_GETVOLUME(volch, + SND_VOL_C_PCM, SND_CHN_T_FL); + *(int *)arg |= CHN_GETVOLUME(volch, + SND_VOL_C_PCM, SND_CHN_T_FR) << 8; + } + ret = 0; + } else if (rdch != NULL || wrch != NULL) { + switch (j) { + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_CAPS: + case SOUND_MIXER_STEREODEVS: + if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = 0; + if (rdch != NULL) + *(int *)arg |= SOUND_MASK_RECLEV; + if (wrch != NULL) + *(int *)arg |= SOUND_MASK_PCM; + } + ret = 0; + break; + case SOUND_MIXER_RECMASK: + case SOUND_MIXER_RECSRC: + if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) + *(int *)arg = 0; + ret = 0; + break; + default: + break; + } + } + + if (volch != NULL) + CHN_UNLOCK(volch); + + return (ret); +} + +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 *arg_i, ret, kill, tmp, xcmd; + int *arg_i, ret, tmp, xcmd; d = dsp_get_info(i_dev); if (!DSP_REGISTERED(d, i_dev)) @@ -782,15 +1066,15 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * arg_i = (int *)arg; ret = 0; xcmd = 0; + chn = NULL; - /* - * this is an evil hack to allow broken apps to perform mixer ioctls - * on dsp devices. - */ if (IOCGROUP(cmd) == 'M') { - /* - * This is at least, a bug to bug compatible with OSS. - */ + ret = dsp_ioctl_channel(i_dev, PCM_VOLCH(i_dev), cmd, arg); + if (ret != -1) { + PCM_GIANT_EXIT(d); + return (ret); + } + if (d->mixer_dev != NULL) { PCM_ACQUIRE_QUICK(d); ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td, @@ -835,23 +1119,12 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * getchns(i_dev, &rdch, &wrch, 0); - kill = 0; - if (wrch && (wrch->flags & CHN_F_DEAD)) - kill |= 1; - if (rdch && (rdch->flags & CHN_F_DEAD)) - kill |= 2; - if (kill == 3) { - relchns(i_dev, rdch, wrch, 0); - PCM_GIANT_EXIT(d); - return (EINVAL); - } - if (kill & 1) + if (wrch != NULL && (wrch->flags & CHN_F_DEAD)) wrch = NULL; - if (kill & 2) + if (rdch != NULL && (rdch->flags & CHN_F_DEAD)) rdch = NULL; if (wrch == NULL && rdch == NULL) { - relchns(i_dev, rdch, wrch, 0); PCM_GIANT_EXIT(d); return (EINVAL); } @@ -930,11 +1203,14 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * if (wrch) { CHN_LOCK(wrch); if (cmd == AIOSFMT && p->play_format != 0) { - chn_setformat(wrch, p->play_format); + chn_setformat(wrch, + SND_FORMAT(p->play_format, + AFMT_CHANNEL(wrch->format), + AFMT_EXTCHANNEL(wrch->format))); chn_setspeed(wrch, p->play_rate); } p->play_rate = wrch->speed; - p->play_format = wrch->format; + p->play_format = AFMT_ENCODING(wrch->format); CHN_UNLOCK(wrch); } else { p->play_rate = 0; @@ -943,11 +1219,14 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * if (rdch) { CHN_LOCK(rdch); if (cmd == AIOSFMT && p->rec_format != 0) { - chn_setformat(rdch, p->rec_format); + chn_setformat(rdch, + SND_FORMAT(p->rec_format, + AFMT_CHANNEL(rdch->format), + AFMT_EXTCHANNEL(rdch->format))); chn_setspeed(rdch, p->rec_rate); } p->rec_rate = rdch->speed; - p->rec_format = rdch->format; + p->rec_format = AFMT_ENCODING(rdch->format); CHN_UNLOCK(rdch); } else { p->rec_rate = 0; @@ -963,7 +1242,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * struct pcmchan_caps *pcaps = NULL, *rcaps = NULL; struct cdev *pdev; - pcm_lock(d); + PCM_LOCK(d); if (rdch) { CHN_LOCK(rdch); rcaps = chn_getcaps(rdch); @@ -991,7 +1270,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * CHN_UNLOCK(wrch); if (rdch) CHN_UNLOCK(rdch); - pcm_unlock(d); + PCM_UNLOCK(d); } break; @@ -1150,19 +1429,21 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * case SNDCTL_DSP_STEREO: tmp = -1; - *arg_i = (*arg_i)? AFMT_STEREO : 0; + *arg_i = (*arg_i)? 2 : 1; PCM_ACQUIRE_QUICK(d); if (wrch) { CHN_LOCK(wrch); - ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); - tmp = (wrch->format & AFMT_STEREO)? 1 : 0; + ret = chn_setformat(wrch, + SND_FORMAT(wrch->format, *arg_i, 0)); + tmp = (AFMT_CHANNEL(wrch->format) > 1)? 1 : 0; CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); - ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i); + ret = chn_setformat(rdch, + SND_FORMAT(rdch->format, *arg_i, 0)); if (tmp == -1) - tmp = (rdch->format & AFMT_STEREO)? 1 : 0; + tmp = (AFMT_CHANNEL(rdch->format) > 1)? 1 : 0; CHN_UNLOCK(rdch); } PCM_RELEASE_QUICK(d); @@ -1171,21 +1452,39 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * case SOUND_PCM_WRITE_CHANNELS: /* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */ + if (*arg_i < 0) { + *arg_i = 0; + ret = EINVAL; + break; + } if (*arg_i != 0) { + struct pcmchan_matrix *m; + uint32_t ext; + tmp = 0; - *arg_i = (*arg_i != 1)? AFMT_STEREO : 0; + if (*arg_i > SND_CHN_MAX) + *arg_i = SND_CHN_MAX; + + m = feeder_matrix_default_channel_map(*arg_i); + if (m != NULL) + ext = m->ext; + else + ext = 0; + PCM_ACQUIRE_QUICK(d); if (wrch) { CHN_LOCK(wrch); - ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); - tmp = (wrch->format & AFMT_STEREO)? 2 : 1; + ret = chn_setformat(wrch, + SND_FORMAT(wrch->format, *arg_i, ext)); + tmp = AFMT_CHANNEL(wrch->format); CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); - ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i); + ret = chn_setformat(rdch, + SND_FORMAT(rdch->format, *arg_i, ext)); if (tmp == 0) - tmp = (rdch->format & AFMT_STEREO)? 2 : 1; + tmp = AFMT_CHANNEL(rdch->format); CHN_UNLOCK(rdch); } PCM_RELEASE_QUICK(d); @@ -1193,7 +1492,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * } else { chn = wrch ? wrch : rdch; CHN_LOCK(chn); - *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; + *arg_i = AFMT_CHANNEL(chn->format); CHN_UNLOCK(chn); } break; @@ -1202,7 +1501,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * chn = wrch ? wrch : rdch; if (chn) { CHN_LOCK(chn); - *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; + *arg_i = AFMT_CHANNEL(chn->format); CHN_UNLOCK(chn); } else { *arg_i = 0; @@ -1223,28 +1522,32 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * break; case SNDCTL_DSP_SETFMT: /* sets _one_ format */ - if ((*arg_i != AFMT_QUERY)) { + if (*arg_i != AFMT_QUERY) { tmp = 0; PCM_ACQUIRE_QUICK(d); if (wrch) { CHN_LOCK(wrch); - ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO)); - tmp = wrch->format & ~AFMT_STEREO; + ret = chn_setformat(wrch, SND_FORMAT(*arg_i, + AFMT_CHANNEL(wrch->format), + AFMT_EXTCHANNEL(wrch->format))); + tmp = wrch->format; CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); - ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO)); + ret = chn_setformat(rdch, SND_FORMAT(*arg_i, + AFMT_CHANNEL(rdch->format), + AFMT_EXTCHANNEL(rdch->format))); if (tmp == 0) - tmp = rdch->format & ~AFMT_STEREO; + tmp = rdch->format; CHN_UNLOCK(rdch); } PCM_RELEASE_QUICK(d); - *arg_i = tmp; + *arg_i = AFMT_ENCODING(tmp); } else { chn = wrch ? wrch : rdch; CHN_LOCK(chn); - *arg_i = chn->format & ~AFMT_STEREO; + *arg_i = AFMT_ENCODING(chn->format); CHN_UNLOCK(chn); } break; @@ -1374,11 +1677,11 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * break; case SNDCTL_DSP_GETCAPS: - pcm_lock(d); + PCM_LOCK(d); *arg_i = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER; if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX)) *arg_i |= PCM_CAP_DUPLEX; - pcm_unlock(d); + PCM_UNLOCK(d); break; case SOUND_PCM_READ_BITS: @@ -1471,10 +1774,10 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * * switch to full-duplex mode if card is in half-duplex * mode and is able to work in full-duplex mode */ - pcm_lock(d); + PCM_LOCK(d); if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX)) dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX); - pcm_unlock(d); + PCM_UNLOCK(d); break; /* @@ -1483,20 +1786,34 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * * command". */ case SNDCTL_DSP_GETRECVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_READ_RECLEV; + chn = rdch; + } /* FALLTHROUGH */ case SNDCTL_DSP_SETRECVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_WRITE_RECLEV; + chn = rdch; + } /* FALLTHROUGH */ case SNDCTL_DSP_GETPLAYVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_READ_PCM; + chn = wrch; + } /* FALLTHROUGH */ case SNDCTL_DSP_SETPLAYVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_WRITE_PCM; + chn = wrch; + } + + ret = dsp_ioctl_channel(i_dev, chn, xcmd, arg); + if (ret != -1) { + PCM_GIANT_EXIT(d); + return (ret); + } if (d->mixer_dev != NULL) { PCM_ACQUIRE_QUICK(d); @@ -1505,6 +1822,7 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * PCM_RELEASE_QUICK(d); } else ret = ENOTSUP; + break; case SNDCTL_DSP_GET_RECSRC_NAMES: @@ -1634,11 +1952,11 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * bs = chn->bufsoft; #if 0 tmp = (sndbuf_getsize(b) + chn_getptr(chn) - sndbuf_gethwptr(b)) % sndbuf_getsize(b); - oc->samples = (sndbuf_gettotal(b) + tmp) / sndbuf_getbps(b); - oc->fifo_samples = (sndbuf_getready(b) - tmp) / sndbuf_getbps(b); + oc->samples = (sndbuf_gettotal(b) + tmp) / sndbuf_getalign(b); + oc->fifo_samples = (sndbuf_getready(b) - tmp) / sndbuf_getalign(b); #else - oc->samples = sndbuf_gettotal(bs) / sndbuf_getbps(bs); - oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getbps(bs); + oc->samples = sndbuf_gettotal(bs) / sndbuf_getalign(bs); + oc->fifo_samples = sndbuf_getready(bs) / sndbuf_getalign(bs); #endif CHN_UNLOCK(chn); } @@ -1719,6 +2037,30 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * PCM_RELEASE_QUICK(d); break; + case SNDCTL_DSP_COOKEDMODE: + PCM_ACQUIRE_QUICK(d); + if (!(dsp_get_flags(i_dev) & SD_F_BITPERFECT)) + ret = dsp_oss_cookedmode(wrch, rdch, *arg_i); + PCM_RELEASE_QUICK(d); + break; + case SNDCTL_DSP_GET_CHNORDER: + PCM_ACQUIRE_QUICK(d); + ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg); + PCM_RELEASE_QUICK(d); + break; + case SNDCTL_DSP_SET_CHNORDER: + PCM_ACQUIRE_QUICK(d); + ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg); + PCM_RELEASE_QUICK(d); + break; + case SNDCTL_DSP_GETCHANNELMASK: /* XXX vlc */ + PCM_ACQUIRE_QUICK(d); + ret = dsp_oss_getchannelmask(wrch, rdch, (int *)arg); + PCM_RELEASE_QUICK(d); + break; + case SNDCTL_DSP_BIND_CHANNEL: /* XXX what?!? */ + ret = EINVAL; + break; #ifdef OSSV4_EXPERIMENT /* * XXX The following ioctls are not yet supported and just return @@ -1749,15 +2091,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * * XXX Once implemented, revisit this for proper cv protection * (if necessary). */ - case SNDCTL_DSP_COOKEDMODE: - ret = dsp_oss_cookedmode(wrch, rdch, *arg_i); - break; - case SNDCTL_DSP_GET_CHNORDER: - ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg); - break; - case SNDCTL_DSP_SET_CHNORDER: - ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg); - break; case SNDCTL_GETLABEL: ret = dsp_oss_getlabel(wrch, rdch, (oss_label_t *)arg); break; @@ -1802,8 +2135,6 @@ dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread * break; } - relchns(i_dev, rdch, wrch, 0); - PCM_GIANT_LEAVE(d); return (ret); @@ -1861,7 +2192,13 @@ dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) * http://lists.freebsd.org/pipermail/freebsd-emulation/2007-June/003698.html * */ - if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec == 0) +#ifdef SV_ABI_LINUX + if ((nprot & PROT_EXEC) && (dsp_mmap_allow_prot_exec < 0 || + (dsp_mmap_allow_prot_exec == 0 && + SV_CURPROC_ABI() != SV_ABI_LINUX))) +#else + if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec < 1) +#endif return (-1); d = dsp_get_info(i_dev); @@ -1898,9 +2235,9 @@ dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) /* XXX full-duplex quack. */ if (wrch != NULL) - wrch->flags |= CHN_F_MAPPED; + wrch->flags |= CHN_F_MMAP; if (rdch != NULL) - rdch->flags |= CHN_F_MAPPED; + rdch->flags |= CHN_F_MMAP; *paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset)); relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); @@ -1910,8 +2247,6 @@ dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) return (0); } -#ifdef USING_DEVFS - /* So much for dev_stdclone() */ static int dsp_stdclone(char *name, char *namep, char *sep, int use_sep, int *u, int *c) @@ -1975,7 +2310,7 @@ dsp_clone(void *arg, struct snd_clone_entry *ce; struct pcm_channel *c; int i, unit, udcmask, cunit, devtype, devhw, devcmax, tumax; - char *devname, *devsep; + char *devname, *devcmp, *devsep; KASSERT(dsp_umax >= 0 && dsp_cmax >= 0, ("Uninitialized unit!")); @@ -1994,13 +2329,16 @@ dsp_clone(void *arg, for (i = 0; unit == -1 && i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { devtype = dsp_cdevs[i].type; - devname = dsp_cdevs[i].name; + devcmp = dsp_cdevs[i].name; devsep = dsp_cdevs[i].sep; + devname = dsp_cdevs[i].alias; + if (devname == NULL) + devname = devcmp; devhw = dsp_cdevs[i].hw; devcmax = dsp_cdevs[i].max - 1; - if (strcmp(name, devname) == 0) + if (strcmp(name, devcmp) == 0) unit = snd_unit; - else if (dsp_stdclone(name, devname, devsep, + else if (dsp_stdclone(name, devcmp, devsep, dsp_cdevs[i].use_sep, &unit, &cunit) != 0) { unit = -1; cunit = -1; @@ -2013,15 +2351,15 @@ dsp_clone(void *arg, /* XXX Need Giant magic entry ??? */ - pcm_lock(d); + PCM_LOCK(d); if (snd_clone_disabled(d->clones)) { - pcm_unlock(d); + PCM_UNLOCK(d); return; } PCM_WAIT(d); PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); udcmask = snd_u2unit(unit) | snd_d2unit(devtype); @@ -2089,7 +2427,7 @@ dsp_clone_alloc: snd_clone_setmaxunit(d->clones, tumax); if (ce != NULL) { udcmask |= snd_c2unit(cunit); - *dev = make_dev(&dsp_cdevsw, udcmask, + *dev = make_dev(&dsp_cdevsw, PCMMINOR(udcmask), UID_ROOT, GID_WHEEL, 0666, "%s%d%s%d", devname, unit, devsep, cunit); snd_clone_register(ce, *dev); @@ -2124,7 +2462,6 @@ dsp_sysuninit(void *p) SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL); SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL); -#endif char * dsp_unit2name(char *buf, size_t len, int unit) @@ -2137,7 +2474,7 @@ dsp_unit2name(char *buf, size_t len, int unit) dtype = snd_unit2d(unit); for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { - if (dtype != dsp_cdevs[i].type) + if (dtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) continue; snprintf(buf, len, "%s%d%s%d", dsp_cdevs[i].name, snd_unit2u(unit), dsp_cdevs[i].sep, snd_unit2c(unit)); @@ -2215,11 +2552,11 @@ dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai) /* XXX Need Giant magic entry ??? */ /* See the note in function docblock */ - mtx_assert(d->lock, MA_NOTOWNED); - pcm_lock(d); + PCM_UNLOCKASSERT(d); + PCM_LOCK(d); CHN_FOREACH(ch, d, channels.pcm) { - mtx_assert(ch->lock, MA_NOTOWNED); + CHN_UNLOCKASSERT(ch); CHN_LOCK(ch); if (ai->dev == -1) { if (DSP_REGISTERED(d, i_dev) && @@ -2299,7 +2636,7 @@ dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai) fmts = 0; for (i = 0; caps->fmtlist[i]; i++) { fmts |= caps->fmtlist[i]; - if (caps->fmtlist[i] & AFMT_STEREO) { + if (AFMT_CHANNEL(caps->fmtlist[i]) > 1) { minch = (minch == 0) ? 2 : minch; maxch = 2; } else { @@ -2371,7 +2708,7 @@ dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai) CHN_UNLOCK(ch); } - pcm_unlock(d); + PCM_UNLOCK(d); if (devname != NULL) return (0); @@ -2629,7 +2966,7 @@ dsp_oss_syncstart(int sg_id) /* Proceed only if no errors encountered. */ if (ret == 0) { /* Launch channels */ - while((sm = SLIST_FIRST(&sg->members)) != NULL) { + while ((sm = SLIST_FIRST(&sg->members)) != NULL) { SLIST_REMOVE_HEAD(&sg->members, link); c = sm->ch; @@ -2716,7 +3053,6 @@ dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy) return (ret); } -#ifdef OSSV4_EXPERIMENT /** * @brief Enable or disable "cooked" mode * @@ -2733,10 +3069,6 @@ dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy) * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_COOKEDMODE.html * for more details. * - * @note Currently, this function is just a stub that always returns EINVAL. - * - * @todo Figure out how to and actually implement this. - * * @param wrch playback channel (optional; may be NULL) * @param rdch recording channel (optional; may be NULL) * @param enabled 0 = raw mode, 1 = cooked mode @@ -2746,7 +3078,38 @@ dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy) static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled) { - return (EINVAL); + + /* + * XXX I just don't get it. Why don't they call it + * "BITPERFECT" ~ SNDCTL_DSP_BITPERFECT !?!?. + * This is just plain so confusing, incoherent, + * <insert any non-printable characters here>. + */ + if (!(enabled == 1 || enabled == 0)) + return (EINVAL); + + /* + * I won't give in. I'm inverting its logic here and now. + * Brag all you want, but "BITPERFECT" should be the better + * term here. + */ + enabled ^= 0x00000001; + + if (wrch != NULL) { + CHN_LOCK(wrch); + wrch->flags &= ~CHN_F_BITPERFECT; + wrch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000; + CHN_UNLOCK(wrch); + } + + if (rdch != NULL) { + CHN_LOCK(rdch); + rdch->flags &= ~CHN_F_BITPERFECT; + rdch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000; + CHN_UNLOCK(rdch); + } + + return (0); } /** @@ -2769,7 +3132,18 @@ dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabl static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map) { - return (EINVAL); + struct pcm_channel *ch; + int ret; + + ch = (wrch != NULL) ? wrch : rdch; + if (ch != NULL) { + CHN_LOCK(ch); + ret = chn_oss_getorder(ch, map); + CHN_UNLOCK(ch); + } else + ret = EINVAL; + + return (ret); } /** @@ -2789,9 +3163,50 @@ dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map) { - return (EINVAL); + int ret; + + ret = 0; + + if (wrch != NULL) { + CHN_LOCK(wrch); + ret = chn_oss_setorder(wrch, map); + CHN_UNLOCK(wrch); + } + + if (ret == 0 && rdch != NULL) { + CHN_LOCK(rdch); + ret = chn_oss_setorder(rdch, map); + CHN_UNLOCK(rdch); + } + + return (ret); +} + +static int +dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, + int *mask) +{ + struct pcm_channel *ch; + uint32_t chnmask; + int ret; + + chnmask = 0; + ch = (wrch != NULL) ? wrch : rdch; + + if (ch != NULL) { + CHN_LOCK(ch); + ret = chn_oss_getmask(ch, &chnmask); + CHN_UNLOCK(ch); + } else + ret = EINVAL; + + if (ret == 0) + *mask = chnmask; + + return (ret); } +#ifdef OSSV4_EXPERIMENT /** * @brief Retrieve an audio device's label * |