• 目前公司TP常用一套代码。MTK 平台使用.ko形式加载,所以跟读一下加深理解。
static struct i2c_driver tpd_i2c_driver = {.driver = {.of_match_table = of_match_ptr(gt9xx_dt_match),},.probe = tpd_i2c_probe,.remove = tpd_i2c_remove,.detect = tpd_i2c_detect,.driver.name = "gt9xx",.id_table = tpd_i2c_id,.address_list = (const unsigned short *)forces,
};static int __init tpd_driver_init(void)
{print_info("gt9xx touch panel driver init\n");if (i2c_add_driver(&tpd_i2c_driver) != 0) {print_info("unable to add i2c driver.\n");return -1;}return 0;
}static void __exit tpd_driver_exit(void)
{print_info("gt9xx touch panel driver exit\n");i2c_del_driver(&tpd_i2c_driver);
}

使用i2c_add_driver();去加载i2c驱动(由于同一套代码需要兼容很多的屏和TP,导致TP IC需要兼容不同厂商的不同IC)如果i2c地址一样并且使用i2c_add_driver()成功注册了i2c设备并且probe不报错,那么这时候兼容就会有问题(比如gt2xx厂商给的驱动是在module_init调用一个自定义函数去实现goodix_i2c_bus_init,然后注册platform_driver_register(&goodix_ts_driver))。这样就算是给了不同的屏加载驱动的时候platform_driver_register 的probe会报错,但是goodix_i2c_bus_init 调用的i2c_add_driver注册的设备probe就不会报错,那么sys/class/i2c-0/0-005d这个设备就会注册成功,导致后面的驱动i2c_add_driver()失败。)

eg:static int __init goodix_ts_core_init(void)
{int ret;ts_info("Core layer init:%s", GOODIX_DRIVER_VERSION);
#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPIret = goodix_spi_bus_init();
#elseret = goodix_i2c_bus_init();
#endifif (ret) {ts_err("failed add bus driver");return ret;}return platform_driver_register(&goodix_ts_driver);
}
----->
int goodix_i2c_bus_init(void)
{ts_info("Goodix i2c driver init");return i2c_add_driver(&goodix_i2c_driver);
}

解决办法就是先加载那个i2c probe会失败的那个,但是解决中又遇到了,reset时序的问题,如果先加载i2c probe会失败的那个后面的时序会对不上。(由于是采用.ko形式存在内核中,如果probe失败执行rmmod那么下一个驱动就可以成功这样就可以写一个shell脚本,在驱动使用DEVICE_ATTR(attr*)show 一个全局变量 shell中使用cat 去读对应节点的值,如果为0就是驱动还没加载完的等一下,如果为1 就是这个TP就是我的驱动的你不用rmmod 如果为-1就是执行rmmod 要在init.rc里面去执行这个.sh脚本)

接下来看GT928.c的驱动:


** 1.gt9xx_get_gpio_info()以及init_powerup();是获取pinctl的gpio并设置gpio状态 **
使用了devm_pinctrl_get(dev);
pinctrl_lookup_state
pinctrl_select_state
正常是要使用devm_pinctrl_put(dev)去释放gpio资源的,也可以不使用因为驱动报错之后会自动回收gpio资源。
** 2. gtp_i2c_read(0x8140寄存器,并将读取的值放在test_buf[3~12里面根据表格就是读的product_ID])

** 3 tp_init_param(test_data, 16); **
初始话tp参数,这里使用了module_param() 从insmod里面获取参数

insmod /vendor/lib/modules/gt9xx_driver.ko screen_inch=${ro.hw.lcm.inch:-0}

就是获取到TP是多少寸的。

void tp_init_param(u8 *buf, int len)
{struct fb_info *fb_info = registered_fb[0];char *p = virtual_keys;  //指针P指向定义的虚拟按键bufp += sprintf(p, "# GT9xx %s' :%02X %02X %02X %02X %02X, %02X %02X, %02X %02X, %02X.\n", screen_inch,buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12]);if (fb_info) {pr_err("GT9xx get LCM size %dx%d\n", fb_info->var.xres, fb_info->var.yres);}/** 通过之前gtp_i2c_read读取的data[0] data[1] 寄存器里的值判断该TP的信号去设置虚拟按键区域的大小 buf[3~12]* __stringify函数的作用是 C 的宏,它在编译时将表达式转换为字符串文本。调试包含变量或常量值的输出* 这样就会通过sprintf把数据打印并存储到*P中。*/if(buf[3] == 0x32 && buf[4] == 0x37 && buf[5] == 0x31 && buf[6] == 0x40 && buf[7] == 0x10 && buf[8] == 0x31 \&& buf[9] == 0x4 && buf[10] == 0x58 && buf[11] == 0x2 && buf[12] == 0x0)    //NO 1{p += sprintf(p, "# build %s at %d\n", __TIME__, __LINE__);p += sprintf(p,/** 虚拟按键填充规则:当然,在这里tpd keys这个定义key的数组和定义区域的tpd keys dim要准确的填充才可以的。具体的填充的规则如下:*   每一个虚拟按键有六个参数:*  1、0x01: A version code. Must always be Ox91.*   2、<Linux key code>: The Linux key code of the virtual key.*   3、<centerx>: The X pixel coordinate of the center of the virtual key.*    4、<centerY>: The Y pixel coordinate of the center of the virtual key.*    5、<width>: The width of the virtual key in pixels.*   6、<height>: The height of the virtual key in pixels.6*   EV_KEY:KEY_CLOSECD:1065:107:50:40*   EV_KEY:通过_set_bit去设置input数据类型为按键*   KEY_CLOSECD:键值都在内核定义好了,内核上报之后会进入对应的上层进行处理。*   centerx: 按键中心X轴坐标  : centerY 按键中心Y轴坐标  : width 按键宽 :height 按键长 (通过这四个值的数据确定一个虚拟按键在TP上的位置)*   */__stringify(EV_KEY) ":" __stringify(KEY_CLOSECD)    ":1065:107:50:40\n"__stringify(EV_KEY) ":" __stringify(KEY_HOMEPAGE)   ":1065:155:50:40\n"__stringify(EV_KEY) ":" __stringify(KEY_BACK)       ":1065:238:50:40\n"__stringify(EV_KEY) ":" __stringify(KEY_VOLUMEUP)   ":1065:320:50:40\n"__stringify(EV_KEY) ":" __stringify(KEY_VOLUMEDOWN) ":1065:380:50:40\n");}

这里会设置虚拟按键的区域以及键值

** 4、注册设备以及文件系统 **
kobject_create_and_add()
sysfs_create_group()
input_allocate_device
** 5、设置input 结构体的一些成员 **
gtp_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
这表示这个设备支持SYN KEY ABS设备 同步 键盘 绝对坐标事件

#if GTP_HAVE_TOUCH_KEYstatic const u16 touch_key_array[] = {KEY_F3, KEY_F4, KEY_F5, KEY_F6};#define GTP_MAX_KEY_NUM  (sizeof(touch_key_array)/sizeof(touch_key_array[0]))
#endif#if GTP_HAVE_TOUCH_KEYfor (index = 0; index < GTP_MAX_KEY_NUM; index++) {input_set_capability(gtp_dev, EV_KEY, touch_key_array[index]);}
#endif

== 设置输入设备的功能函数。这里有一个疑问?为什么touch_key_array[] = {KEY_F3, KEY_F4, KEY_F5, KEY_F6};写死了为const,而在为什么又在3 tp_init_param()函数中写出KEY_CLOSECD等key值???==
填充dev结构体的一些成员信息

 /* 设置绝对轴输入事件的范围参数: 最小值 最大值 模糊筛选:0 平坦过滤:0 */input_set_abs_params(gtp_dev, ABS_X, 0, screen_size[0], 0, 0);input_set_abs_params(gtp_dev, ABS_Y, 0, screen_size[1], 0, 0);input_set_abs_params(gtp_dev, ABS_PRESSURE, 0, 255, 0, 0);input_set_abs_params(gtp_dev, ABS_MT_POSITION_X, 0, screen_size[0], 0, 0);input_set_abs_params(gtp_dev, ABS_MT_POSITION_Y, 0, screen_size[1], 0, 0);input_set_abs_params(gtp_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);input_set_abs_params(gtp_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);//input_set_abs_params(gtp_dev, ABS_MT_PRESSURE, 0, 255, 0, 0);input_set_abs_params(gtp_dev, ABS_MT_TRACKING_ID, 0, GTP_MAX_TOUCH, 0, 0);sprintf(phys, "input/ts");gtp_dev->name = "TS_GT9xx";gtp_dev->phys = phys;gtp_dev->id.bustype = BUS_I2C;gtp_dev->id.vendor = 0xDEAD;gtp_dev->id.product = 0xBEEF;gtp_dev->id.version = 10427;ret = input_register_device(gtp_dev);  //注册Input设备if (ret) {pr_err("Register %s input device failed", gtp_dev->name);return -ENODEV;}fb_register_client(&pm_event_notifier);

** 创建内核线程,监听中断事件 **

 thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);  //开辟内核多线程,用于事件监听上报if (IS_ERR(thread)) {err = PTR_ERR(thread);print_info(TPD_DEVICE " failed to create kernel thread: %d", err);return -1; }tpd_irq_registration(); //注册中断

首先执行touch_event_handler函数

static int touch_event_handler(void *unused)
{struct sched_param param = { .sched_priority = 4 };sched_setscheduler(current, SCHED_RR, &param);do {set_current_state(TASK_INTERRUPTIBLE);wait_event_interruptible(waiter, tpd_flag || kthread_should_stop());set_current_state(TASK_RUNNING);if (tpd_flag)report_data_handle();tpd_flag = 0;} while (!kthread_should_stop());return 0;
}

它会阻塞在wait_event_interruptible()函数这里等待又事件上报
参数:等待队列头waiter
tpd_flag || kthread_should_stop() 当这个变为true时开始执行下一步。
一直阻塞在这里直到tpd_irq_registration中断注册函数调用。

static int tpd_irq_registration(void)
{struct device_node *node = NULL;int ret = 0;u32 ints[2] = { 0, 0 };const int32_t irqtype = override_irq_type > 0 ? override_irq_type : irq_type;node = of_find_compatible_node(NULL, NULL, "mediatek,cap_touch");if (node) {of_property_read_u32_array(node, "debounce", ints, ARRAY_SIZE(ints));print_info("debounce = %d %d\n", ints[0],ints[1]);gpio_set_debounce(ints[0], ints[1]);/*touch_irq = gpio_to_irq(tpd_int_gpio_number);*/touch_irq = irq_of_parse_and_map(node, 0);//40print_info("---cgx--#####touch_irq number %d\n", touch_irq);if (irqtype == GT_INT_TRIGGER_RISING) {/* EINTF_TRIGGER */print_info("GT_INT_TRIGGER_RISING\n");ret = request_irq(touch_irq, tpd_interrupt_handler, IRQF_TRIGGER_RISING, TPD_DEVICE, NULL);if (ret > 0)print_info("tpd request_irq IRQ LINE NOT AVAILABLE!.");} else {print_info("GT_INT_TRIGGER_FAILING\n");ret = request_irq(touch_irq, tpd_interrupt_handler, IRQF_TRIGGER_FALLING, TPD_DEVICE, NULL);if (ret > 0)print_info("tpd request_irq IRQ LINE NOT AVAILABLE!.");}} else {print_info("tpd request_irq can not find touch eint device node!.");}return ret;
}

核心使用了request_irq(handle…,rising) 上升沿触发。
看中断处理函数

static irqreturn_t tpd_interrupt_handler(int irq, void *dev_id)
{tpd_flag = 1;wake_up_interruptible(&waiter);return IRQ_HANDLED;
}
wake_up_interruptible是 Linux 内核中的一个函数,用于唤醒在给定等待队列上等待的所有进程,
并将它们标记为中断。此函数通常与 结合使用,使调用进程进入休眠状态,直到满足给定条件。
当满足条件时,内核将调用以唤醒等待该条件的任何进程。wait_event_interruptible  wake_up_interruptible

这时候线程就不阻塞了。然后走到
report_data_handle

static void report_data_handle(void)
{u8 end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0};u8  point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF};  //触摸数据寄存器0x814E  {0x81,0x4E}u8 touch_num = 0;u8   finger = 0;static u8 pre_touch = 0;static u8 pre_key = 0;static u8 need_calibrate = 0;u8    key_value = 0;u8 *coor_data = NULL;s32 id = 0;s32 i  = 0;s32 ret = -1;mdelay(5);ret = gtp_i2c_read(i2c_client, point_data, 12);  if (ret < 0) {pr_err("I2C transfer error. errno:%d\n ", ret);msleep_interruptible(800);init_powerup();msleep_interruptible(200);return;}finger = point_data[GTP_ADDR_LENGTH];if ((finger & 0x80) == 0) {goto exit_work_func;}touch_num = finger & 0x0f;// print_info("====touch_num = %d====\n", touch_num);if (touch_num > GTP_MAX_TOUCH) {goto exit_work_func;}if (touch_num > 1) {//如果是多点触摸就 读ponit 2的寄存器值 0x814E +10 =0x8158 8158~815B 存储着xy的坐标值 最多支持5点触摸,即最多有point5的寄存器存储触摸数据u8 buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff}; ret = gtp_i2c_read(i2c_client, buf, 2 + 8 * (touch_num - 1));memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));}#if GTP_HAVE_TOUCH_KEYkey_value = point_data[3 + 8 * touch_num];if (key_value || pre_key){for (i = 0; i < GTP_MAX_KEY_NUM; i++){input_report_key(gtp_dev, touch_key_array[i], key_value & (0x01<<i));    }touch_num = 0;pre_touch = 0;}
#endifpre_key = key_value;if (pre_touch || touch_num) {uint32_t pos = 0;uint32_t touch_index = 0;coor_data = &point_data[3];if (touch_num){id = coor_data[pos] & 0x0F;touch_index |= (0x01<<id);}for (i = 0; i < GTP_MAX_TOUCH; i++) {if (touch_index & (0x01<<i)){s32 input_x  = coor_data[pos + 1] | coor_data[pos + 2] << 8;s32 input_y  = coor_data[pos + 3] | coor_data[pos + 4] << 8;s32 input_w  = coor_data[pos + 5] | coor_data[pos + 6] << 8;bool report = true;const bool inscreen = (input_x < screen_size[0]) && (input_y < screen_size[1]);const bool down = !(pre_touch & (0x01 << i));  // 按下操作// 只有按下时才需要判断是否需要校正if (down) {if (inscreen)need_calibrate |= (1 << i);elseneed_calibrate &= ~(1 << i);}if (need_calibrate & (1 << i)) {calibrate(&input_x, &input_y);} else {report = !inscreen;}//if (report)tpd_down(input_x, input_y, input_w, id);pre_touch |= 0x01 << i;pos += 8;id = coor_data[pos] & 0x0F;touch_index |= (0x01<<id);}else // if (pre_touch & (0x01 << i)){tpd_up(i);pre_touch &= ~(0x01 << i);need_calibrate &= ~(1 << i);}}}input_sync(gtp_dev);exit_work_func:if (!gtp_rawdiff_mode) {ret = gtp_i2c_write(i2c_client, end_cmd, 3);if (ret < 0) {pr_err("I2C write end_cmd  error!");}}}

这里核心去读取寄存器里面的值,会判断是否支持多点触摸以及是否又触摸按键。
读的寄存器列表

这里可以看出这最多支持5点触摸。

     for (i = 0; i < GTP_MAX_TOUCH; i++) {if (touch_index & (0x01<<i)){s32 input_x  = coor_data[pos + 1] | coor_data[pos + 2] << 8;s32 input_y  = coor_data[pos + 3] | coor_data[pos + 4] << 8;s32 input_w  = coor_data[pos + 5] | coor_data[pos + 6] << 8;bool report = true;const bool inscreen = (input_x < screen_size[0]) && (input_y < screen_size[1]);const bool down = !(pre_touch & (0x01 << i));  // 按下操作

这里把取出来的值给到一个变量,后面进行report_ads(…)
自此TP中断就完了。
还有一个按键上报。

#if GTP_HAVE_TOUCH_KEYkey_value = point_data[3 + 8 * touch_num];if (key_value || pre_key){for (i = 0; i < GTP_MAX_KEY_NUM; i++){input_report_key(gtp_dev, touch_key_array[i], key_value & (0x01<<i));    }touch_num = 0;pre_touch = 0;}
#endif

为什么这里上报的键值为F3~F5 实际应该为 KEY_CLOSECD,KEY_HOMEPAGE,KEY_BACK,KEY_VOLUMEUP,KEY_VOLUMEDOWN吧?
附上android input框架

GT928 TP驱动跟读及虚拟按键上报解析相关推荐

  1. Android 虚拟按键上报

    概述  本文主要讲述触摸屏上可能用到的虚拟按键menu.home.return,底层驱动的实现和相关实现原理,其中和上层有联系的只是概述. 两种实现方式  对于触摸按键的发送可以分为两种方法: 1. ...

  2. linux驱动由浅入深系列:基于高通平台分析触摸屏(TP)、虚拟按键驱动

    触摸屏的触摸板(touch panel简称TP)驱动的基本架构和普通按键驱动基本一致,可以参考文章: linux驱动由浅入深系列:输入子系统之二(编写一个gpio_key驱动).只是其功能稍稍复杂些, ...

  3. 【TP调试】android虚拟按键无振动效果

    1.在normal mode下,tp button也是和其它触摸事件一样,以坐标形式的input_event进行上报.在初始化时会通过tpd_button_setting()函数根据定义在tpd_cu ...

  4. 4412开发板学习之Linux驱动开发(八):GPIO读操作与按键轮询实现

    GPIO读操作与按键轮询实现 GPIO读操作 硬件 查找对应IO口 寄存器配置 软件 需要的函数 注册设备 代码及分析 实验效果 按键轮询实现 原理分析 硬件 软件 用到的函数 先前准备工作 代码及分 ...

  5. input子系统与tp驱动

    1.层级结构 jiang-pc:~/build_projects2/build/60_ali/kernel-3.18/drivers/input$ ls apm-power.c ff-core.c g ...

  6. 过 DNF TP 驱动保护(一)

    文章目录: 01. 博文简介: 02. 环境及工具准备: 03. 分析 TP 所做的保护: 04. 干掉 NtOpenProcess 中的 Deep InLine Hook: 05. 干掉 NtOpe ...

  7. MTK 驱动(63)---MTK TP驱动移植

    MTK TP驱动移植 对于MTK TP驱动移植一般分为六部分: 1.硬件IO口配置: 2.TP驱动移植: 3.I2C通信: 4.中断触发: 5.数据上报: 6.虚拟按键: 硬件电路: 1.GPIO配置 ...

  8. MTK 驱动(51)---TP 驱动移植

    对于MTK TP驱动移植一般分为六部分: 1.硬件IO口配置: 2.TP驱动移植: 3.I2C通信: 4.中断触发: 5.数据上报: 6.虚拟按键: 硬件电路: 1.GPIO配置 打开 mediate ...

  9. MTK 驱动开发(29)---TP 驱动移植

    对于MTK TP驱动移植一般分为六部分: 1.硬件IO口配置: 2.TP驱动移植: 3.I2C通信: 4.中断触发: 5.数据上报: 6.虚拟按键: 硬件电路: 1.GPIO配置 打开 mediate ...

最新文章

  1. python中的装饰器有哪些-python 装饰器以及开发中常用的例子
  2. mongodb3.2 java,MongoDB学习笔记:(3)、mongodb 3.2在java环境中的简单CRUD
  3. 恍然小悟,去掉Excel的worksheet(工作表)保护的新方法
  4. ArcGIS实验教程——实验十五:拓扑关系创建与编辑
  5. java知识总结-25
  6. R语言自然语言处理:关键词提取与文本摘要(TextRank)
  7. 泥瓦匠 5 年 Java 的成长感悟(下)
  8. 计算机常见错误代码,电脑常见错误蓝屏代码汇总及解决方法
  9. Rational Rose下载安装教程
  10. Vmware15虚拟机安装win7镜像
  11. php 漏洞扫描,Webvulscan:一款基于PHP的漏洞扫描器
  12. 竞价被恶意点击怎么办?该怎么屏蔽?
  13. 吃什么食物对眼睛最好
  14. python换行输入数据_python 对比两个文件内容或字符串内容时的换行符/交作业检测小程序...
  15. Python数据处理(三)——美国西雅图自行车流量可视化
  16. STM32通用定时器实现us微秒延时
  17. 2021-2025年中国物理疗法电子病历和计费软件行业市场供需与战略研究报告
  18. linux魔兽世界黑屏,在UBUNTU下玩魔兽世界并不轻松。许多问题无法自己解决,请有兴趣的...
  19. 京东智联云:2019年云综合收入9.0亿元,波澜不惊
  20. 手把手Excel图表美化(1)--仪表图

热门文章

  1. ETL常用的三种工具介绍及对比 Datastage,Informatica 和 Kettle
  2. 【璟丰机电】台湾APEX行星减速机ABR系列特性及参数
  3. 计算机桌面显示左右有黑边,电脑屏幕两侧有黑边框如何恢复全屏_电脑左右有黑边框怎么弄-win7之家...
  4. 这可能是最强的AI算法可视化神器!
  5. AIGC:导航网站精选
  6. c#五子棋实验报告_(C#)五子棋
  7. 3DMax广告海报立体字效果制作
  8. php 做公众号获取用户openid
  9. Laravel 5.1 使用短信验证码插件laravel-sms
  10. 计算机绘画作品 星空,太空的科学幻想绘画作品欣赏