Acpi 和 apm

 

Acpi apm 主要是针对在X86架构上存在bios芯片上的linux电源管理机制。

acpi是新更多的依赖于操作系统,是今后发展的主流。同时还增加了cpu battery相关的状态检测。

 

 

在嵌入式系统上,由于不存在bios的硬件则在内核编译时就没有对应的配置选项。只存在apm emulation的模拟。此部分只支持几种apmevent,但在应用层可以使用apmd来进行相应处理。

 

Linux的关机流程分为两种方式:

1.acpi 或者apm。如果在嵌入式系统上没有对应部分的支持则应该使用第二种方法。

2.sys_reboot。系统调用,在应用层存在对应的库函数,reboot()。

在内核中,对应参数会转到kernel_power_off,kernel_halt,kernel_restart

对应转到

machine_power_offmachine_haltmachine_restart

对应转到

pm_power_off ,....

 

pm_power_off 会准对不同平台的关机方式,进行处理,使电源关闭

 

 

 

2009-09-18

 

这几天跟刘兴宝共同调查了,嵌入式电源管理方面的问题。

关机的流程已经在上面的叙述中提及。

今天又在marvell的电路板上模拟运行了所构想的方案。使用apm emulation + ampd + input_handle ,在input_handle中使用按键F1F2F3F4,模拟电源事件。

昨天晚上在春晖的机器上,在strachbox 下,交叉编译了apmd。没有棘手的问题。整个编译只依赖于 apmd.c,apm.h,apmlib.c

昨天晚上没有试通这个想法,因为marvell电路板上的linux 内核已经编译进了apm emulation。而这个情况我不知道,所以又重新以模块的方式编译了apm emulation。当我将编好的ko插入marvell的内核时,系统提示内核已经存在了apm emulation。于是查看了apm emulation 的应用层表现(/dev/apm-bios--event,/proc/apm--data),发现确实已经在内核编译过程中选择了将apm emulation 编了内核。

 

所以昨晚的一段时间是浪费了。

apmd昨天下午就交叉编译好了,拷到marvell中,log信息提示正常。但是没有内核层的事件驱动,apmd自身无法进行其他更有意义的操作。

今天早上。编辑了input驱动apm event 的底层驱动程序。该程序大部分是从imx31内核源码的apm-power.c中,拷贝的。因为该部分的代码实现的正好是我所需要的input_handle,关于键盘处理的代码。

将全篇拷贝过来,修改其中的,input event maskEV_KEY,这样就可以接受所有的key事件,这样如果keycodeF1F2F3F4,就可以向apm event queue 中发送事件,以触发apmd 读取对应的事件。apmd在收到事件时,会通过/proc/apm 读取对应事件的详细信息。此部分通过内核中的 apm_get_power_status 函数获得,所以要重写该函数指针。使用get_power_status模拟对应的power status,即填充info结构。这样驱动就ok了。

 

驱动是编辑好了,可以需要一个交叉编译环境和对应的内核源码header。以前在编译驱动时,都是存在一个header,这样就可以在make是用C选项指定header的位置。但是,现在没有header,上网查找一下,得到了方法。将 apm_support.c拷贝到 driver/char 下。修改该目录下的Makefile。在其中添加 objs-m += apm_support.o。然后重新make 是就可以看到生成了apm_support.ko

 

同样用U盘将apm_support.ko拷贝到marvell /home 下。Insmod apm_support.ko。成功!

运行apmdF1,F2,F3,F4Greatapmdlog上清晰的提示着,不同的apm event

下一步就是测试apmdcallproxy 是否好用了。该proxy对应的是/etc/apmd_proxy。于是在/etcvi 一个 apmd_proxy ,chmod为可执行属性。在apmd_proxy中,可以用touch,或者mkdir做简单的测试。

 

好了,成功了。按下不同的按键,就会在 /home 下创建对应的文件。这预示着从底层到应用层的所有路径都已经打通了。剩下要做的就是,在移植到实际硬件中时,修改apm event 的发起者和apm_get_power_status 的内部数据。而在用户层,要做的就是修改apmd_proxy中的电源管理策略(比如,按顺序关闭某些进程,关闭背光等等)。

 

总结。虽然最后的实现过程仅仅只有一天不到的时间,但前期的调查却使用了大约一个星期的时间。这个期间,每天都在内核中寻找关于apm的蛛丝马迹,甚至同时参阅3个版本的内核源码。从最初的对acpiapm的初略了解,到最后的使用apm emulation模拟实际apm,整个过程,每天都是在不断更新对linux 电源管理的认识。其中进步最大的,提速最快的是与刘兴宝的几次交流,每个人都不可能了解到所有方面,而或许对某个方面的一个小小问题都能带来新的思考,从而打开这个方面更全面更新的认识。

 

由于跟随项目的需求,查看了电源管理芯片mc13783的驱动。觉得其中的电源事件可以用来驱动apm event。这样就省略了大部分的编码,复用了极大部分的内核代码。在mc13783中断事件的中断服务函数下半部中可以 向apm 发送event,同时准备好power_status 。在硬件完整的情况下,该部分应该在2天以内完成。

 

对于应用层的电源管理策略,我不再进行跟踪了,这是一个简单的过程。在suspend时,按照一定的顺序关掉一组进程和服务,在resume时,按照相反的顺序启动进程和服务。在low battery时,关闭某些功耗设备和进行。在change power 时,实时显示电源状态,等等。

 

至于关闭进程的顺序,可以参照pc 机上 acpi 的关机脚本的顺序来进行。Ubuntu下,该目录位于 /etc/acpi/suspend.d ,/etc/acpi/resume.d,/etc/acpi/battery.d 。其中(标号S代表startK代表kill)数字标号越小,代表越先执行。

 

2008-09-18 下午

 

 

注:根据需求可能需要修改的地方。在apm emulation中,默认的suspendPM_SUSPEND_MEM,如果需要改变关机状态到DSM,那么就需要修改这个suspend 的参数。----在此提及,以防出现恐慌。

 

个人想法:

在项目的需求中,由于用到了“关机”状态led指示,所以只是假关机,实际只是进入了DSMdeep sleep mode)。每次电源事件都会唤醒CPUCPU处理后,继续进入DSM

而进入DSM的操作由apm来实现,所以系统命令haltshutdownpoweroff,都处于无效状态。原因是,没有在内核中实现对应的操作代码。可以使用这些系统命令来进行完全关机(掉电),而不是使CPU进入DSM状态。要实现这个功能,只需简单的将power_off指针转到mc13783poweroff函数就可以了。

 

这样最终系统的可以操作的结果就是:

1.按下电源键,系统通过apm进入DSM的假关机状态,此时有充电指示。

2.在终端下,输入halt。系统立即完全掉电。

 

 

 

2009-09-18

关于电源状态

 

以上主要描述的是系统的关机流程。而完整的电源管理还包含电源状态的检测甚至调整。而acpi还包括了对CPUFan,等等其他系统硬件的管理。

下面讲述一下嵌入式(arm)下的电源状态管理。

1.apm

同样通过apm emulation来进行管理。在power staus 中涵盖了ACbattery的状态。因此可以通过apmd在应用层读取到对应的电源状态。如果应用层没有syslogd,那么最后的信息会打印到控制台。否则,信息进入/var/log/apm中。

2./sys/class/power_supply

在该文件夹下,可以支持四种不同的供电方式:batterymain,UPS,USB。其中可选的属性有很多:POWER_SUPPLY_PROP_STATUS = 0,

POWER_SUPPLY_PROP_HEALTH,

POWER_SUPPLY_PROP_PRESENT,

POWER_SUPPLY_PROP_ONLINE,

POWER_SUPPLY_PROP_TECHNOLOGY,

POWER_SUPPLY_PROP_VOLTAGE_MAX,

POWER_SUPPLY_PROP_VOLTAGE_MIN,

POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,

POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,

POWER_SUPPLY_PROP_VOLTAGE_NOW,

POWER_SUPPLY_PROP_VOLTAGE_AVG,

POWER_SUPPLY_PROP_CURRENT_NOW,

POWER_SUPPLY_PROP_CURRENT_AVG,

POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,

POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,

POWER_SUPPLY_PROP_CHARGE_FULL,

POWER_SUPPLY_PROP_CHARGE_EMPTY,

POWER_SUPPLY_PROP_CHARGE_NOW,

POWER_SUPPLY_PROP_CHARGE_AVG,

POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,

POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,

POWER_SUPPLY_PROP_ENERGY_FULL,

POWER_SUPPLY_PROP_ENERGY_EMPTY,

POWER_SUPPLY_PROP_ENERGY_NOW,

POWER_SUPPLY_PROP_ENERGY_AVG,

POWER_SUPPLY_PROP_CAPACITY, /* in percents! */

POWER_SUPPLY_PROP_TEMP,

POWER_SUPPLY_PROP_TEMP_AMBIENT,

POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,

POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,

POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,

POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,

/* Properties of type `const char *' */

POWER_SUPPLY_PROP_MODEL_NAME,

POWER_SUPPLY_PROP_MANUFACTURER,

POWER_SUPPLY_PROP_SERIAL_NUMBER,

 

这样针对不同类型的电源,就有详细的属性可以来观察检测。

使用这种方法的过程是:将电源模拟成device,作为device的驱动插入内核,在device的注册过程中的probe中,使用power_supply_register进行注册。其中最要的操作get_property

需要device来提供。最后,power_supply注册到power_supply_classpower_supply_class被作为EXPORT_SYMBOL_GPL,那么其实在apm中也可以关注到该deviceproperty(用于apm_get_power_status的实现)。

 

查看了pda关于电源管理的部分。

通常在内核中起一个定时器,或者工作队列,或者线程,不断检测电源的property,如果发生改变,就使用power_supply_changed 函数通知电源状态发生了改变。这个函数最终调用psyexternal_power_changed 函数。同时他会发出一个ueventCHANGE),通知应用层。所以在应用层可以通过external_power_changed 中的机制获得信息,亦可以通过udev来简单的获得uevent,进而做出处理。

 

总结:比较两种电源检测的方法,实际上apmpower_supply的上一层次。apm可以通过power_supply_class获知电源状态信息,进而通过apmd进行管理策略上的调整。power_supply 是一种比较全面的对于电源设备的检测的方法,既可以为apm提供底层的数据支持,也可以进行其他扩展,通过uevent与用户层通信。一个完整的系统应该需要两个部分的共同支持:apmsuspend,和策略调整,power_supply的对电源的详细属性的提供。

 

如果需要获得电源的详细属性,则需要明确提供power_supply的支持。

你可能感兴趣的:(linux,开发日记)