Linux 输入子系统分析(一)
Linux 输入子系统分析(二)
分析一个内核提供的input_handler
对应一些常用的输入设备,如键盘、鼠标等,内核提供的对应的input_handler,用来统一处理这类设备的事件。比如键盘,不管哪种型号的键盘,能上传的事件信息都是一样的,如上传键值’A’、'B’等。
本次分析drivers\input\evdev.c,其注册的input_handler支持所有的input_dev。
入口函数:
/* evdev_handler只设置了driver_info,evbit、keybit等未设置,都为0,从上一章分析
input_attach_handler函数得知,该input_handler会与所有的input_dev匹配成功
*/
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
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,
};
static int __init evdev_init(void)
{
//注册input_handler
return input_register_handler(&evdev_handler);
}
input_handler与input_dev匹配成功,调用evdev_connect:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int dev_no;
int error;
......
//分配一个struct evdev结构体
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
......
//初始化分配的struct evdev结构体
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
evdev->exist = true;
......
dev_set_name(&evdev->dev, "event%d", dev_no);
//初始化input_handle
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
//注册input_handle
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
//初始化一个字符设备,file_operations为evdev_fops
cdev_init(&evdev->cdev, &evdev_fops);
evdev->cdev.kobj.parent = &evdev->dev.kobj;
//注册字符设备
error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev);
......
}
前面分析事件处理层时,我们知道事件处理层除了处理驱动层提交的数据处理,还为应用层提供编程的接口,所以在evdev_connect函数里,会注册字符设备。
在evdev_connect函数里,会初始化一个struct input_handle并注册:
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
//把input_handle插入到input_dev的h_list链表
if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list);
else
list_add_tail_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
//把input_handle插入到input_handler的h_list链表
list_add_tail_rcu(&handle->h_node, &handler->h_list);
//执行input_handler里的start函数
if (handler->start)
handler->start(handle);
return 0;
}
evdev_connect函数执行完之后,input_dev与input_handler之间关系已建立,并在/dev目录下生成eventX设备文件,对应file_operations为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,
};
打开/dev/eventX,对应的调用到evdev_open:
struct evdev_client {
unsigned int head;
unsigned int tail;
unsigned int packet_head;
spinlock_t buffer_lock;
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
unsigned int clk_type;
bool revoked;
unsigned long *evmasks[EV_CNT];
unsigned int bufsize;
struct input_event buffer[];
};
static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
unsigned int size = sizeof(struct evdev_client) +
bufsize * sizeof(struct input_event);
struct evdev_client *client;
int error;
//为打开设备文件的每个进程分配一个evdev_client结构体
client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
if (!client)
client = vzalloc(size);
if (!client)
return -ENOMEM;
//设置evdev_client
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
client->evdev = evdev;
//把evdev_client插入到evdev的client_list链表
evdev_attach_client(evdev, client);
//open devcie
error = evdev_open_device(evdev);
if (error)
goto err_free_client;
file->private_data = client;
nonseekable_open(inode, file);
return 0;
err_free_client:
evdev_detach_client(evdev, client);
kvfree(client);
return error;
}
evdev_open_device:
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++) { //首次打开
//调用input_dev->open函数
retval = input_open_device(&evdev->handle);
if (retval)
evdev->open--;
}
mutex_unlock(&evdev->mutex);
return retval;
}
“read /dev/eventX”,对应的调用到evdev_read:
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;
size_t read = 0;
int error;
if (count != 0 && count < input_event_size())
return -EINVAL;
for (;;) {
......
//无数据,且不阻塞读取数据,返回-EAGAIN,这里使用环形队列的存取设备上报的事件
if (client->packet_head == client->tail &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
......
while (read + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
//有数据则拷贝回用户空间
if (input_event_to_user(buffer + read, &event))
return -EFAULT;
read += input_event_size();
}
if (read)
break;
//无数据,阻塞读取数据则进程睡眠,等待有数据
if (!(file->f_flags & O_NONBLOCK)) {
error = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail ||
!evdev->exist || client->revoked);
if (error)
return error;
}
}
return read;
}
前面分析了,设备上报事件,核心层最终会调用到input_handler的events函数进行处理,对于evdev这个input_handler而言,events函数对应为evdev_events:
static void evdev_events(struct input_handle *handle,
const struct input_value *vals, unsigned int count)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
ktime_t ev_time[EV_CLK_MAX];
......
//遍历evdev->client_list链表,调用evdev_pass_values,对应多个进程读取数据
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_values(client, vals, count, ev_time);
......
rcu_read_unlock();
}
evdev_pass_values:
static void evdev_pass_values(struct evdev_client *client,
const struct input_value *vals, unsigned int count,
ktime_t *ev_time)
{
struct evdev *evdev = client->evdev;
const struct input_value *v;
struct input_event event;
bool wakeup = false;
......
for (v = vals; v != vals + count; v++) {
if (__evdev_is_filtered(client, v->type, v->code))
continue;
if (v->type == EV_SYN && v->code == SYN_REPORT) {
/* drop empty SYN_REPORT */
if (client->packet_head == client->head)
continue;
wakeup = true;
}
event.type = v->type;
event.code = v->code;
event.value = v->value;
//把事件放入环形缓冲区
__pass_event(client, &event);
}
spin_unlock(&client->buffer_lock);
if (wakeup)
wake_up_interruptible(&evdev->wait); //唤醒进程
}