Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2124378
  • 博文数量: 288
  • 博客积分: 10594
  • 博客等级: 上将
  • 技术积分: 3469
  • 用 户 组: 普通用户
  • 注册时间: 2006-10-27 19:27
文章分类

全部博文(288)

文章存档

2012年(4)

2011年(30)

2010年(40)

2009年(32)

2008年(71)

2007年(79)

2006年(32)

分类: LINUX

2010-10-20 11:27:08

最近研究marvell平台上的PMU管理机制,算是边学边写吧。 自己是个菜鸟,可能写起来就信马由缰了,毕竟懂得东西实在太少了。

marvell主要PMU管理在pxa3xx_pm.c中,而很多函数是建立在kernel/power目录下的main.c上的。下面先分析main.c文件吧

首先当然是注册了:
static int __init pm_init(void);
    这个函数使用了core_initcall(pm_init); 这个就涉及到linux driver启动顺序的问题。 core_initcall确保可以在比较靠前的顺序进行注册,防止了后面如果有用到这个驱动里的函数,但是此时此驱动还没有注册。(据说如果默认使用module_init,注册顺序是不固定的)。

在pm_init里注册了subsystem,
    subsystem_register(&power_subsys);
当然在进行注册前,先声明了变量power_subsys,使用方法是 decl_subsys(power,NULL,NULL);
可以对应源代码,看看decl_subsys到底干了啥事情,说白了就是初始化呗,声明并初始化了一个叫power_subsys的变量。
##在linux还是蛮常用的嘛。
#define decl_subsys(_name,_type,_uevent_ops) \
struct kset _name##_subsys = { \
        .kobj = { .name = __stringify(_name) }, \
        .ktype = _type, \
        .uevent_ops =_uevent_ops, \
}
 
Ok,子系统注册完了,该在sys下生成文件了。可以使用sysfs_create_group,在指定目录生成sys文件了,刚开始还很奇怪为啥使用sysfs_create_group,而不是使用sysfs_create_file,后来发现作者为了便于调试,通过宏定义,定义了一个trace的sysfs。这样,使用group函数可以在该目录同时生成两个,不用一步一步分别create了。代码如下:
#ifdef CONFIG_PM_TRACE
。。。。。。
static struct attribute * g[] = {
 &state_attr.attr,
 &pm_trace_attr.attr,
 NULL,
};
#else
static struct attribute * g[] = {
 &state_attr.attr,
 NULL,
};
#endif /* CONFIG_PM_TRACE */
static struct attribute_group attr_group = {
 .attrs = g,
};
 

Ok,到这里init就完成了。还算是比较简单。但是linux的作者们各种技巧确实给俺留下了深刻的印象。
例如sysfs的operation的声明,已经不止一次看到使用一个宏定义进行sysfs的operation声明了。
power_attr(pm_trace); //这样就声明了一个叫做pm_trace_attr的sysfs操作,attr为pm_trace。
欣赏一下它的原型吧:

#define power_attr(_name) \
static struct subsys_attribute _name##_attr = { \
 .attr = {    \
  .name = __stringify(_name), \
  .mode = 0644,   \
 },     \
 .show = _name##_show,   \
 .store = _name##_store,  \
}

很帅,至少我认为是这样。特别是有2个以上的sysfs文件是。 不过带来的坏处就是使用source insight时,经常不知所踪。唉。

pm_trace其实是使用sysfs来控制trace的显示,不过看代码,没有看到它控制哪里的trace啊,奇怪。

学学sysfs的show和store。嘿嘿,跑题了,不过谁让俺水平就那么低了,经过坎坷的找工作,刚开始入手linux。

static ssize_t pm_trace_show(struct subsystem * subsys, char * buf)
显示文件里的内容,直接使用sprintf就可以啦。 sprintf(buf, "%d\n", pm_trace_enabled);

static ssize_t pm_trace_store(struct subsystem * subsys, const char * buf, size_t n)
当往文件里写入时,执行该函数。 sscanf(buf, "%d", &val), 然后pm_trace_enabled = !!val;哈哈。结束了。多么简单。

其实proc文件系统也蛮简单的,以后阅读到的时候再分析吧。

还有有必要看看state的show和store吧,我们可以通过写state文件,来进入不同的PM状态,如standby, mem, lcdfresh, deepsleep等。下面看看咋实现的:
static ssize_t state_show(struct subsystem * subsys, char * buf)

s += sprintf(s,"%s ", pm_states[i]);
。。。。。
s += sprintf(s,"\n");

在state中,将这几种状态都打印出来。
static const char * const pm_states[PM_SUSPEND_MAX] = {
 [PM_SUSPEND_STANDBY] = "standby",
 [PM_SUSPEND_MEM] = "mem",
#ifdef CONFIG_SOFTWARE_SUSPEND
 [PM_SUSPEND_DISK] = "disk",
#endif
 [PM_SUSPEND_LCDREFRESH] = "lcdrefresh",
 [PM_SUSPEND_DEEPSLEEP] = "deepsleep",
 NULL,
};

#define PM_SUSPEND_ON  ((__force suspend_state_t) 0)
注意__force的用法。:)

static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n)
如果向state写入standby,则会进入standby模式。
 p = memchr(buf, '\n', n);
 len = p ? p - buf : n;
剥离回车换行符。memchr函数是在指定的n字节内查找'\n'字符,并返回所在位置。

 for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
  if (*s && !strncmp(buf, *s, len))
   break;
 }
查找匹配的状态。
 if (state < PM_SUSPEND_MAX && *s)
  error = enter_state(state);
进入指定状态。 enter_state()。。。。。

唉,这才进入main.c文件的核心部分 enter_state。
这才到了最关键的地方static int enter_state(suspend_state_t state)

enter_state 是负责处理进入何种状态的函数。貌似在arm linux里,电源管理都经过如下几步:
1. suspend_prepare
2. suspend_enter
3. suspend_finish
当然这几个函数又有封装,但是从总体上看,上层实现时,operation也对应这几个函数。

先看看suspend_prepare的实现吧
pm_prepare_console(); 设置console为suspend模式
freeze_processes()     将user space和kernel的thread freeze
global_page_state()     貌似要free一些space出来,不是很确定呃。
pm_ops->prepare(state)     调用上层operation的prepare函数
suspend_console();        获取console信号量,设置suspend标志位,标志进入suspend,与其对应的resume_console
device_suspend(PMSG_SUSPEND); 每个设备进入suspend模式,有待进一步分析内部实现。
disable_nonboot_cpus();    对于我们这个单CPU,没啥用处

suspend prepare 是为了suspend做准备的一部,下一步就要进入suspend状态了。

suspend_enter实现要简单多了
device_power_down(PMSG_SUSPEND)   关闭所有device的power,如果失败,则退出,并且suspend也失败。       
pm_ops->enter(state);          进入suspend模式。
device_power_up();           退出suspend模式,将device的状态恢复
 
suspend_finish实现,主要恢复suspend prepare里的工作。
enable_nonboot_cpus();      
pm_finish(state);
device_resume();
resume_console();
thaw_processes();
pm_restore_console();

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/sdlizhe/archive/2008/10/09/3042848.aspx
阅读(2128) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2011-04-04 11:27:52

学习了,多谢楼主分享哦!也欢迎广大linux爱好者来我的论坛一起讨论arm哦!www.lt-net.cn

chinaunix网友2010-10-23 14:52:52

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com