eeepc-laptop: fix rfkill memory leak on unload
[deliverable/linux.git] / drivers / platform / x86 / eeepc-laptop.c
CommitLineData
e59f8796
EC
1/*
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
19b53289
JP
19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
20
e59f8796
EC
21#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/init.h>
24#include <linux/types.h>
25#include <linux/platform_device.h>
a5fa429b
CC
26#include <linux/backlight.h>
27#include <linux/fb.h>
e1faa9da
CC
28#include <linux/hwmon.h>
29#include <linux/hwmon-sysfs.h>
e59f8796
EC
30#include <acpi/acpi_drivers.h>
31#include <acpi/acpi_bus.h>
32#include <linux/uaccess.h>
a195dcdc
MG
33#include <linux/input.h>
34#include <linux/rfkill.h>
5740294c 35#include <linux/pci.h>
2b121bc2 36#include <linux/pci_hotplug.h>
e59f8796
EC
37
38#define EEEPC_LAPTOP_VERSION "0.1"
39
40#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver"
41#define EEEPC_HOTK_FILE "eeepc"
42#define EEEPC_HOTK_CLASS "hotkey"
43#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
44#define EEEPC_HOTK_HID "ASUS010"
45
e59f8796
EC
46
47/*
48 * Definitions for Asus EeePC
49 */
50#define NOTIFY_WLAN_ON 0x10
a5fa429b
CC
51#define NOTIFY_BRN_MIN 0x20
52#define NOTIFY_BRN_MAX 0x2f
e59f8796
EC
53
54enum {
55 DISABLE_ASL_WLAN = 0x0001,
56 DISABLE_ASL_BLUETOOTH = 0x0002,
57 DISABLE_ASL_IRDA = 0x0004,
58 DISABLE_ASL_CAMERA = 0x0008,
59 DISABLE_ASL_TV = 0x0010,
60 DISABLE_ASL_GPS = 0x0020,
61 DISABLE_ASL_DISPLAYSWITCH = 0x0040,
62 DISABLE_ASL_MODEM = 0x0080,
b7b700d4
CC
63 DISABLE_ASL_CARDREADER = 0x0100,
64 DISABLE_ASL_3G = 0x0200,
65 DISABLE_ASL_WIMAX = 0x0400,
66 DISABLE_ASL_HWCF = 0x0800
e59f8796
EC
67};
68
69enum {
70 CM_ASL_WLAN = 0,
71 CM_ASL_BLUETOOTH,
72 CM_ASL_IRDA,
73 CM_ASL_1394,
74 CM_ASL_CAMERA,
75 CM_ASL_TV,
76 CM_ASL_GPS,
77 CM_ASL_DVDROM,
78 CM_ASL_DISPLAYSWITCH,
79 CM_ASL_PANELBRIGHT,
80 CM_ASL_BIOSFLASH,
81 CM_ASL_ACPIFLASH,
82 CM_ASL_CPUFV,
83 CM_ASL_CPUTEMPERATURE,
84 CM_ASL_FANCPU,
85 CM_ASL_FANCHASSIS,
86 CM_ASL_USBPORT1,
87 CM_ASL_USBPORT2,
88 CM_ASL_USBPORT3,
89 CM_ASL_MODEM,
90 CM_ASL_CARDREADER,
b7b700d4
CC
91 CM_ASL_3G,
92 CM_ASL_WIMAX,
93 CM_ASL_HWCF,
94 CM_ASL_LID,
95 CM_ASL_TYPE,
96 CM_ASL_PANELPOWER, /*P901*/
97 CM_ASL_TPD
e59f8796
EC
98};
99
14109461 100static const char *cm_getv[] = {
3af9bfcb 101 "WLDG", "BTHG", NULL, NULL,
e59f8796
EC
102 "CAMG", NULL, NULL, NULL,
103 NULL, "PBLG", NULL, NULL,
104 "CFVG", NULL, NULL, NULL,
105 "USBG", NULL, NULL, "MODG",
b7b700d4
CC
106 "CRDG", "M3GG", "WIMG", "HWCF",
107 "LIDG", "TYPE", "PBPG", "TPDG"
e59f8796
EC
108};
109
14109461 110static const char *cm_setv[] = {
3af9bfcb 111 "WLDS", "BTHS", NULL, NULL,
e59f8796
EC
112 "CAMS", NULL, NULL, NULL,
113 "SDSP", "PBLS", "HDPS", NULL,
114 "CFVS", NULL, NULL, NULL,
115 "USBG", NULL, NULL, "MODS",
b7b700d4
CC
116 "CRDS", "M3GS", "WIMS", NULL,
117 NULL, NULL, "PBPS", "TPDS"
e59f8796
EC
118};
119
e1faa9da
CC
120#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
121
122#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
123#define EEEPC_EC_SC02 0x63
124#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
125#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
126#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */
127#define EEEPC_EC_SFB3 0xD3
128
e59f8796
EC
129/*
130 * This is the main structure, we can use it to store useful information
131 * about the hotk device
132 */
133struct eeepc_hotk {
134 struct acpi_device *device; /* the device we are in */
135 acpi_handle handle; /* the handle of the hotk device */
136 u32 cm_supported; /* the control methods supported
137 by this BIOS */
138 uint init_flag; /* Init flags */
139 u16 event_count[128]; /* count for each event */
a195dcdc
MG
140 struct input_dev *inputdev;
141 u16 *keycode_map;
7de39389
CC
142 struct rfkill *wlan_rfkill;
143 struct rfkill *bluetooth_rfkill;
3cd530b5 144 struct rfkill *wwan3g_rfkill;
d1ec9c3d 145 struct rfkill *wimax_rfkill;
2b121bc2 146 struct hotplug_slot *hotplug_slot;
dcf443b5 147 struct mutex hotplug_lock;
e59f8796
EC
148};
149
150/* The actual device the driver binds to */
151static struct eeepc_hotk *ehotk;
152
153/* Platform device/driver */
c200da5d
AJ
154static int eeepc_hotk_thaw(struct device *device);
155static int eeepc_hotk_restore(struct device *device);
156
157static struct dev_pm_ops eeepc_pm_ops = {
158 .thaw = eeepc_hotk_thaw,
159 .restore = eeepc_hotk_restore,
160};
161
e59f8796
EC
162static struct platform_driver platform_driver = {
163 .driver = {
164 .name = EEEPC_HOTK_FILE,
165 .owner = THIS_MODULE,
c200da5d 166 .pm = &eeepc_pm_ops,
e59f8796
EC
167 }
168};
169
170static struct platform_device *platform_device;
171
a195dcdc
MG
172struct key_entry {
173 char type;
174 u8 code;
175 u16 keycode;
176};
177
178enum { KE_KEY, KE_END };
179
180static struct key_entry eeepc_keymap[] = {
181 /* Sleep already handled via generic ACPI code */
182 {KE_KEY, 0x10, KEY_WLAN },
978605c4 183 {KE_KEY, 0x11, KEY_WLAN },
a195dcdc
MG
184 {KE_KEY, 0x12, KEY_PROG1 },
185 {KE_KEY, 0x13, KEY_MUTE },
186 {KE_KEY, 0x14, KEY_VOLUMEDOWN },
187 {KE_KEY, 0x15, KEY_VOLUMEUP },
b5f6f265
MG
188 {KE_KEY, 0x1a, KEY_COFFEE },
189 {KE_KEY, 0x1b, KEY_ZOOM },
190 {KE_KEY, 0x1c, KEY_PROG2 },
191 {KE_KEY, 0x1d, KEY_PROG3 },
64b86b65
DS
192 {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN },
193 {KE_KEY, NOTIFY_BRN_MIN + 2, KEY_BRIGHTNESSUP },
a195dcdc
MG
194 {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
195 {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
196 {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
197 {KE_END, 0},
198};
199
e59f8796
EC
200/*
201 * The hotkey driver declaration
202 */
203static int eeepc_hotk_add(struct acpi_device *device);
204static int eeepc_hotk_remove(struct acpi_device *device, int type);
d9b9bd7b 205static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
e59f8796
EC
206
207static const struct acpi_device_id eeepc_device_ids[] = {
208 {EEEPC_HOTK_HID, 0},
209 {"", 0},
210};
211MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
212
213static struct acpi_driver eeepc_hotk_driver = {
214 .name = EEEPC_HOTK_NAME,
215 .class = EEEPC_HOTK_CLASS,
216 .ids = eeepc_device_ids,
d9b9bd7b 217 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
e59f8796
EC
218 .ops = {
219 .add = eeepc_hotk_add,
220 .remove = eeepc_hotk_remove,
d9b9bd7b 221 .notify = eeepc_hotk_notify,
e59f8796
EC
222 },
223};
224
2b121bc2
CC
225/* PCI hotplug ops */
226static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value);
227
228static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
229 .owner = THIS_MODULE,
230 .get_adapter_status = eeepc_get_adapter_status,
231 .get_power_status = eeepc_get_adapter_status,
232};
233
a5fa429b
CC
234/* The backlight device /sys/class/backlight */
235static struct backlight_device *eeepc_backlight_device;
236
e1faa9da
CC
237/* The hwmon device */
238static struct device *eeepc_hwmon_device;
239
a5fa429b
CC
240/*
241 * The backlight class declaration
242 */
243static int read_brightness(struct backlight_device *bd);
244static int update_bl_status(struct backlight_device *bd);
245static struct backlight_ops eeepcbl_ops = {
246 .get_brightness = read_brightness,
247 .update_status = update_bl_status,
248};
249
e59f8796
EC
250MODULE_AUTHOR("Corentin Chary, Eric Cooper");
251MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
252MODULE_LICENSE("GPL");
253
254/*
255 * ACPI Helpers
256 */
257static int write_acpi_int(acpi_handle handle, const char *method, int val,
258 struct acpi_buffer *output)
259{
260 struct acpi_object_list params;
261 union acpi_object in_obj;
262 acpi_status status;
263
264 params.count = 1;
265 params.pointer = &in_obj;
266 in_obj.type = ACPI_TYPE_INTEGER;
267 in_obj.integer.value = val;
268
269 status = acpi_evaluate_object(handle, (char *)method, &params, output);
270 return (status == AE_OK ? 0 : -1);
271}
272
273static int read_acpi_int(acpi_handle handle, const char *method, int *val)
274{
275 acpi_status status;
27663c58 276 unsigned long long result;
e59f8796
EC
277
278 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
279 if (ACPI_FAILURE(status)) {
280 *val = -1;
281 return -1;
282 } else {
283 *val = result;
284 return 0;
285 }
286}
287
288static int set_acpi(int cm, int value)
289{
290 if (ehotk->cm_supported & (0x1 << cm)) {
291 const char *method = cm_setv[cm];
292 if (method == NULL)
293 return -ENODEV;
294 if (write_acpi_int(ehotk->handle, method, value, NULL))
19b53289 295 pr_warning("Error writing %s\n", method);
e59f8796
EC
296 }
297 return 0;
298}
299
300static int get_acpi(int cm)
301{
f36509e7 302 int value = -ENODEV;
e59f8796
EC
303 if ((ehotk->cm_supported & (0x1 << cm))) {
304 const char *method = cm_getv[cm];
305 if (method == NULL)
306 return -ENODEV;
307 if (read_acpi_int(ehotk->handle, method, &value))
19b53289 308 pr_warning("Error reading %s\n", method);
e59f8796
EC
309 }
310 return value;
311}
312
a5fa429b
CC
313/*
314 * Backlight
315 */
316static int read_brightness(struct backlight_device *bd)
317{
318 return get_acpi(CM_ASL_PANELBRIGHT);
319}
320
321static int set_brightness(struct backlight_device *bd, int value)
322{
323 value = max(0, min(15, value));
324 return set_acpi(CM_ASL_PANELBRIGHT, value);
325}
326
327static int update_bl_status(struct backlight_device *bd)
328{
329 return set_brightness(bd, bd->props.brightness);
330}
331
a195dcdc
MG
332/*
333 * Rfkill helpers
334 */
335
19d337df 336static bool eeepc_wlan_rfkill_blocked(void)
a195dcdc
MG
337{
338 if (get_acpi(CM_ASL_WLAN) == 1)
19d337df
JB
339 return false;
340 return true;
a195dcdc
MG
341}
342
19d337df 343static int eeepc_rfkill_set(void *data, bool blocked)
a195dcdc 344{
19d337df
JB
345 unsigned long asl = (unsigned long)data;
346 return set_acpi(asl, !blocked);
a195dcdc
MG
347}
348
19d337df
JB
349static const struct rfkill_ops eeepc_rfkill_ops = {
350 .set_block = eeepc_rfkill_set,
351};
a195dcdc 352
cede2cb6
PE
353static void __init eeepc_enable_camera(void)
354{
355 /*
356 * If the following call to set_acpi() fails, it's because there's no
357 * camera so we can ignore the error.
358 */
359 set_acpi(CM_ASL_CAMERA, 1);
360}
361
e59f8796
EC
362/*
363 * Sys helpers
364 */
365static int parse_arg(const char *buf, unsigned long count, int *val)
366{
367 if (!count)
368 return 0;
369 if (sscanf(buf, "%i", val) != 1)
370 return -EINVAL;
371 return count;
372}
373
374static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
375{
376 int rv, value;
377
378 rv = parse_arg(buf, count, &value);
379 if (rv > 0)
f36509e7
CC
380 value = set_acpi(cm, value);
381 if (value < 0)
382 return value;
e59f8796
EC
383 return rv;
384}
385
386static ssize_t show_sys_acpi(int cm, char *buf)
387{
f36509e7
CC
388 int value = get_acpi(cm);
389
390 if (value < 0)
391 return value;
392 return sprintf(buf, "%d\n", value);
e59f8796
EC
393}
394
395#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \
396 static ssize_t show_##_name(struct device *dev, \
397 struct device_attribute *attr, \
398 char *buf) \
399 { \
400 return show_sys_acpi(_cm, buf); \
401 } \
402 static ssize_t store_##_name(struct device *dev, \
403 struct device_attribute *attr, \
404 const char *buf, size_t count) \
405 { \
406 return store_sys_acpi(_cm, buf, count); \
407 } \
408 static struct device_attribute dev_attr_##_name = { \
409 .attr = { \
410 .name = __stringify(_name), \
411 .mode = 0644 }, \
412 .show = show_##_name, \
413 .store = store_##_name, \
414 }
415
416EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
417EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
418EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
b31d0fde
CC
419
420struct eeepc_cpufv {
421 int num;
422 int cur;
423};
424
425static int get_cpufv(struct eeepc_cpufv *c)
426{
427 c->cur = get_acpi(CM_ASL_CPUFV);
428 c->num = (c->cur >> 8) & 0xff;
429 c->cur &= 0xff;
430 if (c->cur < 0 || c->num <= 0 || c->num > 12)
431 return -ENODEV;
432 return 0;
433}
434
435static ssize_t show_available_cpufv(struct device *dev,
436 struct device_attribute *attr,
437 char *buf)
438{
439 struct eeepc_cpufv c;
440 int i;
441 ssize_t len = 0;
442
443 if (get_cpufv(&c))
444 return -ENODEV;
445 for (i = 0; i < c.num; i++)
446 len += sprintf(buf + len, "%d ", i);
447 len += sprintf(buf + len, "\n");
448 return len;
449}
450
451static ssize_t show_cpufv(struct device *dev,
452 struct device_attribute *attr,
453 char *buf)
454{
455 struct eeepc_cpufv c;
456
457 if (get_cpufv(&c))
458 return -ENODEV;
459 return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
460}
461
462static ssize_t store_cpufv(struct device *dev,
463 struct device_attribute *attr,
464 const char *buf, size_t count)
465{
466 struct eeepc_cpufv c;
467 int rv, value;
468
469 if (get_cpufv(&c))
470 return -ENODEV;
471 rv = parse_arg(buf, count, &value);
472 if (rv < 0)
473 return rv;
474 if (!rv || value < 0 || value >= c.num)
475 return -EINVAL;
476 set_acpi(CM_ASL_CPUFV, value);
477 return rv;
478}
479
480static struct device_attribute dev_attr_cpufv = {
481 .attr = {
482 .name = "cpufv",
483 .mode = 0644 },
484 .show = show_cpufv,
485 .store = store_cpufv
486};
487
488static struct device_attribute dev_attr_available_cpufv = {
489 .attr = {
490 .name = "available_cpufv",
491 .mode = 0444 },
492 .show = show_available_cpufv
493};
e59f8796
EC
494
495static struct attribute *platform_attributes[] = {
496 &dev_attr_camera.attr,
497 &dev_attr_cardr.attr,
498 &dev_attr_disp.attr,
158ca1d7 499 &dev_attr_cpufv.attr,
b31d0fde 500 &dev_attr_available_cpufv.attr,
e59f8796
EC
501 NULL
502};
503
504static struct attribute_group platform_attribute_group = {
505 .attrs = platform_attributes
506};
507
508/*
509 * Hotkey functions
510 */
a195dcdc
MG
511static struct key_entry *eepc_get_entry_by_scancode(int code)
512{
513 struct key_entry *key;
514
515 for (key = eeepc_keymap; key->type != KE_END; key++)
516 if (code == key->code)
517 return key;
518
519 return NULL;
520}
521
522static struct key_entry *eepc_get_entry_by_keycode(int code)
523{
524 struct key_entry *key;
525
526 for (key = eeepc_keymap; key->type != KE_END; key++)
527 if (code == key->keycode && key->type == KE_KEY)
528 return key;
529
530 return NULL;
531}
532
533static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
534{
535 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
536
537 if (key && key->type == KE_KEY) {
538 *keycode = key->keycode;
539 return 0;
540 }
541
542 return -EINVAL;
543}
544
545static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
546{
547 struct key_entry *key;
548 int old_keycode;
549
550 if (keycode < 0 || keycode > KEY_MAX)
551 return -EINVAL;
552
553 key = eepc_get_entry_by_scancode(scancode);
554 if (key && key->type == KE_KEY) {
555 old_keycode = key->keycode;
556 key->keycode = keycode;
557 set_bit(keycode, dev->keybit);
558 if (!eepc_get_entry_by_keycode(old_keycode))
559 clear_bit(old_keycode, dev->keybit);
560 return 0;
561 }
562
563 return -EINVAL;
564}
565
dbfa3ba9
CC
566static void cmsg_quirk(int cm, const char *name)
567{
568 int dummy;
569
570 /* Some BIOSes do not report cm although it is avaliable.
571 Check if cm_getv[cm] works and, if yes, assume cm should be set. */
572 if (!(ehotk->cm_supported & (1 << cm))
573 && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) {
574 pr_info("%s (%x) not reported by BIOS,"
575 " enabling anyway\n", name, 1 << cm);
576 ehotk->cm_supported |= 1 << cm;
577 }
578}
579
580static void cmsg_quirks(void)
581{
582 cmsg_quirk(CM_ASL_LID, "LID");
583 cmsg_quirk(CM_ASL_TYPE, "TYPE");
584 cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER");
585 cmsg_quirk(CM_ASL_TPD, "TPD");
586}
587
e59f8796
EC
588static int eeepc_hotk_check(void)
589{
590 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
591 int result;
592
593 result = acpi_bus_get_status(ehotk->device);
594 if (result)
595 return result;
596 if (ehotk->device->status.present) {
597 if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
598 &buffer)) {
19b53289 599 pr_err("Hotkey initialization failed\n");
e59f8796
EC
600 return -ENODEV;
601 } else {
19b53289 602 pr_notice("Hotkey init flags 0x%x\n", ehotk->init_flag);
e59f8796
EC
603 }
604 /* get control methods supported */
605 if (read_acpi_int(ehotk->handle, "CMSG"
606 , &ehotk->cm_supported)) {
19b53289 607 pr_err("Get control methods supported failed\n");
e59f8796
EC
608 return -ENODEV;
609 } else {
dbfa3ba9 610 cmsg_quirks();
19b53289
JP
611 pr_info("Get control methods supported: 0x%x\n",
612 ehotk->cm_supported);
e59f8796
EC
613 }
614 } else {
19b53289 615 pr_err("Hotkey device not present, aborting\n");
e59f8796
EC
616 return -EINVAL;
617 }
618 return 0;
619}
620
64b86b65 621static int notify_brn(void)
a5fa429b 622{
64b86b65 623 /* returns the *previous* brightness, or -1 */
a5fa429b 624 struct backlight_device *bd = eeepc_backlight_device;
64b86b65
DS
625 if (bd) {
626 int old = bd->props.brightness;
7695fb04 627 bd->props.brightness = read_brightness(bd);
64b86b65
DS
628 return old;
629 }
630 return -1;
a5fa429b
CC
631}
632
2b121bc2
CC
633static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
634 u8 *value)
635{
636 int val = get_acpi(CM_ASL_WLAN);
637
638 if (val == 1 || val == 0)
639 *value = val;
640 else
641 return -EINVAL;
642
643 return 0;
644}
645
dcf443b5 646static void eeepc_rfkill_hotplug(void)
5740294c
MG
647{
648 struct pci_dev *dev;
6d41839e
AJ
649 struct pci_bus *bus;
650 bool blocked = eeepc_wlan_rfkill_blocked();
5740294c 651
07e84aa9
AJ
652 if (ehotk->wlan_rfkill)
653 rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
6d41839e 654
dcf443b5
AJ
655 mutex_lock(&ehotk->hotplug_lock);
656
07e84aa9
AJ
657 if (ehotk->hotplug_slot) {
658 bus = pci_find_bus(0, 1);
659 if (!bus) {
660 pr_warning("Unable to find PCI bus 1?\n");
dcf443b5 661 goto out_unlock;
5740294c 662 }
07e84aa9
AJ
663
664 if (!blocked) {
665 dev = pci_get_slot(bus, 0);
666 if (dev) {
667 /* Device already present */
668 pci_dev_put(dev);
669 goto out_unlock;
670 }
671 dev = pci_scan_single_device(bus, 0);
672 if (dev) {
673 pci_bus_assign_resources(bus);
674 if (pci_bus_add_device(dev))
675 pr_err("Unable to hotplug wifi\n");
676 }
677 } else {
678 dev = pci_get_slot(bus, 0);
679 if (dev) {
680 pci_remove_bus_device(dev);
681 pci_dev_put(dev);
682 }
5740294c
MG
683 }
684 }
dcf443b5
AJ
685
686out_unlock:
687 mutex_unlock(&ehotk->hotplug_lock);
5740294c
MG
688}
689
96e9cfeb
AJ
690static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
691{
692 if (event != ACPI_NOTIFY_BUS_CHECK)
693 return;
694
dcf443b5 695 eeepc_rfkill_hotplug();
96e9cfeb
AJ
696}
697
d9b9bd7b 698static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
e59f8796 699{
a195dcdc 700 static struct key_entry *key;
7950b71c 701 u16 count;
64b86b65 702 int brn = -ENODEV;
7950b71c 703
e59f8796
EC
704 if (!ehotk)
705 return;
d9b9bd7b
BH
706 if (event > ACPI_MAX_SYS_NOTIFY)
707 return;
a5fa429b 708 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
64b86b65 709 brn = notify_brn();
7950b71c
CC
710 count = ehotk->event_count[event % 128]++;
711 acpi_bus_generate_proc_event(ehotk->device, event, count);
2b25c9f0
CC
712 acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class,
713 dev_name(&ehotk->device->dev), event,
7950b71c 714 count);
a195dcdc 715 if (ehotk->inputdev) {
64b86b65
DS
716 if (brn != -ENODEV) {
717 /* brightness-change events need special
718 * handling for conversion to key events
719 */
720 if (brn < 0)
721 brn = event;
722 else
723 brn += NOTIFY_BRN_MIN;
724 if (event < brn)
725 event = NOTIFY_BRN_MIN; /* brightness down */
726 else if (event > brn)
727 event = NOTIFY_BRN_MIN + 2; /* ... up */
728 else
729 event = NOTIFY_BRN_MIN + 1; /* ... unchanged */
730 }
a195dcdc
MG
731 key = eepc_get_entry_by_scancode(event);
732 if (key) {
733 switch (key->type) {
734 case KE_KEY:
735 input_report_key(ehotk->inputdev, key->keycode,
736 1);
737 input_sync(ehotk->inputdev);
738 input_report_key(ehotk->inputdev, key->keycode,
739 0);
740 input_sync(ehotk->inputdev);
741 break;
742 }
743 }
744 }
e59f8796
EC
745}
746
5740294c
MG
747static int eeepc_register_rfkill_notifier(char *node)
748{
749 acpi_status status = AE_OK;
750 acpi_handle handle;
751
752 status = acpi_get_handle(NULL, node, &handle);
753
754 if (ACPI_SUCCESS(status)) {
755 status = acpi_install_notify_handler(handle,
756 ACPI_SYSTEM_NOTIFY,
757 eeepc_rfkill_notify,
758 NULL);
759 if (ACPI_FAILURE(status))
19b53289 760 pr_warning("Failed to register notify on %s\n", node);
5740294c
MG
761 } else
762 return -ENODEV;
763
764 return 0;
765}
766
767static void eeepc_unregister_rfkill_notifier(char *node)
768{
769 acpi_status status = AE_OK;
770 acpi_handle handle;
771
772 status = acpi_get_handle(NULL, node, &handle);
773
774 if (ACPI_SUCCESS(status)) {
775 status = acpi_remove_notify_handler(handle,
776 ACPI_SYSTEM_NOTIFY,
777 eeepc_rfkill_notify);
778 if (ACPI_FAILURE(status))
19b53289 779 pr_err("Error removing rfkill notify handler %s\n",
5740294c
MG
780 node);
781 }
782}
783
2b121bc2
CC
784static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
785{
786 kfree(hotplug_slot->info);
787 kfree(hotplug_slot);
788}
789
790static int eeepc_setup_pci_hotplug(void)
791{
792 int ret = -ENOMEM;
793 struct pci_bus *bus = pci_find_bus(0, 1);
794
795 if (!bus) {
19b53289 796 pr_err("Unable to find wifi PCI bus\n");
2b121bc2
CC
797 return -ENODEV;
798 }
799
800 ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
801 if (!ehotk->hotplug_slot)
802 goto error_slot;
803
804 ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
805 GFP_KERNEL);
806 if (!ehotk->hotplug_slot->info)
807 goto error_info;
808
809 ehotk->hotplug_slot->private = ehotk;
810 ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
811 ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
812 eeepc_get_adapter_status(ehotk->hotplug_slot,
813 &ehotk->hotplug_slot->info->adapter_status);
814
815 ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
816 if (ret) {
19b53289 817 pr_err("Unable to register hotplug slot - %d\n", ret);
2b121bc2
CC
818 goto error_register;
819 }
820
821 return 0;
822
823error_register:
824 kfree(ehotk->hotplug_slot->info);
825error_info:
826 kfree(ehotk->hotplug_slot);
827 ehotk->hotplug_slot = NULL;
828error_slot:
829 return ret;
830}
831
c200da5d 832static int eeepc_hotk_thaw(struct device *device)
96e9cfeb 833{
7de39389 834 if (ehotk->wlan_rfkill) {
96e9cfeb
AJ
835 bool wlan;
836
c1edd99f
AJ
837 /*
838 * Work around bios bug - acpi _PTS turns off the wireless led
839 * during suspend. Normally it restores it on resume, but
c200da5d 840 * we should kick it ourselves in case hibernation is aborted.
96e9cfeb
AJ
841 */
842 wlan = get_acpi(CM_ASL_WLAN);
843 set_acpi(CM_ASL_WLAN, wlan);
c200da5d
AJ
844 }
845
846 return 0;
847}
96e9cfeb 848
c200da5d
AJ
849static int eeepc_hotk_restore(struct device *device)
850{
851 /* Refresh both wlan rfkill state and pci hotplug */
852 if (ehotk->wlan_rfkill)
dcf443b5 853 eeepc_rfkill_hotplug();
96e9cfeb 854
7de39389
CC
855 if (ehotk->bluetooth_rfkill)
856 rfkill_set_sw_state(ehotk->bluetooth_rfkill,
96e9cfeb 857 get_acpi(CM_ASL_BLUETOOTH) != 1);
a4746101
AJ
858 if (ehotk->wwan3g_rfkill)
859 rfkill_set_sw_state(ehotk->wwan3g_rfkill,
860 get_acpi(CM_ASL_3G) != 1);
d1ec9c3d
CC
861 if (ehotk->wimax_rfkill)
862 rfkill_set_sw_state(ehotk->wimax_rfkill,
863 get_acpi(CM_ASL_WIMAX) != 1);
96e9cfeb
AJ
864
865 return 0;
866}
867
e1faa9da
CC
868/*
869 * Hwmon
870 */
871static int eeepc_get_fan_pwm(void)
872{
873 int value = 0;
874
875 read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
04dcd84b 876 value = value * 255 / 100;
e1faa9da
CC
877 return (value);
878}
879
880static void eeepc_set_fan_pwm(int value)
881{
04dcd84b
CC
882 value = SENSORS_LIMIT(value, 0, 255);
883 value = value * 100 / 255;
e1faa9da
CC
884 ec_write(EEEPC_EC_SC02, value);
885}
886
887static int eeepc_get_fan_rpm(void)
888{
889 int high = 0;
890 int low = 0;
891
892 read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
893 read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
894 return (high << 8 | low);
895}
896
897static int eeepc_get_fan_ctrl(void)
898{
899 int value = 0;
900
901 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
902 return ((value & 0x02 ? 1 : 0));
903}
904
905static void eeepc_set_fan_ctrl(int manual)
906{
907 int value = 0;
908
909 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
910 if (manual)
911 value |= 0x02;
912 else
913 value &= ~0x02;
914 ec_write(EEEPC_EC_SFB3, value);
915}
916
917static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
918{
919 int rv, value;
920
921 rv = parse_arg(buf, count, &value);
922 if (rv > 0)
923 set(value);
924 return rv;
925}
926
927static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
928{
929 return sprintf(buf, "%d\n", get());
930}
931
932#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
933 static ssize_t show_##_name(struct device *dev, \
934 struct device_attribute *attr, \
935 char *buf) \
936 { \
937 return show_sys_hwmon(_set, buf); \
938 } \
939 static ssize_t store_##_name(struct device *dev, \
940 struct device_attribute *attr, \
941 const char *buf, size_t count) \
942 { \
943 return store_sys_hwmon(_get, buf, count); \
944 } \
945 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
946
947EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
04dcd84b 948EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
e1faa9da
CC
949 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
950EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
951 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
952
04dcd84b
CC
953static ssize_t
954show_name(struct device *dev, struct device_attribute *attr, char *buf)
955{
956 return sprintf(buf, "eeepc\n");
957}
958static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
959
e1faa9da 960static struct attribute *hwmon_attributes[] = {
04dcd84b 961 &sensor_dev_attr_pwm1.dev_attr.attr,
e1faa9da
CC
962 &sensor_dev_attr_fan1_input.dev_attr.attr,
963 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
04dcd84b 964 &sensor_dev_attr_name.dev_attr.attr,
e1faa9da
CC
965 NULL
966};
967
968static struct attribute_group hwmon_attribute_group = {
969 .attrs = hwmon_attributes
970};
971
e59f8796
EC
972/*
973 * exit/init
974 */
a5fa429b
CC
975static void eeepc_backlight_exit(void)
976{
977 if (eeepc_backlight_device)
978 backlight_device_unregister(eeepc_backlight_device);
a9df80c5
CC
979 eeepc_backlight_device = NULL;
980}
981
982static void eeepc_rfkill_exit(void)
983{
7de39389
CC
984 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
985 eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
07e84aa9 986 if (ehotk->wlan_rfkill) {
7de39389 987 rfkill_unregister(ehotk->wlan_rfkill);
a8258069 988 rfkill_destroy(ehotk->wlan_rfkill);
07e84aa9
AJ
989 ehotk->wlan_rfkill = NULL;
990 }
991 /*
992 * Refresh pci hotplug in case the rfkill state was changed after
993 * eeepc_unregister_rfkill_notifier()
994 */
995 eeepc_rfkill_hotplug();
996 if (ehotk->hotplug_slot)
997 pci_hp_deregister(ehotk->hotplug_slot);
998
a8258069 999 if (ehotk->bluetooth_rfkill) {
7de39389 1000 rfkill_unregister(ehotk->bluetooth_rfkill);
a8258069
AJ
1001 rfkill_destroy(ehotk->bluetooth_rfkill);
1002 ehotk->bluetooth_rfkill = NULL;
1003 }
1004 if (ehotk->wwan3g_rfkill) {
3cd530b5 1005 rfkill_unregister(ehotk->wwan3g_rfkill);
a8258069
AJ
1006 rfkill_destroy(ehotk->wwan3g_rfkill);
1007 ehotk->wwan3g_rfkill = NULL;
1008 }
1009 if (ehotk->wimax_rfkill) {
d1ec9c3d 1010 rfkill_unregister(ehotk->wimax_rfkill);
a8258069
AJ
1011 rfkill_destroy(ehotk->wimax_rfkill);
1012 ehotk->wimax_rfkill = NULL;
1013 }
a9df80c5
CC
1014}
1015
1016static void eeepc_input_exit(void)
1017{
1018 if (ehotk->inputdev)
1019 input_unregister_device(ehotk->inputdev);
a5fa429b
CC
1020}
1021
e1faa9da
CC
1022static void eeepc_hwmon_exit(void)
1023{
1024 struct device *hwmon;
1025
1026 hwmon = eeepc_hwmon_device;
1027 if (!hwmon)
1028 return ;
e1faa9da
CC
1029 sysfs_remove_group(&hwmon->kobj,
1030 &hwmon_attribute_group);
f1441318 1031 hwmon_device_unregister(hwmon);
e1faa9da
CC
1032 eeepc_hwmon_device = NULL;
1033}
1034
7de39389
CC
1035static int eeepc_new_rfkill(struct rfkill **rfkill,
1036 const char *name, struct device *dev,
1037 enum rfkill_type type, int cm)
1038{
1039 int result;
1040
f36509e7
CC
1041 result = get_acpi(cm);
1042 if (result < 0)
1043 return result;
7de39389
CC
1044
1045 *rfkill = rfkill_alloc(name, dev, type,
1046 &eeepc_rfkill_ops, (void *)(unsigned long)cm);
1047
1048 if (!*rfkill)
1049 return -EINVAL;
1050
1051 rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1);
1052 result = rfkill_register(*rfkill);
1053 if (result) {
1054 rfkill_destroy(*rfkill);
1055 *rfkill = NULL;
1056 return result;
1057 }
1058 return 0;
1059}
1060
1061
1062static int eeepc_rfkill_init(struct device *dev)
1063{
1064 int result = 0;
1065
dcf443b5 1066 mutex_init(&ehotk->hotplug_lock);
7334546a 1067
7de39389
CC
1068 result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
1069 "eeepc-wlan", dev,
1070 RFKILL_TYPE_WLAN, CM_ASL_WLAN);
1071
1072 if (result && result != -ENODEV)
1073 goto exit;
1074
1075 result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill,
1076 "eeepc-bluetooth", dev,
1077 RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH);
1078
1079 if (result && result != -ENODEV)
1080 goto exit;
1081
3cd530b5
CC
1082 result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill,
1083 "eeepc-wwan3g", dev,
1084 RFKILL_TYPE_WWAN, CM_ASL_3G);
1085
1086 if (result && result != -ENODEV)
1087 goto exit;
1088
d1ec9c3d
CC
1089 result = eeepc_new_rfkill(&ehotk->wimax_rfkill,
1090 "eeepc-wimax", dev,
1091 RFKILL_TYPE_WIMAX, CM_ASL_WIMAX);
1092
1093 if (result && result != -ENODEV)
1094 goto exit;
1095
7de39389
CC
1096 result = eeepc_setup_pci_hotplug();
1097 /*
1098 * If we get -EBUSY then something else is handling the PCI hotplug -
1099 * don't fail in this case
1100 */
1101 if (result == -EBUSY)
1102 result = 0;
1103
07e84aa9
AJ
1104 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
1105 eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
1106 /*
1107 * Refresh pci hotplug in case the rfkill state was changed during
1108 * setup.
1109 */
1110 eeepc_rfkill_hotplug();
1111
7de39389
CC
1112exit:
1113 if (result && result != -ENODEV)
1114 eeepc_rfkill_exit();
1115 return result;
1116}
1117
a5fa429b
CC
1118static int eeepc_backlight_init(struct device *dev)
1119{
1120 struct backlight_device *bd;
1121
1122 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
1123 NULL, &eeepcbl_ops);
1124 if (IS_ERR(bd)) {
19b53289 1125 pr_err("Could not register eeepc backlight device\n");
a5fa429b
CC
1126 eeepc_backlight_device = NULL;
1127 return PTR_ERR(bd);
1128 }
1129 eeepc_backlight_device = bd;
1130 bd->props.max_brightness = 15;
1131 bd->props.brightness = read_brightness(NULL);
1132 bd->props.power = FB_BLANK_UNBLANK;
1133 backlight_update_status(bd);
1134 return 0;
1135}
1136
e1faa9da
CC
1137static int eeepc_hwmon_init(struct device *dev)
1138{
1139 struct device *hwmon;
1140 int result;
1141
1142 hwmon = hwmon_device_register(dev);
1143 if (IS_ERR(hwmon)) {
19b53289 1144 pr_err("Could not register eeepc hwmon device\n");
e1faa9da
CC
1145 eeepc_hwmon_device = NULL;
1146 return PTR_ERR(hwmon);
1147 }
1148 eeepc_hwmon_device = hwmon;
1149 result = sysfs_create_group(&hwmon->kobj,
1150 &hwmon_attribute_group);
1151 if (result)
1152 eeepc_hwmon_exit();
1153 return result;
1154}
1155
f2a9d5e8
AJ
1156static int eeepc_input_init(struct device *dev)
1157{
1158 const struct key_entry *key;
1159 int result;
1160
1161 ehotk->inputdev = input_allocate_device();
1162 if (!ehotk->inputdev) {
1163 pr_info("Unable to allocate input device\n");
1164 return -ENOMEM;
1165 }
1166 ehotk->inputdev->name = "Asus EeePC extra buttons";
1167 ehotk->inputdev->dev.parent = dev;
1168 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
1169 ehotk->inputdev->id.bustype = BUS_HOST;
1170 ehotk->inputdev->getkeycode = eeepc_getkeycode;
1171 ehotk->inputdev->setkeycode = eeepc_setkeycode;
1172
1173 for (key = eeepc_keymap; key->type != KE_END; key++) {
1174 switch (key->type) {
1175 case KE_KEY:
1176 set_bit(EV_KEY, ehotk->inputdev->evbit);
1177 set_bit(key->keycode, ehotk->inputdev->keybit);
1178 break;
1179 }
1180 }
1181 result = input_register_device(ehotk->inputdev);
1182 if (result) {
1183 pr_info("Unable to register input device\n");
1184 input_free_device(ehotk->inputdev);
1185 return result;
1186 }
1187 return 0;
1188}
1189
1e779854 1190static int eeepc_hotk_add(struct acpi_device *device)
e59f8796
EC
1191{
1192 struct device *dev;
1193 int result;
1194
1e779854 1195 if (!device)
aeb41b85 1196 return -EINVAL;
1e779854
AJ
1197 pr_notice(EEEPC_HOTK_NAME "\n");
1198 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
1199 if (!ehotk)
1200 return -ENOMEM;
1201 ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
1202 ehotk->handle = device->handle;
1203 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
1204 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
1205 device->driver_data = ehotk;
1206 ehotk->device = device;
cede2cb6 1207
1e779854
AJ
1208 result = eeepc_hotk_check();
1209 if (result)
f2a9d5e8 1210 goto fail_platform_driver;
cede2cb6
PE
1211 eeepc_enable_camera();
1212
e59f8796
EC
1213 /* Register platform stuff */
1214 result = platform_driver_register(&platform_driver);
1215 if (result)
1216 goto fail_platform_driver;
1217 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
1218 if (!platform_device) {
1219 result = -ENOMEM;
1220 goto fail_platform_device1;
1221 }
1222 result = platform_device_add(platform_device);
1223 if (result)
1224 goto fail_platform_device2;
1225 result = sysfs_create_group(&platform_device->dev.kobj,
1226 &platform_attribute_group);
1227 if (result)
1228 goto fail_sysfs;
7de39389 1229
1ddec2f9
CC
1230 dev = &platform_device->dev;
1231
1232 if (!acpi_video_backlight_support()) {
1233 result = eeepc_backlight_init(dev);
1234 if (result)
1235 goto fail_backlight;
1236 } else
1237 pr_info("Backlight controlled by ACPI video "
1238 "driver\n");
1239
f2a9d5e8
AJ
1240 result = eeepc_input_init(dev);
1241 if (result)
1242 goto fail_input;
1243
1ddec2f9
CC
1244 result = eeepc_hwmon_init(dev);
1245 if (result)
1246 goto fail_hwmon;
1247
7de39389
CC
1248 result = eeepc_rfkill_init(dev);
1249 if (result)
1250 goto fail_rfkill;
1251
e59f8796 1252 return 0;
1e779854 1253
7de39389 1254fail_rfkill:
1ddec2f9
CC
1255 eeepc_hwmon_exit();
1256fail_hwmon:
f2a9d5e8
AJ
1257 eeepc_input_exit();
1258fail_input:
1ddec2f9
CC
1259 eeepc_backlight_exit();
1260fail_backlight:
7de39389
CC
1261 sysfs_remove_group(&platform_device->dev.kobj,
1262 &platform_attribute_group);
e59f8796
EC
1263fail_sysfs:
1264 platform_device_del(platform_device);
1265fail_platform_device2:
1266 platform_device_put(platform_device);
1267fail_platform_device1:
1268 platform_driver_unregister(&platform_driver);
1269fail_platform_driver:
1e779854
AJ
1270 kfree(ehotk);
1271
e59f8796
EC
1272 return result;
1273}
1274
1e779854
AJ
1275static int eeepc_hotk_remove(struct acpi_device *device, int type)
1276{
1277 if (!device || !acpi_driver_data(device))
aeb41b85 1278 return -EINVAL;
1e779854
AJ
1279
1280 eeepc_backlight_exit();
1281 eeepc_rfkill_exit();
1282 eeepc_input_exit();
1283 eeepc_hwmon_exit();
1284 sysfs_remove_group(&platform_device->dev.kobj,
1285 &platform_attribute_group);
1286 platform_device_unregister(platform_device);
1287 platform_driver_unregister(&platform_driver);
1288
1289 kfree(ehotk);
1290 return 0;
1291}
1292
1293static int __init eeepc_laptop_init(void)
1294{
1295 int result;
1296
1297 if (acpi_disabled)
1298 return -ENODEV;
1299 result = acpi_bus_register_driver(&eeepc_hotk_driver);
1300 if (result < 0)
1301 return result;
1302 if (!ehotk) {
1303 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1304 return -ENODEV;
1305 }
1306 return 0;
1307}
1308
1309static void __exit eeepc_laptop_exit(void)
1310{
1311 acpi_bus_unregister_driver(&eeepc_hotk_driver);
1312}
1313
e59f8796
EC
1314module_init(eeepc_laptop_init);
1315module_exit(eeepc_laptop_exit);
This page took 0.282149 seconds and 5 git commands to generate.