linux/drivers/platform/x86/asus-laptop.c
<<
>>
Prefs
   1/*
   2 *  asus-laptop.c - Asus Laptop Support
   3 *
   4 *
   5 *  Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
   6 *  Copyright (C) 2006-2007 Corentin Chary
   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 *  You should have received a copy of the GNU General Public License
  19 *  along with this program; if not, write to the Free Software
  20 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21 *
  22 *
  23 *  The development page for this driver is located at
  24 *  http://sourceforge.net/projects/acpi4asus/
  25 *
  26 *  Credits:
  27 *  Pontus Fuchs   - Helper functions, cleanup
  28 *  Johann Wiesner - Small compile fixes
  29 *  John Belmonte  - ACPI code for Toshiba laptop was a good starting point.
  30 *  Eric Burghard  - LED display support for W1N
  31 *  Josh Green     - Light Sens support
  32 *  Thomas Tuttle  - His first patch for led support was very helpfull
  33 *  Sam Lin        - GPS support
  34 */
  35
  36#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  37
  38#include <linux/kernel.h>
  39#include <linux/module.h>
  40#include <linux/init.h>
  41#include <linux/types.h>
  42#include <linux/err.h>
  43#include <linux/proc_fs.h>
  44#include <linux/backlight.h>
  45#include <linux/fb.h>
  46#include <linux/leds.h>
  47#include <linux/platform_device.h>
  48#include <acpi/acpi_drivers.h>
  49#include <acpi/acpi_bus.h>
  50#include <asm/uaccess.h>
  51#include <linux/input.h>
  52
  53#define ASUS_LAPTOP_VERSION "0.42"
  54
  55#define ASUS_HOTK_NAME          "Asus Laptop Support"
  56#define ASUS_HOTK_CLASS         "hotkey"
  57#define ASUS_HOTK_DEVICE_NAME   "Hotkey"
  58#define ASUS_HOTK_FILE          KBUILD_MODNAME
  59#define ASUS_HOTK_PREFIX        "\\_SB.ATKD."
  60
  61
  62/*
  63 * Some events we use, same for all Asus
  64 */
  65#define ATKD_BR_UP       0x10
  66#define ATKD_BR_DOWN     0x20
  67#define ATKD_LCD_ON      0x33
  68#define ATKD_LCD_OFF     0x34
  69
  70/*
  71 * Known bits returned by \_SB.ATKD.HWRS
  72 */
  73#define WL_HWRS     0x80
  74#define BT_HWRS     0x100
  75
  76/*
  77 * Flags for hotk status
  78 * WL_ON and BT_ON are also used for wireless_status()
  79 */
  80#define WL_ON       0x01        /* internal Wifi */
  81#define BT_ON       0x02        /* internal Bluetooth */
  82#define MLED_ON     0x04        /* mail LED */
  83#define TLED_ON     0x08        /* touchpad LED */
  84#define RLED_ON     0x10        /* Record LED */
  85#define PLED_ON     0x20        /* Phone LED */
  86#define GLED_ON     0x40        /* Gaming LED */
  87#define LCD_ON      0x80        /* LCD backlight */
  88#define GPS_ON      0x100       /* GPS */
  89#define KEY_ON      0x200       /* Keyboard backlight */
  90
  91#define ASUS_LOG    ASUS_HOTK_FILE ": "
  92#define ASUS_ERR    KERN_ERR    ASUS_LOG
  93#define ASUS_WARNING    KERN_WARNING    ASUS_LOG
  94#define ASUS_NOTICE KERN_NOTICE ASUS_LOG
  95#define ASUS_INFO   KERN_INFO   ASUS_LOG
  96#define ASUS_DEBUG  KERN_DEBUG  ASUS_LOG
  97
  98MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
  99MODULE_DESCRIPTION(ASUS_HOTK_NAME);
 100MODULE_LICENSE("GPL");
 101
 102/*
 103 * WAPF defines the behavior of the Fn+Fx wlan key
 104 * The significance of values is yet to be found, but
 105 * most of the time:
 106 * 0x0 will do nothing
 107 * 0x1 will allow to control the device with Fn+Fx key.
 108 * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key
 109 * 0x5 like 0x1 or 0x4
 110 * So, if something doesn't work as you want, just try other values =)
 111 */
 112static uint wapf = 1;
 113module_param(wapf, uint, 0644);
 114MODULE_PARM_DESC(wapf, "WAPF value");
 115
 116#define ASUS_HANDLE(object, paths...)                                   \
 117        static acpi_handle  object##_handle = NULL;                     \
 118        static char *object##_paths[] = { paths }
 119
 120/* LED */
 121ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX "MLED");
 122ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED");
 123ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED"); /* W1JC */
 124ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED"); /* A7J */
 125ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */
 126
 127/* LEDD */
 128ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM");
 129
 130/*
 131 * Bluetooth and WLAN
 132 * WLED and BLED are not handled like other XLED, because in some dsdt
 133 * they also control the WLAN/Bluetooth device.
 134 */
 135ASUS_HANDLE(wl_switch, ASUS_HOTK_PREFIX "WLED");
 136ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED");
 137ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS");  /* All new models */
 138
 139/* Brightness */
 140ASUS_HANDLE(brightness_set, ASUS_HOTK_PREFIX "SPLV");
 141ASUS_HANDLE(brightness_get, ASUS_HOTK_PREFIX "GPLV");
 142
 143/* Backlight */
 144ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10",     /* All new models */
 145            "\\_SB.PCI0.ISA.EC0._Q10",  /* A1x */
 146            "\\_SB.PCI0.PX40.ECD0._Q10",        /* L3C */
 147            "\\_SB.PCI0.PX40.EC0.Q10",  /* M1A */
 148            "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */
 149            "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */
 150            "\\_SB.PCI0.PX40.Q10",      /* S1x */
 151            "\\Q10");           /* A2x, L2D, L3D, M2E */
 152
 153/* Display */
 154ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP");
 155ASUS_HANDLE(display_get,
 156            /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */
 157            "\\_SB.PCI0.P0P1.VGA.GETD",
 158            /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */
 159            "\\_SB.PCI0.P0P2.VGA.GETD",
 160            /* A6V A6Q */
 161            "\\_SB.PCI0.P0P3.VGA.GETD",
 162            /* A6T, A6M */
 163            "\\_SB.PCI0.P0PA.VGA.GETD",
 164            /* L3C */
 165            "\\_SB.PCI0.PCI1.VGAC.NMAP",
 166            /* Z96F */
 167            "\\_SB.PCI0.VGA.GETD",
 168            /* A2D */
 169            "\\ACTD",
 170            /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
 171            "\\ADVG",
 172            /* P30 */
 173            "\\DNXT",
 174            /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
 175            "\\INFB",
 176            /* A3F A6F A3N A3L M6N W3N W6A */
 177            "\\SSTE");
 178
 179ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */
 180ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL");  /* Z71A Z71V */
 181
 182/* GPS */
 183/* R2H use different handle for GPS on/off */
 184ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON");   /* R2H */
 185ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF");  /* R2H */
 186ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST");
 187
 188/* Keyboard light */
 189ASUS_HANDLE(kled_set, ASUS_HOTK_PREFIX "SLKB");
 190ASUS_HANDLE(kled_get, ASUS_HOTK_PREFIX "GLKB");
 191
 192/*
 193 * This is the main structure, we can use it to store anything interesting
 194 * about the hotk device
 195 */
 196struct asus_hotk {
 197        char *name;             /* laptop name */
 198        struct acpi_device *device;     /* the device we are in */
 199        acpi_handle handle;     /* the handle of the hotk device */
 200        char status;            /* status of the hotk, for LEDs, ... */
 201        u32 ledd_status;        /* status of the LED display */
 202        u8 light_level;         /* light sensor level */
 203        u8 light_switch;        /* light sensor switch value */
 204        u16 event_count[128];   /* count for each event TODO make this better */
 205        struct input_dev *inputdev;
 206        u16 *keycode_map;
 207};
 208
 209/*
 210 * This header is made available to allow proper configuration given model,
 211 * revision number , ... this info cannot go in struct asus_hotk because it is
 212 * available before the hotk
 213 */
 214static struct acpi_table_header *asus_info;
 215
 216/* The actual device the driver binds to */
 217static struct asus_hotk *hotk;
 218
 219/*
 220 * The hotkey driver declaration
 221 */
 222static const struct acpi_device_id asus_device_ids[] = {
 223        {"ATK0100", 0},
 224        {"", 0},
 225};
 226MODULE_DEVICE_TABLE(acpi, asus_device_ids);
 227
 228static int asus_hotk_add(struct acpi_device *device);
 229static int asus_hotk_remove(struct acpi_device *device, int type);
 230static void asus_hotk_notify(struct acpi_device *device, u32 event);
 231
 232static struct acpi_driver asus_hotk_driver = {
 233        .name = ASUS_HOTK_NAME,
 234        .class = ASUS_HOTK_CLASS,
 235        .ids = asus_device_ids,
 236        .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
 237        .ops = {
 238                .add = asus_hotk_add,
 239                .remove = asus_hotk_remove,
 240                .notify = asus_hotk_notify,
 241                },
 242};
 243
 244/* The backlight device /sys/class/backlight */
 245static struct backlight_device *asus_backlight_device;
 246
 247/*
 248 * The backlight class declaration
 249 */
 250static int read_brightness(struct backlight_device *bd);
 251static int update_bl_status(struct backlight_device *bd);
 252static struct backlight_ops asusbl_ops = {
 253        .get_brightness = read_brightness,
 254        .update_status = update_bl_status,
 255};
 256
 257/*
 258 * These functions actually update the LED's, and are called from a
 259 * workqueue. By doing this as separate work rather than when the LED
 260 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
 261 * potentially bad time, such as a timer interrupt.
 262 */
 263static struct workqueue_struct *led_workqueue;
 264
 265#define ASUS_LED(object, ledname, max)                                  \
 266        static void object##_led_set(struct led_classdev *led_cdev,     \
 267                                     enum led_brightness value);        \
 268        static enum led_brightness object##_led_get(                    \
 269                struct led_classdev *led_cdev);                         \
 270        static void object##_led_update(struct work_struct *ignored);   \
 271        static int object##_led_wk;                                     \
 272        static DECLARE_WORK(object##_led_work, object##_led_update);    \
 273        static struct led_classdev object##_led = {                     \
 274                .name           = "asus::" ledname,                     \
 275                .brightness_set = object##_led_set,                     \
 276                .brightness_get = object##_led_get,                     \
 277                .max_brightness = max                                   \
 278        }
 279
 280ASUS_LED(mled, "mail", 1);
 281ASUS_LED(tled, "touchpad", 1);
 282ASUS_LED(rled, "record", 1);
 283ASUS_LED(pled, "phone", 1);
 284ASUS_LED(gled, "gaming", 1);
 285ASUS_LED(kled, "kbd_backlight", 3);
 286
 287struct key_entry {
 288        char type;
 289        u8 code;
 290        u16 keycode;
 291};
 292
 293enum { KE_KEY, KE_END };
 294
 295static struct key_entry asus_keymap[] = {
 296        {KE_KEY, 0x30, KEY_VOLUMEUP},
 297        {KE_KEY, 0x31, KEY_VOLUMEDOWN},
 298        {KE_KEY, 0x32, KEY_MUTE},
 299        {KE_KEY, 0x33, KEY_SWITCHVIDEOMODE},
 300        {KE_KEY, 0x34, KEY_SWITCHVIDEOMODE},
 301        {KE_KEY, 0x40, KEY_PREVIOUSSONG},
 302        {KE_KEY, 0x41, KEY_NEXTSONG},
 303        {KE_KEY, 0x43, KEY_STOPCD},
 304        {KE_KEY, 0x45, KEY_PLAYPAUSE},
 305        {KE_KEY, 0x4c, KEY_MEDIA},
 306        {KE_KEY, 0x50, KEY_EMAIL},
 307        {KE_KEY, 0x51, KEY_WWW},
 308        {KE_KEY, 0x55, KEY_CALC},
 309        {KE_KEY, 0x5C, KEY_SCREENLOCK},  /* Screenlock */
 310        {KE_KEY, 0x5D, KEY_WLAN},
 311        {KE_KEY, 0x5E, KEY_WLAN},
 312        {KE_KEY, 0x5F, KEY_WLAN},
 313        {KE_KEY, 0x60, KEY_SWITCHVIDEOMODE},
 314        {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE},
 315        {KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */
 316        {KE_KEY, 0x82, KEY_CAMERA},
 317        {KE_KEY, 0x8A, KEY_PROG1},
 318        {KE_KEY, 0x95, KEY_MEDIA},
 319        {KE_KEY, 0x99, KEY_PHONE},
 320        {KE_KEY, 0xc4, KEY_KBDILLUMUP},
 321        {KE_KEY, 0xc5, KEY_KBDILLUMDOWN},
 322        {KE_END, 0},
 323};
 324
 325/*
 326 * This function evaluates an ACPI method, given an int as parameter, the
 327 * method is searched within the scope of the handle, can be NULL. The output
 328 * of the method is written is output, which can also be NULL
 329 *
 330 * returns 0 if write is successful, -1 else.
 331 */
 332static int write_acpi_int(acpi_handle handle, const char *method, int val,
 333                          struct acpi_buffer *output)
 334{
 335        struct acpi_object_list params; /* list of input parameters (an int) */
 336        union acpi_object in_obj;       /* the only param we use */
 337        acpi_status status;
 338
 339        if (!handle)
 340                return 0;
 341
 342        params.count = 1;
 343        params.pointer = &in_obj;
 344        in_obj.type = ACPI_TYPE_INTEGER;
 345        in_obj.integer.value = val;
 346
 347        status = acpi_evaluate_object(handle, (char *)method, &params, output);
 348        if (status == AE_OK)
 349                return 0;
 350        else
 351                return -1;
 352}
 353
 354static int read_wireless_status(int mask)
 355{
 356        unsigned long long status;
 357        acpi_status rv = AE_OK;
 358
 359        if (!wireless_status_handle)
 360                return (hotk->status & mask) ? 1 : 0;
 361
 362        rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
 363        if (ACPI_FAILURE(rv))
 364                pr_warning("Error reading Wireless status\n");
 365        else
 366                return (status & mask) ? 1 : 0;
 367
 368        return (hotk->status & mask) ? 1 : 0;
 369}
 370
 371static int read_gps_status(void)
 372{
 373        unsigned long long status;
 374        acpi_status rv = AE_OK;
 375
 376        rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status);
 377        if (ACPI_FAILURE(rv))
 378                pr_warning("Error reading GPS status\n");
 379        else
 380                return status ? 1 : 0;
 381
 382        return (hotk->status & GPS_ON) ? 1 : 0;
 383}
 384
 385/* Generic LED functions */
 386static int read_status(int mask)
 387{
 388        /* There is a special method for both wireless devices */
 389        if (mask == BT_ON || mask == WL_ON)
 390                return read_wireless_status(mask);
 391        else if (mask == GPS_ON)
 392                return read_gps_status();
 393
 394        return (hotk->status & mask) ? 1 : 0;
 395}
 396
 397static void write_status(acpi_handle handle, int out, int mask)
 398{
 399        hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask);
 400
 401        switch (mask) {
 402        case MLED_ON:
 403                out = !(out & 0x1);
 404                break;
 405        case GLED_ON:
 406                out = (out & 0x1) + 1;
 407                break;
 408        case GPS_ON:
 409                handle = (out) ? gps_on_handle : gps_off_handle;
 410                out = 0x02;
 411                break;
 412        default:
 413                out &= 0x1;
 414                break;
 415        }
 416
 417        if (write_acpi_int(handle, NULL, out, NULL))
 418                pr_warning(" write failed %x\n", mask);
 419}
 420
 421/* /sys/class/led handlers */
 422#define ASUS_LED_HANDLER(object, mask)                                  \
 423        static void object##_led_set(struct led_classdev *led_cdev,     \
 424                                     enum led_brightness value)         \
 425        {                                                               \
 426                object##_led_wk = (value > 0) ? 1 : 0;                  \
 427                queue_work(led_workqueue, &object##_led_work);          \
 428        }                                                               \
 429        static void object##_led_update(struct work_struct *ignored)    \
 430        {                                                               \
 431                int value = object##_led_wk;                            \
 432                write_status(object##_set_handle, value, (mask));       \
 433        }                                                               \
 434        static enum led_brightness object##_led_get(                    \
 435                struct led_classdev *led_cdev)                          \
 436        {                                                               \
 437                return led_cdev->brightness;                            \
 438        }
 439
 440ASUS_LED_HANDLER(mled, MLED_ON);
 441ASUS_LED_HANDLER(pled, PLED_ON);
 442ASUS_LED_HANDLER(rled, RLED_ON);
 443ASUS_LED_HANDLER(tled, TLED_ON);
 444ASUS_LED_HANDLER(gled, GLED_ON);
 445
 446/*
 447 * Keyboard backlight
 448 */
 449static int get_kled_lvl(void)
 450{
 451        unsigned long long kblv;
 452        struct acpi_object_list params;
 453        union acpi_object in_obj;
 454        acpi_status rv;
 455
 456        params.count = 1;
 457        params.pointer = &in_obj;
 458        in_obj.type = ACPI_TYPE_INTEGER;
 459        in_obj.integer.value = 2;
 460
 461        rv = acpi_evaluate_integer(kled_get_handle, NULL, &params, &kblv);
 462        if (ACPI_FAILURE(rv)) {
 463                pr_warning("Error reading kled level\n");
 464                return 0;
 465        }
 466        return kblv;
 467}
 468
 469static int set_kled_lvl(int kblv)
 470{
 471        if (kblv > 0)
 472                kblv = (1 << 7) | (kblv & 0x7F);
 473        else
 474                kblv = 0;
 475
 476        if (write_acpi_int(kled_set_handle, NULL, kblv, NULL)) {
 477                pr_warning("Keyboard LED display write failed\n");
 478                return -EINVAL;
 479        }
 480        return 0;
 481}
 482
 483static void kled_led_set(struct led_classdev *led_cdev,
 484                         enum led_brightness value)
 485{
 486        kled_led_wk = value;
 487        queue_work(led_workqueue, &kled_led_work);
 488}
 489
 490static void kled_led_update(struct work_struct *ignored)
 491{
 492        set_kled_lvl(kled_led_wk);
 493}
 494
 495static enum led_brightness kled_led_get(struct led_classdev *led_cdev)
 496{
 497        return get_kled_lvl();
 498}
 499
 500static int get_lcd_state(void)
 501{
 502        return read_status(LCD_ON);
 503}
 504
 505static int set_lcd_state(int value)
 506{
 507        int lcd = 0;
 508        acpi_status status = 0;
 509
 510        lcd = value ? 1 : 0;
 511
 512        if (lcd == get_lcd_state())
 513                return 0;
 514
 515        if (lcd_switch_handle) {
 516                status = acpi_evaluate_object(lcd_switch_handle,
 517                                              NULL, NULL, NULL);
 518
 519                if (ACPI_FAILURE(status))
 520                        pr_warning("Error switching LCD\n");
 521        }
 522
 523        write_status(NULL, lcd, LCD_ON);
 524        return 0;
 525}
 526
 527static void lcd_blank(int blank)
 528{
 529        struct backlight_device *bd = asus_backlight_device;
 530
 531        if (bd) {
 532                bd->props.power = blank;
 533                backlight_update_status(bd);
 534        }
 535}
 536
 537static int read_brightness(struct backlight_device *bd)
 538{
 539        unsigned long long value;
 540        acpi_status rv = AE_OK;
 541
 542        rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
 543        if (ACPI_FAILURE(rv))
 544                pr_warning("Error reading brightness\n");
 545
 546        return value;
 547}
 548
 549static int set_brightness(struct backlight_device *bd, int value)
 550{
 551        int ret = 0;
 552
 553        value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
 554        /* 0 <= value <= 15 */
 555
 556        if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) {
 557                pr_warning("Error changing brightness\n");
 558                ret = -EIO;
 559        }
 560
 561        return ret;
 562}
 563
 564static int update_bl_status(struct backlight_device *bd)
 565{
 566        int rv;
 567        int value = bd->props.brightness;
 568
 569        rv = set_brightness(bd, value);
 570        if (rv)
 571                return rv;
 572
 573        value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0;
 574        return set_lcd_state(value);
 575}
 576
 577/*
 578 * Platform device handlers
 579 */
 580
 581/*
 582 * We write our info in page, we begin at offset off and cannot write more
 583 * than count bytes. We set eof to 1 if we handle those 2 values. We return the
 584 * number of bytes written in page
 585 */
 586static ssize_t show_infos(struct device *dev,
 587                          struct device_attribute *attr, char *page)
 588{
 589        int len = 0;
 590        unsigned long long temp;
 591        char buf[16];           /* enough for all info */
 592        acpi_status rv = AE_OK;
 593
 594        /*
 595         * We use the easy way, we don't care of off and count, so we don't set eof
 596         * to 1
 597         */
 598
 599        len += sprintf(page, ASUS_HOTK_NAME " " ASUS_LAPTOP_VERSION "\n");
 600        len += sprintf(page + len, "Model reference    : %s\n", hotk->name);
 601        /*
 602         * The SFUN method probably allows the original driver to get the list
 603         * of features supported by a given model. For now, 0x0100 or 0x0800
 604         * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
 605         * The significance of others is yet to be found.
 606         */
 607        rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp);
 608        if (!ACPI_FAILURE(rv))
 609                len += sprintf(page + len, "SFUN value         : %#x\n",
 610                               (uint) temp);
 611        /*
 612         * The HWRS method return informations about the hardware.
 613         * 0x80 bit is for WLAN, 0x100 for Bluetooth.
 614         * The significance of others is yet to be found.
 615         * If we don't find the method, we assume the device are present.
 616         */
 617        rv = acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &temp);
 618        if (!ACPI_FAILURE(rv))
 619                len += sprintf(page + len, "HRWS value         : %#x\n",
 620                               (uint) temp);
 621        /*
 622         * Another value for userspace: the ASYM method returns 0x02 for
 623         * battery low and 0x04 for battery critical, its readings tend to be
 624         * more accurate than those provided by _BST.
 625         * Note: since not all the laptops provide this method, errors are
 626         * silently ignored.
 627         */
 628        rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp);
 629        if (!ACPI_FAILURE(rv))
 630                len += sprintf(page + len, "ASYM value         : %#x\n",
 631                               (uint) temp);
 632        if (asus_info) {
 633                snprintf(buf, 16, "%d", asus_info->length);
 634                len += sprintf(page + len, "DSDT length        : %s\n", buf);
 635                snprintf(buf, 16, "%d", asus_info->checksum);
 636                len += sprintf(page + len, "DSDT checksum      : %s\n", buf);
 637                snprintf(buf, 16, "%d", asus_info->revision);
 638                len += sprintf(page + len, "DSDT revision      : %s\n", buf);
 639                snprintf(buf, 7, "%s", asus_info->oem_id);
 640                len += sprintf(page + len, "OEM id             : %s\n", buf);
 641                snprintf(buf, 9, "%s", asus_info->oem_table_id);
 642                len += sprintf(page + len, "OEM table id       : %s\n", buf);
 643                snprintf(buf, 16, "%x", asus_info->oem_revision);
 644                len += sprintf(page + len, "OEM revision       : 0x%s\n", buf);
 645                snprintf(buf, 5, "%s", asus_info->asl_compiler_id);
 646                len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
 647                snprintf(buf, 16, "%x", asus_info->asl_compiler_revision);
 648                len += sprintf(page + len, "ASL comp revision  : 0x%s\n", buf);
 649        }
 650
 651        return len;
 652}
 653
 654static int parse_arg(const char *buf, unsigned long count, int *val)
 655{
 656        if (!count)
 657                return 0;
 658        if (count > 31)
 659                return -EINVAL;
 660        if (sscanf(buf, "%i", val) != 1)
 661                return -EINVAL;
 662        return count;
 663}
 664
 665static ssize_t store_status(const char *buf, size_t count,
 666                            acpi_handle handle, int mask)
 667{
 668        int rv, value;
 669        int out = 0;
 670
 671        rv = parse_arg(buf, count, &value);
 672        if (rv > 0)
 673                out = value ? 1 : 0;
 674
 675        write_status(handle, out, mask);
 676
 677        return rv;
 678}
 679
 680/*
 681 * LEDD display
 682 */
 683static ssize_t show_ledd(struct device *dev,
 684                         struct device_attribute *attr, char *buf)
 685{
 686        return sprintf(buf, "0x%08x\n", hotk->ledd_status);
 687}
 688
 689static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,
 690                          const char *buf, size_t count)
 691{
 692        int rv, value;
 693
 694        rv = parse_arg(buf, count, &value);
 695        if (rv > 0) {
 696                if (write_acpi_int(ledd_set_handle, NULL, value, NULL))
 697                        pr_warning("LED display write failed\n");
 698                else
 699                        hotk->ledd_status = (u32) value;
 700        }
 701        return rv;
 702}
 703
 704/*
 705 * WLAN
 706 */
 707static ssize_t show_wlan(struct device *dev,
 708                         struct device_attribute *attr, char *buf)
 709{
 710        return sprintf(buf, "%d\n", read_status(WL_ON));
 711}
 712
 713static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
 714                          const char *buf, size_t count)
 715{
 716        return store_status(buf, count, wl_switch_handle, WL_ON);
 717}
 718
 719/*
 720 * Bluetooth
 721 */
 722static ssize_t show_bluetooth(struct device *dev,
 723                              struct device_attribute *attr, char *buf)
 724{
 725        return sprintf(buf, "%d\n", read_status(BT_ON));
 726}
 727
 728static ssize_t store_bluetooth(struct device *dev,
 729                               struct device_attribute *attr, const char *buf,
 730                               size_t count)
 731{
 732        return store_status(buf, count, bt_switch_handle, BT_ON);
 733}
 734
 735/*
 736 * Display
 737 */
 738static void set_display(int value)
 739{
 740        /* no sanity check needed for now */
 741        if (write_acpi_int(display_set_handle, NULL, value, NULL))
 742                pr_warning("Error setting display\n");
 743        return;
 744}
 745
 746static int read_display(void)
 747{
 748        unsigned long long value = 0;
 749        acpi_status rv = AE_OK;
 750
 751        /*
 752         * In most of the case, we know how to set the display, but sometime
 753         * we can't read it
 754         */
 755        if (display_get_handle) {
 756                rv = acpi_evaluate_integer(display_get_handle, NULL,
 757                                           NULL, &value);
 758                if (ACPI_FAILURE(rv))
 759                        pr_warning("Error reading display status\n");
 760        }
 761
 762        value &= 0x0F;          /* needed for some models, shouldn't hurt others */
 763
 764        return value;
 765}
 766
 767/*
 768 * Now, *this* one could be more user-friendly, but so far, no-one has
 769 * complained. The significance of bits is the same as in store_disp()
 770 */
 771static ssize_t show_disp(struct device *dev,
 772                         struct device_attribute *attr, char *buf)
 773{
 774        return sprintf(buf, "%d\n", read_display());
 775}
 776
 777/*
 778 * Experimental support for display switching. As of now: 1 should activate
 779 * the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI.
 780 * Any combination (bitwise) of these will suffice. I never actually tested 4
 781 * displays hooked up simultaneously, so be warned. See the acpi4asus README
 782 * for more info.
 783 */
 784static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
 785                          const char *buf, size_t count)
 786{
 787        int rv, value;
 788
 789        rv = parse_arg(buf, count, &value);
 790        if (rv > 0)
 791                set_display(value);
 792        return rv;
 793}
 794
 795/*
 796 * Light Sens
 797 */
 798static void set_light_sens_switch(int value)
 799{
 800        if (write_acpi_int(ls_switch_handle, NULL, value, NULL))
 801                pr_warning("Error setting light sensor switch\n");
 802        hotk->light_switch = value;
 803}
 804
 805static ssize_t show_lssw(struct device *dev,
 806                         struct device_attribute *attr, char *buf)
 807{
 808        return sprintf(buf, "%d\n", hotk->light_switch);
 809}
 810
 811static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,
 812                          const char *buf, size_t count)
 813{
 814        int rv, value;
 815
 816        rv = parse_arg(buf, count, &value);
 817        if (rv > 0)
 818                set_light_sens_switch(value ? 1 : 0);
 819
 820        return rv;
 821}
 822
 823static void set_light_sens_level(int value)
 824{
 825        if (write_acpi_int(ls_level_handle, NULL, value, NULL))
 826                pr_warning("Error setting light sensor level\n");
 827        hotk->light_level = value;
 828}
 829
 830static ssize_t show_lslvl(struct device *dev,
 831                          struct device_attribute *attr, char *buf)
 832{
 833        return sprintf(buf, "%d\n", hotk->light_level);
 834}
 835
 836static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,
 837                           const char *buf, size_t count)
 838{
 839        int rv, value;
 840
 841        rv = parse_arg(buf, count, &value);
 842        if (rv > 0) {
 843                value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
 844                /* 0 <= value <= 15 */
 845                set_light_sens_level(value);
 846        }
 847
 848        return rv;
 849}
 850
 851/*
 852 * GPS
 853 */
 854static ssize_t show_gps(struct device *dev,
 855                        struct device_attribute *attr, char *buf)
 856{
 857        return sprintf(buf, "%d\n", read_status(GPS_ON));
 858}
 859
 860static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
 861                         const char *buf, size_t count)
 862{
 863        return store_status(buf, count, NULL, GPS_ON);
 864}
 865
 866/*
 867 * Hotkey functions
 868 */
 869static struct key_entry *asus_get_entry_by_scancode(int code)
 870{
 871        struct key_entry *key;
 872
 873        for (key = asus_keymap; key->type != KE_END; key++)
 874                if (code == key->code)
 875                        return key;
 876
 877        return NULL;
 878}
 879
 880static struct key_entry *asus_get_entry_by_keycode(int code)
 881{
 882        struct key_entry *key;
 883
 884        for (key = asus_keymap; key->type != KE_END; key++)
 885                if (code == key->keycode && key->type == KE_KEY)
 886                        return key;
 887
 888        return NULL;
 889}
 890
 891static int asus_getkeycode(struct input_dev *dev, int scancode, int *keycode)
 892{
 893        struct key_entry *key = asus_get_entry_by_scancode(scancode);
 894
 895        if (key && key->type == KE_KEY) {
 896                *keycode = key->keycode;
 897                return 0;
 898        }
 899
 900        return -EINVAL;
 901}
 902
 903static int asus_setkeycode(struct input_dev *dev, int scancode, int keycode)
 904{
 905        struct key_entry *key;
 906        int old_keycode;
 907
 908        if (keycode < 0 || keycode > KEY_MAX)
 909                return -EINVAL;
 910
 911        key = asus_get_entry_by_scancode(scancode);
 912        if (key && key->type == KE_KEY) {
 913                old_keycode = key->keycode;
 914                key->keycode = keycode;
 915                set_bit(keycode, dev->keybit);
 916                if (!asus_get_entry_by_keycode(old_keycode))
 917                        clear_bit(old_keycode, dev->keybit);
 918                return 0;
 919        }
 920
 921        return -EINVAL;
 922}
 923
 924static void asus_hotk_notify(struct acpi_device *device, u32 event)
 925{
 926        static struct key_entry *key;
 927        u16 count;
 928
 929        /* TODO Find a better way to handle events count. */
 930        if (!hotk)
 931                return;
 932
 933        /*
 934         * We need to tell the backlight device when the backlight power is
 935         * switched
 936         */
 937        if (event == ATKD_LCD_ON) {
 938                write_status(NULL, 1, LCD_ON);
 939                lcd_blank(FB_BLANK_UNBLANK);
 940        } else if (event == ATKD_LCD_OFF) {
 941                write_status(NULL, 0, LCD_ON);
 942                lcd_blank(FB_BLANK_POWERDOWN);
 943        }
 944
 945        count = hotk->event_count[event % 128]++;
 946        acpi_bus_generate_proc_event(hotk->device, event, count);
 947        acpi_bus_generate_netlink_event(hotk->device->pnp.device_class,
 948                                        dev_name(&hotk->device->dev), event,
 949                                        count);
 950
 951        if (hotk->inputdev) {
 952                key = asus_get_entry_by_scancode(event);
 953                if (!key)
 954                        return ;
 955
 956                switch (key->type) {
 957                case KE_KEY:
 958                        input_report_key(hotk->inputdev, key->keycode, 1);
 959                        input_sync(hotk->inputdev);
 960                        input_report_key(hotk->inputdev, key->keycode, 0);
 961                        input_sync(hotk->inputdev);
 962                        break;
 963                }
 964        }
 965}
 966
 967#define ASUS_CREATE_DEVICE_ATTR(_name)                                  \
 968        struct device_attribute dev_attr_##_name = {                    \
 969                .attr = {                                               \
 970                        .name = __stringify(_name),                     \
 971                        .mode = 0 },                                    \
 972                .show   = NULL,                                         \
 973                .store  = NULL,                                         \
 974        }
 975
 976#define ASUS_SET_DEVICE_ATTR(_name, _mode, _show, _store)               \
 977        do {                                                            \
 978                dev_attr_##_name.attr.mode = _mode;                     \
 979                dev_attr_##_name.show = _show;                          \
 980                dev_attr_##_name.store = _store;                        \
 981        } while(0)
 982
 983static ASUS_CREATE_DEVICE_ATTR(infos);
 984static ASUS_CREATE_DEVICE_ATTR(wlan);
 985static ASUS_CREATE_DEVICE_ATTR(bluetooth);
 986static ASUS_CREATE_DEVICE_ATTR(display);
 987static ASUS_CREATE_DEVICE_ATTR(ledd);
 988static ASUS_CREATE_DEVICE_ATTR(ls_switch);
 989static ASUS_CREATE_DEVICE_ATTR(ls_level);
 990static ASUS_CREATE_DEVICE_ATTR(gps);
 991
 992static struct attribute *asuspf_attributes[] = {
 993        &dev_attr_infos.attr,
 994        &dev_attr_wlan.attr,
 995        &dev_attr_bluetooth.attr,
 996        &dev_attr_display.attr,
 997        &dev_attr_ledd.attr,
 998        &dev_attr_ls_switch.attr,
 999        &dev_attr_ls_level.attr,
1000        &dev_attr_gps.attr,
1001        NULL
1002};
1003
1004static struct attribute_group asuspf_attribute_group = {
1005        .attrs = asuspf_attributes
1006};
1007
1008static struct platform_driver asuspf_driver = {
1009        .driver = {
1010                   .name = ASUS_HOTK_FILE,
1011                   .owner = THIS_MODULE,
1012                   }
1013};
1014
1015static struct platform_device *asuspf_device;
1016
1017static void asus_hotk_add_fs(void)
1018{
1019        ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL);
1020
1021        if (wl_switch_handle)
1022                ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan);
1023
1024        if (bt_switch_handle)
1025                ASUS_SET_DEVICE_ATTR(bluetooth, 0644,
1026                                     show_bluetooth, store_bluetooth);
1027
1028        if (display_set_handle && display_get_handle)
1029                ASUS_SET_DEVICE_ATTR(display, 0644, show_disp, store_disp);
1030        else if (display_set_handle)
1031                ASUS_SET_DEVICE_ATTR(display, 0200, NULL, store_disp);
1032
1033        if (ledd_set_handle)
1034                ASUS_SET_DEVICE_ATTR(ledd, 0644, show_ledd, store_ledd);
1035
1036        if (ls_switch_handle && ls_level_handle) {
1037                ASUS_SET_DEVICE_ATTR(ls_level, 0644, show_lslvl, store_lslvl);
1038                ASUS_SET_DEVICE_ATTR(ls_switch, 0644, show_lssw, store_lssw);
1039        }
1040
1041        if (gps_status_handle && gps_on_handle && gps_off_handle)
1042                ASUS_SET_DEVICE_ATTR(gps, 0644, show_gps, store_gps);
1043}
1044
1045static int asus_handle_init(char *name, acpi_handle * handle,
1046                            char **paths, int num_paths)
1047{
1048        int i;
1049        acpi_status status;
1050
1051        for (i = 0; i < num_paths; i++) {
1052                status = acpi_get_handle(NULL, paths[i], handle);
1053                if (ACPI_SUCCESS(status))
1054                        return 0;
1055        }
1056
1057        *handle = NULL;
1058        return -ENODEV;
1059}
1060
1061#define ASUS_HANDLE_INIT(object)                                        \
1062        asus_handle_init(#object, &object##_handle, object##_paths,     \
1063                         ARRAY_SIZE(object##_paths))
1064
1065/*
1066 * This function is used to initialize the hotk with right values. In this
1067 * method, we can make all the detection we want, and modify the hotk struct
1068 */
1069static int asus_hotk_get_info(void)
1070{
1071        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1072        union acpi_object *model = NULL;
1073        unsigned long long bsts_result, hwrs_result;
1074        char *string = NULL;
1075        acpi_status status;
1076
1077        /*
1078         * Get DSDT headers early enough to allow for differentiating between
1079         * models, but late enough to allow acpi_bus_register_driver() to fail
1080         * before doing anything ACPI-specific. Should we encounter a machine,
1081         * which needs special handling (i.e. its hotkey device has a different
1082         * HID), this bit will be moved. A global variable asus_info contains
1083         * the DSDT header.
1084         */
1085        status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
1086        if (ACPI_FAILURE(status))
1087                pr_warning("Couldn't get the DSDT table header\n");
1088
1089        /* We have to write 0 on init this far for all ASUS models */
1090        if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
1091                pr_err("Hotkey initialization failed\n");
1092                return -ENODEV;
1093        }
1094
1095        /* This needs to be called for some laptops to init properly */
1096        status =
1097            acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
1098        if (ACPI_FAILURE(status))
1099                pr_warning("Error calling BSTS\n");
1100        else if (bsts_result)
1101                pr_notice("BSTS called, 0x%02x returned\n",
1102                       (uint) bsts_result);
1103
1104        /* This too ... */
1105        write_acpi_int(hotk->handle, "CWAP", wapf, NULL);
1106
1107        /*
1108         * Try to match the object returned by INIT to the specific model.
1109         * Handle every possible object (or the lack of thereof) the DSDT
1110         * writers might throw at us. When in trouble, we pass NULL to
1111         * asus_model_match() and try something completely different.
1112         */
1113        if (buffer.pointer) {
1114                model = buffer.pointer;
1115                switch (model->type) {
1116                case ACPI_TYPE_STRING:
1117                        string = model->string.pointer;
1118                        break;
1119                case ACPI_TYPE_BUFFER:
1120                        string = model->buffer.pointer;
1121                        break;
1122                default:
1123                        string = "";
1124                        break;
1125                }
1126        }
1127        hotk->name = kstrdup(string, GFP_KERNEL);
1128        if (!hotk->name)
1129                return -ENOMEM;
1130
1131        if (*string)
1132                pr_notice("  %s model detected\n", string);
1133
1134        ASUS_HANDLE_INIT(mled_set);
1135        ASUS_HANDLE_INIT(tled_set);
1136        ASUS_HANDLE_INIT(rled_set);
1137        ASUS_HANDLE_INIT(pled_set);
1138        ASUS_HANDLE_INIT(gled_set);
1139
1140        ASUS_HANDLE_INIT(ledd_set);
1141
1142        ASUS_HANDLE_INIT(kled_set);
1143        ASUS_HANDLE_INIT(kled_get);
1144
1145        /*
1146         * The HWRS method return informations about the hardware.
1147         * 0x80 bit is for WLAN, 0x100 for Bluetooth.
1148         * The significance of others is yet to be found.
1149         * If we don't find the method, we assume the device are present.
1150         */
1151        status =
1152            acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result);
1153        if (ACPI_FAILURE(status))
1154                hwrs_result = WL_HWRS | BT_HWRS;
1155
1156        if (hwrs_result & WL_HWRS)
1157                ASUS_HANDLE_INIT(wl_switch);
1158        if (hwrs_result & BT_HWRS)
1159                ASUS_HANDLE_INIT(bt_switch);
1160
1161        ASUS_HANDLE_INIT(wireless_status);
1162
1163        ASUS_HANDLE_INIT(brightness_set);
1164        ASUS_HANDLE_INIT(brightness_get);
1165
1166        ASUS_HANDLE_INIT(lcd_switch);
1167
1168        ASUS_HANDLE_INIT(display_set);
1169        ASUS_HANDLE_INIT(display_get);
1170
1171        /*
1172         * There is a lot of models with "ALSL", but a few get
1173         * a real light sens, so we need to check it.
1174         */
1175        if (!ASUS_HANDLE_INIT(ls_switch))
1176                ASUS_HANDLE_INIT(ls_level);
1177
1178        ASUS_HANDLE_INIT(gps_on);
1179        ASUS_HANDLE_INIT(gps_off);
1180        ASUS_HANDLE_INIT(gps_status);
1181
1182        kfree(model);
1183
1184        return AE_OK;
1185}
1186
1187static int asus_input_init(void)
1188{
1189        const struct key_entry *key;
1190        int result;
1191
1192        hotk->inputdev = input_allocate_device();
1193        if (!hotk->inputdev) {
1194                pr_info("Unable to allocate input device\n");
1195                return 0;
1196        }
1197        hotk->inputdev->name = "Asus Laptop extra buttons";
1198        hotk->inputdev->phys = ASUS_HOTK_FILE "/input0";
1199        hotk->inputdev->id.bustype = BUS_HOST;
1200        hotk->inputdev->getkeycode = asus_getkeycode;
1201        hotk->inputdev->setkeycode = asus_setkeycode;
1202
1203        for (key = asus_keymap; key->type != KE_END; key++) {
1204                switch (key->type) {
1205                case KE_KEY:
1206                        set_bit(EV_KEY, hotk->inputdev->evbit);
1207                        set_bit(key->keycode, hotk->inputdev->keybit);
1208                        break;
1209                }
1210        }
1211        result = input_register_device(hotk->inputdev);
1212        if (result) {
1213                pr_info("Unable to register input device\n");
1214                input_free_device(hotk->inputdev);
1215        }
1216        return result;
1217}
1218
1219static int asus_hotk_check(void)
1220{
1221        int result = 0;
1222
1223        result = acpi_bus_get_status(hotk->device);
1224        if (result)
1225                return result;
1226
1227        if (hotk->device->status.present) {
1228                result = asus_hotk_get_info();
1229        } else {
1230                pr_err("Hotkey device not present, aborting\n");
1231                return -EINVAL;
1232        }
1233
1234        return result;
1235}
1236
1237static int asus_hotk_found;
1238
1239static int asus_hotk_add(struct acpi_device *device)
1240{
1241        int result;
1242
1243        if (!device)
1244                return -EINVAL;
1245
1246        pr_notice("Asus Laptop Support version %s\n",
1247               ASUS_LAPTOP_VERSION);
1248
1249        hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
1250        if (!hotk)
1251                return -ENOMEM;
1252
1253        hotk->handle = device->handle;
1254        strcpy(acpi_device_name(device), ASUS_HOTK_DEVICE_NAME);
1255        strcpy(acpi_device_class(device), ASUS_HOTK_CLASS);
1256        device->driver_data = hotk;
1257        hotk->device = device;
1258
1259        result = asus_hotk_check();
1260        if (result)
1261                goto end;
1262
1263        asus_hotk_add_fs();
1264
1265        asus_hotk_found = 1;
1266
1267        /* WLED and BLED are on by default */
1268        write_status(bt_switch_handle, 1, BT_ON);
1269        write_status(wl_switch_handle, 1, WL_ON);
1270
1271        /* If the h/w switch is off, we need to check the real status */
1272        write_status(NULL, read_status(BT_ON), BT_ON);
1273        write_status(NULL, read_status(WL_ON), WL_ON);
1274
1275        /* LCD Backlight is on by default */
1276        write_status(NULL, 1, LCD_ON);
1277
1278        /* Keyboard Backlight is on by default */
1279        if (kled_set_handle)
1280                set_kled_lvl(1);
1281
1282        /* LED display is off by default */
1283        hotk->ledd_status = 0xFFF;
1284
1285        /* Set initial values of light sensor and level */
1286        hotk->light_switch = 1; /* Default to light sensor disabled */
1287        hotk->light_level = 0;  /* level 5 for sensor sensitivity */
1288
1289        if (ls_switch_handle)
1290                set_light_sens_switch(hotk->light_switch);
1291
1292        if (ls_level_handle)
1293                set_light_sens_level(hotk->light_level);
1294
1295        /* GPS is on by default */
1296        write_status(NULL, 1, GPS_ON);
1297
1298end:
1299        if (result) {
1300                kfree(hotk->name);
1301                kfree(hotk);
1302        }
1303
1304        return result;
1305}
1306
1307static int asus_hotk_remove(struct acpi_device *device, int type)
1308{
1309        if (!device || !acpi_driver_data(device))
1310                return -EINVAL;
1311
1312        kfree(hotk->name);
1313        kfree(hotk);
1314
1315        return 0;
1316}
1317
1318static void asus_backlight_exit(void)
1319{
1320        if (asus_backlight_device)
1321                backlight_device_unregister(asus_backlight_device);
1322}
1323
1324#define  ASUS_LED_UNREGISTER(object)                            \
1325        if (object##_led.dev)                                   \
1326                led_classdev_unregister(&object##_led)
1327
1328static void asus_led_exit(void)
1329{
1330        destroy_workqueue(led_workqueue);
1331        ASUS_LED_UNREGISTER(mled);
1332        ASUS_LED_UNREGISTER(tled);
1333        ASUS_LED_UNREGISTER(pled);
1334        ASUS_LED_UNREGISTER(rled);
1335        ASUS_LED_UNREGISTER(gled);
1336        ASUS_LED_UNREGISTER(kled);
1337}
1338
1339static void asus_input_exit(void)
1340{
1341        if (hotk->inputdev)
1342                input_unregister_device(hotk->inputdev);
1343}
1344
1345static void __exit asus_laptop_exit(void)
1346{
1347        asus_backlight_exit();
1348        asus_led_exit();
1349        asus_input_exit();
1350
1351        acpi_bus_unregister_driver(&asus_hotk_driver);
1352        sysfs_remove_group(&asuspf_device->dev.kobj, &asuspf_attribute_group);
1353        platform_device_unregister(asuspf_device);
1354        platform_driver_unregister(&asuspf_driver);
1355}
1356
1357static int asus_backlight_init(struct device *dev)
1358{
1359        struct backlight_device *bd;
1360
1361        if (brightness_set_handle && lcd_switch_handle) {
1362                bd = backlight_device_register(ASUS_HOTK_FILE, dev,
1363                                               NULL, &asusbl_ops);
1364                if (IS_ERR(bd)) {
1365                        pr_err("Could not register asus backlight device\n");
1366                        asus_backlight_device = NULL;
1367                        return PTR_ERR(bd);
1368                }
1369
1370                asus_backlight_device = bd;
1371
1372                bd->props.max_brightness = 15;
1373                bd->props.brightness = read_brightness(NULL);
1374                bd->props.power = FB_BLANK_UNBLANK;
1375                backlight_update_status(bd);
1376        }
1377        return 0;
1378}
1379
1380static int asus_led_register(acpi_handle handle,
1381                             struct led_classdev *ldev, struct device *dev)
1382{
1383        if (!handle)
1384                return 0;
1385
1386        return led_classdev_register(dev, ldev);
1387}
1388
1389#define ASUS_LED_REGISTER(object, device)                               \
1390        asus_led_register(object##_set_handle, &object##_led, device)
1391
1392static int asus_led_init(struct device *dev)
1393{
1394        int rv;
1395
1396        rv = ASUS_LED_REGISTER(mled, dev);
1397        if (rv)
1398                goto out;
1399
1400        rv = ASUS_LED_REGISTER(tled, dev);
1401        if (rv)
1402                goto out1;
1403
1404        rv = ASUS_LED_REGISTER(rled, dev);
1405        if (rv)
1406                goto out2;
1407
1408        rv = ASUS_LED_REGISTER(pled, dev);
1409        if (rv)
1410                goto out3;
1411
1412        rv = ASUS_LED_REGISTER(gled, dev);
1413        if (rv)
1414                goto out4;
1415
1416        if (kled_set_handle && kled_get_handle)
1417                rv = ASUS_LED_REGISTER(kled, dev);
1418        if (rv)
1419                goto out5;
1420
1421        led_workqueue = create_singlethread_workqueue("led_workqueue");
1422        if (!led_workqueue)
1423                goto out6;
1424
1425        return 0;
1426out6:
1427        rv = -ENOMEM;
1428        ASUS_LED_UNREGISTER(kled);
1429out5:
1430        ASUS_LED_UNREGISTER(gled);
1431out4:
1432        ASUS_LED_UNREGISTER(pled);
1433out3:
1434        ASUS_LED_UNREGISTER(rled);
1435out2:
1436        ASUS_LED_UNREGISTER(tled);
1437out1:
1438        ASUS_LED_UNREGISTER(mled);
1439out:
1440        return rv;
1441}
1442
1443static int __init asus_laptop_init(void)
1444{
1445        int result;
1446
1447        if (acpi_disabled)
1448                return -ENODEV;
1449
1450        result = acpi_bus_register_driver(&asus_hotk_driver);
1451        if (result < 0)
1452                return result;
1453
1454        /*
1455         * This is a bit of a kludge.  We only want this module loaded
1456         * for ASUS systems, but there's currently no way to probe the
1457         * ACPI namespace for ASUS HIDs.  So we just return failure if
1458         * we didn't find one, which will cause the module to be
1459         * unloaded.
1460         */
1461        if (!asus_hotk_found) {
1462                acpi_bus_unregister_driver(&asus_hotk_driver);
1463                return -ENODEV;
1464        }
1465
1466        result = asus_input_init();
1467        if (result)
1468                goto fail_input;
1469
1470        /* Register platform stuff */
1471        result = platform_driver_register(&asuspf_driver);
1472        if (result)
1473                goto fail_platform_driver;
1474
1475        asuspf_device = platform_device_alloc(ASUS_HOTK_FILE, -1);
1476        if (!asuspf_device) {
1477                result = -ENOMEM;
1478                goto fail_platform_device1;
1479        }
1480
1481        result = platform_device_add(asuspf_device);
1482        if (result)
1483                goto fail_platform_device2;
1484
1485        result = sysfs_create_group(&asuspf_device->dev.kobj,
1486                                    &asuspf_attribute_group);
1487        if (result)
1488                goto fail_sysfs;
1489
1490        result = asus_led_init(&asuspf_device->dev);
1491        if (result)
1492                goto fail_led;
1493
1494        if (!acpi_video_backlight_support()) {
1495                result = asus_backlight_init(&asuspf_device->dev);
1496                if (result)
1497                        goto fail_backlight;
1498        } else
1499                pr_info("Brightness ignored, must be controlled by "
1500                       "ACPI video driver\n");
1501
1502        return 0;
1503
1504fail_backlight:
1505       asus_led_exit();
1506
1507fail_led:
1508       sysfs_remove_group(&asuspf_device->dev.kobj,
1509                          &asuspf_attribute_group);
1510
1511fail_sysfs:
1512        platform_device_del(asuspf_device);
1513
1514fail_platform_device2:
1515        platform_device_put(asuspf_device);
1516
1517fail_platform_device1:
1518        platform_driver_unregister(&asuspf_driver);
1519
1520fail_platform_driver:
1521        asus_input_exit();
1522
1523fail_input:
1524
1525        return result;
1526}
1527
1528module_init(asus_laptop_init);
1529module_exit(asus_laptop_exit);
1530