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

嵌入式Linux驱动笔记(六)------浅析input输入子系统框架

2017-07-23 13:29 393 查看
你好!这里是风筝的博客,

欢迎和我一起交流。

基于设备驱动分层的思想,其实理解了platform总线,输入子系统也是可以很好理解的。

以kernel 4.8.17为例:

input.c文件:

static char *input_devnode(struct device *dev, umode_t *mode)

{

return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev));

}

struct class input_class = {

.name = "input",

.devnode = input_devnode,

};

static int __init input_init(void)

{

int err;

err = class_register(&input_class);

if (err) {

pr_err("unable to register input_dev class\n");

return err;

}

err = input_proc_init();

if (err)

goto fail1;

err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),

INPUT_MAX_CHAR_DEVICES, "input");

if (err) {

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;

}

初始化的工作主要做了:

创建一个input_class类.

在/proc下创建入口项.即/proc/bus/input目录产生设备信息.

注册字符设备input,主设备号为13(#define INPUT_MAJOR13)

至于input_class结构体下的input_devnode,我也不知道干什么的......

值得注意的是注册input设备的时候都是在这个input下,即/dev/input/event0等等......

好吧,input注册后,基于设备驱动分层的思想,总要有设备文件和驱动文件吧,下面可以看下evdev.c文件,(为什么看这个文件?其实我想看gpio_key.c这个文件的,但是这个驱动文件里的probe涉及到设备树,目前还不会搞,脑阔疼......)

evdev.c:

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)

{

return input_register_handler(&evdev_handler);

}

向输入子系统注册驱动handler,注意哦,这里是handler,译为:处理者,

之后还会看到一个词:handle,译为:手柄,这两个不要搞混了。

接下来进入input_register_handler函数:

int input_register_handler(struct input_handler *handler)

{

struct input_dev *dev;

int error;

error = mutex_lock_interruptible(&input_mutex);

if (error)

return error;

INIT_LIST_HEAD(&handler->h_list);

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();

mutex_unlock(&input_mutex);

return 0;

}

初始化h_list链表.

14、15行,重头戏,遍历链表,寻找handler匹配的设备.

跟踪进入input_attach_handler函数:

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)

{

const struct input_device_id *id;

int error;

id = input_match_device(handler, dev);

if (!id)

return -ENODEV;

error = handler->connect(handler, dev, id);

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;

}

这里面有一个匹配函数,就像platform总线一样,会把设备和驱动两两匹配起来。

如果匹配成功,这会执行connect函数进行连接(就好比probe函数一样).

设备和驱动是怎么匹配的:

static const struct input_device_id *input_match_device(struct input_handler *handler,

struct input_dev *dev)

{

const struct input_device_id *id;

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)

continue;

if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)

if (id->vendor != dev->id.vendor)

continue;

if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)

if (id->product != dev->id.product)

continue;

if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)

if (id->version != dev->id.version)

continue;

if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))

continue;

if (!bitmap_subset(id->keybit, dev->keybit, KEY_MAX))

continue;

if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))

continue;

if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))

continue;

if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))

continue;

if (!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX))

continue;

if (!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX))

continue;

if (!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX))

continue;

if (!bitmap_subset(id->swbit, dev->swbit, SW_MAX))

continue;

if (!handler->match || handler->match(handler, dev))

return id;

}

很遗憾,这个match函数我也看不懂他们是怎么具体匹配的..........伤心啊 。

不过据我了解,evdev.c的handler可以适应任何的dev设备,即分配一个input_dev结构体,即使什么都不做,也能和evdev.c的handler匹配成功。而且这个match的时候,即使匹配成功了也没用break跳出,所以会出现一个dev对应多个handler的情况......

当它们匹配成功后,就会调用connect函数.这个connect函数是什么呢,就是之前input_register_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 dev_no;

int error;

/*比较长,省略了一部分代码*/

dev_set_name(&evdev->dev, "event%d", dev_no);

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);

error = input_register_handle(&evdev->handle);

if (error)

goto err_free_evdev;

cdev_init(&evdev->cdev, &evdev_fops);

evdev->cdev.kobj.parent = &evdev->dev.kobj;

error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);

/*比较长,省略了一部分代码*/

return 0;

}

注意第10行,注册input的时候,就是这样会在/dev/input下生成event0、1、2...n.

还有注意handle(不是handler哦) ,之前说过,这里会把dev、name、handler、private通过evdev->handle记录下来,并注册handle(23行,即把dev和handler添加到h_list链表中),这样通过handle就可以找到dev与handler,实现设备和驱动的连接了。

最后,通过device_add就实现设备的注册了("关于正真设备文件的创建(不是指sys下的文件), 最终是由device_add函数里头的kobject_uevent(&dev->kobj, KOBJ_ADD)完成的对hotplug_helper的调用的。

如果想了解uevent的详情,可以阅读 《Linux设备模型浅析之uevent篇》或者他的一系列文章。

")

上面讲的是驱动部分,接下来就是设备部分,原理是一样的:

就在input.c文件里的input_register_device函数,方法是一模一样的,

调用:

list_for_each_entry(handler, &input_handler_list, node)

input_attach_handler(dev, handler);

所以说,不管事设备先注册还是驱动先注册,他们都会去寻找、等待自己的另一半(好好的码着代码,突然就虐狗了............)

驱动和设备建立好连接后,怎么用呢?

当然是通过event上报event(事件)到Handler层进行处理,然后提交给用户了。

input_event->input_handle_event->input_pass_values->input_to_handler->handler->events(handle, vals, count);

所以看下evdev.c文件里的evdev_event函数:

static void evdev_event(struct input_handle *handle,

unsigned int type, unsigned int code, int value)

{

struct input_value vals[] = { { type, code, value } };

evdev_events(handle, vals, 1);

}

它会调用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];

ev_time[EV_CLK_MONO] = ktime_get();

ev_time[EV_CLK_REAL] = ktime_mono_to_real(ev_time[EV_CLK_MONO]);

ev_time[EV_CLK_BOOT] = ktime_mono_to_any(ev_time[EV_CLK_MONO],

TK_OFFS_BOOT);

rcu_read_lock();

client = rcu_dereference(evdev->grab);

if (client)

evdev_pass_values(client, vals, count, ev_time);

else

list_for_each_entry_rcu(client, &evdev->client_list, node)

evdev_pass_values(client, vals, count, ev_time);

rcu_read_unlock();

}

可以看出,刚开始对读操作上锁,防止被干扰,之后进行填充数据,完成之后再解锁。

填充数据由evdev_pass_values函数完成。

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;

if (client->revoked)

return;

event.time = ktime_to_timeval(ev_time[client->clk_type]);

/* Interrupts are disabled, just acquire the lock. */

spin_lock(&client->buffer_lock);

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);

}

值得注意的是,当事件的类型为EV_SYN、事件代码为SYN_REPORT,并且client->packet_head != client->head(buffer采用的是环形方式存储,即当设备的信息包的头!=设备的头?不知道怎么理解)时,会唤醒 指定的注册在等待队列上的进程.

即通过input_sync函数产生:

static inline void input_sync(struct input_dev *dev)

{

input_event(dev, EV_SYN, SYN_REPORT, 0);

}

当然,这个不是我们现在关心的,先码住,心理默念:会唤醒某个东西,会唤醒某个东西,会唤醒某个东西,,,,,,,我们比较关心__pass_event函数,就是在这个函数里填充消息。

__pass_event函数:

static void __pass_event(struct evdev_client *client,

const struct input_event *event)

{

client->buffer[client->head++] = *event;

client->head &= client->bufsize - 1;

if (unlikely(client->head == client->tail)) {

/*

* This effectively "drops" all unconsumed events, leaving

* EV_SYN/SYN_DROPPED plus the newest event in the queue.

*/

client->tail = (client->head - 2) & (client->bufsize - 1);

client->buffer[client->tail].time = event->time;

client->buffer[client->tail].type = EV_SYN;

client->buffer[client->tail].code = SYN_DROPPED;

client->buffer[client->tail].value = 0;

client->packet_head = client->tail;

}

if (event->type == EV_SYN && event->code == SYN_REPORT) {

client->packet_head = client->head;

kill_fasync(&client->fasync, SIGIO, POLL_IN);

}

}

就这样,将消息填充到了buffer中......

那用户应用程序又怎么操作的?

还记得之前说的evdev_connect函数吗?

里面有一句:cdev_init(&evdev->cdev, &evdev_fops);

chardevice的初始化,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,

};

终于又回到了我们最熟悉的字符设备的file_operations啊,真是 这里的山路十八弯~不容易啊......

进入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;

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 (;;) {

if (!evdev->exist || client->revoked)

return -ENODEV;

if (client->packet_head == client->tail &&

(file->f_flags & O_NONBLOCK))

return -EAGAIN;

/*

* count == 0 is special - no IO is done but we check

* for error conditions (see above).

*/

if (count == 0)

break;

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;

}

有点长,但是为了最后的胜利,忍了..........

分析下这个函数:

第10行:如果读取的消息不为0且长度小于消息buffer里的长度

第17行:又见环形缓存存储,当信息包的头等于设备的尾时?不知道,估计想描述buffer是否为空时吧,当消息为空并且文件是非阻塞方式打开时,立即返回.......

第31行:最重要的一行了,input_event_to_user这个函数里面会调用copy_to_user函数,把buffer里的数据传输到应用空间,这样,即实现了应用程序的数据读取。

第40行:如果文件不是以非阻塞方式打开,则会执行wait_event_interruptible休眠等待。嘿嘿,还记得之前默念的东西吗,就是这里!!!!别慌,好好睡觉,当input_sync函数调用时,我就来叫你起床,嘿嘿嘿......

好了,写到这里好像写了挺多的了,累了,,,剩下的write、open之类的懒得分析了,套路都是一样的,剩下的自己练练手咯,祝你们成功!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: