• android 电容屏(二):驱动调试之基本概念篇


    关键词:android  电容屏 tp 工作队列 中断 多点触摸协议
    平台信息:
    内核:linux2.6/linux3.0
    系统:android/android4.0 
    平台:S5PV310(samsung exynos 4210) 

    作者:xubin341719(欢迎转载,请注明作者)

    參考站点:http://edsionte.com/techblog/archives/1582
    这部分參考别人的多一点

    android 电容屏(一):电容屏基本原理篇

    android 电容屏(二):驱动调试之基本概念篇

    android 电容屏(三):驱动调试之驱动程序分析篇


            电容屏驱动调试先了解Linux电容屏驱动中几个经常使用的概念:
                  中断下半部-工作队列;
                  input机制。
                  Linux与Android 多点触摸协议。


    一、中断下半部-工作队列

    1、中断

                   先看一下宋宝华先生的《linux设备驱动开发详细解释》里面对中断的描写叙述吧。这本书个人感觉 写的比較好,从開始学驱动到如今,还能从中得到不少知识。

                   设备的中断会打断内核中进程的正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽可能地短小精悍。可是,这个良好的愿望往往与现实并不吻合。在大多数真实的系统中,其中断到来时,要完毕的工作往往并不会是短小的。它可能要进行较大量的耗时处理。

    例如以下图描写叙述了Linux内核的中断处理机制。为了在中断运行时间尽可能短和中断处理需完毕大量工作之间找到一个平衡点。Linux将中断处理程序分解为两个半部:顶半部(top  half)和底半部(bottom half)。

    顶半部完毕尽可能少的比較紧急的功能,它往往仅仅是简单地读取寄存器中的中断状态并清除中断标志后就进行“登记中断”的工作。“登记中断”意味着将底半部处理程序挂到该设备的底半部运行队列中去。这样。顶半部运行的速度就会非常快。能够服务很多其他的中断请求。如今。中断处理工作的重心就落在了底半部的头上,它来完毕中断事件的绝大多数任务。底半部差点儿做了中断处理程序全部的事情,并且能够被新的中断打断。这也是底半部和顶半部的最大不同,由于顶半部往往被设计成不可中断。底半部则相对来说并非非常紧急的。并且相对照较耗时,不在硬件中断服务程序中运行。虽然顶半部、底半部的结合能够改善系统的响应能力,可是,僵化地觉得Linux设备驱动中的中断处理一定要分两个半部则是不正确的。假设中断要处理的工作本身非常少。则全然能够直接在顶半部全部完毕。

               事实上上面这一段大致说明一个问题。那就是:中断要尽可能耗时比較短,尽快恢复系统正常调试,所以把中断触发、中断运行分开,也就是所说的“上半部分(中断触发)、底半部(中断运行)”,事实上就是我们后面说的中断上下文。下半部分一般有tasklet、工作队列实现,触摸屏中中断实现以工作队列形式实现的,所以我们今天重点讲工作队列。


    2、为什么还须要工作队列?

               工作队列(work queue)是第二种将中断的部分工作推后的一种方式。它能够实现一些tasklet不能实现的工作,比方工作队列机制能够睡眠。

    这种差异的本质原因是。在工作队列机制中。将推后的工作交给一个称之为工作者线程(worker thread)的内核线程去完毕(单核下通常会交给默认的线程events/0)。因此,在该机制中。当内核在运行中断的剩余工作时就处在进程上下文(process context)中。

    也就是说由工作队列所运行的中断代码会表现出进程的一些特性。最典型的就是能够又一次调度甚至睡眠。

                 对于tasklet机制(中断处理程序也是如此),内核在运行时处于中断上下文(interrupt context)中。而中断上下文与进程毫无瓜葛。所以在中断上下文中就不能睡眠。因此。选择tasklet还是工作队列来完毕下半部分应该不难选择。

    当推后的那部分中断程序须要睡眠时,工作队列毫无疑问是你的最佳选择;否则,还是用tasklet吧。

    3、中断上下文

              有上面那个图能够看出上下两部分吧。就是上下文吧,这个比較好理解。

              看下别人比較专业的解释:

              在了解中断上下文时。先来回想还有一个熟悉概念:进程上下文(这个中文翻译真的不是非常好理解,用“环境”比它好非常多)。

    一般的进程运行在用户态,假设这个进程进行了系统调用,那么此时用户空间中的程序就进入了内核空间,并且称内核代表该进程运行于内核空间中。由于用户空间和内核空间具有不同的地址映射。并且用户空间的进程要传递非常多变量、參数给内核,内核也要保存用户进程的一些寄存器、变量等。以便系统调用结束后回到用户空间继续运行。这样就产生了进程上下文。

               所谓的进程上下文。就是一个进程在运行的时候,CPU的全部寄存器中的值、进程的状态以及堆栈中的内容。当内核须要切换到还有一个进程时(上下文切换),它须要保存当前进程的全部状态。即保存当前进程的进程上下文,以便再次运行该进程时,能够恢复切换时的状态继续运行。上述所说的工作队列所要做的工作都交给工作者线程来处理,因此它能够表现出进程的一些特性,比方说能够睡眠等。

                  对于中断而言。是硬件通过触发信号,导致内核调用中断处理程序。进入内核空间。

    这个过程中。硬件的一些变量和參数也要传递给内核,内核通过这些參数进行中断处理,中断上下文就能够理解为硬件传递过来的这些參数和内核须要保存的一些环境,主要是被中断的进程的环境。因此处于中断上下文的tasklet不会有睡眠这种特性。

    4、工作队列的用法

    内核中通过下述结构体来表示一个详细的工作:

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

               而这些工作(结构体)链接成的链表就是所谓的工作队列。工作者线程会在被唤醒时运行链表上的全部工作,当一个工作被运行完毕后,相应的work_struct结构体也会被删除。

    当这个工作链表上没有工作时,工作线程就会休眠。

    (1)、通过例如以下宏能够创建一个要推后的完毕的工作:

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

    (2)、也能够通过下述宏动态创建一个工作:

    1. INIT_WORK(struct work_struct*work,void(*func)(void*),void *data);  

    与tasklet相似,每一个工作都有详细的工作队列处理函数,原型例如以下:

    1. void work_handler(void *data)  

    将工作队列机制相应到详细的中断程序中,即那些被推后的工作将会在func所指向的那个工作队列处理函数中被运行。

    (3)、实现了工作队列处理函数后,就须要schedule_work函数对这个工作进行调度,就像这样:

    1. schedule_work(&work);  

    这样work会立即就被调度,一旦工作线程被唤醒,这个工作就会被运行(由于其所在工作队列会被运行)。

    二、input子系统概述 可见文章基于 mini2440 电阻式触摸屏(三):Linux输入子系统(InputSubsystem)

                按键、鼠标、触摸屏、电池信息等。都是通过input子系统上报。

    三、Linux与Android 多点触摸协议

                为了使用功能强大的多点触控设备,就须要一种方案去上报用户层所需的详细的手指触摸数据。

    这个文档所描写叙述的多点触控协议能够让内核驱动程序向用户层上报随意多指的数据信息。

    1、使用说明

                单点触摸信息是以ABS承载并按一定顺序发送,如BTN_TOUCH、ABS_X、ABS_Y、SYNC。而多点触摸信息则是以ABS_MT承载并按一定顺序发送,如ABS_MT_POSITION_X、ABS_MT_POSITION_Y,然后通过调用input_mt_sync()产生一个 SYN_MT_REPORT event来标记一个点的结束。告诉接收方接收当前手指的信息并准备接收其他手指的触控信息。

    最后调用 input_sync()函数上报触摸信息開始动作并告诉接收方開始接收下一系列多点触摸信息。

                 协议定义了一系列ABS_MT事件,这些事件被分为几大类,充许仅仅应用其中的一部份,多点触摸最小的事件集中应包含ABS_MT_TOUCH_MAJOR、ABS_MT_POSITION_X和 ABS_MT_POSITION_Y,以此来实现多点触摸。

    假设设备支持ABS_MT_WIDTH_MAJOR这个事件,那么此事件能够提供手指触摸接触面积大小。

    触摸方向等信息能够由ABS_MT_TOUCH_MINOR, ABS_MT_WIDTH_MINOR and ABS_MT_ORIENTATION提供。ABS_MT_TOOL_TYPE提供触摸设备的类别,如手或是笔或是其他。最后有些设备可能会支持ABS_MT_TRACKING_ID。用来支持硬件跟踪多点信息。即该点属于哪一条线等。

    1. 下面是两点触摸支持的最小事件集序列:  
    2. ABS_MT_TOUCH_MAJOR  
    3. ABS_MT_POSITION_X  
    4. ABS_MT_POSITION_Y  
    5. SYN_MT_REPORT //上报第一个点  
    6. ABS_MT_TOUCH_MAJOR  
    7. ABS_MT_POSITION_X  
    8. ABS_MT_POSITION_Y  
    9. SYN_MT_REPORT //上报第二个点  
    10.            ………… //完毕多点上报  
    11. SYN_REPORT //開始动作  

    2、Event原语

                    接触”一词用来描写叙述一个物体直接碰到还有一个物体的表面。

                    ABS_MT_TOUCH_MAJOR描写叙述了主接触面的长轴,它和X。Y同一个单位,假设一个面的分辨率为X*Y,则ABS_MT_TOUCH_MAJOR的最大值为sqrt(X^2+Y^2)

    1. <span style="white-space:pre">  </span>ABS_MT_TOUCH_MINOR描写叙述了接触面的短轴,假设接触面是圆形。它能够不用。

        

    2.     ABS_MT_WIDTH_MAJOR描写叙述了接触工具的长轴  
    3.     ABS_MT_WIDTH_MINOR描写叙述了接触工具的短轴  
    4.     ABS_MT_TOUCH_MAJOR := max(X, Y)  
    5.     ABS_MT_TOUCH_MINOR := min(X, Y)  
    6.     ABS_MT_ORIENTATION := bool(X > Y)  

                   以上四个參数能够用来生成额外的触摸信息。ABS_MT_TOUCH_MAJOR/ABS_MT_WIDTH_MAJOR的比率能够用来描写叙述压力。

                    ABS_MT_ORIENTATION

                    ABS_MT_POSITION_X接触面的中心点X坐标

                    ABS_MT_POSITION_Y接触面的中心点Y坐标

                    ABS_MT_TOOL_TYPE描写叙述接触工具类型,非常多内核驱动无法区分此參数如手指及笔,假设是这样。该參数能够不用,协议眼下支持MT_TOOL_FINGER和MT_TOOL_PEN两种类型。

                    ABS_MT_BLOB_ID形状集ID。集合几个点以描写叙述一个形状。非常多驱动没有形状属性,此參数能够不用。ABS_MT_TRACKING_ID描写叙述了从接触開始到释放的整个过程的集合。假设设备不支持。此參数可是不用。

    3、触摸轨迹

                仅有少数设备能够明触的标识真实的 trackingID。多数情况下 trackingID仅仅能来标识一次触摸动作的过程。

    4、手势

                多点触摸指定的应用是创建手势动作, TOUCH和 WIDTH參数经经常使用来差别手指的压力和手指间的距离。另外 MINOR类的參数能够用来差别设备的接触面的大小(点接触还是面接触),ORIENTATION能够产生旋转事件。

    5、在Linux内核支持的基础上。Android在其2.0源代码中添加多点触摸功能(android4.0中间层有所不同)

                由此触摸屏在Android的frameworks被全然分为2种实现途径:单点触摸屏的单点方式。多点触摸屏的单点和多点方式。

                 在Linux的input.h中,多点触摸功能依赖于下面几个基本的软件位:

    1. ……  
    2. #define SYN_REPORT0  
    3. #define SYN_CONFIG1  
    4. #define SYN_MT_REPORT2  
    5. ……  
    6. #define ABS_MT_TOUCH_MAJOR0x30  
    7. #define ABS_MT_TOUCH_MINOR0x31  
    8. #define ABS_MT_WIDTH_MAJOR0x32  
    9. #define ABS_MT_WIDTH_MINOR0x33  
    10. #define ABS_MT_ORIENTATION0x34  
    11. #define ABS_MT_POSITION_X0x35  
    12. #define ABS_MT_POSITION_Y0x36  
    13. #define ABS_MT_TOOL_TYPE0x37  
    14. #define ABS_MT_BLOB_ID0x38  
    15. ……  

    在Android中相应的软件位定义在RawInputEvent.java中:

      

    1. ……  
    2.  public class RawInputEvent {  
    3.  ……  
    4.  public static final int CLASS_TOUCHSCREEN_MT = 0x00000010;  
    5.  ……  
    6.  public static final int ABS_MT_TOUCH_MAJOR = 0x30;  
    7.  public static final int ABS_MT_TOUCH_MINOR = 0x31;  
    8.  public static final int ABS_MT_WIDTH_MAJOR = 0x32;  
    9.  public static final int ABS_MT_WIDTH_MINOR = 0x33;  
    10.  public static final int ABS_MT_ORIENTATION = 0x34;  
    11.  public static final int ABS_MT_POSITION_X = 0x35;  
    12.  public static final int ABS_MT_POSITION_Y = 0x36;  
    13.  public static final int ABS_MT_TOOL_TYPE = 0x37;  
    14.  public static final int ABS_MT_BLOB_ID = 0x38;  
    15.  ……  
    16.  public static final int SYN_REPORT = 0;  
    17.  public static final int SYN_CONFIG = 1;  
    18.  public static final int SYN_MT_REPORT = 2;  
    19.  ……  

        在Android中,多点触摸的实现方法在详细的代码实现中和单点是全然区分开的。在Android代码的EventHub.cpp中,单点屏和多点屏由例如以下代码段来判定:

    1. int EventHub::open_device(const char *deviceName)  
    2. {  
    3. ……  
    4. if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask)  
    5. && test_bit(ABS_MT_POSITION_X, abs_bitmask)  
    6. && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {  
    7. device->classes |= CLASS_TOUCHSCREEN | CLASS_TOUCHSCREEN_MT;  
    8. //LOGI("It is a multi-touch screen!");  
    9. }  
    10.   
    11. //single-touch?  
    12. else if (test_bit(BTN_TOUCH, key_bitmask)  
    13. && test_bit(ABS_X, abs_bitmask)  
    14. && test_bit(ABS_Y, abs_bitmask)) {  
    15. device->classes |= CLASS_TOUCHSCREEN;  
    16. //LOGI("It is a single-touch screen!")。  
    17. }  
    18. ……  
    19. }  

                    我们知道。在触摸屏驱动中,通常在probe函数中会调用input_set_abs_params给设备的input_dev结构体初始化,这些input_dev的參数会在Android的EventHub.cpp中被读取。

    如上可知。假设我们的触摸屏想被当成多点屏被处理,仅仅须要在驱动中给input_dev额外添加下面几个參数就可以:

    1.  input_set_abs_params(mcs_data.input, ABS_MT_POSITION_X, pdata->abs_x_min, pdata->abs_x_max, 0, 0);  
    2.  input_set_abs_params(mcs_data.input, ABS_MT_POSITION_Y, pdata->abs_y_min, pdata->abs_y_max, 0, 0);  
    3.  input_set_abs_params(mcs_data.input, ABS_MT_TOUCH_MAJOR, 0, 15, 0, 0);//相当于单点屏的ABX_PRESSURE  
    4.  input_set_abs_params(mcs_data.input, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0)。//相当于单点屏的ABS_TOOL_WIDTH  

                   由于多点触摸技术须要採集到多个点。然后再一起处理这些点,所以在软件实现中须要保证每一波点的准确性和完整性。因此,Linux内核提供了input_mt_sync(struct input_dev * input)函数。在每波的每一个点上报后须要紧跟一句input_mt_sync(),当这波全部点上报后再使用input_sync()进行同步。

    1. 比如一波要上报3个点:  
    2.    ……  
    3.   input_mt_sync(input);  
    4.   ……  
    5.   input_mt_sync(input);  
    6.   ……  
    7.   input_mt_sync(input)。  
    8.   input_sync(input);  
    9.  注:即使是仅上报一个点的单点事件。也须要一次input_mt_sync。

      <span style="font-family:Arial, Helvetica, sans-serif;"><span style="white-space: normal;">  

    10. </span></span>  
  • 相关阅读:
    HDOJ 1877
    POJ 2210
    HDOJ 1230(火星A+B)
    大数想减
    HDU 2115
    HDOJ 1234
    HDOJ 3784
    HDOJ3782(xxx定理)
    C# 使用 Stopwatch 测量代码运行时间
    SQL返回当前天是星期几
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/7219830.html
Copyright © 2020-2023  润新知