linux/drivers/acpi/ec_sys.c
<<
>>
Prefs
   1/*
   2 * ec_sys.c
   3 *
   4 * Copyright (C) 2010 SUSE Products GmbH/Novell
   5 * Author:
   6 *      Thomas Renninger <trenn@suse.de>
   7 *
   8 * This work is licensed under the terms of the GNU GPL, version 2.
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/acpi.h>
  13#include <linux/debugfs.h>
  14#include "internal.h"
  15
  16MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
  17MODULE_DESCRIPTION("ACPI EC sysfs access driver");
  18MODULE_LICENSE("GPL");
  19
  20static bool write_support;
  21module_param(write_support, bool, 0644);
  22MODULE_PARM_DESC(write_support, "Dangerous, reboot and removal of battery may "
  23                 "be needed.");
  24
  25#define EC_SPACE_SIZE 256
  26
  27struct sysdev_class acpi_ec_sysdev_class = {
  28        .name = "ec",
  29};
  30
  31static struct dentry *acpi_ec_debugfs_dir;
  32
  33static int acpi_ec_open_io(struct inode *i, struct file *f)
  34{
  35        f->private_data = i->i_private;
  36        return 0;
  37}
  38
  39static ssize_t acpi_ec_read_io(struct file *f, char __user *buf,
  40                               size_t count, loff_t *off)
  41{
  42        /* Use this if support reading/writing multiple ECs exists in ec.c:
  43         * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private;
  44         */
  45        unsigned int size = EC_SPACE_SIZE;
  46        u8 *data = (u8 *) buf;
  47        loff_t init_off = *off;
  48        int err = 0;
  49
  50        if (*off >= size)
  51                return 0;
  52        if (*off + count >= size) {
  53                size -= *off;
  54                count = size;
  55        } else
  56                size = count;
  57
  58        while (size) {
  59                err = ec_read(*off, &data[*off - init_off]);
  60                if (err)
  61                        return err;
  62                *off += 1;
  63                size--;
  64        }
  65        return count;
  66}
  67
  68static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf,
  69                                size_t count, loff_t *off)
  70{
  71        /* Use this if support reading/writing multiple ECs exists in ec.c:
  72         * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private;
  73         */
  74
  75        unsigned int size = count;
  76        loff_t init_off = *off;
  77        u8 *data = (u8 *) buf;
  78        int err = 0;
  79
  80        if (*off >= EC_SPACE_SIZE)
  81                return 0;
  82        if (*off + count >= EC_SPACE_SIZE) {
  83                size = EC_SPACE_SIZE - *off;
  84                count = size;
  85        }
  86
  87        while (size) {
  88                u8 byte_write = data[*off - init_off];
  89                err = ec_write(*off, byte_write);
  90                if (err)
  91                        return err;
  92
  93                *off += 1;
  94                size--;
  95        }
  96        return count;
  97}
  98
  99static struct file_operations acpi_ec_io_ops = {
 100        .owner = THIS_MODULE,
 101        .open  = acpi_ec_open_io,
 102        .read  = acpi_ec_read_io,
 103        .write = acpi_ec_write_io,
 104        .llseek = default_llseek,
 105};
 106
 107int acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count)
 108{
 109        struct dentry *dev_dir;
 110        char name[64];
 111        mode_t mode = 0400;
 112
 113        if (ec_device_count == 0) {
 114                acpi_ec_debugfs_dir = debugfs_create_dir("ec", NULL);
 115                if (!acpi_ec_debugfs_dir)
 116                        return -ENOMEM;
 117        }
 118
 119        sprintf(name, "ec%u", ec_device_count);
 120        dev_dir = debugfs_create_dir(name, acpi_ec_debugfs_dir);
 121        if (!dev_dir) {
 122                if (ec_device_count != 0)
 123                        goto error;
 124                return -ENOMEM;
 125        }
 126
 127        if (!debugfs_create_x32("gpe", 0444, dev_dir, (u32 *)&first_ec->gpe))
 128                goto error;
 129        if (!debugfs_create_bool("use_global_lock", 0444, dev_dir,
 130                                 (u32 *)&first_ec->global_lock))
 131                goto error;
 132
 133        if (write_support)
 134                mode = 0600;
 135        if (!debugfs_create_file("io", mode, dev_dir, ec, &acpi_ec_io_ops))
 136                goto error;
 137
 138        return 0;
 139
 140error:
 141        debugfs_remove_recursive(acpi_ec_debugfs_dir);
 142        return -ENOMEM;
 143}
 144
 145static int __init acpi_ec_sys_init(void)
 146{
 147        int err = 0;
 148        if (first_ec)
 149                err = acpi_ec_add_debugfs(first_ec, 0);
 150        else
 151                err = -ENODEV;
 152        return err;
 153}
 154
 155static void __exit acpi_ec_sys_exit(void)
 156{
 157        debugfs_remove_recursive(acpi_ec_debugfs_dir);
 158}
 159
 160module_init(acpi_ec_sys_init);
 161module_exit(acpi_ec_sys_exit);
 162