/*
* sound/oss/audio.c
*
* Device file manager for /dev/audio
*/
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
/*
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
* Thomas Sailer : moved several static variables into struct audio_operations
* (which is grossly misnamed btw.) because they have the same
* lifetime as the rest in there and dynamic allocation saves
* 12k or so
* Thomas Sailer : use more logical O_NONBLOCK semantics
* Daniel Rodriksson: reworked the use of the device specific copy_user
* still generic
* Horst von Brand: Add missing #include <linux/string.h>
* Chris Rankin : Update the module-usage counter for the coprocessor,
* and decrement the counters again if we cannot open
* the audio device.
*/
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/kmod.h>
#include "sound_config.h"
#include "ulaw.h"
#include "coproc.h"
#define NEUTRAL8 0x80
#define NEUTRAL16 0x00
static int dma_ioctl(int dev, unsigned int cmd, void __user *arg);
static int set_format(int dev, int fmt)
{
if (fmt != AFMT_QUERY)
{
audio_devs[dev]->local_conversion = 0;
if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */
{
if (fmt == AFMT_MU_LAW)
{
fmt = AFMT_U8;
audio_devs[dev]->local_conversion = CNV_MU_LAW;
}
else
fmt = AFMT_U8; /* This is always supported */
}
audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt);
audio_devs[dev]->local_format = fmt;
}
else
return audio_devs[dev]->local_format;
if (audio_devs[dev]->local_conversion)
return audio_devs[dev]->local_conversion;
else
return audio_devs[dev]->local_format;
}
int audio_open(int dev, struct file *file)
{
int ret;
int bits;
int dev_type = dev & 0x0f;
int mode = translate_mode(file);
const struct audio_driver *driver;
const struct coproc_operations *coprocessor;
dev = dev >> 4;
if (dev_type == SND_DEV_DSP16)
bits = 16;
else
bits = 8;
if (dev < 0 || dev >= num_audiodevs)
return -ENXIO;
driver = audio_devs[dev]->d;
if (!try_module_get(driver->owner))
return -ENODEV;
if ((ret = DMAbuf_open(dev, mode)) < 0)
goto error_1;
if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
if (!try_module_get(coprocessor->owner))
goto error_2;
if ((ret = coprocessor->open(coprocessor->devc, COPR_PCM)) < 0) {
printk(KERN_WARNING "Sound: Can't access coprocessor device\n");
goto error_3;
}
}
audio_devs[dev]->local_conversion = 0;
if (dev_type == SND_DEV_AUDIO)
set_format(dev, AFMT_MU_LAW);
else
set_format(dev, bits);
audio_devs[dev]->audio_mode = AM_NONE;
return 0;
/*
* Clean-up stack: this is what needs (un)doing if
* we can't open the audio device ...
*/
error_3:
module_put(coprocessor->owner);
error_2:
DMAbuf_release(dev, mode);
error_1:
module_put(driver->owner);
return ret;
}
static void sync_output(int dev)
{
int p, i;
int l;
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
if (dmap->fragment_size <= 0)
return;
dmap->flags |= DMA_POST;
/* Align the write pointer with fragment boundaries */
if ((l = dmap->user_counter % dmap->fragment_size) > 0)
{
int len;
unsigned long offs = dmap->user_counter % dmap->bytes_in_use;
len = dmap->fragment_size - l;
memset(dmap->raw_buf + offs, dmap->neutral_byte, len);
DMAbuf_move_wrpointer(dev, len);
}
/*
* Clean all unused buffer fragments.
*/
p = dmap->qtail;
dmap->flags |= DMA_POST;
for (i = dmap->qlen + 1; i < dmap->nbufs; i++)
{
p = (p + 1)