1. user space Interface
In kernel/power/Main. C, a set of sysfs attribute files are defined, one of which is:
Power_attr (State );
After the macro is expanded:
static struct kobj_attribute state_attr = { \.attr ={ \.name = "state", \.mode = 0644, \}, \.show =state_show, \.store =state_store, \}
Let's take a look at the main. c entry:
static int __init pm_init(void){power_kobj =kobject_create_and_add("power", NULL);if (!power_kobj)return -ENOMEM;return sysfs_create_group(power_kobj,&attr_group);}
Obviously, after the function is executed, a series of attribute files are created under the/sys/power directory, one of which is the/sys/power/State file. The user space writes to the file will cause state_store to be called, and reading the file will cause the state_show function to be called.
Now go back to the Hal layer of Android and check the code: Hardware/libhardware_legacy/power. C:
// Define the command string for writing/sys/power/State static const char * off_state = "mem"; static const char * on_state = "on "; // open the/sys/power/state attribute file and save the corresponding file descriptor static writable pen_file_descriptors (constchar * const paths []) {int I; for (I = 0; I // Finally, the user space power management system will call the set_screen_state function to trigger the suspend process, this function is actually writing the "mem" or "on" command string to the/sys/power/State file. <PRE lang = "C" line = "1"> intset_screen_state (inton) {initialize_fds (); char Buf [32]; int Len; If (on) len = snprintf (BUF, sizeof (BUF), "% s", on_state); elselen = snprintf (BUF, sizeof (BUF), "% s", off_state ); buf [sizeof (BUF)-1] = '\ 0'; Len = write (g_fds [request_state], Buf, Len); Return 0 ;}
2. Data Structures and interfaces in the kernel
Data Structures and interfaces related to earlysuspend are defined in earlysuspend. h.
-Early_suspend Structure
struct early_suspend {#ifdef CONFIG_HAS_EARLYSUSPENDstructlist_head link;int level;void(*suspend)(struct early_suspend *h);void(*resume)(struct early_suspend *h);#endif};
If you want to run the early suspend device, the device driver must register with the power management system. This struct is used to register earlysuspend/lateresume with the power management system. When the power management system starts the suspend process, the callback function suspend will be called. On the contrary, in the final stage of resume, the callback function resume will be called. The level field is used to adjust the position of the struct in the registration linked list. During suspend, the smaller the value of level, the earlier the callback function is called, and the reverse is used in resume. Android pre-defines three levels:
enum {EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,};
If you want your device to perform its early suspend callback before the FB device is disabled, the device driver should set the level value to a value smaller than 150, then register the early_suspend structure with the system. The registration and anti-registration functions are:
void register_early_suspend(struct early_suspend *handler);void unregister_early_suspend(struct early_suspend *handler);
Early_suspend_handlers linked list
All early_suspend structures registered to the system are added to the global linked list early_suspend_handlers in order of level values.
3. Workflow
First, we start with the initialization function in kernel/power/wakelock. C:
static int __init wakelocks_init(void){int ret;int i;......for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)INIT_LIST_HEAD(&active_wake_locks[i]);......wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");wake_lock(&main_wake_lock);wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups");......ret = platform_device_register(&power_device);ret = platform_driver_register(&power_driver);......suspend_work_queue = create_singlethread_workqueue("suspend");......return 0;}
We can see that the array of the initialization active_wake_locks chain table is displayed, and then the main_wake_lock is initialized and locked, and the platform device power_device is registered. We will discuss these arrays, locks, and power_device in subsequent articles, the last action we pay attention to here: Creates a working queue thread suspend_work_queue, which is the core of earlysuspend.
After the system is started, the relevant driver registers the early suspend feature through the register_early_suspend () function. After a period of time, if there is no user activity (such as buttons or touch operations ), the user space's Power Management Service will eventually call the set_screen_state () function mentioned in section 1, and then call state_store () in the kernel through sysfs ():
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t n){#ifdef CONFIG_SUSPEND#ifdef CONFIG_EARLYSUSPENDsuspend_state_t state = PM_SUSPEND_ON;#elsesuspend_state_t state = PM_SUSPEND_STANDBY;#endifconst char * const *s;#endifchar *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();goto Exit;}#ifdef CONFIG_SUSPENDfor (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_EARLYSUSPENDif (state == PM_SUSPEND_ON || valid_state(state)) {error = 0;request_suspend_state(state);}#elseerror = enter_state(state);#endif#endifExit:return error ? error : n;}
Then, the Request status is obtained through the pm_states Array Based on the command string query. By default, the android kernel is configured with config_earlysuspend, so the request_suspend_state () function is called, however, valid_state () will be performed before the function is called, which gives the platform-related code a chance to confirm whether the platform supports the requested power status. For the specific implementation of valid_state (), refer to the kernel code tree.
void request_suspend_state(suspend_state_t new_state){unsigned long irqflags;int old_sleep;spin_lock_irqsave(&state_lock, irqflags);old_sleep = state & SUSPEND_REQUESTED;......if (!old_sleep && new_state != PM_SUSPEND_ON) {state |= SUSPEND_REQUESTED;if (queue_work(suspend_work_queue, &early_suspend_work))pr_info("early_suspend_work is in queue already\n");} else if (old_sleep && new_state == PM_SUSPEND_ON) {state &= ~SUSPEND_REQUESTED;wake_lock(&main_wake_lock);if (!queue_work(suspend_work_queue,&late_resume_work))pr_info("late_resume_work is in queue already\n");}requested_suspend_state = new_state;spin_unlock_irqrestore(&state_lock, irqflags);}
Do you still remember the work queue suspend_woek_queue created during initialization? Based on the previous power status and Request status, request_suspend_state () simply adds early_suspend_work or late_resume_work to suspend_work_queue and schedules them for execution. The working function of early_suspend_work is early_suspend ():
staticvoid early_suspend(struct work_struct *work){struct early_suspend *pos;unsigned long irqflags;int abort = 0;mutex_lock(&early_suspend_lock);spin_lock_irqsave(&state_lock,irqflags);if (state == SUSPEND_REQUESTED)state |= SUSPENDED;elseabort = 1;spin_unlock_irqrestore(&state_lock,irqflags);if (abort) {}list_for_each_entry(pos,&early_suspend_handlers, link) {if (pos->suspend != NULL) {if (debug_mask &DEBUG_SUSPEND)printk(KERN_DEBUG"pos->suspend: %pF begin\n", pos->suspend);pos->suspend(pos);if (debug_mask &DEBUG_SUSPEND)printk(KERN_DEBUG"pos->suspend: %pF finish\n", pos->suspend);}}mutex_unlock(&early_suspend_lock);if (debug_mask & DEBUG_SUSPEND)pr_info("early_suspend:sync\n");sys_sync();abort:spin_lock_irqsave(&state_lock,irqflags);if (state ==SUSPEND_REQUESTED_AND_SUSPENDED)wake_unlock(&main_wake_lock);spin_unlock_irqrestore(&state_lock,irqflags);}
Finally, early_suspend () traverses the early_suspend_handlers linked list, retrieves the early_suspend structure registered by each driver, and calls its suspend callback function. Finally, the main_wake_lock is released, and the entire earlysuspend process is completed. The following sequence diagram clearly shows the entire call process:
Others:
Http://blog.sina.com.cn/s/blog_55465b470100n9yx.html