
简介这是GSL1680/GSL1688电容屏控制器在Android系统下的驱动源码包适合嵌入式驱动开发、触摸屏适配以及Linux内核学习人群也是了解Android触摸链路的上佳切片。压缩包大小仅22KB包含2个文件分别为一个C源文件和一个头文件C源文件集中体现驱动初始化、中断处理、数据读取与上报逻辑头文件则定义了寄存器映射及关键数据结构便于对照分析。目前已有175人学习下载短小精悍适合快速通读并作为调试时的参考。通过研读这套驱动可理解Linux设备驱动模型下触摸芯片与主机通信的方式以及驱动如何将多点触控原始信号转换为标准输入事件并传递至Android Input系统同时能察觉GSL1680与GSL1688在触控参数、上报格式等方面的差异。对于需要移植、优化同系列电容屏驱动的开发者这套源码可作为调试Log分析、异常排查与参数调优的基础素材帮助缩短BSP适配周期并提升触摸交互稳定性。1. 老安卓触摸屏驱动的“黑匣子”先搞清楚 GSL1680-Driver.rar 是什么搞 Android 底层开发的一定见过这类名字GSL1680-Driver.rar、gsl1680.c、123GSL1680.h。说好听点是驱动源码包说直白点就是矽创SileadGSL1680/GSL1688 电容触摸屏控制器在 Linux/Android 内核里的实现。它解决的问题非常具体手机主板上那颗触摸 IC 把电容变化转成坐标数据驱动负责通过 I2C 把数据读出来再上报给 input 子系统最终屏幕上才有反应。没有这份驱动屏点亮了也是一块“死玻璃”。这份资源适合三类人正在给老方案全志、瑞芯微、MTK 的 Android 4.x/5.x 板子移植触摸屏的工程师、想读懂 Linux input 子系统上报流程的初学者、以及手头有 GSL1680/GSL1688 板子需要把固件下载流程理顺的开发。本文不聊空洞概念直接拆文件、讲编译、讲固件下载再讲三个最容易翻车的坑。2. 驱动源码的结构gsl1680.c、123GSL1680.h 和 gslx680_code 的分工2.1 文件解析哪个是主体哪个是配置哪个是参考拿到 GSL1680-Driver.rar 之后先不要急着往内核里塞。解压后常见的文件是gsl1680.c、gsl1680.h偶尔会带123GSL1680.h这样的带数字前缀头文件以及一些gslx680_code相关的补丁参考文件。它们的定位完全不同文件典型名字一般是什么建议处理方式gsl1680.c驱动主体包含 probe、中断处理、坐标解析、I2C 读写放进kernel/drivers/input/touchscreen/下编译gsl1680.h驱动用到的寄存器地址、ID 定义、部分宏开关与 .c 成对放置123GSL1680.h带项目号的屏参配置文件本质是一张寄存器初始化表如果板子屏幕分辨率、翻转方向一致可直接使用否则需要重新生成gslx680_code相关文件另一颗同系列芯片 GSLX680 的参考代码常被误当成 GSL1680 使用建议只做寄存器参考不要直接替换编译这里有个容易踩的误区很多人习惯把gslx680_code里的代码直接拿去编 GSL1680原因是两者寄存器风格接近。但 1680 和 680 的中断处理、坐标计算、输入上报格式有差异混用的结果通常是能识别到 I2C 设备但触摸上报不连续或者只能单点。正确做法是先确认芯片丝印和驱动源码里chip_id的对应关系再决定用哪套代码。2.2 从 probe 函数看驱动是怎么“活”起来的gsl1680.c里最值得先读的是gsl1680_probe。它做的事按顺序大致是从设备树拿 I2C 信息 → 申请中断 → 初始化寄存器把 123GSL1680.h 的配置表写进芯片→ 注册 input 设备 → 启动工作队列或中断线程。我把核心流程简化成下面这样static int gsl1680_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct gsl_ts *ts; int ret; // 1. 分配驱动私有数据结构 ts kzalloc(sizeof(*ts), GFP_KERNEL); if (!ts) return -ENOMEM; ts-client client; i2c_set_clientdata(client, ts); // 2. 读取芯片 ID确认当前是 GSL1680 还是 GSL1688 ret gsl_ts_read_chip_id(client); if (ret 0) { dev_err(client-dev, read chip id failed\n); return ret; } // 3. 把屏参配置表写进芯片这一步会把坐标分辨率等参数下发 gsl_ts_init_chip(client); // 4. 注册 input 设备 ts-input_dev input_allocate_device(); if (!ts-input_dev) return -ENOMEM; // 5. 设置上报类型多点触摸绝对坐标、按键事件等 __set_bit(EV_ABS, ts-input_dev-evbit); input_set_abs_params(ts-input_dev, ABS_MT_POSITION_X, 0, ts-max_x, 0, 0); input_set_abs_params(ts-input_dev, ABS_MT_POSITION_Y, 0, ts-max_y, 0, 0); ret input_register_device(ts-input_dev); if (ret) return ret; // 6. 申请触摸中断通常是 GPIO 边沿触发 ret request_threaded_irq(ts-irq, NULL, gsl_ts_irq_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT, gsl1680, ts); if (ret) return ret; return 0; }这段代码的注意点有三个。第一gsl_ts_read_chip_id失败时不要强行继续很多“驱动编进去了但触摸没反应”的问题就出在 I2C 上拉电阻不对芯片 ID 读不到probe 直接返回错误。第二input_set_abs_params里的max_x/max_y来自 123GSL1680.h 或者设备树必须和屏幕分辨率一致否则触摸点会偏移。第三中断标志IRQF_TRIGGER_LOW | IRQF_ONESHOT是要和硬件上触摸脚的电平状态匹配的改成IRQF_TRIGGER_FALLING在部分硬件上会导致中断风暴。2.3 坐标上报一条完整的事件怎么走到 Android 上层驱动里最重要的一段代码是中断处理函数里对坐标数据的解析。GSL1680 上报坐标一般走input_mt_slot和input_report_abs这两个接口。简化后的逻辑是这样的static irqreturn_t gsl_ts_irq_handler(int irq, void *dev_id) { struct gsl_ts *ts dev_id; u8 buf[64]; int ret; // 1. 通过 I2C 读取触控数据包 ret i2c_read_bytes(ts-client, GSL_REG_TOUCH_DATA, buf, sizeof(buf)); if (ret 0) return IRQ_HANDLED; // 2. 解析数据包取触点数量、每个触点的 x/y 坐标 ts-touch_count buf[0] 0x0F; for (int i 0; i ts-touch_count; i) { int x (buf[2 i * 4] 8) | buf[3 i * 4]; int y (buf[4 i * 4] 8) | buf[5 i * 4]; // 3. 按屏幕方向做坐标变换 x ts-flip_x ? ts-max_x - x : x; y ts-flip_y ? ts-max_y - y : y; // 4. 上报给 input 子系统 input_mt_slot(ts-input_dev, i); input_mt_report_slot_state(ts-input_dev, MT_TOOL_FINGER, 1); input_report_abs(ts-input_dev, ABS_MT_POSITION_X, x); input_report_abs(ts-input_dev, ABS_MT_POSITION_Y, y); } // 5. 事件同步这一步漏掉的话上层永远收不到 input_mt_sync_frame(ts-input_dev); input_sync(ts-input_dev); return IRQ_HANDLED; }这里特别说明一下input_mt_sync_frame和input_sync的区别。input_mt_sync_frame是告诉内核这一帧多点触控数据结束了input_sync才是真正把整个事件包提交给事件队列。两者都必须要调用而且顺序不能反。我调试时见过有人把input_sync写在input_mt_sync_frame前面结果是触摸点乱跳因为内核还没完成 slot 的同步就把包发上去了。另外i2c_read_bytes这个函数在真实驱动里往往是个i2c_transfer的封装。GSL1680 的数据包格式在不同的固件版本里不完全一样有的版本首字节是0x01表示有数据有的版本触点数量在 bit4-7。调试时第一件事是打印原始 buf而不是直接相信解析代码。3. 把驱动编进 Android 内核从设备树到 make 配置3.1 在 dts 里添加 I2C 节点和中断脚GSL1680 是标准的 I2C 触摸控制器挂载在某个 I2C 总线上中断脚一般是一个 GPIO。在 Android 内核的设备树里添加节点的常见写法如下i2c2 { status okay; gsl168040 { compatible silead,gsl1680; reg 0x40; interrupt-parent gpio1; interrupts 13 IRQ_TYPE_EDGE_FALLING; touchscreen-max-x 720; touchscreen-max-y 1280; touchscreen-size-x 720; touchscreen-size-y 1280; reset-gpio gpio1 12 GPIO_ACTIVE_LOW; vdd-supply vcc_io; }; };这里的reg 0x40是芯片的 I2C 从机地址不同批次可能不一样常见的有 0x40 和 0x41如果 I2C 探测不到设备先用i2cdetect扫描一下总线上真实的地址再回来改。interrupts 13 IRQ_TYPE_EDGE_FALLING表示触摸芯片的中断脚连接到了 GPIO1 的第 13 脚下降沿触发。如果硬件上是高电平有效就要改成IRQ_TYPE_EDGE_RISING如果芯片是低电平保持型推荐用IRQ_TYPE_LEVEL_LOW。reset-gpio不是必须的但建议加上。GSL1680 在上电后有时需要硬件复位时序驱动里如果没做软复位设备会一直处于异常状态。touchscreen-max-x和touchscreen-size-x的区别容易被忽略max 告诉驱动坐标能达到的最大值size 告诉 input 子系统的分辨率范围。老驱动里通常只用 max新内核两者都要对上。3.2 配置内核编译选项直接编入还是模块加载Android 内核的触摸屏驱动一般直接编进内核不推荐做成模块。原因很简单触摸屏驱动的 probe 依赖 I2C 总线的注册顺序做成模块后加载时序不可控容易出现“模块加载时 I2C 还没就绪”的问题。在Kconfig里对应的条目一般是config TOUCHSCREEN_GSL1680 tristate Silead GSL1680 touchscreen driver depends on I2C help Say Y here if you have a GSL1680/GSL1688 touchscreen controller connected to your system.然后在Makefile中把源文件挂上obj-$(CONFIG_TOUCHSCREEN_GSL1680) gsl1680.o之后在defconfig或menuconfig里打开这个配置项make ARCHarm menuconfig在 Device Drivers → Input device support → Touchscreens 下选中 Silead GSL1680然后重新编译内核。如果用的是全志或者 RK 的方案编译脚本通常是./build.sh或者直接make -j8。编译完之后确认gsl1680.o被链入镜像而不是漏编了find out/ -name gsl1680*如果只找到.o文件但没编进kernel.img或boot.img多半是 Kconfig 依赖有问题。常见的原因是depends on I2C写成了depends on I2Cy导致模块模式下配置项灰掉另一个原因是gsl1680.o的编译命令里缺少-DCONFIG_TOUCHSCREEN_GSL1680。统统检查一遍再烧录避免在真机上反复试错。4. 固件下载与 GSL1688 适配gslx680_code 到 GSL1688 的差异点4.1 固件下发时机什么时候需要给芯片下载固件GSL1680 和 GSL1688 有个不太一样的特性一部分批次的 GSL1688 芯片内部没有固化完整固件上电后需要主控通过 I2C 把固件 bin 写进去芯片才能正常上报触摸数据。这个过程在驱动里通常叫fw_update或者gsl_load_fw。常见的做法是驱动 probe 的时候先读芯片 ID然后比较当前固件版本和驱动里内置的版本号如果不一致就执行下载。核心代码如下static int gsl_load_fw(struct i2c_client *client) { const struct firmware *fw; int ret; unsigned char *buf; // 1. 从内核查找名为 gsl1688.fw 的固件文件 ret request_firmware(fw, gsl1688.fw, client-dev); if (ret) { dev_err(client-dev, request firmware failed\n); return ret; } // 2. 计算校验和GSL 系列固件头部通常带一个校验字 if (!check_fw_crc(fw-data, fw-size)) goto release_fw; // 3. 分块写入芯片每块 4KB 左右 buf (unsigned char *)fw-data; for (int i 0; i fw-size / 4096; i) { i2c_write_bytes(client, GSL_REG_FW_ADDR i * 4096, buf i * 4096, 4096); } // 4. 软复位芯片让固件生效 i2c_write_byte(client, GSL_REG_SOFT_RESET, 0x01); msleep(20); release_fw: release_firmware(fw); return 0; }这个流程里的request_firmware依赖内核的 CONFIG_FW_LOADER 选项而且固件文件要放到根文件系统的/etc/firmware或内核源码的firmware/目录下。如果你的 Android 系统把根文件系统锁了驱动在用户空间加载不到固件probe 就会失败。这种情况下可以在内核配置里把固件编译进内核镜像CONFIG_EXTRA_FIRMWAREgsl1688.fw CONFIG_EXTRA_FIRMWARE_DIRfirmware这属于保底手段能解决一部分“固件放不进 /etc/firmware”的板子。需要注意GSL1680 通常不需要下载固件它出厂就是可用的所以如果你拿到的是 1680 的驱动不要照搬 1688 的request_firmware流程否则每次开机都会白等一次超时。4.2 GSL1680 与 GSL1688 的差异对照很多开发者把这两个型号当一回事实际上驱动虽然同源但适配时有几个维度要注意对比维度GSL1680GSL1688适配时的影响最大触点数5 点10 点上报循环次数和input_mt_slot的 slot 数不同固件下载一般不需要部分批次必须下载决定是否启用CONFIG_FW_LOADER寄存器映射较简单多一组工作寄存器gslx680_code里的地址不能直接套用上报协议大多走 Type A/B 混合通常走 Type B是否调用input_mt_report_slot_state功耗模式普通增加低功耗模式重点关注 suspend/resume 回调我一般会先用 I2C 工具把芯片的 ID 寄存器读出来确认究竟是哪一颗再决定用哪套代码。如果拿到的资源包是gslx680_code而板子上实际是 GSL1688不要直接编译先对比寄存器定义把0x80、0x90这类地址逐一确认后再改。否则轻则触摸上报异常重则芯片不响应。4.3 固件资源应该放在哪里Android 的/system/etc/firmware与内核 firmware 目录在 Android 系统里触摸屏固件的存放位置和内核的request_firmware查找路径有关联。标准的查找路径是/etc/firmware在 Android 平台上往往映射到/system/etc/firmware。如果你的板子 root 分区是只读的又不想重新打包 system.img可以用mount -o remount,rw /system把固件放进去但这只适合开发阶段不是量产做法。更稳妥的方式是把固件编译进内核镜像做法就是在defconfig里写CONFIG_FW_LOADERy CONFIG_EXTRA_FIRMWAREgsl1688.fw CONFIG_EXTRA_FIRMWARE_DIRkernel/drivers/input/touchscreen/firmware然后重新编译内核。这里的CONFIG_EXTRA_FIRMWARE_DIR是相对内核源码根目录的路径固件文件必须真实存在于这个目录否则编译直接报错。如果你打算同时支持 1680 和 1688 两个项目可以写多个固件名用空格隔开CONFIG_EXTRA_FIRMWAREgsl1680.fw gsl1688.fw这样驱动里分别request_firmware(gsl1688.fw)就能找到。生产的时候同一份内核也能兼顾两种屏切换屏只需要改设备树里的 compatible 字符串。5. 移植触摸驱动避坑指南坐标错乱、中断不触发、多点失效5.1 坐标方向反了横竖屏翻转变量的设置现象是触摸能上报但在屏幕上点的位置和实际触摸位置呈镜像或者颠倒了。比如点屏幕左上角光标在右下角。原因几乎都是123GSL1680.h里的翻转宏没改对或者驱动代码里的flip_x/flip_y参数没有和屏的贴合方向匹配。解决办法是先确认屏幕是横屏还是竖屏、TP 的 FPC 出线方向在哪里。驱动里常见的宏是#define GSL_X_FLIP (0) #define GSL_Y_FLIP (1)GSL_X_FLIP置 1 表示把 X 坐标做镜像GSL_Y_FLIP同理。如果你的屏是竖屏但系统显示是横屏需要在驱动里同时处理坐标系旋转也就是把max_x和max_y互换后再做翻转。这一步建议在驱动里打日志直接打印原始坐标和最终坐标对比一个已知位置来确认。我一般会在驱动里写一个调试节点比如/sys/devices/platform/gsl1680/debug_coord读出来的坐标直观判断比反复改参数重新编译高效得多。5.2 中断不触发GPIO 的复用配置和触发方式不匹配现象是驱动本身 probe 成功了I2C 设备也能读到但手指按上去完全没有事件上报。看dmesg会发现中断没有被调用。这类问题十有八九是设备树里的interrupts描述和硬件实际连接不一致。先检查 GPIO 的 pinctrl 配置确认这个引脚没有被复用为其他功能比如被配成了 UART 或者 PWM。内核里可以通过 sysfs 查看引脚复用状态。另一个常见原因是触发方式不匹配。GSL1688 的中断脚有的板子是低电平保持有的板子是下降沿脉冲。驱动里如果写的是IRQF_TRIGGER_LOW但硬件实际是下降沿那么一旦 GPIO 被拉低中断会一直触发所以看起来是 stuck反过来如果写的是IRQF_TRIGGER_FALLING但硬件是低电平保持那么只有在按下瞬间触发一次后续触点移动不会产生新中断现象就是“第一下有反应后面全卡住”。解决办法是确认硬件原理图上 TP_INT 脚连接到 SoC 的方式用示波器量一下按下时的电平波形再决定触发标志。最后还有一个小坑有些 SoC 的 GPIO 需要设置上下拉设备树里没写bias-pull-up或者bias-pull-down中断脚在空闲时会浮空导致误触发。这个问题在量产板子上经常被忽略我建议在 dts 的 pinctrl 里显式加上pinctrl_gsl_int { gsl1680_int: gsl1680-int { pins GPIO1_13; function gpio; bias-pull-up; }; };5.3 多点触控失效input 子系统和触摸屏协议不匹配现象是单点触摸正常第二根手指放上去没反应或者触点乱跳。原因通常是驱动上报的逻辑和 input 子系统期望的 Type B 协议不一致。GSL1688 支持多点但必须正确使用input_mt_slot、input_mt_report_slot_state和input_mt_sync_frame。很多老驱动用的是 Type A 的input_report_abs加input_sync这种方式只能上报单个触点或者同时上报多个但系统不知道如何关联。正确的 Type B 写法要包含 slot 的概念。另外还需要确认input_set_capability里有没有设置ABS_MT_SLOT和ABS_MT_TRACKING_ID。缺少后者会导致内核无法区分同一根手指的连续移动。还有一个细节如果你的触摸芯片最大支持 5 点而驱动里循环上报 10 个点多余的 slot 会被当成无效触点Android 上层偶尔会出现“幽灵触摸”。解决办法是在解析数据包时严格限制触点数量超出部分直接丢弃if (ts-touch_count ts-max_touch_points) ts-touch_count ts-max_touch_points;max_touch_points在 probe 时从设备树读取或者默认 5。这样不会出现越界读取 I2C buf 的风险同时也保证上报事件始终合法。加上内核的CONFIG_INPUT_MT编译选项这个选项如果没开多点触控相关 API 虽然能链接但运行时不会有任何事件产生。查这个问题的标准命令是adb shell cat /proc/bus/input/devices如果看到触摸屏设备有B: EV...但ABS位图里没有ABS_MT_*说明input_set_capability配置有遗漏直接补上再测试。5.4 固件下载失败request_firmware老是超时的排错现象是 dmesg 报错gsl_load_fw: request firmware failed或者等待超时。原因分三种一是固件文件路径不对request_firmware默认去/etc/firmware找Android 系统里实际是/system/etc/firmware二是固件文件本身 CRC 校验不过三是 I2C 写入速度太快芯片来不及处理。第三种比较隐蔽I2C 时钟从 400kHz 降到 100kHz 往往能解决。排查建议用内核的 dynamic debug 把gsl_ts_read_chip_id和gsl_load_fw的日志全部打开echo file gsl1680.c p /sys/kernel/debug/dynamic_debug/control如果日志停在read chip id之后、进入固件下载之前那多半是固件版本号不匹配驱动认为不需要更新。此时可以把固件版本校验的代码临时注释掉强制走一遍下载流程确认芯片响应正常后再恢复版本判断逻辑。6. 触摸屏驱动的验收清单用 getevent 看原始坐标再判断要不要动驱动驱动编进去之后不要急着拿手指闷头点。先做两件事看内核日志确认 probe 成功然后用getevent看原始事件流。连接 adb 后执行adb shell getevent -lt /dev/input/event2注意把event2换成触摸屏的实际设备号可以通过/proc/bus/input/devices查触摸设备的名字。正常手指按下时会看到类似这样的输出EV_ABS ABS_MT_SLOT 00000000 EV_ABS ABS_MT_TRACKING_ID 00000001 EV_ABS ABS_MT_POSITION_X 000002c3 EV_ABS ABS_MT_POSITION_Y 00000510 EV_KEY BTN_TOUCH 00000001 EV_SYN SYN_REPORT 00000000POSITION_X和POSITION_Y是原始坐标范围由驱动里的input_set_abs_params决定。如果你设备树的max_x写的是 720而屏真实分辨率是 1280这里出来的坐标就是被截断的表现为永远无法点到屏幕右边区域。此时把原始坐标和屏幕宽高换算一下就能验证adb shell getevent -p /dev/input/event2这个命令打印设备的绝对坐标范围比如ABS_MT_POSITION_X : value 0, min 0, max 719看到 max 是 719 就知道驱动配置不对了。正确值应该是max 719对应 720 分辨率max 1279对应 1280 分辨率建议验这一行就够。坐标方向对不对也可以在没有触摸屏 GUI 的情况下用monkeyrunner或者直接写一个读取/dev/input/event2的小工具验证。如果坐标范围和方向都对那触摸事件基本没问题剩下的是系统上层校准比如边缘防误触、手套模式。最后说一个我反复踩过的教训每次拿到 GSL1680/GSL1688 驱动包我都强制自己按这个顺序走一遍——先读芯片 ID、核对坐标范围、再看触点数量、最后才测手感。以前跳过坐标范围直接测手感结果在 800x480 的屏上用了 1024x600 的配置触摸能报点但位置永远偏浪费了一整天。从那以后我每移植一份触摸驱动都会在验收单上写上“原始坐标范围已核对”这一条再继续。希望这个习惯能帮到你。本文还有配套的精品资源点击获取