📄 cs46xx.c
字号:
static int cs_midi_open(struct inode *inode, struct file *file){ int minor = MINOR(inode->i_rdev); struct cs_card *card = devs; unsigned long flags; while (card && card->dev_midi != minor) card = card->next; if (!card) return -ENODEV; file->private_data = card; /* wait for device to become free */ down(&card->midi.open_sem); while (card->midi.open_mode & file->f_mode) { if (file->f_flags & O_NONBLOCK) { up(&card->midi.open_sem); return -EBUSY; } up(&card->midi.open_sem); interruptible_sleep_on(&card->midi.open_wait); if (signal_pending(current)) return -ERESTARTSYS; down(&card->midi.open_sem); } spin_lock_irqsave(&card->midi.lock, flags); if (!(card->midi.open_mode & (FMODE_READ | FMODE_WRITE))) { card->midi.ird = card->midi.iwr = card->midi.icnt = 0; card->midi.ord = card->midi.owr = card->midi.ocnt = 0; card->midi.ird = card->midi.iwr = card->midi.icnt = 0; cs461x_pokeBA0(card, BA0_MIDCR, 0x0000000f); /* Enable xmit, rcv. */ cs461x_pokeBA0(card, BA0_HICR, HICR_IEV | HICR_CHGM); /* Enable interrupts */ } if (file->f_mode & FMODE_READ) { card->midi.ird = card->midi.iwr = card->midi.icnt = 0; } if (file->f_mode & FMODE_WRITE) { card->midi.ord = card->midi.owr = card->midi.ocnt = 0; } spin_unlock_irqrestore(&card->midi.lock, flags); card->midi.open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE)); up(&card->midi.open_sem); MOD_INC_USE_COUNT; return 0;}static int cs_midi_release(struct inode *inode, struct file *file){ struct cs_card *card = (struct cs_card *)file->private_data; DECLARE_WAITQUEUE(wait, current); unsigned long flags; unsigned count, tmo; if (file->f_mode & FMODE_WRITE) { current->state = TASK_INTERRUPTIBLE; add_wait_queue(&card->midi.owait, &wait); for (;;) { spin_lock_irqsave(&card->midi.lock, flags); count = card->midi.ocnt; spin_unlock_irqrestore(&card->midi.lock, flags); if (count <= 0) break; if (signal_pending(current)) break; if (file->f_flags & O_NONBLOCK) { remove_wait_queue(&card->midi.owait, &wait); current->state = TASK_RUNNING; return -EBUSY; } tmo = (count * HZ) / 3100; if (!schedule_timeout(tmo ? : 1) && tmo) printk(KERN_DEBUG "cs46xx: midi timed out??\n"); } remove_wait_queue(&card->midi.owait, &wait); current->state = TASK_RUNNING; } down(&card->midi.open_sem); card->midi.open_mode &= (~(file->f_mode & (FMODE_READ | FMODE_WRITE))); up(&card->midi.open_sem); wake_up(&card->midi.open_wait); MOD_DEC_USE_COUNT; return 0;}/* * Midi file operations struct. */static /*const*/ struct file_operations cs_midi_fops = { llseek: cs_llseek, read: cs_midi_read, write: cs_midi_write, poll: cs_midi_poll, open: cs_midi_open, release: cs_midi_release,};static loff_t cs_llseek(struct file *file, loff_t offset, int origin){ return -ESPIPE;}/* * * CopySamples copies 16-bit stereo signed samples from the source to the * destination, possibly converting down to unsigned 8-bit and/or mono. * count specifies the number of output bytes to write. * * Arguments: * * dst - Pointer to a destination buffer. * src - Pointer to a source buffer * count - The number of bytes to copy into the destination buffer. * fmt - CS_FMT_16BIT and/or CS_FMT_STEREO bits * dmabuf - pointer to the dma buffer structure * * NOTES: only call this routine if the output desired is not 16 Signed Stereo * * */static void CopySamples(char *dst, char *src, int count, unsigned fmt, struct dmabuf *dmabuf){ s32 s32AudioSample; s16 *psSrc=(s16 *)src; s16 *psDst=(s16 *)dst; u8 *pucDst=(u8 *)dst; CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs4281: CopySamples()+ ") ); CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO " dst=0x%x src=0x%x count=%d fmt=0x%x\n", (unsigned)dst,(unsigned)src,(unsigned)count,(unsigned)fmt) ); /* * See if the data should be output as 8-bit unsigned stereo. */ if((fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT)) { /* * Convert each 16-bit signed stereo sample to 8-bit unsigned * stereo using rounding. */ psSrc = (s16 *)src; count = count/2; while(count--) { *(pucDst++) = (u8)(((s16)(*psSrc++) + (s16)0x8000) >> 8); } } /* * See if the data should be output at 8-bit unsigned mono. */ else if(!(fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT)) { /* * Convert each 16-bit signed stereo sample to 8-bit unsigned * mono using averaging and rounding. */ psSrc = (s16 *)src; count = count/2; while(count--) { s32AudioSample = ((*psSrc)+(*(psSrc + 1)))/2 + (s32)0x80; if(s32AudioSample > 0x7fff) s32AudioSample = 0x7fff; *(pucDst++) = (u8)(((s16)s32AudioSample + (s16)0x8000) >> 8); psSrc += 2; } } /* * See if the data should be output at 16-bit signed mono. */ else if(!(fmt & CS_FMT_STEREO) && (fmt & CS_FMT_16BIT)) { /* * Convert each 16-bit signed stereo sample to 16-bit signed * mono using averaging. */ psSrc = (s16 *)src; count = count/2; while(count--) { *(psDst++) = (s16)((*psSrc)+(*(psSrc + 1)))/2; psSrc += 2; } }}/* * cs_copy_to_user() * replacement for the standard copy_to_user, to allow for a conversion from * 16 bit to 8 bit and from stereo to mono, if the record conversion is active. * The current CS46xx/CS4280 static image only records in 16bit unsigned Stereo, * so we convert from any of the other format combinations. */static unsigned cs_copy_to_user( struct cs_state *s, void *dest, void *hwsrc, unsigned cnt, unsigned *copied){ struct dmabuf *dmabuf = &s->dmabuf; void *src = hwsrc; /* default to the standard destination buffer addr */ CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO "cs_copy_to_user()+ fmt=0x%x cnt=%d dest=0x%.8x\n", dmabuf->fmt,(unsigned)cnt,(unsigned)dest) ); if(cnt > dmabuf->dmasize) { cnt = dmabuf->dmasize; } if(!cnt) { *copied = 0; return 0; } if(dmabuf->divisor != 1) { if(!dmabuf->tmpbuff) { *copied = cnt/dmabuf->divisor; return 0; } CopySamples((char *)dmabuf->tmpbuff, (char *)hwsrc, cnt, dmabuf->fmt, dmabuf); src = dmabuf->tmpbuff; cnt = cnt/dmabuf->divisor; } if (copy_to_user(dest, src, cnt)) { CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR "cs4281: cs_copy_to_user()- fault dest=0x%x src=0x%x cnt=%d\n", (unsigned)dest,(unsigned)src,cnt) ); *copied = 0; return -EFAULT; } *copied = cnt; CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs4281: cs_copy_to_user()- copied bytes is %d \n",cnt) ); return 0;}/* 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 cs_read(struct file *file, char *buffer, size_t count, loff_t *ppos){ struct cs_card *card=devs; struct cs_state *state; DECLARE_WAITQUEUE(wait, current); struct dmabuf *dmabuf; ssize_t ret = 0; unsigned long flags; unsigned swptr; int cnt; unsigned copied=0; CS_DBGOUT(CS_WAVE_READ, 4, printk("cs461x: cs_read()+ %d\n",count) ); state = (struct cs_state *)card->states[0]; if(!state) return -ENODEV; dmabuf = &state->dmabuf; if (ppos != &file->f_pos) return -ESPIPE; if (dmabuf->mapped) return -ENXIO; if (!dmabuf->ready && (ret = prog_dmabuf(state))) return ret; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; add_wait_queue(&state->dmabuf.wait, &wait); while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); swptr = dmabuf->swptr; cnt = dmabuf->dmasize - swptr; if (dmabuf->count < cnt) cnt = dmabuf->count; if (cnt <= 0) __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&state->card->lock, flags); if (cnt > (count * dmabuf->divisor)) cnt = count * dmabuf->divisor; if (cnt <= 0) { /* buffer is empty, start the dma machine and wait for data to be recorded */ start_adc(state); if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; break; } schedule(); if (signal_pending(current)) { ret = ret ? ret : -ERESTARTSYS; break; } continue; } CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO "_read() copy_to cnt=%d count=%d ", cnt,count) ); CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO " .dmasize=%d .count=%d buffer=0x%.8x ret=%d\n", dmabuf->dmasize,dmabuf->count,(unsigned)buffer,ret) ); if (cs_copy_to_user(state, buffer, (void *)((unsigned)dmabuf->rawbuf + swptr), cnt, &copied)) { if (!ret) ret = -EFAULT; break; } swptr = (swptr + cnt) % dmabuf->dmasize; spin_lock_irqsave(&card->lock, flags); dmabuf->swptr = swptr; dmabuf->count -= cnt; spin_unlock_irqrestore(&card->lock, flags); count -= copied; buffer += copied; ret += copied; start_adc(state); } remove_wait_queue(&state->dmabuf.wait, &wait); set_current_state(TASK_RUNNING); CS_DBGOUT(CS_WAVE_READ, 4, printk("cs461x: cs_read()- %d\n",ret) ); return ret;}/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to the soundcard. it is drained by the dma machine and filled by this loop. */static ssize_t cs_write(struct file *file, const char *buffer, size_t count, loff_t *ppos){ struct cs_card *card=devs; struct cs_state *state; DECLARE_WAITQUEUE(wait, current); struct dmabuf *dmabuf; ssize_t ret = 0; unsigned long flags; unsigned swptr; int cnt; CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 4, printk("cs461x: cs_write called, count = %d\n", count) ); state = (struct cs_state *)card->states[1]; if(!state) return -ENODEV; dmabuf = &state->dmabuf; if (ppos != &file->f_pos) return -ESPIPE; if (dmabuf->mapped) return -ENXIO; if (!dmabuf->ready && (ret = prog_dmabuf(state))) return ret; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; add_wait_queue(&state->dmabuf.wait, &wait); while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); if (dmabuf->count < 0) { /* buffer underrun, we are recovering from sleep_on_timeout, resync hwptr and swptr */ dmabuf->count = 0; dmabuf->swptr = dmabuf->hwptr; } if (dmabuf->underrun) { dmabuf->underrun = 0; dmabuf->hwptr = cs_get_dma_addr(state); dmabuf->swptr = dmabuf->hwptr; } swptr = dmabuf->swptr; cnt = dmabuf->dmasize - swptr; if (dmabuf->count + cnt > dmabuf->dmasize) cnt = dmabuf->dmasize - dmabuf->count; if (cnt <= 0) __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&state->card->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { /* buffer is full, start the dma machine and wait for data to be played */ start_dac(state); if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; break; } schedule(); if (signal_pending(current)) { ret = ret ? ret : -ERESTARTSYS; break; } continue; } if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { if (!ret) ret = -EFAULT; return ret; } swptr = (swptr + cnt) % dmabuf->dmasize; spin_lock_irqsave(&state->card->lock, flags); dmabuf->swptr = swptr; dmabuf->count += cnt; if(dmabuf->count > dmabuf->dmasize) { CS_DBGOUT(CS_WAVE_WRITE | CS_ERROR, 2, printk( "cs46xx: cs_write() d->count > dmasize - resetting\n")); dmabuf->count = dmabuf->dmasize; } dmabuf->en
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -