mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-15 12:54:27 +00:00
Revert "sound: Get rid of snd_clone and use DEVFS_CDEVPRIV(9)"
This reverts commit dc831e93ba
.
After several reports in the mailing lists, this commit breaks
pulseaudio. Revert until the issue is resolved.
This commit is contained in:
parent
365067e98e
commit
c0d8f58629
|
@ -23,7 +23,7 @@
|
|||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd March 24, 2024
|
||||
.Dd January 10, 2024
|
||||
.Dt SOUND 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -513,6 +513,14 @@ device state on each tick using a
|
|||
mechanism.
|
||||
Disabled by default and currently only available for a few device drivers.
|
||||
.El
|
||||
.Ss Recording Channels
|
||||
On devices that have more than one recording source (ie: mic and line),
|
||||
there is a corresponding
|
||||
.Pa /dev/dsp%d.r%d
|
||||
device.
|
||||
The
|
||||
.Xr mixer 8
|
||||
utility can be used to start and stop recording from an specific device.
|
||||
.Ss Statistics
|
||||
Channel statistics are only kept while the device is open.
|
||||
So with situations involving overruns and underruns, consider the output
|
||||
|
@ -534,24 +542,36 @@ functions.
|
|||
.Sh FILES
|
||||
The
|
||||
.Nm
|
||||
drivers may create the following device nodes:
|
||||
drivers may create the following
|
||||
device nodes:
|
||||
.Pp
|
||||
.Bl -tag -width ".Pa /dev/sndstat" -compact
|
||||
.It Pa /dev/dsp%d
|
||||
Audio device.
|
||||
The number represents the unit number of the device.
|
||||
.It Pa /dev/dsp
|
||||
Alias of
|
||||
.Pa /dev/dsp${hw.snd.default_unit} .
|
||||
Available only if
|
||||
.Pa hw.snd.basename_clone
|
||||
is set.
|
||||
.Bl -tag -width ".Pa /dev/audio%d.%d" -compact
|
||||
.It Pa /dev/audio%d.%d
|
||||
Sparc-compatible audio device.
|
||||
.It Pa /dev/dsp%d.%d
|
||||
Digitized voice device.
|
||||
.It Pa /dev/dspW%d.%d
|
||||
Like
|
||||
.Pa /dev/dsp ,
|
||||
but 16 bits per sample.
|
||||
.It Pa /dev/dsp%d.p%d
|
||||
Playback channel.
|
||||
.It Pa /dev/dsp%d.r%d
|
||||
Record channel.
|
||||
.It Pa /dev/dsp%d.vp%d
|
||||
Virtual playback channel.
|
||||
.It Pa /dev/dsp%d.vr%d
|
||||
Virtual recording channel.
|
||||
.It Pa /dev/sndstat
|
||||
Current
|
||||
.Nm
|
||||
status, including all channels and drivers.
|
||||
.El
|
||||
.Pp
|
||||
The first number in the device node
|
||||
represents the unit number of the
|
||||
.Nm
|
||||
device.
|
||||
All
|
||||
.Nm
|
||||
devices are listed
|
||||
|
@ -561,6 +581,15 @@ Additional messages are sometimes recorded when the
|
|||
device is probed and attached, these messages can be viewed with the
|
||||
.Xr dmesg 8
|
||||
utility.
|
||||
.Pp
|
||||
The above device nodes are only created on demand through the dynamic
|
||||
.Xr devfs 5
|
||||
clone handler.
|
||||
Users are strongly discouraged to access them directly.
|
||||
For specific sound card access, please instead use
|
||||
.Pa /dev/dsp
|
||||
or
|
||||
.Pa /dev/dsp%d .
|
||||
.Sh EXAMPLES
|
||||
Use the sound metadriver to load all
|
||||
.Nm
|
||||
|
|
|
@ -3064,6 +3064,7 @@ dev/smc/if_smc.c optional smc
|
|||
dev/smc/if_smc_acpi.c optional smc acpi
|
||||
dev/smc/if_smc_fdt.c optional smc fdt
|
||||
dev/snp/snp.c optional snp
|
||||
dev/sound/clone.c optional sound
|
||||
dev/sound/unit.c optional sound
|
||||
dev/sound/pci/als4000.c optional snd_als4000 pci
|
||||
dev/sound/pci/atiixp.c optional snd_atiixp pci
|
||||
|
|
705
sys/dev/sound/clone.c
Normal file
705
sys/dev/sound/clone.c
Normal file
|
@ -0,0 +1,705 @@
|
|||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/proc.h>
|
||||
|
||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
||||
#include "opt_snd.h"
|
||||
#endif
|
||||
|
||||
#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(...) KASSERT(__VA_ARGS__)
|
||||
#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;
|
||||
getnanouptime(&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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
getnanouptime(&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;
|
||||
|
||||
getnanouptime(&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);
|
||||
}
|
127
sys/dev/sound/clone.h
Normal file
127
sys/dev/sound/clone.h
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#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
|
||||
|
||||
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);
|
||||
uint32_t snd_clone_getflags(struct snd_clone *);
|
||||
uint32_t snd_clone_setflags(struct snd_clone *, uint32_t);
|
||||
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 */
|
File diff suppressed because it is too large
Load diff
|
@ -33,9 +33,12 @@
|
|||
|
||||
extern struct cdevsw dsp_cdevsw;
|
||||
|
||||
int dsp_make_dev(device_t);
|
||||
void dsp_destroy_dev(device_t);
|
||||
struct dsp_cdevinfo;
|
||||
|
||||
char *dsp_unit2name(char *, size_t, int);
|
||||
int dsp_oss_audioinfo(struct cdev *, oss_audioinfo *);
|
||||
|
||||
void dsp_cdevinfo_init(struct snddev_info *);
|
||||
void dsp_cdevinfo_flush(struct snddev_info *);
|
||||
|
||||
#endif /* !_PCMDSP_H_ */
|
||||
|
|
|
@ -117,6 +117,24 @@ snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand
|
|||
return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep);
|
||||
}
|
||||
|
||||
static void
|
||||
pcm_clonereset(struct snddev_info *d)
|
||||
{
|
||||
int cmax;
|
||||
|
||||
PCM_BUSYASSERT(d);
|
||||
|
||||
cmax = d->playcount + d->reccount - 1;
|
||||
if (d->pvchancount > 0)
|
||||
cmax += max(d->pvchancount, snd_maxautovchans) - 1;
|
||||
if (d->rvchancount > 0)
|
||||
cmax += max(d->rvchancount, snd_maxautovchans) - 1;
|
||||
if (cmax > PCMMAXCLONE)
|
||||
cmax = PCMMAXCLONE;
|
||||
(void)snd_clone_gc(d->clones);
|
||||
(void)snd_clone_setmaxunit(d->clones, cmax);
|
||||
}
|
||||
|
||||
int
|
||||
pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
|
||||
{
|
||||
|
@ -202,6 +220,8 @@ pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
|
|||
CHN_UNLOCK(ch);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
else
|
||||
pcm_clonereset(d);
|
||||
} else if (newcnt < vcnt) {
|
||||
KASSERT(num == -1,
|
||||
("bogus vchan_destroy() request num=%d", num));
|
||||
|
@ -232,6 +252,7 @@ pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num)
|
|||
CHN_UNLOCK(c);
|
||||
break;
|
||||
}
|
||||
pcm_clonereset(d);
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
@ -380,6 +401,8 @@ pcm_setmaxautovchans(struct snddev_info *d, int num)
|
|||
(void)pcm_setvchans(d, PCMDIR_REC, num, -1);
|
||||
else if (num > 0 && d->rvchancount == 0)
|
||||
(void)pcm_setvchans(d, PCMDIR_REC, 1, -1);
|
||||
|
||||
pcm_clonereset(d);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -742,6 +765,10 @@ pcm_setstatus(device_t dev, char *str)
|
|||
|
||||
PCM_LOCK(d);
|
||||
|
||||
/* Last stage, enable cloning. */
|
||||
if (d->clones != NULL)
|
||||
(void)snd_clone_enable(d->clones);
|
||||
|
||||
/* Done, we're ready.. */
|
||||
d->flags |= SD_F_REGISTERED;
|
||||
|
||||
|
@ -856,6 +883,118 @@ sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)
|
|||
return (err);
|
||||
}
|
||||
|
||||
#ifdef SND_DEBUG
|
||||
static int
|
||||
sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
uint32_t flags;
|
||||
int err;
|
||||
|
||||
d = oidp->oid_arg1;
|
||||
if (!PCM_REGISTERED(d) || d->clones == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
PCM_ACQUIRE_QUICK(d);
|
||||
|
||||
flags = snd_clone_getflags(d->clones);
|
||||
err = sysctl_handle_int(oidp, &flags, 0, req);
|
||||
|
||||
if (err == 0 && req->newptr != NULL) {
|
||||
if (flags & ~SND_CLONE_MASK)
|
||||
err = EINVAL;
|
||||
else
|
||||
(void)snd_clone_setflags(d->clones, flags);
|
||||
}
|
||||
|
||||
PCM_RELEASE_QUICK(d);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
int err, deadline;
|
||||
|
||||
d = oidp->oid_arg1;
|
||||
if (!PCM_REGISTERED(d) || d->clones == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
PCM_ACQUIRE_QUICK(d);
|
||||
|
||||
deadline = snd_clone_getdeadline(d->clones);
|
||||
err = sysctl_handle_int(oidp, &deadline, 0, req);
|
||||
|
||||
if (err == 0 && req->newptr != NULL) {
|
||||
if (deadline < 0)
|
||||
err = EINVAL;
|
||||
else
|
||||
(void)snd_clone_setdeadline(d->clones, deadline);
|
||||
}
|
||||
|
||||
PCM_RELEASE_QUICK(d);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
int err, val;
|
||||
|
||||
d = oidp->oid_arg1;
|
||||
if (!PCM_REGISTERED(d) || d->clones == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
val = 0;
|
||||
err = sysctl_handle_int(oidp, &val, 0, req);
|
||||
|
||||
if (err == 0 && req->newptr != NULL && val != 0) {
|
||||
PCM_ACQUIRE_QUICK(d);
|
||||
val = snd_clone_gc(d->clones);
|
||||
PCM_RELEASE_QUICK(d);
|
||||
if (bootverbose != 0 || snd_verbose > 3)
|
||||
device_printf(d->dev, "clone gc: pruned=%d\n", val);
|
||||
}
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
int i, err, val;
|
||||
|
||||
val = 0;
|
||||
err = sysctl_handle_int(oidp, &val, 0, req);
|
||||
|
||||
if (err == 0 && req->newptr != NULL && val != 0) {
|
||||
for (i = 0; pcm_devclass != NULL &&
|
||||
i < devclass_get_maxunit(pcm_devclass); i++) {
|
||||
d = devclass_get_softc(pcm_devclass, i);
|
||||
if (!PCM_REGISTERED(d) || d->clones == NULL)
|
||||
continue;
|
||||
PCM_ACQUIRE_QUICK(d);
|
||||
val = snd_clone_gc(d->clones);
|
||||
PCM_RELEASE_QUICK(d);
|
||||
if (bootverbose != 0 || snd_verbose > 3)
|
||||
device_printf(d->dev, "clone gc: pruned=%d\n",
|
||||
val);
|
||||
}
|
||||
}
|
||||
|
||||
return (err);
|
||||
}
|
||||
SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc,
|
||||
CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int),
|
||||
sysctl_hw_snd_clone_gc, "I",
|
||||
"global clone garbage collector");
|
||||
#endif
|
||||
|
||||
static u_int8_t
|
||||
pcm_mode_init(struct snddev_info *d)
|
||||
{
|
||||
|
@ -894,6 +1033,23 @@ pcm_sysinit(device_t dev)
|
|||
OID_AUTO, "mode", CTLFLAG_RD, NULL, mode,
|
||||
"mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than one"
|
||||
"mode is supported)");
|
||||
#ifdef SND_DEBUG
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
|
||||
"clone_flags", CTLTYPE_UINT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
|
||||
d, sizeof(d), sysctl_dev_pcm_clone_flags, "IU",
|
||||
"clone flags");
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
|
||||
"clone_deadline", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
|
||||
d, sizeof(d), sysctl_dev_pcm_clone_deadline, "I",
|
||||
"clone expiration deadline (ms)");
|
||||
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
|
||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
|
||||
"clone_gc",
|
||||
CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, sizeof(d),
|
||||
sysctl_dev_pcm_clone_gc, "I", "clone garbage collector");
|
||||
#endif
|
||||
if (d->flags & SD_F_AUTOVCHAN)
|
||||
vchan_initsys(dev);
|
||||
if (d->flags & SD_F_EQ)
|
||||
|
@ -925,6 +1081,7 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
|||
d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
|
||||
cv_init(&d->cv, device_get_nameunit(dev));
|
||||
PCM_ACQUIRE_QUICK(d);
|
||||
dsp_cdevinfo_init(d);
|
||||
#if 0
|
||||
/*
|
||||
* d->flags should be cleared by the allocator of the softc.
|
||||
|
@ -953,6 +1110,16 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
|||
d->rvchanrate = 0;
|
||||
d->rvchanformat = 0;
|
||||
|
||||
/*
|
||||
* Create clone manager, disabled by default. Cloning will be
|
||||
* enabled during final stage of driver initialization through
|
||||
* pcm_setstatus().
|
||||
*/
|
||||
d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE,
|
||||
SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK |
|
||||
SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF |
|
||||
SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED);
|
||||
|
||||
CHN_INIT(d, channels.pcm);
|
||||
CHN_INIT(d, channels.pcm.busy);
|
||||
CHN_INIT(d, channels.pcm.opened);
|
||||
|
@ -975,7 +1142,7 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
|||
|
||||
sndstat_register(dev, d->status);
|
||||
|
||||
return (dsp_make_dev(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -1012,11 +1179,23 @@ pcm_unregister(device_t dev)
|
|||
CHN_UNLOCK(ch);
|
||||
}
|
||||
|
||||
dsp_destroy_dev(dev);
|
||||
if (d->clones != NULL) {
|
||||
if (snd_clone_busy(d->clones) != 0) {
|
||||
device_printf(dev, "unregister: clone busy\n");
|
||||
PCM_RELEASE_QUICK(d);
|
||||
return (EBUSY);
|
||||
} else {
|
||||
PCM_LOCK(d);
|
||||
(void)snd_clone_disable(d->clones);
|
||||
PCM_UNLOCK(d);
|
||||
}
|
||||
}
|
||||
|
||||
if (mixer_uninit(dev) == EBUSY) {
|
||||
device_printf(dev, "unregister: mixer busy\n");
|
||||
PCM_LOCK(d);
|
||||
if (d->clones != NULL)
|
||||
(void)snd_clone_enable(d->clones);
|
||||
PCM_RELEASE(d);
|
||||
PCM_UNLOCK(d);
|
||||
return (EBUSY);
|
||||
|
@ -1030,6 +1209,15 @@ pcm_unregister(device_t dev)
|
|||
d->flags &= ~SD_F_REGISTERED;
|
||||
PCM_UNLOCK(d);
|
||||
|
||||
/*
|
||||
* No lock being held, so this thing can be flushed without
|
||||
* stucking into devdrn oblivion.
|
||||
*/
|
||||
if (d->clones != NULL) {
|
||||
snd_clone_destroy(d->clones);
|
||||
d->clones = NULL;
|
||||
}
|
||||
|
||||
if (d->play_sysctl_tree != NULL) {
|
||||
sysctl_ctx_free(&d->play_sysctl_ctx);
|
||||
d->play_sysctl_tree = NULL;
|
||||
|
@ -1042,6 +1230,8 @@ pcm_unregister(device_t dev)
|
|||
while (!CHN_EMPTY(d, channels.pcm))
|
||||
pcm_killchan(dev);
|
||||
|
||||
dsp_cdevinfo_flush(d);
|
||||
|
||||
PCM_LOCK(d);
|
||||
PCM_RELEASE(d);
|
||||
cv_destroy(&d->cv);
|
||||
|
|
|
@ -88,6 +88,7 @@ struct snd_mixer;
|
|||
#include <dev/sound/pcm/feeder.h>
|
||||
#include <dev/sound/pcm/mixer.h>
|
||||
#include <dev/sound/pcm/dsp.h>
|
||||
#include <dev/sound/clone.h>
|
||||
#include <dev/sound/unit.h>
|
||||
|
||||
#define PCM_SOFTC_SIZE (sizeof(struct snddev_info))
|
||||
|
@ -111,6 +112,8 @@ struct snd_mixer;
|
|||
#define PCMMAXDEV (snd_max_d())
|
||||
#define PCMMAXCHAN (snd_max_c())
|
||||
|
||||
#define PCMMAXCLONE PCMMAXCHAN
|
||||
|
||||
#define PCMUNIT(x) (snd_unit2u(dev2unit(x)))
|
||||
#define PCMDEV(x) (snd_unit2d(dev2unit(x)))
|
||||
#define PCMCHAN(x) (snd_unit2c(dev2unit(x)))
|
||||
|
@ -367,6 +370,8 @@ struct snddev_info {
|
|||
} opened;
|
||||
} pcm;
|
||||
} channels;
|
||||
TAILQ_HEAD(dsp_cdevinfo_linkhead, dsp_cdevinfo) dsp_cdevinfo_pool;
|
||||
struct snd_clone *clones;
|
||||
unsigned devcount, playcount, reccount, pvchancount, rvchancount ;
|
||||
unsigned flags;
|
||||
unsigned int bufsz;
|
||||
|
@ -375,7 +380,6 @@ struct snddev_info {
|
|||
char status[SND_STATUSLEN];
|
||||
struct mtx *lock;
|
||||
struct cdev *mixer_dev;
|
||||
struct cdev *dsp_dev;
|
||||
uint32_t pvchanrate, pvchanformat;
|
||||
uint32_t rvchanrate, rvchanformat;
|
||||
int32_t eqpreamp;
|
||||
|
|
|
@ -16,7 +16,7 @@ SRCS+= feeder_matrix.c feeder_mixer.c
|
|||
SRCS+= feeder_eq_gen.h feeder_rate_gen.h snd_fxdiv_gen.h
|
||||
SRCS+= mpu_if.h mpufoi_if.h synth_if.h
|
||||
SRCS+= mpu_if.c mpufoi_if.c synth_if.c
|
||||
SRCS+= ac97.c ac97_patch.c buffer.c channel.c dsp.c
|
||||
SRCS+= ac97.c ac97_patch.c buffer.c channel.c clone.c dsp.c
|
||||
SRCS+= mixer.c sndstat.c sound.c unit.c vchan.c
|
||||
SRCS+= midi.c mpu401.c sequencer.c
|
||||
|
||||
|
|
Loading…
Reference in a new issue