input子系统
谨以此文纪念过往的岁月。
三.input handler
花开两朵,各表一枝,话说前文讲述了input设备的添加,那现在就来看一下input设备的"驱动"。
在linux系统启动时,会添加一个万能的event handler,其初始化在 /drivers/input/evdev.c中。
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
咱看evdev_handler是什么?
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,
};
那来看input_register_handler这个函数。
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int retval;
retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler; --保存handler
}
list_add_tail(&handler->node, &input_handler_list); --这个才是最最主要的。
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
out:
mutex_unlock(&input_mutex);
return retval;
}
在系统刚启动时,没有任何input_dev注册。input_register_handler先注册一个适合于所有input设备的handler注册。此时input_dev_list为空,input_handler_list
上有一个节点,就是万能input handler(evdev_handler)。这样我们在回过头去看input_attach_handler就很好懂了。其实在没有额外的handler添加时,每一个input dev
注册时均会调用evdev_connect。那来看看connect是怎么运行的。
3.1 evdev_connect
将handler和dev连接起来。这里的handler为evdev_handler
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;
for (minor = 0; minor < EVDEV_MINORS; minor++) --EVDEV_MINORS = 32 ,其采用了全局变量的手段来管理evdev_table.
if (!evdev_table[minor])
break;
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
evdev->exist = 1;
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;
dev_set_name(&evdev->dev, evdev->name);
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
error = evdev_install_chrdev(evdev); -- evdev_table[evdev->minor] = evdev
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev);
if (error)
goto err_cleanup_evdev;
return 0;
err_cleanup_evdev:
evdev_cleanup(evdev);
err_unregister_handle:
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
return error;
}
上面的函数就是赋值注册。可以看出每一个event都会有一个evevt device来对应。其中最主要的一个函数是input_register_handle这个函数和前面
input_register_handler是不同的的。
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;
list_add_tail_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
synchronize_rcu();
list_add_tail(&handle->h_node, &handler->h_list); --添加到handler链表。
if (handler->start)
handler->start(handle);
return 0;
}
为什么会有上面的函数呢,因为同一个handler可以去对应n个input dev这个就需要对应了,其实handle类似于platfrom中的device_drvier,起到一个连接作用。
通过该结构可以将handler和input_dev 一起管理起来。
到此为止,input_dev的注册应该了解了。类似于platform中的设备已经注册并且probe,那下面来看设备是怎样运行的。
四.input运行
在input子系统中通过input_event向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);
}
}
我们以按键为例来说明下面的函数
static void input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
switch (type) {
...
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&!!test_bit(code, dev->key) != value) { --检测数据
if (value != 2) { --这段程序理解简单,但是其为什么会这么做啊???有可能是用于ctrl+x类型函数的处理。
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
...
}
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);
}
下面的函数是重新启动定时器。
static void input_start_autorepeat(struct input_dev *dev, int code)
{
if (test_bit(EV_REP, dev->evbit) &&dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&dev->timer.data) {
dev->repeat_key = code;
mod_timer(&dev->timer,jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
}
}
input_pass_event函数中会调用handler->event 在没有添加特别的handler时,会调用evdev_handler.evdev_event,那来看evdev_event
这个函数。在这个函数中一个是对event进行时间赋值,再找这里面有两个很重要,一个是evdev_pass_event另一个则是wake_up_interruptible
关于client在下面讲打开设备以及调用时再说。
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;
struct timespec ts;
ktime_get_ts(&ts);
event.time.tv_sec = ts.tv_sec;
event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
event.type = type;
event.code = code;
event.value = value;
rcu_read_lock();
client = rcu_dereference(evdev->grab); --关于这些东东,在以后学习中在说。
if (client)
evdev_pass_event(lient, &eventc);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
rcu_read_unlock();
wake_up_interruptible(&evdev->wait); --这个其实是唤醒阻塞,这个在read的时候被阻塞。
}
static void evdev_pass_event(struct evdev_client *client,struct input_event *event)
{
spin_lock(&client->buffer_lock);
wake_lock_timeout(&client->wake_lock, 5 * HZ);
client->buffer[client->head++] = *event;
client->head &= EVDEV_BUFFER_SIZE - 1;
spin_unlock(&client->buffer_lock);
kill_fasync(&client->fasync, SIGIO, POLL_IN); --异步通知,这个也是以后再说。
}
五.input应用层
在调用input时,首先会open event设备。由于对于同一个input 设备其对应的应用程序可能会有好几个,那个怎么来区分呢?所以client就诞生了,用
client来标示每一个进程。在见识多了platform种的种种结构体的嵌套,那这儿也就不奇怪了。有client的成员保存evdev的信息。
在这里面会client,针对每一个进程
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]; --获取evdev
if (evdev)
get_device(&evdev->dev);
mutex_unlock(&evdev_table_mutex);
if (!evdev)
return -ENODEV;
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); --创建client
if (!client) {
error = -ENOMEM;
goto err_put_evdev;
}
spin_lock_init(&client->buffer_lock);
snprintf(client->name, sizeof(client->name), "%s-%d", evdev->name,task_tgid_vnr(current));
wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
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 void evdev_attach_client(struct evdev *evdev,struct evdev_client *client)
{
spin_lock(&evdev->client_lock);
list_add_tail_rcu(&client->node, &evdev->client_list); --将client 添加入evdev—>client_list链表中。
spin_unlock(&evdev->client_lock);
synchronize_rcu();
}
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++) { --如果evdev是首次打开,则调用input_open_device。
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); --这也是为什么input_dev->open 在没有使用的情况下必须要返回0。
if (retval) {
dev->users--;
if (!--handle->open) {
/*
* Make sure we are not delivering any more events
* through this handle
*/
synchronize_rcu();
}
}
out:
mutex_unlock(&dev->mutex);
return retval;
}
以上的几个函数都是设置input_dev以及event设备的一些标志。如果顺利的话,设备就会打开。
应用层调用read的时候,将会调用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; --这个在open的时候就保存了。
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)) --如果client没有打开,并且file标识符为非阻塞。直接over
return -EAGAIN;
retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist); --阻塞,在input_event中会唤醒。
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;
}
六.总结
在input子系统中,其设备驱动和应用层均比较简单,其很多工作都交给内核去做。在该篇中就是要去理解内核到底做什么了,为什么这么去做。
在linux系统刚启动时,会先注册一个handler用于处理所有的input_dev。在没有注册其余的handler前,所有的驱动均会使用该handler来处理。