linux输入设备的总结:
上一篇文章中,用户空间可以open 我们的event1设备文件,也可以read我们的设备文件。可是我们的代码里面并没有实现read 和 open函数。但是依旧可以抓到数据。这也就是说内核帮我们做好了很多input设备公共的操作函数。我们只要按照特定的框架去开发就可以了。
具体的一些相关的代码和structure有贴出来,以防止以后忘记。在这里做个备份.
1) evdev handler
static struct input_handler evdev_handler = {
.event = evdev_event,
.events = evdev_events,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
2) evdev fops
static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.flush = evdev_flush,
.llseek = no_llseek,
};
3)read the event* ,how we get the data stand for?
int input_event_to_user(char __user *buffer,
const struct input_event *event)
{
if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
struct input_event_compat compat_event;
compat_event.time.tv_sec = event->time.tv_sec;
compat_event.time.tv_usec = event->time.tv_usec;
compat_event.type = event->type;
compat_event.code = event->code;
compat_event.value = event->value;
if (copy_to_user(buffer, &compat_event,
sizeof(struct input_event_compat)))
return -EFAULT;
} else {
if (copy_to_user(buffer, event, sizeof(struct input_event)))
return -EFAULT;
}
return 0;
}
4) user space: read
static const struct file_operations input_devices_fileops = {
.owner = THIS_MODULE,
.open = input_proc_devices_open,
.poll = input_proc_devices_poll,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static const struct seq_operations input_devices_seq_ops = {
.start = input_devices_seq_start,
.next = input_devices_seq_next,
.stop = input_seq_stop,
.show = input_devices_seq_show,
};
static int input_proc_devices_open(struct inode *inode, struct file *file)
{
return seq_open(file, &input_devices_seq_ops);
}
struct seq_file {
char *buf;
size_t size;
size_t from;
size_t count;
loff_t index;
loff_t read_pos;
u64 version;
struct mutex lock;
const struct seq_operations *op;
int poll_event;
#ifdef CONFIG_USER_NS
struct user_namespace *user_ns;
#endif
void *private;
};
int seq_open(struct file *file, const struct seq_operations *op)
{
struct seq_file *p = file->private_data;
if (!p) {
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (!p)
return -ENOMEM;
file->private_data = p;
}
memset(p, 0, sizeof(*p));
mutex_init(&p->lock);
p->op = op;
#ifdef CONFIG_USER_NS
p->user_ns = file->f_cred->user_ns;
#endif
/*
* Wrappers around seq_open(e.g. swaps_open) need to be
* aware of this. If they set f_version themselves, they
* should call seq_open first and then set f_version.
*/
file->f_version = 0;
/*
* seq_files support lseek() and pread(). They do not implement
* write() at all, but we clear FMODE_PWRITE here for historical
* reasons.
*
* If a client of seq_files a) implements file.write() and b) wishes to
* support pwrite() then that client will need to implement its own
* file.open() which calls seq_open() and then sets FMODE_PWRITE.
*/
file->f_mode &= ~FMODE_PWRITE;
return 0;
}
input.c ---> input_fops.open() //input_proc_devices_open
---->seq_open() //open函数主要就是调用seq_open()函数将一个struct seq_operations结构和struct file链接起来
----> evdev.open
read_write.c:
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
struct fd f = fdget(fd);
ssize_t ret = -EBADF;
if (f.file) {
loff_t pos = file_pos_read(f.file);
ret = vfs_read(f.file, buf, count, &pos);
file_pos_write(f.file, pos);
fdput(f);
}
return ret;
}
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
ssize_t ret;
if (!(file->f_mode & FMODE_READ))
return -EBADF;
if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
return -EINVAL;
if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
return -EFAULT;
ret = rw_verify_area(READ, file, pos, count);
if (ret >= 0) {
count = ret;
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, pos);
else
ret = do_sync_read(file, buf, count, pos);
if (ret > 0) {
fsnotify_access(file);
add_rchar(current, ret);
}
inc_syscr(current);
}
return ret;
}
read()---> sys_read() // system call
----->vfs_read()
----->file->f_op->read
----->evdev.read
5) input_register_device()
---->list_add_tail(&dev->node, &input_dev_list);
|
|
---->list_for_each_entry(handler, &input_handler_list, node)
|
|
---->input_attach_handler(dev, handler); // each match input_dev_list with input_handler_list
---->handler->connect(handler, dev, id) //connect
----> //set up the minor device num, set up the device node file
100) fun remark
register_chrdev(unsigned int major,const char * name,const struct file_operations * fops);
register_chrdev_region(dev_t,unsigned,const char *);
整个input的框架大致如上图。
1.input dev通过 input_register_device 向 input core 模块注册设备,注册成功则在对应的 input_dev_list上添加一个设备节点
2.event handler通过 input_register_handler向input core模块注册 handlers,注册成功则在对应的input_handler_list上添加一个节点.
3.注册的设备通过 input_match_device 来遍历 input_handler_list上的节点,如果match到,则调用对应的handler的connect函数
4.connect函数会获得次设备号,创建设备节点文件.
5.用户空间进行系统调用时的 open,read操作,实际上操作的是 input dev match到的evdev的fops.open 和 fops.read.
整个逻辑大致这样。