Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.22-rc7 531 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 17ACPI_MODULE_NAME("sleep") 18#ifdef CONFIG_ACPI_SLEEP_PROC_SLEEP 19static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset) 20{ 21 int i; 22 23 ACPI_FUNCTION_TRACE("acpi_system_sleep_seq_show"); 24 25 for (i = 0; i <= ACPI_STATE_S5; i++) { 26 if (sleep_states[i]) { 27 seq_printf(seq, "S%d ", i); 28 } 29 } 30 31 seq_puts(seq, "\n"); 32 33 return 0; 34} 35 36static int acpi_system_sleep_open_fs(struct inode *inode, struct file *file) 37{ 38 return single_open(file, acpi_system_sleep_seq_show, PDE(inode)->data); 39} 40 41static ssize_t 42acpi_system_write_sleep(struct file *file, 43 const char __user * buffer, size_t count, loff_t * ppos) 44{ 45 char str[12]; 46 u32 state = 0; 47 int error = 0; 48 49 if (count > sizeof(str) - 1) 50 goto Done; 51 memset(str, 0, sizeof(str)); 52 if (copy_from_user(str, buffer, count)) 53 return -EFAULT; 54 55 /* Check for S4 bios request */ 56 if (!strcmp(str, "4b")) { 57 error = acpi_suspend(4); 58 goto Done; 59 } 60 state = simple_strtoul(str, NULL, 0); 61#ifdef CONFIG_SOFTWARE_SUSPEND 62 if (state == 4) { 63 error = hibernate(); 64 goto Done; 65 } 66#endif 67 error = acpi_suspend(state); 68 Done: 69 return error ? error : count; 70} 71#endif /* CONFIG_ACPI_SLEEP_PROC_SLEEP */ 72 73#if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE) 74/* use /sys/class/rtc/rtcX/wakealarm instead; it's not ACPI-specific */ 75#else 76#define HAVE_ACPI_LEGACY_ALARM 77#endif 78 79#ifdef HAVE_ACPI_LEGACY_ALARM 80 81static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset) 82{ 83 u32 sec, min, hr; 84 u32 day, mo, yr, cent = 0; 85 unsigned char rtc_control = 0; 86 unsigned long flags; 87 88 ACPI_FUNCTION_TRACE("acpi_system_alarm_seq_show"); 89 90 spin_lock_irqsave(&rtc_lock, flags); 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_alarm) 99 /* ACPI spec: only low 6 its should be cared */ 100 day = CMOS_READ(acpi_gbl_FADT.day_alarm) & 0x3F; 101 else 102 day = CMOS_READ(RTC_DAY_OF_MONTH); 103 if (acpi_gbl_FADT.month_alarm) 104 mo = CMOS_READ(acpi_gbl_FADT.month_alarm); 105 else 106 mo = CMOS_READ(RTC_MONTH); 107 if (acpi_gbl_FADT.century) 108 cent = CMOS_READ(acpi_gbl_FADT.century); 109 110 yr = CMOS_READ(RTC_YEAR); 111 112 spin_unlock_irqrestore(&rtc_lock, flags); 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 BCD_TO_BIN(cent); 122 } 123 124 /* we're trusting the FADT (see above) */ 125 if (!acpi_gbl_FADT.century) 126 /* If we're not trusting the FADT, we should at least make it 127 * right for _this_ century... ehm, what is _this_ century? 128 * 129 * TBD: 130 * ASAP: find piece of code in the kernel, e.g. star tracker driver, 131 * which we can trust to determine the century correctly. Atom 132 * watch driver would be nice, too... 133 * 134 * if that has not happened, change for first release in 2050: 135 * if (yr<50) 136 * yr += 2100; 137 * else 138 * yr += 2000; // current line of code 139 * 140 * if that has not happened either, please do on 2099/12/31:23:59:59 141 * s/2000/2100 142 * 143 */ 144 yr += 2000; 145 else 146 yr += cent * 100; 147 148 seq_printf(seq, "%4.4u-", yr); 149 (mo > 12) ? seq_puts(seq, "**-") : seq_printf(seq, "%2.2u-", mo); 150 (day > 31) ? seq_puts(seq, "** ") : seq_printf(seq, "%2.2u ", day); 151 (hr > 23) ? seq_puts(seq, "**:") : seq_printf(seq, "%2.2u:", hr); 152 (min > 59) ? seq_puts(seq, "**:") : seq_printf(seq, "%2.2u:", min); 153 (sec > 59) ? seq_puts(seq, "**\n") : seq_printf(seq, "%2.2u\n", sec); 154 155 return 0; 156} 157 158static int acpi_system_alarm_open_fs(struct inode *inode, struct file *file) 159{ 160 return single_open(file, acpi_system_alarm_seq_show, PDE(inode)->data); 161} 162 163static int get_date_field(char **p, u32 * value) 164{ 165 char *next = NULL; 166 char *string_end = NULL; 167 int result = -EINVAL; 168 169 /* 170 * Try to find delimeter, only to insert null. The end of the 171 * string won't have one, but is still valid. 172 */ 173 next = strpbrk(*p, "- :"); 174 if (next) 175 *next++ = '\0'; 176 177 *value = simple_strtoul(*p, &string_end, 10); 178 179 /* Signal success if we got a good digit */ 180 if (string_end != *p) 181 result = 0; 182 183 if (next) 184 *p = next; 185 186 return result; 187} 188 189static ssize_t 190acpi_system_write_alarm(struct file *file, 191 const char __user * buffer, size_t count, loff_t * ppos) 192{ 193 int result = 0; 194 char alarm_string[30] = { '\0' }; 195 char *p = alarm_string; 196 u32 sec, min, hr, day, mo, yr; 197 int adjust = 0; 198 unsigned char rtc_control = 0; 199 200 ACPI_FUNCTION_TRACE("acpi_system_write_alarm"); 201 202 if (count > sizeof(alarm_string) - 1) 203 return_VALUE(-EINVAL); 204 205 if (copy_from_user(alarm_string, buffer, count)) 206 return_VALUE(-EFAULT); 207 208 alarm_string[count] = '\0'; 209 210 /* check for time adjustment */ 211 if (alarm_string[0] == '+') { 212 p++; 213 adjust = 1; 214 } 215 216 if ((result = get_date_field(&p, &yr))) 217 goto end; 218 if ((result = get_date_field(&p, &mo))) 219 goto end; 220 if ((result = get_date_field(&p, &day))) 221 goto end; 222 if ((result = get_date_field(&p, &hr))) 223 goto end; 224 if ((result = get_date_field(&p, &min))) 225 goto end; 226 if ((result = get_date_field(&p, &sec))) 227 goto end; 228 229 if (sec > 59) { 230 min += 1; 231 sec -= 60; 232 } 233 if (min > 59) { 234 hr += 1; 235 min -= 60; 236 } 237 if (hr > 23) { 238 day += 1; 239 hr -= 24; 240 } 241 if (day > 31) { 242 mo += 1; 243 day -= 31; 244 } 245 if (mo > 12) { 246 yr += 1; 247 mo -= 12; 248 } 249 250 spin_lock_irq(&rtc_lock); 251 252 rtc_control = CMOS_READ(RTC_CONTROL); 253 if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 254 BIN_TO_BCD(yr); 255 BIN_TO_BCD(mo); 256 BIN_TO_BCD(day); 257 BIN_TO_BCD(hr); 258 BIN_TO_BCD(min); 259 BIN_TO_BCD(sec); 260 } 261 262 if (adjust) { 263 yr += CMOS_READ(RTC_YEAR); 264 mo += CMOS_READ(RTC_MONTH); 265 day += CMOS_READ(RTC_DAY_OF_MONTH); 266 hr += CMOS_READ(RTC_HOURS); 267 min += CMOS_READ(RTC_MINUTES); 268 sec += CMOS_READ(RTC_SECONDS); 269 } 270 271 spin_unlock_irq(&rtc_lock); 272 273 if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 274 BCD_TO_BIN(yr); 275 BCD_TO_BIN(mo); 276 BCD_TO_BIN(day); 277 BCD_TO_BIN(hr); 278 BCD_TO_BIN(min); 279 BCD_TO_BIN(sec); 280 } 281 282 if (sec > 59) { 283 min++; 284 sec -= 60; 285 } 286 if (min > 59) { 287 hr++; 288 min -= 60; 289 } 290 if (hr > 23) { 291 day++; 292 hr -= 24; 293 } 294 if (day > 31) { 295 mo++; 296 day -= 31; 297 } 298 if (mo > 12) { 299 yr++; 300 mo -= 12; 301 } 302 if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 303 BIN_TO_BCD(yr); 304 BIN_TO_BCD(mo); 305 BIN_TO_BCD(day); 306 BIN_TO_BCD(hr); 307 BIN_TO_BCD(min); 308 BIN_TO_BCD(sec); 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_WRITE(hr, RTC_HOURS_ALARM); 322 CMOS_WRITE(min, RTC_MINUTES_ALARM); 323 CMOS_WRITE(sec, RTC_SECONDS_ALARM); 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_WRITE(day, acpi_gbl_FADT.day_alarm); 332 if (acpi_gbl_FADT.month_alarm) 333 CMOS_WRITE(mo, acpi_gbl_FADT.month_alarm); 334 if (acpi_gbl_FADT.century) 335 CMOS_WRITE(yr / 100, acpi_gbl_FADT.century); 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_SLEEP_PROC_SLEEP 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_SLEEP_PROC_SLEEP */ 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_SLEEP_PROC_SLEEP 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 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);