struct input_event {
-
struct timeval time;
-
__u16 type;
-
__u16 code;
-
__s32 value;
-
};
-
struct evdev_client {
-
struct input_event buffer[EVDEV_BUFFER_SIZE];
-
int head;
-
int tail;
-
spinlock_t buffer_lock;
-
struct fasync_struct *fasync;
-
struct evdev *evdev;
-
struct list_head node;
-
};
-
static struct input_handler evdev_handler = {
-
.event = evdev_event,
-
.connect = evdev_connect,
-
.disconnect = evdev_disconnect,
-
.fops = &evdev_fops,
-
.minor = EVDEV_MINOR_BASE,
-
.name = "evdev",
-
.id_table = evdev_ids,
-
};
这里的次设备号是EVDEV_MINOR_BASE(64),也就是说evdev_handler所表示的设备文件范围(13,64)~(13,64+32)。
如下一个结构体:evdev_handler匹配所有设备。
-
static const struct input_device_id evdev_ids[] = {
-
{ .driver_info = 1 },
-
{ },
-
};
-
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
-
};
在驱动程序中我们会调用input_report_abs等函数:
-
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
-
{
-
input_event(dev, EV_ABS, code, value);
-
}
跟踪input_event如下:
-
void input_event(struct input_dev *dev,
-
unsigned int type, unsigned int code, int value)
-
{
-
unsigned long flags;
-
-
if (is_event_supported(type, dev->evbit, EV_MAX)) {
-
-
spin_lock_irqsave(&dev->event_lock, flags);
-
-
add_input_randomness(type, code, value);
-
input_handle_event(dev, type, code, value);
-
spin_unlock_irqrestore(&dev->event_lock, flags);
-
}
-
}
跟踪input_handle_event如下:
-
static void input_handle_event(struct input_dev *dev,
-
unsigned int type, unsigned int code, int value)
-
{
-
int disposition = INPUT_IGNORE_EVENT;
-
-
switch (type) {
-
。。。。。。。。。。。。。。。。
-
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
-
dev->sync = 0;
-
-
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
-
dev->event(dev, type, code, value);
-
-
if (disposition & INPUT_PASS_TO_HANDLERS)
-
input_pass_event(dev, type, code, value);
-
}
如果该事件需要input
device来完成,就会将disposition设置成INPUT_PASS_TO_DEVICE,如果需要input
handler来完成,就会将disposition设置成INPUT_PASS_TO_DEVICE,如果需要两者都参与,则将disposition
设置成INPUT_PASS_TO_ALL。
跟踪input_pass_event如下:
-
static void input_pass_event(struct input_dev *dev,
-
unsigned int type, unsigned int code, int value)
-
{
-
struct input_handle *handle;
-
-
rcu_read_lock();
-
-
handle = rcu_dereference(dev->grab);
-
if (handle)
-
-
handle->handler->event(handle, type, code, value);
-
else
-
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
-
if (handle->open)
-
handle->handler->event(handle,
-
type, code, value);
-
rcu_read_unlock();
-
}
比如下边的evdev_handler的evdev_event:
-
static void evdev_event(struct input_handle *handle,
-
unsigned int type, unsigned int code, int value)
-
{
-
struct evdev *evdev = handle->private;
-
struct evdev_client *client;
-
struct input_event event;
-
-
do_gettimeofday(&event.time);
-
event.type = type;
-
event.code = code;
-
event.value = value;
-
-
rcu_read_lock();
-
client = rcu_dereference(evdev->grab);
-
if (client)
-
-
evdev_pass_event(client, &event);
-
else
-
list_for_each_entry_rcu(client, &evdev->client_list, node)
-
evdev_pass_event(client, &event);
-
-
rcu_read_unlock();
-
-
wake_up_interruptible(&evdev->wait);
-
}
-
static void evdev_pass_event(struct evdev_client *client,
-
struct input_event *event)
-
{
-
-
-
-
spin_lock(&client->buffer_lock);
-
-
client->buffer[client->head++] = *event;
-
client->head &= EVDEV_BUFFER_SIZE - 1;
-
spin_unlock(&client->buffer_lock);
-
-
kill_fasync(&client->fasync, SIGIO, POLL_IN);
-
}
这里总结一下事件的传递过程:首先在驱动层中,调用
inport_report_abs,然后他调用了input
core层的input_event,input_event调用了input_handle_event对事件进行分派,调用
input_pass_event,在这里他会把事件传递给具体的handler层,然后在相应handler的event处理函数中,封装一个
event,然后把它投入evdev的那个client_list上的client的事件buffer中,等待用户空间来读取。
当
用户空间打开设备节点/dev/input/event0~/dev/input/event4的时候,会使用input_fops中的
input_open_file()函数,input_open_file()->evdev_open()(如果handler是evdev的
话)->evdev_open_device()->input_open_device()->dev->open()。也就
是struct
file_operations input_fops提供了通用接口,最终会调用具体input_dev的open函数。下边看一下用户程序打开文件时的过程,首先调用了input_open_file:
-
static int input_open_file(struct inode *inode, struct file *file)
-
{
-
struct input_handler *handler;
-
const struct file_operations *old_fops, *new_fops = NULL;
-
int err;
-
-
lock_kernel();
-
-
-
handler = input_table[iminor(inode) >> 5];
-
if (!handler || !(new_fops = fops_get(handler->fops))) {
-
err = -ENODEV;
-
goto out;
-
}
-
-
-
-
-
-
if (!new_fops->open) {
-
fops_put(new_fops);
-
err = -ENODEV;
-
goto out;
-
}
-
-
old_fops = file->f_op;
-
file->f_op = new_fops;
-
-
err = new_fops->open(inode, file);
-
-
if (err) {
-
fops_put(file->f_op);
-
file->f_op = fops_get(old_fops);
-
}
-
fops_put(old_fops);
-
out:
-
unlock_kernel();
-
return err;
-
}
这里还是假设handler是evdev_handler。
-
static int evdev_open(struct inode *inode, struct file *file)
-
{
-
struct evdev *evdev;
-
struct evdev_client *client;
-
-
int i = iminor(inode) - EVDEV_MINOR_BASE;
-
int error;
-
-
if (i >= EVDEV_MINORS)
-
return -ENODEV;
-
-
error = mutex_lock_interruptible(&evdev_table_mutex);
-
if (error)
-
return error;
-
-
evdev = evdev_table[i];
-
if (evdev)
-
get_device(&evdev->dev);
-
mutex_unlock(&evdev_table_mutex);
-
-
if (!evdev)
-
return -ENODEV;
-
-
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
-
if (!client) {
-
error = -ENOMEM;
-
goto err_put_evdev;
-
}
-
-
spin_lock_init(&client->buffer_lock);
-
-
client->evdev = evdev;
-
evdev_attach_client(evdev, client);
-
-
error = evdev_open_device(evdev);
-
if (error)
-
goto err_free_client;
-
-
file->private_data = client;
-
return 0;
-
-
err_free_client:
-
evdev_detach_client(evdev, client);
-
kfree(client);
-
err_put_evdev:
-
put_device(&evdev->dev);
-
return error;
-
}
-
static int evdev_open_device(struct evdev *evdev)
-
{
-
int retval;
-
-
retval = mutex_lock_interruptible(&evdev->mutex);
-
if (retval)
-
return retval;
-
-
if (!evdev->exist)
-
retval = -ENODEV;
-
-
else if (!evdev->open++) {
-
retval = input_open_device(&evdev->handle);
-
if (retval)
-
evdev->open--;
-
}
-
-
mutex_unlock(&evdev->mutex);
-
return retval;
-
}
-
int input_open_device(struct input_handle *handle)
-
{
-
struct input_dev *dev = handle->dev;
-
int retval;
-
-
retval = mutex_lock_interruptible(&dev->mutex);
-
if (retval)
-
return retval;
-
-
if (dev->going_away) {
-
retval = -ENODEV;
-
goto out;
-
}
-
-
handle->open++;
-
-
if (!dev->users++ && dev->open)
-
retval = dev->open(dev);
-
-
if (retval) {
-
dev->users--;
-
if (!--handle->open) {
-
-
-
-
-
synchronize_rcu();
-
}
-
}
-
-
out:
-
mutex_unlock(&dev->mutex);
-
return retval;
-
}
下面是用户进程读取event的底层实现:
-
static ssize_t evdev_read(struct file *file, char __user *buffer,
-
size_t count, loff_t *ppos)
-
{
-
-
struct evdev_client *client = file->private_data;
-
struct evdev *evdev = client->evdev;
-
struct input_event event;
-
int retval;
-
-
if (count < input_event_size())
-
return -EINVAL;
-
-
if (client->head == client->tail && evdev->exist &&
-
(file->f_flags & O_NONBLOCK))
-
return -EAGAIN;
-
-
retval = wait_event_interruptible(evdev->wait,
-
client->head != client->tail || !evdev->exist);
-
if (retval)
-
return retval;
-
-
if (!evdev->exist)
-
return -ENODEV;
-
-
while (retval + input_event_size() <= count &&
-
evdev_fetch_next_event(client, &event)) {
-
-
if (input_event_to_user(buffer + retval, &event))
-
return -EFAULT;
-
-
retval += input_event_size();
-
}
-
-
return retval;
-
}
-
static int evdev_fetch_next_event(struct evdev_client *client,
-
struct input_event *event)
-
{
-
int have_event;
-
-
spin_lock_irq(&client->buffer_lock);
-
-
have_event = client->head != client->tail;
-
-
if (have_event) {
-
*event = client->buffer[client->tail++];
-
client->tail &= EVDEV_BUFFER_SIZE - 1;
-
}
-
-
spin_unlock_irq(&client->buffer_lock);
-
-
return have_event;
-
}
-
int input_event_to_user(char __user *buffer,
-
const struct input_event *event)
-
{
-
-
if (INPUT_COMPAT_TEST) {
-
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;
-
}
这里总结一下:如果两个进程打开同一个文件,每个进程在打开时都会生成一个
evdev_client,evdev_client被挂在evdev的client_list,在handle收到一个事件的时候,会把事件copy到
挂在client_list上的所有evdev_client的buffer中。这样所有打开同一个设备的进程都会收到这个消息而唤醒。