aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig14
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/sony-laptop.c504
3 files changed, 519 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 00db31c314e0..78fc47ba9717 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -88,4 +88,18 @@ config MSI_LAPTOP
88 88
89 If you have an MSI S270 laptop, say Y or M here. 89 If you have an MSI S270 laptop, say Y or M here.
90 90
91config SONY_LAPTOP
92 tristate "Sony Laptop Extras"
93 depends on X86 && ACPI
94 select BACKLIGHT_CLASS_DEVICE
95 ---help---
96 This mini-driver drives the ACPI SNC device present in the
97 ACPI BIOS of the Sony Vaio laptops.
98
99 It gives access to some extra laptop functionalities. In
100 its current form, the only thing this driver does is letting
101 the user set or query the screen brightness.
102
103 Read <file:Documentation/acpi/sony_acpi.txt> for more information.
104
91endmenu 105endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index c9e98ab021c5..f86f9dcfcfe5 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_LKDTM) += lkdtm.o
10obj-$(CONFIG_TIFM_CORE) += tifm_core.o 10obj-$(CONFIG_TIFM_CORE) += tifm_core.o
11obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o 11obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
12obj-$(CONFIG_SGI_IOC4) += ioc4.o 12obj-$(CONFIG_SGI_IOC4) += ioc4.o
13obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c
new file mode 100644
index 000000000000..103657e1b449
--- /dev/null
+++ b/drivers/misc/sony-laptop.c
@@ -0,0 +1,504 @@
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 <linux/backlight.h>
31#include <linux/err.h>
32#include <acpi/acpi_drivers.h>
33#include <acpi/acpi_bus.h>
34#include <asm/uaccess.h>
35
36#define ACPI_SNC_CLASS "sony"
37#define ACPI_SNC_HID "SNY5001"
38#define ACPI_SNC_DRIVER_NAME "ACPI Sony Notebook Control Driver v0.3"
39
40/* the device uses 1-based values, while the backlight subsystem uses
41 0-based values */
42#define SONY_MAX_BRIGHTNESS 8
43
44#define LOG_PFX KERN_WARNING "sony_acpi: "
45
46MODULE_AUTHOR("Stelian Pop");
47MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME);
48MODULE_LICENSE("GPL");
49
50static int debug;
51module_param(debug, int, 0);
52MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
53 "the development of this driver");
54
55static acpi_handle sony_acpi_handle;
56static struct proc_dir_entry *sony_acpi_dir;
57static struct acpi_device *sony_acpi_acpi_device = NULL;
58
59static int sony_backlight_update_status(struct backlight_device *bd);
60static int sony_backlight_get_brightness(struct backlight_device *bd);
61static struct backlight_device *sony_backlight_device;
62static struct backlight_properties sony_backlight_properties = {
63 .owner = THIS_MODULE,
64 .update_status = sony_backlight_update_status,
65 .get_brightness = sony_backlight_get_brightness,
66 .max_brightness = SONY_MAX_BRIGHTNESS - 1,
67};
68
69static struct sony_acpi_value {
70 char *name; /* name of the entry */
71 struct proc_dir_entry *proc; /* /proc entry */
72 char *acpiget;/* name of the ACPI get function */
73 char *acpiset;/* name of the ACPI get function */
74 int min; /* minimum allowed value or -1 */
75 int max; /* maximum allowed value or -1 */
76 int value; /* current setting */
77 int valid; /* Has ever been set */
78 int debug; /* active only in debug mode ? */
79} sony_acpi_values[] = {
80 {
81 /* for backward compatibility only */
82 .name = "brightness",
83 .acpiget = "GBRT",
84 .acpiset = "SBRT",
85 .min = 1,
86 .max = SONY_MAX_BRIGHTNESS,
87 .debug = 0,
88 },
89 {
90 .name = "brightness_default",
91 .acpiget = "GPBR",
92 .acpiset = "SPBR",
93 .min = 1,
94 .max = SONY_MAX_BRIGHTNESS,
95 .debug = 0,
96 },
97 {
98 .name = "fnkey",
99 .acpiget = "GHKE",
100 .debug = 0,
101 },
102 {
103 .name = "cdpower",
104 .acpiget = "GCDP",
105 .acpiset = "SCDP",
106 .min = 0,
107 .max = 1,
108 .debug = 0,
109 },
110 {
111 .name = "cdpower",
112 .acpiget = "GCDP",
113 .acpiset = "CDPW",
114 .min = 0,
115 .max = 1,
116 .debug = 0,
117 },
118 {
119 .name = "audiopower",
120 .acpiget = "GAZP",
121 .acpiset = "AZPW",
122 .min = 0,
123 .max = 1,
124 .debug = 0,
125 },
126 {
127 .name = "lanpower",
128 .acpiget = "GLNP",
129 .acpiset = "LNPW",
130 .min = 0,
131 .max = 1,
132 .debug = 1,
133 },
134 {
135 .name = "PID",
136 .acpiget = "GPID",
137 .debug = 1,
138 },
139 {
140 .name = "CTR",
141 .acpiget = "GCTR",
142 .acpiset = "SCTR",
143 .min = -1,
144 .max = -1,
145 .debug = 1,
146 },
147 {
148 .name = "PCR",
149 .acpiget = "GPCR",
150 .acpiset = "SPCR",
151 .min = -1,
152 .max = -1,
153 .debug = 1,
154 },
155 {
156 .name = "CMI",
157 .acpiget = "GCMI",
158 .acpiset = "SCMI",
159 .min = -1,
160 .max = -1,
161 .debug = 1,
162 },
163 {
164 .name = NULL,
165 }
166};
167
168static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
169{
170 struct acpi_buffer output;
171 union acpi_object out_obj;
172 acpi_status status;
173
174 output.length = sizeof(out_obj);
175 output.pointer = &out_obj;
176
177 status = acpi_evaluate_object(handle, name, NULL, &output);
178 if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
179 *result = out_obj.integer.value;
180 return 0;
181 }
182
183 printk(LOG_PFX "acpi_callreadfunc failed\n");
184
185 return -1;
186}
187
188static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
189 int *result)
190{
191 struct acpi_object_list params;
192 union acpi_object in_obj;
193 struct acpi_buffer output;
194 union acpi_object out_obj;
195 acpi_status status;
196
197 params.count = 1;
198 params.pointer = &in_obj;
199 in_obj.type = ACPI_TYPE_INTEGER;
200 in_obj.integer.value = value;
201
202 output.length = sizeof(out_obj);
203 output.pointer = &out_obj;
204
205 status = acpi_evaluate_object(handle, name, &params, &output);
206 if (status == AE_OK) {
207 if (result != NULL) {
208 if (out_obj.type != ACPI_TYPE_INTEGER) {
209 printk(LOG_PFX "acpi_evaluate_object bad "
210 "return type\n");
211 return -1;
212 }
213 *result = out_obj.integer.value;
214 }
215 return 0;
216 }
217
218 printk(LOG_PFX "acpi_evaluate_object failed\n");
219
220 return -1;
221}
222
223static int parse_buffer(const char __user *buffer, unsigned long count,
224 int *val) {
225 char s[32];
226 int ret;
227
228 if (count > 31)
229 return -EINVAL;
230 if (copy_from_user(s, buffer, count))
231 return -EFAULT;
232 s[count] = '\0';
233 ret = simple_strtoul(s, NULL, 10);
234 *val = ret;
235 return 0;
236}
237
238static int sony_acpi_read(char* page, char** start, off_t off, int count,
239 int* eof, void *data)
240{
241 struct sony_acpi_value *item = data;
242 int value;
243
244 if (!item->acpiget)
245 return -EIO;
246
247 if (acpi_callgetfunc(sony_acpi_handle, item->acpiget, &value) < 0)
248 return -EIO;
249
250 return sprintf(page, "%d\n", value);
251}
252
253static int sony_acpi_write(struct file *file, const char __user *buffer,
254 unsigned long count, void *data)
255{
256 struct sony_acpi_value *item = data;
257 int result;
258 int value;
259
260 if (!item->acpiset)
261 return -EIO;
262
263 if ((result = parse_buffer(buffer, count, &value)) < 0)
264 return result;
265
266 if (item->min != -1 && value < item->min)
267 return -EINVAL;
268 if (item->max != -1 && value > item->max)
269 return -EINVAL;
270
271 if (acpi_callsetfunc(sony_acpi_handle, item->acpiset, value, NULL) < 0)
272 return -EIO;
273 item->value = value;
274 item->valid = 1;
275 return count;
276}
277
278static int sony_acpi_resume(struct acpi_device *device)
279{
280 struct sony_acpi_value *item;
281
282 for (item = sony_acpi_values; item->name; item++) {
283 int ret;
284
285 if (!item->valid)
286 continue;
287 ret = acpi_callsetfunc(sony_acpi_handle, item->acpiset,
288 item->value, NULL);
289 if (ret < 0) {
290 printk("%s: %d\n", __FUNCTION__, ret);
291 break;
292 }
293 }
294 return 0;
295}
296
297static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
298{
299 if (debug)
300 printk(LOG_PFX "sony_acpi_notify, event: %d\n", event);
301 acpi_bus_generate_event(sony_acpi_acpi_device, 1, event);
302}
303
304static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
305 void *context, void **return_value)
306{
307 struct acpi_namespace_node *node;
308 union acpi_operand_object *operand;
309
310 node = (struct acpi_namespace_node *) handle;
311 operand = (union acpi_operand_object *) node->object;
312
313 printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
314 (u32) operand->method.param_count);
315
316 return AE_OK;
317}
318
319static int sony_acpi_add(struct acpi_device *device)
320{
321 acpi_status status;
322 int result;
323 acpi_handle handle;
324 mode_t proc_file_mode;
325 struct sony_acpi_value *item;
326
327 sony_acpi_acpi_device = device;
328
329 sony_acpi_handle = device->handle;
330
331 acpi_driver_data(device) = NULL;
332 acpi_device_dir(device) = sony_acpi_dir;
333
334 if (debug) {
335 status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_acpi_handle,
336 1, sony_walk_callback, NULL, NULL);
337 if (ACPI_FAILURE(status)) {
338 printk(LOG_PFX "unable to walk acpi resources\n");
339 result = -ENODEV;
340 goto outwalk;
341 }
342 }
343
344 status = acpi_install_notify_handler(sony_acpi_handle,
345 ACPI_DEVICE_NOTIFY,
346 sony_acpi_notify,
347 NULL);
348 if (ACPI_FAILURE(status)) {
349 printk(LOG_PFX "unable to install notify handler\n");
350 result = -ENODEV;
351 goto outnotify;
352 }
353
354 if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle, "GBRT", &handle))) {
355 sony_backlight_device = backlight_device_register("sony", NULL,
356 NULL, &sony_backlight_properties);
357
358 if (IS_ERR(sony_backlight_device)) {
359 printk(LOG_PFX "unable to register backlight device\n");
360 sony_backlight_device = NULL;
361 }
362 else
363 sony_backlight_properties.brightness =
364 sony_backlight_get_brightness(sony_backlight_device);
365 }
366
367 for (item = sony_acpi_values; item->name; ++item) {
368 proc_file_mode = 0;
369
370 if (!debug && item->debug)
371 continue;
372
373 if (item->acpiget) {
374 if (ACPI_FAILURE(acpi_get_handle(sony_acpi_handle,
375 item->acpiget, &handle)))
376 continue;
377
378 proc_file_mode |= S_IRUSR;
379 }
380
381 if (item->acpiset) {
382 if (ACPI_FAILURE(acpi_get_handle(sony_acpi_handle,
383 item->acpiset, &handle)))
384 continue;
385
386 proc_file_mode |= S_IWUSR;
387 }
388
389 item->proc = create_proc_entry(item->name, proc_file_mode,
390 acpi_device_dir(device));
391 if (!item->proc) {
392 printk(LOG_PFX "unable to create proc entry\n");
393 result = -EIO;
394 goto outproc;
395 }
396
397 item->proc->read_proc = sony_acpi_read;
398 item->proc->write_proc = sony_acpi_write;
399 item->proc->data = item;
400 item->proc->owner = THIS_MODULE;
401 }
402
403 printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n");
404
405 return 0;
406
407outproc:
408 if (sony_backlight_device)
409 backlight_device_unregister(sony_backlight_device);
410
411 for (item = sony_acpi_values; item->name; ++item)
412 if (item->proc)
413 remove_proc_entry(item->name, acpi_device_dir(device));
414outnotify:
415 status = acpi_remove_notify_handler(sony_acpi_handle,
416 ACPI_DEVICE_NOTIFY,
417 sony_acpi_notify);
418 if (ACPI_FAILURE(status))
419 printk(LOG_PFX "unable to remove notify handler\n");
420outwalk:
421 return result;
422}
423
424static int sony_acpi_remove(struct acpi_device *device, int type)
425{
426 acpi_status status;
427 struct sony_acpi_value *item;
428
429 if (sony_backlight_device)
430 backlight_device_unregister(sony_backlight_device);
431
432 sony_acpi_acpi_device = NULL;
433
434 status = acpi_remove_notify_handler(sony_acpi_handle,
435 ACPI_DEVICE_NOTIFY,
436 sony_acpi_notify);
437 if (ACPI_FAILURE(status))
438 printk(LOG_PFX "unable to remove notify handler\n");
439
440 for (item = sony_acpi_values; item->name; ++item)
441 if (item->proc)
442 remove_proc_entry(item->name, acpi_device_dir(device));
443
444 printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n");
445
446 return 0;
447}
448
449static int sony_backlight_update_status(struct backlight_device *bd)
450{
451 return acpi_callsetfunc(sony_acpi_handle, "SBRT",
452 bd->props->brightness + 1,
453 NULL);
454}
455
456static int sony_backlight_get_brightness(struct backlight_device *bd)
457{
458 int value;
459
460 if (acpi_callgetfunc(sony_acpi_handle, "GBRT", &value))
461 return 0;
462 /* brightness levels are 1-based, while backlight ones are 0-based */
463 return value - 1;
464}
465
466static struct acpi_driver sony_acpi_driver = {
467 .name = ACPI_SNC_DRIVER_NAME,
468 .class = ACPI_SNC_CLASS,
469 .ids = ACPI_SNC_HID,
470 .ops = {
471 .add = sony_acpi_add,
472 .remove = sony_acpi_remove,
473 .resume = sony_acpi_resume,
474 },
475};
476
477static int __init sony_acpi_init(void)
478{
479 int result;
480
481 sony_acpi_dir = proc_mkdir("sony", acpi_root_dir);
482 if (!sony_acpi_dir) {
483 printk(LOG_PFX "unable to create /proc entry\n");
484 return -ENODEV;
485 }
486 sony_acpi_dir->owner = THIS_MODULE;
487
488 result = acpi_bus_register_driver(&sony_acpi_driver);
489 if (result < 0) {
490 remove_proc_entry("sony", acpi_root_dir);
491 return -ENODEV;
492 }
493 return 0;
494}
495
496
497static void __exit sony_acpi_exit(void)
498{
499 acpi_bus_unregister_driver(&sony_acpi_driver);
500 remove_proc_entry("sony", acpi_root_dir);
501}
502
503module_init(sony_acpi_init);
504module_exit(sony_acpi_exit);