Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * ec_sys.c
0004  *
0005  * Copyright (C) 2010 SUSE Products GmbH/Novell
0006  * Author:
0007  *      Thomas Renninger <trenn@suse.de>
0008  */
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/acpi.h>
0012 #include <linux/debugfs.h>
0013 #include <linux/module.h>
0014 #include <linux/uaccess.h>
0015 #include "internal.h"
0016 
0017 MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
0018 MODULE_DESCRIPTION("ACPI EC sysfs access driver");
0019 MODULE_LICENSE("GPL");
0020 
0021 static bool write_support;
0022 module_param_hw(write_support, bool, other, 0644);
0023 MODULE_PARM_DESC(write_support, "Dangerous, reboot and removal of battery may "
0024          "be needed.");
0025 
0026 #define EC_SPACE_SIZE 256
0027 
0028 static struct dentry *acpi_ec_debugfs_dir;
0029 
0030 static ssize_t acpi_ec_read_io(struct file *f, char __user *buf,
0031                    size_t count, loff_t *off)
0032 {
0033     /* Use this if support reading/writing multiple ECs exists in ec.c:
0034      * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private;
0035      */
0036     unsigned int size = EC_SPACE_SIZE;
0037     loff_t init_off = *off;
0038     int err = 0;
0039 
0040     if (*off >= size)
0041         return 0;
0042     if (*off + count >= size) {
0043         size -= *off;
0044         count = size;
0045     } else
0046         size = count;
0047 
0048     while (size) {
0049         u8 byte_read;
0050         err = ec_read(*off, &byte_read);
0051         if (err)
0052             return err;
0053         if (put_user(byte_read, buf + *off - init_off)) {
0054             if (*off - init_off)
0055                 return *off - init_off; /* partial read */
0056             return -EFAULT;
0057         }
0058         *off += 1;
0059         size--;
0060     }
0061     return count;
0062 }
0063 
0064 static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf,
0065                 size_t count, loff_t *off)
0066 {
0067     /* Use this if support reading/writing multiple ECs exists in ec.c:
0068      * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private;
0069      */
0070 
0071     unsigned int size = count;
0072     loff_t init_off = *off;
0073     int err = 0;
0074 
0075     if (!write_support)
0076         return -EINVAL;
0077 
0078     if (*off >= EC_SPACE_SIZE)
0079         return 0;
0080     if (*off + count >= EC_SPACE_SIZE) {
0081         size = EC_SPACE_SIZE - *off;
0082         count = size;
0083     }
0084 
0085     while (size) {
0086         u8 byte_write;
0087         if (get_user(byte_write, buf + *off - init_off)) {
0088             if (*off - init_off)
0089                 return *off - init_off; /* partial write */
0090             return -EFAULT;
0091         }
0092         err = ec_write(*off, byte_write);
0093         if (err)
0094             return err;
0095 
0096         *off += 1;
0097         size--;
0098     }
0099     return count;
0100 }
0101 
0102 static const struct file_operations acpi_ec_io_ops = {
0103     .owner = THIS_MODULE,
0104     .open  = simple_open,
0105     .read  = acpi_ec_read_io,
0106     .write = acpi_ec_write_io,
0107     .llseek = default_llseek,
0108 };
0109 
0110 static void acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count)
0111 {
0112     struct dentry *dev_dir;
0113     char name[64];
0114     umode_t mode = 0400;
0115 
0116     if (ec_device_count == 0)
0117         acpi_ec_debugfs_dir = debugfs_create_dir("ec", NULL);
0118 
0119     sprintf(name, "ec%u", ec_device_count);
0120     dev_dir = debugfs_create_dir(name, acpi_ec_debugfs_dir);
0121 
0122     debugfs_create_x32("gpe", 0444, dev_dir, &first_ec->gpe);
0123     debugfs_create_bool("use_global_lock", 0444, dev_dir,
0124                 &first_ec->global_lock);
0125 
0126     if (write_support)
0127         mode = 0600;
0128     debugfs_create_file("io", mode, dev_dir, ec, &acpi_ec_io_ops);
0129 }
0130 
0131 static int __init acpi_ec_sys_init(void)
0132 {
0133     if (first_ec)
0134         acpi_ec_add_debugfs(first_ec, 0);
0135     return 0;
0136 }
0137 
0138 static void __exit acpi_ec_sys_exit(void)
0139 {
0140     debugfs_remove_recursive(acpi_ec_debugfs_dir);
0141 }
0142 
0143 module_init(acpi_ec_sys_init);
0144 module_exit(acpi_ec_sys_exit);