📄 dmasound_core.c
字号:
static ssize_t sound_copy_translate(TRANS *trans, 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); switch (dmasound.soft.format) { case AFMT_MU_LAW: ct_func = trans->ct_ulaw; break; case AFMT_A_LAW: ct_func = trans->ct_alaw; break; case AFMT_S8: ct_func = trans->ct_s8; break; case AFMT_U8: ct_func = trans->ct_u8; break; case AFMT_S16_BE: ct_func = trans->ct_s16be; break; case AFMT_U16_BE: ct_func = trans->ct_u16be; break; case AFMT_S16_LE: ct_func = trans->ct_s16le; break; case AFMT_U16_LE: ct_func = trans->ct_u16le; break; default: return 0; } return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);} /* * /dev/mixer abstraction */static struct { int busy; int modify_counter;} mixer;static int mixer_open(struct inode *inode, struct file *file){ dmasound.mach.open(); mixer.busy = 1; return 0;}static int mixer_release(struct inode *inode, struct file *file){ lock_kernel(); mixer.busy = 0; dmasound.mach.release(); unlock_kernel(); return 0;}static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg){ if (_SIOC_DIR(cmd) & _SIOC_WRITE) mixer.modify_counter++; switch (cmd) { case OSS_GETVERSION: return IOCTL_OUT(arg, SOUND_VERSION); case SOUND_MIXER_INFO: { mixer_info info; strncpy(info.id, dmasound.mach.name2, sizeof(info.id)); strncpy(info.name, dmasound.mach.name2, 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; } } if (dmasound.mach.mixer_ioctl) return dmasound.mach.mixer_ioctl(cmd, arg); return -EINVAL;}static struct file_operations mixer_fops ={ owner: THIS_MODULE, llseek: no_llseek, ioctl: mixer_ioctl, open: mixer_open, release: mixer_release,};static void __init mixer_init(void){#ifndef MODULE int mixer_unit;#endif mixer_unit = register_sound_mixer(&mixer_fops, -1); if (mixer_unit < 0) return; mixer.busy = 0; dmasound.treble = 0; dmasound.bass = 0; if (dmasound.mach.mixer_init) dmasound.mach.mixer_init();} /* * Sound queue stuff, the heart of the driver */struct sound_queue dmasound_write_sq;#ifdef HAS_RECORDstruct sound_queue dmasound_read_sq;#endifstatic int sq_allocate_buffers(struct sound_queue *sq, int num, int size){ int i; if (sq->buffers) return 0; sq->numBufs = num; sq->bufSize = size; sq->buffers = kmalloc (num * sizeof(char *), GFP_KERNEL); if (!sq->buffers) return -ENOMEM; for (i = 0; i < num; i++) { sq->buffers[i] = dmasound.mach.dma_alloc(size, GFP_KERNEL); if (!sq->buffers[i]) { while (i--) dmasound.mach.dma_free(sq->buffers[i], size); kfree(sq->buffers); sq->buffers = 0; return -ENOMEM; } } return 0;}static void sq_release_buffers(struct sound_queue *sq){ int i; if (sq->buffers) { if (sq != &write_sq && dmasound.mach.abort_read) dmasound.mach.abort_read(); for (i = 0; i < sq->numBufs; i++) dmasound.mach.dma_free(sq->buffers[i], sq->bufSize); kfree(sq->buffers); sq->buffers = NULL; }}static void sq_setup(struct sound_queue *sq, int max_count, int max_active, int block_size){ void (*setup_func)(void); sq->max_count = max_count; sq->max_active = max_active; sq->block_size = block_size; sq->front = sq->count = sq->rear_size = 0; sq->syncing = 0; sq->active = 0; if (sq == &write_sq) { sq->rear = -1; setup_func = dmasound.mach.write_sq_setup; } else { sq->rear = 0; setup_func = dmasound.mach.read_sq_setup; } if (setup_func) setup_func();}static inline void sq_play(void){ dmasound.mach.play();}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 write_sq.rear isn't affected by the interrupt.) */ if (write_sq.count > 0 && (bLeft = write_sq.block_size-write_sq.rear_size) > 0) { dest = write_sq.buffers[write_sq.rear]; bUsed = write_sq.rear_size; uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft, dest, &bUsed, bLeft); if (uUsed <= 0) return uUsed; src += uUsed; uWritten += uUsed; uLeft -= uUsed; write_sq.rear_size = bUsed; } do { while (write_sq.count == write_sq.max_active) { sq_play(); if (write_sq.open_mode & O_NONBLOCK) return uWritten > 0 ? uWritten : -EAGAIN; SLEEP(write_sq.action_queue); if (signal_pending(current)) return uWritten > 0 ? uWritten : -EINTR; } /* Here, we can avoid disabling the interrupt by first * copying and translating the data, and then updating * the write_sq variables. Until this is done, the interrupt * won't see the new frame and we can work on it * undisturbed. */ dest = write_sq.buffers[(write_sq.rear+1) % write_sq.max_count]; bUsed = 0; bLeft = write_sq.block_size; uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft, dest, &bUsed, bLeft); if (uUsed <= 0) break; src += uUsed; uWritten += uUsed; uLeft -= uUsed; if (bUsed) { write_sq.rear = (write_sq.rear+1) % write_sq.max_count; write_sq.rear_size = bUsed; write_sq.count++; } } while (bUsed); /* uUsed may have been 0 */ sq_play(); return uUsed < 0? uUsed: uWritten;}#ifdef HAS_RECORD /* * 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 && dmasound.mach.record) dmasound.mach.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 (read_sq.open_mode & O_NONBLOCK) { return uRead > 0 ? uRead : -EAGAIN; } SLEEP(read_sq.action_queue); if (signal_pending(current)) 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(dmasound.trans_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;}#endif /* HAS_RECORD */static inline void sq_init_waitqueue(struct sound_queue *sq){ init_waitqueue_head(&sq->action_queue); init_waitqueue_head(&sq->open_queue); init_waitqueue_head(&sq->sync_queue); sq->busy = 0;}static inline void sq_wake_up(struct sound_queue *sq, struct file *file, mode_t mode){ if (file->f_mode & mode) { sq->busy = 0; WAKE_UP(sq->open_queue); }}static int sq_open2(struct sound_queue *sq, struct file *file, mode_t mode, int numbufs, int bufsize){ int rc = 0; if (file->f_mode & mode) { if (sq->busy) { rc = -EBUSY; if (file->f_flags & O_NONBLOCK) return rc; rc = -EINTR; while (sq->busy) { SLEEP(sq->open_queue); if (signal_pending(current)) return rc; } rc = 0; } sq->busy = 1; /* Let's play spot-the-race-condition */ if (sq_allocate_buffers(sq, numbufs, bufsize)) { sq_wake_up(sq, file, mode); return rc; } sq_setup(sq, numbufs, numbufs, bufsize); sq->open_mode = file->f_mode; } return rc;}#define write_sq_init_waitqueue() sq_init_waitqueue(&write_sq)#define write_sq_wake_up(file) sq_wake_up(&write_sq, file, FMODE_WRITE)#define write_sq_release_buffers() sq_release_buffers(&write_sq)#define write_sq_open(file) \ sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize << 10)#ifdef HAS_RECORD#define read_sq_init_waitqueue() sq_init_waitqueue(&read_sq)#define read_sq_wake_up(file) sq_wake_up(&read_sq, file, FMODE_READ)#define read_sq_release_buffers() sq_release_buffers(&read_sq)#define read_sq_open(file) \ sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize << 10)#else /* !HAS_RECORD */#define read_sq_init_waitqueue() do {} while (0)#define read_sq_wake_up(file) do {} while (0)#define read_sq_release_buffers() do {} while (0)#define read_sq_open(file) (0)#endif /* !HAS_RECORD */static int sq_open(struct inode *inode, struct file *file){ int rc; dmasound.mach.open(); if ((rc = write_sq_open(file)) || (rc = read_sq_open(file))) { dmasound.mach.release(); return rc; } if (dmasound.mach.sq_open) dmasound.mach.sq_open(); dmasound.minDev = MINOR(inode->i_rdev) & 0x0f; dmasound.soft = dmasound.dsp; dmasound.hard = dmasound.dsp; sound_init(); if ((MINOR(inode->i_rdev) & 0x0f) == SND_DEV_AUDIO) { sound_set_speed(8000); sound_set_stereo(0); sound_set_format(AFMT_MU_LAW); }#if 0 if (file->f_mode == FMODE_READ && dmasound.mach.record) { /* Start dma'ing straight away */ dmasound.mach.record(); }#endif return 0;}static void sq_reset(void){ sound_silence(); write_sq.active = 0; write_sq.count = 0; write_sq.front = (write_sq.rear+1) % write_sq.max_count;}static int sq_fsync(struct file *filp, struct dentry *dentry)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -