一、触摸问题
生产线 在烧录固件时,会偶然出现稍完之后屏幕触摸用不了。前期以为是烧录没弄好,后面又发生,就怀疑与产品有关了。
首先进行抓日志分析:有问题的设备先确认下dmesg信息
adb连接设备进行日志抓取:
logcat > /sdcard/logcat.log
dmesg > /sdcard/dmesg.log
确认问题日志信息如下:
[ 6.224059] ___touch_compatible_probe() start____
[ 6.225273] \x0avfs_read return ret=12
[ 6.225292] touch_compatible:Not first Start the system,Already read touch\xef\xbc\x9aUSB_touch
[ 6.225297] LOUHN in touch_module_init:USB_touch
[ 6.225514] pwm-backlight backlight1: backlight1 supply power not found, using dummy regulator
[ 6.225780] pwm-backlight backlight1: Linked as a consumer to regulator.0
[ 6.225837] pwm-backlight backlight1: Dropping the link to regulator.0
[ 6.249039] usb 1-1: new high-speed USB device number 3 using ehci-platform
[ 6.400229] usb 1-1: New USB device found, idVendor=a69c, idProduct=8801, bcdDevice= 1.00
[ 6.400245] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
根据找出有问题的日志片段,我们可以进行详细的分析:
- touch_compatible_probe() start:说明这时刚刚开始触摸匹配。
- touch_compatible:Not first Start the system,Already read touch\xef\xbc\x9aUSB_touch:该消息表明这不是系统的第一次启动,之前已经读取了触摸输入设备,且是通过 USB 接口连接的。
- LOUHN in touch_module_init:USB_touch:指明正在初始化的触摸模块是通过 USB 接口连接的。
但是我们的触摸是I2C触摸,为什么会识别成USB触摸呢?所以我们要到的驱动里去看一下这个touch_compatible_probe()函数的实现。
触摸驱动路径:
Android12.0/kernel-4.19/drivers/input/touchscreen/touch_adapter_driver/touch_adapter.c
touch_compatible_probe实现:
static int touch_compatible_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0,i = 0;
struct device_node *np = client->dev.of_node;
printk("___%s() start____ \n", __func__);
ret = read_touch_type(TOUCH_TYPE_FILE,touch_type,31);
if(ret) {
printk("touch_compatible:read %s faild ret=%d!",TOUCH_TYPE_FILE,ret);
return -ENOMEM;
} else {
if( strlen(touch_type) ) {//touch_type不为空,则说明不是第一次启动
printk("touch_compatible:Not first Start the system,Already read touch:%s\n",touch_type);
touch_module_init(touch_type);
return 0;
}
}
comp_irq_gpio = of_get_named_gpio(np, "compatible,riq-gpio",0);
comp_reset_gpio = of_get_named_gpio(np, "compatible,reset-gpio", 0);
//gtp_request_io_port
ret = gpio_request(comp_reset_gpio, "touch_gpio_reset");
ret += gpio_request(comp_irq_gpio, "touch_gpio_irq");
if(!ret) {
for (i = 0; i < (sizeof(ts_list) / sizeof(touchscreen)); i++) {
if (ts_list[i].check_i2c_func) {
if((ts_list[i].check_i2c_func)(client, ts_list[i].i2c_addr)) {
printk("Found Touch I2C IC: %s\n",ts_list[i].touch_name);
if (save_data_to_file(TOUCH_TYPE_FILE,ts_list[i].touch_name,strlen(ts_list[i].touch_name)) ) {
printk("touch_compatible:save %s faild!",TOUCH_TYPE_FILE);
return -ENOMEM;
}
touch_module_init(ts_list[i].touch_name);
break;
}
} else {
printk("Touch[%s] Handle function is NULL\n", ts_list[i].touch_name);
}
if (i == ((sizeof(ts_list) / sizeof(touchscreen) -1)) ) {
save_data_to_file(TOUCH_TYPE_FILE,USB_TOUCH,strlen(ts_list[i].touch_name));//没有匹配到I2C触摸,默认为USB触摸
return 0;
}
}
}
return 0;
}
由上述信息可知:
根据触摸屏驱动的probe 函数, 首先通过读取一个文件来获取触摸屏类型,如果读取成功并且触摸屏类型不为空,则说明不是第一次启动系统,直接根据触摸屏类型进行初始化,并返回成功。如果触摸屏类型为空,说明是第一次启动系统,并使用 gpio_request() 函数获取设备树中指定的 GPIO 引脚名称获取对应的 GPIO 号。如果请求成功,循环遍历 ts_list 数组中的元素,逐个检查是否存在 I2C 触摸设备。如果存在 I2C 触摸设备,打印信息并将触摸设备的名称写入 TOUCH_TYPE_FILE 文件中,然后调用touch_module_init进行触摸模块的初始化,最后跳出循环。如果循环结束后仍未找到匹配的 I2C 触摸设备,将默认将触摸设备类型写入 TOUCH_TYPE_FILE 文件中。
该函数的主要功能是通过 I2C 获取与触摸设备有关的信息,并根据配置信息选择不同的触摸模块进行初始化。如果没有找到匹配的触摸设备,将默认选择 USB 触摸设备进行初始化。
很明显,出现问题的设备就是识别为了USB触摸,但我们使用的是I2C触摸,所以才会导致触摸无法使用的情况。我们先找到TOUCH_TYPE_FILE这个文件,进行删除,定位一下原因。
文件位置查找:
Android12.0/kernel-4.19/drivers/input/touchscreen/touch_adapter_driver/touch_compatible.h
可知TOUCH_TYPE_FILE的文件路径在 /data/hardware_status/touch_type
对 touch_type 进行删除后重新创建,触摸能正常使用。
初步考虑是烧录固件的时候没有接入I2C触摸,烧录完成后才进行组装。所以第一次并未识别到I2C触摸屏,如果没有找到任何匹配的I2C触摸屏设备,那么会调用save_data_to_file函数将触摸屏类型设置为USB触摸。后面和工厂的生产线落实确实是这种情况。
二、解决方案
1、设计思路
2、kernel代码修改
kernel-4.19/drivers/input/touchscreen/touch_adapter_driver/touch_adapter.c
diff --git a/drivers/input/touchscreen/touch_adapter_driver/touch_adapter.c
b/drivers/input/touchscreen/touch_adapter_driver/touch_adapter.c
index b26f09c63318..6510bb1ebbb6 100755
--- a/drivers/input/touchscreen/touch_adapter_driver/touch_adapter.c
+++ b/drivers/input/touchscreen/touch_adapter_driver/touch_adapter.c
@@ -60,7 +60,7 @@ static int read_touch_type(const char * file_path,char*buff, int size)
mm_segment_t old_fs;
loff_t pos;
int ret=0;
- file = filp_open(file_path,O_CREAT|O_RDWR,0664);
+ file = filp_open(file_path,O_CREAT|O_RDWR, 0666);
// 修改创建的"/data/hardware_status/touch_type" 权限⽤于上层修改
if(IS_ERR(file))
{
printk("\nfile_read: filp_open fail!\n");
@@ -84,7 +84,7 @@ static int save_data_to_file(const char* save_file_name,
char *buff, int size)
int ret=0;
printk("save_file_name = %s!\n",save_file_name);
- fp = filp_open(save_file_name, O_RDWR | O_CREAT, 0644);
+ fp = filp_open(save_file_name, O_RDWR | O_CREAT, 0666); // 修改权限
if (IS_ERR(fp))
{
printk("\nsave_data_to_file create file error\n");
@@ -435,7 +435,8 @@ static int touch_compatible_probe(struct i2c_client
*client, const struct i2c_de
printk("Touch[%s] Handle function is NULL\n",
ts_list[i].touch_name);
}
if (i == ((sizeof(ts_list) / sizeof(touchscreen) -1)) ) {
-save_data_to_file(TOUCH_TYPE_FILE,USB_TOUCH,strlen(ts_list[i].touch_name));
//没有匹配到I2C触摸,默认为USB触摸
+ // 若识别不到触摸兼容内的I2C设备,则等待下⼀次设备重启检测,并打印⽇志
+ printk("Not found I2C touch present.");
+ save_data_to_file(TOUCH_TYPE_FILE,USB_TOUCH,strlen(ts_list[i].touch_name));
//没有匹配到I2C触摸,默认为USB触摸
return 0;
}
}
3、framework代码修改
frameworks/native/services/inputflinger/reader/EventHub.cpp
diff --git a/services/inputflinger/reader/EventHub.cpp
b/services/inputflinger/reader/EventHub.cpp
index 3067d87293..1f7011d5a0 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -61,6 +61,9 @@
using android::base::StringPrintf;
using namespace android::flag_operators;
+#define USB_TOUCH "USB_touch"
+#define TOUCH_TYPE_FILE "/data/hardware_status/touch_type"
+
namespace android {
static const char* DEVICE_PATH = "/dev/input";
@@ -1846,6 +1849,35 @@ void
EventHub::reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier&
identifier.bus, obfuscatedId.c_str(),
classes.get());
}
+//保存数据⾄⽂件
+static void save_data_to_file(const std::string& filename, const std::string&
data)
+{
+ FILE* file = std::fopen(filename.c_str(), "r");
+ if (file != nullptr) {
+ std::fseek(file, 0, SEEK_END); // 移动⽂件指针到⽂件末尾
+ long size = std::ftell(file); // 获取当前⽂件指针的偏移量,即⽂件⼤⼩
+
+ if (size > 0) {
+ ALOGW("File already exists and has data. Data will not be written.");
+ std::fclose(file);
+ return ;
+ }
+
+ std::fclose(file);
+ }
+ // 将USB_touch写⼊⽂件
+ file = std::fopen(filename.c_str(), "w");
+ if (file != nullptr) {
+ std::fwrite(data.c_str(), sizeof(char), data.size(), file);
+ std::fclose(file);
+ ALOGW("Data saved to TOUCH_TYPE_FILE successfully.");
+ } else {
+ ALOGW("Failed to open the TOUCH_TYPE_FILE.");
+ }
+
+ return ;
+}
+
void EventHub::openDeviceLocked(const std::string& devicePath) {
@@ -2138,9 +2170,16 @@ void EventHub::openDeviceLocked(const std::string&
devicePath) {
device->classes |= InputDeviceClass::MIC;
}
+ char value[120];
// 获取特定产品的内部USB触摸节点信息
+ property_get("ro.touchinput.internal", value, "");
+
// Determine whether the device is external or internal.
// 若⽆USB触摸则不会进⼊下⽅if判断内,即进⼊if判断后 device->identifier.location.c_str() ⼀定有值
if (device->isExternalDeviceLocked()) {
- device->classes |= InputDeviceClass::EXTERNAL;
+ if (strcmp(device->identifier.location.c_str(), value)){
// 若与产品的属性不等,则作为副屏,设置外部触摸属性
+ device->classes |= InputDeviceClass::EXTERNAL;
+ } else if ( strcmp(device->identifier.location.c_str(), value) == 0) {
// 若与产品的属性相等,则写⼊⽂件信息
+ save_data_to_file(TOUCH_TYPE_FILE, USB_TOUCH);
+ }
}