📄 timer.c
字号:
static int snd_timer_user_tselect(struct file *file, struct snd_timer_select __user *_tselect){ struct snd_timer_user *tu; struct snd_timer_select tselect; char str[32]; int err = 0; tu = file->private_data; mutex_lock(&tu->tread_sem); if (tu->timeri) { snd_timer_close(tu->timeri); tu->timeri = NULL; } if (copy_from_user(&tselect, _tselect, sizeof(tselect))) { err = -EFAULT; goto __err; } sprintf(str, "application %i", current->pid); if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid); if (err < 0) goto __err; kfree(tu->queue); tu->queue = NULL; kfree(tu->tqueue); tu->tqueue = NULL; if (tu->tread) { tu->tqueue = kmalloc(tu->queue_size * sizeof(struct snd_timer_tread), GFP_KERNEL); if (tu->tqueue == NULL) err = -ENOMEM; } else { tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read), GFP_KERNEL); if (tu->queue == NULL) err = -ENOMEM; } if (err < 0) { snd_timer_close(tu->timeri); tu->timeri = NULL; } else { tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; tu->timeri->callback = tu->tread ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; tu->timeri->ccallback = snd_timer_user_ccallback; tu->timeri->callback_data = (void *)tu; } __err: mutex_unlock(&tu->tread_sem); return err;}static int snd_timer_user_info(struct file *file, struct snd_timer_info __user *_info){ struct snd_timer_user *tu; struct snd_timer_info *info; struct snd_timer *t; int err = 0; tu = file->private_data; if (!tu->timeri) return -EBADFD; t = tu->timeri->timer; if (!t) return -EBADFD; info = kzalloc(sizeof(*info), GFP_KERNEL); if (! info) return -ENOMEM; info->card = t->card ? t->card->number : -1; if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) info->flags |= SNDRV_TIMER_FLG_SLAVE; strlcpy(info->id, t->id, sizeof(info->id)); strlcpy(info->name, t->name, sizeof(info->name)); info->resolution = t->hw.resolution; if (copy_to_user(_info, info, sizeof(*_info))) err = -EFAULT; kfree(info); return err;}static int snd_timer_user_params(struct file *file, struct snd_timer_params __user *_params){ struct snd_timer_user *tu; struct snd_timer_params params; struct snd_timer *t; struct snd_timer_read *tr; struct snd_timer_tread *ttr; int err; tu = file->private_data; if (!tu->timeri) return -EBADFD; t = tu->timeri->timer; if (!t) return -EBADFD; if (copy_from_user(¶ms, _params, sizeof(params))) return -EFAULT; if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) { err = -EINVAL; goto _end; } if (params.queue_size > 0 && (params.queue_size < 32 || params.queue_size > 1024)) { err = -EINVAL; goto _end; } if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)| (1<<SNDRV_TIMER_EVENT_TICK)| (1<<SNDRV_TIMER_EVENT_START)| (1<<SNDRV_TIMER_EVENT_STOP)| (1<<SNDRV_TIMER_EVENT_CONTINUE)| (1<<SNDRV_TIMER_EVENT_PAUSE)| (1<<SNDRV_TIMER_EVENT_SUSPEND)| (1<<SNDRV_TIMER_EVENT_RESUME)| (1<<SNDRV_TIMER_EVENT_MSTART)| (1<<SNDRV_TIMER_EVENT_MSTOP)| (1<<SNDRV_TIMER_EVENT_MCONTINUE)| (1<<SNDRV_TIMER_EVENT_MPAUSE)| (1<<SNDRV_TIMER_EVENT_MSUSPEND)| (1<<SNDRV_TIMER_EVENT_MRESUME))) { err = -EINVAL; goto _end; } snd_timer_stop(tu->timeri); spin_lock_irq(&t->lock); tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO| SNDRV_TIMER_IFLG_EXCLUSIVE| SNDRV_TIMER_IFLG_EARLY_EVENT); if (params.flags & SNDRV_TIMER_PSFLG_AUTO) tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE) tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE; if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT) tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT; spin_unlock_irq(&t->lock); if (params.queue_size > 0 && (unsigned int)tu->queue_size != params.queue_size) { if (tu->tread) { ttr = kmalloc(params.queue_size * sizeof(*ttr), GFP_KERNEL); if (ttr) { kfree(tu->tqueue); tu->queue_size = params.queue_size; tu->tqueue = ttr; } } else { tr = kmalloc(params.queue_size * sizeof(*tr), GFP_KERNEL); if (tr) { kfree(tu->queue); tu->queue_size = params.queue_size; tu->queue = tr; } } } tu->qhead = tu->qtail = tu->qused = 0; if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { if (tu->tread) { struct snd_timer_tread tread; tread.event = SNDRV_TIMER_EVENT_EARLY; tread.tstamp.tv_sec = 0; tread.tstamp.tv_nsec = 0; tread.val = 0; snd_timer_user_append_to_tqueue(tu, &tread); } else { struct snd_timer_read *r = &tu->queue[0]; r->resolution = 0; r->ticks = 0; tu->qused++; tu->qtail++; } } tu->filter = params.filter; tu->ticks = params.ticks; err = 0; _end: if (copy_to_user(_params, ¶ms, sizeof(params))) return -EFAULT; return err;}static int snd_timer_user_status(struct file *file, struct snd_timer_status __user *_status){ struct snd_timer_user *tu; struct snd_timer_status status; tu = file->private_data; if (!tu->timeri) return -EBADFD; memset(&status, 0, sizeof(status)); status.tstamp = tu->tstamp; status.resolution = snd_timer_resolution(tu->timeri); status.lost = tu->timeri->lost; status.overrun = tu->overrun; spin_lock_irq(&tu->qlock); status.queue = tu->qused; spin_unlock_irq(&tu->qlock); if (copy_to_user(_status, &status, sizeof(status))) return -EFAULT; return 0;}static int snd_timer_user_start(struct file *file){ int err; struct snd_timer_user *tu; tu = file->private_data; if (!tu->timeri) return -EBADFD; snd_timer_stop(tu->timeri); tu->timeri->lost = 0; tu->last_resolution = 0; return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0;}static int snd_timer_user_stop(struct file *file){ int err; struct snd_timer_user *tu; tu = file->private_data; if (!tu->timeri) return -EBADFD; return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;}static int snd_timer_user_continue(struct file *file){ int err; struct snd_timer_user *tu; tu = file->private_data; if (!tu->timeri) return -EBADFD; tu->timeri->lost = 0; return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;}static int snd_timer_user_pause(struct file *file){ int err; struct snd_timer_user *tu; tu = file->private_data; if (!tu->timeri) return -EBADFD; return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;}enum { SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20), SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21), SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22), SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),};static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ struct snd_timer_user *tu; void __user *argp = (void __user *)arg; int __user *p = argp; tu = file->private_data; switch (cmd) { case SNDRV_TIMER_IOCTL_PVERSION: return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; case SNDRV_TIMER_IOCTL_NEXT_DEVICE: return snd_timer_user_next_device(argp); case SNDRV_TIMER_IOCTL_TREAD: { int xarg; mutex_lock(&tu->tread_sem); if (tu->timeri) { /* too late */ mutex_unlock(&tu->tread_sem); return -EBUSY; } if (get_user(xarg, p)) { mutex_unlock(&tu->tread_sem); return -EFAULT; } tu->tread = xarg ? 1 : 0; mutex_unlock(&tu->tread_sem); return 0; } case SNDRV_TIMER_IOCTL_GINFO: return snd_timer_user_ginfo(file, argp); case SNDRV_TIMER_IOCTL_GPARAMS: return snd_timer_user_gparams(file, argp); case SNDRV_TIMER_IOCTL_GSTATUS: return snd_timer_user_gstatus(file, argp); case SNDRV_TIMER_IOCTL_SELECT: return snd_timer_user_tselect(file, argp); case SNDRV_TIMER_IOCTL_INFO: return snd_timer_user_info(file, argp); case SNDRV_TIMER_IOCTL_PARAMS: return snd_timer_user_params(file, argp); case SNDRV_TIMER_IOCTL_STATUS: return snd_timer_user_status(file, argp); case SNDRV_TIMER_IOCTL_START: case SNDRV_TIMER_IOCTL_START_OLD: return snd_timer_user_start(file); case SNDRV_TIMER_IOCTL_STOP: case SNDRV_TIMER_IOCTL_STOP_OLD: return snd_timer_user_stop(file); case SNDRV_TIMER_IOCTL_CONTINUE: case SNDRV_TIMER_IOCTL_CONTINUE_OLD: return snd_timer_user_continue(file); case SNDRV_TIMER_IOCTL_PAUSE: case SNDRV_TIMER_IOCTL_PAUSE_OLD: return snd_timer_user_pause(file); } return -ENOTTY;}static int snd_timer_user_fasync(int fd, struct file * file, int on){ struct snd_timer_user *tu; int err; tu = file->private_data; err = fasync_helper(fd, file, on, &tu->fasync); if (err < 0) return err; return 0;}static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, size_t count, loff_t *offset){ struct snd_timer_user *tu; long result = 0, unit; int err = 0; tu = file->private_data; unit = tu->tread ? sizeof(struct snd_timer_tread) : sizeof(struct snd_timer_read); spin_lock_irq(&tu->qlock); while ((long)count - result >= unit) { while (!tu->qused) { wait_queue_t wait; if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { err = -EAGAIN; break; } set_current_state(TASK_INTERRUPTIBLE); init_waitqueue_entry(&wait, current); add_wait_queue(&tu->qchange_sleep, &wait); spin_unlock_irq(&tu->qlock); schedule(); spin_lock_irq(&tu->qlock); remove_wait_queue(&tu->qchange_sleep, &wait); if (signal_pending(current)) { err = -ERESTARTSYS; break; } } spin_unlock_irq(&tu->qlock); if (err < 0) goto _error; if (tu->tread) { if (copy_to_user(buffer, &tu->tqueue[tu->qhead++], sizeof(struct snd_timer_tread))) { err = -EFAULT; goto _error; } } else { if (copy_to_user(buffer, &tu->queue[tu->qhead++], sizeof(struct snd_timer_read))) { err = -EFAULT; goto _error; } } tu->qhead %= tu->queue_size; result += unit; buffer += unit; spin_lock_irq(&tu->qlock); tu->qused--; } spin_unlock_irq(&tu->qlock); _error: return result > 0 ? result : err;}static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait){ unsigned int mask; struct snd_timer_user *tu; tu = file->private_data; poll_wait(file, &tu->qchange_sleep, wait); mask = 0; if (tu->qused) mask |= POLLIN | POLLRDNORM; return mask;}#ifdef CONFIG_COMPAT#include "timer_compat.c"#else#define snd_timer_user_ioctl_compat NULL#endifstatic const struct file_operations snd_timer_f_ops ={ .owner = THIS_MODULE, .read = snd_timer_user_read, .open = snd_timer_user_open, .release = snd_timer_user_release, .poll = snd_timer_user_poll, .unlocked_ioctl = snd_timer_user_ioctl, .compat_ioctl = snd_timer_user_ioctl_compat, .fasync = snd_timer_user_fasync,};/* * ENTRY functions */static int __init alsa_timer_init(void){ int err;#ifdef SNDRV_OSS_INFO_DEV_TIMERS snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, "system timer");#endif if ((err = snd_timer_register_system()) < 0) snd_printk(KERN_ERR "unable to register system timer (%i)\n", err); if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0, &snd_timer_f_ops, NULL, "timer")) < 0) snd_printk(KERN_ERR "unable to register timer device (%i)\n", err); snd_timer_proc_init(); return 0;}static void __exit alsa_timer_exit(void){ struct list_head *p, *n; snd_unregister_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0); /* unregister the system timer */ list_for_each_safe(p, n, &snd_timer_list) { struct snd_timer *timer = list_entry(p, struct snd_timer, device_list); snd_timer_free(timer); } snd_timer_proc_done();#ifdef SNDRV_OSS_INFO_DEV_TIMERS snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1);#endif}module_init(alsa_timer_init)module_exit(alsa_timer_exit)EXPORT_SYMBOL(snd_timer_open);EXPORT_SYMBOL(snd_timer_close);EXPORT_SYMBOL(snd_timer_resolution);EXPORT_SYMBOL(snd_timer_start);EXPORT_SYMBOL(snd_timer_stop);EXPORT_SYMBOL(snd_timer_continue);EXPORT_SYMBOL(snd_timer_pause);EXPORT_SYMBOL(snd_timer_new);EXPORT_SYMBOL(snd_timer_notify);EXPORT_SYMBOL(snd_timer_global_new);EXPORT_SYMBOL(snd_timer_global_free);EXPORT_SYMBOL(snd_timer_global_register);EXPORT_SYMBOL(snd_timer_interrupt);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -