一、Kernel与HAL接口分析
Kernel与HAL接口是通过/sys/power下面的一系统文件来实现的,如:/sys/power/state
Kernel中/sys/power下的文件实现过程如下:
1. sysfs的属性文件
在kernel/power/main.c中,定义了一组sysfs的属性文件:
static struct attribute * g[] = { &state_attr.attr, #ifdef CONFIG_PM_TRACE &pm_trace_attr.attr, &pm_trace_dev_match_attr.attr, #endif #ifdef CONFIG_PM_SLEEP &pm_async_attr.attr, &wakeup_count_attr.attr, #ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, #endif #ifdef CONFIG_USER_WAKELOCK &wake_lock_attr.attr, &wake_unlock_attr.attr, #endif #endif NULL, }; static struct attribute_group attr_group = { .attrs = g, };
如state_attr定义如下:
power_attr(state); #define power_attr(_name) \ static struct kobj_attribute _name##_attr = { \ .attr = { \ .name = __stringify(_name), \ .mode = 0644, \ }, \ .show = _name##_show, \ .store = _name##_store, \ } 即: static struct kobj_attribute state_attr = { \ .attr = { \ .name = "state", \ .mode = 0644, \ }, \ .show = state_show, \ .store = state_store, \ }
2. 创建sysfs文件
static int __init pm_init(void) { int error = pm_start_workqueue(); if (error) return error; hibernate_image_size_init(); hibernate_reserved_size_init(); power_kobj = kobject_create_and_add("power", NULL); if (!power_kobj) return -ENOMEM; return sysfs_create_group(power_kobj, &attr_group); }
pm_init函数执行后,会创建/sys/power目录,且目录下会建立一系列属性文件,其中一个是/sys/power/state文件。用户空间写该文件将会导致state_store被调用,读该文件将会导致state_show函数被调用。
二、HAL代码分析
HAL代码位于hardware/libhardware_legacy/power/power.c
1. 路径及写入字符串定义
const char * const NEW_PATHS[] = { "/sys/power/wake_lock", "/sys/power/wake_unlock", "/sys/power/state" }; static const char *off_state = "mem"; static const char *on_state = "on";
2. 打开上面定义的三个文件
static int open_file_descriptors(const char * const paths[]) { int i; for (i=0; i<OUR_FD_COUNT; i++) { int fd = open(paths[i], O_RDWR); if (fd < 0) { fprintf(stderr, "fatal error opening \"%s\"\n", paths[i]); g_error = errno; return -1; } g_fds[i] = fd; } g_error = 0; return 0; }
3. 修改状态
用户态的电源管理系统会调用set_screen_state函数来触发suspend的流程,该函数实际上就是往/sys/power/state文件写入"mem"或"on"命令字符串。
int set_screen_state(int on) { QEMU_FALLBACK(set_screen_state(on)); LOGI("*** set_screen_state %d", on); initialize_fds(); //LOGI("go_to_sleep eventTime=%lld now=%lld g_error=%s\n", eventTime, // systemTime(), strerror(g_error)); if (g_error) goto failure; char buf[32]; int len; if(on) len = snprintf(buf, sizeof(buf), "%s", on_state); else len = snprintf(buf, sizeof(buf), "%s", off_state); buf[sizeof(buf) - 1] = '\0'; len = write(g_fds[REQUEST_STATE], buf, len); if(len < 0) { failure: LOGE("Failed setting last user activity: g_error=%d\n", g_error); } return 0; }