aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/acpi/sony_acpi.txt87
-rw-r--r--drivers/acpi/Kconfig14
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/sony_acpi.c397
4 files changed, 499 insertions, 0 deletions
diff --git a/Documentation/acpi/sony_acpi.txt b/Documentation/acpi/sony_acpi.txt
new file mode 100644
index 000000000000..35a04bea38d7
--- /dev/null
+++ b/Documentation/acpi/sony_acpi.txt
@@ -0,0 +1,87 @@
1ACPI Sony Notebook Control Driver (SNC) Readme
2----------------------------------------------
3 Copyright (C) 2004- 2005 Stelian Pop <stelian@popies.net>
4
5This mini-driver drives the ACPI SNC device present in the
6ACPI BIOS of the Sony Vaio laptops.
7
8It gives access to some extra laptop functionalities. In
9its current form, this driver is mainly useful for controlling the
10screen brightness, but it may do more in the future.
11
12You should probably start by trying the sonypi driver, and try
13sony_acpi only if sonypi doesn't work for you.
14
15Usage:
16------
17
18Loading the sony_acpi module will create a /proc/acpi/sony/
19directory populated with a couple of files.
20
21You then read/write integer values from/to those files by using
22standard UNIX tools.
23
24The files are:
25 brightness current screen brightness
26 brightness_default screen brightness which will be set
27 when the laptop will be rebooted
28 cdpower power on/off the internal CD drive
29
30Note that some files may be missing if they are not supported
31by your particular laptop model.
32
33Example usage:
34 # echo "1" > /proc/acpi/sony/brightness
35sets the lowest screen brightness,
36 # echo "8" > /proc/acpi/sony/brightness
37sets the highest screen brightness,
38 # cat /proc/acpi/sony/brightness
39retrieves the current screen brightness.
40
41Development:
42------------
43
44If you want to help with the development of this driver (and
45you are not afraid of any side effects doing strange things with
46your ACPI BIOS could have on your laptop), load the driver and
47pass the option 'debug=1'.
48
49REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS.
50
51In your kernel logs you will find the list of all ACPI methods
52the SNC device has on your laptop. You can see the GBRT/SBRT methods
53used to get/set the brightness, but there are others.
54
55I HAVE NO IDEA WHAT THOSE METHODS DO.
56
57The sony_acpi driver creates, for some of those methods (the most
58current ones found on several Vaio models), an entry under
59/proc/acpi/sony/, just like the 'brightness' one. You can create
60other entries corresponding to your own laptop methods by further
61editing the source (see the 'sony_acpi_values' table, and add a new
62structure to this table with your get/set method names).
63
64Your mission, should you accept it, is to try finding out what
65those entries are for, by reading/writing random values from/to those
66files and find out what is the impact on your laptop.
67
68Should you find anything interesting, please report it back to me,
69I will not disavow all knowledge of your actions :)
70
71Bugs/Limitations:
72-----------------
73
74* This driver is not based on official documentation from Sony
75 (because there is none), so there is no guarantee this driver
76 will work at all, or do the right thing. Although this hasn't
77 happened to me, this driver could do very bad things to your
78 laptop, including permanent damage.
79
80* The sony_acpi and sonypi drivers do not interact at all. In the
81 future, sonypi could use sony_acpi to do (part of) its business.
82
83* spicctrl, which is the userspace tool used to communicate with the
84 sonypi driver (through /dev/sonypi) does not try to use the
85 sony_acpi driver. In the future, spicctrl could try sonypi first,
86 and if it isn't present, try sony_acpi instead.
87
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index f4f000abc4e9..e11371fb9191 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -252,6 +252,20 @@ config ACPI_TOSHIBA
252 If you have a legacy free Toshiba laptop (such as the Libretto L1 252 If you have a legacy free Toshiba laptop (such as the Libretto L1
253 series), say Y. 253 series), say Y.
254 254
255config ACPI_SONY
256 tristate "Sony Laptop Extras"
257 depends on X86 && ACPI
258 default m
259 ---help---
260 This mini-driver drives the ACPI SNC device present in the
261 ACPI BIOS of the Sony Vaio laptops.
262
263 It gives access to some extra laptop functionalities. In
264 its current form, the only thing this driver does is letting
265 the user set or query the screen brightness.
266
267 Read <file:Documentation/acpi/sony_acpi.txt> for more information.
268
255config ACPI_CUSTOM_DSDT 269config ACPI_CUSTOM_DSDT
256 bool "Include Custom DSDT" 270 bool "Include Custom DSDT"
257 depends on !STANDALONE 271 depends on !STANDALONE
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index bce7ca27b429..31b9ad2218af 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_ACPI_THERMAL) += thermal.o
53obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o 53obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o
54obj-$(CONFIG_ACPI_DEBUG) += debug.o 54obj-$(CONFIG_ACPI_DEBUG) += debug.o
55obj-$(CONFIG_ACPI_NUMA) += numa.o 55obj-$(CONFIG_ACPI_NUMA) += numa.o
56obj-$(CONFIG_ACPI_SONY) += sony_acpi.o
56obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o 57obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
57obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o 58obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o
58obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o 59obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
diff --git a/drivers/acpi/sony_acpi.c b/drivers/acpi/sony_acpi.c
new file mode 100644
index 000000000000..e23522a02965
--- /dev/null
+++ b/drivers/acpi/sony_acpi.c
@@ -0,0 +1,397 @@
1/*
2 * ACPI Sony Notebook Control Driver (SNC)
3 *
4 * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
5 *
6 * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
7 * which are copyrighted by their respective authors.
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
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 */
24
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/moduleparam.h>
28#include <linux/init.h>
29#include <linux/types.h>
30#include <acpi/acpi_drivers.h>
31#include <acpi/acpi_bus.h>
32#include <asm/uaccess.h>
33
34#define ACPI_SNC_CLASS "sony"
35#define ACPI_SNC_HID "SNY5001"
36#define ACPI_SNC_DRIVER_NAME "ACPI Sony Notebook Control Driver v0.2"
37
38#define LOG_PFX KERN_WARNING "sony_acpi: "
39
40MODULE_AUTHOR("Stelian Pop");
41MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME);
42MODULE_LICENSE("GPL");
43
44static int debug;
45module_param(debug, int, 0);
46MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
47 "the development of this driver");
48
49static int sony_acpi_add (struct acpi_device *device);
50static int sony_acpi_remove (struct acpi_device *device, int type);
51
52static struct acpi_driver sony_acpi_driver = {
53 .name = ACPI_SNC_DRIVER_NAME,
54 .class = ACPI_SNC_CLASS,
55 .ids = ACPI_SNC_HID,
56 .ops = {
57 .add = sony_acpi_add,
58 .remove = sony_acpi_remove,
59 },
60};
61
62static acpi_handle sony_acpi_handle;
63static struct proc_dir_entry *sony_acpi_dir;
64
65static struct sony_acpi_value {
66 char *name; /* name of the entry */
67 struct proc_dir_entry *proc; /* /proc entry */
68 char *acpiget;/* name of the ACPI get function */
69 char *acpiset;/* name of the ACPI get function */
70 int min; /* minimum allowed value or -1 */
71 int max; /* maximum allowed value or -1 */
72 int debug; /* active only in debug mode ? */
73} sony_acpi_values[] = {
74 {
75 .name = "brightness",
76 .acpiget = "GBRT",
77 .acpiset = "SBRT",
78 .min = 1,
79 .max = 8,
80 .debug = 0,
81 },
82 {
83 .name = "brightness_default",
84 .acpiget = "GPBR",
85 .acpiset = "SPBR",
86 .min = 1,
87 .max = 8,
88 .debug = 0,
89 },
90 {
91 .name = "fnkey",
92 .acpiget = "GHKE",
93 .debug = 0,
94 },
95 {
96 .name = "cdpower",
97 .acpiget = "GCDP",
98 .acpiset = "SCDP",
99 .min = -1,
100 .max = -1,
101 .debug = 0,
102 },
103 {
104 .name = "PID",
105 .acpiget = "GPID",
106 .debug = 1,
107 },
108 {
109 .name = "CTR",
110 .acpiget = "GCTR",
111 .acpiset = "SCTR",
112 .min = -1,
113 .max = -1,
114 .debug = 1,
115 },
116 {
117 .name = "PCR",
118 .acpiget = "GPCR",
119 .acpiset = "SPCR",
120 .min = -1,
121 .max = -1,
122 .debug = 1,
123 },
124 {
125 .name = "CMI",
126 .acpiget = "GCMI",
127 .acpiset = "SCMI",
128 .min = -1,
129 .max = -1,
130 .debug = 1,
131 },
132 {
133 .name = NULL,
134 }
135};
136
137static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
138{
139 struct acpi_buffer output;
140 union acpi_object out_obj;
141 acpi_status status;
142
143 output.length = sizeof(out_obj);
144 output.pointer = &out_obj;
145
146 status = acpi_evaluate_object(handle, name, NULL, &output);
147 if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
148 *result = out_obj.integer.value;
149 return 0;
150 }
151
152 printk(LOG_PFX "acpi_callreadfunc failed\n");
153
154 return -1;
155}
156
157static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
158 int *result)
159{
160 struct acpi_object_list params;
161 union acpi_object in_obj;
162 struct acpi_buffer output;
163 union acpi_object out_obj;
164 acpi_status status;
165
166 params.count = 1;
167 params.pointer = &in_obj;
168 in_obj.type = ACPI_TYPE_INTEGER;
169 in_obj.integer.value = value;
170
171 output.length = sizeof(out_obj);
172 output.pointer = &out_obj;
173
174 status = acpi_evaluate_object(handle, name, &params, &output);
175 if (status == AE_OK) {
176 if (result != NULL) {
177 if (out_obj.type != ACPI_TYPE_INTEGER) {
178 printk(LOG_PFX "acpi_evaluate_object bad "
179 "return type\n");
180 return -1;
181 }
182 *result = out_obj.integer.value;
183 }
184 return 0;
185 }
186
187 printk(LOG_PFX "acpi_evaluate_object failed\n");
188
189 return -1;
190}
191
192static int parse_buffer(const char __user *buffer, unsigned long count,
193 int *val) {
194 char s[32];
195 int ret;
196
197 if (count > 31)
198 return -EINVAL;
199 if (copy_from_user(s, buffer, count))
200 return -EFAULT;
201 s[count] = '\0';
202 ret = simple_strtoul(s, NULL, 10);
203 *val = ret;
204 return 0;
205}
206
207static int sony_acpi_read(char* page, char** start, off_t off, int count,
208 int* eof, void *data)
209{
210 struct sony_acpi_value *item = data;
211 int value;
212
213 if (!item->acpiget)
214 return -EIO;
215
216 if (acpi_callgetfunc(sony_acpi_handle, item->acpiget, &value) < 0)
217 return -EIO;
218
219 return sprintf(page, "%d\n", value);
220}
221
222static int sony_acpi_write(struct file *file, const char __user *buffer,
223 unsigned long count, void *data)
224{
225 struct sony_acpi_value *item = data;
226 int result;
227 int value;
228
229 if (!item->acpiset)
230 return -EIO;
231
232 if ((result = parse_buffer(buffer, count, &value)) < 0)
233 return result;
234
235 if (item->min != -1 && value < item->min)
236 return -EINVAL;
237 if (item->max != -1 && value > item->max)
238 return -EINVAL;
239
240 if (acpi_callsetfunc(sony_acpi_handle, item->acpiset, value, NULL) < 0)
241 return -EIO;
242
243 return count;
244}
245
246static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
247{
248 printk(LOG_PFX "sony_acpi_notify\n");
249}
250
251static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
252 void *context, void **return_value)
253{
254 struct acpi_namespace_node *node;
255 union acpi_operand_object *operand;
256
257 node = (struct acpi_namespace_node *) handle;
258 operand = (union acpi_operand_object *) node->object;
259
260 printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
261 (u32) operand->method.param_count);
262
263 return AE_OK;
264}
265
266static int sony_acpi_add(struct acpi_device *device)
267{
268 acpi_status status;
269 int result;
270 struct sony_acpi_value *item;
271
272 sony_acpi_handle = device->handle;
273
274 acpi_driver_data(device) = NULL;
275 acpi_device_dir(device) = sony_acpi_dir;
276
277 if (debug) {
278 status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_acpi_handle,
279 1, sony_walk_callback, NULL, NULL);
280 if (ACPI_FAILURE(status)) {
281 printk(LOG_PFX "unable to walk acpi resources\n");
282 result = -ENODEV;
283 goto outwalk;
284 }
285
286 status = acpi_install_notify_handler(sony_acpi_handle,
287 ACPI_DEVICE_NOTIFY,
288 sony_acpi_notify,
289 NULL);
290 if (ACPI_FAILURE(status)) {
291 printk(LOG_PFX "unable to install notify handler\n");
292 result = -ENODEV;
293 goto outnotify;
294 }
295 }
296
297 for (item = sony_acpi_values; item->name; ++item) {
298 acpi_handle handle;
299
300 if (!debug && item->debug)
301 continue;
302
303 if (item->acpiget &&
304 ACPI_FAILURE(acpi_get_handle(sony_acpi_handle,
305 item->acpiget, &handle)))
306 continue;
307
308 if (item->acpiset &&
309 ACPI_FAILURE(acpi_get_handle(sony_acpi_handle,
310 item->acpiset, &handle)))
311 continue;
312
313 item->proc = create_proc_entry(item->name, 0600,
314 acpi_device_dir(device));
315 if (!item->proc) {
316 printk(LOG_PFX "unable to create proc entry\n");
317 result = -EIO;
318 goto outproc;
319 }
320
321 item->proc->read_proc = sony_acpi_read;
322 item->proc->write_proc = sony_acpi_write;
323 item->proc->data = item;
324 item->proc->owner = THIS_MODULE;
325 }
326
327 printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n");
328
329 return 0;
330
331outproc:
332 if (debug) {
333 status = acpi_remove_notify_handler(sony_acpi_handle,
334 ACPI_DEVICE_NOTIFY,
335 sony_acpi_notify);
336 if (ACPI_FAILURE(status))
337 printk(LOG_PFX "unable to remove notify handler\n");
338 }
339outnotify:
340 for (item = sony_acpi_values; item->name; ++item)
341 if (item->proc)
342 remove_proc_entry(item->name, acpi_device_dir(device));
343outwalk:
344 return result;
345}
346
347
348static int sony_acpi_remove(struct acpi_device *device, int type)
349{
350 acpi_status status;
351 struct sony_acpi_value *item;
352
353 if (debug) {
354 status = acpi_remove_notify_handler(sony_acpi_handle,
355 ACPI_DEVICE_NOTIFY,
356 sony_acpi_notify);
357 if (ACPI_FAILURE(status))
358 printk(LOG_PFX "unable to remove notify handler\n");
359 }
360
361 for (item = sony_acpi_values; item->name; ++item)
362 if (item->proc)
363 remove_proc_entry(item->name, acpi_device_dir(device));
364
365 printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n");
366
367 return 0;
368}
369
370static int __init sony_acpi_init(void)
371{
372 int result;
373
374 sony_acpi_dir = proc_mkdir("sony", acpi_root_dir);
375 if (!sony_acpi_dir) {
376 printk(LOG_PFX "unable to create /proc entry\n");
377 return -ENODEV;
378 }
379 sony_acpi_dir->owner = THIS_MODULE;
380
381 result = acpi_bus_register_driver(&sony_acpi_driver);
382 if (result < 0) {
383 remove_proc_entry("sony", acpi_root_dir);
384 return -ENODEV;
385 }
386 return 0;
387}
388
389
390static void __exit sony_acpi_exit(void)
391{
392 acpi_bus_unregister_driver(&sony_acpi_driver);
393 remove_proc_entry("sony", acpi_root_dir);
394}
395
396module_init(sony_acpi_init);
397module_exit(sony_acpi_exit);