extractedLnx/linux-2.6.9/sound/oss/i810_audio.c_i810_ioctl.c
static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct i810_state *state = (struct i810_state *)file->private_data;
struct i810_channel *c = NULL;
struct dmabuf *dmabuf = &state->dmabuf;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
unsigned int i_glob_cnt;
int val = 0, ret;
struct ac97_codec *codec = state->card->ac97_codec[0];
void __user *argp = (void __user *)arg;
int __user *p = argp;
#ifdef DEBUG
printk("i810_audio: i810_ioctl, arg=0x%x, cmd=", arg ? *p : 0);
#endif
switch (cmd)
{
case OSS_GETVERSION:
#ifdef DEBUG
printk("OSS_GETVERSION\n");
#endif
return put_user(SOUND_VERSION, p);
case SNDCTL_DSP_RESET:
#ifdef DEBUG
printk("SNDCTL_DSP_RESET\n");
#endif
spin_lock_irqsave(&state->card->lock, flags);
if (dmabuf->enable == DAC_RUNNING) {
c = dmabuf->write_channel;
__stop_dac(state);
}
if (dmabuf->enable == ADC_RUNNING) {
c = dmabuf->read_channel;
__stop_adc(state);
}
if (c != NULL) {
I810_IOWRITEB(2, state->card, c->port+OFF_CR); /* reset DMA machine */
while ( I810_IOREADB(state->card, c->port+OFF_CR) & 2 )
cpu_relax();
I810_IOWRITEL((u32)state->card->chandma +
c->num*sizeof(struct i810_channel),
state->card, c->port+OFF_BDBAR);
CIV_TO_LVI(state->card, c->port, 0);
}
spin_unlock_irqrestore(&state->card->lock, flags);
synchronize_irq(state->card->pci_dev->irq);
dmabuf->ready = 0;
dmabuf->swptr = dmabuf->hwptr = 0;
dmabuf->count = dmabuf->total_bytes = 0;
return 0;
case SNDCTL_DSP_SYNC:
#ifdef DEBUG
printk("SNDCTL_DSP_SYNC\n");
#endif
if (dmabuf->enable != DAC_RUNNING || file->f_flags & O_NONBLOCK)
return 0;
if((val = drain_dac(state, 1)))
return val;
dmabuf->total_bytes = 0;
return 0;
case SNDCTL_DSP_SPEED: /* set smaple rate */
#ifdef DEBUG
printk("SNDCTL_DSP_SPEED\n");
#endif
if (get_user(val, p))
return -EFAULT;
if (val >= 0) {
if (file->f_mode & FMODE_WRITE) {
if ( (state->card->ac97_status & SPDIF_ON) ) { /* S/PDIF Enabled */
/* AD1886 only supports 48000, need to check that */
if ( i810_valid_spdif_rate ( codec, val ) ) {
/* Set DAC rate */
i810_set_spdif_output ( state, -1, 0 );
stop_dac(state);
dmabuf->ready = 0;
spin_lock_irqsave(&state->card->lock, flags);
i810_set_dac_rate(state, val);
spin_unlock_irqrestore(&state->card->lock, flags);
/* Set S/PDIF transmitter rate. */
i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, val );
if ( ! (state->card->ac97_status & SPDIF_ON) ) {
val = dmabuf->rate;
}
} else { /* Not a valid rate for S/PDIF, ignore it */
val = dmabuf->rate;
}
} else {
stop_dac(state);
dmabuf->ready = 0;
spin_lock_irqsave(&state->card->lock, flags);
i810_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);
i810_set_adc_rate(state, val);
spin_unlock_irqrestore(&state->card->lock, flags);
}
}
return put_user(dmabuf->rate, p);
case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
#ifdef DEBUG
printk("SNDCTL_DSP_STEREO\n");
#endif
if (dmabuf->enable & DAC_RUNNING) {
stop_dac(state);
}
if (dmabuf->enable & ADC_RUNNING) {
stop_adc(state);
}
return put_user(1, p);
case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE) {
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)))
return val;
}
if (file->f_mode & FMODE_READ) {
if (!dmabuf->ready && (val = prog_dmabuf(state, 1)))
return val;
}
#ifdef DEBUG
printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->userfragsize);
#endif
return put_user(dmabuf->userfragsize, p);
case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
#ifdef DEBUG
printk("SNDCTL_DSP_GETFMTS\n");
#endif
return put_user(AFMT_S16_LE, p);
case SNDCTL_DSP_SETFMT: /* Select sample format */
#ifdef DEBUG
printk("SNDCTL_DSP_SETFMT\n");
#endif
return put_user(AFMT_S16_LE, p);
case SNDCTL_DSP_CHANNELS:
#ifdef DEBUG
printk("SNDCTL_DSP_CHANNELS\n");
#endif
if (get_user(val, p))
return -EFAULT;
if (val > 0) {
if (dmabuf->enable & DAC_RUNNING) {
stop_dac(state);
}
if (dmabuf->enable & ADC_RUNNING) {
stop_adc(state);
}
} else {
return put_user(state->card->channels, p);
}
/* ICH and ICH0 only support 2 channels */
if ( state->card->pci_id == PCI_DEVICE_ID_INTEL_82801AA_5
|| state->card->pci_id == PCI_DEVICE_ID_INTEL_82801AB_5)
return put_user(2, p);
/* Multi-channel support was added with ICH2. Bits in */
/* Global Status and Global Control register are now */
/* used to indicate this. */
i_glob_cnt = I810_IOREADL(state->card, GLOB_CNT);
/* Current # of channels enabled */
if ( i_glob_cnt & 0x0100000 )
ret = 4;
else if ( i_glob_cnt & 0x0200000 )
ret = 6;
else
ret = 2;
switch ( val ) {
case 2: /* 2 channels is always supported */
I810_IOWRITEL(i_glob_cnt & 0xffcfffff,
state->card, GLOB_CNT);
/* Do we need to change mixer settings???? */
break;
case 4: /* Supported on some chipsets, better check first */
if ( state->card->channels >= 4 ) {
I810_IOWRITEL((i_glob_cnt & 0xffcfffff) | 0x100000,
state->card, GLOB_CNT);
/* Do we need to change mixer settings??? */
} else {
val = ret;
}
break;
case 6: /* Supported on some chipsets, better check first */
if ( state->card->channels >= 6 ) {
I810_IOWRITEL((i_glob_cnt & 0xffcfffff) | 0x200000,
state->card, GLOB_CNT);
/* Do we need to change mixer settings??? */
} else {
val = ret;
}
break;
default: /* nothing else is ever supported by the chipset */
val = ret;
break;
}
return put_user(val, p);
case SNDCTL_DSP_POST: /* the user has sent all data and is notifying us */
/* we update the swptr to the end of the last sg segment then return */
#ifdef DEBUG
printk("SNDCTL_DSP_POST\n");
#endif
if(!dmabuf->ready || (dmabuf->enable != DAC_RUNNING))
return 0;
if((dmabuf->swptr % dmabuf->fragsize) != 0) {
val = dmabuf->fragsize - (dmabuf->swptr % dmabuf->fragsize);
dmabuf->swptr += val;
dmabuf->count += val;
}
return 0;
case SNDCTL_DSP_SUBDIVIDE:
if (dmabuf->subdivision)
return -EINVAL;
if (get_user(val, p))
return -EFAULT;
if (val != 1 && val != 2 && val != 4)
return -EINVAL;
#ifdef DEBUG
printk("SNDCTL_DSP_SUBDIVIDE %d\n", val);
#endif
dmabuf->subdivision = val;
dmabuf->ready = 0;
return 0;
case SNDCTL_DSP_SETFRAGMENT:
if (get_user(val, p))
return -EFAULT;
dmabuf->ossfragsize = 1<<(val & 0xffff);
dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags)
return -EINVAL;
/*
* Bound the frag size into our allowed range of 256 - 4096
*/
if (dmabuf->ossfragsize < 256)
dmabuf->ossfragsize = 256;
else if (dmabuf->ossfragsize > 4096)
dmabuf->ossfragsize = 4096;
/*
* The numfrags could be something reasonable, or it could
* be 0xffff meaning "Give me as much as possible". So,
* we check the numfrags * fragsize doesn't exceed our
* 64k buffer limit, nor is it less than our 8k minimum.
* If it fails either one of these checks, then adjust the
* number of fragments, not the size of them. It's OK if
* our number of fragments doesn't equal 32 or anything
* like our hardware based number now since we are using
* a different frag count for the hardware. Before we get
* into this though, bound the maxfrags to avoid overflow
* issues. A reasonable bound would be 64k / 256 since our
* maximum buffer size is 64k and our minimum frag size is
* 256. On the other end, our minimum buffer size is 8k and
* our maximum frag size is 4k, so the lower bound should
* be 2.
*/
if(dmabuf->ossmaxfrags > 256)
dmabuf->ossmaxfrags = 256;
else if (dmabuf->ossmaxfrags < 2)
dmabuf->ossmaxfrags = 2;
val = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
while (val < 8192) {
val <<= 1;
dmabuf->ossmaxfrags <<= 1;
}
while (val > 65536) {
val >>= 1;
dmabuf->ossmaxfrags >>= 1;
}
dmabuf->ready = 0;
#ifdef DEBUG
printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val,
dmabuf->ossfragsize, dmabuf->ossmaxfrags);
#endif
return 0;
case SNDCTL_DSP_GETOSPACE:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
return val;
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
abinfo.fragsize = dmabuf->userfragsize;
abinfo.fragstotal = dmabuf->userfrags;
if (dmabuf->mapped)
abinfo.bytes = dmabuf->dmasize;
else
abinfo.bytes = i810_get_free_write_space(state);
abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
spin_unlock_irqrestore(&state->card->lock, flags);
#if defined(DEBUG) || defined(DEBUG_MMAP)
printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n", abinfo.bytes,
abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);
#endif
return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_GETOPTR:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
return val;
spin_lock_irqsave(&state->card->lock, flags);
val = i810_get_free_write_space(state);
cinfo.bytes = dmabuf->total_bytes;
cinfo.ptr = dmabuf->hwptr;
cinfo.blocks = val/dmabuf->userfragsize;
if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
dmabuf->count += val;
dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
__i810_update_lvi(state, 0);
}
spin_unlock_irqrestore(&state->card->lock, flags);
#if defined(DEBUG) || defined(DEBUG_MMAP)
printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes,
cinfo.blocks, cinfo.ptr, dmabuf->count);
#endif
return copy_to_user(argp, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_GETISPACE:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
return val;
spin_lock_irqsave(&state->card->lock, flags);
abinfo.bytes = i810_get_available_read_data(state);
abinfo.fragsize = dmabuf->userfragsize;
abinfo.fragstotal = dmabuf->userfrags;
abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
spin_unlock_irqrestore(&state->card->lock, flags);
#if defined(DEBUG) || defined(DEBUG_MMAP)
printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n", abinfo.bytes,
abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);
#endif
return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_GETIPTR:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
return val;
spin_lock_irqsave(&state->card->lock, flags);
val = i810_get_available_read_data(state);
cinfo.bytes = dmabuf->total_bytes;
cinfo.blocks = val/dmabuf->userfragsize;
cinfo.ptr = dmabuf->hwptr;
if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_INPUT)) {
dmabuf->count -= val;
dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
__i810_update_lvi(state, 1);
}
spin_unlock_irqrestore(&state->card->lock, flags);
#if defined(DEBUG) || defined(DEBUG_MMAP)
printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes,
cinfo.blocks, cinfo.ptr, dmabuf->count);
#endif
return copy_to_user(argp, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_NONBLOCK:
#ifdef DEBUG
printk("SNDCTL_DSP_NONBLOCK\n");
#endif
file->f_flags |= O_NONBLOCK;
return 0;
case SNDCTL_DSP_GETCAPS:
#ifdef DEBUG
printk("SNDCTL_DSP_GETCAPS\n");
#endif
return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND,
p);
case SNDCTL_DSP_GETTRIGGER:
val = 0;
#ifdef DEBUG
printk("SNDCTL_DSP_GETTRIGGER 0x%x\n", dmabuf->trigger);
#endif
return put_user(dmabuf->trigger, p);
case SNDCTL_DSP_SETTRIGGER:
if (get_user(val, p))
return -EFAULT;
#if defined(DEBUG) || defined(DEBUG_MMAP)
printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val);
#endif
/* silently ignore invalid PCM_ENABLE_xxx bits,
* like the other drivers do
*/
if (!(file->f_mode & FMODE_READ ))
val &= ~PCM_ENABLE_INPUT;
if (!(file->f_mode & FMODE_WRITE ))
val &= ~PCM_ENABLE_OUTPUT;
if((file->f_mode & FMODE_READ) && !(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) {
stop_adc(state);
}
if((file->f_mode & FMODE_WRITE) && !(val & PCM_ENABLE_OUTPUT) && dmabuf->enable == DAC_RUNNING) {
stop_dac(state);
}
dmabuf->trigger = val;
if((val & PCM_ENABLE_OUTPUT) && !(dmabuf->enable & DAC_RUNNING)) {
if (!dmabuf->write_channel) {
dmabuf->ready = 0;
dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
if (!dmabuf->write_channel)
return -EBUSY;
}
if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
return ret;
if (dmabuf->mapped) {
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
dmabuf->count = 0;
dmabuf->swptr = dmabuf->hwptr;
dmabuf->count = i810_get_free_write_space(state);
dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
spin_unlock_irqrestore(&state->card->lock, flags);
}
i810_update_lvi(state, 0);
start_dac(state);
}
if((val & PCM_ENABLE_INPUT) && !(dmabuf->enable & ADC_RUNNING)) {
if (!dmabuf->read_channel) {
dmabuf->ready = 0;
dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
if (!dmabuf->read_channel)
return -EBUSY;
}
if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
return ret;
if (dmabuf->mapped) {
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
dmabuf->swptr = dmabuf->hwptr;
dmabuf->count = 0;
spin_unlock_irqrestore(&state->card->lock, flags);
}
i810_update_lvi(state, 1);
start_adc(state);
}
return 0;
case SNDCTL_DSP_SETDUPLEX:
#ifdef DEBUG
printk("SNDCTL_DSP_SETDUPLEX\n");
#endif
return -EINVAL;
case SNDCTL_DSP_GETODELAY:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
val = dmabuf->count;
spin_unlock_irqrestore(&state->card->lock, flags);
#ifdef DEBUG
printk("SNDCTL_DSP_GETODELAY %d\n", dmabuf->count);
#endif
return put_user(val, p);
case SOUND_PCM_READ_RATE:
#ifdef DEBUG
printk("SOUND_PCM_READ_RATE %d\n", dmabuf->rate);
#endif
return put_user(dmabuf->rate, p);
case SOUND_PCM_READ_CHANNELS:
#ifdef DEBUG
printk("SOUND_PCM_READ_CHANNELS\n");
#endif
return put_user(2, p);
case SOUND_PCM_READ_BITS:
#ifdef DEBUG
printk("SOUND_PCM_READ_BITS\n");
#endif
return put_user(AFMT_S16_LE, p);
case SNDCTL_DSP_SETSPDIF: /* Set S/PDIF Control register */
#ifdef DEBUG
printk("SNDCTL_DSP_SETSPDIF\n");
#endif
if (get_user(val, p))
return -EFAULT;
/* Check to make sure the codec supports S/PDIF transmitter */
if((state->card->ac97_features & 4)) {
/* mask out the transmitter speed bits so the user can't set them */
val &= ~0x3000;
/* Add the current transmitter speed bits to the passed value */
ret = i810_ac97_get(codec, AC97_SPDIF_CONTROL);
val |= (ret & 0x3000);
i810_ac97_set(codec, AC97_SPDIF_CONTROL, val);
if(i810_ac97_get(codec, AC97_SPDIF_CONTROL) != val ) {
printk(KERN_ERR "i810_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val);
return -EFAULT;
}
}
#ifdef DEBUG
else
printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n");
#endif
return put_user(val, p);
case SNDCTL_DSP_GETSPDIF: /* Get S/PDIF Control register */
#ifdef DEBUG
printk("SNDCTL_DSP_GETSPDIF\n");
#endif
if (get_user(val, p))
return -EFAULT;
/* Check to make sure the codec supports S/PDIF transmitter */
if(!(state->card->ac97_features & 4)) {
#ifdef DEBUG
printk(KERN_WARNING "i810_audio: S/PDIF transmitter not avalible.\n");
#endif
val = 0;
} else {
val = i810_ac97_get(codec, AC97_SPDIF_CONTROL);
}
//return put_user((val & 0xcfff), p);
return put_user(val, p);
case SNDCTL_DSP_GETCHANNELMASK:
#ifdef DEBUG
printk("SNDCTL_DSP_GETCHANNELMASK\n");
#endif
if (get_user(val, p))
return -EFAULT;
/* Based on AC'97 DAC support, not ICH hardware */
val = DSP_BIND_FRONT;
if ( state->card->ac97_features & 0x0004 )
val |= DSP_BIND_SPDIF;
if ( state->card->ac97_features & 0x0080 )
val |= DSP_BIND_SURR;
if ( state->card->ac97_features & 0x0140 )
val |= DSP_BIND_CENTER_LFE;
return put_user(val, p);
case SNDCTL_DSP_BIND_CHANNEL:
#ifdef DEBUG
printk("SNDCTL_DSP_BIND_CHANNEL\n");
#endif
if (get_user(val, p))
return -EFAULT;
if ( val == DSP_BIND_QUERY ) {
val = DSP_BIND_FRONT; /* Always report this as being enabled */
if ( state->card->ac97_status & SPDIF_ON )
val |= DSP_BIND_SPDIF;
else {
if ( state->card->ac97_status & SURR_ON )
val |= DSP_BIND_SURR;
if ( state->card->ac97_status & CENTER_LFE_ON )
val |= DSP_BIND_CENTER_LFE;
}
} else { /* Not a query, set it */
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
if ( dmabuf->enable == DAC_RUNNING ) {
stop_dac(state);
}
if ( val & DSP_BIND_SPDIF ) { /* Turn on SPDIF */
/* Ok, this should probably define what slots
* to use. For now, we'll only set it to the
* defaults:
*
* non multichannel codec maps to slots 3&4
* 2 channel codec maps to slots 7&8
* 4 channel codec maps to slots 6&9
* 6 channel codec maps to slots 10&11
*
* there should be some way for the app to
* select the slot assignment.
*/
i810_set_spdif_output ( state, AC97_EA_SPSA_3_4, dmabuf->rate );
if ( !(state->card->ac97_status & SPDIF_ON) )
val &= ~DSP_BIND_SPDIF;
} else {
int mask;
int channels;
/* Turn off S/PDIF if it was on */
if ( state->card->ac97_status & SPDIF_ON )
i810_set_spdif_output ( state, -1, 0 );
mask = val & (DSP_BIND_FRONT | DSP_BIND_SURR | DSP_BIND_CENTER_LFE);
switch (mask) {
case DSP_BIND_FRONT:
channels = 2;
break;
case DSP_BIND_FRONT|DSP_BIND_SURR:
channels = 4;
break;
case DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE:
channels = 6;
break;
default:
val = DSP_BIND_FRONT;
channels = 2;
break;
}
i810_set_dac_channels ( state, channels );
/* check that they really got turned on */
if (!(state->card->ac97_status & SURR_ON))
val &= ~DSP_BIND_SURR;
if (!(state->card->ac97_status & CENTER_LFE_ON))
val &= ~DSP_BIND_CENTER_LFE;
}
}
return put_user(val, p);
case SNDCTL_DSP_MAPINBUF:
case SNDCTL_DSP_MAPOUTBUF:
case SNDCTL_DSP_SETSYNCRO:
case SOUND_PCM_WRITE_FILTER:
case SOUND_PCM_READ_FILTER:
#ifdef DEBUG
printk("SNDCTL_* -EINVAL\n");
#endif
return -EINVAL;
}
return -EINVAL;
}
Generated by GNU enscript 1.6.4.