一、概述
MTK框架可以分为两部分,AP和SCP。
AP是主芯片,SCP是协处理器,他们一起工作来处理sensor数据。
SCP 是用来处理sensor和audio相关功能和其他客制化需求的一个协处理理器,MTK SCP选择freeRTOS作为操作系统,CHRE是处理传感器相关操作的专门任务。
kernel层负责汇总处理sensor传输上来的数据,以及处理应用层传递下来的指令。
hal层是硬件抽象层,具有硬件供应商实现的标准接口,允许Android不了解低级别的驱动程序实现。sensor service通过动态链接的方式加载hal层模块。经过动态链接,service可以调用hal层的函数,方便将控制传递,也可以从hal层获取数据。这样使用hal就可以在不影响或修改更高级别系统的情况下实现功能。
framework层中sensor service 能创建实例对象,并增加到service manager中,同时可以从驱动中获取原始数据并发送到客户端。
应用层中的应用可以发送调用sensor的指令到下层,同时也可以获得下层传来的传感器数据并进行分析处理。
整个sensor体系中包括:应用层、framework层、jni、hal层、kernel层、SCP/CHRE。
因为对于日常生活来说有一部分sensor是使用频率是很高的,所以必然也伴随着手机功耗的增加如果每次都是CPU进行处理的化,而且CPU一旦休眠还伴随着sensor会停止工作,为了优化手机使用Google和MTK分别开发了CHRE 和SCP 进行sensor控制。
二,介绍SCP
SCP是处理客制化需求的协处理器。
SCP 是用来处理sensor和audio相关功能和其他客制化需求的一个协处理理器,MTK SCP选择freeRTOS作为操作系统,CHRE是处理传感器相关操作的专门任务,它的架构如下
然后是CHRE:
在SCP下,MTK传感器集线器功能是在google CHRE ar上开发的,chre(Context Hub Runtime Environment)是一种事件驱动的体系结构,也可以被视为操作系统。
黄色部分是事件队列,CHRE只有一个while循环来处理事件队列中的头事件。如果以前的调用尚未完成,CHRE将无法调用队列中的一个任务。因此,没有优先级概念,当前事件队列处理只能
被中断中断。默认情况下,CHRE在事件队列中最多支持512个事件。CHRE的目的是实现实时性和轻量级,因此事件队列中调用的所有任务都必须快速运行。CHRE中的驱动程序实现称为nano hub app。
三,sensor2.0基本框架
整个sensor体系中包括:应用层、framework层、jni、hal层、kernel层、SCP/CHRE。
3.1 scp模块代码路径
路径:vendor/vendor/mediatek/proprietary/tinysys/freertos/source/
drivers/CM4_A:针对不同硬件的专有驱动程序代码
3.2 SCP 代码大小限制机制
memoryReport.py是一个用于在构建时限制代码大小的脚本。
如果代码大小超过了设置,它将会导致构建错误,以警告编码器。
如果你认为代码确实占据了你所看到的大小,你可以修改Setting.ini文件。
修改路径:
vendor/vendor/mediatek/proprietary/tinysys/freertos/source/tools/memoryReport.py
vendor/vendor/mediatek/proprietary/tinysys/freertos/source/project/CM4_A/$PLATFORM/platform/Setting.ini
3.3 overlay机制详解
一种类型的传感器可能来自两个不同的供应商。
如果将两个驱动程序放在SCP SRAM中进行自动检测,它会消耗大量SRAM。
因此,MTK解决方案将在DRAM中找到两个驱动程序,并在SCP启动时加载一个驱动程序。
驱动加载步骤:overlay加载流程:
内存将SCP loader部分的代码从DRAM复制到SRAM中,同时SCP启动
SCP loader将Tinysys相同的代码从DRAM复制到SRAM,操作系统启动,同时传感器驱动进行初始化
OverlayRemap将传感器驱动1复制到SRAM,hw进行验证(主要是sensor的id),如果失败,就复制驱动2,如此往复,如果成功,就remap下一个传感器类型
一个section表示一个传感器类型(如加速度传感器类型),一个类型中可能有很多个驱动
3.4 sensor 加载顺序
代码位置
vendor/vendor/mediatek/proprietary/tinysys/freertos/source/drivers/CM4_A/$PLATFORM/overlay/inc/
目录下的文件mtk_overlay_init.h,它被overlay.c文件引用。
这段枚举,定义了每个sensor类型在overlay时的顺序编号。
上图的代码来自mtk_overlay_init.c文件,里面设置overlay table时使用的switch判断条件就是section的id。
在switch判断完成之后,所有sensor类型的虚拟地址vma,大小size等参数会被复制到table中,之后在进行加载时也是以section的id为判断依据。
理论上不同sensor类型的加载顺序可以改变
但是改变sensor加载顺序需要对后续所有相关代码的id顺序进行修改,修改会非常麻烦,容易出现bug。
所以一般情况下不改变sensor的加载顺序。
3.5 加载驱动流程
下面加载驱动的流程都是以挂载在SCP侧的驱动为例:
想要加载一个新的驱动,需要把驱动添加到如下目录中:
vendor/vendor/mediatek/proprietary/tinysys/freertos/source/project/CM4_A/ P L A T F O R M / PLATFORM/ PLATFORM/PROJECT/cust/
不同的目录是负责加载不同的传感器的:
accGyro加载加速度、陀螺仪传感器
alsps负责加载光距感传感器
barometer加载气压计传感器
magnetometer加载地磁传感器
overlay负责加载sensor overlay
3.6 添加客制化sensor信息
以加速度传感器为例,将需要添加的传感器添加到文件cust_accGyro.c中
文件路径:vendor/vendor/mediatek/proprietary/tinysys/freertos/source/project/CM4_A/ P L A T F O R M / PLATFORM/ PLATFORM/PROJECT/cust/accGyro/
路径中包含的 P L A T F O R M 是指 M T K S O C 平台代码, PLATFORM是指MTK SOC平台代码, PLATFORM是指MTKSOC平台代码,PROJECT是项目代码
配置包含sensor的方向以及i2c地址等等,需要根据实际情况进行修改
补充:对于.direction参数,在目录**/vendor/vendor/mediatek/proprietary/tinysys/freertos/source/middleware/contexthub/**下的hwsen.c文件中有详细体现
参数0-7对应map[]中的8个元素,前面的参数±1表示取该轴的哪个方向为正方向,后面的参数0、1、2分别表示xyz轴,如map[1]表示芯片的正方向为x,-y,z
3.7 启用overlay功能,添加sensor编译宏
vendor/vendor/mediatek/proprietary/tinysys/freertos/source/project/CM4_A/ P L A T F O R M / PLATFORM/ PLATFORM/PROJECT/
在文件ProjectConfig.mk中添加
将传感器驱动添加到系统编译中
目录:
vendor/vendor/mediatek/proprietary/tinysys/freertos/source/project/CM4_A/$PLATFORM//platform/feature_config/
在文件chre.mk中添加
判断条件由上一步的编译宏控制
3.8 将传感器驱动添加到overlayremap中
vendor/vendor/mediatek/proprietary/tinysys/freertos/source/project/CM4_A/ P L A T F O R M / PLATFORM/ PLATFORM/PROJECT/cust/overlay.c
在链接脚本中添加对象,添加到对应传感器区域
如加速度传感器添加到ACC中:
vendor/vendor/mediatek/proprietary/tinysys/freertos/source/project/CM4_A/ P L A T F O R M / PLATFORM/ PLATFORM/PROJECT/inc/overlay_sensor.c
四,kernel 层
4.1 kernel层代码位于kernel文件夹下,至于kernel版本会在device下定义,我们先看kernel-4.14。sensor在kernel中的代码位于:
kernel-4.14/drivers/misc/mediatek/sensor
kernel-4.14/drivers/misc/mediatek/sensors-1.0
kernel-4.14/drivers/staging/nanohub
sensor 文件夹下有一个我们要的类hf_manager.c,打开这个文件不然发现,它创建了节点dev/hf_manager,这刚好就和hal层的HfManager.cpp 链接上了,hal层的指令通过这个节点来到了kernel,这是一个普通的文件节点,里面有一些读写、指令操作。这里面有个核心结构体hf_core,后面很多操作都和这个结构体有关系
struct hf_core {
struct mutex manager_lock;
struct list_head manager_list; //外面注册的manager
struct sensor_state state[SENSOR_TYPE_SENSOR_MAX];
spinlock_t client_lock;
struct list_head client_list;
struct mutex device_lock;
struct list_head device_list;
struct kthread_worker kworker;
首先我们看下hf_manager 被打开时做了什么事情:创造了一个hf_client,这个hf_client记录了当前进程和线程的id,还创建了一个FIFO,用于数据交换
static int hf_manager_open(struct inode *inode, struct file *filp)
{
struct hf_client *client = hf_client_create();
if (!client)
return -ENOMEM;
filp->private_data = client;
nonseekable_open(inode, filp);
return 0;
}
然后,大概浏览一下代码不难发现,指令好像后面都被hf_device这个结构体承担了,这个结构体来自与hf_manager,hf_manager 每次使用之前都会调用hf_manager_find_manager方法来查找,那我么看看这个方法做了什么:
static struct hf_manager *hf_manager_find_manager(struct hf_core *core,
uint8_t sensor_type)
{
int i = 0;
struct hf_manager *manager = NULL;
struct hf_device *device = NULL;
list_for_each_entry(manager, &core->manager_list, list) {
device = READ_ONCE(manager->hf_dev);
if (!device || !device->support_list)
continue;
for (i = 0; i < device->support_size; ++i) {
if (sensor_type == device->support_list[i].sensor_type)
return manager;
}
}
pr_err("Failed to find manager, %u unregistered\n", sensor_type);
return NULL;
}
发现它只是从一个list中去查找,根据sensor_type进行匹配,这个core→manager_list 又是由hf_manager_create方法进行注册,而这个方法是向外提供的,然后搜索一下不难发现:
./2.0/sensorhub/transceiver.c 和 ./2.0/mtk_nanohub/mtk_nanohub.c使用到了它,也就是说这两个类分别注册了自己的hf_device,并且进行了相关的数据处理,这两个类所处的文件夹可以猜测一个是跟sensorhub相关,另一个则和CHRE相关。
4.2 sensor hub
sensor hub是个什么东西呢,故名思意,它是一个传感器集线器,也就是说某些传感器的数据要经过这个地方进行中转,transceiver.c它会和SCP通过IPI通信,将这边的指令还有DRAM地址发送到SCP端,然后把SCP 端的信息回传,先是读取DRAM内存获取信息,然后通过manager→report方法会传到hf_fifo,然后hf_fifo上的数据会被hal层读走。
ap侧sensor hub 通过kernel-4.14/drivers/misc/mediatek/sensor/2.0/sensorhub/ipi_comm.c和SCP侧建立链接:IPI_IN_SENSOR_NOTIFY接收ID,IPI_OUT_SENSOR_NOTIFY接收ID,IPI_IN_SENSOR_CTRL指令ID
sensor hub 所链接的SCP 侧在:vendor/mediatek/proprietary/tinysys/scp/middleware/sensorhub/comm/ipi_comm.c
4.3 nano hub
nano hub 是CHRE框架中的集线器,和sensor hub类似,nano hub 也是通过mtk_nonohub.c注册了hf_device ,和SCP通过IPI通信,将DRAM地址发送到SCP端,通过IPI把指令发过去,但是通过DRAM把信息读回来,然后通过manager→report 传递到hal层
ap侧nano hub 通过kernel-4.14/drivers/misc/mediatek/sensor/2.0/mtk_nanohub/nanohub/nanohub-mtk.c 和SCP建立链接 通过ID IPI_CHRE
nanohub 所链接的SCP侧在:vendor/mediatek/proprietary/hardware/contexthub/firmware/links/plat/src/hostIntfIPI.c
然后CHRE会调用bool SensorQueueEnqueue(struct SimpleQueue* sq, const void *data, int length, bool possiblyDiscardable)将数据传递到scp/middleware/contexthub/contexthub_fw.c,然后contexthub_fw.c 将数据写入DRAM,供AP侧读取
还有一部分指令会通过IPI_SENSOR直接传递到scp/middleware/contexthub/contexthub_fw.c
vendor/mediatek/proprierary/hardware/contexthub/firmware 下面有一些sensor,会将数据传递给hostIntf,然后传递给contexthub.c
vendor/mediatek/proprierary/tinysys/scp/middleware/sensorhub 下面也有一些sensor会传递信息
五,hal层的实现
真正实现hal转接口的代码是在:vendor/mediatek/proprietary/hardware/sensor 这个文件夹中也会有多个版本的代码根据android.mk 进行选择,由宏控制,选择相应的版本,当前选择最新2.0进行讨论
与framework层通信的代码在hidl文件夹下有多个文件夹:
1.0/2.0 表示版本号,配置hal哪个版本就哪个
multihal:多hal,需要单独配置由/vendor/etc/sensors/hals.conf这个文件控制
Sensors.h 中可以看出Sensors 继承了ISensors并实现了这些方法
从Sensors.cpp代码可以看出,Sensors初始化时会先判断是否使用了多hal,如果使用则会加载多hal模块,如果使用则通过hw_get_module加载id为SENSORS_HARDWARE_MODULE_ID的模块,并把它赋值给了mSensorModule。发现这个模块代码就放在sensor目录下面对应的那些不同版本的文件夹下,直接到vendor/mediatek/proprietary/hardware/sensor/2.0/hal 这个目录下放的都是2.0 hal 的代码,其中sensors.cpp 中的正式id为SENSORS_HARDWARE_MODULE_ID的模块
static struct hw_module_methods_t sensors_module_methods = {
.open = open_sensors
};
struct sensors_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = SENSORS_HARDWARE_MODULE_ID,
.name = "MTK SENSORS Module",
.author = "Mediatek",
.methods = &sensors_module_methods,
},
所以可以从此看出,从framework层过来的指令给了mSensorModule和mSensorDevice 进行处理,间接的传递给了这个模块处理,然后此模块又分出很多个类进行分别处理不同的指令,部分指令又会汇集到一个类去和另一个层通信它就是HfManager,这个类在2.0/core/文件夹下,
进到HfManager 我们发现,这个类里面还有一个内部类HfLooper,但是无论哪个类,他们的方法最后会通过一个节点他就是dev/hf_manager,hal层则是通过这个节点来到了kernel层kernel-4.14/d/misc/mediatek/sensor/2.0/core/hf_manager.h
所以,整个框架信息传递如下图: