Last major commit and updates for RELENG_7:

Add few new files. The _real_ commit will follow shortly, so fasten
up your seatbelts, sit back and enjoy the ride..
This commit is contained in:
Ariff Abdullah 2007-05-31 18:35:24 +00:00
parent 88843a0a1e
commit f03f99e35f
5 changed files with 1254 additions and 0 deletions

834
sys/dev/sound/clone.c Normal file
View File

@ -0,0 +1,834 @@
/*-
* Copyright (c) 2007 Ariff Abdullah <ariff@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$
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.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;
#ifdef SND_DIAGNOSTIC
struct mtx *lock;
#endif
struct timespec tsp;
int refcount;
int size;
int typemask;
int maxunit;
int deadline;
uint32_t flags;
};
#ifdef SND_DIAGNOSTIC
#define SND_CLONE_LOCKASSERT(x) do { \
if ((x)->lock == NULL) \
panic("%s(): NULL mutex!", __func__); \
if (mtx_owned((x)->lock) == 0) \
panic("%s(): mutex not owned!", __func__); \
} while(0)
#define SND_CLONE_ASSERT(x, y) do { \
if (!(x)) \
panic y; \
} while(0)
#else
#define SND_CLONE_LOCKASSERT(...)
#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, sizeof(val), 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. Mutex is
* not a mandatory requirement if the caller can guarantee safety across
* concurrent access.
*/
struct snd_clone *
snd_clone_create(
#ifdef SND_DIAGNOSTIC
struct mtx *lock,
#endif
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);
#ifdef SND_DIAGNOSTIC
c->lock = lock;
#endif
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"));
SND_CLONE_LOCKASSERT(c);
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"));
SND_CLONE_LOCKASSERT(c);
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"));
SND_CLONE_LOCKASSERT(c);
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"));
SND_CLONE_LOCKASSERT(c);
return (c->size);
}
int
snd_clone_getmaxunit(struct snd_clone *c)
{
SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
SND_CLONE_LOCKASSERT(c);
return (c->maxunit);
}
int
snd_clone_setmaxunit(struct snd_clone *c, int maxunit)
{
SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
SND_CLONE_LOCKASSERT(c);
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"));
SND_CLONE_LOCKASSERT(c);
return (c->deadline);
}
int
snd_clone_setdeadline(struct snd_clone *c, int deadline)
{
SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
SND_CLONE_LOCKASSERT(c);
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"));
SND_CLONE_LOCKASSERT(c);
*tsp = c->tsp;
return (0);
}
uint32_t
snd_clone_getflags(struct snd_clone *c)
{
SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
SND_CLONE_LOCKASSERT(c);
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_LOCKASSERT(c);
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"));
SND_CLONE_LOCKASSERT(ce->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"));
SND_CLONE_LOCKASSERT(ce->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"));
SND_CLONE_LOCKASSERT(ce->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"));
SND_CLONE_LOCKASSERT(c);
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;
SND_CLONE_ASSERT(c != NULL, ("NULL snd_clone"));
SND_CLONE_ASSERT(c->refcount == 0, ("refcount > 0"));
SND_CLONE_LOCKASSERT(c);
while (!TAILQ_EMPTY(&c->head)) {
ce = TAILQ_FIRST(&c->head);
TAILQ_REMOVE(&c->head, ce, link);
if (ce->devt != NULL)
destroy_dev(ce->devt);
free(ce, M_DEVBUF);
}
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"));
SND_CLONE_LOCKASSERT(ce->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"));
SND_CLONE_LOCKASSERT(ce->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"));
SND_CLONE_LOCKASSERT(c);
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"));
SND_CLONE_LOCKASSERT(c);
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"));
SND_CLONE_LOCKASSERT(ce->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_LOCKASSERT(c);
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 since we had to
* give up locking for further setup. Unit magic is set right here
* to avoid collision with other contesting handler.
*/
ce = malloc(sizeof(*ce), M_DEVBUF, 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);
}

132
sys/dev/sound/clone.h Normal file
View File

@ -0,0 +1,132 @@
/*-
* Copyright (c) 2007 Ariff Abdullah <ariff@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$
*/
#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.
*/
#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_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)
/*
* 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 *);
#ifdef SND_DIAGNOSTIC
struct snd_clone *snd_clone_create(struct mtx *, int, int, int, uint32_t);
#else
struct snd_clone *snd_clone_create(int, int, int, uint32_t);
#endif
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 */

194
sys/dev/sound/unit.c Normal file
View File

@ -0,0 +1,194 @@
/*-
* Copyright (c) 2007 Ariff Abdullah <ariff@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$
*/
#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);
}

52
sys/dev/sound/unit.h Normal file
View File

@ -0,0 +1,52 @@
/*-
* Copyright (c) 2007 Ariff Abdullah <ariff@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$
*/
#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_ */

42
sys/dev/sound/version.h Normal file
View File

@ -0,0 +1,42 @@
/*-
* Copyright (c) 2007 Ariff Abdullah <ariff@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$
*/
#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 2007060100
#endif /* !_SND_VERSION_H_ */