在 MTK timer 小结 2 说道最常用的MMI timer 实现机制的初始化过程。今天继续忘下说,下面要说的是创建一个timer。MMI 层,启动一个timer,最终都会调用到 L4StartTimer 这个函数。具体来分析一下这个函数
// 参数 nTimerId 是要自己定义一个timer id,在 timerEvents.h 里的 MMI_TIMER_IDS 定义,用来区分timer // TimerExpiry 超时的回调函数 // funcArg 回调函数回传的参数 // nTimeDuration 超时时间,单位ms // alignment alignment或者non-alignment,在 MTK timer 小结 2 说明过 static void L4StartTimer( unsigned short nTimerId, oslTimerFuncPtr TimerExpiry, void *funcArg, unsigned long nTimeDuration, unsigned char alignment) { /*----------------------------------------------------------------*/ /* Local Variables */ /*----------------------------------------------------------------*/ TIMERTABLE *pTable = NULL; U16 i = 0; U32 temp; /*----------------------------------------------------------------*/ /* Code Body */ /*----------------------------------------------------------------*/ if (TimerExpiry == NULL) { /* If TimerExpiry is NULL, we don't start the timer */ MMI_ASSERT(0); return; } MMI_ASSERT(nTimerId < MAX_TIMERS); if (L4TimerUsePreciseTick(nTimerId)) { alignment = TIMER_IS_NO_ALIGNMENT; } // 把 ticks 转化为ms // mtk 底层的timer 的单位都是 ticks if (nTimeDuration == 1000) { temp = KAL_TICKS_1_SEC - 4; } else { temp = (U32)((nTimeDuration / 5) * MMI_TICKS_5_MSEC); } if (temp == 0) { /* Cause by by rounding. If expire immediately, MoDIS boot-up failure because MMI keeps running and block NVRAM task */ temp = (U32)MMI_TICKS_5_MSEC; } //取得存放所有MMI timer 的table // 在 MTK timer 小结 2 中提到,这个table 是 list + array 组合 // 如果只用存数组的话,初始化大小不好确定,而且要 array relloc. 如果考虑内存回收还容易引起数组震荡 // 如果只用list的话,一个timer如果很频繁的话,会不停的malloc 和 free pTable = &g_timer_table; //判断timer 是否已经 满了 // 如果满了,那么需要在timer table 后面增加一个timer node if (g_timer_table_used >= g_timer_table_size) { do { if (pTable->next == NULL) { pTable->next = OslMalloc(sizeof(TIMERTABLE)); memset(pTable->next, 0, sizeof(TIMERTABLE)); g_timer_table_size += SIMULTANEOUS_TIMER_NUM; pTable = pTable->next; i = 0; break; } pTable = pTable->next; } while (pTable != NULL); } else { //寻找空的 timer node i = 0; do { if (pTable->tm[i].event_id == NULL) { /* find the empty space */ break; } i++; if (i >= SIMULTANEOUS_TIMER_NUM) { pTable = pTable->next; i = 0; } } while (pTable != NULL); if (pTable == NULL) { /* Can't find the empty space in TIMERTABLE list, assert!!! */ MMI_ASSERT(0); } } /* if (g_timer_table_used >= g_timer_table_size) */ // 根据 algigment 属性,分别创建一个 event scheduler timer // 把 timer 的信息保存到 timer node 里面 if (alignment == TIMER_IS_NO_ALIGNMENT) { /* MSB(Most Significant Bit) is align_timer_mask */ pTable->tm[i].timer_info = nTimerId | NO_ALIGNMENT_TIMER_MASK; pTable->tm[i].event_id = evshed_set_event( event_scheduler1_ptr, (kal_timer_func_ptr) L4CallBackTimer, (void*)&(pTable->tm[i]), temp); pTable->tm[i].arg = funcArg; pTable->tm[i].callback_func = TimerExpiry; g_timer_table_used++; } else if (alignment == TIMER_IS_ALIGNMENT) { /* MSB(Most Significant Bit) is align_timer_mask */ pTable->tm[i].timer_info = nTimerId | ALIGNMENT_TIMER_MASK; pTable->tm[i].event_id = evshed_set_event( event_scheduler2_ptr, (kal_timer_func_ptr) L4CallBackTimer, (void*)&(pTable->tm[i]), temp); pTable->tm[i].arg = funcArg; pTable->tm[i].callback_func = TimerExpiry; g_timer_table_used++; } }
再说一句 timer table,其实也可以用 timer pool 来实现,把超时或者stop的timer node,放入到free pool 中,需要时再拿出来,也可以避免重复的malloc 和 free。
在 设置 event scheduler timer 中,设置了一个回调函数 L4CallBackTimer, 这个回调函数就是在 stack timer 超时需要回调的函数。
那具体再哪里回调?在 MTK timer 小结 2 提到, event scheduler 还是依赖与 stack timer,在 MTK timer 小结 1 提到 stack timer 超时后是向相应的mod 发送消息。
在初始化L4InitTimer函数中,stack timer 初始化(stack_init_timer(&base_timer1, "MMI_Base_Timer1", MOD_MMI))时mod 是 MOD_MMI,那么超时后,就会像 mmi task 发送超时消息。
MMI 层 在 MMI_task 函数中处理所有的消息,while 消息处理中,可以看到
case MSG_ID_TIMER_EXPIRY: { kal_uint16 msg_len; EvshedMMITimerHandler(get_local_para_ptr(Message.oslDataPtr, &msg_len)); } break;
这个就是处理 stack timer 地方。也就是stack timer 超时后,回回调 EvshedMMITimerHandler 函数。
void EvshedMMITimerHandler(void *dataPtr) { /*----------------------------------------------------------------*/ /* Local Variables */ /*----------------------------------------------------------------*/ stack_timer_struct *stack_timer_ptr; stack_timer_ptr = (stack_timer_struct*) dataPtr; /*----------------------------------------------------------------*/ /* Code Body */ /*----------------------------------------------------------------*/ // 判断是哪个 stack timer if (stack_timer_ptr == &base_timer1) { // 这里需要判断 这 stack timer 是否还是有效,也就是否被stop // 这种情况出现环境在 MTK timer 小结 1 中介绍过 // 如果无效,就不要触发这个timer if (stack_is_time_out_valid(&base_timer1)) { //调用 这个函数,处罚注册的 event scheduler timer evshed_timer_handler(event_scheduler1_ptr); } stack_process_time_out(&base_timer1); } else if (stack_timer_ptr == &base_timer2) { if (stack_is_time_out_valid(&base_timer2)) { evshed_timer_handler(event_scheduler2_ptr); } stack_process_time_out(&base_timer2); } }
这个函数在 调用 evshed_timer_handler 函数是,就会回调 由 evshed_set_event 注册的timer 回调函数 L4CallBackTimer。