Android TP驱动模型框架分析

news2024/11/15 15:36:08

本文主要是对TP驱动框架的学习。

一、概述
1、触摸IC的工作原理
tp与主控间的通信接口一般是i2c,即scl、sda、gnd、vcc。在正常工作时需要加上rst、int脚。
整个过程是:通过点击屏幕,tp ic端会将int 脚电平拉低,等待主控的读取。当然主控端需先注册中断,然后在中断回调函数中先进行关中断(或上拉)再读取ic端的数据,最后将数据通过input子系统上报。
注:在触发点击事件后,int 脚的波形(矩形波)会一直存在,只有当数据被读取或者主控端的int脚输出上拉电平。

2、input 子系统介绍
输入子系统由驱动层、输入子系统核心、事件处理层三部分组成。一个输入事件,如鼠标移动通过Driver->Input core->Event handler->user space的顺序到达用户控件的应用程序。
驱动层:将底层的硬件输入转化为统一事件形式,向输入核心(Input Core)汇报。
Input Core:承上启下, 为驱动层 提供输入设备注册与操作接口,如:input_register_device;通知事件处理层对事件进行处理;在/Proc下产生相应的设备信息。
事件处理层:主要是和用户空间交互。(Linux中在用户空间将所有的设备都当初文件来处理,由于在一般的驱动程序中都有提供fops接口,以及在/dev下会生成相应的设备文件nod,这些操作在输入子系统中由事件处理层完成)。

3、工作队列介绍
工作队列(work queue)是Linux kernel中将工作推后执行的一种机制。这种机制和BH或Tasklets不同之处在于工作队列是把推后的工作交由一个内核线程去执行,因此工作队列的优势就在于它允许重新调度甚至睡眠。

4、中断部分
主要是通过注册int脚的中断函数,当有点击事件发生时,在主控端会被触发进入中断,因此可以在中断回调函数中获取点击的坐标数据等。

二、各功能详细介绍
1、input子系统介绍
1)input子系统的使用
1.1)先使用 input_allocate_device 函数申请一个 input_dev

struct input_dev *input_allocate_device(void)

input_dev的结构体大致如下:

struct input_dev {
	const char *name;
	const char *phys;
	const char *uniq;
	struct input_id id;
	unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
	unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
	unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
	... ...
};

1.2)再初始化支持的事件

set_bit()告诉input输入子系统支持哪些事件,哪些按键。例如:
set_bit(EV_KEY,button_dev.evbit)  (其中button_dev是 struct input_dev类型)
evbit: 事件类型(包括 EV_RST,EV_REL,EV_MSC,EV_KEY,EV_ABS,EV_REP等)
keybit:与某种事件类型对应的按键类型(eg. 当事件类型为EV_KEY, 包括KEY_F11,BTN_LEFT, BTN_0, BTN_1 ,BTN_MIDDLE等 )

1.3)然后使用input_register_device注册输入设备函数

int input_register_device(struct input_dev *dev)

1.4)报告事件
在解析出x、y坐标后,通过input_report_xxx进行上报,用于报告EV_KEY,EV_REL,EV_ABS事件的函数分别为:

void input_report_key (struct input_dev *dev, unsigned int code, int value)
void input_report_rel (struct input_dev *dev, unsigned int code, int value)
void input_report_abs (struct input_dev *dev, unsigned int code, int value)

1.5)报告结束
使用input sync进行报告状态的结束

input_sync()同步用于告诉input core子系统报告结束。
void input_mt_sync(struct input_dev *dev);
void input_mt_slot(struct input_dev *dev, int slot);

1.6)使用input_unregister_device注销输入设备函数
在卸载阶段进行取消注册

void input_unregister_device(struct input_dev *dev)

1.7)使用input_free_device函数释放一个input_dev
在卸载阶段进行释放资源

void input_free_device(struct input_dev *dev)

2)getevent用法
getevent 指令用于获取 input 输入事件,比如获取按键上报信息、获取触摸屏上报信息等。
2.1)getevent 命令
显示当前有那些输入设备,数量与 /dev/input 目录下相同

root@rk3288:/ # getevent
getevent
add device 1: /dev/input/event3
  name:     "ILITEK Multi-Touch-V3020"
add device 2: /dev/input/event2
  name:     "PC Camera"
add device 3: /dev/input/event1
  name:     "gsensor"
add device 4: /dev/input/event0
  name:     "rk29-keypad"

root@rk3288:/ # ls /dev/input
ls /dev/input
event0
event1
event2
event3

2.2)getevent -l
以文本形式输出事件类型和名称,比 -t 更清楚直观

// 读取 event3 数据(触摸屏)
root@rk3288:/ # getevent -l /dev/input/event3
getevent -l /dev/input/event3
// 事件类型      事件码              事件值
EV_ABS       ABS_MT_TRACKING_ID   0000000f
EV_ABS       ABS_MT_POSITION_X    00002bbc
EV_ABS       ABS_MT_POSITION_Y    00001b6d
EV_KEY       BTN_TOUCH            DOWN
EV_ABS       ABS_X                00002bbc
EV_ABS       ABS_Y                00001b6d
EV_SYN       SYN_REPORT           00000000
EV_ABS       ABS_MT_TRACKING_ID   ffffffff
EV_KEY       BTN_TOUCH            UP
EV_SYN       SYN_REPORT           00000000

3)协议方面
A协议:如果从Device获取的当前数据与上一个数据相同,而不管两次数据是否一致都上报,那就是A协议。
B协议:如果从Device获取的当前数据与上一个数据相同,而如果我们选择不上报,那么既然需要比较,总需要把上一次数据存起来吧,slot就是做这个事情的,显然这就是Slot(B)协议。

2、工作队列接口
1)创建工作队列
1.1)create_workqueue
用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。

struct workqueue_struct *create_workqueue(const char *name);

1.2)create_singlethread_workqueue
用于创建workqueue,只创建一个内核线程

注: 相对于create_singlethread_workqueue, create_workqueue同样会分配一个wq的工作队列,但是不同之处在于,对于多CPU系统而言,对每一个CPU,都会为之创建一个per-CPU的cwq结构,对应每一个cwq,都会生成一个新的worker_thread进程。但是当用queue_work向cwq上提交work节点时,是哪个CPU调用该函数,那么便向该CPU对应的cwq上的worklist上增加work节点。

当用户调用workqueue的初始化接口create_workqueue或者create_singlethread_workqueue对workqueue队列进行初始化时,内核就开始为用户分配一个workqueue对象,并且将其链到一个全局的workqueue队列中。然后Linux根据当前CPU的情况,为workqueue对象分配与CPU个数相同的cpu_workqueue_struct对象,每个cpu_workqueue_struct对象都会存在一条任务队列。紧接着,Linux为每个cpu_workqueue_struct对象分配一个内核thread,即内核daemon去处理每个队列中的任务。至此,用户调用初始化接口将workqueue初始化完毕,返回workqueue的指针。

workqueue初始化完毕之后,将任务运行的上下文环境构建起来了,但是具体还没有可执行的任务,所以,需要定义具体的work_struct对象。然后将work_struct加入到任务队列中,Linux会唤醒daemon去处理任务。

2)创建工作
2.1)静态创建
通过DECLARE_WORK在编译时静态地建一个工作:

DECLARE_WORK(name,void (*func) (void *), void *data);

这样就会静态地创建一个名为name,待执行函数为func,参数为data的work_struct结构。
2.2)动态创建
通过INIT_WORK在运行时通过指针创建一个工作:

INIT_WORK(structwork_struct *work, woid(*func) (void *), void *data);

3)调度工作
在大多数情况下, 并不需要自己建立工作队列,而是只定义工作, 将工作结构挂接到内核预定义的事件工作队列中调度, 在kernel/workqueue.c中定义了一个静态全局量的工作队列static struct workqueue_struct *keventd_wq;默认的工作者线程叫做events/n,这里n是处理器的编号,每个处理器对应一个线程。比如,单处理器的系统只有events/0这样一个线程。而双处理器的系统就会多一个events/1线程。
调度工作结构, 将工作结构添加到全局的事件工作队列keventd_wq,调用了queue_work通用模块。对外屏蔽了keventd_wq的接口,用户无需知道此参数,相当于使用了默认参数。keventd_wq由内核自己维护,创建,销毁。这样work马上就会被调度,一旦其所在的处理器上的工作者线程被唤醒,它就会被执行。
3.1)schedule_work
调度执行一个具体的任务,执行的任务将会被挂入Linux系统提供的workqueue

int schedule_work(struct work_struct *work);

3.2)schedule_delayed_work
延迟一定时间去执行一个具体的任务,功能与schedule_work类似,多了一个延迟时间

int schedule_delayed_work(struct work_struct *work, unsigned long delay);

4)执行
4.1)queue_work
调度执行一个指定workqueue中的任务。

int queue_work(struct workqueue_struct *wq, struct work_struct *work);

4.2)queue_delayed_work
延迟调度执行一个指定workqueue中的任务,功能与queue_work类似,输入参数多了一个delay。

int queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay);

数据结构如下:

struct work_struct
    {
      unsigned long pending;//这个工作是否正在等待处理
      struct list_head entry;//链接所有工作的链表,形成工作队列
      void (*func)(void *);//处理函数
      void *data;//传递给处理函数的参数
      void *wq_data;//内部使用数据
      struct timer_list timer;//延迟的工作队列所用到的定时器
    };

5)中断上下文介绍
上半部完成尽可能少的比较紧急的功能,它往往只是简单地读取寄存器中的中断状态并清除中断标志后就进行“登记中断”的工作。“登记中断”意味着将下半部处理程序挂到该设备的下半部执行队列中去。这样,上半部执行的速度就会很快,可以服务更多的中断请求。因为中断要尽可能耗时比较短,尽快恢复系统正常调试,所以把中断触发、中断执行分开,也就是所说的“上半部分(中断触发)、底半部(中断执行)”,下半部分一般有tasklet、工作队列实现。
在这里插入图片描述
3、中断处理
1)request_irq
注册中断

int request_irq(unsigned int irq, irq_handler, unsigned long flags, const char *name, void *dev);

其中:
irq: 申请的硬件中断号
handler: 向系统登记的中断处理函数(顶半部),是一个回调函数,中断发生时,系统调用这个函数
irqflags: 中断处理的属性,指定中断的触发方式及处理方式
触发方式有:IRQF_TRIGGER_RISING、IRQF_TRIGGER_FALLING、IRQF_TRIGGER_HIGH、IRQF_TRIGGER_LOW
处理方式:IRQF_SHARED, 共享中断标志。 一般设置为NULL
返回值:0—成功, -EINVAL 中断号无效,-EBUSY中断被占用或不能共享
配合工作队列即为:

//中断一般都是这样流程
ts_probe()        
    |       
    |         //在单cpu上创建一个名字时goodix_wq的工作队列
     ----- ts->goodix_wq = create_singlethread_workqueue("goodix_wq");    
    |
    |        //创建一个推后的任务,任务标识为ts->work, 这个任务的工作内容时goodix_ts_work_func这个函数
     ----- INIT_WORK(&ts->work, goodix_ts_work_func);           
    |
     ----- gtp_request_irq()
                 -----  中断号(线),中断发生时执行的函数,irq handler,中断标志,设备名,设备id
                 ----- int request_threaded_irq(unsigned int irq, irq_handler_t handler, 
                                                irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)
				----- ret = request_threaded_irq(ts->client->irq, NULL, goodix_ts_irq_handler,
                                     ts->pdata->irq_gpio_flags, ts->client->name, ts);
static irqreturn_t goodix_irq_handler(int irq, void *dev_id)
{
    struct goodix_ts_data *ts = dev_id;
    gtp_irq_disable(ts);
    //将工作(任务)放到队列中,之后cpu在合适的时候执行任务函数
    queue_work(ts->goodix_wq, &ts->work);      
    return IRQ_HANDLED;
}
总结:
        1.注册好中断下半部任务,也就是work对应的工作任务函数goodix_ts_work_func
        2.申请中断,把中断号(线)与中断处理函数(上半部),也就是goodix_ts_irq_handler连系起来,硬件上中断触发,cpu就会执行中断处理函数
        3.中断处理函数里面,把任务放到工作队列,这时候宏观上任务函数就执行了

注:一般在中断上半部是处理和硬件、状态指示等实时的操作,而在中断的下半部中处理耗时的部分。

三、总结
tp驱动的涉及的内容一般是:
1、解析设备树,获取硬件信息
2、i2c句柄、i2c私有数据的初始化
3、input子系统的初始化
4、中断注册、工作中队列的初始化
5、额外的一些配置属性的初始化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/98712.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【技术分享】Anaconda下载、安装、pip切换镜像源、conda切换镜像、conda创建指定Python版本虚拟环境教程

文章目录1.下载Anaconda1.1.下载最新版本Anaconda1.2.下载历史版本的Anaconda2.安装Anaconda3.conda切换镜像源4.pip切换镜像源5.conda创建指定版本Python环境1.下载Anaconda 1.1.下载最新版本Anaconda 步骤: 进入Anaconda官网,点击Download按钮下载最…

海量数据小内存!如何找到高频数

文章目录题目解答总结题目 如何在 20 亿个无符号整数中找到出现次数最多的那个数,在只提供 1 G 内存的条件下 解答 找到出现次数最多的数,通常的思维就是使用 HashMap 来统计这 20 亿个无符号整数中每个数出现的次数 已知只有 20 亿个数,…

b站黑马的Vue快速入门案例代码——【axios+Vue2】悦听player(音乐播放器)

目录 本文中修改的原代码中的BUG: 修改方法: 本文案例代码仍有的BUG:(欢迎大家献计献策) 目标效果: 悦音player案例——效果展示视频: 更换的新接口/参数: 1.歌曲搜索接口&…

实战讲解及分析Spring新建Bean的几种方式以及创建过程(图+文+源码)

1 缘起 作为一个应用开发人员而言,会使用某一个工具分为两个层次(个人观点): 第一个层次,知道工具,会使用这个工具解决问题; 第二个层次,理解工具的实现原理。 关于Spring的学习&am…

Linux Centos7 磁盘的分区、挂载

1、前言 注:看不懂的同学可以直接跟着后面的步骤操作 一块新的磁盘放到电脑上,要经过分区-->给分区设置文件系统--->挂载才能用。 也就是说要想将磁盘挂载,必须完成给磁盘分区和给分区设置文件系统这两步。 分区的时候先分成主分区和扩…

【DBN分类】基于matlab深度置信网络DBN变压器故障诊断【含Matlab源码 2284期】

一、深度置信网络DBN变压器故障诊断简介 1 DBN模型 DBN是深度学习中最关键的一个多层网络架构,如图2所示,由多层RBM堆叠而成,前一层RBM的输出为后一层RBM的输入,最顶层采用Softmax分类器作为标签层,输出分类识别的结果…

AD-DA转换(PCF8591)

AD转换目录一、AD转换(PCF8591)①初始化函数②读取ADC值的函数二、DA转换(PCF8591)三、STC15系列单片机用户手册.pdf—第10章一、AD转换(PCF8591) 思路:(66,两个地址0x90…

RNA-seq——上游分析练习2(数据下载+trim-galore+hisat2+samtools+featureCounts)

目录软件安装新建文件夹一、下载数据二、质控过滤1.数据质量检测2.数据质量控制3.对处理后的数据再次QC三、序列比对1.hisat2比对2.flagstat检查一下结果四、featureCounts定量写在前面——本文是转录组上游分析的实战练习。主要包含四个步骤: 数据下载&#xff08…

DockerCompose编排Redis6.2.6以及遇到的那些坑

场景 Docker中使用Dockerfile的方式部署SpringBootVue前后端分离的项目(若依前后端分离框架为例): Docker中使用Dockerfile的方式部署SpringBootVue前后端分离的项目(若依前后端分离框架为例)_霸道流氓气质的博客-CSDN博客_若依 dockerfile 在上面使用Dockerfile分别构建每个…

Heron‘s formula

In geometry, Heron’s formula (or Hero’s formula) gives the area A of a triangle in terms of the three side lengths a, b, c. If {\textstyle s{\tfrac {1}{2}}(abc)}{\textstyle s{\tfrac {1}{2}}(abc)} is the semiperimeter of the triangle, the area is,[1] {\d…

影视中学职场套路——《如懿传》中职场生存法则

目录 一、老板决定的事,赞成不赞成都要执行 二、居人之下,聪明劲儿别往外露 三、切忌大庭广众直接与上级冲突 四、取悦所有人,不如取悦最大的boss 五、再强的人,也需要团队作战 六、人善被人欺(首先要自保&#…

第三十一章 linux-模块的加载过程一

第三十一章 linux-模块的加载过程一 文章目录第三十一章 linux-模块的加载过程一sys_init_modulestruct moduleload_module模块ELF静态的内存视图字符串表(string Table)HDR视图的第一次改写find_sec函数ps:kernel symbol内核符号表,就是在内核的内部函数…

opencv图像去畸变

图像去畸变的思路 对于目标图像(无畸变图像)上的每个像素点,转换到normalize平面,再进行畸变变换,进行投影,得到这个像素点畸变后的位置,然后将这个位置的源图像(畸变图像)的像素值作为目标图像…

Visual Studio 2022安装与编译简单c语言以及C#语言(番外)

文章目录1 软件下载网站2 下载与安装3 创建并学习C语言4 创建并学习C#语言1 软件下载网站 Visual Studio官网 2 下载与安装 1、下载社区版即可。 2、下载得到安装文件,右键以管理员方式运行安装文件。 3、点击继续。 4、等待下载完成。 5、这里学习C选择使用…

SpringBoot文件上传同时,接收复杂参数

目录 环境信息 问题描述 错误分析 解决方法 简单参数 总结 环境信息 Spring Boot:2.0.8.RELEASE Spring Boot内置的tomcat:tomcat-embed-core 8.5.37 问题描述 收到文件上传的开发工作,要求能适配各种场景,并且各场景的请求…

C语言——操作符详解(上)

C语言——操作符详解(上) 操作符的分类 C语言中的操作符主要分为算术操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式、下标引用、函数调用和结构成员。我将分成三篇文章为大家详细介绍以上所…

[附源码]Python计算机毕业设计Django网约车智能接单规划小程序

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

[附源码]Python计算机毕业设计华夏商场红酒管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等…

AI绘画火爆,以昆仑万维AIGC为例,揭秘AI绘画背后的模型算法

AI绘画火爆,以昆仑万维AIGC为例,揭秘AI绘画背后的模型算法 一、前言 最近AI绘画让人工智能再次走进大众视野。在人工智能发展早起,一直认为人工智能能实现的功能非常有限。通常都是些死板的东西,像是下棋、问答之类的&#xff0…

mysql锁范围(一)表级锁变行级锁

文章目录行级锁1. 用两个连接connection登陆mysql2. 测试无索引情况1)机器1开启事务,执行更新北京仓数据sql,不提交事务2)机器2开启事务,先查询北京仓3)机器2开始更新上海仓数据4)机器1事务回滚…