
简介这份资源面向Android底层驱动开发者和系统框架学习者围绕联发科平台传感器驱动与安卓传感器服务框架展开帮助理解传感器数据从硬件到应用层的完整通路。压缩包共2个文件一个C源文件对应驱动核心实现涉及设备树配置、中断处理与电源管理逻辑一个Java源文件对应系统层Sensor框架示例整体仅32KB内容精炼。目前已有2139人学习下载。借助hwmsen_dev.c可熟悉Linux内核设备模型掌握传感器数据从硬件到上层传输的关键路径借助Sensor.java可了解Android SensorService通过JNI与底层交互、SensorManager注册监听以及SensorEvent分发机制从而串联驱动与系统服务两层知识适合用于快速剖析MTK传感器框架的代码结构。 MTK平台点传感器说难不难说简单也真不简单。前阵子我拿到一块工程板板子上电源、I2C、中断脚全部设计好了驱动代码也是从原厂release包拷来的编译一次通过但开机后系统里就是看不到这颗g-sensor。从硬件排查到驱动、从HAL到框架层整整折腾了一天最后发现是设备树里一个不起眼的电源字段和PMIC对不上。这颗看似普通的传感器背后串起的其实是mtk sensor驱动和安卓层框架一整条链路任何一个环节断了数据就出不来。这篇文章我就把这条链路完整拆开从点亮传感器到数据最终出现在App里每一步怎么验证、哪些地方最容易翻车一次讲清楚。不管你是刚接手sensor的BSP新人还是被SensorService折腾过两回的Framework工程师应该都能找到点有用的东西。1. 点亮一颗MTK sensor的前置条件先让IC活过来1.1 电源、地址和中断脚三个被反复踩的坑传感器一般挂在I2C总线上但I2C能通不代表芯片真的在工作。很多人一上来就查驱动忽略了最底层的东西——供电。几乎每颗sensor都有独立的模拟电源和数字IO电源分别叫VDD和VIO。MTK平台上这两路电通常由PMIC的某个LDO提供而LDO的开关控制恰恰是通过设备树里的electrical属性告诉内核的。我在实际调试中遇到过一次典型的“假活”现象I2C探测能过但读WHO_AM_I寄存器返回值全FF。量了一下VDD引脚电压发现只有0.8V原来是PMIC LDO配置档位不对。MTK老式设备树里通常这么写i2c0 { gsensor6b { compatible mediatek,gsensor; reg 0x6b; i2c_num 0; i2c_addr 0x6b; direction 4; power_id 0x53; power_vol 0x32; firlen 32; }; };这里的power_id对应PMIC LDO编号power_vol是电压档位。很多从老平台搬过来的代码这两个值在新平台上直接就不适用因为新平台PMIC型号变了。判断逻辑很简单如果log里出现mtk_ldo_get/put失败或者sensor probe返回-ENXIO八成就是电源问题。第二个常见坑是I2C地址冲突。MTK的sensor设备树节点是用6b和reg 0x6b来声明地址的如果一个I2C总线上挂了两个地址相同的设备后加载的那个会直接失败。我见过有人同时挂了两颗相同型号的加速度计结果第二颗怎么都枚举不出来。第三个坑是中断GPIO。sensor通常有一根INT脚来通知AP数据准备好。MTK平台这一般对应PIO的EINT功能设备树里要声明interrupt-parent pio和interrupts xx IRQ_TYPE_LEVEL_LOW。如果你只配了I2C没配中断sensor也能probe成功但永远没有数据上报因为驱动根本没收到“准备好”的信号。1.2 先读寄存器验证别急着怀疑驱动代码硬件链路到底通没通最直接的验证动作是用I2C工具去读sensor的ID寄存器不需要运行任何安卓代码。比如常见的LSM6DS3这颗六轴芯片WHO_AM_I寄存器地址0x0F默认值应该是0x69。在android adb shell里可以用自带的i2c工具操作adb root adb shell ls /sys/bus/i2c/devices/ cat /sys/bus/i2c/devices/0-006b/name如果设备节点下能读出name说明内核I2C子系统已经枚举出这个器件驱动和硬件匹配成功。此时再用i2cget或i2ctransfer读寄存器看到的值如果有变化或符合数据手册整条硬件链路就是通的。这一步相当于把自己从“驱动到底有没有问题”的争论里摘出来。只要这一步通过后面所有的问题都落在驱动逻辑、HAL层或者框架层如果这一步过不了问题就锁定在硬件配置、设备树匹配或者芯片上电时序。1.3 点亮后的一条通用提醒不同平台吃的是不同套路MTK新老平台的差异很容易被忽视。老平台Kernel 4.4/4.14喜欢用自定义属性填电源和中断新平台Kernel 5.4/5.10逐渐向标准电源管理框架靠拢用的是vdd-supply配regulator。直接把老平台dts搬过来的传统做法在新内核上大概率踩坑。海思平台的sensor调试有pqtool.sh这种统一配置工具MTK这边则相对零散不少还依赖各家驱动里自定义的宏和节点。所以动手之前先去确认你的内核版本对应的sensor框架长什么样别凭老经验硬套。2. 驱动层的三件事probe、input子系统上报与中断管理2.1 probe成功不等于驱动写完I2C设备驱动在probe回调里一般做三件事检查I2C功能、初始化寄存器、注册input设备。MTK平台裸驱动通常直接复用input子系统上报方式是用input_report_abs把数据塞进input事件。static int gsensor_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct input_dev *idev; idev input_allocate_device(); if (!idev) return -ENOMEM; idev-name gsensor; idev-id.bustype BUS_I2C; input_set_capability(idev, EV_ABS, ABS_X); input_set_capability(idev, EV_ABS, ABS_Y); input_set_capability(idev, EV_ABS, ABS_Z); input_set_abs_params(idev, ABS_X, -32768, 32767, 0, 0); input_set_abs_params(idev, ABS_Y, -32768, 32767, 0, 0); input_set_abs_params(idev, ABS_Z, -32768, 32767, 0, 0); input_register_device(idev); return 0; }这里最容易出问题的是“事件类型必须和HAL层的解析匹配”。MTK的HAL在解析g-sensor数据时默认读的就是ABS_X、ABS_Y、ABS_ZALS/Proximity则可能走ABS_MISC。如果你驱动里用了别的类型比如ABS_MT_POSITION_X那上层HAL根本不会理你。这个映射关系散落在各家HAL的SensorBase.cpp里不对log的话你根本不知道是驱动没上报还是上报了没人认账。2.2 中断上报还是轮询上报优先选择中断老式sensor驱动很多用轮询线程每隔几十毫秒读完寄存器再上报。这种方案在驱动里写起来简单但功耗很难看而且安卓框架里对sensor采样率的要求越来越细靠轮询很难做到精确控制。MTK平台现在主流方式是中断驱动sensor数据准备好后通过INT脚触发EINT驱动在中断处理函数里读寄存器并上报事件static irqreturn_t gsensor_irq_handler(int irq, void *dev_id) { struct gsensor_data *data dev_id; /* 读取x/y/z寄存器 */ input_report_abs(data-idev, ABS_X, x); input_report_abs(data-idev, ABS_Y, y); input_report_abs(data-idev, ABS_Z, z); input_sync(data-idev); return IRQ_HANDLED; }注意中断里不要做任何I2C重操作或延时I2C读取本身很快但如果在里面加打印、加memset大buffer分分钟触发“irq handler slow”警告。2.3 休眠时还要干活双击唤醒背后的中断管理“mtk 手势双击唤醒”这类功能本质上是让一颗传感器在系统suspend时继续工作检测到特定手势后把系统唤醒。这里面最微妙的点在于系统休眠了你的中断还能不能进来在驱动里如果注册中断时没有设置IRQF_NO_SUSPEND标志那系统进入suspend后这个中断会被mask掉手势来了也没反应。但如果你无脑加上IRQF_NO_SUSPEND传感器就会在休眠时不停产生中断功耗直接崩掉。正确做法是注册中断时加IRQF_NO_SUSPEND但同时在suspend回调里把传感器配置成低功耗手势检测模式只有在检测到有效手势时才拉高中断脚在resume回调里恢复全速模式。这样既保证唤醒能力又能控制功耗。另外一个MTK平台的细节EINT去抖时间配置会影响双击识别的成功率。去抖太短一次物理抖动可能被识别成两次点击去抖太长用户双击屏幕的手势会被吞掉。我一般建议从30ms开始调再根据具体触控面板的手势参数做微调。3. 安卓上层链路从HAL层到SensorService3.1 一路走来的三副面孔libhardware、HIDL、AIDLAndroid传感器HAL这几年的变化可以说是一个完整的接口演进历史。Android 8.0之前MTK和绝大多数厂商都用libhardware的sensors_module_t结构代码路径通常在hardware/libhardware/modules/sensors。到了Android 8.0之后Google强制推向HIDL接口变成了android.hardware.sensors1.0再后来又升级到2.0。Android 13开始HIDL逐渐被AIDL取代android.hardware.sensors.aidl成为新方向。对做MTK平台的人而言最直接的感受就是厂商release的代码目录结构一直在变。老平台的sensor HAL大部分逻辑集中在vendor/mediatek/proprietary/hardware/sensors新平台则可能拆成了HIDL service和HAL实现两个部分。迁移到AIDL版本时有个特别隐蔽的坑VINTF manifest。Android 13的SensorService会去manifest里找sensors服务如果你编译的system版本默认声明的是HIDL接口但vendor侧实际跑的是AIDL版本logcat里就会反复出现找不到android.hardware.sensorsservice的报错SensorService无法和HAL建立连接传感器列表直接为空。遇到这种情况先去检查manifest.xml里的声明和实际启动的sensor service是否一致。3.2 SensorService与HAL怎么协作SensorService在system_server进程里运行启动时会调用HAL的接口拿到所有sensor的信息建立一个全局的Sensor列表。之后App通过SensorManager向SensorService注册监听SensorService再按需要从HAL获取数据并分发给各个客户端。事件流大致是sensor芯片产生中断 - Linux input子系统上报事件 - SensorService读取input节点并解析 - SensorManager把数据分发到App回调这里有个容易混淆的点MTK有些新平台把低功耗传感器挂在了一个独立的MCU或者SensorHub上数据不经过kernel的input子系统而是通过共享内存或者vendor专用通道直接进入框架层。调试这种方案时你抓getevent是看不到任何数据的别一上来就怀疑驱动没工作。判断自己的sensor走哪种路径最直接的方法是看HAL层的实现。如果HAL里有专门的SensorHubClient或共享内存读取代码说明走的是hub通道如果HAL逻辑主要是打开/dev/input/eventX并解析事件那就是普通的input路径。3.3 上层看不到我的sensor先查这三处dumpsys sensorservice里看不到新增的sensor通常有三个原因。第一个是HAL层get_sensors_list返回的列表里根本没填充这个sensor这种情况要去看HAL的初始化代码确认新sensor是否被编进编译宏。第二个是VINTF service没有把sensor HAL真正跑起来检查进程是否存在、权限是否正常。第三个是权限或版本问题——Android 12之后有些平台对sensor类型有白名单限制非标准类型可能需要额外声明。这三个原因按出现频率排序HAL列表没填充几乎占七成。MTK提供的HAL模块里传感器种类通常由一组#define SENSOR_TYPE_*控制新增加sensor时需要同步修改这一堆宏漏一个就可能导致列表很短但你是看不出编译错误的。4. 调试三板斧logcat、dumpsys、sysfs4.1 用dumpsys sensorservice判断哪一层出了问题拿到一台sensor异常的设备我的习惯是第一件事执行adb shell dumpsys sensorservice输出里重点看三块Sensor List系统感知到的传感器列表有没有你的sensorActive sensors当前有哪些sensor被App激活采样率、延迟配的多少Sensor Events最近的事件流有没有在滚动看List确认HAL层是否注册成功看Active sensors确认是否有应用在请求数据看Events确认数据是否从底层一路送到了SensorService。这样一遍过下来问题大概锁定在哪个区域就清楚了。在实际项目里我最常碰到的是Sensor List能看到、Active sensors里也能看到但Events里一条都没有。这种状态说明应用侧已经拿到sensor但数据压根没上来链路断在kernel input事件到HAL解析这个区间多半是驱动上报的event type和HAL解析的不一致。4.2 用getevent做底层验证如果怀疑是驱动没上报用getevent确认一下kernel层到底有没有产生事件adb shell getevent -lt正常滚动时你会看到类似下面的输出/dev/input/event4: EV_ABS ABS_X 00000010 /dev/input/event4: EV_ABS ABS_Y 00000020 /dev/input/event4: EV_SYN SYN_REPORT有事件说明驱动和硬件工作正常问题在上层。没事件说明驱动没跑起来或者硬件数据没准备好。这里有一个实战经验getevent是input子系统的直接观测点但如果遇到走SensorHub的方案这里就是空的属于正常情况别误判。4.3 用sysfs节点确认驱动真实状态驱动源码里最好给自己留一个调试用的sysfs节点比如static ssize_t gsensor_reg_debug_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { /* 读取指定寄存器并以hex格式返回 */ }然后通过adb shell cat /sys/devices/platform/xxx/reg_debug随时查看寄存器值。这种调试节点在项目早期极其有用可以快速验证驱动初始化时有没有把芯片配成预期状态。我看到很多团队喜欢用全局变量加printk来调试其实一个简单的debug节点比一百次抓log都高效。5. 我在MTK sensor调试中踩过的几个深坑5.1 system_server内存泄漏sensor也会成为元凶热搜词里有“mtk内存泄漏排查”我确实因为sensor栽过一次。现象是系统跑了十几个小时后system_server的RSS从300MB涨到1.2GBUI开始卡顿最后触发LMK。排查了很久才定位到SensorService的EventThread往一个缓存队列里塞数据时某个sensor事件带了一个非法的大尺寸buffer导致内存一直被占用。那次根因是HAL层在构造sensor事件时一个长度字段没有初始化随机值被上层当成真实长度反复分配大内存。定位方法其实是把问题范围一步步缩小先用top监控system_server内存趋势再用dumpsys sensorservice对比每个sensor的事件频率找出异常sensor最后在HAL代码里审查事件构造逻辑。如果你也遇到类似问题我的建议是先确认是不是某个特定sensor在高频触发。排查时可以临时把所有sensor的采样率降到最低内存曲线如果明显平缓嫌疑就在sensor事件处理路径里。5.2 双击唤醒误触发接个电话屏幕亮半天做“mtk 手势双击唤醒”功能时我遇到的另一个大坑是误唤醒。用户把手机放口袋里腿部摩擦几下传感器就判定为双击屏幕在口袋里亮了大半天功耗血崩。排查过程是这样的先在kernel log里抓EINT触发记录发现一个晚上有上百次中断再量了一下传感器在休眠状态下的功耗明显偏高。逐层分析后确定问题出在两点——触发灵敏度和去抖时间设置不合理以及suspend状态下传感器没有进入真正低功耗监控模式。修改方案是双管齐下驱动侧的debounce时间从原来的5ms调到30ms同时suspend回调里把传感器切换到低功耗手势检测模式正常情况下不产生中断只有检测到有效双击才唤醒。改完后误触发的次数基本归零夜间待机功耗也恢复正常。这类问题很能说明一个道理sensor驱动不是“能上报数据”就算完功耗和稳定性才是量产的关键。5.3 从摄像头调试gc5025学到的通用经验我在MTK平台上调过gc5025摄像头虽然那是摄像头sensor但调试方法论和sensor几乎一模一样先确认I2C读写正常、再验证供电和复位时序、然后抓寄存器确认sensor初始化成功。摄像头平台常有一套标准调试脚本跑一遍就能定位大部分问题反观加速计这类小sensor很多人反而容易忽略系统性的排查顺序一上来就盯驱动代码。我的经验是把问题分层是第一件事硬件层能不能读ID、驱动层有没有probe成功、HAL层列表里有没有注册、框架层事件流有没有滚动。谁出了问题就在那一层修。这套分层排查的思维比你记住任何一段具体代码都管用。5.4 最后一个省时间的建议调MTK平台的sensor工程机上一定要有root权限哪怕是userdebug编译版本也行。很多看似诡异的问题比如HAL读不到数据、SensorService连不上其实只是权限不够导致节点访问失败。真到了量产机上你没法root那问题复杂度会翻倍。所以项目早期就确认好编译版本和调试工具链别在权限上浪费热情。另外如果开机后发现sensor完全不工作先去看有没有被系统服务杀掉或拒绝。抓一次完整的开机log用关键字sensor和i2c过滤基本能定位八成问题。别一上来就去翻源码全局log比代码更有说服力。本文还有配套的精品资源点击获取