linux/drivers/platform/x86/hp-wmi.c
<<
>>
Prefs
   1/*
   2 * HP WMI hotkeys
   3 *
   4 * Copyright (C) 2008 Red Hat <mjg@redhat.com>
   5 * Copyright (C) 2010, 2011 Anssi Hannula <anssi.hannula@iki.fi>
   6 *
   7 * Portions based on wistron_btns.c:
   8 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
   9 * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
  10 * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
  11 *
  12 *  This program is free software; you can redistribute it and/or modify
  13 *  it under the terms of the GNU General Public License as published by
  14 *  the Free Software Foundation; either version 2 of the License, or
  15 *  (at your option) any later version.
  16 *
  17 *  This program is distributed in the hope that it will be useful,
  18 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 *  GNU General Public License for more details.
  21 *
  22 *  You should have received a copy of the GNU General Public License
  23 *  along with this program; if not, write to the Free Software
  24 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  25 */
  26
  27#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  28
  29#include <linux/kernel.h>
  30#include <linux/module.h>
  31#include <linux/init.h>
  32#include <linux/slab.h>
  33#include <linux/types.h>
  34#include <linux/input.h>
  35#include <linux/input/sparse-keymap.h>
  36#include <linux/platform_device.h>
  37#include <linux/acpi.h>
  38#include <linux/rfkill.h>
  39#include <linux/string.h>
  40
  41MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
  42MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
  43MODULE_LICENSE("GPL");
  44
  45MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
  46MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
  47
  48#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
  49#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
  50
  51#define HPWMI_DISPLAY_QUERY 0x1
  52#define HPWMI_HDDTEMP_QUERY 0x2
  53#define HPWMI_ALS_QUERY 0x3
  54#define HPWMI_HARDWARE_QUERY 0x4
  55#define HPWMI_WIRELESS_QUERY 0x5
  56#define HPWMI_BIOS_QUERY 0x9
  57#define HPWMI_HOTKEY_QUERY 0xc
  58#define HPWMI_FEATURE_QUERY 0xd
  59#define HPWMI_WIRELESS2_QUERY 0x1b
  60#define HPWMI_POSTCODEERROR_QUERY 0x2a
  61
  62enum hp_wmi_radio {
  63        HPWMI_WIFI = 0,
  64        HPWMI_BLUETOOTH = 1,
  65        HPWMI_WWAN = 2,
  66        HPWMI_GPS = 3,
  67};
  68
  69enum hp_wmi_event_ids {
  70        HPWMI_DOCK_EVENT = 1,
  71        HPWMI_PARK_HDD = 2,
  72        HPWMI_SMART_ADAPTER = 3,
  73        HPWMI_BEZEL_BUTTON = 4,
  74        HPWMI_WIRELESS = 5,
  75        HPWMI_CPU_BATTERY_THROTTLE = 6,
  76        HPWMI_LOCK_SWITCH = 7,
  77        HPWMI_LID_SWITCH = 8,
  78        HPWMI_SCREEN_ROTATION = 9,
  79        HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A,
  80        HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B,
  81        HPWMI_PROXIMITY_SENSOR = 0x0C,
  82        HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D,
  83        HPWMI_PEAKSHIFT_PERIOD = 0x0F,
  84        HPWMI_BATTERY_CHARGE_PERIOD = 0x10,
  85};
  86
  87struct bios_args {
  88        u32 signature;
  89        u32 command;
  90        u32 commandtype;
  91        u32 datasize;
  92        u32 data;
  93};
  94
  95struct bios_return {
  96        u32 sigpass;
  97        u32 return_code;
  98};
  99
 100enum hp_return_value {
 101        HPWMI_RET_WRONG_SIGNATURE       = 0x02,
 102        HPWMI_RET_UNKNOWN_COMMAND       = 0x03,
 103        HPWMI_RET_UNKNOWN_CMDTYPE       = 0x04,
 104        HPWMI_RET_INVALID_PARAMETERS    = 0x05,
 105};
 106
 107enum hp_wireless2_bits {
 108        HPWMI_POWER_STATE       = 0x01,
 109        HPWMI_POWER_SOFT        = 0x02,
 110        HPWMI_POWER_BIOS        = 0x04,
 111        HPWMI_POWER_HARD        = 0x08,
 112};
 113
 114#define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \
 115                         != (HPWMI_POWER_BIOS | HPWMI_POWER_HARD))
 116#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
 117
 118struct bios_rfkill2_device_state {
 119        u8 radio_type;
 120        u8 bus_type;
 121        u16 vendor_id;
 122        u16 product_id;
 123        u16 subsys_vendor_id;
 124        u16 subsys_product_id;
 125        u8 rfkill_id;
 126        u8 power;
 127        u8 unknown[4];
 128};
 129
 130/* 7 devices fit into the 128 byte buffer */
 131#define HPWMI_MAX_RFKILL2_DEVICES       7
 132
 133struct bios_rfkill2_state {
 134        u8 unknown[7];
 135        u8 count;
 136        u8 pad[8];
 137        struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES];
 138};
 139
 140static const struct key_entry hp_wmi_keymap[] = {
 141        { KE_KEY, 0x02,   { KEY_BRIGHTNESSUP } },
 142        { KE_KEY, 0x03,   { KEY_BRIGHTNESSDOWN } },
 143        { KE_KEY, 0x20e6, { KEY_PROG1 } },
 144        { KE_KEY, 0x20e8, { KEY_MEDIA } },
 145        { KE_KEY, 0x2142, { KEY_MEDIA } },
 146        { KE_KEY, 0x213b, { KEY_INFO } },
 147        { KE_KEY, 0x2169, { KEY_DIRECTION } },
 148        { KE_KEY, 0x216a, { KEY_SETUP } },
 149        { KE_KEY, 0x231b, { KEY_HELP } },
 150        { KE_END, 0 }
 151};
 152
 153static struct input_dev *hp_wmi_input_dev;
 154static struct platform_device *hp_wmi_platform_dev;
 155
 156static struct rfkill *wifi_rfkill;
 157static struct rfkill *bluetooth_rfkill;
 158static struct rfkill *wwan_rfkill;
 159static struct rfkill *gps_rfkill;
 160
 161struct rfkill2_device {
 162        u8 id;
 163        int num;
 164        struct rfkill *rfkill;
 165};
 166
 167static int rfkill2_count;
 168static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
 169
 170/*
 171 * hp_wmi_perform_query
 172 *
 173 * query:       The commandtype -> What should be queried
 174 * write:       The command -> 0 read, 1 write, 3 ODM specific
 175 * buffer:      Buffer used as input and/or output
 176 * insize:      Size of input buffer
 177 * outsize:     Size of output buffer
 178 *
 179 * returns zero on success
 180 *         an HP WMI query specific error code (which is positive)
 181 *         -EINVAL if the query was not successful at all
 182 *         -EINVAL if the output buffer size exceeds buffersize
 183 *
 184 * Note: The buffersize must at least be the maximum of the input and output
 185 *       size. E.g. Battery info query (0x7) is defined to have 1 byte input
 186 *       and 128 byte output. The caller would do:
 187 *       buffer = kzalloc(128, GFP_KERNEL);
 188 *       ret = hp_wmi_perform_query(0x7, 0, buffer, 1, 128)
 189 */
 190static int hp_wmi_perform_query(int query, int write, void *buffer,
 191                                int insize, int outsize)
 192{
 193        struct bios_return *bios_return;
 194        int actual_outsize;
 195        union acpi_object *obj;
 196        struct bios_args args = {
 197                .signature = 0x55434553,
 198                .command = write ? 0x2 : 0x1,
 199                .commandtype = query,
 200                .datasize = insize,
 201                .data = 0,
 202        };
 203        struct acpi_buffer input = { sizeof(struct bios_args), &args };
 204        struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
 205        u32 rc;
 206
 207        if (WARN_ON(insize > sizeof(args.data)))
 208                return -EINVAL;
 209        memcpy(&args.data, buffer, insize);
 210
 211        wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
 212
 213        obj = output.pointer;
 214
 215        if (!obj)
 216                return -EINVAL;
 217        else if (obj->type != ACPI_TYPE_BUFFER) {
 218                kfree(obj);
 219                return -EINVAL;
 220        }
 221
 222        bios_return = (struct bios_return *)obj->buffer.pointer;
 223        rc = bios_return->return_code;
 224
 225        if (rc) {
 226                if (rc != HPWMI_RET_UNKNOWN_CMDTYPE)
 227                        pr_warn("query 0x%x returned error 0x%x\n", query, rc);
 228                kfree(obj);
 229                return rc;
 230        }
 231
 232        if (!outsize) {
 233                /* ignore output data */
 234                kfree(obj);
 235                return 0;
 236        }
 237
 238        actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return)));
 239        memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize);
 240        memset(buffer + actual_outsize, 0, outsize - actual_outsize);
 241        kfree(obj);
 242        return 0;
 243}
 244
 245static int hp_wmi_display_state(void)
 246{
 247        int state = 0;
 248        int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state,
 249                                       sizeof(state), sizeof(state));
 250        if (ret)
 251                return -EINVAL;
 252        return state;
 253}
 254
 255static int hp_wmi_hddtemp_state(void)
 256{
 257        int state = 0;
 258        int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state,
 259                                       sizeof(state), sizeof(state));
 260        if (ret)
 261                return -EINVAL;
 262        return state;
 263}
 264
 265static int hp_wmi_als_state(void)
 266{
 267        int state = 0;
 268        int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state,
 269                                       sizeof(state), sizeof(state));
 270        if (ret)
 271                return -EINVAL;
 272        return state;
 273}
 274
 275static int hp_wmi_dock_state(void)
 276{
 277        int state = 0;
 278        int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
 279                                       sizeof(state), sizeof(state));
 280
 281        if (ret)
 282                return -EINVAL;
 283
 284        return state & 0x1;
 285}
 286
 287static int hp_wmi_tablet_state(void)
 288{
 289        int state = 0;
 290        int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
 291                                       sizeof(state), sizeof(state));
 292        if (ret)
 293                return ret;
 294
 295        return (state & 0x4) ? 1 : 0;
 296}
 297
 298static int __init hp_wmi_bios_2009_later(void)
 299{
 300        int state = 0;
 301        int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, 0, &state,
 302                                       sizeof(state), sizeof(state));
 303        if (ret)
 304                return ret;
 305
 306        return (state & 0x10) ? 1 : 0;
 307}
 308
 309static int hp_wmi_enable_hotkeys(void)
 310{
 311        int ret;
 312        int query = 0x6e;
 313
 314        ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query),
 315                                   0);
 316
 317        if (ret)
 318                return -EINVAL;
 319        return 0;
 320}
 321
 322static int hp_wmi_set_block(void *data, bool blocked)
 323{
 324        enum hp_wmi_radio r = (enum hp_wmi_radio) data;
 325        int query = BIT(r + 8) | ((!blocked) << r);
 326        int ret;
 327
 328        ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1,
 329                                   &query, sizeof(query), 0);
 330        if (ret)
 331                return -EINVAL;
 332        return 0;
 333}
 334
 335static const struct rfkill_ops hp_wmi_rfkill_ops = {
 336        .set_block = hp_wmi_set_block,
 337};
 338
 339static bool hp_wmi_get_sw_state(enum hp_wmi_radio r)
 340{
 341        int wireless = 0;
 342        int mask;
 343        hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
 344                             &wireless, sizeof(wireless),
 345                             sizeof(wireless));
 346        /* TBD: Pass error */
 347
 348        mask = 0x200 << (r * 8);
 349
 350        if (wireless & mask)
 351                return false;
 352        else
 353                return true;
 354}
 355
 356static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
 357{
 358        int wireless = 0;
 359        int mask;
 360        hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
 361                             &wireless, sizeof(wireless),
 362                             sizeof(wireless));
 363        /* TBD: Pass error */
 364
 365        mask = 0x800 << (r * 8);
 366
 367        if (wireless & mask)
 368                return false;
 369        else
 370                return true;
 371}
 372
 373static int hp_wmi_rfkill2_set_block(void *data, bool blocked)
 374{
 375        int rfkill_id = (int)(long)data;
 376        char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked };
 377
 378        if (hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 1,
 379                                   buffer, sizeof(buffer), 0))
 380                return -EINVAL;
 381        return 0;
 382}
 383
 384static const struct rfkill_ops hp_wmi_rfkill2_ops = {
 385        .set_block = hp_wmi_rfkill2_set_block,
 386};
 387
 388static int hp_wmi_rfkill2_refresh(void)
 389{
 390        int err, i;
 391        struct bios_rfkill2_state state;
 392
 393        err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state,
 394                                   0, sizeof(state));
 395        if (err)
 396                return err;
 397
 398        for (i = 0; i < rfkill2_count; i++) {
 399                int num = rfkill2[i].num;
 400                struct bios_rfkill2_device_state *devstate;
 401                devstate = &state.device[num];
 402
 403                if (num >= state.count ||
 404                    devstate->rfkill_id != rfkill2[i].id) {
 405                        pr_warn("power configuration of the wireless devices unexpectedly changed\n");
 406                        continue;
 407                }
 408
 409                rfkill_set_states(rfkill2[i].rfkill,
 410                                  IS_SWBLOCKED(devstate->power),
 411                                  IS_HWBLOCKED(devstate->power));
 412        }
 413
 414        return 0;
 415}
 416
 417static int hp_wmi_post_code_state(void)
 418{
 419        int state = 0;
 420        int ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, 0, &state,
 421                                       sizeof(state), sizeof(state));
 422        if (ret)
 423                return -EINVAL;
 424        return state;
 425}
 426
 427static ssize_t show_display(struct device *dev, struct device_attribute *attr,
 428                            char *buf)
 429{
 430        int value = hp_wmi_display_state();
 431        if (value < 0)
 432                return -EINVAL;
 433        return sprintf(buf, "%d\n", value);
 434}
 435
 436static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr,
 437                            char *buf)
 438{
 439        int value = hp_wmi_hddtemp_state();
 440        if (value < 0)
 441                return -EINVAL;
 442        return sprintf(buf, "%d\n", value);
 443}
 444
 445static ssize_t show_als(struct device *dev, struct device_attribute *attr,
 446                        char *buf)
 447{
 448        int value = hp_wmi_als_state();
 449        if (value < 0)
 450                return -EINVAL;
 451        return sprintf(buf, "%d\n", value);
 452}
 453
 454static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
 455                         char *buf)
 456{
 457        int value = hp_wmi_dock_state();
 458        if (value < 0)
 459                return -EINVAL;
 460        return sprintf(buf, "%d\n", value);
 461}
 462
 463static ssize_t show_tablet(struct device *dev, struct device_attribute *attr,
 464                         char *buf)
 465{
 466        int value = hp_wmi_tablet_state();
 467        if (value < 0)
 468                return -EINVAL;
 469        return sprintf(buf, "%d\n", value);
 470}
 471
 472static ssize_t show_postcode(struct device *dev, struct device_attribute *attr,
 473                         char *buf)
 474{
 475        /* Get the POST error code of previous boot failure. */
 476        int value = hp_wmi_post_code_state();
 477        if (value < 0)
 478                return -EINVAL;
 479        return sprintf(buf, "0x%x\n", value);
 480}
 481
 482static ssize_t set_als(struct device *dev, struct device_attribute *attr,
 483                       const char *buf, size_t count)
 484{
 485        u32 tmp = simple_strtoul(buf, NULL, 10);
 486        int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp,
 487                                       sizeof(tmp), sizeof(tmp));
 488        if (ret)
 489                return -EINVAL;
 490
 491        return count;
 492}
 493
 494static ssize_t set_postcode(struct device *dev, struct device_attribute *attr,
 495                       const char *buf, size_t count)
 496{
 497        int ret;
 498        u32 tmp;
 499        long unsigned int tmp2;
 500
 501        ret = kstrtoul(buf, 10, &tmp2);
 502        if (ret || tmp2 != 1)
 503                return -EINVAL;
 504
 505        /* Clear the POST error code. It is kept until until cleared. */
 506        tmp = (u32) tmp2;
 507        ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, 1, &tmp,
 508                                       sizeof(tmp), sizeof(tmp));
 509        if (ret)
 510                return -EINVAL;
 511
 512        return count;
 513}
 514
 515static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
 516static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
 517static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
 518static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
 519static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
 520static DEVICE_ATTR(postcode, S_IRUGO | S_IWUSR, show_postcode, set_postcode);
 521
 522static void hp_wmi_notify(u32 value, void *context)
 523{
 524        struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
 525        union acpi_object *obj;
 526        u32 event_id, event_data;
 527        int key_code = 0, ret;
 528        u32 *location;
 529        acpi_status status;
 530
 531        status = wmi_get_event_data(value, &response);
 532        if (status != AE_OK) {
 533                pr_info("bad event status 0x%x\n", status);
 534                return;
 535        }
 536
 537        obj = (union acpi_object *)response.pointer;
 538
 539        if (!obj)
 540                return;
 541        if (obj->type != ACPI_TYPE_BUFFER) {
 542                pr_info("Unknown response received %d\n", obj->type);
 543                kfree(obj);
 544                return;
 545        }
 546
 547        /*
 548         * Depending on ACPI version the concatenation of id and event data
 549         * inside _WED function will result in a 8 or 16 byte buffer.
 550         */
 551        location = (u32 *)obj->buffer.pointer;
 552        if (obj->buffer.length == 8) {
 553                event_id = *location;
 554                event_data = *(location + 1);
 555        } else if (obj->buffer.length == 16) {
 556                event_id = *location;
 557                event_data = *(location + 2);
 558        } else {
 559                pr_info("Unknown buffer length %d\n", obj->buffer.length);
 560                kfree(obj);
 561                return;
 562        }
 563        kfree(obj);
 564
 565        switch (event_id) {
 566        case HPWMI_DOCK_EVENT:
 567                input_report_switch(hp_wmi_input_dev, SW_DOCK,
 568                                    hp_wmi_dock_state());
 569                input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
 570                                    hp_wmi_tablet_state());
 571                input_sync(hp_wmi_input_dev);
 572                break;
 573        case HPWMI_PARK_HDD:
 574                break;
 575        case HPWMI_SMART_ADAPTER:
 576                break;
 577        case HPWMI_BEZEL_BUTTON:
 578                ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
 579                                           &key_code,
 580                                           sizeof(key_code),
 581                                           sizeof(key_code));
 582                if (ret)
 583                        break;
 584
 585                if (!sparse_keymap_report_event(hp_wmi_input_dev,
 586                                                key_code, 1, true))
 587                        pr_info("Unknown key code - 0x%x\n", key_code);
 588                break;
 589        case HPWMI_WIRELESS:
 590                if (rfkill2_count) {
 591                        hp_wmi_rfkill2_refresh();
 592                        break;
 593                }
 594
 595                if (wifi_rfkill)
 596                        rfkill_set_states(wifi_rfkill,
 597                                          hp_wmi_get_sw_state(HPWMI_WIFI),
 598                                          hp_wmi_get_hw_state(HPWMI_WIFI));
 599                if (bluetooth_rfkill)
 600                        rfkill_set_states(bluetooth_rfkill,
 601                                          hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
 602                                          hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
 603                if (wwan_rfkill)
 604                        rfkill_set_states(wwan_rfkill,
 605                                          hp_wmi_get_sw_state(HPWMI_WWAN),
 606                                          hp_wmi_get_hw_state(HPWMI_WWAN));
 607                if (gps_rfkill)
 608                        rfkill_set_states(gps_rfkill,
 609                                          hp_wmi_get_sw_state(HPWMI_GPS),
 610                                          hp_wmi_get_hw_state(HPWMI_GPS));
 611                break;
 612        case HPWMI_CPU_BATTERY_THROTTLE:
 613                pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n");
 614                break;
 615        case HPWMI_LOCK_SWITCH:
 616                break;
 617        case HPWMI_LID_SWITCH:
 618                break;
 619        case HPWMI_SCREEN_ROTATION:
 620                break;
 621        case HPWMI_COOLSENSE_SYSTEM_MOBILE:
 622                break;
 623        case HPWMI_COOLSENSE_SYSTEM_HOT:
 624                break;
 625        case HPWMI_PROXIMITY_SENSOR:
 626                break;
 627        case HPWMI_BACKLIT_KB_BRIGHTNESS:
 628                break;
 629        case HPWMI_PEAKSHIFT_PERIOD:
 630                break;
 631        case HPWMI_BATTERY_CHARGE_PERIOD:
 632                break;
 633        default:
 634                pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data);
 635                break;
 636        }
 637}
 638
 639static int __init hp_wmi_input_setup(void)
 640{
 641        acpi_status status;
 642        int err;
 643
 644        hp_wmi_input_dev = input_allocate_device();
 645        if (!hp_wmi_input_dev)
 646                return -ENOMEM;
 647
 648        hp_wmi_input_dev->name = "HP WMI hotkeys";
 649        hp_wmi_input_dev->phys = "wmi/input0";
 650        hp_wmi_input_dev->id.bustype = BUS_HOST;
 651
 652        __set_bit(EV_SW, hp_wmi_input_dev->evbit);
 653        __set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
 654        __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
 655
 656        err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL);
 657        if (err)
 658                goto err_free_dev;
 659
 660        /* Set initial hardware state */
 661        input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
 662        input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
 663                            hp_wmi_tablet_state());
 664        input_sync(hp_wmi_input_dev);
 665
 666        if (hp_wmi_bios_2009_later() == 4)
 667                hp_wmi_enable_hotkeys();
 668
 669        status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
 670        if (ACPI_FAILURE(status)) {
 671                err = -EIO;
 672                goto err_free_keymap;
 673        }
 674
 675        err = input_register_device(hp_wmi_input_dev);
 676        if (err)
 677                goto err_uninstall_notifier;
 678
 679        return 0;
 680
 681 err_uninstall_notifier:
 682        wmi_remove_notify_handler(HPWMI_EVENT_GUID);
 683 err_free_keymap:
 684        sparse_keymap_free(hp_wmi_input_dev);
 685 err_free_dev:
 686        input_free_device(hp_wmi_input_dev);
 687        return err;
 688}
 689
 690static void hp_wmi_input_destroy(void)
 691{
 692        wmi_remove_notify_handler(HPWMI_EVENT_GUID);
 693        sparse_keymap_free(hp_wmi_input_dev);
 694        input_unregister_device(hp_wmi_input_dev);
 695}
 696
 697static void cleanup_sysfs(struct platform_device *device)
 698{
 699        device_remove_file(&device->dev, &dev_attr_display);
 700        device_remove_file(&device->dev, &dev_attr_hddtemp);
 701        device_remove_file(&device->dev, &dev_attr_als);
 702        device_remove_file(&device->dev, &dev_attr_dock);
 703        device_remove_file(&device->dev, &dev_attr_tablet);
 704        device_remove_file(&device->dev, &dev_attr_postcode);
 705}
 706
 707static int __init hp_wmi_rfkill_setup(struct platform_device *device)
 708{
 709        int err;
 710        int wireless = 0;
 711
 712        err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, &wireless,
 713                                   sizeof(wireless), sizeof(wireless));
 714        if (err)
 715                return err;
 716
 717        if (wireless & 0x1) {
 718                wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
 719                                           RFKILL_TYPE_WLAN,
 720                                           &hp_wmi_rfkill_ops,
 721                                           (void *) HPWMI_WIFI);
 722                if (!wifi_rfkill)
 723                        return -ENOMEM;
 724                rfkill_init_sw_state(wifi_rfkill,
 725                                     hp_wmi_get_sw_state(HPWMI_WIFI));
 726                rfkill_set_hw_state(wifi_rfkill,
 727                                    hp_wmi_get_hw_state(HPWMI_WIFI));
 728                err = rfkill_register(wifi_rfkill);
 729                if (err)
 730                        goto register_wifi_error;
 731        }
 732
 733        if (wireless & 0x2) {
 734                bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev,
 735                                                RFKILL_TYPE_BLUETOOTH,
 736                                                &hp_wmi_rfkill_ops,
 737                                                (void *) HPWMI_BLUETOOTH);
 738                if (!bluetooth_rfkill) {
 739                        err = -ENOMEM;
 740                        goto register_wifi_error;
 741                }
 742                rfkill_init_sw_state(bluetooth_rfkill,
 743                                     hp_wmi_get_sw_state(HPWMI_BLUETOOTH));
 744                rfkill_set_hw_state(bluetooth_rfkill,
 745                                    hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
 746                err = rfkill_register(bluetooth_rfkill);
 747                if (err)
 748                        goto register_bluetooth_error;
 749        }
 750
 751        if (wireless & 0x4) {
 752                wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev,
 753                                           RFKILL_TYPE_WWAN,
 754                                           &hp_wmi_rfkill_ops,
 755                                           (void *) HPWMI_WWAN);
 756                if (!wwan_rfkill) {
 757                        err = -ENOMEM;
 758                        goto register_bluetooth_error;
 759                }
 760                rfkill_init_sw_state(wwan_rfkill,
 761                                     hp_wmi_get_sw_state(HPWMI_WWAN));
 762                rfkill_set_hw_state(wwan_rfkill,
 763                                    hp_wmi_get_hw_state(HPWMI_WWAN));
 764                err = rfkill_register(wwan_rfkill);
 765                if (err)
 766                        goto register_wwan_error;
 767        }
 768
 769        if (wireless & 0x8) {
 770                gps_rfkill = rfkill_alloc("hp-gps", &device->dev,
 771                                                RFKILL_TYPE_GPS,
 772                                                &hp_wmi_rfkill_ops,
 773                                                (void *) HPWMI_GPS);
 774                if (!gps_rfkill) {
 775                        err = -ENOMEM;
 776                        goto register_wwan_error;
 777                }
 778                rfkill_init_sw_state(gps_rfkill,
 779                                     hp_wmi_get_sw_state(HPWMI_GPS));
 780                rfkill_set_hw_state(gps_rfkill,
 781                                    hp_wmi_get_hw_state(HPWMI_GPS));
 782                err = rfkill_register(gps_rfkill);
 783                if (err)
 784                        goto register_gps_error;
 785        }
 786
 787        return 0;
 788register_gps_error:
 789        rfkill_destroy(gps_rfkill);
 790        gps_rfkill = NULL;
 791        if (bluetooth_rfkill)
 792                rfkill_unregister(bluetooth_rfkill);
 793register_wwan_error:
 794        rfkill_destroy(wwan_rfkill);
 795        wwan_rfkill = NULL;
 796        if (gps_rfkill)
 797                rfkill_unregister(gps_rfkill);
 798register_bluetooth_error:
 799        rfkill_destroy(bluetooth_rfkill);
 800        bluetooth_rfkill = NULL;
 801        if (wifi_rfkill)
 802                rfkill_unregister(wifi_rfkill);
 803register_wifi_error:
 804        rfkill_destroy(wifi_rfkill);
 805        wifi_rfkill = NULL;
 806        return err;
 807}
 808
 809static int __init hp_wmi_rfkill2_setup(struct platform_device *device)
 810{
 811        int err, i;
 812        struct bios_rfkill2_state state;
 813        err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state,
 814                                   0, sizeof(state));
 815        if (err)
 816                return err;
 817
 818        if (state.count > HPWMI_MAX_RFKILL2_DEVICES) {
 819                pr_warn("unable to parse 0x1b query output\n");
 820                return -EINVAL;
 821        }
 822
 823        for (i = 0; i < state.count; i++) {
 824                struct rfkill *rfkill;
 825                enum rfkill_type type;
 826                char *name;
 827                switch (state.device[i].radio_type) {
 828                case HPWMI_WIFI:
 829                        type = RFKILL_TYPE_WLAN;
 830                        name = "hp-wifi";
 831                        break;
 832                case HPWMI_BLUETOOTH:
 833                        type = RFKILL_TYPE_BLUETOOTH;
 834                        name = "hp-bluetooth";
 835                        break;
 836                case HPWMI_WWAN:
 837                        type = RFKILL_TYPE_WWAN;
 838                        name = "hp-wwan";
 839                        break;
 840                case HPWMI_GPS:
 841                        type = RFKILL_TYPE_GPS;
 842                        name = "hp-gps";
 843                        break;
 844                default:
 845                        pr_warn("unknown device type 0x%x\n",
 846                                state.device[i].radio_type);
 847                        continue;
 848                }
 849
 850                if (!state.device[i].vendor_id) {
 851                        pr_warn("zero device %d while %d reported\n",
 852                                i, state.count);
 853                        continue;
 854                }
 855
 856                rfkill = rfkill_alloc(name, &device->dev, type,
 857                                      &hp_wmi_rfkill2_ops, (void *)(long)i);
 858                if (!rfkill) {
 859                        err = -ENOMEM;
 860                        goto fail;
 861                }
 862
 863                rfkill2[rfkill2_count].id = state.device[i].rfkill_id;
 864                rfkill2[rfkill2_count].num = i;
 865                rfkill2[rfkill2_count].rfkill = rfkill;
 866
 867                rfkill_init_sw_state(rfkill,
 868                                     IS_SWBLOCKED(state.device[i].power));
 869                rfkill_set_hw_state(rfkill,
 870                                    IS_HWBLOCKED(state.device[i].power));
 871
 872                if (!(state.device[i].power & HPWMI_POWER_BIOS))
 873                        pr_info("device %s blocked by BIOS\n", name);
 874
 875                err = rfkill_register(rfkill);
 876                if (err) {
 877                        rfkill_destroy(rfkill);
 878                        goto fail;
 879                }
 880
 881                rfkill2_count++;
 882        }
 883
 884        return 0;
 885fail:
 886        for (; rfkill2_count > 0; rfkill2_count--) {
 887                rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill);
 888                rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill);
 889        }
 890        return err;
 891}
 892
 893static int __init hp_wmi_bios_setup(struct platform_device *device)
 894{
 895        int err;
 896
 897        /* clear detected rfkill devices */
 898        wifi_rfkill = NULL;
 899        bluetooth_rfkill = NULL;
 900        wwan_rfkill = NULL;
 901        gps_rfkill = NULL;
 902        rfkill2_count = 0;
 903
 904        if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device))
 905                hp_wmi_rfkill2_setup(device);
 906
 907        err = device_create_file(&device->dev, &dev_attr_display);
 908        if (err)
 909                goto add_sysfs_error;
 910        err = device_create_file(&device->dev, &dev_attr_hddtemp);
 911        if (err)
 912                goto add_sysfs_error;
 913        err = device_create_file(&device->dev, &dev_attr_als);
 914        if (err)
 915                goto add_sysfs_error;
 916        err = device_create_file(&device->dev, &dev_attr_dock);
 917        if (err)
 918                goto add_sysfs_error;
 919        err = device_create_file(&device->dev, &dev_attr_tablet);
 920        if (err)
 921                goto add_sysfs_error;
 922        err = device_create_file(&device->dev, &dev_attr_postcode);
 923        if (err)
 924                goto add_sysfs_error;
 925        return 0;
 926
 927add_sysfs_error:
 928        cleanup_sysfs(device);
 929        return err;
 930}
 931
 932static int __exit hp_wmi_bios_remove(struct platform_device *device)
 933{
 934        int i;
 935        cleanup_sysfs(device);
 936
 937        for (i = 0; i < rfkill2_count; i++) {
 938                rfkill_unregister(rfkill2[i].rfkill);
 939                rfkill_destroy(rfkill2[i].rfkill);
 940        }
 941
 942        if (wifi_rfkill) {
 943                rfkill_unregister(wifi_rfkill);
 944                rfkill_destroy(wifi_rfkill);
 945        }
 946        if (bluetooth_rfkill) {
 947                rfkill_unregister(bluetooth_rfkill);
 948                rfkill_destroy(bluetooth_rfkill);
 949        }
 950        if (wwan_rfkill) {
 951                rfkill_unregister(wwan_rfkill);
 952                rfkill_destroy(wwan_rfkill);
 953        }
 954        if (gps_rfkill) {
 955                rfkill_unregister(gps_rfkill);
 956                rfkill_destroy(gps_rfkill);
 957        }
 958
 959        return 0;
 960}
 961
 962static int hp_wmi_resume_handler(struct device *device)
 963{
 964        /*
 965         * Hardware state may have changed while suspended, so trigger
 966         * input events for the current state. As this is a switch,
 967         * the input layer will only actually pass it on if the state
 968         * changed.
 969         */
 970        if (hp_wmi_input_dev) {
 971                input_report_switch(hp_wmi_input_dev, SW_DOCK,
 972                                    hp_wmi_dock_state());
 973                input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
 974                                    hp_wmi_tablet_state());
 975                input_sync(hp_wmi_input_dev);
 976        }
 977
 978        if (rfkill2_count)
 979                hp_wmi_rfkill2_refresh();
 980
 981        if (wifi_rfkill)
 982                rfkill_set_states(wifi_rfkill,
 983                                  hp_wmi_get_sw_state(HPWMI_WIFI),
 984                                  hp_wmi_get_hw_state(HPWMI_WIFI));
 985        if (bluetooth_rfkill)
 986                rfkill_set_states(bluetooth_rfkill,
 987                                  hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
 988                                  hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
 989        if (wwan_rfkill)
 990                rfkill_set_states(wwan_rfkill,
 991                                  hp_wmi_get_sw_state(HPWMI_WWAN),
 992                                  hp_wmi_get_hw_state(HPWMI_WWAN));
 993        if (gps_rfkill)
 994                rfkill_set_states(gps_rfkill,
 995                                  hp_wmi_get_sw_state(HPWMI_GPS),
 996                                  hp_wmi_get_hw_state(HPWMI_GPS));
 997
 998        return 0;
 999}
1000
1001static const struct dev_pm_ops hp_wmi_pm_ops = {
1002        .resume  = hp_wmi_resume_handler,
1003        .restore  = hp_wmi_resume_handler,
1004};
1005
1006static struct platform_driver hp_wmi_driver = {
1007        .driver = {
1008                .name = "hp-wmi",
1009                .owner = THIS_MODULE,
1010                .pm = &hp_wmi_pm_ops,
1011        },
1012        .remove = __exit_p(hp_wmi_bios_remove),
1013};
1014
1015static int __init hp_wmi_init(void)
1016{
1017        int err;
1018        int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
1019        int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
1020
1021        if (!bios_capable && !event_capable)
1022                return -ENODEV;
1023
1024        if (event_capable) {
1025                err = hp_wmi_input_setup();
1026                if (err)
1027                        return err;
1028        }
1029
1030        if (bios_capable) {
1031                hp_wmi_platform_dev =
1032                        platform_device_register_simple("hp-wmi", -1, NULL, 0);
1033                if (IS_ERR(hp_wmi_platform_dev)) {
1034                        err = PTR_ERR(hp_wmi_platform_dev);
1035                        goto err_destroy_input;
1036                }
1037
1038                err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup);
1039                if (err)
1040                        goto err_unregister_device;
1041        }
1042
1043        return 0;
1044
1045err_unregister_device:
1046        platform_device_unregister(hp_wmi_platform_dev);
1047err_destroy_input:
1048        if (event_capable)
1049                hp_wmi_input_destroy();
1050
1051        return err;
1052}
1053module_init(hp_wmi_init);
1054
1055static void __exit hp_wmi_exit(void)
1056{
1057        if (wmi_has_guid(HPWMI_EVENT_GUID))
1058                hp_wmi_input_destroy();
1059
1060        if (hp_wmi_platform_dev) {
1061                platform_device_unregister(hp_wmi_platform_dev);
1062                platform_driver_unregister(&hp_wmi_driver);
1063        }
1064}
1065module_exit(hp_wmi_exit);
1066