【MTK sensor2.0】【SCP】数据与控制信息传递简析

相关文章点击->【Sensor2.0】SCP与AP数据流分析

-----欢迎点赞,收藏和关注------

以stk3a5x光距感为例

须知:SCP的每个Sensor驱动(如:stk3a5x)的操作会封装为一个Module,通过MODULE_DECLARE()宏声明。而一类Sensor(如:光距感alsps)的操作又会封装为一个APP,通过INTERNAL_APP_INIT()来声明。SCP中的APP有很多,除了每类sensor的APP还有各类事件处理的APP。

1. Module内部的事件传递

1.1 sensor事件接收队列注册

sensor初始化时,会填充struct broadcast_receiver结构体来保存自身的类型sensor_type和事件接收函数receive_event,再通过sensor_broadcast_receiver_register函数,把broadcast_receiver添加进一个双向链表中:

//stk3a5.c
static int init_stk3a5x(void)
{
......
    stk3a5x_dev.bc_recv[ALS].sensor_type = SENS_TYPE_ALS;
    stk3a5x_dev.bc_recv[ALS].receive_event = stk3a5x_receive_event;
    sensor_broadcast_receiver_register(deputy, &stk3a5x_dev.bc_recv[ALS]);
    stk3a5x_dev.bc_recv[PS].sensor_type = SENS_TYPE_PROX;
    stk3a5x_dev.bc_recv[PS].receive_event = stk3a5x_receive_event;
    sensor_broadcast_receiver_register(deputy, &stk3a5x_dev.bc_recv[PS]);
#ifdef STK_TUNE0
    stk3a5x_dev.bc_recv[AUTOK].sensor_type = SENS_TYPE_PROX;
    stk3a5x_dev.bc_recv[AUTOK].receive_event = stk3a5x_receive_event;
    sensor_broadcast_receiver_register(deputy, &stk3a5x_dev.bc_recv[AUTOK]);
#endif
    return 0;
......
}
//sensor_broadcast.c
void sensor_broadcast_receiver_register(uint8_t task,
        struct broadcast_receiver *br)
{
......
    bt = &bc_task[task];
    xSemaphoreTake(bt->mutex, portMAX_DELAY);
    if (!check_receiver_repeat(&bt->br_head, br->sensor_type)) {
        list_init(&br->list);
        list_add_tail(&bt->br_head, &br->list);//添加sensor到事件接收链表中
    }
    xSemaphoreGive(bt->mutex);
}

接收事件注册完,驱动初始化就算完成了。等待事件的到来。

1.2 sensor事件消息队列

sensor驱动发送事件消息是通过sensor_broadcast_event函数实现的,而sensor_broadcast_event的实现为_sensor_broadcast_event函数:

//sensor_broadcast_event(principal, bm, SENS_TYPE_ALS, EVENT_DATA, sizeof(*send)); 发送数据事件消息
int _sensor_broadcast_event(uint8_t task, struct broadcast_message *bm,
        uint8_t sensor_type, uint8_t command, uint32_t size)
{
......

    flags = spinlock_lock_irqsave(&bt->lock);
    list_add_tail(&bt->bm_head, &bm->list);//把事件消息添加进链表
    spinlock_unlock_irqrestore(&bt->lock, flags);
.......
    broadcast_wakeup(task);
    return 0;
}

事件消息被添加后,等待事件消息被取出执行。

1.3 sensor事件监听

所有sensor模块的初始化都在deputy_task_run函数进行。sensor都初始化完后,开始循环监听事件消息,当监听到有消息时,根据消息的内容,调用对应sensor的事件处理函数:

/*
deputy_task_run() --> sensor_receive_event() --> receive_event() --> dispatch_to() --> receive_event()
*/
static int receive_event(uint8_t task)
{
......
    bt = &bc_task[task];
    flags = spinlock_lock_irqsave(&bt->lock);
    while (!list_is_empty(&bt->bm_head)) {//是否有消息产生
        bm = container_of(bt->bm_head.next,
            struct broadcast_message, list);
        list_delete(&bm->list);
        spinlock_unlock_irqrestore(&bt->lock, flags);
        start = rtcGetTime();
        dispatch_to(bt, bm->sensor_type, bm->command, bm->buffer, bm->size);//事件调度
......
}

static void dispatch_to(struct broadcast_task *bt, uint8_t sensor_type,
        uint8_t command, uint8_t *data, int size)
{
......
    xSemaphoreTake(bt->mutex, portMAX_DELAY);
    list_iterate(&bt->br_head, curr, tmp) {//遍历链表
        br = container_of(curr, struct broadcast_receiver, list);
        if (br->sensor_type == sensor_type) {//定位到sensor类型
            br_exist = true;
            if (br->private && br->receive)
                br->receive(br->private, sensor_type, command, data, size);
            else if (br->receive_event)
                br->receive_event(sensor_type, command, data, size);//将消息事件传递给事件处理函数
            break;
        }
    }
......
}

1.4 一个简单的实例

光距感APP收到一个事件EVT_APP_START,并开始调用它的handleEvent函数

//alsps.c
static void handleEvent(uint32_t evtType, const void* evtData)
{
    struct broadcast_message *bm = NULL;

    switch (evtType) {
        case EVT_APP_START: {
            bm = sensor_prepare_event();
            sensor_broadcast_event(deputy, bm, SENS_TYPE_ALS, EVENT_INIT_START, 0);
           ......
        }
    }
}

这里广播了一个sensor类型为SENS_TYPE_ALS的EVENT_INIT_START事件,这个事件被stk3a5x_receive_event()函数接收到,根据事件信息最后调用stk3a5x_als_init_start()函数来初始化stk3a5x硬件,

/*
stk3a5.c:
    stk3a5x_dev.bc_recv[ALS].sensor_type = SENS_TYPE_ALS;
    stk3a5x_dev.bc_recv[ALS].receive_event = stk3a5x_receive_event;
*/
static void stk3a5x_receive_event(uint8_t sensor_type, uint8_t command,
                                  const uint8_t *data, int size)
{
    switch (sensor_type)//判断sensor类型
    {
        case SENS_TYPE_ALS:
            stk3a5x_als_receive_event(command, data, size);
            break;
......
    }
}

static void stk3a5x_als_receive_event(uint8_t command,
                                      const uint8_t *data, int size)
{
    switch (command)//判断事件
    {
......
        case EVENT_INIT_START:
            stk3a5x_als_init_start(data, size);
            break;
......
    }
}

最后又广播了一个事件EVENT_INIT_DONE,被光距感APP接收,最终调用alsInitDone()函数处理。

2. APP的消息传递

APP的消息传递有:各APP之间和APP与sensor驱动之间。

2.1 APP的结构

光距感APP的声明注册如下:

INTERNAL_APP_INIT(APP_ID_MAKE(APP_ID_VENDOR_MTK, MTK_APP_ID_WRAP(SENS_TYPE_ALS, 0, 0)), 0, startTask, endTask, handleEvent);

在SCP启动后,会调用所有APP的startTask函数来初始化APP,其中会订阅一个EVT_APP_START事件,这个操作实际上是发出一个订阅事件。handleEvent函数用来处理接收事件并把事件转换后广播给sensor驱动。

在startTask函数中,可以看到光距感APP主要是为了和驱动传递消息的,分装了一系列的事件处理函数。sensorRegister()函数是把一系列的sensor操作函数添加进全局结构体中struct Sensor mSensors[MAX_REGISTERED_SENSORS],方便后续调用。

//vendor\mediatek\proprietary\tinysys\scp\middleware\contexthub\MEMS_Driver\alsps\alsps.c
static bool startTask(uint32_t taskId)
{
......
    mTask.bcRecv[ALS].sensor_type = SENS_TYPE_ALS;
    mTask.bcRecv[ALS].receive_event = alsPsReceiveEvent;//事件处理函数
    sensor_broadcast_receiver_register(principal, &mTask.bcRecv[ALS]);
......

    mTask.handle[ALS] =
        sensorRegister(&mSensorInfo[ALS], &mSensorOps[ALS], NULL, false);
......

    osEventSubscribe(mTask.id, EVT_APP_START);
......
    return true;
}

static void handleEvent(uint32_t evtType, const void* evtData)
{
......
    switch (evtType) {
        case EVT_APP_START: {
            bm = sensor_prepare_event();
            sensor_broadcast_event(deputy, bm, SENS_TYPE_ALS, EVENT_INIT_START, 0);
......
            break;
        }
    }
}

2.2 APP添加事件到队列

假设现在驱动stk3a5x数据采样完成,广播了EVENT_DATA事件,被光距感APP接收到,最终在alsRecvData()函数中处理。函数中osEnqueueEvt()就是添加APP事件到事件队列的函数,它最终会调用evtQueueEnqueue()函数把事件加入队列。sensorGetMyEventType()是进行事件转换的宏。

static int alsRecvData(const uint8_t *data, int size)
{
    union EmbeddedDataPoint sample;
    struct data_param *recv = (struct data_param*)data;

    if (mTask.alsLastSample != recv->values[0]) {
        sample.fdata = mTask.alsLastSample = recv->values[0];
        osLog(LOG_INFO, "24778451 11 alsRecvData evtType=0x%x\n",sensorGetMyEventType(SENS_TYPE_ALS));
        osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), sample.vptr, NULL);
    }
    return 0;
}

2.3 APP事件监听

所有APP的加载是从osMain()函数开始,osMainDequeueLoop()函数中evtQueueDequeue()从事件对列中取出事件,然后把事件和订阅事件列表中的事件进行匹配,订阅了该事件的任务都会收到该事件,事件的执行是通过osTaskHandle()函数最终调用到对应APP的handleEvent函数来执行。

//vendor\mediatek\proprietary\hardware\contexthub\firmware\src\seos.c
void __attribute__((noreturn)) osMain(void)
{
    osMainInit();

    while (true)
    {
        osMainDequeueLoop();
    }
}

void osMainDequeueLoop(void)
{
......
    /* get an event */
    if (!evtQueueDequeue(mEvtsInternal, &evtType, &evtData, &evtFreeingInfo, true))
        return;
    evtType = EVENT_GET_EVENT(evtType);
    tid = EVENT_GET_ORIGIN(evtType);
    task = osTaskFindByTid(tid);
    if (task)
        osTaskAddIoCount(task, -1);

    /* by default we free them when we're done with them */
    mCurEvtEventFreeingInfo = &evtFreeingInfo;

    if (evtType < EVT_NO_FIRST_USER_EVENT) {
        /* handle deferred actions and other reserved events here */
        osInternalEvtHandle(evtType, evtData);
    } else {
        /* send this event to all tasks who want it */
        for_each_task(&mTasks, task) {
            for (j = 0; j < task->subbedEvtCount; j++) {
                if (task->subbedEvents[j] == evtType) {
                    osTaskHandle(task, evtType, evtData);
                    break;
                }
            }
        }
    }
......
}

2.4 两个重要的实例

实例一

在osMainInit()函数中,添加了EVT_APP_START事件进入事件队列,

void osMainInit(void)
{
......
    //broadcast app start to all already-loaded apps
    (void)osEnqueueEvt(EVT_APP_START, NULL, NULL);
}

而之前光距感APP订阅了该事件,因此最终会调用到光距感APP的handleEvent()事件处理函数。那么就回到了**<1.4 一个简单的实例>**小节的消息传递流程。

实例二

假设kernel通过IPI向SCP发送了光感使能命令,其中事件为EVT_NO_SENSOR_CONFIG_EVEN,命令为CONFIG_CMD_ENABLE。

在hostIntf.c文件中声明了一个APP,这里称它为hostIntf APP。此APP为SCP的核心APP,核心事件基本都在这里处理。

在SCP初始化时,发出的EVT_APP_START事件,也被hostIntf APP接收,并在此事件中订阅了一系列事件,其中就包括EVT_NO_SENSOR_CONFIG_EVEN事件。

static void hostIntfHandleEvent(uint32_t evtType, const void* evtData)
{
    ......
    if (evtType == EVT_APP_START) {
        ...
        	osEventSubscribe(mHostIntfTid, EVT_NO_SENSOR_CONFIG_EVENT);
        ...
    }
    ......
    else if (evtType == EVT_NO_SENSOR_CONFIG_EVENT) {
        ...
            else if (cmd->cmd == CONFIG_CMD_ENABLE) {
                        if (sensorRequestRateChange(mHostIntfTid, sensor->sensorHandle, cmd->rate, cmd->latency)) {...}
            }
        ...
    }
    ......
}

hostIntf APP收到EVT_NO_SENSOR_CONFIG_EVEN事件,并根据cmd信息开始进行处理,通过sensorRequestRateChange()函数调用到宏函数INVOKE_AS_OWNER_AND_RETURN()

//sensorRequestRateChange() -> sensorReconfig() -> sensorCallFuncSetRate()
static bool sensorCallFuncSetRate(struct Sensor* s, uint32_t rate, uint64_t latency)
{
    if (IS_LOCAL_APP(s)) {
        INVOKE_AS_OWNER_AND_RETURN(LOCAL_APP_OPS(s)->sensorSetRate, rate, latency, s->callData);
    } else {...}
}

这里的LOCAL_APP_OPS(s)->sensorSetRate实际是调用之前注册好的sensor操作函数(**<2.1 APP结构>**有提到)。之前被注册的结构体如下:

static const struct SensorOps mSensorOps[MAX_HANDLE] = {
    {
        .sensorPower = sensorPowerAls,
        .sensorFirmwareUpload = sensorFirmwareAls,
        .sensorSetRate = sensorRateAls,
        .sensorFlush = sensorFlushAls,
        .sensorSendOneDirectEvt = sendLastSampleAls,
        .sensorCalibrate = sensorCaliAls,
        .sensorCfgData = sensorCfgAls,
    },
......
};

通过调用sensorRateAls()函数向光距感驱动广播了一个EVENT_BATCH事件,事件被驱动接收处理,开启了一个定时器,每隔一段时间触发定时器来广播光感数据采样事件EVENT_SAMPLE。光感把抓取完数据向光距感APP广播EVENT_DATA事件,光距感APP接到事件并把事件加入到APP事件队列中

#define sensorGetMyEventType(_sensorType) (EVT_NO_FIRST_SENSOR_EVENT + (_sensorType))
//如下是光距感APP把EVENT_DATA事件处理完并加入APP事件队列的语句
osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), sample.vptr, NULL);

这里使用sensorGetMyEventType()宏获取对应的事件,而所有sensor类型通过sensorGetMyEventType获取的事件都已被hostIntf APP订阅(具体查看hostIntf.c文件中initSensors()函数)。hostIntf APP接收到该事件后,处理函数进入如下条件执行代码:

else if (evtType > EVT_NO_FIRST_SENSOR_EVENT && evtType < EVT_NO_SENSOR_CONFIG_EVENT && mSensorList[(evtType & 0xFF)-1] < MAX_REGISTERED_SENSORS){...}

处理函数最终会调用simpleQueueEnqueue()函数。simpleQueueEnqueue()有两个定义,一个定义在simpleQ.c文件,另一个定义在mtkQ.c文件,hostIntf APP处理函数调用的是mtkQ.c文件中定义的。simpleQueueEnqueue()函数调用contexthub_fw.c文件中的SensorQueueEnqueue()函数 ,继续追踪可以看到采样的数据会被先写入到SRAM,SRAM满了后全部写入到共享DRAM中,并通知AP FIFO已满。

-----欢迎点赞,收藏和关注------

  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值