linux/drivers/acpi/sleep/proc.c
<<
>>
Prefs
   1#include <linux/proc_fs.h>
   2#include <linux/seq_file.h>
   3#include <linux/suspend.h>
   4#include <linux/bcd.h>
   5#include <asm/uaccess.h>
   6
   7#include <acpi/acpi_bus.h>
   8#include <acpi/acpi_drivers.h>
   9
  10#ifdef CONFIG_X86
  11#include <linux/mc146818rtc.h>
  12#endif
  13
  14#include "sleep.h"
  15
  16#define _COMPONENT              ACPI_SYSTEM_COMPONENT
  17
  18/*
  19 * this file provides support for:
  20 * /proc/acpi/sleep
  21 * /proc/acpi/alarm
  22 * /proc/acpi/wakeup
  23 */
  24
  25ACPI_MODULE_NAME("sleep")
  26#ifdef  CONFIG_ACPI_PROCFS
  27static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset)
  28{
  29        int i;
  30
  31        ACPI_FUNCTION_TRACE("acpi_system_sleep_seq_show");
  32
  33        for (i = 0; i <= ACPI_STATE_S5; i++) {
  34                if (sleep_states[i]) {
  35                        seq_printf(seq, "S%d ", i);
  36                }
  37        }
  38
  39        seq_puts(seq, "\n");
  40
  41        return 0;
  42}
  43
  44static int acpi_system_sleep_open_fs(struct inode *inode, struct file *file)
  45{
  46        return single_open(file, acpi_system_sleep_seq_show, PDE(inode)->data);
  47}
  48
  49static ssize_t
  50acpi_system_write_sleep(struct file *file,
  51                        const char __user * buffer, size_t count, loff_t * ppos)
  52{
  53        char str[12];
  54        u32 state = 0;
  55        int error = 0;
  56
  57        if (count > sizeof(str) - 1)
  58                goto Done;
  59        memset(str, 0, sizeof(str));
  60        if (copy_from_user(str, buffer, count))
  61                return -EFAULT;
  62
  63        /* Check for S4 bios request */
  64        if (!strcmp(str, "4b")) {
  65                error = acpi_suspend(4);
  66                goto Done;
  67        }
  68        state = simple_strtoul(str, NULL, 0);
  69#ifdef CONFIG_HIBERNATION
  70        if (state == 4) {
  71                error = hibernate();
  72                goto Done;
  73        }
  74#endif
  75        error = acpi_suspend(state);
  76      Done:
  77        return error ? error : count;
  78}
  79#endif                          /* CONFIG_ACPI_PROCFS */
  80
  81#if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE) || !defined(CONFIG_X86)
  82/* use /sys/class/rtc/rtcX/wakealarm instead; it's not ACPI-specific */
  83#else
  84#define HAVE_ACPI_LEGACY_ALARM
  85#endif
  86
  87#ifdef  HAVE_ACPI_LEGACY_ALARM
  88
  89static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset)
  90{
  91        u32 sec, min, hr;
  92        u32 day, mo, yr, cent = 0;
  93        unsigned char rtc_control = 0;
  94        unsigned long flags;
  95
  96        ACPI_FUNCTION_TRACE("acpi_system_alarm_seq_show");
  97
  98        spin_lock_irqsave(&rtc_lock, flags);
  99
 100        sec = CMOS_READ(RTC_SECONDS_ALARM);
 101        min = CMOS_READ(RTC_MINUTES_ALARM);
 102        hr = CMOS_READ(RTC_HOURS_ALARM);
 103        rtc_control = CMOS_READ(RTC_CONTROL);
 104
 105        /* If we ever get an FACP with proper values... */
 106        if (acpi_gbl_FADT.day_alarm)
 107                /* ACPI spec: only low 6 its should be cared */
 108                day = CMOS_READ(acpi_gbl_FADT.day_alarm) & 0x3F;
 109        else
 110                day = CMOS_READ(RTC_DAY_OF_MONTH);
 111        if (acpi_gbl_FADT.month_alarm)
 112                mo = CMOS_READ(acpi_gbl_FADT.month_alarm);
 113        else
 114                mo = CMOS_READ(RTC_MONTH);
 115        if (acpi_gbl_FADT.century)
 116                cent = CMOS_READ(acpi_gbl_FADT.century);
 117
 118        yr = CMOS_READ(RTC_YEAR);
 119
 120        spin_unlock_irqrestore(&rtc_lock, flags);
 121
 122        if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
 123                BCD_TO_BIN(sec);
 124                BCD_TO_BIN(min);
 125                BCD_TO_BIN(hr);
 126                BCD_TO_BIN(day);
 127                BCD_TO_BIN(mo);
 128                BCD_TO_BIN(yr);
 129                BCD_TO_BIN(cent);
 130        }
 131
 132        /* we're trusting the FADT (see above) */
 133        if (!acpi_gbl_FADT.century)
 134                /* If we're not trusting the FADT, we should at least make it
 135                 * right for _this_ century... ehm, what is _this_ century?
 136                 *
 137                 * TBD:
 138                 *  ASAP: find piece of code in the kernel, e.g. star tracker driver,
 139                 *        which we can trust to determine the century correctly. Atom
 140                 *        watch driver would be nice, too...
 141                 *
 142                 *  if that has not happened, change for first release in 2050:
 143                 *        if (yr<50)
 144                 *                yr += 2100;
 145                 *        else
 146                 *                yr += 2000;   // current line of code
 147                 *
 148                 *  if that has not happened either, please do on 2099/12/31:23:59:59
 149                 *        s/2000/2100
 150                 *
 151                 */
 152                yr += 2000;
 153        else
 154                yr += cent * 100;
 155
 156        seq_printf(seq, "%4.4u-", yr);
 157        (mo > 12) ? seq_puts(seq, "**-") : seq_printf(seq, "%2.2u-", mo);
 158        (day > 31) ? seq_puts(seq, "** ") : seq_printf(seq, "%2.2u ", day);
 159        (hr > 23) ? seq_puts(seq, "**:") : seq_printf(seq, "%2.2u:", hr);
 160        (min > 59) ? seq_puts(seq, "**:") : seq_printf(seq, "%2.2u:", min);
 161        (sec > 59) ? seq_puts(seq, "**\n") : seq_printf(seq, "%2.2u\n", sec);
 162
 163        return 0;
 164}
 165
 166static int acpi_system_alarm_open_fs(struct inode *inode, struct file *file)
 167{
 168        return single_open(file, acpi_system_alarm_seq_show, PDE(inode)->data);
 169}
 170
 171static int get_date_field(char **p, u32 * value)
 172{
 173        char *next = NULL;
 174        char *string_end = NULL;
 175        int result = -EINVAL;
 176
 177        /*
 178         * Try to find delimeter, only to insert null.  The end of the
 179         * string won't have one, but is still valid.
 180         */
 181        next = strpbrk(*p, "- :");
 182        if (next)
 183                *next++ = '\0';
 184
 185        *value = simple_strtoul(*p, &string_end, 10);
 186
 187        /* Signal success if we got a good digit */
 188        if (string_end != *p)
 189                result = 0;
 190
 191        if (next)
 192                *p = next;
 193
 194        return result;
 195}
 196
 197/* Read a possibly BCD register, always return binary */
 198static u32 cmos_bcd_read(int offset, int rtc_control)
 199{
 200        u32 val = CMOS_READ(offset);
 201        if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
 202                BCD_TO_BIN(val);
 203        return val;
 204}
 205
 206/* Write binary value into possibly BCD register */
 207static void cmos_bcd_write(u32 val, int offset, int rtc_control)
 208{
 209        if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
 210                BIN_TO_BCD(val);
 211        CMOS_WRITE(val, offset);
 212}
 213
 214static ssize_t
 215acpi_system_write_alarm(struct file *file,
 216                        const char __user * buffer, size_t count, loff_t * ppos)
 217{
 218        int result = 0;
 219        char alarm_string[30] = { '\0' };
 220        char *p = alarm_string;
 221        u32 sec, min, hr, day, mo, yr;
 222        int adjust = 0;
 223        unsigned char rtc_control = 0;
 224
 225        ACPI_FUNCTION_TRACE("acpi_system_write_alarm");
 226
 227        if (count > sizeof(alarm_string) - 1)
 228                return_VALUE(-EINVAL);
 229
 230        if (copy_from_user(alarm_string, buffer, count))
 231                return_VALUE(-EFAULT);
 232
 233        alarm_string[count] = '\0';
 234
 235        /* check for time adjustment */
 236        if (alarm_string[0] == '+') {
 237                p++;
 238                adjust = 1;
 239        }
 240
 241        if ((result = get_date_field(&p, &yr)))
 242                goto end;
 243        if ((result = get_date_field(&p, &mo)))
 244                goto end;
 245        if ((result = get_date_field(&p, &day)))
 246                goto end;
 247        if ((result = get_date_field(&p, &hr)))
 248                goto end;
 249        if ((result = get_date_field(&p, &min)))
 250                goto end;
 251        if ((result = get_date_field(&p, &sec)))
 252                goto end;
 253
 254        if (sec > 59) {
 255                min += 1;
 256                sec -= 60;
 257        }
 258        if (min > 59) {
 259                hr += 1;
 260                min -= 60;
 261        }
 262        if (hr > 23) {
 263                day += 1;
 264                hr -= 24;
 265        }
 266        if (day > 31) {
 267                mo += 1;
 268                day -= 31;
 269        }
 270        if (mo > 12) {
 271                yr += 1;
 272                mo -= 12;
 273        }
 274
 275        spin_lock_irq(&rtc_lock);
 276
 277        rtc_control = CMOS_READ(RTC_CONTROL);
 278
 279        if (adjust) {
 280                yr += cmos_bcd_read(RTC_YEAR, rtc_control);
 281                mo += cmos_bcd_read(RTC_MONTH, rtc_control);
 282                day += cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control);
 283                hr += cmos_bcd_read(RTC_HOURS, rtc_control);
 284                min += cmos_bcd_read(RTC_MINUTES, rtc_control);
 285                sec += cmos_bcd_read(RTC_SECONDS, rtc_control);
 286        }
 287
 288        spin_unlock_irq(&rtc_lock);
 289
 290        if (sec > 59) {
 291                min++;
 292                sec -= 60;
 293        }
 294        if (min > 59) {
 295                hr++;
 296                min -= 60;
 297        }
 298        if (hr > 23) {
 299                day++;
 300                hr -= 24;
 301        }
 302        if (day > 31) {
 303                mo++;
 304                day -= 31;
 305        }
 306        if (mo > 12) {
 307                yr++;
 308                mo -= 12;
 309        }
 310
 311        spin_lock_irq(&rtc_lock);
 312        /*
 313         * Disable alarm interrupt before setting alarm timer or else
 314         * when ACPI_EVENT_RTC is enabled, a spurious ACPI interrupt occurs
 315         */
 316        rtc_control &= ~RTC_AIE;
 317        CMOS_WRITE(rtc_control, RTC_CONTROL);
 318        CMOS_READ(RTC_INTR_FLAGS);
 319
 320        /* write the fields the rtc knows about */
 321        cmos_bcd_write(hr, RTC_HOURS_ALARM, rtc_control);
 322        cmos_bcd_write(min, RTC_MINUTES_ALARM, rtc_control);
 323        cmos_bcd_write(sec, RTC_SECONDS_ALARM, rtc_control);
 324
 325        /*
 326         * If the system supports an enhanced alarm it will have non-zero
 327         * offsets into the CMOS RAM here -- which for some reason are pointing
 328         * to the RTC area of memory.
 329         */
 330        if (acpi_gbl_FADT.day_alarm)
 331                cmos_bcd_write(day, acpi_gbl_FADT.day_alarm, rtc_control);
 332        if (acpi_gbl_FADT.month_alarm)
 333                cmos_bcd_write(mo, acpi_gbl_FADT.month_alarm, rtc_control);
 334        if (acpi_gbl_FADT.century)
 335                cmos_bcd_write(yr / 100, acpi_gbl_FADT.century, rtc_control);
 336        /* enable the rtc alarm interrupt */
 337        rtc_control |= RTC_AIE;
 338        CMOS_WRITE(rtc_control, RTC_CONTROL);
 339        CMOS_READ(RTC_INTR_FLAGS);
 340
 341        spin_unlock_irq(&rtc_lock);
 342
 343        acpi_clear_event(ACPI_EVENT_RTC);
 344        acpi_enable_event(ACPI_EVENT_RTC, 0);
 345
 346        *ppos += count;
 347
 348        result = 0;
 349      end:
 350        return_VALUE(result ? result : count);
 351}
 352#endif                          /* HAVE_ACPI_LEGACY_ALARM */
 353
 354extern struct list_head acpi_wakeup_device_list;
 355extern spinlock_t acpi_device_lock;
 356
 357static int
 358acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
 359{
 360        struct list_head *node, *next;
 361
 362        seq_printf(seq, "Device\tS-state\t  Status   Sysfs node\n");
 363
 364        spin_lock(&acpi_device_lock);
 365        list_for_each_safe(node, next, &acpi_wakeup_device_list) {
 366                struct acpi_device *dev =
 367                    container_of(node, struct acpi_device, wakeup_list);
 368                struct device *ldev;
 369
 370                if (!dev->wakeup.flags.valid)
 371                        continue;
 372                spin_unlock(&acpi_device_lock);
 373
 374                ldev = acpi_get_physical_device(dev->handle);
 375                seq_printf(seq, "%s\t  S%d\t%c%-8s  ",
 376                           dev->pnp.bus_id,
 377                           (u32) dev->wakeup.sleep_state,
 378                           dev->wakeup.flags.run_wake ? '*' : ' ',
 379                           dev->wakeup.state.enabled ? "enabled" : "disabled");
 380                if (ldev)
 381                        seq_printf(seq, "%s:%s",
 382                                   ldev->bus ? ldev->bus->name : "no-bus",
 383                                   ldev->bus_id);
 384                seq_printf(seq, "\n");
 385                put_device(ldev);
 386
 387                spin_lock(&acpi_device_lock);
 388        }
 389        spin_unlock(&acpi_device_lock);
 390        return 0;
 391}
 392
 393static ssize_t
 394acpi_system_write_wakeup_device(struct file *file,
 395                                const char __user * buffer,
 396                                size_t count, loff_t * ppos)
 397{
 398        struct list_head *node, *next;
 399        char strbuf[5];
 400        char str[5] = "";
 401        int len = count;
 402        struct acpi_device *found_dev = NULL;
 403
 404        if (len > 4)
 405                len = 4;
 406
 407        if (copy_from_user(strbuf, buffer, len))
 408                return -EFAULT;
 409        strbuf[len] = '\0';
 410        sscanf(strbuf, "%s", str);
 411
 412        spin_lock(&acpi_device_lock);
 413        list_for_each_safe(node, next, &acpi_wakeup_device_list) {
 414                struct acpi_device *dev =
 415                    container_of(node, struct acpi_device, wakeup_list);
 416                if (!dev->wakeup.flags.valid)
 417                        continue;
 418
 419                if (!strncmp(dev->pnp.bus_id, str, 4)) {
 420                        dev->wakeup.state.enabled =
 421                            dev->wakeup.state.enabled ? 0 : 1;
 422                        found_dev = dev;
 423                        break;
 424                }
 425        }
 426        if (found_dev) {
 427                list_for_each_safe(node, next, &acpi_wakeup_device_list) {
 428                        struct acpi_device *dev = container_of(node,
 429                                                               struct
 430                                                               acpi_device,
 431                                                               wakeup_list);
 432
 433                        if ((dev != found_dev) &&
 434                            (dev->wakeup.gpe_number ==
 435                             found_dev->wakeup.gpe_number)
 436                            && (dev->wakeup.gpe_device ==
 437                                found_dev->wakeup.gpe_device)) {
 438                                printk(KERN_WARNING
 439                                       "ACPI: '%s' and '%s' have the same GPE, "
 440                                       "can't disable/enable one seperately\n",
 441                                       dev->pnp.bus_id, found_dev->pnp.bus_id);
 442                                dev->wakeup.state.enabled =
 443                                    found_dev->wakeup.state.enabled;
 444                        }
 445                }
 446        }
 447        spin_unlock(&acpi_device_lock);
 448        return count;
 449}
 450
 451static int
 452acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file)
 453{
 454        return single_open(file, acpi_system_wakeup_device_seq_show,
 455                           PDE(inode)->data);
 456}
 457
 458static const struct file_operations acpi_system_wakeup_device_fops = {
 459        .open = acpi_system_wakeup_device_open_fs,
 460        .read = seq_read,
 461        .write = acpi_system_write_wakeup_device,
 462        .llseek = seq_lseek,
 463        .release = single_release,
 464};
 465
 466#ifdef  CONFIG_ACPI_PROCFS
 467static const struct file_operations acpi_system_sleep_fops = {
 468        .open = acpi_system_sleep_open_fs,
 469        .read = seq_read,
 470        .write = acpi_system_write_sleep,
 471        .llseek = seq_lseek,
 472        .release = single_release,
 473};
 474#endif                          /* CONFIG_ACPI_PROCFS */
 475
 476#ifdef  HAVE_ACPI_LEGACY_ALARM
 477static const struct file_operations acpi_system_alarm_fops = {
 478        .open = acpi_system_alarm_open_fs,
 479        .read = seq_read,
 480        .write = acpi_system_write_alarm,
 481        .llseek = seq_lseek,
 482        .release = single_release,
 483};
 484
 485static u32 rtc_handler(void *context)
 486{
 487        acpi_clear_event(ACPI_EVENT_RTC);
 488        acpi_disable_event(ACPI_EVENT_RTC, 0);
 489
 490        return ACPI_INTERRUPT_HANDLED;
 491}
 492#endif                          /* HAVE_ACPI_LEGACY_ALARM */
 493
 494static int __init acpi_sleep_proc_init(void)
 495{
 496        struct proc_dir_entry *entry = NULL;
 497
 498        if (acpi_disabled)
 499                return 0;
 500
 501#ifdef  CONFIG_ACPI_PROCFS
 502        /* 'sleep' [R/W] */
 503        entry =
 504            create_proc_entry("sleep", S_IFREG | S_IRUGO | S_IWUSR,
 505                              acpi_root_dir);
 506        if (entry)
 507                entry->proc_fops = &acpi_system_sleep_fops;
 508#endif                          /* CONFIG_ACPI_PROCFS */
 509
 510#ifdef  HAVE_ACPI_LEGACY_ALARM
 511        /* 'alarm' [R/W] */
 512        entry =
 513            create_proc_entry("alarm", S_IFREG | S_IRUGO | S_IWUSR,
 514                              acpi_root_dir);
 515        if (entry)
 516                entry->proc_fops = &acpi_system_alarm_fops;
 517
 518        acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
 519#endif                          /* HAVE_ACPI_LEGACY_ALARM */
 520
 521        /* 'wakeup device' [R/W] */
 522        entry =
 523            create_proc_entry("wakeup", S_IFREG | S_IRUGO | S_IWUSR,
 524                              acpi_root_dir);
 525        if (entry)
 526                entry->proc_fops = &acpi_system_wakeup_device_fops;
 527
 528        return 0;
 529}
 530
 531late_initcall(acpi_sleep_proc_init);
 532