[Midnightbsd-cvs] src: dev/sound: Update sound code

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Tue Dec 2 22:05:46 EST 2008


Log Message:
-----------
Update sound code

Added Files:
-----------
    src/sys/dev/sound:
        clone.c (r1.1)
        clone.h (r1.1)
        unit.c (r1.1)
        unit.h (r1.1)
        version.h (r1.1)

-------------- next part --------------
--- /dev/null
+++ sys/dev/sound/unit.c
@@ -0,0 +1,194 @@
+/*-
+ * Copyright (c) 2007 Ariff Abdullah <ariff 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.
+ *
+ * $FreeBSD: src/sys/dev/sound/unit.c,v 1.1 2007/05/31 18:35:24 ariff Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <dev/sound/unit.h>
+
+/*
+ * Unit magic allocator for sound driver.
+ *
+ * 'u' = Unit of attached soundcards
+ * 'd' = Device type
+ * 'c' = Channel number
+ *
+ * eg: dsp0.p1  - u=0, d=p, c=1
+ *     dsp1.vp0 - u=1, d=vp, c=0
+ *     dsp0.10  - u=0, d=clone, c=allocated clone (see further explanation)
+ *
+ * Maximum unit of soundcards can be tuned through "hw.snd.maxunit", which
+ * is between SND_UNIT_UMIN (16) and SND_UNIT_UMAX (2048). By design,
+ * maximum allowable allocated channel is 256, with exception for clone
+ * devices which doesn't have any notion of channel numbering. The use of
+ * channel numbering in a clone device is simply to provide uniqueness among
+ * allocated clones. This also means that the maximum allowable clonable
+ * device is largely dependant and dynamically tuned depending on
+ * hw.snd.maxunit.
+ */
+
+/* Default width */
+static int snd_u_shift = 9;	/* 0 - 0x1ff :  512 distinct soundcards   */
+static int snd_d_shift = 5;	/* 0 - 0x1f  :   32 distinct device types */
+static int snd_c_shift = 10;	/* 0 - 0x3ff : 1024 distinct channels
+					       (256 limit "by design",
+					       except for clone devices)  */
+
+static int snd_unit_initialized = 0;
+
+#ifdef SND_DIAGNOSTIC
+#define SND_UNIT_ASSERT()	do {					\
+	if (snd_unit_initialized == 0)					\
+		panic("%s(): Uninitialized sound unit!", __func__);	\
+} while(0)
+#else
+#define SND_UNIT_ASSERT()	KASSERT(snd_unit_initialized != 0,	\
+				("%s(): Uninitialized sound unit!",	\
+				__func__))
+#endif
+
+#define MKMASK(x)	((1 << snd_##x##_shift) - 1)
+
+int
+snd_max_u(void)
+{
+	SND_UNIT_ASSERT();
+
+	return (MKMASK(u));
+}
+
+int
+snd_max_d(void)
+{
+	SND_UNIT_ASSERT();
+
+	return (MKMASK(d));
+}
+
+int
+snd_max_c(void)
+{
+	SND_UNIT_ASSERT();
+
+	return (MKMASK(c));
+}
+
+int
+snd_unit2u(int unit)
+{
+	SND_UNIT_ASSERT();
+
+	return ((unit >> (snd_c_shift + snd_d_shift)) & MKMASK(u));
+}
+
+int
+snd_unit2d(int unit)
+{
+	SND_UNIT_ASSERT();
+
+	return ((unit >> snd_c_shift) & MKMASK(d));
+}
+
+int
+snd_unit2c(int unit)
+{
+	SND_UNIT_ASSERT();
+
+	return (unit & MKMASK(c));
+}
+
+int
+snd_u2unit(int u)
+{
+	SND_UNIT_ASSERT();
+
+	return ((u & MKMASK(u)) << (snd_c_shift + snd_d_shift));
+}
+
+int
+snd_d2unit(int d)
+{
+	SND_UNIT_ASSERT();
+
+	return ((d & MKMASK(d)) << snd_c_shift);
+}
+
+int
+snd_c2unit(int c)
+{
+	SND_UNIT_ASSERT();
+
+	return (c & MKMASK(c));
+}
+
+int
+snd_mkunit(int u, int d, int c)
+{
+	SND_UNIT_ASSERT();
+
+	return ((c & MKMASK(c)) | ((d & MKMASK(d)) << snd_c_shift) |
+	    ((u & MKMASK(u)) << (snd_c_shift + snd_d_shift)));
+}
+
+/*
+ * This *must* be called first before any of the functions above!!!
+ */
+void
+snd_unit_init(void)
+{
+	int i;
+
+	if (snd_unit_initialized != 0)
+		return;
+
+	snd_unit_initialized = 1;
+
+	if (getenv_int("hw.snd.maxunit", &i) != 0) {
+		if (i < SND_UNIT_UMIN)
+			i = SND_UNIT_UMIN;
+		else if (i > SND_UNIT_UMAX)
+			i = SND_UNIT_UMAX;
+		else
+			i = roundup2(i, 2);
+
+		for (snd_u_shift = 0; (i >> (snd_u_shift + 1)) != 0;
+		    snd_u_shift++)
+			;
+
+		/*
+		 * Make room for channels/clones allocation unit
+		 * to fit within 24bit MAXMINOR limit.
+		 */
+		snd_c_shift = 24 - snd_u_shift - snd_d_shift;
+	}
+
+	if (bootverbose != 0)
+		printf("%s() u=0x%08x [%d] d=0x%08x [%d] c=0x%08x [%d]\n",
+		    __func__, SND_U_MASK, snd_max_u() + 1,
+		    SND_D_MASK, snd_max_d() + 1, SND_C_MASK, snd_max_c() + 1);
+}
--- /dev/null
+++ sys/dev/sound/unit.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2007 Ariff Abdullah <ariff 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.
+ *
+ * $FreeBSD: src/sys/dev/sound/unit.h,v 1.1 2007/05/31 18:35:24 ariff Exp $
+ */
+
+#ifndef _SND_UNIT_H_
+#define _SND_UNIT_H_
+
+#define SND_UNIT_UMIN	16
+#define SND_UNIT_UMAX	2048
+
+int snd_max_u(void);
+int snd_max_d(void);
+int snd_max_c(void);
+int snd_unit2u(int);
+int snd_unit2d(int);
+int snd_unit2c(int);
+int snd_u2unit(int);
+int snd_d2unit(int);
+int snd_c2unit(int);
+int snd_mkunit(int, int, int);
+
+void snd_unit_init(void);
+
+#define SND_U_MASK	(snd_u2unit(snd_max_u()))
+#define SND_D_MASK	(snd_d2unit(snd_max_d()))
+#define SND_C_MASK	(snd_c2unit(snd_max_c()))
+
+#endif /* !_SND_UNIT_H_ */
--- /dev/null
+++ sys/dev/sound/clone.h
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 2007 Ariff Abdullah <ariff 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.
+ *
+ * $FreeBSD: src/sys/dev/sound/clone.h,v 1.2 2007/06/14 11:10:21 ariff Exp $
+ */
+
+#ifndef _SND_CLONE_H_
+#define _SND_CLONE_H_
+
+struct snd_clone_entry;
+struct snd_clone;
+
+/*
+ * 750 milisecond default deadline. Short enough to not cause excessive
+ * garbage collection, long enough to indicate stalled VFS.
+ */
+#define SND_CLONE_DEADLINE_DEFAULT	750
+
+/*
+ * Fit within 24bit MAXMINOR.
+ */
+#define SND_CLONE_MAXUNIT		0xffffff
+
+/*
+ * Creation flags, mostly related to the behaviour of garbage collector.
+ *
+ * SND_CLONE_ENABLE     - Enable clone allocation.
+ * SND_CLONE_GC_ENABLE  - Enable garbage collector operation, automatically
+ *                        or if explicitly called upon.
+ * SND_CLONE_GC_UNREF   - Garbage collect during unref operation.
+ * SND_CLONE_GC_LASTREF - Garbage collect during last reference
+ *                        (refcount = 0)
+ * SND_CLONE_GC_EXPIRED - Don't garbage collect unless the global clone
+ *                        handler has been expired.
+ * SND_CLONE_GC_REVOKE  - Revoke clone invocation status which has been
+ *                        expired instead of removing and freeing it.
+ * SND_CLONE_WAITOK     - malloc() is allowed to sleep while allocating
+ *                        clone entry.
+ */
+#define SND_CLONE_ENABLE	0x00000001
+#define SND_CLONE_GC_ENABLE	0x00000002
+#define SND_CLONE_GC_UNREF	0x00000004
+#define SND_CLONE_GC_LASTREF	0x00000008
+#define SND_CLONE_GC_EXPIRED	0x00000010
+#define SND_CLONE_GC_REVOKE	0x00000020
+#define SND_CLONE_WAITOK	0x80000000
+
+#define SND_CLONE_GC_MASK	(SND_CLONE_GC_ENABLE  |			\
+				 SND_CLONE_GC_UNREF   |			\
+				 SND_CLONE_GC_LASTREF |			\
+				 SND_CLONE_GC_EXPIRED |			\
+				 SND_CLONE_GC_REVOKE)
+
+#define SND_CLONE_MASK		(SND_CLONE_ENABLE | SND_CLONE_GC_MASK |	\
+				 SND_CLONE_WAITOK)
+
+/*
+ * Runtime clone device flags
+ *
+ * These are mostly private to the clone manager operation:
+ *
+ * SND_CLONE_NEW    - New clone allocation in progress.
+ * SND_CLONE_INVOKE - Cloning being invoked, waiting for next VFS operation.
+ * SND_CLONE_BUSY   - In progress, being referenced by living thread/proc.
+ */
+#define SND_CLONE_NEW		0x00000001
+#define SND_CLONE_INVOKE	0x00000002
+#define SND_CLONE_BUSY		0x00000004
+
+/*
+ * Nothing important, just for convenience.
+ */
+#define SND_CLONE_ALLOC		(SND_CLONE_NEW | SND_CLONE_INVOKE |	\
+				 SND_CLONE_BUSY)
+
+#define SND_CLONE_DEVMASK	SND_CLONE_ALLOC
+
+
+void snd_timestamp(struct timespec *);
+
+struct snd_clone *snd_clone_create(int, int, int, uint32_t);
+int snd_clone_busy(struct snd_clone *);
+int snd_clone_enable(struct snd_clone *);
+int snd_clone_disable(struct snd_clone *);
+int snd_clone_getsize(struct snd_clone *);
+int snd_clone_getmaxunit(struct snd_clone *);
+int snd_clone_setmaxunit(struct snd_clone *, int);
+int snd_clone_getdeadline(struct snd_clone *);
+int snd_clone_setdeadline(struct snd_clone *, int);
+int snd_clone_gettime(struct snd_clone *, struct timespec *);
+uint32_t snd_clone_getflags(struct snd_clone *);
+uint32_t snd_clone_setflags(struct snd_clone *, uint32_t);
+int snd_clone_getdevtime(struct cdev *, struct timespec *);
+uint32_t snd_clone_getdevflags(struct cdev *);
+uint32_t snd_clone_setdevflags(struct cdev *, uint32_t);
+int snd_clone_gc(struct snd_clone *);
+void snd_clone_destroy(struct snd_clone *);
+int snd_clone_acquire(struct cdev *);
+int snd_clone_release(struct cdev *);
+int snd_clone_ref(struct cdev *);
+int snd_clone_unref(struct cdev *);
+void snd_clone_register(struct snd_clone_entry *, struct cdev *);
+struct snd_clone_entry *snd_clone_alloc(struct snd_clone *, struct cdev **,
+    int *, int);
+
+#define snd_clone_enabled(x)	((x) != NULL && 			\
+				(snd_clone_getflags(x) & SND_CLONE_ENABLE))
+#define snd_clone_disabled(x)	(!snd_clone_enabled(x))
+
+#endif /* !_SND_CLONE_H */
--- /dev/null
+++ sys/dev/sound/clone.c
@@ -0,0 +1,795 @@
+/*-
+ * Copyright (c) 2007 Ariff Abdullah <ariff 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.
+ *
+ * $FreeBSD: src/sys/dev/sound/clone.c,v 1.4 2007/06/14 11:10:21 ariff Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG)
+#include <dev/sound/pcm/sound.h>
+#endif
+
+#include <dev/sound/clone.h>
+
+/*
+ * So here we go again, another clonedevs manager. Unlike default clonedevs,
+ * this clone manager is designed to withstand various abusive behavior
+ * (such as 'while : ; do ls /dev/whatever ; done', etc.), reusable object
+ * after reaching certain expiration threshold, aggressive garbage collector,
+ * transparent device allocator and concurrency handling across multiple
+ * thread/proc. Due to limited information given by dev_clone EVENTHANDLER,
+ * we don't have much clues whether the caller wants a real open() or simply
+ * making fun of us with things like stat(), mtime() etc. Assuming that:
+ * 1) Time window between dev_clone EH <-> real open() should be small
+ * enough and 2) mtime()/stat() etc. always looks like a half way / stalled
+ * operation, we can decide whether a new cdev must be created, old
+ * (expired) cdev can be reused or an existing cdev can be shared.
+ *
+ * Most of the operations and logics are generic enough and can be applied
+ * on other places (such as if_tap, snp, etc).  Perhaps this can be
+ * rearranged to complement clone_*(). However, due to this still being
+ * specific to the sound driver (and as a proof of concept on how it can be
+ * done), si_drv2 is used to keep the pointer of the clone list entry to
+ * avoid expensive lookup.
+ */
+
+/* clone entry */
+struct snd_clone_entry {
+	TAILQ_ENTRY(snd_clone_entry) link;
+	struct snd_clone *parent;
+	struct cdev *devt;
+	struct timespec tsp;
+	uint32_t flags;
+	pid_t pid;
+	int unit;
+};
+
+/* clone manager */
+struct snd_clone {
+	TAILQ_HEAD(link_head, snd_clone_entry) head;
+	struct timespec tsp;
+	int refcount;
+	int size;
+	int typemask;
+	int maxunit;
+	int deadline;
+	uint32_t flags;
+};
+
+#ifdef SND_DIAGNOSTIC
+#define SND_CLONE_ASSERT(x, y)		do {			\
+	if (!(x))						\
+		panic y;					\
+} while(0)
+#else
+#define SND_CLONE_ASSERT(x...)		KASSERT(x)
+#endif
+
+/*
+ * Shamelessly ripped off from vfs_subr.c
+ * We need at least 1/HZ precision as default timestamping.
+ */
+enum { SND_TSP_SEC, SND_TSP_HZ, SND_TSP_USEC, SND_TSP_NSEC };
+
+static int snd_timestamp_precision = SND_TSP_HZ;
+TUNABLE_INT("hw.snd.timestamp_precision", &snd_timestamp_precision);
+
+void
+snd_timestamp(struct timespec *tsp)
+{
+	struct timeval tv;
+
+	switch (snd_timestamp_precision) {
+	case SND_TSP_SEC:
+		tsp->tv_sec = time_second;
+		tsp->tv_nsec = 0;
+		break;
+	case SND_TSP_HZ:
+		getnanouptime(tsp);
+		break;
+	case SND_TSP_USEC:
+		microuptime(&tv);
+		TIMEVAL_TO_TIMESPEC(&tv, tsp);
+		break;
+	case SND_TSP_NSEC:
+		nanouptime(tsp);
+		break;
+	default:
+		snd_timestamp_precision = SND_TSP_HZ;
+		getnanouptime(tsp);
+		break;
+	}
+}
+
+#if defined(SND_DIAGNOSTIC) || defined(SND_DEBUG)
+static int
+sysctl_hw_snd_timestamp_precision(SYSCTL_HANDLER_ARGS)
+{
+	int err, val;
+
+	val = snd_timestamp_precision;
+	err = sysctl_handle_int(oidp, &val, 0, req);
+	if (err == 0 && req->newptr != NULL) {
+		switch (val) {
+		case SND_TSP_SEC:
+		case SND_TSP_HZ:
+		case SND_TSP_USEC:
+		case SND_TSP_NSEC:
+			snd_timestamp_precision = val;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return (err);
+}
+SYSCTL_PROC(_hw_snd, OID_AUTO, timestamp_precision, CTLTYPE_INT | CTLFLAG_RW,
+    0, sizeof(int), sysctl_hw_snd_timestamp_precision, "I",
+    "timestamp precision (0=s 1=hz 2=us 3=ns)");
+#endif
+
+/*
+ * snd_clone_create() : Return opaque allocated clone manager.
+ */
+struct snd_clone *
+snd_clone_create(int typemask, int maxunit, int deadline, uint32_t flags)
+{
+	struct snd_clone *c;
+
+	SND_CLONE_ASSERT(!(typemask & ~SND_CLONE_MAXUNIT),
+	    ("invalid typemask: 0x%08x", typemask));
+	SND_CLONE_ASSERT(maxunit == -1 ||
+	    !(maxunit & ~(~typemask & SND_CLONE_MAXUNIT)),
+	    ("maxunit overflow: typemask=0x%08x maxunit=%d",
+	    typemask, maxunit));
+	SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK),
+	    ("invalid clone flags=0x%08x", flags));
+
+	c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO);
+	c->refcount = 0;
+	c->size = 0;
+	c->typemask = typemask;
+	c->maxunit = (maxunit == -1) ? (~typemask & SND_CLONE_MAXUNIT) :
+	    maxunit;
+	c->deadline = deadline;
+	c->flags = flags;
+	snd_timestamp(&c->tsp);
+	TAILQ_INIT(&c->head);
+
+	return (c);
+}
+
+int
+snd_clone_busy(struct snd_clone *c)
+{
+	struct snd_clone_entry *ce;
+
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+
+	if (c->size == 0)
+		return (0);
+
+	TAILQ_FOREACH(ce, &c->head, link) {
+		if ((ce->flags & SND_CLONE_BUSY) ||
+		    (ce->devt != NULL && ce->devt->si_threadcount != 0))
+			return (EBUSY);
+	}
+
+	return (0);
+}
+
+/*
+ * snd_clone_enable()/disable() : Suspend/resume clone allocation through
+ * snd_clone_alloc(). Everything else will not be affected by this.
+ */
+int
+snd_clone_enable(struct snd_clone *c)
+{
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+
+	if (c->flags & SND_CLONE_ENABLE)
+		return (EINVAL);
+
+	c->flags |= SND_CLONE_ENABLE;
+
+	return (0);
+}
+
+int
+snd_clone_disable(struct snd_clone *c)
+{
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+
+	if (!(c->flags & SND_CLONE_ENABLE))
+		return (EINVAL);
+
+	c->flags &= ~SND_CLONE_ENABLE;
+
+	return (0);
+}
+
+/*
+ * Getters / Setters. Not worth explaining :)
+ */
+int
+snd_clone_getsize(struct snd_clone *c)
+{
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+
+	return (c->size);
+}
+
+int
+snd_clone_getmaxunit(struct snd_clone *c)
+{
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+
+	return (c->maxunit);
+}
+
+int
+snd_clone_setmaxunit(struct snd_clone *c, int maxunit)
+{
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+	SND_CLONE_ASSERT(maxunit == -1 ||
+	    !(maxunit & ~(~c->typemask & SND_CLONE_MAXUNIT)),
+	    ("maxunit overflow: typemask=0x%08x maxunit=%d",
+	    c->typemask, maxunit));
+
+	c->maxunit = (maxunit == -1) ? (~c->typemask & SND_CLONE_MAXUNIT) :
+	    maxunit;
+
+	return (c->maxunit);
+}
+
+int
+snd_clone_getdeadline(struct snd_clone *c)
+{
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+
+	return (c->deadline);
+}
+
+int
+snd_clone_setdeadline(struct snd_clone *c, int deadline)
+{
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+
+	c->deadline = deadline;
+
+	return (c->deadline);
+}
+
+int
+snd_clone_gettime(struct snd_clone *c, struct timespec *tsp)
+{
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+	SND_CLONE_ASSERT(tsp != NULL, ("NULL timespec"));
+
+	*tsp = c->tsp;
+
+	return (0);
+}
+
+uint32_t
+snd_clone_getflags(struct snd_clone *c)
+{
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+
+	return (c->flags);
+}
+
+uint32_t
+snd_clone_setflags(struct snd_clone *c, uint32_t flags)
+{
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+	SND_CLONE_ASSERT(!(flags & ~SND_CLONE_MASK),
+	    ("invalid clone flags=0x%08x", flags));
+
+	c->flags = flags;
+
+	return (c->flags);
+}
+
+int
+snd_clone_getdevtime(struct cdev *dev, struct timespec *tsp)
+{
+	struct snd_clone_entry *ce;
+
+	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
+	SND_CLONE_ASSERT(tsp != NULL, ("NULL timespec"));
+
+	ce = dev->si_drv2;
+	if (ce == NULL)
+		return (ENODEV);
+
+	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
+
+	*tsp = ce->tsp;
+
+	return (0);
+}
+
+uint32_t
+snd_clone_getdevflags(struct cdev *dev)
+{
+	struct snd_clone_entry *ce;
+
+	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
+
+	ce = dev->si_drv2;
+	if (ce == NULL)
+		return (0xffffffff);
+
+	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
+
+	return (ce->flags);
+}
+
+uint32_t
+snd_clone_setdevflags(struct cdev *dev, uint32_t flags)
+{
+	struct snd_clone_entry *ce;
+
+	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
+	SND_CLONE_ASSERT(!(flags & ~SND_CLONE_DEVMASK),
+	    ("invalid clone dev flags=0x%08x", flags));
+
+	ce = dev->si_drv2;
+	if (ce == NULL)
+		return (0xffffffff);
+
+	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
+
+	ce->flags = flags;
+
+	return (ce->flags);
+}
+
+/* Elapsed time conversion to ms */
+#define SND_CLONE_ELAPSED(x, y)						\
+	((((x)->tv_sec - (y)->tv_sec) * 1000) +				\
+	(((y)->tv_nsec > (x)->tv_nsec) ?				\
+	(((1000000000L + (x)->tv_nsec -					\
+	(y)->tv_nsec) / 1000000) - 1000) :				\
+	(((x)->tv_nsec - (y)->tv_nsec) / 1000000)))
+
+#define SND_CLONE_EXPIRED(x, y, z)					\
+	((x)->deadline < 1 ||						\
+	((y)->tv_sec - (z)->tv_sec) > ((x)->deadline / 1000) ||		\
+	SND_CLONE_ELAPSED(y, z) > (x)->deadline)
+
+/*
+ * snd_clone_gc() : Garbage collector for stalled, expired objects. Refer to
+ * clone.h for explanations on GC settings.
+ */
+int
+snd_clone_gc(struct snd_clone *c)
+{
+	struct snd_clone_entry *ce, *tce;
+	struct timespec now;
+	int pruned;
+
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+
+	if (!(c->flags & SND_CLONE_GC_ENABLE) || c->size == 0)
+		return (0);
+
+	snd_timestamp(&now);
+
+	/*
+	 * Bail out if the last clone handler was invoked below the deadline
+	 * threshold.
+	 */
+	if ((c->flags & SND_CLONE_GC_EXPIRED) &&
+	    !SND_CLONE_EXPIRED(c, &now, &c->tsp))
+		return (0);
+
+	pruned = 0;
+
+	/*
+	 * Visit each object in reverse order. If the object is still being
+	 * referenced by a valid open(), skip it. Look for expired objects
+	 * and either revoke its clone invocation status or mercilessly
+	 * throw it away.
+	 */
+	TAILQ_FOREACH_REVERSE_SAFE(ce, &c->head, link_head, link, tce) {
+		if (!(ce->flags & SND_CLONE_BUSY) &&
+		    (!(ce->flags & SND_CLONE_INVOKE) ||
+		    SND_CLONE_EXPIRED(c, &now, &ce->tsp))) {
+			if ((c->flags & SND_CLONE_GC_REVOKE) ||
+			    ce->devt->si_threadcount != 0) {
+				ce->flags &= ~SND_CLONE_INVOKE;
+				ce->pid = -1;
+			} else {
+				TAILQ_REMOVE(&c->head, ce, link);
+				destroy_dev(ce->devt);
+				free(ce, M_DEVBUF);
+				c->size--;
+			}
+			pruned++;
+		}
+	}
+
+	/* return total pruned objects */
+	return (pruned);
+}
+
+void
+snd_clone_destroy(struct snd_clone *c)
+{
+	struct snd_clone_entry *ce, *tmp;
+
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+
+	ce = TAILQ_FIRST(&c->head);
+	while (ce != NULL) {
+		tmp = TAILQ_NEXT(ce, link);
+		if (ce->devt != NULL)
+			destroy_dev(ce->devt);
+		free(ce, M_DEVBUF);
+		ce = tmp;
+	}
+
+	free(c, M_DEVBUF);
+}
+
+/*
+ * snd_clone_acquire() : The vital part of concurrency management. Must be
+ * called somewhere at the beginning of open() handler. ENODEV is not really
+ * fatal since it just tell the caller that this is not cloned stuff.
+ * EBUSY is *real*, don't forget that!
+ */
+int
+snd_clone_acquire(struct cdev *dev)
+{
+	struct snd_clone_entry *ce;
+
+	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
+
+	ce = dev->si_drv2;
+	if (ce == NULL)
+		return (ENODEV);
+
+	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
+
+	ce->flags &= ~SND_CLONE_INVOKE;
+
+	if (ce->flags & SND_CLONE_BUSY)
+		return (EBUSY);
+
+	ce->flags |= SND_CLONE_BUSY;
+
+	return (0);
+}
+
+/*
+ * snd_clone_release() : Release busy status. Must be called somewhere at
+ * the end of close() handler, or somewhere after fail open().
+ */
+int
+snd_clone_release(struct cdev *dev)
+{
+	struct snd_clone_entry *ce;
+
+	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
+
+	ce = dev->si_drv2;
+	if (ce == NULL)
+		return (ENODEV);
+
+	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
+
+	ce->flags &= ~SND_CLONE_INVOKE;
+
+	if (!(ce->flags & SND_CLONE_BUSY))
+		return (EBADF);
+
+	ce->flags &= ~SND_CLONE_BUSY;
+	ce->pid = -1;
+
+	return (0);
+}
+
+/*
+ * snd_clone_ref/unref() : Garbage collector reference counter. To make
+ * garbage collector run automatically, the sequence must be something like
+ * this (both in open() and close() handlers):
+ *
+ *  open() - 1) snd_clone_acquire()
+ *           2) .... check check ... if failed, snd_clone_release()
+ *           3) Success. Call snd_clone_ref()
+ *
+ * close() - 1) .... check check check ....
+ *           2) Success. snd_clone_release()
+ *           3) snd_clone_unref() . Garbage collector will run at this point
+ *              if this is the last referenced object.
+ */
+int
+snd_clone_ref(struct cdev *dev)
+{
+	struct snd_clone_entry *ce;
+	struct snd_clone *c;
+
+	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
+
+	ce = dev->si_drv2;
+	if (ce == NULL)
+		return (0);
+
+	c = ce->parent;
+	SND_CLONE_ASSERT(c != NULL, ("NULL parent"));
+	SND_CLONE_ASSERT(c->refcount >= 0, ("refcount < 0"));
+
+	return (++c->refcount);
+}
+
+int
+snd_clone_unref(struct cdev *dev)
+{
+	struct snd_clone_entry *ce;
+	struct snd_clone *c;
+
+	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
+
+	ce = dev->si_drv2;
+	if (ce == NULL)
+		return (0);
+
+	c = ce->parent;
+	SND_CLONE_ASSERT(c != NULL, ("NULL parent"));
+	SND_CLONE_ASSERT(c->refcount > 0, ("refcount <= 0"));
+
+	c->refcount--;
+
+	/* 
+	 * Run automatic garbage collector, if needed.
+	 */
+	if ((c->flags & SND_CLONE_GC_UNREF) &&
+	    (!(c->flags & SND_CLONE_GC_LASTREF) ||
+	    (c->refcount == 0 && (c->flags & SND_CLONE_GC_LASTREF))))
+		(void)snd_clone_gc(c);
+
+	return (c->refcount);
+}
+
+void
+snd_clone_register(struct snd_clone_entry *ce, struct cdev *dev)
+{
+	SND_CLONE_ASSERT(ce != NULL, ("NULL snd_clone_entry"));
+	SND_CLONE_ASSERT(dev != NULL, ("NULL dev"));
+	SND_CLONE_ASSERT(dev->si_drv2 == NULL, ("dev->si_drv2 not NULL"));
+	SND_CLONE_ASSERT((ce->flags & SND_CLONE_ALLOC) == SND_CLONE_ALLOC,
+	    ("invalid clone alloc flags=0x%08x", ce->flags));
+	SND_CLONE_ASSERT(ce->devt == NULL, ("ce->devt not NULL"));
+	SND_CLONE_ASSERT(ce->unit == dev2unit(dev),
+	    ("invalid unit ce->unit=0x%08x dev2unit=0x%08x",
+	    ce->unit, dev2unit(dev)));
+
+	SND_CLONE_ASSERT(ce->parent != NULL, ("NULL parent"));
+
+	dev->si_drv2 = ce;
+	ce->devt = dev;
+	ce->flags &= ~SND_CLONE_ALLOC;
+	ce->flags |= SND_CLONE_INVOKE;
+}
+
+struct snd_clone_entry *
+snd_clone_alloc(struct snd_clone *c, struct cdev **dev, int *unit, int tmask)
+{
+	struct snd_clone_entry *ce, *after, *bce, *cce, *nce, *tce;
+	struct timespec now;
+	int cunit, allocunit;
+	pid_t curpid;
+
+	SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
+	SND_CLONE_ASSERT(dev != NULL, ("NULL dev pointer"));
+	SND_CLONE_ASSERT((c->typemask & tmask) == tmask,
+	    ("invalid tmask: typemask=0x%08x tmask=0x%08x",
+	    c->typemask, tmask));
+	SND_CLONE_ASSERT(unit != NULL, ("NULL unit pointer"));
+	SND_CLONE_ASSERT(*unit == -1 || !(*unit & (c->typemask | tmask)),
+	    ("typemask collision: typemask=0x%08x tmask=0x%08x *unit=%d",
+	    c->typemask, tmask, *unit));
+
+	if (!(c->flags & SND_CLONE_ENABLE) ||
+	    (*unit != -1 && *unit > c->maxunit))
+		return (NULL);
+
+	ce = NULL;
+	after = NULL;
+	bce = NULL;	/* "b"usy candidate */
+	cce = NULL;	/* "c"urthread/proc candidate */
+	nce = NULL;	/* "n"ull, totally unbusy candidate */
+	tce = NULL;	/* Last "t"ry candidate */
+	cunit = 0;
+	allocunit = (*unit == -1) ? 0 : *unit;
+	curpid = curthread->td_proc->p_pid;
+
+	snd_timestamp(&now);
+
+	TAILQ_FOREACH(ce, &c->head, link) {
+		/*
+		 * Sort incrementally according to device type.
+		 */
+		if (tmask > (ce->unit & c->typemask)) {
+			if (cunit == 0)
+				after = ce;
+			continue;
+		} else if (tmask < (ce->unit & c->typemask))
+			break;
+
+		/*
+		 * Shoot.. this is where the grumpiness begin. Just
+		 * return immediately.
+		 */
+		if (*unit != -1 && *unit == (ce->unit & ~tmask))
+			goto snd_clone_alloc_out;
+
+		cunit++;
+		/*
+		 * Simmilar device type. Sort incrementally according
+		 * to allocation unit. While here, look for free slot
+		 * and possible collision for new / future allocation.
+		 */
+		if (*unit == -1 && (ce->unit & ~tmask) == allocunit)
+			allocunit++;
+		if ((ce->unit & ~tmask) < allocunit)
+			after = ce;
+		/*
+		 * Clone logic:
+		 *   1. Look for non busy, but keep track of the best
+		 *      possible busy cdev.
+		 *   2. Look for the best (oldest referenced) entry that is
+		 *      in a same process / thread.
+		 *   3. Look for the best (oldest referenced), absolute free
+		 *      entry.
+		 *   4. Lastly, look for the best (oldest referenced)
+		 *      any entries that doesn't fit with anything above.
+		 */
+		if (ce->flags & SND_CLONE_BUSY) {
+			if (ce->devt != NULL && (bce == NULL ||
+			    timespeccmp(&ce->tsp, &bce->tsp, <)))
+				bce = ce;
+			continue;
+		}
+		if (ce->pid == curpid &&
+		    (cce == NULL || timespeccmp(&ce->tsp, &cce->tsp, <)))
+			cce = ce;
+		else if (!(ce->flags & SND_CLONE_INVOKE) &&
+		    (nce == NULL || timespeccmp(&ce->tsp, &nce->tsp, <)))
+			nce = ce;
+		else if (tce == NULL || timespeccmp(&ce->tsp, &tce->tsp, <))
+			tce = ce;
+	}
+	if (*unit != -1)
+		goto snd_clone_alloc_new;
+	else if (cce != NULL) {
+		/* Same proc entry found, go for it */
+		ce = cce;
+		goto snd_clone_alloc_out;
+	} else if (nce != NULL) {
+		/*
+		 * Next, try absolute free entry. If the calculated
+		 * allocunit is smaller, create new entry instead.
+		 */
+		if (allocunit < (nce->unit & ~tmask))
+			goto snd_clone_alloc_new;
+		ce = nce;
+		goto snd_clone_alloc_out;
+	} else if (allocunit > c->maxunit) {
+		/*
+		 * Maximum allowable unit reached. Try returning any
+		 * available cdev and hope for the best. If the lookup is
+		 * done for things like stat(), mtime() etc. , things should
+		 * be ok. Otherwise, open() handler should do further checks
+		 * and decide whether to return correct error code or not.
+		 */
+		if (tce != NULL) {
+			ce = tce;
+			goto snd_clone_alloc_out;
+		} else if (bce != NULL) {
+			ce = bce;
+			goto snd_clone_alloc_out;
+		}
+		return (NULL);
+	}
+
+snd_clone_alloc_new:
+	/*
+	 * No free entries found, and we still haven't reached maximum
+	 * allowable units. Allocate, setup a minimal unique entry with busy
+	 * status so nobody will monkey on this new entry. Unit magic is set
+	 * right here to avoid collision with other contesting handler.
+	 * The caller must be carefull here to maintain its own
+	 * synchronization, as long as it will not conflict with malloc(9)
+	 * operations.
+	 *
+	 * That said, go figure.
+	 */
+	ce = malloc(sizeof(*ce), M_DEVBUF,
+	    ((c->flags & SND_CLONE_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
+	if (ce == NULL) {
+		if (*unit != -1)
+			return (NULL);
+		/*
+		 * We're being dense, ignorance is bliss,
+		 * Super Regulatory Measure (TM).. TRY AGAIN!
+		 */
+		if (nce != NULL) {
+			ce = nce;
+			goto snd_clone_alloc_out;
+		} else if (tce != NULL) {
+			ce = tce;
+			goto snd_clone_alloc_out;
+		} else if (bce != NULL) {
+			ce = bce;
+			goto snd_clone_alloc_out;
+		}
+		return (NULL);
+	}
+	/* Setup new entry */
+	ce->parent = c;
+	ce->unit = tmask | allocunit;
+	ce->pid = curpid;
+	ce->tsp = now;
+	ce->flags |= SND_CLONE_ALLOC;
+	if (after != NULL) {
+		TAILQ_INSERT_AFTER(&c->head, after, ce, link);
+	} else {
+		TAILQ_INSERT_HEAD(&c->head, ce, link);
+	}
+	c->size++;
+	c->tsp = now;
+	/*
+	 * Save new allocation unit for caller which will be used
+	 * by make_dev().
+	 */
+	*unit = allocunit;
+
+	return (ce);
+
+snd_clone_alloc_out:
+	/*
+	 * Set, mark, timestamp the entry if this is a truly free entry.
+	 * Leave busy entry alone.
+	 */
+	if (!(ce->flags & SND_CLONE_BUSY)) {
+		ce->pid = curpid;
+		ce->tsp = now;
+		ce->flags |= SND_CLONE_INVOKE;
+	}
+	c->tsp = now;
+	*dev = ce->devt;
+
+	return (NULL);
+}
--- /dev/null
+++ sys/dev/sound/version.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2007 Ariff Abdullah <ariff 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.
+ *
+ * $FreeBSD: src/sys/dev/sound/version.h,v 1.2 2007/06/16 03:37:27 ariff Exp $
+ */
+
+#ifndef _SND_VERSION_H_
+#define _SND_VERSION_H_
+
+/* 
+ * FreeBSD sound driver internal versioning, nothing to do
+ * with OSS whatsoever. Dear future maintainer, please revisit
+ * this _before_ Jan 1 2148
+ *
+ * Last 2 decimal places reserved for daily versioning, starting
+ * with 0.
+ */
+#define SND_DRV_VERSION		2007061600
+
+#endif /* !_SND_VERSION_H_ */


More information about the Midnightbsd-cvs mailing list