extractedLnx/linux-2.6.9/sound/oss/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;
void __user *argp = (void __user *)arg;
int __user *p = argp;
VALIDATE_STATE(state);
mapped = ((file->f_mode & (FMODE_WRITE | FMODE_READ)) && dmabuf->mapped);
pr_debug("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",
_IOC_NR(cmd), arg ? *p : 0);
switch (cmd) {
case OSS_GETVERSION:
ret = put_user(SOUND_VERSION, p);
break;
case SNDCTL_DSP_RESET:
/* FIXME: spin_lock ? */
if (file->f_mode & FMODE_WRITE) {
stop_dac(state);
synchronize_irq(card->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(card->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, p)) {
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, p);
break;
case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
if (get_user(val, p)) {
ret = -EFAULT;
break;
}
if ((ret = lock_set_fmt(state)) < 0)
return ret;
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_playback(state)))
ret = val;
else
ret = put_user(dmabuf->fragsize, p);
break;
}
if (file->f_mode & FMODE_READ) {
if ((val = prog_dmabuf_record(state)))
ret = val;
else
ret = put_user(dmabuf->fragsize, p);
break;
}
/* neither READ nor WRITE? is this even possible? */
ret = -EINVAL;
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, p);
break;
case SNDCTL_DSP_SETFMT: /* Select sample format */
if (get_user(val, p)) {
ret = -EFAULT;
break;
}
if ((ret = lock_set_fmt(state)) < 0)
return ret;
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, p);
break;
case SNDCTL_DSP_CHANNELS:
if (get_user(val, p)) {
ret = -EFAULT;
break;
}
if (val != 0) {
if ((ret = lock_set_fmt(state)) < 0)
return ret;
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, p);
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, p)) {
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, p)) {
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_playback(state)) != 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(argp, &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_record(state)) != 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(argp, &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, p);
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, p);
break;
case SNDCTL_DSP_SETTRIGGER:
if (get_user(val, p)) {
ret = -EFAULT;
break;
}
if (file->f_mode & FMODE_READ) {
if (val & PCM_ENABLE_INPUT) {
if (!dmabuf->ready &&
(ret = prog_dmabuf_record(state)))
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_playback(state)))
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_record(state))
!= 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(argp, &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_playback(state))
!= 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(argp, &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_playback(state)) != 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, p);
break;
case SOUND_PCM_READ_RATE:
ret = put_user(dmabuf->rate, p);
break;
case SOUND_PCM_READ_CHANNELS:
ret = put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
p);
break;
case SOUND_PCM_READ_BITS:
ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ? AFMT_S16_LE :
AFMT_U8, p);
break;
case SNDCTL_DSP_GETCHANNELMASK:
ret = put_user(DSP_BIND_FRONT | DSP_BIND_SURR |
DSP_BIND_CENTER_LFE, p);
break;
case SNDCTL_DSP_BIND_CHANNEL:
if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) {
ret = -EINVAL;
break;
}
if (get_user(val, p)) {
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, p);
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.