trident.c
来自「linux 内核源代码」· C语言 代码 · 共 2,292 行 · 第 1/5 页
C
2,292 行
} event = inl(TRID_REG(card, T4D_MISCINT)); outl(event | (ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(card, T4D_MISCINT)); spin_unlock(&card->lock); return IRQ_HANDLED; } /* manually clear interrupt status, bad hardware design, blame T^2 */ outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(card, T4D_MISCINT)); spin_unlock(&card->lock); return IRQ_HANDLED;}/* 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_ttrident_read(struct file *file, char __user *buffer, size_t count, loff_t * ppos){ struct trident_state *state = (struct trident_state *)file->private_data; struct dmabuf *dmabuf = &state->dmabuf; ssize_t ret = 0; unsigned long flags; unsigned swptr; int cnt; pr_debug("trident: trident_read called, count = %zd\n", count); VALIDATE_STATE(state); if (dmabuf->mapped) return -ENXIO; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; mutex_lock(&state->sem); if (!dmabuf->ready && (ret = prog_dmabuf_record(state))) goto out; while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); if (dmabuf->count > (signed) dmabuf->dmasize) { /* buffer overrun, we are recovering from */ /* sleep_on_timeout, resync hwptr and swptr, */ /* make process flush the buffer */ dmabuf->count = dmabuf->dmasize; dmabuf->swptr = dmabuf->hwptr; } swptr = dmabuf->swptr; cnt = dmabuf->dmasize - swptr; if (dmabuf->count < cnt) cnt = dmabuf->count; spin_unlock_irqrestore(&state->card->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { unsigned long tmo; /* 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; goto out; } mutex_unlock(&state->sem); /* No matter how much space left in the buffer, */ /* we have to wait until CSO == ESO/2 or CSO == ESO */ /* when address engine interrupts */ tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); tmo >>= sample_shift[dmabuf->fmt]; /* There are two situations when sleep_on_timeout returns, one is when the interrupt is serviced correctly and the process is waked up by ISR ON TIME. Another is when timeout is expired, which means that either interrupt is NOT serviced correctly (pending interrupt) or it is TOO LATE for the process to be scheduled to run (scheduler latency) which results in a (potential) buffer overrun. And worse, there is NOTHING we can do to prevent it. */ if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { pr_debug(KERN_ERR "trident: recording schedule timeout, " "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, dmabuf->hwptr, dmabuf->swptr); /* a buffer overrun, we delay the recovery until next time the while loop begin and we REALLY have space to record */ } if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; goto out; } mutex_lock(&state->sem); if (dmabuf->mapped) { if (!ret) ret = -ENXIO; goto out; } continue; } if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { if (!ret) ret = -EFAULT; goto out; } swptr = (swptr + cnt) % dmabuf->dmasize; spin_lock_irqsave(&state->card->lock, flags); dmabuf->swptr = swptr; dmabuf->count -= cnt; spin_unlock_irqrestore(&state->card->lock, flags); count -= cnt; buffer += cnt; ret += cnt; start_adc(state); }out: mutex_unlock(&state->sem); 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_ttrident_write(struct file *file, const char __user *buffer, size_t count, loff_t * ppos){ struct trident_state *state = (struct trident_state *)file->private_data; struct dmabuf *dmabuf = &state->dmabuf; ssize_t ret; unsigned long flags; unsigned swptr; int cnt; unsigned int state_cnt; unsigned int copy_count; int lret; /* for lock_set_fmt */ pr_debug("trident: trident_write called, count = %zd\n", count); VALIDATE_STATE(state); /* * Guard against an mmap or ioctl while writing */ mutex_lock(&state->sem); if (dmabuf->mapped) { ret = -ENXIO; goto out; } if (!dmabuf->ready && (ret = prog_dmabuf_playback(state))) goto out; if (!access_ok(VERIFY_READ, buffer, count)) { ret = -EFAULT; goto out; } ret = 0; 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; } swptr = dmabuf->swptr; cnt = dmabuf->dmasize - swptr; if (dmabuf->count + cnt > dmabuf->dmasize) cnt = dmabuf->dmasize - dmabuf->count; spin_unlock_irqrestore(&state->card->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { unsigned long tmo; /* 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; goto out; } /* No matter how much data left in the buffer, */ /* we have to wait until CSO == ESO/2 or CSO == ESO */ /* when address engine interrupts */ lock_set_fmt(state); tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); tmo >>= sample_shift[dmabuf->fmt]; unlock_set_fmt(state); mutex_unlock(&state->sem); /* There are two situations when sleep_on_timeout */ /* returns, one is when the interrupt is serviced */ /* correctly and the process is waked up by ISR */ /* ON TIME. Another is when timeout is expired, which */ /* means that either interrupt is NOT serviced */ /* correctly (pending interrupt) or it is TOO LATE */ /* for the process to be scheduled to run */ /* (scheduler latency) which results in a (potential) */ /* buffer underrun. And worse, there is NOTHING we */ /* can do to prevent it. */ if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { pr_debug(KERN_ERR "trident: playback schedule " "timeout, dmasz %u fragsz %u count %i " "hwptr %u swptr %u\n", dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, dmabuf->hwptr, dmabuf->swptr); /* a buffer underrun, we delay the recovery */ /* until next time the while loop begin and */ /* we REALLY have data to play */ } if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; goto out_nolock; } mutex_lock(&state->sem); if (dmabuf->mapped) { if (!ret) ret = -ENXIO; goto out; } continue; } if ((lret = lock_set_fmt(state)) < 0) { ret = lret; goto out; } if (state->chans_num == 6) { copy_count = 0; state_cnt = 0; if (ali_write_5_1(state, buffer, cnt, ©_count, &state_cnt) == -EFAULT) { if (state_cnt) { swptr = (swptr + state_cnt) % dmabuf->dmasize; spin_lock_irqsave(&state->card->lock, flags); dmabuf->swptr = swptr; dmabuf->count += state_cnt; dmabuf->endcleared = 0; spin_unlock_irqrestore(&state->card->lock, flags); } ret += copy_count; if (!ret) ret = -EFAULT; unlock_set_fmt(state); goto out; } } else { if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { if (!ret) ret = -EFAULT; unlock_set_fmt(state); goto out; } state_cnt = cnt; } unlock_set_fmt(state); swptr = (swptr + state_cnt) % dmabuf->dmasize; spin_lock_irqsave(&state->card->lock, flags); dmabuf->swptr = swptr; dmabuf->count += state_cnt; dmabuf->endcleared = 0; spin_unlock_irqrestore(&state->card->lock, flags); count -= cnt; buffer += cnt; ret += cnt; start_dac(state); }out: mutex_unlock(&state->sem);out_nolock: return ret;}/* No kernel lock - we have our own spinlock */static unsigned inttrident_poll(struct file *file, struct poll_table_struct *wait){ struct trident_state *state = (struct trident_state *)file->private_data; struct dmabuf *dmabuf = &state->dmabuf; unsigned long flags; unsigned int mask = 0; VALIDATE_STATE(state); /* * Guard against a parallel poll and write causing multiple * prog_dmabuf events */ mutex_lock(&state->sem); if (file->f_mode & FMODE_WRITE) { if (!dmabuf->ready && prog_dmabuf_playback(state)) { mutex_unlock(&state->sem); return 0; } poll_wait(file, &dmabuf->wait, wait); } if (file->f_mode & FMODE_READ) { if (!dmabuf->ready && prog_dmabuf_record(state)) { mutex_unlock(&state->sem); return 0; } poll_wait(file, &dmabuf->wait, wait); } mutex_unlock(&state->sem); spin_lock_irqsave(&state->card->lock, flags); trident_update_ptr(state); if (file->f_mode & FMODE_READ) { if (dmabuf->count >= (signed) dmabuf->fragsize) mask |= POLLIN | POLLRDNORM; } if (file->f_mode & FMODE_WRITE) { if (dmabuf->mapped) { if (dmabuf->count >= (signed) dmabuf->fragsize) mask |= POLLOUT | POLLWRNORM; } else { if ((signed) dmabuf->dmasize >= dmabuf->count + (signed) dmabuf->fragsize) mask |= POLLOUT | POLLWRNORM; } } spin_unlock_irqrestore(&state->card->lock, flags); return mask;}static inttrident_mmap(struct file *file, struct vm_area_struct *vma){ struct trident_state *state = (struct trident_state *)file->private_data; struct dmabuf *dmabuf = &state->dmabuf; int ret = -EINVAL; unsigned long size; VALIDATE_STATE(state); /* * Lock against poll read write or mmap creating buffers. Also lock * a read or write against an mmap. */ mutex_lock(&state->sem); if (vma->vm_flags & VM_WRITE) { if ((ret = prog_dmabuf_playback(state)) != 0) goto out; } else if (vma->vm_flags & VM_READ) { if ((ret = prog_dmabuf_record(state)) != 0) goto out; } else goto out; ret = -EINVAL; if (vma->vm_pgoff != 0) goto out; size = vma->vm_end - vma->vm_start; if (size > (PAGE_SIZE << dmabuf->buforder)) goto out; ret = -EAGAIN; if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(dmabuf->rawbuf) >> PAGE_SHIFT, size, vma->vm_page_prot)) goto out; dmabuf->mapped = 1; ret = 0;out: mutex_unlock(&state->sem); return ret;}static inttrident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct trident_state *state = (struct trident_state *)file->private_data; struct dmabuf *dmabuf = &state->dmabuf; unsigned long flags; audio_buf_info abinfo; count_info cinfo; int val, mapped, ret = 0; struct trident_card *card = state->card; void __user *argp = (void __user *)arg; int __user *p = argp; VALIDATE_STATE(state); mapped = ((file->f_mode & (FMODE_WRITE | FMODE_READ)) && dmabuf->mapped); pr_debug("trident: trident_ioctl, command = %2d, arg = 0x%08x\n", _IOC_NR(cmd), arg ? *p : 0); switch (cmd) { case OSS_GETVERSION: ret = put_user(SOUND_VERSION, p); break; case SNDCTL_DSP_RESET: /* FIXME: spin_lock ? */ if (file->f_mode & FMODE_WRITE) { stop_dac(state); synchronize_irq(card->irq); dmabuf->ready = 0; dmabuf->swptr = dmabuf->hwptr = 0; dmabuf->count = dmabuf->total_bytes = 0; } if (file->f_mode & FMODE_READ) { stop_adc(state); synchronize_irq(card->irq); dmabuf->ready = 0; dmabuf->swptr = dmabuf->hwptr = 0; dmabuf->count = dmabuf->total_bytes = 0; } break; case SNDCTL_DSP_SYNC: if (file->f_mode & FMODE_WRITE) ret = drain_dac(state, file->f_flags & O_NONBLOCK); break; case SNDCTL_DSP_SPEED: /* set smaple rate */ if (get_user(val, p)) { ret = -EFAULT; break; } if (val >= 0) { if (file->f_mode & FMODE_WRITE) { stop_dac(state); dmabuf->ready = 0; spin_lock_irqsave(&state->card->lock, flags); trident_set_dac_rate(state, val); spin_unlock_irqrestore(&state->card->lock, flags); } if (file-
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?