linux/drivers/misc/hp-wmi.c
<<
>>
Prefs
   1/*
   2 * HP WMI hotkeys
   3 *
   4 * Copyright (C) 2008 Red Hat <mjg@redhat.com>
   5 *
   6 * Portions based on wistron_btns.c:
   7 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
   8 * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
   9 * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
  10 *
  11 *  This program is free software; you can redistribute it and/or modify
  12 *  it under the terms of the GNU General Public License as published by
  13 *  the Free Software Foundation; either version 2 of the License, or
  14 *  (at your option) any later version.
  15 *
  16 *  This program is distributed in the hope that it will be useful,
  17 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  18 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19 *  GNU General Public License for more details.
  20 *
  21 *  You should have received a copy of the GNU General Public License
  22 *  along with this program; if not, write to the Free Software
  23 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  24 */
  25
  26#include <linux/kernel.h>
  27#include <linux/module.h>
  28#include <linux/init.h>
  29#include <linux/types.h>
  30#include <linux/input.h>
  31#include <acpi/acpi_drivers.h>
  32#include <linux/platform_device.h>
  33#include <linux/acpi.h>
  34#include <linux/rfkill.h>
  35#include <linux/string.h>
  36
  37MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
  38MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
  39MODULE_LICENSE("GPL");
  40
  41MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
  42MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
  43
  44#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
  45#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
  46
  47#define HPWMI_DISPLAY_QUERY 0x1
  48#define HPWMI_HDDTEMP_QUERY 0x2
  49#define HPWMI_ALS_QUERY 0x3
  50#define HPWMI_DOCK_QUERY 0x4
  51#define HPWMI_WIRELESS_QUERY 0x5
  52#define HPWMI_HOTKEY_QUERY 0xc
  53
  54static int __init hp_wmi_bios_setup(struct platform_device *device);
  55static int __exit hp_wmi_bios_remove(struct platform_device *device);
  56
  57struct bios_args {
  58        u32 signature;
  59        u32 command;
  60        u32 commandtype;
  61        u32 datasize;
  62        u32 data;
  63};
  64
  65struct bios_return {
  66        u32 sigpass;
  67        u32 return_code;
  68        u32 value;
  69};
  70
  71struct key_entry {
  72        char type;              /* See KE_* below */
  73        u16 code;
  74        u16 keycode;
  75};
  76
  77enum { KE_KEY, KE_SW, KE_END };
  78
  79static struct key_entry hp_wmi_keymap[] = {
  80        {KE_SW, 0x01, SW_DOCK},
  81        {KE_KEY, 0x02, KEY_BRIGHTNESSUP},
  82        {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
  83        {KE_KEY, 0x20e6, KEY_PROG1},
  84        {KE_KEY, 0x2142, KEY_MEDIA},
  85        {KE_KEY, 0x213b, KEY_INFO},
  86        {KE_KEY, 0x231b, KEY_HELP},
  87        {KE_END, 0}
  88};
  89
  90static struct input_dev *hp_wmi_input_dev;
  91static struct platform_device *hp_wmi_platform_dev;
  92
  93static struct rfkill *wifi_rfkill;
  94static struct rfkill *bluetooth_rfkill;
  95static struct rfkill *wwan_rfkill;
  96
  97static struct platform_driver hp_wmi_driver = {
  98        .driver = {
  99                   .name = "hp-wmi",
 100                   .owner = THIS_MODULE,
 101        },
 102        .probe = hp_wmi_bios_setup,
 103        .remove = hp_wmi_bios_remove,
 104};
 105
 106static int hp_wmi_perform_query(int query, int write, int value)
 107{
 108        struct bios_return bios_return;
 109        acpi_status status;
 110        union acpi_object *obj;
 111        struct bios_args args = {
 112                .signature = 0x55434553,
 113                .command = write ? 0x2 : 0x1,
 114                .commandtype = query,
 115                .datasize = write ? 0x4 : 0,
 116                .data = value,
 117        };
 118        struct acpi_buffer input = { sizeof(struct bios_args), &args };
 119        struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
 120
 121        status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
 122
 123        obj = output.pointer;
 124
 125        if (!obj || obj->type != ACPI_TYPE_BUFFER)
 126                return -EINVAL;
 127
 128        bios_return = *((struct bios_return *)obj->buffer.pointer);
 129        if (bios_return.return_code > 0)
 130                return bios_return.return_code * -1;
 131        else
 132                return bios_return.value;
 133}
 134
 135static int hp_wmi_display_state(void)
 136{
 137        return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0);
 138}
 139
 140static int hp_wmi_hddtemp_state(void)
 141{
 142        return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0);
 143}
 144
 145static int hp_wmi_als_state(void)
 146{
 147        return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0);
 148}
 149
 150static int hp_wmi_dock_state(void)
 151{
 152        return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
 153}
 154
 155static int hp_wmi_wifi_set(void *data, enum rfkill_state state)
 156{
 157        if (state)
 158                return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101);
 159        else
 160                return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100);
 161}
 162
 163static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state)
 164{
 165        if (state)
 166                return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202);
 167        else
 168                return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200);
 169}
 170
 171static int hp_wmi_wwan_set(void *data, enum rfkill_state state)
 172{
 173        if (state)
 174                return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404);
 175        else
 176                return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400);
 177}
 178
 179static int hp_wmi_wifi_state(void)
 180{
 181        int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
 182
 183        if (wireless & 0x100)
 184                return RFKILL_STATE_UNBLOCKED;
 185        else
 186                return RFKILL_STATE_SOFT_BLOCKED;
 187}
 188
 189static int hp_wmi_bluetooth_state(void)
 190{
 191        int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
 192
 193        if (wireless & 0x10000)
 194                return RFKILL_STATE_UNBLOCKED;
 195        else
 196                return RFKILL_STATE_SOFT_BLOCKED;
 197}
 198
 199static int hp_wmi_wwan_state(void)
 200{
 201        int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
 202
 203        if (wireless & 0x1000000)
 204                return RFKILL_STATE_UNBLOCKED;
 205        else
 206                return RFKILL_STATE_SOFT_BLOCKED;
 207}
 208
 209static ssize_t show_display(struct device *dev, struct device_attribute *attr,
 210                            char *buf)
 211{
 212        int value = hp_wmi_display_state();
 213        if (value < 0)
 214                return -EINVAL;
 215        return sprintf(buf, "%d\n", value);
 216}
 217
 218static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr,
 219                            char *buf)
 220{
 221        int value = hp_wmi_hddtemp_state();
 222        if (value < 0)
 223                return -EINVAL;
 224        return sprintf(buf, "%d\n", value);
 225}
 226
 227static ssize_t show_als(struct device *dev, struct device_attribute *attr,
 228                        char *buf)
 229{
 230        int value = hp_wmi_als_state();
 231        if (value < 0)
 232                return -EINVAL;
 233        return sprintf(buf, "%d\n", value);
 234}
 235
 236static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
 237                         char *buf)
 238{
 239        int value = hp_wmi_dock_state();
 240        if (value < 0)
 241                return -EINVAL;
 242        return sprintf(buf, "%d\n", value);
 243}
 244
 245static ssize_t set_als(struct device *dev, struct device_attribute *attr,
 246                       const char *buf, size_t count)
 247{
 248        u32 tmp = simple_strtoul(buf, NULL, 10);
 249        hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp);
 250        return count;
 251}
 252
 253static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
 254static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
 255static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
 256static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
 257
 258static struct key_entry *hp_wmi_get_entry_by_scancode(int code)
 259{
 260        struct key_entry *key;
 261
 262        for (key = hp_wmi_keymap; key->type != KE_END; key++)
 263                if (code == key->code)
 264                        return key;
 265
 266        return NULL;
 267}
 268
 269static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode)
 270{
 271        struct key_entry *key;
 272
 273        for (key = hp_wmi_keymap; key->type != KE_END; key++)
 274                if (key->type == KE_KEY && keycode == key->keycode)
 275                        return key;
 276
 277        return NULL;
 278}
 279
 280static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode)
 281{
 282        struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
 283
 284        if (key && key->type == KE_KEY) {
 285                *keycode = key->keycode;
 286                return 0;
 287        }
 288
 289        return -EINVAL;
 290}
 291
 292static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode)
 293{
 294        struct key_entry *key;
 295        int old_keycode;
 296
 297        if (keycode < 0 || keycode > KEY_MAX)
 298                return -EINVAL;
 299
 300        key = hp_wmi_get_entry_by_scancode(scancode);
 301        if (key && key->type == KE_KEY) {
 302                old_keycode = key->keycode;
 303                key->keycode = keycode;
 304                set_bit(keycode, dev->keybit);
 305                if (!hp_wmi_get_entry_by_keycode(old_keycode))
 306                        clear_bit(old_keycode, dev->keybit);
 307                return 0;
 308        }
 309
 310        return -EINVAL;
 311}
 312
 313static void hp_wmi_notify(u32 value, void *context)
 314{
 315        struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
 316        static struct key_entry *key;
 317        union acpi_object *obj;
 318
 319        wmi_get_event_data(value, &response);
 320
 321        obj = (union acpi_object *)response.pointer;
 322
 323        if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) {
 324                int eventcode = *((u8 *) obj->buffer.pointer);
 325                if (eventcode == 0x4)
 326                        eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
 327                                                         0);
 328                key = hp_wmi_get_entry_by_scancode(eventcode);
 329                if (key) {
 330                        switch (key->type) {
 331                        case KE_KEY:
 332                                input_report_key(hp_wmi_input_dev,
 333                                                 key->keycode, 1);
 334                                input_sync(hp_wmi_input_dev);
 335                                input_report_key(hp_wmi_input_dev,
 336                                                 key->keycode, 0);
 337                                input_sync(hp_wmi_input_dev);
 338                                break;
 339                        case KE_SW:
 340                                input_report_switch(hp_wmi_input_dev,
 341                                                    key->keycode,
 342                                                    hp_wmi_dock_state());
 343                                input_sync(hp_wmi_input_dev);
 344                                break;
 345                        }
 346                } else if (eventcode == 0x5) {
 347                        if (wifi_rfkill)
 348                                rfkill_force_state(wifi_rfkill,
 349                                                   hp_wmi_wifi_state());
 350                        if (bluetooth_rfkill)
 351                                rfkill_force_state(bluetooth_rfkill,
 352                                                   hp_wmi_bluetooth_state());
 353                        if (wwan_rfkill)
 354                                rfkill_force_state(wwan_rfkill,
 355                                                   hp_wmi_wwan_state());
 356                } else
 357                        printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
 358                               eventcode);
 359        } else
 360                printk(KERN_INFO "HP WMI: Unknown response received\n");
 361}
 362
 363static int __init hp_wmi_input_setup(void)
 364{
 365        struct key_entry *key;
 366        int err;
 367
 368        hp_wmi_input_dev = input_allocate_device();
 369
 370        hp_wmi_input_dev->name = "HP WMI hotkeys";
 371        hp_wmi_input_dev->phys = "wmi/input0";
 372        hp_wmi_input_dev->id.bustype = BUS_HOST;
 373        hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
 374        hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
 375
 376        for (key = hp_wmi_keymap; key->type != KE_END; key++) {
 377                switch (key->type) {
 378                case KE_KEY:
 379                        set_bit(EV_KEY, hp_wmi_input_dev->evbit);
 380                        set_bit(key->keycode, hp_wmi_input_dev->keybit);
 381                        break;
 382                case KE_SW:
 383                        set_bit(EV_SW, hp_wmi_input_dev->evbit);
 384                        set_bit(key->keycode, hp_wmi_input_dev->swbit);
 385                        break;
 386                }
 387        }
 388
 389        err = input_register_device(hp_wmi_input_dev);
 390
 391        if (err) {
 392                input_free_device(hp_wmi_input_dev);
 393                return err;
 394        }
 395
 396        return 0;
 397}
 398
 399static void cleanup_sysfs(struct platform_device *device)
 400{
 401        device_remove_file(&device->dev, &dev_attr_display);
 402        device_remove_file(&device->dev, &dev_attr_hddtemp);
 403        device_remove_file(&device->dev, &dev_attr_als);
 404        device_remove_file(&device->dev, &dev_attr_dock);
 405}
 406
 407static int __init hp_wmi_bios_setup(struct platform_device *device)
 408{
 409        int err;
 410        int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
 411
 412        err = device_create_file(&device->dev, &dev_attr_display);
 413        if (err)
 414                goto add_sysfs_error;
 415        err = device_create_file(&device->dev, &dev_attr_hddtemp);
 416        if (err)
 417                goto add_sysfs_error;
 418        err = device_create_file(&device->dev, &dev_attr_als);
 419        if (err)
 420                goto add_sysfs_error;
 421        err = device_create_file(&device->dev, &dev_attr_dock);
 422        if (err)
 423                goto add_sysfs_error;
 424
 425        if (wireless & 0x1) {
 426                wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
 427                wifi_rfkill->name = "hp-wifi";
 428                wifi_rfkill->state = hp_wmi_wifi_state();
 429                wifi_rfkill->toggle_radio = hp_wmi_wifi_set;
 430                wifi_rfkill->user_claim_unsupported = 1;
 431                rfkill_register(wifi_rfkill);
 432        }
 433
 434        if (wireless & 0x2) {
 435                bluetooth_rfkill = rfkill_allocate(&device->dev,
 436                                                   RFKILL_TYPE_BLUETOOTH);
 437                bluetooth_rfkill->name = "hp-bluetooth";
 438                bluetooth_rfkill->state = hp_wmi_bluetooth_state();
 439                bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set;
 440                bluetooth_rfkill->user_claim_unsupported = 1;
 441                rfkill_register(bluetooth_rfkill);
 442        }
 443
 444        if (wireless & 0x4) {
 445                wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
 446                wwan_rfkill->name = "hp-wwan";
 447                wwan_rfkill->state = hp_wmi_wwan_state();
 448                wwan_rfkill->toggle_radio = hp_wmi_wwan_set;
 449                wwan_rfkill->user_claim_unsupported = 1;
 450                rfkill_register(wwan_rfkill);
 451        }
 452
 453        return 0;
 454add_sysfs_error:
 455        cleanup_sysfs(device);
 456        return err;
 457}
 458
 459static int __exit hp_wmi_bios_remove(struct platform_device *device)
 460{
 461        cleanup_sysfs(device);
 462
 463        if (wifi_rfkill)
 464                rfkill_unregister(wifi_rfkill);
 465        if (bluetooth_rfkill)
 466                rfkill_unregister(bluetooth_rfkill);
 467        if (wwan_rfkill)
 468                rfkill_unregister(wwan_rfkill);
 469
 470        return 0;
 471}
 472
 473static int __init hp_wmi_init(void)
 474{
 475        int err;
 476
 477        if (wmi_has_guid(HPWMI_EVENT_GUID)) {
 478                err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
 479                                                 hp_wmi_notify, NULL);
 480                if (!err)
 481                        hp_wmi_input_setup();
 482        }
 483
 484        if (wmi_has_guid(HPWMI_BIOS_GUID)) {
 485                err = platform_driver_register(&hp_wmi_driver);
 486                if (err)
 487                        return 0;
 488                hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
 489                if (!hp_wmi_platform_dev) {
 490                        platform_driver_unregister(&hp_wmi_driver);
 491                        return 0;
 492                }
 493                platform_device_add(hp_wmi_platform_dev);
 494        }
 495
 496        return 0;
 497}
 498
 499static void __exit hp_wmi_exit(void)
 500{
 501        if (wmi_has_guid(HPWMI_EVENT_GUID)) {
 502                wmi_remove_notify_handler(HPWMI_EVENT_GUID);
 503                input_unregister_device(hp_wmi_input_dev);
 504        }
 505        if (hp_wmi_platform_dev) {
 506                platform_device_del(hp_wmi_platform_dev);
 507                platform_driver_unregister(&hp_wmi_driver);
 508        }
 509}
 510
 511module_init(hp_wmi_init);
 512module_exit(hp_wmi_exit);
 513