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 v2.6.27-rc9 666 lines 16 kB view raw
1/* 2 * eepc-laptop.c - Asus Eee PC extras 3 * 4 * Based on asus_acpi.c as patched for the Eee PC by Asus: 5 * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar 6 * Based on eee.c from eeepc-linux 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 19#include <linux/kernel.h> 20#include <linux/module.h> 21#include <linux/init.h> 22#include <linux/types.h> 23#include <linux/platform_device.h> 24#include <linux/backlight.h> 25#include <linux/fb.h> 26#include <linux/hwmon.h> 27#include <linux/hwmon-sysfs.h> 28#include <acpi/acpi_drivers.h> 29#include <acpi/acpi_bus.h> 30#include <linux/uaccess.h> 31 32#define EEEPC_LAPTOP_VERSION "0.1" 33 34#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver" 35#define EEEPC_HOTK_FILE "eeepc" 36#define EEEPC_HOTK_CLASS "hotkey" 37#define EEEPC_HOTK_DEVICE_NAME "Hotkey" 38#define EEEPC_HOTK_HID "ASUS010" 39 40#define EEEPC_LOG EEEPC_HOTK_FILE ": " 41#define EEEPC_ERR KERN_ERR EEEPC_LOG 42#define EEEPC_WARNING KERN_WARNING EEEPC_LOG 43#define EEEPC_NOTICE KERN_NOTICE EEEPC_LOG 44#define EEEPC_INFO KERN_INFO EEEPC_LOG 45 46/* 47 * Definitions for Asus EeePC 48 */ 49#define NOTIFY_WLAN_ON 0x10 50#define NOTIFY_BRN_MIN 0x20 51#define NOTIFY_BRN_MAX 0x2f 52 53enum { 54 DISABLE_ASL_WLAN = 0x0001, 55 DISABLE_ASL_BLUETOOTH = 0x0002, 56 DISABLE_ASL_IRDA = 0x0004, 57 DISABLE_ASL_CAMERA = 0x0008, 58 DISABLE_ASL_TV = 0x0010, 59 DISABLE_ASL_GPS = 0x0020, 60 DISABLE_ASL_DISPLAYSWITCH = 0x0040, 61 DISABLE_ASL_MODEM = 0x0080, 62 DISABLE_ASL_CARDREADER = 0x0100 63}; 64 65enum { 66 CM_ASL_WLAN = 0, 67 CM_ASL_BLUETOOTH, 68 CM_ASL_IRDA, 69 CM_ASL_1394, 70 CM_ASL_CAMERA, 71 CM_ASL_TV, 72 CM_ASL_GPS, 73 CM_ASL_DVDROM, 74 CM_ASL_DISPLAYSWITCH, 75 CM_ASL_PANELBRIGHT, 76 CM_ASL_BIOSFLASH, 77 CM_ASL_ACPIFLASH, 78 CM_ASL_CPUFV, 79 CM_ASL_CPUTEMPERATURE, 80 CM_ASL_FANCPU, 81 CM_ASL_FANCHASSIS, 82 CM_ASL_USBPORT1, 83 CM_ASL_USBPORT2, 84 CM_ASL_USBPORT3, 85 CM_ASL_MODEM, 86 CM_ASL_CARDREADER, 87 CM_ASL_LID 88}; 89 90static const char *cm_getv[] = { 91 "WLDG", NULL, NULL, NULL, 92 "CAMG", NULL, NULL, NULL, 93 NULL, "PBLG", NULL, NULL, 94 "CFVG", NULL, NULL, NULL, 95 "USBG", NULL, NULL, "MODG", 96 "CRDG", "LIDG" 97}; 98 99static const char *cm_setv[] = { 100 "WLDS", NULL, NULL, NULL, 101 "CAMS", NULL, NULL, NULL, 102 "SDSP", "PBLS", "HDPS", NULL, 103 "CFVS", NULL, NULL, NULL, 104 "USBG", NULL, NULL, "MODS", 105 "CRDS", NULL 106}; 107 108#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0." 109 110#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */ 111#define EEEPC_EC_SC02 0x63 112#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */ 113#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */ 114#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */ 115#define EEEPC_EC_SFB3 0xD3 116 117/* 118 * This is the main structure, we can use it to store useful information 119 * about the hotk device 120 */ 121struct eeepc_hotk { 122 struct acpi_device *device; /* the device we are in */ 123 acpi_handle handle; /* the handle of the hotk device */ 124 u32 cm_supported; /* the control methods supported 125 by this BIOS */ 126 uint init_flag; /* Init flags */ 127 u16 event_count[128]; /* count for each event */ 128}; 129 130/* The actual device the driver binds to */ 131static struct eeepc_hotk *ehotk; 132 133/* Platform device/driver */ 134static struct platform_driver platform_driver = { 135 .driver = { 136 .name = EEEPC_HOTK_FILE, 137 .owner = THIS_MODULE, 138 } 139}; 140 141static struct platform_device *platform_device; 142 143/* 144 * The hotkey driver declaration 145 */ 146static int eeepc_hotk_add(struct acpi_device *device); 147static int eeepc_hotk_remove(struct acpi_device *device, int type); 148 149static const struct acpi_device_id eeepc_device_ids[] = { 150 {EEEPC_HOTK_HID, 0}, 151 {"", 0}, 152}; 153MODULE_DEVICE_TABLE(acpi, eeepc_device_ids); 154 155static struct acpi_driver eeepc_hotk_driver = { 156 .name = EEEPC_HOTK_NAME, 157 .class = EEEPC_HOTK_CLASS, 158 .ids = eeepc_device_ids, 159 .ops = { 160 .add = eeepc_hotk_add, 161 .remove = eeepc_hotk_remove, 162 }, 163}; 164 165/* The backlight device /sys/class/backlight */ 166static struct backlight_device *eeepc_backlight_device; 167 168/* The hwmon device */ 169static struct device *eeepc_hwmon_device; 170 171/* 172 * The backlight class declaration 173 */ 174static int read_brightness(struct backlight_device *bd); 175static int update_bl_status(struct backlight_device *bd); 176static struct backlight_ops eeepcbl_ops = { 177 .get_brightness = read_brightness, 178 .update_status = update_bl_status, 179}; 180 181MODULE_AUTHOR("Corentin Chary, Eric Cooper"); 182MODULE_DESCRIPTION(EEEPC_HOTK_NAME); 183MODULE_LICENSE("GPL"); 184 185/* 186 * ACPI Helpers 187 */ 188static int write_acpi_int(acpi_handle handle, const char *method, int val, 189 struct acpi_buffer *output) 190{ 191 struct acpi_object_list params; 192 union acpi_object in_obj; 193 acpi_status status; 194 195 params.count = 1; 196 params.pointer = &in_obj; 197 in_obj.type = ACPI_TYPE_INTEGER; 198 in_obj.integer.value = val; 199 200 status = acpi_evaluate_object(handle, (char *)method, &params, output); 201 return (status == AE_OK ? 0 : -1); 202} 203 204static int read_acpi_int(acpi_handle handle, const char *method, int *val) 205{ 206 acpi_status status; 207 ulong result; 208 209 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); 210 if (ACPI_FAILURE(status)) { 211 *val = -1; 212 return -1; 213 } else { 214 *val = result; 215 return 0; 216 } 217} 218 219static int set_acpi(int cm, int value) 220{ 221 if (ehotk->cm_supported & (0x1 << cm)) { 222 const char *method = cm_setv[cm]; 223 if (method == NULL) 224 return -ENODEV; 225 if (write_acpi_int(ehotk->handle, method, value, NULL)) 226 printk(EEEPC_WARNING "Error writing %s\n", method); 227 } 228 return 0; 229} 230 231static int get_acpi(int cm) 232{ 233 int value = -1; 234 if ((ehotk->cm_supported & (0x1 << cm))) { 235 const char *method = cm_getv[cm]; 236 if (method == NULL) 237 return -ENODEV; 238 if (read_acpi_int(ehotk->handle, method, &value)) 239 printk(EEEPC_WARNING "Error reading %s\n", method); 240 } 241 return value; 242} 243 244/* 245 * Backlight 246 */ 247static int read_brightness(struct backlight_device *bd) 248{ 249 return get_acpi(CM_ASL_PANELBRIGHT); 250} 251 252static int set_brightness(struct backlight_device *bd, int value) 253{ 254 value = max(0, min(15, value)); 255 return set_acpi(CM_ASL_PANELBRIGHT, value); 256} 257 258static int update_bl_status(struct backlight_device *bd) 259{ 260 return set_brightness(bd, bd->props.brightness); 261} 262 263/* 264 * Sys helpers 265 */ 266static int parse_arg(const char *buf, unsigned long count, int *val) 267{ 268 if (!count) 269 return 0; 270 if (sscanf(buf, "%i", val) != 1) 271 return -EINVAL; 272 return count; 273} 274 275static ssize_t store_sys_acpi(int cm, const char *buf, size_t count) 276{ 277 int rv, value; 278 279 rv = parse_arg(buf, count, &value); 280 if (rv > 0) 281 set_acpi(cm, value); 282 return rv; 283} 284 285static ssize_t show_sys_acpi(int cm, char *buf) 286{ 287 return sprintf(buf, "%d\n", get_acpi(cm)); 288} 289 290#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \ 291 static ssize_t show_##_name(struct device *dev, \ 292 struct device_attribute *attr, \ 293 char *buf) \ 294 { \ 295 return show_sys_acpi(_cm, buf); \ 296 } \ 297 static ssize_t store_##_name(struct device *dev, \ 298 struct device_attribute *attr, \ 299 const char *buf, size_t count) \ 300 { \ 301 return store_sys_acpi(_cm, buf, count); \ 302 } \ 303 static struct device_attribute dev_attr_##_name = { \ 304 .attr = { \ 305 .name = __stringify(_name), \ 306 .mode = 0644 }, \ 307 .show = show_##_name, \ 308 .store = store_##_name, \ 309 } 310 311EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA); 312EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER); 313EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH); 314EEEPC_CREATE_DEVICE_ATTR(wlan, CM_ASL_WLAN); 315 316static struct attribute *platform_attributes[] = { 317 &dev_attr_camera.attr, 318 &dev_attr_cardr.attr, 319 &dev_attr_disp.attr, 320 &dev_attr_wlan.attr, 321 NULL 322}; 323 324static struct attribute_group platform_attribute_group = { 325 .attrs = platform_attributes 326}; 327 328/* 329 * Hotkey functions 330 */ 331static int eeepc_hotk_check(void) 332{ 333 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 334 int result; 335 336 result = acpi_bus_get_status(ehotk->device); 337 if (result) 338 return result; 339 if (ehotk->device->status.present) { 340 if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag, 341 &buffer)) { 342 printk(EEEPC_ERR "Hotkey initialization failed\n"); 343 return -ENODEV; 344 } else { 345 printk(EEEPC_NOTICE "Hotkey init flags 0x%x\n", 346 ehotk->init_flag); 347 } 348 /* get control methods supported */ 349 if (read_acpi_int(ehotk->handle, "CMSG" 350 , &ehotk->cm_supported)) { 351 printk(EEEPC_ERR 352 "Get control methods supported failed\n"); 353 return -ENODEV; 354 } else { 355 printk(EEEPC_INFO 356 "Get control methods supported: 0x%x\n", 357 ehotk->cm_supported); 358 } 359 } else { 360 printk(EEEPC_ERR "Hotkey device not present, aborting\n"); 361 return -EINVAL; 362 } 363 return 0; 364} 365 366static void notify_wlan(u32 *event) 367{ 368 /* if DISABLE_ASL_WLAN is set, the notify code for fn+f2 369 will always be 0x10 */ 370 if (ehotk->cm_supported & (0x1 << CM_ASL_WLAN)) { 371 const char *method = cm_getv[CM_ASL_WLAN]; 372 int value; 373 if (read_acpi_int(ehotk->handle, method, &value)) 374 printk(EEEPC_WARNING "Error reading %s\n", 375 method); 376 else if (value == 1) 377 *event = 0x11; 378 } 379} 380 381static void notify_brn(void) 382{ 383 struct backlight_device *bd = eeepc_backlight_device; 384 bd->props.brightness = read_brightness(bd); 385} 386 387static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) 388{ 389 if (!ehotk) 390 return; 391 if (event == NOTIFY_WLAN_ON && (DISABLE_ASL_WLAN & ehotk->init_flag)) 392 notify_wlan(&event); 393 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) 394 notify_brn(); 395 acpi_bus_generate_proc_event(ehotk->device, event, 396 ehotk->event_count[event % 128]++); 397} 398 399static int eeepc_hotk_add(struct acpi_device *device) 400{ 401 acpi_status status = AE_OK; 402 int result; 403 404 if (!device) 405 return -EINVAL; 406 printk(EEEPC_NOTICE EEEPC_HOTK_NAME "\n"); 407 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL); 408 if (!ehotk) 409 return -ENOMEM; 410 ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; 411 ehotk->handle = device->handle; 412 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME); 413 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS); 414 acpi_driver_data(device) = ehotk; 415 ehotk->device = device; 416 result = eeepc_hotk_check(); 417 if (result) 418 goto end; 419 status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY, 420 eeepc_hotk_notify, ehotk); 421 if (ACPI_FAILURE(status)) 422 printk(EEEPC_ERR "Error installing notify handler\n"); 423 end: 424 if (result) { 425 kfree(ehotk); 426 ehotk = NULL; 427 } 428 return result; 429} 430 431static int eeepc_hotk_remove(struct acpi_device *device, int type) 432{ 433 acpi_status status = 0; 434 435 if (!device || !acpi_driver_data(device)) 436 return -EINVAL; 437 status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY, 438 eeepc_hotk_notify); 439 if (ACPI_FAILURE(status)) 440 printk(EEEPC_ERR "Error removing notify handler\n"); 441 kfree(ehotk); 442 return 0; 443} 444 445/* 446 * Hwmon 447 */ 448static int eeepc_get_fan_pwm(void) 449{ 450 int value = 0; 451 452 read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value); 453 return (value); 454} 455 456static void eeepc_set_fan_pwm(int value) 457{ 458 value = SENSORS_LIMIT(value, 0, 100); 459 ec_write(EEEPC_EC_SC02, value); 460} 461 462static int eeepc_get_fan_rpm(void) 463{ 464 int high = 0; 465 int low = 0; 466 467 read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high); 468 read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low); 469 return (high << 8 | low); 470} 471 472static int eeepc_get_fan_ctrl(void) 473{ 474 int value = 0; 475 476 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); 477 return ((value & 0x02 ? 1 : 0)); 478} 479 480static void eeepc_set_fan_ctrl(int manual) 481{ 482 int value = 0; 483 484 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); 485 if (manual) 486 value |= 0x02; 487 else 488 value &= ~0x02; 489 ec_write(EEEPC_EC_SFB3, value); 490} 491 492static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count) 493{ 494 int rv, value; 495 496 rv = parse_arg(buf, count, &value); 497 if (rv > 0) 498 set(value); 499 return rv; 500} 501 502static ssize_t show_sys_hwmon(int (*get)(void), char *buf) 503{ 504 return sprintf(buf, "%d\n", get()); 505} 506 507#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \ 508 static ssize_t show_##_name(struct device *dev, \ 509 struct device_attribute *attr, \ 510 char *buf) \ 511 { \ 512 return show_sys_hwmon(_set, buf); \ 513 } \ 514 static ssize_t store_##_name(struct device *dev, \ 515 struct device_attribute *attr, \ 516 const char *buf, size_t count) \ 517 { \ 518 return store_sys_hwmon(_get, buf, count); \ 519 } \ 520 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0); 521 522EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL); 523EEEPC_CREATE_SENSOR_ATTR(fan1_pwm, S_IRUGO | S_IWUSR, 524 eeepc_get_fan_pwm, eeepc_set_fan_pwm); 525EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, 526 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl); 527 528static struct attribute *hwmon_attributes[] = { 529 &sensor_dev_attr_fan1_pwm.dev_attr.attr, 530 &sensor_dev_attr_fan1_input.dev_attr.attr, 531 &sensor_dev_attr_pwm1_enable.dev_attr.attr, 532 NULL 533}; 534 535static struct attribute_group hwmon_attribute_group = { 536 .attrs = hwmon_attributes 537}; 538 539/* 540 * exit/init 541 */ 542static void eeepc_backlight_exit(void) 543{ 544 if (eeepc_backlight_device) 545 backlight_device_unregister(eeepc_backlight_device); 546 eeepc_backlight_device = NULL; 547} 548 549static void eeepc_hwmon_exit(void) 550{ 551 struct device *hwmon; 552 553 hwmon = eeepc_hwmon_device; 554 if (!hwmon) 555 return ; 556 sysfs_remove_group(&hwmon->kobj, 557 &hwmon_attribute_group); 558 hwmon_device_unregister(hwmon); 559 eeepc_hwmon_device = NULL; 560} 561 562static void __exit eeepc_laptop_exit(void) 563{ 564 eeepc_backlight_exit(); 565 eeepc_hwmon_exit(); 566 acpi_bus_unregister_driver(&eeepc_hotk_driver); 567 sysfs_remove_group(&platform_device->dev.kobj, 568 &platform_attribute_group); 569 platform_device_unregister(platform_device); 570 platform_driver_unregister(&platform_driver); 571} 572 573static int eeepc_backlight_init(struct device *dev) 574{ 575 struct backlight_device *bd; 576 577 bd = backlight_device_register(EEEPC_HOTK_FILE, dev, 578 NULL, &eeepcbl_ops); 579 if (IS_ERR(bd)) { 580 printk(EEEPC_ERR 581 "Could not register eeepc backlight device\n"); 582 eeepc_backlight_device = NULL; 583 return PTR_ERR(bd); 584 } 585 eeepc_backlight_device = bd; 586 bd->props.max_brightness = 15; 587 bd->props.brightness = read_brightness(NULL); 588 bd->props.power = FB_BLANK_UNBLANK; 589 backlight_update_status(bd); 590 return 0; 591} 592 593static int eeepc_hwmon_init(struct device *dev) 594{ 595 struct device *hwmon; 596 int result; 597 598 hwmon = hwmon_device_register(dev); 599 if (IS_ERR(hwmon)) { 600 printk(EEEPC_ERR 601 "Could not register eeepc hwmon device\n"); 602 eeepc_hwmon_device = NULL; 603 return PTR_ERR(hwmon); 604 } 605 eeepc_hwmon_device = hwmon; 606 result = sysfs_create_group(&hwmon->kobj, 607 &hwmon_attribute_group); 608 if (result) 609 eeepc_hwmon_exit(); 610 return result; 611} 612 613static int __init eeepc_laptop_init(void) 614{ 615 struct device *dev; 616 int result; 617 618 if (acpi_disabled) 619 return -ENODEV; 620 result = acpi_bus_register_driver(&eeepc_hotk_driver); 621 if (result < 0) 622 return result; 623 if (!ehotk) { 624 acpi_bus_unregister_driver(&eeepc_hotk_driver); 625 return -ENODEV; 626 } 627 dev = acpi_get_physical_device(ehotk->device->handle); 628 result = eeepc_backlight_init(dev); 629 if (result) 630 goto fail_backlight; 631 result = eeepc_hwmon_init(dev); 632 if (result) 633 goto fail_hwmon; 634 /* Register platform stuff */ 635 result = platform_driver_register(&platform_driver); 636 if (result) 637 goto fail_platform_driver; 638 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1); 639 if (!platform_device) { 640 result = -ENOMEM; 641 goto fail_platform_device1; 642 } 643 result = platform_device_add(platform_device); 644 if (result) 645 goto fail_platform_device2; 646 result = sysfs_create_group(&platform_device->dev.kobj, 647 &platform_attribute_group); 648 if (result) 649 goto fail_sysfs; 650 return 0; 651fail_sysfs: 652 platform_device_del(platform_device); 653fail_platform_device2: 654 platform_device_put(platform_device); 655fail_platform_device1: 656 platform_driver_unregister(&platform_driver); 657fail_platform_driver: 658 eeepc_hwmon_exit(); 659fail_hwmon: 660 eeepc_backlight_exit(); 661fail_backlight: 662 return result; 663} 664 665module_init(eeepc_laptop_init); 666module_exit(eeepc_laptop_exit);