?? ymfpci.c
字號:
ymf_playback_trigger(unit, &state->wpcm, 1); } spin_unlock_irqrestore(&unit->reg_lock, flags); if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; break; } schedule(); set_current_state(TASK_INTERRUPTIBLE); 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(&unit->reg_lock, flags); if (unit->suspended) { spin_unlock_irqrestore(&unit->reg_lock, flags); continue; } 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->wpcm.running) { ymf_playback_trigger(unit, &state->wpcm, 1); } spin_unlock_irqrestore(&unit->reg_lock, flags); count -= cnt; buffer += cnt; ret += cnt; } set_current_state(TASK_RUNNING); remove_wait_queue(&dmabuf->wait, &waita); YMFDBGW("ymf_write: ret %d dmabuf.count %d\n", ret, 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; int redzone; unsigned long flags; unsigned int mask = 0; if (file->f_mode & FMODE_WRITE) poll_wait(file, &state->wpcm.dmabuf.wait, wait); if (file->f_mode & FMODE_READ) poll_wait(file, &state->rpcm.dmabuf.wait, wait); spin_lock_irqsave(&state->unit->reg_lock, flags); if (file->f_mode & FMODE_READ) { dmabuf = &state->rpcm.dmabuf; if (dmabuf->count >= (signed)dmabuf->fragsize) mask |= POLLIN | POLLRDNORM; } if (file->f_mode & FMODE_WRITE) { redzone = ymf_calc_lend(state->format.rate); redzone <<= state->format.shift; redzone *= 3; dmabuf = &state->wpcm.dmabuf; if (dmabuf->mapped) { if (dmabuf->count >= (signed)dmabuf->fragsize) mask |= POLLOUT | POLLWRNORM; } else { /* * Don't select unless a full fragment is available. * Otherwise artsd does GETOSPACE, sees 0, and loops. */ if (dmabuf->count + redzone + dmabuf->fragsize <= dmabuf->dmasize) 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->wpcm.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;/* P3 */ printk(KERN_INFO "ymfpci: using memory mapped sound, untested!\n"); 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; unsigned long flags; audio_buf_info abinfo; count_info cinfo; int redzone; int val; switch (cmd) { case OSS_GETVERSION: YMFDBGX("ymf_ioctl: cmd 0x%x(GETVER) arg 0x%lx\n", cmd, arg); return put_user(SOUND_VERSION, (int *)arg); case SNDCTL_DSP_RESET: YMFDBGX("ymf_ioctl: cmd 0x%x(RESET)\n", cmd); if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); dmabuf = &state->wpcm.dmabuf; spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->ready = 0; dmabuf->swptr = dmabuf->hwptr; dmabuf->count = dmabuf->total_bytes = 0; spin_unlock_irqrestore(&state->unit->reg_lock, flags); } if (file->f_mode & FMODE_READ) { ymf_stop_adc(state); dmabuf = &state->rpcm.dmabuf; spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf->ready = 0; dmabuf->swptr = dmabuf->hwptr; dmabuf->count = dmabuf->total_bytes = 0; spin_unlock_irqrestore(&state->unit->reg_lock, flags); } return 0; case SNDCTL_DSP_SYNC: YMFDBGX("ymf_ioctl: cmd 0x%x(SYNC)\n", cmd); if (file->f_mode & FMODE_WRITE) { dmabuf = &state->wpcm.dmabuf; if (file->f_flags & O_NONBLOCK) { spin_lock_irqsave(&state->unit->reg_lock, flags); if (dmabuf->count != 0 && !state->wpcm.running) { ymf_start_dac(state); } spin_unlock_irqrestore(&state->unit->reg_lock, flags); } else { ymf_wait_dac(state); } } /* XXX What does this do for reading? dmabuf->count=0; ? */ return 0; case SNDCTL_DSP_SPEED: /* set smaple rate */ if (get_user(val, (int *)arg)) return -EFAULT; YMFDBGX("ymf_ioctl: cmd 0x%x(SPEED) sp %d\n", cmd, val); if (val >= 8000 && val <= 48000) { if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); dmabuf = &state->wpcm.dmabuf; 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); } if (file->f_mode & FMODE_READ) { ymf_stop_adc(state); dmabuf = &state->rpcm.dmabuf; 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 calls it. I wonder, why Michael Hipp used it. */ case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ if (get_user(val, (int *)arg)) return -EFAULT; YMFDBGX("ymf_ioctl: cmd 0x%x(STEREO) st %d\n", cmd, val); if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); dmabuf = &state->wpcm.dmabuf; 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 (file->f_mode & FMODE_READ) { ymf_stop_adc(state); dmabuf = &state->rpcm.dmabuf; 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); } return 0; case SNDCTL_DSP_GETBLKSIZE: YMFDBGX("ymf_ioctl: cmd 0x%x(GETBLK)\n", cmd); if (file->f_mode & FMODE_WRITE) { if ((val = prog_dmabuf(state, 0))) return val; val = state->wpcm.dmabuf.fragsize; YMFDBGX("ymf_ioctl: GETBLK w %d\n", val); return put_user(val, (int *)arg); } if (file->f_mode & FMODE_READ) { if ((val = prog_dmabuf(state, 1))) return val; val = state->rpcm.dmabuf.fragsize; YMFDBGX("ymf_ioctl: GETBLK r %d\n", val); return put_user(val, (int *)arg); } return -EINVAL; case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ YMFDBGX("ymf_ioctl: cmd 0x%x(GETFMTS)\n", cmd); 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; YMFDBGX("ymf_ioctl: cmd 0x%x(SETFMT) fmt %d\n", cmd, val); if (val == AFMT_S16_LE || val == AFMT_U8) { if (file->f_mode & FMODE_WRITE) { ymf_wait_dac(state); dmabuf = &state->wpcm.dmabuf; 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); } if (file->f_mode & FMODE_READ) { ymf_stop_adc(state); dmabuf = &state->rpcm.dmabuf; 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; YMFDBGX("ymf_ioctl: cmd 0x%x(CHAN) ch %d\n", cmd, 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 = &state->wpcm.dmabuf; dmabuf->ready = 0; state->format.voices = val; ymf_pcm_update_shift(&state->format); spin_unlock_irqrestore(&state->unit->reg_lock, flags); } } if (file->f_mode & FMODE_READ) { ymf_stop_adc(state); if (val == 1 || val == 2) { spin_lock_irqsave(&state->unit->reg_lock, flags); dmabuf = &state->rpcm.dmabuf; dmabuf->ready = 0; state->format.voices = val; ymf_pcm_update_shift(&state->format); spin_unlock_irqrestore(&state->unit->reg_lock, flags); } } } return put_user(state->format.voices, (int *)arg); case SNDCTL_DSP_POST: YMFDBGX("ymf_ioctl: cmd 0x%x(POST)\n", cmd); /* * 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. */ spin_lock_irqsave(&state->unit->reg_lock, flags); if (state->wpcm.dmabuf.count != 0 && !state->wpcm.running) { ymf_start_dac(state); } spin_unlock_irqrestore(&state->unit->reg_lock, flags); return 0; case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, (int *)arg)) return -EFAULT; YMFDBGX("ymf_ioctl: cmd 0x%x(SETFRAG) fr 0x%04x:%04x(%d:%d)\n", cmd, (val >> 16) & 0xFFFF, val & 0xFFFF, (val >> 16) & 0xFFFF, val & 0xFFFF); dmabuf = &state->wpcm.dmabuf; dmabuf->ossfragshift = val & 0xffff; dmabuf->ossmaxfrags = (val >> 16) & 0xffff; if (dmabuf->ossfragshift < 4) dmabuf->ossfragshift = 4; if (dmabuf->ossfragshift > 15) dmabuf->ossfragshift = 15; return 0; case SNDCTL_DSP_GETOSPACE: YMFDBGX("ymf_ioctl: cmd 0x%x(GETOSPACE)\n", cmd); if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; dmabuf = &state->wpcm.dmabuf; if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0) return val; redzone = ymf_calc_lend(state->format.rate); redzone <<= state->format.shift; redzone *= 3; spin_lock_irqsave(&state->unit->reg_lock, flags); abinfo.fragsize = dmabuf->fragsize; abinfo.bytes = dmabuf->dmasize - dmabuf->count - redzone; 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; case SNDCTL_DSP_GETISPACE: YMFDBGX("ymf_ioctl: cmd 0x%x(GETISPACE)\n", cmd); if (!(file->f_mode & FMODE_READ)) return -EINVAL; dmabuf = &state->rpcm.dmabuf; if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0) return val; spin_lock_irqsave(&state->unit->reg_lock, flags); abinfo.fragsize = dmabuf->fragsize; abinfo.bytes = 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; case SNDCTL_DSP_NONBLOCK: YMFDBGX("ymf_ioctl: cmd 0x%x(NONBLOCK)\n", cmd); file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_GETCAPS: YMFDBGX("ymf_ioctl: cmd 0x%x(GETCAPS)\n", cmd); /* return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, (int *)arg); */ return put_user(0, (int *)arg); case SNDCTL_DSP_GETIPTR: YMFDBGX("ymf_ioctl: cmd 0x%x(GETIPTR)\n", cmd); if (!(file->f_mode & FMODE_READ)) return -EINVAL; dmabuf = &state->rpcm.dmabuf; spin_lock_irqsave(&state->unit->reg_lock, flags); cinfo.bytes = dmabuf->total_bytes; cinfo.blocks = dmabuf->count >> dmabuf->fragshift; cinfo.ptr = dmabuf->hwptr; spin_unlock_irqrestore(&state->unit->reg_lock, flags); YMFDBGX("ymf_ioctl: GETIPTR ptr %d bytes %d\n", cinfo.ptr, cinfo.bytes); return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; case SNDCTL_DSP_GETOPTR: YMFDBGX("ymf_ioctl: cmd 0x%x(GETOPTR)\n", cmd); if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; dmabuf = &state->wpcm.dmabuf; spin_lock_irqsave(&state->unit->reg_lock, flags); cinfo.bytes = dmabuf->total_bytes; cinfo.blocks = dmabuf->count >> dmabuf->fragshift; cinfo.ptr = dmabuf->hwptr; spin_unlock_irqrestore(&state->unit->reg_lock, flags); YMFDBGX("ymf_ioctl: GETOPTR ptr %d bytes %d\n", cinfo.ptr, cinfo.bytes); return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; case SNDCTL_DSP_SETDUPLEX: /* XXX TODO */ YMFDBGX("ymf_ioctl: cmd 0x%x(SETDUPLEX)\n", cmd); return -EINVAL; case SOUND_PCM_READ_RATE: YMFDBGX("ymf_ioctl: cmd 0x%x(READ_RATE)\n", cmd); return put_user(state->format.rate, (int *)arg); case SOUND_PCM_READ_CHANNELS: YMFDBGX("ymf_ioctl: cmd 0x%x(READ_CH)\n", cmd); return put_user(state->format.voices, (int *)arg); case SOUND_PCM_READ_BITS: YMFDBGX("ymf_ioctl: cmd 0x%x(READ_BITS)\n", cmd); return put_user(AFMT_S16_LE, (int *)arg); case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: YMFDBGX("ymf_ioctl: cmd 0x%x unsupported\n", cmd); return -ENOTTY; default: /* * Some programs mix up audio devices and ioctls * or perhaps they expect "universal" ioctls, * for instance we get SNDCTL_TMR_CONTINUE here. */ YMFDBGX("ymf_ioctl: cmd 0x%x unknown\n", cmd); break;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -