首页 > 其他分享 >字符设备驱动之输入子系统分析(四)

字符设备驱动之输入子系统分析(四)

时间:2023-07-12 17:57:42浏览次数:32  
标签:字符 handle evdev dev 系统分析 handler input 驱动 设备

作者:Bright-Ho

联系方式:[email protected]


这一节主要讲解“设备硬件层”;这一层的内容就需要我们自己来实现;这里主要讲解框架;


在“核心层”里面会提供一个input_register_device()这样一个函数;


在“设备硬件层”先构造并初始化一个struct input_dev *dev结构,通过input_register_device()函数,来注册:

(1)把dev结构放入设备链表

list_add_tail(&dev->node, &input_dev_list);

(2)把“事件处理层”的handler链表里面每一项handler取出来,通过idtable和dev进行匹配;匹配成功,就调用input_handler里面的connect函数,建立“连接”;

list_for_each_entry(handler, &input_handler_list, node)

input_attach_handler(dev, handler);


从框架来讲,主要就是做这两件事情;


这里小小的总结一下,设备硬件层,核心层,事件处理层,各个层次,主要做了哪些工作?

设备硬件层:和具体硬件设备相关的,需要自己构造一个符合硬件设备特性的结构dev;并注册进设备链表;


设备硬件层:该层构造一个符合硬件设备的dev结构,并注册进dev链表;


核心层:该层会给设备驱动层,事件处理层,提供注册接口,以及匹配函数,等等;类似于一个工具箱,设备驱动层和事件处理层要使用什么接口,直接调用核心层提供的接口;


事件处理层:这一层也是构建一个符合事件处理的结构,并注册进事件处理的链表;并且提供设备接口给应用层;(该结构实现了file_operations里面的驱动接口,以便给应用层调用)


设备和事件通过id号,匹配成功后就会建立连接;一旦“连接”成功,应用层open,read,write一个设备节点,就能调用到“事件处理层”里面的驱动接口;


和之前我们写的字符设备的那5点相比,我们就能知道引入“输入子系统”后的字符设备之间差异;

差异:

(1)file_opreations结构不用我们自己实现了,“事件处理层”帮我们做好了;

(2)不用创建设备节点了;“事件处理层”帮我们做好了


最重要的是,简化了我们写设备驱动程序的难度;我们只关注设备驱动层,事件处理层和核心层,都由内核帮我们完成的;


现在以evdev.c中input_handler实现的connect函数为例,看看它是怎么建立“连接”的?


evdev_connect函数:

(1)内核空间分配一个evdev结构

evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

(2)设置evdev结构

646 evdev->exist = 1;

647 evdev->minor = minor;

648 evdev->handle.dev = dev;

649 evdev->handle.name = evdev->name;

650 evdev->handle.handler = handler;

651 evdev->handle.private = evdev;

652 sprintf(evdev->name, "event%d", minor);

 

注意:这里handle.dev->dev(左边); handle.handler->handler(右边)


(3)创建设备节点

cdev = class_device_create(&input_class, &dev->cdev, devt, dev->cdev.dev, evdev->name);

注意:为什么会在这里创建设备?

之前写字符设备创建类和设备的时候都是一气呵成;其原因是设备驱动和事件处理驱动,都是自己一次性完成的,不存在设备匹配事件的过程;那么引入输入子系统后,在核心层input.c中,一开始就通过class_register(&input_class)来创建了设备;至于什么时候在设备下面创建节点?那就是当执行connnect函数的时候,肯定设备和事件已经匹配成功了;所以在连接函数中创建设备节点,才会使节点拥有设备信息!!!


(4)注册evdev中的handle

input_register_handle(&evdev->handle);

1223 int input_register_handle(struct input_handle *handle)

1224 {

1225 struct input_handler *handler = handle->handler;

1226

1227 list_add_tail(&handle->d_node, &handle->dev->h_list);

1228 list_add_tail(&handle->h_node, &handler->h_list);

1229

1230 if (handler->start)

1231 handler->start(handle);

1232

1233 return 0;

1234 }


重点:

这里dev链表和handler链表同时都指向handle结构,handle在设置的时候,里面的dev就指向connect函数传进来的dev设备(这个是自己实现的),里面的handler就指向connect函数传进来的handler处理事件(这个是事件处理层提供的,并且是与dev匹配成功的);这样dev设备和handler处理事件,就建立了联系!!!

以后dev设备就可以通过dev链表找到handle,再通过handle中的handler就可以直接找到具体的处理事件;反过来,handler处理事件就可以通过handler链表找到handle,再通过handle中的dev就可以直接找到具体的硬件设备;


这里再简要说明一下“事件处理层”input_handler.evdev_event事件上报函数;


evdev_event:该函数什么时候调用?

46 static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)

47 {

...

54 do_gettimeofday(&client->buffer[client->head].time);

55 client->buffer[client->head].type = type;

56 client->buffer[client->head].code = code;

57 client->buffer[client->head].value = value;

58 client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1); ...

69

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

73 wake_up_interruptible(&evdev->wait);

74 }

那么根据该函数实现的内容来看,数据在环形缓冲区中准备好了,就来唤醒等待队列中的进程;有唤醒操作,前提是有进程会休眠,那么是哪个进程进入休眠了呢?

我们可以看“事件处理层”中input_handlr.evdev_fopse.vdev_read,也就是实现驱动接口的读函数;

281 static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)

282 {

287 if (count < evdev_event_size())

288 return -EINVAL;

289

290 if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))

291 return -EAGAIN;

292

293 retval = wait_event_interruptible(evdev->wait,

client->head != client->tail || !evdev->exist); ...

312 return retval;

313 }


当应用程序调用read函数去去读取硬件设备的数据的时候,会调用到“事件处理层”里面的提供的read驱动接口函数,该函数发现环形缓冲区没有数据,并且以阻塞方式打开该设备接口时(注意看上面红色代码),就会陷入休眠,等待数据的到来;我们分析了evdev_event事件上报函数是数据准备好了用来换唤醒等待队列的进程,那么关键问题来了,是谁来调用这个事件上报函数呢?我们写按键驱动的时候就知道,当按键按下的时候,会产生按键数据,同时会产生按键中断,并进入按键中断服务程序,在中断程序中,采集数据,并唤醒休眠的应用程序,告诉应用程序,数据已经准备好了;所以说这里evdev_event事件处理函数会被中断处理程序调用;


例如:内核自带的按键驱动例子

30 static irqreturn_t gpio_keys_isr(int irq, void *dev_id)

31 {

32 int i;

33 struct platform_device *pdev = dev_id;

34 struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;

35 struct input_dev *input = platform_get_drvdata(pdev);

36

37 for (i = 0; i < pdata->nbuttons; i++) {

38 struct gpio_keys_button *button = &pdata->buttons[i];

39 int gpio = button->gpio;

40

41 if (irq == gpio_to_irq(gpio)) {

42 unsigned int type = button->type ?: EV_KEY;

43 int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low;

44

45 input_event(input, type, button->code, !!state);

46 input_sync(input);

47 }

48 }

49

50 return IRQ_HANDLED;

51 }


input_event就是上报事件函数;原型如下:

46 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) {

...

192 list_for_each_entry(handle, &dev->h_list, d_node)

193 if (handle->open)

194 handle->handler->event(handle, type, code, value);


}


该中断函数,最终会调用到“事件处理层”提供的事件上报函数event;在环形缓冲区中准备号数据,并唤醒等待队列中的应用程序;


目前软件框架讲解的差不多了,现在应该知道,引入“输入子系统”的字符设备驱动,哪些是由自己完成的,哪些是由内核帮我们完成了;注意了,这里只是分析了软件框架,硬件设备原理相关的内容,会在后面根据具体的硬件设备进行详细的分析;

标签:字符,handle,evdev,dev,系统分析,handler,input,驱动,设备
From: https://www.cnblogs.com/Bright-Ho/p/17548361.html

相关文章

  • 字符设备驱动之输入子系统分析(三)
     作者:Bright-Ho联系方式:[email protected]输入子系统之“事件处理层”分析:注意:我们现在只是涉及输入子系统的框架;具体细节会涉及到硬件的工作原理,在后面我们写驱动的时候会说到细节方面;这一节,我们接触到了“事件处理层”;事件处理层包含了evdev.c(事件设备),tsdev.c......
  • HJ102 字符统计
    1.题目读题HJ102 字符统计  考查点 这道题的考查点可能是以下几个方面:字符串的处理和操作,如遍历、分割、拼接等。数据结构的选择和使用,如数组、字典、列表等。排序算法的理解和实现,如冒泡排序、选择排序、快速排序等。编程语言的基本语法和规范,如变量、函数、循......
  • C# 获取字符串自动换行
    ```//判断是否是汉字publicstaticboolIsChinese(charchr){return(int)chr>0x4E00&&(int)chr<0x9FA5;}///<summary>///截取字符串,不限制字符串长度///</summary>///<paramname="str&qu......
  • Windows 驱动程序签名教程(一) --- 开发测试篇
    一、内核驱动签名初篇1.大概聊一聊现有驱动情况1.开启安全启动(SecureBoot)1.使用微软WHQL签名2.使用2013-2015年签发的驱动签名,已过期未吊销未拉黑(不知道什么时候打个补丁会修复).2.关闭安全启动(SecureBoot)1.使用微软WHQL签名2.使用过期签名的sha1sha256都可以正常......
  • Java IO:字节流、字符流、缓冲流
    原文:https://blog.csdn.net/mu_wind/article/details/108674284流的特性有下面几点:先进先出:最先写入输出流的数据最先被输入流读取到。顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile除外)只读或只写:......
  • 高通个别驱动创建Buffer耗时高问题的解决
    前言最近在优化游戏的时候,发现在在高通特定驱动版本的机器上(855,855+等),创建VB的耗时跟VB的数量成正比,这个应该是驱动的bug。跟官方人员确认过,确实是有这个问题,他们给的解决方案是减少Buffer的数量,经过一轮优化后,Buffer数量减少了将近30%,但是这个耗时的问题还是没能解决,在正常机......
  • 如何实现mysql驱动的具体操作步骤
    MySQL驱动简介及使用示例1.什么是MySQL驱动?MySQL驱动是用于连接和操作MySQL数据库的软件模块。MySQL是一种常用的关系型数据库管理系统,而驱动则是用于连接不同编程语言和MySQL数据库之间的桥梁。驱动有助于程序员使用编程语言操作MySQL数据库,例如执行查询、插入、更新和删除数据......
  • 构造字符串有感
    题目要求用小写字母构造一个字符串,这个字符串最大到1e6,这种题一般有两种思路。思路我们要做的就是,找到规律。打表找规律用dp思想,线性推,考虑当前位置字符只受前面已经出现的字符影响,且无后效性。因为字母只有26个,所以找到状态方程,就可以很好的解决了题目参考cf:https://codef......
  • 【剑指Offer】54、字符流中第一个不重复的字符
    【剑指Offer】54、字符流中第一个不重复的字符题目描述:请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。输出描述:如果当......
  • 1-14 编写一个程序,打印输入中各个字符出现频度的直方图
    ArchlinuxGCC13.1.1 202304292023-07-1123:25:36星期二 做了调整,只输出大小写字母共计56个字符的横向直方图,如有其他需要,自行添加。点击查看代码#include<stdio.h>#defineTRUE1#defineFALSE0intmain(){intc_in;intc_num[52];intc......