aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/sony_acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/sony_acpi.c')
-rw-r--r--drivers/acpi/sony_acpi.c397
1 files changed, 397 insertions, 0 deletions
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);