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
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/queue.h>
|
|
|
|
|
1999-11-20 16:50:33 +00:00
|
|
|
#include <dev/sound/pcm/sound.h>
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
SND_DECLARE_FILE("$FreeBSD$");
|
|
|
|
|
2000-05-27 14:40:17 +00:00
|
|
|
#define OLDPCM_IOCTL
|
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
static d_open_t dsp_open;
|
|
|
|
static d_close_t dsp_close;
|
|
|
|
static d_read_t dsp_read;
|
|
|
|
static d_write_t dsp_write;
|
|
|
|
static d_ioctl_t dsp_ioctl;
|
|
|
|
static d_poll_t dsp_poll;
|
|
|
|
static d_mmap_t dsp_mmap;
|
|
|
|
|
2004-01-20 03:58:57 +00:00
|
|
|
struct cdevsw dsp_cdevsw = {
|
2004-02-21 21:10:55 +00:00
|
|
|
.d_version = D_VERSION,
|
|
|
|
.d_flags = D_NEEDGIANT,
|
2003-03-03 12:15:54 +00:00
|
|
|
.d_open = dsp_open,
|
|
|
|
.d_close = dsp_close,
|
|
|
|
.d_read = dsp_read,
|
|
|
|
.d_write = dsp_write,
|
|
|
|
.d_ioctl = dsp_ioctl,
|
|
|
|
.d_poll = dsp_poll,
|
|
|
|
.d_mmap = dsp_mmap,
|
|
|
|
.d_name = "dsp",
|
2001-06-16 21:25:10 +00:00
|
|
|
};
|
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
#ifdef USING_DEVFS
|
2001-06-16 21:25:10 +00:00
|
|
|
static eventhandler_tag dsp_ehtag;
|
2001-06-23 17:36:51 +00:00
|
|
|
#endif
|
2001-06-16 21:25:10 +00:00
|
|
|
|
|
|
|
static struct snddev_info *
|
2004-06-16 09:47:26 +00:00
|
|
|
dsp_get_info(struct cdev *dev)
|
2001-06-16 21:25:10 +00:00
|
|
|
{
|
|
|
|
struct snddev_info *d;
|
|
|
|
int unit;
|
|
|
|
|
|
|
|
unit = PCMUNIT(dev);
|
2001-06-18 00:10:47 +00:00
|
|
|
if (unit >= devclass_get_maxunit(pcm_devclass))
|
2001-06-16 21:25:10 +00:00
|
|
|
return NULL;
|
|
|
|
d = devclass_get_softc(pcm_devclass, unit);
|
|
|
|
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
static u_int32_t
|
2004-06-16 09:47:26 +00:00
|
|
|
dsp_get_flags(struct cdev *dev)
|
2001-08-23 11:30:52 +00:00
|
|
|
{
|
|
|
|
device_t bdev;
|
|
|
|
int unit;
|
|
|
|
|
|
|
|
unit = PCMUNIT(dev);
|
|
|
|
if (unit >= devclass_get_maxunit(pcm_devclass))
|
|
|
|
return 0xffffffff;
|
|
|
|
bdev = devclass_get_device(pcm_devclass, unit);
|
|
|
|
|
|
|
|
return pcm_getflags(bdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2004-06-16 09:47:26 +00:00
|
|
|
dsp_set_flags(struct cdev *dev, u_int32_t flags)
|
2001-08-23 11:30:52 +00:00
|
|
|
{
|
|
|
|
device_t bdev;
|
|
|
|
int unit;
|
|
|
|
|
|
|
|
unit = PCMUNIT(dev);
|
|
|
|
if (unit >= devclass_get_maxunit(pcm_devclass))
|
|
|
|
return;
|
|
|
|
bdev = devclass_get_device(pcm_devclass, unit);
|
|
|
|
|
|
|
|
pcm_setflags(bdev, flags);
|
|
|
|
}
|
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
/*
|
|
|
|
* return the channels channels associated with an open device instance.
|
|
|
|
* set the priority if the device is simplex and one direction (only) is
|
|
|
|
* specified.
|
|
|
|
* lock channels specified.
|
|
|
|
*/
|
1999-09-01 04:08:39 +00:00
|
|
|
static int
|
2004-06-16 09:47:26 +00:00
|
|
|
getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio)
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
2001-06-16 21:25:10 +00:00
|
|
|
struct snddev_info *d;
|
2001-08-23 11:30:52 +00:00
|
|
|
u_int32_t flags;
|
2001-06-16 21:25:10 +00:00
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
flags = dsp_get_flags(dev);
|
2001-06-16 21:25:10 +00:00
|
|
|
d = dsp_get_info(dev);
|
2001-08-23 11:30:52 +00:00
|
|
|
pcm_inprog(d, 1);
|
2004-01-28 08:02:15 +00:00
|
|
|
pcm_lock(d);
|
2001-08-23 11:30:52 +00:00
|
|
|
KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
|
1999-12-05 19:09:13 +00:00
|
|
|
("getchns: read and write both prioritised"));
|
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) {
|
|
|
|
flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR);
|
|
|
|
dsp_set_flags(dev, flags);
|
|
|
|
}
|
2001-06-16 21:25:10 +00:00
|
|
|
|
|
|
|
*rdch = dev->si_drv1;
|
|
|
|
*wrch = dev->si_drv2;
|
2001-08-23 11:30:52 +00:00
|
|
|
if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) {
|
2001-06-16 21:25:10 +00:00
|
|
|
if (prio) {
|
2001-08-23 11:30:52 +00:00
|
|
|
if (*rdch && flags & SD_F_PRIO_WR) {
|
2001-06-16 21:25:10 +00:00
|
|
|
dev->si_drv1 = NULL;
|
2001-08-23 11:30:52 +00:00
|
|
|
*rdch = pcm_getfakechan(d);
|
|
|
|
} else if (*wrch && flags & SD_F_PRIO_RD) {
|
2001-06-16 21:25:10 +00:00
|
|
|
dev->si_drv2 = NULL;
|
2001-08-23 11:30:52 +00:00
|
|
|
*wrch = pcm_getfakechan(d);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
pcm_getfakechan(d)->flags |= CHN_F_BUSY;
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
2001-08-23 11:30:52 +00:00
|
|
|
pcm_unlock(d);
|
2001-06-16 21:25:10 +00:00
|
|
|
|
2001-08-23 11:30:52 +00:00
|
|
|
if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
|
2001-06-16 21:25:10 +00:00
|
|
|
CHN_LOCK(*rdch);
|
2001-08-23 11:30:52 +00:00
|
|
|
if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
|
2001-06-16 21:25:10 +00:00
|
|
|
CHN_LOCK(*wrch);
|
|
|
|
|
1999-09-01 04:08:39 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
/* unlock specified channels */
|
1999-09-01 04:08:39 +00:00
|
|
|
static void
|
2004-06-16 09:47:26 +00:00
|
|
|
relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio)
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
2001-06-16 21:25:10 +00:00
|
|
|
struct snddev_info *d;
|
|
|
|
|
|
|
|
d = dsp_get_info(dev);
|
2001-08-23 11:30:52 +00:00
|
|
|
if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR))
|
2001-06-16 21:25:10 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
2001-08-23 11:30:52 +00:00
|
|
|
if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD))
|
2001-06-16 21:25:10 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
2001-08-23 11:30:52 +00:00
|
|
|
pcm_inprog(d, -1);
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
static int
|
2004-06-16 09:47:26 +00:00
|
|
|
dsp_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
2001-03-24 23:10:29 +00:00
|
|
|
struct pcm_channel *rdch, *wrch;
|
2001-06-16 21:25:10 +00:00
|
|
|
struct snddev_info *d;
|
2001-06-23 17:36:51 +00:00
|
|
|
intrmask_t s;
|
1999-09-01 04:08:39 +00:00
|
|
|
u_int32_t fmt;
|
2001-06-16 21:25:10 +00:00
|
|
|
int devtype;
|
2003-11-11 05:38:28 +00:00
|
|
|
int rdref;
|
|
|
|
int error;
|
2001-06-16 21:25:10 +00:00
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
s = spltty();
|
2001-06-16 21:25:10 +00:00
|
|
|
d = dsp_get_info(i_dev);
|
|
|
|
devtype = PCMDEV(i_dev);
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2001-06-14 13:31:30 +00:00
|
|
|
/* decide default format */
|
|
|
|
switch (devtype) {
|
|
|
|
case SND_DEV_DSP16:
|
|
|
|
fmt = AFMT_S16_LE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SND_DEV_DSP:
|
|
|
|
fmt = AFMT_U8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SND_DEV_AUDIO:
|
|
|
|
fmt = AFMT_MU_LAW;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SND_DEV_NORESET:
|
|
|
|
fmt = 0;
|
|
|
|
break;
|
|
|
|
|
2001-09-05 16:28:41 +00:00
|
|
|
case SND_DEV_DSPREC:
|
|
|
|
fmt = AFMT_U8;
|
|
|
|
if (mode & FWRITE) {
|
|
|
|
splx(s);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2001-06-14 13:31:30 +00:00
|
|
|
default:
|
|
|
|
panic("impossible devtype %d", devtype);
|
|
|
|
}
|
|
|
|
|
2003-11-11 05:38:28 +00:00
|
|
|
rdref = 0;
|
|
|
|
|
2001-06-14 13:31:30 +00:00
|
|
|
/* lock snddev so nobody else can monkey with it */
|
2001-08-23 11:30:52 +00:00
|
|
|
pcm_lock(d);
|
2002-08-28 15:19:30 +00:00
|
|
|
|
|
|
|
rdch = i_dev->si_drv1;
|
|
|
|
wrch = i_dev->si_drv2;
|
|
|
|
|
|
|
|
if ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && (rdch || wrch)) {
|
2003-08-15 02:31:13 +00:00
|
|
|
/* we're a simplex device and already open, no go */
|
2001-08-23 11:30:52 +00:00
|
|
|
pcm_unlock(d);
|
2001-06-23 17:36:51 +00:00
|
|
|
splx(s);
|
2001-05-27 17:22:00 +00:00
|
|
|
return EBUSY;
|
2001-06-14 13:31:30 +00:00
|
|
|
}
|
2001-03-13 18:43:22 +00:00
|
|
|
|
2002-08-28 15:19:30 +00:00
|
|
|
if (((flags & FREAD) && rdch) || ((flags & FWRITE) && wrch)) {
|
2003-08-15 02:31:13 +00:00
|
|
|
/*
|
|
|
|
* device already open in one or both directions that
|
|
|
|
* the opener wants; we can't handle this.
|
|
|
|
*/
|
2002-08-28 15:19:30 +00:00
|
|
|
pcm_unlock(d);
|
|
|
|
splx(s);
|
|
|
|
return EBUSY;
|
|
|
|
}
|
2001-05-27 17:22:00 +00:00
|
|
|
|
2003-08-15 02:31:13 +00:00
|
|
|
/*
|
|
|
|
* if we get here, the open request is valid- either:
|
|
|
|
* * we were previously not open
|
|
|
|
* * we were open for play xor record and the opener wants
|
|
|
|
* the non-open direction
|
|
|
|
*/
|
2001-06-16 21:25:10 +00:00
|
|
|
if (flags & FREAD) {
|
2001-06-14 13:31:30 +00:00
|
|
|
/* open for read */
|
2002-08-28 15:19:30 +00:00
|
|
|
if (devtype == SND_DEV_DSPREC)
|
|
|
|
rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, PCMCHAN(i_dev));
|
|
|
|
else
|
|
|
|
rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, -1);
|
|
|
|
if (!rdch) {
|
|
|
|
/* no channel available, exit */
|
2001-08-23 11:30:52 +00:00
|
|
|
pcm_unlock(d);
|
2001-06-23 17:36:51 +00:00
|
|
|
splx(s);
|
2001-05-27 17:22:00 +00:00
|
|
|
return EBUSY;
|
2001-06-14 13:31:30 +00:00
|
|
|
}
|
2002-08-28 15:19:30 +00:00
|
|
|
/* got a channel, already locked for us */
|
|
|
|
if (chn_reset(rdch, fmt)) {
|
|
|
|
pcm_chnrelease(rdch);
|
2003-02-11 16:58:54 +00:00
|
|
|
i_dev->si_drv1 = NULL;
|
2002-08-28 15:19:30 +00:00
|
|
|
pcm_unlock(d);
|
|
|
|
splx(s);
|
|
|
|
return ENODEV;
|
|
|
|
}
|
2003-11-11 05:38:28 +00:00
|
|
|
|
2002-08-28 15:19:30 +00:00
|
|
|
if (flags & O_NONBLOCK)
|
|
|
|
rdch->flags |= CHN_F_NBIO;
|
2001-09-14 20:26:03 +00:00
|
|
|
pcm_chnref(rdch, 1);
|
2001-06-14 13:31:30 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
2003-11-11 05:38:28 +00:00
|
|
|
rdref = 1;
|
|
|
|
/*
|
|
|
|
* Record channel created, ref'ed and unlocked
|
|
|
|
*/
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
2003-11-11 05:38:28 +00:00
|
|
|
|
2002-08-28 15:19:30 +00:00
|
|
|
if (flags & FWRITE) {
|
2003-11-11 05:38:28 +00:00
|
|
|
/* open for write */
|
|
|
|
wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1);
|
|
|
|
error = 0;
|
|
|
|
|
|
|
|
if (!wrch)
|
|
|
|
error = EBUSY; /* XXX Right return code? */
|
|
|
|
else if (chn_reset(wrch, fmt))
|
|
|
|
error = ENODEV;
|
|
|
|
|
|
|
|
if (error != 0) {
|
|
|
|
if (wrch) {
|
|
|
|
/*
|
|
|
|
* Free play channel
|
|
|
|
*/
|
|
|
|
pcm_chnrelease(wrch);
|
|
|
|
i_dev->si_drv2 = NULL;
|
2002-08-28 15:19:30 +00:00
|
|
|
}
|
2003-11-11 05:38:28 +00:00
|
|
|
if (rdref) {
|
|
|
|
/*
|
|
|
|
* Lock, deref and release previously created record channel
|
|
|
|
*/
|
|
|
|
CHN_LOCK(rdch);
|
|
|
|
pcm_chnref(rdch, -1);
|
|
|
|
pcm_chnrelease(rdch);
|
|
|
|
i_dev->si_drv1 = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcm_unlock(d);
|
|
|
|
splx(s);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & O_NONBLOCK)
|
|
|
|
wrch->flags |= CHN_F_NBIO;
|
|
|
|
pcm_chnref(wrch, 1);
|
|
|
|
CHN_UNLOCK(wrch);
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
2003-11-11 05:38:28 +00:00
|
|
|
|
|
|
|
i_dev->si_drv1 = rdch;
|
|
|
|
i_dev->si_drv2 = wrch;
|
|
|
|
|
2003-05-01 16:31:21 +00:00
|
|
|
pcm_unlock(d);
|
2001-06-23 17:36:51 +00:00
|
|
|
splx(s);
|
1999-09-01 04:08:39 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
static int
|
2004-06-16 09:47:26 +00:00
|
|
|
dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
2001-03-24 23:10:29 +00:00
|
|
|
struct pcm_channel *rdch, *wrch;
|
2001-06-16 21:25:10 +00:00
|
|
|
struct snddev_info *d;
|
2001-06-23 17:36:51 +00:00
|
|
|
intrmask_t s;
|
2003-11-11 05:38:28 +00:00
|
|
|
int refs;
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
s = spltty();
|
2001-06-16 21:25:10 +00:00
|
|
|
d = dsp_get_info(i_dev);
|
2001-08-23 11:30:52 +00:00
|
|
|
pcm_lock(d);
|
2001-06-16 21:25:10 +00:00
|
|
|
rdch = i_dev->si_drv1;
|
|
|
|
wrch = i_dev->si_drv2;
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2003-11-11 05:38:28 +00:00
|
|
|
refs = 0;
|
2001-06-14 13:31:30 +00:00
|
|
|
|
|
|
|
if (rdch) {
|
|
|
|
CHN_LOCK(rdch);
|
2003-11-11 05:38:28 +00:00
|
|
|
refs += pcm_chnref(rdch, -1);
|
|
|
|
CHN_UNLOCK(rdch);
|
2001-06-14 13:31:30 +00:00
|
|
|
}
|
|
|
|
if (wrch) {
|
|
|
|
CHN_LOCK(wrch);
|
2003-11-11 05:38:28 +00:00
|
|
|
refs += pcm_chnref(wrch, -1);
|
|
|
|
CHN_UNLOCK(wrch);
|
2001-06-14 13:31:30 +00:00
|
|
|
}
|
|
|
|
|
2003-11-11 05:38:28 +00:00
|
|
|
/*
|
|
|
|
* If there are no more references, release the channels.
|
|
|
|
*/
|
|
|
|
if ((rdch || wrch) && refs == 0) {
|
2001-06-14 13:31:30 +00:00
|
|
|
|
2003-11-11 05:38:28 +00:00
|
|
|
if (pcm_getfakechan(d))
|
|
|
|
pcm_getfakechan(d)->flags = 0;
|
2001-05-27 17:22:00 +00:00
|
|
|
|
2003-11-11 05:38:28 +00:00
|
|
|
i_dev->si_drv1 = NULL;
|
|
|
|
i_dev->si_drv2 = NULL;
|
2002-08-20 08:02:56 +00:00
|
|
|
|
2003-11-11 05:38:28 +00:00
|
|
|
dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT);
|
2001-05-27 17:22:00 +00:00
|
|
|
|
2003-11-11 05:38:28 +00:00
|
|
|
pcm_unlock(d);
|
2001-05-27 17:22:00 +00:00
|
|
|
|
2003-11-11 05:38:28 +00:00
|
|
|
if (rdch) {
|
|
|
|
CHN_LOCK(rdch);
|
|
|
|
chn_abort(rdch); /* won't sleep */
|
|
|
|
rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
|
|
|
|
chn_reset(rdch, 0);
|
|
|
|
pcm_chnrelease(rdch);
|
|
|
|
}
|
|
|
|
if (wrch) {
|
|
|
|
CHN_LOCK(wrch);
|
|
|
|
/*
|
|
|
|
* XXX: Maybe the right behaviour is to abort on non_block.
|
|
|
|
* It seems that mplayer flushes the audio queue by quickly
|
|
|
|
* closing and re-opening. In FBSD, there's a long pause
|
|
|
|
* while the audio queue flushes that I presume isn't there in
|
|
|
|
* linux.
|
|
|
|
*/
|
|
|
|
chn_flush(wrch); /* may sleep */
|
|
|
|
wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD);
|
|
|
|
chn_reset(wrch, 0);
|
|
|
|
pcm_chnrelease(wrch);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
pcm_unlock(d);
|
2001-06-23 17:36:51 +00:00
|
|
|
splx(s);
|
1999-09-01 04:08:39 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
static int
|
2004-06-16 09:47:26 +00:00
|
|
|
dsp_read(struct cdev *i_dev, struct uio *buf, int flag)
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
2001-03-24 23:10:29 +00:00
|
|
|
struct pcm_channel *rdch, *wrch;
|
2001-06-23 17:36:51 +00:00
|
|
|
intrmask_t s;
|
2001-03-24 23:10:29 +00:00
|
|
|
int ret;
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
s = spltty();
|
2001-06-16 21:25:10 +00:00
|
|
|
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD);
|
2001-06-14 13:31:30 +00:00
|
|
|
|
2000-01-10 07:05:15 +00:00
|
|
|
KASSERT(rdch, ("dsp_read: nonexistant channel"));
|
|
|
|
KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
|
2001-03-24 23:10:29 +00:00
|
|
|
|
|
|
|
if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
|
2001-06-16 21:25:10 +00:00
|
|
|
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
|
2001-06-23 17:36:51 +00:00
|
|
|
splx(s);
|
2001-03-24 23:10:29 +00:00
|
|
|
return EINVAL;
|
|
|
|
}
|
2000-04-17 17:06:47 +00:00
|
|
|
if (!(rdch->flags & CHN_F_RUNNING))
|
1999-09-28 21:43:35 +00:00
|
|
|
rdch->flags |= CHN_F_RUNNING;
|
2001-03-24 23:10:29 +00:00
|
|
|
ret = chn_read(rdch, buf);
|
2001-06-16 21:25:10 +00:00
|
|
|
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD);
|
2001-03-24 23:10:29 +00:00
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
splx(s);
|
2001-03-24 23:10:29 +00:00
|
|
|
return ret;
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
static int
|
2004-06-16 09:47:26 +00:00
|
|
|
dsp_write(struct cdev *i_dev, struct uio *buf, int flag)
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
2001-03-24 23:10:29 +00:00
|
|
|
struct pcm_channel *rdch, *wrch;
|
2001-06-23 17:36:51 +00:00
|
|
|
intrmask_t s;
|
2001-03-24 23:10:29 +00:00
|
|
|
int ret;
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
s = spltty();
|
2001-06-16 21:25:10 +00:00
|
|
|
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR);
|
2001-06-14 13:31:30 +00:00
|
|
|
|
1999-12-05 19:09:13 +00:00
|
|
|
KASSERT(wrch, ("dsp_write: nonexistant channel"));
|
|
|
|
KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
|
2001-03-24 23:10:29 +00:00
|
|
|
|
|
|
|
if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) {
|
2001-06-16 21:25:10 +00:00
|
|
|
relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
|
2001-06-23 17:36:51 +00:00
|
|
|
splx(s);
|
2001-03-24 23:10:29 +00:00
|
|
|
return EINVAL;
|
|
|
|
}
|
2000-04-17 17:06:47 +00:00
|
|
|
if (!(wrch->flags & CHN_F_RUNNING))
|
1999-09-28 21:43:35 +00:00
|
|
|
wrch->flags |= CHN_F_RUNNING;
|
2001-03-24 23:10:29 +00:00
|
|
|
ret = chn_write(wrch, buf);
|
2001-06-16 21:25:10 +00:00
|
|
|
relchns(i_dev, rdch, wrch, SD_F_PRIO_WR);
|
2001-03-24 23:10:29 +00:00
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
splx(s);
|
2001-03-24 23:10:29 +00:00
|
|
|
return ret;
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
static int
|
2004-06-16 09:47:26 +00:00
|
|
|
dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
2004-02-28 19:42:48 +00:00
|
|
|
struct pcm_channel *chn, *rdch, *wrch;
|
2001-06-16 21:25:10 +00:00
|
|
|
struct snddev_info *d;
|
2001-06-23 17:36:51 +00:00
|
|
|
intrmask_t s;
|
2001-06-16 21:25:10 +00:00
|
|
|
int kill;
|
|
|
|
int ret = 0, *arg_i = (int *)arg, tmp;
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
/*
|
|
|
|
* this is an evil hack to allow broken apps to perform mixer ioctls
|
|
|
|
* on dsp devices.
|
|
|
|
*/
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2004-01-17 10:37:11 +00:00
|
|
|
d = dsp_get_info(i_dev);
|
|
|
|
if (IOCGROUP(cmd) == 'M')
|
|
|
|
return mixer_ioctl(d->mixer_dev, cmd, arg, mode, td);
|
2001-06-16 21:25:10 +00:00
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
s = spltty();
|
2001-06-16 21:25:10 +00:00
|
|
|
getchns(i_dev, &rdch, &wrch, 0);
|
|
|
|
|
|
|
|
kill = 0;
|
2000-06-20 23:27:12 +00:00
|
|
|
if (wrch && (wrch->flags & CHN_F_DEAD))
|
2001-06-16 21:25:10 +00:00
|
|
|
kill |= 1;
|
|
|
|
if (rdch && (rdch->flags & CHN_F_DEAD))
|
|
|
|
kill |= 2;
|
|
|
|
if (kill == 3) {
|
|
|
|
relchns(i_dev, rdch, wrch, 0);
|
2001-06-23 17:36:51 +00:00
|
|
|
splx(s);
|
2000-06-20 23:27:12 +00:00
|
|
|
return EINVAL;
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
if (kill & 1)
|
|
|
|
wrch = NULL;
|
|
|
|
if (kill & 2)
|
|
|
|
rdch = NULL;
|
2004-01-28 08:02:15 +00:00
|
|
|
|
1999-09-01 04:08:39 +00:00
|
|
|
switch(cmd) {
|
2000-04-15 05:04:12 +00:00
|
|
|
#ifdef OLDPCM_IOCTL
|
1999-09-01 04:08:39 +00:00
|
|
|
/*
|
|
|
|
* we start with the new ioctl interface.
|
|
|
|
*/
|
|
|
|
case AIONWRITE: /* how many bytes can write ? */
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2001-03-24 23:10:29 +00:00
|
|
|
/*
|
|
|
|
if (wrch && wrch->bufhard.dl)
|
|
|
|
while (chn_wrfeed(wrch) == 0);
|
|
|
|
*/
|
|
|
|
*arg_i = wrch? sndbuf_getfree(wrch->bufsoft) : 0;
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
1999-09-01 04:08:39 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case AIOSSIZE: /* set the current blocksize */
|
|
|
|
{
|
|
|
|
struct snd_size *p = (struct snd_size *)arg;
|
2001-06-14 13:31:30 +00:00
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
p->play_size = 0;
|
|
|
|
p->rec_size = 0;
|
|
|
|
if (wrch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2000-05-27 14:40:17 +00:00
|
|
|
chn_setblocksize(wrch, 2, p->play_size);
|
2001-06-16 21:25:10 +00:00
|
|
|
p->play_size = sndbuf_getblksz(wrch->bufsoft);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
if (rdch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(rdch);
|
2000-05-27 14:40:17 +00:00
|
|
|
chn_setblocksize(rdch, 2, p->rec_size);
|
2001-06-16 21:25:10 +00:00
|
|
|
p->rec_size = sndbuf_getblksz(rdch->bufsoft);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
2001-06-16 21:25:10 +00:00
|
|
|
break;
|
1999-09-01 04:08:39 +00:00
|
|
|
case AIOGSIZE: /* get the current blocksize */
|
|
|
|
{
|
|
|
|
struct snd_size *p = (struct snd_size *)arg;
|
2001-06-14 13:31:30 +00:00
|
|
|
|
2004-02-28 19:42:48 +00:00
|
|
|
if (wrch) {
|
|
|
|
CHN_LOCK(wrch);
|
2001-03-24 23:10:29 +00:00
|
|
|
p->play_size = sndbuf_getblksz(wrch->bufsoft);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
|
|
|
}
|
|
|
|
if (rdch) {
|
|
|
|
CHN_LOCK(rdch);
|
2001-03-24 23:10:29 +00:00
|
|
|
p->rec_size = sndbuf_getblksz(rdch->bufsoft);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
|
|
|
}
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AIOSFMT:
|
2004-02-28 19:42:48 +00:00
|
|
|
case AIOGFMT:
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
|
|
|
snd_chan_param *p = (snd_chan_param *)arg;
|
2001-06-14 13:31:30 +00:00
|
|
|
|
1999-09-01 04:08:39 +00:00
|
|
|
if (wrch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
|
|
|
if (cmd == AIOSFMT) {
|
|
|
|
chn_setformat(wrch, p->play_format);
|
|
|
|
chn_setspeed(wrch, p->play_rate);
|
|
|
|
}
|
|
|
|
p->play_rate = wrch->speed;
|
|
|
|
p->play_format = wrch->format;
|
|
|
|
CHN_UNLOCK(wrch);
|
|
|
|
} else {
|
|
|
|
p->play_rate = 0;
|
|
|
|
p->play_format = 0;
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
if (rdch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(rdch);
|
|
|
|
if (cmd == AIOSFMT) {
|
|
|
|
chn_setformat(rdch, p->rec_format);
|
|
|
|
chn_setspeed(rdch, p->rec_rate);
|
|
|
|
}
|
|
|
|
p->rec_rate = rdch->speed;
|
|
|
|
p->rec_format = rdch->format;
|
|
|
|
CHN_UNLOCK(rdch);
|
|
|
|
} else {
|
|
|
|
p->rec_rate = 0;
|
|
|
|
p->rec_format = 0;
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AIOGCAP: /* get capabilities */
|
|
|
|
{
|
|
|
|
snd_capabilities *p = (snd_capabilities *)arg;
|
2001-03-24 23:10:29 +00:00
|
|
|
struct pcmchan_caps *pcaps = NULL, *rcaps = NULL;
|
2004-06-16 09:47:26 +00:00
|
|
|
struct cdev *pdev;
|
2001-06-14 13:31:30 +00:00
|
|
|
|
2004-02-28 19:42:48 +00:00
|
|
|
if (rdch) {
|
|
|
|
CHN_LOCK(rdch);
|
2001-06-14 13:31:30 +00:00
|
|
|
rcaps = chn_getcaps(rdch);
|
2004-02-28 19:42:48 +00:00
|
|
|
}
|
|
|
|
if (wrch) {
|
|
|
|
CHN_LOCK(wrch);
|
2001-06-14 13:31:30 +00:00
|
|
|
pcaps = chn_getcaps(wrch);
|
2004-02-28 19:42:48 +00:00
|
|
|
}
|
1999-09-01 04:08:39 +00:00
|
|
|
p->rate_min = max(rcaps? rcaps->minspeed : 0,
|
|
|
|
pcaps? pcaps->minspeed : 0);
|
|
|
|
p->rate_max = min(rcaps? rcaps->maxspeed : 1000000,
|
|
|
|
pcaps? pcaps->maxspeed : 1000000);
|
2001-03-24 23:10:29 +00:00
|
|
|
p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000,
|
|
|
|
wrch? sndbuf_getsize(wrch->bufsoft) : 1000000);
|
1999-09-01 04:08:39 +00:00
|
|
|
/* XXX bad on sb16 */
|
2000-08-20 22:18:56 +00:00
|
|
|
p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) &
|
|
|
|
(wrch? chn_getformats(wrch) : 0xffffffff);
|
2000-06-07 11:51:30 +00:00
|
|
|
if (rdch && wrch)
|
2001-08-23 11:30:52 +00:00
|
|
|
p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX;
|
2004-01-17 10:37:11 +00:00
|
|
|
pdev = d->mixer_dev;
|
1999-09-01 04:08:39 +00:00
|
|
|
p->mixers = 1; /* default: one mixer */
|
2001-06-16 21:25:10 +00:00
|
|
|
p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0;
|
1999-09-01 04:08:39 +00:00
|
|
|
p->left = p->right = 100;
|
2004-02-28 19:42:48 +00:00
|
|
|
if (rdch)
|
|
|
|
CHN_UNLOCK(rdch);
|
|
|
|
if (wrch)
|
|
|
|
CHN_UNLOCK(wrch);
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AIOSTOP:
|
2004-02-28 19:42:48 +00:00
|
|
|
if (*arg_i == AIOSYNC_PLAY && wrch) {
|
|
|
|
CHN_LOCK(wrch);
|
update code dealing with snd_dbuf objects to do so using a functional interface
modify chn_setblocksize() to pick a default soft-blocksize appropriate to the
sample rate and format in use. it will aim for a power of two size small
enough to generate block sizes of at most 20ms. it will also set the
hard-blocksize taking into account rate/format conversions in use.
update drivers to implement setblocksize correctly:
updated, tested: sb16, emu10k1, maestro, solo
updated, untested: ad1816, ess, mss, sb8, csa
not updated: ds1, es137x, fm801, neomagic, t4dwave, via82c686
i lack hardware to test: ad1816, csa, fm801, neomagic
others will be updated/tested in the next few days.
2000-12-23 03:16:13 +00:00
|
|
|
*arg_i = chn_abort(wrch);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
|
|
|
} else if (*arg_i == AIOSYNC_CAPTURE && rdch) {
|
|
|
|
CHN_LOCK(rdch);
|
update code dealing with snd_dbuf objects to do so using a functional interface
modify chn_setblocksize() to pick a default soft-blocksize appropriate to the
sample rate and format in use. it will aim for a power of two size small
enough to generate block sizes of at most 20ms. it will also set the
hard-blocksize taking into account rate/format conversions in use.
update drivers to implement setblocksize correctly:
updated, tested: sb16, emu10k1, maestro, solo
updated, untested: ad1816, ess, mss, sb8, csa
not updated: ds1, es137x, fm801, neomagic, t4dwave, via82c686
i lack hardware to test: ad1816, csa, fm801, neomagic
others will be updated/tested in the next few days.
2000-12-23 03:16:13 +00:00
|
|
|
*arg_i = chn_abort(rdch);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
|
|
|
} else {
|
1999-09-01 04:08:39 +00:00
|
|
|
printf("AIOSTOP: bad channel 0x%x\n", *arg_i);
|
|
|
|
*arg_i = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AIOSYNC:
|
|
|
|
printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
|
|
|
|
((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos);
|
|
|
|
break;
|
2000-04-23 18:09:18 +00:00
|
|
|
#endif
|
1999-09-19 09:53:57 +00:00
|
|
|
/*
|
|
|
|
* here follow the standard ioctls (filio.h etc.)
|
|
|
|
*/
|
1999-09-01 04:08:39 +00:00
|
|
|
case FIONREAD: /* get # bytes to read */
|
2004-02-28 19:42:48 +00:00
|
|
|
if (rdch) {
|
|
|
|
CHN_LOCK(rdch);
|
|
|
|
/* if (rdch && rdch->bufhard.dl)
|
|
|
|
while (chn_rdfeed(rdch) == 0);
|
|
|
|
*/
|
|
|
|
*arg_i = sndbuf_getready(rdch->bufsoft);
|
|
|
|
CHN_UNLOCK(rdch);
|
|
|
|
} else
|
|
|
|
*arg_i = 0;
|
1999-09-01 04:08:39 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FIOASYNC: /*set/clear async i/o */
|
|
|
|
DEB( printf("FIOASYNC\n") ; )
|
|
|
|
break;
|
2000-04-23 18:09:18 +00:00
|
|
|
|
1999-09-01 04:08:39 +00:00
|
|
|
case SNDCTL_DSP_NONBLOCK:
|
|
|
|
case FIONBIO: /* set/clear non-blocking i/o */
|
2004-02-28 19:42:48 +00:00
|
|
|
if (rdch) {
|
|
|
|
CHN_LOCK(rdch);
|
|
|
|
if (*arg_i)
|
2001-06-14 13:31:30 +00:00
|
|
|
rdch->flags |= CHN_F_NBIO;
|
2004-02-28 19:42:48 +00:00
|
|
|
else
|
|
|
|
rdch->flags &= ~CHN_F_NBIO;
|
|
|
|
CHN_UNLOCK(rdch);
|
|
|
|
}
|
|
|
|
if (wrch) {
|
|
|
|
CHN_LOCK(wrch);
|
|
|
|
if (*arg_i)
|
2001-06-14 13:31:30 +00:00
|
|
|
wrch->flags |= CHN_F_NBIO;
|
2004-02-28 19:42:48 +00:00
|
|
|
else
|
|
|
|
wrch->flags &= ~CHN_F_NBIO;
|
|
|
|
CHN_UNLOCK(wrch);
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
1999-09-19 09:53:57 +00:00
|
|
|
* Finally, here is the linux-compatible ioctl interface
|
|
|
|
*/
|
|
|
|
#define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int)
|
1999-09-01 04:08:39 +00:00
|
|
|
case THE_REAL_SNDCTL_DSP_GETBLKSIZE:
|
|
|
|
case SNDCTL_DSP_GETBLKSIZE:
|
2004-02-28 19:42:48 +00:00
|
|
|
chn = wrch ? wrch : rdch;
|
|
|
|
CHN_LOCK(chn);
|
|
|
|
*arg_i = sndbuf_getblksz(chn->bufsoft);
|
|
|
|
CHN_UNLOCK(chn);
|
1999-09-01 04:08:39 +00:00
|
|
|
break ;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_SETBLKSIZE:
|
2000-04-15 05:04:12 +00:00
|
|
|
RANGE(*arg_i, 16, 65536);
|
2004-02-28 19:42:48 +00:00
|
|
|
if (wrch) {
|
|
|
|
CHN_LOCK(wrch);
|
2001-06-14 13:31:30 +00:00
|
|
|
chn_setblocksize(wrch, 2, *arg_i);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
|
|
|
}
|
|
|
|
if (rdch) {
|
|
|
|
CHN_LOCK(rdch);
|
2001-06-14 13:31:30 +00:00
|
|
|
chn_setblocksize(rdch, 2, *arg_i);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
|
|
|
}
|
1999-09-01 04:08:39 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_RESET:
|
|
|
|
DEB(printf("dsp reset\n"));
|
2004-01-20 05:30:09 +00:00
|
|
|
if (wrch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2001-03-24 23:10:29 +00:00
|
|
|
chn_abort(wrch);
|
2004-01-20 05:30:09 +00:00
|
|
|
chn_resetbuf(wrch);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
2004-01-20 05:30:09 +00:00
|
|
|
}
|
|
|
|
if (rdch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(rdch);
|
2001-03-24 23:10:29 +00:00
|
|
|
chn_abort(rdch);
|
2004-01-20 05:30:09 +00:00
|
|
|
chn_resetbuf(rdch);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
2004-01-20 05:30:09 +00:00
|
|
|
}
|
1999-09-01 04:08:39 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_SYNC:
|
1999-09-19 09:53:57 +00:00
|
|
|
DEB(printf("dsp sync\n"));
|
2001-06-16 21:25:10 +00:00
|
|
|
/* chn_sync may sleep */
|
2004-02-28 19:42:48 +00:00
|
|
|
if (wrch) {
|
|
|
|
CHN_LOCK(wrch);
|
2001-06-16 21:25:10 +00:00
|
|
|
chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
|
|
|
}
|
1999-09-01 04:08:39 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_SPEED:
|
2001-06-16 21:25:10 +00:00
|
|
|
/* chn_setspeed may sleep */
|
|
|
|
tmp = 0;
|
|
|
|
if (wrch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2000-05-26 21:55:13 +00:00
|
|
|
ret = chn_setspeed(wrch, *arg_i);
|
2001-06-16 21:25:10 +00:00
|
|
|
tmp = wrch->speed;
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
if (rdch && ret == 0) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(rdch);
|
2000-05-26 21:55:13 +00:00
|
|
|
ret = chn_setspeed(rdch, *arg_i);
|
2001-06-16 21:25:10 +00:00
|
|
|
if (tmp == 0)
|
|
|
|
tmp = rdch->speed;
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
*arg_i = tmp;
|
|
|
|
break;
|
1999-09-01 04:08:39 +00:00
|
|
|
|
|
|
|
case SOUND_PCM_READ_RATE:
|
2004-02-28 19:42:48 +00:00
|
|
|
chn = wrch ? wrch : rdch;
|
|
|
|
CHN_LOCK(chn);
|
|
|
|
*arg_i = chn->speed;
|
|
|
|
CHN_UNLOCK(chn);
|
1999-09-01 04:08:39 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_STEREO:
|
2001-06-16 21:25:10 +00:00
|
|
|
tmp = -1;
|
|
|
|
*arg_i = (*arg_i)? AFMT_STEREO : 0;
|
|
|
|
if (wrch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2001-06-16 21:25:10 +00:00
|
|
|
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
|
|
|
|
tmp = (wrch->format & AFMT_STEREO)? 1 : 0;
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
if (rdch && ret == 0) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(rdch);
|
2001-06-16 21:25:10 +00:00
|
|
|
ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
|
|
|
|
if (tmp == -1)
|
|
|
|
tmp = (rdch->format & AFMT_STEREO)? 1 : 0;
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
*arg_i = tmp;
|
1999-09-01 04:08:39 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SOUND_PCM_WRITE_CHANNELS:
|
2000-05-26 21:55:13 +00:00
|
|
|
/* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */
|
2001-08-23 12:21:12 +00:00
|
|
|
if (*arg_i != 0) {
|
2001-06-16 21:25:10 +00:00
|
|
|
tmp = 0;
|
2001-08-23 12:21:12 +00:00
|
|
|
*arg_i = (*arg_i != 1)? AFMT_STEREO : 0;
|
2001-06-16 21:25:10 +00:00
|
|
|
if (wrch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2001-06-16 21:25:10 +00:00
|
|
|
ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i);
|
|
|
|
tmp = (wrch->format & AFMT_STEREO)? 2 : 1;
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
if (rdch && ret == 0) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(rdch);
|
2001-06-16 21:25:10 +00:00
|
|
|
ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i);
|
|
|
|
if (tmp == 0)
|
|
|
|
tmp = (rdch->format & AFMT_STEREO)? 2 : 1;
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
*arg_i = tmp;
|
2004-02-28 19:42:48 +00:00
|
|
|
} else {
|
|
|
|
chn = wrch ? wrch : rdch;
|
|
|
|
CHN_LOCK(chn);
|
|
|
|
*arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
|
|
|
|
CHN_UNLOCK(chn);
|
|
|
|
}
|
2000-05-26 21:55:13 +00:00
|
|
|
break;
|
1999-09-01 04:08:39 +00:00
|
|
|
|
|
|
|
case SOUND_PCM_READ_CHANNELS:
|
2004-02-28 19:42:48 +00:00
|
|
|
chn = wrch ? wrch : rdch;
|
|
|
|
CHN_LOCK(chn);
|
|
|
|
*arg_i = (chn->format & AFMT_STEREO) ? 2 : 1;
|
|
|
|
CHN_UNLOCK(chn);
|
1999-09-01 04:08:39 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */
|
2004-02-28 19:42:48 +00:00
|
|
|
chn = wrch ? wrch : rdch;
|
|
|
|
CHN_LOCK(chn);
|
|
|
|
*arg_i = chn_getformats(chn);
|
|
|
|
CHN_UNLOCK(chn);
|
1999-09-01 04:08:39 +00:00
|
|
|
break ;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_SETFMT: /* sets _one_ format */
|
2000-10-26 01:34:54 +00:00
|
|
|
if ((*arg_i != AFMT_QUERY)) {
|
2001-06-16 21:25:10 +00:00
|
|
|
tmp = 0;
|
|
|
|
if (wrch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2000-10-26 01:34:54 +00:00
|
|
|
ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO));
|
2001-06-16 21:25:10 +00:00
|
|
|
tmp = wrch->format & ~AFMT_STEREO;
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
if (rdch && ret == 0) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(rdch);
|
2000-10-26 01:34:54 +00:00
|
|
|
ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO));
|
2001-06-16 21:25:10 +00:00
|
|
|
if (tmp == 0)
|
|
|
|
tmp = rdch->format & ~AFMT_STEREO;
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
*arg_i = tmp;
|
2004-02-28 19:42:48 +00:00
|
|
|
} else {
|
|
|
|
chn = wrch ? wrch : rdch;
|
|
|
|
CHN_LOCK(chn);
|
|
|
|
*arg_i = chn->format & ~AFMT_STEREO;
|
|
|
|
CHN_UNLOCK(chn);
|
|
|
|
}
|
1999-09-01 04:08:39 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_SETFRAGMENT:
|
|
|
|
DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg));
|
|
|
|
{
|
2000-04-15 05:04:12 +00:00
|
|
|
u_int32_t fragln = (*arg_i) & 0x0000ffff;
|
|
|
|
u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16;
|
|
|
|
u_int32_t fragsz;
|
|
|
|
|
|
|
|
RANGE(fragln, 4, 16);
|
|
|
|
fragsz = 1 << fragln;
|
|
|
|
|
|
|
|
if (maxfrags == 0)
|
|
|
|
maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
|
2003-06-26 13:13:18 +00:00
|
|
|
if (maxfrags < 2)
|
|
|
|
maxfrags = 2;
|
2000-04-15 05:04:12 +00:00
|
|
|
if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE)
|
|
|
|
maxfrags = CHN_2NDBUFMAXSIZE / fragsz;
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2000-04-23 18:09:18 +00:00
|
|
|
DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz));
|
2001-06-16 21:25:10 +00:00
|
|
|
if (rdch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(rdch);
|
2000-04-15 05:04:12 +00:00
|
|
|
ret = chn_setblocksize(rdch, maxfrags, fragsz);
|
2001-06-16 21:25:10 +00:00
|
|
|
maxfrags = sndbuf_getblkcnt(rdch->bufsoft);
|
|
|
|
fragsz = sndbuf_getblksz(rdch->bufsoft);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
if (wrch && ret == 0) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2000-04-15 05:04:12 +00:00
|
|
|
ret = chn_setblocksize(wrch, maxfrags, fragsz);
|
2001-06-16 21:25:10 +00:00
|
|
|
maxfrags = sndbuf_getblkcnt(wrch->bufsoft);
|
|
|
|
fragsz = sndbuf_getblksz(wrch->bufsoft);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
2000-04-15 05:04:12 +00:00
|
|
|
|
2000-05-26 21:55:13 +00:00
|
|
|
fragln = 0;
|
|
|
|
while (fragsz > 1) {
|
|
|
|
fragln++;
|
|
|
|
fragsz >>= 1;
|
|
|
|
}
|
2001-06-16 21:25:10 +00:00
|
|
|
*arg_i = (maxfrags << 16) | fragln;
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2001-10-24 12:40:54 +00:00
|
|
|
case SNDCTL_DSP_GETISPACE:
|
1999-12-29 03:46:54 +00:00
|
|
|
/* return the size of data available in the input queue */
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
|
|
|
audio_buf_info *a = (audio_buf_info *)arg;
|
|
|
|
if (rdch) {
|
2001-03-24 23:10:29 +00:00
|
|
|
struct snd_dbuf *bs = rdch->bufsoft;
|
|
|
|
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(rdch);
|
2001-10-24 12:40:54 +00:00
|
|
|
a->bytes = sndbuf_getready(bs);
|
2001-03-24 23:10:29 +00:00
|
|
|
a->fragments = a->bytes / sndbuf_getblksz(bs);
|
|
|
|
a->fragstotal = sndbuf_getblkcnt(bs);
|
|
|
|
a->fragsize = sndbuf_getblksz(bs);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_GETOSPACE:
|
|
|
|
/* return space available in the output queue */
|
|
|
|
{
|
|
|
|
audio_buf_info *a = (audio_buf_info *)arg;
|
|
|
|
if (wrch) {
|
2001-03-24 23:10:29 +00:00
|
|
|
struct snd_dbuf *bs = wrch->bufsoft;
|
|
|
|
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2001-03-24 23:10:29 +00:00
|
|
|
chn_wrupdate(wrch);
|
|
|
|
a->bytes = sndbuf_getfree(bs);
|
|
|
|
a->fragments = a->bytes / sndbuf_getblksz(bs);
|
|
|
|
a->fragstotal = sndbuf_getblkcnt(bs);
|
|
|
|
a->fragsize = sndbuf_getblksz(bs);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_GETIPTR:
|
|
|
|
{
|
|
|
|
count_info *a = (count_info *)arg;
|
|
|
|
if (rdch) {
|
2001-03-24 23:10:29 +00:00
|
|
|
struct snd_dbuf *bs = rdch->bufsoft;
|
|
|
|
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(rdch);
|
2001-03-24 23:10:29 +00:00
|
|
|
chn_rdupdate(rdch);
|
|
|
|
a->bytes = sndbuf_gettotal(bs);
|
|
|
|
a->blocks = sndbuf_getblocks(bs) - rdch->blocks;
|
|
|
|
a->ptr = sndbuf_getreadyptr(bs);
|
|
|
|
rdch->blocks = sndbuf_getblocks(bs);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
2001-06-14 13:31:30 +00:00
|
|
|
} else
|
|
|
|
ret = EINVAL;
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_GETOPTR:
|
|
|
|
{
|
|
|
|
count_info *a = (count_info *)arg;
|
|
|
|
if (wrch) {
|
2001-03-24 23:10:29 +00:00
|
|
|
struct snd_dbuf *bs = wrch->bufsoft;
|
|
|
|
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2001-03-24 23:10:29 +00:00
|
|
|
chn_wrupdate(wrch);
|
|
|
|
a->bytes = sndbuf_gettotal(bs);
|
|
|
|
a->blocks = sndbuf_getblocks(bs) - wrch->blocks;
|
|
|
|
a->ptr = sndbuf_getreadyptr(bs);
|
|
|
|
wrch->blocks = sndbuf_getblocks(bs);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
2001-06-14 13:31:30 +00:00
|
|
|
} else
|
|
|
|
ret = EINVAL;
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_GETCAPS:
|
|
|
|
*arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
|
2001-08-23 11:30:52 +00:00
|
|
|
if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX))
|
1999-09-01 04:08:39 +00:00
|
|
|
*arg_i |= DSP_CAP_DUPLEX;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOUND_PCM_READ_BITS:
|
2004-02-28 19:42:48 +00:00
|
|
|
chn = wrch ? wrch : rdch;
|
|
|
|
CHN_LOCK(chn);
|
Whats New:
1. Support wide range sampling rate, as low as 1hz up to int32 max
(which is, insane) through new feeder_rate, multiple precisions
choice (32/64 bit converter). This is indeed, quite insane, but it
does give us more room and flexibility. Plenty sysctl options to
adjust resampling characteristics.
2. Support 24/32 bit pcm format conversion through new, much improved,
simplified and optimized feeder_fmt.
Changes:
1. buffer.c / dsp.c / sound.h
* Support for 24/32 AFMT.
2. feeder_rate.c
* New implementation of sampling rate conversion with 32/64 bit
precision, 1 - int32max hz (which is, ridiculous, yet very
addictive). Much improved / smarter buffer management to not
cause any missing samples at the end of conversion process
* Tunable sysctls for various aspect:
hw.snd.feeder_rate_ratemin - minimum allowable sampling rate
(default to 4000)
hw.snd.feeder_rate_ratemax - maximum allowable sampling rate
(default to 1102500)
hw.snd.feeder_rate_buffersize - conversion buffer size
(default to 8192)
hw.snd.feeder_rate_scaling - scaling / conversion method
(please refer to the source for explaination). Default to
previous implementation type.
3. feeder_fmt.c / sound.h
* New implementation, support for 24/32bit conversion, optimized,
and simplified. Few routines has been removed (8 to xlaw, 16 to
8). It just doesn't make sense.
4. channel.c
* Support for 24/32 AFMT
* Fix wrong xruns increment, causing incorrect underruns statistic
while using vchans.
5. vchan.c
* Support for 24/32 AFMT
* Proper speed / rate detection especially for fixed rate ac97.
User can override it using kernel hint:
hint.pcm.<unit>.vchanrate="xxxx".
Notes / Issues:
* Virtual Channels (vchans)
Enabling vchans can really, really help to solve overrun
issues. This is quite understandable, because it operates
entirely within its own buffering system without relying on
hardware interrupt / state. Even if you don't need vchan,
just enable single channel can help much. Few soundcards
(notably via8233x, sblive, possibly others) have their own
hardware multi channel, and this is unfortunately beyond
vchan reachability.
* The arrival of 24/32 also come with a price. Applications
that can do 24/32bit playback need to be recompiled (notably
mplayer). Use (recompiled) mplayer to experiment / test /
debug this various format using -af format=fmt. Note that
24bit seeking in mplayer is a little bit broken, sometimes
can cause silence or loud static noise. Pausing / seeking
few times can solve this problem.
You don't have to rebuild world entirely for this. Simply
copy /usr/src/sys/sys/soundcard.h to
/usr/include/sys/soundcard.h would suffice. Few drivers also
need recompilation, and this can be done via
/usr/src/sys/modules/sound/.
Support for 24bit hardware playback is beyond the scope of
this changes. That would require spessific hardware driver
changes.
* Don't expect playing 9999999999hz is a wise decision. Be
reasonable. The new feeder_rate implemention provide
flexibility, not insanity. You can easily chew up your CPU
with this kind of mind instability. Please use proper
mosquito repellent device for this obvious cracked brain
attempt. As for testing purposes, you can use (again)
mplayer to generate / play with different sampling rate. Use
something like "mplayer -af resample=192000:0:0 <files>".
Submitted by: Ariff Abdullah <skywizard@MyBSD.org.my>
Tested by: multimedia@
2005-07-31 16:16:22 +00:00
|
|
|
if (chn->format & AFMT_8BIT)
|
|
|
|
*arg_i = 8;
|
|
|
|
else if (chn->format & AFMT_16BIT)
|
|
|
|
*arg_i = 16;
|
|
|
|
else if (chn->format & AFMT_24BIT)
|
|
|
|
*arg_i = 24;
|
|
|
|
else if (chn->format & AFMT_32BIT)
|
|
|
|
*arg_i = 32;
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(chn);
|
1999-09-01 04:08:39 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_SETTRIGGER:
|
|
|
|
if (rdch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(rdch);
|
2000-04-23 18:09:18 +00:00
|
|
|
rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
|
2001-08-23 11:58:38 +00:00
|
|
|
if (*arg_i & PCM_ENABLE_INPUT)
|
2001-03-24 23:10:29 +00:00
|
|
|
chn_start(rdch, 1);
|
2001-08-23 11:58:38 +00:00
|
|
|
else
|
2000-04-23 18:09:18 +00:00
|
|
|
rdch->flags |= CHN_F_NOTRIGGER;
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(rdch);
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
if (wrch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2000-04-23 18:09:18 +00:00
|
|
|
wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER);
|
2001-08-23 11:58:38 +00:00
|
|
|
if (*arg_i & PCM_ENABLE_OUTPUT)
|
2001-03-24 23:10:29 +00:00
|
|
|
chn_start(wrch, 1);
|
2001-08-23 11:58:38 +00:00
|
|
|
else
|
2000-04-23 18:09:18 +00:00
|
|
|
wrch->flags |= CHN_F_NOTRIGGER;
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDCTL_DSP_GETTRIGGER:
|
|
|
|
*arg_i = 0;
|
2004-02-28 19:42:48 +00:00
|
|
|
if (wrch) {
|
|
|
|
CHN_LOCK(wrch);
|
|
|
|
if (wrch->flags & CHN_F_TRIGGERED)
|
|
|
|
*arg_i |= PCM_ENABLE_OUTPUT;
|
|
|
|
CHN_UNLOCK(wrch);
|
|
|
|
}
|
|
|
|
if (rdch) {
|
|
|
|
CHN_LOCK(rdch);
|
|
|
|
if (rdch->flags & CHN_F_TRIGGERED)
|
|
|
|
*arg_i |= PCM_ENABLE_INPUT;
|
|
|
|
CHN_UNLOCK(rdch);
|
|
|
|
}
|
1999-09-01 04:08:39 +00:00
|
|
|
break;
|
|
|
|
|
1999-12-29 03:46:54 +00:00
|
|
|
case SNDCTL_DSP_GETODELAY:
|
|
|
|
if (wrch) {
|
2001-03-24 23:10:29 +00:00
|
|
|
struct snd_dbuf *b = wrch->bufhard;
|
|
|
|
struct snd_dbuf *bs = wrch->bufsoft;
|
|
|
|
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2001-03-24 23:10:29 +00:00
|
|
|
chn_wrupdate(wrch);
|
|
|
|
*arg_i = sndbuf_getready(b) + sndbuf_getready(bs);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
1999-12-29 03:46:54 +00:00
|
|
|
} else
|
|
|
|
ret = EINVAL;
|
|
|
|
break;
|
2000-10-27 22:20:45 +00:00
|
|
|
|
2000-10-26 01:34:54 +00:00
|
|
|
case SNDCTL_DSP_POST:
|
|
|
|
if (wrch) {
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_LOCK(wrch);
|
2000-10-26 01:34:54 +00:00
|
|
|
wrch->flags &= ~CHN_F_NOTRIGGER;
|
|
|
|
chn_start(wrch, 1);
|
2004-02-28 19:42:48 +00:00
|
|
|
CHN_UNLOCK(wrch);
|
2000-10-26 01:34:54 +00:00
|
|
|
}
|
|
|
|
break;
|
2000-10-27 22:20:45 +00:00
|
|
|
|
2004-07-02 15:31:44 +00:00
|
|
|
case SNDCTL_DSP_SETDUPLEX:
|
|
|
|
/*
|
|
|
|
* switch to full-duplex mode if card is in half-duplex
|
|
|
|
* mode and is able to work in full-duplex mode
|
|
|
|
*/
|
|
|
|
if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX))
|
|
|
|
dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX);
|
|
|
|
break;
|
|
|
|
|
1999-09-01 04:08:39 +00:00
|
|
|
case SNDCTL_DSP_MAPINBUF:
|
|
|
|
case SNDCTL_DSP_MAPOUTBUF:
|
|
|
|
case SNDCTL_DSP_SETSYNCRO:
|
|
|
|
/* undocumented */
|
|
|
|
|
2001-03-24 23:10:29 +00:00
|
|
|
case SNDCTL_DSP_SUBDIVIDE:
|
1999-09-01 04:08:39 +00:00
|
|
|
case SOUND_PCM_WRITE_FILTER:
|
|
|
|
case SOUND_PCM_READ_FILTER:
|
|
|
|
/* dunno what these do, don't sound important */
|
2004-07-02 15:31:44 +00:00
|
|
|
|
1999-09-01 04:08:39 +00:00
|
|
|
default:
|
2002-01-25 04:14:12 +00:00
|
|
|
DEB(printf("default ioctl fn 0x%08lx fail\n", cmd));
|
1999-09-01 04:08:39 +00:00
|
|
|
ret = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2001-06-16 21:25:10 +00:00
|
|
|
relchns(i_dev, rdch, wrch, 0);
|
2001-06-14 13:31:30 +00:00
|
|
|
splx(s);
|
1999-09-01 04:08:39 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
static int
|
2004-06-16 09:47:26 +00:00
|
|
|
dsp_poll(struct cdev *i_dev, int events, struct thread *td)
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
2001-03-24 23:10:29 +00:00
|
|
|
struct pcm_channel *wrch = NULL, *rdch = NULL;
|
2001-06-23 17:36:51 +00:00
|
|
|
intrmask_t s;
|
|
|
|
int ret, e;
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
s = spltty();
|
2001-06-14 13:31:30 +00:00
|
|
|
ret = 0;
|
2001-06-16 21:25:10 +00:00
|
|
|
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
|
2001-03-24 23:10:29 +00:00
|
|
|
|
2001-06-14 13:31:30 +00:00
|
|
|
if (wrch) {
|
|
|
|
e = (events & (POLLOUT | POLLWRNORM));
|
|
|
|
if (e)
|
2001-09-12 08:38:13 +00:00
|
|
|
ret |= chn_poll(wrch, e, td);
|
2001-06-14 13:31:30 +00:00
|
|
|
}
|
|
|
|
if (rdch) {
|
|
|
|
e = (events & (POLLIN | POLLRDNORM));
|
|
|
|
if (e)
|
2001-09-12 08:38:13 +00:00
|
|
|
ret |= chn_poll(rdch, e, td);
|
2001-06-14 13:31:30 +00:00
|
|
|
}
|
2001-06-16 21:25:10 +00:00
|
|
|
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
|
2001-03-24 23:10:29 +00:00
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
splx(s);
|
1999-09-01 04:08:39 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
static int
|
2004-06-16 09:47:26 +00:00
|
|
|
dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
|
1999-09-01 04:08:39 +00:00
|
|
|
{
|
2001-06-16 21:25:10 +00:00
|
|
|
struct pcm_channel *wrch = NULL, *rdch = NULL, *c;
|
2001-06-23 17:36:51 +00:00
|
|
|
intrmask_t s;
|
1999-09-01 04:08:39 +00:00
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
if (nprot & PROT_EXEC)
|
|
|
|
return -1;
|
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
s = spltty();
|
2001-06-16 21:25:10 +00:00
|
|
|
getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
|
2001-03-24 23:10:29 +00:00
|
|
|
#if 0
|
|
|
|
/*
|
2001-06-16 21:25:10 +00:00
|
|
|
* XXX the linux api uses the nprot to select read/write buffer
|
|
|
|
* our vm system doesn't allow this, so force write buffer
|
2001-03-24 23:10:29 +00:00
|
|
|
*/
|
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
if (wrch && (nprot & PROT_WRITE)) {
|
2000-05-26 21:55:13 +00:00
|
|
|
c = wrch;
|
2001-06-23 17:36:51 +00:00
|
|
|
} else if (rdch && (nprot & PROT_READ)) {
|
2000-05-26 21:55:13 +00:00
|
|
|
c = rdch;
|
2001-06-23 17:36:51 +00:00
|
|
|
} else {
|
|
|
|
splx(s);
|
2001-06-16 21:25:10 +00:00
|
|
|
return -1;
|
2001-06-23 17:36:51 +00:00
|
|
|
}
|
2001-03-24 23:10:29 +00:00
|
|
|
#else
|
|
|
|
c = wrch;
|
|
|
|
#endif
|
|
|
|
|
2001-06-16 21:25:10 +00:00
|
|
|
if (c == NULL) {
|
|
|
|
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
|
2001-06-23 17:36:51 +00:00
|
|
|
splx(s);
|
2000-05-26 21:55:13 +00:00
|
|
|
return -1;
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
|
2002-01-23 04:32:18 +00:00
|
|
|
if (offset >= sndbuf_getsize(c->bufsoft)) {
|
|
|
|
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
|
|
|
|
splx(s);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2001-07-01 18:57:16 +00:00
|
|
|
if (!(c->flags & CHN_F_MAPPED))
|
|
|
|
c->flags |= CHN_F_MAPPED;
|
|
|
|
|
2003-02-25 03:21:22 +00:00
|
|
|
*paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset));
|
2001-06-16 21:25:10 +00:00
|
|
|
relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR);
|
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
splx(s);
|
2003-02-25 03:21:22 +00:00
|
|
|
return 0;
|
1999-09-01 04:08:39 +00:00
|
|
|
}
|
|
|
|
|
2001-06-23 17:36:51 +00:00
|
|
|
#ifdef USING_DEVFS
|
2004-01-20 03:58:57 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Clone logic is this:
|
|
|
|
* x E X = {dsp, dspW, audio}
|
|
|
|
* x -> x${sysctl("hw.snd.unit")}
|
|
|
|
* xN->
|
|
|
|
* for i N = 1 to channels of device N
|
|
|
|
* if xN.i isn't busy, return its dev_t
|
|
|
|
*/
|
2001-06-16 21:25:10 +00:00
|
|
|
static void
|
2005-08-08 19:55:32 +00:00
|
|
|
dsp_clone(void *arg, struct ucred *cred, char *name, int namelen,
|
|
|
|
struct cdev **dev)
|
2001-06-16 21:25:10 +00:00
|
|
|
{
|
2004-06-16 09:47:26 +00:00
|
|
|
struct cdev *pdev;
|
2004-01-20 03:58:57 +00:00
|
|
|
struct snddev_info *pcm_dev;
|
|
|
|
struct snddev_channel *pcm_chan;
|
|
|
|
int i, unit, devtype;
|
2001-06-27 19:51:02 +00:00
|
|
|
int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
|
|
|
|
char *devnames[3] = {"dsp", "dspW", "audio"};
|
2001-06-16 21:25:10 +00:00
|
|
|
|
2004-06-17 17:16:53 +00:00
|
|
|
if (*dev != NULL)
|
2001-06-16 21:25:10 +00:00
|
|
|
return;
|
2001-06-17 20:15:29 +00:00
|
|
|
if (pcm_devclass == NULL)
|
|
|
|
return;
|
2001-06-16 21:25:10 +00:00
|
|
|
|
2001-06-27 19:51:02 +00:00
|
|
|
devtype = 0;
|
|
|
|
unit = -1;
|
|
|
|
for (i = 0; (i < 3) && (unit == -1); i++) {
|
|
|
|
devtype = devtypes[i];
|
|
|
|
if (strcmp(name, devnames[i]) == 0) {
|
|
|
|
unit = snd_unit;
|
|
|
|
} else {
|
|
|
|
if (dev_stdclone(name, NULL, devnames[i], &unit) != 1)
|
|
|
|
unit = -1;
|
|
|
|
}
|
|
|
|
}
|
2001-06-18 00:10:47 +00:00
|
|
|
if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass))
|
2001-06-16 21:25:10 +00:00
|
|
|
return;
|
|
|
|
|
2004-01-20 03:58:57 +00:00
|
|
|
pcm_dev = devclass_get_softc(pcm_devclass, unit);
|
|
|
|
|
2004-01-25 22:46:22 +00:00
|
|
|
if (pcm_dev == NULL)
|
|
|
|
return;
|
|
|
|
|
2004-01-20 03:58:57 +00:00
|
|
|
SLIST_FOREACH(pcm_chan, &pcm_dev->channels, link) {
|
|
|
|
|
|
|
|
switch(devtype) {
|
|
|
|
case SND_DEV_DSP:
|
|
|
|
pdev = pcm_chan->dsp_devt;
|
|
|
|
break;
|
|
|
|
case SND_DEV_DSP16:
|
|
|
|
pdev = pcm_chan->dspW_devt;
|
|
|
|
break;
|
|
|
|
case SND_DEV_AUDIO:
|
|
|
|
pdev = pcm_chan->audio_devt;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
panic("Unknown devtype %d", devtype);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
|
|
|
|
*dev = pdev;
|
2005-03-31 12:19:44 +00:00
|
|
|
dev_ref(*dev);
|
2004-01-20 03:58:57 +00:00
|
|
|
return;
|
2001-06-17 23:23:06 +00:00
|
|
|
}
|
|
|
|
}
|
2001-06-16 21:25:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dsp_sysinit(void *p)
|
|
|
|
{
|
|
|
|
dsp_ehtag = EVENTHANDLER_REGISTER(dev_clone, dsp_clone, 0, 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dsp_sysuninit(void *p)
|
|
|
|
{
|
|
|
|
if (dsp_ehtag != NULL)
|
|
|
|
EVENTHANDLER_DEREGISTER(dev_clone, dsp_ehtag);
|
|
|
|
}
|
|
|
|
|
|
|
|
SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL);
|
|
|
|
SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL);
|
2001-06-23 17:36:51 +00:00
|
|
|
#endif
|
2001-06-16 21:25:10 +00:00
|
|
|
|
|
|
|
|