Android-based power management of the Linux kernel: Early Suspend

1. The user interface space
in the kernel / power / main.c in sysfs defines a set of attributes of a file, where a definition is:

power_attr (state);
the expansion after this macro:

staticstruct kobj_attribute state_attr = { \
 
        .attr ={                                \
 
                 .name = "state",    \
 
                 .mode = 0644,                           \
 
        },                                            \
 
        .show      =state_show,                           \
 
        .store       =state_store,                  \
 
}

Let us look at the entrance main.c:

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, will generate / sys / power directory, the directory will establish a series of property files, one of which is / sys / power / state files. Write to user space to the file will result in state_store is invoked to read the file will cause state_show function is called.

Now back to Android's HAL layer, look at the code: hardware / libhardware_legacy / power / power.c:

 // definition of write / sys / power / state command string
 
staticconst off_state char * = "MEM";
 
staticconst on_state char * = "ON";
 
// Open / sys / power / state file attributes, file save the corresponding descriptor
 
staticint
 
open_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;
 
}

Eventually, the power management system will call the user-space set_screen_state function to trigger suspend the process, the function is actually to / sys / power / state files are written "mem" or "on" command string.

int
 
set_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;
 
}

/ ************************************************* ******************************************* /
Disclaimer: this blog content are http://blog.csdn.net/droidphone by the original, please indicate the source, thank you!
/ ************************************************* ******************************************* /

2. The kernel interfaces and data structures
associated with earlysuspend data structures and interfaces are defined in the earlysuspend.h.

- early_suspend structure

early_suspend {struct
 
#ifdef CONFIG_HAS_EARLYSUSPEND
 
         structlist_head Link;
 
         int Level;
 
         void (* Suspend) (struct early_suspend * H);
 
         void (* Resume) (struct early_suspend * H);
 
#endif
 
};
want to perform early suspend the device, his equipment drivers need to register to the power management system, the structures used to register earlysuspend / lateresume to the power management system, when the power management system suspend process starts, suspend callback function will be called the contrary, the final stage of the resume callback function will resume it is called, a field level of the structure for adjusting the position of the registration list, the smaller the suspend time, the value level, the callback function is called the earlier time, in turn, when the resume. Android predefined level 3 rating:

{enum
    EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,
    EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,
    EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,
};
if you want to execute your device FB device is prohibited until his early suspend callback, the device driver should be set to a value smaller than the level of a 150 value, then the system configuration register early_suspend. Registration and the registration function is:
 void register_early_suspend (struct early_suspend * Handler);
 void unregister_early_suspend (struct early_suspend * Handler);
- early_suspend_handlers list

All registered in the system configuration of early_suspend level values ​​are added sequentially according to the global list of early_suspend_handlers.

3. workflow
First, we start from the kernel / power / wakelock.c initialization function:

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;
}

You can see the display array initialization active_wake_locks list, then initialized and locked main_wake_lock, equipment power_device Registry Platform, these arrays, locks and power_device we'll discuss in a future article, we focus here last action: creating a work queue threads suspend_work_queue the work queue is at the heart of earlysuspend.

After completion of system startup, the relevant driver by register_early_suspend () function to register the early suspend feature, after waiting for some time, if there is no user activity (such as buttons, touch operation, etc.), user space power management service will eventually call first section mentioned set_screen_state () function, through sysfs, then calls to the kernel state_store ():

static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
               const char *buf, size_t n)
{
#ifdef CONFIG_SUSPEND
#ifdef CONFIG_EARLYSUSPEND
    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();
  goto Exit;
    }
 
#ifdef CONFIG_SUSPEND
    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
        if (state == PM_SUSPEND_ON || valid_state(state)) {
            error = 0;
            request_suspend_state(state);
        }
#else
        error = enter_state(state);
#endif
#endif
 
 Exit:
    return error ? error : n;
}

Did not see, said the previous article, suspend to disk to do a special deal, where a direct comparison of the string passed in, rather than using an array of subsequent pm_states, here I do not care suspend to disk, so skip the analysis of hibernate .

Then, by pm_states array, according to the command string query to get status of the request, by default, Android's kernel will be configured CONFIG_EARLYSUSPEND, so calls request_suspend_state () function, but before calling the function will first valid_state () it, this gives a platform-specific code for the opportunity to confirm whether the platform supports power state requested. valid_state () refer to the specific implementation of 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);
}

Remember the work queue suspend_woek_queue build upon initialization of the front right? The power status and status requests before, request_suspend_state () is simply added to the suspend_work_queue early_suspend_work or late_resume_work schedule and execute them. early_suspend_work work function 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;

        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 saw it, early_suspend () traversal early_suspend_handlers list, removed from early_suspend structure of each registered driver, then call it suspend callback function. Finally, the release main_wake_lock lock, bringing the entire earlysuspend the process is complete. The following sequence diagram clearly shows that the whole process of call:

                                                                                                     Figure 3.1 early suspend the calling process

 

However, when the entire system is only in the so-called idle state, cpu still working, background processes are at work, what time the system will actually go to sleep? He notes that the last sentence is not critical call:

wake_unlock(&main_wake_lock);

Unlock action will trigger the standard linux suspend the process, this process is left to write an essay discussing it.
 

Guess you like

Origin blog.csdn.net/ds1130071727/article/details/94716184