📄 audio.c
字号:
up(&open_sem); wake_up(&open_wait); while (!list_empty(&s->audiolist)) { as = list_entry(s->audiolist.next, struct usb_audiodev, list); list_del(&as->list); usbin_release(as); usbout_release(as); dmabuf_release(&as->usbin.dma); dmabuf_release(&as->usbout.dma); kfree(as); } while (!list_empty(&s->mixerlist)) { ms = list_entry(s->mixerlist.next, struct usb_mixerdev, list); list_del(&ms->list); kfree(ms); } kfree(s);}extern inline int prog_dmabuf_in(struct usb_audiodev *as){ usbin_stop(as); return dmabuf_init(&as->usbin.dma);}extern inline int prog_dmabuf_out(struct usb_audiodev *as){ usbout_stop(as); return dmabuf_init(&as->usbout.dma);}/* --------------------------------------------------------------------- */static loff_t usb_audio_llseek(struct file *file, loff_t offset, int origin){ return -ESPIPE;}/* --------------------------------------------------------------------- */static int usb_audio_open_mixdev(struct inode *inode, struct file *file){ int minor = MINOR(inode->i_rdev); struct list_head *devs, *mdevs; struct usb_mixerdev *ms; struct usb_audio_state *s; down(&open_sem); for (devs = audiodevs.next; devs != &audiodevs; devs = devs->next) { s = list_entry(devs, struct usb_audio_state, audiodev); for (mdevs = s->mixerlist.next; mdevs != &s->mixerlist; mdevs = mdevs->next) { ms = list_entry(mdevs, struct usb_mixerdev, list); if (ms->dev_mixer == minor) goto mixer_found; } } up(&open_sem); return -ENODEV; mixer_found: if (!s->usbdev) { up(&open_sem); return -EIO; } file->private_data = ms; s->count++; MOD_INC_USE_COUNT; up(&open_sem); return 0;}static int usb_audio_release_mixdev(struct inode *inode, struct file *file){ struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data; struct usb_audio_state *s = ms->state; down(&open_sem); release(s); MOD_DEC_USE_COUNT; return 0;}static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data; int i, j, val; if (!ms->state->usbdev) return -ENODEV; if (cmd == SOUND_MIXER_INFO) { mixer_info info; strncpy(info.id, "USB_AUDIO", sizeof(info.id)); strncpy(info.name, "USB Audio Class Driver", sizeof(info.name)); info.modify_counter = ms->modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; strncpy(info.id, "USB_AUDIO", sizeof(info.id)); strncpy(info.name, "USB Audio Class Driver", sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == OSS_GETVERSION) return put_user(SOUND_VERSION, (int *)arg); if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) return -EINVAL; if (_IOC_DIR(cmd) == _IOC_READ) { switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ /* don't know how to handle this yet */ return put_user(0, (int *)arg); case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ for (val = i = 0; i < ms->numch; i++) val |= 1 << ms->ch[i].osschannel; return put_user(val, (int *)arg); case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ /* don't know how to handle this yet */ return put_user(0, (int *)arg); case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ for (val = i = 0; i < ms->numch; i++) if (ms->ch[i].flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) val |= 1 << ms->ch[i].osschannel; return put_user(val, (int *)arg); case SOUND_MIXER_CAPS: return put_user(0, (int *)arg); default: i = _IOC_NR(cmd); if (i >= SOUND_MIXER_NRDEVICES) return -EINVAL; for (j = 0; j < ms->numch; j++) { if (ms->ch[j].osschannel == i) { return put_user(ms->ch[j].value, (int *)arg); } } return -EINVAL; } } if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) return -EINVAL; ms->modcnt++; switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ get_user_ret(val, (int *)arg, -EFAULT); /* set recording source: val */ return 0; default: i = _IOC_NR(cmd); if (i >= SOUND_MIXER_NRDEVICES) return -EINVAL; for (j = 0; j < ms->numch && ms->ch[j].osschannel != i; j++); if (j >= ms->numch) return -EINVAL; get_user_ret(val, (int *)arg, -EFAULT); if (wrmixer(ms, j, val)) return -EIO; return put_user(ms->ch[j].value, (int *)arg); }}static /*const*/ struct file_operations usb_mixer_fops = { llseek: usb_audio_llseek, ioctl: usb_audio_ioctl_mixdev, open: usb_audio_open_mixdev, release: usb_audio_release_mixdev,};/* --------------------------------------------------------------------- */static int drain_out(struct usb_audiodev *as, int nonblock){ DECLARE_WAITQUEUE(wait, current); unsigned long flags; int count, tmo; if (as->usbout.dma.mapped || !as->usbout.dma.ready) return 0; add_wait_queue(&as->usbout.dma.wait, &wait); for (;;) { __set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&as->lock, flags); count = as->usbout.dma.count; spin_unlock_irqrestore(&as->lock, flags); if (count <= 0) break; if (signal_pending(current)) break; if (nonblock) { remove_wait_queue(&as->usbout.dma.wait, &wait); set_current_state(TASK_RUNNING); return -EBUSY; } tmo = 3 * HZ * count / as->usbout.dma.srate; tmo >>= AFMT_BYTESSHIFT(as->usbout.dma.format); if (!schedule_timeout(tmo + 1)) { printk(KERN_DEBUG "usbaudio: dma timed out??\n"); break; } } remove_wait_queue(&as->usbout.dma.wait, &wait); set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; return 0;}/* --------------------------------------------------------------------- */static ssize_t usb_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos){ struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; DECLARE_WAITQUEUE(wait, current); ssize_t ret = 0; unsigned long flags; unsigned int ptr; int cnt, err; if (ppos != &file->f_pos) return -ESPIPE; if (as->usbin.dma.mapped) return -ENXIO; if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as))) return ret; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; add_wait_queue(&as->usbin.dma.wait, &wait); while (count > 0) { spin_lock_irqsave(&as->lock, flags); ptr = as->usbin.dma.rdptr; cnt = as->usbin.dma.count; /* set task state early to avoid wakeup races */ if (cnt <= 0) __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&as->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { if (usbin_start(as)) { if (!ret) ret = -ENODEV; break; } if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; break; } schedule(); if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; break; } continue; } if ((err = dmabuf_copyout_user(&as->usbin.dma, ptr, buffer, cnt))) { if (!ret) ret = err; break; } ptr += cnt; if (ptr >= as->usbin.dma.dmasize) ptr -= as->usbin.dma.dmasize; spin_lock_irqsave(&as->lock, flags); as->usbin.dma.rdptr = ptr; as->usbin.dma.count -= cnt; spin_unlock_irqrestore(&as->lock, flags); count -= cnt; buffer += cnt; ret += cnt; } __set_current_state(TASK_RUNNING); remove_wait_queue(&as->usbin.dma.wait, &wait); return ret;}static ssize_t usb_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos){ struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; DECLARE_WAITQUEUE(wait, current); ssize_t ret = 0; unsigned long flags; unsigned int ptr; int cnt, err; if (ppos != &file->f_pos) return -ESPIPE; if (as->usbout.dma.mapped) return -ENXIO; if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as))) return ret; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; add_wait_queue(&as->usbout.dma.wait, &wait); while (count > 0) {#if 0 printk(KERN_DEBUG "usb_audio_write: count %u dma: count %u rdptr %u wrptr %u dmasize %u fragsize %u flags 0x%02x taskst 0x%x\n", count, as->usbout.dma.count, as->usbout.dma.rdptr, as->usbout.dma.wrptr, as->usbout.dma.dmasize, as->usbout.dma.fragsize, as->usbout.flags, current->state);#endif spin_lock_irqsave(&as->lock, flags); if (as->usbout.dma.count < 0) { as->usbout.dma.count = 0; as->usbout.dma.rdptr = as->usbout.dma.wrptr; } ptr = as->usbout.dma.wrptr; cnt = as->usbout.dma.dmasize - as->usbout.dma.count; /* set task state early to avoid wakeup races */ if (cnt <= 0) __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&as->lock, flags); if (cnt > count) cnt = count; if (cnt <= 0) { if (usbout_start(as)) { if (!ret) ret = -ENODEV; break; } if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; break; } schedule(); if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; break; } continue; } if ((err = dmabuf_copyin_user(&as->usbout.dma, ptr, buffer, cnt))) { if (!ret) ret = err; break; } ptr += cnt; if (ptr >= as->usbout.dma.dmasize) ptr -= as->usbout.dma.dmasize; spin_lock_irqsave(&as->lock, flags); as->usbout.dma.wrptr = ptr; as->usbout.dma.count += cnt; spin_unlock_irqrestore(&as->lock, flags); count -= cnt; buffer += cnt; ret += cnt; if (usbout_start(as)) { if (!ret) ret = -ENODEV; break; } } __set_current_state(TASK_RUNNING); remove_wait_queue(&as->usbout.dma.wait, &wait); return ret;}static unsigned int usb_audio_poll(struct file *file, struct poll_table_struct *wait){ struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; unsigned long flags; unsigned int mask = 0; if (file->f_mode & FMODE_WRITE) { if (!as->usbout.dma.ready) prog_dmabuf_out(as); poll_wait(file, &as->usbout.dma.wait, wait); } if (file->f_mode & FMODE_READ) { if (!as->usbin.dma.ready) prog_dmabuf_in(as); poll_wait(file, &as->usbin.dma.wait, wait); } spin_lock_irqsave(&as->lock, flags); if (file->f_mode & FMODE_READ) { if (as->usbin.dma.count >= (signed)as->usbin.dma.fragsize) mask |= POLLIN | POLLRDNORM; } if (file->f_mode & FMODE_WRITE) { if (as->usbout.dma.mapped) { if (as->usbout.dma.count >= (signed)as->usbout.dma.fragsize) mask |= POLLOUT | POLLWRNORM; } else { if ((signed)as->usbout.dma.dmasize >= as->usbout.dma.count + (signed)as->usbout.dma.fragsize) mask |= POLLOUT | POLLWRNORM; } } spin_unlock_irqrestore(&as->lock, flags); return mask;}static int usb_audio_mmap(struct file *file, struct vm_area_struct *vma){ struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; struct dmabuf *db; int ret; if (vma->vm_flags & VM_WRITE) { if ((ret = prog_dmabuf_out(as)) != 0) return ret; db = &as->usbout.dma; } else if (vma->vm_flags & VM_READ) { if ((ret = prog_dmabuf_in(as)) != 0) return ret; db = &as->usbin.dma; } else return -EINVAL;#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,22) if (vma->vm_pgoff != 0) return -EINVAL;#endif return dmabuf_mmap(db, vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot);}static int usb_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; struct usb_audio_state *s = as->state; uns
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -