aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLen Brown <len.brown@intel.com>2007-02-16 22:10:55 -0500
committerLen Brown <len.brown@intel.com>2007-02-16 22:10:55 -0500
commit9cdd79c9b99873d600d397fda012fc3f57cc2776 (patch)
treecead6543c1a53bdadee8953391a80d6c264f911b
parentc0cd79d11412969b6b8fa1624cdc1277db82e2fe (diff)
parent156c221b728ccd4657c359cd25428946856beca8 (diff)
Pull sony into release branch
-rw-r--r--Documentation/sony-laptop.txt106
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/misc/Kconfig15
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/sony-laptop.c562
5 files changed, 686 insertions, 0 deletions
diff --git a/Documentation/sony-laptop.txt b/Documentation/sony-laptop.txt
new file mode 100644
index 000000000000..dfd26df056f4
--- /dev/null
+++ b/Documentation/sony-laptop.txt
@@ -0,0 +1,106 @@
1Sony Notebook Control Driver (SNC) Readme
2-----------------------------------------
3 Copyright (C) 2004- 2005 Stelian Pop <stelian@popies.net>
4 Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
5
6This mini-driver drives the SNC device present in the ACPI BIOS of
7the Sony Vaio laptops.
8
9It gives access to some extra laptop functionalities. In its current
10form, this driver let the user set or query the screen brightness
11through the backlight subsystem and remove/apply power to some devices.
12
13Backlight control:
14------------------
15If your laptop model supports it, you will find sysfs files in the
16/sys/class/backlight/sony/
17directory. You will be able to query and set the current screen
18brightness:
19 brightness get/set screen brightness (an iteger
20 between 0 and 7)
21 actual_brightness reading from this file will query the HW
22 to get real brightness value
23 max_brightness the maximum brightness value
24
25
26Platform specific:
27------------------
28Loading the sony-laptop module will create a
29/sys/devices/platform/sony-laptop/
30directory populated with some files.
31
32You then read/write integer values from/to those files by using
33standard UNIX tools.
34
35The files are:
36 brightness_default screen brightness which will be set
37 when the laptop will be rebooted
38 cdpower power on/off the internal CD drive
39 audiopower power on/off the internal sound card
40 lanpower power on/off the internal ethernet card
41 (only in debug mode)
42
43Note that some files may be missing if they are not supported
44by your particular laptop model.
45
46Example usage:
47 # echo "1" > /sys/devices/platform/sony-laptop/brightness_default
48sets the lowest screen brightness for the next and later reboots,
49 # echo "8" > /sys/devices/platform/sony-laptop/brightness_default
50sets the highest screen brightness for the next and later reboots,
51 # cat /sys/devices/platform/sony-laptop/brightness_default
52retrieves the value.
53
54 # echo "0" > /sys/devices/platform/sony-laptop/audiopower
55powers off the sound card,
56 # echo "1" > /sys/devices/platform/sony-laptop/audiopower
57powers on the sound card.
58
59Development:
60------------
61
62If you want to help with the development of this driver (and
63you are not afraid of any side effects doing strange things with
64your ACPI BIOS could have on your laptop), load the driver and
65pass the option 'debug=1'.
66
67REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS.
68
69In your kernel logs you will find the list of all ACPI methods
70the SNC device has on your laptop. You can see the GCDP/GCDP methods
71used to pwer on/off the CD drive, but there are others.
72
73I HAVE NO IDEA WHAT THOSE METHODS DO.
74
75The sony-laptop driver creates, for some of those methods (the most
76current ones found on several Vaio models), an entry under
77/sys/devices/platform/sony-laptop, just like the 'cdpower' one.
78You can create other entries corresponding to your own laptop methods by
79further editing the source (see the 'sony_acpi_values' table, and add a new
80entry to this table with your get/set method names using the
81HANDLE_NAMES macro).
82
83Your mission, should you accept it, is to try finding out what
84those entries are for, by reading/writing random values from/to those
85files and find out what is the impact on your laptop.
86
87Should you find anything interesting, please report it back to me,
88I will not disavow all knowledge of your actions :)
89
90Bugs/Limitations:
91-----------------
92
93* This driver is not based on official documentation from Sony
94 (because there is none), so there is no guarantee this driver
95 will work at all, or do the right thing. Although this hasn't
96 happened to me, this driver could do very bad things to your
97 laptop, including permanent damage.
98
99* The sony-laptop and sonypi drivers do not interact at all. In the
100 future, sonypi could use sony-laptop to do (part of) its business.
101
102* spicctrl, which is the userspace tool used to communicate with the
103 sonypi driver (through /dev/sonypi) does not try to use the
104 sony-laptop driver. In the future, spicctrl could try sonypi first,
105 and if it isn't present, try sony-laptop instead.
106
diff --git a/MAINTAINERS b/MAINTAINERS
index 5a546e594567..a384551a5978 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3068,6 +3068,8 @@ S: Maintained
3068SONY VAIO CONTROL DEVICE DRIVER 3068SONY VAIO CONTROL DEVICE DRIVER
3069P: Stelian Pop 3069P: Stelian Pop
3070M: stelian@popies.net 3070M: stelian@popies.net
3071P: Mattia Dongili
3072M: malattia@linux.it
3071W: http://popies.net/sonypi/ 3073W: http://popies.net/sonypi/
3072S: Maintained 3074S: Maintained
3073 3075
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index bedae4ad3f74..80b199fa0aa9 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -107,4 +107,19 @@ config MSI_LAPTOP
107 107
108 If you have an MSI S270 laptop, say Y or M here. 108 If you have an MSI S270 laptop, say Y or M here.
109 109
110config SONY_LAPTOP
111 tristate "Sony Laptop Extras"
112 depends on X86 && ACPI
113 select BACKLIGHT_CLASS_DEVICE
114 ---help---
115 This mini-driver drives the SNC device present in the ACPI BIOS of
116 the Sony Vaio laptops.
117
118 It gives access to some extra laptop functionalities. In its current
119 form, this driver let the user set or query the screen brightness
120 through the backlight subsystem and remove/apply power to some
121 devices.
122
123 Read <file:Documentation/sony-laptop.txt> for more information.
124
110endmenu 125endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 35da53c409c0..7793ccd79049 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_LKDTM) += lkdtm.o
11obj-$(CONFIG_TIFM_CORE) += tifm_core.o 11obj-$(CONFIG_TIFM_CORE) += tifm_core.o
12obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o 12obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
13obj-$(CONFIG_SGI_IOC4) += ioc4.o 13obj-$(CONFIG_SGI_IOC4) += ioc4.o
14obj-$(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..cabbed0015e4
--- /dev/null
+++ b/drivers/misc/sony-laptop.c
@@ -0,0 +1,562 @@
1/*
2 * ACPI Sony Notebook Control Driver (SNC)
3 *
4 * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
5 * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
6 *
7 * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
8 * which are copyrighted by their respective authors.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25
26#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/moduleparam.h>
29#include <linux/init.h>
30#include <linux/types.h>
31#include <linux/backlight.h>
32#include <linux/platform_device.h>
33#include <linux/err.h>
34#include <acpi/acpi_drivers.h>
35#include <acpi/acpi_bus.h>
36#include <asm/uaccess.h>
37
38#define ACPI_SNC_CLASS "sony"
39#define ACPI_SNC_HID "SNY5001"
40#define ACPI_SNC_DRIVER_NAME "ACPI Sony Notebook Control Driver v0.4"
41
42/* the device uses 1-based values, while the backlight subsystem uses
43 0-based values */
44#define SONY_MAX_BRIGHTNESS 8
45
46#define LOG_PFX KERN_WARNING "sony-laptop: "
47
48MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
49MODULE_DESCRIPTION(ACPI_SNC_DRIVER_NAME);
50MODULE_LICENSE("GPL");
51
52static int debug;
53module_param(debug, int, 0);
54MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
55 "the development of this driver");
56
57static ssize_t sony_acpi_show(struct device *, struct device_attribute *,
58 char *);
59static ssize_t sony_acpi_store(struct device *, struct device_attribute *,
60 const char *, size_t);
61static int boolean_validate(const int, const int);
62static int brightness_default_validate(const int, const int);
63
64#define SNC_VALIDATE_IN 0
65#define SNC_VALIDATE_OUT 1
66
67struct sony_acpi_value {
68 char *name; /* name of the entry */
69 char **acpiget; /* names of the ACPI get function */
70 char **acpiset; /* names of the ACPI set function */
71 int (*validate)(const int, const int); /* input/output validation */
72 int value; /* current setting */
73 int valid; /* Has ever been set */
74 int debug; /* active only in debug mode ? */
75 struct device_attribute devattr; /* sysfs atribute */
76};
77
78#define HANDLE_NAMES(_name, _values...) \
79 static char *snc_##_name[] = { _values, NULL }
80
81#define SONY_ACPI_VALUE(_name, _getters, _setters, _validate, _debug) \
82 { \
83 .name = __stringify(_name), \
84 .acpiget = _getters, \
85 .acpiset = _setters, \
86 .validate = _validate, \
87 .debug = _debug, \
88 .devattr = __ATTR(_name, 0, sony_acpi_show, sony_acpi_store), \
89 }
90
91#define SONY_ACPI_VALUE_NULL { .name = NULL }
92
93HANDLE_NAMES(fnkey_get, "GHKE");
94
95HANDLE_NAMES(brightness_def_get, "GPBR");
96HANDLE_NAMES(brightness_def_set, "SPBR");
97
98HANDLE_NAMES(cdpower_get, "GCDP");
99HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
100
101HANDLE_NAMES(audiopower_get, "GAZP");
102HANDLE_NAMES(audiopower_set, "AZPW");
103
104HANDLE_NAMES(lanpower_get, "GLNP");
105HANDLE_NAMES(lanpower_set, "LNPW");
106
107HANDLE_NAMES(PID_get, "GPID");
108
109HANDLE_NAMES(CTR_get, "GCTR");
110HANDLE_NAMES(CTR_set, "SCTR");
111
112HANDLE_NAMES(PCR_get, "GPCR");
113HANDLE_NAMES(PCR_set, "SPCR");
114
115HANDLE_NAMES(CMI_get, "GCMI");
116HANDLE_NAMES(CMI_set, "SCMI");
117
118static struct sony_acpi_value sony_acpi_values[] = {
119 SONY_ACPI_VALUE(brightness_default, snc_brightness_def_get,
120 snc_brightness_def_set, brightness_default_validate, 0),
121 SONY_ACPI_VALUE(fnkey, snc_fnkey_get, NULL, NULL, 0),
122 SONY_ACPI_VALUE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),
123 SONY_ACPI_VALUE(audiopower, snc_audiopower_get, snc_audiopower_set,
124 boolean_validate, 0),
125 SONY_ACPI_VALUE(lanpower, snc_lanpower_get, snc_lanpower_set,
126 boolean_validate, 1),
127 /* unknown methods */
128 SONY_ACPI_VALUE(PID, snc_PID_get, NULL, NULL, 1),
129 SONY_ACPI_VALUE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
130 SONY_ACPI_VALUE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),
131 SONY_ACPI_VALUE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),
132 SONY_ACPI_VALUE_NULL
133};
134
135static acpi_handle sony_acpi_handle;
136static struct acpi_device *sony_acpi_acpi_device = NULL;
137
138/*
139 * acpi_evaluate_object wrappers
140 */
141static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
142{
143 struct acpi_buffer output;
144 union acpi_object out_obj;
145 acpi_status status;
146
147 output.length = sizeof(out_obj);
148 output.pointer = &out_obj;
149
150 status = acpi_evaluate_object(handle, name, NULL, &output);
151 if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
152 *result = out_obj.integer.value;
153 return 0;
154 }
155
156 printk(LOG_PFX "acpi_callreadfunc failed\n");
157
158 return -1;
159}
160
161static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
162 int *result)
163{
164 struct acpi_object_list params;
165 union acpi_object in_obj;
166 struct acpi_buffer output;
167 union acpi_object out_obj;
168 acpi_status status;
169
170 params.count = 1;
171 params.pointer = &in_obj;
172 in_obj.type = ACPI_TYPE_INTEGER;
173 in_obj.integer.value = value;
174
175 output.length = sizeof(out_obj);
176 output.pointer = &out_obj;
177
178 status = acpi_evaluate_object(handle, name, &params, &output);
179 if (status == AE_OK) {
180 if (result != NULL) {
181 if (out_obj.type != ACPI_TYPE_INTEGER) {
182 printk(LOG_PFX "acpi_evaluate_object bad "
183 "return type\n");
184 return -1;
185 }
186 *result = out_obj.integer.value;
187 }
188 return 0;
189 }
190
191 printk(LOG_PFX "acpi_evaluate_object failed\n");
192
193 return -1;
194}
195
196/*
197 * sony_acpi_values input/output validate functions
198 */
199
200/* brightness_default_validate:
201 *
202 * manipulate input output values to keep consistency with the
203 * backlight framework for which brightness values are 0-based.
204 */
205static int brightness_default_validate(const int direction, const int value)
206{
207 switch (direction) {
208 case SNC_VALIDATE_OUT:
209 return value - 1;
210 case SNC_VALIDATE_IN:
211 if (value >= 0 && value < SONY_MAX_BRIGHTNESS)
212 return value + 1;
213 }
214 return -EINVAL;
215}
216
217/* boolean_validate:
218 *
219 * on input validate boolean values 0/1, on output just pass the
220 * received value.
221 */
222static int boolean_validate(const int direction, const int value)
223{
224 if (direction == SNC_VALIDATE_IN) {
225 if (value != 0 && value != 1)
226 return -EINVAL;
227 }
228 return value;
229}
230
231/*
232 * Sysfs show/store common to all sony_acpi_values
233 */
234static ssize_t sony_acpi_show(struct device *dev, struct device_attribute *attr,
235 char *buffer)
236{
237 int value;
238 struct sony_acpi_value *item =
239 container_of(attr, struct sony_acpi_value, devattr);
240
241 if (!*item->acpiget)
242 return -EIO;
243
244 if (acpi_callgetfunc(sony_acpi_handle, *item->acpiget, &value) < 0)
245 return -EIO;
246
247 if (item->validate)
248 value = item->validate(SNC_VALIDATE_OUT, value);
249
250 return snprintf(buffer, PAGE_SIZE, "%d\n", value);
251}
252
253static ssize_t sony_acpi_store(struct device *dev,
254 struct device_attribute *attr,
255 const char *buffer, size_t count)
256{
257 int value;
258 struct sony_acpi_value *item =
259 container_of(attr, struct sony_acpi_value, devattr);
260
261 if (!item->acpiset)
262 return -EIO;
263
264 if (count > 31)
265 return -EINVAL;
266
267 value = simple_strtoul(buffer, NULL, 10);
268
269 if (item->validate)
270 value = item->validate(SNC_VALIDATE_IN, value);
271
272 if (value < 0)
273 return value;
274
275 if (acpi_callsetfunc(sony_acpi_handle, *item->acpiset, value, NULL) < 0)
276 return -EIO;
277 item->value = value;
278 item->valid = 1;
279 return count;
280}
281
282/*
283 * Platform device
284 */
285static struct platform_driver sncpf_driver = {
286 .driver = {
287 .name = "sony-laptop",
288 .owner = THIS_MODULE,
289 }
290};
291static struct platform_device *sncpf_device;
292
293static int sony_snc_pf_add(void)
294{
295 acpi_handle handle;
296 struct sony_acpi_value *item;
297 int ret = 0;
298
299 ret = platform_driver_register(&sncpf_driver);
300 if (ret)
301 goto out;
302
303 sncpf_device = platform_device_alloc("sony-laptop", -1);
304 if (!sncpf_device) {
305 ret = -ENOMEM;
306 goto out_platform_registered;
307 }
308
309 ret = platform_device_add(sncpf_device);
310 if (ret)
311 goto out_platform_alloced;
312
313 for (item = sony_acpi_values; item->name; ++item) {
314
315 if (!debug && item->debug)
316 continue;
317
318 /* find the available acpiget as described in the DSDT */
319 for (; item->acpiget && *item->acpiget; ++item->acpiget) {
320 if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle,
321 *item->acpiget,
322 &handle))) {
323 if (debug)
324 printk(LOG_PFX "Found %s getter: %s\n",
325 item->name, *item->acpiget);
326 item->devattr.attr.mode |= S_IRUGO;
327 break;
328 }
329 }
330
331 /* find the available acpiset as described in the DSDT */
332 for (; item->acpiset && *item->acpiset; ++item->acpiset) {
333 if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle,
334 *item->acpiset,
335 &handle))) {
336 if (debug)
337 printk(LOG_PFX "Found %s setter: %s\n",
338 item->name, *item->acpiset);
339 item->devattr.attr.mode |= S_IWUSR;
340 break;
341 }
342 }
343
344 if (item->devattr.attr.mode != 0) {
345 ret =
346 device_create_file(&sncpf_device->dev,
347 &item->devattr);
348 if (ret)
349 goto out_sysfs;
350 }
351 }
352
353 return 0;
354
355 out_sysfs:
356 for (item = sony_acpi_values; item->name; ++item) {
357 device_remove_file(&sncpf_device->dev, &item->devattr);
358 }
359 platform_device_del(sncpf_device);
360 out_platform_alloced:
361 platform_device_put(sncpf_device);
362 out_platform_registered:
363 platform_driver_unregister(&sncpf_driver);
364 out:
365 return ret;
366}
367
368static void sony_snc_pf_remove(void)
369{
370 struct sony_acpi_value *item;
371
372 for (item = sony_acpi_values; item->name; ++item) {
373 device_remove_file(&sncpf_device->dev, &item->devattr);
374 }
375
376 platform_device_del(sncpf_device);
377 platform_device_put(sncpf_device);
378 platform_driver_unregister(&sncpf_driver);
379}
380
381/*
382 * Backlight device
383 */
384static int sony_backlight_update_status(struct backlight_device *bd)
385{
386 return acpi_callsetfunc(sony_acpi_handle, "SBRT",
387 bd->props->brightness + 1, NULL);
388}
389
390static int sony_backlight_get_brightness(struct backlight_device *bd)
391{
392 int value;
393
394 if (acpi_callgetfunc(sony_acpi_handle, "GBRT", &value))
395 return 0;
396 /* brightness levels are 1-based, while backlight ones are 0-based */
397 return value - 1;
398}
399
400static struct backlight_device *sony_backlight_device;
401static struct backlight_properties sony_backlight_properties = {
402 .owner = THIS_MODULE,
403 .update_status = sony_backlight_update_status,
404 .get_brightness = sony_backlight_get_brightness,
405 .max_brightness = SONY_MAX_BRIGHTNESS - 1,
406};
407
408/*
409 * ACPI callbacks
410 */
411static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
412{
413 if (debug)
414 printk(LOG_PFX "sony_acpi_notify, event: %d\n", event);
415 acpi_bus_generate_event(sony_acpi_acpi_device, 1, event);
416}
417
418static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
419 void *context, void **return_value)
420{
421 struct acpi_namespace_node *node;
422 union acpi_operand_object *operand;
423
424 node = (struct acpi_namespace_node *)handle;
425 operand = (union acpi_operand_object *)node->object;
426
427 printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
428 (u32) operand->method.param_count);
429
430 return AE_OK;
431}
432
433/*
434 * ACPI device
435 */
436static int sony_acpi_resume(struct acpi_device *device)
437{
438 struct sony_acpi_value *item;
439
440 for (item = sony_acpi_values; item->name; item++) {
441 int ret;
442
443 if (!item->valid)
444 continue;
445 ret = acpi_callsetfunc(sony_acpi_handle, *item->acpiset,
446 item->value, NULL);
447 if (ret < 0) {
448 printk("%s: %d\n", __FUNCTION__, ret);
449 break;
450 }
451 }
452 return 0;
453}
454
455static int sony_acpi_add(struct acpi_device *device)
456{
457 acpi_status status;
458 int result;
459 acpi_handle handle;
460
461 sony_acpi_acpi_device = device;
462
463 sony_acpi_handle = device->handle;
464
465 if (debug) {
466 status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_acpi_handle,
467 1, sony_walk_callback, NULL, NULL);
468 if (ACPI_FAILURE(status)) {
469 printk(LOG_PFX "unable to walk acpi resources\n");
470 result = -ENODEV;
471 goto outwalk;
472 }
473 }
474
475 status = acpi_install_notify_handler(sony_acpi_handle,
476 ACPI_DEVICE_NOTIFY,
477 sony_acpi_notify, NULL);
478 if (ACPI_FAILURE(status)) {
479 printk(LOG_PFX "unable to install notify handler\n");
480 result = -ENODEV;
481 goto outwalk;
482 }
483
484 if (ACPI_SUCCESS(acpi_get_handle(sony_acpi_handle, "GBRT", &handle))) {
485 sony_backlight_device = backlight_device_register("sony", NULL,
486 NULL,
487 &sony_backlight_properties);
488
489 if (IS_ERR(sony_backlight_device)) {
490 printk(LOG_PFX "unable to register backlight device\n");
491 sony_backlight_device = NULL;
492 } else
493 sony_backlight_properties.brightness =
494 sony_backlight_get_brightness
495 (sony_backlight_device);
496 }
497
498 if (sony_snc_pf_add())
499 goto outbacklight;
500
501 printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully installed\n");
502
503 return 0;
504
505 outbacklight:
506 if (sony_backlight_device)
507 backlight_device_unregister(sony_backlight_device);
508
509 status = acpi_remove_notify_handler(sony_acpi_handle,
510 ACPI_DEVICE_NOTIFY,
511 sony_acpi_notify);
512 if (ACPI_FAILURE(status))
513 printk(LOG_PFX "unable to remove notify handler\n");
514 outwalk:
515 return result;
516}
517
518static int sony_acpi_remove(struct acpi_device *device, int type)
519{
520 acpi_status status;
521
522 if (sony_backlight_device)
523 backlight_device_unregister(sony_backlight_device);
524
525 sony_acpi_acpi_device = NULL;
526
527 status = acpi_remove_notify_handler(sony_acpi_handle,
528 ACPI_DEVICE_NOTIFY,
529 sony_acpi_notify);
530 if (ACPI_FAILURE(status))
531 printk(LOG_PFX "unable to remove notify handler\n");
532
533 sony_snc_pf_remove();
534
535 printk(KERN_INFO ACPI_SNC_DRIVER_NAME " successfully removed\n");
536
537 return 0;
538}
539
540static struct acpi_driver sony_acpi_driver = {
541 .name = ACPI_SNC_DRIVER_NAME,
542 .class = ACPI_SNC_CLASS,
543 .ids = ACPI_SNC_HID,
544 .ops = {
545 .add = sony_acpi_add,
546 .remove = sony_acpi_remove,
547 .resume = sony_acpi_resume,
548 },
549};
550
551static int __init sony_acpi_init(void)
552{
553 return acpi_bus_register_driver(&sony_acpi_driver);
554}
555
556static void __exit sony_acpi_exit(void)
557{
558 acpi_bus_unregister_driver(&sony_acpi_driver);
559}
560
561module_init(sony_acpi_init);
562module_exit(sony_acpi_exit);