相关文章点击->【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添加进一个双向链表中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32//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函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14//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的事件处理函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37/* 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函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14//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硬件,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30/* 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的声明注册如下:
1
2INTERNAL_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],方便后续调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31//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()是进行事件转换的宏。
1
2
3
4
5
6
7
8
9
10
11
12
13static 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函数来执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43//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事件进入事件队列,
1
2
3
4
5
6
7void 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事件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19static 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()
1
2
3
4
5
6
7
8//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结构>**有提到)。之前被注册的结构体如下:
1
2
3
4
5
6
7
8
9
10
11
12
13static 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事件队列中
1
2
3
4#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接收到该事件后,处理函数进入如下条件执行代码:
1
2else 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内容请搜索靠谱客的其他文章。
发表评论 取消回复