blob: 2d05d5b7e125c3699cdcdbbde853acdf0522a6e4 [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>
Mattia Dongilied3aa4b2007-02-07 20:01:54 +01005 * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
Stelian Pop7f09c432007-01-13 23:04:31 +01006 *
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>
Alessandro Guido50f62af2007-01-13 23:04:34 +010031#include <linux/backlight.h>
Mattia Dongilied3aa4b2007-02-07 20:01:54 +010032#include <linux/platform_device.h>
Alessandro Guido50f62af2007-01-13 23:04:34 +010033#include <linux/err.h>
Stelian Pop7f09c432007-01-13 23:04:31 +010034#include <acpi/acpi_drivers.h>
35#include <acpi/acpi_bus.h>
36#include <asm/uaccess.h>
37
malattia@linux.it59b19102007-04-09 10:19:04 +020038#define SONY_NC_CLASS "sony"
39#define SONY_NC_HID "SNY5001"
40#define SONY_NC_DRIVER_NAME "ACPI Sony Notebook Control Driver v0.4"
Alessandro Guido50f62af2007-01-13 23:04:34 +010041
42/* the device uses 1-based values, while the backlight subsystem uses
43 0-based values */
44#define SONY_MAX_BRIGHTNESS 8
Stelian Pop7f09c432007-01-13 23:04:31 +010045
Mattia Dongilied3aa4b2007-02-07 20:01:54 +010046#define LOG_PFX KERN_WARNING "sony-laptop: "
Stelian Pop7f09c432007-01-13 23:04:31 +010047
Mattia Dongilied3aa4b2007-02-07 20:01:54 +010048MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
malattia@linux.it59b19102007-04-09 10:19:04 +020049MODULE_DESCRIPTION(SONY_NC_DRIVER_NAME);
Stelian Pop7f09c432007-01-13 23:04:31 +010050MODULE_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 "
Len Browna02d1c12007-02-07 15:34:02 -050055 "the development of this driver");
Stelian Pop7f09c432007-01-13 23:04:31 +010056
malattia@linux.it56b87562007-04-09 10:19:05 +020057/*********** Platform Device ***********/
58
59static atomic_t sony_pf_users = ATOMIC_INIT(0);
60static struct platform_driver sony_pf_driver = {
61 .driver = {
62 .name = "sony-laptop",
63 .owner = THIS_MODULE,
64 }
65};
66static struct platform_device *sony_pf_device;
67
68static int sony_pf_add(void)
69{
70 int ret = 0;
71
72 /* don't run again if already initialized */
73 if (atomic_add_return(1, &sony_pf_users) > 1)
74 return 0;
75
76 ret = platform_driver_register(&sony_pf_driver);
77 if (ret)
78 goto out;
79
80 sony_pf_device = platform_device_alloc("sony-laptop", -1);
81 if (!sony_pf_device) {
82 ret = -ENOMEM;
83 goto out_platform_registered;
84 }
85
86 ret = platform_device_add(sony_pf_device);
87 if (ret)
88 goto out_platform_alloced;
89
90 return 0;
91
92 out_platform_alloced:
93 platform_device_put(sony_pf_device);
94 sony_pf_device = NULL;
95 out_platform_registered:
96 platform_driver_unregister(&sony_pf_driver);
97 out:
98 atomic_dec(&sony_pf_users);
99 return ret;
100}
101
102static void sony_pf_remove(void)
103{
104 /* deregister only after the last user has gone */
105 if (!atomic_dec_and_test(&sony_pf_users))
106 return;
107
108 platform_device_del(sony_pf_device);
109 platform_device_put(sony_pf_device);
110 platform_driver_unregister(&sony_pf_driver);
111}
112
113/*********** SNC (SNY5001) Device ***********/
114
malattia@linux.it59b19102007-04-09 10:19:04 +0200115static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *,
Len Browna02d1c12007-02-07 15:34:02 -0500116 char *);
malattia@linux.it59b19102007-04-09 10:19:04 +0200117static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *,
Len Browna02d1c12007-02-07 15:34:02 -0500118 const char *, size_t);
Mattia Dongili156c2212007-02-12 22:01:07 +0100119static int boolean_validate(const int, const int);
120static int brightness_default_validate(const int, const int);
121
122#define SNC_VALIDATE_IN 0
123#define SNC_VALIDATE_OUT 1
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100124
malattia@linux.it59b19102007-04-09 10:19:04 +0200125struct sony_nc_value {
Len Browna02d1c12007-02-07 15:34:02 -0500126 char *name; /* name of the entry */
127 char **acpiget; /* names of the ACPI get function */
128 char **acpiset; /* names of the ACPI set function */
Mattia Dongili156c2212007-02-12 22:01:07 +0100129 int (*validate)(const int, const int); /* input/output validation */
Len Browna02d1c12007-02-07 15:34:02 -0500130 int value; /* current setting */
131 int valid; /* Has ever been set */
132 int debug; /* active only in debug mode ? */
133 struct device_attribute devattr; /* sysfs atribute */
Stelian Pop7f09c432007-01-13 23:04:31 +0100134};
135
malattia@linux.it59b19102007-04-09 10:19:04 +0200136#define SNC_HANDLE_NAMES(_name, _values...) \
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100137 static char *snc_##_name[] = { _values, NULL }
138
malattia@linux.it59b19102007-04-09 10:19:04 +0200139#define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100140 { \
141 .name = __stringify(_name), \
142 .acpiget = _getters, \
143 .acpiset = _setters, \
Mattia Dongili156c2212007-02-12 22:01:07 +0100144 .validate = _validate, \
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100145 .debug = _debug, \
malattia@linux.it59b19102007-04-09 10:19:04 +0200146 .devattr = __ATTR(_name, 0, sony_nc_sysfs_show, sony_nc_sysfs_store), \
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100147 }
148
malattia@linux.it59b19102007-04-09 10:19:04 +0200149#define SNC_HANDLE_NULL { .name = NULL }
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100150
malattia@linux.it59b19102007-04-09 10:19:04 +0200151SNC_HANDLE_NAMES(fnkey_get, "GHKE");
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100152
malattia@linux.it59b19102007-04-09 10:19:04 +0200153SNC_HANDLE_NAMES(brightness_def_get, "GPBR");
154SNC_HANDLE_NAMES(brightness_def_set, "SPBR");
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100155
malattia@linux.it59b19102007-04-09 10:19:04 +0200156SNC_HANDLE_NAMES(cdpower_get, "GCDP");
157SNC_HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100158
malattia@linux.it59b19102007-04-09 10:19:04 +0200159SNC_HANDLE_NAMES(audiopower_get, "GAZP");
160SNC_HANDLE_NAMES(audiopower_set, "AZPW");
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100161
malattia@linux.it59b19102007-04-09 10:19:04 +0200162SNC_HANDLE_NAMES(lanpower_get, "GLNP");
163SNC_HANDLE_NAMES(lanpower_set, "LNPW");
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100164
malattia@linux.it59b19102007-04-09 10:19:04 +0200165SNC_HANDLE_NAMES(PID_get, "GPID");
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100166
malattia@linux.it59b19102007-04-09 10:19:04 +0200167SNC_HANDLE_NAMES(CTR_get, "GCTR");
168SNC_HANDLE_NAMES(CTR_set, "SCTR");
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100169
malattia@linux.it59b19102007-04-09 10:19:04 +0200170SNC_HANDLE_NAMES(PCR_get, "GPCR");
171SNC_HANDLE_NAMES(PCR_set, "SPCR");
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100172
malattia@linux.it59b19102007-04-09 10:19:04 +0200173SNC_HANDLE_NAMES(CMI_get, "GCMI");
174SNC_HANDLE_NAMES(CMI_set, "SCMI");
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100175
malattia@linux.it59b19102007-04-09 10:19:04 +0200176static struct sony_nc_value sony_nc_values[] = {
177 SNC_HANDLE(brightness_default, snc_brightness_def_get,
Mattia Dongili156c2212007-02-12 22:01:07 +0100178 snc_brightness_def_set, brightness_default_validate, 0),
malattia@linux.it59b19102007-04-09 10:19:04 +0200179 SNC_HANDLE(fnkey, snc_fnkey_get, NULL, NULL, 0),
180 SNC_HANDLE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),
181 SNC_HANDLE(audiopower, snc_audiopower_get, snc_audiopower_set,
Mattia Dongili156c2212007-02-12 22:01:07 +0100182 boolean_validate, 0),
malattia@linux.it59b19102007-04-09 10:19:04 +0200183 SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set,
Mattia Dongili156c2212007-02-12 22:01:07 +0100184 boolean_validate, 1),
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100185 /* unknown methods */
malattia@linux.it59b19102007-04-09 10:19:04 +0200186 SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1),
187 SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
188 SNC_HANDLE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),
189 SNC_HANDLE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),
190 SNC_HANDLE_NULL
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100191};
192
malattia@linux.it59b19102007-04-09 10:19:04 +0200193static acpi_handle sony_nc_acpi_handle;
194static struct acpi_device *sony_nc_acpi_device = NULL;
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100195
Mattia Dongilid78865c2007-02-07 20:01:56 +0100196/*
197 * acpi_evaluate_object wrappers
198 */
Stelian Pop7f09c432007-01-13 23:04:31 +0100199static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
200{
201 struct acpi_buffer output;
202 union acpi_object out_obj;
203 acpi_status status;
204
205 output.length = sizeof(out_obj);
206 output.pointer = &out_obj;
207
208 status = acpi_evaluate_object(handle, name, NULL, &output);
209 if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
210 *result = out_obj.integer.value;
211 return 0;
212 }
213
214 printk(LOG_PFX "acpi_callreadfunc failed\n");
215
216 return -1;
217}
218
219static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
220 int *result)
221{
222 struct acpi_object_list params;
223 union acpi_object in_obj;
224 struct acpi_buffer output;
225 union acpi_object out_obj;
226 acpi_status status;
227
228 params.count = 1;
229 params.pointer = &in_obj;
230 in_obj.type = ACPI_TYPE_INTEGER;
231 in_obj.integer.value = value;
232
233 output.length = sizeof(out_obj);
234 output.pointer = &out_obj;
235
236 status = acpi_evaluate_object(handle, name, &params, &output);
237 if (status == AE_OK) {
238 if (result != NULL) {
239 if (out_obj.type != ACPI_TYPE_INTEGER) {
240 printk(LOG_PFX "acpi_evaluate_object bad "
241 "return type\n");
242 return -1;
243 }
244 *result = out_obj.integer.value;
245 }
246 return 0;
247 }
248
249 printk(LOG_PFX "acpi_evaluate_object failed\n");
250
251 return -1;
252}
253
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100254/*
malattia@linux.it59b19102007-04-09 10:19:04 +0200255 * sony_nc_values input/output validate functions
Mattia Dongili156c2212007-02-12 22:01:07 +0100256 */
257
258/* brightness_default_validate:
259 *
260 * manipulate input output values to keep consistency with the
261 * backlight framework for which brightness values are 0-based.
262 */
263static int brightness_default_validate(const int direction, const int value)
264{
265 switch (direction) {
266 case SNC_VALIDATE_OUT:
267 return value - 1;
268 case SNC_VALIDATE_IN:
269 if (value >= 0 && value < SONY_MAX_BRIGHTNESS)
270 return value + 1;
271 }
272 return -EINVAL;
273}
274
275/* boolean_validate:
276 *
277 * on input validate boolean values 0/1, on output just pass the
278 * received value.
279 */
280static int boolean_validate(const int direction, const int value)
281{
282 if (direction == SNC_VALIDATE_IN) {
283 if (value != 0 && value != 1)
284 return -EINVAL;
285 }
286 return value;
287}
288
289/*
malattia@linux.it59b19102007-04-09 10:19:04 +0200290 * Sysfs show/store common to all sony_nc_values
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100291 */
malattia@linux.it59b19102007-04-09 10:19:04 +0200292static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr,
Len Browna02d1c12007-02-07 15:34:02 -0500293 char *buffer)
Stelian Pop7f09c432007-01-13 23:04:31 +0100294{
Stelian Pop7f09c432007-01-13 23:04:31 +0100295 int value;
malattia@linux.it59b19102007-04-09 10:19:04 +0200296 struct sony_nc_value *item =
297 container_of(attr, struct sony_nc_value, devattr);
Stelian Pop7f09c432007-01-13 23:04:31 +0100298
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100299 if (!*item->acpiget)
Stelian Pop7f09c432007-01-13 23:04:31 +0100300 return -EIO;
301
malattia@linux.it59b19102007-04-09 10:19:04 +0200302 if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0)
Stelian Pop7f09c432007-01-13 23:04:31 +0100303 return -EIO;
304
Mattia Dongili156c2212007-02-12 22:01:07 +0100305 if (item->validate)
306 value = item->validate(SNC_VALIDATE_OUT, value);
307
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100308 return snprintf(buffer, PAGE_SIZE, "%d\n", value);
Stelian Pop7f09c432007-01-13 23:04:31 +0100309}
310
malattia@linux.it59b19102007-04-09 10:19:04 +0200311static ssize_t sony_nc_sysfs_store(struct device *dev,
Len Browna02d1c12007-02-07 15:34:02 -0500312 struct device_attribute *attr,
313 const char *buffer, size_t count)
Stelian Pop7f09c432007-01-13 23:04:31 +0100314{
Stelian Pop7f09c432007-01-13 23:04:31 +0100315 int value;
malattia@linux.it59b19102007-04-09 10:19:04 +0200316 struct sony_nc_value *item =
317 container_of(attr, struct sony_nc_value, devattr);
Stelian Pop7f09c432007-01-13 23:04:31 +0100318
319 if (!item->acpiset)
320 return -EIO;
321
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100322 if (count > 31)
323 return -EINVAL;
324
325 value = simple_strtoul(buffer, NULL, 10);
Stelian Pop7f09c432007-01-13 23:04:31 +0100326
Mattia Dongili156c2212007-02-12 22:01:07 +0100327 if (item->validate)
328 value = item->validate(SNC_VALIDATE_IN, value);
329
330 if (value < 0)
331 return value;
Stelian Pop7f09c432007-01-13 23:04:31 +0100332
malattia@linux.it59b19102007-04-09 10:19:04 +0200333 if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0)
Stelian Pop7f09c432007-01-13 23:04:31 +0100334 return -EIO;
Andrew Morton3f4f4612007-01-13 23:04:32 +0100335 item->value = value;
336 item->valid = 1;
Stelian Pop7f09c432007-01-13 23:04:31 +0100337 return count;
338}
339
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100340
Mattia Dongilid78865c2007-02-07 20:01:56 +0100341/*
342 * Backlight device
343 */
344static int sony_backlight_update_status(struct backlight_device *bd)
Andrew Morton3f4f4612007-01-13 23:04:32 +0100345{
malattia@linux.it59b19102007-04-09 10:19:04 +0200346 return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
Richard Purdie321709c2007-02-10 15:04:08 +0000347 bd->props.brightness + 1, NULL);
Andrew Morton3f4f4612007-01-13 23:04:32 +0100348}
349
Mattia Dongilid78865c2007-02-07 20:01:56 +0100350static int sony_backlight_get_brightness(struct backlight_device *bd)
351{
352 int value;
353
malattia@linux.it59b19102007-04-09 10:19:04 +0200354 if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value))
Mattia Dongilid78865c2007-02-07 20:01:56 +0100355 return 0;
356 /* brightness levels are 1-based, while backlight ones are 0-based */
357 return value - 1;
358}
359
360static struct backlight_device *sony_backlight_device;
Richard Purdie321709c2007-02-10 15:04:08 +0000361static struct backlight_ops sony_backlight_ops = {
Len Browna02d1c12007-02-07 15:34:02 -0500362 .update_status = sony_backlight_update_status,
363 .get_brightness = sony_backlight_get_brightness,
Mattia Dongilid78865c2007-02-07 20:01:56 +0100364};
365
366/*
367 * ACPI callbacks
368 */
Stelian Pop7f09c432007-01-13 23:04:31 +0100369static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
370{
Stelian Popc5611622007-01-13 23:04:37 +0100371 if (debug)
372 printk(LOG_PFX "sony_acpi_notify, event: %d\n", event);
malattia@linux.it59b19102007-04-09 10:19:04 +0200373 acpi_bus_generate_event(sony_nc_acpi_device, 1, event);
Stelian Pop7f09c432007-01-13 23:04:31 +0100374}
375
376static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
377 void *context, void **return_value)
378{
379 struct acpi_namespace_node *node;
380 union acpi_operand_object *operand;
381
Len Browna02d1c12007-02-07 15:34:02 -0500382 node = (struct acpi_namespace_node *)handle;
383 operand = (union acpi_operand_object *)node->object;
Stelian Pop7f09c432007-01-13 23:04:31 +0100384
385 printk(LOG_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
386 (u32) operand->method.param_count);
387
388 return AE_OK;
389}
390
Mattia Dongilid78865c2007-02-07 20:01:56 +0100391/*
392 * ACPI device
393 */
malattia@linux.it59b19102007-04-09 10:19:04 +0200394static int sony_nc_resume(struct acpi_device *device)
Mattia Dongilid78865c2007-02-07 20:01:56 +0100395{
malattia@linux.it59b19102007-04-09 10:19:04 +0200396 struct sony_nc_value *item;
Mattia Dongilid78865c2007-02-07 20:01:56 +0100397
malattia@linux.it59b19102007-04-09 10:19:04 +0200398 for (item = sony_nc_values; item->name; item++) {
Mattia Dongilid78865c2007-02-07 20:01:56 +0100399 int ret;
400
401 if (!item->valid)
402 continue;
malattia@linux.it59b19102007-04-09 10:19:04 +0200403 ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset,
Len Browna02d1c12007-02-07 15:34:02 -0500404 item->value, NULL);
Mattia Dongilid78865c2007-02-07 20:01:56 +0100405 if (ret < 0) {
406 printk("%s: %d\n", __FUNCTION__, ret);
407 break;
408 }
409 }
410 return 0;
411}
412
malattia@linux.it59b19102007-04-09 10:19:04 +0200413static int sony_nc_add(struct acpi_device *device)
Stelian Pop7f09c432007-01-13 23:04:31 +0100414{
415 acpi_status status;
Andrew Morton8607c672007-03-06 02:29:42 -0800416 int result = 0;
Alessandro Guido50f62af2007-01-13 23:04:34 +0100417 acpi_handle handle;
malattia@linux.it56b87562007-04-09 10:19:05 +0200418 struct sony_nc_value *item;
Stelian Pop7f09c432007-01-13 23:04:31 +0100419
malattia@linux.it59b19102007-04-09 10:19:04 +0200420 sony_nc_acpi_device = device;
Stelian Popc5611622007-01-13 23:04:37 +0100421
malattia@linux.it59b19102007-04-09 10:19:04 +0200422 sony_nc_acpi_handle = device->handle;
Stelian Pop7f09c432007-01-13 23:04:31 +0100423
Stelian Pop7f09c432007-01-13 23:04:31 +0100424 if (debug) {
malattia@linux.it59b19102007-04-09 10:19:04 +0200425 status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
Stelian Pop7f09c432007-01-13 23:04:31 +0100426 1, sony_walk_callback, NULL, NULL);
427 if (ACPI_FAILURE(status)) {
428 printk(LOG_PFX "unable to walk acpi resources\n");
429 result = -ENODEV;
430 goto outwalk;
431 }
Stelian Popc5611622007-01-13 23:04:37 +0100432 }
Stelian Pop7f09c432007-01-13 23:04:31 +0100433
malattia@linux.it59b19102007-04-09 10:19:04 +0200434 status = acpi_install_notify_handler(sony_nc_acpi_handle,
Stelian Popc5611622007-01-13 23:04:37 +0100435 ACPI_DEVICE_NOTIFY,
Len Browna02d1c12007-02-07 15:34:02 -0500436 sony_acpi_notify, NULL);
Stelian Popc5611622007-01-13 23:04:37 +0100437 if (ACPI_FAILURE(status)) {
438 printk(LOG_PFX "unable to install notify handler\n");
439 result = -ENODEV;
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100440 goto outwalk;
Stelian Pop7f09c432007-01-13 23:04:31 +0100441 }
442
malattia@linux.it59b19102007-04-09 10:19:04 +0200443 if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) {
Alessandro Guido50f62af2007-01-13 23:04:34 +0100444 sony_backlight_device = backlight_device_register("sony", NULL,
Len Browna02d1c12007-02-07 15:34:02 -0500445 NULL,
Richard Purdie321709c2007-02-10 15:04:08 +0000446 &sony_backlight_ops);
Mattia Dongili7df03b822007-01-13 23:04:41 +0100447
Len Browna02d1c12007-02-07 15:34:02 -0500448 if (IS_ERR(sony_backlight_device)) {
Mattia Dongili91fbc1d2007-02-07 20:01:53 +0100449 printk(LOG_PFX "unable to register backlight device\n");
Mattia Dongili7df03b822007-01-13 23:04:41 +0100450 sony_backlight_device = NULL;
Richard Purdie321709c2007-02-10 15:04:08 +0000451 } else {
452 sony_backlight_device->props.brightness =
Len Browna02d1c12007-02-07 15:34:02 -0500453 sony_backlight_get_brightness
454 (sony_backlight_device);
Richard Purdie321709c2007-02-10 15:04:08 +0000455 sony_backlight_device->props.max_brightness =
456 SONY_MAX_BRIGHTNESS - 1;
457 }
458
Alessandro Guido50f62af2007-01-13 23:04:34 +0100459 }
Stelian Pop7f09c432007-01-13 23:04:31 +0100460
malattia@linux.it56b87562007-04-09 10:19:05 +0200461 if (sony_pf_add())
Mattia Dongilied3aa4b2007-02-07 20:01:54 +0100462 goto outbacklight;
Stelian Pop7f09c432007-01-13 23:04:31 +0100463
malattia@linux.it56b87562007-04-09 10:19:05 +0200464 /* create sony_pf sysfs attributes related to the SNC device */
465 for (item = sony_nc_values; item->name; ++item) {
466
467 if (!debug && item->debug)
468 continue;
469
470 /* find the available acpiget as described in the DSDT */
471 for (; item->acpiget && *item->acpiget; ++item->acpiget) {
472 if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
473 *item->acpiget,
474 &handle))) {
475 if (debug)
476 printk(LOG_PFX "Found %s getter: %s\n",
477 item->name, *item->acpiget);
478 item->devattr.attr.mode |= S_IRUGO;
479 break;
480 }
481 }
482
483 /* find the available acpiset as described in the DSDT */
484 for (; item->acpiset && *item->acpiset; ++item->acpiset) {
485 if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
486 *item->acpiset,
487 &handle))) {
488 if (debug)
489 printk(LOG_PFX "Found %s setter: %s\n",
490 item->name, *item->acpiset);
491 item->devattr.attr.mode |= S_IWUSR;
492 break;
493 }
494 }
495
496 if (item->devattr.attr.mode != 0) {
497 result =
498 device_create_file(&sony_pf_device->dev,
499 &item->devattr);
500 if (result)
501 goto out_sysfs;
502 }
503 }
504
malattia@linux.it59b19102007-04-09 10:19:04 +0200505 printk(KERN_INFO SONY_NC_DRIVER_NAME " successfully installed\n");
Stelian Pop7f09c432007-01-13 23:04:31 +0100506
507 return 0;
508
malattia@linux.it56b87562007-04-09 10:19:05 +0200509 out_sysfs:
510 for (item = sony_nc_values; item->name; ++item) {
511 device_remove_file(&sony_pf_device->dev, &item->devattr);
512 }
513 sony_pf_remove();
Len Browna02d1c12007-02-07 15:34:02 -0500514 outbacklight:
Mattia Dongili7df03b822007-01-13 23:04:41 +0100515 if (sony_backlight_device)
516 backlight_device_unregister(sony_backlight_device);
517
malattia@linux.it59b19102007-04-09 10:19:04 +0200518 status = acpi_remove_notify_handler(sony_nc_acpi_handle,
Stelian Popc5611622007-01-13 23:04:37 +0100519 ACPI_DEVICE_NOTIFY,
520 sony_acpi_notify);
521 if (ACPI_FAILURE(status))
522 printk(LOG_PFX "unable to remove notify handler\n");
Len Browna02d1c12007-02-07 15:34:02 -0500523 outwalk:
Stelian Pop7f09c432007-01-13 23:04:31 +0100524 return result;
525}
526
malattia@linux.it59b19102007-04-09 10:19:04 +0200527static int sony_nc_remove(struct acpi_device *device, int type)
Stelian Pop7f09c432007-01-13 23:04:31 +0100528{
529 acpi_status status;
malattia@linux.it56b87562007-04-09 10:19:05 +0200530 struct sony_nc_value *item;
Stelian Pop7f09c432007-01-13 23:04:31 +0100531
Alessandro Guido50f62af2007-01-13 23:04:34 +0100532 if (sony_backlight_device)
533 backlight_device_unregister(sony_backlight_device);
534
malattia@linux.it59b19102007-04-09 10:19:04 +0200535 sony_nc_acpi_device = NULL;
Stelian Popc5611622007-01-13 23:04:37 +0100536
malattia@linux.it59b19102007-04-09 10:19:04 +0200537 status = acpi_remove_notify_handler(sony_nc_acpi_handle,
Stelian Popc5611622007-01-13 23:04:37 +0100538 ACPI_DEVICE_NOTIFY,
539 sony_acpi_notify);
540 if (ACPI_FAILURE(status))
541 printk(LOG_PFX "unable to remove notify handler\n");
Stelian Pop7f09c432007-01-13 23:04:31 +0100542
malattia@linux.it56b87562007-04-09 10:19:05 +0200543 for (item = sony_nc_values; item->name; ++item) {
544 device_remove_file(&sony_pf_device->dev, &item->devattr);
545 }
546
547 sony_pf_remove();
Stelian Pop7f09c432007-01-13 23:04:31 +0100548
malattia@linux.it59b19102007-04-09 10:19:04 +0200549 printk(KERN_INFO SONY_NC_DRIVER_NAME " successfully removed\n");
Stelian Pop7f09c432007-01-13 23:04:31 +0100550
551 return 0;
552}
553
malattia@linux.it59b19102007-04-09 10:19:04 +0200554static struct acpi_driver sony_nc_driver = {
555 .name = SONY_NC_DRIVER_NAME,
556 .class = SONY_NC_CLASS,
557 .ids = SONY_NC_HID,
Len Browna02d1c12007-02-07 15:34:02 -0500558 .ops = {
malattia@linux.it59b19102007-04-09 10:19:04 +0200559 .add = sony_nc_add,
560 .remove = sony_nc_remove,
561 .resume = sony_nc_resume,
Len Browna02d1c12007-02-07 15:34:02 -0500562 },
Andrew Morton3f4f4612007-01-13 23:04:32 +0100563};
564
malattia@linux.it59b19102007-04-09 10:19:04 +0200565static int __init sony_laptop_init(void)
Stelian Pop7f09c432007-01-13 23:04:31 +0100566{
malattia@linux.it59b19102007-04-09 10:19:04 +0200567 return acpi_bus_register_driver(&sony_nc_driver);
Stelian Pop7f09c432007-01-13 23:04:31 +0100568}
569
malattia@linux.it59b19102007-04-09 10:19:04 +0200570static void __exit sony_laptop_exit(void)
Stelian Pop7f09c432007-01-13 23:04:31 +0100571{
malattia@linux.it59b19102007-04-09 10:19:04 +0200572 acpi_bus_unregister_driver(&sony_nc_driver);
Stelian Pop7f09c432007-01-13 23:04:31 +0100573}
574
malattia@linux.it59b19102007-04-09 10:19:04 +0200575module_init(sony_laptop_init);
576module_exit(sony_laptop_exit);