blob: beb56b9df5c8f1bc0f0d2968a245644fb80119d6 [file] [log] [blame]
Stelian Pop7f09c432007-01-13 23:04:31 +01001/*
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
Stelian Pop7f09c432007-01-13 23:04:31 +010049static acpi_handle sony_acpi_handle;
50static struct proc_dir_entry *sony_acpi_dir;
51
52static struct sony_acpi_value {
53 char *name; /* name of the entry */
54 struct proc_dir_entry *proc; /* /proc entry */
55 char *acpiget;/* name of the ACPI get function */
56 char *acpiset;/* name of the ACPI get function */
57 int min; /* minimum allowed value or -1 */
58 int max; /* maximum allowed value or -1 */
Andrew Morton3f4f4612007-01-13 23:04:32 +010059 int value; /* current setting */
60 int valid; /* Has ever been set */
Stelian Pop7f09c432007-01-13 23:04:31 +010061 int debug; /* active only in debug mode ? */
62} sony_acpi_values[] = {
63 {
64 .name = "brightness",
65 .acpiget = "GBRT",
66 .acpiset = "SBRT",
67 .min = 1,
68 .max = 8,
69 .debug = 0,
70 },
71 {
72 .name = "brightness_default",
73 .acpiget = "GPBR",
74 .acpiset = "SPBR",
75 .min = 1,
76 .max = 8,
77 .debug = 0,
78 },
79 {
80 .name = "fnkey",
81 .acpiget = "GHKE",
82 .debug = 0,
83 },
84 {
85 .name = "cdpower",
86 .acpiget = "GCDP",
87 .acpiset = "SCDP",
88 .min = -1,
89 .max = -1,
90 .debug = 0,
91 },
92 {
93 .name = "PID",
94 .acpiget = "GPID",
95 .debug = 1,
96 },
97 {
98 .name = "CTR",
99 .acpiget = "GCTR",
100 .acpiset = "SCTR",
101 .min = -1,
102 .max = -1,
103 .debug = 1,
104 },
105 {
106 .name = "PCR",
107 .acpiget = "GPCR",
108 .acpiset = "SPCR",
109 .min = -1,
110 .max = -1,
111 .debug = 1,
112 },
113 {
114 .name = "CMI",
115 .acpiget = "GCMI",
116 .acpiset = "SCMI",
117 .min = -1,
118 .max = -1,
119 .debug = 1,
120 },
121 {
122 .name = NULL,
123 }
124};
125
126static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
127{
128 struct acpi_buffer output;
129 union acpi_object out_obj;
130 acpi_status status;
131
132 output.length = sizeof(out_obj);
133 output.pointer = &out_obj;
134
135 status = acpi_evaluate_object(handle, name, NULL, &output);
136 if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
137 *result = out_obj.integer.value;
138 return 0;
139 }
140
141 printk(LOG_PFX "acpi_callreadfunc failed\n");
142
143 return -1;
144}
145
146static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
147 int *result)
148{
149 struct acpi_object_list params;
150 union acpi_object in_obj;
151 struct acpi_buffer output;
152 union acpi_object out_obj;
153 acpi_status status;
154
155 params.count = 1;
156 params.pointer = &in_obj;
157 in_obj.type = ACPI_TYPE_INTEGER;
158 in_obj.integer.value = value;
159
160 output.length = sizeof(out_obj);
161 output.pointer = &out_obj;
162
163 status = acpi_evaluate_object(handle, name, &params, &output);
164 if (status == AE_OK) {
165 if (result != NULL) {
166 if (out_obj.type != ACPI_TYPE_INTEGER) {
167 printk(LOG_PFX "acpi_evaluate_object bad "
168 "return type\n");
169 return -1;
170 }
171 *result = out_obj.integer.value;
172 }
173 return 0;
174 }
175
176 printk(LOG_PFX "acpi_evaluate_object failed\n");
177
178 return -1;
179}
180
181static int parse_buffer(const char __user *buffer, unsigned long count,
182 int *val) {
183 char s[32];
184 int ret;
185
186 if (count > 31)
187 return -EINVAL;
188 if (copy_from_user(s, buffer, count))
189 return -EFAULT;
190 s[count] = '\0';
191 ret = simple_strtoul(s, NULL, 10);
192 *val = ret;
193 return 0;
194}
195
196static int sony_acpi_read(char* page, char** start, off_t off, int count,
197 int* eof, void *data)
198{
199 struct sony_acpi_value *item = data;
200 int value;
201
202 if (!item->acpiget)
203 return -EIO;
204
205 if (acpi_callgetfunc(sony_acpi_handle, item->acpiget, &value) < 0)
206 return -EIO;
207
208 return sprintf(page, "%d\n", value);
209}
210
211static int sony_acpi_write(struct file *file, const char __user *buffer,
212 unsigned long count, void *data)
213{
214 struct sony_acpi_value *item = data;
215 int result;
216 int value;
217
218 if (!item->acpiset)
219 return -EIO;
220
221 if ((result = parse_buffer(buffer, count, &value)) < 0)
222 return result;
223
224 if (item->min != -1 && value < item->min)
225 return -EINVAL;
226 if (item->max != -1 && value > item->max)
227 return -EINVAL;
228
229 if (acpi_callsetfunc(sony_acpi_handle, item->acpiset, value, NULL) < 0)
230 return -EIO;
Andrew Morton3f4f4612007-01-13 23:04:32 +0100231 item->value = value;
232 item->valid = 1;
Stelian Pop7f09c432007-01-13 23:04:31 +0100233 return count;
234}
235
Andrew Morton3f4f4612007-01-13 23:04:32 +0100236static int sony_acpi_resume(struct acpi_device *device, int state)
237{
238 struct sony_acpi_value *item;
239
240 for (item = sony_acpi_values; item->name; item++) {
241 int ret;
242
243 if (!item->valid)
244 continue;
245 ret = acpi_callsetfunc(sony_acpi_handle, item->acpiset,
246 item->value, NULL);
247 if (ret < 0) {
248 printk("%s: %d\n", __FUNCTION__, ret);
249 break;
250 }
251 }
252 return 0;
253}
254
Stelian Pop7f09c432007-01-13 23:04:31 +0100255static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
256{
257 printk(LOG_PFX "sony_acpi_notify\n");
258}
259
260static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
261 void *context, void **return_value)
262{
263 struct acpi_namespace_node *node;
264 union acpi_operand_object *operand;
265
266 node = (struct acpi_namespace_node *) handle;
267 operand = (union acpi_operand_object *) node->object;
268
269 printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
270 (u32) operand->method.param_count);
271
272 return AE_OK;
273}
274
275static int sony_acpi_add(struct acpi_device *device)
276{
277 acpi_status status;
278 int result;
279 struct sony_acpi_value *item;
280
281 sony_acpi_handle = device->handle;
282
283 acpi_driver_data(device) = NULL;
284 acpi_device_dir(device) = sony_acpi_dir;
285
286 if (debug) {
287 status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_acpi_handle,
288 1, sony_walk_callback, NULL, NULL);
289 if (ACPI_FAILURE(status)) {
290 printk(LOG_PFX "unable to walk acpi resources\n");
291 result = -ENODEV;
292 goto outwalk;
293 }
294
295 status = acpi_install_notify_handler(sony_acpi_handle,
296 ACPI_DEVICE_NOTIFY,
297 sony_acpi_notify,
298 NULL);
299 if (ACPI_FAILURE(status)) {
300 printk(LOG_PFX "unable to install notify handler\n");
301 result = -ENODEV;
302 goto outnotify;
303 }
304 }
305
306 for (item = sony_acpi_values; item->name; ++item) {
307 acpi_handle handle;
308
309 if (!debug && item->debug)
310 continue;
311
312 if (item->acpiget &&
313 ACPI_FAILURE(acpi_get_handle(sony_acpi_handle,
314 item->acpiget, &handle)))
315 continue;
316
317 if (item->acpiset &&
318 ACPI_FAILURE(acpi_get_handle(sony_acpi_handle,
319 item->acpiset, &handle)))
320 continue;
321
322 item->proc = create_proc_entry(item->name, 0600,
323 acpi_device_dir(device));
324 if (!item->proc) {
325 printk(LOG_PFX "unable to create proc entry\n");
326 result = -EIO;
327 goto outproc;
328 }
329
330 item->proc->read_proc = sony_acpi_read;
331 item->proc->write_proc = sony_acpi_write;
332 item->proc->data = item;
333 item->proc->owner = THIS_MODULE;
334 }
335
336 printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n");
337
338 return 0;
339
340outproc:
341 if (debug) {
342 status = acpi_remove_notify_handler(sony_acpi_handle,
343 ACPI_DEVICE_NOTIFY,
344 sony_acpi_notify);
345 if (ACPI_FAILURE(status))
346 printk(LOG_PFX "unable to remove notify handler\n");
347 }
348outnotify:
349 for (item = sony_acpi_values; item->name; ++item)
350 if (item->proc)
351 remove_proc_entry(item->name, acpi_device_dir(device));
352outwalk:
353 return result;
354}
355
Stelian Pop7f09c432007-01-13 23:04:31 +0100356static int sony_acpi_remove(struct acpi_device *device, int type)
357{
358 acpi_status status;
359 struct sony_acpi_value *item;
360
361 if (debug) {
362 status = acpi_remove_notify_handler(sony_acpi_handle,
363 ACPI_DEVICE_NOTIFY,
364 sony_acpi_notify);
365 if (ACPI_FAILURE(status))
366 printk(LOG_PFX "unable to remove notify handler\n");
367 }
368
369 for (item = sony_acpi_values; item->name; ++item)
370 if (item->proc)
371 remove_proc_entry(item->name, acpi_device_dir(device));
372
373 printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n");
374
375 return 0;
376}
377
Andrew Morton3f4f4612007-01-13 23:04:32 +0100378static struct acpi_driver sony_acpi_driver = {
379 .name = ACPI_SNC_DRIVER_NAME,
380 .class = ACPI_SNC_CLASS,
381 .ids = ACPI_SNC_HID,
382 .ops = {
383 .add = sony_acpi_add,
384 .remove = sony_acpi_remove,
385 .resume = sony_acpi_resume,
386 },
387};
388
Stelian Pop7f09c432007-01-13 23:04:31 +0100389static int __init sony_acpi_init(void)
390{
391 int result;
392
393 sony_acpi_dir = proc_mkdir("sony", acpi_root_dir);
394 if (!sony_acpi_dir) {
395 printk(LOG_PFX "unable to create /proc entry\n");
396 return -ENODEV;
397 }
398 sony_acpi_dir->owner = THIS_MODULE;
399
400 result = acpi_bus_register_driver(&sony_acpi_driver);
401 if (result < 0) {
402 remove_proc_entry("sony", acpi_root_dir);
403 return -ENODEV;
404 }
405 return 0;
406}
407
408
409static void __exit sony_acpi_exit(void)
410{
411 acpi_bus_unregister_driver(&sony_acpi_driver);
412 remove_proc_entry("sony", acpi_root_dir);
413}
414
415module_init(sony_acpi_init);
416module_exit(sony_acpi_exit);