linux-bk/kernel/power/main.c
<<
>>
Prefs
   1/*
   2 * kernel/power/main.c - PM subsystem core functionality.
   3 *
   4 * Copyright (c) 2003 Patrick Mochel
   5 * Copyright (c) 2003 Open Source Development Lab
   6 * 
   7 * This file is released under the GPLv2
   8 *
   9 */
  10
  11#include <linux/suspend.h>
  12#include <linux/kobject.h>
  13#include <linux/string.h>
  14#include <linux/delay.h>
  15#include <linux/errno.h>
  16#include <linux/init.h>
  17#include <linux/pm.h>
  18
  19
  20#include "power.h"
  21
  22DECLARE_MUTEX(pm_sem);
  23
  24struct pm_ops * pm_ops = NULL;
  25suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN;
  26
  27/**
  28 *      pm_set_ops - Set the global power method table. 
  29 *      @ops:   Pointer to ops structure.
  30 */
  31
  32void pm_set_ops(struct pm_ops * ops)
  33{
  34        down(&pm_sem);
  35        pm_ops = ops;
  36        up(&pm_sem);
  37}
  38
  39
  40/**
  41 *      suspend_prepare - Do prep work before entering low-power state.
  42 *      @state:         State we're entering.
  43 *
  44 *      This is common code that is called for each state that we're 
  45 *      entering. Allocate a console, stop all processes, then make sure
  46 *      the platform can enter the requested state.
  47 */
  48
  49static int suspend_prepare(suspend_state_t state)
  50{
  51        int error = 0;
  52
  53        if (!pm_ops || !pm_ops->enter)
  54                return -EPERM;
  55
  56        pm_prepare_console();
  57
  58        if (freeze_processes()) {
  59                error = -EAGAIN;
  60                goto Thaw;
  61        }
  62
  63        if (pm_ops->prepare) {
  64                if ((error = pm_ops->prepare(state)))
  65                        goto Thaw;
  66        }
  67
  68        if ((error = device_suspend(PMSG_SUSPEND)))
  69                goto Finish;
  70        return 0;
  71 Finish:
  72        if (pm_ops->finish)
  73                pm_ops->finish(state);
  74 Thaw:
  75        thaw_processes();
  76        pm_restore_console();
  77        return error;
  78}
  79
  80
  81static int suspend_enter(suspend_state_t state)
  82{
  83        int error = 0;
  84        unsigned long flags;
  85
  86        local_irq_save(flags);
  87
  88        if ((error = device_power_down(PMSG_SUSPEND)))
  89                goto Done;
  90        error = pm_ops->enter(state);
  91        device_power_up();
  92 Done:
  93        local_irq_restore(flags);
  94        return error;
  95}
  96
  97
  98/**
  99 *      suspend_finish - Do final work before exiting suspend sequence.
 100 *      @state:         State we're coming out of.
 101 *
 102 *      Call platform code to clean up, restart processes, and free the 
 103 *      console that we've allocated. This is not called for suspend-to-disk.
 104 */
 105
 106static void suspend_finish(suspend_state_t state)
 107{
 108        device_resume();
 109        if (pm_ops && pm_ops->finish)
 110                pm_ops->finish(state);
 111        thaw_processes();
 112        pm_restore_console();
 113}
 114
 115
 116
 117
 118char * pm_states[] = {
 119        [PM_SUSPEND_STANDBY]    = "standby",
 120        [PM_SUSPEND_MEM]        = "mem",
 121        [PM_SUSPEND_DISK]       = "disk",
 122        NULL,
 123};
 124
 125
 126/**
 127 *      enter_state - Do common work of entering low-power state.
 128 *      @state:         pm_state structure for state we're entering.
 129 *
 130 *      Make sure we're the only ones trying to enter a sleep state. Fail
 131 *      if someone has beat us to it, since we don't want anything weird to
 132 *      happen when we wake up.
 133 *      Then, do the setup for suspend, enter the state, and cleaup (after
 134 *      we've woken up).
 135 */
 136
 137static int enter_state(suspend_state_t state)
 138{
 139        int error;
 140
 141        if (down_trylock(&pm_sem))
 142                return -EBUSY;
 143
 144        if (state == PM_SUSPEND_DISK) {
 145                error = pm_suspend_disk();
 146                goto Unlock;
 147        }
 148
 149        /* Suspend is hard to get right on SMP. */
 150        if (num_online_cpus() != 1) {
 151                error = -EPERM;
 152                goto Unlock;
 153        }
 154
 155        pr_debug("PM: Preparing system for suspend\n");
 156        if ((error = suspend_prepare(state)))
 157                goto Unlock;
 158
 159        pr_debug("PM: Entering state.\n");
 160        error = suspend_enter(state);
 161
 162        pr_debug("PM: Finishing up.\n");
 163        suspend_finish(state);
 164 Unlock:
 165        up(&pm_sem);
 166        return error;
 167}
 168
 169/*
 170 * This is main interface to the outside world. It needs to be
 171 * called from process context.
 172 */
 173int software_suspend(void)
 174{
 175        return enter_state(PM_SUSPEND_DISK);
 176}
 177
 178
 179/**
 180 *      pm_suspend - Externally visible function for suspending system.
 181 *      @state:         Enumarted value of state to enter.
 182 *
 183 *      Determine whether or not value is within range, get state 
 184 *      structure, and enter (above).
 185 */
 186
 187int pm_suspend(suspend_state_t state)
 188{
 189        if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX)
 190                return enter_state(state);
 191        return -EINVAL;
 192}
 193
 194
 195
 196decl_subsys(power,NULL,NULL);
 197
 198
 199/**
 200 *      state - control system power state.
 201 *
 202 *      show() returns what states are supported, which is hard-coded to
 203 *      'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and
 204 *      'disk' (Suspend-to-Disk).
 205 *
 206 *      store() accepts one of those strings, translates it into the 
 207 *      proper enumerated value, and initiates a suspend transition.
 208 */
 209
 210static ssize_t state_show(struct subsystem * subsys, char * buf)
 211{
 212        int i;
 213        char * s = buf;
 214
 215        for (i = 0; i < PM_SUSPEND_MAX; i++) {
 216                if (pm_states[i])
 217                        s += sprintf(s,"%s ",pm_states[i]);
 218        }
 219        s += sprintf(s,"\n");
 220        return (s - buf);
 221}
 222
 223static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n)
 224{
 225        suspend_state_t state = PM_SUSPEND_STANDBY;
 226        char ** s;
 227        char *p;
 228        int error;
 229        int len;
 230
 231        p = memchr(buf, '\n', n);
 232        len = p ? p - buf : n;
 233
 234        for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
 235                if (*s && !strncmp(buf, *s, len))
 236                        break;
 237        }
 238        if (*s)
 239                error = enter_state(state);
 240        else
 241                error = -EINVAL;
 242        return error ? error : n;
 243}
 244
 245power_attr(state);
 246
 247static struct attribute * g[] = {
 248        &state_attr.attr,
 249        NULL,
 250};
 251
 252static struct attribute_group attr_group = {
 253        .attrs = g,
 254};
 255
 256
 257static int __init pm_init(void)
 258{
 259        int error = subsystem_register(&power_subsys);
 260        if (!error)
 261                error = sysfs_create_group(&power_subsys.kset.kobj,&attr_group);
 262        return error;
 263}
 264
 265core_initcall(pm_init);
 266
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.