linux/drivers/platform/x86/acer-wmi.c
<<
>>
Prefs
   1/*
   2 *  Acer WMI Laptop Extras
   3 *
   4 *  Copyright (C) 2007-2009     Carlos Corbacho <carlos@strangeworlds.co.uk>
   5 *
   6 *  Based on acer_acpi:
   7 *    Copyright (C) 2005-2007   E.M. Smith
   8 *    Copyright (C) 2007-2008   Carlos Corbacho <cathectic@gmail.com>
   9 *
  10 *  This program is free software; you can redistribute it and/or modify
  11 *  it under the terms of the GNU General Public License as published by
  12 *  the Free Software Foundation; either version 2 of the License, or
  13 *  (at your option) any later version.
  14 *
  15 *  This program is distributed in the hope that it will be useful,
  16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 *  GNU General Public License for more details.
  19 *
  20 *  You should have received a copy of the GNU General Public License
  21 *  along with this program; if not, write to the Free Software
  22 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  23 */
  24
  25#include <linux/kernel.h>
  26#include <linux/module.h>
  27#include <linux/init.h>
  28#include <linux/types.h>
  29#include <linux/dmi.h>
  30#include <linux/fb.h>
  31#include <linux/backlight.h>
  32#include <linux/leds.h>
  33#include <linux/platform_device.h>
  34#include <linux/acpi.h>
  35#include <linux/i8042.h>
  36#include <linux/rfkill.h>
  37#include <linux/workqueue.h>
  38#include <linux/debugfs.h>
  39
  40#include <acpi/acpi_drivers.h>
  41
  42MODULE_AUTHOR("Carlos Corbacho");
  43MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
  44MODULE_LICENSE("GPL");
  45
  46#define ACER_LOGPREFIX "acer-wmi: "
  47#define ACER_ERR KERN_ERR ACER_LOGPREFIX
  48#define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
  49#define ACER_INFO KERN_INFO ACER_LOGPREFIX
  50
  51/*
  52 * The following defines quirks to get some specific functions to work
  53 * which are known to not be supported over ACPI-WMI (such as the mail LED
  54 * on WMID based Acer's)
  55 */
  56struct acer_quirks {
  57        const char *vendor;
  58        const char *model;
  59        u16 quirks;
  60};
  61
  62/*
  63 * Magic Number
  64 * Meaning is unknown - this number is required for writing to ACPI for AMW0
  65 * (it's also used in acerhk when directly accessing the BIOS)
  66 */
  67#define ACER_AMW0_WRITE 0x9610
  68
  69/*
  70 * Bit masks for the AMW0 interface
  71 */
  72#define ACER_AMW0_WIRELESS_MASK  0x35
  73#define ACER_AMW0_BLUETOOTH_MASK 0x34
  74#define ACER_AMW0_MAILLED_MASK   0x31
  75
  76/*
  77 * Method IDs for WMID interface
  78 */
  79#define ACER_WMID_GET_WIRELESS_METHODID         1
  80#define ACER_WMID_GET_BLUETOOTH_METHODID        2
  81#define ACER_WMID_GET_BRIGHTNESS_METHODID       3
  82#define ACER_WMID_SET_WIRELESS_METHODID         4
  83#define ACER_WMID_SET_BLUETOOTH_METHODID        5
  84#define ACER_WMID_SET_BRIGHTNESS_METHODID       6
  85#define ACER_WMID_GET_THREEG_METHODID           10
  86#define ACER_WMID_SET_THREEG_METHODID           11
  87
  88/*
  89 * Acer ACPI method GUIDs
  90 */
  91#define AMW0_GUID1              "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
  92#define AMW0_GUID2              "431F16ED-0C2B-444C-B267-27DEB140CF9C"
  93#define WMID_GUID1              "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
  94#define WMID_GUID2              "95764E09-FB56-4e83-B31A-37761F60994A"
  95
  96MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
  97MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
  98
  99/* Temporary workaround until the WMI sysfs interface goes in */
 100MODULE_ALIAS("dmi:*:*Acer*:*:");
 101
 102/*
 103 * Interface capability flags
 104 */
 105#define ACER_CAP_MAILLED                (1<<0)
 106#define ACER_CAP_WIRELESS               (1<<1)
 107#define ACER_CAP_BLUETOOTH              (1<<2)
 108#define ACER_CAP_BRIGHTNESS             (1<<3)
 109#define ACER_CAP_THREEG                 (1<<4)
 110#define ACER_CAP_ANY                    (0xFFFFFFFF)
 111
 112/*
 113 * Interface type flags
 114 */
 115enum interface_flags {
 116        ACER_AMW0,
 117        ACER_AMW0_V2,
 118        ACER_WMID,
 119};
 120
 121#define ACER_DEFAULT_WIRELESS  0
 122#define ACER_DEFAULT_BLUETOOTH 0
 123#define ACER_DEFAULT_MAILLED   0
 124#define ACER_DEFAULT_THREEG    0
 125
 126static int max_brightness = 0xF;
 127
 128static int mailled = -1;
 129static int brightness = -1;
 130static int threeg = -1;
 131static int force_series;
 132
 133module_param(mailled, int, 0444);
 134module_param(brightness, int, 0444);
 135module_param(threeg, int, 0444);
 136module_param(force_series, int, 0444);
 137MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
 138MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
 139MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
 140MODULE_PARM_DESC(force_series, "Force a different laptop series");
 141
 142struct acer_data {
 143        int mailled;
 144        int threeg;
 145        int brightness;
 146};
 147
 148struct acer_debug {
 149        struct dentry *root;
 150        struct dentry *devices;
 151        u32 wmid_devices;
 152};
 153
 154static struct rfkill *wireless_rfkill;
 155static struct rfkill *bluetooth_rfkill;
 156
 157/* Each low-level interface must define at least some of the following */
 158struct wmi_interface {
 159        /* The WMI device type */
 160        u32 type;
 161
 162        /* The capabilities this interface provides */
 163        u32 capability;
 164
 165        /* Private data for the current interface */
 166        struct acer_data data;
 167
 168        /* debugfs entries associated with this interface */
 169        struct acer_debug debug;
 170};
 171
 172/* The static interface pointer, points to the currently detected interface */
 173static struct wmi_interface *interface;
 174
 175/*
 176 * Embedded Controller quirks
 177 * Some laptops require us to directly access the EC to either enable or query
 178 * features that are not available through WMI.
 179 */
 180
 181struct quirk_entry {
 182        u8 wireless;
 183        u8 mailled;
 184        s8 brightness;
 185        u8 bluetooth;
 186};
 187
 188static struct quirk_entry *quirks;
 189
 190static void set_quirks(void)
 191{
 192        if (!interface)
 193                return;
 194
 195        if (quirks->mailled)
 196                interface->capability |= ACER_CAP_MAILLED;
 197
 198        if (quirks->brightness)
 199                interface->capability |= ACER_CAP_BRIGHTNESS;
 200}
 201
 202static int dmi_matched(const struct dmi_system_id *dmi)
 203{
 204        quirks = dmi->driver_data;
 205        return 0;
 206}
 207
 208static struct quirk_entry quirk_unknown = {
 209};
 210
 211static struct quirk_entry quirk_acer_aspire_1520 = {
 212        .brightness = -1,
 213};
 214
 215static struct quirk_entry quirk_acer_travelmate_2490 = {
 216        .mailled = 1,
 217};
 218
 219/* This AMW0 laptop has no bluetooth */
 220static struct quirk_entry quirk_medion_md_98300 = {
 221        .wireless = 1,
 222};
 223
 224static struct quirk_entry quirk_fujitsu_amilo_li_1718 = {
 225        .wireless = 2,
 226};
 227
 228/* The Aspire One has a dummy ACPI-WMI interface - disable it */
 229static struct dmi_system_id __devinitdata acer_blacklist[] = {
 230        {
 231                .ident = "Acer Aspire One (SSD)",
 232                .matches = {
 233                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 234                        DMI_MATCH(DMI_PRODUCT_NAME, "AOA110"),
 235                },
 236        },
 237        {
 238                .ident = "Acer Aspire One (HDD)",
 239                .matches = {
 240                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 241                        DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"),
 242                },
 243        },
 244        {}
 245};
 246
 247static struct dmi_system_id acer_quirks[] = {
 248        {
 249                .callback = dmi_matched,
 250                .ident = "Acer Aspire 1360",
 251                .matches = {
 252                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 253                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
 254                },
 255                .driver_data = &quirk_acer_aspire_1520,
 256        },
 257        {
 258                .callback = dmi_matched,
 259                .ident = "Acer Aspire 1520",
 260                .matches = {
 261                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 262                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1520"),
 263                },
 264                .driver_data = &quirk_acer_aspire_1520,
 265        },
 266        {
 267                .callback = dmi_matched,
 268                .ident = "Acer Aspire 3100",
 269                .matches = {
 270                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 271                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
 272                },
 273                .driver_data = &quirk_acer_travelmate_2490,
 274        },
 275        {
 276                .callback = dmi_matched,
 277                .ident = "Acer Aspire 3610",
 278                .matches = {
 279                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 280                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3610"),
 281                },
 282                .driver_data = &quirk_acer_travelmate_2490,
 283        },
 284        {
 285                .callback = dmi_matched,
 286                .ident = "Acer Aspire 5100",
 287                .matches = {
 288                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 289                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
 290                },
 291                .driver_data = &quirk_acer_travelmate_2490,
 292        },
 293        {
 294                .callback = dmi_matched,
 295                .ident = "Acer Aspire 5610",
 296                .matches = {
 297                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 298                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
 299                },
 300                .driver_data = &quirk_acer_travelmate_2490,
 301        },
 302        {
 303                .callback = dmi_matched,
 304                .ident = "Acer Aspire 5630",
 305                .matches = {
 306                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 307                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
 308                },
 309                .driver_data = &quirk_acer_travelmate_2490,
 310        },
 311        {
 312                .callback = dmi_matched,
 313                .ident = "Acer Aspire 5650",
 314                .matches = {
 315                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 316                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
 317                },
 318                .driver_data = &quirk_acer_travelmate_2490,
 319        },
 320        {
 321                .callback = dmi_matched,
 322                .ident = "Acer Aspire 5680",
 323                .matches = {
 324                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 325                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
 326                },
 327                .driver_data = &quirk_acer_travelmate_2490,
 328        },
 329        {
 330                .callback = dmi_matched,
 331                .ident = "Acer Aspire 9110",
 332                .matches = {
 333                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 334                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
 335                },
 336                .driver_data = &quirk_acer_travelmate_2490,
 337        },
 338        {
 339                .callback = dmi_matched,
 340                .ident = "Acer TravelMate 2490",
 341                .matches = {
 342                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 343                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
 344                },
 345                .driver_data = &quirk_acer_travelmate_2490,
 346        },
 347        {
 348                .callback = dmi_matched,
 349                .ident = "Acer TravelMate 4200",
 350                .matches = {
 351                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 352                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4200"),
 353                },
 354                .driver_data = &quirk_acer_travelmate_2490,
 355        },
 356        {
 357                .callback = dmi_matched,
 358                .ident = "Fujitsu Siemens Amilo Li 1718",
 359                .matches = {
 360                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 361                        DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Li 1718"),
 362                },
 363                .driver_data = &quirk_fujitsu_amilo_li_1718,
 364        },
 365        {
 366                .callback = dmi_matched,
 367                .ident = "Medion MD 98300",
 368                .matches = {
 369                        DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
 370                        DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
 371                },
 372                .driver_data = &quirk_medion_md_98300,
 373        },
 374        {}
 375};
 376
 377/* Find which quirks are needed for a particular vendor/ model pair */
 378static void find_quirks(void)
 379{
 380        if (!force_series) {
 381                dmi_check_system(acer_quirks);
 382        } else if (force_series == 2490) {
 383                quirks = &quirk_acer_travelmate_2490;
 384        }
 385
 386        if (quirks == NULL)
 387                quirks = &quirk_unknown;
 388
 389        set_quirks();
 390}
 391
 392/*
 393 * General interface convenience methods
 394 */
 395
 396static bool has_cap(u32 cap)
 397{
 398        if ((interface->capability & cap) != 0)
 399                return 1;
 400
 401        return 0;
 402}
 403
 404/*
 405 * AMW0 (V1) interface
 406 */
 407struct wmab_args {
 408        u32 eax;
 409        u32 ebx;
 410        u32 ecx;
 411        u32 edx;
 412};
 413
 414struct wmab_ret {
 415        u32 eax;
 416        u32 ebx;
 417        u32 ecx;
 418        u32 edx;
 419        u32 eex;
 420};
 421
 422static acpi_status wmab_execute(struct wmab_args *regbuf,
 423struct acpi_buffer *result)
 424{
 425        struct acpi_buffer input;
 426        acpi_status status;
 427        input.length = sizeof(struct wmab_args);
 428        input.pointer = (u8 *)regbuf;
 429
 430        status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
 431
 432        return status;
 433}
 434
 435static acpi_status AMW0_get_u32(u32 *value, u32 cap,
 436struct wmi_interface *iface)
 437{
 438        int err;
 439        u8 result;
 440
 441        switch (cap) {
 442        case ACER_CAP_MAILLED:
 443                switch (quirks->mailled) {
 444                default:
 445                        err = ec_read(0xA, &result);
 446                        if (err)
 447                                return AE_ERROR;
 448                        *value = (result >> 7) & 0x1;
 449                        return AE_OK;
 450                }
 451                break;
 452        case ACER_CAP_WIRELESS:
 453                switch (quirks->wireless) {
 454                case 1:
 455                        err = ec_read(0x7B, &result);
 456                        if (err)
 457                                return AE_ERROR;
 458                        *value = result & 0x1;
 459                        return AE_OK;
 460                case 2:
 461                        err = ec_read(0x71, &result);
 462                        if (err)
 463                                return AE_ERROR;
 464                        *value = result & 0x1;
 465                        return AE_OK;
 466                default:
 467                        err = ec_read(0xA, &result);
 468                        if (err)
 469                                return AE_ERROR;
 470                        *value = (result >> 2) & 0x1;
 471                        return AE_OK;
 472                }
 473                break;
 474        case ACER_CAP_BLUETOOTH:
 475                switch (quirks->bluetooth) {
 476                default:
 477                        err = ec_read(0xA, &result);
 478                        if (err)
 479                                return AE_ERROR;
 480                        *value = (result >> 4) & 0x1;
 481                        return AE_OK;
 482                }
 483                break;
 484        case ACER_CAP_BRIGHTNESS:
 485                switch (quirks->brightness) {
 486                default:
 487                        err = ec_read(0x83, &result);
 488                        if (err)
 489                                return AE_ERROR;
 490                        *value = result;
 491                        return AE_OK;
 492                }
 493                break;
 494        default:
 495                return AE_ERROR;
 496        }
 497        return AE_OK;
 498}
 499
 500static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
 501{
 502        struct wmab_args args;
 503
 504        args.eax = ACER_AMW0_WRITE;
 505        args.ebx = value ? (1<<8) : 0;
 506        args.ecx = args.edx = 0;
 507
 508        switch (cap) {
 509        case ACER_CAP_MAILLED:
 510                if (value > 1)
 511                        return AE_BAD_PARAMETER;
 512                args.ebx |= ACER_AMW0_MAILLED_MASK;
 513                break;
 514        case ACER_CAP_WIRELESS:
 515                if (value > 1)
 516                        return AE_BAD_PARAMETER;
 517                args.ebx |= ACER_AMW0_WIRELESS_MASK;
 518                break;
 519        case ACER_CAP_BLUETOOTH:
 520                if (value > 1)
 521                        return AE_BAD_PARAMETER;
 522                args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
 523                break;
 524        case ACER_CAP_BRIGHTNESS:
 525                if (value > max_brightness)
 526                        return AE_BAD_PARAMETER;
 527                switch (quirks->brightness) {
 528                default:
 529                        return ec_write(0x83, value);
 530                        break;
 531                }
 532        default:
 533                return AE_ERROR;
 534        }
 535
 536        /* Actually do the set */
 537        return wmab_execute(&args, NULL);
 538}
 539
 540static acpi_status AMW0_find_mailled(void)
 541{
 542        struct wmab_args args;
 543        struct wmab_ret ret;
 544        acpi_status status = AE_OK;
 545        struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
 546        union acpi_object *obj;
 547
 548        args.eax = 0x86;
 549        args.ebx = args.ecx = args.edx = 0;
 550
 551        status = wmab_execute(&args, &out);
 552        if (ACPI_FAILURE(status))
 553                return status;
 554
 555        obj = (union acpi_object *) out.pointer;
 556        if (obj && obj->type == ACPI_TYPE_BUFFER &&
 557        obj->buffer.length == sizeof(struct wmab_ret)) {
 558                ret = *((struct wmab_ret *) obj->buffer.pointer);
 559        } else {
 560                return AE_ERROR;
 561        }
 562
 563        if (ret.eex & 0x1)
 564                interface->capability |= ACER_CAP_MAILLED;
 565
 566        kfree(out.pointer);
 567
 568        return AE_OK;
 569}
 570
 571static acpi_status AMW0_set_capabilities(void)
 572{
 573        struct wmab_args args;
 574        struct wmab_ret ret;
 575        acpi_status status = AE_OK;
 576        struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
 577        union acpi_object *obj;
 578
 579        /*
 580         * On laptops with this strange GUID (non Acer), normal probing doesn't
 581         * work.
 582         */
 583        if (wmi_has_guid(AMW0_GUID2)) {
 584                interface->capability |= ACER_CAP_WIRELESS;
 585                return AE_OK;
 586        }
 587
 588        args.eax = ACER_AMW0_WRITE;
 589        args.ecx = args.edx = 0;
 590
 591        args.ebx = 0xa2 << 8;
 592        args.ebx |= ACER_AMW0_WIRELESS_MASK;
 593
 594        status = wmab_execute(&args, &out);
 595        if (ACPI_FAILURE(status))
 596                return status;
 597
 598        obj = (union acpi_object *) out.pointer;
 599        if (obj && obj->type == ACPI_TYPE_BUFFER &&
 600        obj->buffer.length == sizeof(struct wmab_ret)) {
 601                ret = *((struct wmab_ret *) obj->buffer.pointer);
 602        } else {
 603                return AE_ERROR;
 604        }
 605
 606        if (ret.eax & 0x1)
 607                interface->capability |= ACER_CAP_WIRELESS;
 608
 609        args.ebx = 2 << 8;
 610        args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
 611
 612        status = wmab_execute(&args, &out);
 613        if (ACPI_FAILURE(status))
 614                return status;
 615
 616        obj = (union acpi_object *) out.pointer;
 617        if (obj && obj->type == ACPI_TYPE_BUFFER
 618        && obj->buffer.length == sizeof(struct wmab_ret)) {
 619                ret = *((struct wmab_ret *) obj->buffer.pointer);
 620        } else {
 621                return AE_ERROR;
 622        }
 623
 624        if (ret.eax & 0x1)
 625                interface->capability |= ACER_CAP_BLUETOOTH;
 626
 627        kfree(out.pointer);
 628
 629        /*
 630         * This appears to be safe to enable, since all Wistron based laptops
 631         * appear to use the same EC register for brightness, even if they
 632         * differ for wireless, etc
 633         */
 634        if (quirks->brightness >= 0)
 635                interface->capability |= ACER_CAP_BRIGHTNESS;
 636
 637        return AE_OK;
 638}
 639
 640static struct wmi_interface AMW0_interface = {
 641        .type = ACER_AMW0,
 642};
 643
 644static struct wmi_interface AMW0_V2_interface = {
 645        .type = ACER_AMW0_V2,
 646};
 647
 648/*
 649 * New interface (The WMID interface)
 650 */
 651static acpi_status
 652WMI_execute_u32(u32 method_id, u32 in, u32 *out)
 653{
 654        struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
 655        struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
 656        union acpi_object *obj;
 657        u32 tmp;
 658        acpi_status status;
 659
 660        status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
 661
 662        if (ACPI_FAILURE(status))
 663                return status;
 664
 665        obj = (union acpi_object *) result.pointer;
 666        if (obj && obj->type == ACPI_TYPE_BUFFER &&
 667                obj->buffer.length == sizeof(u32)) {
 668                tmp = *((u32 *) obj->buffer.pointer);
 669        } else {
 670                tmp = 0;
 671        }
 672
 673        if (out)
 674                *out = tmp;
 675
 676        kfree(result.pointer);
 677
 678        return status;
 679}
 680
 681static acpi_status WMID_get_u32(u32 *value, u32 cap,
 682struct wmi_interface *iface)
 683{
 684        acpi_status status;
 685        u8 tmp;
 686        u32 result, method_id = 0;
 687
 688        switch (cap) {
 689        case ACER_CAP_WIRELESS:
 690                method_id = ACER_WMID_GET_WIRELESS_METHODID;
 691                break;
 692        case ACER_CAP_BLUETOOTH:
 693                method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
 694                break;
 695        case ACER_CAP_BRIGHTNESS:
 696                method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
 697                break;
 698        case ACER_CAP_THREEG:
 699                method_id = ACER_WMID_GET_THREEG_METHODID;
 700                break;
 701        case ACER_CAP_MAILLED:
 702                if (quirks->mailled == 1) {
 703                        ec_read(0x9f, &tmp);
 704                        *value = tmp & 0x1;
 705                        return 0;
 706                }
 707        default:
 708                return AE_ERROR;
 709        }
 710        status = WMI_execute_u32(method_id, 0, &result);
 711
 712        if (ACPI_SUCCESS(status))
 713                *value = (u8)result;
 714
 715        return status;
 716}
 717
 718static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
 719{
 720        u32 method_id = 0;
 721        char param;
 722
 723        switch (cap) {
 724        case ACER_CAP_BRIGHTNESS:
 725                if (value > max_brightness)
 726                        return AE_BAD_PARAMETER;
 727                method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
 728                break;
 729        case ACER_CAP_WIRELESS:
 730                if (value > 1)
 731                        return AE_BAD_PARAMETER;
 732                method_id = ACER_WMID_SET_WIRELESS_METHODID;
 733                break;
 734        case ACER_CAP_BLUETOOTH:
 735                if (value > 1)
 736                        return AE_BAD_PARAMETER;
 737                method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
 738                break;
 739        case ACER_CAP_THREEG:
 740                if (value > 1)
 741                        return AE_BAD_PARAMETER;
 742                method_id = ACER_WMID_SET_THREEG_METHODID;
 743                break;
 744        case ACER_CAP_MAILLED:
 745                if (value > 1)
 746                        return AE_BAD_PARAMETER;
 747                if (quirks->mailled == 1) {
 748                        param = value ? 0x92 : 0x93;
 749                        i8042_lock_chip();
 750                        i8042_command(&param, 0x1059);
 751                        i8042_unlock_chip();
 752                        return 0;
 753                }
 754                break;
 755        default:
 756                return AE_ERROR;
 757        }
 758        return WMI_execute_u32(method_id, (u32)value, NULL);
 759}
 760
 761static acpi_status WMID_set_capabilities(void)
 762{
 763        struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
 764        union acpi_object *obj;
 765        acpi_status status;
 766        u32 devices;
 767
 768        status = wmi_query_block(WMID_GUID2, 1, &out);
 769        if (ACPI_FAILURE(status))
 770                return status;
 771
 772        obj = (union acpi_object *) out.pointer;
 773        if (obj && obj->type == ACPI_TYPE_BUFFER &&
 774                obj->buffer.length == sizeof(u32)) {
 775                devices = *((u32 *) obj->buffer.pointer);
 776        } else {
 777                return AE_ERROR;
 778        }
 779
 780        /* Not sure on the meaning of the relevant bits yet to detect these */
 781        interface->capability |= ACER_CAP_WIRELESS;
 782        interface->capability |= ACER_CAP_THREEG;
 783
 784        /* WMID always provides brightness methods */
 785        interface->capability |= ACER_CAP_BRIGHTNESS;
 786
 787        if (devices & 0x10)
 788                interface->capability |= ACER_CAP_BLUETOOTH;
 789
 790        if (!(devices & 0x20))
 791                max_brightness = 0x9;
 792
 793        return status;
 794}
 795
 796static struct wmi_interface wmid_interface = {
 797        .type = ACER_WMID,
 798};
 799
 800/*
 801 * Generic Device (interface-independent)
 802 */
 803
 804static acpi_status get_u32(u32 *value, u32 cap)
 805{
 806        acpi_status status = AE_ERROR;
 807
 808        switch (interface->type) {
 809        case ACER_AMW0:
 810                status = AMW0_get_u32(value, cap, interface);
 811                break;
 812        case ACER_AMW0_V2:
 813                if (cap == ACER_CAP_MAILLED) {
 814                        status = AMW0_get_u32(value, cap, interface);
 815                        break;
 816                }
 817        case ACER_WMID:
 818                status = WMID_get_u32(value, cap, interface);
 819                break;
 820        }
 821
 822        return status;
 823}
 824
 825static acpi_status set_u32(u32 value, u32 cap)
 826{
 827        acpi_status status;
 828
 829        if (interface->capability & cap) {
 830                switch (interface->type) {
 831                case ACER_AMW0:
 832                        return AMW0_set_u32(value, cap, interface);
 833                case ACER_AMW0_V2:
 834                        if (cap == ACER_CAP_MAILLED)
 835                                return AMW0_set_u32(value, cap, interface);
 836
 837                        /*
 838                         * On some models, some WMID methods don't toggle
 839                         * properly. For those cases, we want to run the AMW0
 840                         * method afterwards to be certain we've really toggled
 841                         * the device state.
 842                         */
 843                        if (cap == ACER_CAP_WIRELESS ||
 844                                cap == ACER_CAP_BLUETOOTH) {
 845                                status = WMID_set_u32(value, cap, interface);
 846                                if (ACPI_FAILURE(status))
 847                                        return status;
 848
 849                                return AMW0_set_u32(value, cap, interface);
 850                        }
 851                case ACER_WMID:
 852                        return WMID_set_u32(value, cap, interface);
 853                default:
 854                        return AE_BAD_PARAMETER;
 855                }
 856        }
 857        return AE_BAD_PARAMETER;
 858}
 859
 860static void __init acer_commandline_init(void)
 861{
 862        /*
 863         * These will all fail silently if the value given is invalid, or the
 864         * capability isn't available on the given interface
 865         */
 866        set_u32(mailled, ACER_CAP_MAILLED);
 867        set_u32(threeg, ACER_CAP_THREEG);
 868        set_u32(brightness, ACER_CAP_BRIGHTNESS);
 869}
 870
 871/*
 872 * LED device (Mail LED only, no other LEDs known yet)
 873 */
 874static void mail_led_set(struct led_classdev *led_cdev,
 875enum led_brightness value)
 876{
 877        set_u32(value, ACER_CAP_MAILLED);
 878}
 879
 880static struct led_classdev mail_led = {
 881        .name = "acer-wmi::mail",
 882        .brightness_set = mail_led_set,
 883};
 884
 885static int __devinit acer_led_init(struct device *dev)
 886{
 887        return led_classdev_register(dev, &mail_led);
 888}
 889
 890static void acer_led_exit(void)
 891{
 892        led_classdev_unregister(&mail_led);
 893}
 894
 895/*
 896 * Backlight device
 897 */
 898static struct backlight_device *acer_backlight_device;
 899
 900static int read_brightness(struct backlight_device *bd)
 901{
 902        u32 value;
 903        get_u32(&value, ACER_CAP_BRIGHTNESS);
 904        return value;
 905}
 906
 907static int update_bl_status(struct backlight_device *bd)
 908{
 909        int intensity = bd->props.brightness;
 910
 911        if (bd->props.power != FB_BLANK_UNBLANK)
 912                intensity = 0;
 913        if (bd->props.fb_blank != FB_BLANK_UNBLANK)
 914                intensity = 0;
 915
 916        set_u32(intensity, ACER_CAP_BRIGHTNESS);
 917
 918        return 0;
 919}
 920
 921static struct backlight_ops acer_bl_ops = {
 922        .get_brightness = read_brightness,
 923        .update_status = update_bl_status,
 924};
 925
 926static int __devinit acer_backlight_init(struct device *dev)
 927{
 928        struct backlight_device *bd;
 929
 930        bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops);
 931        if (IS_ERR(bd)) {
 932                printk(ACER_ERR "Could not register Acer backlight device\n");
 933                acer_backlight_device = NULL;
 934                return PTR_ERR(bd);
 935        }
 936
 937        acer_backlight_device = bd;
 938
 939        bd->props.power = FB_BLANK_UNBLANK;
 940        bd->props.brightness = max_brightness;
 941        bd->props.max_brightness = max_brightness;
 942        backlight_update_status(bd);
 943        return 0;
 944}
 945
 946static void acer_backlight_exit(void)
 947{
 948        backlight_device_unregister(acer_backlight_device);
 949}
 950
 951/*
 952 * Rfkill devices
 953 */
 954static void acer_rfkill_update(struct work_struct *ignored);
 955static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update);
 956static void acer_rfkill_update(struct work_struct *ignored)
 957{
 958        u32 state;
 959        acpi_status status;
 960
 961        status = get_u32(&state, ACER_CAP_WIRELESS);
 962        if (ACPI_SUCCESS(status))
 963                rfkill_set_sw_state(wireless_rfkill, !state);
 964
 965        if (has_cap(ACER_CAP_BLUETOOTH)) {
 966                status = get_u32(&state, ACER_CAP_BLUETOOTH);
 967                if (ACPI_SUCCESS(status))
 968                        rfkill_set_sw_state(bluetooth_rfkill, !state);
 969        }
 970
 971        schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
 972}
 973
 974static int acer_rfkill_set(void *data, bool blocked)
 975{
 976        acpi_status status;
 977        u32 cap = (unsigned long)data;
 978        status = set_u32(!blocked, cap);
 979        if (ACPI_FAILURE(status))
 980                return -ENODEV;
 981        return 0;
 982}
 983
 984static const struct rfkill_ops acer_rfkill_ops = {
 985        .set_block = acer_rfkill_set,
 986};
 987
 988static struct rfkill *acer_rfkill_register(struct device *dev,
 989                                           enum rfkill_type type,
 990                                           char *name, u32 cap)
 991{
 992        int err;
 993        struct rfkill *rfkill_dev;
 994
 995        rfkill_dev = rfkill_alloc(name, dev, type,
 996                                  &acer_rfkill_ops,
 997                                  (void *)(unsigned long)cap);
 998        if (!rfkill_dev)
 999                return ERR_PTR(-ENOMEM);
1000
1001        err = rfkill_register(rfkill_dev);
1002        if (err) {
1003                rfkill_destroy(rfkill_dev);
1004                return ERR_PTR(err);
1005        }
1006        return rfkill_dev;
1007}
1008
1009static int acer_rfkill_init(struct device *dev)
1010{
1011        wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
1012                "acer-wireless", ACER_CAP_WIRELESS);
1013        if (IS_ERR(wireless_rfkill))
1014                return PTR_ERR(wireless_rfkill);
1015
1016        if (has_cap(ACER_CAP_BLUETOOTH)) {
1017                bluetooth_rfkill = acer_rfkill_register(dev,
1018                        RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
1019                        ACER_CAP_BLUETOOTH);
1020                if (IS_ERR(bluetooth_rfkill)) {
1021                        rfkill_unregister(wireless_rfkill);
1022                        rfkill_destroy(wireless_rfkill);
1023                        return PTR_ERR(bluetooth_rfkill);
1024                }
1025        }
1026
1027        schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
1028
1029        return 0;
1030}
1031
1032static void acer_rfkill_exit(void)
1033{
1034        cancel_delayed_work_sync(&acer_rfkill_work);
1035
1036        rfkill_unregister(wireless_rfkill);
1037        rfkill_destroy(wireless_rfkill);
1038
1039        if (has_cap(ACER_CAP_BLUETOOTH)) {
1040                rfkill_unregister(bluetooth_rfkill);
1041                rfkill_destroy(bluetooth_rfkill);
1042        }
1043        return;
1044}
1045
1046/*
1047 * sysfs interface
1048 */
1049static ssize_t show_bool_threeg(struct device *dev,
1050        struct device_attribute *attr, char *buf)
1051{
1052        u32 result; \
1053        acpi_status status = get_u32(&result, ACER_CAP_THREEG);
1054        if (ACPI_SUCCESS(status))
1055                return sprintf(buf, "%u\n", result);
1056        return sprintf(buf, "Read error\n");
1057}
1058
1059static ssize_t set_bool_threeg(struct device *dev,
1060        struct device_attribute *attr, const char *buf, size_t count)
1061{
1062        u32 tmp = simple_strtoul(buf, NULL, 10);
1063        acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
1064                if (ACPI_FAILURE(status))
1065                        return -EINVAL;
1066        return count;
1067}
1068static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg,
1069        set_bool_threeg);
1070
1071static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
1072        char *buf)
1073{
1074        switch (interface->type) {
1075        case ACER_AMW0:
1076                return sprintf(buf, "AMW0\n");
1077        case ACER_AMW0_V2:
1078                return sprintf(buf, "AMW0 v2\n");
1079        case ACER_WMID:
1080                return sprintf(buf, "WMID\n");
1081        default:
1082                return sprintf(buf, "Error!\n");
1083        }
1084}
1085
1086static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR,
1087        show_interface, NULL);
1088
1089/*
1090 * debugfs functions
1091 */
1092static u32 get_wmid_devices(void)
1093{
1094        struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
1095        union acpi_object *obj;
1096        acpi_status status;
1097
1098        status = wmi_query_block(WMID_GUID2, 1, &out);
1099        if (ACPI_FAILURE(status))
1100                return 0;
1101
1102        obj = (union acpi_object *) out.pointer;
1103        if (obj && obj->type == ACPI_TYPE_BUFFER &&
1104                obj->buffer.length == sizeof(u32)) {
1105                return *((u32 *) obj->buffer.pointer);
1106        } else {
1107                return 0;
1108        }
1109}
1110
1111/*
1112 * Platform device
1113 */
1114static int __devinit acer_platform_probe(struct platform_device *device)
1115{
1116        int err;
1117
1118        if (has_cap(ACER_CAP_MAILLED)) {
1119                err = acer_led_init(&device->dev);
1120                if (err)
1121                        goto error_mailled;
1122        }
1123
1124        if (has_cap(ACER_CAP_BRIGHTNESS)) {
1125                err = acer_backlight_init(&device->dev);
1126                if (err)
1127                        goto error_brightness;
1128        }
1129
1130        err = acer_rfkill_init(&device->dev);
1131        if (err)
1132                goto error_rfkill;
1133
1134        return err;
1135
1136error_rfkill:
1137        if (has_cap(ACER_CAP_BRIGHTNESS))
1138                acer_backlight_exit();
1139error_brightness:
1140        if (has_cap(ACER_CAP_MAILLED))
1141                acer_led_exit();
1142error_mailled:
1143        return err;
1144}
1145
1146static int acer_platform_remove(struct platform_device *device)
1147{
1148        if (has_cap(ACER_CAP_MAILLED))
1149                acer_led_exit();
1150        if (has_cap(ACER_CAP_BRIGHTNESS))
1151                acer_backlight_exit();
1152
1153        acer_rfkill_exit();
1154        return 0;
1155}
1156
1157static int acer_platform_suspend(struct platform_device *dev,
1158pm_message_t state)
1159{
1160        u32 value;
1161        struct acer_data *data = &interface->data;
1162
1163        if (!data)
1164                return -ENOMEM;
1165
1166        if (has_cap(ACER_CAP_MAILLED)) {
1167                get_u32(&value, ACER_CAP_MAILLED);
1168                data->mailled = value;
1169        }
1170
1171        if (has_cap(ACER_CAP_BRIGHTNESS)) {
1172                get_u32(&value, ACER_CAP_BRIGHTNESS);
1173                data->brightness = value;
1174        }
1175
1176        return 0;
1177}
1178
1179static int acer_platform_resume(struct platform_device *device)
1180{
1181        struct acer_data *data = &interface->data;
1182
1183        if (!data)
1184                return -ENOMEM;
1185
1186        if (has_cap(ACER_CAP_MAILLED))
1187                set_u32(data->mailled, ACER_CAP_MAILLED);
1188
1189        if (has_cap(ACER_CAP_BRIGHTNESS))
1190                set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
1191
1192        return 0;
1193}
1194
1195static struct platform_driver acer_platform_driver = {
1196        .driver = {
1197                .name = "acer-wmi",
1198                .owner = THIS_MODULE,
1199        },
1200        .probe = acer_platform_probe,
1201        .remove = acer_platform_remove,
1202        .suspend = acer_platform_suspend,
1203        .resume = acer_platform_resume,
1204};
1205
1206static struct platform_device *acer_platform_device;
1207
1208static int remove_sysfs(struct platform_device *device)
1209{
1210        if (has_cap(ACER_CAP_THREEG))
1211                device_remove_file(&device->dev, &dev_attr_threeg);
1212
1213        device_remove_file(&device->dev, &dev_attr_interface);
1214
1215        return 0;
1216}
1217
1218static int create_sysfs(void)
1219{
1220        int retval = -ENOMEM;
1221
1222        if (has_cap(ACER_CAP_THREEG)) {
1223                retval = device_create_file(&acer_platform_device->dev,
1224                        &dev_attr_threeg);
1225                if (retval)
1226                        goto error_sysfs;
1227        }
1228
1229        retval = device_create_file(&acer_platform_device->dev,
1230                &dev_attr_interface);
1231        if (retval)
1232                goto error_sysfs;
1233
1234        return 0;
1235
1236error_sysfs:
1237                remove_sysfs(acer_platform_device);
1238        return retval;
1239}
1240
1241static void remove_debugfs(void)
1242{
1243        debugfs_remove(interface->debug.devices);
1244        debugfs_remove(interface->debug.root);
1245}
1246
1247static int create_debugfs(void)
1248{
1249        interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
1250        if (!interface->debug.root) {
1251                printk(ACER_ERR "Failed to create debugfs directory");
1252                return -ENOMEM;
1253        }
1254
1255        interface->debug.devices = debugfs_create_u32("devices", S_IRUGO,
1256                                        interface->debug.root,
1257                                        &interface->debug.wmid_devices);
1258        if (!interface->debug.devices)
1259                goto error_debugfs;
1260
1261        return 0;
1262
1263error_debugfs:
1264        remove_debugfs();
1265        return -ENOMEM;
1266}
1267
1268static int __init acer_wmi_init(void)
1269{
1270        int err;
1271
1272        printk(ACER_INFO "Acer Laptop ACPI-WMI Extras\n");
1273
1274        if (dmi_check_system(acer_blacklist)) {
1275                printk(ACER_INFO "Blacklisted hardware detected - "
1276                                "not loading\n");
1277                return -ENODEV;
1278        }
1279
1280        find_quirks();
1281
1282        /*
1283         * Detect which ACPI-WMI interface we're using.
1284         */
1285        if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1286                interface = &AMW0_V2_interface;
1287
1288        if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1289                interface = &wmid_interface;
1290
1291        if (wmi_has_guid(WMID_GUID2) && interface) {
1292                if (ACPI_FAILURE(WMID_set_capabilities())) {
1293                        printk(ACER_ERR "Unable to detect available WMID "
1294                                        "devices\n");
1295                        return -ENODEV;
1296                }
1297        } else if (!wmi_has_guid(WMID_GUID2) && interface) {
1298                printk(ACER_ERR "No WMID device detection method found\n");
1299                return -ENODEV;
1300        }
1301
1302        if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
1303                interface = &AMW0_interface;
1304
1305                if (ACPI_FAILURE(AMW0_set_capabilities())) {
1306                        printk(ACER_ERR "Unable to detect available AMW0 "
1307                                        "devices\n");
1308                        return -ENODEV;
1309                }
1310        }
1311
1312        if (wmi_has_guid(AMW0_GUID1))
1313                AMW0_find_mailled();
1314
1315        if (!interface) {
1316                printk(ACER_ERR "No or unsupported WMI interface, unable to "
1317                                "load\n");
1318                return -ENODEV;
1319        }
1320
1321        set_quirks();
1322
1323        if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
1324                interface->capability &= ~ACER_CAP_BRIGHTNESS;
1325                printk(ACER_INFO "Brightness must be controlled by "
1326                       "generic video driver\n");
1327        }
1328
1329        if (platform_driver_register(&acer_platform_driver)) {
1330                printk(ACER_ERR "Unable to register platform driver.\n");
1331                goto error_platform_register;
1332        }
1333        acer_platform_device = platform_device_alloc("acer-wmi", -1);
1334        platform_device_add(acer_platform_device);
1335
1336        err = create_sysfs();
1337        if (err)
1338                return err;
1339
1340        if (wmi_has_guid(WMID_GUID2)) {
1341                interface->debug.wmid_devices = get_wmid_devices();
1342                err = create_debugfs();
1343                if (err)
1344                        return err;
1345        }
1346
1347        /* Override any initial settings with values from the commandline */
1348        acer_commandline_init();
1349
1350        return 0;
1351
1352error_platform_register:
1353        return -ENODEV;
1354}
1355
1356static void __exit acer_wmi_exit(void)
1357{
1358        remove_sysfs(acer_platform_device);
1359        remove_debugfs();
1360        platform_device_del(acer_platform_device);
1361        platform_driver_unregister(&acer_platform_driver);
1362
1363        printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
1364        return;
1365}
1366
1367module_init(acer_wmi_init);
1368module_exit(acer_wmi_exit);
1369