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
Alexander Leidinger
netchild at FreeBSD.org
Wed Oct 15 15:32:04 UTC 2008
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 at 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 ***
More information about the svn-src-user
mailing list