| Open Sound System |
| Do you have problems with sound/audio application development? Don't panic! Click here for help! |
This file is part of Open Sound System.
Copyright (C) 4Front Technologies 1996-2008.
This this source file is released under GPL v2 license (no other versions). See the COPYING file included in the main directory of this source distribution for the license terms and conditions.
#undef NO_COOKED_MODE #define AUDIO_C #define GRC 3 /* Some debugging macros (for future use) */ #define UP_STATUS(x) #define DOWN_STATUS(x) #include <oss_config.h> extern int src_quality; extern int vmix_disabled; oss_mutex_t audio_global_mutex;
Resizeable audio device tables
static oss_memblk_t *audio_global_memblk=NULL; int oss_max_audio_devfiles=4; int oss_max_audio_engines=8; #define AUDIO_MALLOC(osdev, size) oss_memblk_malloc(&audio_global_memblk, size) #define AUDIO_FREE(osdev, addr) oss_memblk_free(&audio_global_memblk, addr) #define AUDIO_ENGINE_INCREMENT (oss_max_audio_engines/2) #define AUDIO_DEVFILE_INCREMENT (oss_max_audio_devfiles/2)
List of audio devices in the system
adev_t **audio_engines = NULL; int num_audio_engines = 0; void *oss_adev_pointer = NULL; /* Alias of audio_engines for Cuckoo */ adev_t **audio_devfiles = NULL; int num_audio_devfiles = 0; extern int flat_device_model; #if 0
Device lists (input, output, duplex) for /dev/dsp.
oss_devlist_t dspinlist = { 0 };
oss_devlist_t dspoutlist = { 0 };
oss_devlist_t dspinoutlist = { 0 };
oss_devlist_t dspoutlist2 = { 0 }; /* 2nd priority output devices */
#endif
Applications known to require special handling (mmap, etc.). These applications are listed here because they use the OSS API incorrectly and some workarounds by OSS are required to make them to work.
typedef struct
{
char *name;
int open_flags;
} oss_specialapp_t;
oss_specialapp_t special_apps[] = {
{"quake", OF_MMAP},
{"quake2", OF_MMAP},
{"q3demo.x86", OF_MMAP},
{"wolfsp.x86", OF_MMAP},
{"wolfmp.x86", OF_MMAP},
{"quake3", OF_MMAP},
{"wolf3d", OF_MMAP},
{"rtcw", OF_MMAP},
{"artsd", OF_BLOCK | OF_NOCONV},
{"realplay.bin", OF_SMALLFRAGS},
{"hxplay.bin", OF_SMALLFRAGS},
#if 1
{"auserver", OF_NOCONV}, /* Win4lin */
#endif
{"quake3.x86", OF_MMAP},
{"wolf.x86", OF_MMAP},
{"et.x86", OF_MMAP},
{"doom.x86", OF_MMAP},
// {"mozilla-bin", OF_MEDIUMBUF}, /* For the flash plugin */
{NULL, 0}
};
#ifdef APPLIST_SUPPORT
Lookup tables for applications. Use these lists to assign fixed target devices for given applications.
app_routing_t oss_applist[APPLIST_SIZE];
int oss_applist_size = 0;
static int
app_lookup (int mode, const char *appname, int *open_flags)
{
int i, dev;
if (appname == NULL)
return -2;
for (i = 0; i < oss_applist_size; i++)
{
if (oss_applist[i].mode == mode)
if (strncmp (appname, oss_applist[i].name, 32) == 0)
{
dev = oss_applist[i].dev;
if (dev < -1 || dev >= num_audio_devfiles)
return -1;
*open_flags |= oss_applist[i].open_flags;
return dev;
}
}
return -2;
}
#endif
/*ARGSUSED*/
static int
get_open_flags (int mode, int open_flags, struct fileinfo *file)
{
char *name;
int i;
if ((name = GET_PROCESS_NAME (file)) != NULL)
{
#ifdef APPLIST_SUPPORT
if (app_lookup (mode, name, &open_flags) >= -1)
return open_flags;
#endif
for (i = 0; special_apps[i].name != NULL; i++)
if (strcmp (special_apps[i].name, name) == 0)
{
open_flags |= special_apps[i].open_flags;
return open_flags;
}
}
return open_flags;
}
#define BIGWRITE_SIZE 1024
#define NEUTRAL8 0x80
#define NEUTRAL16 0x00
#define OSS_PLUGIN_VERSION (0x100|oss_sample_bits)
int always_cooked = 0;
static unsigned long sync_seed = 0;
#define COOKED_BLOCK_SIZE 4096
extern oss_uint64_t oss_tmp_timer;
Structure used by oss_audio_register_client()
typedef struct
{
oss_audio_startup_func func;
void *devc;
oss_device_t *osdev;
} audio_startup_t;
#define MAX_STARTUPS 5
static audio_startup_t audio_startups[MAX_STARTUPS];
static int num_audio_startups = 0;
int
dmap_get_qlen (dmap_p dmap)
{
int len;
if (dmap->dma_mode == PCM_ENABLE_OUTPUT)
{
if (dmap->fragment_size == 0)
{
cmn_err (CE_WARN, "dmap (out) fragment_size=0, dev=%s\n",
dmap->adev->name);
return 0;
}
len =
(int) ((dmap->user_counter -
dmap->byte_counter) / dmap->fragment_size);
if (len < 0)
len = 0;
return len;
}
if (dmap->dma_mode == PCM_ENABLE_INPUT)
{
if (dmap->fragment_size == 0)
{
cmn_err (CE_WARN, "dmap (in) fragment_size=0, dev=%s\n",
dmap->adev->name);
return 0;
}
len =
(int) ((dmap->byte_counter -
dmap->user_counter) / dmap->fragment_size);
if (len < 0)
len = 0;
return len;
}
return 0;
}
int
dmap_get_qhead (dmap_p dmap)
{
unsigned int ptr;
if (dmap->fragment_size == 0)
return 0;
if (dmap->dma_mode == PCM_ENABLE_OUTPUT)
{
ptr = (int) (dmap->byte_counter % dmap->bytes_in_use);
return ptr / dmap->fragment_size;
}
if (dmap->dma_mode == PCM_ENABLE_INPUT)
{
ptr = (int) (dmap->user_counter % dmap->bytes_in_use);
return ptr / dmap->fragment_size;
}
return 0;
}
int
dmap_get_qtail (dmap_p dmap)
{
unsigned int ptr;
if (dmap->fragment_size == 0)
return 0;
if (dmap->dma_mode == PCM_ENABLE_INPUT)
{
ptr = (int) (dmap->byte_counter % dmap->bytes_in_use);
return ptr / dmap->fragment_size;
}
if (dmap->dma_mode == PCM_ENABLE_OUTPUT)
{
ptr = (int) (dmap->user_counter % dmap->bytes_in_use);
return ptr / dmap->fragment_size;
}
return 0;
}
int
oss_audio_set_format (int dev, int fmt, int format_mask)
{
adev_p adev;
int ret, newfmt;
unsigned char neutral_byte;
audio_format_info_p fmt_info;
sync_seed++;
if (dev < 0 || dev >= num_audio_engines)
return OSS_ENXIO;
adev = audio_engines[dev];
if (!adev->enabled)
return OSS_ENXIO;
if (fmt == 0)
return adev->user_parms.fmt;
if (fmt == AFMT_AC3)
{
AC3 cannot tolerate any format conversions.
adev->cooked_enable = 0;
if (adev->flags & ADEV_VMIX)
{
oss_audio_set_error (adev->engine_num, E_REC,
OSSERR (1018,
"AFMT_AC3 used with virtual mixing device."),
0);
Errordesc: Devices that support virtual or hardware mixing are probably not capable to play AC3 streams properly.
Virtual mixing is enabled on the audio device. The virtual mixer driver will do voolume control that corrupts the AC3 bitstream. Applications using AC3 should open the audio device with O_EXCL to make sure that virtual mixing is properly bypassed.
}
if (adev->flags & ADEV_HWMIX)
{
oss_audio_set_error (adev->engine_num, E_REC,
OSSERR (1019,
"AFMT_AC3 used with hardware mixing device."),
0);
Errordesc: Devices that support virtual or hardware mixing are probably not capable to play AC3 streams properly.
This error may be caused by user who selected a wrong audio device.
}
if (adev->dmask & DMASK_OUT)
if (adev->dmap_out->flags & DMAP_COOKED)
{
oss_audio_set_error (adev->engine_num, E_REC,
OSSERR (1020,
"AFMT_AC3 used with format conversions enabled."),
0);
Errordesc: AC3 audio format (AFMT_AC3) bitstreams don't tolerate any kind of sample rate or format conversions. However it looks like conversions would be needed. The reason may be that the application had earlier requested some sample rate that is not supported by the device.
Applications using AC3 should call SNDCTL_DSP_COOKEDMODE to disable format conversions.
}
}
ret = adev->d->adrv_set_format (dev, fmt);
adev->user_parms.fmt = ret;
adev->hw_parms.fmt = ret;
if ((fmt_info = oss_find_format (fmt)) != NULL)
{
if (fmt_info->no_convert) /* Cannot convert this format */
{
if (fmt == AFMT_AC3)
return OSS_EIO;
return ret;
}
}
if ((fmt_info = oss_find_format (ret)) == NULL)
neutral_byte = 0;
else
{
if (fmt_info->no_convert) /* Cannot convert this format */
return ret;
neutral_byte = fmt_info->neutral_byte;
}
if (adev->dmask & DMASK_OUT)
adev->dmap_out->neutral_byte = neutral_byte;
if (adev->dmask & DMASK_IN)
adev->dmap_in->neutral_byte = neutral_byte;
/* Disable format conversions if mmap() */
if ((adev->dmap_out->mapping_flags & DMA_MAP_MAPPED) ||
(adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) ||
(adev->open_flags & OF_MMAP))
return ret;
#ifdef NO_COOKED_MODE
return ret;
#else
if (!adev->cooked_enable)
return ret;
if (ret == fmt)
return ret;
/* Convertable format? */
if (!(fmt & CONVERTABLE_FORMATS))
{
return ret;
}
Needs to perform format conversions
adev->user_parms.fmt = fmt;
if (adev->dmask & DMASK_OUT)
{
adev->dmap_out->flags |= DMAP_COOKED;
}
if (adev->dmask & DMASK_OUT)
{
adev->dmap_in->flags |= DMAP_COOKED;
}
/* If the device is in 16 bit format then just return */
if (ret & (AFMT_S16_LE | AFMT_S16_BE))
return fmt;
/* Try to find a suitable format */
if (fmt == AFMT_MU_LAW && ret == AFMT_U8) /* mu-Law <-> 8 bit linear */
return fmt;
if (format_mask & AFMT_S16_LE)
{
newfmt = AFMT_S16_LE;
goto got_it;
}
if (format_mask & AFMT_S16_BE)
{
newfmt = AFMT_S16_BE;
goto got_it;
}
return fmt; /* Nothing better than the one suggested by the device */
got_it:
ret = adev->d->adrv_set_format (dev, newfmt);
adev->hw_parms.fmt = ret;
return fmt;
#endif
}
int
oss_audio_set_channels (int dev, int ch)
{
adev_p adev;
int ret;
sync_seed++;
if (dev < 0 || dev >= num_audio_engines)
return OSS_ENXIO;
adev = audio_engines[dev];
if (!adev->enabled)
return OSS_ENXIO;
if (ch == 0)
return adev->user_parms.channels;
ret = adev->d->adrv_set_channels (dev, ch);
if (ret < 1)
{
cmn_err (CE_WARN,
"Audio engine %d: Internal error in channel setup, err=%d, ch=%d\n",
dev, ret, ch);
return OSS_EIO;
}
if (ch > 2 && ch > ret) /* Requested multi channel mode not possible */
ch = ret;
adev->user_parms.channels = ret;
adev->hw_parms.channels = ret;
/* Disable format conversions if mmap() */
if ((adev->dmap_out->mapping_flags & DMA_MAP_MAPPED) ||
(adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) ||
(adev->open_flags & OF_MMAP))
return ret;
#ifdef NO_COOKED_MODE
return ret;
#else
if (!adev->cooked_enable)
return ret;
if (ret == ch)
return ret;
/* For the time being only stereo <->mono is possible */
if (ch != ret)
if (!((ch == 1 && ret == 2) || (ch == 2 && ret == 1)))
return ret;
Needs to perform format conversions
adev->user_parms.channels = ch;
if (adev->dmask & DMASK_OUT)
{
adev->dmap_out->flags |= DMAP_COOKED;
}
if (adev->dmask & DMASK_OUT)
{
adev->dmap_in->flags |= DMAP_COOKED;
}
return ch;
#endif
}
int
oss_audio_set_rate (int dev, int rate)
{
adev_p adev;
int ret;
sync_seed++;
if (dev < 0 || dev >= num_audio_engines)
return OSS_ENXIO;
adev = audio_engines[dev];
if (!adev->enabled)
return OSS_ENXIO;
if (rate == 0)
return adev->user_parms.rate;
ret = adev->d->adrv_set_rate (dev, rate);
if (ret < 0)
return ret;
adev->user_parms.rate = ret;
adev->hw_parms.rate = ret;
/* Disable format conversions if mmap() */
if ((adev->dmap_out->mapping_flags & DMA_MAP_MAPPED) ||
(adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) ||
(adev->flags & ADEV_NOSRC) || (adev->open_flags & OF_MMAP))
return ret;
if (!adev->cooked_enable)
return ret;
#if defined(NO_COOKED_MODE) || GRC == 0
return ret;
#else
if (ret == rate) /* No SRC needed */
return ret;
Needs to perform format conversions
adev->user_parms.rate = rate;
if (adev->dmask & DMASK_OUT)
{
adev->dmap_out->flags |= DMAP_COOKED;
}
if (adev->dmask & DMASK_OUT)
{
adev->dmap_in->flags |= DMAP_COOKED;
}
return rate;
#endif
}
static void
reset_dmap (dmap_p dmap)
{
#ifdef DO_TIMINGS
oss_do_timing ("Reset dmap");
#endif
dmap->dma_mode = 0;
dmap->flags &= (DMAP_FRAGFIXED | DMAP_COOKED);
dmap->byte_counter = dmap->user_counter = 0;
dmap->fragment_counter = 0;
dmap->interrupt_count = 0;
dmap->expand_factor = UNIT_EXPAND; /* 1:1 */
dmap->play_underruns = dmap->rec_overruns = 0;
dmap->num_errors = 0;
dmap->leftover_bytes = 0;
dmap->leftover_buf = NULL;
}
int
oss_alloc_dmabuf (int dev, dmap_p dmap, int direction)
{
adev_t *adev = dmap->adev;
if (adev == NULL)
{
cmn_err (CE_WARN, "oss_alloc_dmabuf: adev==NULL\n");
return OSS_EIO;
}
return __oss_alloc_dmabuf (dev, dmap, adev->dmabuf_alloc_flags,
adev->dmabuf_maxaddr, direction);
}
static int
default_alloc_buffer (int dev, dmap_t * dmap, int direction)
{
int err;
if (dmap->dmabuf != NULL)
return 0;
if ((err = oss_alloc_dmabuf (dev, dmap, direction)) < 0)
return err;
return 0;
}
/*ARGSUSED*/
static int
default_free_buffer (int dev, dmap_t * dmap, int direction)
{
if (dmap->dmabuf == NULL)
return 0;
oss_free_dmabuf (dev, dmap);
dmap->dmabuf = NULL;
dmap->dmabuf_phys = 0;
return 0;
}
static int
init_dmap (adev_p adev, dmap_p dmap, int direction)
{
int retval;
int l, static_len;
char *p;
oss_native_word flags;
if (dmap->osdev == NULL)
{
cmn_err (CE_WARN, "dmap->osdev==NULL\n");
return OSS_EIO;
}
if (dmap->dmabuf == NULL)
{
if (adev->d->adrv_alloc_buffer != NULL)
{
retval =
adev->d->adrv_alloc_buffer (adev->engine_num, dmap, direction);
}
else
{
retval = default_alloc_buffer (adev->engine_num, dmap, direction);
}
if (retval < 0)
{
cmn_err (CE_WARN,
"Failed to allocate DMA buffer for audio engine %d/%s\n",
adev->engine_num, adev->name);
return retval;
}
if (dmap->dmabuf_phys == 0) /* Cannot do mmap */
adev->flags |= ADEV_NOMMAP;
}
#ifdef linux
if (dmap->dmabuf_phys == 0)
adev->flags |= ADEV_NOMMAP;
#endif
dmap->flags &= ~DMAP_FRAGFIXED;
l = sizeof (dmap_t);
static_len = (oss_native_word) & dmap->flags - (oss_native_word) dmap;
p = (char *) &dmap->flags;
l -= static_len;
if (p != NULL)
memset (p, 0, l); /* Clear all */
if (!dmap->buffer_protected && dmap->dmabuf != NULL)
memset (dmap->dmabuf, dmap->neutral_byte, dmap->buffsize);
MUTEX_ENTER_IRQDISABLE (dmap->mutex, flags);
reset_dmap (dmap);
dmap->bytes_in_use = dmap->buffsize;
dmap->frame_size = 1;
dmap->user_frame_size = 1;
dmap->flags = 0;
dmap->audio_callback = NULL;
MUTEX_EXIT_IRQRESTORE (dmap->mutex, flags);
return 0;
}
void
audio_reset_adev (adev_p adev)
{
oss_native_word flags, dflags;
#ifdef DO_TIMINGS
oss_do_timing ("Reset input & output\n");
#endif
sync_seed++;
if (!adev->enabled)
return;
dflags = 0;
MUTEX_ENTER_IRQDISABLE (adev->mutex, flags);
adev->go = 1;
adev->enable_bits = adev->open_mode;
if (adev->open_mode & OPEN_WRITE)
{
dmap_p dmap = adev->dmap_out;
dmap->flags &= ~DMAP_PREPARED;
memset (dmap->dmabuf, 0, dmap->buffsize);
}
if (adev->d->adrv_halt_input && adev->d->adrv_halt_output)
{
if (adev->open_mode & OPEN_READ)
{
dmap_p dmap = adev->dmap_in;
MUTEX_ENTER (dmap->mutex, dflags);
dmap->flags &= ~DMAP_PREPARED;
if ((dmap->dma_mode == PCM_ENABLE_INPUT)
&& (dmap->flags & DMAP_STARTED))
{
#ifdef CONFIG_OSSD
ossd_event (adev->engine_num, OSSD_EV_RESET_INPUT);
#endif
MUTEX_EXIT (dmap->mutex, dflags);
MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
adev->d->adrv_halt_input (adev->engine_num);
MUTEX_ENTER_IRQDISABLE (adev->mutex, flags);
MUTEX_ENTER (dmap->mutex, dflags);
reset_dmap (dmap);
}
MUTEX_EXIT (dmap->mutex, dflags);
}
if (adev->open_mode & OPEN_WRITE)
{
dmap_p dmap = adev->dmap_out;
MUTEX_ENTER (dmap->mutex, dflags);
if ((dmap->dma_mode == PCM_ENABLE_OUTPUT) &&
(dmap->flags & DMAP_STARTED))
{
#ifdef CONFIG_OSSD
ossd_event (adev->engine_num, OSSD_EV_RESET_OUTPUT);
#endif
MUTEX_EXIT (dmap->mutex, dflags);
MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
adev->d->adrv_halt_output (adev->engine_num);
MUTEX_ENTER_IRQDISABLE (adev->mutex, flags);
MUTEX_ENTER (dmap->mutex, dflags);
reset_dmap (dmap);
}
MUTEX_EXIT (dmap->mutex, dflags);
}
MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
return;
}
#ifdef CONFIG_OSSD
ossd_event (adev->engine_num, OSSD_EV_RESET_OUTPUT);
ossd_event (adev->engine_num, OSSD_EV_RESET_INPUT);
#endif
adev->d->adrv_halt_io (adev->engine_num);
reset_dmap (adev->dmap_out);
reset_dmap (adev->dmap_in);
MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
}
static void
audio_reset_input (adev_p adev)
{
dmap_p dmap = adev->dmap_in;
oss_native_word flags, dflags;
dflags = 0;
if (!(adev->open_mode & OPEN_READ))
return;
if (dmap->dma_mode != PCM_ENABLE_INPUT)
return;
if (!(dmap->flags & DMAP_STARTED))
return;
#ifdef DO_TIMINGS
oss_do_timing ("Reset input\n");
#endif
dmap->flags &= ~DMAP_PREPARED;
if (adev->d->adrv_halt_input)
{
#ifdef CONFIG_OSSD
ossd_event (adev->engine_num, OSSD_EV_RESET_INPUT);
#endif
adev->d->adrv_halt_input (adev->engine_num);
MUTEX_ENTER_IRQDISABLE (dmap->mutex, flags);
reset_dmap (dmap);
MUTEX_EXIT_IRQRESTORE (dmap->mutex, flags);
return;
}
adev->d->adrv_halt_io (adev->engine_num);
MUTEX_ENTER_IRQDISABLE (adev->mutex, flags);
MUTEX_ENTER (dmap->mutex, dflags);
reset_dmap (dmap);
MUTEX_EXIT (dmap->mutex, dflags);
MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
}
static void
audio_reset_output (adev_p adev)
{
dmap_p dmap = adev->dmap_out;
oss_native_word flags, dflags;
dflags = 0;
sync_seed++;
if (!(adev->open_mode & OPEN_WRITE))
return;
if (dmap->dma_mode != PCM_ENABLE_OUTPUT)
return;
if (!(dmap->flags & DMAP_STARTED))
return;
dmap->flags &= ~DMAP_PREPARED;
#ifdef DO_TIMINGS
oss_do_timing ("Reset output\n");
#endif
if (adev->d->adrv_halt_output)
{
#ifdef CONFIG_OSSD
ossd_event (adev->engine_num, OSSD_EV_RESET_OUTPUT);
#endif
adev->d->adrv_halt_output (adev->engine_num);
MUTEX_ENTER_IRQDISABLE (dmap->mutex, flags);
reset_dmap (dmap);
MUTEX_EXIT_IRQRESTORE (dmap->mutex, flags);
return;
}
adev->d->adrv_halt_io (adev->engine_num);
MUTEX_ENTER_IRQDISABLE (adev->mutex, flags);
MUTEX_ENTER (dmap->mutex, dflags);
reset_dmap (dmap);
MUTEX_EXIT (dmap->mutex, dflags);
MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
}
static int launch_output (adev_p adev, dmap_p dmap);
static int launch_input (adev_p adev, dmap_p dmap);
static void
oss_audio_post (adev_p adev)
{
int i, n, p;
oss_uint64_t limit;
dmap_p dmap = adev->dmap_out;
if (!(adev->open_mode & OPEN_WRITE))
return;
if (dmap->user_counter == 0)
return;
if (dmap->flags & DMAP_STARTED)
return;
#ifdef DO_TIMINGS
oss_do_timing ("Post output\n");
#endif
/* Clean the unused buffer space (in slow way) */
limit = dmap->byte_counter + dmap->bytes_in_use;
if (limit > dmap->user_counter + dmap->bytes_in_use)
limit = dmap->user_counter + dmap->bytes_in_use;
n = (int) (limit - dmap->user_counter) + 1;
p = (int) (dmap->user_counter % dmap->bytes_in_use); /*Â Current ptr */
for (i = 0; i < n; i++)
{
dmap->dmabuf[p] = dmap->neutral_byte;
p = (p + 1) % dmap->bytes_in_use;
}
launch_output (adev, dmap);
}
static void
oss_audio_sync (adev_p adev)
{
dmap_p dmap = adev->dmap_out;
oss_uint64_t uc, limit;
oss_native_word flags;
unsigned int status;
int n = 0, loops = 0, tmout = OSS_HZ, i;
int prev_underruns;
if (dmap->dma_mode != PCM_ENABLE_OUTPUT)
return;
if (adev->dmap_out->mapping_flags & DMA_MAP_MAPPED)
return;
if (!(dmap->flags & DMAP_STARTED))
oss_audio_post (adev);
if (!(dmap->flags & DMAP_STARTED))
return;
/* Don't wait if output is untriggered */
if (adev->go == 0 || !(adev->enable_bits & PCM_ENABLE_OUTPUT))
return;
#ifdef DO_TIMINGS
oss_do_timing ("Sync output\n");
#endif
MUTEX_ENTER_IRQDISABLE (dmap->mutex, flags);
dmap->flags |= DMAP_POST;
uc = dmap->user_counter;
prev_underruns = dmap->play_underruns;
dmap->play_underruns = 0;
while (dmap->play_underruns == 0 && loops++ < dmap->nfrags
&& dmap->byte_counter < uc)
{
int p;
adev->dmap_out->error = 0;
/* Clean the unused buffer space (in a slow but precise way) */
limit = dmap->byte_counter + dmap->bytes_in_use;
if (limit > dmap->user_counter + dmap->bytes_in_use)
limit = dmap->user_counter + dmap->bytes_in_use;
p = (int) (dmap->user_counter % dmap->bytes_in_use); /*Â Current ptr */
n = (int) (limit - dmap->user_counter) + 1;
for (i = 0; i < n; i++)
{
dmap->dmabuf[p] = dmap->neutral_byte;
p = (p + 1) % dmap->bytes_in_use;
}
tmout = (dmap->fragment_size * OSS_HZ) / dmap->data_rate;
tmout += OSS_HZ / 10;
if (!oss_sleep (adev->out_wq, &dmap->mutex, tmout, &flags, &status))
{
#ifndef __FreeBSD__
cmn_err (CE_WARN, "Output timed out (sync) on audio engine %d\n",
adev->engine_num);
#endif
adev->timeout_count++;
MUTEX_EXIT_IRQRESTORE (dmap->mutex, flags);
FMA_EREPORT(adev->osdev, DDI_FM_DEVICE_STALL, NULL, NULL, NULL);
FMA_IMPACT(adev->osdev, DDI_SERVICE_LOST);
return;
}
}
MUTEX_EXIT_IRQRESTORE (dmap->mutex, flags);
n = 0;
if (adev->d->adrv_local_qlen) /* Drain the internal queue too */
while (n++ <=
adev->d->adrv_local_qlen (adev->engine_num) / dmap->fragment_size)
{
MUTEX_ENTER_IRQDISABLE (dmap->mutex, flags);
audio_engines[adev->engine_num]->dmap_out->error = 0;
tmout = (dmap->fragment_size * OSS_HZ) / dmap->data_rate;
tmout += OSS_HZ / 10;
oss_sleep (adev->out_wq, &dmap->mutex, tmout, &flags, &status);
MUTEX_EXIT_IRQRESTORE (dmap->mutex, flags);
}
dmap->play_underruns = prev_underruns;
}
static int prepare_output (adev_p adev, dmap_p dmap);
static int prepare_input (adev_p adev, dmap_p dmap);
void
oss_audio_set_error (int dev, int mode, int err, int parm)
{
int i, n;
dmap_t *dmap;
#ifdef DO_TIMINGS
{
if (mode == E_REC)
oss_timing_printf ("Audio rec error code %05d/%d, engine=%d", err, parm,
mode);
else
oss_timing_printf ("Audio play error code %05d/%d, engine=%d", err, parm,
mode);
}
#endif
#if 0
if (mode == E_REC)
cmn_err (CE_CONT, "Audio rec error code %05d/%d, engine=%d\n", err, parm,
mode);
else
cmn_err (CE_CONT, "Audio play error code %05d/%d, engine=%d\n", err, parm,
mode);
#endif
switch (mode)
{
case E_PLAY:
dmap = audio_engines[dev]->dmap_out;
break;
case E_REC:
dmap = audio_engines[dev]->dmap_in;
break;
default:
dmap = audio_engines[dev]->dmap_out; /* Actually this would be an error */
}
n = dmap->num_errors++;
if (n > MAX_AUDIO_ERRORS)
n = MAX_AUDIO_ERRORS;
if (n > 0)
{
Move previous errors (if any) upwards in the list.
for (i = n; i > 0; i--)
{
dmap->errors[i] = dmap->errors[i - 1];
dmap->error_parms[i] = dmap->error_parms[i - 1];
}
}
dmap->errors[0] = err;
dmap->error_parms[0] = parm;
}
/*ARGSUSED*/
static void
report_errors (char *s, const char *hdr, adev_t * adev, dmap_t * dmap,
int bufsize)
{
int i, l, j;
if (bufsize < 30) /* No space left */
return;
strcpy (s, hdr);
s += l = strlen (s);
bufsize -= l;
for (i = 0; i < dmap->num_errors && i < MAX_AUDIO_ERRORS; i++)
{
int dupe = 0;
if (bufsize < 30)
return;
for (j = 0; !dupe && j < i; j++)
if (dmap->errors[i] == dmap->errors[j])
dupe = 1;
if (dupe)
continue;
sprintf (s, " %05d:%d", dmap->errors[i], dmap->error_parms[i]);
s += l = strlen (s);
bufsize -= l;
}
}
static void
store_history (int dev)
{
int p;
char *tmp, *s;
adev_p adev = audio_engines[dev];
if (adev->pid == 0) /* Virtual mixer */
return;
if (*adev->cmd && strcmp (adev->cmd, "sndconf") == 0)
return;
p = oss_history_p;
oss_history_p = (oss_history_p + 1) % OSS_HISTORY_SIZE;
tmp = oss_history[p];
memset (tmp, 0, OSS_HISTORY_STRSIZE);
s = tmp;
sprintf (s, "%s.%02d: ", adev->devnode, adev->engine_num);
s += strlen (s);
if (adev->pid != -1)
{
sprintf (s, "pid %d ", adev->pid);
s += strlen (s);
}
if (*adev->cmd != 0)
{
sprintf (s, "cmd '%s' ", adev->cmd);
s += strlen (s);
}
else
{
strcpy (s, "cmd: Unknown ");
s += strlen (s);
}
if (adev->open_mode & OPEN_READ)
{
sprintf (s, "IN ");
s += strlen (s);
}
if (adev->open_mode & OPEN_WRITE)
{
sprintf (s, "OUT ");
s += strlen (s);
}
if (adev->timeout_count > 0)
{
sprintf (s, "%d timeouts ", adev->timeout_count);
s += strlen (s);
}
if (adev->dmap_out->play_underruns > 0)
{
sprintf (s, "%d underruns ", adev->dmap_out->play_underruns);
s += strlen (s);
}
if (adev->dmap_in->rec_overruns > 0)
{
sprintf (s, "%d overruns ", adev->dmap_in->rec_overruns);
s += strlen (s);
}
if (adev->dmap_out->num_errors > 0)
{
report_errors (s, "Play events:", adev, adev->dmap_out,
OSS_HISTORY_STRSIZE - strlen (tmp) - 1);
s += strlen (s);
*s++ = ' ';
}
if (adev->dmap_in->num_errors > 0)
{
report_errors (s, "Rec events:", adev, adev->dmap_in,
OSS_HISTORY_STRSIZE - strlen (tmp) - 1);
s += strlen (s);
*s++ = ' ';
}
}
char *
audio_show_latency (int dev)
{
static char str[32];
adev_p adev = audio_engines[dev];
dmap_p dmap;
int dr, fs;
*str = 0;
if (dev < 0 || dev >= num_audio_engines)
return "Bad device";
if (adev->open_mode == 0)
return "Not opened";
if (adev->open_mode == OPEN_READ)
dmap = adev->dmap_in;
else
dmap = adev->dmap_out;
if (dmap == NULL)
return "Unknown";
if (!(dmap->flags & DMAP_STARTED))
return "Not started";
dr = dmap->data_rate;
fs = dmap->fragment_size;
if (dr <= 0)
sprintf (str, "%d", dmap->fragment_size);
else
{
int r = (fs * 10000) / dr;
sprintf (str, "%d/%d (%d.%d msec)", dmap->fragment_size, dr, r / 10,
r % 10);
}
return str;
}
void
oss_audio_release (int dev, struct fileinfo *file)
{
adev_p adev;
int mode;
oss_native_word flags;
sync_seed++;
if (dev < 0 || dev >= num_audio_engines)
return;
if (file)
mode = file->mode & O_ACCMODE;
else
mode = OPEN_READ | OPEN_WRITE;
#ifdef DO_TIMINGS
oss_timing_printf ("=-=-=- Closing audio engine %d -=-=-=", dev);
#endif
adev = audio_engines[dev];
if (adev->unloaded || !adev->enabled)
{
cmn_err (CE_CONT, "Audio device %s not available - close aborted\n",
adev->name);
return;
}
oss_audio_post (adev);
oss_audio_sync (adev);
#ifdef CONFIG_OSSD
ossd_event (adev->engine_num, OSSD_EV_CLOSE);
#endif
adev->d->adrv_halt_io (dev);
adev->d->adrv_close (dev, mode);
MUTEX_ENTER_IRQDISABLE (adev->mutex, flags);
if (adev->dmask & DMASK_OUT)
{
adev->dmap_out->audio_callback = NULL;
if (!adev->dmap_out->buffer_protected)
if (adev->dmap_out->dmabuf != NULL)
memset (adev->dmap_out->dmabuf, adev->dmap_out->neutral_byte,
adev->dmap_out->buffsize);
}
if (adev->dmask & DMASK_IN)
{
adev->dmap_in->audio_callback = NULL;
}
store_history (dev);
memset (audio_engines[dev]->cmd, 0, sizeof (audio_engines[dev]->cmd));
memset (audio_engines[dev]->label, 0, sizeof (audio_engines[dev]->label));
memset (audio_engines[dev]->song_name, 0,
sizeof (audio_engines[dev]->song_name));
audio_engines[dev]->pid = -1;
MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
Free the DMA buffer(s)
if (adev->dmask & DMASK_OUT)
if (adev->dmap_out->dmabuf != NULL)
{
if (adev->d->adrv_free_buffer != NULL)
{
adev->d->adrv_free_buffer (dev, adev->dmap_out, OPEN_WRITE);
}
else
default_free_buffer (dev, adev->dmap_out, OPEN_WRITE);
adev->dmap_out->dmabuf = NULL;
}
if (adev->dmask & DMASK_IN)
if (adev->dmap_in != adev->dmap_out && adev->dmap_in->dmabuf != NULL)
{
if (adev->d->adrv_free_buffer != NULL)
{
adev->d->adrv_free_buffer (dev, adev->dmap_in, OPEN_READ);
}
else
default_free_buffer (dev, adev->dmap_in, OPEN_READ);
adev->dmap_in->dmabuf = NULL;
}
MUTEX_ENTER_IRQDISABLE (adev->mutex, flags);
adev->open_mode = 0;
adev->flags &= ~ADEV_OPENED;
MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
}
static void audio_outputintr (int dev, int intr_flags);
static void audio_inputintr (int dev, int intr_flags);
/*ARGSUSED*/
int
oss_audio_open_engine (int dev, int no_worries, struct fileinfo *file,
int recursive, int open_flags, int *newdev)
{
Open audio engine
adev_p adev;
int retval = 0, fmt;
int mode;
int saved_cooked;
int channels, rate;
oss_native_word flags;
extern int cooked_enable; /* Config option */
DOWN_STATUS (0xff);
if (file)
{
mode = file->mode & O_ACCMODE;
}
else
mode = OPEN_READ | OPEN_WRITE;
sync_seed++;
DDB (cmn_err
(CE_CONT, "oss_audio_open_engine(%d, mode=0x%x)\n", dev, mode));
#ifdef DO_TIMINGS
oss_timing_printf ("-----> oss_audio_open_engine(%d, mode=0x%x)", dev, mode);
#endif
if (dev < 0 || dev >= num_audio_engines || audio_engines == NULL)
return OSS_ENXIO;
adev = audio_engines[dev];
if (adev->unloaded)
return OSS_ENODEV;
if (!adev->enabled)
return OSS_ENXIO;
if (adev->osdev == NULL)
{
cmn_err (CE_WARN, "adev->osdev==NULL\n");
return OSS_EIO;
}
open_flags = get_open_flags (mode, open_flags, file);
MUTEX_ENTER_IRQDISABLE (adev->mutex, flags);
if (adev->open_mode != 0)
{
MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
return OSS_EBUSY;
}
adev->open_mode = mode;
MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
adev->cooked_enable = cooked_enable; /* This must be done before drv->open() */
#ifdef DO_TIMINGS
if (adev->cooked_enable)
oss_do_timing ("Initial cooked mode ON");
else
oss_do_timing ("Initial cooked mode OFF");
#endif
adev->src_quality = src_quality;
if ((retval = adev->d->adrv_open (dev, mode, open_flags)) < 0)
{
adev->open_mode = 0;
return retval;
}
MUTEX_ENTER_IRQDISABLE (adev->mutex, flags);
adev->outputintr = audio_outputintr;
adev->inputintr = audio_inputintr;
adev->forced_nonblock = 0;
adev->setfragment_warned = 0;
adev->getispace_error_count = 0;
adev->sync_next = NULL;
adev->sync_group = 0;
adev->sync_flags = 0;
adev->open_flags = open_flags;
adev->flags |= ADEV_OPENED;
adev->timeout_count = 0;
adev->policy = -1;
adev->song_name[0] = 0;
adev->label[0] = 0;
if (open_flags & (OF_NOCONV | OF_MMAP))
{
adev->cooked_enable = 0;
#ifdef DO_TIMINGS
oss_do_timing ("Forcing cooked mode OFF");
#endif
}
memset (audio_engines[dev]->cmd, 0, sizeof (audio_engines[dev]->cmd));
if (recursive)
{
strcpy (audio_engines[dev]->cmd, "OSS internal");
audio_engines[dev]->pid = 0;
}
else
{
char *cmd;
if ((cmd = GET_PROCESS_NAME (file)) != NULL)
{
strncpy (audio_engines[dev]->cmd, cmd, 15);
strncpy (audio_engines[dev]->label, cmd, 15);
}
audio_engines[dev]->pid = GET_PROCESS_PID (file);
}
adev->dmask = 0;
Find out which dmap structures are required.
if (mode == OPEN_WRITE)
adev->dmask = DMASK_OUT;
else if (mode == OPEN_READ)
adev->dmask = DMASK_IN;
else
{
if (mode != (OPEN_WRITE | OPEN_READ))
cmn_err (CE_NOTE, "Unrecognized open mode %x\n", mode);
adev->dmask = DMASK_OUT;
if ((adev->flags & ADEV_DUPLEX) && adev->dmap_out != adev->dmap_in)
adev->dmask = DMASK_OUT | DMASK_IN;
}
#if 0
Handling of non-blocking doesn't belong here. It will be handled by read/write.
if (file != NULL && !(open_flags & OF_BLOCK))
if (ISSET_FILE_FLAG (file, O_NONBLOCK) || adev->forced_nonblock)
{
#ifdef DO_TIMINGS
oss_do_timing ("*** Non-blocking open ***");
#endif
adev->nonblock = 1;
}
#endif
MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
if (adev->dmask & DMASK_OUT)
{
Use small DMA buffer if requested and if the device supports it.
if (SMALL_DMABUF_SIZE >= (adev->min_block * 2) &&
(open_flags & OF_SMALLBUF))
adev->dmap_out->flags |= DMAP_SMALLBUF;
else
if (MEDIUM_DMABUF_SIZE >= (adev->min_block * 2) &&
(open_flags & OF_MEDIUMBUF))
adev->dmap_out->flags |= DMAP_MEDIUMBUF;
else
adev->dmap_out->flags &= ~(DMAP_SMALLBUF | DMAP_MEDIUMBUF);
if ((retval = init_dmap (adev, adev->dmap_out, OPEN_WRITE)) < 0)
{
oss_audio_release (dev, file);
return retval;
}
}
if (adev->dmask & DMASK_IN)
{
Use small DMA buffer if requested and if the device supports it.
if (SMALL_DMABUF_SIZE >= (adev->min_block * 2) &&
(open_flags & OF_SMALLBUF))
adev->dmap_in->flags |= DMAP_SMALLBUF;
else
adev->dmap_in->flags &= ~DMAP_SMALLBUF;
if ((retval = init_dmap (adev, adev->dmap_in, OPEN_READ)) < 0)
{
oss_audio_release (dev, file);
return retval;
}
}
adev->dmap_out->num_errors = 0;
adev->dmap_in->num_errors = 0;
if ((mode & OPEN_WRITE) && !(adev->flags & ADEV_NOOUTPUT))
{
oss_reset_wait_queue (adev->out_wq);
}
if ((mode & OPEN_READ) && !(adev->flags & ADEV_NOINPUT))
{
oss_reset_wait_queue (adev->in_wq);
}
adev->xformat_mask = 0;
switch (adev->dmask)
{
case DMASK_OUT:
adev->xformat_mask = adev->oformat_mask;
break;
case DMASK_IN:
adev->xformat_mask = adev->iformat_mask;
break;
case DMASK_OUT | DMASK_IN:
adev->xformat_mask = adev->oformat_mask & adev->iformat_mask;
break;
default:
cmn_err (CE_WARN, "Internal error A during open\n");
}
fmt = DSP_DEFAULT_FMT;
rate = DSP_DEFAULT_SPEED;
if (adev->flags & ADEV_FIXEDRATE && adev->fixed_rate != 0)
rate = adev->fixed_rate;
if (adev->flags & ADEV_16BITONLY)
fmt = AFMT_S16_LE;
adev->go = 1;
adev->enable_bits = adev->open_mode;
channels = 1;
if (adev->flags & ADEV_STEREOONLY)
channels = 2;
saved_cooked = adev->cooked_enable;
adev->cooked_enable = 0;
oss_audio_set_format (dev, fmt, adev->xformat_mask);
oss_audio_set_channels (dev, channels);
oss_audio_set_rate (dev, rate);
adev->cooked_enable = saved_cooked;
/* Clear the Cooked mode to permit mmap() */
if (adev->dmask & DMASK_OUT)
adev->dmap_out->flags &= ~DMAP_COOKED;
if (adev->dmask & DMASK_IN)
adev->dmap_in->flags &= ~DMAP_COOKED;
#ifdef DO_TIMINGS
{
char *p_name;
pid_t proc_pid;
oss_timing_printf ("=-=-=- Audio device %d (%s) opened, mode %x -=-=-=",
adev->engine_num, adev->name, mode);
if ((proc_pid = GET_PROCESS_PID (file)) != -1)
oss_timing_printf ("Opened by PID: %d", proc_pid);
if ((p_name = GET_PROCESS_NAME (file)) != NULL)
oss_timing_printf ("Opening process name: %s", p_name);
}
#endif
#ifdef CONFIG_OSSD
ossd_event (adev->engine_num, OSSD_EV_OPEN);
#endif
return adev->engine_num;
}
#ifdef VDEV_SUPPORT
int
oss_audio_open_devfile (int dev, int dev_class, struct fileinfo *file,
int recursive, int open_flags, int *newdev)
{
Open audio device file (by calling oss_audio_open_engine).
int err, d, n;
int open_excl = 0;
adev_p adev;
int mode = OPEN_READ | OPEN_WRITE;
#ifdef DO_TIMINGS
oss_timing_printf ("-----> oss_audio_open_devfile(%d, mode=0x%x)", dev, mode);
#endif
if (file)
{
mode = file->mode & O_ACCMODE;
open_flags = get_open_flags (mode, open_flags, file);
if (file->acc_flags & O_EXCL)
{
open_excl = 1;
}
}
DDB (cmn_err
(CE_CONT, "oss_audio_open_devfile(%d, mode=0x%x, excl=%d)\n", dev,
mode, open_excl));
if (dev < 0 || dev >= num_audio_devfiles)
{
return OSS_ENODEV;
}
adev = audio_devfiles[dev];
dev = adev->engine_num;
n=0;
while (adev->d->adrv_redirect != NULL)
{
int next_dev = dev;
if (n++ > num_audio_engines)
{
cmn_err(CE_CONT, "Recursive audio device redirection\n");
return OSS_ELOOP;
}
next_dev = adev->d->adrv_redirect (dev, mode, open_flags);
#ifdef DO_TIMINGS
oss_timing_printf ("Engine redirect %d -> %d\n", dev, next_dev);
#endif
if (next_dev == dev) /* No change */
break;
if (next_dev < 0 || next_dev >= num_audio_engines)
return OSS_ENXIO;
dev = next_dev;
adev = audio_engines[dev];
}
Check Exclusive mode open - do not do any kind of device sharing. For the time being this mode is not supported with devices that do hardware mixing.
if (open_excl && !(adev->flags & ADEV_HWMIX))
{
DDB (cmn_err
(CE_CONT, "Exclusive open, engine=%d\n", adev->engine_num));
if ((dev = oss_audio_open_engine (adev->engine_num, dev_class, file,
recursive, open_flags, newdev)) < 0)
{
return dev;
}
goto done;
}
#ifdef CONFIG_OSS_VMIX
Get a vmix engine if the device has vmix support enabled.
if (!vmix_disabled)
if (adev->vmix_mixer != NULL) // Virtual mixer attached
{
int vmix_dev;
Create a new vmix client instance.
if ((vmix_dev=vmix_create_client(adev->vmix_mixer))>=0)
{
#ifdef DO_TIMINGS
oss_timing_printf ("Vmix redirect %d -> %d\n", dev, vmix_dev);
#endif
if ((dev = oss_audio_open_engine (vmix_dev, dev_class, file,
recursive, open_flags, newdev)) < 0)
{
//cmn_err(CE_WARN, "Failed to open vmix engine %d, err=%d\n", vmix_dev, dev);
return dev;
}
#ifdef DO_TIMINGS
oss_timing_printf ("Vmix engine opened %d -> %d\n", vmix_dev, dev);
#endif
goto done;
}
}
#endif
#if 0
Follow redirection chain for the device.
(TODO: This feature was for earlier versions of vmix/softoss and not in use for the time being. However it is possible that some other driver finds use for it in the future).
if (!open_excl)
{
if (mode == OPEN_WRITE)
{
redirect = adev->redirect_out;
}
else if (mode == OPEN_READ)
{
redirect = adev->redirect_in;
}
else
{
Read-write open. Check that redirection for both directions are identical.
if (adev->redirect_out == adev->redirect_in)
redirect = adev->redirect_out;
}
}
DDB (cmn_err
(CE_CONT, "Redirect=%d (%d, %d)\n", redirect, adev->redirect_out,
adev->redirect_in));
if (redirect >= 0 && redirect < num_audio_engines)
{
adev = audio_engines[redirect];
DDB (cmn_err
(CE_CONT, "Redirect devfile %d -> engine=%d\n", dev,
adev->engine_num));
dev = redirect;
}
#endif
while (adev != NULL)
{
DDB (cmn_err (CE_CONT, "Trying engine=%d\n", adev->engine_num));
if (!adev->enabled)
return OSS_EAGAIN;
if (adev->unloaded)
return OSS_EAGAIN;
*newdev = adev->engine_num;
if ((err =
oss_audio_open_engine (adev->engine_num, dev_class, file,
recursive, open_flags, newdev)) < 0)
{
Check if the device was busy and if it has a shadow device.
if (err != OSS_EBUSY || adev->next_out == NULL)
return err;
adev = adev->next_out;
}
else
{
dev = adev->engine_num;
DDB (cmn_err (CE_CONT, "Engine %d opened\n", adev->engine_num));
goto done;
}
}
return OSS_EBUSY;
done:
Try to find which minor number matches this /dev/dsp# device.
if ((d = oss_find_minor (OSS_DEV_DSP_ENGINE, dev)) < 0)
{
oss_audio_release (dev, file);
return d;
}
*newdev = d;
return dev;
}
#endif
static struct
{
int sz, nfrags;
} policies[] =
{
{
8, 2}, /* 0 */
{
16, 2}, /* 1 */
{
128, 2}, /* 2 */
{
256, 4}, /* 3 */
{
512, 4}, /* 4 */
{
1024, 0}, /* 5 */
{
2048, 0}, /* 6 */
{
4096, 0}, /* 7 */
{
16384, 0}, /* 8 */
{
32768, 0}, /* 9 */
{
256 *1024, 0}, /* 10 */
};
static void
setup_fragments (adev_p adev, dmap_p dmap, int direction)
{
int sz, maxfrags = 100000;
int min;
extern int max_intrate;
if (adev->max_intrate == 0 || adev->max_intrate > max_intrate)
adev->max_intrate = max_intrate;
if (dmap->flags & DMAP_FRAGFIXED)
return;
dmap->flags |= DMAP_FRAGFIXED;
#ifdef DO_TIMINGS
oss_timing_printf ("setup_fragments(%d, dir=%d)", adev->engine_num, direction);
#endif
sz = 1024;
dmap->low_water_rq = 0;
dmap->low_water = -1;
if (dmap->fragsize_rq != 0) /* SNDCTL_DSP_SETFRAGMENT was called */
{
int v;
v = dmap->fragsize_rq & 0xffff;
sz = (1 << v);
if (dmap->expand_factor != UNIT_EXPAND)
{
int sz2;
sz2 = (sz * dmap->expand_factor) / UNIT_EXPAND;
if (sz < sz2)
{
while (sz < sz2)
sz *= 2;
}
else if (sz > sz2)
{
while (sz > sz2)
sz /= 2;
}
}
if (sz < 8)
sz = 8;
if (sz > dmap->buffsize / 2)
sz = dmap->buffsize / 2;
maxfrags = (dmap->fragsize_rq >> 16) & 0x7fff;
if (maxfrags == 0)
maxfrags = 0x7fff;
else if (maxfrags < 2)
maxfrags = 2;
if (maxfrags < adev->min_fragments)
maxfrags = adev->min_fragments;
else if (adev->max_fragments >= 2 && maxfrags > adev->max_fragments)
maxfrags = adev->max_fragments;
#if 1
Workaround for realplay
if (adev->open_flags & OF_SMALLFRAGS)
while (sz > dmap->buffsize / 8)
{
maxfrags *= 2;
sz /= 2;
}
#endif
}
if (adev->policy >= 0 && adev->policy <= 10)
{
sz = policies[adev->policy].sz;
maxfrags = policies[adev->policy].nfrags;
if (maxfrags == 0) /* Unlimited */
maxfrags = 0xffff;
}
#if 1
/* Use short fragments if ossd has registered this device */
if (adev->ossd_registered)
sz = 256;
#endif
/* Sanity check */
if (adev->min_block && adev->max_block)
if (adev->min_block > adev->max_block)
adev->min_block = adev->max_block = 0;
if (adev->max_block > 0 && sz > adev->max_block)
sz = adev->max_block;
if (adev->min_block > 0 && sz < adev->min_block)
sz = adev->min_block;
if (sz < 8)
sz = 8;
/* Ensure that the interrupt rate is within the limits */
min = 0;
if (adev->max_intrate > 0)
{
int data_rate;
data_rate = dmap->data_rate;
if (data_rate < 8000)
data_rate = adev->hw_parms.rate * 4;
min = data_rate / adev->max_intrate;
if (min > dmap->buffsize / 2)
min = dmap->buffsize / 2;
#ifdef DO_TIMINGS
oss_timing_printf ("Max intrate %d -> min buffer %d", adev->max_intrate,
min);
#endif
}
#if 1
if (dmap->flags & DMAP_COOKED)
if (min < 256)
min = 256;
#endif
if (adev->max_block && min > adev->max_block)
min = adev->max_block;
if (sz < min)
{
while (sz < min)
sz *= 2;
if (adev->max_block > 0 && sz > adev->max_block)
sz = adev->max_block;
}
dmap->fragment_size = sz;
dmap->nfrags = dmap->buffsize / sz;
if (dmap == adev->dmap_out)
{
if (direction == OPEN_WRITE)
{
if (dmap->nfrags > maxfrags)
dmap->nfrags = maxfrags;
}
}
if (adev->d->adrv_setup_fragments)
{
adev->d->adrv_setup_fragments (adev->engine_num, dmap, direction);
}
dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size;
if (dmap->nfrags < 2)
{
dmap->nfrags = 2;
dmap->fragment_size = dmap->bytes_in_use / 2;
}
while (dmap->nfrags < adev->min_fragments)
{
dmap->nfrags *= 2;
dmap->fragment_size /= 2;
}
if (adev->max_fragments >= 2)
while (dmap->nfrags > adev->max_fragments)
{
dmap->nfrags /= 2;
dmap->fragment_size *= 2;
}
while (dmap->nfrags < adev->min_fragments)
{
dmap->nfrags *= 2;
dmap->fragment_size /= 2;
}
dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size;
dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size;
dmap->low_water_rq = dmap->fragment_size;
#if 1
if (dmap->nfrags < 4)
#endif
if (dmap->low_water_rq < dmap->bytes_in_use / 4)
dmap->low_water_rq = dmap->bytes_in_use / 4;
#ifdef DO_TIMINGS
oss_timing_printf ("fragsz=%d, nfrags=%d", dmap->fragment_size, dmap->nfrags);
#endif
}
static int
getblksize (adev_p adev)
{
int size = 4096;
dmap_p dmap;
oss_native_word flags, dflags;
dflags = 0;
MUTEX_ENTER_IRQDISABLE (adev->mutex, flags);
if (adev->dmask & DMASK_IN)
{
dmap = adev->dmap_in;
if (!(dmap->flags & DMAP_FRAGFIXED))
{
MUTEX_ENTER (dmap->mutex, dflags);
setup_fragments (adev, dmap, OPEN_READ);
size = dmap->fragment_size;
MUTEX_EXIT (dmap->mutex, dflags);
}
}
if (adev->dmask & DMASK_OUT)
{
dmap = adev->dmap_out;
if (!(dmap->flags & DMAP_FRAGFIXED))
{
MUTEX_ENTER (dmap->mutex, dflags);
setup_fragments (adev, dmap, OPEN_WRITE);
size = dmap->fragment_size;
MUTEX_EXIT (dmap->mutex, dflags);
}
}
MUTEX_EXIT_IRQRESTORE (adev->mutex, flags);
return size;
}
/*ARGSUSED*/
static oss_uint64_t
get_output_pointer (adev_p adev, dmap_p dmap, int do_adjust)
{
oss_uint64_t ptr;
if (adev->d->adrv_get_output_pointer == NULL)
{
return dmap->fragment_counter * dmap->fragment_size;
}
UP_STATUS (STS_PTR);
ptr =
adev->d->adrv_get_output_pointer (adev->engine_num, dmap,
PCM_ENABLE_OUTPUT) & ~7;
DOWN_STATUS (STS_PTR);
return ptr;
}
static oss_uint64_t
get_input_pointer (adev_p adev, dmap_p dmap, int do_adjust)
{
oss_uint64_t ptr;
if (adev->d->adrv_get_input_pointer == NULL)
return dmap->fragment_counter * dmap->fragment_size;
ptr =
adev->d->adrv_get_input_pointer (adev->engine_num, dmap,
PCM_ENABLE_INPUT) & ~7;
if (ptr < dmap->byte_counter)
ptr += dmap->bytes_in_use;
ptr %= dmap->bytes_in_use;
if (do_adjust)
{
oss_uint64_t tmp = dmap->byte_counter;
tmp = (tmp / dmap->bytes_in_use) * dmap->bytes_in_use;
dmap->byte_counter = tmp + ptr;
}
return ptr;
}
static int
get_optr (adev_p adev, dmap_p dmap, ioctl_arg arg)
{
count_info *info = (count_info *) arg;
oss_native_word flags;
oss_uint64_t bytes;
memset ((char *) info, 0, sizeof (count_info));
if (dmap->dma_mode != PCM_ENABLE_OUTPUT || !(dmap->flags & DMAP_STARTED))
return 0;
MUTEX_ENTER_IRQDISABLE (dmap->mutex, flags);
info->ptr = get_output_pointer (adev, dmap, 1);
info->blocks = dmap->interrupt_count;
dmap->interrupt_count = 0;
bytes = (dmap->byte_counter / dmap->bytes_in_use) * dmap->bytes_in_use;
bytes += info->ptr;
if (bytes < dmap->byte_counter)
bytes += dmap->bytes_in_use;
info->bytes = (unsigned int) bytes;
MUTEX_EXIT_IRQRESTORE (dmap->mutex, flags);
/* Adjust for format conversions */
info->ptr = (info->ptr * UNIT_EXPAND) / dmap->expand_factor;
info->bytes = (info->bytes / dmap->expand_factor) * UNIT_EXPAND;
info->bytes &= 0x3fffffff;
#ifdef DO_TIMINGS
oss_timing_printf ("GETOPTR(%d,%d,%d)", info->bytes, info->ptr, info->blocks);
#endif
return 0;
}
static int
get_iptr (adev_p adev, dmap_p dmap, ioctl_arg arg)
{
count_info *info = (count_info *) arg;
oss_native_word flags;
oss_uint64_t bytes;
memset ((char *) info, 0, sizeof (count_info));
if (dmap->dma_mode != PCM_ENABLE_INPUT || !(dmap->flags & DMAP_STARTED))
return 0;
MUTEX_ENTER_IRQDISABLE (dmap->mutex, flags);
info->ptr = get_input_pointer (adev, dmap, 1);
info->blocks = dmap->interrupt_count;
dmap->interrupt_count = 0;
bytes = (dmap->byte_counter / dmap->bytes_in_use) * dmap->bytes_in_use;
bytes += info->ptr;
if (bytes < dmap->byte_counter)
bytes += dmap->bytes_in_use;
info->bytes = (unsigned int) bytes;
MUTEX_EXIT_IRQRESTORE (dmap->mutex, flags);
/* Adjust for format conversions */
info->ptr = (info->ptr * UNIT_EXPAND) / dmap->expand_factor;
info->bytes = (info->bytes / dmap->expand_factor) * UNIT_EXPAND;
info->bytes &= 0x3fffffff;
#ifdef DO_TIMINGS
oss_timing_printf ("GETIPTR(%d,%d,%d)", info->bytes, info->ptr, info->blocks);
#endif
return 0;
}
#ifndef OSS_NO_LONG_LONG
static int
get_long_optr (adev_p adev, dmap_p dmap, ioctl_arg arg)
{
oss_count_t *ptr = (oss_count_t *) arg;
oss_native_word flags;
memset ((char *) ptr, 0, sizeof (*ptr));
if (dmap->dma_mode != PCM_ENABLE_OUTPUT || !(dmap->flags & DMAP_STARTED))
return 0;
ptr->fifo_samples = 0;
MUTEX_ENTER_IRQDISABLE (dmap->mutex, flags);
if (dmap->frame_size < 1)
dmap->frame_size = 1;
if (dmap->user_frame_size < 1)
dmap->user_frame_size = 1;
get_output_pointer (adev, dmap, 1);
ptr->samples = (unsigned int) (dmap->byte_counter / dmap->frame_size);
/* Adjust for format conversions */
ptr->samples = (ptr->samples * UNIT_EXPAND) / dmap->expand_factor;
if (adev->d->adrv_local_qlen)
{
ptr->fifo_samples =
adev->d->adrv_local_qlen (adev->engine_num) / dmap->frame_size;
ptr->fifo_samples =
(ptr->fifo_samples * UNIT_EXPAND) / dmap->expand_factor;
}
MUTEX_EXIT_IRQRESTORE (dmap->mutex, flags);