linux/drivers/misc/acer-wmi.c
<<
>>
Prefs
   1/*
   2 *  Acer WMI Laptop Extras
   3 *
   4 *  Copyright (C) 2007-2008     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
 228static struct dmi_system_id acer_quirks[] = {
 229        {
 230                .callback = dmi_matched,
 231                .ident = "Acer Aspire 1360",
 232                .matches = {
 233                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 234                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
 235                },
 236                .driver_data = &quirk_acer_aspire_1520,
 237        },
 238        {
 239                .callback = dmi_matched,
 240                .ident = "Acer Aspire 1520",
 241                .matches = {
 242                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 243                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1520"),
 244                },
 245                .driver_data = &quirk_acer_aspire_1520,
 246        },
 247        {
 248                .callback = dmi_matched,
 249                .ident = "Acer Aspire 3100",
 250                .matches = {
 251                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 252                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
 253                },
 254                .driver_data = &quirk_acer_travelmate_2490,
 255        },
 256        {
 257                .callback = dmi_matched,
 258                .ident = "Acer Aspire 3610",
 259                .matches = {
 260                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 261                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3610"),
 262                },
 263                .driver_data = &quirk_acer_travelmate_2490,
 264        },
 265        {
 266                .callback = dmi_matched,
 267                .ident = "Acer Aspire 5100",
 268                .matches = {
 269                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 270                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
 271                },
 272                .driver_data = &quirk_acer_travelmate_2490,
 273        },
 274        {
 275                .callback = dmi_matched,
 276                .ident = "Acer Aspire 5610",
 277                .matches = {
 278                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 279                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
 280                },
 281                .driver_data = &quirk_acer_travelmate_2490,
 282        },
 283        {
 284                .callback = dmi_matched,
 285                .ident = "Acer Aspire 5630",
 286                .matches = {
 287                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 288                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
 289                },
 290                .driver_data = &quirk_acer_travelmate_2490,
 291        },
 292        {
 293                .callback = dmi_matched,
 294                .ident = "Acer Aspire 5650",
 295                .matches = {
 296                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 297                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
 298                },
 299                .driver_data = &quirk_acer_travelmate_2490,
 300        },
 301        {
 302                .callback = dmi_matched,
 303                .ident = "Acer Aspire 5680",
 304                .matches = {
 305                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 306                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
 307                },
 308                .driver_data = &quirk_acer_travelmate_2490,
 309        },
 310        {
 311                .callback = dmi_matched,
 312                .ident = "Acer Aspire 9110",
 313                .matches = {
 314                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 315                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
 316                },
 317                .driver_data = &quirk_acer_travelmate_2490,
 318        },
 319        {
 320                .callback = dmi_matched,
 321                .ident = "Acer TravelMate 2490",
 322                .matches = {
 323                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 324                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
 325                },
 326                .driver_data = &quirk_acer_travelmate_2490,
 327        },
 328        {
 329                .callback = dmi_matched,
 330                .ident = "Acer TravelMate 4200",
 331                .matches = {
 332                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 333                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4200"),
 334                },
 335                .driver_data = &quirk_acer_travelmate_2490,
 336        },
 337        {
 338                .callback = dmi_matched,
 339                .ident = "Fujitsu Siemens Amilo Li 1718",
 340                .matches = {
 341                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 342                        DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Li 1718"),
 343                },
 344                .driver_data = &quirk_fujitsu_amilo_li_1718,
 345        },
 346        {
 347                .callback = dmi_matched,
 348                .ident = "Medion MD 98300",
 349                .matches = {
 350                        DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
 351                        DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
 352                },
 353                .driver_data = &quirk_medion_md_98300,
 354        },
 355        {}
 356};
 357
 358/* Find which quirks are needed for a particular vendor/ model pair */
 359static void find_quirks(void)
 360{
 361        if (!force_series) {
 362                dmi_check_system(acer_quirks);
 363        } else if (force_series == 2490) {
 364                quirks = &quirk_acer_travelmate_2490;
 365        }
 366
 367        if (quirks == NULL)
 368                quirks = &quirk_unknown;
 369
 370        set_quirks();
 371}
 372
 373/*
 374 * General interface convenience methods
 375 */
 376
 377static bool has_cap(u32 cap)
 378{
 379        if ((interface->capability & cap) != 0)
 380                return 1;
 381
 382        return 0;
 383}
 384
 385/*
 386 * AMW0 (V1) interface
 387 */
 388struct wmab_args {
 389        u32 eax;
 390        u32 ebx;
 391        u32 ecx;
 392        u32 edx;
 393};
 394
 395struct wmab_ret {
 396        u32 eax;
 397        u32 ebx;
 398        u32 ecx;
 399        u32 edx;
 400        u32 eex;
 401};
 402
 403static acpi_status wmab_execute(struct wmab_args *regbuf,
 404struct acpi_buffer *result)
 405{
 406        struct acpi_buffer input;
 407        acpi_status status;
 408        input.length = sizeof(struct wmab_args);
 409        input.pointer = (u8 *)regbuf;
 410
 411        status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
 412
 413        return status;
 414}
 415
 416static acpi_status AMW0_get_u32(u32 *value, u32 cap,
 417struct wmi_interface *iface)
 418{
 419        int err;
 420        u8 result;
 421
 422        switch (cap) {
 423        case ACER_CAP_MAILLED:
 424                switch (quirks->mailled) {
 425                default:
 426                        err = ec_read(0xA, &result);
 427                        if (err)
 428                                return AE_ERROR;
 429                        *value = (result >> 7) & 0x1;
 430                        return AE_OK;
 431                }
 432                break;
 433        case ACER_CAP_WIRELESS:
 434                switch (quirks->wireless) {
 435                case 1:
 436                        err = ec_read(0x7B, &result);
 437                        if (err)
 438                                return AE_ERROR;
 439                        *value = result & 0x1;
 440                        return AE_OK;
 441                case 2:
 442                        err = ec_read(0x71, &result);
 443                        if (err)
 444                                return AE_ERROR;
 445                        *value = result & 0x1;
 446                        return AE_OK;
 447                default:
 448                        err = ec_read(0xA, &result);
 449                        if (err)
 450                                return AE_ERROR;
 451                        *value = (result >> 2) & 0x1;
 452                        return AE_OK;
 453                }
 454                break;
 455        case ACER_CAP_BLUETOOTH:
 456                switch (quirks->bluetooth) {
 457                default:
 458                        err = ec_read(0xA, &result);
 459                        if (err)
 460                                return AE_ERROR;
 461                        *value = (result >> 4) & 0x1;
 462                        return AE_OK;
 463                }
 464                break;
 465        case ACER_CAP_BRIGHTNESS:
 466                switch (quirks->brightness) {
 467                default:
 468                        err = ec_read(0x83, &result);
 469                        if (err)
 470                                return AE_ERROR;
 471                        *value = result;
 472                        return AE_OK;
 473                }
 474                break;
 475        default:
 476                return AE_ERROR;
 477        }
 478        return AE_OK;
 479}
 480
 481static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
 482{
 483        struct wmab_args args;
 484
 485        args.eax = ACER_AMW0_WRITE;
 486        args.ebx = value ? (1<<8) : 0;
 487        args.ecx = args.edx = 0;
 488
 489        switch (cap) {
 490        case ACER_CAP_MAILLED:
 491                if (value > 1)
 492                        return AE_BAD_PARAMETER;
 493                args.ebx |= ACER_AMW0_MAILLED_MASK;
 494                break;
 495        case ACER_CAP_WIRELESS:
 496                if (value > 1)
 497                        return AE_BAD_PARAMETER;
 498                args.ebx |= ACER_AMW0_WIRELESS_MASK;
 499                break;
 500        case ACER_CAP_BLUETOOTH:
 501                if (value > 1)
 502                        return AE_BAD_PARAMETER;
 503                args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
 504                break;
 505        case ACER_CAP_BRIGHTNESS:
 506                if (value > max_brightness)
 507                        return AE_BAD_PARAMETER;
 508                switch (quirks->brightness) {
 509                default:
 510                        return ec_write(0x83, value);
 511                        break;
 512                }
 513        default:
 514                return AE_ERROR;
 515        }
 516
 517        /* Actually do the set */
 518        return wmab_execute(&args, NULL);
 519}
 520
 521static acpi_status AMW0_find_mailled(void)
 522{
 523        struct wmab_args args;
 524        struct wmab_ret ret;
 525        acpi_status status = AE_OK;
 526        struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
 527        union acpi_object *obj;
 528
 529        args.eax = 0x86;
 530        args.ebx = args.ecx = args.edx = 0;
 531
 532        status = wmab_execute(&args, &out);
 533        if (ACPI_FAILURE(status))
 534                return status;
 535
 536        obj = (union acpi_object *) out.pointer;
 537        if (obj && obj->type == ACPI_TYPE_BUFFER &&
 538        obj->buffer.length == sizeof(struct wmab_ret)) {
 539                ret = *((struct wmab_ret *) obj->buffer.pointer);
 540        } else {
 541                return AE_ERROR;
 542        }
 543
 544        if (ret.eex & 0x1)
 545                interface->capability |= ACER_CAP_MAILLED;
 546
 547        kfree(out.pointer);
 548
 549        return AE_OK;
 550}
 551
 552static acpi_status AMW0_set_capabilities(void)
 553{
 554        struct wmab_args args;
 555        struct wmab_ret ret;
 556        acpi_status status = AE_OK;
 557        struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
 558        union acpi_object *obj;
 559
 560        /*
 561         * On laptops with this strange GUID (non Acer), normal probing doesn't
 562         * work.
 563         */
 564        if (wmi_has_guid(AMW0_GUID2)) {
 565                interface->capability |= ACER_CAP_WIRELESS;
 566                return AE_OK;
 567        }
 568
 569        args.eax = ACER_AMW0_WRITE;
 570        args.ecx = args.edx = 0;
 571
 572        args.ebx = 0xa2 << 8;
 573        args.ebx |= ACER_AMW0_WIRELESS_MASK;
 574
 575        status = wmab_execute(&args, &out);
 576        if (ACPI_FAILURE(status))
 577                return status;
 578
 579        obj = (union acpi_object *) out.pointer;
 580        if (obj && obj->type == ACPI_TYPE_BUFFER &&
 581        obj->buffer.length == sizeof(struct wmab_ret)) {
 582                ret = *((struct wmab_ret *) obj->buffer.pointer);
 583        } else {
 584                return AE_ERROR;
 585        }
 586
 587        if (ret.eax & 0x1)
 588                interface->capability |= ACER_CAP_WIRELESS;
 589
 590        args.ebx = 2 << 8;
 591        args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
 592
 593        status = wmab_execute(&args, &out);
 594        if (ACPI_FAILURE(status))
 595                return status;
 596
 597        obj = (union acpi_object *) out.pointer;
 598        if (obj && obj->type == ACPI_TYPE_BUFFER
 599        && obj->buffer.length == sizeof(struct wmab_ret)) {
 600                ret = *((struct wmab_ret *) obj->buffer.pointer);
 601        } else {
 602                return AE_ERROR;
 603        }
 604
 605        if (ret.eax & 0x1)
 606                interface->capability |= ACER_CAP_BLUETOOTH;
 607
 608        kfree(out.pointer);
 609
 610        /*
 611         * This appears to be safe to enable, since all Wistron based laptops
 612         * appear to use the same EC register for brightness, even if they
 613         * differ for wireless, etc
 614         */
 615        if (quirks->brightness >= 0)
 616                interface->capability |= ACER_CAP_BRIGHTNESS;
 617
 618        return AE_OK;
 619}
 620
 621static struct wmi_interface AMW0_interface = {
 622        .type = ACER_AMW0,
 623};
 624
 625static struct wmi_interface AMW0_V2_interface = {
 626        .type = ACER_AMW0_V2,
 627};
 628
 629/*
 630 * New interface (The WMID interface)
 631 */
 632static acpi_status
 633WMI_execute_u32(u32 method_id, u32 in, u32 *out)
 634{
 635        struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
 636        struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
 637        union acpi_object *obj;
 638        u32 tmp;
 639        acpi_status status;
 640
 641        status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
 642
 643        if (ACPI_FAILURE(status))
 644                return status;
 645
 646        obj = (union acpi_object *) result.pointer;
 647        if (obj && obj->type == ACPI_TYPE_BUFFER &&
 648                obj->buffer.length == sizeof(u32)) {
 649                tmp = *((u32 *) obj->buffer.pointer);
 650        } else {
 651                tmp = 0;
 652        }
 653
 654        if (out)
 655                *out = tmp;
 656
 657        kfree(result.pointer);
 658
 659        return status;
 660}
 661
 662static acpi_status WMID_get_u32(u32 *value, u32 cap,
 663struct wmi_interface *iface)
 664{
 665        acpi_status status;
 666        u8 tmp;
 667        u32 result, method_id = 0;
 668
 669        switch (cap) {
 670        case ACER_CAP_WIRELESS:
 671                method_id = ACER_WMID_GET_WIRELESS_METHODID;
 672                break;
 673        case ACER_CAP_BLUETOOTH:
 674                method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
 675                break;
 676        case ACER_CAP_BRIGHTNESS:
 677                method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
 678                break;
 679        case ACER_CAP_THREEG:
 680                method_id = ACER_WMID_GET_THREEG_METHODID;
 681                break;
 682        case ACER_CAP_MAILLED:
 683                if (quirks->mailled == 1) {
 684                        ec_read(0x9f, &tmp);
 685                        *value = tmp & 0x1;
 686                        return 0;
 687                }
 688        default:
 689                return AE_ERROR;
 690        }
 691        status = WMI_execute_u32(method_id, 0, &result);
 692
 693        if (ACPI_SUCCESS(status))
 694                *value = (u8)result;
 695
 696        return status;
 697}
 698
 699static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
 700{
 701        u32 method_id = 0;
 702        char param;
 703
 704        switch (cap) {
 705        case ACER_CAP_BRIGHTNESS:
 706                if (value > max_brightness)
 707                        return AE_BAD_PARAMETER;
 708                method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
 709                break;
 710        case ACER_CAP_WIRELESS:
 711                if (value > 1)
 712                        return AE_BAD_PARAMETER;
 713                method_id = ACER_WMID_SET_WIRELESS_METHODID;
 714                break;
 715        case ACER_CAP_BLUETOOTH:
 716                if (value > 1)
 717                        return AE_BAD_PARAMETER;
 718                method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
 719                break;
 720        case ACER_CAP_THREEG:
 721                if (value > 1)
 722                        return AE_BAD_PARAMETER;
 723                method_id = ACER_WMID_SET_THREEG_METHODID;
 724                break;
 725        case ACER_CAP_MAILLED:
 726                if (value > 1)
 727                        return AE_BAD_PARAMETER;
 728                if (quirks->mailled == 1) {
 729                        param = value ? 0x92 : 0x93;
 730                        i8042_command(&param, 0x1059);
 731                        return 0;
 732                }
 733                break;
 734        default:
 735                return AE_ERROR;
 736        }
 737        return WMI_execute_u32(method_id, (u32)value, NULL);
 738}
 739
 740static acpi_status WMID_set_capabilities(void)
 741{
 742        struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
 743        union acpi_object *obj;
 744        acpi_status status;
 745        u32 devices;
 746
 747        status = wmi_query_block(WMID_GUID2, 1, &out);
 748        if (ACPI_FAILURE(status))
 749                return status;
 750
 751        obj = (union acpi_object *) out.pointer;
 752        if (obj && obj->type == ACPI_TYPE_BUFFER &&
 753                obj->buffer.length == sizeof(u32)) {
 754                devices = *((u32 *) obj->buffer.pointer);
 755        } else {
 756                return AE_ERROR;
 757        }
 758
 759        /* Not sure on the meaning of the relevant bits yet to detect these */
 760        interface->capability |= ACER_CAP_WIRELESS;
 761        interface->capability |= ACER_CAP_THREEG;
 762
 763        /* WMID always provides brightness methods */
 764        interface->capability |= ACER_CAP_BRIGHTNESS;
 765
 766        if (devices & 0x10)
 767                interface->capability |= ACER_CAP_BLUETOOTH;
 768
 769        if (!(devices & 0x20))
 770                max_brightness = 0x9;
 771
 772        return status;
 773}
 774
 775static struct wmi_interface wmid_interface = {
 776        .type = ACER_WMID,
 777};
 778
 779/*
 780 * Generic Device (interface-independent)
 781 */
 782
 783static acpi_status get_u32(u32 *value, u32 cap)
 784{
 785        acpi_status status = AE_ERROR;
 786
 787        switch (interface->type) {
 788        case ACER_AMW0:
 789                status = AMW0_get_u32(value, cap, interface);
 790                break;
 791        case ACER_AMW0_V2:
 792                if (cap == ACER_CAP_MAILLED) {
 793                        status = AMW0_get_u32(value, cap, interface);
 794                        break;
 795                }
 796        case ACER_WMID:
 797                status = WMID_get_u32(value, cap, interface);
 798                break;
 799        }
 800
 801        return status;
 802}
 803
 804static acpi_status set_u32(u32 value, u32 cap)
 805{
 806        acpi_status status;
 807
 808        if (interface->capability & cap) {
 809                switch (interface->type) {
 810                case ACER_AMW0:
 811                        return AMW0_set_u32(value, cap, interface);
 812                case ACER_AMW0_V2:
 813                        if (cap == ACER_CAP_MAILLED)
 814                                return AMW0_set_u32(value, cap, interface);
 815
 816                        /*
 817                         * On some models, some WMID methods don't toggle
 818                         * properly. For those cases, we want to run the AMW0
 819                         * method afterwards to be certain we've really toggled
 820                         * the device state.
 821                         */
 822                        if (cap == ACER_CAP_WIRELESS ||
 823                                cap == ACER_CAP_BLUETOOTH) {
 824                                status = WMID_set_u32(value, cap, interface);
 825                                if (ACPI_FAILURE(status))
 826                                        return status;
 827
 828                                return AMW0_set_u32(value, cap, interface);
 829                        }
 830                case ACER_WMID:
 831                        return WMID_set_u32(value, cap, interface);
 832                default:
 833                        return AE_BAD_PARAMETER;
 834                }
 835        }
 836        return AE_BAD_PARAMETER;
 837}
 838
 839static void __init acer_commandline_init(void)
 840{
 841        /*
 842         * These will all fail silently if the value given is invalid, or the
 843         * capability isn't available on the given interface
 844         */
 845        set_u32(mailled, ACER_CAP_MAILLED);
 846        set_u32(threeg, ACER_CAP_THREEG);
 847        set_u32(brightness, ACER_CAP_BRIGHTNESS);
 848}
 849
 850/*
 851 * LED device (Mail LED only, no other LEDs known yet)
 852 */
 853static void mail_led_set(struct led_classdev *led_cdev,
 854enum led_brightness value)
 855{
 856        set_u32(value, ACER_CAP_MAILLED);
 857}
 858
 859static struct led_classdev mail_led = {
 860        .name = "acer-wmi::mail",
 861        .brightness_set = mail_led_set,
 862};
 863
 864static int __devinit acer_led_init(struct device *dev)
 865{
 866        return led_classdev_register(dev, &mail_led);
 867}
 868
 869static void acer_led_exit(void)
 870{
 871        led_classdev_unregister(&mail_led);
 872}
 873
 874/*
 875 * Backlight device
 876 */
 877static struct backlight_device *acer_backlight_device;
 878
 879static int read_brightness(struct backlight_device *bd)
 880{
 881        u32 value;
 882        get_u32(&value, ACER_CAP_BRIGHTNESS);
 883        return value;
 884}
 885
 886static int update_bl_status(struct backlight_device *bd)
 887{
 888        int intensity = bd->props.brightness;
 889
 890        if (bd->props.power != FB_BLANK_UNBLANK)
 891                intensity = 0;
 892        if (bd->props.fb_blank != FB_BLANK_UNBLANK)
 893                intensity = 0;
 894
 895        set_u32(intensity, ACER_CAP_BRIGHTNESS);
 896
 897        return 0;
 898}
 899
 900static struct backlight_ops acer_bl_ops = {
 901        .get_brightness = read_brightness,
 902        .update_status = update_bl_status,
 903};
 904
 905static int __devinit acer_backlight_init(struct device *dev)
 906{
 907        struct backlight_device *bd;
 908
 909        bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops);
 910        if (IS_ERR(bd)) {
 911                printk(ACER_ERR "Could not register Acer backlight device\n");
 912                acer_backlight_device = NULL;
 913                return PTR_ERR(bd);
 914        }
 915
 916        acer_backlight_device = bd;
 917
 918        bd->props.power = FB_BLANK_UNBLANK;
 919        bd->props.brightness = max_brightness;
 920        bd->props.max_brightness = max_brightness;
 921        backlight_update_status(bd);
 922        return 0;
 923}
 924
 925static void acer_backlight_exit(void)
 926{
 927        backlight_device_unregister(acer_backlight_device);
 928}
 929
 930/*
 931 * Rfkill devices
 932 */
 933static void acer_rfkill_update(struct work_struct *ignored);
 934static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update);
 935static void acer_rfkill_update(struct work_struct *ignored)
 936{
 937        u32 state;
 938        acpi_status status;
 939
 940        status = get_u32(&state, ACER_CAP_WIRELESS);
 941        if (ACPI_SUCCESS(status))
 942                rfkill_force_state(wireless_rfkill, state ?
 943                        RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED);
 944
 945        if (has_cap(ACER_CAP_BLUETOOTH)) {
 946                status = get_u32(&state, ACER_CAP_BLUETOOTH);
 947                if (ACPI_SUCCESS(status))
 948                        rfkill_force_state(bluetooth_rfkill, state ?
 949                                RFKILL_STATE_UNBLOCKED :
 950                                RFKILL_STATE_SOFT_BLOCKED);
 951        }
 952
 953        schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
 954}
 955
 956static int acer_rfkill_set(void *data, enum rfkill_state state)
 957{
 958        acpi_status status;
 959        u32 *cap = data;
 960        status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap);
 961        if (ACPI_FAILURE(status))
 962                return -ENODEV;
 963        return 0;
 964}
 965
 966static struct rfkill * acer_rfkill_register(struct device *dev,
 967enum rfkill_type type, char *name, u32 cap)
 968{
 969        int err;
 970        u32 state;
 971        u32 *data;
 972        struct rfkill *rfkill_dev;
 973
 974        rfkill_dev = rfkill_allocate(dev, type);
 975        if (!rfkill_dev)
 976                return ERR_PTR(-ENOMEM);
 977        rfkill_dev->name = name;
 978        get_u32(&state, cap);
 979        rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED :
 980                RFKILL_STATE_SOFT_BLOCKED;
 981        data = kzalloc(sizeof(u32), GFP_KERNEL);
 982        if (!data) {
 983                rfkill_free(rfkill_dev);
 984                return ERR_PTR(-ENOMEM);
 985        }
 986        *data = cap;
 987        rfkill_dev->data = data;
 988        rfkill_dev->toggle_radio = acer_rfkill_set;
 989        rfkill_dev->user_claim_unsupported = 1;
 990
 991        err = rfkill_register(rfkill_dev);
 992        if (err) {
 993                kfree(rfkill_dev->data);
 994                rfkill_free(rfkill_dev);
 995                return ERR_PTR(err);
 996        }
 997        return rfkill_dev;
 998}
 999
1000static int acer_rfkill_init(struct device *dev)
1001{
1002        wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
1003                "acer-wireless", ACER_CAP_WIRELESS);
1004        if (IS_ERR(wireless_rfkill))
1005                return PTR_ERR(wireless_rfkill);
1006
1007        if (has_cap(ACER_CAP_BLUETOOTH)) {
1008                bluetooth_rfkill = acer_rfkill_register(dev,
1009                        RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
1010                        ACER_CAP_BLUETOOTH);
1011                if (IS_ERR(bluetooth_rfkill)) {
1012                        kfree(wireless_rfkill->data);
1013                        rfkill_unregister(wireless_rfkill);
1014                        return PTR_ERR(bluetooth_rfkill);
1015                }
1016        }
1017
1018        schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
1019
1020        return 0;
1021}
1022
1023static void acer_rfkill_exit(void)
1024{
1025        cancel_delayed_work_sync(&acer_rfkill_work);
1026        kfree(wireless_rfkill->data);
1027        rfkill_unregister(wireless_rfkill);
1028        if (has_cap(ACER_CAP_BLUETOOTH)) {
1029                kfree(wireless_rfkill->data);
1030                rfkill_unregister(bluetooth_rfkill);
1031        }
1032        return;
1033}
1034
1035/*
1036 * sysfs interface
1037 */
1038static ssize_t show_bool_threeg(struct device *dev,
1039        struct device_attribute *attr, char *buf)
1040{
1041        u32 result; \
1042        acpi_status status = get_u32(&result, ACER_CAP_THREEG);
1043        if (ACPI_SUCCESS(status))
1044                return sprintf(buf, "%u\n", result);
1045        return sprintf(buf, "Read error\n");
1046}
1047
1048static ssize_t set_bool_threeg(struct device *dev,
1049        struct device_attribute *attr, const char *buf, size_t count)
1050{
1051        u32 tmp = simple_strtoul(buf, NULL, 10);
1052        acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
1053                if (ACPI_FAILURE(status))
1054                        return -EINVAL;
1055        return count;
1056}
1057static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg,
1058        set_bool_threeg);
1059
1060static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
1061        char *buf)
1062{
1063        switch (interface->type) {
1064        case ACER_AMW0:
1065                return sprintf(buf, "AMW0\n");
1066        case ACER_AMW0_V2:
1067                return sprintf(buf, "AMW0 v2\n");
1068        case ACER_WMID:
1069                return sprintf(buf, "WMID\n");
1070        default:
1071                return sprintf(buf, "Error!\n");
1072        }
1073}
1074
1075static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR,
1076        show_interface, NULL);
1077
1078/*
1079 * debugfs functions
1080 */
1081static u32 get_wmid_devices(void)
1082{
1083        struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
1084        union acpi_object *obj;
1085        acpi_status status;
1086
1087        status = wmi_query_block(WMID_GUID2, 1, &out);
1088        if (ACPI_FAILURE(status))
1089                return 0;
1090
1091        obj = (union acpi_object *) out.pointer;
1092        if (obj && obj->type == ACPI_TYPE_BUFFER &&
1093                obj->buffer.length == sizeof(u32)) {
1094                return *((u32 *) obj->buffer.pointer);
1095        } else {
1096                return 0;
1097        }
1098}
1099
1100/*
1101 * Platform device
1102 */
1103static int __devinit acer_platform_probe(struct platform_device *device)
1104{
1105        int err;
1106
1107        if (has_cap(ACER_CAP_MAILLED)) {
1108                err = acer_led_init(&device->dev);
1109                if (err)
1110                        goto error_mailled;
1111        }
1112
1113        if (has_cap(ACER_CAP_BRIGHTNESS)) {
1114                err = acer_backlight_init(&device->dev);
1115                if (err)
1116                        goto error_brightness;
1117        }
1118
1119        err = acer_rfkill_init(&device->dev);
1120
1121        return err;
1122
1123error_brightness:
1124        acer_led_exit();
1125error_mailled:
1126        return err;
1127}
1128
1129static int acer_platform_remove(struct platform_device *device)
1130{
1131        if (has_cap(ACER_CAP_MAILLED))
1132                acer_led_exit();
1133        if (has_cap(ACER_CAP_BRIGHTNESS))
1134                acer_backlight_exit();
1135
1136        acer_rfkill_exit();
1137        return 0;
1138}
1139
1140static int acer_platform_suspend(struct platform_device *dev,
1141pm_message_t state)
1142{
1143        u32 value;
1144        struct acer_data *data = &interface->data;
1145
1146        if (!data)
1147                return -ENOMEM;
1148
1149        if (has_cap(ACER_CAP_MAILLED)) {
1150                get_u32(&value, ACER_CAP_MAILLED);
1151                data->mailled = value;
1152        }
1153
1154        if (has_cap(ACER_CAP_BRIGHTNESS)) {
1155                get_u32(&value, ACER_CAP_BRIGHTNESS);
1156                data->brightness = value;
1157        }
1158
1159        return 0;
1160}
1161
1162static int acer_platform_resume(struct platform_device *device)
1163{
1164        struct acer_data *data = &interface->data;
1165
1166        if (!data)
1167                return -ENOMEM;
1168
1169        if (has_cap(ACER_CAP_MAILLED))
1170                set_u32(data->mailled, ACER_CAP_MAILLED);
1171
1172        if (has_cap(ACER_CAP_BRIGHTNESS))
1173                set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
1174
1175        return 0;
1176}
1177
1178static struct platform_driver acer_platform_driver = {
1179        .driver = {
1180                .name = "acer-wmi",
1181                .owner = THIS_MODULE,
1182        },
1183        .probe = acer_platform_probe,
1184        .remove = acer_platform_remove,
1185        .suspend = acer_platform_suspend,
1186        .resume = acer_platform_resume,
1187};
1188
1189static struct platform_device *acer_platform_device;
1190
1191static int remove_sysfs(struct platform_device *device)
1192{
1193        if (has_cap(ACER_CAP_THREEG))
1194                device_remove_file(&device->dev, &dev_attr_threeg);
1195
1196        device_remove_file(&device->dev, &dev_attr_interface);
1197
1198        return 0;
1199}
1200
1201static int create_sysfs(void)
1202{
1203        int retval = -ENOMEM;
1204
1205        if (has_cap(ACER_CAP_THREEG)) {
1206                retval = device_create_file(&acer_platform_device->dev,
1207                        &dev_attr_threeg);
1208                if (retval)
1209                        goto error_sysfs;
1210        }
1211
1212        retval = device_create_file(&acer_platform_device->dev,
1213                &dev_attr_interface);
1214        if (retval)
1215                goto error_sysfs;
1216
1217        return 0;
1218
1219error_sysfs:
1220                remove_sysfs(acer_platform_device);
1221        return retval;
1222}
1223
1224static void remove_debugfs(void)
1225{
1226        debugfs_remove(interface->debug.devices);
1227        debugfs_remove(interface->debug.root);
1228}
1229
1230static int create_debugfs(void)
1231{
1232        interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
1233        if (!interface->debug.root) {
1234                printk(ACER_ERR "Failed to create debugfs directory");
1235                return -ENOMEM;
1236        }
1237
1238        interface->debug.devices = debugfs_create_u32("devices", S_IRUGO,
1239                                        interface->debug.root,
1240                                        &interface->debug.wmid_devices);
1241        if (!interface->debug.devices)
1242                goto error_debugfs;
1243
1244        return 0;
1245
1246error_debugfs:
1247        remove_debugfs();
1248        return -ENOMEM;
1249}
1250
1251static int __init acer_wmi_init(void)
1252{
1253        int err;
1254
1255        printk(ACER_INFO "Acer Laptop ACPI-WMI Extras\n");
1256
1257        find_quirks();
1258
1259        /*
1260         * Detect which ACPI-WMI interface we're using.
1261         */
1262        if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1263                interface = &AMW0_V2_interface;
1264
1265        if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1266                interface = &wmid_interface;
1267
1268        if (wmi_has_guid(WMID_GUID2) && interface) {
1269                if (ACPI_FAILURE(WMID_set_capabilities())) {
1270                        printk(ACER_ERR "Unable to detect available WMID "
1271                                        "devices\n");
1272                        return -ENODEV;
1273                }
1274        } else if (!wmi_has_guid(WMID_GUID2) && interface) {
1275                printk(ACER_ERR "No WMID device detection method found\n");
1276                return -ENODEV;
1277        }
1278
1279        if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
1280                interface = &AMW0_interface;
1281
1282                if (ACPI_FAILURE(AMW0_set_capabilities())) {
1283                        printk(ACER_ERR "Unable to detect available AMW0 "
1284                                        "devices\n");
1285                        return -ENODEV;
1286                }
1287        }
1288
1289        if (wmi_has_guid(AMW0_GUID1))
1290                AMW0_find_mailled();
1291
1292        if (!interface) {
1293                printk(ACER_ERR "No or unsupported WMI interface, unable to "
1294                                "load\n");
1295                return -ENODEV;
1296        }
1297
1298        set_quirks();
1299
1300        if (!acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
1301                interface->capability &= ~ACER_CAP_BRIGHTNESS;
1302                printk(ACER_INFO "Brightness must be controlled by "
1303                       "generic video driver\n");
1304        }
1305
1306        if (platform_driver_register(&acer_platform_driver)) {
1307                printk(ACER_ERR "Unable to register platform driver.\n");
1308                goto error_platform_register;
1309        }
1310        acer_platform_device = platform_device_alloc("acer-wmi", -1);
1311        platform_device_add(acer_platform_device);
1312
1313        err = create_sysfs();
1314        if (err)
1315                return err;
1316
1317        if (wmi_has_guid(WMID_GUID2)) {
1318                interface->debug.wmid_devices = get_wmid_devices();
1319                err = create_debugfs();
1320                if (err)
1321                        return err;
1322        }
1323
1324        /* Override any initial settings with values from the commandline */
1325        acer_commandline_init();
1326
1327        return 0;
1328
1329error_platform_register:
1330        return -ENODEV;
1331}
1332
1333static void __exit acer_wmi_exit(void)
1334{
1335        remove_sysfs(acer_platform_device);
1336        remove_debugfs();
1337        platform_device_del(acer_platform_device);
1338        platform_driver_unregister(&acer_platform_driver);
1339
1340        printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
1341        return;
1342}
1343
1344module_init(acer_wmi_init);
1345module_exit(acer_wmi_exit);
1346