MFp4 the sound Google Summer of Code project:

The goal was to sync with the OSSv4 API 4Front Technologies uses in their
proprietary OSS driver. This was successful as far as possible. The part
of the API which is stable is implemented, for the rest there are some
stubs already.

New system ioctls:
 - SNDCTL_SYSINFO - obtain audio system info (version, # of audio/midi/
   mixer devices, etc.)
 - SNDCTL_AUDIOINFO - fetch details about a specific audio device
 - SNDCTL_MIXERINFO - fetch details about a specific mixer device

New audio ioctls:
 - Sync groups (SNDCTL_DSP_SYNCGROUP/SNDCTL_DSP_SYNCSTART) which allow
   triggered playback/recording on multiple devices (even across processes
   simultaneously).
 - Peak meters (SNDCTL_DSP_GETIPEAKS/SNDCTL_DSP_GETOPEAKS) - can query
   audio drivers for peak levels (needs driver support, disabled for now).
 - Per channel playback/recording levels -
   SNDCTL_DSP_{GET,SET}{PLAY,REC}VOL.  Note that these are still in name
   only, just wrapping around the AC97-style mixer at the moment. The next
   step is to push them down to the drivers.

Audio ioctls still under development by 4Front (for which stubs may exist
in this commit):
 - SNDCTL_GETNAME, SNDCTL_{GET,SET}{SONG,LABEL}
 - SNDCTL_DSP_{GET,SET}_CHNORDER
 - SNDCTL_MIX_ENUMINFO, SNDCTL_MIX_EXTINFO - (might be documented enough in
   the OSS releases to work on this.  These ioctls cover the cool "twiddle
   any knob on your card" features.)

Missing:
 - SNDCTL_DSP_COOKEDMODE -- this ioctl is used to give applications direct
   access to a card's buffers, bypassing the feeder architecture.  It's
   a toughy -- "someone" needs to decide :
   (a) if this is desireable, and (b) if it's reasonably feasible.

Updates for driver writers:
 So far, only two routines to the channel class (in channel_if.m) are added.
 One is for fetching a list of discrete supported playback/recording rates
 of a channel, and the other is for fetching peak level info (useful for
 drawing peak meters).  Interested parties may want to help pushing down
 SNDCTL_DSP_{GET,SET}{PLAY,REC}VOL into the drivers.

To use the new stuff you need to rebuild the sound drivers or your kernel
(depending on if you use modules or not) and to install soundcard.h (a
buildworld/installworld handles this).

Sponsored by:	Google SoC 2006
Submitted by:	ryanb
Many thanks to:	4Front Technologies for their cooperation, explanations
		and the nice license of their soundcard.h.
This commit is contained in:
Alexander Leidinger 2006-09-23 20:45:47 +00:00
parent 402865f637
commit b611c801f0
13 changed files with 2509 additions and 11 deletions

View File

@ -117,6 +117,11 @@ sndbuf_free(struct snd_dbuf *b)
free(b->tmpbuf, M_DEVBUF);
b->tmpbuf = NULL;
if (b->shadbuf)
free(b->shadbuf, M_DEVBUF);
b->shadbuf = NULL;
b->sl = 0;
if (b->dmamap)
bus_dmamap_unload(b->dmatag, b->dmamap);
@ -168,6 +173,7 @@ int
sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
{
u_int8_t *buf, *tmpbuf, *f1, *f2;
u_int8_t *shadbuf, *f3;
unsigned int bufsize;
int ret;
@ -189,6 +195,15 @@ sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
ret = ENOMEM;
goto out;
}
shadbuf = malloc(bufsize, M_DEVBUF, M_WAITOK);
if (shadbuf == NULL) {
free(buf, M_DEVBUF);
free(tmpbuf, M_DEVBUF);
ret = ENOMEM;
goto out;
}
chn_lock(b->channel);
b->blkcnt = blkcnt;
@ -199,6 +214,9 @@ sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
f2 = b->tmpbuf;
b->buf = buf;
b->tmpbuf = tmpbuf;
f3 = b->shadbuf;
b->shadbuf = shadbuf;
b->sl = bufsize;
sndbuf_reset(b);
@ -207,6 +225,8 @@ sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
free(f1, M_DEVBUF);
if (f2)
free(f2, M_DEVBUF);
if (f3)
free(f3, M_DEVBUF);
ret = 0;
out:
@ -214,6 +234,15 @@ sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
return ret;
}
/**
* @brief Zero out space in buffer free area
*
* This function clears a chunk of @c length bytes in the buffer free area
* (i.e., where the next write will be placed).
*
* @param b buffer context
* @param length number of bytes to blank
*/
void
sndbuf_clear(struct snd_dbuf *b, unsigned int length)
{
@ -241,6 +270,11 @@ sndbuf_clear(struct snd_dbuf *b, unsigned int length)
}
}
/**
* @brief Zap buffer contents, resetting "ready area" fields
*
* @param b buffer context
*/
void
sndbuf_fillsilence(struct snd_dbuf *b)
{
@ -260,6 +294,23 @@ sndbuf_fillsilence(struct snd_dbuf *b)
b->rl = b->bufsize;
}
/**
* @brief Reset buffer w/o flushing statistics
*
* This function just zeroes out buffer contents and sets the "ready length"
* to zero. This was originally to facilitate minimal playback interruption
* (i.e., dropped samples) in SNDCTL_DSP_SILENCE/SKIP ioctls.
*
* @param b buffer context
*/
void
sndbuf_softreset(struct snd_dbuf *b)
{
b->rl = 0;
if (b->buf && b->bufsize > 0)
sndbuf_clear(b, b->bufsize);
}
void
sndbuf_reset(struct snd_dbuf *b)
{
@ -272,6 +323,7 @@ sndbuf_reset(struct snd_dbuf *b)
b->xrun = 0;
if (b->buf && b->bufsize > 0)
sndbuf_clear(b, b->bufsize);
sndbuf_clearshadow(b);
}
u_int32_t
@ -493,6 +545,19 @@ sndbuf_updateprevtotal(struct snd_dbuf *b)
/************************************************************/
/**
* @brief Acquire buffer space to extend ready area
*
* This function extends the ready area length by @c count bytes, and may
* optionally copy samples from another location stored in @c from. The
* counter @c snd_dbuf::total is also incremented by @c count bytes.
*
* @param b audio buffer
* @param from sample source (optional)
* @param count number of bytes to acquire
*
* @retval 0 Unconditional
*/
int
sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count)
{
@ -516,6 +581,20 @@ sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count)
return 0;
}
/**
* @brief Dispose samples from channel buffer, increasing size of ready area
*
* This function discards samples from the supplied buffer by advancing the
* ready area start pointer and decrementing the ready area length. If
* @c to is not NULL, then the discard samples will be copied to the location
* it points to.
*
* @param b PCM channel sound buffer
* @param to destination buffer (optional)
* @param count number of bytes to discard
*
* @returns 0 unconditionally
*/
int
sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count)
{
@ -592,3 +671,49 @@ sndbuf_setflags(struct snd_dbuf *b, u_int32_t flags, int on)
b->flags |= flags;
}
/**
* @brief Clear the shadow buffer by filling with samples equal to zero.
*
* @param b buffer to clear
*/
void
sndbuf_clearshadow(struct snd_dbuf *b)
{
KASSERT(b != NULL, ("b is a null pointer"));
KASSERT(b->sl >= 0, ("illegal shadow length"));
if ((b->shadbuf != NULL) && (b->sl > 0)) {
if (b->fmt & AFMT_SIGNED)
memset(b->shadbuf, 0x00, b->sl);
else
memset(b->shadbuf, 0x80, b->sl);
}
}
#ifdef OSSV4_EXPERIMENT
/**
* @brief Return peak value from samples in buffer ready area.
*
* Peak ranges from 0-32767. If channel is monaural, most significant 16
* bits will be zero. For now, only expects to work with 1-2 channel
* buffers.
*
* @note Currently only operates with linear PCM formats.
*
* @param b buffer to analyze
* @param lpeak pointer to store left peak value
* @param rpeak pointer to store right peak value
*/
void
sndbuf_getpeaks(struct snd_dbuf *b, int *lp, int *rp)
{
u_int32_t lpeak, rpeak;
lpeak = 0;
rpeak = 0;
/**
* @todo fill this in later
*/
}
#endif

View File

@ -38,6 +38,8 @@
struct snd_dbuf {
device_t dev;
u_int8_t *buf, *tmpbuf;
u_int8_t *shadbuf; /**< shadow buffer used w/ S_D_SILENCE/SKIP */
volatile int sl; /**< shadbuf ready length in # of bytes */
unsigned int bufsize, maxsize;
volatile int dl; /* transfer size */
volatile int rp; /* pointers to the ready area */
@ -70,6 +72,8 @@ int sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz)
void sndbuf_reset(struct snd_dbuf *b);
void sndbuf_clear(struct snd_dbuf *b, unsigned int length);
void sndbuf_fillsilence(struct snd_dbuf *b);
void sndbuf_softreset(struct snd_dbuf *b);
void sndbuf_clearshadow(struct snd_dbuf *b);
u_int32_t sndbuf_getfmt(struct snd_dbuf *b);
int sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt);
@ -117,3 +121,7 @@ int sndbuf_dmasetdir(struct snd_dbuf *b, int dir);
void sndbuf_dma(struct snd_dbuf *b, int go);
int sndbuf_dmaptr(struct snd_dbuf *b);
void sndbuf_dmabounce(struct snd_dbuf *b);
#ifdef OSSV4_EXPERIMENT
void sndbuf_getpeaks(struct snd_dbuf *b, int *lp, int *rp);
#endif

View File

@ -68,6 +68,24 @@ static int report_soft_formats = 1;
SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
&report_soft_formats, 1, "report software-emulated formats");
/**
* @brief Channel sync group lock
*
* Clients should acquire this lock @b without holding any channel locks
* before touching syncgroups or the main syncgroup list.
*/
struct mtx snd_pcm_syncgroups_mtx;
MTX_SYSINIT(pcm_syncgroup, &snd_pcm_syncgroups_mtx, "PCM channel sync group lock", MTX_DEF);
/**
* @brief syncgroups' master list
*
* Each time a channel syncgroup is created, it's added to this list. This
* list should only be accessed with @sa snd_pcm_syncgroups_mtx held.
*
* See SNDCTL_DSP_SYNCGROUP for more information.
*/
struct pcm_synclist snd_pcm_syncgroups = SLIST_HEAD_INITIALIZER(head);
static int chn_buildfeeder(struct pcm_channel *c);
static void
@ -87,14 +105,23 @@ chn_lockinit(struct pcm_channel *c, int dir)
c->lock = snd_mtxcreate(c->name, "pcm fake channel");
break;
}
cv_init(&c->cv, c->name);
}
static void
chn_lockdestroy(struct pcm_channel *c)
{
snd_mtxfree(c->lock);
cv_destroy(&c->cv);
}
/**
* @brief Determine channel is ready for I/O
*
* @retval 1 = ready for I/O
* @retval 0 = not ready for I/O
*/
static int
chn_polltrigger(struct pcm_channel *c)
{
@ -112,8 +139,8 @@ chn_polltrigger(struct pcm_channel *c)
#if 0
lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1;
#endif
lim = 1;
return (amt >= lim)? 1 : 0;
lim = c->lw;
return (amt >= lim) ? 1 : 0;
}
return 0;
}
@ -310,12 +337,25 @@ chn_write(struct pcm_channel *c, struct uio *buf)
ret = 0;
count = hz;
while (!ret && (buf->uio_resid > 0) && (count > 0)) {
sz = sndbuf_getfree(bs);
if (sz == 0) {
if (c->flags & CHN_F_NBIO)
ret = EWOULDBLOCK;
else {
else if (c->flags & CHN_F_NOTRIGGER) {
/**
* @todo Evaluate whether EAGAIN is truly desirable.
* 4Front drivers behave like this, but I'm
* not sure if it at all violates the "write
* should be allowed to block" model.
*
* The idea is that, while set with CHN_F_NOTRIGGER,
* a channel isn't playing, *but* without this we
* end up with "interrupt timeout / channel dead".
*/
ret = EAGAIN;
} else {
timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs));
if (timeout < 1)
timeout = 1;
@ -829,6 +869,7 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
c->bufsoft = bs;
c->flags = 0;
c->feederflags = 0;
c->sm = NULL;
ret = ENODEV;
CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */
@ -853,6 +894,19 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction)
if (ret)
goto out;
/**
* @todo Should this be moved somewhere else? The primary buffer
* is allocated by the driver or via DMA map setup, and tmpbuf
* seems to only come into existence in sndbuf_resize().
*/
if (c->direction == PCMDIR_PLAY) {
bs->sl = sndbuf_getmaxsize(bs);
bs->shadbuf = malloc(bs->sl, M_DEVBUF, M_NOWAIT);
if (bs->shadbuf == NULL) {
ret = ENOMEM;
goto out;
}
}
out:
CHN_UNLOCK(c);
@ -1195,6 +1249,12 @@ chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz)
goto out1;
}
/*
* OSSv4 docs: "By default OSS will set the low water level equal
* to the fragment size which is optimal in most cases."
*/
c->lw = sndbuf_getblksz(bs);
chn_resetbuf(c);
out1:
KASSERT(sndbuf_getsize(bs) == 0 ||
@ -1243,6 +1303,17 @@ chn_trigger(struct pcm_channel *c, int go)
return ret;
}
/**
* @brief Queries sound driver for sample-aligned hardware buffer pointer index
*
* This function obtains the hardware pointer location, then aligns it to
* the current bytes-per-sample value before returning. (E.g., a channel
* running in 16 bit stereo mode would require 4 bytes per sample, so a
* hwptr value ranging from 32-35 would be returned as 32.)
*
* @param c PCM channel context
* @returns sample-aligned hardware buffer pointer index
*/
int
chn_getptr(struct pcm_channel *c)
{
@ -1501,6 +1572,77 @@ chn_notify(struct pcm_channel *c, u_int32_t flags)
return 0;
}
/**
* @brief Fetch array of supported discrete sample rates
*
* Wrapper for CHANNEL_GETRATES. Please see channel_if.m:getrates() for
* detailed information.
*
* @note If the operation isn't supported, this function will just return 0
* (no rates in the array), and *rates will be set to NULL. Callers
* should examine rates @b only if this function returns non-zero.
*
* @param c pcm channel to examine
* @param rates pointer to array of integers; rate table will be recorded here
*
* @return number of rates in the array pointed to be @c rates
*/
int
chn_getrates(struct pcm_channel *c, int **rates)
{
KASSERT(rates != NULL, ("rates is null"));
CHN_LOCKASSERT(c);
return CHANNEL_GETRATES(c->methods, c->devinfo, rates);
}
/**
* @brief Remove channel from a sync group, if there is one.
*
* This function is initially intended for the following conditions:
* - Starting a syncgroup (@c SNDCTL_DSP_SYNCSTART ioctl)
* - Closing a device. (A channel can't be destroyed if it's still in use.)
*
* @note Before calling this function, the syncgroup list mutex must be
* held. (Consider pcm_channel::sm protected by the SG list mutex
* whether @c c is locked or not.)
*
* @param c channel device to be started or closed
* @returns If this channel was the only member of a group, the group ID
* is returned to the caller so that the caller can release it
* via free_unr() after giving up the syncgroup lock. Else it
* returns 0.
*/
int
chn_syncdestroy(struct pcm_channel *c)
{
struct pcmchan_syncmember *sm;
struct pcmchan_syncgroup *sg;
int sg_id;
sg_id = 0;
PCM_SG_LOCKASSERT(MA_OWNED);
if (c->sm != NULL) {
sm = c->sm;
sg = sm->parent;
c->sm = NULL;
KASSERT(sg != NULL, ("syncmember has null parent"));
SLIST_REMOVE(&sg->members, sm, pcmchan_syncmember, link);
free(sm, M_DEVBUF);
if (SLIST_EMPTY(&sg->members)) {
SLIST_REMOVE(&snd_pcm_syncgroups, sg, pcmchan_syncgroup, link);
sg_id = sg->id;
free(sg, M_DEVBUF);
}
}
return sg_id;
}
void
chn_lock(struct pcm_channel *c)
{
@ -1512,3 +1654,12 @@ chn_unlock(struct pcm_channel *c)
{
CHN_UNLOCK(c);
}
#ifdef OSSV4_EXPERIMENT
int
chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak)
{
CHN_LOCKASSERT(c);
return CHANNEL_GETPEAKS(c->methods, c->devinfo, lpeak, rpeak);
}
#endif

View File

@ -37,6 +37,37 @@ struct pcmchan_caps {
u_int32_t caps;
};
/* Forward declarations */
struct pcm_channel;
struct pcmchan_syncgroup;
struct pcmchan_syncmember;
extern struct mtx snd_pcm_syncgroups_mtx;
extern SLIST_HEAD(pcm_synclist, pcmchan_syncgroup) snd_pcm_syncgroups;
#define PCM_SG_LOCK() mtx_lock(&snd_pcm_syncgroups_mtx)
#define PCM_SG_TRYLOCK() mtx_trylock(&snd_pcm_syncgroups_mtx)
#define PCM_SG_UNLOCK() mtx_unlock(&snd_pcm_syncgroups_mtx)
#define PCM_SG_LOCKASSERT(arg) mtx_assert(&snd_pcm_syncgroups_mtx, arg)
/**
* @brief Specifies an audio device sync group
*/
struct pcmchan_syncgroup {
SLIST_ENTRY(pcmchan_syncgroup) link;
SLIST_HEAD(, pcmchan_syncmember) members;
int id; /**< Group identifier; set to address of group. */
};
/**
* @brief Specifies a container for members of a sync group
*/
struct pcmchan_syncmember {
SLIST_ENTRY(pcmchan_syncmember) link;
struct pcmchan_syncgroup *parent; /**< group head */
struct pcm_channel *ch;
};
#define CHN_NAMELEN 32
struct pcm_channel {
kobj_t methods;
@ -63,6 +94,33 @@ struct pcm_channel {
device_t dev;
char name[CHN_NAMELEN];
struct mtx *lock;
/**
* Increment,decrement this around operations that temporarily yield
* lock.
*/
unsigned int inprog;
/**
* Special channel operations should examine @c inprog after acquiring
* lock. If zero, operations may continue. Else, thread should
* wait on this cv for previous operation to finish.
*/
struct cv cv;
/**
* Low water mark for select()/poll().
*
* This is initialized to the channel's fragment size, and will be
* overwritten if a new fragment size is set. Users may alter this
* value directly with the @c SNDCTL_DSP_LOW_WATER ioctl.
*/
unsigned int lw;
/**
* If part of a sync group, this will point to the syncmember
* container.
*/
struct pcmchan_syncmember *sm;
#ifdef OSSV4_EXPERIMENT
u_int16_t lpeak, rpeak; /**< Peak value from 0-32767. */
#endif
SLIST_HEAD(, pcmchan_children) children;
};
@ -102,13 +160,22 @@ int chn_notify(struct pcm_channel *c, u_int32_t flags);
void chn_lock(struct pcm_channel *c);
void chn_unlock(struct pcm_channel *c);
int chn_getrates(struct pcm_channel *c, int **rates);
int chn_syncdestroy(struct pcm_channel *c);
#ifdef OSSV4_EXPERIMENT
int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak);
#endif
#ifdef USING_MUTEX
#define CHN_LOCK(c) mtx_lock((struct mtx *)((c)->lock))
#define CHN_UNLOCK(c) mtx_unlock((struct mtx *)((c)->lock))
#define CHN_TRYLOCK(c) mtx_trylock((struct mtx *)((c)->lock))
#define CHN_LOCKASSERT(c) mtx_assert((struct mtx *)((c)->lock), MA_OWNED)
#else
#define CHN_LOCK(c)
#define CHN_UNLOCK(c)
#define CHN_TRYLOCK(c)
#define CHN_LOCKASSERT(c)
#endif

View File

@ -70,6 +70,19 @@ CODE {
return 0;
}
static int
channel_nogetpeaks(kobj_t obj, void *data, int *lpeak, int *rpeak)
{
return -1;
}
static int
channel_nogetrates(kobj_t obj, void *data, int **rates)
{
*rates = NULL;
return 0;
}
};
METHOD void* init {
@ -140,3 +153,54 @@ METHOD int notify {
void *data;
u_int32_t changed;
} DEFAULT channel_nonotify;
/**
* @brief Retrieve channel peak values
*
* This function is intended to obtain peak volume values for samples
* played/recorded on a channel. Values are on a linear scale from 0 to
* 32767. If the channel is monaural, a single value should be recorded
* in @c lpeak.
*
* If hardware support isn't available, the SNDCTL_DSP_GET[IO]PEAKS
* operation should return EINVAL. However, we may opt to provide
* software support that the user may toggle via sysctl/mixext.
*
* @param obj standard kobj object (usually @c channel->methods)
* @param data driver-specific data (usually @c channel->devinfo)
* @param lpeak pointer to store left peak level
* @param rpeak pointer to store right peak level
*
* @retval -1 Error; usually operation isn't supported.
* @retval 0 success
*/
METHOD int getpeaks {
kobj_t obj;
void *data;
int *lpeak;
int *rpeak;
} DEFAULT channel_nogetpeaks;
/**
* @brief Retrieve discrete supported sample rates
*
* Some cards operate at fixed rates, and this call is intended to retrieve
* those rates primarily for when in-kernel rate adjustment is undesirable
* (e.g., application wants direct DMA access after setting a channel to run
* "uncooked").
*
* The parameter @c rates is a double pointer which will be reset to
* point to an array of supported sample rates. The number of elements
* in the array is returned to the caller.
*
* @param obj standard kobj object (usually @c channel->methods)
* @param data driver-specific data (usually @c channel->devinfo)
* @param rates rate array pointer
*
* @return Number of rates in the array
*/
METHOD int getrates {
kobj_t obj;
void *data;
int **rates;
} DEFAULT channel_nogetrates;

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
#ifndef _PCMDSP_H_
#define _PCMDSP_H_
/*-
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
* All rights reserved.
@ -27,3 +29,7 @@
*/
extern struct cdevsw dsp_cdevsw;
int dsp_oss_audioinfo(struct cdev *, oss_audioinfo *);
#endif /* !_PCMDSP_H_ */

View File

@ -49,6 +49,13 @@ struct snd_mixer {
u_int16_t level[32];
char name[MIXER_NAMELEN];
struct mtx *lock;
oss_mixer_enuminfo enuminfo;
/**
* Counter is incremented when applications change any of this
* mixer's controls. A change in value indicates that persistent
* mixer applications should update their displays.
*/
int modify_counter;
};
static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
@ -83,6 +90,11 @@ static struct cdevsw mixer_cdevsw = {
.d_name = "mixer",
};
/**
* Keeps a count of mixer devices; used only by OSSv4 SNDCTL_SYSINFO ioctl.
*/
int mixer_count = 0;
#ifdef USING_DEVFS
static eventhandler_tag mixer_ehtag;
#endif
@ -181,6 +193,83 @@ mixer_getrecsrc(struct snd_mixer *mixer)
return mixer->recsrc;
}
/**
* @brief Retrieve the route number of the current recording device
*
* OSSv4 assigns routing numbers to recording devices, unlike the previous
* API which relied on a fixed table of device numbers and names. This
* function returns the routing number of the device currently selected
* for recording.
*
* For now, this function is kind of a goofy compatibility stub atop the
* existing sound system. (For example, in theory, the old sound system
* allows multiple recording devices to be specified via a bitmask.)
*
* @param m mixer context container thing
*
* @retval 0 success
* @retval EIDRM no recording device found (generally not possible)
* @todo Ask about error code
*/
static int
mixer_get_recroute(struct snd_mixer *m, int *route)
{
int i, cnt;
cnt = 0;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
/** @todo can user set a multi-device mask? (== or &?) */
if ((1 << i) == m->recsrc)
break;
if ((1 << i) & m->recdevs)
++cnt;
}
if (i == SOUND_MIXER_NRDEVICES)
return EIDRM;
*route = cnt;
return 0;
}
/**
* @brief Select a device for recording
*
* This function sets a recording source based on a recording device's
* routing number. Said number is translated to an old school recdev
* mask and passed over mixer_setrecsrc.
*
* @param m mixer context container thing
*
* @retval 0 success(?)
* @retval EINVAL User specified an invalid device number
* @retval otherwise error from mixer_setrecsrc
*/
static int
mixer_set_recroute(struct snd_mixer *m, int route)
{
int i, cnt, ret;
ret = 0;
cnt = 0;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
if ((1 << i) & m->recdevs) {
if (route == cnt)
break;
++cnt;
}
}
if (i == SOUND_MIXER_NRDEVICES)
ret = EINVAL;
else
ret = mixer_setrecsrc(m, (1 << i));
return ret;
}
void
mix_setdevs(struct snd_mixer *m, u_int32_t v)
{
@ -190,9 +279,71 @@ mix_setdevs(struct snd_mixer *m, u_int32_t v)
m->devs = v;
}
/**
* @brief Record mask of available recording devices
*
* Calling functions are responsible for defining the mask of available
* recording devices. This function records that value in a structure
* used by the rest of the mixer code.
*
* This function also populates a structure used by the SNDCTL_DSP_*RECSRC*
* family of ioctls that are part of OSSV4. All recording device labels
* are concatenated in ascending order corresponding to their routing
* numbers. (Ex: a system might have 0 => 'vol', 1 => 'cd', 2 => 'line',
* etc.) For now, these labels are just the standard recording device
* names (cd, line1, etc.), but will eventually be fully dynamic and user
* controlled.
*
* @param m mixer device context container thing
* @param v mask of recording devices
*/
void
mix_setrecdevs(struct snd_mixer *m, u_int32_t v)
{
oss_mixer_enuminfo *ei;
char *loc;
int i, nvalues, nwrote, nleft, ncopied;
ei = &m->enuminfo;
nvalues = 0;
nwrote = 0;
nleft = sizeof(ei->strings);
loc = ei->strings;
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
if ((1 << i) & v) {
ei->strindex[nvalues] = nwrote;
ncopied = strlcpy(loc, snd_mixernames[i], nleft) + 1;
/* strlcpy retval doesn't include terminator */
nwrote += ncopied;
nleft -= ncopied;
nvalues++;
/*
* XXX I don't think this should ever be possible.
* Even with a move to dynamic device/channel names,
* each label is limited to ~16 characters, so that'd
* take a LOT to fill this buffer.
*/
if ((nleft <= 0) || (nvalues >= OSS_ENUM_MAXVALUE)) {
device_printf(m->dev,
"mix_setrecdevs: Not enough room to store device names--please file a bug report.\n");
device_printf(m->dev,
"mix_setrecdevs: Please include details about your sound hardware, OS version, etc.\n");
break;
}
loc = &ei->strings[nwrote];
}
}
/*
* NB: The SNDCTL_DSP_GET_RECSRC_NAMES ioctl ignores the dev
* and ctrl fields.
*/
ei->nvalues = nvalues;
m->recdevs = v;
}
@ -256,6 +407,8 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
snddev = device_get_softc(dev);
snddev->mixer_dev = pdev;
++mixer_count;
return 0;
bad:
@ -300,6 +453,8 @@ mixer_uninit(device_t dev)
d->mixer_dev = NULL;
--mixer_count;
return 0;
}
@ -481,6 +636,11 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread
return EBADF;
}
if (cmd == SNDCTL_MIXERINFO) {
snd_mtxunlock(m->lock);
return mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg);
}
if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
if (j == SOUND_MIXER_RECSRC)
ret = mixer_setrecsrc(m, *arg_i);
@ -513,8 +673,32 @@ mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread
snd_mtxunlock(m->lock);
return (v != -1)? 0 : ENXIO;
}
ret = 0;
switch (cmd) {
/** @todo Double check return values, error codes. */
case SNDCTL_SYSINFO:
sound_oss_sysinfo((oss_sysinfo *)arg);
break;
case SNDCTL_AUDIOINFO:
ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg);
break;
case SNDCTL_DSP_GET_RECSRC_NAMES:
bcopy((void *)&m->enuminfo, arg, sizeof(oss_mixer_enuminfo));
break;
case SNDCTL_DSP_GET_RECSRC:
ret = mixer_get_recroute(m, arg_i);
break;
case SNDCTL_DSP_SET_RECSRC:
ret = mixer_set_recroute(m, *arg_i);
break;
default:
ret = ENXIO;
}
snd_mtxunlock(m->lock);
return ENXIO;
return ret;
}
#ifdef USING_DEVFS
@ -552,4 +736,154 @@ SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
#endif
/**
* @brief Handler for SNDCTL_MIXERINFO
*
* This function searches for a mixer based on the numeric ID stored
* in oss_miserinfo::dev. If set to -1, then information about the
* current mixer handling the request is provided. Note, however, that
* this ioctl may be made with any sound device (audio, mixer, midi).
*
* @note Caller must not hold any PCM device, channel, or mixer locks.
*
* See http://manuals.opensound.com/developer/SNDCTL_MIXERINFO.html for
* more information.
*
* @param i_dev character device on which the ioctl arrived
* @param arg user argument (oss_mixerinfo *)
*
* @retval EINVAL oss_mixerinfo::dev specified a bad value
* @retval 0 success
*/
int
mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
{
struct snddev_info *d;
struct snd_mixer *m;
struct cdev *t_cdev;
int nmix, ret, pcmunit, i;
/*
* If probing the device handling the ioctl, make sure it's a mixer
* device. (This ioctl is valid on audio, mixer, and midi devices.)
*/
if ((mi->dev == -1) && (i_dev->si_devsw != &mixer_cdevsw))
return EINVAL;
m = NULL;
t_cdev = NULL;
nmix = 0;
ret = 0;
pcmunit = -1; /* pcmX */
/*
* There's a 1:1 relationship between mixers and PCM devices, so
* begin by iterating over PCM devices and search for our mixer.
*/
for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
d = devclass_get_softc(pcm_devclass, i);
if (d == NULL)
continue;
/* See the note in function docblock. */
mtx_assert(d->lock, MA_NOTOWNED);
pcm_inprog(d, 1);
pcm_lock(d);
if (d->mixer_dev != NULL) {
if (((mi->dev == -1) && (d->mixer_dev == i_dev)) || (mi->dev == nmix)) {
t_cdev = d->mixer_dev;
pcmunit = i;
break;
}
++nmix;
}
pcm_unlock(d);
pcm_inprog(d, -1);
}
/*
* If t_cdev is NULL, then search was exhausted and device wasn't
* found. No locks are held, so just return.
*/
if (t_cdev == NULL)
return EINVAL;
m = t_cdev->si_drv1;
mtx_lock(m->lock);
/*
* At this point, the following synchronization stuff has happened:
* - a specific PCM device is locked and its "in progress
* operations" counter has been incremented, so be sure to unlock
* and decrement when exiting;
* - a specific mixer device has been locked, so be sure to unlock
* when existing.
*/
bzero((void *)mi, sizeof(*mi));
mi->dev = nmix;
snprintf(mi->id, sizeof(mi->id), "mixer%d", dev2unit(t_cdev));
strlcpy(mi->name, m->name, sizeof(mi->name));
mi->modify_counter = m->modify_counter;
mi->card_number = pcmunit;
/*
* Currently, FreeBSD assumes 1:1 relationship between a pcm and
* mixer devices, so this is hardcoded to 0.
*/
mi->port_number = 0;
/**
* @todo Fill in @sa oss_mixerinfo::mixerhandle.
* @note From 4Front: "mixerhandle is an arbitrary string that
* identifies the mixer better than the device number
* (mixerinfo.dev). Device numbers may change depending on
* the order the drivers are loaded. However the handle
* should remain the same provided that the sound card is
* not moved to another PCI slot."
*/
/**
* @note
* @sa oss_mixerinfo::magic is a reserved field.
*
* @par
* From 4Front: "magic is usually 0. However some devices may have
* dedicated setup utilities and the magic field may contain an
* unique driver specific value (managed by [4Front])."
*/
mi->enabled = device_is_attached(m->dev) ? 1 : 0;
/**
* The only flag for @sa oss_mixerinfo::caps is currently
* MIXER_CAP_VIRTUAL, which I'm not sure we really worry about.
*/
/**
* Mixer extensions currently aren't supported, so leave
* @sa oss_mixerinfo::nrext blank for now.
*/
/**
* @todo Fill in @sa oss_mixerinfo::priority (requires touching
* drivers?)
* @note The priority field is for mixer applets to determine which
* mixer should be the default, with 0 being least preferred and 10
* being most preferred. From 4Front: "OSS drivers like ICH use
* higher values (10) because such chips are known to be used only
* on motherboards. Drivers for high end pro devices use 0 because
* they will never be the default mixer. Other devices use values 1
* to 9 depending on the estimated probability of being the default
* device.
*
* XXX Described by Hannu@4Front, but not found in soundcard.h.
strlcpy(mi->devnode, t_cdev->si_name, sizeof(mi->devnode));
mi->legacy_device = pcmunit;
*/
mtx_unlock(m->lock);
pcm_unlock(d);
pcm_inprog(d, -1);
return ret;
}

View File

@ -30,6 +30,7 @@ int mixer_init(device_t dev, kobj_class_t cls, void *devinfo);
int mixer_uninit(device_t dev);
int mixer_reinit(device_t dev);
int mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td);
int mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi);
int mixer_hwvol_init(device_t dev);
void mixer_hwvol_mute(device_t dev);
@ -41,10 +42,17 @@ u_int32_t mix_getdevs(struct snd_mixer *m);
u_int32_t mix_getrecdevs(struct snd_mixer *m);
void *mix_getdevinfo(struct snd_mixer *m);
extern int mixer_count;
/*
* this is a kludge to allow hiding of the struct snd_mixer definition
* 512 should be enough for all architectures
*/
#define MIXER_SIZE (512 + sizeof(struct kobj))
#ifdef OSSV4_EXPERIMENT
# define MIXER_SIZE (512 + sizeof(struct kobj) + \
sizeof(oss_mixer_enuminfo))
#else
# define MIXER_SIZE (512 + sizeof(struct kobj))
#endif
#define MIXER_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, MIXER_SIZE)

View File

@ -28,6 +28,7 @@
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/vchan.h>
#include <dev/sound/pcm/dsp.h>
#include <sys/limits.h>
#include <sys/sysctl.h>
#include "feeder_if.h"
@ -51,6 +52,11 @@ TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
/**
* @brief Unit number allocator for syncgroup IDs
*/
struct unrhdr *pcmsg_unrhdr = NULL;
static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
struct sysctl_ctx_list *
@ -1126,13 +1132,143 @@ sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
/************************************************************************/
/**
* @brief Handle OSSv4 SNDCTL_SYSINFO ioctl.
*
* @param si Pointer to oss_sysinfo struct where information about the
* sound subsystem will be written/copied.
*
* This routine returns information about the sound system, such as the
* current OSS version, number of audio, MIDI, and mixer drivers, etc.
* Also includes a bitmask showing which of the above types of devices
* are open (busy).
*
* @note
* Calling threads must not hold any snddev_info or pcm_channel locks.
*
* @author Ryan Beasley <ryanb@FreeBSD.org>
*/
void
sound_oss_sysinfo(oss_sysinfo *si)
{
static char si_product[] = "FreeBSD native OSS ABI";
static char si_version[] = __XSTRING(__FreeBSD_version);
static int intnbits = sizeof(int) * 8; /* Better suited as macro?
Must pester a C guru. */
struct snddev_channel *sce;
struct snddev_info *d;
struct pcm_channel *c;
int i, j, ncards;
ncards = 0;
strlcpy(si->product, si_product, sizeof(si->product));
strlcpy(si->version, si_version, sizeof(si->version));
si->versionnum = SOUND_VERSION;
/*
* Iterate over PCM devices and their channels, gathering up data
* for the numaudios, ncards, and openedaudio fields.
*/
si->numaudios = 0;
bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
if (pcm_devclass != NULL) {
j = 0;
for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
d = devclass_get_softc(pcm_devclass, i);
if (!d)
continue;
/* See note in function's docblock */
mtx_assert(d->lock, MA_NOTOWNED);
/* Increment device's "operations in progress" */
pcm_inprog(d, 1);
pcm_lock(d);
si->numaudios += d->devcount;
++ncards;
SLIST_FOREACH(sce, &d->channels, link) {
c = sce->channel;
mtx_assert(c->lock, MA_NOTOWNED);
CHN_LOCK(c);
if (c->flags & CHN_F_BUSY)
si->openedaudio[j / intnbits] |=
(1 << (j % intnbits));
CHN_UNLOCK(c);
j++;
}
pcm_unlock(d);
pcm_inprog(d, -1);
}
}
si->numsynths = 0; /* OSSv4 docs: this field is obsolete */
/**
* @todo Collect num{midis,timers}.
*
* Need access to sound/midi/midi.c::midistat_lock in order
* to safely touch midi_devices and get a head count of, well,
* MIDI devices. midistat_lock is a global static (i.e., local to
* midi.c), but midi_devices is a regular global; should the mutex
* be publicized, or is there another way to get this information?
*
* NB: MIDI/sequencer stuff is currently on hold.
*/
si->nummidis = 0;
si->numtimers = 0;
si->nummixers = mixer_count;
si->numcards = ncards;
/* OSSv4 docs: Intended only for test apps; API doesn't
really have much of a concept of cards. Shouldn't be
used by applications. */
/**
* @todo Fill in "busy devices" fields.
*
* si->openedmidi = " MIDI devices
*/
bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
/*
* Si->filler is a reserved array, but according to docs each
* element should be set to -1.
*/
for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++)
si->filler[i] = -1;
}
/************************************************************************/
static int
sound_modevent(module_t mod, int type, void *data)
{
int ret;
#if 0
return (midi_modevent(mod, type, data));
#else
return 0;
ret = 0;
switch(type) {
case MOD_LOAD:
pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
break;
case MOD_UNLOAD:
case MOD_SHUTDOWN:
if (pcmsg_unrhdr != NULL) {
delete_unrhdr(pcmsg_unrhdr);
pcmsg_unrhdr = NULL;
}
break;
default:
ret = EOPNOTSUPP;
}
return ret;
#endif
}

View File

@ -73,6 +73,7 @@
#if __FreeBSD_version > 500000
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#define USING_MUTEX
#define USING_DEVFS
@ -92,6 +93,7 @@ struct snd_mixer;
#include <dev/sound/pcm/channel.h>
#include <dev/sound/pcm/feeder.h>
#include <dev/sound/pcm/mixer.h>
#include <dev/sound/pcm/dsp.h>
#define PCM_SOFTC_SIZE 512
@ -193,6 +195,7 @@ int fkchan_kill(struct pcm_channel *c);
extern int pcm_veto_load;
extern int snd_unit;
extern devclass_t pcm_devclass;
extern struct unrhdr *pcmsg_unrhdr;
/*
* some macros for debugging purposes
@ -304,6 +307,8 @@ struct snddev_info {
};
void sound_oss_sysinfo(oss_sysinfo *);
#ifdef PCM_DEBUG_MTX
#define pcm_lock(d) mtx_lock(((struct snddev_info *)(d))->lock)
#define pcm_unlock(d) mtx_unlock(((struct snddev_info *)(d))->lock)

View File

@ -57,7 +57,7 @@
* is created, otherwise 1.
*/
#undef __FreeBSD_version
#define __FreeBSD_version 700022 /* Master, propagated to newvers */
#define __FreeBSD_version 700023 /* Master, propagated to newvers */
#ifndef LOCORE
#include <sys/types.h>

View File

@ -3,7 +3,7 @@
*/
/*-
* Copyright by Hannu Savolainen 1993
* Copyright by Hannu Savolainen 1993 / 4Front Technologies 1993-2006
* Modified for the new FreeBSD sound driver by Luigi Rizzo, 1997
*
* Redistribution and use in source and binary forms, with or without
@ -32,6 +32,12 @@
* $FreeBSD$
*/
/*
* Unless coordinating changes with 4Front Technologies, do NOT make any
* modifications to ioctl commands, types, etc. that would break
* compatibility with the OSS API.
*/
#ifndef _SYS_SOUNDCARD_H_
#define _SYS_SOUNDCARD_H_
/*
@ -719,8 +725,6 @@ struct sound_timer_info {
int caps;
};
#define MIDI_CAP_MPU401 1 /* MPU-401 intelligent mode */
struct midi_info {
char name[30];
int device; /* 0-N. INITIALIZE BEFORE CALLING */
@ -1437,4 +1441,433 @@ void seqbuf_dump(void); /* This function must be provided by programs */
#define SOUND_PCM_MAPINBUF SNDCTL_DSP_MAPINBUF
#define SOUND_PCM_MAPOUTBUF SNDCTL_DSP_MAPOUTBUF
/***********************************************************************/
/**
* XXX OSSv4 defines -- some bits taken straight out of the new
* sys/soundcard.h bundled with recent OSS releases.
*
* NB: These macros and structures will be reorganized and inserted
* in appropriate places throughout this file once the code begins
* to take shape.
*
* @todo reorganize layout more like the 4Front version
* @todo ask about maintaining __SIOWR vs. _IOWR ioctl cmd defines
*/
/**
* @note The @c OSSV4_EXPERIMENT macro is meant to wrap new development code
* in the sound system relevant to adopting 4Front's OSSv4 specification.
* Users should not enable this! Really!
*/
#if 0
# define OSSV4_EXPERIMENT 1
#else
# undef OSSV4_EXPERIMENT
#endif
#ifdef SOUND_VERSION
# undef SOUND_VERSION
# define SOUND_VERSION 0x040000
#endif /* !SOUND_VERSION */
#define OSS_LONGNAME_SIZE 64
#define OSS_LABEL_SIZE 16
#define OSS_DEVNODE_SIZE 32
typedef char oss_longname_t[OSS_LONGNAME_SIZE];
typedef char oss_label_t[OSS_LABEL_SIZE];
typedef char oss_devnode_t[OSS_DEVNODE_SIZE];
typedef struct audio_errinfo
{
int play_underruns;
int rec_overruns;
unsigned int play_ptradjust;
unsigned int rec_ptradjust;
int play_errorcount;
int rec_errorcount;
int play_lasterror;
int rec_lasterror;
long play_errorparm;
long rec_errorparm;
int filler[16];
} audio_errinfo;
#define SNDCTL_DSP_GETPLAYVOL _IOR ('P', 24, int)
#define SNDCTL_DSP_SETPLAYVOL _IOWR('P', 24, int)
#define SNDCTL_DSP_GETERROR _IOR ('P', 25, audio_errinfo)
/*
****************************************************************************
* Sync groups for audio devices
*/
typedef struct oss_syncgroup
{
int id;
int mode;
int filler[16];
} oss_syncgroup;
#define SNDCTL_DSP_SYNCGROUP _IOWR('P', 28, oss_syncgroup)
#define SNDCTL_DSP_SYNCSTART _IOW ('P', 29, int)
/*
**************************************************************************
* "cooked" mode enables software based conversions for sample rate, sample
* format (bits) and number of channels (mono/stereo). These conversions are
* required with some devices that support only one sample rate or just stereo
* to let the applications to use other formats. The cooked mode is enabled by
* default. However it's necessary to disable this mode when mmap() is used or
* when very deterministic timing is required. SNDCTL_DSP_COOKEDMODE is an
* optional call introduced in OSS 3.9.6f. It's _error return must be ignored_
* since normally this call will return erno=EINVAL.
*
* SNDCTL_DSP_COOKEDMODE must be called immediately after open before doing
* anything else. Otherwise the call will not have any effect.
*/
#define SNDCTL_DSP_COOKEDMODE _IOW ('P', 30, int)
/*
**************************************************************************
* SNDCTL_DSP_SILENCE and SNDCTL_DSP_SKIP are new calls in OSS 3.99.0
* that can be used to implement pause/continue during playback (no effect
* on recording).
*/
#define SNDCTL_DSP_SILENCE _IO ('P', 31)
#define SNDCTL_DSP_SKIP _IO ('P', 32)
/*
****************************************************************************
* Abort transfer (reset) functions for input and output
*/
#define SNDCTL_DSP_HALT_INPUT _IO ('P', 33)
#define SNDCTL_DSP_RESET_INPUT SNDCTL_DSP_HALT_INPUT /* Old name */
#define SNDCTL_DSP_HALT_OUTPUT _IO ('P', 34)
#define SNDCTL_DSP_RESET_OUTPUT SNDCTL_DSP_HALT_OUTPUT /* Old name */
/*
****************************************************************************
* Low water level control
*/
#define SNDCTL_DSP_LOW_WATER _IOW ('P', 34, int)
/** @todo Get rid of OSS_NO_LONG_LONG references? */
/*
****************************************************************************
* 64 bit pointer support. Only available in environments that support
* the 64 bit (long long) integer type.
*/
#ifndef OSS_NO_LONG_LONG
typedef struct
{
long long samples;
int fifo_samples;
int filler[32]; /* For future use */
} oss_count_t;
#define SNDCTL_DSP_CURRENT_IPTR _IOR ('P', 35, oss_count_t)
#define SNDCTL_DSP_CURRENT_OPTR _IOR ('P', 36, oss_count_t)
#endif
/*
****************************************************************************
* Interface for selecting recording sources and playback output routings.
*/
#define SNDCTL_DSP_GET_RECSRC_NAMES _IOR ('P', 37, oss_mixer_enuminfo)
#define SNDCTL_DSP_GET_RECSRC _IOR ('P', 38, int)
#define SNDCTL_DSP_SET_RECSRC _IOWR('P', 38, int)
#define SNDCTL_DSP_GET_PLAYTGT_NAMES _IOR ('P', 39, oss_mixer_enuminfo)
#define SNDCTL_DSP_GET_PLAYTGT _IOR ('P', 40, int)
#define SNDCTL_DSP_SET_PLAYTGT _IOWR('P', 40, int)
#define SNDCTL_DSP_GETRECVOL _IOR ('P', 41, int)
#define SNDCTL_DSP_SETRECVOL _IOWR('P', 41, int)
/*
***************************************************************************
* Some calls for setting the channel assignment with multi channel devices
* (see the manual for details). */
#define SNDCTL_DSP_GET_CHNORDER _IOR ('P', 42, unsigned long long)
#define SNDCTL_DSP_SET_CHNORDER _IOWR('P', 42, unsigned long long)
# define CHID_UNDEF 0
# define CHID_L 1 # define CHID_R 2
# define CHID_C 3
# define CHID_LFE 4
# define CHID_LS 5
# define CHID_RS 6
# define CHID_LR 7
# define CHID_RR 8
#define CHNORDER_UNDEF 0x0000000000000000ULL
#define CHNORDER_NORMAL 0x0000000087654321ULL
#define MAX_PEAK_CHANNELS 128
typedef unsigned short oss_peaks_t[MAX_PEAK_CHANNELS];
#define SNDCTL_DSP_GETIPEAKS _IOR('P', 43, oss_peaks_t)
#define SNDCTL_DSP_GETOPEAKS _IOR('P', 44, oss_peaks_t)
#define SNDCTL_DSP_POLICY _IOW('P', 45, int) /* See the manual */
/**
* @brief Argument for SNDCTL_SYSINFO ioctl.
*
* For use w/ the SNDCTL_SYSINFO ioctl available on audio (/dev/dsp*),
* mixer, and MIDI devices.
*/
typedef struct oss_sysinfo
{
char product[32]; /* For example OSS/Free, OSS/Linux or
OSS/Solaris */
char version[32]; /* For example 4.0a */
int versionnum; /* See OSS_GETVERSION */
char options[128]; /* Reserved */
int numaudios; /* # of audio/dsp devices */
int openedaudio[8]; /* Bit mask telling which audio devices
are busy */
int numsynths; /* # of availavle synth devices */
int nummidis; /* # of available MIDI ports */
int numtimers; /* # of available timer devices */
int nummixers; /* # of mixer devices */
int openedmidi[8]; /* Bit mask telling which midi devices
are busy */
int numcards; /* Number of sound cards in the system */
int filler[241]; /* For future expansion (set to -1) */
} oss_sysinfo;
typedef struct oss_mixext
{
int dev; /* Mixer device number */
int ctrl; /* Controller number */
int type; /* Entry type */
# define MIXT_DEVROOT 0 /* Device root entry */
# define MIXT_GROUP 1 /* Controller group */
# define MIXT_ONOFF 2 /* OFF (0) or ON (1) */
# define MIXT_ENUM 3 /* Enumerated (0 to maxvalue) */
# define MIXT_MONOSLIDER 4 /* Mono slider (0 to 100) */
# define MIXT_STEREOSLIDER 5 /* Stereo slider (dual 0 to 100) */
# define MIXT_MESSAGE 6 /* (Readable) textual message */
# define MIXT_MONOVU 7 /* VU meter value (mono) */
# define MIXT_STEREOVU 8 /* VU meter value (stereo) */
# define MIXT_MONOPEAK 9 /* VU meter peak value (mono) */
# define MIXT_STEREOPEAK 10 /* VU meter peak value (stereo) */
# define MIXT_RADIOGROUP 11 /* Radio button group */
# define MIXT_MARKER 12 /* Separator between normal and extension entries */
# define MIXT_VALUE 13 /* Decimal value entry */
# define MIXT_HEXVALUE 14 /* Hexadecimal value entry */
# define MIXT_MONODB 15 /* Mono atten. slider (0 to -144) */
# define MIXT_STEREODB 16 /* Stereo atten. slider (dual 0 to -144) */
# define MIXT_SLIDER 17 /* Slider (mono) with full integer range */
# define MIXT_3D 18
/* Possible value range (minvalue to maxvalue) */
/* Note that maxvalue may also be smaller than minvalue */
int maxvalue;
int minvalue;
int flags;
# define MIXF_READABLE 0x00000001 /* Has readable value */
# define MIXF_WRITEABLE 0x00000002 /* Has writeable value */
# define MIXF_POLL 0x00000004 /* May change itself */
# define MIXF_HZ 0x00000008 /* Herz scale */
# define MIXF_STRING 0x00000010 /* Use dynamic extensions for value */
# define MIXF_DYNAMIC 0x00000010 /* Supports dynamic extensions */
# define MIXF_OKFAIL 0x00000020 /* Interpret value as 1=OK, 0=FAIL */
# define MIXF_FLAT 0x00000040 /* Flat vertical space requirements */
# define MIXF_LEGACY 0x00000080 /* Legacy mixer control group */
char id[16]; /* Mnemonic ID (mainly for internal use) */
int parent; /* Entry# of parent (group) node (-1 if root) */
int dummy; /* Internal use */
int timestamp;
char data[64]; /* Misc data (entry type dependent) */
unsigned char enum_present[32]; /* Mask of allowed enum values */
int control_no; /* SOUND_MIXER_VOLUME..SOUND_MIXER_MIDI */
/* (-1 means not indicated) */
/*
* The desc field is reserved for internal purposes of OSS. It should not be
* used by applications.
*/
unsigned int desc;
#define MIXEXT_SCOPE_MASK 0x0000003f
#define MIXEXT_SCOPE_OTHER 0x00000000
#define MIXEXT_SCOPE_INPUT 0x00000001
#define MIXEXT_SCOPE_OUTPUT 0x00000002
#define MIXEXT_SCOPE_MONITOR 0x00000003
#define MIXEXT_SCOPE_RECSWITCH 0x00000004
char extname[32];
int update_counter;
int filler[7];
} oss_mixext;
typedef struct oss_mixext_root
{
char id[16];
char name[48];
} oss_mixext_root;
typedef struct oss_mixer_value
{
int dev;
int ctrl;
int value;
int flags; /* Reserved for future use. Initialize to 0 */
int timestamp; /* Must be set to oss_mixext.timestamp */
int filler[8]; /* Reserved for future use. Initialize to 0 */
} oss_mixer_value;
#define OSS_ENUM_MAXVALUE 255
typedef struct oss_mixer_enuminfo
{
int dev;
int ctrl;
int nvalues;
int version; /* Read the manual */
short strindex[OSS_ENUM_MAXVALUE];
char strings[3000];
} oss_mixer_enuminfo;
#define OPEN_READ PCM_ENABLE_INPUT
#define OPEN_WRITE PCM_ENABLE_OUTPUT
#define OPEN_READWRITE (OPEN_READ|OPEN_WRITE)
/**
* @brief Argument for SNDCTL_AUDIOINFO ioctl.
*
* For use w/ the SNDCTL_AUDIOINFO ioctl available on audio (/dev/dsp*)
* devices.
*/
typedef struct oss_audioinfo
{
int dev; /* Audio device number */
char name[64];
int busy; /* 0, OPEN_READ, OPEN_WRITE or OPEN_READWRITE */
int pid;
int caps; /* DSP_CAP_INPUT, DSP_CAP_OUTPUT */
int iformats;
int oformats;
int magic; /* Reserved for internal use */
char cmd[64]; /* Command using the device (if known) */
int card_number;
int port_number;
int mixer_dev;
int real_device; /* Obsolete field. Replaced by devnode */
int enabled; /* 1=enabled, 0=device not ready at this
moment */
int flags; /* For internal use only - no practical
meaning */
int min_rate; /* Sample rate limits */
int max_rate;
int min_channels; /* Number of channels supported */
int max_channels;
int binding; /* DSP_BIND_FRONT, etc. 0 means undefined */
int rate_source;
char handle[32];
#define MAX_SAMPLE_RATES 20 /* Cannot be changed */
unsigned int nrates;
unsigned int rates[MAX_SAMPLE_RATES]; /* Please read the manual before using these */
oss_longname_t song_name; /* Song name (if given) */
oss_label_t label; /* Device label (if given) */
int latency; /* In usecs, -1=unknown */
oss_devnode_t devnode; /* Device special file name (inside
/dev) */
int filler[186];
} oss_audioinfo;
typedef struct oss_mixerinfo
{
int dev;
char id[16];
char name[32];
int modify_counter;
int card_number;
int port_number;
char handle[32];
int magic; /* Reserved */
int enabled; /* Reserved */
int caps;
#define MIXER_CAP_VIRTUAL 0x00000001
int flags; /* Reserved */
int nrext;
/*
* The priority field can be used to select the default (motherboard)
* mixer device. The mixer with the highest priority is the
* most preferred one. -2 or less means that this device cannot be used
* as the default mixer.
*/
int priority;
int filler[254]; /* Reserved */
} oss_mixerinfo;
typedef struct oss_midi_info
{
int dev; /* Midi device number */
char name[64];
int busy; /* 0, OPEN_READ, OPEN_WRITE or OPEN_READWRITE */
int pid;
char cmd[64]; /* Command using the device (if known) */
int caps;
#define MIDI_CAP_MPU401 0x00000001 /**** OBSOLETE ****/
#define MIDI_CAP_INPUT 0x00000002
#define MIDI_CAP_OUTPUT 0x00000004
#define MIDI_CAP_INOUT (MIDI_CAP_INPUT|MIDI_CAP_OUTPUT)
#define MIDI_CAP_VIRTUAL 0x00000008 /* Pseudo device */
#define MIDI_CAP_MTCINPUT 0x00000010 /* Supports SNDCTL_MIDI_MTCINPUT */
#define MIDI_CAP_CLIENT 0x00000020 /* Virtual client side device */
#define MIDI_CAP_SERVER 0x00000040 /* Virtual server side device */
#define MIDI_CAP_INTERNAL 0x00000080 /* Internal (synth) device */
#define MIDI_CAP_EXTERNAL 0x00000100 /* external (MIDI port) device */
#define MIDI_CAP_PTOP 0x00000200 /* Point to point link to one device */
#define MIDI_CAP_MTC 0x00000400 /* MTC/SMPTE (control) device */
int magic; /* Reserved for internal use */
int card_number;
int port_number;
int enabled; /* 1=enabled, 0=device not ready at this moment */
int flags; /* For internal use only - no practical meaning */
char handle[32];
oss_longname_t song_name; /* Song name (if known) */
oss_label_t label; /* Device label (if given) */
int latency; /* In usecs, -1=unknown */
int filler[244];
} oss_midi_info;
typedef struct oss_card_info
{
int card;
char shortname[16];
char longname[128];
int flags;
int filler[256];
} oss_card_info;
#define SNDCTL_SYSINFO _IOR ('X', 1, oss_sysinfo)
#define OSS_SYSINFO SNDCTL_SYSINFO /* Old name */
#define SNDCTL_MIX_NRMIX _IOR ('X', 2, int)
#define SNDCTL_MIX_NREXT _IOWR('X', 3, int)
#define SNDCTL_MIX_EXTINFO _IOWR('X', 4, oss_mixext)
#define SNDCTL_MIX_READ _IOWR('X', 5, oss_mixer_value)
#define SNDCTL_MIX_WRITE _IOWR('X', 6, oss_mixer_value)
#define SNDCTL_AUDIOINFO _IOWR('X', 7, oss_audioinfo)
#define SNDCTL_MIX_ENUMINFO _IOWR('X', 8, oss_mixer_enuminfo)
#define SNDCTL_MIDIINFO _IOWR('X', 9, oss_midi_info)
#define SNDCTL_MIXERINFO _IOWR('X',10, oss_mixerinfo)
#define SNDCTL_CARDINFO _IOWR('X',11, oss_card_info)
/*
* Few more "globally" available ioctl calls.
*/
#define SNDCTL_SETSONG _IOW ('Y', 2, oss_longname_t)
#define SNDCTL_GETSONG _IOR ('Y', 2, oss_longname_t)
#define SNDCTL_SETNAME _IOW ('Y', 3, oss_longname_t)
#define SNDCTL_SETLABEL _IOW ('Y', 4, oss_label_t)
#define SNDCTL_GETLABEL _IOR ('Y', 4, oss_label_t)
#endif /* !_SYS_SOUNDCARD_H_ */