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