概述
相关文章点击->【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],方便后续调用。
//vendormediatekproprietarytinysysscpmiddlewarecontexthubMEMS_Driveralspsalsps.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%xn",sensorGetMyEventType(SENS_TYPE_ALS));
osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), sample.vptr, NULL);
}
return 0;
}
2.3 APP事件监听
所有APP的加载是从osMain()函数开始,osMainDequeueLoop()函数中evtQueueDequeue()从事件对列中取出事件,然后把事件和订阅事件列表中的事件进行匹配,订阅了该事件的任务都会收到该事件,事件的执行是通过osTaskHandle()函数最终调用到对应APP的handleEvent函数来执行。
//vendormediatekproprietaryhardwarecontexthubfirmwaresrcseos.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已满。
-----欢迎点赞,收藏和关注------
最后
以上就是淡定薯片为你收集整理的【MTK sensor2.0】【SCP】数据与控制信息传递简析的全部内容,希望文章能够帮你解决【MTK sensor2.0】【SCP】数据与控制信息传递简析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复