GT928 TP驱动跟读及虚拟按键上报解析

news/2024/11/30 0:51:37/
  • 目前公司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框架
在这里插入图片描述


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

相关文章

leetcode-328 奇偶链表

题目如下&#xff1a; 给定单链表的头节点 head &#xff0c;将所有索引为奇数的节点和索引为偶数的节点分别组合在一起&#xff0c;然后返回重新排序的列表。 第一个节点的索引被认为是 奇数 &#xff0c; 第二个节点的索引为 偶数 &#xff0c;以此类推。 请注意&#xff0c;…

聚观早报|特斯拉向第三方电动车开放充电桩;Epic 诉苹果垄断败诉

今日要闻&#xff1a;特斯拉向第三方电动车开放充电桩&#xff1b;我国全面实现不动产统一登记&#xff1b;Epic 诉苹果垄断败诉&#xff1b;腾讯大股东Naspers再减持近79万股&#xff1b;星巴克中国门店将超过万家 特斯拉向第三方电动车开放充电桩 近日&#xff0c;特斯拉官方…

OpenJudge - 39:多项式输出

目录 一、题目描述 二、代码实现 一、题目描述 一元 n 次多项式可用如下的表达式表示&#xff1a; ​ 其中&#xff0c;​ 称为 i 次项&#xff0c;​ 称为 i 次项的系数。给出一个一元多项式各项的次数和系数&#xff0c;请按照如下规定的格式要求输出该多项式&#xff…

计算机视觉毕业后找不到工作怎么办?怒刷leetcode,还是另寻他路?

文章目录 一、计算机视觉毕业后找不到工作怎么办&#xff1f;二、大环境&#xff1a;前两年的泡沫太大三、还是要把自己的基本功搞扎实&#xff0c;真正的人才什么时候都紧缺四、转换思路&#xff0c;另投他坑五、要有毅力&#xff0c;心态放平六、最后的建议 一、计算机视觉毕…

合创视觉平面设计中基础色彩原则

1、灰度 灰度是一种黑白模式的色彩模式。但与黑白二色的位图不同&#xff0c;从0--255有256种不同等级的明度变化。灰度是所有创造的根源&#xff0c;也是色彩感觉不佳设计师的救星。   2、RGB RGB是一种以色光为基础的色彩模式,它是由红(Red)、绿(Green)、蓝(BIue)三种…

选择什么电容笔比较好?平价好用的iPad电容笔推荐

科学技术的迅速发展使人们的生活发生了巨大的变化。如今&#xff0c;众多的电子和数码产品层出不穷&#xff0c;而这种能够与平板电脑配套的电容笔也是如此。随着电容笔的不断发展&#xff0c;其应用范围也将不断扩大&#xff0c;今天&#xff0c;我将向大家推荐一些具有较高性…

人群计数:技术难点、商业产品化成功案例、现状、传统做法、硬件设备、

现状&#xff1a; 比较成熟了&#xff0c;准确率已经很高了&#xff08;有多高&#xff0c;后面我搞懂metrics高到什么程度&#xff0c;再汇报过来&#xff09; 商业公司基本把这个领域做的很透彻了&#xff0c;performance基本到了一一个无法提高的位置了&#xff08;和图像…

实时数仓--数据实时接入模块相关视频录制完成

数据实时接入部分的视频已于昨晚录制完成&#xff0c;由于视频中涉及实现思路和实现代码都来自生产项目&#xff0c;且经过作者多次熬夜录制完成&#xff0c;所以这套视频需付费观看&#xff0c;介意的朋友请见谅。 具体说明如下&#xff1a; 对《实时数仓架构那些事儿》系列文…