]> Git Repo - linux.git/blob - drivers/platform/x86/asus-laptop.c
crypto: akcipher - Drop sign/verify operations
[linux.git] / drivers / platform / x86 / asus-laptop.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  asus-laptop.c - Asus Laptop Support
4  *
5  *  Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
6  *  Copyright (C) 2006-2007 Corentin Chary
7  *  Copyright (C) 2011 Wind River Systems
8  *
9  *  The development page for this driver is located at
10  *  http://sourceforge.net/projects/acpi4asus/
11  *
12  *  Credits:
13  *  Pontus Fuchs   - Helper functions, cleanup
14  *  Johann Wiesner - Small compile fixes
15  *  John Belmonte  - ACPI code for Toshiba laptop was a good starting point.
16  *  Eric Burghard  - LED display support for W1N
17  *  Josh Green     - Light Sens support
18  *  Thomas Tuttle  - His first patch for led support was very helpful
19  *  Sam Lin        - GPS support
20  */
21
22 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23
24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <linux/init.h>
27 #include <linux/types.h>
28 #include <linux/err.h>
29 #include <linux/proc_fs.h>
30 #include <linux/backlight.h>
31 #include <linux/leds.h>
32 #include <linux/platform_device.h>
33 #include <linux/uaccess.h>
34 #include <linux/input.h>
35 #include <linux/input/sparse-keymap.h>
36 #include <linux/rfkill.h>
37 #include <linux/slab.h>
38 #include <linux/dmi.h>
39 #include <linux/acpi.h>
40 #include <acpi/video.h>
41
42 #define ASUS_LAPTOP_VERSION     "0.42"
43
44 #define ASUS_LAPTOP_NAME        "Asus Laptop Support"
45 #define ASUS_LAPTOP_CLASS       "hotkey"
46 #define ASUS_LAPTOP_DEVICE_NAME "Hotkey"
47 #define ASUS_LAPTOP_FILE        KBUILD_MODNAME
48 #define ASUS_LAPTOP_PREFIX      "\\_SB.ATKD."
49
50 MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
51 MODULE_DESCRIPTION(ASUS_LAPTOP_NAME);
52 MODULE_LICENSE("GPL");
53
54 /*
55  * WAPF defines the behavior of the Fn+Fx wlan key
56  * The significance of values is yet to be found, but
57  * most of the time:
58  * Bit | Bluetooth | WLAN
59  *  0  | Hardware  | Hardware
60  *  1  | Hardware  | Software
61  *  4  | Software  | Software
62  */
63 static uint wapf = 1;
64 module_param(wapf, uint, 0444);
65 MODULE_PARM_DESC(wapf, "WAPF value");
66
67 static char *wled_type = "unknown";
68 static char *bled_type = "unknown";
69
70 module_param(wled_type, charp, 0444);
71 MODULE_PARM_DESC(wled_type, "Set the wled type on boot "
72                  "(unknown, led or rfkill). "
73                  "default is unknown");
74
75 module_param(bled_type, charp, 0444);
76 MODULE_PARM_DESC(bled_type, "Set the bled type on boot "
77                  "(unknown, led or rfkill). "
78                  "default is unknown");
79
80 static int wlan_status = 1;
81 static int bluetooth_status = 1;
82 static int wimax_status = -1;
83 static int wwan_status = -1;
84 static int als_status;
85
86 module_param(wlan_status, int, 0444);
87 MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot "
88                  "(0 = disabled, 1 = enabled, -1 = don't do anything). "
89                  "default is -1");
90
91 module_param(bluetooth_status, int, 0444);
92 MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot "
93                  "(0 = disabled, 1 = enabled, -1 = don't do anything). "
94                  "default is -1");
95
96 module_param(wimax_status, int, 0444);
97 MODULE_PARM_DESC(wimax_status, "Set the wireless status on boot "
98                  "(0 = disabled, 1 = enabled, -1 = don't do anything). "
99                  "default is -1");
100
101 module_param(wwan_status, int, 0444);
102 MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot "
103                  "(0 = disabled, 1 = enabled, -1 = don't do anything). "
104                  "default is -1");
105
106 module_param(als_status, int, 0444);
107 MODULE_PARM_DESC(als_status, "Set the ALS status on boot "
108                  "(0 = disabled, 1 = enabled). "
109                  "default is 0");
110
111 /*
112  * Some events we use, same for all Asus
113  */
114 #define ATKD_BRNUP_MIN          0x10
115 #define ATKD_BRNUP_MAX          0x1f
116 #define ATKD_BRNDOWN_MIN        0x20
117 #define ATKD_BRNDOWN_MAX        0x2f
118 #define ATKD_BRNDOWN            0x20
119 #define ATKD_BRNUP              0x2f
120 #define ATKD_LCD_ON     0x33
121 #define ATKD_LCD_OFF    0x34
122
123 /*
124  * Known bits returned by \_SB.ATKD.HWRS
125  */
126 #define WL_HWRS         0x80
127 #define BT_HWRS         0x100
128
129 /*
130  * Flags for hotk status
131  * WL_ON and BT_ON are also used for wireless_status()
132  */
133 #define WL_RSTS         0x01    /* internal Wifi */
134 #define BT_RSTS         0x02    /* internal Bluetooth */
135 #define WM_RSTS         0x08    /* internal wimax */
136 #define WW_RSTS         0x20    /* internal wwan */
137
138 /* WLED and BLED type */
139 #define TYPE_UNKNOWN    0
140 #define TYPE_LED        1
141 #define TYPE_RFKILL     2
142
143 /* LED */
144 #define METHOD_MLED             "MLED"
145 #define METHOD_TLED             "TLED"
146 #define METHOD_RLED             "RLED"  /* W1JC */
147 #define METHOD_PLED             "PLED"  /* A7J */
148 #define METHOD_GLED             "GLED"  /* G1, G2 (probably) */
149
150 /* LEDD */
151 #define METHOD_LEDD             "SLCM"
152
153 /*
154  * Bluetooth and WLAN
155  * WLED and BLED are not handled like other XLED, because in some dsdt
156  * they also control the WLAN/Bluetooth device.
157  */
158 #define METHOD_WLAN             "WLED"
159 #define METHOD_BLUETOOTH        "BLED"
160
161 /* WWAN and WIMAX */
162 #define METHOD_WWAN             "GSMC"
163 #define METHOD_WIMAX            "WMXC"
164
165 #define METHOD_WL_STATUS        "RSTS"
166
167 /* Brightness */
168 #define METHOD_BRIGHTNESS_SET   "SPLV"
169 #define METHOD_BRIGHTNESS_GET   "GPLV"
170
171 /* Display */
172 #define METHOD_SWITCH_DISPLAY   "SDSP"
173
174 #define METHOD_ALS_CONTROL      "ALSC" /* Z71A Z71V */
175 #define METHOD_ALS_LEVEL        "ALSL" /* Z71A Z71V */
176
177 /* GPS */
178 /* R2H use different handle for GPS on/off */
179 #define METHOD_GPS_ON           "SDON"
180 #define METHOD_GPS_OFF          "SDOF"
181 #define METHOD_GPS_STATUS       "GPST"
182
183 /* Keyboard light */
184 #define METHOD_KBD_LIGHT_SET    "SLKB"
185 #define METHOD_KBD_LIGHT_GET    "GLKB"
186
187 /* For Pegatron Lucid tablet */
188 #define DEVICE_NAME_PEGA        "Lucid"
189
190 #define METHOD_PEGA_ENABLE      "ENPR"
191 #define METHOD_PEGA_DISABLE     "DAPR"
192 #define PEGA_WLAN       0x00
193 #define PEGA_BLUETOOTH  0x01
194 #define PEGA_WWAN       0x02
195 #define PEGA_ALS        0x04
196 #define PEGA_ALS_POWER  0x05
197
198 #define METHOD_PEGA_READ        "RDLN"
199 #define PEGA_READ_ALS_H 0x02
200 #define PEGA_READ_ALS_L 0x03
201
202 #define PEGA_ACCEL_NAME "pega_accel"
203 #define PEGA_ACCEL_DESC "Pegatron Lucid Tablet Accelerometer"
204 #define METHOD_XLRX "XLRX"
205 #define METHOD_XLRY "XLRY"
206 #define METHOD_XLRZ "XLRZ"
207 #define PEGA_ACC_CLAMP 512 /* 1G accel is reported as ~256, so clamp to 2G */
208 #define PEGA_ACC_RETRIES 3
209
210 /*
211  * Define a specific led structure to keep the main structure clean
212  */
213 struct asus_led {
214         int wk;
215         struct work_struct work;
216         struct led_classdev led;
217         struct asus_laptop *asus;
218         const char *method;
219 };
220
221 /*
222  * Same thing for rfkill
223  */
224 struct asus_rfkill {
225         /* type of control. Maps to PEGA_* values or *_RSTS  */
226         int control_id;
227         struct rfkill *rfkill;
228         struct asus_laptop *asus;
229 };
230
231 /*
232  * This is the main structure, we can use it to store anything interesting
233  * about the hotk device
234  */
235 struct asus_laptop {
236         char *name;             /* laptop name */
237
238         struct acpi_table_header *dsdt_info;
239         struct platform_device *platform_device;
240         struct acpi_device *device;             /* the device we are in */
241         struct backlight_device *backlight_device;
242
243         struct input_dev *inputdev;
244         struct key_entry *keymap;
245         struct input_dev *pega_accel_poll;
246
247         struct asus_led wled;
248         struct asus_led bled;
249         struct asus_led mled;
250         struct asus_led tled;
251         struct asus_led rled;
252         struct asus_led pled;
253         struct asus_led gled;
254         struct asus_led kled;
255         struct workqueue_struct *led_workqueue;
256
257         int wled_type;
258         int bled_type;
259         int wireless_status;
260         bool have_rsts;
261         bool is_pega_lucid;
262         bool pega_acc_live;
263         int pega_acc_x;
264         int pega_acc_y;
265         int pega_acc_z;
266
267         struct asus_rfkill wlan;
268         struct asus_rfkill bluetooth;
269         struct asus_rfkill wwan;
270         struct asus_rfkill wimax;
271         struct asus_rfkill gps;
272
273         acpi_handle handle;     /* the handle of the hotk device */
274         u32 ledd_status;        /* status of the LED display */
275         u8 light_level;         /* light sensor level */
276         u8 light_switch;        /* light sensor switch value */
277         u16 event_count[128];   /* count for each event TODO make this better */
278 };
279
280 static const struct key_entry asus_keymap[] = {
281         /* Lenovo SL Specific keycodes */
282         {KE_KEY, 0x02, { KEY_SCREENLOCK } },
283         {KE_KEY, 0x05, { KEY_WLAN } },
284         {KE_KEY, 0x08, { KEY_F13 } },
285         {KE_KEY, 0x09, { KEY_PROG2 } }, /* Dock */
286         {KE_KEY, 0x17, { KEY_ZOOM } },
287         {KE_KEY, 0x1f, { KEY_BATTERY } },
288         /* End of Lenovo SL Specific keycodes */
289         {KE_KEY, ATKD_BRNDOWN, { KEY_BRIGHTNESSDOWN } },
290         {KE_KEY, ATKD_BRNUP, { KEY_BRIGHTNESSUP } },
291         {KE_KEY, 0x30, { KEY_VOLUMEUP } },
292         {KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
293         {KE_KEY, 0x32, { KEY_MUTE } },
294         {KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */
295         {KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */
296         {KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
297         {KE_KEY, 0x41, { KEY_NEXTSONG } },
298         {KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */
299         {KE_KEY, 0x45, { KEY_PLAYPAUSE } },
300         {KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */
301         {KE_KEY, 0x50, { KEY_EMAIL } },
302         {KE_KEY, 0x51, { KEY_WWW } },
303         {KE_KEY, 0x55, { KEY_CALC } },
304         {KE_IGNORE, 0x57, },  /* Battery mode */
305         {KE_IGNORE, 0x58, },  /* AC mode */
306         {KE_KEY, 0x5C, { KEY_SCREENLOCK } },  /* Screenlock */
307         {KE_KEY, 0x5D, { KEY_WLAN } }, /* WLAN Toggle */
308         {KE_KEY, 0x5E, { KEY_WLAN } }, /* WLAN Enable */
309         {KE_KEY, 0x5F, { KEY_WLAN } }, /* WLAN Disable */
310         {KE_KEY, 0x60, { KEY_TOUCHPAD_ON } },
311         {KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */
312         {KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */
313         {KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */
314         {KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */
315         {KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */
316         {KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */
317         {KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
318         {KE_KEY, 0x6A, { KEY_TOUCHPAD_TOGGLE } }, /* Lock Touchpad Fn + F9 */
319         {KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, /* Lock Touchpad */
320         {KE_KEY, 0x6C, { KEY_SLEEP } }, /* Suspend */
321         {KE_KEY, 0x6D, { KEY_SLEEP } }, /* Hibernate */
322         {KE_IGNORE, 0x6E, },  /* Low Battery notification */
323         {KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
324         {KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
325         {KE_KEY, 0x82, { KEY_CAMERA } },
326         {KE_KEY, 0x88, { KEY_RFKILL  } }, /* Radio Toggle Key */
327         {KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */
328         {KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */
329         {KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */
330         {KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */
331         {KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */
332         {KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */
333         {KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */
334         {KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
335         {KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */
336         {KE_KEY, 0x95, { KEY_MEDIA } },
337         {KE_KEY, 0x99, { KEY_PHONE } },
338         {KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
339         {KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
340         {KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
341         {KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */
342         {KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */
343         {KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */
344         {KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */
345         {KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */
346         {KE_KEY, 0xB5, { KEY_CALC } },
347         {KE_KEY, 0xC4, { KEY_KBDILLUMUP } },
348         {KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },
349         {KE_END, 0},
350 };
351
352
353 /*
354  * This function evaluates an ACPI method, given an int as parameter, the
355  * method is searched within the scope of the handle, can be NULL. The output
356  * of the method is written is output, which can also be NULL
357  *
358  * returns 0 if write is successful, -1 else.
359  */
360 static int write_acpi_int_ret(acpi_handle handle, const char *method, int val,
361                               struct acpi_buffer *output)
362 {
363         struct acpi_object_list params; /* list of input parameters (an int) */
364         union acpi_object in_obj;       /* the only param we use */
365         acpi_status status;
366
367         if (!handle)
368                 return -1;
369
370         params.count = 1;
371         params.pointer = &in_obj;
372         in_obj.type = ACPI_TYPE_INTEGER;
373         in_obj.integer.value = val;
374
375         status = acpi_evaluate_object(handle, (char *)method, &params, output);
376         if (status == AE_OK)
377                 return 0;
378         else
379                 return -1;
380 }
381
382 static int write_acpi_int(acpi_handle handle, const char *method, int val)
383 {
384         return write_acpi_int_ret(handle, method, val, NULL);
385 }
386
387 static int acpi_check_handle(acpi_handle handle, const char *method,
388                              acpi_handle *ret)
389 {
390         acpi_status status;
391
392         if (method == NULL)
393                 return -ENODEV;
394
395         if (ret)
396                 status = acpi_get_handle(handle, (char *)method,
397                                          ret);
398         else {
399                 acpi_handle dummy;
400
401                 status = acpi_get_handle(handle, (char *)method,
402                                          &dummy);
403         }
404
405         if (status != AE_OK) {
406                 if (ret)
407                         pr_warn("Error finding %s\n", method);
408                 return -ENODEV;
409         }
410         return 0;
411 }
412
413 static bool asus_check_pega_lucid(struct asus_laptop *asus)
414 {
415         return !strcmp(asus->name, DEVICE_NAME_PEGA) &&
416            !acpi_check_handle(asus->handle, METHOD_PEGA_ENABLE, NULL) &&
417            !acpi_check_handle(asus->handle, METHOD_PEGA_DISABLE, NULL) &&
418            !acpi_check_handle(asus->handle, METHOD_PEGA_READ, NULL);
419 }
420
421 static int asus_pega_lucid_set(struct asus_laptop *asus, int unit, bool enable)
422 {
423         char *method = enable ? METHOD_PEGA_ENABLE : METHOD_PEGA_DISABLE;
424         return write_acpi_int(asus->handle, method, unit);
425 }
426
427 static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method)
428 {
429         int i, delta;
430         unsigned long long val;
431         for (i = 0; i < PEGA_ACC_RETRIES; i++) {
432                 acpi_evaluate_integer(asus->handle, method, NULL, &val);
433
434                 /* The output is noisy.  From reading the ASL
435                  * dissassembly, timeout errors are returned with 1's
436                  * in the high word, and the lack of locking around
437                  * thei hi/lo byte reads means that a transition
438                  * between (for example) -1 and 0 could be read as
439                  * 0xff00 or 0x00ff. */
440                 delta = abs(curr - (short)val);
441                 if (delta < 128 && !(val & ~0xffff))
442                         break;
443         }
444         return clamp_val((short)val, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP);
445 }
446
447 static void pega_accel_poll(struct input_dev *input)
448 {
449         struct device *parent = input->dev.parent;
450         struct asus_laptop *asus = dev_get_drvdata(parent);
451
452         /* In some cases, the very first call to poll causes a
453          * recursive fault under the polldev worker.  This is
454          * apparently related to very early userspace access to the
455          * device, and perhaps a firmware bug. Fake the first report. */
456         if (!asus->pega_acc_live) {
457                 asus->pega_acc_live = true;
458                 input_report_abs(input, ABS_X, 0);
459                 input_report_abs(input, ABS_Y, 0);
460                 input_report_abs(input, ABS_Z, 0);
461                 input_sync(input);
462                 return;
463         }
464
465         asus->pega_acc_x = pega_acc_axis(asus, asus->pega_acc_x, METHOD_XLRX);
466         asus->pega_acc_y = pega_acc_axis(asus, asus->pega_acc_y, METHOD_XLRY);
467         asus->pega_acc_z = pega_acc_axis(asus, asus->pega_acc_z, METHOD_XLRZ);
468
469         /* Note transform, convert to "right/up/out" in the native
470          * landscape orientation (i.e. the vector is the direction of
471          * "real up" in the device's cartiesian coordinates). */
472         input_report_abs(input, ABS_X, -asus->pega_acc_x);
473         input_report_abs(input, ABS_Y, -asus->pega_acc_y);
474         input_report_abs(input, ABS_Z,  asus->pega_acc_z);
475         input_sync(input);
476 }
477
478 static void pega_accel_exit(struct asus_laptop *asus)
479 {
480         if (asus->pega_accel_poll) {
481                 input_unregister_device(asus->pega_accel_poll);
482                 asus->pega_accel_poll = NULL;
483         }
484 }
485
486 static int pega_accel_init(struct asus_laptop *asus)
487 {
488         int err;
489         struct input_dev *input;
490
491         if (!asus->is_pega_lucid)
492                 return -ENODEV;
493
494         if (acpi_check_handle(asus->handle, METHOD_XLRX, NULL) ||
495             acpi_check_handle(asus->handle, METHOD_XLRY, NULL) ||
496             acpi_check_handle(asus->handle, METHOD_XLRZ, NULL))
497                 return -ENODEV;
498
499         input = input_allocate_device();
500         if (!input)
501                 return -ENOMEM;
502
503         input->name = PEGA_ACCEL_DESC;
504         input->phys = PEGA_ACCEL_NAME "/input0";
505         input->dev.parent = &asus->platform_device->dev;
506         input->id.bustype = BUS_HOST;
507
508         input_set_abs_params(input, ABS_X,
509                              -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
510         input_set_abs_params(input, ABS_Y,
511                              -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
512         input_set_abs_params(input, ABS_Z,
513                              -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
514
515         err = input_setup_polling(input, pega_accel_poll);
516         if (err)
517                 goto exit;
518
519         input_set_poll_interval(input, 125);
520         input_set_min_poll_interval(input, 50);
521         input_set_max_poll_interval(input, 2000);
522
523         err = input_register_device(input);
524         if (err)
525                 goto exit;
526
527         asus->pega_accel_poll = input;
528         return 0;
529
530 exit:
531         input_free_device(input);
532         return err;
533 }
534
535 /* Generic LED function */
536 static int asus_led_set(struct asus_laptop *asus, const char *method,
537                          int value)
538 {
539         if (!strcmp(method, METHOD_MLED))
540                 value = !value;
541         else if (!strcmp(method, METHOD_GLED))
542                 value = !value + 1;
543         else
544                 value = !!value;
545
546         return write_acpi_int(asus->handle, method, value);
547 }
548
549 /*
550  * LEDs
551  */
552 /* /sys/class/led handlers */
553 static void asus_led_cdev_set(struct led_classdev *led_cdev,
554                          enum led_brightness value)
555 {
556         struct asus_led *led = container_of(led_cdev, struct asus_led, led);
557         struct asus_laptop *asus = led->asus;
558
559         led->wk = !!value;
560         queue_work(asus->led_workqueue, &led->work);
561 }
562
563 static void asus_led_cdev_update(struct work_struct *work)
564 {
565         struct asus_led *led = container_of(work, struct asus_led, work);
566         struct asus_laptop *asus = led->asus;
567
568         asus_led_set(asus, led->method, led->wk);
569 }
570
571 static enum led_brightness asus_led_cdev_get(struct led_classdev *led_cdev)
572 {
573         return led_cdev->brightness;
574 }
575
576 /*
577  * Keyboard backlight (also a LED)
578  */
579 static int asus_kled_lvl(struct asus_laptop *asus)
580 {
581         unsigned long long kblv;
582         struct acpi_object_list params;
583         union acpi_object in_obj;
584         acpi_status rv;
585
586         params.count = 1;
587         params.pointer = &in_obj;
588         in_obj.type = ACPI_TYPE_INTEGER;
589         in_obj.integer.value = 2;
590
591         rv = acpi_evaluate_integer(asus->handle, METHOD_KBD_LIGHT_GET,
592                                    &params, &kblv);
593         if (ACPI_FAILURE(rv)) {
594                 pr_warn("Error reading kled level\n");
595                 return -ENODEV;
596         }
597         return kblv;
598 }
599
600 static int asus_kled_set(struct asus_laptop *asus, int kblv)
601 {
602         if (kblv > 0)
603                 kblv = (1 << 7) | (kblv & 0x7F);
604         else
605                 kblv = 0;
606
607         if (write_acpi_int(asus->handle, METHOD_KBD_LIGHT_SET, kblv)) {
608                 pr_warn("Keyboard LED display write failed\n");
609                 return -EINVAL;
610         }
611         return 0;
612 }
613
614 static void asus_kled_cdev_set(struct led_classdev *led_cdev,
615                               enum led_brightness value)
616 {
617         struct asus_led *led = container_of(led_cdev, struct asus_led, led);
618         struct asus_laptop *asus = led->asus;
619
620         led->wk = value;
621         queue_work(asus->led_workqueue, &led->work);
622 }
623
624 static void asus_kled_cdev_update(struct work_struct *work)
625 {
626         struct asus_led *led = container_of(work, struct asus_led, work);
627         struct asus_laptop *asus = led->asus;
628
629         asus_kled_set(asus, led->wk);
630 }
631
632 static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev)
633 {
634         struct asus_led *led = container_of(led_cdev, struct asus_led, led);
635         struct asus_laptop *asus = led->asus;
636
637         return asus_kled_lvl(asus);
638 }
639
640 static void asus_led_exit(struct asus_laptop *asus)
641 {
642         led_classdev_unregister(&asus->wled.led);
643         led_classdev_unregister(&asus->bled.led);
644         led_classdev_unregister(&asus->mled.led);
645         led_classdev_unregister(&asus->tled.led);
646         led_classdev_unregister(&asus->pled.led);
647         led_classdev_unregister(&asus->rled.led);
648         led_classdev_unregister(&asus->gled.led);
649         led_classdev_unregister(&asus->kled.led);
650
651         if (asus->led_workqueue) {
652                 destroy_workqueue(asus->led_workqueue);
653                 asus->led_workqueue = NULL;
654         }
655 }
656
657 /*  Ugly macro, need to fix that later */
658 static int asus_led_register(struct asus_laptop *asus,
659                              struct asus_led *led,
660                              const char *name, const char *method)
661 {
662         struct led_classdev *led_cdev = &led->led;
663
664         if (!method || acpi_check_handle(asus->handle, method, NULL))
665                 return 0; /* Led not present */
666
667         led->asus = asus;
668         led->method = method;
669
670         INIT_WORK(&led->work, asus_led_cdev_update);
671         led_cdev->name = name;
672         led_cdev->brightness_set = asus_led_cdev_set;
673         led_cdev->brightness_get = asus_led_cdev_get;
674         led_cdev->max_brightness = 1;
675         return led_classdev_register(&asus->platform_device->dev, led_cdev);
676 }
677
678 static int asus_led_init(struct asus_laptop *asus)
679 {
680         int r = 0;
681
682         /*
683          * The Pegatron Lucid has no physical leds, but all methods are
684          * available in the DSDT...
685          */
686         if (asus->is_pega_lucid)
687                 return 0;
688
689         /*
690          * Functions that actually update the LED's are called from a
691          * workqueue. By doing this as separate work rather than when the LED
692          * subsystem asks, we avoid messing with the Asus ACPI stuff during a
693          * potentially bad time, such as a timer interrupt.
694          */
695         asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
696         if (!asus->led_workqueue)
697                 return -ENOMEM;
698
699         if (asus->wled_type == TYPE_LED)
700                 r = asus_led_register(asus, &asus->wled, "asus::wlan",
701                                       METHOD_WLAN);
702         if (r)
703                 goto error;
704         if (asus->bled_type == TYPE_LED)
705                 r = asus_led_register(asus, &asus->bled, "asus::bluetooth",
706                                       METHOD_BLUETOOTH);
707         if (r)
708                 goto error;
709         r = asus_led_register(asus, &asus->mled, "asus::mail", METHOD_MLED);
710         if (r)
711                 goto error;
712         r = asus_led_register(asus, &asus->tled, "asus::touchpad", METHOD_TLED);
713         if (r)
714                 goto error;
715         r = asus_led_register(asus, &asus->rled, "asus::record", METHOD_RLED);
716         if (r)
717                 goto error;
718         r = asus_led_register(asus, &asus->pled, "asus::phone", METHOD_PLED);
719         if (r)
720                 goto error;
721         r = asus_led_register(asus, &asus->gled, "asus::gaming", METHOD_GLED);
722         if (r)
723                 goto error;
724         if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL) &&
725             !acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_GET, NULL)) {
726                 struct asus_led *led = &asus->kled;
727                 struct led_classdev *cdev = &led->led;
728
729                 led->asus = asus;
730
731                 INIT_WORK(&led->work, asus_kled_cdev_update);
732                 cdev->name = "asus::kbd_backlight";
733                 cdev->brightness_set = asus_kled_cdev_set;
734                 cdev->brightness_get = asus_kled_cdev_get;
735                 cdev->max_brightness = 3;
736                 r = led_classdev_register(&asus->platform_device->dev, cdev);
737         }
738 error:
739         if (r)
740                 asus_led_exit(asus);
741         return r;
742 }
743
744 /*
745  * Backlight device
746  */
747 static int asus_read_brightness(struct backlight_device *bd)
748 {
749         struct asus_laptop *asus = bl_get_data(bd);
750         unsigned long long value;
751         acpi_status rv;
752
753         rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET,
754                                    NULL, &value);
755         if (ACPI_FAILURE(rv)) {
756                 pr_warn("Error reading brightness\n");
757                 return 0;
758         }
759
760         return value;
761 }
762
763 static int asus_set_brightness(struct backlight_device *bd, int value)
764 {
765         struct asus_laptop *asus = bl_get_data(bd);
766
767         if (write_acpi_int(asus->handle, METHOD_BRIGHTNESS_SET, value)) {
768                 pr_warn("Error changing brightness\n");
769                 return -EIO;
770         }
771         return 0;
772 }
773
774 static int update_bl_status(struct backlight_device *bd)
775 {
776         int value = bd->props.brightness;
777
778         return asus_set_brightness(bd, value);
779 }
780
781 static const struct backlight_ops asusbl_ops = {
782         .get_brightness = asus_read_brightness,
783         .update_status = update_bl_status,
784 };
785
786 static int asus_backlight_notify(struct asus_laptop *asus)
787 {
788         struct backlight_device *bd = asus->backlight_device;
789         int old = bd->props.brightness;
790
791         backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
792
793         return old;
794 }
795
796 static int asus_backlight_init(struct asus_laptop *asus)
797 {
798         struct backlight_device *bd;
799         struct backlight_properties props;
800
801         if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) ||
802             acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL))
803                 return 0;
804
805         memset(&props, 0, sizeof(struct backlight_properties));
806         props.max_brightness = 15;
807         props.type = BACKLIGHT_PLATFORM;
808
809         bd = backlight_device_register(ASUS_LAPTOP_FILE,
810                                        &asus->platform_device->dev, asus,
811                                        &asusbl_ops, &props);
812         if (IS_ERR(bd)) {
813                 pr_err("Could not register asus backlight device\n");
814                 asus->backlight_device = NULL;
815                 return PTR_ERR(bd);
816         }
817
818         asus->backlight_device = bd;
819         bd->props.brightness = asus_read_brightness(bd);
820         bd->props.power = BACKLIGHT_POWER_ON;
821         backlight_update_status(bd);
822         return 0;
823 }
824
825 static void asus_backlight_exit(struct asus_laptop *asus)
826 {
827         backlight_device_unregister(asus->backlight_device);
828         asus->backlight_device = NULL;
829 }
830
831 /*
832  * Platform device handlers
833  */
834
835 /*
836  * We write our info in page, we begin at offset off and cannot write more
837  * than count bytes. We set eof to 1 if we handle those 2 values. We return the
838  * number of bytes written in page
839  */
840 static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
841                           char *page)
842 {
843         struct asus_laptop *asus = dev_get_drvdata(dev);
844         int len = 0;
845         unsigned long long temp;
846         char buf[16];           /* enough for all info */
847         acpi_status rv;
848
849         /*
850          * We use the easy way, we don't care of off and count,
851          * so we don't set eof to 1
852          */
853
854         len += sysfs_emit_at(page, len, ASUS_LAPTOP_NAME " " ASUS_LAPTOP_VERSION "\n");
855         len += sysfs_emit_at(page, len, "Model reference    : %s\n", asus->name);
856         /*
857          * The SFUN method probably allows the original driver to get the list
858          * of features supported by a given model. For now, 0x0100 or 0x0800
859          * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
860          * The significance of others is yet to be found.
861          */
862         rv = acpi_evaluate_integer(asus->handle, "SFUN", NULL, &temp);
863         if (ACPI_SUCCESS(rv))
864                 len += sysfs_emit_at(page, len, "SFUN value         : %#x\n",
865                                (uint) temp);
866         /*
867          * The HWRS method return informations about the hardware.
868          * 0x80 bit is for WLAN, 0x100 for Bluetooth.
869          * 0x40 for WWAN, 0x10 for WIMAX.
870          * The significance of others is yet to be found.
871          * We don't currently use this for device detection, and it
872          * takes several seconds to run on some systems.
873          */
874         rv = acpi_evaluate_integer(asus->handle, "HWRS", NULL, &temp);
875         if (ACPI_SUCCESS(rv))
876                 len += sysfs_emit_at(page, len, "HWRS value         : %#x\n",
877                                (uint) temp);
878         /*
879          * Another value for userspace: the ASYM method returns 0x02 for
880          * battery low and 0x04 for battery critical, its readings tend to be
881          * more accurate than those provided by _BST.
882          * Note: since not all the laptops provide this method, errors are
883          * silently ignored.
884          */
885         rv = acpi_evaluate_integer(asus->handle, "ASYM", NULL, &temp);
886         if (ACPI_SUCCESS(rv))
887                 len += sysfs_emit_at(page, len, "ASYM value         : %#x\n",
888                                (uint) temp);
889         if (asus->dsdt_info) {
890                 snprintf(buf, 16, "%d", asus->dsdt_info->length);
891                 len += sysfs_emit_at(page, len, "DSDT length        : %s\n", buf);
892                 snprintf(buf, 16, "%d", asus->dsdt_info->checksum);
893                 len += sysfs_emit_at(page, len, "DSDT checksum      : %s\n", buf);
894                 snprintf(buf, 16, "%d", asus->dsdt_info->revision);
895                 len += sysfs_emit_at(page, len, "DSDT revision      : %s\n", buf);
896                 snprintf(buf, 7, "%s", asus->dsdt_info->oem_id);
897                 len += sysfs_emit_at(page, len, "OEM id             : %s\n", buf);
898                 snprintf(buf, 9, "%s", asus->dsdt_info->oem_table_id);
899                 len += sysfs_emit_at(page, len, "OEM table id       : %s\n", buf);
900                 snprintf(buf, 16, "%x", asus->dsdt_info->oem_revision);
901                 len += sysfs_emit_at(page, len, "OEM revision       : 0x%s\n", buf);
902                 snprintf(buf, 5, "%s", asus->dsdt_info->asl_compiler_id);
903                 len += sysfs_emit_at(page, len, "ASL comp vendor id : %s\n", buf);
904                 snprintf(buf, 16, "%x", asus->dsdt_info->asl_compiler_revision);
905                 len += sysfs_emit_at(page, len, "ASL comp revision  : 0x%s\n", buf);
906         }
907
908         return len;
909 }
910 static DEVICE_ATTR_RO(infos);
911
912 static ssize_t sysfs_acpi_set(struct asus_laptop *asus,
913                               const char *buf, size_t count,
914                               const char *method)
915 {
916         int rv, value;
917
918         rv = kstrtoint(buf, 0, &value);
919         if (rv < 0)
920                 return rv;
921
922         if (write_acpi_int(asus->handle, method, value))
923                 return -ENODEV;
924         return count;
925 }
926
927 /*
928  * LEDD display
929  */
930 static ssize_t ledd_show(struct device *dev, struct device_attribute *attr,
931                          char *buf)
932 {
933         struct asus_laptop *asus = dev_get_drvdata(dev);
934
935         return sysfs_emit(buf, "0x%08x\n", asus->ledd_status);
936 }
937
938 static ssize_t ledd_store(struct device *dev, struct device_attribute *attr,
939                           const char *buf, size_t count)
940 {
941         struct asus_laptop *asus = dev_get_drvdata(dev);
942         int rv, value;
943
944         rv = kstrtoint(buf, 0, &value);
945         if (rv < 0)
946                 return rv;
947
948         if (write_acpi_int(asus->handle, METHOD_LEDD, value)) {
949                 pr_warn("LED display write failed\n");
950                 return -ENODEV;
951         }
952
953         asus->ledd_status = (u32) value;
954         return count;
955 }
956 static DEVICE_ATTR_RW(ledd);
957
958 /*
959  * Wireless
960  */
961 static int asus_wireless_status(struct asus_laptop *asus, int mask)
962 {
963         unsigned long long status;
964         acpi_status rv = AE_OK;
965
966         if (!asus->have_rsts)
967                 return (asus->wireless_status & mask) ? 1 : 0;
968
969         rv = acpi_evaluate_integer(asus->handle, METHOD_WL_STATUS,
970                                    NULL, &status);
971         if (ACPI_FAILURE(rv)) {
972                 pr_warn("Error reading Wireless status\n");
973                 return -EINVAL;
974         }
975         return !!(status & mask);
976 }
977
978 /*
979  * WLAN
980  */
981 static int asus_wlan_set(struct asus_laptop *asus, int status)
982 {
983         if (write_acpi_int(asus->handle, METHOD_WLAN, !!status)) {
984                 pr_warn("Error setting wlan status to %d\n", status);
985                 return -EIO;
986         }
987         return 0;
988 }
989
990 static ssize_t wlan_show(struct device *dev, struct device_attribute *attr,
991                          char *buf)
992 {
993         struct asus_laptop *asus = dev_get_drvdata(dev);
994
995         return sysfs_emit(buf, "%d\n", asus_wireless_status(asus, WL_RSTS));
996 }
997
998 static ssize_t wlan_store(struct device *dev, struct device_attribute *attr,
999                           const char *buf, size_t count)
1000 {
1001         struct asus_laptop *asus = dev_get_drvdata(dev);
1002
1003         return sysfs_acpi_set(asus, buf, count, METHOD_WLAN);
1004 }
1005 static DEVICE_ATTR_RW(wlan);
1006
1007 /*e
1008  * Bluetooth
1009  */
1010 static int asus_bluetooth_set(struct asus_laptop *asus, int status)
1011 {
1012         if (write_acpi_int(asus->handle, METHOD_BLUETOOTH, !!status)) {
1013                 pr_warn("Error setting bluetooth status to %d\n", status);
1014                 return -EIO;
1015         }
1016         return 0;
1017 }
1018
1019 static ssize_t bluetooth_show(struct device *dev, struct device_attribute *attr,
1020                               char *buf)
1021 {
1022         struct asus_laptop *asus = dev_get_drvdata(dev);
1023
1024         return sysfs_emit(buf, "%d\n", asus_wireless_status(asus, BT_RSTS));
1025 }
1026
1027 static ssize_t bluetooth_store(struct device *dev,
1028                                struct device_attribute *attr, const char *buf,
1029                                size_t count)
1030 {
1031         struct asus_laptop *asus = dev_get_drvdata(dev);
1032
1033         return sysfs_acpi_set(asus, buf, count, METHOD_BLUETOOTH);
1034 }
1035 static DEVICE_ATTR_RW(bluetooth);
1036
1037 /*
1038  * Wimax
1039  */
1040 static int asus_wimax_set(struct asus_laptop *asus, int status)
1041 {
1042         if (write_acpi_int(asus->handle, METHOD_WIMAX, !!status)) {
1043                 pr_warn("Error setting wimax status to %d\n", status);
1044                 return -EIO;
1045         }
1046         return 0;
1047 }
1048
1049 static ssize_t wimax_show(struct device *dev, struct device_attribute *attr,
1050                           char *buf)
1051 {
1052         struct asus_laptop *asus = dev_get_drvdata(dev);
1053
1054         return sysfs_emit(buf, "%d\n", asus_wireless_status(asus, WM_RSTS));
1055 }
1056
1057 static ssize_t wimax_store(struct device *dev, struct device_attribute *attr,
1058                            const char *buf, size_t count)
1059 {
1060         struct asus_laptop *asus = dev_get_drvdata(dev);
1061
1062         return sysfs_acpi_set(asus, buf, count, METHOD_WIMAX);
1063 }
1064 static DEVICE_ATTR_RW(wimax);
1065
1066 /*
1067  * Wwan
1068  */
1069 static int asus_wwan_set(struct asus_laptop *asus, int status)
1070 {
1071         if (write_acpi_int(asus->handle, METHOD_WWAN, !!status)) {
1072                 pr_warn("Error setting wwan status to %d\n", status);
1073                 return -EIO;
1074         }
1075         return 0;
1076 }
1077
1078 static ssize_t wwan_show(struct device *dev, struct device_attribute *attr,
1079                          char *buf)
1080 {
1081         struct asus_laptop *asus = dev_get_drvdata(dev);
1082
1083         return sysfs_emit(buf, "%d\n", asus_wireless_status(asus, WW_RSTS));
1084 }
1085
1086 static ssize_t wwan_store(struct device *dev, struct device_attribute *attr,
1087                           const char *buf, size_t count)
1088 {
1089         struct asus_laptop *asus = dev_get_drvdata(dev);
1090
1091         return sysfs_acpi_set(asus, buf, count, METHOD_WWAN);
1092 }
1093 static DEVICE_ATTR_RW(wwan);
1094
1095 /*
1096  * Display
1097  */
1098 static void asus_set_display(struct asus_laptop *asus, int value)
1099 {
1100         /* no sanity check needed for now */
1101         if (write_acpi_int(asus->handle, METHOD_SWITCH_DISPLAY, value))
1102                 pr_warn("Error setting display\n");
1103         return;
1104 }
1105
1106 /*
1107  * Experimental support for display switching. As of now: 1 should activate
1108  * the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI.
1109  * Any combination (bitwise) of these will suffice. I never actually tested 4
1110  * displays hooked up simultaneously, so be warned. See the acpi4asus README
1111  * for more info.
1112  */
1113 static ssize_t display_store(struct device *dev, struct device_attribute *attr,
1114                              const char *buf, size_t count)
1115 {
1116         struct asus_laptop *asus = dev_get_drvdata(dev);
1117         int rv, value;
1118
1119         rv = kstrtoint(buf, 0, &value);
1120         if (rv < 0)
1121                 return rv;
1122
1123         asus_set_display(asus, value);
1124         return count;
1125 }
1126 static DEVICE_ATTR_WO(display);
1127
1128 /*
1129  * Light Sens
1130  */
1131 static void asus_als_switch(struct asus_laptop *asus, int value)
1132 {
1133         int ret;
1134
1135         if (asus->is_pega_lucid) {
1136                 ret = asus_pega_lucid_set(asus, PEGA_ALS, value);
1137                 if (!ret)
1138                         ret = asus_pega_lucid_set(asus, PEGA_ALS_POWER, value);
1139         } else {
1140                 ret = write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value);
1141         }
1142         if (ret)
1143                 pr_warn("Error setting light sensor switch\n");
1144
1145         asus->light_switch = value;
1146 }
1147
1148 static ssize_t ls_switch_show(struct device *dev, struct device_attribute *attr,
1149                               char *buf)
1150 {
1151         struct asus_laptop *asus = dev_get_drvdata(dev);
1152
1153         return sysfs_emit(buf, "%d\n", asus->light_switch);
1154 }
1155
1156 static ssize_t ls_switch_store(struct device *dev,
1157                                struct device_attribute *attr, const char *buf,
1158                                size_t count)
1159 {
1160         struct asus_laptop *asus = dev_get_drvdata(dev);
1161         int rv, value;
1162
1163         rv = kstrtoint(buf, 0, &value);
1164         if (rv < 0)
1165                 return rv;
1166
1167         asus_als_switch(asus, value ? 1 : 0);
1168         return count;
1169 }
1170 static DEVICE_ATTR_RW(ls_switch);
1171
1172 static void asus_als_level(struct asus_laptop *asus, int value)
1173 {
1174         if (write_acpi_int(asus->handle, METHOD_ALS_LEVEL, value))
1175                 pr_warn("Error setting light sensor level\n");
1176         asus->light_level = value;
1177 }
1178
1179 static ssize_t ls_level_show(struct device *dev, struct device_attribute *attr,
1180                              char *buf)
1181 {
1182         struct asus_laptop *asus = dev_get_drvdata(dev);
1183
1184         return sysfs_emit(buf, "%d\n", asus->light_level);
1185 }
1186
1187 static ssize_t ls_level_store(struct device *dev, struct device_attribute *attr,
1188                               const char *buf, size_t count)
1189 {
1190         struct asus_laptop *asus = dev_get_drvdata(dev);
1191         int rv, value;
1192
1193         rv = kstrtoint(buf, 0, &value);
1194         if (rv < 0)
1195                 return rv;
1196
1197         value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
1198         /* 0 <= value <= 15 */
1199         asus_als_level(asus, value);
1200
1201         return count;
1202 }
1203 static DEVICE_ATTR_RW(ls_level);
1204
1205 static int pega_int_read(struct asus_laptop *asus, int arg, int *result)
1206 {
1207         struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1208         int err = write_acpi_int_ret(asus->handle, METHOD_PEGA_READ, arg,
1209                                      &buffer);
1210         if (!err) {
1211                 union acpi_object *obj = buffer.pointer;
1212                 if (obj && obj->type == ACPI_TYPE_INTEGER)
1213                         *result = obj->integer.value;
1214                 else
1215                         err = -EIO;
1216         }
1217         return err;
1218 }
1219
1220 static ssize_t ls_value_show(struct device *dev, struct device_attribute *attr,
1221                              char *buf)
1222 {
1223         struct asus_laptop *asus = dev_get_drvdata(dev);
1224         int err, hi, lo;
1225
1226         err = pega_int_read(asus, PEGA_READ_ALS_H, &hi);
1227         if (!err)
1228                 err = pega_int_read(asus, PEGA_READ_ALS_L, &lo);
1229         if (!err)
1230                 return sysfs_emit(buf, "%d\n", 10 * hi + lo);
1231         return err;
1232 }
1233 static DEVICE_ATTR_RO(ls_value);
1234
1235 /*
1236  * GPS
1237  */
1238 static int asus_gps_status(struct asus_laptop *asus)
1239 {
1240         unsigned long long status;
1241         acpi_status rv;
1242
1243         rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS,
1244                                    NULL, &status);
1245         if (ACPI_FAILURE(rv)) {
1246                 pr_warn("Error reading GPS status\n");
1247                 return -ENODEV;
1248         }
1249         return !!status;
1250 }
1251
1252 static int asus_gps_switch(struct asus_laptop *asus, int status)
1253 {
1254         const char *meth = status ? METHOD_GPS_ON : METHOD_GPS_OFF;
1255
1256         if (write_acpi_int(asus->handle, meth, 0x02))
1257                 return -ENODEV;
1258         return 0;
1259 }
1260
1261 static ssize_t gps_show(struct device *dev, struct device_attribute *attr,
1262                         char *buf)
1263 {
1264         struct asus_laptop *asus = dev_get_drvdata(dev);
1265
1266         return sysfs_emit(buf, "%d\n", asus_gps_status(asus));
1267 }
1268
1269 static ssize_t gps_store(struct device *dev, struct device_attribute *attr,
1270                          const char *buf, size_t count)
1271 {
1272         struct asus_laptop *asus = dev_get_drvdata(dev);
1273         int rv, value;
1274         int ret;
1275
1276         rv = kstrtoint(buf, 0, &value);
1277         if (rv < 0)
1278                 return rv;
1279         ret = asus_gps_switch(asus, !!value);
1280         if (ret)
1281                 return ret;
1282         rfkill_set_sw_state(asus->gps.rfkill, !value);
1283         return count;
1284 }
1285 static DEVICE_ATTR_RW(gps);
1286
1287 /*
1288  * rfkill
1289  */
1290 static int asus_gps_rfkill_set(void *data, bool blocked)
1291 {
1292         struct asus_laptop *asus = data;
1293
1294         return asus_gps_switch(asus, !blocked);
1295 }
1296
1297 static const struct rfkill_ops asus_gps_rfkill_ops = {
1298         .set_block = asus_gps_rfkill_set,
1299 };
1300
1301 static int asus_rfkill_set(void *data, bool blocked)
1302 {
1303         struct asus_rfkill *rfk = data;
1304         struct asus_laptop *asus = rfk->asus;
1305
1306         if (rfk->control_id == WL_RSTS)
1307                 return asus_wlan_set(asus, !blocked);
1308         else if (rfk->control_id == BT_RSTS)
1309                 return asus_bluetooth_set(asus, !blocked);
1310         else if (rfk->control_id == WM_RSTS)
1311                 return asus_wimax_set(asus, !blocked);
1312         else if (rfk->control_id == WW_RSTS)
1313                 return asus_wwan_set(asus, !blocked);
1314
1315         return -EINVAL;
1316 }
1317
1318 static const struct rfkill_ops asus_rfkill_ops = {
1319         .set_block = asus_rfkill_set,
1320 };
1321
1322 static void asus_rfkill_terminate(struct asus_rfkill *rfk)
1323 {
1324         if (!rfk->rfkill)
1325                 return ;
1326
1327         rfkill_unregister(rfk->rfkill);
1328         rfkill_destroy(rfk->rfkill);
1329         rfk->rfkill = NULL;
1330 }
1331
1332 static void asus_rfkill_exit(struct asus_laptop *asus)
1333 {
1334         asus_rfkill_terminate(&asus->wwan);
1335         asus_rfkill_terminate(&asus->bluetooth);
1336         asus_rfkill_terminate(&asus->wlan);
1337         asus_rfkill_terminate(&asus->gps);
1338 }
1339
1340 static int asus_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *rfk,
1341                              const char *name, int control_id, int type,
1342                              const struct rfkill_ops *ops)
1343 {
1344         int result;
1345
1346         rfk->control_id = control_id;
1347         rfk->asus = asus;
1348         rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev,
1349                                    type, ops, rfk);
1350         if (!rfk->rfkill)
1351                 return -EINVAL;
1352
1353         result = rfkill_register(rfk->rfkill);
1354         if (result) {
1355                 rfkill_destroy(rfk->rfkill);
1356                 rfk->rfkill = NULL;
1357         }
1358
1359         return result;
1360 }
1361
1362 static int asus_rfkill_init(struct asus_laptop *asus)
1363 {
1364         int result = 0;
1365
1366         if (asus->is_pega_lucid)
1367                 return -ENODEV;
1368
1369         if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) &&
1370             !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) &&
1371             !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL))
1372                 result = asus_rfkill_setup(asus, &asus->gps, "asus-gps",
1373                                            -1, RFKILL_TYPE_GPS,
1374                                            &asus_gps_rfkill_ops);
1375         if (result)
1376                 goto exit;
1377
1378
1379         if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL) &&
1380             asus->wled_type == TYPE_RFKILL)
1381                 result = asus_rfkill_setup(asus, &asus->wlan, "asus-wlan",
1382                                            WL_RSTS, RFKILL_TYPE_WLAN,
1383                                            &asus_rfkill_ops);
1384         if (result)
1385                 goto exit;
1386
1387         if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL) &&
1388             asus->bled_type == TYPE_RFKILL)
1389                 result = asus_rfkill_setup(asus, &asus->bluetooth,
1390                                            "asus-bluetooth", BT_RSTS,
1391                                            RFKILL_TYPE_BLUETOOTH,
1392                                            &asus_rfkill_ops);
1393         if (result)
1394                 goto exit;
1395
1396         if (!acpi_check_handle(asus->handle, METHOD_WWAN, NULL))
1397                 result = asus_rfkill_setup(asus, &asus->wwan, "asus-wwan",
1398                                            WW_RSTS, RFKILL_TYPE_WWAN,
1399                                            &asus_rfkill_ops);
1400         if (result)
1401                 goto exit;
1402
1403         if (!acpi_check_handle(asus->handle, METHOD_WIMAX, NULL))
1404                 result = asus_rfkill_setup(asus, &asus->wimax, "asus-wimax",
1405                                            WM_RSTS, RFKILL_TYPE_WIMAX,
1406                                            &asus_rfkill_ops);
1407         if (result)
1408                 goto exit;
1409
1410 exit:
1411         if (result)
1412                 asus_rfkill_exit(asus);
1413
1414         return result;
1415 }
1416
1417 static int pega_rfkill_set(void *data, bool blocked)
1418 {
1419         struct asus_rfkill *rfk = data;
1420
1421         int ret = asus_pega_lucid_set(rfk->asus, rfk->control_id, !blocked);
1422         return ret;
1423 }
1424
1425 static const struct rfkill_ops pega_rfkill_ops = {
1426         .set_block = pega_rfkill_set,
1427 };
1428
1429 static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *rfk,
1430                              const char *name, int controlid, int rfkill_type)
1431 {
1432         return asus_rfkill_setup(asus, rfk, name, controlid, rfkill_type,
1433                                  &pega_rfkill_ops);
1434 }
1435
1436 static int pega_rfkill_init(struct asus_laptop *asus)
1437 {
1438         int ret = 0;
1439
1440         if(!asus->is_pega_lucid)
1441                 return -ENODEV;
1442
1443         ret = pega_rfkill_setup(asus, &asus->wlan, "pega-wlan",
1444                                 PEGA_WLAN, RFKILL_TYPE_WLAN);
1445         if(ret)
1446                 goto exit;
1447
1448         ret = pega_rfkill_setup(asus, &asus->bluetooth, "pega-bt",
1449                                 PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH);
1450         if(ret)
1451                 goto exit;
1452
1453         ret = pega_rfkill_setup(asus, &asus->wwan, "pega-wwan",
1454                                 PEGA_WWAN, RFKILL_TYPE_WWAN);
1455
1456 exit:
1457         if (ret)
1458                 asus_rfkill_exit(asus);
1459
1460         return ret;
1461 }
1462
1463 /*
1464  * Input device (i.e. hotkeys)
1465  */
1466 static void asus_input_notify(struct asus_laptop *asus, int event)
1467 {
1468         if (!asus->inputdev)
1469                 return ;
1470         if (!sparse_keymap_report_event(asus->inputdev, event, 1, true))
1471                 pr_info("Unknown key %x pressed\n", event);
1472 }
1473
1474 static int asus_input_init(struct asus_laptop *asus)
1475 {
1476         struct input_dev *input;
1477         int error;
1478
1479         input = input_allocate_device();
1480         if (!input)
1481                 return -ENOMEM;
1482
1483         input->name = "Asus Laptop extra buttons";
1484         input->phys = ASUS_LAPTOP_FILE "/input0";
1485         input->id.bustype = BUS_HOST;
1486         input->dev.parent = &asus->platform_device->dev;
1487
1488         error = sparse_keymap_setup(input, asus_keymap, NULL);
1489         if (error) {
1490                 pr_err("Unable to setup input device keymap\n");
1491                 goto err_free_dev;
1492         }
1493         error = input_register_device(input);
1494         if (error) {
1495                 pr_warn("Unable to register input device\n");
1496                 goto err_free_dev;
1497         }
1498
1499         asus->inputdev = input;
1500         return 0;
1501
1502 err_free_dev:
1503         input_free_device(input);
1504         return error;
1505 }
1506
1507 static void asus_input_exit(struct asus_laptop *asus)
1508 {
1509         if (asus->inputdev)
1510                 input_unregister_device(asus->inputdev);
1511         asus->inputdev = NULL;
1512 }
1513
1514 /*
1515  * ACPI driver
1516  */
1517 static void asus_acpi_notify(struct acpi_device *device, u32 event)
1518 {
1519         struct asus_laptop *asus = acpi_driver_data(device);
1520         u16 count;
1521
1522         /* TODO Find a better way to handle events count. */
1523         count = asus->event_count[event % 128]++;
1524         acpi_bus_generate_netlink_event(asus->device->pnp.device_class,
1525                                         dev_name(&asus->device->dev), event,
1526                                         count);
1527
1528         if (event >= ATKD_BRNUP_MIN && event <= ATKD_BRNUP_MAX)
1529                 event = ATKD_BRNUP;
1530         else if (event >= ATKD_BRNDOWN_MIN &&
1531                  event <= ATKD_BRNDOWN_MAX)
1532                 event = ATKD_BRNDOWN;
1533
1534         /* Brightness events are special */
1535         if (event == ATKD_BRNDOWN || event == ATKD_BRNUP) {
1536                 if (asus->backlight_device != NULL) {
1537                         /* Update the backlight device. */
1538                         asus_backlight_notify(asus);
1539                         return ;
1540                 }
1541         }
1542
1543         /* Accelerometer "coarse orientation change" event */
1544         if (asus->pega_accel_poll && event == 0xEA) {
1545                 kobject_uevent(&asus->pega_accel_poll->dev.kobj, KOBJ_CHANGE);
1546                 return ;
1547         }
1548
1549         asus_input_notify(asus, event);
1550 }
1551
1552 static struct attribute *asus_attributes[] = {
1553         &dev_attr_infos.attr,
1554         &dev_attr_wlan.attr,
1555         &dev_attr_bluetooth.attr,
1556         &dev_attr_wimax.attr,
1557         &dev_attr_wwan.attr,
1558         &dev_attr_display.attr,
1559         &dev_attr_ledd.attr,
1560         &dev_attr_ls_value.attr,
1561         &dev_attr_ls_level.attr,
1562         &dev_attr_ls_switch.attr,
1563         &dev_attr_gps.attr,
1564         NULL
1565 };
1566
1567 static umode_t asus_sysfs_is_visible(struct kobject *kobj,
1568                                     struct attribute *attr,
1569                                     int idx)
1570 {
1571         struct device *dev = kobj_to_dev(kobj);
1572         struct asus_laptop *asus = dev_get_drvdata(dev);
1573         acpi_handle handle = asus->handle;
1574         bool supported;
1575
1576         if (asus->is_pega_lucid) {
1577                 /* no ls_level interface on the Lucid */
1578                 if (attr == &dev_attr_ls_switch.attr)
1579                         supported = true;
1580                 else if (attr == &dev_attr_ls_level.attr)
1581                         supported = false;
1582                 else
1583                         goto normal;
1584
1585                 return supported ? attr->mode : 0;
1586         }
1587
1588 normal:
1589         if (attr == &dev_attr_wlan.attr) {
1590                 supported = !acpi_check_handle(handle, METHOD_WLAN, NULL);
1591
1592         } else if (attr == &dev_attr_bluetooth.attr) {
1593                 supported = !acpi_check_handle(handle, METHOD_BLUETOOTH, NULL);
1594
1595         } else if (attr == &dev_attr_display.attr) {
1596                 supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL);
1597
1598         } else if (attr == &dev_attr_wimax.attr) {
1599                 supported =
1600                         !acpi_check_handle(asus->handle, METHOD_WIMAX, NULL);
1601
1602         } else if (attr == &dev_attr_wwan.attr) {
1603                 supported = !acpi_check_handle(asus->handle, METHOD_WWAN, NULL);
1604
1605         } else if (attr == &dev_attr_ledd.attr) {
1606                 supported = !acpi_check_handle(handle, METHOD_LEDD, NULL);
1607
1608         } else if (attr == &dev_attr_ls_switch.attr ||
1609                    attr == &dev_attr_ls_level.attr) {
1610                 supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) &&
1611                         !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL);
1612         } else if (attr == &dev_attr_ls_value.attr) {
1613                 supported = asus->is_pega_lucid;
1614         } else if (attr == &dev_attr_gps.attr) {
1615                 supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) &&
1616                             !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) &&
1617                             !acpi_check_handle(handle, METHOD_GPS_STATUS, NULL);
1618         } else {
1619                 supported = true;
1620         }
1621
1622         return supported ? attr->mode : 0;
1623 }
1624
1625
1626 static const struct attribute_group asus_attr_group = {
1627         .is_visible     = asus_sysfs_is_visible,
1628         .attrs          = asus_attributes,
1629 };
1630
1631 static int asus_platform_init(struct asus_laptop *asus)
1632 {
1633         int result;
1634
1635         asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, PLATFORM_DEVID_NONE);
1636         if (!asus->platform_device)
1637                 return -ENOMEM;
1638         platform_set_drvdata(asus->platform_device, asus);
1639
1640         result = platform_device_add(asus->platform_device);
1641         if (result)
1642                 goto fail_platform_device;
1643
1644         result = sysfs_create_group(&asus->platform_device->dev.kobj,
1645                                     &asus_attr_group);
1646         if (result)
1647                 goto fail_sysfs;
1648
1649         return 0;
1650
1651 fail_sysfs:
1652         platform_device_del(asus->platform_device);
1653 fail_platform_device:
1654         platform_device_put(asus->platform_device);
1655         return result;
1656 }
1657
1658 static void asus_platform_exit(struct asus_laptop *asus)
1659 {
1660         sysfs_remove_group(&asus->platform_device->dev.kobj, &asus_attr_group);
1661         platform_device_unregister(asus->platform_device);
1662 }
1663
1664 static struct platform_driver platform_driver = {
1665         .driver = {
1666                 .name = ASUS_LAPTOP_FILE,
1667         },
1668 };
1669
1670 /*
1671  * This function is used to initialize the context with right values. In this
1672  * method, we can make all the detection we want, and modify the asus_laptop
1673  * struct
1674  */
1675 static int asus_laptop_get_info(struct asus_laptop *asus)
1676 {
1677         struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1678         union acpi_object *model = NULL;
1679         unsigned long long bsts_result;
1680         char *string = NULL;
1681         acpi_status status;
1682
1683         /*
1684          * Get DSDT headers early enough to allow for differentiating between
1685          * models, but late enough to allow acpi_bus_register_driver() to fail
1686          * before doing anything ACPI-specific. Should we encounter a machine,
1687          * which needs special handling (i.e. its hotkey device has a different
1688          * HID), this bit will be moved.
1689          */
1690         status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus->dsdt_info);
1691         if (ACPI_FAILURE(status))
1692                 pr_warn("Couldn't get the DSDT table header\n");
1693
1694         /* We have to write 0 on init this far for all ASUS models */
1695         if (write_acpi_int_ret(asus->handle, "INIT", 0, &buffer)) {
1696                 pr_err("Hotkey initialization failed\n");
1697                 return -ENODEV;
1698         }
1699
1700         /* This needs to be called for some laptops to init properly */
1701         status =
1702             acpi_evaluate_integer(asus->handle, "BSTS", NULL, &bsts_result);
1703         if (ACPI_FAILURE(status))
1704                 pr_warn("Error calling BSTS\n");
1705         else if (bsts_result)
1706                 pr_notice("BSTS called, 0x%02x returned\n",
1707                        (uint) bsts_result);
1708
1709         /* This too ... */
1710         if (write_acpi_int(asus->handle, "CWAP", wapf))
1711                 pr_err("Error calling CWAP(%d)\n", wapf);
1712         /*
1713          * Try to match the object returned by INIT to the specific model.
1714          * Handle every possible object (or the lack of thereof) the DSDT
1715          * writers might throw at us. When in trouble, we pass NULL to
1716          * asus_model_match() and try something completely different.
1717          */
1718         if (buffer.pointer) {
1719                 model = buffer.pointer;
1720                 switch (model->type) {
1721                 case ACPI_TYPE_STRING:
1722                         string = model->string.pointer;
1723                         break;
1724                 case ACPI_TYPE_BUFFER:
1725                         string = model->buffer.pointer;
1726                         break;
1727                 default:
1728                         string = "";
1729                         break;
1730                 }
1731         }
1732         asus->name = kstrdup(string, GFP_KERNEL);
1733         if (!asus->name) {
1734                 kfree(buffer.pointer);
1735                 return -ENOMEM;
1736         }
1737
1738         if (string)
1739                 pr_notice("  %s model detected\n", string);
1740
1741         if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL))
1742                 asus->have_rsts = true;
1743
1744         kfree(model);
1745
1746         return AE_OK;
1747 }
1748
1749 static int asus_acpi_init(struct asus_laptop *asus)
1750 {
1751         int result = 0;
1752
1753         result = acpi_bus_get_status(asus->device);
1754         if (result)
1755                 return result;
1756         if (!asus->device->status.present) {
1757                 pr_err("Hotkey device not present, aborting\n");
1758                 return -ENODEV;
1759         }
1760
1761         result = asus_laptop_get_info(asus);
1762         if (result)
1763                 return result;
1764
1765         if (!strcmp(bled_type, "led"))
1766                 asus->bled_type = TYPE_LED;
1767         else if (!strcmp(bled_type, "rfkill"))
1768                 asus->bled_type = TYPE_RFKILL;
1769
1770         if (!strcmp(wled_type, "led"))
1771                 asus->wled_type = TYPE_LED;
1772         else if (!strcmp(wled_type, "rfkill"))
1773                 asus->wled_type = TYPE_RFKILL;
1774
1775         if (bluetooth_status >= 0)
1776                 asus_bluetooth_set(asus, !!bluetooth_status);
1777
1778         if (wlan_status >= 0)
1779                 asus_wlan_set(asus, !!wlan_status);
1780
1781         if (wimax_status >= 0)
1782                 asus_wimax_set(asus, !!wimax_status);
1783
1784         if (wwan_status >= 0)
1785                 asus_wwan_set(asus, !!wwan_status);
1786
1787         /* Keyboard Backlight is on by default */
1788         if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL))
1789                 asus_kled_set(asus, 1);
1790
1791         /* LED display is off by default */
1792         asus->ledd_status = 0xFFF;
1793
1794         /* Set initial values of light sensor and level */
1795         asus->light_switch = !!als_status;
1796         asus->light_level = 5;  /* level 5 for sensor sensitivity */
1797
1798         if (asus->is_pega_lucid) {
1799                 asus_als_switch(asus, asus->light_switch);
1800         } else if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) &&
1801                    !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {
1802                 asus_als_switch(asus, asus->light_switch);
1803                 asus_als_level(asus, asus->light_level);
1804         }
1805
1806         return result;
1807 }
1808
1809 static void asus_dmi_check(void)
1810 {
1811         const char *model;
1812
1813         model = dmi_get_system_info(DMI_PRODUCT_NAME);
1814         if (!model)
1815                 return;
1816
1817         /* On L1400B WLED control the sound card, don't mess with it ... */
1818         if (strncmp(model, "L1400B", 6) == 0)
1819                 wlan_status = -1;
1820 }
1821
1822 static bool asus_device_present;
1823
1824 static int asus_acpi_add(struct acpi_device *device)
1825 {
1826         struct asus_laptop *asus;
1827         int result;
1828
1829         pr_notice("Asus Laptop Support version %s\n",
1830                   ASUS_LAPTOP_VERSION);
1831         asus = kzalloc(sizeof(struct asus_laptop), GFP_KERNEL);
1832         if (!asus)
1833                 return -ENOMEM;
1834         asus->handle = device->handle;
1835         strcpy(acpi_device_name(device), ASUS_LAPTOP_DEVICE_NAME);
1836         strcpy(acpi_device_class(device), ASUS_LAPTOP_CLASS);
1837         device->driver_data = asus;
1838         asus->device = device;
1839
1840         asus_dmi_check();
1841
1842         result = asus_acpi_init(asus);
1843         if (result)
1844                 goto fail_platform;
1845
1846         /*
1847          * Need platform type detection first, then the platform
1848          * device.  It is used as a parent for the sub-devices below.
1849          */
1850         asus->is_pega_lucid = asus_check_pega_lucid(asus);
1851         result = asus_platform_init(asus);
1852         if (result)
1853                 goto fail_platform;
1854
1855         if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
1856                 result = asus_backlight_init(asus);
1857                 if (result)
1858                         goto fail_backlight;
1859         }
1860
1861         result = asus_input_init(asus);
1862         if (result)
1863                 goto fail_input;
1864
1865         result = asus_led_init(asus);
1866         if (result)
1867                 goto fail_led;
1868
1869         result = asus_rfkill_init(asus);
1870         if (result && result != -ENODEV)
1871                 goto fail_rfkill;
1872
1873         result = pega_accel_init(asus);
1874         if (result && result != -ENODEV)
1875                 goto fail_pega_accel;
1876
1877         result = pega_rfkill_init(asus);
1878         if (result && result != -ENODEV)
1879                 goto fail_pega_rfkill;
1880
1881         asus_device_present = true;
1882         return 0;
1883
1884 fail_pega_rfkill:
1885         pega_accel_exit(asus);
1886 fail_pega_accel:
1887         asus_rfkill_exit(asus);
1888 fail_rfkill:
1889         asus_led_exit(asus);
1890 fail_led:
1891         asus_input_exit(asus);
1892 fail_input:
1893         asus_backlight_exit(asus);
1894 fail_backlight:
1895         asus_platform_exit(asus);
1896 fail_platform:
1897         kfree(asus);
1898
1899         return result;
1900 }
1901
1902 static void asus_acpi_remove(struct acpi_device *device)
1903 {
1904         struct asus_laptop *asus = acpi_driver_data(device);
1905
1906         asus_backlight_exit(asus);
1907         asus_rfkill_exit(asus);
1908         asus_led_exit(asus);
1909         asus_input_exit(asus);
1910         pega_accel_exit(asus);
1911         asus_platform_exit(asus);
1912
1913         kfree(asus->name);
1914         kfree(asus);
1915 }
1916
1917 static const struct acpi_device_id asus_device_ids[] = {
1918         {"ATK0100", 0},
1919         {"ATK0101", 0},
1920         {"", 0},
1921 };
1922 MODULE_DEVICE_TABLE(acpi, asus_device_ids);
1923
1924 static struct acpi_driver asus_acpi_driver = {
1925         .name = ASUS_LAPTOP_NAME,
1926         .class = ASUS_LAPTOP_CLASS,
1927         .ids = asus_device_ids,
1928         .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
1929         .ops = {
1930                 .add = asus_acpi_add,
1931                 .remove = asus_acpi_remove,
1932                 .notify = asus_acpi_notify,
1933                 },
1934 };
1935
1936 static int __init asus_laptop_init(void)
1937 {
1938         int result;
1939
1940         result = platform_driver_register(&platform_driver);
1941         if (result < 0)
1942                 return result;
1943
1944         result = acpi_bus_register_driver(&asus_acpi_driver);
1945         if (result < 0)
1946                 goto fail_acpi_driver;
1947         if (!asus_device_present) {
1948                 result = -ENODEV;
1949                 goto fail_no_device;
1950         }
1951         return 0;
1952
1953 fail_no_device:
1954         acpi_bus_unregister_driver(&asus_acpi_driver);
1955 fail_acpi_driver:
1956         platform_driver_unregister(&platform_driver);
1957         return result;
1958 }
1959
1960 static void __exit asus_laptop_exit(void)
1961 {
1962         acpi_bus_unregister_driver(&asus_acpi_driver);
1963         platform_driver_unregister(&platform_driver);
1964 }
1965
1966 module_init(asus_laptop_init);
1967 module_exit(asus_laptop_exit);
This page took 0.144135 seconds and 4 git commands to generate.