一、总体结构
1. Software Architecture
MediaTek Inc . (MTK)
2. MMI Architecture
MTK 平台采用的是Pixtel Communications,Inc. 的PIXTEL MMI (现已被MTK 收购,作
为MTK 印度研发中心) 。MMI 的基本工作方式是消息驱动,整个MMI 就是一个任务,不
停的从消息队列中取出消息进行处理。
MMI 只与L4 层打交道, 两者通过消息队列交互。L4 层提供了所有对底层(PS 和驱动)
的封装。MMI 目录位于plutommi\ 。
Framework 是MMI 的基本功能部分, 提供消息管理(Event Handler )、窗口管理(History
Manager)等基本功能, 驱动整个MMI 的运作。Framework 目录位于plutommi\mmi\Framework\
二、消息分发
消息(事件) ID 定义文件:
adaptation\include\stack_msgs.h 定义了所有消息类型typedef enum { ⋯ }msg_type;
interface\mmi\mmi_sap.h interface\ps\cc_sap.h sms_sap.h ⋯⋯
plutommi\mmi\AsyncEvents\AsyncEventsInc\ProtocolEvents.h 再取别名
事件处理实现文件:
plutommi\mmi\Framework\EventHandling\EventsSrc\Events.c
事件功能初始化:
InitEvents()
InitFramework()
InitializeAll()
MMI_task()
任务消息结构:
adaptation\include\app_ltlcom.h
typedef struct ilm_struct {
module_type src_mod_id;
module_type dest_mod_id;
sap_type sap_id;
msg_type msg_id;
local_para_struct *local_para_ptr;
peer_buff_struct *peer_buff_ptr;
} ilm_struct;
#define oslSrcId src_mod_id
#define oslDestId dest_mod_id
#define oslSapId sap_id
#define oslMsgId msg_id
#define oslDataPtr local_para_ptr
#define oslPeerBuffPtr peer_buff_ptr
在由SetProtocolEventHandler() 设置的消息回调函数中传入的只是local_para_struct
*local_para_ptr 部分,具体定义参见文件:
ps\l4\include\mmi_msg_struct.h
从MMI 消息队列中读消息:
U8 OslReadCircularQ(void *msgPtr)
#define OslReceiveMsgExtQ receive_msg_ext_q
kal_status receive_msg_ext_q ( kal_msgqid task_ext_qid, ilm_struct *ilm_ptr )
向L4C 消息队列中发送消息:
#define OslMsgSendExtQueue OslIntMsgSendExtQueue
void OslIntMsgSendExtQueue ( MYQUEUE *Message )
Framework 提供给上层的事件可分为四类:
1. PS 事件
2. Keypad 事件
3. Highlight 事件
4. Timer 事件
PS 事件
PS 事件用于底层和MMI 间的通讯,实际上也只有PS 事件是事件ID 和处理函数一一
对应的。MMI 任务从消息队列中取出一条消息,并根据消息分发表中已注册的回调函数执
行此消息处理函数。对上层用户而言执行消息处理函数就称之为响应事件。消息分发表是个
PseventInfo{U16 eventID; PsFuncPtr entryFuncPtr;} 类型的一维数组static PseventInfo
protocolEventHandler[MAX_PROTOCOL_EVENT]; Framework 在设置事件时将在表中找到
一个空项填入eventID 和entryFuncPtr ;而在执行事件时将根据eventID 在表中查找对应ID
然后取出回调函数执行。
Framework 在处理事件时有一个简单的算法, 使得一个事件每被处理一次其位置就被向
前移动一项一直到数组的最顶端为止, 这样做的意义是可以保证最频繁处理的事件始终在数
组的最前端,有利于提高查找速度。
紧接着Framework 将先处理中断事件,在中断事件表中查找匹配的事件,如果有则执
行,如果不存在或未处理则根椐消息分发表中的匹配项执行。在处理完中断事件和普通事件
后还会执行中断事件中的后端处理回调函数。中断事件用于需要优先处理的事件, 一般是硬
件状态通知如GPIO 事件MSG_ID_MMI_EQ_GPIO_DETECT_IND 、电池状态事件
PRT_BATTERY_STATUS_IND 等。
中断事件表类型PsInteventInfo :
{U16 eventID; PsIntFuncPtr entryIntFuncPtr; PsIntFuncPtr postIntFuncPtr;}
中断事件表定义:
static PsInteventInfo interruptEventHandler[MAX_INTERRUPT_EVENT];
Framework 对驱动层的控制是通过向L4C 发送相应消息,由L4C 调用最终的驱动函数
来实现的。如设置GPIO , 将调用函数OslMsgSendExtQueue() 向L4C 发送消息
MSG_ID_MMI_EQ_EXE_GPIO_LEVEL_REQ 。具体实现请参见如下文件:
plutommi\mmi\gpio\gpioInc\gpioInc.h
plutommi\mmi\gpio\gpioSrc\GeneralDeviceInterface.c
Framework 提供如下API 给应用处理PS 事件:
- Set protocol event handler.
void SetProtocolEventHandler(PsFuncPtr funcPtr, U16 eventID)
- Execute Current protocol event handler.
输入参数分别对应消息结构中: msg_id, local_para_ptr, src_mod_id, ilm_struct*
void ProtocolEventHandler(U16 eventID, void *MsgStruct, int mod_src, void *peerBuf)
void ExecuteCurrProtocolHandler(U16 eventID, void *MsgStruct, int mod_src, void *peerBuf)
- Clear handler for specific protocol event.
void ClearProtocolEventHandler(U16 eventID)
- Clear all protocol event handlers.
void ClearAllProtocolEventHandler(void)
- Set interrupt event handler
void SetInterruptEventHandler(PsIntFuncPtr funcPtr, PsIntFuncPtr postfuncPtr, U16 eventID)
- Set interrupt event group handler
void SetGroupInterruptEventHandler(PsIntFuncPtr funcPtr, PsIntFuncPtr postfuncPtr, PU16
eventsID, U8 len)
一些重要的事件:
注册键盘处理消息
SetProtocolEventHandler (L4KeyHandle, MSG_ID_MMI_EQ_KEYPAD_DETECT_IND );
#define L4KeyHandle mmi_frm_key_handle
extern void mmi_frm_key_handle(void *paraBuff);
注册来电消息:
SetProtocolEventHandler (PsCBackCallIncoming, PRT_INCOMINGCALL_EVENT);
#define PRT_INCOMINGCALL_EVENT MSG_ID_MMI_CC_CALL_RING_IND
注册接收新SMS 消息:
SetProtocolEventHandler(mmi_frm_sms_new_sms_ind,PRT_MSG_ID_MMI_SMS_DELIVER_
MSG_IND);
#define PRT_MSG_ID_MMI_SMS_DELIVER_MSG_IND
MSG_ID_MMI_SMS_DELIVER_MSG_IND
注册响闹消息:
SetProtocolEventHandler (AlmExpiryHandler, MSG_ID_MMI_EQ_ALARM_IND);
Keypad 事件
所有Keypad 事件是通过统一响应一个ID 号为MSG_ID_MMI_EQ_KEYPAD_DETECT_IND
的PS 事件来处理的:
SetProtocolEventHandler (L4KeyHandle, MSG_ID_MMI_EQ_KEYPAD_DETECT_IND);
#define L4KeyHandle mmi_frm_key_handle
收到键盘PS 事件,在将按其转换成Keypad 事件前, Framework 还要调用KeyBrd.c 文件中
的函数执行一系列的处理, 包括调用背光管理、键盘锁等功能。最后由函数KeyEventHandler()
提交键盘事件处理器ExecuteCurrKeyHandler() 。KeyEventHandler() 中作了
键盘事件处理器会根据传入的用户按键值和按键类型在键盘事件表中查找已注册的匹配项
并取出回调函数执行MMI 按键响应处理。
键盘事件表类型及定义:
typedef void (*FuncPtr) (void);
FuncPtr currKeyFuncPtrs[MAX_KEYS][MAX_KEY_TYPE];
typedef enum{
KEY_FULL_PRESS_DOWN,
KEY_EVENT_DOWN = KEY_FULL_PRESS_DOWN,
KEY_EVENT_UP,
KEY_LONG_PRESS,
KEY_REPEAT,
KEY_HALF_PRESS_DOWN,
KEY_HALF_PRESS_UP,
MAX_KEY_TYPE
} mmi_key_types_enum;
typedef enum{
KEY_0 = 0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9,
KEY_LSK, KEY_RSK, KEY_CSK,
KEY_UP_ARROW, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_RIGHT_ARROW,
KEY_SEND, KEY_END, KEY_CLEAR, KEY_STAR,
KEY_POUND, KEY_VOL_UP, KEY_VOL_DOWN,
KEY_QUICK_ACS,
KEY_CAMERA = KEY_QUICK_ACS,
KEY_ENTER,
KEY_WAP = KEY_ENTER,
KEY_IP = KEY_ENTER,
KEY_EXTRA_1, KEY_EXTRA_2,
KEY_PLAY_STOP, KEY_FWD, KEY_BACK,
TOUCH_KEY_1, TOUCH_KEY_2, TOUCH_KEY_3, TOUCH_KEY_4, TOUCH_KEY_5,
KEY_POWER, KEY_MP3, KEY_GAME,
MAX_KEYS, /* max key codes support */
KEY_INV ALID = 0xFE // 255 /* By JL, to sync with driver */
} mmi_keypads_enum;
键盘处理流程
//plutommi\mmi\Framework\Osl\OslSrc\KeyBrd.c
void mmi_frm_key_handle (void *paraBuff)
void mmi_frm_convert_process_key_event(U32 Keyevent, U16 KeyMapIndex)
void ProcessKeyEvent(U32 MsgType, U16 KeyMapIndex)
static void KeyEventHandler(KEYBRD_MESSAGE *eventKey) //在此函数中会调用函数
U8 mmi_kbd_app_key_hdlr(KEYBRD_MESSAGE *eventKey) 进行背光、键盘锁、按键音的处理。
plutommi\mmi\Framework\EventHandling\EventsSrc\Events.c
void ExecuteCurrKeyHandler(S16 keyCode, S16 keyType)
Framework 提供如下API 给应用处理Keypad 事件:
- Set key handler for particular key
void SetKeyHandler (FuncPtr funcPtr, U16 keyCode, U16 keyType)
- Set Key handler for group of keys
void SetGroupKeyHandler (FuncPtr funcPtr, PU16 keyCodes, U8 len, U16 keyType)
- Execute current key handler for key press event
void ExecuteCurrKeyHandler (S16 keyCode, S16 keyType)
- Clear key handlers for particular key
void ClearKeyHandler (U16 keyCode, U16 keyType)
- Clear key handlers for all keys
void ClearAllKeyHandler (void)
- Special handling for Power and End Key
void PowerAndEndKeyHandler (void)
Highlight 事件
Highlight events are internal events generated by framework when a particular menu item
is highlighted. Applications use these events to perform functions like hint display, mapping of
keys and changing labels of soft keys.
Highlight handler functions are registered as callback functions by the application for menu
items: RegisterHighlightHandler(ExecuteCurrHiliteHandler);
// plutommi\mmi\GUI\GUI_SRC\wgui_categories_util.c
在菜单项高亮时也能显示一个小提示窗口用于说明当前项的内容。
typedef struct _hiliteInfo{
FuncPtrShort hintFuncPtr;
FuncPtr entryFuncPtr;
} hiliteInfo;
hiliteInfo maxHiliteInfo[MAX_HILITE_HANDLER]; /* available hilite func ptrs */
U8 hintData[MAX_SUB_MENUS][MAX_SUB_MENU_HINT_SIZE];
Framework provides APIs for the following functions to the application for handling protocol
events:
- Set highlight handler.
void SetHiliteHandler(U16 itemid, FuncPtr hiliteFuncPtr)
- Execute current highlight handler.
void ExecuteCurrHiliteHandler(S32 hiliteid)
- Clear highlight handler.
void ClearHiliteHandler(U16 itemid)
- Execute item highlight handler.
void ExecuteItemHiliteHandler(U16 hiliteItemID)
void ConstructHintsList(U16 parentID, U8 **hintArray)
void SetHintHandler(U16 itemid, FuncPtrShort hintFuncPtr)
void ClearHintHandler(U16 itemid)
Timer Events
Events are used to notify starting or stopping a timer to the underlying layer. The notification
of a timer expiry also comes as an event. Framework provides wrappers for the following
functions:
- Start a timer
void StartTimer(U16 timerid, U32 delay, FuncPtr funcPtr)
- Stop a Timer
void StopTimer(U16 timerid)
On timer expiry, the framework makes an application function callback to a function, which is
given as an argument while starting the timer.
MMI Framework provides simple and well-defined interfaces for the applications written
on top. Applications have to register callback function with a timer id. The Application
callback handler is called when the timer expires.
The L4 allows MMI to support two kinds of timers:
1. Alignment timers – These timers are not very accurate. But they keep on running even
when the backlight of the LCD is turned off. All the applications use these timers .
2. Non-Alignment timers – These timers are very accurate. They do not run when the
backlight is turned off. All the UI components use Non Alignment timers .
This enum contains the list of all the timer ids in the MMI. To add a new timer in MMI,
the timer id should be added in this enum.
plutommi\mmi\Inc\TimerEvents.h
typedef enum{
/* Start for for Keypad based timer.*/
KEY_TIMER_ID_NONE = 0,
KEY_TIMER_ID0 = 1,
KEY_TIMER_ID1,
。。。。。。
UI_TIMER_ID_BASE,
UI_TIMER_ID_MAX = (UI_TIMER_ID_BASE + 10),
。。。。。。
MAX_TIMERS
} MMI_TIMER_IDS;
UI 定时器调用层次:
void InitializeAll()
void setup_UI_wrappers(void)
gui_start_timer = UI_start_timer;
void UI_start_timer(S32 count, void (*callback) (void))
void HW_StartTimer(U16 timerid, U32 delay, FuncPtr funcPtr)
StartMyTimerInt(timerid, delay, (oslTimerFuncPtr) funcPtr, TIMER_IS_NO_ALIGNMENT);
APP 定时器调用层次:
void StartTimer(U16 timerid, U32 delay, FuncPtr funcPtr)
#define StartMyTimer(nTimerId,nTimeDuration,TimerExpiryFunction)
StartMyTimerInt(nTimerId,nTimeDuration,TimerExpiryFunction, TIMER_IS_ALIGNMENT)
三、窗口管理
MTK APP只是一种功能上的称呼,它并不对应软件实体,即没有针对APP操
作的API。一个APP有若干个Category Screen ,Category Screen 是一系列预
先定义好的界面集合。每个Category Screen 又有若干个控件 (Widget) 。History
则用来进行Category Screen 间的切换。
大致的调用层次是: APP->Screen->Widget(WGUI->GUI)->GDI
相关文件:
History :plutommi\mmi\Framework\History\
Category Screen 和Widget:plutommi\mmi\GUI\GUI_SRC
History Screen ID 定义文件: plutommi\mmi\Inc\MMIDataType.h
History
在用户弹出新的屏幕前,需要使用History 保存当前屏幕信息(参见结构
historyNode );而在用户退出当前屏幕时就只是把History 中已保存的上个屏幕信
息取出用来重画屏幕。加入History 的一定是后台Screen,当前显示的Screen 不
会出现在History 中。Idle Screen 是History 中的第一个Screen,当只有Idle Screen
时, History 为空。
History 内部实现是使用堆栈方式, 保存屏幕执行堆栈PUSH 操作,返回上级
屏幕执行堆栈POP 操作。堆栈是用一维数组historyNode
historyData[MAX_HISTORY] 实现,有一个索引变量S16 currHistoryIndex 总是指
向栈顶。
/* history information stuct */
typedef struct _historyNode{
U16 scrnID;
FuncPtr entryFuncPtr;
U8 *inputBuffer; /* running text data */
U8 *guiBuffer; /* this includes hilite item, input mode & etc.. */
MemAlloc mallcFuncPtr; /* keep the memory allocated function */
MemFree mfreeFuncPtr; /* keep the memory freed function */
} historyNode;
static S16 currHistoryIndex = -1;
static historyNode historyData[MAX_HISTORY]; /* array of history data */
所有Screen 的ID 是唯一,每个APP 都划分了一段ID 范围。MMI 为每个APP
定义了起始ID ( 如MAIN_MENU_BASE 、PHONE_BOOK_BASE 、
SHORTCUTS_BASE 等),APP 在基础上再定义自已的Screen ID 。注意这些起始
ID 也作为String 、Image 等资源的起始ID 。
进入新屏幕前,将当前屏幕加入History 流程:
1. U8 EntryNewScreen (U16 newscrnID, FuncPtr newExitHandler,
FuncPtr newEntryHandler, void *peerBuf)
// newExitHandler :注册Screen 退出回调函数,在每次进入新Screen 时调用
2. static void ExecuteCurrExitHandler(void)
3. void ExecuteCurrExitHandler_Ext(void)
4. 执行由EntryNewScreen ()注册的退出回调函数(*currExitFuncPtr) ();
//当前Screen 退出回调函数
5. void GenericExitScreen(U16 scrnID, FuncPtr entryFuncPtr)
6. void AddHistoryReference(history *addHistory)
返回到上一级屏幕:
1. void GoBackHistory(void)
2. static void ExecutePopHistory(void) 弹出一个node,并使用其信息执行屏幕入
口函数以重画屏幕: (*(historyData[currHistoryIndex].entryFuncPtr)) ();
History 提供的一些重要API :
1. void InitHistory(void)
2. #define AddHistory(addHistory) AddHistoryReference(&(addHistory))
extern void AddHistoryReference(history *addHistory);
// This function adds a history node to the history database.
3. void DeleteNHistory(U16 DeleteCount)
// This function Deletes 'n' history nodes from the history database.
4. void GoBackHistory(void)
// This function deletes the node on the top of the history database and execute its Entry function.
5. U8 GoBackToHistory(U16 ScreenID)
// This function delete all the nodes in the history up to the specified node and
//execute the Entry Function for the specified ScreenID.
6. U8 GetHistory(U16 ScreenID,history *ptrHistory)
// This function retrieves the history Data on the basis of the ScreenID passed.
//ptrHistory is the out put parameter that contains the retrieved history.
7. U8* GetCurrGuiBuffer(U16 ScreenID)
// This function is used to retrieve GUI buffer of the specified ScreenID. In GUI
//buffer all gui related information is stored by frame work whenever this screen is
//overwritten.
8. U8* GetCurrInputBuffer(U16 ScreenID)
// This function is used to retrieve Input buffer of the specified ScreenID. In input
//buffer all running text related information is stored by frame work whenever this
//screen is overwritten.
9. void ExecutecurrHisIndEntryFunc(void)
//This function is used to execute current history node Entry function.
10. U8 GetHistoryScrID(U16 scrnID, historyNode **ptrHistory)
// This is used to retrieves pointer to already saved history of a particular screen.
11. void ExecutePopHistory(void)
// This executes the entry function of the screen on top of the history stack and then
//deletes it from the history.
12. U16 GetCurrScrnId(void)
// This returns the screen id of the screen on top of history stack.
13. U16 DeleteBetweenScreen(U16 StartScrId, U16 EndScrId)
//To delete screens between screen with ID StartScrId till screen with ID EndScrId.
14. U16 DeleteScreen(U16 ScrId)
//To delete screen from history.
15. pBOOL IsScreenPresent(U16 scrnId)
//Queries history for presence of a screen Id.
Category Screen
获取MMI外部消息队列大小。queue_node_number = msg_get_extq_messages();
从外部消息队列获取消息,放入循环队列mmi_frm_fetch_msg_from_extQ_to_circularQ();
获取循环消息队列消息个数 OslNumOfCircularQMsgs。
设置活跃模块为MOD_MMI
轮训循环消息队列做相应处理。
判断目标模块是否为MOD_WAP或MOD_MMS分别处理。
判断消息类型是否为MSG_ID_TIMER_EXPIRY 调用EvshedMMITimerHandler(&Message);处理。
判断消息类型是否为MSG_ID_MMI_EQ_POWER_ON_IND做一系列初始化工作包括
如果不是以上两类消息那么调用mmi_frm_execute_current_protocol_handler处理消息
做一些消息的清理操作 OslFreeInterTaskMsg(&Message);
mmi_frm_invoke_post_event();(所以每次消息回调处理完后就要重新注册一次)
最后调度消息队列vm_appcomm_dispatch_msg(); vm_appcomm_dispatch_msg();(可能是吧其他新的消息放到MMI的消息队列做处理)。
---------------------
作者:请叫我相公
来源:CSDN
原文:https://blog.csdn.net/weixin_38041577/article/details/78715913
版权声明:本文为博主原创文章,转载请附上博文链接!
四、Frame 提供的其它资源
五、附录
参考文档:
参考项目: X100 MT6225