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