📄 ymfpci.c
字号:
if ((swptr += cnt) >= dmabuf->dmasize) { swptr -= dmabuf->dmasize; } } dmabuf->swptr = swptr; } else { /* * XXX This is not right if dmabuf->count is small - * about 2*x frame size or less. We cannot count on * on appending and not causing an artefact. * Should use a variation of the count==0 case above. */ swptr = dmabuf->swptr; } cnt = dmabuf->dmasize - swptr; if (dmabuf->count + cnt > dmabuf->dmasize - redzone) cnt = (dmabuf->dmasize - redzone) - dmabuf->count; spin_unlock_irqrestore(&state->unit->reg_lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) {/* P3 */ /* printk("ymf_write: full, count %d swptr %d\n", dmabuf->count, dmabuf->swptr); */ /* * buffer is full, start the dma machine and * wait for data to be played */ spin_lock_irqsave(&state->unit->reg_lock, flags); if (!state->ypcm.running) { ymf_playback_trigger(state->unit, &state->ypcm, 1); } spin_unlock_irqrestore(&state->unit->reg_lock, flags); if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; break; } set_current_state(TASK_INTERRUPTIBLE); schedule(); if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; break; } continue; } if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { if (!ret) ret = -EFAULT; break; } if ((swptr += cnt) >= dmabuf->dmasize) { swptr -= dmabuf->dmasize; } spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->swptr = swptr; dmabuf->count += cnt; /* * Start here is a bad idea - may cause startup click * in /bin/play when dmabuf is not full yet. * However, some broken applications do not make * any use of SNDCTL_DSP_SYNC (Doom is the worst). * One frame is about 5.3ms, Doom write size is 46ms. */ delay = state->format.rate / 20; /* 50ms */ delay <<= state->format.shift; if (dmabuf->count >= delay && !state->ypcm.running) { ymf_playback_trigger(state->unit, &state->ypcm, 1); } spin_unlock_irqrestore(&state->unit->reg_lock, flags); count -= cnt; buffer += cnt; ret += cnt; } set_current_state(TASK_RUNNING); remove_wait_queue(&dmabuf->wait, &waita);/* P3 */ /* printk("ymf_write: dmabuf.count %d\n", dmabuf->count); */ return ret;}static unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait){ struct ymf_state *state = (struct ymf_state *)file->private_data; struct ymf_dmabuf *dmabuf = &state->dmabuf; unsigned long flags; unsigned int mask = 0; if (file->f_mode & (FMODE_WRITE | FMODE_READ)) poll_wait(file, &dmabuf->wait, wait); spin_lock_irqsave(&state->unit->reg_lock, flags); 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->unit->reg_lock, flags); return mask;}static int ymf_mmap(struct file *file, struct vm_area_struct *vma){ struct ymf_state *state = (struct ymf_state *)file->private_data; struct ymf_dmabuf *dmabuf = &state->dmabuf; int ret; unsigned long size; if (vma->vm_flags & VM_WRITE) { if ((ret = prog_dmabuf(state, 0)) != 0) return ret; } else if (vma->vm_flags & VM_READ) { if ((ret = prog_dmabuf(state, 1)) != 0) return ret; } else return -EINVAL; if (vma->vm_pgoff != 0) return -EINVAL; size = vma->vm_end - vma->vm_start; if (size > (PAGE_SIZE << dmabuf->buforder)) return -EINVAL; if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf), size, vma->vm_page_prot)) return -EAGAIN; dmabuf->mapped = 1; return 0;}static int ymf_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct ymf_state *state = (struct ymf_state *)file->private_data; struct ymf_dmabuf *dmabuf = &state->dmabuf; unsigned long flags; audio_buf_info abinfo; count_info cinfo; int val; switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, (int *)arg); case SNDCTL_DSP_RESET: if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->ready = 0; dmabuf->swptr = dmabuf->hwptr = 0; dmabuf->count = dmabuf->total_bytes = 0; spin_unlock_irqrestore(&state->unit->reg_lock, flags); }#if HAVE_RECORD if (file->f_mode & FMODE_READ) { stop_adc(state); synchronize_irq(); dmabuf->ready = 0; dmabuf->swptr = dmabuf->hwptr = 0; dmabuf->count = dmabuf->total_bytes = 0; }#endif return 0; case SNDCTL_DSP_SYNC: if (file->f_mode & FMODE_WRITE) { if (file->f_flags & O_NONBLOCK) { spin_lock_irqsave(&state->unit->reg_lock, flags); if (dmabuf->count != 0 && !state->ypcm.running) { ymf_start_dac(state); } spin_unlock_irqrestore(&state->unit->reg_lock, flags); } else { ymf_wait_dac(state); } } return 0; case SNDCTL_DSP_SPEED: /* set smaple rate */ if (get_user(val, (int *)arg)) return -EFAULT; if (val >= 8000 && val <= 48000) { if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); }#if HAVE_RECORD if (file->f_mode & FMODE_READ) { stop_adc(state); }#endif spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->ready = 0; state->format.rate = val; ymf_pcm_update_shift(&state->format); spin_unlock_irqrestore(&state->unit->reg_lock, flags); } return put_user(state->format.rate, (int *)arg); /* * OSS manual does not mention SNDCTL_DSP_STEREO at all. * All channels are mono and if you want stereo, you * play into two channels with SNDCTL_DSP_CHANNELS. * However, mpg123 uses it. I wonder, why Michael Hipp uses it. */ case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->ready = 0; state->format.voices = val ? 2 : 1; ymf_pcm_update_shift(&state->format); spin_unlock_irqrestore(&state->unit->reg_lock, flags); }#if HAVE_RECORD if (file->f_mode & FMODE_READ) { /* stop_adc(state); */ dmabuf->ready = 0; if(val) dmabuf->fmt |= CS_FMT_STEREO; else dmabuf->fmt &= ~CS_FMT_STEREO; }#endif return 0; case SNDCTL_DSP_GETBLKSIZE: if (file->f_mode & FMODE_WRITE) { if ((val = prog_dmabuf(state, 0))) return val; return put_user(dmabuf->fragsize, (int *)arg); } if (file->f_mode & FMODE_READ) { if ((val = prog_dmabuf(state, 1))) return val; return put_user(dmabuf->fragsize, (int *)arg); } return -EINVAL; case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); case SNDCTL_DSP_SETFMT: /* Select sample format */ if (get_user(val, (int *)arg)) return -EFAULT; if (val == AFMT_S16_LE || val == AFMT_U8) { if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); }#if HAVE_RECORD if (file->f_mode & FMODE_READ) { stop_adc(state); }#endif spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->ready = 0; state->format.format = val; ymf_pcm_update_shift(&state->format); spin_unlock_irqrestore(&state->unit->reg_lock, flags); } return put_user(state->format.format, (int *)arg); case SNDCTL_DSP_CHANNELS: if (get_user(val, (int *)arg)) return -EFAULT; /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_CHANNELS 0x%x\n", val); */ if (val != 0) { if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); if (val == 1 || val == 2) { spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->ready = 0; state->format.voices = val; ymf_pcm_update_shift(&state->format); spin_unlock_irqrestore(&state->unit->reg_lock, flags); } }#if HAVE_RECORD if (file->f_mode & FMODE_READ) { spin_lock_irqsave(&state->unit->reg_lock, flags); stop_adc(state); dmabuf->ready = 0; spin_unlock_irqrestore(&state->unit->reg_lock, flags); }#endif } return put_user(state->format.voices, (int *)arg); case SNDCTL_DSP_POST: /* * Quoting OSS PG: * The ioctl SNDCTL_DSP_POST is a lightweight version of * SNDCTL_DSP_SYNC. It just tells to the driver that there * is likely to be a pause in the output. This makes it * possible for the device to handle the pause more * intelligently. This ioctl doesn't block the application. * * The paragraph above is a clumsy way to say "flush ioctl". * This ioctl is used by mpg123. */ /* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_POST\n"); */ spin_lock_irqsave(&state->unit->reg_lock, flags); if (dmabuf->count != 0 && !state->ypcm.running) { ymf_start_dac(state); } spin_unlock_irqrestore(&state->unit->reg_lock, flags); return 0; case SNDCTL_DSP_SUBDIVIDE: if (dmabuf->subdivision) return -EINVAL; if (get_user(val, (int *)arg)) return -EFAULT; if (val != 1 && val != 2) return -EINVAL; dmabuf->subdivision = val; return 0; case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, (int *)arg)) return -EFAULT; /* P3: these frags are for Doom. Amasingly, it sets [2,2**11]. */ /* P3 */ // printk("ymfpci: ioctl SNDCTL_DSP_SETFRAGMENT 0x%x\n", val); dmabuf->ossfragshift = val & 0xffff; dmabuf->ossmaxfrags = (val >> 16) & 0xffff; switch (dmabuf->ossmaxfrags) { case 1: dmabuf->ossfragshift = 12; return 0; default: /* Fragments must be 2K long */ dmabuf->ossfragshift = 11; dmabuf->ossmaxfrags = 2; } return 0; case SNDCTL_DSP_GETOSPACE: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) return val; spin_lock_irqsave(&state->unit->reg_lock, flags); /* cs_update_ptr(state); */ /* XXX Always up to date? */ abinfo.fragsize = dmabuf->fragsize; abinfo.bytes = dmabuf->dmasize - dmabuf->count; abinfo.fragstotal = dmabuf->numfrag; abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; spin_unlock_irqrestore(&state->unit->reg_lock, flags); return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;#if HAVE_RECORD case SNDCTL_DSP_GETISPACE: if (!(file->f_mode & FMODE_READ)) return -EINVAL; if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) return val; spin_lock_irqsave(&state->card->lock, flags); cs_update_ptr(state); abinfo.fragsize = dmabuf->fragsize; abinfo.bytes = dmabuf->count; abinfo.fragstotal = dmabuf->numfrag; abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; spin_unlock_irqrestore(&state->card->lock, flags); return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;#endif case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_GETCAPS: /* return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, (int *)arg); */ return put_user(0, (int *)arg);#if 0 /* old */ case SNDCTL_DSP_GETTRIGGER: val = 0; if (file->f_mode & FMODE_READ && dmabuf->enable) val |= PCM_ENABLE_INPUT; if (file->f_mode & FMODE_WRITE && dmabuf->enable) val |= PCM_ENABLE_OUTPUT; return put_user(val, (int *)arg); case SNDCTL_DSP_SETTRIGGER: if (get_user(val, (int *)arg)) return -EFAULT; if (file->f_mode & FMODE_READ) { if (val & PCM_ENABLE_INPUT) { if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) return ret; start_adc(state); } else stop_adc(state); } if (file->f_mode & FMODE_WRITE) { if (val & PCM_ENABLE_OUTPUT) { if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) return ret; start_dac(state); // sure? } else stop_dac(state); } return 0;#endif#if HAVE_RECORD case SNDCTL_DSP_GETIPTR: if (!(file->f_mode & FMODE_READ)) return -EINVAL; spin_lock_irqsave(&state->unit->reg_lock, flags); cs_update_ptr(state); cinfo.bytes = dmabuf->total_bytes; cinfo.blocks = dmabuf->count >> dmabuf->fragshift; cinfo.ptr = dmabuf->hwptr; if (dmabuf->mapped) dmabuf->count &= dmabuf->fragsize-1; spin_unlock_irqrestore(&state->unit->reg_lock, flags); return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));#endif case SNDCTL_DSP_GETOPTR: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; spin_lock_irqsave(&state->unit->reg_lock, flags); /* cs_update_ptr(state); */ /* Always up to date */ cinfo.bytes = dmabuf->total_bytes; cinfo.blocks = dmabuf->count >> dmabuf->fragshift; cinfo.ptr = dmabuf->hwptr; if (dmabuf->mapped) dmabuf->count &= dmabuf->fragsize-1; spin_unlock_irqrestore(&state->unit->reg_lock, flags); return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); case SNDCTL_DSP_SETDUPLEX: /* XXX TODO */ return -EINVAL;#if 0 /* old */ case SNDCTL_DSP_GETODELAY: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; spin_lock_irqsave(&state->unit->reg_lock, flags); cs_update_ptr(state); val = dmabuf->count;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -