您的位置:首页 > 运维架构 > Linux

linux input子系统学习(一)之 input-core

2015-10-19 10:42 691 查看
linux中有众多的子系统,input子系统是其中的一种,用来处理一类型的输入设备,例如keypad,touchpanel,mice等等,我目前接触到就只有前2种也只写过前2种的驱动,所以后面的就只介绍到key的驱动以及TP的驱动了。

input的核心是一个文件,即input.c,(drivers\input),说实在的其实input子系统也是一种字符设备,只不过进行了一下封装。整个input子系统用一个系统框图就可以很好的概括了,当然这个都是大牛的书上写的,抄来的。



static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};

static int __init input_init(void)
{
int err;

err = class_register(&input_class);                           //会在sys/class/下产生input的目录
if (err) {
pr_err("unable to register input_dev class\n");
return err;
}

err = input_proc_init();                                    //会在proc/bus/产生input的目录,以及生成devices以及handlers的文件
if (err)
goto fail1;

err = register_chrdev(INPUT_MAJOR, "input", &input_fops);      //注册一个字符设备,主设备号INPUT_MAJOR = 13,名字是input,相关的操作函数初始化为
if (err) {                                                     //input_fops
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}

return 0;

fail2:	input_proc_exit();
fail1:	class_unregister(&input_class);
return err;
}

static void __exit input_exit(void)
{
input_proc_exit();
unregister_chrdev(INPUT_MAJOR, "input");
class_unregister(&input_class);
}

subsys_initcall(input_init);                     //subsys_initcall 保证较高优先级初始化
module_exit(input_exit);
由此说明,input子系统也是字符设备。我们知道,当上层应用需要访问input的设备时,必然要调用input的open函数:
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;

err = mutex_lock_interruptible(&input_mutex);
if (err)
return err;

/* No load-on-demand here? */
handler = input_table[iminor(inode) >> 5];               //input_table存放的是handler,相当于我们平常所用的驱动指针
if (handler)
new_fops = fops_get(handler->fops);

mutex_unlock(&input_mutex);

/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops || !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);                       //实际调用的是handler的指针,以evdev handler为例,实际调用的是
if (err) {                                               //evdev_open的函数
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
return err;
}

在input.c中还有几个比较重要接口用来注册input的设备及驱动的。input_register_handler 是用来注册input的驱动的,这个驱动管理着同一类的设备,一个驱动最多能管理32个设备。
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);            // 初始化handler->h_list 的链表

if (handler->fops != NULL) {                //判断handler是否为空
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;       //根据minor将全局的input_table 中初始化为handler
}

list_add_tail(&handler->node, &input_handler_list);   //input_handler_list是一个全局的存放handler的链表

list_for_each_entry(dev, &input_dev_list, node)      //遍历input_dev_list的链表,这个链表是存放input dev的链表
input_attach_handler(dev, handler);              //对dev和handler的进行匹配

input_wakeup_procfs_readers();                      //唤醒input_devices_poll_wait的等待队列

out:
mutex_unlock(&input_mutex);
return retval;
}
EXPORT_SYMBOL(input_register_handler);
那么比较重要的是他们是如何匹配的,看input_attach_handler
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;                          //id中包括input_device的设备的总线类型,version,支持的输入类型,vendor等等
int error;

id = input_match_device(handler, dev);                    //实际进行match的函数,具体分析
if (!id)
return -ENODEV;

error = handler->connect(handler, dev, id);             //将dev及handler进行Connect具体分析
if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);

return error;
}
看input_match_device的函数
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
int i;

for (id = handler->id_table; id->flags || id->driver_info; id++) {

if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype)                         //判断bustype 是否相等
continue;

if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)                          //判断vendor 是否相等
continue;

if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)                       //判断product 是否相等
continue;

if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)                      //判断version 是否相等
continue;

MATCH_BIT(evbit,  EV_MAX);                                  //判断handler的是否支持dev的事件类型,不支持的话直接进行下一轮的比较
MATCH_BIT(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
MATCH_BIT(absbit, ABS_MAX);
MATCH_BIT(mscbit, MSC_MAX);
MATCH_BIT(ledbit, LED_MAX);
MATCH_BIT(sndbit, SND_MAX);
MATCH_BIT(ffbit,  FF_MAX);
MATCH_BIT(swbit,  SW_MAX);

if (!handler->match || handler->match(handler, dev))     //判断match是否为空,后调用handler的match函数
return id;                                           //返回id
}

return NULL;
}
当成功匹配到时,会调用handler的connect的函数即handler->connect(handler, dev, id)。
好,看注册设备的input_register_device(struct input_dev *dev)。
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;

/* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit);

/* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit);

/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);

if (!dev->hint_events_per_packet)
dev->hint_events_per_packet =
input_estimate_events_per_packet(dev);

/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
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;                  //查看是否dev的getkeycode是否被赋值,否则采用input_default_getkeycode

if (!dev->setkeycode)                                           //查看是否dev的setkeycode是否被赋值,否则采用input_default_setkeycode
dev->setkeycode = input_default_setkeycode;

dev_set_name(&dev->dev, "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);          //<span style="font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px; background-color: rgb(248, 248, 248);">设置input_dev中device的名字,会在/class/input中出现input0,input1...</span>
pr_info("%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);                     //将dev添加到input_dev_list中

list_for_each_entry(handler, &input_handler_list, node)        //遍历input_handler_list 链表
input_attach_handler(dev, handler);                        //匹配dev和handler,过程和注册handler是一样的

input_wakeup_procfs_readers();                                 //唤醒读取的线程

mutex_unlock(&input_mutex);

return 0;
}

申请一个input设备input_allocate_device

struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;

dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);       //申请input_dev大空间
if (dev) {                                                 //对申请的空间进行初始化赋值
dev->dev.type = &input_dev_type;
dev->dev.class = &input_class;
device_initialize(&dev->dev);
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
INIT_LIST_HEAD(&dev->h_list);                         //初始化dev的h_list
INIT_LIST_HEAD(&dev->node);                           //初始化dev的node

__module_get(THIS_MODULE);
}

return dev;
}


总结:在input.c中,初始化了input的子系统,并且提供了一些有关将设备以及驱动如何添加到input子系统中的函数,利用这些函数,就可以往input子系统中的添加设备,读取设备的值等等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: