blob: 4207b26ff990634d3a75d35394f20b4c80afba21 [file] [log] [blame]
Eric Coopere59f8792008-03-13 12:55:46 +01001/*
2 * eepc-laptop.c - Asus Eee PC extras
3 *
4 * Based on asus_acpi.c as patched for the Eee PC by Asus:
5 * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
6 * Based on eee.c from eeepc-linux
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/types.h>
23#include <linux/platform_device.h>
Corentin Charya5fa4292008-03-13 12:56:37 +010024#include <linux/backlight.h>
25#include <linux/fb.h>
Corentin Charye1faa9d2008-03-13 12:57:18 +010026#include <linux/hwmon.h>
27#include <linux/hwmon-sysfs.h>
Eric Coopere59f8792008-03-13 12:55:46 +010028#include <acpi/acpi_drivers.h>
29#include <acpi/acpi_bus.h>
30#include <linux/uaccess.h>
Matthew Garretta195dcd2008-08-19 12:13:20 +010031#include <linux/input.h>
32#include <linux/rfkill.h>
Matthew Garrett57402942009-01-20 16:17:48 +010033#include <linux/pci.h>
Eric Coopere59f8792008-03-13 12:55:46 +010034
35#define EEEPC_LAPTOP_VERSION "0.1"
36
37#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver"
38#define EEEPC_HOTK_FILE "eeepc"
39#define EEEPC_HOTK_CLASS "hotkey"
40#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
41#define EEEPC_HOTK_HID "ASUS010"
42
43#define EEEPC_LOG EEEPC_HOTK_FILE ": "
44#define EEEPC_ERR KERN_ERR EEEPC_LOG
45#define EEEPC_WARNING KERN_WARNING EEEPC_LOG
46#define EEEPC_NOTICE KERN_NOTICE EEEPC_LOG
47#define EEEPC_INFO KERN_INFO EEEPC_LOG
48
49/*
50 * Definitions for Asus EeePC
51 */
52#define NOTIFY_WLAN_ON 0x10
Corentin Charya5fa4292008-03-13 12:56:37 +010053#define NOTIFY_BRN_MIN 0x20
54#define NOTIFY_BRN_MAX 0x2f
Eric Coopere59f8792008-03-13 12:55:46 +010055
56enum {
57 DISABLE_ASL_WLAN = 0x0001,
58 DISABLE_ASL_BLUETOOTH = 0x0002,
59 DISABLE_ASL_IRDA = 0x0004,
60 DISABLE_ASL_CAMERA = 0x0008,
61 DISABLE_ASL_TV = 0x0010,
62 DISABLE_ASL_GPS = 0x0020,
63 DISABLE_ASL_DISPLAYSWITCH = 0x0040,
64 DISABLE_ASL_MODEM = 0x0080,
Corentin Charyb7b700d2009-06-16 19:28:52 +000065 DISABLE_ASL_CARDREADER = 0x0100,
66 DISABLE_ASL_3G = 0x0200,
67 DISABLE_ASL_WIMAX = 0x0400,
68 DISABLE_ASL_HWCF = 0x0800
Eric Coopere59f8792008-03-13 12:55:46 +010069};
70
71enum {
72 CM_ASL_WLAN = 0,
73 CM_ASL_BLUETOOTH,
74 CM_ASL_IRDA,
75 CM_ASL_1394,
76 CM_ASL_CAMERA,
77 CM_ASL_TV,
78 CM_ASL_GPS,
79 CM_ASL_DVDROM,
80 CM_ASL_DISPLAYSWITCH,
81 CM_ASL_PANELBRIGHT,
82 CM_ASL_BIOSFLASH,
83 CM_ASL_ACPIFLASH,
84 CM_ASL_CPUFV,
85 CM_ASL_CPUTEMPERATURE,
86 CM_ASL_FANCPU,
87 CM_ASL_FANCHASSIS,
88 CM_ASL_USBPORT1,
89 CM_ASL_USBPORT2,
90 CM_ASL_USBPORT3,
91 CM_ASL_MODEM,
92 CM_ASL_CARDREADER,
Corentin Charyb7b700d2009-06-16 19:28:52 +000093 CM_ASL_3G,
94 CM_ASL_WIMAX,
95 CM_ASL_HWCF,
96 CM_ASL_LID,
97 CM_ASL_TYPE,
98 CM_ASL_PANELPOWER, /*P901*/
99 CM_ASL_TPD
Eric Coopere59f8792008-03-13 12:55:46 +0100100};
101
Adrian Bunk14109462008-06-25 19:25:47 +0300102static const char *cm_getv[] = {
Jonathan McDowell3af9bfc2008-12-03 20:31:11 +0000103 "WLDG", "BTHG", NULL, NULL,
Eric Coopere59f8792008-03-13 12:55:46 +0100104 "CAMG", NULL, NULL, NULL,
105 NULL, "PBLG", NULL, NULL,
106 "CFVG", NULL, NULL, NULL,
107 "USBG", NULL, NULL, "MODG",
Corentin Charyb7b700d2009-06-16 19:28:52 +0000108 "CRDG", "M3GG", "WIMG", "HWCF",
109 "LIDG", "TYPE", "PBPG", "TPDG"
Eric Coopere59f8792008-03-13 12:55:46 +0100110};
111
Adrian Bunk14109462008-06-25 19:25:47 +0300112static const char *cm_setv[] = {
Jonathan McDowell3af9bfc2008-12-03 20:31:11 +0000113 "WLDS", "BTHS", NULL, NULL,
Eric Coopere59f8792008-03-13 12:55:46 +0100114 "CAMS", NULL, NULL, NULL,
115 "SDSP", "PBLS", "HDPS", NULL,
116 "CFVS", NULL, NULL, NULL,
117 "USBG", NULL, NULL, "MODS",
Corentin Charyb7b700d2009-06-16 19:28:52 +0000118 "CRDS", "M3GS", "WIMS", NULL,
119 NULL, NULL, "PBPS", "TPDS"
Eric Coopere59f8792008-03-13 12:55:46 +0100120};
121
Corentin Charye1faa9d2008-03-13 12:57:18 +0100122#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
123
124#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
125#define EEEPC_EC_SC02 0x63
126#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
127#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
128#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */
129#define EEEPC_EC_SFB3 0xD3
130
Eric Coopere59f8792008-03-13 12:55:46 +0100131/*
132 * This is the main structure, we can use it to store useful information
133 * about the hotk device
134 */
135struct eeepc_hotk {
136 struct acpi_device *device; /* the device we are in */
137 acpi_handle handle; /* the handle of the hotk device */
138 u32 cm_supported; /* the control methods supported
139 by this BIOS */
140 uint init_flag; /* Init flags */
141 u16 event_count[128]; /* count for each event */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100142 struct input_dev *inputdev;
143 u16 *keycode_map;
144 struct rfkill *eeepc_wlan_rfkill;
145 struct rfkill *eeepc_bluetooth_rfkill;
Eric Coopere59f8792008-03-13 12:55:46 +0100146};
147
148/* The actual device the driver binds to */
149static struct eeepc_hotk *ehotk;
150
151/* Platform device/driver */
152static struct platform_driver platform_driver = {
153 .driver = {
154 .name = EEEPC_HOTK_FILE,
155 .owner = THIS_MODULE,
156 }
157};
158
159static struct platform_device *platform_device;
160
Matthew Garretta195dcd2008-08-19 12:13:20 +0100161struct key_entry {
162 char type;
163 u8 code;
164 u16 keycode;
165};
166
167enum { KE_KEY, KE_END };
168
169static struct key_entry eeepc_keymap[] = {
170 /* Sleep already handled via generic ACPI code */
171 {KE_KEY, 0x10, KEY_WLAN },
Alan Jenkins978605c2009-04-27 09:23:39 +0200172 {KE_KEY, 0x11, KEY_WLAN },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100173 {KE_KEY, 0x12, KEY_PROG1 },
174 {KE_KEY, 0x13, KEY_MUTE },
175 {KE_KEY, 0x14, KEY_VOLUMEDOWN },
176 {KE_KEY, 0x15, KEY_VOLUMEUP },
Matthew Garrettb5f6f262009-01-20 16:17:46 +0100177 {KE_KEY, 0x1a, KEY_COFFEE },
178 {KE_KEY, 0x1b, KEY_ZOOM },
179 {KE_KEY, 0x1c, KEY_PROG2 },
180 {KE_KEY, 0x1d, KEY_PROG3 },
Darren Salt64b86b62009-04-27 09:23:38 +0200181 {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN },
182 {KE_KEY, NOTIFY_BRN_MIN + 2, KEY_BRIGHTNESSUP },
Matthew Garretta195dcd2008-08-19 12:13:20 +0100183 {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
184 {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
185 {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
186 {KE_END, 0},
187};
188
Eric Coopere59f8792008-03-13 12:55:46 +0100189/*
190 * The hotkey driver declaration
191 */
192static int eeepc_hotk_add(struct acpi_device *device);
193static int eeepc_hotk_remove(struct acpi_device *device, int type);
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100194static int eeepc_hotk_resume(struct acpi_device *device);
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600195static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
Eric Coopere59f8792008-03-13 12:55:46 +0100196
197static const struct acpi_device_id eeepc_device_ids[] = {
198 {EEEPC_HOTK_HID, 0},
199 {"", 0},
200};
201MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
202
203static struct acpi_driver eeepc_hotk_driver = {
204 .name = EEEPC_HOTK_NAME,
205 .class = EEEPC_HOTK_CLASS,
206 .ids = eeepc_device_ids,
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600207 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
Eric Coopere59f8792008-03-13 12:55:46 +0100208 .ops = {
209 .add = eeepc_hotk_add,
210 .remove = eeepc_hotk_remove,
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100211 .resume = eeepc_hotk_resume,
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600212 .notify = eeepc_hotk_notify,
Eric Coopere59f8792008-03-13 12:55:46 +0100213 },
214};
215
Corentin Charya5fa4292008-03-13 12:56:37 +0100216/* The backlight device /sys/class/backlight */
217static struct backlight_device *eeepc_backlight_device;
218
Corentin Charye1faa9d2008-03-13 12:57:18 +0100219/* The hwmon device */
220static struct device *eeepc_hwmon_device;
221
Corentin Charya5fa4292008-03-13 12:56:37 +0100222/*
223 * The backlight class declaration
224 */
225static int read_brightness(struct backlight_device *bd);
226static int update_bl_status(struct backlight_device *bd);
227static struct backlight_ops eeepcbl_ops = {
228 .get_brightness = read_brightness,
229 .update_status = update_bl_status,
230};
231
Eric Coopere59f8792008-03-13 12:55:46 +0100232MODULE_AUTHOR("Corentin Chary, Eric Cooper");
233MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
234MODULE_LICENSE("GPL");
235
236/*
237 * ACPI Helpers
238 */
239static int write_acpi_int(acpi_handle handle, const char *method, int val,
240 struct acpi_buffer *output)
241{
242 struct acpi_object_list params;
243 union acpi_object in_obj;
244 acpi_status status;
245
246 params.count = 1;
247 params.pointer = &in_obj;
248 in_obj.type = ACPI_TYPE_INTEGER;
249 in_obj.integer.value = val;
250
251 status = acpi_evaluate_object(handle, (char *)method, &params, output);
252 return (status == AE_OK ? 0 : -1);
253}
254
255static int read_acpi_int(acpi_handle handle, const char *method, int *val)
256{
257 acpi_status status;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400258 unsigned long long result;
Eric Coopere59f8792008-03-13 12:55:46 +0100259
260 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
261 if (ACPI_FAILURE(status)) {
262 *val = -1;
263 return -1;
264 } else {
265 *val = result;
266 return 0;
267 }
268}
269
270static int set_acpi(int cm, int value)
271{
272 if (ehotk->cm_supported & (0x1 << cm)) {
273 const char *method = cm_setv[cm];
274 if (method == NULL)
275 return -ENODEV;
276 if (write_acpi_int(ehotk->handle, method, value, NULL))
277 printk(EEEPC_WARNING "Error writing %s\n", method);
278 }
279 return 0;
280}
281
282static int get_acpi(int cm)
283{
284 int value = -1;
285 if ((ehotk->cm_supported & (0x1 << cm))) {
286 const char *method = cm_getv[cm];
287 if (method == NULL)
288 return -ENODEV;
289 if (read_acpi_int(ehotk->handle, method, &value))
290 printk(EEEPC_WARNING "Error reading %s\n", method);
291 }
292 return value;
293}
294
295/*
Corentin Charya5fa4292008-03-13 12:56:37 +0100296 * Backlight
297 */
298static int read_brightness(struct backlight_device *bd)
299{
300 return get_acpi(CM_ASL_PANELBRIGHT);
301}
302
303static int set_brightness(struct backlight_device *bd, int value)
304{
305 value = max(0, min(15, value));
306 return set_acpi(CM_ASL_PANELBRIGHT, value);
307}
308
309static int update_bl_status(struct backlight_device *bd)
310{
311 return set_brightness(bd, bd->props.brightness);
312}
313
314/*
Matthew Garretta195dcd2008-08-19 12:13:20 +0100315 * Rfkill helpers
316 */
317
Johannes Berg19d337d2009-06-02 13:01:37 +0200318static bool eeepc_wlan_rfkill_blocked(void)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100319{
320 if (get_acpi(CM_ASL_WLAN) == 1)
Johannes Berg19d337d2009-06-02 13:01:37 +0200321 return false;
322 return true;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100323}
324
Johannes Berg19d337d2009-06-02 13:01:37 +0200325static int eeepc_rfkill_set(void *data, bool blocked)
Matthew Garretta195dcd2008-08-19 12:13:20 +0100326{
Johannes Berg19d337d2009-06-02 13:01:37 +0200327 unsigned long asl = (unsigned long)data;
328 return set_acpi(asl, !blocked);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100329}
330
Johannes Berg19d337d2009-06-02 13:01:37 +0200331static const struct rfkill_ops eeepc_rfkill_ops = {
332 .set_block = eeepc_rfkill_set,
333};
Matthew Garretta195dcd2008-08-19 12:13:20 +0100334
Pekka Enbergcede2cb2009-06-16 19:28:45 +0000335static void __init eeepc_enable_camera(void)
336{
337 /*
338 * If the following call to set_acpi() fails, it's because there's no
339 * camera so we can ignore the error.
340 */
341 set_acpi(CM_ASL_CAMERA, 1);
342}
343
Matthew Garretta195dcd2008-08-19 12:13:20 +0100344/*
Eric Coopere59f8792008-03-13 12:55:46 +0100345 * Sys helpers
346 */
347static int parse_arg(const char *buf, unsigned long count, int *val)
348{
349 if (!count)
350 return 0;
351 if (sscanf(buf, "%i", val) != 1)
352 return -EINVAL;
353 return count;
354}
355
356static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
357{
358 int rv, value;
359
360 rv = parse_arg(buf, count, &value);
361 if (rv > 0)
362 set_acpi(cm, value);
363 return rv;
364}
365
366static ssize_t show_sys_acpi(int cm, char *buf)
367{
368 return sprintf(buf, "%d\n", get_acpi(cm));
369}
370
371#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \
372 static ssize_t show_##_name(struct device *dev, \
373 struct device_attribute *attr, \
374 char *buf) \
375 { \
376 return show_sys_acpi(_cm, buf); \
377 } \
378 static ssize_t store_##_name(struct device *dev, \
379 struct device_attribute *attr, \
380 const char *buf, size_t count) \
381 { \
382 return store_sys_acpi(_cm, buf, count); \
383 } \
384 static struct device_attribute dev_attr_##_name = { \
385 .attr = { \
386 .name = __stringify(_name), \
387 .mode = 0644 }, \
388 .show = show_##_name, \
389 .store = store_##_name, \
390 }
391
392EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
393EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
394EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000395
396struct eeepc_cpufv {
397 int num;
398 int cur;
399};
400
401static int get_cpufv(struct eeepc_cpufv *c)
402{
403 c->cur = get_acpi(CM_ASL_CPUFV);
404 c->num = (c->cur >> 8) & 0xff;
405 c->cur &= 0xff;
406 if (c->cur < 0 || c->num <= 0 || c->num > 12)
407 return -ENODEV;
408 return 0;
409}
410
411static ssize_t show_available_cpufv(struct device *dev,
412 struct device_attribute *attr,
413 char *buf)
414{
415 struct eeepc_cpufv c;
416 int i;
417 ssize_t len = 0;
418
419 if (get_cpufv(&c))
420 return -ENODEV;
421 for (i = 0; i < c.num; i++)
422 len += sprintf(buf + len, "%d ", i);
423 len += sprintf(buf + len, "\n");
424 return len;
425}
426
427static ssize_t show_cpufv(struct device *dev,
428 struct device_attribute *attr,
429 char *buf)
430{
431 struct eeepc_cpufv c;
432
433 if (get_cpufv(&c))
434 return -ENODEV;
435 return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
436}
437
438static ssize_t store_cpufv(struct device *dev,
439 struct device_attribute *attr,
440 const char *buf, size_t count)
441{
442 struct eeepc_cpufv c;
443 int rv, value;
444
445 if (get_cpufv(&c))
446 return -ENODEV;
447 rv = parse_arg(buf, count, &value);
448 if (rv < 0)
449 return rv;
450 if (!rv || value < 0 || value >= c.num)
451 return -EINVAL;
452 set_acpi(CM_ASL_CPUFV, value);
453 return rv;
454}
455
456static struct device_attribute dev_attr_cpufv = {
457 .attr = {
458 .name = "cpufv",
459 .mode = 0644 },
460 .show = show_cpufv,
461 .store = store_cpufv
462};
463
464static struct device_attribute dev_attr_available_cpufv = {
465 .attr = {
466 .name = "available_cpufv",
467 .mode = 0444 },
468 .show = show_available_cpufv
469};
Eric Coopere59f8792008-03-13 12:55:46 +0100470
471static struct attribute *platform_attributes[] = {
472 &dev_attr_camera.attr,
473 &dev_attr_cardr.attr,
474 &dev_attr_disp.attr,
Grigori Goronzy158ca1d2009-04-27 09:23:40 +0200475 &dev_attr_cpufv.attr,
Corentin Charyb31d0fd2009-06-16 19:28:56 +0000476 &dev_attr_available_cpufv.attr,
Eric Coopere59f8792008-03-13 12:55:46 +0100477 NULL
478};
479
480static struct attribute_group platform_attribute_group = {
481 .attrs = platform_attributes
482};
483
484/*
485 * Hotkey functions
486 */
Matthew Garretta195dcd2008-08-19 12:13:20 +0100487static struct key_entry *eepc_get_entry_by_scancode(int code)
488{
489 struct key_entry *key;
490
491 for (key = eeepc_keymap; key->type != KE_END; key++)
492 if (code == key->code)
493 return key;
494
495 return NULL;
496}
497
498static struct key_entry *eepc_get_entry_by_keycode(int code)
499{
500 struct key_entry *key;
501
502 for (key = eeepc_keymap; key->type != KE_END; key++)
503 if (code == key->keycode && key->type == KE_KEY)
504 return key;
505
506 return NULL;
507}
508
509static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
510{
511 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
512
513 if (key && key->type == KE_KEY) {
514 *keycode = key->keycode;
515 return 0;
516 }
517
518 return -EINVAL;
519}
520
521static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
522{
523 struct key_entry *key;
524 int old_keycode;
525
526 if (keycode < 0 || keycode > KEY_MAX)
527 return -EINVAL;
528
529 key = eepc_get_entry_by_scancode(scancode);
530 if (key && key->type == KE_KEY) {
531 old_keycode = key->keycode;
532 key->keycode = keycode;
533 set_bit(keycode, dev->keybit);
534 if (!eepc_get_entry_by_keycode(old_keycode))
535 clear_bit(old_keycode, dev->keybit);
536 return 0;
537 }
538
539 return -EINVAL;
540}
541
Eric Coopere59f8792008-03-13 12:55:46 +0100542static int eeepc_hotk_check(void)
543{
Matthew Garretta195dcd2008-08-19 12:13:20 +0100544 const struct key_entry *key;
Eric Coopere59f8792008-03-13 12:55:46 +0100545 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
546 int result;
547
548 result = acpi_bus_get_status(ehotk->device);
549 if (result)
550 return result;
551 if (ehotk->device->status.present) {
552 if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
553 &buffer)) {
554 printk(EEEPC_ERR "Hotkey initialization failed\n");
555 return -ENODEV;
556 } else {
557 printk(EEEPC_NOTICE "Hotkey init flags 0x%x\n",
558 ehotk->init_flag);
559 }
560 /* get control methods supported */
561 if (read_acpi_int(ehotk->handle, "CMSG"
562 , &ehotk->cm_supported)) {
563 printk(EEEPC_ERR
564 "Get control methods supported failed\n");
565 return -ENODEV;
566 } else {
567 printk(EEEPC_INFO
568 "Get control methods supported: 0x%x\n",
569 ehotk->cm_supported);
570 }
Matthew Garretta195dcd2008-08-19 12:13:20 +0100571 ehotk->inputdev = input_allocate_device();
572 if (!ehotk->inputdev) {
573 printk(EEEPC_INFO "Unable to allocate input device\n");
574 return 0;
575 }
576 ehotk->inputdev->name = "Asus EeePC extra buttons";
577 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
578 ehotk->inputdev->id.bustype = BUS_HOST;
579 ehotk->inputdev->getkeycode = eeepc_getkeycode;
580 ehotk->inputdev->setkeycode = eeepc_setkeycode;
581
582 for (key = eeepc_keymap; key->type != KE_END; key++) {
583 switch (key->type) {
584 case KE_KEY:
585 set_bit(EV_KEY, ehotk->inputdev->evbit);
586 set_bit(key->keycode, ehotk->inputdev->keybit);
587 break;
588 }
589 }
590 result = input_register_device(ehotk->inputdev);
591 if (result) {
592 printk(EEEPC_INFO "Unable to register input device\n");
593 input_free_device(ehotk->inputdev);
594 return 0;
595 }
Eric Coopere59f8792008-03-13 12:55:46 +0100596 } else {
597 printk(EEEPC_ERR "Hotkey device not present, aborting\n");
598 return -EINVAL;
599 }
600 return 0;
601}
602
Darren Salt64b86b62009-04-27 09:23:38 +0200603static int notify_brn(void)
Corentin Charya5fa4292008-03-13 12:56:37 +0100604{
Darren Salt64b86b62009-04-27 09:23:38 +0200605 /* returns the *previous* brightness, or -1 */
Corentin Charya5fa4292008-03-13 12:56:37 +0100606 struct backlight_device *bd = eeepc_backlight_device;
Darren Salt64b86b62009-04-27 09:23:38 +0200607 if (bd) {
608 int old = bd->props.brightness;
Darren Salt7695fb02009-02-07 01:02:07 -0500609 bd->props.brightness = read_brightness(bd);
Darren Salt64b86b62009-04-27 09:23:38 +0200610 return old;
611 }
612 return -1;
Corentin Charya5fa4292008-03-13 12:56:37 +0100613}
614
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100615static void eeepc_rfkill_hotplug(void)
Matthew Garrett57402942009-01-20 16:17:48 +0100616{
617 struct pci_dev *dev;
618 struct pci_bus *bus = pci_find_bus(0, 1);
Johannes Berg19d337d2009-06-02 13:01:37 +0200619 bool blocked;
Matthew Garrett57402942009-01-20 16:17:48 +0100620
Matthew Garrett57402942009-01-20 16:17:48 +0100621 if (!bus) {
622 printk(EEEPC_WARNING "Unable to find PCI bus 1?\n");
623 return;
624 }
625
Johannes Berg19d337d2009-06-02 13:01:37 +0200626 blocked = eeepc_wlan_rfkill_blocked();
627 if (!blocked) {
Matthew Garrett57402942009-01-20 16:17:48 +0100628 dev = pci_get_slot(bus, 0);
629 if (dev) {
630 /* Device already present */
631 pci_dev_put(dev);
632 return;
633 }
634 dev = pci_scan_single_device(bus, 0);
635 if (dev) {
636 pci_bus_assign_resources(bus);
637 if (pci_bus_add_device(dev))
638 printk(EEEPC_ERR "Unable to hotplug wifi\n");
639 }
640 } else {
641 dev = pci_get_slot(bus, 0);
642 if (dev) {
643 pci_remove_bus_device(dev);
644 pci_dev_put(dev);
645 }
646 }
Alan Jenkins978605c2009-04-27 09:23:39 +0200647
Johannes Berg19d337d2009-06-02 13:01:37 +0200648 rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill, blocked);
Matthew Garrett57402942009-01-20 16:17:48 +0100649}
650
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100651static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
652{
653 if (event != ACPI_NOTIFY_BUS_CHECK)
654 return;
655
656 eeepc_rfkill_hotplug();
657}
658
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600659static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
Eric Coopere59f8792008-03-13 12:55:46 +0100660{
Matthew Garretta195dcd2008-08-19 12:13:20 +0100661 static struct key_entry *key;
Corentin Chary7950b712009-02-15 19:30:20 +0100662 u16 count;
Darren Salt64b86b62009-04-27 09:23:38 +0200663 int brn = -ENODEV;
Corentin Chary7950b712009-02-15 19:30:20 +0100664
Eric Coopere59f8792008-03-13 12:55:46 +0100665 if (!ehotk)
666 return;
Bjorn Helgaasd9b9bd72009-04-30 09:36:03 -0600667 if (event > ACPI_MAX_SYS_NOTIFY)
668 return;
Corentin Charya5fa4292008-03-13 12:56:37 +0100669 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
Darren Salt64b86b62009-04-27 09:23:38 +0200670 brn = notify_brn();
Corentin Chary7950b712009-02-15 19:30:20 +0100671 count = ehotk->event_count[event % 128]++;
672 acpi_bus_generate_proc_event(ehotk->device, event, count);
Corentin Chary2b25c9f2009-01-20 16:17:49 +0100673 acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class,
674 dev_name(&ehotk->device->dev), event,
Corentin Chary7950b712009-02-15 19:30:20 +0100675 count);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100676 if (ehotk->inputdev) {
Darren Salt64b86b62009-04-27 09:23:38 +0200677 if (brn != -ENODEV) {
678 /* brightness-change events need special
679 * handling for conversion to key events
680 */
681 if (brn < 0)
682 brn = event;
683 else
684 brn += NOTIFY_BRN_MIN;
685 if (event < brn)
686 event = NOTIFY_BRN_MIN; /* brightness down */
687 else if (event > brn)
688 event = NOTIFY_BRN_MIN + 2; /* ... up */
689 else
690 event = NOTIFY_BRN_MIN + 1; /* ... unchanged */
691 }
Matthew Garretta195dcd2008-08-19 12:13:20 +0100692 key = eepc_get_entry_by_scancode(event);
693 if (key) {
694 switch (key->type) {
695 case KE_KEY:
696 input_report_key(ehotk->inputdev, key->keycode,
697 1);
698 input_sync(ehotk->inputdev);
699 input_report_key(ehotk->inputdev, key->keycode,
700 0);
701 input_sync(ehotk->inputdev);
702 break;
703 }
704 }
705 }
Eric Coopere59f8792008-03-13 12:55:46 +0100706}
707
Matthew Garrett57402942009-01-20 16:17:48 +0100708static int eeepc_register_rfkill_notifier(char *node)
709{
710 acpi_status status = AE_OK;
711 acpi_handle handle;
712
713 status = acpi_get_handle(NULL, node, &handle);
714
715 if (ACPI_SUCCESS(status)) {
716 status = acpi_install_notify_handler(handle,
717 ACPI_SYSTEM_NOTIFY,
718 eeepc_rfkill_notify,
719 NULL);
720 if (ACPI_FAILURE(status))
721 printk(EEEPC_WARNING
722 "Failed to register notify on %s\n", node);
723 } else
724 return -ENODEV;
725
726 return 0;
727}
728
729static void eeepc_unregister_rfkill_notifier(char *node)
730{
731 acpi_status status = AE_OK;
732 acpi_handle handle;
733
734 status = acpi_get_handle(NULL, node, &handle);
735
736 if (ACPI_SUCCESS(status)) {
737 status = acpi_remove_notify_handler(handle,
738 ACPI_SYSTEM_NOTIFY,
739 eeepc_rfkill_notify);
740 if (ACPI_FAILURE(status))
741 printk(EEEPC_ERR
742 "Error removing rfkill notify handler %s\n",
743 node);
744 }
745}
746
Eric Coopere59f8792008-03-13 12:55:46 +0100747static int eeepc_hotk_add(struct acpi_device *device)
748{
Eric Coopere59f8792008-03-13 12:55:46 +0100749 int result;
750
751 if (!device)
752 return -EINVAL;
753 printk(EEEPC_NOTICE EEEPC_HOTK_NAME "\n");
754 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
755 if (!ehotk)
756 return -ENOMEM;
757 ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
758 ehotk->handle = device->handle;
759 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
760 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
Pavel Machekdb89b4f2008-09-22 14:37:34 -0700761 device->driver_data = ehotk;
Eric Coopere59f8792008-03-13 12:55:46 +0100762 ehotk->device = device;
763 result = eeepc_hotk_check();
764 if (result)
Matthew Garrettc9ddf8f2009-01-20 16:17:47 +0100765 goto ehotk_fail;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100766
Alan Jenkinsfbc97e42009-04-27 09:23:37 +0200767 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
768 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
769
Matthew Garretta195dcd2008-08-19 12:13:20 +0100770 if (get_acpi(CM_ASL_WLAN) != -1) {
Johannes Berg19d337d2009-06-02 13:01:37 +0200771 ehotk->eeepc_wlan_rfkill = rfkill_alloc("eeepc-wlan",
772 &device->dev,
773 RFKILL_TYPE_WLAN,
774 &eeepc_rfkill_ops,
775 (void *)CM_ASL_WLAN);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100776
777 if (!ehotk->eeepc_wlan_rfkill)
Matthew Garrettc9ddf8f2009-01-20 16:17:47 +0100778 goto wlan_fail;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100779
Alan Jenkins06d5caf2009-06-16 15:39:51 +0100780 rfkill_init_sw_state(ehotk->eeepc_wlan_rfkill,
781 get_acpi(CM_ASL_WLAN) != 1);
Matthew Garrettc9ddf8f2009-01-20 16:17:47 +0100782 result = rfkill_register(ehotk->eeepc_wlan_rfkill);
783 if (result)
784 goto wlan_fail;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100785 }
786
787 if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
788 ehotk->eeepc_bluetooth_rfkill =
Johannes Berg19d337d2009-06-02 13:01:37 +0200789 rfkill_alloc("eeepc-bluetooth",
790 &device->dev,
791 RFKILL_TYPE_BLUETOOTH,
792 &eeepc_rfkill_ops,
793 (void *)CM_ASL_BLUETOOTH);
Matthew Garretta195dcd2008-08-19 12:13:20 +0100794
795 if (!ehotk->eeepc_bluetooth_rfkill)
Matthew Garrettc9ddf8f2009-01-20 16:17:47 +0100796 goto bluetooth_fail;
Matthew Garretta195dcd2008-08-19 12:13:20 +0100797
Alan Jenkins06d5caf2009-06-16 15:39:51 +0100798 rfkill_init_sw_state(ehotk->eeepc_bluetooth_rfkill,
799 get_acpi(CM_ASL_BLUETOOTH) != 1);
Matthew Garrettc9ddf8f2009-01-20 16:17:47 +0100800 result = rfkill_register(ehotk->eeepc_bluetooth_rfkill);
801 if (result)
802 goto bluetooth_fail;
Eric Coopere59f8792008-03-13 12:55:46 +0100803 }
Matthew Garrett57402942009-01-20 16:17:48 +0100804
Matthew Garrettc9ddf8f2009-01-20 16:17:47 +0100805 return 0;
806
807 bluetooth_fail:
Johannes Berg19d337d2009-06-02 13:01:37 +0200808 rfkill_destroy(ehotk->eeepc_bluetooth_rfkill);
Matthew Garrettc9ddf8f2009-01-20 16:17:47 +0100809 rfkill_unregister(ehotk->eeepc_wlan_rfkill);
Matthew Garrettc9ddf8f2009-01-20 16:17:47 +0100810 wlan_fail:
Johannes Berg19d337d2009-06-02 13:01:37 +0200811 rfkill_destroy(ehotk->eeepc_wlan_rfkill);
Corentin Charybd320052009-04-27 09:23:43 +0200812 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
813 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
Matthew Garrettc9ddf8f2009-01-20 16:17:47 +0100814 ehotk_fail:
815 kfree(ehotk);
816 ehotk = NULL;
817
Eric Coopere59f8792008-03-13 12:55:46 +0100818 return result;
819}
820
821static int eeepc_hotk_remove(struct acpi_device *device, int type)
822{
Eric Coopere59f8792008-03-13 12:55:46 +0100823 if (!device || !acpi_driver_data(device))
824 return -EINVAL;
Matthew Garrett57402942009-01-20 16:17:48 +0100825
826 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
827 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
828
Eric Coopere59f8792008-03-13 12:55:46 +0100829 kfree(ehotk);
830 return 0;
831}
832
Alan Jenkins96e9cfe2009-06-16 14:53:52 +0100833static int eeepc_hotk_resume(struct acpi_device *device)
834{
835 if (ehotk->eeepc_wlan_rfkill) {
836 bool wlan;
837
838 /* Workaround - it seems that _PTS disables the wireless
839 without notification or changing the value read by WLAN.
840 Normally this is fine because the correct value is restored
841 from the non-volatile storage on resume, but we need to do
842 it ourself if case suspend is aborted, or we lose wireless.
843 */
844 wlan = get_acpi(CM_ASL_WLAN);
845 set_acpi(CM_ASL_WLAN, wlan);
846
847 rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill,
848 wlan != 1);
849
850 eeepc_rfkill_hotplug();
851 }
852
853 if (ehotk->eeepc_bluetooth_rfkill)
854 rfkill_set_sw_state(ehotk->eeepc_bluetooth_rfkill,
855 get_acpi(CM_ASL_BLUETOOTH) != 1);
856
857 return 0;
858}
859
Eric Coopere59f8792008-03-13 12:55:46 +0100860/*
Corentin Charye1faa9d2008-03-13 12:57:18 +0100861 * Hwmon
862 */
863static int eeepc_get_fan_pwm(void)
864{
865 int value = 0;
866
867 read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
Corentin Chary04dcd842008-10-09 15:33:57 +0200868 value = value * 255 / 100;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100869 return (value);
870}
871
872static void eeepc_set_fan_pwm(int value)
873{
Corentin Chary04dcd842008-10-09 15:33:57 +0200874 value = SENSORS_LIMIT(value, 0, 255);
875 value = value * 100 / 255;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100876 ec_write(EEEPC_EC_SC02, value);
877}
878
879static int eeepc_get_fan_rpm(void)
880{
881 int high = 0;
882 int low = 0;
883
884 read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
885 read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
886 return (high << 8 | low);
887}
888
889static int eeepc_get_fan_ctrl(void)
890{
891 int value = 0;
892
893 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
894 return ((value & 0x02 ? 1 : 0));
895}
896
897static void eeepc_set_fan_ctrl(int manual)
898{
899 int value = 0;
900
901 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
902 if (manual)
903 value |= 0x02;
904 else
905 value &= ~0x02;
906 ec_write(EEEPC_EC_SFB3, value);
907}
908
909static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
910{
911 int rv, value;
912
913 rv = parse_arg(buf, count, &value);
914 if (rv > 0)
915 set(value);
916 return rv;
917}
918
919static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
920{
921 return sprintf(buf, "%d\n", get());
922}
923
924#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
925 static ssize_t show_##_name(struct device *dev, \
926 struct device_attribute *attr, \
927 char *buf) \
928 { \
929 return show_sys_hwmon(_set, buf); \
930 } \
931 static ssize_t store_##_name(struct device *dev, \
932 struct device_attribute *attr, \
933 const char *buf, size_t count) \
934 { \
935 return store_sys_hwmon(_get, buf, count); \
936 } \
937 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
938
939EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
Corentin Chary04dcd842008-10-09 15:33:57 +0200940EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100941 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
942EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
943 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
944
Corentin Chary04dcd842008-10-09 15:33:57 +0200945static ssize_t
946show_name(struct device *dev, struct device_attribute *attr, char *buf)
947{
948 return sprintf(buf, "eeepc\n");
949}
950static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
951
Corentin Charye1faa9d2008-03-13 12:57:18 +0100952static struct attribute *hwmon_attributes[] = {
Corentin Chary04dcd842008-10-09 15:33:57 +0200953 &sensor_dev_attr_pwm1.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100954 &sensor_dev_attr_fan1_input.dev_attr.attr,
955 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
Corentin Chary04dcd842008-10-09 15:33:57 +0200956 &sensor_dev_attr_name.dev_attr.attr,
Corentin Charye1faa9d2008-03-13 12:57:18 +0100957 NULL
958};
959
960static struct attribute_group hwmon_attribute_group = {
961 .attrs = hwmon_attributes
962};
963
964/*
Eric Coopere59f8792008-03-13 12:55:46 +0100965 * exit/init
966 */
Corentin Charya5fa4292008-03-13 12:56:37 +0100967static void eeepc_backlight_exit(void)
968{
969 if (eeepc_backlight_device)
970 backlight_device_unregister(eeepc_backlight_device);
Corentin Charya9df80c2009-01-20 16:17:40 +0100971 eeepc_backlight_device = NULL;
972}
973
974static void eeepc_rfkill_exit(void)
975{
Matthew Garretta195dcd2008-08-19 12:13:20 +0100976 if (ehotk->eeepc_wlan_rfkill)
977 rfkill_unregister(ehotk->eeepc_wlan_rfkill);
978 if (ehotk->eeepc_bluetooth_rfkill)
979 rfkill_unregister(ehotk->eeepc_bluetooth_rfkill);
Corentin Charya9df80c2009-01-20 16:17:40 +0100980}
981
982static void eeepc_input_exit(void)
983{
984 if (ehotk->inputdev)
985 input_unregister_device(ehotk->inputdev);
Corentin Charya5fa4292008-03-13 12:56:37 +0100986}
987
Corentin Charye1faa9d2008-03-13 12:57:18 +0100988static void eeepc_hwmon_exit(void)
989{
990 struct device *hwmon;
991
992 hwmon = eeepc_hwmon_device;
993 if (!hwmon)
994 return ;
Corentin Charye1faa9d2008-03-13 12:57:18 +0100995 sysfs_remove_group(&hwmon->kobj,
996 &hwmon_attribute_group);
Matthew Garrettf1441312008-08-20 14:08:57 -0700997 hwmon_device_unregister(hwmon);
Corentin Charye1faa9d2008-03-13 12:57:18 +0100998 eeepc_hwmon_device = NULL;
999}
1000
Eric Coopere59f8792008-03-13 12:55:46 +01001001static void __exit eeepc_laptop_exit(void)
1002{
Corentin Charya5fa4292008-03-13 12:56:37 +01001003 eeepc_backlight_exit();
Corentin Charya9df80c2009-01-20 16:17:40 +01001004 eeepc_rfkill_exit();
1005 eeepc_input_exit();
Corentin Charye1faa9d2008-03-13 12:57:18 +01001006 eeepc_hwmon_exit();
Eric Coopere59f8792008-03-13 12:55:46 +01001007 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1008 sysfs_remove_group(&platform_device->dev.kobj,
1009 &platform_attribute_group);
1010 platform_device_unregister(platform_device);
1011 platform_driver_unregister(&platform_driver);
1012}
1013
Corentin Charya5fa4292008-03-13 12:56:37 +01001014static int eeepc_backlight_init(struct device *dev)
1015{
1016 struct backlight_device *bd;
1017
1018 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
1019 NULL, &eeepcbl_ops);
1020 if (IS_ERR(bd)) {
1021 printk(EEEPC_ERR
1022 "Could not register eeepc backlight device\n");
1023 eeepc_backlight_device = NULL;
1024 return PTR_ERR(bd);
1025 }
1026 eeepc_backlight_device = bd;
1027 bd->props.max_brightness = 15;
1028 bd->props.brightness = read_brightness(NULL);
1029 bd->props.power = FB_BLANK_UNBLANK;
1030 backlight_update_status(bd);
1031 return 0;
1032}
1033
Corentin Charye1faa9d2008-03-13 12:57:18 +01001034static int eeepc_hwmon_init(struct device *dev)
1035{
1036 struct device *hwmon;
1037 int result;
1038
1039 hwmon = hwmon_device_register(dev);
1040 if (IS_ERR(hwmon)) {
1041 printk(EEEPC_ERR
1042 "Could not register eeepc hwmon device\n");
1043 eeepc_hwmon_device = NULL;
1044 return PTR_ERR(hwmon);
1045 }
1046 eeepc_hwmon_device = hwmon;
1047 result = sysfs_create_group(&hwmon->kobj,
1048 &hwmon_attribute_group);
1049 if (result)
1050 eeepc_hwmon_exit();
1051 return result;
1052}
1053
Eric Coopere59f8792008-03-13 12:55:46 +01001054static int __init eeepc_laptop_init(void)
1055{
1056 struct device *dev;
1057 int result;
1058
1059 if (acpi_disabled)
1060 return -ENODEV;
1061 result = acpi_bus_register_driver(&eeepc_hotk_driver);
1062 if (result < 0)
1063 return result;
1064 if (!ehotk) {
1065 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1066 return -ENODEV;
1067 }
1068 dev = acpi_get_physical_device(ehotk->device->handle);
Thomas Renningera2bf8c02008-08-01 17:37:59 +02001069
1070 if (!acpi_video_backlight_support()) {
1071 result = eeepc_backlight_init(dev);
1072 if (result)
1073 goto fail_backlight;
1074 } else
1075 printk(EEEPC_INFO "Backlight controlled by ACPI video "
1076 "driver\n");
1077
Corentin Charye1faa9d2008-03-13 12:57:18 +01001078 result = eeepc_hwmon_init(dev);
1079 if (result)
1080 goto fail_hwmon;
Pekka Enbergcede2cb2009-06-16 19:28:45 +00001081
1082 eeepc_enable_camera();
1083
Eric Coopere59f8792008-03-13 12:55:46 +01001084 /* Register platform stuff */
1085 result = platform_driver_register(&platform_driver);
1086 if (result)
1087 goto fail_platform_driver;
1088 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
1089 if (!platform_device) {
1090 result = -ENOMEM;
1091 goto fail_platform_device1;
1092 }
1093 result = platform_device_add(platform_device);
1094 if (result)
1095 goto fail_platform_device2;
1096 result = sysfs_create_group(&platform_device->dev.kobj,
1097 &platform_attribute_group);
1098 if (result)
1099 goto fail_sysfs;
1100 return 0;
1101fail_sysfs:
1102 platform_device_del(platform_device);
1103fail_platform_device2:
1104 platform_device_put(platform_device);
1105fail_platform_device1:
1106 platform_driver_unregister(&platform_driver);
1107fail_platform_driver:
Corentin Charye1faa9d2008-03-13 12:57:18 +01001108 eeepc_hwmon_exit();
1109fail_hwmon:
Corentin Charya5fa4292008-03-13 12:56:37 +01001110 eeepc_backlight_exit();
1111fail_backlight:
Corentin Charya9df80c2009-01-20 16:17:40 +01001112 eeepc_input_exit();
1113 eeepc_rfkill_exit();
Eric Coopere59f8792008-03-13 12:55:46 +01001114 return result;
1115}
1116
1117module_init(eeepc_laptop_init);
1118module_exit(eeepc_laptop_exit);