三、pm_test屬性文件讀寫
int pm_test_level = TEST_NONE;
static const char * const pm_tests[__TEST_AFTER_LAST] = {
[TEST_NONE] = "none",
[TEST_CORE] = "core",
[TEST_CPUS] = "processors",
[TEST_PLATFORM] = "platform",
[TEST_DEVICES] = "devices",
[TEST_FREEZER] = "freezer",
};
// core >> processors >> platform >> devices >> freezer, 控制範圍示意
cat pm_test的時候最終會調用函數pm_test_show(),在終端上打印出上面數組中的字符串,當前的模式用[]表示出來。
echo devices > pm_test的時候會最終調用到函數pm_test_store()中去,該函數中設置全局變量pm_test_level的值,可以是0-5,分別代表上none ~ freezer。該全局變量會在後面的suspend和resume中被引用到。
memchr函數說明:
原型:extern void *memchr(void *buf, char ch, unsigned int count);
用法:#include <string.h>
功能:從buf所指內存區域的前count個字節查找字符ch。
說明:當第一次遇到字符ch時停止查找。如果成功,返回指向字符ch的指針;否則返回NULL。
四、state屬性文件
power_attr(state)宏定義了一個struct kobj_attribute結構體state_attr:
static struct kobj_attribute state_attr = {
.attr = {
.name = __stringify(state),
.mode = 0644,
},
.show = state_show,
.store = state_store,
}
kobj_attribute結構體封裝了struct attribute結構體,新建屬性文件是依據struct attribute結構體。最終通過函數kobj_attr_show和kobj_attr_store回調到實際的show和store函數(kobject.c)。
state_show()函數主要是顯示當前系統支持哪幾種省電模式。
static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
char *s = buf;
#ifdef CONFIG_SUSPEND //def
int i;
for (i = 0; i < PM_SUSPEND_MAX; i++) {
if (pm_states[i] && valid_state(i))
s += sprintf(s,"%s ", pm_states[i]);
}
#endif
#ifdef CONFIG_HIBERNATION // undef, don't support STD mode
s += sprintf(s, "%s/n", "disk");
#else
if (s != buf)
/* convert the last space to a newline */
*(s-1) = '/n';
#endif
return (s - buf);
}
@ kernel/include/linux/suspend.h
#define PM_SUSPEND_ON ((__force suspend_state_t) 0)
#define PM_SUSPEND_STANDBY ((__force suspend_state_t) 1)
#define PM_SUSPEND_MEM ((__force suspend_state_t) 3)
#define PM_SUSPEND_DISK ((__force suspend_state_t) 4)
#define PM_SUSPEND_MAX ((__force suspend_state_t) 5)
@ kernel/kernel/power/suspend.c
const char *const pm_states[PM_SUSPEND_MAX] = {
#ifdef CONFIG_EARLYSUSPEND // android修改了標準linux的休眠喚醒機制,增加了eraly suspend和late resume機制,如果是android內核,則這個宏是需要定義的。
[PM_SUSPEND_ON] = "on",
#endif
[PM_SUSPEND_STANDBY] = "standby",
[PM_SUSPEND_MEM] = "mem",
};
該函數中值得注意的地方應該是valid_state(i),這個函數是用戶配置的支持省電模式的驗證函數,如果沒有這個驗證過程,cat時候打印出來的模式則是on standby mem,給上層用戶的使用造成困擾。
那這個valid_state()函數在哪裏定義的呢?一般定義於文件kernel/kernel/power/suspend.c
static struct platform_suspend_ops *suspend_ops;
void suspend_set_ops(struct platform_suspend_ops *ops) // 該函數調用見後面
{
mutex_lock(&pm_mutex);
suspend_ops = ops;
mutex_unlock(&pm_mutex);
}
bool valid_state(suspend_state_t state)
{
return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
}
而實際平臺的platform_suspend_ops結構體一般都是在文件arch/arm/mach-xxxx/pm.c中進行定義,對於mtk的平臺是文件mtkpm.c,如下:
@ kernel/include/linux/suspend.h
struct platform_suspend_ops {
int (*valid)(suspend_state_t state);
int (*begin)(suspend_state_t state);
int (*prepare)(void);
int (*prepare_late)(void);
int (*enter)(suspend_state_t state);
void (*wake)(void);
void (*finish)(void);
void (*end)(void);
void (*recover)(void);
};
經過後面的代碼分析,得出瞭如下結論:
休眠喚醒過程依次會執行的函數是:begin,prepare,prepare_late,enter,wake, finish, end。同顏色的函數執行了恰好相反的工作。休眠的時候代碼執行是停留在函數enter中,wake之後也是從suspend的時候停留的地方繼續運行。
至於recover函數貌似只有在pm_test處於devices的模式下,纔會被調用到。
@ kernel/arch/arm/mach-mt6516/mtkpm.c
static struct platform_suspend_ops mtk_pm_ops = {
.valid = mtk_pm_state_valid,
.begin = mtk_pm_begin,
.prepare = mtk_pm_prepare,
.enter = mtk_pm_enter,
.finish = mtk_pm_finish,
.end = mtk_pm_end,
};
static int mtk_pm_state_valid(suspend_state_t pm_state)
{
return pm_state == PM_SUSPEND_MEM ;
}
void mtk_pm_init(void)
{
_Chip_PM_init();
/* Register and set suspend operation */
suspend_set_ops(&mtk_pm_ops);
}
而函數mtk_pm_init()是在函數mt6516_init_irq()中調用。可以看出該平臺只支持mem的省電模式。
state_store()函數:
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
#ifdef CONFIG_SUSPEND // set
#ifdef CONFIG_EARLYSUSPEND //對標準linux而言,這個宏不存在
suspend_state_t state = PM_SUSPEND_ON;
#else
suspend_state_t state = PM_SUSPEND_STANDBY;
#endif
const char * const *s;
#endif
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '/n', n);
len = p ? p - buf : n;
/* First, check if we are requested to hibernate */
if (len == 4 && !strncmp(buf, "disk", len)) {
error = hibernate(); // 如果值是disk,那麼進入STD模式,該模式暫不討論
goto Exit;
}
#ifdef CONFIG_SUSPEND // def
for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
break;
}
if (state < PM_SUSPEND_MAX && *s)
#ifdef CONFIG_EARLYSUSPEND
// android的linux內核會定義該宏,首先進入eraly suspend模式
if (state == PM_SUSPEND_ON || valid_state(state)) {
error = 0;
request_suspend_state(state);
}
#else // 標準linux內核直接enter_state()函數
error = enter_state(state); // kernel/kernel/power/suspend.c
#endif
#endif
Exit:
return error ? error : n;
}