📄 cs4218_tdm.c
字号:
break; case AFMT_S16_BE: ct_func = sound.trans_write->ct_s16be; break; case AFMT_U16_BE: ct_func = sound.trans_write->ct_u16be; break; case AFMT_S16_LE: ct_func = sound.trans_write->ct_s16le; break; case AFMT_U16_LE: ct_func = sound.trans_write->ct_u16le; break; } if (ct_func) return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); else return 0;}static ssize_t sound_copy_translate_read(const u_char *userPtr, size_t userCount, u_char frame[], ssize_t *frameUsed, ssize_t frameLeft){ ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL; switch (sound.soft.format) { case AFMT_MU_LAW: ct_func = sound.trans_read->ct_ulaw; break; case AFMT_A_LAW: ct_func = sound.trans_read->ct_alaw; break; case AFMT_S8: ct_func = sound.trans_read->ct_s8; break; case AFMT_U8: ct_func = sound.trans_read->ct_u8; break; case AFMT_S16_BE: ct_func = sound.trans_read->ct_s16be; break; case AFMT_U16_BE: ct_func = sound.trans_read->ct_u16be; break; case AFMT_S16_LE: ct_func = sound.trans_read->ct_s16le; break; case AFMT_U16_LE: ct_func = sound.trans_read->ct_u16le; break; } if (ct_func) return ct_func(userPtr, userCount, frame, frameUsed, frameLeft); else return 0;}/* * /dev/mixer abstraction */static int mixer_open(struct inode *inode, struct file *file){ MOD_INC_USE_COUNT; mixer.busy = 1; return 0;}static int mixer_release(struct inode *inode, struct file *file){ mixer.busy = 0; MOD_DEC_USE_COUNT; return 0;}static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg){ int data; uint tmpcs; if (_SIOC_DIR(cmd) & _SIOC_WRITE) mixer.modify_counter++; if (cmd == OSS_GETVERSION) return IOCTL_OUT(arg, SOUND_VERSION); switch (cmd) { case SOUND_MIXER_INFO: { mixer_info info; strncpy(info.id, "CS4218_TDM", sizeof(info.id)); strncpy(info.name, "CS4218_TDM", sizeof(info.name)); info.name[sizeof(info.name)-1] = 0; info.modify_counter = mixer.modify_counter; if (copy_to_user((int *)arg, &info, sizeof(info))) return -EFAULT; return 0; } case SOUND_MIXER_READ_DEVMASK: data = SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_RECLEV | SOUND_MASK_ALTPCM; return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_RECMASK: data = SOUND_MASK_LINE | SOUND_MASK_MIC; return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_RECSRC: if (cs4218_control & CS_DO1) data = SOUND_MASK_LINE; else data = SOUND_MASK_MIC; return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_RECSRC: IOCTL_IN(arg, data); data &= (SOUND_MASK_LINE | SOUND_MASK_MIC); if (data & SOUND_MASK_LINE) tmpcs = cs4218_control | (CS_ISL | CS_ISR | CS_DO1); if (data & SOUND_MASK_MIC) tmpcs = cs4218_control & ~(CS_ISL | CS_ISR | CS_DO1); if (tmpcs != cs4218_control) cs4218_ctl_write(tmpcs); return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_STEREODEVS: data = SOUND_MASK_VOLUME | SOUND_MASK_RECLEV; return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_CAPS: return IOCTL_OUT(arg, 0); case SOUND_MIXER_READ_VOLUME: data = (cs4218_control & CS_MUTE)? 0: cs_get_volume(cs4218_control); return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_VOLUME: IOCTL_IN(arg, data); return IOCTL_OUT(arg, sound_set_volume(data)); case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ IOCTL_IN(arg, data); beep_volume = data & 0xff; /* fall through */ case SOUND_MIXER_READ_ALTPCM: return IOCTL_OUT(arg, beep_volume); case SOUND_MIXER_WRITE_RECLEV: IOCTL_IN(arg, data); data = cs_set_gain(data); return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_RECLEV: data = cs_get_gain(cs4218_control); return IOCTL_OUT(arg, data); } return -EINVAL;}static struct file_operations mixer_fops ={ owner: THIS_MODULE, llseek: sound_lseek, ioctl: mixer_ioctl, open: mixer_open, release: mixer_release,};static void __init mixer_init(void){ mixer_unit = register_sound_mixer(&mixer_fops, -1); if (mixer_unit < 0) return; mixer.busy = 0; sound.treble = 0; sound.bass = 0; /* Set Line input, no gain, no attenuation. */ cs4218_control = CS_ISL | CS_ISR | CS_DO1; cs4218_control |= CS_LGAIN_SET(0) | CS_RGAIN_SET(0); cs4218_control |= CS_LATTEN_SET(0) | CS_RATTEN_SET(0); cs4218_ctl_write(cs4218_control);}/* * Sound queue stuff, the heart of the driver */static int sq_allocate_buffers(void){ int i; if (sound_buffers) return 0; sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL); if (!sound_buffers) return -ENOMEM; for (i = 0; i < numBufs; i++) { sound_buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL); if (!sound_buffers[i]) { while (i--) sound.mach.dma_free (sound_buffers[i], bufSize << 10); kfree (sound_buffers); sound_buffers = 0; return -ENOMEM; } } return 0;}static void sq_release_buffers(void){ int i; if (sound_buffers) { for (i = 0; i < numBufs; i++) sound.mach.dma_free (sound_buffers[i], bufSize << 10); kfree (sound_buffers); sound_buffers = 0; }}static int sq_allocate_read_buffers(void){ int i; if (sound_read_buffers) return 0; sound_read_buffers = kmalloc(numReadBufs * sizeof(char *), GFP_KERNEL); if (!sound_read_buffers) return -ENOMEM; for (i = 0; i < numBufs; i++) { sound_read_buffers[i] = sound.mach.dma_alloc (readbufSize<<10, GFP_KERNEL); if (!sound_read_buffers[i]) { while (i--) sound.mach.dma_free (sound_read_buffers[i], readbufSize << 10); kfree (sound_read_buffers); sound_read_buffers = 0; return -ENOMEM; } } return 0;}static void sq_release_read_buffers(void){ int i; if (sound_read_buffers) { cpmp->cp_smc[1].smc_smcmr &= ~SMCMR_REN; for (i = 0; i < numReadBufs; i++) sound.mach.dma_free (sound_read_buffers[i], bufSize << 10); kfree (sound_read_buffers); sound_read_buffers = 0; }}static void sq_setup(int numBufs, int bufSize, char **write_buffers){ int i; volatile cbd_t *bdp; volatile cpm8xx_t *cp; volatile smc_t *sp; /* Make sure the SMC transmit is shut down. */ cp = cpmp; sp = &cpmp->cp_smc[1]; sp->smc_smcmr &= ~SMCMR_TEN; sq.max_count = numBufs; sq.max_active = numBufs; sq.block_size = bufSize; sq.buffers = write_buffers; sq.front = sq.count = 0; sq.rear = -1; sq.syncing = 0; sq.active = 0; bdp = tx_base; for (i=0; i<numBufs; i++) { bdp->cbd_bufaddr = virt_to_bus(write_buffers[i]); bdp++; } /* This causes the SMC to sync up with the first buffer again. */ cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_TX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG);}static void read_sq_setup(int numBufs, int bufSize, char **read_buffers){ int i; volatile cbd_t *bdp; volatile cpm8xx_t *cp; volatile smc_t *sp; /* Make sure the SMC receive is shut down. */ cp = cpmp; sp = &cpmp->cp_smc[1]; sp->smc_smcmr &= ~SMCMR_REN; read_sq.max_count = numBufs; read_sq.max_active = numBufs; read_sq.block_size = bufSize; read_sq.buffers = read_buffers; read_sq.front = read_sq.count = 0; read_sq.rear = 0; read_sq.rear_size = 0; read_sq.syncing = 0; read_sq.active = 0; bdp = rx_base; for (i=0; i<numReadBufs; i++) { bdp->cbd_bufaddr = virt_to_bus(read_buffers[i]); bdp->cbd_datlen = read_sq.block_size; bdp++; } /* This causes the SMC to sync up with the first buffer again. */ cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_RX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG);}static void sq_play(void){ (*sound.mach.play)();}/* ++TeSche: radically changed this one too */static ssize_t sq_write(struct file *file, const char *src, size_t uLeft, loff_t *ppos){ ssize_t uWritten = 0; u_char *dest; ssize_t uUsed, bUsed, bLeft; /* ++TeSche: Is something like this necessary? * Hey, that's an honest question! Or does any other part of the * filesystem already checks this situation? I really don't know. */ if (uLeft == 0) return 0; /* The interrupt doesn't start to play the last, incomplete frame. * Thus we can append to it without disabling the interrupts! (Note * also that sq.rear isn't affected by the interrupt.) */ if (sq.count > 0 && (bLeft = sq.block_size-sq.rear_size) > 0) { dest = sq_block_address(sq.rear); bUsed = sq.rear_size; uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); if (uUsed <= 0) return uUsed; src += uUsed; uWritten += uUsed; uLeft -= uUsed; sq.rear_size = bUsed; } do { while (sq.count == sq.max_active) { sq_play(); if (NON_BLOCKING(sq.open_mode)) return uWritten > 0 ? uWritten : -EAGAIN; SLEEP(sq.action_queue); if (SIGNAL_RECEIVED) return uWritten > 0 ? uWritten : -EINTR; } /* Here, we can avoid disabling the interrupt by first * copying and translating the data, and then updating * the sq variables. Until this is done, the interrupt * won't see the new frame and we can work on it * undisturbed. */ dest = sq_block_address((sq.rear+1) % sq.max_count); bUsed = 0; bLeft = sq.block_size; uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft); if (uUsed <= 0) break; src += uUsed; uWritten += uUsed; uLeft -= uUsed; if (bUsed) { sq.rear = (sq.rear+1) % sq.max_count; sq.rear_size = bUsed; sq.count++; } } while (bUsed); /* uUsed may have been 0 */ sq_play(); return uUsed < 0? uUsed: uWritten;}/***********//* Here is how the values are used for reading. * The value 'active' simply indicates the DMA is running. This is * done so the driver semantics are DMA starts when the first read is * posted. The value 'front' indicates the buffer we should next * send to the user. The value 'rear' indicates the buffer the DMA is * currently filling. When 'front' == 'rear' the buffer "ring" is * empty (we always have an empty available). The 'rear_size' is used * to track partial offsets into the current buffer. Right now, I just keep * The DMA running. If the reader can't keep up, the interrupt tosses * the oldest buffer. We could also shut down the DMA in this case. */static ssize_t sq_read(struct file *file, char *dst, size_t uLeft, loff_t *ppos){ ssize_t uRead, bLeft, bUsed, uUsed; if (uLeft == 0) return 0; if (!read_sq.active) CS_Record(); /* Kick off the record process. */ uRead = 0; /* Move what the user requests, depending upon other options. */ while (uLeft > 0) { /* When front == rear, the DMA is not done yet. */ while (read_sq.front == read_sq.rear) { if (NON_BLOCKING(read_sq.open_mode)) { return uRead > 0 ? uRead : -EAGAIN; } SLEEP(read_sq.action_queue); if (SIGNAL_RECEIVED) return uRead > 0 ? uRead : -EINTR; } /* The amount we move is either what is left in the * current buffer or what the user wants. */ bLeft = read_sq.block_size - read_sq.rear_size; bUsed = read_sq.rear_size; uUsed = sound_copy_translate_read(dst, uLeft, read_sq.buffers[read_sq.front], &bUsed, bLeft); if (uUsed <= 0) return uUsed; dst += uUsed; uRead += uUsed; uLeft -= uUsed; read_sq.rear_size += bUsed; if (read_sq.rear_size >= read_sq.block_size) { read_sq.rear_size = 0; read_sq.front++; if (read_sq.front >= read_sq.max_active) read_sq.front = 0; } } return uRead;}static int sq_open(struct inode *inode, struct file *file){ int rc = 0; MOD_INC_USE_COUNT; if (file->f_mode & FMODE_WRITE) { if (sq.busy) { rc = -EBUSY; if (NON_BLOCKING(file->f_flags)) goto err_out; rc = -EINTR; while (sq.busy) { SLEEP(sq.open_queue); if (SIGNAL_RECEIVED) goto err_out; } } sq.busy = 1; /* Let's play spot-the-race-condition */ if (sq_allocate_buffers()) goto err_out_nobusy; sq_setup(numBufs, bufSize<<10,sound_buffers); sq.open_mode = file->f_mode; } if (file->f_mode & FMODE_READ) { if (read_sq.busy) { rc = -EBUSY; if (NON_BLOCKING(file->f_flags)) goto err_out; rc = -EINTR; while (read_sq.busy) { SLEEP(read_sq.open_queue); if (SIGNAL_RECEIVED) goto err_out; } rc = 0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -