邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛
分类: LINUX
2015-11-09 17:00:38
转载: http://blog.sina.com.cn/s/articlelist_2034446750_0_1.html
linux输入子系统—架构分析
1、 在input输入子系统体系结构里,其核心是input core,其实input core对应的具体文件就是input.c。那么我们就来分析一下吧:
分析驱动,首先从xx_init(void)函数开始,如下:
static int __init input_init(void)
{
int err;
err =
class_register(&input_class);
if (err) {
printk(KERN_ERR "input: unable to
register input_dev class\n");
return err;
}
err =
input_proc_init();
if (err)
goto fail1;
err = register_chrdev(INPUT_MAJOR,
"input",
&input_fops);
//注册字符设备
if (err) {
printk(KERN_ERR "input: unable to
register char major %d", INPUT_MAJOR);
goto
fail2;
}
return 0;
fail2:
input_proc_exit();
fail1:
class_unregister(&input_class);
return err;
}
static const struct file_operations input_fops
= {
.owner = THIS_MODULE,
.open =
input_open_file, //只有一个open函数,看来open函数肩负重任啊
};
注释1:从上面我们可以看出,input.c里面完成的字符设备的注册,以及file_operations结构体的设置。看来input子系统设计的驱动程序也属于字符设备驱动程序,与一般的字符设备驱动程序的不同之处在于,input子系统字符设备的注册由内核自动完成,不需要程序员来完成,程序员要做的就是,将底层的硬件输入转化为统一事件形式,向输入核心层(input
core)汇报。
2、我们看到在file_operations结构体里只有一个open函数,如果要想读设备或则写设备怎么办呢?这说明pen函数肩负着艰巨的使命,下面就来分析这一函数
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]; //根据打开的文件的次设备号,来得到一个input_handler结构体,关于这个结构体请见注释1
if (!handler || !(new_fops = fops_get(handler->fops))) //通过handler得到新的 file_operations结构体
{
err = -ENODEV;
goto out;
}
if (!new_fops->open) {
fops_put(new_fops);
err = -ENODEV;
goto out;
}
old_fops = file->f_op; // 保存文件之前的f_op
file->f_op = new_fops; // 将新的f_op赋给当前文件的f_op
err = new_fops->open(inode, file);//调用open函数,当应用程序读的时候,最终会调用这个函数
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
unlock_kernel();
return err;
}
注释1:struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
const struct file_operations *fops;
int minor;
const char *name;
const struct input_device_id *id_table;
const struct input_device_id *blacklist;
struct list_head h_list;
struct list_head node;
};
这个结构体可以通过 input_table来获得,那么 input_table是何许人也呢?查了一下发现input_table在input_register_handler里被构造了:
input_table[handler->minor >> 5] = handler;
而input_register_handler函数由诸如下面一些文件调用:
keyboard.c、mousedev.c、evdev.c等等
我们进入edev.c来看一看,发下了重要线索:
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
};
.............................................................
.............................................................
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,//用来表示是否支持该设备,这里表示可以支持任何设备
};
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
有点乱对吧!先来理一下:
在edev.c中构造了input_handler结构体(当然在这个结构体里包含了file_operation结构体),并向input.c注册这个结构体。在input.c的函数input_register_handler中根据该结构体构造了input_table:input_table[handler->minor >> 5] = handler,而在 input_open_file函数中,又通过input_table构造了一个input_handler结构体:handler = input_table[iminor(inode) >> 5];这样看input_table起到一个中转作用。然后通过handler构造了新的file_operation结构体:new_fops = fops_get(handler->fops)。(即evdev_handler结构体变量中的.fops = &evdev_fops )
3、上面分析了input core的情况,下面我们从最底层的驱动来一步一步分析:
(1)在入口函数中注册input设备:input_register_device(button_dev);
(2)注册函数定义在input.c中:
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
const char *path;
int error;
__set_bit(EV_SYN, dev->evbit);
init_timer(&dev->timer);
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
error = device_add(&dev->dev);
if (error)
return error;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&input_mutex);
if (error) {
device_del(&dev->dev);
return error;
}
list_add_tail(&dev->node, &input_dev_list);//将input设备放入一个链表input_dev_list中
list_for_each_entry(handler, &input_handler_list, node);//对于一个input设备,使其与input_handler_list链表中的每一个handler都比较一下,在注册input_handler结构体时,会将该input_handler结构体放入input_handler_list链表中,见注释
input_attach_handler(dev, handler);//根据id.table比较来判断handler是否支持设备
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
EXPORT_SYMBOL(input_register_device);
注释:注册input_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;
}
list_add_tail(&handler->node, &input_handler_list);//将handler加入链表input_handler_list
list_for_each_entry(dev, &input_dev_list, node)//与每一个input设备进行比较
input_attach_handler(dev, handler);//判断是否匹配
input_wakeup_procfs_readers();
out:
mutex_unlock(&input_mutex);
return retval;
}
综上:注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"
(3)那么这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 error;
for (minor = 0; minor < EVDEV_MINORS; minor++)
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);//分配一个struct evdev结构体,然后设置
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);//input设备放进去
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;//handler处理函数放进去
evdev->handle.private = evdev;
strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));
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);//注册handle,我们可以走进去,见注释1
if (error)
goto err_free_evdev;
error = evdev_install_chrdev(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;
}
注释1:
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;//handler处理函数拿出来
struct input_dev *dev = handle->dev;//input设备拿出来
int error;
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
list_add_tail_rcu(&handle->d_node, &dev->h_list);//将handle加入到dev->h_list链表中
mutex_unlock(&dev->mutex);
synchronize_rcu();
list_add_tail(&handle->h_node, &handler->h_list);//将handle加入到handler->h_list链表中
if (handler->start)
handler->start(handle);
return 0;
}
现在我们就清楚了,如果handler支持input设备,通过connect的连接,input_device可以根据其dev->h_list来找到handle,然后根据handle找到相应的处理函数。反之,处理函数也一样。
我们可以整理出来框架:
evdev.c文件中:
input_register_handler(&evdev_handler);//evdev_handler里面定义了event、connect和fops等一些东东
input_table[handler->minor >> 5] = handler;//将根据次设备号将handler放入数组中
list_add_tail(&handler->node, &input_handler_list);//将handler加入链表input_handler_list
list_for_each_entry(dev, &input_dev_list, node)//遍历input_ dev _list链表中的所有dev
input_attach_handler(dev, handler);//判断设备与处理函数是否匹配
id = input_match_device(handler->id_table, dev);//根据handler->id_table判断设备与处理函数是否匹配
if (!id)
return -ENODEV;
handler->connect(handler, dev, id);//如果匹配就调用handler->connect函数
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
evdev->handle.dev = dev;//将dev放进去
evdev->handle.handler = handler;//将handler放进去
input_register_handle(&evdev->handle);
struct input_handler *handler = handle->handler;
list_add_tail(&handle->d_node, &handle->dev->h_list);//将handle加入到dev->h_list链表中
list_add_tail(&handle->h_node, &handler->h_list);//将handle加入到handler->h_list链表中
input_register_device(input);
list_add_tail(&dev->node, &input_dev_list);//将dev将加入input_dev_list列表中
list_for_each_entry(handler, &input_handler_list, node)//遍历input_handler_list链表中所有的handler判断是否支持设备
input_attach_handler(dev, handler);//判断设备与处理函数是否匹配
接下来的分析跟上面完全一致,我们不再分析
这样处理函数和设备就联系起来了。我们再来啰嗦一下:我们在注册设备的时候会有次设备号,而注册处理函数的时候又会根据这个次设备号来将处理函数放入数组中,然后在对设备进行操作的时候,根据次设备号来找到操作函数。这样我们就对应起来了。
input.c文件中:
register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册字符设备
input_fops里面只有open函数,在open函数中会完成取出操作函数的工作。