电容屏FT5x06驱动与设计

news/2024/12/31 6:33:31/

电容屏FT5x06驱动与设计

说明书

一、概述

当下,触摸式输入方法深受大众喜爱。电容式触摸屏因为其具备准确,不需用力按压,高灵敏度,通透性好等优点而成为了时代的宠儿。

结构组成上,电容式触控屏可以简单地看成是由四层复合屏构成的屏体:最外层是玻璃保护层,接着是导电层,第三层是不导电的玻璃屏,最内的第四层也是导电层。最内导电层是屏蔽层,起到屏蔽内部电气信号的作用,中间的导电层是整个触控屏的关键部分,四个角或四条边上有直接的引线,负责触控点位置的检测。

硬件原理上,电容式触摸屏在触摸屏四边均镀上狭长的电极,在导电体内形成一个低电压交流电场。在触摸屏幕时,由于人体电场,手指与导体层间会形成一个耦合电容,四边电极发出的电流会流向触点,而电流强弱与手指到电极的距离成正比,位于触摸屏幕后的控制器便会计算电流的比例及强弱,准确算出触摸点的位置。电容触摸屏的双玻璃不但能保护导体及感应器,更有效地防止外在环境因素对触摸屏造成影响,就算屏幕沾有污秽、尘埃或油渍,电容式触摸屏依然能准确算出触摸位置。

软件设计上,电容式触摸屏自带IC进行数据处理。采用主从通信方式与主机进行数据交换。本文侧重从数据交换和主机数据处理来深入了解触摸屏的Linux驱动设计。


二、硬件原理

1. 引脚说明

  • I2C/SPI: an interface for data exchange with host
  • INT: an interrupt signal to inform the host processor that touchdata is ready for read
  • WAKE: an interrupt signal for the host to change F5x06 fromHibernate to Active mode
  • /RST: an external low signal reset the chip.

目前国内G870D项目中采用了SPI接口,INT输入和/RST输出信号。

2.引脚连接

外部信号跟HOST连接情况:


TP外部信号

核心板引脚

软件描述名

TP-INT

POWER_FAIL_int

MX25_PIN_POWER_FAIL

TP-RST

拓展IO13

拓展IO13

I2C1-SDA

I2C1-SDA

I2C1-SDA

I2C1-SCL

I2C1-SCL

I2C1-SCL

因为触摸屏功耗较小,硬件上没有考虑低功耗问题,所以WAKE引脚不使用。

三、软件设计

电容式触摸屏Linux多点触摸驱动总的来说只有两种,Type A和Type B。以下为linux kernel文档的介绍:

The protocol isdivided into two types, depending on the capabilities of the hardware. Fordevices handling anonymous contacts (type A), the protocol describes how tosend the raw data for all contacts to the receiver. For devices capable oftracking identifiable contacts (type B), the protocol describes how to sendupdates for individual contacts via event slots.

For type Adevices, the kernel driver should generate an arbitrary enumeration of the fullset of anonymous contacts currently on the surface. The order in which thepackets appear in the event stream is not important.  Event filtering and finger tracking is leftto user space [3].

For type Bdevices, the kernel driver should associate a slot with each identifiedcontact, and use that slot to propagate changes for the contact.Creation,replacement and destruction of contacts is achieved by modifying theABS_MT_TRACKING_ID of the associated slot. A non-negative tracking id is interpreted as a contact, and the value -1denotes an unused slot. A tracking id not previously present is considered new,and a tracking id no longer present is considered removed.  Since only changes are propagated,the fullstate of each initiated contact has to reside in the receiving end.Uponreceiving an MT event, one simply updates the appropriate attribute of thecurrent slot.

-- Multi-touch (MT) Protocol

由于G870D采用的Linux kernel的input子系统支持Type A类型的多点触摸,因而本文涉及到多点触摸地方也只分析Type A。事实上,公司的触摸屏中间件函数只支持单点,而且多点触摸的上报方式跟单点的上报方式不兼容。所以驱动只预留多点触摸功能,使用单点触摸功能。单点触摸的上报方式非常简单。只需要上报ABS_X,ABS_Y,BTN_TOUCH和ABS_PRESSURE,最后调用input_sync同步即可。


1.FTx06

本处只列举本驱动使用到的数据,更多信息请参考《FTS_AN_CTPM_Standard_eng.pdf》。

I2C写数据格式:

       I2C读数据设置地址格式:

       I2C读数据格式:

RegisterMap:


2.驱动模块处理流程

在等待列队里面,接收触摸屏数据和上报应用层

 

激活等待列队

 

四、驱动架构分析

1.初始化阶段

G870D的触摸屏使用I2C进行数据通信,而主板的I2C信息一般都在一个i2c_board_info类型结构体填充。i2c_board_info结构体的原形为:

struct i2c_board_info {char        type[I2C_NAME_SIZE];unsigned short  flags;unsigned short  addr;void        *platform_data;struct dev_archdata*archdata;int     irq;};

G870D里面,相应的结构体为mxc_i2c_board_info。以下为触摸屏的填充信息。

{.type  ="ft5206IIC_ts",.addr  = 0x38,.irq   = IOMUX_TO_IRQ(MX25_PIN_POWER_FAIL),},

上述填充信息指示了,i2c的client名字,从地址和使用的中断资源。从i2c的协议,可知内核是预知从设备的地址的。其实,从地址是i2c从设备程序自身定的地址,可能跟产商协定。完成i2c设备信息填充之后,便可以通过i2c_add_driver建立触摸屏设备驱动。触摸屏使用的i2c_driver结构体为:

static struct i2c_drivermigor_ts_driver = {.driver = {.name = "ft5206IIC_ts",                                                                                                              }, .probe = migor_ts_probe,.remove = migor_ts_remove,.suspend = migor_ts_suspend,.resume = migor_ts_resume,.id_table = migor_ts_id,};

在i2c_add_driver(&migor_ts_driver)的时候,成功找到client之后就会回调migor_ts_probe函数。migor_ts_probe函数会利用找到的client信息完成触摸屏的后期初始化工作。简单介绍一下

static intmigor_ts_probe(struct i2c_client *client, const struct i2c_device_id *idp){…input =input_allocate_device();if (!input) {dev_err(&client->dev, "Failed to allocate inputdevice.\n");error = -ENOMEM;goto err1;}#ifdef CONFIG_FT5X0X_MULTITOUCH                    //支持多点触摸input->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY);input->evbit[BIT_WORD(EV_ABS)]|= BIT_MASK(EV_ABS);input->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);input->absbit[BIT_WORD(ABS_MT_POSITION_X)]|=BIT_MASK(ABS_MT_POSITION_X);input->absbit[BIT_WORD(ABS_MT_POSITION_Y)]|=BIT_MASK(ABS_MT_POSITION_Y);input->absbit[BIT_WORD(ABS_MT_TOUCH_MAJOR)]|=BIT_MASK(ABS_MT_TOUCH_MAJOR);input->absbit[BIT_WORD(ABS_MT_WIDTH_MAJOR)]|=BIT_MASK(ABS_MT_WIDTH_MAJOR);input->absbit[BIT_WORD(ABS_PRESSURE)] |=BIT_MASK(ABS_PRESSURE);input_set_abs_params(input,ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);input_set_abs_params(input,ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);input_set_abs_params(input,ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);input_set_abs_params(input,ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);#else                                                                             //只用单点触屏input->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY);     // 设置支持事件input->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);input->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);input->absbit[BIT_WORD(ABS_X)] |=BIT_MASK(ABS_X);input->absbit[BIT_WORD(ABS_Y)] |=BIT_MASK(ABS_Y);                                                                                       input->absbit[BIT_WORD(ABS_PRESSURE)] |=BIT_MASK(ABS_PRESSURE);input_set_abs_params(input, ABS_X, 0, SCREEN_MAX_X, 0, 0);   // 设置分辨率input_set_abs_params(input, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);input_set_abs_params(input, ABS_PRESSURE, 0, PRESS_MAX, 0 , 0);#endifinput->name =client->name;input->id.bustype =BUS_I2C;input->dev.parent =&client->dev;input->open =migor_ts_open;input->close =migor_ts_close;input_set_drvdata(input,priv);priv->client = client;priv->input = input;INIT_DELAYED_WORK(&priv->work, migor_ts_poscheck);         // 初始化等待列队priv->irq =client->irq;error =input_register_device(input);                                              //注册输入设备if (error)goto err1;error=request_irq(priv->irq, migor_ts_isr, IRQF_TRIGGER_FALLING, client->name,priv);if (error) {                                                                                   //申请中断dev_err(&client->dev, "Unable to request touchscreenIRQ.\n");goto err2;}#ifdef TS_WAKEUPdevice_init_wakeup(&client->dev, 1);                       // 设置是否支持唤醒低功耗#endif…}

从上述程序,可见后期的初始化工作主要做了设置input设备支持的事件和注册input设备;初始化等待列队、申请中断和设置触摸屏是否能唤醒低功耗。到此,完成了初始化的工作。

2.数据处理

在初始化的工作中,驱动程序已经申请了中断,初始化了等待列队。从驱动模块处理流程图可知,驱动进入等待数据的环节。当用户触摸屏幕的时候,触摸屏检测到有效数据便会通过TP-INT发出中断信号。HOST端接收到中断信号后,便进入中断服务函数激活等待工作列队。这里是考虑到HOST的处理速度比触摸屏IC快,通过等待工作列队可以保证触摸屏IC有足够时间整理数据。但应该注意到等待工作列队的等待时间会影响HOST反映速度,此值应折中处理。在等待列队里,HOST进行数据处理,然后上报应用层。简单介绍一下工作列队,工作列队归根到底是用内核线程实现的。当内核比较忙碌的时候,有可能导致线程响应慢。分析一下,中断处理程序:

static irqreturn_t migor_ts_isr(intirq, void *dev_id){struct migor_ts_priv *priv = dev_id;disable_irq_nosync(irq);                                           //关闭中断// 设置响应速度schedule_delayed_work(&priv->work, HZ / 100);             // 唤醒等待工作队列return IRQ_HANDLED;}

非常简单,关闭中断(防止在工作列队数据处理的过程中再出现TP-INT中断,必须等待处理完成再使能TP-INT中断),唤醒等待工作队列。继续分析工作队列的程序:

static void migor_ts_poscheck(structwork_struct *work){…buf[0] = 0;                                                      //设置register map偏移if (i2c_master_send(priv->client, buf, 1) != 1) {dev_err(&priv->client->dev,"Unable to write i2c index\n");goto out;}//读取数据if (i2c_master_recv(priv->client, buf, sizeof(buf)) != sizeof(buf)) {dev_err(&priv->client->dev,"Unable to read i2c page\n");goto out;}#ifdef CONFIG_FT5X0X_MULTITOUCHfor(count = 0; count < (buf[2] & 0x0f); count++){xpos = ((buf[3 + count * 6] & 0x03)<< 8 | buf[4 + count * 6]);ypos = ((buf[5 + count * 6] & 0x03)<< 8 | buf[6 + count * 6]);event_flag = buf[3 + count * 6]>> 6;#ifdef TS_DEBUGtouch_id = buf[5 + count * 6] >>4;#endifts_debug("(%x,%x,%d,%d)\n",touch_id,event_flag,xpos,ypos);if (event_flag == EVENT_PENDOWN ||event_flag == EVENT_REPEAT) {input_report_abs(priv->input,ABS_MT_POSITION_X, xpos);input_report_abs(priv->input,ABS_MT_POSITION_Y, ypos);input_report_key(priv->input,BTN_TOUCH, 1);input_report_abs(priv->input,ABS_PRESSURE, 1);input_mt_sync(priv->input);} else if (event_flag == EVENT_PENUP) {input_report_abs(priv->input,ABS_MT_POSITION_X, xpos);input_report_abs(priv->input,ABS_MT_POSITION_Y, ypos);input_report_key(priv->input,BTN_TOUCH, 0);input_report_abs(priv->input,ABS_PRESSURE, 0);input_mt_sync(priv->input);}}input_sync(priv->input);#else//根据ft5x06的数据格式提取数据xpos = ((buf[3 + 0 * 6] & 0x03) << 8 | buf[4 + 0 * 6]);    ypos = ((buf[5 + 0 * 6] & 0x03) << 8 | buf[6 + 0 * 6]);event_flag = buf[3 + 0 * 6] >> 6;#ifdef TS_DEBUGtouch_id = buf[5 + 0 * 6] >> 4;#endifts_debug("(%x,%x,%d,%d)\n",touch_id,event_flag,xpos,ypos);if (event_flag == EVENT_PENDOWN || event_flag == EVENT_REPEAT) {input_report_abs(priv->input, ABS_X,xpos);                   // 上报ABS_Xinput_report_abs(priv->input, ABS_Y,ypos);         // 上报ABS_Yinput_report_key(priv->input,BTN_TOUCH, 1);              // 上报按下事件input_report_abs(priv->input,ABS_PRESSURE, 1);input_sync(priv->input);} else if (event_flag == EVENT_PENUP) {input_report_abs(priv->input, ABS_X,xpos);input_report_abs(priv->input, ABS_Y,ypos);input_report_key(priv->input,BTN_TOUCH, 0);input_report_abs(priv->input,ABS_PRESSURE, 0);input_sync(priv->input);}#endifout:enable_irq(priv->irq);                                                            //使能TP-INT中断}
数据处理的过程也比较简单,首先按照ft5x06的数据格式(前面已经提及)提取数据,然后按照input子系统的格式上报到用户分区即可。还有,前面在中断服务程序里面已经关闭了中断,这里必须使能中断,以便接收下一次的数据。到此数据处理完成。

3.低功耗

前面已经说到,触摸屏的低功耗并没做考虑。这里只关注触摸屏能不能唤醒低功耗。触摸屏比较容易导致误唤醒,实际上大多数手持设备禁止触摸屏激活设备。这里只预留唤醒功能,默认不开。程序如下:

static int migor_ts_suspend(structi2c_client *client, pm_message_t mesg)                                                                   {      struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);if (device_may_wakeup(&client->dev))              // 使能触摸屏中断唤醒enable_irq_wake(priv->irq);return 0;}


4.驱动测试方法以及案例

触摸屏驱动测试这要考虑准确率,响应速度和边缘检测。本驱动只考虑单点触摸的情况,测试程序可以使用tslib。

Tslib的链接:

https://codeload.github.com/kergoth/tslib/zip/master

编译方法:

Cd tslib-master/

./configure--host=arm-none-linux-gnueabi --prefix=(此处添加生成的路径)

Make

安装方法:

将tslib生成的所有文件复制到G870D的/mtd0/tslib/目录,并在/etc/profile文件里面添加如下环境变量重启即可。

export TSLIB_ROOT=/mtd0/tslib

exportTSLIB_PLUGINDIR=${TSLIB_ROOT}/lib/ts

exportTSLIB_CALIBFILE=${TSLIB_ROOT}/pointercal

exportTSLIB_CONFFILE=${TSLIB_ROOT}/etc/ts.conf

exportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:${TSLIB_ROOT}/lib

export TSLIB_FBDEVICE=/dev/fb0

export TSLIB_CONSOLEDEVICE=none

exportTSLIB_TSDEVICE=/dev/input/event1

export PATH=$PATH:/mtd0/tslib/bin

测试方法:

1.校准。理论上,电容屏不用校准。但是,考虑到安装的误差,更重要的是tslib需要利用pointercal文件进行点转换,所以还是要进行一次校准。

./ts_calibrate

然后,在目录上提示的地方输入即可。

2.测试

./ts_test

可以进行拖动,划线测试。

3.测试案例

a.快速拖动,查看跟随速度测试响应速度。

b.各种速度进行划线操作,查看是否漏点处理测试驱动的可靠性。

c.长期待机后继续操作,查看是否正常测试稳定性。

d.辐射,静电和湿润等恶劣条件进行操作和老化操作,查看是否正常工作测试抗干扰能力。

e.边缘点测试

五、总结

因限制于linux内核版本,芯片处理速度和接口兼容的局限,本驱动在设计过程只在驱动上预留了多点触摸的上报。但手势识别一般来说,由硬件支持或者应用层计算完成识别。Ft5x06这块芯片数据格式上写着存在手势支持,但事实上返回的数据为空。所以需要应用层处理。目前来说,小型的pos机无需求。如果开发ATM,商场大屏幕等设备,则添加功能可以提供更好体验。


六、input子系统

1.概述

输入设备总类繁杂,包括按键,键盘,触摸屏,鼠标,摇杆等等,它们本身都是字符设备,不过内核为了能将这些设备的共性抽象出来,简化驱动的开发,建立了一个Input子系统。Input子系统分为三层,从下至上分别是输入设备驱动层,输入核心层以及输入事件驱动层。下图可以简单说明,input子系统的结构。

核心层已经在linux kernel较好的实现了。因为输入设备存在共性,所以事件驱动理论上可以驱动共享。当无法共享的时候,才会单独实现。一般来说,驱动开发者仅仅只需要关心输入设备驱动层。

2.Input子系统初始化流程分析

子系统初始化有专用的宏接口subsys_initcall。Input子系统初始化入口为:

subsys_initcall(input_init);

以上只是在kernel初始化流程中插入了input_init接口,也就是说input_init是承担了初始化input子系统的主要工作。具体做了那些工作,可以接着分析input_init函数。

static int __init input_init(void){…err= class_register(&input_class);        //注册input_class…err= input_proc_init();                       // 在/proc文件系统里面建立相应的树结构…err= register_chrdev(INPUT_MAJOR, "input", &input_fops);  // INPUT_MAJOR为13//建立input子系统主设备号的字符设备。…}

在注释已经指出,input子系统的初始化工作主要有三。注册input_class是出于方便linux kernel归类管理的目的(例如低功耗的处理会利用class链表挂起设备)。在/proc文件系统里面建立相应的树结构是希望将驱动的运行信息通过在用户空间的文件系统反映出来。相比前两点,最关键的是第三点,建立input子系统主设备号的字符设备。这意味所有主设备号为13的节点(包含本文的触摸屏)都通过input_fops结构体的接口来操作。这里详细分析一下input_fops结构体。input_fops结构体的定义:

static const struct file_operationsinput_fops = {.owner= THIS_MODULE,.open= input_open_file,};

显然,没有预料的复杂。莫要遗憾,继续分析仅有的open接口input_open_file。

static int input_open_file(structinode *inode, struct file *file){structinput_handler *handler;conststruct file_operations *old_fops, *new_fops = NULL;interr;…handler= input_table[iminor(inode) >> 5];                       //寻找对应的事件驱动//定义staticstruct input_handler *input_table[8];if(!handler || !(new_fops = fops_get(handler->fops))) {            // 重新链接接口组err= -ENODEV;gotoout;}…err = new_fops->open(inode, file);                                  // 利用新的接口重新打开节点…}

必须提及是,事件驱动处理器可以共享,内核预设事件驱动处理器只有8个(从input_table的定义可以证实)。内核利用节点的次设备号来对应,每64个共享一个事件驱动处理器。继续分析上面的源码,容易理解,input_fops只是完成kernel的抽象到现实的意义。输入设备是一个抽象出来的类,现实的设备(例如触摸屏)才是实际的。到这里,应该可以形成一个共识,设备层驱动对应一个事件驱动处理器;一个事件驱动处理器最多可以对应64个设备层驱动。至于里面的联系,后续再分析。

3.input driver编写要点

分配、注册、注销input设备接口

struct input_dev*input_allocate_device(void)int input_register_device(structinput_dev *dev)void input_unregister_device(structinput_dev *dev)

数据上报、同步接口

static inline voidinput_report_key(struct input_dev *dev, unsigned int code, int value)static inline voidinput_report_rel(struct input_dev *dev, unsigned int code, int value)static inline void input_report_abs(structinput_dev *dev, unsigned int code, int value)static inline void input_sync(structinput_dev *dev)static inline voidinput_mt_sync(struct input_dev *dev)


4.从触摸屏驱动切入的input子系统

曾记否上文中,触摸屏驱动的初始化工作就是初始化input结构体,然后将其注册。触摸屏input结构体初始化的接口有,input->open =migor_ts_open和input->close= migor_ts_close。重点分析input_register_device(input)的调用。

int input_register_device(structinput_dev *dev){                                                                                             …error = device_add(&dev->dev);                        //添加设备…list_add_tail(&dev->node,&input_dev_list);              // 添加input到input_dev_list链表…list_for_each_entry(handler,&input_handler_list, node)input_attach_handler(dev,handler);//匹配事件驱动处理器…}

注册过程重点关注的地方还是只有三个,注释已经指出。实际上linux kernel只有字符设备和块设备。Input设备是一个抽象的设备,注册input设备归根到底是注册一个字符设备。这里必须提及的是input子系统有两个个全局变量,1.输入设备链input_dev_list;2.事件驱动链input_handler_list;input_dev_list负责记录所有的注册的input设备。

input_handler_list负责记录所有的事件驱动处理器。注册Input_dev的时候会通过input_handler_list链表寻找合适的input_handler。反之,注册input_handler的时候会通过input_dev_list寻找input_dev。本文属于前面一种情况。这里分析一下查找的过程。list_for_each_entry(dev,&input_dev_list, node)input_attach_handler(dev,handler);宏list_for_each_entry的作用是依次出列所有input_dev_list元素到dev,然后回调input_attch_handler函数。static intinput_attach_handler(struct input_dev *dev, struct input_handler *handler){…id= input_match_device(handler->id_table, dev);       // 检查input_handler是否匹配input_devif(!id)return-ENODEV;                                     //不匹配则直接返回…error = handler->connect(handler, dev, id);         // 匹配则调用handler的connect接口…}
这个函数简单的从注释就可以明白。匹配的时候,通过对比handler->id_table和dev相关的信息来完成的。匹配了就会调用connect将两个对象链接起来。首先,关注一下input_match_device。

static const struct input_device_id*input_match_device(const struct input_device_id *id,structinput_dev *dev){int i;for (; 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;//handler是否兼容devMATCH_BIT(evbit,  EV_MAX);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);return id;}returnNULL;}


       不得不说的是,MATCH_BIT这个宏。请看:

#define MATCH_BIT(bit, max)                                \for(i = 0; i < BITS_TO_LONGS(max); i++)     \if((id->bit[i] & dev->bit[i]) != id->bit[i]) \break;                                               \if(i != BITS_TO_LONGS(max))                             \continue;

也就是说,if ((id->bit[i] & dev->bit[i]) != id->bit[i])可以看出如果id->bit[i]为0,代表可以肯定兼容;反之,只有id->bit[i]和dev->bit[i]同时为1的时候才兼容。假设匹配成功过,就会如前面所说调用handler->connect。那本文的触摸屏究竟使用那个handler呢?一般来说,没有专门定义的最后都会使用evdev_handler(前面提过可以查看次设备号验证)。显然,本文没有问触摸屏建立专门的事件驱动器,使用evdev_handler事件驱动器。所以handler->connect最终指向的是evdev_connect。分析一下evdev_connect。

static int evdev_connect(structinput_handler *handler, struct input_dev *dev,const struct input_device_id *id){…evdev = kzalloc(sizeof(struct evdev),GFP_KERNEL);              // 分配evdev空间if (!evdev)return -ENOMEM;INIT_LIST_HEAD(&evdev->client_list);                         // 初始化evdevspin_lock_init(&evdev->client_lock);mutex_init(&evdev->mutex);init_waitqueue_head(&evdev->wait);dev_set_name(&evdev->dev,"event%d", minor);evdev->exist = 1;evdev->minor = minor;    evdev->handle.dev =input_get_device(dev);                            //初始化联系体handleevdev->handle.name =dev_name(&evdev->dev);evdev->handle.handler = handler;evdev->handle.private = evdev;evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE+ 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);                //注册handleif (error)goto err_free_evdev;error = evdev_install_chrdev(evdev);                               // 记录evdev到evdev_table表if (error)goto err_unregister_handle;error = device_add(&evdev->dev);                                          // 注册event设备。if (error)goto err_cleanup_evdev;return 0;…}

下面看一下,注册handle的过程。

int input_register_handle(structinput_handle *handle){…list_add_tail_rcu(&handle->d_node,&dev->h_list);…list_add_tail(&handle->h_node,&handler->h_list);…}

比较简单通过链表来记录它联系的handler和dev。到此为止,注册的工作完成。以上述为基础,其他过程,只是简单列举一下。

       a.用户空间的open(“eventX”)      // X代表序号。

  • evdev_handler->fops-> open
  • evdev_open
  • evdev_open_device
  • input_open_device
  • input->open
  • migor_ts_open                //运行开发者自定义open函数

b.驱动数据上报的过程                                                            

  •  input_report_abs
  •  input_event                   // 调用input core层接口
  • input_handle_event
  • input_pass_event
  • handle->handler->event  // 利用handle需找事件驱动器处理
  • evdev_pass_event
  • client->buffer[client->head++] = *event;            // 填写缓冲区
client->head &=EVDEV_BUFFER_SIZE - 1;
  • wake_up_interruptible    // 激活阻塞读操作

c.同步过程与数据上报过程雷同,不做分析。

5.总结

由上文的分析可见,input子系统框架设计思路基本源于驱动设备模型。关键是理解三个数据结构的关系和关系的建立流程。三个数据结构分析是input_handler,input_dev和Input_handle。简单来说,input_handler和input_dev配对存在,input_handle是前两者沟通的渠梁。其建立流程是通过两个全局链表,彼此寻找,匹配成功后通过connect建立渠梁。一般来说,input_handler采用共享的驱动器,驱动开发者重点关心input_dev的建立和数据处理即可。


八、其他

1.eventX对应设备

proc方式

与event对应的相关设备信息位于/proc/bus/input/devices。例如:

 % adb shell cat /proc/bus/input/devices
I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="ACCDET"
P: Phys=
S: Sysfs=/devices/virtual/input/input0
U: Uniq=
H: Handlers=gpufreq_ib event0 
B: PROP=0
B: EV=3
B: KEY=40 0 0 0 0 0 0 1000000000 c000000000000 0I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="fpc_irq"
P: Phys=
S: Sysfs=/devices/fpc_interrupt/input/input1
U: Uniq=
H: Handlers=gpufreq_ib event1 
B: PROP=0
B: EV=400013
B: KEY=8000 10000000000000 0
B: MSC=10I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="gn_hall_key"
P: Phys=
S: Sysfs=/devices/virtual/input/input2
U: Uniq=
H: Handlers=gpufreq_ib event2 
B: PROP=0
B: EV=3
B: KEY=c00 0 0 0I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="gn_home_key"
P: Phys=
S: Sysfs=/devices/virtual/input/input3
U: Uniq=
H: Handlers=gpufreq_ib event3 
B: PROP=0
B: EV=3
B: KEY=100000000000 0 0I: Bus=0019 Vendor=2454 Product=6500 Version=0010
N: Name="mtk-kpd"
P: Phys=
S: Sysfs=/devices/soc/10010000.keypad/input/input4
U: Uniq=
H: Handlers=gpufreq_ib event4 
B: PROP=0
B: EV=3
B: KEY=1c000000000000 0I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="hwmdata"
P: Phys=
S: Sysfs=/devices/virtual/input/input5
U: Uniq=
H: Handlers=gpufreq_ib event5 
B: PROP=0
B: EV=5
B: REL=6I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="m_alsps_input"
P: Phys=
S: Sysfs=/devices/virtual/input/input6
U: Uniq=
H: Handlers=gpufreq_ib event6 
B: PROP=0
B: EV=d
B: REL=6
B: ABS=101I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="m_acc_input"
P: Phys=
S: Sysfs=/devices/virtual/input/input7
U: Uniq=
H: Handlers=gpufreq_ib event7 
B: PROP=0
B: EV=d
B: REL=c1
B: ABS=107I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="m_gyro_input"
P: Phys=
S: Sysfs=/devices/virtual/input/input8
U: Uniq=
H: Handlers=gpufreq_ib event8 
B: PROP=0
B: EV=d
B: REL=c1
B: ABS=107I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="m_mag_input"
P: Phys=
S: Sysfs=/devices/virtual/input/input9
U: Uniq=
H: Handlers=gpufreq_ib event9 
B: PROP=0
B: EV=d
B: REL=3c9
B: ABS=17fI: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="m_step_c_input"
P: Phys=
S: Sysfs=/devices/virtual/input/input10
U: Uniq=
H: Handlers=gpufreq_ib event10 
B: PROP=0
B: EV=d
B: REL=7
B: ABS=100I: Bus=0000 Vendor=0000 Product=1151 Version=0008
N: Name="mtk-tpd"
P: Phys=
S: Sysfs=/devices/virtual/input/input11
U: Uniq=
H: Handlers=gpufreq_ib event11 
B: PROP=2
B: EV=b
B: KEY=10 0 0 0 400 0 4000 800000040000000 0 0
B: ABS=263000001000003I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="mtk-tpd-kpd"
P: Phys=
S: Sysfs=/devices/virtual/input/input12
U: Uniq=
H: Handlers=gpufreq_ib event12 
B: PROP=0
B: EV=3
B: KEY=10 0 0 0 0 0 0 40000000 0 0

getevent方式

 android系统可以通过getevent方式确认。其他系统需要移植改工具。

 % adb shell getevent -i                                                                                                                                                                        16-08-25 12:01
add device 1: /dev/input/event12bus:      0019vendor    0001product   0001version   0100name:     "mtk-tpd-kpd"location: ""id:       ""version:  1.0.1events:KEY (0001): 009e  0244 input props:<none>
add device 2: /dev/input/event11bus:      0000vendor    0000product   1151version   0008name:     "mtk-tpd"location: ""id:       ""version:  1.0.1events:KEY (0001): 009e  00bb  00ce  014a  0244 ABS (0003): 0000  : value 0, min 0, max 1080, fuzz 0, flat 0, resolution 10800001  : value 0, min 0, max 1920, fuzz 0, flat 0, resolution 19200018  : value 0, min 0, max 255, fuzz 0, flat 0, resolution 00030  : value 0, min 0, max 100, fuzz 0, flat 0, resolution 00031  : value 0, min 0, max 100, fuzz 0, flat 0, resolution 00035  : value 0, min 0, max 1080, fuzz 0, flat 0, resolution 00036  : value 0, min 0, max 1920, fuzz 0, flat 0, resolution 00039  : value 0, min 0, max 10, fuzz 0, flat 0, resolution 0input props:INPUT_PROP_DIRECT
add device 3: /dev/input/event10bus:      0000vendor    0000product   0000version   0000name:     "m_step_c_input"location: ""id:       ""version:  1.0.1events:REL (0002): 0000  0001  0002 ABS (0003): 0008  : value 0, min 0, max 64, fuzz 0, flat 0, resolution 0input props:<none>
add device 4: /dev/input/event9bus:      0000vendor    0000product   0000version   0000name:     "m_mag_input"location: ""id:       ""version:  1.0.1events:REL (0002): 0000  0003  0006  0007  0008  0009 ABS (0003): 0000  : value 0, min -32768, max 32767, fuzz 0, flat 0, resolution 00001  : value 0, min -32768, max 32767, fuzz 0, flat 0, resolution 00002  : value 0, min -32768, max 32767, fuzz 0, flat 0, resolution 00003  : value 0, min -32768, max 32767, fuzz 0, flat 0, resolution 00004  : value 0, min -32768, max 32767, fuzz 0, flat 0, resolution 00005  : value 0, min -32768, max 32767, fuzz 0, flat 0, resolution 00006  : value 0, min 0, max 64, fuzz 0, flat 0, resolution 00008  : value 0, min 0, max 64, fuzz 0, flat 0, resolution 0input props:<none>
add device 5: /dev/input/event8bus:      0000vendor    0000product   0000version   0000name:     "m_gyro_input"location: ""id:       ""version:  1.0.1events:REL (0002): 0000  0006  0007 ABS (0003): 0000  : value 0, min -32768, max 32767, fuzz 0, flat 0, resolution 00001  : value 0, min -32768, max 32767, fuzz 0, flat 0, resolution 00002  : value 0, min -32768, max 32767, fuzz 0, flat 0, resolution 00008  : value 0, min 0, max 64, fuzz 0, flat 0, resolution 0input props:<none>
add device 6: /dev/input/event7bus:      0000vendor    0000product   0000version   0000name:     "m_acc_input"location: ""id:       ""version:  1.0.1events:REL (0002): 0000  0006  0007 ABS (0003): 0000  : value -336, min -32768, max 32767, fuzz 0, flat 0, resolution 00001  : value -61, min -32768, max 32767, fuzz 0, flat 0, resolution 00002  : value 9673, min -32768, max 32767, fuzz 0, flat 0, resolution 00008  : value 3, min 0, max 64, fuzz 0, flat 0, resolution 0input props:<none>
add device 7: /dev/input/event6bus:      0000vendor    0000product   0000version   0000name:     "m_alsps_input"location: ""id:       ""version:  1.0.1events:REL (0002): 0001  0002 ABS (0003): 0000  : value 0, min -32768, max 32767, fuzz 0, flat 0, resolution 00008  : value 0, min 0, max 64, fuzz 0, flat 0, resolution 0input props:<none>
add device 8: /dev/input/event5bus:      0000vendor    0000product   0000version   0000name:     "hwmdata"location: ""id:       ""version:  1.0.1events:REL (0002): 0001  0002 input props:<none>
add device 9: /dev/input/event3bus:      0000vendor    0000product   0000version   0000name:     "gn_home_key"location: ""id:       ""version:  1.0.1events:KEY (0001): 00ac input props:<none>
add device 10: /dev/input/event2bus:      0000vendor    0000product   0000version   0000name:     "gn_hall_key"location: ""id:       ""version:  1.0.1events:KEY (0001): 00ca  00cb input props:<none>
add device 11: /dev/input/event0bus:      0019vendor    0000product   0000version   0000name:     "ACCDET"location: ""id:       ""version:  1.0.1events:KEY (0001): 0072  0073  00a4  0246 input props:<none>
add device 12: /dev/input/event1bus:      0000vendor    0000product   0000version   0000name:     "fpc_irq"location: ""id:       ""version:  1.0.1events:KEY (0001): 0074  008f MSC (0004): 0004 input props:<none>
add device 13: /dev/input/event4bus:      0019vendor    2454product   6500version   0010name:     "mtk-kpd"location: ""id:       ""version:  1.0.1events:KEY (0001): 0072  0073  0074 input props:<none>



http://www.ppmy.cn/news/789800.html

相关文章

嘉立创EDA下载及在线操作(团队版本)

1.连接网络 2.软件下载 &#xff08;1.&#xff09;打开浏览器 &#xff08;2.&#xff09;搜索“嘉立创EDA”&#xff0c;找到下图 &#xff08;3.&#xff09;点击进去&#xff0c;下滑底端&#xff0c;找到“下载字样”&#xff0c;并点击 &#xff08;4.&#xff09;进去…

全志A64触摸屏驱动调试

一、前言 linux的触摸屏驱动一般要经历一下几个步骤&#xff08;以4412为例&#xff09;&#xff1a;1.移植驱动到linux源码“driver/input/touchscreen/”目录下&#xff0c;在Kconfig里面增加这款触摸屏驱动的菜单。 #add by long 20170207 config TOUCHSCREEN_GT9XXtristate…

7-3打怪升级(25分)【Floyd、dijkstra】【2021 RoboCom 世界机器人开发者大赛-本科组(初赛)】

考点&#xff1a;Floyd&#xff0c;dijkstra变式&#xff08;记录路径&#xff0c;多优先级&#xff09; 7-3 打怪升级 (25分) 很多游戏都有打怪升级的环节&#xff0c;玩家需要打败一系列怪兽去赢取成就和徽章。这里我们考虑一种简单的打怪升级游戏&#xff0c;游戏规则是&am…

LeetCode-0704

139. 单词拆分(中等) class Solution {public boolean wordBreak(String s, List<String> wordDict) {boolean dp[] new boolean[s.length()1];dp[0] true;for(int i1;i<s.length();i){for(String word:wordDict){if(i-word.length()>0){String cur s.substrin…

可以用美图秀秀裁一寸照片

可以用美图秀秀裁一寸照片 摘自&#xff1a; https://jingyan.baidu.com/article/91f5db1b0e9c491c7f05e312.html

如何把照片裁剪成证件照指定尺寸比例?

我们平时使用的证件照都是有尺寸要求的&#xff0c;现在很多时候已经不需要使用纸质版证件照了&#xff0c;只需要制作出电子版证件照来就可以进行证件照提交报名&#xff0c;但是平时我们手机拍摄的照片的尺寸都不是标准的证件照尺寸比例&#xff0c;那么应该如何进行证件制作…

使用win10的画图将照片变为2寸大小

材料/工具 一台电脑 方法 1 找到你要调整的图片&#xff0c;单击鼠标右键&#xff0c;选择【编辑】。 风云怎样压缩照片大小_2020全新版免费下载图片压缩器查看详情>>广告 2 打开后界面如图&#xff0c;点击【重新调整大小】。 3 弹出如图界面&#xff0c;点击【像素…

如何制作一寸、二寸、六寸照片。以后不用再去照相馆了!

如何制作一寸、二寸、六寸照片希望以后大家再也不用为急着用的一寸照烦恼了,自己制作然后直接拿到相店打印就行了! 首先要知道的数量关系: 6寸10.2cm15.2cm 1寸2.5cm3.5cm 2寸3.5cm5.3cm 1.打开你欲制作照片 2.在工具栏,点裁剪工具(调宽度:2.5厘米,高度:3.5厘米,分辨率:300像素…