2008年9月26日星期五

ARM Linux kernel2.6静态memory mapping

基于ARM的SOC,很多设备寄存器的访问是需要直接静态映射的。而这部分工作一般都是在体系结构初始化时,调用iotable_init()完成。
它的其中一个参数是:
struct map_desc {
unsigned long virtual;
unsigned long pfn;
unsigned long length;
unsigned int type;
};
virtual就是想要映射的虚拟地址,pfn是物理地址,我要强调的是它不是point function,而是Page Frame Number,因此这个物理地址要切换成成页号。

2008年9月22日星期一

unattended mode reference count

PowerPolicyNotify(PPN_UNATTENDEDMODE, TRUE);
PowerPolicyNotify(PPN_UNATTENDEDMODE, FALSE);
这一对函数的作用,相当于
EnterCriticalSection()
LeaveCriticalSection()
这样类似的成对使用的函数。
实际power manager维护着一个global的reference count,当call PowerPolicyNotify(PPN_UNATTENDEDMODE, TRUE)时,reference count++。
当PowerPolicyNotify(PPN_UNATTENDEDMODE, FALSE),reference count--。

如果万一我们call他们时没有对称,那么系统会怎样?

Power manager参考reference count,如果不等于0,那么他会调度决定进入unattended mode。但是并不是说reference count不等于零,power manager就会让系统永远处于unattended mode,而是会有等待一个超时,如果超时,那么系统还是会进入sleep。同时,这个reference count会在进入sleep后清零,也就是"以前所有的不对称都既往不咎"。

2008年9月18日星期四

Windows Mobile 睡死之谜

Windows Mobile很容易出现"睡死过去"的现象,排除驱动程序的代码问题,那么还是可能"睡死过去"。
今天做了一个实验来模拟在suspend最后那一刻,对文件操作。结果,机器整个hang。

FILE *logFile;
FileSystemPowerFunction(FSNOTIFY_POWER_OFF);

logFile = _tfopen(TEXT("hello.txt"), TEXT("w"));

if (logFile != NULL)
{
_ftprintf(logFile, TEXT("%ld"), 12 );
fclose(logFile);
}

FileSystemPowerFunction(FSNOTIFY_POWER_OFF);是power manager在睡前最后几步必做的事情。而下面的写操作是模拟另一个线程在这个时刻后做写文件的操作。结果就是死亡。
这样的结构在实际开发中很容易形成,要特别小心。

IOCTL_POWER_SET, power manager不为你负责

IOCTL_POWER_SET是驱动程序接收power manager指令的接口。你不要以为power manager会保证每次call你的IOCTL_POWER_SET会保证race condition不发生。换句话说就是IOCTL_POWER_SET可能会被同时call到。
今天我就遇到这样的情况,一个是power manager正常的设置bkl1:到D0,结果另外一个驱动调用了
DevicePowerNotify(_T("bkl1:"), D0,POWER_NAME);
结果我偶然发现Backlight的IOCTL_POWER_SET被同时call到,导致race condition的发生。最后就用Critical Section解决了。

2008年9月12日星期五

Linux对S3c2443的支持还不完善

我发现目前最新的Linux kernel 2.6.26.5对三星S3c2443 soc的支持是非常不完善的。我想大概是各位kernel hackers拿到s3c2443的机器太少了。大致浏览了一下代码,发现以下问题:

  1. main clock的读寄存器并计算是有问题的
  2. 由于第1条,导致Uartclock无法正常设置,Uart无法正常工作
  3. s3c2443已经支持4 Uart channelkernel source code目前只支持3
  4. TFT LCD controller完全没有支持

这些问题部分我已经和Ben Dooks联系,有望在2.6.28 release时,对s3c2443 soc的基本功能进行全面的支持。


display和backlight之间不能不说的事情

我们在windows mobile5/6上写display和backlight驱动程序时候,经常可能会遇到"白屏"的现象。
Display关闭,backlight还是打开,这就是"白屏"。一般情况下,Display和backlight是两个独立的驱动程序,那么看来在关闭屏幕和背光这两个驱动程序是有一定顺序的。
我们先考虑关灯的情况,需要按如下顺序,不可颠倒,颠倒就是"白屏":
1. backlight off 2. display off
再考虑开灯的情况,需要按如下顺序,不可颠倒,颠倒就是"白屏":
1. display on 2. backlight on
从上面可以看出,开灯和关灯动作的顺序是相反的。如果从供电的角度来看,display和backlight有着父子关系,即display是backlight的父亲。display有电,backlight才可能有电。
可是Windows Mobile的power manager在睡觉和唤醒时,对设备驱动的操作是线性的。比如先开display,再开backlight,那么必然也是先关display,再关backlight。因此靠power manager来解决白屏问题是不可能了,需要我们自己处理。
而且实际上由于display和backlight的IClass不同,使得他们位于power manager的两个不同的设备管理列表里,导致backlight的流设备列表一定先被操作到,然后才是display。这样一来和上面提到的关灯的顺序是符合的。开灯的顺序相反!

解决方案一
把backlight放在display驱动中去做,这样简单很多,很多变量可以共享

解决方案二
Backlight和display仍然是两个驱动。那么我们的重点是反转开灯的顺序。可以选择在backlight驱动程序处理IOCTL_POWER_SET的D0时,给display驱动的电源状态设置一个floor(请参考 http://cpuwolf.blogspot.com/2008/09/window-mobile-power-managementdevice.html),先调用SetPowerRequirement(pszDevice, D0, POWER_NAME, NULL, 0);再开启硬件背光。这个结论Microsoft的文档也有,但是她绝对不会告诉你为什么。

2008年9月11日星期四

charge icon

用3ds max做了3张充电的图标,尺寸是128x84,如果有需要原图的,直接email我,我这里是bmp原图

揭秘window mobile power management关于device power state的管理

hi, I am cpuwolf。今天我就由深入浅的帮你揭开mobile power manager(也就是pm.dll)是如何调度设备的power state。先分析power manager的内部结构,再从API的角度帮你理解power management API的不同。他们是:
DevicePowerNotify()
SetDevicePower()
SetPowerRequirement()
ReleasePowerRequirement()
这几个函数,如果你不听我讲,光想通过看看microsoft的官方文档来理解,那是不可能的!信不信由你。

重要的数据结构

power manager为每一个被管理的设备维护着一个数据结构,它的定义简化后如下:
// this structure describes a power-manageable device
typedef struct _DeviceState_tag {
。。。
CEDEVICE_POWER_STATE curDx; // current official power state (not necessarily supported by the device)
CEDEVICE_POWER_STATE floorDx; // minimum device power state, or PwrDeviceUnspecified
CEDEVICE_POWER_STATE ceilingDx; // maximum device power state, or PwrDeviceUnspecified
CEDEVICE_POWER_STATE setDx; // power state if explicitly set, or PwrDeviceUnspecified
CEDEVICE_POWER_STATE lastReqDx; // last state requested by the device
CEDEVICE_POWER_STATE actualDx; // current actual device power state
CEDEVICE_POWER_STATE pendingDx; // Pending DX for updating
DWORD dwNumPending; // Number of Pending for updating.
。。。
} DEVICE_STATE, *PDEVICE_STATE;
居然有这么多的device power state来影响最后的一个power state的结果。也就是说power manager是个调度中心,当它最后决定某个设备最终该是什么power state(D0/D1/D2/D3/D4)的时候,要参考上面这些成员变量。所以我们第一件事是要搞清楚power manager的调度原则。

power manager的调度原则

设备的电源状态总共有D0,D1,D2,D3,D4 (D0〈D1〈D2〈D3〈D4)。ceilingDx与floorDx听名字就知道是天花板和地板的意思(ceilingDx〈floorDx),人是生活在天花板和地板之间的空间的。在电源管理里面,意思就是天花板和地板之间的电源状态为有效状态。
例如:
ceilingDx=D1
floorDx=D3
那么D0,D4就是power manager不用考虑的状态,D1,D2,D3就是有效的电源状态。


setDx是最厉害的一个状态,它默认是PwrDeviceUnspecified,只要它不是PwrDeviceUnspecified,那么这个设备的最终状态就等于setDx。
那么setDx是谁设置的呢?
从名字我们一猜就知道,那就是SetDevicePower()。也就是说,只要这个函数一出马,那么不管系统当前是什么状态,或者这个设备是什么状态,这个对应的设备会立即切换到你指定的状态。因此,call这个函数的时候一定要三思而后行。白屏现象就很有可能是他造成的。
例如:
SetDevicePower(_T("BKL1:"),POWER_NAME,D4);
这句话就好像在说:“power manager,我命令你把BKL1:变成D4!!”

如果setDx是PwrDeviceUnspecified,那么power manager就开始考虑lastReqDx。lastReqDx的气势就要虚弱很多,也正如microsoft的文档所说,如果你想改变一个设备的系统状态,同时还想争得power manager的同意,不想太强制,那么call DevicePowerNotify()是再合适不过的了。
例如:
DevicePowerNotify(_T("BKL1:"),D4,POWER_NAME);
这句话就好像在说:“power manager,你看我现在把BKL1:变成D4如何?!”
power manager接到这样的请求,它也是需要掂量一下的,那么它的原则是什么呢?只要是有效状态就可以啦!也就是说ceilingDx〈 lastReqDx〈floorDx。
如果ceilingDx=D1,lastReqDx=D0,那么设备最终也只能是D1。
再如floorDx=D3,astReqDx=D4,那么设备最终也只能是D3。
如果 ceilingDx=D1,floorDx=D3,你请求D2,那么设备最终就是D2。

以上原则很简单吧!可是你有没有注意到我没有提到ceilingDx与floorDx是如何确定的。说到这个就不得不说说SetPowerRequirement()和
例如:
SetPowerRequirement(_T("BKL1:"),D1,POWER_NAME,NULL,0);
好像再说在说:“power manager,帮我把BKL1:变成D1”。你觉得呢?当然不是!
其实你的真正意思是在说:“power manager,至少帮我把BKL1:变成D1”。
然后power manager问你,“D0可以么?(背光更亮一点可以么?)”。
然后你会说,“当然也可以。”
从这个对话中,我们可以知道SetPowerRequirement()实际在设置我们的地板-floorDx。至少是floorDx,不能更低了。floorDx的默认值是D4,因此如果你不去call这个函数,那么就没什么限制。

至于ceilingDx
注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\[ on | BacklightOff | Unattended | xxx ]的下面你可以定义:
"bkl1:"=D2
这样的定义,就好像你在规定:“系统在on的时候,bkl1:最高是D2”。所以bkl1:如果是D3,那么也是可以的。这实际就是在定义设备电源状态的天花板-ceilingDx。同时,如果你没有特别为某个设备指定,那么这个设备的ceilingDx就是用注册表项default的定义值。

综上,floorDx,ceilingDx,setDx,lastReqDx这四个值是power manager调度时要参考的重要参数。

设置设备的电源状态

curDx,actualDx是一对,意思非常接近。
curDx是power manager经过调度算法,最后决定的,该设备的电源状态。可是这个状态,此设备不一定支持。设备声明自己所支持的所有状态是通过IOCTL_POWER_CAPABILITIES做到的。因此万一不支持还需要经过mapping,那么mapping的结果就是actualDx,这个结果就直接通过IOCTL_POWER_SET下达给设备驱动。mapping的过程当然就要参考IOCTL_POWER_CAPABILITIES了。

pendingDx和dwNumPending是用于处理竞争问题设计的计数器。

2008年9月1日星期一

linux kernel 2.6.27-rc5三天前release

linux kernel stable 2.6.27-rc5三天前release。和以往一样,这次release对大多数来说并没有什么特别。但是对我来讲,这版kernel有着特殊的意义。
因为它包含了我第一次成功向Linus Torvalds提交的补丁。从上大学到现在7年了,我终于有能力参于linux内核开发了。这一天我等了很久。虽然我这次的修改非常简单,但是至少能说明我可以开始融入这个开源内核的大家庭。同时我也相信自己会在日后提交更多的修改。
这种感觉就像小学一年级第一次受老师表扬,等到老师认可的快乐!
在这个submit patch的过程中,可以感觉到这个跨世界范围的大项目的运营过程。看他们是怎么集合大家的智慧,在如此多人参与的项目中,他们是如何把控代码的质量。这些都值得我去学习。

http://www.kernel.org/pub/linux/kernel/v2.6/testing/ChangeLog-2.6.27-rc5