2005-01-06 01:43:34 +00:00
|
|
|
/*-
|
2003-09-07 16:28:03 +00:00
|
|
|
* Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
|
1999-09-01 04:08:39 +00:00
|
|
|
* (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
1999-11-20 16:50:33 +00:00
|
|
|
#include <dev/sound/pcm/sound.h>
|
2001-05-27 17:22:00 +00:00
|
|
|
#include <dev/sound/pcm/vchan.h>
|
2004-01-20 03:58:57 +00:00
|
|
|
#include <dev/sound/pcm/dsp.h>
|
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.
2006-09-23 20:45:47 +00:00
|
|
|
#include <sys/limits.h>
|
2000-08-29 16:21:33 +00:00
|
|
|
#include <sys/sysctl.h>
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
#include "feeder_if.h"
|
|
|
|
|
|
|
|
SND_DECLARE_FILE("$FreeBSD$");
|
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
devclass_t pcm_devclass;
|
2001-02-27 07:01:49 +00:00
|
|
|
|
2002-01-26 22:13:24 +00:00
|
|
|
int pcm_veto_load = 1;
|
|
|
|
|
2001-02-27 07:01:49 +00:00
|
|
|
#ifdef USING_DEVFS
|
2001-06-16 21:25:10 +00:00
|
|
|
int snd_unit = 0;
|
2006-06-18 14:14:41 +00:00
|
|
|
TUNABLE_INT("hw.snd.default_unit", &snd_unit);
|
2001-02-27 07:01:49 +00:00
|
|
|
#endif
|
2001-07-03 12:53:09 +00:00
|
|
|
|
2006-06-18 14:14:41 +00:00
|
|
|
int snd_maxautovchans = 4;
|
|
|
|
/* XXX: a tunable implies that we may need more than one sound channel before
|
|
|
|
the system can change a sysctl (/etc/sysctl.conf), do we really need
|
|
|
|
this? */
|
2001-08-23 11:30:52 +00:00
|
|
|
TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
|
2001-02-27 07:01:49 +00:00
|
|
|
|
|
|
|
SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
|
1999-09-01 04:08:39 +00:00
|
|
|
|
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.
2006-09-23 20:45:47 +00:00
|
|
|
/**
|
|
|
|
* @brief Unit number allocator for syncgroup IDs
|
|
|
|
*/
|
|
|
|
struct unrhdr *pcmsg_unrhdr = NULL;
|
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
|
|
|
|
|
|
|
|
struct sysctl_ctx_list *
|
|
|
|
snd_sysctl_tree(device_t dev)
|
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_info *d = device_get_softc(dev);
|
2001-08-23 11:30:52 +00:00
|
|
|
|
|
|
|
return &d->sysctl_tree;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sysctl_oid *
|
|
|
|
snd_sysctl_tree_top(device_t dev)
|
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_info *d = device_get_softc(dev);
|
2001-08-23 11:30:52 +00:00
|
|
|
|
|
|
|
return d->sysctl_tree_top;
|
|
|
|
}
|
|
|
|
|
2001-02-27 07:45:09 +00:00
|
|
|
void *
|
2002-04-04 20:54:27 +00:00
|
|
|
snd_mtxcreate(const char *desc, const char *type)
|
2001-02-27 07:45:09 +00:00
|
|
|
{
|
|
|
|
#ifdef USING_MUTEX
|
|
|
|
struct mtx *m;
|
|
|
|
|
2003-02-19 05:47:46 +00:00
|
|
|
m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
|
2001-02-27 07:45:09 +00:00
|
|
|
if (m == NULL)
|
|
|
|
return NULL;
|
2004-01-28 08:02:15 +00:00
|
|
|
mtx_init(m, desc, type, MTX_DEF);
|
|
|
|
return m;
|
|
|
|
#else
|
|
|
|
return (void *)0xcafebabe;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2001-02-27 07:45:09 +00:00
|
|
|
void
|
|
|
|
snd_mtxfree(void *m)
|
|
|
|
{
|
|
|
|
#ifdef USING_MUTEX
|
|
|
|
struct mtx *mtx = m;
|
|
|
|
|
2002-11-25 17:17:43 +00:00
|
|
|
/* mtx_assert(mtx, MA_OWNED); */
|
2001-02-27 07:45:09 +00:00
|
|
|
mtx_destroy(mtx);
|
|
|
|
free(mtx, M_DEVBUF);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
snd_mtxassert(void *m)
|
|
|
|
{
|
|
|
|
#ifdef USING_MUTEX
|
2001-06-23 17:36:51 +00:00
|
|
|
#ifdef INVARIANTS
|
2001-02-27 07:45:09 +00:00
|
|
|
struct mtx *mtx = m;
|
|
|
|
|
|
|
|
mtx_assert(mtx, MA_OWNED);
|
|
|
|
#endif
|
2001-06-23 17:36:51 +00:00
|
|
|
#endif
|
2001-02-27 07:45:09 +00:00
|
|
|
}
|
2002-11-25 17:17:43 +00:00
|
|
|
/*
|
2001-02-27 07:45:09 +00:00
|
|
|
void
|
|
|
|
snd_mtxlock(void *m)
|
|
|
|
{
|
|
|
|
#ifdef USING_MUTEX
|
|
|
|
struct mtx *mtx = m;
|
|
|
|
|
|
|
|
mtx_lock(mtx);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
snd_mtxunlock(void *m)
|
|
|
|
{
|
|
|
|
#ifdef USING_MUTEX
|
|
|
|
struct mtx *mtx = m;
|
|
|
|
|
|
|
|
mtx_unlock(mtx);
|
|
|
|
#endif
|
|
|
|
}
|
2002-11-25 17:17:43 +00:00
|
|
|
*/
|
2001-02-27 07:45:09 +00:00
|
|
|
int
|
|
|
|
snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
|
|
|
|
{
|
|
|
|
#ifdef USING_MUTEX
|
|
|
|
flags &= INTR_MPSAFE;
|
2001-06-16 22:59:46 +00:00
|
|
|
flags |= INTR_TYPE_AV;
|
2001-02-27 07:45:09 +00:00
|
|
|
#else
|
2001-06-16 22:59:46 +00:00
|
|
|
flags = INTR_TYPE_AV;
|
2001-02-27 07:45:09 +00:00
|
|
|
#endif
|
|
|
|
return bus_setup_intr(dev, res, flags, hand, param, cookiep);
|
|
|
|
}
|
|
|
|
|
2003-08-18 23:20:39 +00:00
|
|
|
#ifndef PCM_DEBUG_MTX
|
2001-08-23 11:30:52 +00:00
|
|
|
void
|
|
|
|
pcm_lock(struct snddev_info *d)
|
|
|
|
{
|
|
|
|
snd_mtxlock(d->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pcm_unlock(struct snddev_info *d)
|
|
|
|
{
|
|
|
|
snd_mtxunlock(d->lock);
|
|
|
|
}
|
2003-08-18 23:20:39 +00:00
|
|
|
#endif
|
2001-08-23 11:30:52 +00:00
|
|
|
|
|
|
|
struct pcm_channel *
|
|
|
|
pcm_getfakechan(struct snddev_info *d)
|
|
|
|
{
|
|
|
|
return d->fakechan;
|
|
|
|
}
|
|
|
|
|
2006-03-31 10:36:36 +00:00
|
|
|
static int
|
|
|
|
pcm_setvchans(struct snddev_info *d, int newcnt)
|
|
|
|
{
|
|
|
|
struct snddev_channel *sce = NULL;
|
|
|
|
struct pcm_channel *c = NULL;
|
|
|
|
int err = 0, vcnt, dcnt, i;
|
|
|
|
|
|
|
|
pcm_inprog(d, 1);
|
|
|
|
|
|
|
|
if (!(d->flags & SD_F_AUTOVCHAN)) {
|
|
|
|
err = EINVAL;
|
|
|
|
goto setvchans_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
vcnt = d->vchancount;
|
|
|
|
dcnt = d->playcount + d->reccount;
|
|
|
|
|
|
|
|
if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) {
|
|
|
|
err = E2BIG;
|
|
|
|
goto setvchans_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
dcnt += vcnt;
|
|
|
|
|
|
|
|
if (newcnt > vcnt) {
|
|
|
|
/* add new vchans - find a parent channel first */
|
|
|
|
SLIST_FOREACH(sce, &d->channels, link) {
|
|
|
|
c = sce->channel;
|
|
|
|
CHN_LOCK(c);
|
|
|
|
if (c->direction == PCMDIR_PLAY &&
|
|
|
|
((c->flags & CHN_F_HAS_VCHAN) ||
|
|
|
|
(vcnt == 0 &&
|
|
|
|
!(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)))))
|
|
|
|
goto addok;
|
|
|
|
CHN_UNLOCK(c);
|
|
|
|
}
|
|
|
|
err = EBUSY;
|
|
|
|
goto setvchans_out;
|
|
|
|
addok:
|
|
|
|
c->flags |= CHN_F_BUSY;
|
|
|
|
while (err == 0 && newcnt > vcnt) {
|
|
|
|
if (dcnt > PCMMAXCHAN) {
|
|
|
|
device_printf(d->dev, "%s: Maximum channel reached.\n", __func__);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
err = vchan_create(c);
|
|
|
|
if (err == 0) {
|
|
|
|
vcnt++;
|
|
|
|
dcnt++;
|
|
|
|
} else if (err == E2BIG && newcnt > vcnt)
|
|
|
|
device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err);
|
|
|
|
}
|
|
|
|
if (vcnt == 0)
|
|
|
|
c->flags &= ~CHN_F_BUSY;
|
|
|
|
CHN_UNLOCK(c);
|
|
|
|
} else if (newcnt < vcnt) {
|
|
|
|
#define ORPHAN_CDEVT(cdevt) \
|
|
|
|
((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \
|
|
|
|
(cdevt)->si_drv2 == NULL))
|
|
|
|
while (err == 0 && newcnt < vcnt) {
|
|
|
|
i = 0;
|
|
|
|
SLIST_FOREACH(sce, &d->channels, link) {
|
|
|
|
c = sce->channel;
|
|
|
|
CHN_LOCK(c);
|
|
|
|
if (c->direction == PCMDIR_PLAY &&
|
|
|
|
(c->flags & CHN_F_VIRTUAL) &&
|
|
|
|
(i++ == newcnt)) {
|
|
|
|
if (!(c->flags & CHN_F_BUSY) &&
|
|
|
|
ORPHAN_CDEVT(sce->dsp_devt) &&
|
|
|
|
ORPHAN_CDEVT(sce->dspW_devt) &&
|
|
|
|
ORPHAN_CDEVT(sce->audio_devt) &&
|
|
|
|
ORPHAN_CDEVT(sce->dspr_devt))
|
|
|
|
goto remok;
|
|
|
|
/*
|
|
|
|
* Either we're busy, or our cdev
|
|
|
|
* has been stolen by dsp_clone().
|
|
|
|
* Skip, and increase newcnt.
|
|
|
|
*/
|
|
|
|
if (!(c->flags & CHN_F_BUSY))
|
|
|
|
device_printf(d->dev,
|
|
|
|
"%s: <%s> somebody steal my cdev!\n",
|
|
|
|
__func__, c->name);
|
|
|
|
newcnt++;
|
|
|
|
}
|
|
|
|
CHN_UNLOCK(c);
|
|
|
|
}
|
|
|
|
if (vcnt != newcnt)
|
|
|
|
err = EBUSY;
|
|
|
|
break;
|
|
|
|
remok:
|
|
|
|
CHN_UNLOCK(c);
|
|
|
|
err = vchan_destroy(c);
|
|
|
|
if (err == 0)
|
|
|
|
vcnt--;
|
|
|
|
else
|
|
|
|
device_printf(d->dev,
|
|
|
|
"%s: WARNING: vchan_destroy() failed!",
|
|
|
|
__func__);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setvchans_out:
|
|
|
|
pcm_inprog(d, -1);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2006-03-21 06:35:48 +00:00
|
|
|
/* return error status and a locked channel */
|
|
|
|
int
|
|
|
|
pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
|
|
|
|
pid_t pid, int chnum)
|
2001-05-27 17:22:00 +00:00
|
|
|
{
|
|
|
|
struct pcm_channel *c;
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_channel *sce;
|
|
|
|
int err;
|
2001-05-27 17:22:00 +00:00
|
|
|
|
2006-03-21 06:35:48 +00:00
|
|
|
retry_chnalloc:
|
2006-03-31 10:36:36 +00:00
|
|
|
err = ENODEV;
|
2001-06-27 19:59:45 +00:00
|
|
|
/* scan for a free channel */
|
2001-05-27 17:22:00 +00:00
|
|
|
SLIST_FOREACH(sce, &d->channels, link) {
|
|
|
|
c = sce->channel;
|
2001-06-07 20:06:22 +00:00
|
|
|
CHN_LOCK(c);
|
2006-03-31 10:36:36 +00:00
|
|
|
if (c->direction == direction && !(c->flags & CHN_F_BUSY)) {
|
2006-03-21 06:35:48 +00:00
|
|
|
if (chnum < 0 || sce->chan_num == chnum) {
|
2001-09-05 16:28:41 +00:00
|
|
|
c->flags |= CHN_F_BUSY;
|
|
|
|
c->pid = pid;
|
2006-03-21 06:35:48 +00:00
|
|
|
*ch = c;
|
|
|
|
return 0;
|
2001-09-05 16:28:41 +00:00
|
|
|
}
|
2001-05-27 17:22:00 +00:00
|
|
|
}
|
2006-03-21 06:35:48 +00:00
|
|
|
if (sce->chan_num == chnum) {
|
|
|
|
if (c->direction != direction)
|
|
|
|
err = EOPNOTSUPP;
|
|
|
|
else if (c->flags & CHN_F_BUSY)
|
|
|
|
err = EBUSY;
|
|
|
|
else
|
|
|
|
err = EINVAL;
|
|
|
|
CHN_UNLOCK(c);
|
|
|
|
return err;
|
|
|
|
} else if (c->direction == direction && (c->flags & CHN_F_BUSY))
|
2006-03-31 10:36:36 +00:00
|
|
|
err = EBUSY;
|
2001-06-07 20:06:22 +00:00
|
|
|
CHN_UNLOCK(c);
|
2001-05-27 17:22:00 +00:00
|
|
|
}
|
2001-06-27 19:59:45 +00:00
|
|
|
|
|
|
|
/* no channel available */
|
2006-03-21 06:35:48 +00:00
|
|
|
if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 &&
|
2006-03-16 04:12:49 +00:00
|
|
|
d->vchancount < snd_maxautovchans &&
|
|
|
|
d->devcount <= PCMMAXCHAN) {
|
2006-03-31 10:36:36 +00:00
|
|
|
err = pcm_setvchans(d, d->vchancount + 1);
|
|
|
|
if (err == 0) {
|
|
|
|
chnum = -2;
|
|
|
|
goto retry_chnalloc;
|
2001-06-27 19:59:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-31 10:36:36 +00:00
|
|
|
return err;
|
2001-05-27 17:22:00 +00:00
|
|
|
}
|
|
|
|
|
2001-06-14 13:31:30 +00:00
|
|
|
/* release a locked channel and unlock it */
|
2001-05-27 17:22:00 +00:00
|
|
|
int
|
2001-06-14 13:31:30 +00:00
|
|
|
pcm_chnrelease(struct pcm_channel *c)
|
2001-05-27 17:22:00 +00:00
|
|
|
{
|
2001-06-14 13:31:30 +00:00
|
|
|
CHN_LOCKASSERT(c);
|
2001-05-27 17:22:00 +00:00
|
|
|
c->flags &= ~CHN_F_BUSY;
|
2001-06-14 13:31:30 +00:00
|
|
|
c->pid = -1;
|
2001-06-07 20:06:22 +00:00
|
|
|
CHN_UNLOCK(c);
|
2001-05-27 17:22:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
pcm_chnref(struct pcm_channel *c, int ref)
|
|
|
|
{
|
2001-06-07 20:06:22 +00:00
|
|
|
int r;
|
|
|
|
|
2001-06-14 13:31:30 +00:00
|
|
|
CHN_LOCKASSERT(c);
|
2001-05-27 17:22:00 +00:00
|
|
|
c->refcount += ref;
|
2001-06-07 20:06:22 +00:00
|
|
|
r = c->refcount;
|
|
|
|
return r;
|
2001-05-27 17:22:00 +00:00
|
|
|
}
|
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
int
|
|
|
|
pcm_inprog(struct snddev_info *d, int delta)
|
|
|
|
{
|
2003-08-18 23:20:39 +00:00
|
|
|
int r;
|
|
|
|
|
|
|
|
if (delta == 0)
|
|
|
|
return d->inprog;
|
|
|
|
|
|
|
|
/* backtrace(); */
|
|
|
|
pcm_lock(d);
|
2001-08-23 11:30:52 +00:00
|
|
|
d->inprog += delta;
|
2003-08-18 23:20:39 +00:00
|
|
|
r = d->inprog;
|
|
|
|
pcm_unlock(d);
|
|
|
|
return r;
|
2001-08-23 11:30:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pcm_setmaxautovchans(struct snddev_info *d, int num)
|
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
if (num > 0 && d->vchancount == 0)
|
|
|
|
pcm_setvchans(d, 1);
|
|
|
|
else if (num == 0 && d->vchancount > 0)
|
|
|
|
pcm_setvchans(d, 0);
|
2001-08-23 11:30:52 +00:00
|
|
|
}
|
|
|
|
|
2001-06-14 13:31:30 +00:00
|
|
|
#ifdef USING_DEVFS
|
2000-09-01 20:09:24 +00:00
|
|
|
static int
|
2006-06-18 14:14:41 +00:00
|
|
|
sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
|
2000-09-01 20:09:24 +00:00
|
|
|
{
|
2001-06-14 13:31:30 +00:00
|
|
|
struct snddev_info *d;
|
2000-09-01 20:09:24 +00:00
|
|
|
int error, unit;
|
|
|
|
|
|
|
|
unit = snd_unit;
|
|
|
|
error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
|
|
|
|
if (error == 0 && req->newptr != NULL) {
|
2001-06-18 00:10:47 +00:00
|
|
|
if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
|
2001-06-14 13:31:30 +00:00
|
|
|
return EINVAL;
|
|
|
|
d = devclass_get_softc(pcm_devclass, unit);
|
2001-06-17 23:23:06 +00:00
|
|
|
if (d == NULL || SLIST_EMPTY(&d->channels))
|
2001-06-14 13:31:30 +00:00
|
|
|
return EINVAL;
|
2000-09-01 20:09:24 +00:00
|
|
|
snd_unit = unit;
|
|
|
|
}
|
|
|
|
return (error);
|
|
|
|
}
|
2006-06-18 14:14:41 +00:00
|
|
|
/* XXX: do we need a way to let the user change the default unit? */
|
|
|
|
SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW,
|
|
|
|
0, sizeof(int), sysctl_hw_snd_default_unit, "I", "");
|
2001-02-27 07:01:49 +00:00
|
|
|
#endif
|
2000-09-01 20:09:24 +00:00
|
|
|
|
2001-06-26 21:54:55 +00:00
|
|
|
static int
|
2001-08-23 11:30:52 +00:00
|
|
|
sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
|
2001-06-26 21:54:55 +00:00
|
|
|
{
|
2001-08-23 11:30:52 +00:00
|
|
|
struct snddev_info *d;
|
|
|
|
int i, v, error;
|
2001-06-27 19:59:45 +00:00
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
v = snd_maxautovchans;
|
2001-06-27 19:59:45 +00:00
|
|
|
error = sysctl_handle_int(oidp, &v, sizeof(v), req);
|
|
|
|
if (error == 0 && req->newptr != NULL) {
|
2006-03-31 10:36:36 +00:00
|
|
|
if (v < 0 || v > PCMMAXCHAN)
|
|
|
|
return E2BIG;
|
2006-03-21 06:35:48 +00:00
|
|
|
if (pcm_devclass != NULL && v != snd_maxautovchans) {
|
2001-08-23 11:30:52 +00:00
|
|
|
for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
|
|
|
|
d = devclass_get_softc(pcm_devclass, i);
|
|
|
|
if (!d)
|
|
|
|
continue;
|
2006-03-31 10:36:36 +00:00
|
|
|
pcm_setmaxautovchans(d, v);
|
2001-08-23 11:30:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
snd_maxautovchans = v;
|
2001-06-27 19:59:45 +00:00
|
|
|
}
|
|
|
|
return (error);
|
|
|
|
}
|
2001-08-23 11:30:52 +00:00
|
|
|
SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
|
|
|
|
0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
|
2001-06-27 19:59:45 +00:00
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
struct pcm_channel *
|
|
|
|
pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
2006-03-21 06:35:48 +00:00
|
|
|
struct snddev_channel *sce;
|
2006-03-31 10:36:36 +00:00
|
|
|
struct pcm_channel *ch, *c;
|
2000-09-01 20:09:24 +00:00
|
|
|
char *dirs;
|
2006-03-31 10:36:36 +00:00
|
|
|
uint32_t flsearch = 0;
|
2006-03-21 06:35:48 +00:00
|
|
|
int direction, err, rpnum, *pnum;
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
switch(dir) {
|
|
|
|
case PCMDIR_PLAY:
|
|
|
|
dirs = "play";
|
2004-02-28 19:47:02 +00:00
|
|
|
direction = PCMDIR_PLAY;
|
2001-09-18 14:50:52 +00:00
|
|
|
pnum = &d->playcount;
|
2001-05-27 17:22:00 +00:00
|
|
|
break;
|
2001-09-18 14:50:52 +00:00
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
case PCMDIR_REC:
|
|
|
|
dirs = "record";
|
2004-02-28 19:47:02 +00:00
|
|
|
direction = PCMDIR_REC;
|
2001-09-18 14:50:52 +00:00
|
|
|
pnum = &d->reccount;
|
2001-05-27 17:22:00 +00:00
|
|
|
break;
|
2001-09-18 14:50:52 +00:00
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
case PCMDIR_VIRTUAL:
|
|
|
|
dirs = "virtual";
|
2004-02-28 19:47:02 +00:00
|
|
|
direction = PCMDIR_PLAY;
|
2001-09-18 14:50:52 +00:00
|
|
|
pnum = &d->vchancount;
|
2006-03-21 06:35:48 +00:00
|
|
|
flsearch = CHN_F_VIRTUAL;
|
2001-05-27 17:22:00 +00:00
|
|
|
break;
|
2001-09-18 14:50:52 +00:00
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
default:
|
|
|
|
return NULL;
|
2000-06-06 22:24:53 +00:00
|
|
|
}
|
2001-05-27 17:22:00 +00:00
|
|
|
|
2003-02-19 05:47:46 +00:00
|
|
|
ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
|
2001-05-27 17:22:00 +00:00
|
|
|
if (!ch)
|
|
|
|
return NULL;
|
|
|
|
|
2003-02-19 05:47:46 +00:00
|
|
|
ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
|
2001-05-27 17:22:00 +00:00
|
|
|
if (!ch->methods) {
|
|
|
|
free(ch, M_DEVBUF);
|
2001-09-18 14:50:52 +00:00
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-11-25 17:17:43 +00:00
|
|
|
snd_mtxlock(d->lock);
|
2006-03-21 06:35:48 +00:00
|
|
|
ch->num = 0;
|
|
|
|
rpnum = 0;
|
|
|
|
SLIST_FOREACH(sce, &d->channels, link) {
|
2006-03-31 10:36:36 +00:00
|
|
|
c = sce->channel;
|
|
|
|
if (direction != c->direction ||
|
|
|
|
(c->flags & CHN_F_VIRTUAL) != flsearch)
|
2006-03-21 06:35:48 +00:00
|
|
|
continue;
|
2006-03-31 10:36:36 +00:00
|
|
|
if (ch->num == c->num)
|
2006-03-21 06:35:48 +00:00
|
|
|
ch->num++;
|
|
|
|
else {
|
2006-03-31 10:36:36 +00:00
|
|
|
#if 0
|
2006-03-21 06:35:48 +00:00
|
|
|
device_printf(d->dev,
|
2006-03-31 10:36:36 +00:00
|
|
|
"%s: %s channel numbering screwed (Expect: %d, Got: %d)\n",
|
|
|
|
__func__, dirs, ch->num, c->num);
|
|
|
|
#endif
|
2006-03-21 06:35:48 +00:00
|
|
|
goto retry_num_search;
|
|
|
|
}
|
|
|
|
rpnum++;
|
|
|
|
}
|
|
|
|
goto retry_num_search_out;
|
|
|
|
retry_num_search:
|
|
|
|
rpnum = 0;
|
|
|
|
SLIST_FOREACH(sce, &d->channels, link) {
|
2006-03-31 10:36:36 +00:00
|
|
|
c = sce->channel;
|
|
|
|
if (direction != c->direction ||
|
|
|
|
(c->flags & CHN_F_VIRTUAL) != flsearch)
|
2006-03-21 06:35:48 +00:00
|
|
|
continue;
|
2006-03-31 10:36:36 +00:00
|
|
|
if (ch->num == c->num) {
|
2006-03-21 06:35:48 +00:00
|
|
|
ch->num++;
|
|
|
|
goto retry_num_search;
|
|
|
|
}
|
|
|
|
rpnum++;
|
|
|
|
}
|
|
|
|
retry_num_search_out:
|
|
|
|
if (*pnum != rpnum) {
|
|
|
|
device_printf(d->dev,
|
2006-03-31 10:36:36 +00:00
|
|
|
"%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
|
2006-03-21 06:35:48 +00:00
|
|
|
__func__, dirs, *pnum, rpnum);
|
|
|
|
*pnum = rpnum;
|
|
|
|
}
|
|
|
|
(*pnum)++;
|
2002-11-25 17:17:43 +00:00
|
|
|
snd_mtxunlock(d->lock);
|
2001-09-18 14:50:52 +00:00
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
ch->pid = -1;
|
|
|
|
ch->parentsnddev = d;
|
|
|
|
ch->parentchannel = parent;
|
2002-01-25 04:14:12 +00:00
|
|
|
ch->dev = d->dev;
|
2006-03-21 06:35:48 +00:00
|
|
|
snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
|
2001-05-27 17:22:00 +00:00
|
|
|
|
2004-02-28 19:47:02 +00:00
|
|
|
err = chn_init(ch, devinfo, dir, direction);
|
2000-12-18 01:36:41 +00:00
|
|
|
if (err) {
|
2001-09-18 14:50:52 +00:00
|
|
|
device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
|
2001-05-27 17:22:00 +00:00
|
|
|
kobj_delete(ch->methods, M_DEVBUF);
|
|
|
|
free(ch, M_DEVBUF);
|
2002-11-25 17:17:43 +00:00
|
|
|
snd_mtxlock(d->lock);
|
2001-09-18 14:50:52 +00:00
|
|
|
(*pnum)--;
|
2002-11-25 17:17:43 +00:00
|
|
|
snd_mtxunlock(d->lock);
|
2001-09-18 14:50:52 +00:00
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ch;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
pcm_chn_destroy(struct pcm_channel *ch)
|
|
|
|
{
|
2001-09-18 14:50:52 +00:00
|
|
|
struct snddev_info *d;
|
2001-05-27 17:22:00 +00:00
|
|
|
int err;
|
|
|
|
|
2001-09-18 14:50:52 +00:00
|
|
|
d = ch->parentsnddev;
|
2001-05-27 17:22:00 +00:00
|
|
|
err = chn_kill(ch);
|
|
|
|
if (err) {
|
2001-09-18 14:50:52 +00:00
|
|
|
device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
|
2001-05-27 17:22:00 +00:00
|
|
|
return err;
|
2000-01-05 20:44:41 +00:00
|
|
|
}
|
2001-05-27 17:22:00 +00:00
|
|
|
|
|
|
|
kobj_delete(ch->methods, M_DEVBUF);
|
|
|
|
free(ch, M_DEVBUF);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2004-01-20 03:58:57 +00:00
|
|
|
pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
|
2001-05-27 17:22:00 +00:00
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_channel *sce, *tmp, *after;
|
2006-03-21 06:35:48 +00:00
|
|
|
unsigned rdevcount;
|
2006-03-31 10:36:36 +00:00
|
|
|
int device = device_get_unit(d->dev);
|
2006-03-21 06:35:48 +00:00
|
|
|
size_t namelen;
|
2004-01-20 03:58:57 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Note it's confusing nomenclature.
|
|
|
|
* dev_t
|
|
|
|
* device -> pcm_device
|
2006-03-31 10:36:36 +00:00
|
|
|
* unit -> pcm_channel
|
2004-01-20 03:58:57 +00:00
|
|
|
* channel -> snddev_channel
|
|
|
|
* device_t
|
|
|
|
* unit -> pcm_device
|
|
|
|
*/
|
2001-06-14 13:31:30 +00:00
|
|
|
|
2003-02-19 05:47:46 +00:00
|
|
|
sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
|
2001-05-27 17:22:00 +00:00
|
|
|
if (!sce) {
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
|
2002-07-22 02:38:20 +00:00
|
|
|
snd_mtxlock(d->lock);
|
2001-05-27 17:22:00 +00:00
|
|
|
sce->channel = ch;
|
2006-03-21 06:35:48 +00:00
|
|
|
sce->chan_num = 0;
|
2006-03-31 10:36:36 +00:00
|
|
|
rdevcount = 0;
|
2006-03-21 06:35:48 +00:00
|
|
|
after = NULL;
|
2006-03-31 10:36:36 +00:00
|
|
|
SLIST_FOREACH(tmp, &d->channels, link) {
|
|
|
|
if (sce->chan_num == tmp->chan_num)
|
|
|
|
sce->chan_num++;
|
|
|
|
else {
|
|
|
|
#if 0
|
|
|
|
device_printf(d->dev,
|
|
|
|
"%s: cdev numbering screwed (Expect: %d, Got: %d)\n",
|
|
|
|
__func__, sce->chan_num, tmp->chan_num);
|
|
|
|
#endif
|
|
|
|
goto retry_chan_num_search;
|
|
|
|
}
|
|
|
|
after = tmp;
|
|
|
|
rdevcount++;
|
|
|
|
}
|
|
|
|
goto retry_chan_num_search_out;
|
2006-03-21 06:35:48 +00:00
|
|
|
retry_chan_num_search:
|
|
|
|
/*
|
|
|
|
* Look for possible channel numbering collision. This may not
|
|
|
|
* be optimized, but it will ensure that no collision occured.
|
2006-03-31 10:36:36 +00:00
|
|
|
* Can be considered cheap since none of the locking/unlocking
|
|
|
|
* operations involved.
|
2006-03-21 06:35:48 +00:00
|
|
|
*/
|
|
|
|
rdevcount = 0;
|
2006-03-31 10:36:36 +00:00
|
|
|
after = NULL;
|
2006-03-21 06:35:48 +00:00
|
|
|
SLIST_FOREACH(tmp, &d->channels, link) {
|
|
|
|
if (sce->chan_num == tmp->chan_num) {
|
|
|
|
sce->chan_num++;
|
|
|
|
goto retry_chan_num_search;
|
2006-03-16 04:12:49 +00:00
|
|
|
}
|
2006-03-31 10:36:36 +00:00
|
|
|
if (sce->chan_num > tmp->chan_num)
|
|
|
|
after = tmp;
|
2006-03-21 06:35:48 +00:00
|
|
|
rdevcount++;
|
|
|
|
}
|
2006-03-31 10:36:36 +00:00
|
|
|
retry_chan_num_search_out:
|
2006-03-21 06:35:48 +00:00
|
|
|
/*
|
|
|
|
* Don't overflow PCMMKMINOR / PCMMAXCHAN.
|
|
|
|
*/
|
|
|
|
if (sce->chan_num > PCMMAXCHAN) {
|
|
|
|
snd_mtxunlock(d->lock);
|
|
|
|
device_printf(d->dev,
|
|
|
|
"%s: WARNING: sce->chan_num overflow! (%d)\n",
|
|
|
|
__func__, sce->chan_num);
|
|
|
|
free(sce, M_DEVBUF);
|
|
|
|
return E2BIG;
|
|
|
|
}
|
|
|
|
if (d->devcount != rdevcount) {
|
|
|
|
device_printf(d->dev,
|
|
|
|
"%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
|
|
|
|
__func__, d->devcount, rdevcount);
|
|
|
|
d->devcount = rdevcount;
|
2001-08-23 11:30:52 +00:00
|
|
|
}
|
2006-03-16 04:12:49 +00:00
|
|
|
d->devcount++;
|
2006-03-21 06:35:48 +00:00
|
|
|
if (after == NULL) {
|
|
|
|
SLIST_INSERT_HEAD(&d->channels, sce, link);
|
|
|
|
} else {
|
|
|
|
SLIST_INSERT_AFTER(after, sce, link);
|
|
|
|
}
|
2006-03-31 10:36:36 +00:00
|
|
|
#if 0
|
|
|
|
if (1) {
|
|
|
|
int cnum = 0;
|
|
|
|
SLIST_FOREACH(tmp, &d->channels, link) {
|
|
|
|
if (cnum != tmp->chan_num)
|
|
|
|
device_printf(d->dev,
|
|
|
|
"%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n",
|
|
|
|
__func__, cnum, tmp->chan_num);
|
|
|
|
cnum++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2006-03-21 06:35:48 +00:00
|
|
|
|
|
|
|
namelen = strlen(ch->name);
|
|
|
|
if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */
|
|
|
|
snprintf(ch->name + namelen,
|
|
|
|
CHN_NAMELEN - namelen, ":dsp%d.%d",
|
|
|
|
device, sce->chan_num);
|
|
|
|
}
|
2002-11-25 17:17:43 +00:00
|
|
|
snd_mtxunlock(d->lock);
|
2006-03-31 10:36:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* I will revisit these someday, and nuke it mercilessly..
|
|
|
|
*/
|
2006-03-21 06:35:48 +00:00
|
|
|
sce->dsp_devt = make_dev(&dsp_cdevsw,
|
2004-01-20 03:58:57 +00:00
|
|
|
PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
|
|
|
|
UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
|
|
|
|
device, sce->chan_num);
|
|
|
|
|
2006-03-21 06:35:48 +00:00
|
|
|
sce->dspW_devt = make_dev(&dsp_cdevsw,
|
2004-01-20 03:58:57 +00:00
|
|
|
PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
|
|
|
|
UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
|
|
|
|
device, sce->chan_num);
|
|
|
|
|
2006-03-21 06:35:48 +00:00
|
|
|
sce->audio_devt = make_dev(&dsp_cdevsw,
|
2004-01-20 03:58:57 +00:00
|
|
|
PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
|
|
|
|
UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
|
|
|
|
device, sce->chan_num);
|
|
|
|
|
|
|
|
if (ch->direction == PCMDIR_REC)
|
|
|
|
sce->dspr_devt = make_dev(&dsp_cdevsw,
|
|
|
|
PCMMKMINOR(device, SND_DEV_DSPREC,
|
|
|
|
sce->chan_num), UID_ROOT, GID_WHEEL,
|
|
|
|
0666, "dspr%d.%d", device, sce->chan_num);
|
2001-06-14 13:31:30 +00:00
|
|
|
|
2000-09-01 20:09:24 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
int
|
2004-01-20 03:58:57 +00:00
|
|
|
pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
|
2000-09-01 20:09:24 +00:00
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_channel *sce;
|
2004-01-17 10:37:11 +00:00
|
|
|
#if 0
|
2003-08-18 23:20:39 +00:00
|
|
|
int ourlock;
|
|
|
|
|
|
|
|
ourlock = 0;
|
|
|
|
if (!mtx_owned(d->lock)) {
|
|
|
|
snd_mtxlock(d->lock);
|
|
|
|
ourlock = 1;
|
|
|
|
}
|
2004-01-17 10:37:11 +00:00
|
|
|
#endif
|
2000-09-01 20:09:24 +00:00
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
SLIST_FOREACH(sce, &d->channels, link) {
|
|
|
|
if (sce->channel == ch)
|
|
|
|
goto gotit;
|
2000-09-01 20:09:24 +00:00
|
|
|
}
|
2004-01-17 10:37:11 +00:00
|
|
|
#if 0
|
2003-08-18 23:20:39 +00:00
|
|
|
if (ourlock)
|
|
|
|
snd_mtxunlock(d->lock);
|
2004-01-17 10:37:11 +00:00
|
|
|
#endif
|
2001-05-27 17:22:00 +00:00
|
|
|
return EINVAL;
|
|
|
|
gotit:
|
|
|
|
SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
|
2002-11-25 17:17:43 +00:00
|
|
|
|
- channel.h
* New definition CHN_F_HAS_VCHAN.
- channel.c
* Use CHN_F_HAS_VCHAN to mark channel with vchan capability instead
of relying on SLIST_EMPTY(&channel->children) == true for better
clarification and future possible usages of children (like
'slave' channel).
* Various fixes, including blocksize / format bps allignment,
better 24bit seeking (mplayer, others).
* Improve format chain building, it's now possible to record something
to a format non-native to the soundcard through various feeder format
converters or to higher sampling rate. This also gains another feature,
like doing vchan mixing on non s16le soundcard such as sb8.
- sound.c
* Increase robustness within various function that handle vchan
creation / termination (these function need a total rewrite, but
that would cause other major rewrite within various places too!).
As far as its robustness can be guaranteed, leave it as is.
* Optimize channel ordering, prefer *real* hardware playback
channels over virtual channels. cat /dev/sndstat should look
better.
* Increase sndstat verbosity to include bufsoft/bufhard allocation.
- vchan.c
* Fix LOR 119.
- http://sources.zabbadoz.net/freebsd/lor.html#119
* Reorder / increase robustness of vchan_create() / destroy().
Enforce destroy_dev() during destroy operation, fix possible
panic / dangling character device.
- http://lists.freebsd.org/pipermail/freebsd-current/2005-May/050308.html
* Tolerate a little bit more during mixing process, this should help
non s16le soundcards.
Note: Recoring in a non-native rate/format may result in overruns. A friendly
application is wavrec from audio/wavplay. The problem is under
investigation.
Submitted by: Ariff Abdullah <skywizard@MyBSD.org.my>
2005-09-10 18:10:31 +00:00
|
|
|
if (ch->flags & CHN_F_VIRTUAL)
|
2002-11-25 17:17:43 +00:00
|
|
|
d->vchancount--;
|
- channel.h
* New definition CHN_F_HAS_VCHAN.
- channel.c
* Use CHN_F_HAS_VCHAN to mark channel with vchan capability instead
of relying on SLIST_EMPTY(&channel->children) == true for better
clarification and future possible usages of children (like
'slave' channel).
* Various fixes, including blocksize / format bps allignment,
better 24bit seeking (mplayer, others).
* Improve format chain building, it's now possible to record something
to a format non-native to the soundcard through various feeder format
converters or to higher sampling rate. This also gains another feature,
like doing vchan mixing on non s16le soundcard such as sb8.
- sound.c
* Increase robustness within various function that handle vchan
creation / termination (these function need a total rewrite, but
that would cause other major rewrite within various places too!).
As far as its robustness can be guaranteed, leave it as is.
* Optimize channel ordering, prefer *real* hardware playback
channels over virtual channels. cat /dev/sndstat should look
better.
* Increase sndstat verbosity to include bufsoft/bufhard allocation.
- vchan.c
* Fix LOR 119.
- http://sources.zabbadoz.net/freebsd/lor.html#119
* Reorder / increase robustness of vchan_create() / destroy().
Enforce destroy_dev() during destroy operation, fix possible
panic / dangling character device.
- http://lists.freebsd.org/pipermail/freebsd-current/2005-May/050308.html
* Tolerate a little bit more during mixing process, this should help
non s16le soundcards.
Note: Recoring in a non-native rate/format may result in overruns. A friendly
application is wavrec from audio/wavplay. The problem is under
investigation.
Submitted by: Ariff Abdullah <skywizard@MyBSD.org.my>
2005-09-10 18:10:31 +00:00
|
|
|
else if (ch->direction == PCMDIR_REC)
|
|
|
|
d->reccount--;
|
2002-11-25 17:17:43 +00:00
|
|
|
else
|
|
|
|
d->playcount--;
|
|
|
|
|
2004-01-17 10:37:11 +00:00
|
|
|
#if 0
|
2003-08-18 23:20:39 +00:00
|
|
|
if (ourlock)
|
|
|
|
snd_mtxunlock(d->lock);
|
2004-01-17 10:37:11 +00:00
|
|
|
#endif
|
2002-11-25 17:17:43 +00:00
|
|
|
free(sce, M_DEVBUF);
|
2001-05-27 17:22:00 +00:00
|
|
|
|
1999-09-01 04:08:39 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
int
|
|
|
|
pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
|
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_info *d = device_get_softc(dev);
|
2001-08-23 11:30:52 +00:00
|
|
|
struct pcm_channel *ch;
|
2006-03-31 10:36:36 +00:00
|
|
|
int err;
|
2001-05-27 17:22:00 +00:00
|
|
|
|
|
|
|
ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
|
|
|
|
if (!ch) {
|
|
|
|
device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
|
|
|
|
return ENODEV;
|
|
|
|
}
|
2001-06-26 21:54:55 +00:00
|
|
|
|
2004-01-20 03:58:57 +00:00
|
|
|
err = pcm_chn_add(d, ch);
|
2001-05-27 17:22:00 +00:00
|
|
|
if (err) {
|
|
|
|
device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
|
|
|
|
pcm_chn_destroy(ch);
|
2001-06-26 21:54:55 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pcm_killchan(device_t dev)
|
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_info *d = device_get_softc(dev);
|
|
|
|
struct snddev_channel *sce;
|
2002-11-04 19:12:02 +00:00
|
|
|
struct pcm_channel *ch;
|
|
|
|
int error = 0;
|
2001-05-27 17:22:00 +00:00
|
|
|
|
|
|
|
sce = SLIST_FIRST(&d->channels);
|
2002-11-04 19:12:02 +00:00
|
|
|
ch = sce->channel;
|
2001-05-27 17:22:00 +00:00
|
|
|
|
2004-01-20 03:58:57 +00:00
|
|
|
error = pcm_chn_remove(d, sce->channel);
|
2002-11-04 19:12:02 +00:00
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
return (pcm_chn_destroy(ch));
|
2001-05-27 17:22:00 +00:00
|
|
|
}
|
|
|
|
|
1999-09-01 04:08:39 +00:00
|
|
|
int
|
|
|
|
pcm_setstatus(device_t dev, char *str)
|
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_info *d = device_get_softc(dev);
|
2001-06-07 20:06:22 +00:00
|
|
|
|
|
|
|
snd_mtxlock(d->lock);
|
1999-09-01 04:08:39 +00:00
|
|
|
strncpy(d->status, str, SND_STATUSLEN);
|
2001-06-07 20:06:22 +00:00
|
|
|
snd_mtxunlock(d->lock);
|
2006-03-31 10:36:36 +00:00
|
|
|
if (snd_maxautovchans > 0)
|
|
|
|
pcm_setvchans(d, 1);
|
1999-09-01 04:08:39 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-03-31 10:36:36 +00:00
|
|
|
uint32_t
|
1999-09-01 04:08:39 +00:00
|
|
|
pcm_getflags(device_t dev)
|
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_info *d = device_get_softc(dev);
|
2001-06-07 20:06:22 +00:00
|
|
|
|
1999-09-01 04:08:39 +00:00
|
|
|
return d->flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-03-31 10:36:36 +00:00
|
|
|
pcm_setflags(device_t dev, uint32_t val)
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_info *d = device_get_softc(dev);
|
2001-06-16 21:25:10 +00:00
|
|
|
|
1999-09-01 04:08:39 +00:00
|
|
|
d->flags = val;
|
|
|
|
}
|
|
|
|
|
2000-03-20 15:30:50 +00:00
|
|
|
void *
|
|
|
|
pcm_getdevinfo(device_t dev)
|
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_info *d = device_get_softc(dev);
|
2001-06-07 20:06:22 +00:00
|
|
|
|
2000-03-20 15:30:50 +00:00
|
|
|
return d->devinfo;
|
|
|
|
}
|
|
|
|
|
2001-09-18 14:50:52 +00:00
|
|
|
unsigned int
|
2006-07-17 17:43:06 +00:00
|
|
|
pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
|
2001-09-18 14:50:52 +00:00
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_info *d = device_get_softc(dev);
|
2002-01-23 05:49:41 +00:00
|
|
|
int sz, x;
|
2001-09-18 14:50:52 +00:00
|
|
|
|
|
|
|
sz = 0;
|
2002-01-23 05:49:41 +00:00
|
|
|
if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
|
|
|
|
x = sz;
|
2006-07-17 17:43:06 +00:00
|
|
|
RANGE(sz, minbufsz, maxbufsz);
|
2002-01-23 05:49:41 +00:00
|
|
|
if (x != sz)
|
2006-07-17 17:43:06 +00:00
|
|
|
device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
|
|
|
|
x = minbufsz;
|
2002-01-23 05:49:41 +00:00
|
|
|
while (x < sz)
|
|
|
|
x <<= 1;
|
|
|
|
if (x > sz)
|
|
|
|
x >>= 1;
|
|
|
|
if (x != sz) {
|
|
|
|
device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
|
|
|
|
sz = x;
|
|
|
|
}
|
|
|
|
} else {
|
2001-09-18 14:50:52 +00:00
|
|
|
sz = deflt;
|
2002-01-23 05:49:41 +00:00
|
|
|
}
|
|
|
|
|
2001-09-18 14:50:52 +00:00
|
|
|
d->bufsz = sz;
|
|
|
|
|
|
|
|
return sz;
|
|
|
|
}
|
|
|
|
|
1999-09-01 04:08:39 +00:00
|
|
|
int
|
|
|
|
pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
|
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_info *d = device_get_softc(dev);
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2002-01-26 22:13:24 +00:00
|
|
|
if (pcm_veto_load) {
|
|
|
|
device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
|
|
|
|
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2002-04-04 20:54:27 +00:00
|
|
|
d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
|
2001-05-27 17:22:00 +00:00
|
|
|
|
2005-07-31 10:55:24 +00:00
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
* d->flags should be cleared by the allocator of the softc.
|
|
|
|
* We cannot clear this field here because several devices set
|
|
|
|
* this flag before calling pcm_register().
|
|
|
|
*/
|
2001-06-26 21:54:55 +00:00
|
|
|
d->flags = 0;
|
2005-07-31 10:55:24 +00:00
|
|
|
#endif
|
2000-06-20 23:27:12 +00:00
|
|
|
d->dev = dev;
|
1999-09-01 04:08:39 +00:00
|
|
|
d->devinfo = devinfo;
|
2001-06-27 19:59:45 +00:00
|
|
|
d->devcount = 0;
|
2001-09-05 16:28:41 +00:00
|
|
|
d->reccount = 0;
|
2001-09-18 14:50:52 +00:00
|
|
|
d->playcount = 0;
|
2001-06-27 19:59:45 +00:00
|
|
|
d->vchancount = 0;
|
2001-06-16 21:25:10 +00:00
|
|
|
d->inprog = 0;
|
2000-01-06 04:11:36 +00:00
|
|
|
|
2004-01-17 10:37:11 +00:00
|
|
|
SLIST_INIT(&d->channels);
|
|
|
|
|
2006-03-31 10:36:36 +00:00
|
|
|
if ((numplay == 0 || numrec == 0) && numplay != numrec)
|
2001-06-14 13:31:30 +00:00
|
|
|
d->flags |= SD_F_SIMPLEX;
|
2001-06-16 21:25:10 +00:00
|
|
|
|
2001-06-14 13:31:30 +00:00
|
|
|
d->fakechan = fkchan_setup(dev);
|
2004-02-28 19:47:02 +00:00
|
|
|
chn_init(d->fakechan, NULL, 0, 0);
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2001-02-27 07:01:49 +00:00
|
|
|
#ifdef SND_DYNSYSCTL
|
2001-01-05 07:07:03 +00:00
|
|
|
sysctl_ctx_init(&d->sysctl_tree);
|
|
|
|
d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
|
2001-01-11 23:22:33 +00:00
|
|
|
SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
|
2001-01-05 07:07:03 +00:00
|
|
|
device_get_nameunit(dev), CTLFLAG_RD, 0, "");
|
2001-01-11 23:22:33 +00:00
|
|
|
if (d->sysctl_tree_top == NULL) {
|
2001-01-05 07:07:03 +00:00
|
|
|
sysctl_ctx_free(&d->sysctl_tree);
|
|
|
|
goto no;
|
|
|
|
}
|
2006-06-18 14:14:41 +00:00
|
|
|
/* XXX: an user should be able to set this with a control tool, the
|
|
|
|
sysadmin then needs min+max sysctls for this */
|
2001-09-18 14:50:52 +00:00
|
|
|
SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
|
2006-06-18 14:14:41 +00:00
|
|
|
OID_AUTO, "_buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
|
2001-02-27 07:01:49 +00:00
|
|
|
#endif
|
- channel.h
* New definition CHN_F_HAS_VCHAN.
- channel.c
* Use CHN_F_HAS_VCHAN to mark channel with vchan capability instead
of relying on SLIST_EMPTY(&channel->children) == true for better
clarification and future possible usages of children (like
'slave' channel).
* Various fixes, including blocksize / format bps allignment,
better 24bit seeking (mplayer, others).
* Improve format chain building, it's now possible to record something
to a format non-native to the soundcard through various feeder format
converters or to higher sampling rate. This also gains another feature,
like doing vchan mixing on non s16le soundcard such as sb8.
- sound.c
* Increase robustness within various function that handle vchan
creation / termination (these function need a total rewrite, but
that would cause other major rewrite within various places too!).
As far as its robustness can be guaranteed, leave it as is.
* Optimize channel ordering, prefer *real* hardware playback
channels over virtual channels. cat /dev/sndstat should look
better.
* Increase sndstat verbosity to include bufsoft/bufhard allocation.
- vchan.c
* Fix LOR 119.
- http://sources.zabbadoz.net/freebsd/lor.html#119
* Reorder / increase robustness of vchan_create() / destroy().
Enforce destroy_dev() during destroy operation, fix possible
panic / dangling character device.
- http://lists.freebsd.org/pipermail/freebsd-current/2005-May/050308.html
* Tolerate a little bit more during mixing process, this should help
non s16le soundcards.
Note: Recoring in a non-native rate/format may result in overruns. A friendly
application is wavrec from audio/wavplay. The problem is under
investigation.
Submitted by: Ariff Abdullah <skywizard@MyBSD.org.my>
2005-09-10 18:10:31 +00:00
|
|
|
if (numplay > 0) {
|
2001-06-26 21:54:55 +00:00
|
|
|
d->flags |= SD_F_AUTOVCHAN;
|
2006-03-21 06:35:48 +00:00
|
|
|
vchan_initsys(dev);
|
- channel.h
* New definition CHN_F_HAS_VCHAN.
- channel.c
* Use CHN_F_HAS_VCHAN to mark channel with vchan capability instead
of relying on SLIST_EMPTY(&channel->children) == true for better
clarification and future possible usages of children (like
'slave' channel).
* Various fixes, including blocksize / format bps allignment,
better 24bit seeking (mplayer, others).
* Improve format chain building, it's now possible to record something
to a format non-native to the soundcard through various feeder format
converters or to higher sampling rate. This also gains another feature,
like doing vchan mixing on non s16le soundcard such as sb8.
- sound.c
* Increase robustness within various function that handle vchan
creation / termination (these function need a total rewrite, but
that would cause other major rewrite within various places too!).
As far as its robustness can be guaranteed, leave it as is.
* Optimize channel ordering, prefer *real* hardware playback
channels over virtual channels. cat /dev/sndstat should look
better.
* Increase sndstat verbosity to include bufsoft/bufhard allocation.
- vchan.c
* Fix LOR 119.
- http://sources.zabbadoz.net/freebsd/lor.html#119
* Reorder / increase robustness of vchan_create() / destroy().
Enforce destroy_dev() during destroy operation, fix possible
panic / dangling character device.
- http://lists.freebsd.org/pipermail/freebsd-current/2005-May/050308.html
* Tolerate a little bit more during mixing process, this should help
non s16le soundcards.
Note: Recoring in a non-native rate/format may result in overruns. A friendly
application is wavrec from audio/wavplay. The problem is under
investigation.
Submitted by: Ariff Abdullah <skywizard@MyBSD.org.my>
2005-09-10 18:10:31 +00:00
|
|
|
}
|
2001-06-26 21:54:55 +00:00
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
sndstat_register(dev, d->status, sndstat_prepare_pcm);
|
2006-03-31 10:36:36 +00:00
|
|
|
return 0;
|
1999-09-01 04:08:39 +00:00
|
|
|
no:
|
2001-06-07 20:06:22 +00:00
|
|
|
snd_mtxfree(d->lock);
|
1999-09-01 04:08:39 +00:00
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
|
2000-09-01 20:09:24 +00:00
|
|
|
int
|
|
|
|
pcm_unregister(device_t dev)
|
2000-08-29 16:21:33 +00:00
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_info *d = device_get_softc(dev);
|
|
|
|
struct snddev_channel *sce;
|
|
|
|
struct pcmchan_children *pce;
|
2001-09-18 14:50:52 +00:00
|
|
|
struct pcm_channel *ch;
|
2000-08-29 16:21:33 +00:00
|
|
|
|
2005-10-02 15:43:57 +00:00
|
|
|
if (sndstat_acquire() != 0) {
|
|
|
|
device_printf(dev, "unregister: sndstat busy\n");
|
|
|
|
return EBUSY;
|
|
|
|
}
|
|
|
|
|
2001-06-07 20:06:22 +00:00
|
|
|
snd_mtxlock(d->lock);
|
2001-06-16 21:25:10 +00:00
|
|
|
if (d->inprog) {
|
2001-09-14 20:26:03 +00:00
|
|
|
device_printf(dev, "unregister: operation in progress\n");
|
|
|
|
snd_mtxunlock(d->lock);
|
2005-10-02 15:43:57 +00:00
|
|
|
sndstat_release();
|
2001-06-16 21:25:10 +00:00
|
|
|
return EBUSY;
|
|
|
|
}
|
2004-01-20 03:58:57 +00:00
|
|
|
|
2001-05-27 17:22:00 +00:00
|
|
|
SLIST_FOREACH(sce, &d->channels, link) {
|
2001-09-18 14:50:52 +00:00
|
|
|
ch = sce->channel;
|
|
|
|
if (ch->refcount > 0) {
|
2002-04-28 22:59:45 +00:00
|
|
|
device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
|
2001-06-07 20:06:22 +00:00
|
|
|
snd_mtxunlock(d->lock);
|
2005-10-02 15:43:57 +00:00
|
|
|
sndstat_release();
|
2001-05-27 17:22:00 +00:00
|
|
|
return EBUSY;
|
|
|
|
}
|
2000-09-05 20:58:51 +00:00
|
|
|
}
|
2004-01-20 03:58:57 +00:00
|
|
|
|
2006-03-22 20:50:04 +00:00
|
|
|
if (mixer_uninit(dev) == EBUSY) {
|
2005-07-31 10:55:24 +00:00
|
|
|
device_printf(dev, "unregister: mixer busy\n");
|
|
|
|
snd_mtxunlock(d->lock);
|
2005-10-02 15:43:57 +00:00
|
|
|
sndstat_release();
|
2005-07-31 10:55:24 +00:00
|
|
|
return EBUSY;
|
|
|
|
}
|
|
|
|
|
2004-01-20 03:58:57 +00:00
|
|
|
SLIST_FOREACH(sce, &d->channels, link) {
|
2006-03-16 04:12:49 +00:00
|
|
|
if (sce->dsp_devt) {
|
- channel.h
* New definition CHN_F_HAS_VCHAN.
- channel.c
* Use CHN_F_HAS_VCHAN to mark channel with vchan capability instead
of relying on SLIST_EMPTY(&channel->children) == true for better
clarification and future possible usages of children (like
'slave' channel).
* Various fixes, including blocksize / format bps allignment,
better 24bit seeking (mplayer, others).
* Improve format chain building, it's now possible to record something
to a format non-native to the soundcard through various feeder format
converters or to higher sampling rate. This also gains another feature,
like doing vchan mixing on non s16le soundcard such as sb8.
- sound.c
* Increase robustness within various function that handle vchan
creation / termination (these function need a total rewrite, but
that would cause other major rewrite within various places too!).
As far as its robustness can be guaranteed, leave it as is.
* Optimize channel ordering, prefer *real* hardware playback
channels over virtual channels. cat /dev/sndstat should look
better.
* Increase sndstat verbosity to include bufsoft/bufhard allocation.
- vchan.c
* Fix LOR 119.
- http://sources.zabbadoz.net/freebsd/lor.html#119
* Reorder / increase robustness of vchan_create() / destroy().
Enforce destroy_dev() during destroy operation, fix possible
panic / dangling character device.
- http://lists.freebsd.org/pipermail/freebsd-current/2005-May/050308.html
* Tolerate a little bit more during mixing process, this should help
non s16le soundcards.
Note: Recoring in a non-native rate/format may result in overruns. A friendly
application is wavrec from audio/wavplay. The problem is under
investigation.
Submitted by: Ariff Abdullah <skywizard@MyBSD.org.my>
2005-09-10 18:10:31 +00:00
|
|
|
destroy_dev(sce->dsp_devt);
|
2006-03-16 04:12:49 +00:00
|
|
|
sce->dsp_devt = NULL;
|
|
|
|
}
|
|
|
|
if (sce->dspW_devt) {
|
- channel.h
* New definition CHN_F_HAS_VCHAN.
- channel.c
* Use CHN_F_HAS_VCHAN to mark channel with vchan capability instead
of relying on SLIST_EMPTY(&channel->children) == true for better
clarification and future possible usages of children (like
'slave' channel).
* Various fixes, including blocksize / format bps allignment,
better 24bit seeking (mplayer, others).
* Improve format chain building, it's now possible to record something
to a format non-native to the soundcard through various feeder format
converters or to higher sampling rate. This also gains another feature,
like doing vchan mixing on non s16le soundcard such as sb8.
- sound.c
* Increase robustness within various function that handle vchan
creation / termination (these function need a total rewrite, but
that would cause other major rewrite within various places too!).
As far as its robustness can be guaranteed, leave it as is.
* Optimize channel ordering, prefer *real* hardware playback
channels over virtual channels. cat /dev/sndstat should look
better.
* Increase sndstat verbosity to include bufsoft/bufhard allocation.
- vchan.c
* Fix LOR 119.
- http://sources.zabbadoz.net/freebsd/lor.html#119
* Reorder / increase robustness of vchan_create() / destroy().
Enforce destroy_dev() during destroy operation, fix possible
panic / dangling character device.
- http://lists.freebsd.org/pipermail/freebsd-current/2005-May/050308.html
* Tolerate a little bit more during mixing process, this should help
non s16le soundcards.
Note: Recoring in a non-native rate/format may result in overruns. A friendly
application is wavrec from audio/wavplay. The problem is under
investigation.
Submitted by: Ariff Abdullah <skywizard@MyBSD.org.my>
2005-09-10 18:10:31 +00:00
|
|
|
destroy_dev(sce->dspW_devt);
|
2006-03-16 04:12:49 +00:00
|
|
|
sce->dspW_devt = NULL;
|
|
|
|
}
|
|
|
|
if (sce->audio_devt) {
|
- channel.h
* New definition CHN_F_HAS_VCHAN.
- channel.c
* Use CHN_F_HAS_VCHAN to mark channel with vchan capability instead
of relying on SLIST_EMPTY(&channel->children) == true for better
clarification and future possible usages of children (like
'slave' channel).
* Various fixes, including blocksize / format bps allignment,
better 24bit seeking (mplayer, others).
* Improve format chain building, it's now possible to record something
to a format non-native to the soundcard through various feeder format
converters or to higher sampling rate. This also gains another feature,
like doing vchan mixing on non s16le soundcard such as sb8.
- sound.c
* Increase robustness within various function that handle vchan
creation / termination (these function need a total rewrite, but
that would cause other major rewrite within various places too!).
As far as its robustness can be guaranteed, leave it as is.
* Optimize channel ordering, prefer *real* hardware playback
channels over virtual channels. cat /dev/sndstat should look
better.
* Increase sndstat verbosity to include bufsoft/bufhard allocation.
- vchan.c
* Fix LOR 119.
- http://sources.zabbadoz.net/freebsd/lor.html#119
* Reorder / increase robustness of vchan_create() / destroy().
Enforce destroy_dev() during destroy operation, fix possible
panic / dangling character device.
- http://lists.freebsd.org/pipermail/freebsd-current/2005-May/050308.html
* Tolerate a little bit more during mixing process, this should help
non s16le soundcards.
Note: Recoring in a non-native rate/format may result in overruns. A friendly
application is wavrec from audio/wavplay. The problem is under
investigation.
Submitted by: Ariff Abdullah <skywizard@MyBSD.org.my>
2005-09-10 18:10:31 +00:00
|
|
|
destroy_dev(sce->audio_devt);
|
2006-03-16 04:12:49 +00:00
|
|
|
sce->audio_devt = NULL;
|
|
|
|
}
|
|
|
|
if (sce->dspr_devt) {
|
2004-01-20 03:58:57 +00:00
|
|
|
destroy_dev(sce->dspr_devt);
|
2006-03-16 04:12:49 +00:00
|
|
|
sce->dspr_devt = NULL;
|
|
|
|
}
|
|
|
|
d->devcount--;
|
2006-03-31 10:36:36 +00:00
|
|
|
ch = sce->channel;
|
|
|
|
if (ch == NULL)
|
|
|
|
continue;
|
|
|
|
pce = SLIST_FIRST(&ch->children);
|
|
|
|
while (pce != NULL) {
|
|
|
|
#if 0
|
|
|
|
device_printf(d->dev, "<%s> removing <%s>\n",
|
|
|
|
ch->name, (pce->channel != NULL) ?
|
|
|
|
pce->channel->name : "unknown");
|
|
|
|
#endif
|
|
|
|
SLIST_REMOVE(&ch->children, pce, pcmchan_children, link);
|
|
|
|
free(pce, M_DEVBUF);
|
|
|
|
pce = SLIST_FIRST(&ch->children);
|
|
|
|
}
|
2004-01-20 03:58:57 +00:00
|
|
|
}
|
|
|
|
|
2001-03-24 23:10:29 +00:00
|
|
|
#ifdef SND_DYNSYSCTL
|
|
|
|
d->sysctl_tree_top = NULL;
|
|
|
|
sysctl_ctx_free(&d->sysctl_tree);
|
|
|
|
#endif
|
2006-03-31 10:36:36 +00:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
SLIST_FOREACH(sce, &d->channels, link) {
|
|
|
|
ch = sce->channel;
|
|
|
|
if (ch == NULL)
|
|
|
|
continue;
|
|
|
|
if (!SLIST_EMPTY(&ch->children))
|
|
|
|
device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n",
|
|
|
|
__func__, ch->name);
|
|
|
|
}
|
|
|
|
#endif
|
2001-06-17 23:23:06 +00:00
|
|
|
while (!SLIST_EMPTY(&d->channels))
|
2001-05-27 17:22:00 +00:00
|
|
|
pcm_killchan(dev);
|
2000-08-29 16:21:33 +00:00
|
|
|
|
2001-03-24 23:10:29 +00:00
|
|
|
chn_kill(d->fakechan);
|
|
|
|
fkchan_kill(d->fakechan);
|
2001-02-27 07:01:49 +00:00
|
|
|
|
2006-03-31 10:36:36 +00:00
|
|
|
#if 0
|
|
|
|
device_printf(d->dev, "%s: devcount=%u, playcount=%u, "
|
|
|
|
"reccount=%u, vchancount=%u\n",
|
|
|
|
__func__, d->devcount, d->playcount, d->reccount,
|
|
|
|
d->vchancount);
|
|
|
|
#endif
|
2004-01-17 10:37:11 +00:00
|
|
|
snd_mtxunlock(d->lock);
|
2001-06-07 20:06:22 +00:00
|
|
|
snd_mtxfree(d->lock);
|
2005-10-02 15:43:57 +00:00
|
|
|
sndstat_unregister(dev);
|
|
|
|
sndstat_release();
|
2000-09-01 20:09:24 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2000-08-29 16:21:33 +00:00
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
/************************************************************************/
|
|
|
|
|
|
|
|
static int
|
|
|
|
sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
|
|
|
|
{
|
2006-03-31 10:36:36 +00:00
|
|
|
struct snddev_info *d;
|
|
|
|
struct snddev_channel *sce;
|
2001-08-23 11:30:52 +00:00
|
|
|
struct pcm_channel *c;
|
|
|
|
struct pcm_feeder *f;
|
2006-03-31 10:36:36 +00:00
|
|
|
int pc, rc, vc;
|
2001-08-23 11:30:52 +00:00
|
|
|
|
|
|
|
if (verbose < 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
d = device_get_softc(dev);
|
|
|
|
if (!d)
|
|
|
|
return ENXIO;
|
|
|
|
|
|
|
|
snd_mtxlock(d->lock);
|
|
|
|
if (!SLIST_EMPTY(&d->channels)) {
|
|
|
|
pc = rc = vc = 0;
|
|
|
|
SLIST_FOREACH(sce, &d->channels, link) {
|
|
|
|
c = sce->channel;
|
|
|
|
if (c->direction == PCMDIR_PLAY) {
|
|
|
|
if (c->flags & CHN_F_VIRTUAL)
|
|
|
|
vc++;
|
|
|
|
else
|
|
|
|
pc++;
|
|
|
|
} else
|
|
|
|
rc++;
|
|
|
|
}
|
2001-09-18 14:50:52 +00:00
|
|
|
sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
|
2001-08-23 11:30:52 +00:00
|
|
|
(d->flags & SD_F_SIMPLEX)? "" : " duplex",
|
|
|
|
#ifdef USING_DEVFS
|
|
|
|
(device_get_unit(dev) == snd_unit)? " default" : ""
|
|
|
|
#else
|
|
|
|
""
|
|
|
|
#endif
|
|
|
|
);
|
2003-08-18 23:20:39 +00:00
|
|
|
|
|
|
|
if (verbose <= 1) {
|
|
|
|
snd_mtxunlock(d->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
SLIST_FOREACH(sce, &d->channels, link) {
|
|
|
|
c = sce->channel;
|
2006-02-05 17:47:26 +00:00
|
|
|
|
|
|
|
KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
|
|
|
|
("hosed pcm channel setup"));
|
|
|
|
|
2001-08-29 02:49:54 +00:00
|
|
|
sbuf_printf(s, "\n\t");
|
|
|
|
|
2004-01-17 10:37:11 +00:00
|
|
|
/* it would be better to indent child channels */
|
2001-08-29 02:49:54 +00:00
|
|
|
sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
|
2002-01-23 06:02:15 +00:00
|
|
|
sbuf_printf(s, "spd %d", c->speed);
|
|
|
|
if (c->speed != sndbuf_getspd(c->bufhard))
|
|
|
|
sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
|
|
|
|
sbuf_printf(s, ", fmt 0x%08x", c->format);
|
|
|
|
if (c->format != sndbuf_getfmt(c->bufhard))
|
|
|
|
sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
|
2003-08-18 23:20:39 +00:00
|
|
|
sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
|
2001-08-23 11:30:52 +00:00
|
|
|
if (c->pid != -1)
|
|
|
|
sbuf_printf(s, ", pid %d", c->pid);
|
|
|
|
sbuf_printf(s, "\n\t");
|
2002-01-23 06:02:15 +00:00
|
|
|
|
2006-02-05 17:47:26 +00:00
|
|
|
sbuf_printf(s, "interrupts %d, ", c->interrupts);
|
|
|
|
if (c->direction == PCMDIR_REC)
|
|
|
|
sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
|
|
|
|
c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
|
|
|
|
sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
|
|
|
|
sndbuf_getblkcnt(c->bufhard),
|
|
|
|
sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
|
|
|
|
sndbuf_getblkcnt(c->bufsoft));
|
|
|
|
else
|
|
|
|
sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
|
|
|
|
c->xruns, sndbuf_getready(c->bufsoft),
|
|
|
|
sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
|
|
|
|
sndbuf_getblkcnt(c->bufhard),
|
|
|
|
sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
|
|
|
|
sndbuf_getblkcnt(c->bufsoft));
|
|
|
|
sbuf_printf(s, "\n\t");
|
2002-01-23 06:02:15 +00:00
|
|
|
|
|
|
|
sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
|
|
|
|
sbuf_printf(s, " -> ");
|
2001-08-23 11:30:52 +00:00
|
|
|
f = c->feeder;
|
2002-01-23 06:02:15 +00:00
|
|
|
while (f->source != NULL)
|
|
|
|
f = f->source;
|
|
|
|
while (f != NULL) {
|
2001-08-23 11:30:52 +00:00
|
|
|
sbuf_printf(s, "%s", f->class->name);
|
|
|
|
if (f->desc->type == FEEDER_FMT)
|
2002-01-23 06:02:15 +00:00
|
|
|
sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
|
2001-08-23 11:30:52 +00:00
|
|
|
if (f->desc->type == FEEDER_RATE)
|
2002-01-23 06:02:15 +00:00
|
|
|
sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
|
2005-10-02 15:43:57 +00:00
|
|
|
if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
|
|
|
|
f->desc->type == FEEDER_VOLUME)
|
2002-01-23 06:02:15 +00:00
|
|
|
sbuf_printf(s, "(0x%08x)", f->desc->out);
|
|
|
|
sbuf_printf(s, " -> ");
|
|
|
|
f = f->parent;
|
2001-08-23 11:30:52 +00:00
|
|
|
}
|
2002-01-23 06:02:15 +00:00
|
|
|
sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
|
2001-08-23 11:30:52 +00:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
sbuf_printf(s, " (mixer only)");
|
|
|
|
snd_mtxunlock(d->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
|
|
|
#ifdef SND_DYNSYSCTL
|
|
|
|
int
|
|
|
|
sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
struct snddev_info *d;
|
2006-03-31 10:36:36 +00:00
|
|
|
int err, newcnt;
|
2001-08-23 11:30:52 +00:00
|
|
|
|
|
|
|
d = oidp->oid_arg1;
|
|
|
|
|
2006-03-31 10:36:36 +00:00
|
|
|
newcnt = d->vchancount;
|
2001-08-23 11:30:52 +00:00
|
|
|
err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
|
2003-08-18 23:20:39 +00:00
|
|
|
|
2006-03-31 10:36:36 +00:00
|
|
|
if (err == 0 && req->newptr != NULL && d->vchancount != newcnt)
|
|
|
|
err = pcm_setvchans(d, newcnt);
|
2001-08-23 11:30:52 +00:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
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.
2006-09-23 20:45:47 +00:00
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
|
|
|
2004-07-16 04:00:08 +00:00
|
|
|
static int
|
|
|
|
sound_modevent(module_t mod, int type, void *data)
|
|
|
|
{
|
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.
2006-09-23 20:45:47 +00:00
|
|
|
int ret;
|
2005-07-31 10:55:24 +00:00
|
|
|
#if 0
|
2004-07-16 04:00:08 +00:00
|
|
|
return (midi_modevent(mod, type, data));
|
2005-07-31 10:55:24 +00:00
|
|
|
#else
|
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.
2006-09-23 20:45:47 +00:00
|
|
|
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;
|
2005-07-31 10:55:24 +00:00
|
|
|
#endif
|
2004-07-16 04:00:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DEV_MODULE(sound, sound_modevent, NULL);
|
|
|
|
MODULE_VERSION(sound, SOUND_MODVER);
|