下半部分将讲述事件通告类型的模式和 ACPI 热拔插在操作系统层上的运行模式:

六. 例子:事件通告/响应型

a.Linux关于Hot-Plug有关的SCI中断以及通用事件(GPE)的处理:

下面介绍GPE相关的事件,底层硬件的处理过程:
通用事件寄存器(见重要名字解释部分)的逻辑位于南桥芯片之内,通常而言,所有的ACPI相关的寄存器和电源管理的IO以及寄存器都位于南桥。例如ICH4;保存在BIOS的FADT中,在Linux内核中通常以fadt_descriptor_rev2的数据格式表示,以下是和ACPI通用事件寄存器相关的一些字段,acpi_gbl_FADT数据结构的各个字段的值通常在初始化的时候从BIOS中的RSDT表中获得:



通过FADT获得当前的事件寄存器描述之后,就知道当前ACPI系统中事件寄存器的硬件地址,长度,各个状态位的情况,这里就有一个更加高级的数据结构介入来对每个事件(状态位)的响应调动对应的句柄,这个数据结构就是GPE Block结构:struct acpi_gpe_block_info *gpe_block,它是GPE的响应核心,在初始化ACPI驱动的阶段将会由acpi_ev_create_gpe_block这个函数创建这个数据结构,扫描名字空间(Namespace);在名字空间中通常会有专门的全局对象节点针对GPEx_STS寄存器的各个位具体执行操作节点进行描述,如下例:_GPE用来表示GPE寄存器,以及相应的需要通知OSPM处理动作,例如:

_GPE
    Method(_L01) { // Update device
       Sleep(250) // Mechanical Delay 
       Notify(CDRM, 1)
    }

_GPE表示的是当前的通用事件寄存器的ASL描述,而Method(_L01)当前的通用寄存器的某个位的控制方法,_L表示这个位的触发状态为电平触发,01表示的通用寄存器的01位。

在Linux中通过在初始化名字空间的时候使用acpi_ns_walk_namespace函数对当前的_GPE名字空间进行扫描:

... ...
status = acpi_ns_walk_namespace (ACPI_TYPE_METHOD, gpe_device,
  ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK, acpi_ev_save_method_info,
  gpe_block, NULL);
... ...

这里回调函数acpi_ev_save_method_info在扫描名字空间的时候被调用,按照名字空间中针对GPE的描述,找到当前的需要实现响应的GPE节点初始化gpe_block中有关GPE寄存器状态位的结构--event_info acpi_ev_save_method_info回调函数找到_GPE通用事件寄存器的描述节点,并为在名字空间中通用事件寄存器的每个位安排相应的响应,在acpi_gpe_block_info中有一个字段event_info(事件消息描述结构)包含了每个位所对应的名字空间中所执行的方法节点(method_node字段),如下:

struct acpi_gpe_event_info
{
struct acpi_namespace_node   *method_node; 
 /* 当前GPE事件所需要在执行的名字空间的方法节点,如上面例中的Method(_L01)*/
acpi_gpe_handler                handler;     
 /* 执行的上层程序(驱动程序层)句柄,
例如某些时候在PCI设备进行热拔插的时候需要在插入后,
对资源进行重新分配,例如访问ECDT表(ACPI嵌入式控制器专用),
重新定义PCI配置空间并不需要再重新定义Notify
的操作就直接通过这个句柄直接执行,
例如某些ACPI嵌入式控制器的驱动中就提供了acpi_install_gpe_
handler的接口来安装这个句柄*/
void                        *context;           /* 代入的参数 */
struct acpi_gpe_register_info   
 *register_info; /*指向当前GPE寄存器组 */
u8                           flags;          
/*响应的电平(边沿触发或者是电平触发)*/
u8                           bit_mask;          /* GPEx_STS的掩码 */
};
例1-4 GPE寄存器在Linux中的数据描述结构

从上图看到通过acpi_gpe_event_info这个数据结构就把硬件上的状态寄存器和名字空间中的方法节点对应起来了。

GPE的事件响应机制:通常当事件寄存器有某个位发生变化的时候,如果相应的使能寄存器的位表示这个位使能(使能寄存器在event_info的GPE寄存器组字段 enable_address寄存器表示),那么表示当前的事件响应会有效,然后引发SCI中断,任何ACPI中断都会引发SCI中断,它一般是一个普通的CPU电平中断(这个和x86系统的特征相关),低电平有效。

在ACPI模块中函数acpi_ev_handler_initialize对SCI的中断句柄进行初始化,通常在Linux的ACPI驱动中有每个中断一般会对应两个GPE Block(对应于两个不同的寄存器GPE0_STS和GPE1_STS),另外还包括gpe_block_list_head和acpi_gbl_gpe_xrupt_list_head队列负责处理SMP的情况,它们的关系如下图:

在linux的ACPI处理机制中acpi_ev_sci_xrupt_handler函数就是SCI中断处理的句柄,所有GPE共享这个SCI中断句柄,在acpi_ev_gpe_detect函数中扫描acpi_gbl_gpe_xrupt_list_head全局队列上挂的GPE Block检查事件状态,并且分发到相关的事件的处理进程,并根据当前使能状态寄存器来确定是否响应当前事件,然后调用acpi_os_queue_for_execution这个ACPI OS层的执行过程把当前的所需要执行的进程acpi_ev_asynch_execute_gpe_method放入当前的OS中的执行队列中异步执行,这个执行过程首先会检查当前寄存器合法性,然后执行GPE的对应方法节点并清零GPEx_STS,(调用acpi_ns_evaluate_by_handle执行)其过程和前面的执行型ACPI命令的处理过程相同。

b.Notify的方法执行/初始化过程:

在热拔插入的处理过程的名字空间中可以看到当一个GPE事件发生之后,所调用的方法节点通常会调用"NOFITY"操作符,通知OSPM进行处理:

通常一个ASL的通告操作表示如下:

Notify(\_SB.PCI0.P2P2,0)


\_SB.PCI0.P2P2表示当Hot Plug阶段的该设备接收响应事件,0表示ACPI向OSPM通告消息BUS Check通告, 通知PCI总线驱动层进行枚举。


在热拔插中要用到以下的几个系统级别的通告消息如下(另外0x80以上是针对具体设备对象通告,例如电池,温度控制,热拔插等设备):



表1-1设备对象的OSPM通告类型
通告值: 描述:
0 总线检查:设备对象出现,通知OSPM,完成"Plug and Play"的枚举操作。当收到通知时,OSPM执行这个操作,在热拔插的时候,由ACPI AML通过Notify的方式通知该值到OSPM,
1 设备检查:用于通知OSPM,设备或出现或消失。如果设备出现,OSPM将从设备节点的父节点"重新枚举(re-enumerate)"。如果设备拔出,OSPM将使设备的状态定为无效。OSPM可以优化重新枚举的过程。
2 设备唤醒:用于通知OSPM设备发出了它的睡眠事件信号,OSPM需要通知OSPM的本地设备驱动,此情况仅用于支持_PRW的设备。
3 拔出要求:用以通知OSPM设备应被弹出,同时OSPM需要执行"Plug and Play 弹出操作",与此同时OSPM将运行_Ejx方法。
6 总线模式错配:用以通知OSPM设备被插入一个非当前操作模式的槽或背板中。例如:当用户试图将一个PCI设备热插入一个运行在PCI-X模式下的总线的槽中。
7 电源故障:用以通知OSPM,设备由于电源故障不能从D3状态下转出。

前面说了,执行对象节点方法和前面的执行型的ACPI命令的处理过程相同,这里只详细介绍Notify操作,这个操作是底层(ACPI层)向上层(驱动层)通告当前消息,它和普通的命令形操作没有本质上的区别,但由于向操作系统层通告消息,所以要调用一些ACPI OS部分的API;和上面介绍的_EJ0方法一样,最后会根据操作符的描述集合op_info执行句柄acpi_ex_opcode_2A_0T_0R,在这个执行例程中会进一步根据NOTIFY的通告值分发到处理过程,相关的处理过程是异步执行的,因为最终而言,会调用到acpi_os_queue_for_execution这个ACPI OS层的执行接口,它在Linux中执行方式就是把需要执行的任务(而acpi_ev_notify_ dispatch调用NOTIFY节点对象的实际执行者)插入任务队列中,不过在某些采用实时的Linux系统场合,有一些人会把它插入到立即队列中(Immediate Task Queue),两者实现的效果是相同的。

对于一个NOTIFY节点来说,其节点对象类型是struct acpi_object_notify_common common_notify类型,通常它的内部关键的字段如下表示:

...
union acpi_operand_object  *system_notify;     /* 系统的消息通告 */
union acpi_operand_object  *device_notify;     /* 设备消息通告*/
union acpi_operand_object  *handler;           /* 地址空间中的中的执行句柄 */
...

acpi_operand_object这个结构内都包含一个代表消息执行句柄的acpi_object_notify_handler结构,其中的handler,node,context字段分别表示针对当前通告的执行句柄,当前执行句柄所对应设备的名字空间节点,以及执行句柄带入的参数;

common_notify结构会在驱动程序初始化时候安装acpi_object_notify_handler中对应事件操作句柄,操作系统驱动程序会调用ACPI提供的接口acpi_install_notify_handler完成这个的动作,使用这个接口可以屏蔽掉一些作为驱动设计者无须知道的硬件和ACPI层的细节:

在下面是该函数在Linux中的原型:

acpi_status
acpi_install_notify_handler (
	acpi_handle                     device,
	u32                             handler_type,
	acpi_notify_handler             handler,
	void                            *context
)

1. 第一个参数acpi_handle是需要接收通告的设备句柄;

2. 第二个参数handler_type表示当前设备可以处理的消息号类型,分成设备类型和系统类型两种;

3. acpi_notify_handler表示执行消息的回调函数;

4. 回调函数的带入参数。

--详见acpi_install_notify_handler的源代码,它的流程和结构都非常简单,这里就不再详叙了。



更多相关文章

  1. 字体图标的引入和通过媒体查询改变导航样式
  2. HTML样式和常用选择器
  3. 字体图标的引用和自定义样式/媒体查询的使用
  4. 数据库的CURD操作、PDO本质与原理的学习
  5. CSS之伪类选择器和简单盒子简单案例
  6. 伪类选择器与盒模型常用属性
  7. 伪类选择器-结构伪类、根据位置选择匹配
  8. 7.4——常用标签与应用场景之表格与单元格
  9. css伪类选择器和盒模型

随机推荐

  1. Android(安卓)iptables 今天小结
  2. 【Android】基于XMAPP协议实现Android推
  3. Eclipse环境下格式化Android的代码风格
  4. Android(安卓)方向传感器 (Orientation Se
  5. View事件传递分析
  6. 深入理解Activity启动流程(一)–Activity
  7. Android应用使用自定义字体
  8. Android - Android Studio 的 Preview窗
  9. Android中visibility的3个属性说明
  10. Android 中文api (81)——InputMethod [