2855
- 收藏
- 点赞
- 分享
- 举报
OAL层解析(2)
正如CE的帮助文档所言,创建OAL是一个非常复杂的任务,而通常的办法是复制原有的相同平台的OAL代码,然后修改来适应平台的特殊要求。也就是说对于没有特殊要求的平台,复制原有相同平台的OAL代码就足够了。由于OAL的复杂性在这篇文章中我只讲解常用的部分。
一、实现ISR
1.ISR的概念
ISR(interrupt service routine)是处理IRQs(interrupt request line)的程序。Windows CE用一个ISR来处理所有的IRQ请求。当一个中断发生时,内核的异常处理程序先调用内核ISR,内核ISR禁用所有具有相同优先级和较低优先级的中断,然后调用已经注册的OAL ISR程序,一般ISR有下列特征:
执行最小的中断处理,最小的中断处理指能够检验、答复产生中断的硬件,而把更多的处理工作留给IST(interrupt service thread)。
当ISR完成时返回中断ID(中断ID大部分是预定义的)。
2. X86平台的ISR结构
X86平台的ISR保存在%_WINCEROOT%/PUBLIC/COMMON/OAK/CSP/I486/OAL/fwpc.c中,函数名为PeRPISR。下面分析一下此函数的主要代码:
ULONG PeRPISR(void)
{
ULONG ulRet = SYSINTR_NOP; ///返回值,既中断ID(以SYSINTR_为前缀)
UCHAR ucCurrentInterrupt; ///当前中断号
if (fIntrTime) ////// fIntrTime 用于测试SR和IST的延时时间,测试工具为ILTiming.exe。
......
ucCurrentInterrupt = PICGetCurrentInterrupt(); ////返回当前中断IRQ
///IRQ0,IRQ0为系统时钟(system tick)中断,具体见“二、实现系统时钟”
if (ucCurrentInterrupt == INTR_TIMER0)
......
if (dwRebootAddress) ////是否需要重启动
RebootHandler();
......
if(ucCurrentInterrupt == INTR_RTC) ////IRQ8,real-time clock的中断
......
else if (ucCurrentInterrupt <= INTR_MAXIMUM) ///如果中断小于 INTR_MAXIMUM
{
ulRet = NKCallIntChain(ucCurrentInterrupt); ////调用中断链
if (ulRet == SYSINTR_CHAIN) ///如果中断链未包含中断
////在IRQ 和SYSINTR之间转换,此函数返回IRQ对应的SYSINTR
ulRet = OEMTranslateIrq(ucCurrentInterrupt);
......
PICEnableInterrupt(ucCurrentInterrupt, FALSE); ///启用除当前中断以外的所有中断
} ///else if
OEMIndicateIntSource(ulRet); ///通知内核已经发生SYSINTR中断
}
从以上代码不难看出ISR的任务就是返回以“SYSINTR_”为前缀的中断ID,如果不需要进一步执行IST,那么就返回SYSINTR_NOP。
3.中断注册步骤
参考X86平台的代码,中断注册步骤如下:
用SETUP_INTERRUPT_MAP宏关联SYSINTR和IRQ。以“SYSINTR_”为前缀的常量由内核使用,用于唯一标识发生中断的硬件。在Nkintr.h文件中预定义了一些SYSINTR,OEM可以在Oalintr.h文件中自定义SYSINTR。
用HookInterrupt函数关联硬件中断号和ISR。这里的硬件中断号为物理中断号,而非逻辑中断号IRQ。在InitPICs函数(和上述ISR位于同一文件)的最后调用了HookInterrupt函数,如下:
for (i = 64; i < 80; i++)
HookInterrupt(i, (void *)PeRPISR); ///用ISR关联16个中断号
4. 中断处理步骤
调用InterruptInitialize函数关联SYSINTR和IST,具体是关联IST等待的事件。一般在驱动程序中按如下编写:
hEvent = CreateEvent(...) ///创建一个事件对象
InterruptInitialize(SYSINTR_SERIAL, hEvent, ...) ///关联一个串口中断ID和这个事件
hThd = CreateThread(..., MyISTRoutine, hEvent, ...) ///创建一个线程(IST)
CeSetThreadPriority(hThd, 152); ///提高此线程的优先级
IST执行I/O操作,一般IST按如下编写:
for(;;) ///驱动程序一直处于服务状态
{
WaitForSingleObject(hEvent, INFINITE); ////无限等待事件
...... //// I/O操作
InterruptDone(InterruptId); ///结束当前中断处理
}
ISR和IST之间数据传输
假如我们要从一个设备频繁的读取数据而每次读取量非常少,那么每次读取都要调用IST会降低性能。作为解决方案,ISR可以做读取工作(存放到缓冲区),并在缓冲区存放满后由IST到缓冲区读取。因为ISR运行在内核模式而IST运行在用户模式,IST不能轻易地访问ISR的缓冲区,为此CE提供了一个办法(参见标题为“Passing Data between an ISR and an IST”的帮助文档),您也可以到天极网嵌入式开发论坛询问。
二、实现系统时钟
1. 系统时钟(system tick)概念
系统时钟是内核需要的唯一中断(IRQ0),系统时钟每毫秒产生一个中断,当发生中断时内核在ISR中累计,到1000的倍数就是过了一秒钟。在处理系统时钟的ISR中不仅要累计计数,还要决定是否通知内核开始重新调度当前所有的线程。要实现一个OAL,系统时钟是第一个必须做的事。
2. X86平台系统时钟中断的处理工作
系统时钟由InitClock函数负责初始化工作,一般是在OEMInit函数中调用。当发生中断时,ISR首先用下列语句累计计数:
CurMSec += SYSTEM_TICK_MS; /////SYSTEM_TICK_MS = 1
然后根据下列语句判断应该返回什么值:
if ((int) (dwReschedTime – CurMSec) >= 0)
return SYSINTR_RESCHED; ///重新调度
else
return SYSINTR_NOP; ///不再执行任何操作
上述代码中全局变量dwReschedTime在schedule.c中定义,也就是由内核的调度模块决定在何时开始重新调度线程。CurMSec累计了从WindowsCE启动到当前总共产生了多少个system tick。实现系统时钟后还要实现OEMIdle函数,当没有线程准备运行时OEMIdle被调用,OEMIdle函数将CPU置于空闲模式,但在空闲模式下仍然要累计系统时钟。
三、I/O控制代码
1. I/O控制代码作用
应用软件或者驱动程序可以调用KernelIoControl函数与OAL层通信,而KernelIoControl在内部调用OEMIoControl函数。OEMIoControl是一个OAL API,OEM可以在OEMIoControl中编写自己的I/O控制代码实现一些功能,或者说与应用软件通信。I/O控制代码常用的例子如重启计算机、得到系统信息、设置RTC、得到设备ID等。还有一些系统程序使用的特殊的I/O控制代码。在这里说明一下,我经过实验证实CE提供的得到设备ID方法并非有效。
2. 编写自己的I/O控制代码步骤
在pkfuncs.h或者新编写一个.h文件中按如下格式定义:
#define IOCTL_MY_CONTROL CTL_CODE(FILE_DEVICE_HAL, 3000, METHOD_NEITHER, FILE_ANY_ACCESS)
在oemioctl.c中修改OEMIoControl函数,添加如下代码:
case IOCTL_MY_CONTROL:
......
在应用程序中调用KernelIoControl函数,具体参数参见帮助文档。
结束语
OAL所有接口函数和全局变量说明请参见标题为“Supported OAL APIs”的帮助文档。以前的文章《加密Windows CE系统》里介绍的 OEMLoadInit_t pOEMLoadInit 和 OEMLoadModule_t pOEMLoadModule 就是OAL暴露的用于创建可信任环境的全局变量,我们编写好了函数后就可以把函数地址赋给这两个变量。OAL的确很复杂,当实际需要时我们才会具体研究某一部分,因为带着问题研究是最有效率的。希望这两篇文章能够抛砖引玉,让更熟悉OAL的开发者能够公开自己的研究成果,和大家分享。
一、实现ISR
1.ISR的概念
ISR(interrupt service routine)是处理IRQs(interrupt request line)的程序。Windows CE用一个ISR来处理所有的IRQ请求。当一个中断发生时,内核的异常处理程序先调用内核ISR,内核ISR禁用所有具有相同优先级和较低优先级的中断,然后调用已经注册的OAL ISR程序,一般ISR有下列特征:
执行最小的中断处理,最小的中断处理指能够检验、答复产生中断的硬件,而把更多的处理工作留给IST(interrupt service thread)。
当ISR完成时返回中断ID(中断ID大部分是预定义的)。
2. X86平台的ISR结构
X86平台的ISR保存在%_WINCEROOT%/PUBLIC/COMMON/OAK/CSP/I486/OAL/fwpc.c中,函数名为PeRPISR。下面分析一下此函数的主要代码:
ULONG PeRPISR(void)
{
ULONG ulRet = SYSINTR_NOP; ///返回值,既中断ID(以SYSINTR_为前缀)
UCHAR ucCurrentInterrupt; ///当前中断号
if (fIntrTime) ////// fIntrTime 用于测试SR和IST的延时时间,测试工具为ILTiming.exe。
......
ucCurrentInterrupt = PICGetCurrentInterrupt(); ////返回当前中断IRQ
///IRQ0,IRQ0为系统时钟(system tick)中断,具体见“二、实现系统时钟”
if (ucCurrentInterrupt == INTR_TIMER0)
......
if (dwRebootAddress) ////是否需要重启动
RebootHandler();
......
if(ucCurrentInterrupt == INTR_RTC) ////IRQ8,real-time clock的中断
......
else if (ucCurrentInterrupt <= INTR_MAXIMUM) ///如果中断小于 INTR_MAXIMUM
{
ulRet = NKCallIntChain(ucCurrentInterrupt); ////调用中断链
if (ulRet == SYSINTR_CHAIN) ///如果中断链未包含中断
////在IRQ 和SYSINTR之间转换,此函数返回IRQ对应的SYSINTR
ulRet = OEMTranslateIrq(ucCurrentInterrupt);
......
PICEnableInterrupt(ucCurrentInterrupt, FALSE); ///启用除当前中断以外的所有中断
} ///else if
OEMIndicateIntSource(ulRet); ///通知内核已经发生SYSINTR中断
}
从以上代码不难看出ISR的任务就是返回以“SYSINTR_”为前缀的中断ID,如果不需要进一步执行IST,那么就返回SYSINTR_NOP。
3.中断注册步骤
参考X86平台的代码,中断注册步骤如下:
用SETUP_INTERRUPT_MAP宏关联SYSINTR和IRQ。以“SYSINTR_”为前缀的常量由内核使用,用于唯一标识发生中断的硬件。在Nkintr.h文件中预定义了一些SYSINTR,OEM可以在Oalintr.h文件中自定义SYSINTR。
用HookInterrupt函数关联硬件中断号和ISR。这里的硬件中断号为物理中断号,而非逻辑中断号IRQ。在InitPICs函数(和上述ISR位于同一文件)的最后调用了HookInterrupt函数,如下:
for (i = 64; i < 80; i++)
HookInterrupt(i, (void *)PeRPISR); ///用ISR关联16个中断号
4. 中断处理步骤
调用InterruptInitialize函数关联SYSINTR和IST,具体是关联IST等待的事件。一般在驱动程序中按如下编写:
hEvent = CreateEvent(...) ///创建一个事件对象
InterruptInitialize(SYSINTR_SERIAL, hEvent, ...) ///关联一个串口中断ID和这个事件
hThd = CreateThread(..., MyISTRoutine, hEvent, ...) ///创建一个线程(IST)
CeSetThreadPriority(hThd, 152); ///提高此线程的优先级
IST执行I/O操作,一般IST按如下编写:
for(;;) ///驱动程序一直处于服务状态
{
WaitForSingleObject(hEvent, INFINITE); ////无限等待事件
...... //// I/O操作
InterruptDone(InterruptId); ///结束当前中断处理
}
ISR和IST之间数据传输
假如我们要从一个设备频繁的读取数据而每次读取量非常少,那么每次读取都要调用IST会降低性能。作为解决方案,ISR可以做读取工作(存放到缓冲区),并在缓冲区存放满后由IST到缓冲区读取。因为ISR运行在内核模式而IST运行在用户模式,IST不能轻易地访问ISR的缓冲区,为此CE提供了一个办法(参见标题为“Passing Data between an ISR and an IST”的帮助文档),您也可以到天极网嵌入式开发论坛询问。
二、实现系统时钟
1. 系统时钟(system tick)概念
系统时钟是内核需要的唯一中断(IRQ0),系统时钟每毫秒产生一个中断,当发生中断时内核在ISR中累计,到1000的倍数就是过了一秒钟。在处理系统时钟的ISR中不仅要累计计数,还要决定是否通知内核开始重新调度当前所有的线程。要实现一个OAL,系统时钟是第一个必须做的事。
2. X86平台系统时钟中断的处理工作
系统时钟由InitClock函数负责初始化工作,一般是在OEMInit函数中调用。当发生中断时,ISR首先用下列语句累计计数:
CurMSec += SYSTEM_TICK_MS; /////SYSTEM_TICK_MS = 1
然后根据下列语句判断应该返回什么值:
if ((int) (dwReschedTime – CurMSec) >= 0)
return SYSINTR_RESCHED; ///重新调度
else
return SYSINTR_NOP; ///不再执行任何操作
上述代码中全局变量dwReschedTime在schedule.c中定义,也就是由内核的调度模块决定在何时开始重新调度线程。CurMSec累计了从WindowsCE启动到当前总共产生了多少个system tick。实现系统时钟后还要实现OEMIdle函数,当没有线程准备运行时OEMIdle被调用,OEMIdle函数将CPU置于空闲模式,但在空闲模式下仍然要累计系统时钟。
三、I/O控制代码
1. I/O控制代码作用
应用软件或者驱动程序可以调用KernelIoControl函数与OAL层通信,而KernelIoControl在内部调用OEMIoControl函数。OEMIoControl是一个OAL API,OEM可以在OEMIoControl中编写自己的I/O控制代码实现一些功能,或者说与应用软件通信。I/O控制代码常用的例子如重启计算机、得到系统信息、设置RTC、得到设备ID等。还有一些系统程序使用的特殊的I/O控制代码。在这里说明一下,我经过实验证实CE提供的得到设备ID方法并非有效。
2. 编写自己的I/O控制代码步骤
在pkfuncs.h或者新编写一个.h文件中按如下格式定义:
#define IOCTL_MY_CONTROL CTL_CODE(FILE_DEVICE_HAL, 3000, METHOD_NEITHER, FILE_ANY_ACCESS)
在oemioctl.c中修改OEMIoControl函数,添加如下代码:
case IOCTL_MY_CONTROL:
......
在应用程序中调用KernelIoControl函数,具体参数参见帮助文档。
结束语
OAL所有接口函数和全局变量说明请参见标题为“Supported OAL APIs”的帮助文档。以前的文章《加密Windows CE系统》里介绍的 OEMLoadInit_t pOEMLoadInit 和 OEMLoadModule_t pOEMLoadModule 就是OAL暴露的用于创建可信任环境的全局变量,我们编写好了函数后就可以把函数地址赋给这两个变量。OAL的确很复杂,当实际需要时我们才会具体研究某一部分,因为带着问题研究是最有效率的。希望这两篇文章能够抛砖引玉,让更熟悉OAL的开发者能够公开自己的研究成果,和大家分享。
我来回答
回答0个
时间排序
认可量排序
暂无数据
或将文件直接拖到这里
悬赏:
E币
网盘
* 网盘链接:
* 提取码:
悬赏:
E币
Markdown 语法
- 加粗**内容**
- 斜体*内容*
- 删除线~~内容~~
- 引用> 引用内容
- 代码`代码`
- 代码块```编程语言↵代码```
- 链接[链接标题](url)
- 无序列表- 内容
- 有序列表1. 内容
- 缩进内容
- 图片![alt](url)
相关问答
-
02012-12-05 13:37:12
-
2018-11-24 11:26:23
-
2023-04-23 09:50:36
-
2012-12-05 14:32:19
-
2020-11-11 16:49:43
-
2020-05-26 11:21:51
-
2024-07-06 09:19:07
-
2017-04-03 18:02:22
-
2013-11-16 16:10:37
-
2020-04-23 10:30:16
-
2013-11-21 21:59:20
-
2023-02-01 09:09:51
-
2016-04-18 15:54:15
-
2016-03-21 15:20:50
-
2015-08-25 20:35:16
-
2015-03-26 10:59:24
-
2013-11-19 21:50:18
-
2023-03-20 14:25:19
-
2020-08-27 17:43:25
无更多相似问答 去提问
点击登录
-- 积分
-- E币
提问
—
收益
—
被采纳
—
我要提问
切换马甲
上一页
下一页
悬赏问答
-
5Hi3516CV610 如何使用SD卡升级固件
-
5cat /dev/logmpp 报错 <3>[ vi] [func]:vi_send_frame_node [line]:99 [info]:vi pic queue is full!
-
50如何获取vpss chn的图像修改后发送至vo
-
5FPGA通过Bt1120传YUV422数据过来,vi接收不到数据——3516dv500
-
50SS928 运行PQtools 拼接 推到设备里有一半画面会异常
-
53536AV100的sample_vdec输出到CVBS显示
-
10海思板子mpp怎么在vi阶段改变视频数据尺寸
-
10HI3559AV100 多摄像头同步模式
-
9海思ss928单路摄像头vio中加入opencv处理并显示
-
10EB-RV1126-BC-191板子运行自己编码的程序
举报反馈
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明
提醒
你的问题还没有最佳答案,是否结题,结题后将扣除20%的悬赏金
取消
确认
提醒
你的问题还没有最佳答案,是否结题,结题后将根据回答情况扣除相应悬赏金(1回答=1E币)
取消
确认