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