Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.23 539 lines 12 kB view raw
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 197static ssize_t 198acpi_system_write_alarm(struct file *file, 199 const char __user * buffer, size_t count, loff_t * ppos) 200{ 201 int result = 0; 202 char alarm_string[30] = { '\0' }; 203 char *p = alarm_string; 204 u32 sec, min, hr, day, mo, yr; 205 int adjust = 0; 206 unsigned char rtc_control = 0; 207 208 ACPI_FUNCTION_TRACE("acpi_system_write_alarm"); 209 210 if (count > sizeof(alarm_string) - 1) 211 return_VALUE(-EINVAL); 212 213 if (copy_from_user(alarm_string, buffer, count)) 214 return_VALUE(-EFAULT); 215 216 alarm_string[count] = '\0'; 217 218 /* check for time adjustment */ 219 if (alarm_string[0] == '+') { 220 p++; 221 adjust = 1; 222 } 223 224 if ((result = get_date_field(&p, &yr))) 225 goto end; 226 if ((result = get_date_field(&p, &mo))) 227 goto end; 228 if ((result = get_date_field(&p, &day))) 229 goto end; 230 if ((result = get_date_field(&p, &hr))) 231 goto end; 232 if ((result = get_date_field(&p, &min))) 233 goto end; 234 if ((result = get_date_field(&p, &sec))) 235 goto end; 236 237 if (sec > 59) { 238 min += 1; 239 sec -= 60; 240 } 241 if (min > 59) { 242 hr += 1; 243 min -= 60; 244 } 245 if (hr > 23) { 246 day += 1; 247 hr -= 24; 248 } 249 if (day > 31) { 250 mo += 1; 251 day -= 31; 252 } 253 if (mo > 12) { 254 yr += 1; 255 mo -= 12; 256 } 257 258 spin_lock_irq(&rtc_lock); 259 260 rtc_control = CMOS_READ(RTC_CONTROL); 261 if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 262 BIN_TO_BCD(yr); 263 BIN_TO_BCD(mo); 264 BIN_TO_BCD(day); 265 BIN_TO_BCD(hr); 266 BIN_TO_BCD(min); 267 BIN_TO_BCD(sec); 268 } 269 270 if (adjust) { 271 yr += CMOS_READ(RTC_YEAR); 272 mo += CMOS_READ(RTC_MONTH); 273 day += CMOS_READ(RTC_DAY_OF_MONTH); 274 hr += CMOS_READ(RTC_HOURS); 275 min += CMOS_READ(RTC_MINUTES); 276 sec += CMOS_READ(RTC_SECONDS); 277 } 278 279 spin_unlock_irq(&rtc_lock); 280 281 if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 282 BCD_TO_BIN(yr); 283 BCD_TO_BIN(mo); 284 BCD_TO_BIN(day); 285 BCD_TO_BIN(hr); 286 BCD_TO_BIN(min); 287 BCD_TO_BIN(sec); 288 } 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 if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 311 BIN_TO_BCD(yr); 312 BIN_TO_BCD(mo); 313 BIN_TO_BCD(day); 314 BIN_TO_BCD(hr); 315 BIN_TO_BCD(min); 316 BIN_TO_BCD(sec); 317 } 318 319 spin_lock_irq(&rtc_lock); 320 /* 321 * Disable alarm interrupt before setting alarm timer or else 322 * when ACPI_EVENT_RTC is enabled, a spurious ACPI interrupt occurs 323 */ 324 rtc_control &= ~RTC_AIE; 325 CMOS_WRITE(rtc_control, RTC_CONTROL); 326 CMOS_READ(RTC_INTR_FLAGS); 327 328 /* write the fields the rtc knows about */ 329 CMOS_WRITE(hr, RTC_HOURS_ALARM); 330 CMOS_WRITE(min, RTC_MINUTES_ALARM); 331 CMOS_WRITE(sec, RTC_SECONDS_ALARM); 332 333 /* 334 * If the system supports an enhanced alarm it will have non-zero 335 * offsets into the CMOS RAM here -- which for some reason are pointing 336 * to the RTC area of memory. 337 */ 338 if (acpi_gbl_FADT.day_alarm) 339 CMOS_WRITE(day, acpi_gbl_FADT.day_alarm); 340 if (acpi_gbl_FADT.month_alarm) 341 CMOS_WRITE(mo, acpi_gbl_FADT.month_alarm); 342 if (acpi_gbl_FADT.century) 343 CMOS_WRITE(yr / 100, acpi_gbl_FADT.century); 344 /* enable the rtc alarm interrupt */ 345 rtc_control |= RTC_AIE; 346 CMOS_WRITE(rtc_control, RTC_CONTROL); 347 CMOS_READ(RTC_INTR_FLAGS); 348 349 spin_unlock_irq(&rtc_lock); 350 351 acpi_clear_event(ACPI_EVENT_RTC); 352 acpi_enable_event(ACPI_EVENT_RTC, 0); 353 354 *ppos += count; 355 356 result = 0; 357 end: 358 return_VALUE(result ? result : count); 359} 360#endif /* HAVE_ACPI_LEGACY_ALARM */ 361 362extern struct list_head acpi_wakeup_device_list; 363extern spinlock_t acpi_device_lock; 364 365static int 366acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) 367{ 368 struct list_head *node, *next; 369 370 seq_printf(seq, "Device\tS-state\t Status Sysfs node\n"); 371 372 spin_lock(&acpi_device_lock); 373 list_for_each_safe(node, next, &acpi_wakeup_device_list) { 374 struct acpi_device *dev = 375 container_of(node, struct acpi_device, wakeup_list); 376 struct device *ldev; 377 378 if (!dev->wakeup.flags.valid) 379 continue; 380 spin_unlock(&acpi_device_lock); 381 382 ldev = acpi_get_physical_device(dev->handle); 383 seq_printf(seq, "%s\t S%d\t%c%-8s ", 384 dev->pnp.bus_id, 385 (u32) dev->wakeup.sleep_state, 386 dev->wakeup.flags.run_wake ? '*' : ' ', 387 dev->wakeup.state.enabled ? "enabled" : "disabled"); 388 if (ldev) 389 seq_printf(seq, "%s:%s", 390 ldev->bus ? ldev->bus->name : "no-bus", 391 ldev->bus_id); 392 seq_printf(seq, "\n"); 393 put_device(ldev); 394 395 spin_lock(&acpi_device_lock); 396 } 397 spin_unlock(&acpi_device_lock); 398 return 0; 399} 400 401static ssize_t 402acpi_system_write_wakeup_device(struct file *file, 403 const char __user * buffer, 404 size_t count, loff_t * ppos) 405{ 406 struct list_head *node, *next; 407 char strbuf[5]; 408 char str[5] = ""; 409 int len = count; 410 struct acpi_device *found_dev = NULL; 411 412 if (len > 4) 413 len = 4; 414 415 if (copy_from_user(strbuf, buffer, len)) 416 return -EFAULT; 417 strbuf[len] = '\0'; 418 sscanf(strbuf, "%s", str); 419 420 spin_lock(&acpi_device_lock); 421 list_for_each_safe(node, next, &acpi_wakeup_device_list) { 422 struct acpi_device *dev = 423 container_of(node, struct acpi_device, wakeup_list); 424 if (!dev->wakeup.flags.valid) 425 continue; 426 427 if (!strncmp(dev->pnp.bus_id, str, 4)) { 428 dev->wakeup.state.enabled = 429 dev->wakeup.state.enabled ? 0 : 1; 430 found_dev = dev; 431 break; 432 } 433 } 434 if (found_dev) { 435 list_for_each_safe(node, next, &acpi_wakeup_device_list) { 436 struct acpi_device *dev = container_of(node, 437 struct 438 acpi_device, 439 wakeup_list); 440 441 if ((dev != found_dev) && 442 (dev->wakeup.gpe_number == 443 found_dev->wakeup.gpe_number) 444 && (dev->wakeup.gpe_device == 445 found_dev->wakeup.gpe_device)) { 446 printk(KERN_WARNING 447 "ACPI: '%s' and '%s' have the same GPE, " 448 "can't disable/enable one seperately\n", 449 dev->pnp.bus_id, found_dev->pnp.bus_id); 450 dev->wakeup.state.enabled = 451 found_dev->wakeup.state.enabled; 452 } 453 } 454 } 455 spin_unlock(&acpi_device_lock); 456 return count; 457} 458 459static int 460acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file) 461{ 462 return single_open(file, acpi_system_wakeup_device_seq_show, 463 PDE(inode)->data); 464} 465 466static const struct file_operations acpi_system_wakeup_device_fops = { 467 .open = acpi_system_wakeup_device_open_fs, 468 .read = seq_read, 469 .write = acpi_system_write_wakeup_device, 470 .llseek = seq_lseek, 471 .release = single_release, 472}; 473 474#ifdef CONFIG_ACPI_PROCFS 475static const struct file_operations acpi_system_sleep_fops = { 476 .open = acpi_system_sleep_open_fs, 477 .read = seq_read, 478 .write = acpi_system_write_sleep, 479 .llseek = seq_lseek, 480 .release = single_release, 481}; 482#endif /* CONFIG_ACPI_PROCFS */ 483 484#ifdef HAVE_ACPI_LEGACY_ALARM 485static const struct file_operations acpi_system_alarm_fops = { 486 .open = acpi_system_alarm_open_fs, 487 .read = seq_read, 488 .write = acpi_system_write_alarm, 489 .llseek = seq_lseek, 490 .release = single_release, 491}; 492 493static u32 rtc_handler(void *context) 494{ 495 acpi_clear_event(ACPI_EVENT_RTC); 496 acpi_disable_event(ACPI_EVENT_RTC, 0); 497 498 return ACPI_INTERRUPT_HANDLED; 499} 500#endif /* HAVE_ACPI_LEGACY_ALARM */ 501 502static int __init acpi_sleep_proc_init(void) 503{ 504 struct proc_dir_entry *entry = NULL; 505 506 if (acpi_disabled) 507 return 0; 508 509#ifdef CONFIG_ACPI_PROCFS 510 /* 'sleep' [R/W] */ 511 entry = 512 create_proc_entry("sleep", S_IFREG | S_IRUGO | S_IWUSR, 513 acpi_root_dir); 514 if (entry) 515 entry->proc_fops = &acpi_system_sleep_fops; 516#endif /* CONFIG_ACPI_PROCFS */ 517 518#ifdef HAVE_ACPI_LEGACY_ALARM 519 /* 'alarm' [R/W] */ 520 entry = 521 create_proc_entry("alarm", S_IFREG | S_IRUGO | S_IWUSR, 522 acpi_root_dir); 523 if (entry) 524 entry->proc_fops = &acpi_system_alarm_fops; 525 526 acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL); 527#endif /* HAVE_ACPI_LEGACY_ALARM */ 528 529 /* 'wakeup device' [R/W] */ 530 entry = 531 create_proc_entry("wakeup", S_IFREG | S_IRUGO | S_IWUSR, 532 acpi_root_dir); 533 if (entry) 534 entry->proc_fops = &acpi_system_wakeup_device_fops; 535 536 return 0; 537} 538 539late_initcall(acpi_sleep_proc_init);