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