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