MTK Sensor框架 及信息传递详解

一、概述

sensor(传感器)作为手机中一个非常重要且目前来说不可或缺的一种组件,功能强大,但是使用却很简单。Android 传感器属于虚拟设备,可提供来自以下各种物理传感器的数据:加速度计、陀螺仪、磁力计、气压计、湿度传感器、压力传感器、光传感器、近程传感器和心率传感器。因为对于日常生活来说有一部分sensor是使用频率是很高的,所以必然也伴随着手机功耗的增加如果每次都是CPU进行处理的化,而且CPU一旦休眠还伴随着sensor会停止工作,为了优化手机使用Google和MTK分别开发了CHRE 和SCP 进行sensor控制。

首先介绍以下SCP:

SCP 是用来处理sensor和audio相关功能和其他客制化需求的一个协处理理器,MTK SCP选择freeRTOS作为操作系统,CHRE是处理传感器相关操作的专门任务,它的架构如下
SCP
然后是CHRE:
CHRE

在SCP下,MTK传感器集线器功能是在google CHRE ar上开发的,chre(Context Hub Runtime Environment)是一种事件驱动的体系结构,也可以被视为操作系统。

黄色部分是事件队列,CHRE只有一个while循环来处理事件队列中的头事件。如果以前的调用尚未完成,CHRE将无法调用队列中的一个任务。因此,没有优先级概念,当前事件队列处理只能

被中断中断。默认情况下,CHRE在事件队列中最多支持512个事件。CHRE的目的是实现实时性和轻量级,因此事件队列中调用的所有任务都必须快速运行。CHRE中的驱动程序实现称为nano hub app。

整个sensor体系中包括:应用层、framework层、jni、hal层、kernel层、SCP/CHRE。

二、应用层调用

sensor在应用层调用非常简单,代码如下:

public class SensorActivity extends Activity implements SensorEventListener {

         private final SensorManager mSensorManager;

         private final Sensor mAccelerometer;


         public SensorActivity() {

             mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

             mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

         }


         protected void onResume() {

             super.onResume();

             mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);

         }


         protected void onPause() {

             super.onPause();

             mSensorManager.unregisterListener(this);

         }


         public void onAccuracyChanged(Sensor sensor, int accuracy) {

         }


         public void onSensorChanged(SensorEvent event) {

         }

     }

只需要获取到sensormanager然后注册监听就可以,sensor的信息就会通过回调接口上报上来

getSystemService 获取系统服务的标准接口,参数是表示要获取的服务类型
Sensormanger.getDefaultSensor 获取想要使用的sensor 实例 ,参数表示要获取的sensor 类型
SensorManager.registerListener  注册sensor的信息回调,参数分别是上下文,sensor,上报延时

三、framework层

经过上层的getSystemService获取了服务,功能就来到了framework层,这里对应framework/base/core/java/android/hardware/SensorManager.java,但是实际上这只是一个代理抽象类,真正获取的是SystemSensorManager:framework/base/core/java/android/hardware/SystemSensorManager.java。(从SystemServiceRegistry.java中可以看出,这是一个专门为上层注册接口的类)。

SystemSensorManager 在最上层控制着所有的sensor ,他的实现仅仅靠自己是不够的,它还链接了一个jni库,注册了很多native方法。android_hardware_SensorManager :framework/base/core/jni/android_hardware_SensorManager.cpp

static const JNINativeMethod gSystemSensorManagerMethods[] = {

        {"nativeClassInit",

                "()V",

                (void*)nativeClassInit },

        {"nativeCreate",

                 "(Ljava/lang/String;)J",

                 (void*)nativeCreate },


        {"nativeGetSensorAtIndex",

                "(JLandroid/hardware/Sensor;I)Z",

                (void*)nativeGetSensorAtIndex },


        {"nativeGetDynamicSensors",

                "(JLjava/util/List;)V",

                (void*)nativeGetDynamicSensors },


        {"nativeIsDataInjectionEnabled",

                "(J)Z",

                (void*)nativeIsDataInjectionEnabled },


        {"nativeCreateDirectChannel",

                "(JJIILandroid/hardware/HardwareBuffer;)I",

                (void*)nativeCreateDirectChannel },


        {"nativeDestroyDirectChannel",

                "(JI)V",

                (void*)nativeDestroyDirectChannel },


        {"nativeConfigDirectChannel",

                "(JIII)I",

                (void*)nativeConfigDirectChannel },


        {"nativeSetOperationParameter",

                "(JII[F[I)I",

                (void*)nativeSetOperationParameter },

    };


    static const JNINativeMethod gBaseEventQueueMethods[] = {

            {"nativeInitBaseEventQueue",

             "(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;Ljava/lang/String;ILjava/lang/"

             "String;Ljava/lang/String;)J",

             (void *)nativeInitSensorEventQueue},


            {"nativeEnableSensor", "(JIII)I", (void *)nativeEnableSensor},


            {"nativeDisableSensor", "(JI)I", (void *)nativeDisableSensor},


            {"nativeDestroySensorEventQueue", "(J)V", (void *)nativeDestroySensorEventQueue},


            {"nativeFlushSensor", "(J)I", (void *)nativeFlushSensor},


            {"nativeInjectSensorData", "(JI[FIJ)I", (void *)nativeInjectSensorData},

    };


    } //unnamed namespace


    int register_android_hardware_SensorManager(JNIEnv *env)

    {

        RegisterMethodsOrDie(env, "android/hardware/SystemSensorManager",

                gSystemSensorManagerMethods, NELEM(gSystemSensorManagerMethods));


        RegisterMethodsOrDie(env, "android/hardware/SystemSensorManager$BaseEventQueue",

                gBaseEventQueueMethods, NELEM(gBaseEventQueueMethods));


        gBaseEventQueueClassInfo.clazz = FindClassOrDie(env,

                "android/hardware/SystemSensorManager$BaseEventQueue");


        gBaseEventQueueClassInfo.dispatchSensorEvent = GetMethodIDOrDie(env,

                gBaseEventQueueClassInfo.clazz, "dispatchSensorEvent", "(I[FIJ)V");


        gBaseEventQueueClassInfo.dispatchFlushCompleteEvent = GetMethodIDOrDie(env,

                gBaseEventQueueClassInfo.clazz, "dispatchFlushCompleteEvent", "(I)V");


        gBaseEventQueueClassInfo.dispatchAdditionalInfoEvent = GetMethodIDOrDie(env,

                gBaseEventQueueClassInfo.clazz, "dispatchAdditionalInfoEvent", "(III[F[I)V");


        return 0;

    }

上面代码是android_hardware_SensorManager用来绑定java类和相关方法的。然后又将方法实现转移到SensorManager.cpp:framework/native/libs/sensor/SensorManager.cpp,

SystemSensorManager创建时 会创建native SensorManager的实例并持有,然后获取sensorlist。SystemSensorManager中封装了很多方法,如:启用、禁用、刷新、添加监听等等。

SensorManager 也不是自己去做事情,它通过BitTube和SensorService 链接,将指令传到sensorservice。android_hardware_SensorManager 有一个内部类专门监听了BitTube通道文件描述符,会将sensor上报的信息通过这里上报到上层。

然后所有的事情都会转移到frameworks/native/services/sensorservice/ 这个包中,这里面会对所有sensor信息进行整理过滤,这里的所有信息都是从hal层获取,这里有个很关键的类SensorDevice,这个类链接了hal层

 SensorDevice::SensorDevice()

            : mHidlTransportErrors(20),

              mRestartWaiter(new HidlServiceRegistrationWaiter()),

              mEventQueueFlag(nullptr),

              mWakeLockQueueFlag(nullptr),

              mReconnecting(false) {

        if (!connectHidlService()) {

            return;

        }


        initializeSensorList();


        mIsDirectReportSupported =

                (checkReturnAndGetStatus(mSensors->unregisterDirectChannel(-1)) != INVALID_OPERATION);

    }

    ...

    bool SensorDevice::connectHidlService() {

        HalConnectionStatus status = connectHidlServiceV2_1();  // 链接hal 2.1

        if (status == HalConnectionStatus::DOES_NOT_EXIST) {

            status = connectHidlServiceV2_0(); // 链接 hal 2.0

        }


        if (status == HalConnectionStatus::DOES_NOT_EXIST) {

            status = connectHidlServiceV1_0(); // 链接 hal 1.0

        }

        return (status == HalConnectionStatus::CONNECTED);

    }

    SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV1_0() {

        // SensorDevice will wait for HAL service to start if HAL is declared in device manifest.

        size_t retry = 10;

        HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;


        while (retry-- > 0) {

            sp<V1_0::ISensors> sensors = V1_0::ISensors::getService();

            if (sensors == nullptr) {

                // no sensor hidl service found

                connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;

                break;

            }


            mSensors = new ISensorsWrapperV1_0(sensors);

            mRestartWaiter->reset();

            // Poke ISensor service. If it has lingering connection from previous generation of

            // system server, it will kill itself. There is no intention to handle the poll result,

            // which will be done since the size is 0.

            if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {

                // ok to continue

                connectionStatus = HalConnectionStatus::CONNECTED;

                break;

            }


            // hidl service is restarting, pointer is invalid.

            mSensors = nullptr;

            connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;

            ALOGI("%s unsuccessful, remaining retry %zu.", __FUNCTION__, retry);

            mRestartWaiter->wait();

        }


        return connectionStatus;

    }


    SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_0() {

        HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;

        sp<V2_0::ISensors> sensors = V2_0::ISensors::getService();


        if (sensors == nullptr) {

            connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;

        } else {

            mSensors = new ISensorsWrapperV2_0(sensors);

            connectionStatus = initializeHidlServiceV2_X();

        }


        return connectionStatus;

    }


    SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_1() {

        HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;

        sp<V2_1::ISensors> sensors = V2_1::ISensors::getService();


        if (sensors == nullptr) {

            connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;

        } else {

            mSensors = new ISensorsWrapperV2_1(sensors);

            connectionStatus = initializeHidlServiceV2_X();

        }


        return connectionStatus;

    }

    ...

ISensors::getService()是hal层service 的获取方式,对应的是hardware/interfaces/sensors/…/ISensors.hal ,ISensors.hal编译之后会生成ISensors.h,服务端只要实现这个头文件的方法即可
四、hal层

hal层代码接口位于:hardware/interfaces/sensors/

文件夹下定义了多个版本的hal层结构,1.0、2.0、2.1还有公共接口,一般使用最新的2.1或者2.0,文件结构如下
在这里插入图片描述

.hal 文件是接口文件,在编译过程中会被编译成.h,需要使用的就导入.h文件,然后ISensors::getService()获取service服务实例即可,multihal里面是关于多hal的实现,vts里面是vs测试相关,

hal层和framework层通信使用的是HIDL,和AIDL类似,底层使用的都是binder机制实现跨进程通信

ISensorsCallback.hal 里面定义的动态sensor的回调接口

ISensors.hal 里面定义的是服务的主要功能接口

types.hal 里面定义的是一些要用到的结构类型

4.1 hal的实现

真正实现hal转接口的代码是在:vendor/mediatek/proprietary/hardware/sensor 这个文件夹中也会有多个版本的代码根据android.mk 进行选择,由宏控制,选择相应的版本,当前选择最新2.0进行讨论

 LOCAL_PATH := $(call my-dir)


    ifdef MTK_GENERIC_HAL

    ## layer decoupling 2.0 chips will go to this branch

    ## DO NOT USE platform macro in this branch


    include $(call all-named-subdir-makefiles, hidl 2.0)

    else

    ifeq ($(strip $(MTK_SENSOR_ARCHITECTURE)),1.0)

    include $(call all-named-subdir-makefiles,hidl sensors-1.0)

    else ifeq ($(MTK_SENSORS_1_0),yes)

    include $(call all-named-subdir-makefiles,hidl sensors-1.0)

    else

    include $(call all-named-subdir-makefiles,hidl $(strip $(MTK_SENSOR_ARCHITECTURE)))

    endif

    endif

与framework层通信的代码在hidl文件夹下有多个文件夹:
在这里插入图片描述

1.0/2.0 表示版本号,配置hal哪个版本就哪个

multihal:多hal,需要单独配置由/vendor/etc/sensors/hals.conf这个文件控制

Sensors.h 中可以看出Sensors 继承了ISensors并实现了这些方法

    Sensors::Sensors()

        : mInitCheck(NO_INIT),

          mSensorModule(nullptr),

          mSensorDevice(nullptr),

          mEventQueueFlag(nullptr),

          mOutstandingWakeUpEvents(0),

          mReadWakeLockQueueRun(false),

          mAutoReleaseWakeLockTime(0),

          mHasWakeLock(false),

          mRunThreadEnable(false),

          mEventQueueInitialized(false) {

        status_t err = OK;

        if (UseMultiHal()) {

            mSensorModule = ::get_multi_hal_module_info();

        } else {

            err = hw_get_module(

                SENSORS_HARDWARE_MODULE_ID,

                (hw_module_t const **)&mSensorModule); //获取SENSORS_HARDWARE_MODULE_ID的模块

        }

        if (mSensorModule == NULL) {

            err = UNKNOWN_ERROR;

        }


        if (err != OK) {

            LOG(ERROR) << "Couldn't load "

                       << SENSORS_HARDWARE_MODULE_ID

                       << " module ("

                       << strerror(-err)

                       << ")";


            mInitCheck = err;

            return;

        }


        err = sensors_open_1(&mSensorModule->common, &mSensorDevice);        // 加载模块中的sensors_module_methods,并赋值给mSensorDevice


        if (err != OK) {

            LOG(ERROR) << "Couldn't open device for module "

                       << SENSORS_HARDWARE_MODULE_ID

                       << " ("

                       << strerror(-err)

                       << ")";


            mInitCheck = err;

            return;

       ...

    }

从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
五、kernel 层

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

5.1 kernel-4.14/drivers/misc/mediatek/

首先这个文件夹下会包含很多驱动,其中有两个包sensor和sensors-1.0,这两个下面的内容其实是类似的,根据宏CONFIG_MTK_SENSOR_ARCHITECTURE会确定我们要的代码sensor中

 Android.mk


    ...

     88 ifeq ($(subst ",,$(CONFIG_MTK_SENSOR_ARCHITECTURE)),1.0)

     89 obj-y += sensors-1.0/

     90 else

     91 obj-y += sensor/

     92 endif

    ...

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相关。

5.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

5.4 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会传递信息

这两个地方有什么区别,目前暂未搞清楚,但是可以肯定的是sensor核心代码就在这两个地方了,CHRE 和 SCP侧,整个框架信息传递如下图:
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值