mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-18 10:35:55 +00:00
ca83dc2de1
Make the sound configuration a little neater (see /sys/i386/isa/sound/Readme.freebsd) Add support for the Microsoft Sound Source. Document the sound options again. Submitted by: Sujal Patel <smpatel@wam.umd.edu> Obtained from: Voxware
425 lines
8.3 KiB
C
425 lines
8.3 KiB
C
/*
|
|
* sound/audio.c
|
|
*
|
|
* Device file manager for /dev/audio
|
|
*
|
|
* Copyright by Hannu Savolainen 1993
|
|
*
|
|
* 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 "sound_config.h"
|
|
|
|
#ifdef CONFIGURE_SOUNDCARD
|
|
#ifndef EXCLUDE_AUDIO
|
|
|
|
#include "ulaw.h"
|
|
|
|
#define ON 1
|
|
#define OFF 0
|
|
|
|
static int wr_buff_no[MAX_AUDIO_DEV]; /*
|
|
* != -1, if there is
|
|
* a incomplete output
|
|
* block in the queue.
|
|
*/
|
|
static int wr_buff_size[MAX_AUDIO_DEV], wr_buff_ptr[MAX_AUDIO_DEV];
|
|
|
|
static int audio_mode[MAX_AUDIO_DEV];
|
|
|
|
#define AM_NONE 0
|
|
#define AM_WRITE 1
|
|
#define AM_READ 2
|
|
|
|
static char *wr_dma_buf[MAX_AUDIO_DEV];
|
|
static int audio_format[MAX_AUDIO_DEV];
|
|
static int local_conversion[MAX_AUDIO_DEV];
|
|
|
|
static int
|
|
set_format (int dev, int fmt)
|
|
{
|
|
if (fmt != AFMT_QUERY)
|
|
{
|
|
|
|
local_conversion[dev] = 0;
|
|
|
|
if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */
|
|
if (fmt == AFMT_MU_LAW)
|
|
{
|
|
fmt = AFMT_U8;
|
|
local_conversion[dev] = AFMT_MU_LAW;
|
|
}
|
|
else
|
|
fmt = AFMT_U8; /* This is always supported */
|
|
|
|
audio_format[dev] = DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, fmt, 1);
|
|
}
|
|
|
|
if (local_conversion[dev]) /* This shadows the HW format */
|
|
return local_conversion[dev];
|
|
|
|
return audio_format[dev];
|
|
}
|
|
|
|
int
|
|
audio_open (int dev, struct fileinfo *file)
|
|
{
|
|
int ret;
|
|
int bits;
|
|
int dev_type = dev & 0x0f;
|
|
int mode = file->mode & O_ACCMODE;
|
|
|
|
dev = dev >> 4;
|
|
|
|
if (dev_type == SND_DEV_DSP16)
|
|
bits = 16;
|
|
else
|
|
bits = 8;
|
|
|
|
if ((ret = DMAbuf_open (dev, mode)) < 0)
|
|
return ret;
|
|
|
|
local_conversion[dev] = 0;
|
|
|
|
if (DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, bits, 1) != bits)
|
|
{
|
|
audio_release (dev, file);
|
|
return RET_ERROR (ENXIO);
|
|
}
|
|
|
|
if (dev_type == SND_DEV_AUDIO)
|
|
{
|
|
set_format (dev, AFMT_MU_LAW);
|
|
}
|
|
else
|
|
set_format (dev, bits);
|
|
|
|
wr_buff_no[dev] = -1;
|
|
audio_mode[dev] = AM_NONE;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
audio_release (int dev, struct fileinfo *file)
|
|
{
|
|
int mode;
|
|
|
|
dev = dev >> 4;
|
|
mode = file->mode & O_ACCMODE;
|
|
|
|
if (wr_buff_no[dev] >= 0)
|
|
{
|
|
DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
|
|
|
|
wr_buff_no[dev] = -1;
|
|
}
|
|
|
|
DMAbuf_release (dev, mode);
|
|
}
|
|
|
|
#ifdef NO_INLINE_ASM
|
|
static void
|
|
translate_bytes (const unsigned char *table, unsigned char *buff, unsigned long n)
|
|
{
|
|
unsigned long i;
|
|
|
|
for (i = 0; i < n; ++i)
|
|
buff[i] = table[buff[i]];
|
|
}
|
|
|
|
#else
|
|
extern inline void
|
|
translate_bytes (const void *table, void *buff, unsigned long n)
|
|
{
|
|
__asm__ ("cld\n"
|
|
"1:\tlodsb\n\t"
|
|
"xlatb\n\t"
|
|
"stosb\n\t"
|
|
"loop 1b\n\t":
|
|
:"b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff)
|
|
:"bx", "cx", "di", "si", "ax");
|
|
}
|
|
|
|
#endif
|
|
|
|
int
|
|
audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
|
{
|
|
int c, p, l;
|
|
int err;
|
|
|
|
dev = dev >> 4;
|
|
|
|
p = 0;
|
|
c = count;
|
|
|
|
if (audio_mode[dev] == AM_READ) /*
|
|
* Direction changed
|
|
*/
|
|
{
|
|
wr_buff_no[dev] = -1;
|
|
}
|
|
|
|
audio_mode[dev] = AM_WRITE;
|
|
|
|
if (!count) /*
|
|
* Flush output
|
|
*/
|
|
{
|
|
if (wr_buff_no[dev] >= 0)
|
|
{
|
|
DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
|
|
|
|
wr_buff_no[dev] = -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
while (c)
|
|
{ /*
|
|
* Perform output blocking
|
|
*/
|
|
if (wr_buff_no[dev] < 0) /*
|
|
* There is no incomplete buffers
|
|
*/
|
|
{
|
|
if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev], &wr_buff_size[dev])) < 0)
|
|
{
|
|
return wr_buff_no[dev];
|
|
}
|
|
wr_buff_ptr[dev] = 0;
|
|
}
|
|
|
|
l = c;
|
|
if (l > (wr_buff_size[dev] - wr_buff_ptr[dev]))
|
|
l = (wr_buff_size[dev] - wr_buff_ptr[dev]);
|
|
|
|
if (!audio_devs[dev]->copy_from_user)
|
|
{ /*
|
|
* No device specific copy routine
|
|
*/
|
|
COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l);
|
|
}
|
|
else
|
|
audio_devs[dev]->copy_from_user (dev,
|
|
wr_dma_buf[dev], wr_buff_ptr[dev], buf, p, l);
|
|
|
|
|
|
/*
|
|
* Insert local processing here
|
|
*/
|
|
|
|
if (local_conversion[dev] == AFMT_MU_LAW)
|
|
{
|
|
#ifdef linux
|
|
/*
|
|
* This just allows interrupts while the conversion is running
|
|
*/
|
|
__asm__ ("sti");
|
|
#endif
|
|
translate_bytes (ulaw_dsp, (unsigned char *) &wr_dma_buf[dev][wr_buff_ptr[dev]], l);
|
|
}
|
|
|
|
c -= l;
|
|
p += l;
|
|
wr_buff_ptr[dev] += l;
|
|
|
|
if (wr_buff_ptr[dev] >= wr_buff_size[dev])
|
|
{
|
|
if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev])) < 0)
|
|
{
|
|
return err;
|
|
}
|
|
|
|
wr_buff_no[dev] = -1;
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
int
|
|
audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
|
{
|
|
int c, p, l;
|
|
char *dmabuf;
|
|
int buff_no;
|
|
|
|
dev = dev >> 4;
|
|
p = 0;
|
|
c = count;
|
|
|
|
if (audio_mode[dev] == AM_WRITE)
|
|
{
|
|
if (wr_buff_no[dev] >= 0)
|
|
{
|
|
DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
|
|
|
|
wr_buff_no[dev] = -1;
|
|
}
|
|
}
|
|
|
|
audio_mode[dev] = AM_READ;
|
|
|
|
while (c)
|
|
{
|
|
if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l)) < 0)
|
|
return buff_no;
|
|
|
|
if (l > c)
|
|
l = c;
|
|
|
|
/*
|
|
* Insert any local processing here.
|
|
*/
|
|
|
|
if (local_conversion[dev] == AFMT_MU_LAW)
|
|
{
|
|
#ifdef linux
|
|
/*
|
|
* This just allows interrupts while the conversion is running
|
|
*/
|
|
__asm__ ("sti");
|
|
#endif
|
|
|
|
translate_bytes (dsp_ulaw, (unsigned char *) dmabuf, l);
|
|
}
|
|
|
|
COPY_TO_USER (buf, p, dmabuf, l);
|
|
|
|
DMAbuf_rmchars (dev, buff_no, l);
|
|
|
|
p += l;
|
|
c -= l;
|
|
}
|
|
|
|
return count - c;
|
|
}
|
|
|
|
int
|
|
audio_ioctl (int dev, struct fileinfo *file,
|
|
unsigned int cmd, unsigned int arg)
|
|
{
|
|
|
|
dev = dev >> 4;
|
|
|
|
switch (cmd)
|
|
{
|
|
case SNDCTL_DSP_SYNC:
|
|
if (wr_buff_no[dev] >= 0)
|
|
{
|
|
DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
|
|
|
|
wr_buff_no[dev] = -1;
|
|
}
|
|
return DMAbuf_ioctl (dev, cmd, arg, 0);
|
|
break;
|
|
|
|
case SNDCTL_DSP_POST:
|
|
if (wr_buff_no[dev] >= 0)
|
|
{
|
|
DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
|
|
|
|
wr_buff_no[dev] = -1;
|
|
}
|
|
return 0;
|
|
break;
|
|
|
|
case SNDCTL_DSP_RESET:
|
|
wr_buff_no[dev] = -1;
|
|
return DMAbuf_ioctl (dev, cmd, arg, 0);
|
|
break;
|
|
|
|
case SNDCTL_DSP_GETFMTS:
|
|
return IOCTL_OUT (arg, audio_devs[dev]->format_mask);
|
|
break;
|
|
|
|
case SNDCTL_DSP_SETFMT:
|
|
return IOCTL_OUT (arg, set_format (dev, IOCTL_IN (arg)));
|
|
|
|
default:
|
|
return DMAbuf_ioctl (dev, cmd, arg, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
long
|
|
audio_init (long mem_start)
|
|
{
|
|
/*
|
|
* NOTE! This routine could be called several times during boot.
|
|
*/
|
|
return mem_start;
|
|
}
|
|
|
|
#else
|
|
/*
|
|
* Stub versions
|
|
*/
|
|
|
|
int
|
|
audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
|
{
|
|
return RET_ERROR (EIO);
|
|
}
|
|
|
|
int
|
|
audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
|
|
{
|
|
return RET_ERROR (EIO);
|
|
}
|
|
|
|
int
|
|
audio_open (int dev, struct fileinfo *file)
|
|
{
|
|
return RET_ERROR (ENXIO);
|
|
}
|
|
|
|
void
|
|
audio_release (int dev, struct fileinfo *file)
|
|
{
|
|
};
|
|
int
|
|
audio_ioctl (int dev, struct fileinfo *file,
|
|
unsigned int cmd, unsigned int arg)
|
|
{
|
|
return RET_ERROR (EIO);
|
|
}
|
|
|
|
int
|
|
audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig)
|
|
{
|
|
return RET_ERROR (EIO);
|
|
}
|
|
|
|
long
|
|
audio_init (long mem_start)
|
|
{
|
|
return mem_start;
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|