extractedLnx/linux-2.4.37/drivers/sound/trident.c_trident_ioctl.c
static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct trident_state *state = (struct trident_state *)file->private_data;
struct dmabuf *dmabuf = &state->dmabuf;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
int val, mapped, ret = 0;
struct trident_card *card = state->card;
VALIDATE_STATE(state);
mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) ||
((file->f_mode & FMODE_READ) && dmabuf->mapped);
TRDBG("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",
_IOC_NR(cmd), arg ? *(int *)arg : 0);
switch (cmd)
{
case OSS_GETVERSION:
ret = put_user(SOUND_VERSION, (int *)arg);
break;
case SNDCTL_DSP_RESET:
/* FIXME: spin_lock ? */
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
synchronize_irq();
dmabuf->ready = 0;
dmabuf->swptr = dmabuf->hwptr = 0;
dmabuf->count = dmabuf->total_bytes = 0;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
synchronize_irq();
dmabuf->ready = 0;
dmabuf->swptr = dmabuf->hwptr = 0;
dmabuf->count = dmabuf->total_bytes = 0;
}
break;
case SNDCTL_DSP_SYNC:
if (file->f_mode & FMODE_WRITE)
ret = drain_dac(state, file->f_flags & O_NONBLOCK);
break;
case SNDCTL_DSP_SPEED: /* set smaple rate */
if (get_user(val, (int *)arg))
{
ret = -EFAULT;
break;
}
if (val >= 0) {
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
dmabuf->ready = 0;
spin_lock_irqsave(&state->card->lock, flags);
trident_set_dac_rate(state, val);
spin_unlock_irqrestore(&state->card->lock, flags);
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
dmabuf->ready = 0;
spin_lock_irqsave(&state->card->lock, flags);
trident_set_adc_rate(state, val);
spin_unlock_irqrestore(&state->card->lock, flags);
}
}
ret = put_user(dmabuf->rate, (int *)arg);
break;
case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
if (get_user(val, (int *)arg))
{
ret = -EFAULT;
break;
}
lock_set_fmt(state);
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
dmabuf->ready = 0;
if (val)
dmabuf->fmt |= TRIDENT_FMT_STEREO;
else
dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
dmabuf->ready = 0;
if (val)
dmabuf->fmt |= TRIDENT_FMT_STEREO;
else
dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
}
unlock_set_fmt(state);
break;
case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE) {
if ((val = prog_dmabuf(state, 0)))
ret = val;
else
ret = put_user(dmabuf->fragsize, (int *)arg);
break;
}
if (file->f_mode & FMODE_READ) {
if ((val = prog_dmabuf(state, 1)))
ret = val;
else
ret = put_user(dmabuf->fragsize, (int *)arg);
break;
}
case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
ret = put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg);
break;
case SNDCTL_DSP_SETFMT: /* Select sample format */
if (get_user(val, (int *)arg))
{
ret = -EFAULT;
break;
}
lock_set_fmt(state);
if (val != AFMT_QUERY) {
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
dmabuf->ready = 0;
if (val == AFMT_S16_LE)
dmabuf->fmt |= TRIDENT_FMT_16BIT;
else
dmabuf->fmt &= ~TRIDENT_FMT_16BIT;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
dmabuf->ready = 0;
if (val == AFMT_S16_LE)
dmabuf->fmt |= TRIDENT_FMT_16BIT;
else
dmabuf->fmt &= ~TRIDENT_FMT_16BIT;
}
}
unlock_set_fmt(state);
ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
AFMT_S16_LE : AFMT_U8, (int *)arg);
break;
case SNDCTL_DSP_CHANNELS:
if (get_user(val, (int *)arg))
{
ret = -EFAULT;
break;
}
if (val != 0) {
lock_set_fmt(state);
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
dmabuf->ready = 0;
//prevent from memory leak
if ((state->chans_num > 2) && (state->chans_num != val)) {
ali_free_other_states_resources(state);
state->chans_num = 1;
}
if (val >= 2)
{
dmabuf->fmt |= TRIDENT_FMT_STEREO;
if ((val == 6) && (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)) {
if( card->rec_channel_use_count > 0 )
{
printk(KERN_ERR "trident: Record is working on the card!\n");
ret = -EBUSY;
unlock_set_fmt(state);
break;
}
ret = ali_setup_multi_channels(state->card, 6);
if (ret < 0) {
unlock_set_fmt(state);
break;
}
down(&state->card->open_sem);
ret = ali_allocate_other_states_resources(state, 6);
if (ret < 0) {
up(&state->card->open_sem);
unlock_set_fmt(state);
break;
}
state->card->multi_channel_use_count ++;
up(&state->card->open_sem);
}
else val = 2; /*yield to 2-channels*/
}
else
dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
state->chans_num = val;
}
if (file->f_mode & FMODE_READ) {
stop_adc(state);
dmabuf->ready = 0;
if (val >= 2) {
if (!((file->f_mode & FMODE_WRITE) && (val == 6)))
val = 2;
dmabuf->fmt |= TRIDENT_FMT_STEREO;
}
else
dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
state->chans_num = val;
}
unlock_set_fmt(state);
}
ret = put_user(val, (int *)arg);
break;
case SNDCTL_DSP_POST:
/* Cause the working fragment to be output */
break;
case SNDCTL_DSP_SUBDIVIDE:
if (dmabuf->subdivision)
{
ret = -EINVAL;
break;
}
if (get_user(val, (int *)arg))
{
ret = -EFAULT;
break;
}
if (val != 1 && val != 2 && val != 4)
{
ret = -EINVAL;
break;
}
dmabuf->subdivision = val;
break;
case SNDCTL_DSP_SETFRAGMENT:
if (get_user(val, (int *)arg))
{
ret = -EFAULT;
break;
}
dmabuf->ossfragshift = val & 0xffff;
dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
if (dmabuf->ossfragshift < 4)
dmabuf->ossfragshift = 4;
if (dmabuf->ossfragshift > 15)
dmabuf->ossfragshift = 15;
if (dmabuf->ossmaxfrags < 4)
dmabuf->ossmaxfrags = 4;
break;
case SNDCTL_DSP_GETOSPACE:
if (!(file->f_mode & FMODE_WRITE))
{
ret = -EINVAL;
break;
}
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
{
ret = val;
break;
}
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
abinfo.fragsize = dmabuf->fragsize;
abinfo.bytes = dmabuf->dmasize - dmabuf->count;
abinfo.fragstotal = dmabuf->numfrag;
abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
break;
case SNDCTL_DSP_GETISPACE:
if (!(file->f_mode & FMODE_READ))
{
ret = -EINVAL;
break;
}
if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
{
ret = val;
break;
}
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
abinfo.fragsize = dmabuf->fragsize;
abinfo.bytes = dmabuf->count;
abinfo.fragstotal = dmabuf->numfrag;
abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
spin_unlock_irqrestore(&state->card->lock, flags);
ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
break;
case SNDCTL_DSP_NONBLOCK:
file->f_flags |= O_NONBLOCK;
break;
case SNDCTL_DSP_GETCAPS:
ret = put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND,
(int *)arg);
break;
case SNDCTL_DSP_GETTRIGGER:
val = 0;
if ((file->f_mode & FMODE_READ) && dmabuf->enable)
val |= PCM_ENABLE_INPUT;
if ((file->f_mode & FMODE_WRITE) && dmabuf->enable)
val |= PCM_ENABLE_OUTPUT;
ret = put_user(val, (int *)arg);
break;
case SNDCTL_DSP_SETTRIGGER:
if (get_user(val, (int *)arg))
{
ret = -EFAULT;
break;
}
if (file->f_mode & FMODE_READ) {
if (val & PCM_ENABLE_INPUT) {
if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
break;
start_adc(state);
} else
stop_adc(state);
}
if (file->f_mode & FMODE_WRITE) {
if (val & PCM_ENABLE_OUTPUT) {
if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
break;
start_dac(state);
} else
stop_dac(state);
}
break;
case SNDCTL_DSP_GETIPTR:
if (!(file->f_mode & FMODE_READ))
{
ret = -EINVAL;
break;
}
if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
{
ret = val;
break;
}
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
cinfo.bytes = dmabuf->total_bytes;
cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
cinfo.ptr = dmabuf->hwptr;
if (dmabuf->mapped)
dmabuf->count &= dmabuf->fragsize-1;
spin_unlock_irqrestore(&state->card->lock, flags);
ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo))?-EFAULT:0;
break;
case SNDCTL_DSP_GETOPTR:
if (!(file->f_mode & FMODE_WRITE))
{
ret = -EINVAL;
break;
}
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
{
ret = val;
break;
}
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
cinfo.bytes = dmabuf->total_bytes;
cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
cinfo.ptr = dmabuf->hwptr;
if (dmabuf->mapped)
dmabuf->count &= dmabuf->fragsize-1;
spin_unlock_irqrestore(&state->card->lock, flags);
ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo))?-EFAULT:0;
break;
case SNDCTL_DSP_SETDUPLEX:
ret = -EINVAL;
break;
case SNDCTL_DSP_GETODELAY:
if (!(file->f_mode & FMODE_WRITE))
{
ret = -EINVAL;
break;
}
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
{
ret = val;
break;
}
spin_lock_irqsave(&state->card->lock, flags);
trident_update_ptr(state);
val = dmabuf->count;
spin_unlock_irqrestore(&state->card->lock, flags);
ret = put_user(val, (int *)arg);
break;
case SOUND_PCM_READ_RATE:
ret = put_user(dmabuf->rate, (int *)arg);
break;
case SOUND_PCM_READ_CHANNELS:
ret = put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
(int *)arg);
break;
case SOUND_PCM_READ_BITS:
ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
AFMT_S16_LE : AFMT_U8, (int *)arg);
break;
case SNDCTL_DSP_GETCHANNELMASK:
ret = put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE,
(int *)arg);
break;
case SNDCTL_DSP_BIND_CHANNEL:
if (state->card->pci_id != PCI_DEVICE_ID_SI_7018)
{
ret = -EINVAL;
break;
}
if (get_user(val, (int *)arg))
{
ret = -EFAULT;
break;
}
if (val == DSP_BIND_QUERY) {
val = dmabuf->channel->attribute | 0x3c00;
val = attr2mask[val >> 8];
} else {
dmabuf->ready = 0;
if (file->f_mode & FMODE_READ)
dmabuf->channel->attribute = (CHANNEL_REC|SRC_ENABLE);
if (file->f_mode & FMODE_WRITE)
dmabuf->channel->attribute = (CHANNEL_SPC_PB|SRC_ENABLE);
dmabuf->channel->attribute |= mask2attr[ffs(val)];
}
ret = put_user(val, (int *)arg);
break;
case SNDCTL_DSP_MAPINBUF:
case SNDCTL_DSP_MAPOUTBUF:
case SNDCTL_DSP_SETSYNCRO:
case SOUND_PCM_WRITE_FILTER:
case SOUND_PCM_READ_FILTER:
default:
ret = -EINVAL;
break;
}
return ret;
}
Generated by GNU enscript 1.6.4.