Blame view

drivers/acpi/ec_sys.c 3.49 KB
9827886dc   Thomas Renninger   ACPI: Provide /sy...
1
2
3
4
5
6
7
8
9
  /*
   * ec_sys.c
   *
   * Copyright (C) 2010 SUSE Products GmbH/Novell
   * Author:
   *      Thomas Renninger <trenn@suse.de>
   *
   * This work is licensed under the terms of the GNU GPL, version 2.
   */
1195a0981   Thomas Renninger   ACPI: Provide /sy...
10
11
12
  #include <linux/kernel.h>
  #include <linux/acpi.h>
  #include <linux/debugfs.h>
cc4b859c7   Paul Gortmaker   acpi: add module....
13
  #include <linux/module.h>
ecde3003e   Vasiliy Kulikov   ACPI / EC: access...
14
  #include <linux/uaccess.h>
1195a0981   Thomas Renninger   ACPI: Provide /sy...
15
16
17
18
19
  #include "internal.h"
  
  MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
  MODULE_DESCRIPTION("ACPI EC sysfs access driver");
  MODULE_LICENSE("GPL");
500de3dd4   Thomas Renninger   acpi ec_sys: Be m...
20
21
22
23
  static bool write_support;
  module_param(write_support, bool, 0644);
  MODULE_PARM_DESC(write_support, "Dangerous, reboot and removal of battery may "
  		 "be needed.");
9827886dc   Thomas Renninger   ACPI: Provide /sy...
24
  #define EC_SPACE_SIZE 256
1195a0981   Thomas Renninger   ACPI: Provide /sy...
25
  static struct dentry *acpi_ec_debugfs_dir;
9827886dc   Thomas Renninger   ACPI: Provide /sy...
26
27
28
29
30
31
32
  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;
9827886dc   Thomas Renninger   ACPI: Provide /sy...
33
34
35
36
37
38
39
40
41
42
43
44
  	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) {
ecde3003e   Vasiliy Kulikov   ACPI / EC: access...
45
46
  		u8 byte_read;
  		err = ec_read(*off, &byte_read);
9827886dc   Thomas Renninger   ACPI: Provide /sy...
47
48
  		if (err)
  			return err;
ecde3003e   Vasiliy Kulikov   ACPI / EC: access...
49
50
51
52
53
  		if (put_user(byte_read, buf + *off - init_off)) {
  			if (*off - init_off)
  				return *off - init_off; /* partial read */
  			return -EFAULT;
  		}
9827886dc   Thomas Renninger   ACPI: Provide /sy...
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  		*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;
9827886dc   Thomas Renninger   ACPI: Provide /sy...
69
  	int err = 0;
1b6e75ee2   Oleg Drokin   ACPI / EC: Deny w...
70
71
  	if (!write_support)
  		return -EINVAL;
9827886dc   Thomas Renninger   ACPI: Provide /sy...
72
73
74
75
76
77
78
79
  	if (*off >= EC_SPACE_SIZE)
  		return 0;
  	if (*off + count >= EC_SPACE_SIZE) {
  		size = EC_SPACE_SIZE - *off;
  		count = size;
  	}
  
  	while (size) {
ecde3003e   Vasiliy Kulikov   ACPI / EC: access...
80
81
82
83
84
85
  		u8 byte_write;
  		if (get_user(byte_write, buf + *off - init_off)) {
  			if (*off - init_off)
  				return *off - init_off; /* partial write */
  			return -EFAULT;
  		}
9827886dc   Thomas Renninger   ACPI: Provide /sy...
86
87
88
89
90
91
92
93
94
  		err = ec_write(*off, byte_write);
  		if (err)
  			return err;
  
  		*off += 1;
  		size--;
  	}
  	return count;
  }
9c8b04be4   Vasiliy Kulikov   ACPI: constify op...
95
  static const struct file_operations acpi_ec_io_ops = {
9827886dc   Thomas Renninger   ACPI: Provide /sy...
96
  	.owner = THIS_MODULE,
234e34058   Stephen Boyd   simple_open: auto...
97
  	.open  = simple_open,
9827886dc   Thomas Renninger   ACPI: Provide /sy...
98
99
  	.read  = acpi_ec_read_io,
  	.write = acpi_ec_write_io,
6038f373a   Arnd Bergmann   llseek: automatic...
100
  	.llseek = default_llseek,
9827886dc   Thomas Renninger   ACPI: Provide /sy...
101
  };
49894d8d5   Rashika   ACPI / EC: Mark t...
102
  static int acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count)
1195a0981   Thomas Renninger   ACPI: Provide /sy...
103
104
105
  {
  	struct dentry *dev_dir;
  	char name[64];
f4ae40a6a   Al Viro   switch debugfs to...
106
  	umode_t mode = 0400;
500de3dd4   Thomas Renninger   acpi ec_sys: Be m...
107

1195a0981   Thomas Renninger   ACPI: Provide /sy...
108
109
110
111
112
113
114
115
116
  	if (ec_device_count == 0) {
  		acpi_ec_debugfs_dir = debugfs_create_dir("ec", NULL);
  		if (!acpi_ec_debugfs_dir)
  			return -ENOMEM;
  	}
  
  	sprintf(name, "ec%u", ec_device_count);
  	dev_dir = debugfs_create_dir(name, acpi_ec_debugfs_dir);
  	if (!dev_dir) {
500de3dd4   Thomas Renninger   acpi ec_sys: Be m...
117
118
  		if (ec_device_count != 0)
  			goto error;
1195a0981   Thomas Renninger   ACPI: Provide /sy...
119
120
  		return -ENOMEM;
  	}
500de3dd4   Thomas Renninger   acpi ec_sys: Be m...
121
122
123
  	if (!debugfs_create_x32("gpe", 0444, dev_dir, (u32 *)&first_ec->gpe))
  		goto error;
  	if (!debugfs_create_bool("use_global_lock", 0444, dev_dir,
6e58f752a   Viresh Kumar   ACPI / EC: Fix br...
124
  				 &first_ec->global_lock))
500de3dd4   Thomas Renninger   acpi ec_sys: Be m...
125
126
127
128
129
130
  		goto error;
  
  	if (write_support)
  		mode = 0600;
  	if (!debugfs_create_file("io", mode, dev_dir, ec, &acpi_ec_io_ops))
  		goto error;
1195a0981   Thomas Renninger   ACPI: Provide /sy...
131
  	return 0;
500de3dd4   Thomas Renninger   acpi ec_sys: Be m...
132
133
134
135
  
  error:
  	debugfs_remove_recursive(acpi_ec_debugfs_dir);
  	return -ENOMEM;
1195a0981   Thomas Renninger   ACPI: Provide /sy...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  }
  
  static int __init acpi_ec_sys_init(void)
  {
  	int err = 0;
  	if (first_ec)
  		err = acpi_ec_add_debugfs(first_ec, 0);
  	else
  		err = -ENODEV;
  	return err;
  }
  
  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);