asus-laptop.c revision d99b577c729c4a29679fb1f605f9ccace154e0e5
1/* 2 * asus-laptop.c - Asus Laptop Support 3 * 4 * 5 * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor 6 * Copyright (C) 2006-2007 Corentin Chary 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 * 23 * The development page for this driver is located at 24 * http://sourceforge.net/projects/acpi4asus/ 25 * 26 * Credits: 27 * Pontus Fuchs - Helper functions, cleanup 28 * Johann Wiesner - Small compile fixes 29 * John Belmonte - ACPI code for Toshiba laptop was a good starting point. 30 * Eric Burghard - LED display support for W1N 31 * Josh Green - Light Sens support 32 * Thomas Tuttle - His first patch for led support was very helpfull 33 * Sam Lin - GPS support 34 */ 35 36#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 37 38#include <linux/kernel.h> 39#include <linux/module.h> 40#include <linux/init.h> 41#include <linux/types.h> 42#include <linux/err.h> 43#include <linux/proc_fs.h> 44#include <linux/backlight.h> 45#include <linux/fb.h> 46#include <linux/leds.h> 47#include <linux/platform_device.h> 48#include <acpi/acpi_drivers.h> 49#include <acpi/acpi_bus.h> 50#include <asm/uaccess.h> 51#include <linux/input.h> 52 53#define ASUS_LAPTOP_VERSION "0.42" 54 55#define ASUS_LAPTOP_NAME "Asus Laptop Support" 56#define ASUS_LAPTOP_CLASS "hotkey" 57#define ASUS_LAPTOP_DEVICE_NAME "Hotkey" 58#define ASUS_LAPTOP_FILE KBUILD_MODNAME 59#define ASUS_LAPTOP_PREFIX "\\_SB.ATKD." 60 61MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary"); 62MODULE_DESCRIPTION(ASUS_LAPTOP_NAME); 63MODULE_LICENSE("GPL"); 64 65/* 66 * WAPF defines the behavior of the Fn+Fx wlan key 67 * The significance of values is yet to be found, but 68 * most of the time: 69 * 0x0 will do nothing 70 * 0x1 will allow to control the device with Fn+Fx key. 71 * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key 72 * 0x5 like 0x1 or 0x4 73 * So, if something doesn't work as you want, just try other values =) 74 */ 75static uint wapf = 1; 76module_param(wapf, uint, 0644); 77MODULE_PARM_DESC(wapf, "WAPF value"); 78 79static uint wireless_status = 1; 80static uint bluetooth_status = 1; 81 82module_param(wireless_status, uint, 0644); 83MODULE_PARM_DESC(wireless_status, "Set the wireless status on boot " 84 "(0 = disabled, 1 = enabled, -1 = don't do anything). " 85 "default is 1"); 86 87module_param(bluetooth_status, uint, 0644); 88MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot " 89 "(0 = disabled, 1 = enabled, -1 = don't do anything). " 90 "default is 1"); 91 92/* 93 * Some events we use, same for all Asus 94 */ 95#define ATKD_BR_UP 0x10 96#define ATKD_BR_DOWN 0x20 97#define ATKD_LCD_ON 0x33 98#define ATKD_LCD_OFF 0x34 99 100/* 101 * Known bits returned by \_SB.ATKD.HWRS 102 */ 103#define WL_HWRS 0x80 104#define BT_HWRS 0x100 105 106/* 107 * Flags for hotk status 108 * WL_ON and BT_ON are also used for wireless_status() 109 */ 110#define WL_RSTS 0x01 /* internal Wifi */ 111#define BT_RSTS 0x02 /* internal Bluetooth */ 112 113#define ASUS_HANDLE(object, paths...) \ 114 static acpi_handle object##_handle = NULL; \ 115 static char *object##_paths[] = { paths } 116 117/* LED */ 118#define METHOD_MLED "MLED" 119#define METHOD_TLED "TLED" 120#define METHOD_RLED "RLED" /* W1JC */ 121#define METHOD_PLED "PLED" /* A7J */ 122#define METHOD_GLED "GLED" /* G1, G2 (probably) */ 123 124/* LEDD */ 125#define METHOD_LEDD "SLCM" 126 127/* 128 * Bluetooth and WLAN 129 * WLED and BLED are not handled like other XLED, because in some dsdt 130 * they also control the WLAN/Bluetooth device. 131 */ 132#define METHOD_WLAN "WLED" 133#define METHOD_BLUETOOTH "BLED" 134#define METHOD_WL_STATUS "RSTS" 135 136/* Brightness */ 137#define METHOD_BRIGHTNESS_SET "SPLV" 138#define METHOD_BRIGHTNESS_GET "GPLV" 139 140/* Backlight */ 141ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */ 142 "\\_SB.PCI0.ISA.EC0._Q10", /* A1x */ 143 "\\_SB.PCI0.PX40.ECD0._Q10", /* L3C */ 144 "\\_SB.PCI0.PX40.EC0.Q10", /* M1A */ 145 "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */ 146 "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */ 147 "\\_SB.PCI0.PX40.Q10", /* S1x */ 148 "\\Q10"); /* A2x, L2D, L3D, M2E */ 149 150/* Display */ 151#define METHOD_SWITCH_DISPLAY "SDSP" 152ASUS_HANDLE(display_get, 153 /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */ 154 "\\_SB.PCI0.P0P1.VGA.GETD", 155 /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */ 156 "\\_SB.PCI0.P0P2.VGA.GETD", 157 /* A6V A6Q */ 158 "\\_SB.PCI0.P0P3.VGA.GETD", 159 /* A6T, A6M */ 160 "\\_SB.PCI0.P0PA.VGA.GETD", 161 /* L3C */ 162 "\\_SB.PCI0.PCI1.VGAC.NMAP", 163 /* Z96F */ 164 "\\_SB.PCI0.VGA.GETD", 165 /* A2D */ 166 "\\ACTD", 167 /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ 168 "\\ADVG", 169 /* P30 */ 170 "\\DNXT", 171 /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ 172 "\\INFB", 173 /* A3F A6F A3N A3L M6N W3N W6A */ 174 "\\SSTE"); 175 176#define METHOD_ALS_CONTROL "ALSC" /* Z71A Z71V */ 177#define METHOD_ALS_LEVEL "ALSL" /* Z71A Z71V */ 178 179/* GPS */ 180/* R2H use different handle for GPS on/off */ 181#define METHOD_GPS_ON "SDON" 182#define METHOD_GPS_OFF "SDOF" 183#define METHOD_GPS_STATUS "GPST" 184 185/* Keyboard light */ 186#define METHOD_KBD_LIGHT_SET "SLKB" 187#define METHOD_KBD_LIGHT_GET "GLKB" 188 189/* 190 * Define a specific led structure to keep the main structure clean 191 */ 192#define ASUS_DEFINE_LED(object) \ 193 int object##_wk; \ 194 struct work_struct object##_work; \ 195 struct led_classdev object; 196 197 198#define led_to_asus(led_cdev, led) \ 199 container_of(container_of(led_cdev, struct asus_laptop_leds, \ 200 led), \ 201 struct asus_laptop, leds) 202#define work_to_asus(work, led) \ 203 container_of(container_of(work, struct asus_laptop_leds, \ 204 led##_work), \ 205 struct asus_laptop, leds) 206 207struct asus_laptop_leds { 208 ASUS_DEFINE_LED(mled) 209 ASUS_DEFINE_LED(tled) 210 ASUS_DEFINE_LED(rled) 211 ASUS_DEFINE_LED(pled) 212 ASUS_DEFINE_LED(gled) 213 ASUS_DEFINE_LED(kled) 214 struct workqueue_struct *workqueue; 215}; 216 217/* 218 * This is the main structure, we can use it to store anything interesting 219 * about the hotk device 220 */ 221struct asus_laptop { 222 char *name; /* laptop name */ 223 224 struct acpi_table_header *dsdt_info; 225 struct platform_device *platform_device; 226 struct acpi_device *device; /* the device we are in */ 227 struct backlight_device *backlight_device; 228 229 struct input_dev *inputdev; 230 struct key_entry *keymap; 231 232 struct asus_laptop_leds leds; 233 234 int wireless_status; 235 bool have_rsts; 236 int lcd_state; 237 238 acpi_handle handle; /* the handle of the hotk device */ 239 u32 ledd_status; /* status of the LED display */ 240 u8 light_level; /* light sensor level */ 241 u8 light_switch; /* light sensor switch value */ 242 u16 event_count[128]; /* count for each event TODO make this better */ 243 u16 *keycode_map; 244}; 245 246struct key_entry { 247 char type; 248 u8 code; 249 u16 keycode; 250}; 251 252enum { KE_KEY, KE_END }; 253 254static const struct key_entry asus_keymap[] = { 255 {KE_KEY, 0x02, KEY_SCREENLOCK}, 256 {KE_KEY, 0x05, KEY_WLAN}, 257 {KE_KEY, 0x08, KEY_F13}, 258 {KE_KEY, 0x17, KEY_ZOOM}, 259 {KE_KEY, 0x1f, KEY_BATTERY}, 260 {KE_KEY, 0x30, KEY_VOLUMEUP}, 261 {KE_KEY, 0x31, KEY_VOLUMEDOWN}, 262 {KE_KEY, 0x32, KEY_MUTE}, 263 {KE_KEY, 0x33, KEY_SWITCHVIDEOMODE}, 264 {KE_KEY, 0x34, KEY_SWITCHVIDEOMODE}, 265 {KE_KEY, 0x40, KEY_PREVIOUSSONG}, 266 {KE_KEY, 0x41, KEY_NEXTSONG}, 267 {KE_KEY, 0x43, KEY_STOPCD}, 268 {KE_KEY, 0x45, KEY_PLAYPAUSE}, 269 {KE_KEY, 0x4c, KEY_MEDIA}, 270 {KE_KEY, 0x50, KEY_EMAIL}, 271 {KE_KEY, 0x51, KEY_WWW}, 272 {KE_KEY, 0x55, KEY_CALC}, 273 {KE_KEY, 0x5C, KEY_SCREENLOCK}, /* Screenlock */ 274 {KE_KEY, 0x5D, KEY_WLAN}, 275 {KE_KEY, 0x5E, KEY_WLAN}, 276 {KE_KEY, 0x5F, KEY_WLAN}, 277 {KE_KEY, 0x60, KEY_SWITCHVIDEOMODE}, 278 {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE}, 279 {KE_KEY, 0x62, KEY_SWITCHVIDEOMODE}, 280 {KE_KEY, 0x63, KEY_SWITCHVIDEOMODE}, 281 {KE_KEY, 0x6B, KEY_F13}, /* Lock Touchpad */ 282 {KE_KEY, 0x82, KEY_CAMERA}, 283 {KE_KEY, 0x88, KEY_WLAN }, 284 {KE_KEY, 0x8A, KEY_PROG1}, 285 {KE_KEY, 0x95, KEY_MEDIA}, 286 {KE_KEY, 0x99, KEY_PHONE}, 287 {KE_KEY, 0xc4, KEY_KBDILLUMUP}, 288 {KE_KEY, 0xc5, KEY_KBDILLUMDOWN}, 289 {KE_END, 0}, 290}; 291 292/* 293 * This function evaluates an ACPI method, given an int as parameter, the 294 * method is searched within the scope of the handle, can be NULL. The output 295 * of the method is written is output, which can also be NULL 296 * 297 * returns 0 if write is successful, -1 else. 298 */ 299static int write_acpi_int_ret(acpi_handle handle, const char *method, int val, 300 struct acpi_buffer *output) 301{ 302 struct acpi_object_list params; /* list of input parameters (an int) */ 303 union acpi_object in_obj; /* the only param we use */ 304 acpi_status status; 305 306 if (!handle) 307 return 0; 308 309 params.count = 1; 310 params.pointer = &in_obj; 311 in_obj.type = ACPI_TYPE_INTEGER; 312 in_obj.integer.value = val; 313 314 status = acpi_evaluate_object(handle, (char *)method, ¶ms, output); 315 if (status == AE_OK) 316 return 0; 317 else 318 return -1; 319} 320 321static int write_acpi_int(acpi_handle handle, const char *method, int val) 322{ 323 return write_acpi_int_ret(handle, method, val, NULL); 324} 325 326static int acpi_check_handle(acpi_handle handle, const char *method, 327 acpi_handle *ret) 328{ 329 acpi_status status; 330 331 if (method == NULL) 332 return -ENODEV; 333 334 if (ret) 335 status = acpi_get_handle(handle, (char *)method, 336 ret); 337 else { 338 acpi_handle dummy; 339 340 status = acpi_get_handle(handle, (char *)method, 341 &dummy); 342 } 343 344 if (status != AE_OK) { 345 if (ret) 346 pr_warning("Error finding %s\n", method); 347 return -ENODEV; 348 } 349 return 0; 350} 351 352/* Generic LED function */ 353static void asus_led_set(struct asus_laptop *asus, char *method, 354 int value) 355{ 356 if (!strcmp(method, METHOD_MLED)) 357 value = !value; 358 else if (!strcmp(method, METHOD_GLED)) 359 value = !value + 1; 360 else 361 value = !!value; 362 363 write_acpi_int(asus->handle, method, value); 364} 365 366/* 367 * LEDs 368 */ 369#define ASUS_LED(object, ledname, max) \ 370 static void object##_led_set(struct led_classdev *led_cdev, \ 371 enum led_brightness value); \ 372 static enum led_brightness object##_led_get( \ 373 struct led_classdev *led_cdev); \ 374 static void object##_led_update(struct work_struct *ignored); \ 375 static struct led_classdev object##_led = { \ 376 .name = "asus::" ledname, \ 377 .brightness_set = object##_led_set, \ 378 .brightness_get = object##_led_get, \ 379 .max_brightness = max \ 380 } 381 382ASUS_LED(mled, "mail", 1); 383ASUS_LED(tled, "touchpad", 1); 384ASUS_LED(rled, "record", 1); 385ASUS_LED(pled, "phone", 1); 386ASUS_LED(gled, "gaming", 1); 387ASUS_LED(kled, "kbd_backlight", 3); 388 389/* /sys/class/led handlers */ 390#define ASUS_LED_HANDLER(object, method) \ 391 static void object##_led_set(struct led_classdev *led_cdev, \ 392 enum led_brightness value) \ 393 { \ 394 struct asus_laptop *asus = \ 395 led_to_asus(led_cdev, object); \ 396 \ 397 asus->leds.object##_wk = (value > 0) ? 1 : 0; \ 398 queue_work(asus->leds.workqueue, \ 399 &asus->leds.object##_work); \ 400 } \ 401 static void object##_led_update(struct work_struct *work) \ 402 { \ 403 struct asus_laptop *asus = work_to_asus(work, object); \ 404 \ 405 int value = asus->leds.object##_wk; \ 406 asus_led_set(asus, method, value); \ 407 } \ 408 static enum led_brightness object##_led_get( \ 409 struct led_classdev *led_cdev) \ 410 { \ 411 return led_cdev->brightness; \ 412 } 413 414ASUS_LED_HANDLER(mled, METHOD_MLED); 415ASUS_LED_HANDLER(pled, METHOD_PLED); 416ASUS_LED_HANDLER(rled, METHOD_RLED); 417ASUS_LED_HANDLER(tled, METHOD_TLED); 418ASUS_LED_HANDLER(gled, METHOD_GLED); 419 420/* 421 * Keyboard backlight (also a LED) 422 */ 423static int asus_kled_lvl(struct asus_laptop *asus) 424{ 425 unsigned long long kblv; 426 struct acpi_object_list params; 427 union acpi_object in_obj; 428 acpi_status rv; 429 430 params.count = 1; 431 params.pointer = &in_obj; 432 in_obj.type = ACPI_TYPE_INTEGER; 433 in_obj.integer.value = 2; 434 435 rv = acpi_evaluate_integer(asus->handle, METHOD_KBD_LIGHT_GET, 436 ¶ms, &kblv); 437 if (ACPI_FAILURE(rv)) { 438 pr_warning("Error reading kled level\n"); 439 return -ENODEV; 440 } 441 return kblv; 442} 443 444static int asus_kled_set(struct asus_laptop *asus, int kblv) 445{ 446 if (kblv > 0) 447 kblv = (1 << 7) | (kblv & 0x7F); 448 else 449 kblv = 0; 450 451 if (write_acpi_int(asus->handle, METHOD_KBD_LIGHT_SET, kblv)) { 452 pr_warning("Keyboard LED display write failed\n"); 453 return -EINVAL; 454 } 455 return 0; 456} 457 458static void kled_led_set(struct led_classdev *led_cdev, 459 enum led_brightness value) 460{ 461 struct asus_laptop *asus = led_to_asus(led_cdev, kled); 462 463 asus->leds.kled_wk = value; 464 queue_work(asus->leds.workqueue, &asus->leds.kled_work); 465} 466 467static void kled_led_update(struct work_struct *work) 468{ 469 struct asus_laptop *asus = work_to_asus(work, kled); 470 471 asus_kled_set(asus, asus->leds.kled_wk); 472} 473 474static enum led_brightness kled_led_get(struct led_classdev *led_cdev) 475{ 476 struct asus_laptop *asus = led_to_asus(led_cdev, kled); 477 478 return asus_kled_lvl(asus); 479} 480 481#define ASUS_LED_UNREGISTER(object) \ 482 if (object##_led.dev) \ 483 led_classdev_unregister(&object##_led) 484 485static void asus_led_exit(struct asus_laptop *asus) 486{ 487 ASUS_LED_UNREGISTER(mled); 488 ASUS_LED_UNREGISTER(tled); 489 ASUS_LED_UNREGISTER(pled); 490 ASUS_LED_UNREGISTER(rled); 491 ASUS_LED_UNREGISTER(gled); 492 ASUS_LED_UNREGISTER(kled); 493 if (asus->leds.workqueue) { 494 destroy_workqueue(asus->leds.workqueue); 495 asus->leds.workqueue = NULL; 496 } 497} 498 499/* Ugly macro, need to fix that later */ 500#define ASUS_LED_REGISTER(asus, object, _name, max, method) \ 501 do { \ 502 struct led_classdev *ldev = &asus->leds.object; \ 503 \ 504 if (method && acpi_check_handle(asus->handle, method, NULL)) \ 505 break ; \ 506 \ 507 INIT_WORK(&asus->leds.object##_work, object##_led_update); \ 508 ldev->name = "asus::" _name; \ 509 ldev->brightness_set = object##_led_set; \ 510 ldev->max_brightness = max; \ 511 rv = led_classdev_register(&asus->platform_device->dev, ldev); \ 512 if (rv) \ 513 goto error; \ 514 } while (0) 515 516static int asus_led_init(struct asus_laptop *asus) 517{ 518 int rv; 519 520 /* 521 * Functions that actually update the LED's are called from a 522 * workqueue. By doing this as separate work rather than when the LED 523 * subsystem asks, we avoid messing with the Asus ACPI stuff during a 524 * potentially bad time, such as a timer interrupt. 525 */ 526 asus->leds.workqueue = create_singlethread_workqueue("led_workqueue"); 527 if (!asus->leds.workqueue) 528 return -ENOMEM; 529 530 ASUS_LED_REGISTER(asus, mled, "mail", 1, METHOD_MLED); 531 ASUS_LED_REGISTER(asus, tled, "touchpad", 1, METHOD_TLED); 532 ASUS_LED_REGISTER(asus, rled, "record", 1, METHOD_RLED); 533 ASUS_LED_REGISTER(asus, pled, "phone", 1, METHOD_PLED); 534 ASUS_LED_REGISTER(asus, gled, "gaming", 1, METHOD_GLED); 535 if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL) && 536 !acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_GET, NULL)) 537 ASUS_LED_REGISTER(asus, kled, "kbd_backlight", 3, NULL); 538error: 539 if (rv) 540 asus_led_exit(asus); 541 return rv; 542} 543 544/* 545 * Backlight device 546 */ 547static int asus_lcd_status(struct asus_laptop *asus) 548{ 549 return asus->lcd_state; 550} 551 552static int asus_lcd_set(struct asus_laptop *asus, int value) 553{ 554 int lcd = 0; 555 acpi_status status = 0; 556 557 lcd = !!value; 558 559 if (lcd == asus_lcd_status(asus)) 560 return 0; 561 562 if (!lcd_switch_handle) 563 return -ENODEV; 564 565 status = acpi_evaluate_object(lcd_switch_handle, 566 NULL, NULL, NULL); 567 568 if (ACPI_FAILURE(status)) { 569 pr_warning("Error switching LCD\n"); 570 return -ENODEV; 571 } 572 573 asus->lcd_state = lcd; 574 return 0; 575} 576 577static void lcd_blank(struct asus_laptop *asus, int blank) 578{ 579 struct backlight_device *bd = asus->backlight_device; 580 581 asus->lcd_state = (blank == FB_BLANK_UNBLANK); 582 583 if (bd) { 584 bd->props.power = blank; 585 backlight_update_status(bd); 586 } 587} 588 589static int asus_read_brightness(struct backlight_device *bd) 590{ 591 struct asus_laptop *asus = bl_get_data(bd); 592 unsigned long long value; 593 acpi_status rv = AE_OK; 594 595 rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET, 596 NULL, &value); 597 if (ACPI_FAILURE(rv)) 598 pr_warning("Error reading brightness\n"); 599 600 return value; 601} 602 603static int asus_set_brightness(struct backlight_device *bd, int value) 604{ 605 struct asus_laptop *asus = bl_get_data(bd); 606 607 if (write_acpi_int(asus->handle, METHOD_BRIGHTNESS_SET, value)) { 608 pr_warning("Error changing brightness\n"); 609 return -EIO; 610 } 611 return 0; 612} 613 614static int update_bl_status(struct backlight_device *bd) 615{ 616 struct asus_laptop *asus = bl_get_data(bd); 617 int rv; 618 int value = bd->props.brightness; 619 620 rv = asus_set_brightness(bd, value); 621 if (rv) 622 return rv; 623 624 value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0; 625 return asus_lcd_set(asus, value); 626} 627 628static struct backlight_ops asusbl_ops = { 629 .get_brightness = asus_read_brightness, 630 .update_status = update_bl_status, 631}; 632 633static int asus_backlight_init(struct asus_laptop *asus) 634{ 635 struct backlight_device *bd; 636 struct device *dev = &asus->platform_device->dev; 637 638 if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) && 639 !acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) && 640 lcd_switch_handle) { 641 bd = backlight_device_register(ASUS_LAPTOP_FILE, dev, 642 asus, &asusbl_ops); 643 if (IS_ERR(bd)) { 644 pr_err("Could not register asus backlight device\n"); 645 asus->backlight_device = NULL; 646 return PTR_ERR(bd); 647 } 648 649 asus->backlight_device = bd; 650 651 bd->props.max_brightness = 15; 652 bd->props.power = FB_BLANK_UNBLANK; 653 bd->props.brightness = asus_read_brightness(bd); 654 backlight_update_status(bd); 655 } 656 return 0; 657} 658 659static void asus_backlight_exit(struct asus_laptop *asus) 660{ 661 if (asus->backlight_device) 662 backlight_device_unregister(asus->backlight_device); 663} 664 665/* 666 * Platform device handlers 667 */ 668 669/* 670 * We write our info in page, we begin at offset off and cannot write more 671 * than count bytes. We set eof to 1 if we handle those 2 values. We return the 672 * number of bytes written in page 673 */ 674static ssize_t show_infos(struct device *dev, 675 struct device_attribute *attr, char *page) 676{ 677 struct asus_laptop *asus = dev_get_drvdata(dev); 678 int len = 0; 679 unsigned long long temp; 680 char buf[16]; /* enough for all info */ 681 acpi_status rv = AE_OK; 682 683 /* 684 * We use the easy way, we don't care of off and count, so we don't set eof 685 * to 1 686 */ 687 688 len += sprintf(page, ASUS_LAPTOP_NAME " " ASUS_LAPTOP_VERSION "\n"); 689 len += sprintf(page + len, "Model reference : %s\n", asus->name); 690 /* 691 * The SFUN method probably allows the original driver to get the list 692 * of features supported by a given model. For now, 0x0100 or 0x0800 693 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card. 694 * The significance of others is yet to be found. 695 */ 696 rv = acpi_evaluate_integer(asus->handle, "SFUN", NULL, &temp); 697 if (!ACPI_FAILURE(rv)) 698 len += sprintf(page + len, "SFUN value : %#x\n", 699 (uint) temp); 700 /* 701 * The HWRS method return informations about the hardware. 702 * 0x80 bit is for WLAN, 0x100 for Bluetooth. 703 * The significance of others is yet to be found. 704 * If we don't find the method, we assume the device are present. 705 */ 706 rv = acpi_evaluate_integer(asus->handle, "HRWS", NULL, &temp); 707 if (!ACPI_FAILURE(rv)) 708 len += sprintf(page + len, "HRWS value : %#x\n", 709 (uint) temp); 710 /* 711 * Another value for userspace: the ASYM method returns 0x02 for 712 * battery low and 0x04 for battery critical, its readings tend to be 713 * more accurate than those provided by _BST. 714 * Note: since not all the laptops provide this method, errors are 715 * silently ignored. 716 */ 717 rv = acpi_evaluate_integer(asus->handle, "ASYM", NULL, &temp); 718 if (!ACPI_FAILURE(rv)) 719 len += sprintf(page + len, "ASYM value : %#x\n", 720 (uint) temp); 721 if (asus->dsdt_info) { 722 snprintf(buf, 16, "%d", asus->dsdt_info->length); 723 len += sprintf(page + len, "DSDT length : %s\n", buf); 724 snprintf(buf, 16, "%d", asus->dsdt_info->checksum); 725 len += sprintf(page + len, "DSDT checksum : %s\n", buf); 726 snprintf(buf, 16, "%d", asus->dsdt_info->revision); 727 len += sprintf(page + len, "DSDT revision : %s\n", buf); 728 snprintf(buf, 7, "%s", asus->dsdt_info->oem_id); 729 len += sprintf(page + len, "OEM id : %s\n", buf); 730 snprintf(buf, 9, "%s", asus->dsdt_info->oem_table_id); 731 len += sprintf(page + len, "OEM table id : %s\n", buf); 732 snprintf(buf, 16, "%x", asus->dsdt_info->oem_revision); 733 len += sprintf(page + len, "OEM revision : 0x%s\n", buf); 734 snprintf(buf, 5, "%s", asus->dsdt_info->asl_compiler_id); 735 len += sprintf(page + len, "ASL comp vendor id : %s\n", buf); 736 snprintf(buf, 16, "%x", asus->dsdt_info->asl_compiler_revision); 737 len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf); 738 } 739 740 return len; 741} 742 743static int parse_arg(const char *buf, unsigned long count, int *val) 744{ 745 if (!count) 746 return 0; 747 if (count > 31) 748 return -EINVAL; 749 if (sscanf(buf, "%i", val) != 1) 750 return -EINVAL; 751 return count; 752} 753 754static ssize_t sysfs_acpi_set(struct asus_laptop *asus, 755 const char *buf, size_t count, 756 const char *method) 757{ 758 int rv, value; 759 int out = 0; 760 761 rv = parse_arg(buf, count, &value); 762 if (rv > 0) 763 out = value ? 1 : 0; 764 765 if (write_acpi_int(asus->handle, method, value)) 766 return -ENODEV; 767 return rv; 768} 769 770/* 771 * LEDD display 772 */ 773static ssize_t show_ledd(struct device *dev, 774 struct device_attribute *attr, char *buf) 775{ 776 struct asus_laptop *asus = dev_get_drvdata(dev); 777 778 return sprintf(buf, "0x%08x\n", asus->ledd_status); 779} 780 781static ssize_t store_ledd(struct device *dev, struct device_attribute *attr, 782 const char *buf, size_t count) 783{ 784 struct asus_laptop *asus = dev_get_drvdata(dev); 785 int rv, value; 786 787 rv = parse_arg(buf, count, &value); 788 if (rv > 0) { 789 if (write_acpi_int(asus->handle, METHOD_LEDD, value)) 790 pr_warning("LED display write failed\n"); 791 else 792 asus->ledd_status = (u32) value; 793 } 794 return rv; 795} 796 797/* 798 * Wireless 799 */ 800static int asus_wireless_status(struct asus_laptop *asus, int mask) 801{ 802 unsigned long long status; 803 acpi_status rv = AE_OK; 804 805 if (!asus->have_rsts) 806 return (asus->wireless_status & mask) ? 1 : 0; 807 808 rv = acpi_evaluate_integer(asus->handle, METHOD_WL_STATUS, 809 NULL, &status); 810 if (ACPI_FAILURE(rv)) { 811 pr_warning("Error reading Wireless status\n"); 812 return -EINVAL; 813 } 814 return !!(status & mask); 815} 816 817/* 818 * WLAN 819 */ 820static ssize_t show_wlan(struct device *dev, 821 struct device_attribute *attr, char *buf) 822{ 823 struct asus_laptop *asus = dev_get_drvdata(dev); 824 825 return sprintf(buf, "%d\n", asus_wireless_status(asus, WL_RSTS)); 826} 827 828static ssize_t store_wlan(struct device *dev, struct device_attribute *attr, 829 const char *buf, size_t count) 830{ 831 struct asus_laptop *asus = dev_get_drvdata(dev); 832 833 return sysfs_acpi_set(asus, buf, count, METHOD_WLAN); 834} 835 836/* 837 * Bluetooth 838 */ 839static ssize_t show_bluetooth(struct device *dev, 840 struct device_attribute *attr, char *buf) 841{ 842 struct asus_laptop *asus = dev_get_drvdata(dev); 843 844 return sprintf(buf, "%d\n", asus_wireless_status(asus, BT_RSTS)); 845} 846 847static ssize_t store_bluetooth(struct device *dev, 848 struct device_attribute *attr, const char *buf, 849 size_t count) 850{ 851 struct asus_laptop *asus = dev_get_drvdata(dev); 852 853 return sysfs_acpi_set(asus, buf, count, METHOD_BLUETOOTH); 854} 855 856/* 857 * Display 858 */ 859static void asus_set_display(struct asus_laptop *asus, int value) 860{ 861 /* no sanity check needed for now */ 862 if (write_acpi_int(asus->handle, METHOD_SWITCH_DISPLAY, value)) 863 pr_warning("Error setting display\n"); 864 return; 865} 866 867static int read_display(struct asus_laptop *asus) 868{ 869 unsigned long long value = 0; 870 acpi_status rv = AE_OK; 871 872 /* 873 * In most of the case, we know how to set the display, but sometime 874 * we can't read it 875 */ 876 if (display_get_handle) { 877 rv = acpi_evaluate_integer(display_get_handle, NULL, 878 NULL, &value); 879 if (ACPI_FAILURE(rv)) 880 pr_warning("Error reading display status\n"); 881 } 882 883 value &= 0x0F; /* needed for some models, shouldn't hurt others */ 884 885 return value; 886} 887 888/* 889 * Now, *this* one could be more user-friendly, but so far, no-one has 890 * complained. The significance of bits is the same as in store_disp() 891 */ 892static ssize_t show_disp(struct device *dev, 893 struct device_attribute *attr, char *buf) 894{ 895 struct asus_laptop *asus = dev_get_drvdata(dev); 896 897 return sprintf(buf, "%d\n", read_display(asus)); 898} 899 900/* 901 * Experimental support for display switching. As of now: 1 should activate 902 * the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI. 903 * Any combination (bitwise) of these will suffice. I never actually tested 4 904 * displays hooked up simultaneously, so be warned. See the acpi4asus README 905 * for more info. 906 */ 907static ssize_t store_disp(struct device *dev, struct device_attribute *attr, 908 const char *buf, size_t count) 909{ 910 struct asus_laptop *asus = dev_get_drvdata(dev); 911 int rv, value; 912 913 rv = parse_arg(buf, count, &value); 914 if (rv > 0) 915 asus_set_display(asus, value); 916 return rv; 917} 918 919/* 920 * Light Sens 921 */ 922static void asus_als_switch(struct asus_laptop *asus, int value) 923{ 924 if (write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value)) 925 pr_warning("Error setting light sensor switch\n"); 926 asus->light_switch = value; 927} 928 929static ssize_t show_lssw(struct device *dev, 930 struct device_attribute *attr, char *buf) 931{ 932 struct asus_laptop *asus = dev_get_drvdata(dev); 933 934 return sprintf(buf, "%d\n", asus->light_switch); 935} 936 937static ssize_t store_lssw(struct device *dev, struct device_attribute *attr, 938 const char *buf, size_t count) 939{ 940 struct asus_laptop *asus = dev_get_drvdata(dev); 941 int rv, value; 942 943 rv = parse_arg(buf, count, &value); 944 if (rv > 0) 945 asus_als_switch(asus, value ? 1 : 0); 946 947 return rv; 948} 949 950static void asus_als_level(struct asus_laptop *asus, int value) 951{ 952 if (write_acpi_int(asus->handle, METHOD_ALS_LEVEL, value)) 953 pr_warning("Error setting light sensor level\n"); 954 asus->light_level = value; 955} 956 957static ssize_t show_lslvl(struct device *dev, 958 struct device_attribute *attr, char *buf) 959{ 960 struct asus_laptop *asus = dev_get_drvdata(dev); 961 962 return sprintf(buf, "%d\n", asus->light_level); 963} 964 965static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr, 966 const char *buf, size_t count) 967{ 968 struct asus_laptop *asus = dev_get_drvdata(dev); 969 int rv, value; 970 971 rv = parse_arg(buf, count, &value); 972 if (rv > 0) { 973 value = (0 < value) ? ((15 < value) ? 15 : value) : 0; 974 /* 0 <= value <= 15 */ 975 asus_als_level(asus, value); 976 } 977 978 return rv; 979} 980 981/* 982 * GPS 983 * TODO: use rfkill 984 */ 985static int asus_gps_status(struct asus_laptop *asus) 986{ 987 unsigned long long status; 988 acpi_status rv = AE_OK; 989 990 rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS, 991 NULL, &status); 992 if (ACPI_FAILURE(rv)) { 993 pr_warning("Error reading GPS status\n"); 994 return -ENODEV; 995 } 996 return !!status; 997} 998 999static int asus_gps_switch(struct asus_laptop *asus, int status) 1000{ 1001 const char *meth = status ? METHOD_GPS_ON : METHOD_GPS_OFF; 1002 1003 if (write_acpi_int(asus->handle, meth, 0x02)) 1004 return -ENODEV; 1005 return 0; 1006} 1007 1008static ssize_t show_gps(struct device *dev, 1009 struct device_attribute *attr, char *buf) 1010{ 1011 struct asus_laptop *asus = dev_get_drvdata(dev); 1012 1013 return sprintf(buf, "%d\n", asus_gps_status(asus)); 1014} 1015 1016static ssize_t store_gps(struct device *dev, struct device_attribute *attr, 1017 const char *buf, size_t count) 1018{ 1019 struct asus_laptop *asus = dev_get_drvdata(dev); 1020 int rv, value; 1021 int ret; 1022 1023 rv = parse_arg(buf, count, &value); 1024 if (rv <= 0) 1025 return -EINVAL; 1026 ret = asus_gps_switch(asus, !!value); 1027 if (ret) 1028 return ret; 1029 return rv; 1030} 1031 1032/* 1033 * Input device (i.e. hotkeys) 1034 */ 1035static struct key_entry *asus_get_entry_by_scancode(struct asus_laptop *asus, 1036 int code) 1037{ 1038 struct key_entry *key; 1039 1040 for (key = asus->keymap; key->type != KE_END; key++) 1041 if (code == key->code) 1042 return key; 1043 1044 return NULL; 1045} 1046 1047static struct key_entry *asus_get_entry_by_keycode(struct asus_laptop *asus, 1048 int code) 1049{ 1050 struct key_entry *key; 1051 1052 for (key = asus->keymap; key->type != KE_END; key++) 1053 if (code == key->keycode && key->type == KE_KEY) 1054 return key; 1055 1056 return NULL; 1057} 1058 1059static int asus_getkeycode(struct input_dev *dev, int scancode, int *keycode) 1060{ 1061 struct asus_laptop *asus = input_get_drvdata(dev); 1062 struct key_entry *key = asus_get_entry_by_scancode(asus, scancode); 1063 1064 if (key && key->type == KE_KEY) { 1065 *keycode = key->keycode; 1066 return 0; 1067 } 1068 1069 return -EINVAL; 1070} 1071 1072static int asus_setkeycode(struct input_dev *dev, int scancode, int keycode) 1073{ 1074 struct asus_laptop *asus = input_get_drvdata(dev); 1075 struct key_entry *key; 1076 int old_keycode; 1077 1078 if (keycode < 0 || keycode > KEY_MAX) 1079 return -EINVAL; 1080 1081 key = asus_get_entry_by_scancode(asus, scancode); 1082 if (key && key->type == KE_KEY) { 1083 old_keycode = key->keycode; 1084 key->keycode = keycode; 1085 set_bit(keycode, dev->keybit); 1086 if (!asus_get_entry_by_keycode(asus, old_keycode)) 1087 clear_bit(old_keycode, dev->keybit); 1088 return 0; 1089 } 1090 1091 return -EINVAL; 1092} 1093 1094static void asus_input_notify(struct asus_laptop *asus, int event) 1095{ 1096 struct key_entry *key; 1097 1098 key = asus_get_entry_by_scancode(asus, event); 1099 if (!key) 1100 return ; 1101 1102 switch (key->type) { 1103 case KE_KEY: 1104 input_report_key(asus->inputdev, key->keycode, 1); 1105 input_sync(asus->inputdev); 1106 input_report_key(asus->inputdev, key->keycode, 0); 1107 input_sync(asus->inputdev); 1108 break; 1109 } 1110} 1111 1112static int asus_input_init(struct asus_laptop *asus) 1113{ 1114 const struct key_entry *key; 1115 int result; 1116 1117 asus->inputdev = input_allocate_device(); 1118 if (!asus->inputdev) { 1119 pr_info("Unable to allocate input device\n"); 1120 return 0; 1121 } 1122 asus->inputdev->name = "Asus Laptop extra buttons"; 1123 asus->inputdev->dev.parent = &asus->platform_device->dev; 1124 asus->inputdev->phys = ASUS_LAPTOP_FILE "/input0"; 1125 asus->inputdev->id.bustype = BUS_HOST; 1126 asus->inputdev->getkeycode = asus_getkeycode; 1127 asus->inputdev->setkeycode = asus_setkeycode; 1128 input_set_drvdata(asus->inputdev, asus); 1129 1130 asus->keymap = kmemdup(asus_keymap, sizeof(asus_keymap), 1131 GFP_KERNEL); 1132 for (key = asus->keymap; key->type != KE_END; key++) { 1133 switch (key->type) { 1134 case KE_KEY: 1135 set_bit(EV_KEY, asus->inputdev->evbit); 1136 set_bit(key->keycode, asus->inputdev->keybit); 1137 break; 1138 } 1139 } 1140 result = input_register_device(asus->inputdev); 1141 if (result) { 1142 pr_info("Unable to register input device\n"); 1143 input_free_device(asus->inputdev); 1144 } 1145 return result; 1146} 1147 1148static void asus_input_exit(struct asus_laptop *asus) 1149{ 1150 if (asus->inputdev) 1151 input_unregister_device(asus->inputdev); 1152} 1153 1154/* 1155 * ACPI driver 1156 */ 1157static void asus_acpi_notify(struct acpi_device *device, u32 event) 1158{ 1159 struct asus_laptop *asus = acpi_driver_data(device); 1160 u16 count; 1161 1162 /* 1163 * We need to tell the backlight device when the backlight power is 1164 * switched 1165 */ 1166 if (event == ATKD_LCD_ON) 1167 lcd_blank(asus, FB_BLANK_UNBLANK); 1168 else if (event == ATKD_LCD_OFF) 1169 lcd_blank(asus, FB_BLANK_POWERDOWN); 1170 1171 /* TODO Find a better way to handle events count. */ 1172 count = asus->event_count[event % 128]++; 1173 acpi_bus_generate_proc_event(asus->device, event, count); 1174 acpi_bus_generate_netlink_event(asus->device->pnp.device_class, 1175 dev_name(&asus->device->dev), event, 1176 count); 1177 1178 asus_input_notify(asus, event); 1179} 1180 1181#define ASUS_CREATE_DEVICE_ATTR(_name) \ 1182 struct device_attribute dev_attr_##_name = { \ 1183 .attr = { \ 1184 .name = __stringify(_name), \ 1185 .mode = 0 }, \ 1186 .show = NULL, \ 1187 .store = NULL, \ 1188 } 1189 1190#define ASUS_SET_DEVICE_ATTR(_name, _mode, _show, _store) \ 1191 do { \ 1192 dev_attr_##_name.attr.mode = _mode; \ 1193 dev_attr_##_name.show = _show; \ 1194 dev_attr_##_name.store = _store; \ 1195 } while(0) 1196 1197static ASUS_CREATE_DEVICE_ATTR(infos); 1198static ASUS_CREATE_DEVICE_ATTR(wlan); 1199static ASUS_CREATE_DEVICE_ATTR(bluetooth); 1200static ASUS_CREATE_DEVICE_ATTR(display); 1201static ASUS_CREATE_DEVICE_ATTR(ledd); 1202static ASUS_CREATE_DEVICE_ATTR(ls_switch); 1203static ASUS_CREATE_DEVICE_ATTR(ls_level); 1204static ASUS_CREATE_DEVICE_ATTR(gps); 1205 1206static struct attribute *asuspf_attributes[] = { 1207 &dev_attr_infos.attr, 1208 &dev_attr_wlan.attr, 1209 &dev_attr_bluetooth.attr, 1210 &dev_attr_display.attr, 1211 &dev_attr_ledd.attr, 1212 &dev_attr_ls_switch.attr, 1213 &dev_attr_ls_level.attr, 1214 &dev_attr_gps.attr, 1215 NULL 1216}; 1217 1218static struct attribute_group platform_attribute_group = { 1219 .attrs = asuspf_attributes 1220}; 1221 1222static int asus_platform_init(struct asus_laptop *asus) 1223{ 1224 int result; 1225 1226 asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1); 1227 if (!asus->platform_device) 1228 return -ENOMEM; 1229 platform_set_drvdata(asus->platform_device, asus); 1230 1231 result = platform_device_add(asus->platform_device); 1232 if (result) 1233 goto fail_platform_device; 1234 1235 result = sysfs_create_group(&asus->platform_device->dev.kobj, 1236 &platform_attribute_group); 1237 if (result) 1238 goto fail_sysfs; 1239 return 0; 1240 1241fail_sysfs: 1242 platform_device_del(asus->platform_device); 1243fail_platform_device: 1244 platform_device_put(asus->platform_device); 1245 return result; 1246} 1247 1248static void asus_platform_exit(struct asus_laptop *asus) 1249{ 1250 sysfs_remove_group(&asus->platform_device->dev.kobj, 1251 &platform_attribute_group); 1252 platform_device_unregister(asus->platform_device); 1253} 1254 1255static struct platform_driver platform_driver = { 1256 .driver = { 1257 .name = ASUS_LAPTOP_FILE, 1258 .owner = THIS_MODULE, 1259 } 1260}; 1261 1262static void asus_laptop_add_fs(struct asus_laptop *asus) 1263{ 1264 ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL); 1265 1266 if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) 1267 ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan); 1268 1269 if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) 1270 ASUS_SET_DEVICE_ATTR(bluetooth, 0644, 1271 show_bluetooth, store_bluetooth); 1272 1273 if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) { 1274 if (display_get_handle) 1275 ASUS_SET_DEVICE_ATTR(display, 0644, show_disp, 1276 store_disp); 1277 else 1278 ASUS_SET_DEVICE_ATTR(display, 0200, NULL, store_disp); 1279 } 1280 1281 if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) 1282 ASUS_SET_DEVICE_ATTR(ledd, 0644, show_ledd, store_ledd); 1283 1284 if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && 1285 !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { 1286 ASUS_SET_DEVICE_ATTR(ls_level, 0644, show_lslvl, store_lslvl); 1287 ASUS_SET_DEVICE_ATTR(ls_switch, 0644, show_lssw, store_lssw); 1288 } 1289 1290 if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) && 1291 !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) && 1292 !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) 1293 ASUS_SET_DEVICE_ATTR(gps, 0644, show_gps, store_gps); 1294} 1295 1296static int asus_handle_init(char *name, acpi_handle * handle, 1297 char **paths, int num_paths) 1298{ 1299 int i; 1300 acpi_status status; 1301 1302 for (i = 0; i < num_paths; i++) { 1303 status = acpi_get_handle(NULL, paths[i], handle); 1304 if (ACPI_SUCCESS(status)) 1305 return 0; 1306 } 1307 1308 *handle = NULL; 1309 return -ENODEV; 1310} 1311 1312#define ASUS_HANDLE_INIT(object) \ 1313 asus_handle_init(#object, &object##_handle, object##_paths, \ 1314 ARRAY_SIZE(object##_paths)) 1315 1316/* 1317 * This function is used to initialize the context with right values. In this 1318 * method, we can make all the detection we want, and modify the asus_laptop 1319 * struct 1320 */ 1321static int asus_laptop_get_info(struct asus_laptop *asus) 1322{ 1323 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 1324 union acpi_object *model = NULL; 1325 unsigned long long bsts_result, hwrs_result; 1326 char *string = NULL; 1327 acpi_status status; 1328 1329 /* 1330 * Get DSDT headers early enough to allow for differentiating between 1331 * models, but late enough to allow acpi_bus_register_driver() to fail 1332 * before doing anything ACPI-specific. Should we encounter a machine, 1333 * which needs special handling (i.e. its hotkey device has a different 1334 * HID), this bit will be moved. 1335 */ 1336 status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus->dsdt_info); 1337 if (ACPI_FAILURE(status)) 1338 pr_warning("Couldn't get the DSDT table header\n"); 1339 1340 /* We have to write 0 on init this far for all ASUS models */ 1341 if (write_acpi_int_ret(asus->handle, "INIT", 0, &buffer)) { 1342 pr_err("Hotkey initialization failed\n"); 1343 return -ENODEV; 1344 } 1345 1346 /* This needs to be called for some laptops to init properly */ 1347 status = 1348 acpi_evaluate_integer(asus->handle, "BSTS", NULL, &bsts_result); 1349 if (ACPI_FAILURE(status)) 1350 pr_warning("Error calling BSTS\n"); 1351 else if (bsts_result) 1352 pr_notice("BSTS called, 0x%02x returned\n", 1353 (uint) bsts_result); 1354 1355 /* This too ... */ 1356 write_acpi_int(asus->handle, "CWAP", wapf); 1357 1358 /* 1359 * Try to match the object returned by INIT to the specific model. 1360 * Handle every possible object (or the lack of thereof) the DSDT 1361 * writers might throw at us. When in trouble, we pass NULL to 1362 * asus_model_match() and try something completely different. 1363 */ 1364 if (buffer.pointer) { 1365 model = buffer.pointer; 1366 switch (model->type) { 1367 case ACPI_TYPE_STRING: 1368 string = model->string.pointer; 1369 break; 1370 case ACPI_TYPE_BUFFER: 1371 string = model->buffer.pointer; 1372 break; 1373 default: 1374 string = ""; 1375 break; 1376 } 1377 } 1378 asus->name = kstrdup(string, GFP_KERNEL); 1379 if (!asus->name) 1380 return -ENOMEM; 1381 1382 if (*string) 1383 pr_notice(" %s model detected\n", string); 1384 1385 /* 1386 * The HWRS method return informations about the hardware. 1387 * 0x80 bit is for WLAN, 0x100 for Bluetooth. 1388 * The significance of others is yet to be found. 1389 */ 1390 status = 1391 acpi_evaluate_integer(asus->handle, "HRWS", NULL, &hwrs_result); 1392 if (!ACPI_FAILURE(status)) 1393 pr_notice(" HRWS returned %x", (int)hwrs_result); 1394 1395 if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL)) 1396 asus->have_rsts = true; 1397 1398 /* Scheduled for removal */ 1399 ASUS_HANDLE_INIT(lcd_switch); 1400 ASUS_HANDLE_INIT(display_get); 1401 1402 kfree(model); 1403 1404 return AE_OK; 1405} 1406 1407static bool asus_device_present; 1408 1409static int __devinit asus_acpi_init(struct asus_laptop *asus) 1410{ 1411 int result = 0; 1412 1413 result = acpi_bus_get_status(asus->device); 1414 if (result) 1415 return result; 1416 if (!asus->device->status.present) { 1417 pr_err("Hotkey device not present, aborting\n"); 1418 return -ENODEV; 1419 } 1420 1421 result = asus_laptop_get_info(asus); 1422 if (result) 1423 return result; 1424 1425 asus_laptop_add_fs(asus); 1426 1427 /* WLED and BLED are on by default */ 1428 if (bluetooth_status >= 0) 1429 write_acpi_int(asus->handle, METHOD_BLUETOOTH, 1430 !!bluetooth_status); 1431 if (wireless_status >= 0) 1432 write_acpi_int(asus->handle, METHOD_WLAN, 1433 !!wireless_status); 1434 1435 /* Keyboard Backlight is on by default */ 1436 if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL)) 1437 asus_kled_set(asus, 1); 1438 1439 /* LED display is off by default */ 1440 asus->ledd_status = 0xFFF; 1441 1442 /* Set initial values of light sensor and level */ 1443 asus->light_switch = 0; /* Default to light sensor disabled */ 1444 asus->light_level = 5; /* level 5 for sensor sensitivity */ 1445 1446 if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && 1447 !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { 1448 asus_als_switch(asus, asus->light_switch); 1449 asus_als_level(asus, asus->light_level); 1450 } 1451 1452 /* GPS is on by default */ 1453 if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) && 1454 !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) && 1455 !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) 1456 asus_gps_switch(asus, 1); 1457 return result; 1458} 1459 1460static int __devinit asus_acpi_add(struct acpi_device *device) 1461{ 1462 struct asus_laptop *asus; 1463 int result; 1464 1465 pr_notice("Asus Laptop Support version %s\n", 1466 ASUS_LAPTOP_VERSION); 1467 asus = kzalloc(sizeof(struct asus_laptop), GFP_KERNEL); 1468 if (!asus) 1469 return -ENOMEM; 1470 asus->handle = device->handle; 1471 strcpy(acpi_device_name(device), ASUS_LAPTOP_DEVICE_NAME); 1472 strcpy(acpi_device_class(device), ASUS_LAPTOP_CLASS); 1473 device->driver_data = asus; 1474 asus->device = device; 1475 1476 result = asus_acpi_init(asus); 1477 if (result) 1478 goto fail_platform; 1479 1480 /* 1481 * Register the platform device first. It is used as a parent for the 1482 * sub-devices below. 1483 */ 1484 result = asus_platform_init(asus); 1485 if (result) 1486 goto fail_platform; 1487 1488 if (!acpi_video_backlight_support()) { 1489 result = asus_backlight_init(asus); 1490 if (result) 1491 goto fail_backlight; 1492 } else 1493 pr_info("Backlight controlled by ACPI video driver\n"); 1494 1495 result = asus_input_init(asus); 1496 if (result) 1497 goto fail_input; 1498 1499 result = asus_led_init(asus); 1500 if (result) 1501 goto fail_led; 1502 1503 asus_device_present = true; 1504 return 0; 1505 1506fail_led: 1507 asus_input_exit(asus); 1508fail_input: 1509 asus_backlight_exit(asus); 1510fail_backlight: 1511 asus_platform_exit(asus); 1512fail_platform: 1513 kfree(asus->name); 1514 kfree(asus); 1515 1516 return result; 1517} 1518 1519static int asus_acpi_remove(struct acpi_device *device, int type) 1520{ 1521 struct asus_laptop *asus = acpi_driver_data(device); 1522 1523 asus_backlight_exit(asus); 1524 asus_led_exit(asus); 1525 asus_input_exit(asus); 1526 asus_platform_exit(asus); 1527 1528 kfree(asus->name); 1529 kfree(asus); 1530 return 0; 1531} 1532 1533static const struct acpi_device_id asus_device_ids[] = { 1534 {"ATK0100", 0}, 1535 {"ATK0101", 0}, 1536 {"", 0}, 1537}; 1538MODULE_DEVICE_TABLE(acpi, asus_device_ids); 1539 1540static struct acpi_driver asus_acpi_driver = { 1541 .name = ASUS_LAPTOP_NAME, 1542 .class = ASUS_LAPTOP_CLASS, 1543 .owner = THIS_MODULE, 1544 .ids = asus_device_ids, 1545 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, 1546 .ops = { 1547 .add = asus_acpi_add, 1548 .remove = asus_acpi_remove, 1549 .notify = asus_acpi_notify, 1550 }, 1551}; 1552 1553static int __init asus_laptop_init(void) 1554{ 1555 int result; 1556 1557 result = platform_driver_register(&platform_driver); 1558 if (result < 0) 1559 return result; 1560 1561 result = acpi_bus_register_driver(&asus_acpi_driver); 1562 if (result < 0) 1563 goto fail_acpi_driver; 1564 if (!asus_device_present) { 1565 result = -ENODEV; 1566 goto fail_no_device; 1567 } 1568 return 0; 1569 1570fail_no_device: 1571 acpi_bus_unregister_driver(&asus_acpi_driver); 1572fail_acpi_driver: 1573 platform_driver_unregister(&platform_driver); 1574 return result; 1575} 1576 1577static void __exit asus_laptop_exit(void) 1578{ 1579 acpi_bus_unregister_driver(&asus_acpi_driver); 1580 platform_driver_unregister(&platform_driver); 1581} 1582 1583module_init(asus_laptop_init); 1584module_exit(asus_laptop_exit); 1585