]>
Commit | Line | Data |
---|---|---|
7f09c432 SP |
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 | ||
40 | MODULE_AUTHOR("Stelian Pop"); | |
41 | MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME); | |
42 | MODULE_LICENSE("GPL"); | |
43 | ||
44 | static int debug; | |
45 | module_param(debug, int, 0); | |
46 | MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help " | |
47 | "the development of this driver"); | |
48 | ||
49 | static int sony_acpi_add (struct acpi_device *device); | |
50 | static int sony_acpi_remove (struct acpi_device *device, int type); | |
51 | ||
52 | static 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 | ||
62 | static acpi_handle sony_acpi_handle; | |
63 | static struct proc_dir_entry *sony_acpi_dir; | |
64 | ||
65 | static 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 | ||
137 | static 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 | ||
157 | static 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, ¶ms, &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 | ||
192 | static 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 | ||
207 | static 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 | ||
222 | static 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 | ||
246 | static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) | |
247 | { | |
248 | printk(LOG_PFX "sony_acpi_notify\n"); | |
249 | } | |
250 | ||
251 | static 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 | ||
266 | static 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 | ||
331 | outproc: | |
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 | } | |
339 | outnotify: | |
340 | for (item = sony_acpi_values; item->name; ++item) | |
341 | if (item->proc) | |
342 | remove_proc_entry(item->name, acpi_device_dir(device)); | |
343 | outwalk: | |
344 | return result; | |
345 | } | |
346 | ||
347 | ||
348 | static 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 | ||
370 | static 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 | ||
390 | static 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 | ||
396 | module_init(sony_acpi_init); | |
397 | module_exit(sony_acpi_exit); |