1. user space Interface
In kernel/power/Main. C, a set of sysfs attribute files are defined, one of which is:
After the macro is expanded:
staticstruct 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:
staticint __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 staticconst char * off_state = "mem"; staticconst char * on_state = "on "; // open the/sys/power/state attribute file and save the corresponding file descriptor static1_pen_file_descriptors (constchar * 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 erroropening \" % s \ "\ n", paths [I]); g_error = errno; Return-1;} g_fds [I] = FD;} g_error = 0; return 0 ;}
In the end, the Power Management System of the user space calls 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.
intset_screen_state(inton){ ...... initialize_fds(); ...... 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); ...... return 0;}
/*************************************** **************************************** *************/
Statement: the content of this blog is created at http://blog.csdn.net/droidphone. please refer to it for help. Thank you!
/*************************************** **************************************** * ************/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_EARLYSUSPEND structlist_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#endif Exit:return error ? error : n;}
I can't see it. As mentioned in the previous article, suspend to disk performs special processing. Here we compare the passed strings directly, instead of using the subsequent pm_states array. Here I don't care about suspend to disk, so I skipped the hibernate analysis.
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 | = susponded;
Else
Abort = 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:
Figure 3.1 early suspend call Process
However, the whole system is in the so-called idle state, the CPU is still working, and the background process is also working. When will the system truly enter the sleep state? Note that the last key call is missing:
Wake_unlock (& main_wake_lock );
The unlock action will trigger the suspend process of the standard Linux system. This process will be left for discussion in an article.