Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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