acer-wmi.c revision 370525df9dfb4f55dfc177b0c31c26d77694c992
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 0; 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 return AE_ERROR; 776 } 777 778 /* Not sure on the meaning of the relevant bits yet to detect these */ 779 interface->capability |= ACER_CAP_WIRELESS; 780 interface->capability |= ACER_CAP_THREEG; 781 782 /* WMID always provides brightness methods */ 783 interface->capability |= ACER_CAP_BRIGHTNESS; 784 785 if (devices & 0x10) 786 interface->capability |= ACER_CAP_BLUETOOTH; 787 788 if (!(devices & 0x20)) 789 max_brightness = 0x9; 790 791 return status; 792} 793 794static struct wmi_interface wmid_interface = { 795 .type = ACER_WMID, 796}; 797 798/* 799 * Generic Device (interface-independent) 800 */ 801 802static acpi_status get_u32(u32 *value, u32 cap) 803{ 804 acpi_status status = AE_ERROR; 805 806 switch (interface->type) { 807 case ACER_AMW0: 808 status = AMW0_get_u32(value, cap, interface); 809 break; 810 case ACER_AMW0_V2: 811 if (cap == ACER_CAP_MAILLED) { 812 status = AMW0_get_u32(value, cap, interface); 813 break; 814 } 815 case ACER_WMID: 816 status = WMID_get_u32(value, cap, interface); 817 break; 818 } 819 820 return status; 821} 822 823static acpi_status set_u32(u32 value, u32 cap) 824{ 825 acpi_status status; 826 827 if (interface->capability & cap) { 828 switch (interface->type) { 829 case ACER_AMW0: 830 return AMW0_set_u32(value, cap, interface); 831 case ACER_AMW0_V2: 832 if (cap == ACER_CAP_MAILLED) 833 return AMW0_set_u32(value, cap, interface); 834 835 /* 836 * On some models, some WMID methods don't toggle 837 * properly. For those cases, we want to run the AMW0 838 * method afterwards to be certain we've really toggled 839 * the device state. 840 */ 841 if (cap == ACER_CAP_WIRELESS || 842 cap == ACER_CAP_BLUETOOTH) { 843 status = WMID_set_u32(value, cap, interface); 844 if (ACPI_FAILURE(status)) 845 return status; 846 847 return AMW0_set_u32(value, cap, interface); 848 } 849 case ACER_WMID: 850 return WMID_set_u32(value, cap, interface); 851 default: 852 return AE_BAD_PARAMETER; 853 } 854 } 855 return AE_BAD_PARAMETER; 856} 857 858static void __init acer_commandline_init(void) 859{ 860 /* 861 * These will all fail silently if the value given is invalid, or the 862 * capability isn't available on the given interface 863 */ 864 set_u32(mailled, ACER_CAP_MAILLED); 865 set_u32(threeg, ACER_CAP_THREEG); 866 set_u32(brightness, ACER_CAP_BRIGHTNESS); 867} 868 869/* 870 * LED device (Mail LED only, no other LEDs known yet) 871 */ 872static void mail_led_set(struct led_classdev *led_cdev, 873enum led_brightness value) 874{ 875 set_u32(value, ACER_CAP_MAILLED); 876} 877 878static struct led_classdev mail_led = { 879 .name = "acer-wmi::mail", 880 .brightness_set = mail_led_set, 881}; 882 883static int __devinit acer_led_init(struct device *dev) 884{ 885 return led_classdev_register(dev, &mail_led); 886} 887 888static void acer_led_exit(void) 889{ 890 led_classdev_unregister(&mail_led); 891} 892 893/* 894 * Backlight device 895 */ 896static struct backlight_device *acer_backlight_device; 897 898static int read_brightness(struct backlight_device *bd) 899{ 900 u32 value; 901 get_u32(&value, ACER_CAP_BRIGHTNESS); 902 return value; 903} 904 905static int update_bl_status(struct backlight_device *bd) 906{ 907 int intensity = bd->props.brightness; 908 909 if (bd->props.power != FB_BLANK_UNBLANK) 910 intensity = 0; 911 if (bd->props.fb_blank != FB_BLANK_UNBLANK) 912 intensity = 0; 913 914 set_u32(intensity, ACER_CAP_BRIGHTNESS); 915 916 return 0; 917} 918 919static struct backlight_ops acer_bl_ops = { 920 .get_brightness = read_brightness, 921 .update_status = update_bl_status, 922}; 923 924static int __devinit acer_backlight_init(struct device *dev) 925{ 926 struct backlight_properties props; 927 struct backlight_device *bd; 928 929 memset(&props, 0, sizeof(struct backlight_properties)); 930 props.max_brightness = max_brightness; 931 bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops, 932 &props); 933 if (IS_ERR(bd)) { 934 printk(ACER_ERR "Could not register Acer backlight device\n"); 935 acer_backlight_device = NULL; 936 return PTR_ERR(bd); 937 } 938 939 acer_backlight_device = bd; 940 941 bd->props.power = FB_BLANK_UNBLANK; 942 bd->props.brightness = read_brightness(bd); 943 backlight_update_status(bd); 944 return 0; 945} 946 947static void acer_backlight_exit(void) 948{ 949 backlight_device_unregister(acer_backlight_device); 950} 951 952/* 953 * Rfkill devices 954 */ 955static void acer_rfkill_update(struct work_struct *ignored); 956static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update); 957static void acer_rfkill_update(struct work_struct *ignored) 958{ 959 u32 state; 960 acpi_status status; 961 962 status = get_u32(&state, ACER_CAP_WIRELESS); 963 if (ACPI_SUCCESS(status)) 964 rfkill_set_sw_state(wireless_rfkill, !state); 965 966 if (has_cap(ACER_CAP_BLUETOOTH)) { 967 status = get_u32(&state, ACER_CAP_BLUETOOTH); 968 if (ACPI_SUCCESS(status)) 969 rfkill_set_sw_state(bluetooth_rfkill, !state); 970 } 971 972 schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); 973} 974 975static int acer_rfkill_set(void *data, bool blocked) 976{ 977 acpi_status status; 978 u32 cap = (unsigned long)data; 979 status = set_u32(!blocked, cap); 980 if (ACPI_FAILURE(status)) 981 return -ENODEV; 982 return 0; 983} 984 985static const struct rfkill_ops acer_rfkill_ops = { 986 .set_block = acer_rfkill_set, 987}; 988 989static struct rfkill *acer_rfkill_register(struct device *dev, 990 enum rfkill_type type, 991 char *name, u32 cap) 992{ 993 int err; 994 struct rfkill *rfkill_dev; 995 996 rfkill_dev = rfkill_alloc(name, dev, type, 997 &acer_rfkill_ops, 998 (void *)(unsigned long)cap); 999 if (!rfkill_dev) 1000 return ERR_PTR(-ENOMEM); 1001 1002 err = rfkill_register(rfkill_dev); 1003 if (err) { 1004 rfkill_destroy(rfkill_dev); 1005 return ERR_PTR(err); 1006 } 1007 return rfkill_dev; 1008} 1009 1010static int acer_rfkill_init(struct device *dev) 1011{ 1012 wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN, 1013 "acer-wireless", ACER_CAP_WIRELESS); 1014 if (IS_ERR(wireless_rfkill)) 1015 return PTR_ERR(wireless_rfkill); 1016 1017 if (has_cap(ACER_CAP_BLUETOOTH)) { 1018 bluetooth_rfkill = acer_rfkill_register(dev, 1019 RFKILL_TYPE_BLUETOOTH, "acer-bluetooth", 1020 ACER_CAP_BLUETOOTH); 1021 if (IS_ERR(bluetooth_rfkill)) { 1022 rfkill_unregister(wireless_rfkill); 1023 rfkill_destroy(wireless_rfkill); 1024 return PTR_ERR(bluetooth_rfkill); 1025 } 1026 } 1027 1028 schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); 1029 1030 return 0; 1031} 1032 1033static void acer_rfkill_exit(void) 1034{ 1035 cancel_delayed_work_sync(&acer_rfkill_work); 1036 1037 rfkill_unregister(wireless_rfkill); 1038 rfkill_destroy(wireless_rfkill); 1039 1040 if (has_cap(ACER_CAP_BLUETOOTH)) { 1041 rfkill_unregister(bluetooth_rfkill); 1042 rfkill_destroy(bluetooth_rfkill); 1043 } 1044 return; 1045} 1046 1047/* 1048 * sysfs interface 1049 */ 1050static ssize_t show_bool_threeg(struct device *dev, 1051 struct device_attribute *attr, char *buf) 1052{ 1053 u32 result; \ 1054 acpi_status status = get_u32(&result, ACER_CAP_THREEG); 1055 if (ACPI_SUCCESS(status)) 1056 return sprintf(buf, "%u\n", result); 1057 return sprintf(buf, "Read error\n"); 1058} 1059 1060static ssize_t set_bool_threeg(struct device *dev, 1061 struct device_attribute *attr, const char *buf, size_t count) 1062{ 1063 u32 tmp = simple_strtoul(buf, NULL, 10); 1064 acpi_status status = set_u32(tmp, ACER_CAP_THREEG); 1065 if (ACPI_FAILURE(status)) 1066 return -EINVAL; 1067 return count; 1068} 1069static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg, 1070 set_bool_threeg); 1071 1072static ssize_t show_interface(struct device *dev, struct device_attribute *attr, 1073 char *buf) 1074{ 1075 switch (interface->type) { 1076 case ACER_AMW0: 1077 return sprintf(buf, "AMW0\n"); 1078 case ACER_AMW0_V2: 1079 return sprintf(buf, "AMW0 v2\n"); 1080 case ACER_WMID: 1081 return sprintf(buf, "WMID\n"); 1082 default: 1083 return sprintf(buf, "Error!\n"); 1084 } 1085} 1086 1087static DEVICE_ATTR(interface, S_IRUGO, 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 err = platform_driver_register(&acer_platform_driver); 1330 if (err) { 1331 printk(ACER_ERR "Unable to register platform driver.\n"); 1332 goto error_platform_register; 1333 } 1334 1335 acer_platform_device = platform_device_alloc("acer-wmi", -1); 1336 if (!acer_platform_device) { 1337 err = -ENOMEM; 1338 goto error_device_alloc; 1339 } 1340 1341 err = platform_device_add(acer_platform_device); 1342 if (err) 1343 goto error_device_add; 1344 1345 err = create_sysfs(); 1346 if (err) 1347 goto error_create_sys; 1348 1349 if (wmi_has_guid(WMID_GUID2)) { 1350 interface->debug.wmid_devices = get_wmid_devices(); 1351 err = create_debugfs(); 1352 if (err) 1353 goto error_create_debugfs; 1354 } 1355 1356 /* Override any initial settings with values from the commandline */ 1357 acer_commandline_init(); 1358 1359 return 0; 1360 1361error_create_debugfs: 1362 remove_sysfs(acer_platform_device); 1363error_create_sys: 1364 platform_device_del(acer_platform_device); 1365error_device_add: 1366 platform_device_put(acer_platform_device); 1367error_device_alloc: 1368 platform_driver_unregister(&acer_platform_driver); 1369error_platform_register: 1370 return err; 1371} 1372 1373static void __exit acer_wmi_exit(void) 1374{ 1375 remove_sysfs(acer_platform_device); 1376 remove_debugfs(); 1377 platform_device_unregister(acer_platform_device); 1378 platform_driver_unregister(&acer_platform_driver); 1379 1380 printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n"); 1381 return; 1382} 1383 1384module_init(acer_wmi_init); 1385module_exit(acer_wmi_exit); 1386