linux/drivers/platform/x86/asus-nb-wmi.c
<<
>>
Prefs
   1/*
   2 * Asus Notebooks WMI hotkey driver
   3 *
   4 * Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com>
   5 *
   6 *  This program is free software; you can redistribute it and/or modify
   7 *  it under the terms of the GNU General Public License as published by
   8 *  the Free Software Foundation; either version 2 of the License, or
   9 *  (at your option) any later version.
  10 *
  11 *  This program is distributed in the hope that it will be useful,
  12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *  GNU General Public License for more details.
  15 *
  16 *  You should have received a copy of the GNU General Public License
  17 *  along with this program; if not, write to the Free Software
  18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19 */
  20
  21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  22
  23#include <linux/kernel.h>
  24#include <linux/module.h>
  25#include <linux/init.h>
  26#include <linux/input.h>
  27#include <linux/input/sparse-keymap.h>
  28#include <linux/fb.h>
  29#include <linux/dmi.h>
  30
  31#include "asus-wmi.h"
  32
  33#define ASUS_NB_WMI_FILE        "asus-nb-wmi"
  34
  35MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>");
  36MODULE_DESCRIPTION("Asus Notebooks WMI Hotkey Driver");
  37MODULE_LICENSE("GPL");
  38
  39#define ASUS_NB_WMI_EVENT_GUID  "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
  40
  41MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
  42
  43/*
  44 * WAPF defines the behavior of the Fn+Fx wlan key
  45 * The significance of values is yet to be found, but
  46 * most of the time:
  47 * Bit | Bluetooth | WLAN
  48 *  0  | Hardware  | Hardware
  49 *  1  | Hardware  | Software
  50 *  4  | Software  | Software
  51 */
  52static int wapf = -1;
  53module_param(wapf, uint, 0444);
  54MODULE_PARM_DESC(wapf, "WAPF value");
  55
  56static struct quirk_entry *quirks;
  57
  58static struct quirk_entry quirk_asus_unknown = {
  59        .wapf = 0,
  60};
  61
  62/*
  63 * For those machines that need software to control bt/wifi status
  64 * and can't adjust brightness through ACPI interface
  65 * and have duplicate events(ACPI and WMI) for display toggle
  66 */
  67static struct quirk_entry quirk_asus_x55u = {
  68        .wapf = 4,
  69        .wmi_backlight_power = true,
  70        .no_display_toggle = true,
  71};
  72
  73static struct quirk_entry quirk_asus_wapf4 = {
  74        .wapf = 4,
  75};
  76
  77static struct quirk_entry quirk_asus_x200ca = {
  78        .wapf = 2,
  79};
  80
  81static int dmi_matched(const struct dmi_system_id *dmi)
  82{
  83        quirks = dmi->driver_data;
  84        return 1;
  85}
  86
  87static const struct dmi_system_id asus_quirks[] = {
  88        {
  89                .callback = dmi_matched,
  90                .ident = "ASUSTeK COMPUTER INC. U32U",
  91                .matches = {
  92                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
  93                        DMI_MATCH(DMI_PRODUCT_NAME, "U32U"),
  94                },
  95                /*
  96                 * Note this machine has a Brazos APU, and most Brazos Asus
  97                 * machines need quirk_asus_x55u / wmi_backlight_power but
  98                 * here acpi-video seems to work fine for backlight control.
  99                 */
 100                .driver_data = &quirk_asus_wapf4,
 101        },
 102        {
 103                .callback = dmi_matched,
 104                .ident = "ASUSTeK COMPUTER INC. X401U",
 105                .matches = {
 106                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 107                        DMI_MATCH(DMI_PRODUCT_NAME, "X401U"),
 108                },
 109                .driver_data = &quirk_asus_x55u,
 110        },
 111        {
 112                .callback = dmi_matched,
 113                .ident = "ASUSTeK COMPUTER INC. X401A",
 114                .matches = {
 115                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 116                        DMI_MATCH(DMI_PRODUCT_NAME, "X401A"),
 117                },
 118                .driver_data = &quirk_asus_wapf4,
 119        },
 120        {
 121                .callback = dmi_matched,
 122                .ident = "ASUSTeK COMPUTER INC. X401A1",
 123                .matches = {
 124                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 125                        DMI_MATCH(DMI_PRODUCT_NAME, "X401A1"),
 126                },
 127                .driver_data = &quirk_asus_wapf4,
 128        },
 129        {
 130                .callback = dmi_matched,
 131                .ident = "ASUSTeK COMPUTER INC. X456UA",
 132                .matches = {
 133                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 134                        DMI_MATCH(DMI_PRODUCT_NAME, "X456UA"),
 135                },
 136                .driver_data = &quirk_asus_wapf4,
 137        },
 138        {
 139                .callback = dmi_matched,
 140                .ident = "ASUSTeK COMPUTER INC. X456UF",
 141                .matches = {
 142                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 143                        DMI_MATCH(DMI_PRODUCT_NAME, "X456UF"),
 144                },
 145                .driver_data = &quirk_asus_wapf4,
 146        },
 147        {
 148                .callback = dmi_matched,
 149                .ident = "ASUSTeK COMPUTER INC. X501U",
 150                .matches = {
 151                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 152                        DMI_MATCH(DMI_PRODUCT_NAME, "X501U"),
 153                },
 154                .driver_data = &quirk_asus_x55u,
 155        },
 156        {
 157                .callback = dmi_matched,
 158                .ident = "ASUSTeK COMPUTER INC. X501A",
 159                .matches = {
 160                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 161                        DMI_MATCH(DMI_PRODUCT_NAME, "X501A"),
 162                },
 163                .driver_data = &quirk_asus_wapf4,
 164        },
 165        {
 166                .callback = dmi_matched,
 167                .ident = "ASUSTeK COMPUTER INC. X501A1",
 168                .matches = {
 169                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 170                        DMI_MATCH(DMI_PRODUCT_NAME, "X501A1"),
 171                },
 172                .driver_data = &quirk_asus_wapf4,
 173        },
 174        {
 175                .callback = dmi_matched,
 176                .ident = "ASUSTeK COMPUTER INC. X550CA",
 177                .matches = {
 178                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 179                        DMI_MATCH(DMI_PRODUCT_NAME, "X550CA"),
 180                },
 181                .driver_data = &quirk_asus_wapf4,
 182        },
 183        {
 184                .callback = dmi_matched,
 185                .ident = "ASUSTeK COMPUTER INC. X550CC",
 186                .matches = {
 187                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 188                        DMI_MATCH(DMI_PRODUCT_NAME, "X550CC"),
 189                },
 190                .driver_data = &quirk_asus_wapf4,
 191        },
 192        {
 193                .callback = dmi_matched,
 194                .ident = "ASUSTeK COMPUTER INC. X550CL",
 195                .matches = {
 196                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 197                        DMI_MATCH(DMI_PRODUCT_NAME, "X550CL"),
 198                },
 199                .driver_data = &quirk_asus_wapf4,
 200        },
 201        {
 202                .callback = dmi_matched,
 203                .ident = "ASUSTeK COMPUTER INC. X550VB",
 204                .matches = {
 205                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 206                        DMI_MATCH(DMI_PRODUCT_NAME, "X550VB"),
 207                },
 208                .driver_data = &quirk_asus_wapf4,
 209        },
 210        {
 211                .callback = dmi_matched,
 212                .ident = "ASUSTeK COMPUTER INC. X551CA",
 213                .matches = {
 214                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 215                        DMI_MATCH(DMI_PRODUCT_NAME, "X551CA"),
 216                },
 217                .driver_data = &quirk_asus_wapf4,
 218        },
 219        {
 220                .callback = dmi_matched,
 221                .ident = "ASUSTeK COMPUTER INC. X55A",
 222                .matches = {
 223                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 224                        DMI_MATCH(DMI_PRODUCT_NAME, "X55A"),
 225                },
 226                .driver_data = &quirk_asus_wapf4,
 227        },
 228        {
 229                .callback = dmi_matched,
 230                .ident = "ASUSTeK COMPUTER INC. X55C",
 231                .matches = {
 232                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 233                        DMI_MATCH(DMI_PRODUCT_NAME, "X55C"),
 234                },
 235                .driver_data = &quirk_asus_wapf4,
 236        },
 237        {
 238                .callback = dmi_matched,
 239                .ident = "ASUSTeK COMPUTER INC. X55U",
 240                .matches = {
 241                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 242                        DMI_MATCH(DMI_PRODUCT_NAME, "X55U"),
 243                },
 244                .driver_data = &quirk_asus_x55u,
 245        },
 246        {
 247                .callback = dmi_matched,
 248                .ident = "ASUSTeK COMPUTER INC. X55VD",
 249                .matches = {
 250                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 251                        DMI_MATCH(DMI_PRODUCT_NAME, "X55VD"),
 252                },
 253                .driver_data = &quirk_asus_wapf4,
 254        },
 255        {
 256                .callback = dmi_matched,
 257                .ident = "ASUSTeK COMPUTER INC. X75A",
 258                .matches = {
 259                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 260                        DMI_MATCH(DMI_PRODUCT_NAME, "X75A"),
 261                },
 262                .driver_data = &quirk_asus_wapf4,
 263        },
 264        {
 265                .callback = dmi_matched,
 266                .ident = "ASUSTeK COMPUTER INC. X75VBP",
 267                .matches = {
 268                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 269                        DMI_MATCH(DMI_PRODUCT_NAME, "X75VBP"),
 270                },
 271                .driver_data = &quirk_asus_wapf4,
 272        },
 273        {
 274                .callback = dmi_matched,
 275                .ident = "ASUSTeK COMPUTER INC. X75VD",
 276                .matches = {
 277                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 278                        DMI_MATCH(DMI_PRODUCT_NAME, "X75VD"),
 279                },
 280                .driver_data = &quirk_asus_wapf4,
 281        },
 282        {
 283                .callback = dmi_matched,
 284                .ident = "ASUSTeK COMPUTER INC. 1015E",
 285                .matches = {
 286                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 287                        DMI_MATCH(DMI_PRODUCT_NAME, "1015E"),
 288                },
 289                .driver_data = &quirk_asus_wapf4,
 290        },
 291        {
 292                .callback = dmi_matched,
 293                .ident = "ASUSTeK COMPUTER INC. 1015U",
 294                .matches = {
 295                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 296                        DMI_MATCH(DMI_PRODUCT_NAME, "1015U"),
 297                },
 298                .driver_data = &quirk_asus_wapf4,
 299        },
 300        {
 301                .callback = dmi_matched,
 302                .ident = "ASUSTeK COMPUTER INC. X200CA",
 303                .matches = {
 304                        DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
 305                        DMI_MATCH(DMI_PRODUCT_NAME, "X200CA"),
 306                },
 307                .driver_data = &quirk_asus_x200ca,
 308        },
 309        {},
 310};
 311
 312static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
 313{
 314        quirks = &quirk_asus_unknown;
 315        dmi_check_system(asus_quirks);
 316
 317        driver->quirks = quirks;
 318        driver->panel_power = FB_BLANK_UNBLANK;
 319
 320        /* overwrite the wapf setting if the wapf paramater is specified */
 321        if (wapf != -1)
 322                quirks->wapf = wapf;
 323        else
 324                wapf = quirks->wapf;
 325}
 326
 327static const struct key_entry asus_nb_wmi_keymap[] = {
 328        { KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } },
 329        { KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } },
 330        { KE_KEY, 0x30, { KEY_VOLUMEUP } },
 331        { KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
 332        { KE_KEY, 0x32, { KEY_MUTE } },
 333        { KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */
 334        { KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */
 335        { KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
 336        { KE_KEY, 0x41, { KEY_NEXTSONG } },
 337        { KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */
 338        { KE_KEY, 0x45, { KEY_PLAYPAUSE } },
 339        { KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */
 340        { KE_KEY, 0x50, { KEY_EMAIL } },
 341        { KE_KEY, 0x51, { KEY_WWW } },
 342        { KE_KEY, 0x55, { KEY_CALC } },
 343        { KE_IGNORE, 0x57, },  /* Battery mode */
 344        { KE_IGNORE, 0x58, },  /* AC mode */
 345        { KE_KEY, 0x5C, { KEY_F15 } },  /* Power Gear key */
 346        { KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */
 347        { KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */
 348        { KE_KEY, 0x5F, { KEY_WLAN } }, /* Wireless console Disable */
 349        { KE_KEY, 0x60, { KEY_TOUCHPAD_ON } },
 350        { KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */
 351        { KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */
 352        { KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */
 353        { KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */
 354        { KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */
 355        { KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */
 356        { KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
 357        { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
 358        { KE_IGNORE, 0x6E, },  /* Low Battery notification */
 359        { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
 360        { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
 361        { KE_KEY, 0x82, { KEY_CAMERA } },
 362        { KE_KEY, 0x88, { KEY_RFKILL  } }, /* Radio Toggle Key */
 363        { KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */
 364        { KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */
 365        { KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */
 366        { KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */
 367        { KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */
 368        { KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */
 369        { KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */
 370        { KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
 371        { KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */
 372        { KE_KEY, 0x95, { KEY_MEDIA } },
 373        { KE_KEY, 0x99, { KEY_PHONE } },
 374        { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
 375        { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
 376        { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
 377        { KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */
 378        { KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */
 379        { KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */
 380        { KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */
 381        { KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */
 382        { KE_KEY, 0xB5, { KEY_CALC } },
 383        { KE_KEY, 0xC4, { KEY_KBDILLUMUP } },
 384        { KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },
 385        { KE_IGNORE, 0xC6, },  /* Ambient Light Sensor notification */
 386        { KE_END, 0},
 387};
 388
 389static struct asus_wmi_driver asus_nb_wmi_driver = {
 390        .name = ASUS_NB_WMI_FILE,
 391        .owner = THIS_MODULE,
 392        .event_guid = ASUS_NB_WMI_EVENT_GUID,
 393        .keymap = asus_nb_wmi_keymap,
 394        .input_name = "Asus WMI hotkeys",
 395        .input_phys = ASUS_NB_WMI_FILE "/input0",
 396        .detect_quirks = asus_nb_wmi_quirks,
 397};
 398
 399
 400static int __init asus_nb_wmi_init(void)
 401{
 402        return asus_wmi_register_driver(&asus_nb_wmi_driver);
 403}
 404
 405static void __exit asus_nb_wmi_exit(void)
 406{
 407        asus_wmi_unregister_driver(&asus_nb_wmi_driver);
 408}
 409
 410module_init(asus_nb_wmi_init);
 411module_exit(asus_nb_wmi_exit);
 412