forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
3.1 KiB
144 lines
3.1 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* ec_sys.c |
|
* |
|
* Copyright (C) 2010 SUSE Products GmbH/Novell |
|
* Author: |
|
* Thomas Renninger <[email protected]> |
|
*/ |
|
|
|
#include <linux/kernel.h> |
|
#include <linux/acpi.h> |
|
#include <linux/debugfs.h> |
|
#include <linux/module.h> |
|
#include <linux/uaccess.h> |
|
#include "internal.h" |
|
|
|
MODULE_AUTHOR("Thomas Renninger <[email protected]>"); |
|
MODULE_DESCRIPTION("ACPI EC sysfs access driver"); |
|
MODULE_LICENSE("GPL"); |
|
|
|
static bool write_support; |
|
module_param(write_support, bool, 0644); |
|
MODULE_PARM_DESC(write_support, "Dangerous, reboot and removal of battery may " |
|
"be needed."); |
|
|
|
#define EC_SPACE_SIZE 256 |
|
|
|
static struct dentry *acpi_ec_debugfs_dir; |
|
|
|
static ssize_t acpi_ec_read_io(struct file *f, char __user *buf, |
|
size_t count, loff_t *off) |
|
{ |
|
/* Use this if support reading/writing multiple ECs exists in ec.c: |
|
* struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private; |
|
*/ |
|
unsigned int size = EC_SPACE_SIZE; |
|
loff_t init_off = *off; |
|
int err = 0; |
|
|
|
if (*off >= size) |
|
return 0; |
|
if (*off + count >= size) { |
|
size -= *off; |
|
count = size; |
|
} else |
|
size = count; |
|
|
|
while (size) { |
|
u8 byte_read; |
|
err = ec_read(*off, &byte_read); |
|
if (err) |
|
return err; |
|
if (put_user(byte_read, buf + *off - init_off)) { |
|
if (*off - init_off) |
|
return *off - init_off; /* partial read */ |
|
return -EFAULT; |
|
} |
|
*off += 1; |
|
size--; |
|
} |
|
return count; |
|
} |
|
|
|
static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf, |
|
size_t count, loff_t *off) |
|
{ |
|
/* Use this if support reading/writing multiple ECs exists in ec.c: |
|
* struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private; |
|
*/ |
|
|
|
unsigned int size = count; |
|
loff_t init_off = *off; |
|
int err = 0; |
|
|
|
if (!write_support) |
|
return -EINVAL; |
|
|
|
if (*off >= EC_SPACE_SIZE) |
|
return 0; |
|
if (*off + count >= EC_SPACE_SIZE) { |
|
size = EC_SPACE_SIZE - *off; |
|
count = size; |
|
} |
|
|
|
while (size) { |
|
u8 byte_write; |
|
if (get_user(byte_write, buf + *off - init_off)) { |
|
if (*off - init_off) |
|
return *off - init_off; /* partial write */ |
|
return -EFAULT; |
|
} |
|
err = ec_write(*off, byte_write); |
|
if (err) |
|
return err; |
|
|
|
*off += 1; |
|
size--; |
|
} |
|
return count; |
|
} |
|
|
|
static const struct file_operations acpi_ec_io_ops = { |
|
.owner = THIS_MODULE, |
|
.open = simple_open, |
|
.read = acpi_ec_read_io, |
|
.write = acpi_ec_write_io, |
|
.llseek = default_llseek, |
|
}; |
|
|
|
static void acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count) |
|
{ |
|
struct dentry *dev_dir; |
|
char name[64]; |
|
umode_t mode = 0400; |
|
|
|
if (ec_device_count == 0) |
|
acpi_ec_debugfs_dir = debugfs_create_dir("ec", NULL); |
|
|
|
sprintf(name, "ec%u", ec_device_count); |
|
dev_dir = debugfs_create_dir(name, acpi_ec_debugfs_dir); |
|
|
|
debugfs_create_x32("gpe", 0444, dev_dir, &first_ec->gpe); |
|
debugfs_create_bool("use_global_lock", 0444, dev_dir, |
|
&first_ec->global_lock); |
|
|
|
if (write_support) |
|
mode = 0600; |
|
debugfs_create_file("io", mode, dev_dir, ec, &acpi_ec_io_ops); |
|
} |
|
|
|
static int __init acpi_ec_sys_init(void) |
|
{ |
|
if (first_ec) |
|
acpi_ec_add_debugfs(first_ec, 0); |
|
return 0; |
|
} |
|
|
|
static void __exit acpi_ec_sys_exit(void) |
|
{ |
|
debugfs_remove_recursive(acpi_ec_debugfs_dir); |
|
} |
|
|
|
module_init(acpi_ec_sys_init); |
|
module_exit(acpi_ec_sys_exit);
|
|
|