patch-2.4.9 linux/drivers/sound/i810_audio.c
Next file: linux/drivers/sound/maestro.c
Previous file: linux/drivers/sound/esssolo1.c
Back to the patch index
Back to the overall index
- Lines: 443
- Date:
Sun Aug 12 17:37:53 2001
- Orig file:
v2.4.8/linux/drivers/sound/i810_audio.c
- Orig date:
Tue Jul 3 17:08:21 2001
diff -u --recursive --new-file v2.4.8/linux/drivers/sound/i810_audio.c linux/drivers/sound/i810_audio.c
@@ -109,6 +109,7 @@
//#define DEBUG
//#define DEBUG2
+//#define DEBUG_INTERRUPTS
#define ADC_RUNNING 1
#define DAC_RUNNING 2
@@ -190,7 +191,7 @@
#define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI)
-#define DRIVER_VERSION "0.02"
+#define DRIVER_VERSION "0.03"
/* magic numbers to protect our data structures */
#define I810_CARD_MAGIC 0x5072696E /* "Prin" */
@@ -281,10 +282,15 @@
wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */
/* redundant, but makes calculations easier */
- unsigned fragsize;
+ /* what the hardware uses */
unsigned dmasize;
+ unsigned fragsize;
unsigned fragsamples;
+ /* what we tell the user to expect */
+ unsigned userfrags;
+ unsigned userfragsize;
+
/* OSS stuff */
unsigned mapped:1;
unsigned ready:1;
@@ -337,9 +343,8 @@
static int i810_open_mixdev(struct inode *inode, struct file *file);
static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg);
-static loff_t i810_llseek(struct file *file, loff_t offset, int origin);
-extern __inline__ unsigned ld2(unsigned int x)
+static inline unsigned ld2(unsigned int x)
{
unsigned r = 0;
@@ -498,7 +503,7 @@
/* get current playback/recording dma buffer pointer (byte offset from LBA),
called with spinlock held! */
-extern __inline__ unsigned i810_get_dma_addr(struct i810_state *state, int rec)
+static inline unsigned i810_get_dma_addr(struct i810_state *state, int rec)
{
struct dmabuf *dmabuf = &state->dmabuf;
unsigned int civ, offset;
@@ -543,13 +548,18 @@
//}
/* Stop recording (lock held) */
-extern __inline__ void __stop_adc(struct i810_state *state)
+static inline void __stop_adc(struct i810_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
struct i810_card *card = state->card;
dmabuf->enable &= ~ADC_RUNNING;
outb(0, card->iobase + PI_CR);
+ // wait for the card to acknowledge shutdown
+ while( inb(card->iobase + PI_CR) != 0 ) ;
+ // now clear any latent interrupt bits (like the halt bit)
+ outb( inb(card->iobase + PI_SR), card->iobase + PI_SR );
+ outl( inl(card->iobase + GLOB_STA) & INT_PI, card->iobase + GLOB_STA);
}
static void stop_adc(struct i810_state *state)
@@ -578,13 +588,18 @@
}
/* stop playback (lock held) */
-extern __inline__ void __stop_dac(struct i810_state *state)
+static inline void __stop_dac(struct i810_state *state)
{
struct dmabuf *dmabuf = &state->dmabuf;
struct i810_card *card = state->card;
dmabuf->enable &= ~DAC_RUNNING;
outb(0, card->iobase + PO_CR);
+ // wait for the card to acknowledge shutdown
+ while( inb(card->iobase + PO_CR) != 0 ) ;
+ // now clear any latent interrupt bits (like the halt bit)
+ outb( inb(card->iobase + PO_SR), card->iobase + PO_SR );
+ outl( inl(card->iobase + GLOB_STA) & INT_PO, card->iobase + GLOB_STA);
}
static void stop_dac(struct i810_state *state)
@@ -710,24 +725,22 @@
dmabuf->numfrag = SG_LEN;
dmabuf->fragsize = dmabuf->dmasize/dmabuf->numfrag;
dmabuf->fragsamples = dmabuf->fragsize >> 1;
+ dmabuf->userfragsize = dmabuf->ossfragsize;
+ dmabuf->userfrags = dmabuf->dmasize/dmabuf->ossfragsize;
memset(dmabuf->rawbuf, 0, dmabuf->dmasize);
if(dmabuf->ossmaxfrags == 4) {
fragint = 8;
- dmabuf->ossfragsize = dmabuf->dmasize>>2;
dmabuf->fragshift = 2;
} else if (dmabuf->ossmaxfrags == 8) {
fragint = 4;
- dmabuf->ossfragsize = dmabuf->dmasize>>3;
dmabuf->fragshift = 3;
} else if (dmabuf->ossmaxfrags == 16) {
fragint = 2;
- dmabuf->ossfragsize = dmabuf->dmasize>>4;
dmabuf->fragshift = 4;
} else {
fragint = 1;
- dmabuf->ossfragsize = dmabuf->dmasize>>5;
dmabuf->fragshift = 5;
}
/*
@@ -862,7 +875,7 @@
dmabuf->error++;
}
}
- if (dmabuf->count > dmabuf->ossfragsize)
+ if (dmabuf->count > dmabuf->userfragsize)
wake_up(&dmabuf->wait);
}
/* error handling and process wake up for DAC */
@@ -890,7 +903,7 @@
dmabuf->error++;
}
}
- if (dmabuf->count < (dmabuf->dmasize-dmabuf->ossfragsize))
+ if (dmabuf->count < (dmabuf->dmasize-dmabuf->userfragsize))
wake_up(&dmabuf->wait);
}
}
@@ -940,6 +953,8 @@
break;
}
}
+ stop_dac(state);
+ synchronize_irq();
remove_wait_queue(&dmabuf->wait, &wait);
current->state = TASK_RUNNING;
if (signal_pending(current))
@@ -977,13 +992,14 @@
status = inw(port + OFF_SR);
#ifdef DEBUG_INTERRUPTS
- printk("NUM %d PORT %lX IRQ ( ST%d ", c->num, c->port, status);
+ printk("NUM %d PORT %X IRQ ( ST%d ", c->num, c->port, status);
#endif
if(status & DMA_INT_COMPLETE)
{
i810_update_ptr(state);
#ifdef DEBUG_INTERRUPTS
- printk("COMP%d ",x);
+ printk("COMP %d ", dmabuf->hwptr /
+ dmabuf->fragsize);
#endif
}
if(status & DMA_INT_LVI)
@@ -1040,11 +1056,6 @@
spin_unlock(&card->lock);
}
-static loff_t i810_llseek(struct file *file, loff_t offset, int origin)
-{
- return -ESPIPE;
-}
-
/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to
the user's buffer. it is filled by the dma machine and drained by this loop. */
static ssize_t i810_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
@@ -1084,9 +1095,10 @@
spin_lock_irqsave(&state->card->lock, flags);
swptr = dmabuf->swptr;
if (dmabuf->count > dmabuf->dmasize) {
- dmabuf->count = 0;
+ dmabuf->count = dmabuf->dmasize;
}
- cnt = dmabuf->count;
+ cnt = dmabuf->count - dmabuf->fragsize;
+ // this is to make the copy_to_user simpler below
if(cnt > (dmabuf->dmasize - swptr))
cnt = dmabuf->dmasize - swptr;
spin_unlock_irqrestore(&state->card->lock, flags);
@@ -1095,13 +1107,11 @@
cnt = count;
if (cnt <= 0) {
unsigned long tmo;
- // are we already running? only start us if we aren't running
- // currently
- i810_update_lvi(state,1);
if(!dmabuf->enable) {
dmabuf->trigger |= PCM_ENABLE_INPUT;
start_adc(state);
}
+ i810_update_lvi(state,1);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
return ret;
@@ -1150,7 +1160,8 @@
ret += cnt;
}
i810_update_lvi(state,1);
- start_adc(state);
+ if(!(dmabuf->enable & ADC_RUNNING))
+ start_adc(state);
return ret;
}
@@ -1194,23 +1205,30 @@
if (dmabuf->count < 0) {
dmabuf->count = 0;
}
- cnt = dmabuf->dmasize - swptr;
- if(cnt > (dmabuf->dmasize - dmabuf->count))
- cnt = dmabuf->dmasize - dmabuf->count;
+ cnt = dmabuf->dmasize - dmabuf->fragsize - dmabuf->count;
+ // this is to make the copy_from_user simpler below
+ if(cnt > (dmabuf->dmasize - swptr))
+ cnt = dmabuf->dmasize - swptr;
spin_unlock_irqrestore(&state->card->lock, flags);
+#ifdef DEBUG2
+ printk(KERN_INFO "i810_audio: i810_write: %d bytes available space\n", cnt);
+#endif
if (cnt > count)
cnt = count;
if (cnt <= 0) {
unsigned long tmo;
// There is data waiting to be played
- i810_update_lvi(state,0);
if(!dmabuf->enable && dmabuf->count) {
/* force the starting incase SETTRIGGER has been used */
/* to stop it, otherwise this is a deadlock situation */
dmabuf->trigger |= PCM_ENABLE_OUTPUT;
start_dac(state);
}
+ // Update the LVI pointer in case we have already
+ // written data in this syscall and are just waiting
+ // on the tail bit of data
+ i810_update_lvi(state,0);
if (file->f_flags & O_NONBLOCK) {
if (!ret) ret = -EAGAIN;
return ret;
@@ -1259,11 +1277,10 @@
}
if (swptr % dmabuf->fragsize) {
x = dmabuf->fragsize - (swptr % dmabuf->fragsize);
- if((x + dmabuf->count) < dmabuf->dmasize)
- memset(dmabuf->rawbuf + swptr, '\0', x);
+ memset(dmabuf->rawbuf + swptr, '\0', x);
}
i810_update_lvi(state,0);
- if (!dmabuf->enable && dmabuf->count >= dmabuf->ossfragsize)
+ if (!dmabuf->enable && dmabuf->count >= dmabuf->userfragsize)
start_dac(state);
return ret;
@@ -1398,8 +1415,6 @@
if (dmabuf->enable != DAC_RUNNING || file->f_flags & O_NONBLOCK)
return 0;
drain_dac(state, 0);
- stop_dac(state);
- synchronize_irq();
dmabuf->ready = 0;
dmabuf->swptr = dmabuf->hwptr = 0;
dmabuf->count = dmabuf->total_bytes = 0;
@@ -1436,7 +1451,7 @@
if (get_user(val, (int *)arg))
return -EFAULT;
if(val==0) {
- ret = -EINVAL;
+ return -EINVAL;
} else {
ret = 1;
}
@@ -1458,9 +1473,9 @@
return val;
}
#ifdef DEBUG
- printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->ossfragsize);
+ printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->userfragsize);
#endif
- return put_user(dmabuf->ossfragsize, (int *)arg);
+ return put_user(dmabuf->userfragsize, (int *)arg);
case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
#ifdef DEBUG
@@ -1545,13 +1560,13 @@
return val;
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
- abinfo.fragsize = dmabuf->ossfragsize;
- abinfo.fragstotal = dmabuf->ossmaxfrags;
+ abinfo.fragsize = dmabuf->userfragsize;
+ abinfo.fragstotal = dmabuf->userfrags;
if(dmabuf->mapped)
abinfo.bytes = dmabuf->count;
else
abinfo.bytes = dmabuf->dmasize - dmabuf->count;
- abinfo.fragments = abinfo.bytes / dmabuf->ossfragsize;
+ abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
spin_unlock_irqrestore(&state->card->lock, flags);
#ifdef DEBUG
printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n", abinfo.bytes,
@@ -1568,10 +1583,10 @@
i810_update_ptr(state);
cinfo.bytes = dmabuf->total_bytes;
cinfo.ptr = dmabuf->hwptr;
- cinfo.blocks = (dmabuf->dmasize - dmabuf->count)/dmabuf->ossfragsize;
+ cinfo.blocks = (dmabuf->dmasize - dmabuf->count)/dmabuf->userfragsize;
if (dmabuf->mapped) {
dmabuf->count = (dmabuf->dmasize -
- (dmabuf->count & (dmabuf->ossfragsize-1)));
+ (dmabuf->count & (dmabuf->userfragsize-1)));
__i810_update_lvi(state, 0);
}
spin_unlock_irqrestore(&state->card->lock, flags);
@@ -1588,10 +1603,10 @@
return val;
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
- abinfo.fragsize = dmabuf->ossfragsize;
- abinfo.fragstotal = dmabuf->ossmaxfrags;
+ abinfo.fragsize = dmabuf->userfragsize;
+ abinfo.fragstotal = dmabuf->userfrags;
abinfo.bytes = dmabuf->dmasize - dmabuf->count;
- abinfo.fragments = abinfo.bytes / dmabuf->ossfragsize;
+ abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
spin_unlock_irqrestore(&state->card->lock, flags);
#ifdef DEBUG
printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n", abinfo.bytes,
@@ -1607,10 +1622,10 @@
spin_lock_irqsave(&state->card->lock, flags);
i810_update_ptr(state);
cinfo.bytes = dmabuf->total_bytes;
- cinfo.blocks = dmabuf->count/dmabuf->ossfragsize;
+ cinfo.blocks = dmabuf->count/dmabuf->userfragsize;
cinfo.ptr = dmabuf->hwptr;
if (dmabuf->mapped) {
- dmabuf->count &= dmabuf->ossfragsize-1;
+ dmabuf->count &= (dmabuf->userfragsize-1);
__i810_update_lvi(state, 1);
}
spin_unlock_irqrestore(&state->card->lock, flags);
@@ -1667,7 +1682,7 @@
dmabuf->count = dmabuf->dmasize;
i810_update_lvi(state,0);
}
- if (!dmabuf->enable && dmabuf->count > dmabuf->fragsize)
+ if (!dmabuf->enable && dmabuf->count > dmabuf->userfragsize)
start_dac(state);
}
if(val & PCM_ENABLE_INPUT) {
@@ -1684,7 +1699,7 @@
i810_update_lvi(state,1);
}
if (!dmabuf->enable && dmabuf->count <
- (dmabuf->dmasize - dmabuf->fragsize))
+ (dmabuf->dmasize - dmabuf->userfragsize))
start_adc(state);
}
return 0;
@@ -1794,8 +1809,6 @@
dmabuf->trigger |= PCM_ENABLE_OUTPUT;
}
- down(&state->open_sem);
-
/* set default sample format. According to OSS Programmer's Guide /dev/dsp
should be default to unsigned 8-bits, mono, with sample rate 8kHz and
/dev/dspW will accept 16-bits sample, but we don't support those so we
@@ -1806,7 +1819,6 @@
dmabuf->subdivision = 0;
state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- up(&state->open_sem);
return 0;
}
@@ -1814,21 +1826,21 @@
static int i810_release(struct inode *inode, struct file *file)
{
struct i810_state *state = (struct i810_state *)file->private_data;
+ struct i810_card *card = state->card;
struct dmabuf *dmabuf = &state->dmabuf;
+ unsigned long flags;
lock_kernel();
- down(&state->open_sem);
-
/* stop DMA state machine and free DMA buffers/channels */
- if(dmabuf->enable == DAC_RUNNING ||
+ if(dmabuf->enable & DAC_RUNNING ||
(dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT))) {
drain_dac(state,0);
- stop_dac(state);
}
if(dmabuf->enable & ADC_RUNNING) {
stop_adc(state);
}
+ spin_lock_irqsave(&card->lock, flags);
dealloc_dmabuf(state);
if (file->f_mode & FMODE_WRITE) {
state->card->free_pcm_channel(state->card, dmabuf->write_channel->num);
@@ -1837,11 +1849,9 @@
state->card->free_pcm_channel(state->card, dmabuf->read_channel->num);
}
- /* we're covered by the open_sem */
- up(&state->open_sem);
-
state->card->states[state->virt] = NULL;
kfree(state);
+ spin_unlock_irqrestore(&card->lock, flags);
unlock_kernel();
return 0;
@@ -1849,7 +1859,7 @@
static /*const*/ struct file_operations i810_audio_fops = {
owner: THIS_MODULE,
- llseek: i810_llseek,
+ llseek: no_llseek,
read: i810_read,
write: i810_write,
poll: i810_poll,
@@ -1910,7 +1920,7 @@
static /*const*/ struct file_operations i810_mixer_fops = {
owner: THIS_MODULE,
- llseek: i810_llseek,
+ llseek: no_llseek,
ioctl: i810_ioctl_mixdev,
open: i810_open_mixdev,
};
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)