Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.30-rc2 626 lines 16 kB view raw
1/* 2 * acpi_system.c - ACPI System Driver ($Revision: 63 $) 3 * 4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 6 * 7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or (at 12 * your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with this program; if not, write to the Free Software Foundation, Inc., 21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 22 * 23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24 */ 25 26#include <linux/proc_fs.h> 27#include <linux/seq_file.h> 28#include <linux/init.h> 29#include <linux/string.h> 30#include <asm/uaccess.h> 31 32#include <acpi/acpi_drivers.h> 33 34#define _COMPONENT ACPI_SYSTEM_COMPONENT 35ACPI_MODULE_NAME("system"); 36 37#define ACPI_SYSTEM_CLASS "system" 38#define ACPI_SYSTEM_DEVICE_NAME "System" 39 40u32 acpi_irq_handled; 41 42/* 43 * Make ACPICA version work as module param 44 */ 45static int param_get_acpica_version(char *buffer, struct kernel_param *kp) 46{ 47 int result; 48 49 result = sprintf(buffer, "%x", ACPI_CA_VERSION); 50 51 return result; 52} 53 54module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444); 55 56/* -------------------------------------------------------------------------- 57 FS Interface (/sys) 58 -------------------------------------------------------------------------- */ 59static LIST_HEAD(acpi_table_attr_list); 60static struct kobject *tables_kobj; 61static struct kobject *dynamic_tables_kobj; 62 63struct acpi_table_attr { 64 struct bin_attribute attr; 65 char name[8]; 66 int instance; 67 struct list_head node; 68}; 69 70static ssize_t acpi_table_show(struct kobject *kobj, 71 struct bin_attribute *bin_attr, char *buf, 72 loff_t offset, size_t count) 73{ 74 struct acpi_table_attr *table_attr = 75 container_of(bin_attr, struct acpi_table_attr, attr); 76 struct acpi_table_header *table_header = NULL; 77 acpi_status status; 78 char name[ACPI_NAME_SIZE]; 79 80 if (strncmp(table_attr->name, "NULL", 4)) 81 memcpy(name, table_attr->name, ACPI_NAME_SIZE); 82 else 83 memcpy(name, "\0\0\0\0", 4); 84 85 status = 86 acpi_get_table(name, table_attr->instance, 87 &table_header); 88 if (ACPI_FAILURE(status)) 89 return -ENODEV; 90 91 return memory_read_from_buffer(buf, count, &offset, 92 table_header, table_header->length); 93} 94 95static void acpi_table_attr_init(struct acpi_table_attr *table_attr, 96 struct acpi_table_header *table_header) 97{ 98 struct acpi_table_header *header = NULL; 99 struct acpi_table_attr *attr = NULL; 100 101 if (table_header->signature[0] != '\0') 102 memcpy(table_attr->name, table_header->signature, 103 ACPI_NAME_SIZE); 104 else 105 memcpy(table_attr->name, "NULL", 4); 106 107 list_for_each_entry(attr, &acpi_table_attr_list, node) { 108 if (!memcmp(table_attr->name, attr->name, ACPI_NAME_SIZE)) 109 if (table_attr->instance < attr->instance) 110 table_attr->instance = attr->instance; 111 } 112 table_attr->instance++; 113 114 if (table_attr->instance > 1 || (table_attr->instance == 1 && 115 !acpi_get_table 116 (table_header->signature, 2, &header))) 117 sprintf(table_attr->name + ACPI_NAME_SIZE, "%d", 118 table_attr->instance); 119 120 table_attr->attr.size = 0; 121 table_attr->attr.read = acpi_table_show; 122 table_attr->attr.attr.name = table_attr->name; 123 table_attr->attr.attr.mode = 0444; 124 125 return; 126} 127 128static acpi_status 129acpi_sysfs_table_handler(u32 event, void *table, void *context) 130{ 131 struct acpi_table_attr *table_attr; 132 133 switch (event) { 134 case ACPI_TABLE_EVENT_LOAD: 135 table_attr = 136 kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL); 137 if (!table_attr) 138 return AE_NO_MEMORY; 139 140 acpi_table_attr_init(table_attr, table); 141 if (sysfs_create_bin_file(dynamic_tables_kobj, 142 &table_attr->attr)) { 143 kfree(table_attr); 144 return AE_ERROR; 145 } else 146 list_add_tail(&table_attr->node, 147 &acpi_table_attr_list); 148 break; 149 case ACPI_TABLE_EVENT_UNLOAD: 150 /* 151 * we do not need to do anything right now 152 * because the table is not deleted from the 153 * global table list when unloading it. 154 */ 155 break; 156 default: 157 return AE_BAD_PARAMETER; 158 } 159 return AE_OK; 160} 161 162static int acpi_system_sysfs_init(void) 163{ 164 struct acpi_table_attr *table_attr; 165 struct acpi_table_header *table_header = NULL; 166 int table_index = 0; 167 int result; 168 169 tables_kobj = kobject_create_and_add("tables", acpi_kobj); 170 if (!tables_kobj) 171 goto err; 172 173 dynamic_tables_kobj = kobject_create_and_add("dynamic", tables_kobj); 174 if (!dynamic_tables_kobj) 175 goto err_dynamic_tables; 176 177 do { 178 result = acpi_get_table_by_index(table_index, &table_header); 179 if (!result) { 180 table_index++; 181 table_attr = NULL; 182 table_attr = 183 kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL); 184 if (!table_attr) 185 return -ENOMEM; 186 187 acpi_table_attr_init(table_attr, table_header); 188 result = 189 sysfs_create_bin_file(tables_kobj, 190 &table_attr->attr); 191 if (result) { 192 kfree(table_attr); 193 return result; 194 } else 195 list_add_tail(&table_attr->node, 196 &acpi_table_attr_list); 197 } 198 } while (!result); 199 kobject_uevent(tables_kobj, KOBJ_ADD); 200 kobject_uevent(dynamic_tables_kobj, KOBJ_ADD); 201 result = acpi_install_table_handler(acpi_sysfs_table_handler, NULL); 202 203 return result == AE_OK ? 0 : -EINVAL; 204err_dynamic_tables: 205 kobject_put(tables_kobj); 206err: 207 return -ENOMEM; 208} 209 210/* 211 * Detailed ACPI IRQ counters in /sys/firmware/acpi/interrupts/ 212 * See Documentation/ABI/testing/sysfs-firmware-acpi 213 */ 214 215#define COUNT_GPE 0 216#define COUNT_SCI 1 /* acpi_irq_handled */ 217#define COUNT_ERROR 2 /* other */ 218#define NUM_COUNTERS_EXTRA 3 219 220struct event_counter { 221 u32 count; 222 u32 flags; 223}; 224 225static struct event_counter *all_counters; 226static u32 num_gpes; 227static u32 num_counters; 228static struct attribute **all_attrs; 229static u32 acpi_gpe_count; 230 231static struct attribute_group interrupt_stats_attr_group = { 232 .name = "interrupts", 233}; 234static struct kobj_attribute *counter_attrs; 235 236static void delete_gpe_attr_array(void) 237{ 238 struct event_counter *tmp = all_counters; 239 240 all_counters = NULL; 241 kfree(tmp); 242 243 if (counter_attrs) { 244 int i; 245 246 for (i = 0; i < num_gpes; i++) 247 kfree(counter_attrs[i].attr.name); 248 249 kfree(counter_attrs); 250 } 251 kfree(all_attrs); 252 253 return; 254} 255 256void acpi_os_gpe_count(u32 gpe_number) 257{ 258 acpi_gpe_count++; 259 260 if (!all_counters) 261 return; 262 263 if (gpe_number < num_gpes) 264 all_counters[gpe_number].count++; 265 else 266 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]. 267 count++; 268 269 return; 270} 271 272void acpi_os_fixed_event_count(u32 event_number) 273{ 274 if (!all_counters) 275 return; 276 277 if (event_number < ACPI_NUM_FIXED_EVENTS) 278 all_counters[num_gpes + event_number].count++; 279 else 280 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]. 281 count++; 282 283 return; 284} 285 286static int get_status(u32 index, acpi_event_status *status, acpi_handle *handle) 287{ 288 int result = 0; 289 290 if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) 291 goto end; 292 293 if (index < num_gpes) { 294 result = acpi_get_gpe_device(index, handle); 295 if (result) { 296 ACPI_EXCEPTION((AE_INFO, AE_NOT_FOUND, 297 "Invalid GPE 0x%x\n", index)); 298 goto end; 299 } 300 result = acpi_get_gpe_status(*handle, index, 301 ACPI_NOT_ISR, status); 302 } else if (index < (num_gpes + ACPI_NUM_FIXED_EVENTS)) 303 result = acpi_get_event_status(index - num_gpes, status); 304 305end: 306 return result; 307} 308 309static ssize_t counter_show(struct kobject *kobj, 310 struct kobj_attribute *attr, char *buf) 311{ 312 int index = attr - counter_attrs; 313 int size; 314 acpi_handle handle; 315 acpi_event_status status; 316 int result = 0; 317 318 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI].count = 319 acpi_irq_handled; 320 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count = 321 acpi_gpe_count; 322 323 size = sprintf(buf, "%8d", all_counters[index].count); 324 325 /* "gpe_all" or "sci" */ 326 if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) 327 goto end; 328 329 result = get_status(index, &status, &handle); 330 if (result) 331 goto end; 332 333 if (!(status & ACPI_EVENT_FLAG_HANDLE)) 334 size += sprintf(buf + size, " invalid"); 335 else if (status & ACPI_EVENT_FLAG_ENABLED) 336 size += sprintf(buf + size, " enabled"); 337 else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED) 338 size += sprintf(buf + size, " wake_enabled"); 339 else 340 size += sprintf(buf + size, " disabled"); 341 342end: 343 size += sprintf(buf + size, "\n"); 344 return result ? result : size; 345} 346 347/* 348 * counter_set() sets the specified counter. 349 * setting the total "sci" file to any value clears all counters. 350 * enable/disable/clear a gpe/fixed event in user space. 351 */ 352static ssize_t counter_set(struct kobject *kobj, 353 struct kobj_attribute *attr, const char *buf, size_t size) 354{ 355 int index = attr - counter_attrs; 356 acpi_event_status status; 357 acpi_handle handle; 358 int result = 0; 359 360 if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) { 361 int i; 362 for (i = 0; i < num_counters; ++i) 363 all_counters[i].count = 0; 364 acpi_gpe_count = 0; 365 acpi_irq_handled = 0; 366 goto end; 367 } 368 369 /* show the event status for both GPEs and Fixed Events */ 370 result = get_status(index, &status, &handle); 371 if (result) 372 goto end; 373 374 if (!(status & ACPI_EVENT_FLAG_HANDLE)) { 375 printk(KERN_WARNING PREFIX 376 "Can not change Invalid GPE/Fixed Event status\n"); 377 return -EINVAL; 378 } 379 380 if (index < num_gpes) { 381 if (!strcmp(buf, "disable\n") && 382 (status & ACPI_EVENT_FLAG_ENABLED)) 383 result = acpi_disable_gpe(handle, index); 384 else if (!strcmp(buf, "enable\n") && 385 !(status & ACPI_EVENT_FLAG_ENABLED)) 386 result = acpi_enable_gpe(handle, index); 387 else if (!strcmp(buf, "clear\n") && 388 (status & ACPI_EVENT_FLAG_SET)) 389 result = acpi_clear_gpe(handle, index, ACPI_NOT_ISR); 390 else 391 all_counters[index].count = strtoul(buf, NULL, 0); 392 } else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) { 393 int event = index - num_gpes; 394 if (!strcmp(buf, "disable\n") && 395 (status & ACPI_EVENT_FLAG_ENABLED)) 396 result = acpi_disable_event(event, ACPI_NOT_ISR); 397 else if (!strcmp(buf, "enable\n") && 398 !(status & ACPI_EVENT_FLAG_ENABLED)) 399 result = acpi_enable_event(event, ACPI_NOT_ISR); 400 else if (!strcmp(buf, "clear\n") && 401 (status & ACPI_EVENT_FLAG_SET)) 402 result = acpi_clear_event(event); 403 else 404 all_counters[index].count = strtoul(buf, NULL, 0); 405 } else 406 all_counters[index].count = strtoul(buf, NULL, 0); 407 408 if (ACPI_FAILURE(result)) 409 result = -EINVAL; 410end: 411 return result ? result : size; 412} 413 414void acpi_irq_stats_init(void) 415{ 416 int i; 417 418 if (all_counters) 419 return; 420 421 num_gpes = acpi_current_gpe_count; 422 num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA; 423 424 all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1), 425 GFP_KERNEL); 426 if (all_attrs == NULL) 427 return; 428 429 all_counters = kzalloc(sizeof(struct event_counter) * (num_counters), 430 GFP_KERNEL); 431 if (all_counters == NULL) 432 goto fail; 433 434 counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters), 435 GFP_KERNEL); 436 if (counter_attrs == NULL) 437 goto fail; 438 439 for (i = 0; i < num_counters; ++i) { 440 char buffer[12]; 441 char *name; 442 443 if (i < num_gpes) 444 sprintf(buffer, "gpe%02X", i); 445 else if (i == num_gpes + ACPI_EVENT_PMTIMER) 446 sprintf(buffer, "ff_pmtimer"); 447 else if (i == num_gpes + ACPI_EVENT_GLOBAL) 448 sprintf(buffer, "ff_gbl_lock"); 449 else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON) 450 sprintf(buffer, "ff_pwr_btn"); 451 else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON) 452 sprintf(buffer, "ff_slp_btn"); 453 else if (i == num_gpes + ACPI_EVENT_RTC) 454 sprintf(buffer, "ff_rt_clk"); 455 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE) 456 sprintf(buffer, "gpe_all"); 457 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) 458 sprintf(buffer, "sci"); 459 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR) 460 sprintf(buffer, "error"); 461 else 462 sprintf(buffer, "bug%02X", i); 463 464 name = kzalloc(strlen(buffer) + 1, GFP_KERNEL); 465 if (name == NULL) 466 goto fail; 467 strncpy(name, buffer, strlen(buffer) + 1); 468 469 counter_attrs[i].attr.name = name; 470 counter_attrs[i].attr.mode = 0644; 471 counter_attrs[i].show = counter_show; 472 counter_attrs[i].store = counter_set; 473 474 all_attrs[i] = &counter_attrs[i].attr; 475 } 476 477 interrupt_stats_attr_group.attrs = all_attrs; 478 if (!sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group)) 479 return; 480 481fail: 482 delete_gpe_attr_array(); 483 return; 484} 485 486static void __exit interrupt_stats_exit(void) 487{ 488 sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group); 489 490 delete_gpe_attr_array(); 491 492 return; 493} 494 495/* -------------------------------------------------------------------------- 496 FS Interface (/proc) 497 -------------------------------------------------------------------------- */ 498#ifdef CONFIG_ACPI_PROCFS 499#define ACPI_SYSTEM_FILE_INFO "info" 500#define ACPI_SYSTEM_FILE_EVENT "event" 501#define ACPI_SYSTEM_FILE_DSDT "dsdt" 502#define ACPI_SYSTEM_FILE_FADT "fadt" 503 504static int acpi_system_read_info(struct seq_file *seq, void *offset) 505{ 506 507 seq_printf(seq, "version: %x\n", ACPI_CA_VERSION); 508 return 0; 509} 510 511static int acpi_system_info_open_fs(struct inode *inode, struct file *file) 512{ 513 return single_open(file, acpi_system_read_info, PDE(inode)->data); 514} 515 516static const struct file_operations acpi_system_info_ops = { 517 .owner = THIS_MODULE, 518 .open = acpi_system_info_open_fs, 519 .read = seq_read, 520 .llseek = seq_lseek, 521 .release = single_release, 522}; 523 524static ssize_t acpi_system_read_dsdt(struct file *, char __user *, size_t, 525 loff_t *); 526 527static const struct file_operations acpi_system_dsdt_ops = { 528 .owner = THIS_MODULE, 529 .read = acpi_system_read_dsdt, 530}; 531 532static ssize_t 533acpi_system_read_dsdt(struct file *file, 534 char __user * buffer, size_t count, loff_t * ppos) 535{ 536 acpi_status status = AE_OK; 537 struct acpi_table_header *dsdt = NULL; 538 ssize_t res; 539 540 status = acpi_get_table(ACPI_SIG_DSDT, 1, &dsdt); 541 if (ACPI_FAILURE(status)) 542 return -ENODEV; 543 544 res = simple_read_from_buffer(buffer, count, ppos, dsdt, dsdt->length); 545 546 return res; 547} 548 549static ssize_t acpi_system_read_fadt(struct file *, char __user *, size_t, 550 loff_t *); 551 552static const struct file_operations acpi_system_fadt_ops = { 553 .owner = THIS_MODULE, 554 .read = acpi_system_read_fadt, 555}; 556 557static ssize_t 558acpi_system_read_fadt(struct file *file, 559 char __user * buffer, size_t count, loff_t * ppos) 560{ 561 acpi_status status = AE_OK; 562 struct acpi_table_header *fadt = NULL; 563 ssize_t res; 564 565 status = acpi_get_table(ACPI_SIG_FADT, 1, &fadt); 566 if (ACPI_FAILURE(status)) 567 return -ENODEV; 568 569 res = simple_read_from_buffer(buffer, count, ppos, fadt, fadt->length); 570 571 return res; 572} 573 574static int acpi_system_procfs_init(void) 575{ 576 struct proc_dir_entry *entry; 577 int error = 0; 578 579 /* 'info' [R] */ 580 entry = proc_create(ACPI_SYSTEM_FILE_INFO, S_IRUGO, acpi_root_dir, 581 &acpi_system_info_ops); 582 if (!entry) 583 goto Error; 584 585 /* 'dsdt' [R] */ 586 entry = proc_create(ACPI_SYSTEM_FILE_DSDT, S_IRUSR, acpi_root_dir, 587 &acpi_system_dsdt_ops); 588 if (!entry) 589 goto Error; 590 591 /* 'fadt' [R] */ 592 entry = proc_create(ACPI_SYSTEM_FILE_FADT, S_IRUSR, acpi_root_dir, 593 &acpi_system_fadt_ops); 594 if (!entry) 595 goto Error; 596 597 Done: 598 return error; 599 600 Error: 601 remove_proc_entry(ACPI_SYSTEM_FILE_FADT, acpi_root_dir); 602 remove_proc_entry(ACPI_SYSTEM_FILE_DSDT, acpi_root_dir); 603 remove_proc_entry(ACPI_SYSTEM_FILE_INFO, acpi_root_dir); 604 605 error = -EFAULT; 606 goto Done; 607} 608#else 609static int acpi_system_procfs_init(void) 610{ 611 return 0; 612} 613#endif 614 615int __init acpi_system_init(void) 616{ 617 int result; 618 619 result = acpi_system_procfs_init(); 620 if (result) 621 return result; 622 623 result = acpi_system_sysfs_init(); 624 625 return result; 626}