Date: Wed, 15 Oct 2008 15:32:03 +0000 (UTC) From: Alexander Leidinger <netchild@FreeBSD.org> To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r183919 - in user/netchild/doxygen: . src src/sys src/sys/dev src/sys/dev/sound src/sys/dev/sound/pcm src/sys/kern src/tools src/tools/kerneldoc src/tools/kerneldoc/subsys Message-ID: <200810151532.m9FFW34F037106@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: netchild Date: Wed Oct 15 15:32:03 2008 New Revision: 183919 URL: http://svn.freebsd.org/changeset/base/183919 Log: Create baseline for my doxygen related patches. Added: user/netchild/doxygen/ user/netchild/doxygen/README user/netchild/doxygen/src/ user/netchild/doxygen/src/sys/ user/netchild/doxygen/src/sys/dev/ user/netchild/doxygen/src/sys/dev/sound/ user/netchild/doxygen/src/sys/dev/sound/pcm/ user/netchild/doxygen/src/sys/dev/sound/pcm/dsp.c (props changed) - copied unchanged from r183911, head/sys/dev/sound/pcm/dsp.c user/netchild/doxygen/src/sys/kern/ user/netchild/doxygen/src/sys/kern/subr_bus.c (props changed) - copied unchanged from r183911, head/sys/kern/subr_bus.c user/netchild/doxygen/src/tools/ user/netchild/doxygen/src/tools/kerneldoc/ user/netchild/doxygen/src/tools/kerneldoc/subsys/ user/netchild/doxygen/src/tools/kerneldoc/subsys/Doxyfile-linux (props changed) - copied unchanged from r183911, head/tools/kerneldoc/subsys/Doxyfile-linux user/netchild/doxygen/src/tools/kerneldoc/subsys/Makefile (props changed) - copied unchanged from r183911, head/tools/kerneldoc/subsys/Makefile user/netchild/doxygen/src/tools/kerneldoc/subsys/common-Doxyfile (props changed) - copied unchanged from r183911, head/tools/kerneldoc/subsys/common-Doxyfile Added: user/netchild/doxygen/README ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/netchild/doxygen/README Wed Oct 15 15:32:03 2008 (r183919) @@ -0,0 +1,2 @@ +Doxygen related changes. Either improvements to the infrastructure, +fixes of existing doxygen docs, or new doxygen docs. Copied: user/netchild/doxygen/src/sys/dev/sound/pcm/dsp.c (from r183911, head/sys/dev/sound/pcm/dsp.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ user/netchild/doxygen/src/sys/dev/sound/pcm/dsp.c Wed Oct 15 15:32:03 2008 (r183919, copy of r183911, head/sys/dev/sound/pcm/dsp.c) @@ -0,0 +1,2927 @@ +/*- + * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <dev/sound/pcm/sound.h> +#include <sys/ctype.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"); + +struct dsp_cdevinfo { + struct pcm_channel *rdch, *wrch; + 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_SIMPLEX(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->simplex) + +#define DSP_CDEVINFO_CACHESIZE 8 + +#define DSP_REGISTERED(x, y) (PCM_REGISTERED(x) && \ + (y) != NULL && (y)->si_drv1 != NULL) + +#define OLDPCM_IOCTL + +static d_open_t dsp_open; +static d_close_t dsp_close; +static d_read_t dsp_read; +static d_write_t dsp_write; +static d_ioctl_t dsp_ioctl; +static d_poll_t dsp_poll; +static d_mmap_t dsp_mmap; + +struct cdevsw dsp_cdevsw = { + .d_version = D_VERSION, + .d_open = dsp_open, + .d_close = dsp_close, + .d_read = dsp_read, + .d_write = dsp_write, + .d_ioctl = dsp_ioctl, + .d_poll = dsp_poll, + .d_mmap = dsp_mmap, + .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_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); +static int dsp_oss_setsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song); +static int dsp_oss_setname(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *name); +#endif + +static struct snddev_info * +dsp_get_info(struct cdev *dev) +{ + return (devclass_get_softc(pcm_devclass, PCMUNIT(dev))); +} + +static uint32_t +dsp_get_flags(struct cdev *dev) +{ + device_t bdev; + + bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev)); + + return ((bdev != NULL) ? pcm_getflags(bdev) : 0xffffffff); +} + +static void +dsp_set_flags(struct cdev *dev, uint32_t flags) +{ + device_t bdev; + + bdev = devclass_get_device(pcm_devclass, PCMUNIT(dev)); + + if (bdev != NULL) + pcm_setflags(bdev, flags); +} + +/* + * return the channels associated with an open device instance. + * lock channels specified. + */ +static int +getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, + uint32_t prio) +{ + struct snddev_info *d; + struct pcm_channel *ch; + uint32_t flags; + + 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; + dsp_set_flags(dev, flags); + if (ch != NULL) { + CHN_LOCK(ch); + pcm_chnref(ch, -1); + pcm_chnrelease(ch); + } + PCM_RELEASE(d); + pcm_unlock(d); + } + + *rdch = PCM_RDCH(dev); + *wrch = PCM_WRCH(dev); + + if (*rdch != NULL && (prio & SD_F_PRIO_RD)) + CHN_LOCK(*rdch); + if (*wrch != NULL && (prio & SD_F_PRIO_WR)) + CHN_LOCK(*wrch); + + return (0); +} + +/* unlock specified channels */ +static void +relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, + uint32_t prio) +{ + if (wrch != NULL && (prio & SD_F_PRIO_WR)) + CHN_UNLOCK(wrch); + if (rdch != NULL && (prio & SD_F_PRIO_RD)) + CHN_UNLOCK(rdch); +} + +static void +dsp_cdevinfo_alloc(struct cdev *dev, + struct pcm_channel *rdch, struct pcm_channel *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, + ("bogus %s(), what are you trying to accomplish here?", __func__)); + 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; +} + +static void +dsp_cdevinfo_free(struct cdev *dev) +{ + 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 && + PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL, + ("bogus %s(), what are you trying to accomplish here?", __func__)); + PCM_BUSYASSERT(d); + mtx_assert(d->lock, MA_OWNED); + + cdi = dev->si_drv1; + dev->si_drv1 = NULL; + 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); +} + +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); +} + +/* duplex / simplex cdev type */ +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 */ +}; + +#define DSP_F_VALID(x) ((x) & (FREAD | FWRITE)) +#define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE)) +#define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x)) +#define DSP_F_READ(x) ((x) & FREAD) +#define DSP_F_WRITE(x) ((x) & FWRITE) + +static const struct { + int type; + char *name; + char *sep; + int use_sep; + int hw; + int max; + 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 }, +}; + +#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) + +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; + int i, error, rderror, wrerror, devtype, wdevunit, rdevunit; + + /* Kind of impossible.. */ + if (i_dev == NULL || td == NULL) + return (ENODEV); + + d = dsp_get_info(i_dev); + if (!PCM_REGISTERED(d)) + return (EBADF); + + PCM_GIANT_ENTER(d); + + /* Lock snddev so nobody else can monkey with it. */ + pcm_lock(d); + PCM_WAIT(d); + + /* + * Try to acquire cloned device before someone else pick it. + * ENODEV means this is not a cloned droids. + */ + error = snd_clone_acquire(i_dev); + if (!(error == 0 || error == ENODEV)) { + DSP_FIXUP_ERROR(); + pcm_unlock(d); + PCM_GIANT_EXIT(d); + return (error); + } + + error = 0; + DSP_FIXUP_ERROR(); + + if (error != 0) { + (void)snd_clone_release(i_dev); + pcm_unlock(d); + PCM_GIANT_EXIT(d); + return (error); + } + + /* + * That is just enough. Acquire and unlock pcm lock so + * the other will just have to wait until we finish doing + * everything. + */ + PCM_ACQUIRE(d); + pcm_unlock(d); + + devtype = PCMDEV(i_dev); + wdevunit = -1; + rdevunit = -1; + fmt = 0; + spd = 0; + + for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { + if (devtype != dsp_cdevs[i].type) + 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) + wdevunit = dev2unit(i_dev); + else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) + rdevunit = dev2unit(i_dev); + fmt = dsp_cdevs[i].fmt; + spd = dsp_cdevs[i].spd; + break; + } + + /* No matching devtype? */ + if (fmt == 0 || spd == 0) + panic("impossible devtype %d", devtype); + + rdch = NULL; + wrch = NULL; + rderror = 0; + wrerror = 0; + + /* + * if we get here, the open request is valid- either: + * * we were previously not open + * * we were open for play xor record and the opener wants + * the non-open direction + */ + if (DSP_F_READ(flags)) { + /* open for read */ + rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC, + td->td_proc->p_pid, rdevunit); + + if (rderror == 0 && (chn_reset(rdch, fmt) != 0 || + (chn_setspeed(rdch, spd) != 0))) + rderror = ENXIO; + + if (rderror != 0) { + if (rdch != NULL) + pcm_chnrelease(rdch); + 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); + } + } + + if (DSP_F_WRITE(flags)) { + /* open for write */ + wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, + td->td_proc->p_pid, wdevunit); + + if (wrerror == 0 && (chn_reset(wrch, fmt) != 0 || + (chn_setspeed(wrch, spd) != 0))) + wrerror = ENXIO; + + if (wrerror != 0) { + if (wrch != NULL) + pcm_chnrelease(wrch); + 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); + } + wrch = NULL; + } else { + if (flags & O_NONBLOCK) + wrch->flags |= CHN_F_NBIO; + pcm_chnref(wrch, 1); + 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); + + /* + * We're done. Allocate channels information for this cdev. + */ + dsp_cdevinfo_alloc(i_dev, rdch, wrch); + + /* + * Increase clone refcount for its automatic garbage collector. + */ + (void)snd_clone_ref(i_dev); + + PCM_RELEASE(d); + pcm_unlock(d); + + PCM_GIANT_LEAVE(d); + + return (0); +} + +static int +dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) +{ + struct pcm_channel *rdch, *wrch; + struct snddev_info *d; + int sg_ids, refs; + + d = dsp_get_info(i_dev); + if (!DSP_REGISTERED(d, i_dev)) + return (EBADF); + + PCM_GIANT_ENTER(d); + + pcm_lock(d); + PCM_WAIT(d); + + rdch = PCM_RDCH(i_dev); + wrch = PCM_WRCH(i_dev); + + if (rdch || wrch) { + PCM_ACQUIRE(d); + pcm_unlock(d); + + refs = 0; + if (rdch) { + /* + * 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. + */ + PCM_SG_LOCK(); + sg_ids = chn_syncdestroy(rdch); + PCM_SG_UNLOCK(); + if (sg_ids != 0) + free_unr(pcmsg_unrhdr, sg_ids); + + CHN_LOCK(rdch); + refs += pcm_chnref(rdch, -1); + chn_abort(rdch); /* won't sleep */ + rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); + chn_reset(rdch, 0); + pcm_chnrelease(rdch); + PCM_RDCH(i_dev) = NULL; + } + if (wrch) { + /* + * Please see block above. + */ + PCM_SG_LOCK(); + sg_ids = chn_syncdestroy(wrch); + PCM_SG_UNLOCK(); + if (sg_ids != 0) + free_unr(pcmsg_unrhdr, sg_ids); + + CHN_LOCK(wrch); + refs += pcm_chnref(wrch, -1); + chn_flush(wrch); /* may sleep */ + wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); + chn_reset(wrch, 0); + pcm_chnrelease(wrch); + PCM_WRCH(i_dev) = NULL; + } + + 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); + + /* + * 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); + } + + pcm_unlock(d); + + PCM_GIANT_LEAVE(d); + + return (0); +} + +static __inline int +dsp_io_ops(struct cdev *i_dev, struct uio *buf) +{ + struct snddev_info *d; + struct pcm_channel **ch, *rdch, *wrch; + int (*chn_io)(struct pcm_channel *, struct uio *); + int prio, ret; + pid_t runpid; + + KASSERT(i_dev != NULL && buf != NULL && + (buf->uio_rw == UIO_READ || buf->uio_rw == UIO_WRITE), + ("%s(): io train wreck!", __func__)); + + d = dsp_get_info(i_dev); + if (!DSP_REGISTERED(d, i_dev)) + return (EBADF); + + 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; + } + + rdch = NULL; + wrch = NULL; + runpid = buf->uio_td->td_proc->p_pid; + + getchns(i_dev, &rdch, &wrch, prio); + + if (*ch == NULL || !((*ch)->flags & CHN_F_BUSY)) { + PCM_GIANT_EXIT(d); + return (EBADF); + } + + 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; + } + + /* + * 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. + */ + ++(*ch)->inprog; + ret = chn_io(*ch, buf); + --(*ch)->inprog; + + CHN_BROADCAST(&(*ch)->cv); + + relchns(i_dev, rdch, wrch, prio); + + PCM_GIANT_LEAVE(d); + + return (ret); +} + +static int +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 +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; + + d = dsp_get_info(i_dev); + if (!DSP_REGISTERED(d, i_dev)) + 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. + */ + if (IOCGROUP(cmd) == 'M') { + /* + * This is at least, a bug to bug compatible with OSS. + */ + 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); + } + + /* + * Certain ioctls may be made on any type of device (audio, mixer, + * and MIDI). Handle those special cases here. + */ + if (IOCGROUP(cmd) == 'X') { + PCM_ACQUIRE_QUICK(d); + switch(cmd) { + case SNDCTL_SYSINFO: + sound_oss_sysinfo((oss_sysinfo *)arg); + break; + case SNDCTL_AUDIOINFO: + ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg); + break; + case SNDCTL_MIXERINFO: + ret = mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg); + break; + default: + ret = EINVAL; + } + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (ret); + } + + 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) + wrch = NULL; + if (kill & 2) + rdch = NULL; + + if (wrch == NULL && rdch == NULL) { + relchns(i_dev, rdch, wrch, 0); + PCM_GIANT_EXIT(d); + return (EINVAL); + } + + switch(cmd) { +#ifdef OLDPCM_IOCTL + /* + * we start with the new ioctl interface. + */ + case AIONWRITE: /* how many bytes can write ? */ + if (wrch) { + CHN_LOCK(wrch); +/* + if (wrch && wrch->bufhard.dl) + while (chn_wrfeed(wrch) == 0); +*/ + *arg_i = sndbuf_getfree(wrch->bufsoft); + CHN_UNLOCK(wrch); + } else { + *arg_i = 0; + ret = EINVAL; + } + break; + + case AIOSSIZE: /* set the current blocksize */ + { + struct snd_size *p = (struct snd_size *)arg; + + p->play_size = 0; + p->rec_size = 0; + PCM_ACQUIRE_QUICK(d); + if (wrch) { + CHN_LOCK(wrch); + chn_setblocksize(wrch, 2, p->play_size); + p->play_size = sndbuf_getblksz(wrch->bufsoft); + CHN_UNLOCK(wrch); + } + if (rdch) { + CHN_LOCK(rdch); + chn_setblocksize(rdch, 2, p->rec_size); + p->rec_size = sndbuf_getblksz(rdch->bufsoft); + CHN_UNLOCK(rdch); + } + PCM_RELEASE_QUICK(d); + } + break; + case AIOGSIZE: /* get the current blocksize */ + { + struct snd_size *p = (struct snd_size *)arg; + + if (wrch) { + CHN_LOCK(wrch); + p->play_size = sndbuf_getblksz(wrch->bufsoft); + CHN_UNLOCK(wrch); + } + if (rdch) { + CHN_LOCK(rdch); + p->rec_size = sndbuf_getblksz(rdch->bufsoft); + CHN_UNLOCK(rdch); + } + } + break; + + case AIOSFMT: + case AIOGFMT: + { + snd_chan_param *p = (snd_chan_param *)arg; + + if (cmd == AIOSFMT && + ((p->play_format != 0 && p->play_rate == 0) || + (p->rec_format != 0 && p->rec_rate == 0))) { + ret = EINVAL; + break; + } + PCM_ACQUIRE_QUICK(d); + if (wrch) { + CHN_LOCK(wrch); + if (cmd == AIOSFMT && p->play_format != 0) { + chn_setformat(wrch, p->play_format); + chn_setspeed(wrch, p->play_rate); + } + p->play_rate = wrch->speed; + p->play_format = wrch->format; + CHN_UNLOCK(wrch); + } else { + p->play_rate = 0; + p->play_format = 0; + } + if (rdch) { + CHN_LOCK(rdch); + if (cmd == AIOSFMT && p->rec_format != 0) { + chn_setformat(rdch, p->rec_format); + chn_setspeed(rdch, p->rec_rate); + } + p->rec_rate = rdch->speed; + p->rec_format = rdch->format; + CHN_UNLOCK(rdch); + } else { + p->rec_rate = 0; + p->rec_format = 0; + } + PCM_RELEASE_QUICK(d); + } + break; + + case AIOGCAP: /* get capabilities */ + { + snd_capabilities *p = (snd_capabilities *)arg; + struct pcmchan_caps *pcaps = NULL, *rcaps = NULL; + struct cdev *pdev; + + pcm_lock(d); + if (rdch) { + CHN_LOCK(rdch); + rcaps = chn_getcaps(rdch); + } + if (wrch) { + CHN_LOCK(wrch); + pcaps = chn_getcaps(wrch); + } + p->rate_min = max(rcaps? rcaps->minspeed : 0, + pcaps? pcaps->minspeed : 0); + p->rate_max = min(rcaps? rcaps->maxspeed : 1000000, + pcaps? pcaps->maxspeed : 1000000); + p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000, + wrch? sndbuf_getsize(wrch->bufsoft) : 1000000); + /* XXX bad on sb16 */ + p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) & + (wrch? chn_getformats(wrch) : 0xffffffff); + if (rdch && wrch) + p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX; + pdev = d->mixer_dev; + p->mixers = 1; /* default: one mixer */ + p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0; + p->left = p->right = 100; + if (wrch) + CHN_UNLOCK(wrch); + if (rdch) + CHN_UNLOCK(rdch); + pcm_unlock(d); + } + break; + *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200810151532.m9FFW34F037106>