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.39-rc7 999 lines 23 kB view raw
1/*-*-linux-c-*-*/ 2 3/* 4 Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de> 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301, USA. 20 */ 21 22/* 23 * msi-laptop.c - MSI S270 laptop support. This laptop is sold under 24 * various brands, including "Cytron/TCM/Medion/Tchibo MD96100". 25 * 26 * Driver also supports S271, S420 models. 27 * 28 * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/: 29 * 30 * lcd_level - Screen brightness: contains a single integer in the 31 * range 0..8. (rw) 32 * 33 * auto_brightness - Enable automatic brightness control: contains 34 * either 0 or 1. If set to 1 the hardware adjusts the screen 35 * brightness automatically when the power cord is 36 * plugged/unplugged. (rw) 37 * 38 * wlan - WLAN subsystem enabled: contains either 0 or 1. (ro) 39 * 40 * bluetooth - Bluetooth subsystem enabled: contains either 0 or 1 41 * Please note that this file is constantly 0 if no Bluetooth 42 * hardware is available. (ro) 43 * 44 * In addition to these platform device attributes the driver 45 * registers itself in the Linux backlight control subsystem and is 46 * available to userspace under /sys/class/backlight/msi-laptop-bl/. 47 * 48 * This driver might work on other laptops produced by MSI. If you 49 * want to try it you can pass force=1 as argument to the module which 50 * will force it to load even when the DMI data doesn't identify the 51 * laptop as MSI S270. YMMV. 52 */ 53 54#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 55 56#include <linux/module.h> 57#include <linux/kernel.h> 58#include <linux/init.h> 59#include <linux/acpi.h> 60#include <linux/dmi.h> 61#include <linux/backlight.h> 62#include <linux/platform_device.h> 63#include <linux/rfkill.h> 64#include <linux/i8042.h> 65#include <linux/input.h> 66#include <linux/input/sparse-keymap.h> 67 68#define MSI_DRIVER_VERSION "0.5" 69 70#define MSI_LCD_LEVEL_MAX 9 71 72#define MSI_EC_COMMAND_WIRELESS 0x10 73#define MSI_EC_COMMAND_LCD_LEVEL 0x11 74 75#define MSI_STANDARD_EC_COMMAND_ADDRESS 0x2e 76#define MSI_STANDARD_EC_BLUETOOTH_MASK (1 << 0) 77#define MSI_STANDARD_EC_WEBCAM_MASK (1 << 1) 78#define MSI_STANDARD_EC_WLAN_MASK (1 << 3) 79#define MSI_STANDARD_EC_3G_MASK (1 << 4) 80 81/* For set SCM load flag to disable BIOS fn key */ 82#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d 83#define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) 84 85#define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4 86#define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4) 87 88static int msi_laptop_resume(struct platform_device *device); 89 90#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f 91 92static int force; 93module_param(force, bool, 0); 94MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); 95 96static int auto_brightness; 97module_param(auto_brightness, int, 0); 98MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); 99 100static const struct key_entry msi_laptop_keymap[] = { 101 {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} }, /* Touch Pad On */ 102 {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */ 103 {KE_END, 0} 104}; 105 106static struct input_dev *msi_laptop_input_dev; 107 108static bool old_ec_model; 109static int wlan_s, bluetooth_s, threeg_s; 110static int threeg_exists; 111 112/* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G, 113 * those netbook will load the SCM (windows app) to disable the original 114 * Wlan/Bluetooth control by BIOS when user press fn key, then control 115 * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user 116 * cann't on/off 3G module on those 3G netbook. 117 * On Linux, msi-laptop driver will do the same thing to disable the 118 * original BIOS control, then might need use HAL or other userland 119 * application to do the software control that simulate with SCM. 120 * e.g. MSI N034 netbook 121 */ 122static bool load_scm_model; 123static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg; 124 125/* Hardware access */ 126 127static int set_lcd_level(int level) 128{ 129 u8 buf[2]; 130 131 if (level < 0 || level >= MSI_LCD_LEVEL_MAX) 132 return -EINVAL; 133 134 buf[0] = 0x80; 135 buf[1] = (u8) (level*31); 136 137 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), 138 NULL, 0, 1); 139} 140 141static int get_lcd_level(void) 142{ 143 u8 wdata = 0, rdata; 144 int result; 145 146 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, 147 &rdata, 1, 1); 148 if (result < 0) 149 return result; 150 151 return (int) rdata / 31; 152} 153 154static int get_auto_brightness(void) 155{ 156 u8 wdata = 4, rdata; 157 int result; 158 159 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, 160 &rdata, 1, 1); 161 if (result < 0) 162 return result; 163 164 return !!(rdata & 8); 165} 166 167static int set_auto_brightness(int enable) 168{ 169 u8 wdata[2], rdata; 170 int result; 171 172 wdata[0] = 4; 173 174 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, 175 &rdata, 1, 1); 176 if (result < 0) 177 return result; 178 179 wdata[0] = 0x84; 180 wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); 181 182 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, 183 NULL, 0, 1); 184} 185 186static ssize_t set_device_state(const char *buf, size_t count, u8 mask) 187{ 188 int status; 189 u8 wdata = 0, rdata; 190 int result; 191 192 if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1)) 193 return -EINVAL; 194 195 /* read current device state */ 196 result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata); 197 if (result < 0) 198 return -EINVAL; 199 200 if (!!(rdata & mask) != status) { 201 /* reverse device bit */ 202 if (rdata & mask) 203 wdata = rdata & ~mask; 204 else 205 wdata = rdata | mask; 206 207 result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata); 208 if (result < 0) 209 return -EINVAL; 210 } 211 212 return count; 213} 214 215static int get_wireless_state(int *wlan, int *bluetooth) 216{ 217 u8 wdata = 0, rdata; 218 int result; 219 220 result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1); 221 if (result < 0) 222 return -1; 223 224 if (wlan) 225 *wlan = !!(rdata & 8); 226 227 if (bluetooth) 228 *bluetooth = !!(rdata & 128); 229 230 return 0; 231} 232 233static int get_wireless_state_ec_standard(void) 234{ 235 u8 rdata; 236 int result; 237 238 result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata); 239 if (result < 0) 240 return -1; 241 242 wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK); 243 244 bluetooth_s = !!(rdata & MSI_STANDARD_EC_BLUETOOTH_MASK); 245 246 threeg_s = !!(rdata & MSI_STANDARD_EC_3G_MASK); 247 248 return 0; 249} 250 251static int get_threeg_exists(void) 252{ 253 u8 rdata; 254 int result; 255 256 result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata); 257 if (result < 0) 258 return -1; 259 260 threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK); 261 262 return 0; 263} 264 265/* Backlight device stuff */ 266 267static int bl_get_brightness(struct backlight_device *b) 268{ 269 return get_lcd_level(); 270} 271 272 273static int bl_update_status(struct backlight_device *b) 274{ 275 return set_lcd_level(b->props.brightness); 276} 277 278static const struct backlight_ops msibl_ops = { 279 .get_brightness = bl_get_brightness, 280 .update_status = bl_update_status, 281}; 282 283static struct backlight_device *msibl_device; 284 285/* Platform device */ 286 287static ssize_t show_wlan(struct device *dev, 288 struct device_attribute *attr, char *buf) 289{ 290 291 int ret, enabled; 292 293 if (old_ec_model) { 294 ret = get_wireless_state(&enabled, NULL); 295 } else { 296 ret = get_wireless_state_ec_standard(); 297 enabled = wlan_s; 298 } 299 if (ret < 0) 300 return ret; 301 302 return sprintf(buf, "%i\n", enabled); 303} 304 305static ssize_t store_wlan(struct device *dev, 306 struct device_attribute *attr, const char *buf, size_t count) 307{ 308 return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK); 309} 310 311static ssize_t show_bluetooth(struct device *dev, 312 struct device_attribute *attr, char *buf) 313{ 314 315 int ret, enabled; 316 317 if (old_ec_model) { 318 ret = get_wireless_state(NULL, &enabled); 319 } else { 320 ret = get_wireless_state_ec_standard(); 321 enabled = bluetooth_s; 322 } 323 if (ret < 0) 324 return ret; 325 326 return sprintf(buf, "%i\n", enabled); 327} 328 329static ssize_t store_bluetooth(struct device *dev, 330 struct device_attribute *attr, const char *buf, size_t count) 331{ 332 return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK); 333} 334 335static ssize_t show_threeg(struct device *dev, 336 struct device_attribute *attr, char *buf) 337{ 338 339 int ret; 340 341 /* old msi ec not support 3G */ 342 if (old_ec_model) 343 return -1; 344 345 ret = get_wireless_state_ec_standard(); 346 if (ret < 0) 347 return ret; 348 349 return sprintf(buf, "%i\n", threeg_s); 350} 351 352static ssize_t store_threeg(struct device *dev, 353 struct device_attribute *attr, const char *buf, size_t count) 354{ 355 return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK); 356} 357 358static ssize_t show_lcd_level(struct device *dev, 359 struct device_attribute *attr, char *buf) 360{ 361 362 int ret; 363 364 ret = get_lcd_level(); 365 if (ret < 0) 366 return ret; 367 368 return sprintf(buf, "%i\n", ret); 369} 370 371static ssize_t store_lcd_level(struct device *dev, 372 struct device_attribute *attr, const char *buf, size_t count) 373{ 374 375 int level, ret; 376 377 if (sscanf(buf, "%i", &level) != 1 || 378 (level < 0 || level >= MSI_LCD_LEVEL_MAX)) 379 return -EINVAL; 380 381 ret = set_lcd_level(level); 382 if (ret < 0) 383 return ret; 384 385 return count; 386} 387 388static ssize_t show_auto_brightness(struct device *dev, 389 struct device_attribute *attr, char *buf) 390{ 391 392 int ret; 393 394 ret = get_auto_brightness(); 395 if (ret < 0) 396 return ret; 397 398 return sprintf(buf, "%i\n", ret); 399} 400 401static ssize_t store_auto_brightness(struct device *dev, 402 struct device_attribute *attr, const char *buf, size_t count) 403{ 404 405 int enable, ret; 406 407 if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1))) 408 return -EINVAL; 409 410 ret = set_auto_brightness(enable); 411 if (ret < 0) 412 return ret; 413 414 return count; 415} 416 417static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); 418static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, 419 store_auto_brightness); 420static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); 421static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); 422static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); 423 424static struct attribute *msipf_attributes[] = { 425 &dev_attr_lcd_level.attr, 426 &dev_attr_auto_brightness.attr, 427 &dev_attr_bluetooth.attr, 428 &dev_attr_wlan.attr, 429 NULL 430}; 431 432static struct attribute_group msipf_attribute_group = { 433 .attrs = msipf_attributes 434}; 435 436static struct platform_driver msipf_driver = { 437 .driver = { 438 .name = "msi-laptop-pf", 439 .owner = THIS_MODULE, 440 }, 441 .resume = msi_laptop_resume, 442}; 443 444static struct platform_device *msipf_device; 445 446/* Initialization */ 447 448static int dmi_check_cb(const struct dmi_system_id *id) 449{ 450 pr_info("Identified laptop model '%s'.\n", id->ident); 451 return 1; 452} 453 454static struct dmi_system_id __initdata msi_dmi_table[] = { 455 { 456 .ident = "MSI S270", 457 .matches = { 458 DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"), 459 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"), 460 DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), 461 DMI_MATCH(DMI_CHASSIS_VENDOR, 462 "MICRO-STAR INT'L CO.,LTD") 463 }, 464 .callback = dmi_check_cb 465 }, 466 { 467 .ident = "MSI S271", 468 .matches = { 469 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), 470 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"), 471 DMI_MATCH(DMI_PRODUCT_VERSION, "0581"), 472 DMI_MATCH(DMI_BOARD_NAME, "MS-1058") 473 }, 474 .callback = dmi_check_cb 475 }, 476 { 477 .ident = "MSI S420", 478 .matches = { 479 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), 480 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"), 481 DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), 482 DMI_MATCH(DMI_BOARD_NAME, "MS-1412") 483 }, 484 .callback = dmi_check_cb 485 }, 486 { 487 .ident = "Medion MD96100", 488 .matches = { 489 DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"), 490 DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"), 491 DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), 492 DMI_MATCH(DMI_CHASSIS_VENDOR, 493 "MICRO-STAR INT'L CO.,LTD") 494 }, 495 .callback = dmi_check_cb 496 }, 497 { } 498}; 499 500static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = { 501 { 502 .ident = "MSI N034", 503 .matches = { 504 DMI_MATCH(DMI_SYS_VENDOR, 505 "MICRO-STAR INTERNATIONAL CO., LTD"), 506 DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"), 507 DMI_MATCH(DMI_CHASSIS_VENDOR, 508 "MICRO-STAR INTERNATIONAL CO., LTD") 509 }, 510 .callback = dmi_check_cb 511 }, 512 { 513 .ident = "MSI N051", 514 .matches = { 515 DMI_MATCH(DMI_SYS_VENDOR, 516 "MICRO-STAR INTERNATIONAL CO., LTD"), 517 DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"), 518 DMI_MATCH(DMI_CHASSIS_VENDOR, 519 "MICRO-STAR INTERNATIONAL CO., LTD") 520 }, 521 .callback = dmi_check_cb 522 }, 523 { 524 .ident = "MSI N014", 525 .matches = { 526 DMI_MATCH(DMI_SYS_VENDOR, 527 "MICRO-STAR INTERNATIONAL CO., LTD"), 528 DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"), 529 }, 530 .callback = dmi_check_cb 531 }, 532 { 533 .ident = "MSI CR620", 534 .matches = { 535 DMI_MATCH(DMI_SYS_VENDOR, 536 "Micro-Star International"), 537 DMI_MATCH(DMI_PRODUCT_NAME, "CR620"), 538 }, 539 .callback = dmi_check_cb 540 }, 541 { } 542}; 543 544static int rfkill_bluetooth_set(void *data, bool blocked) 545{ 546 /* Do something with blocked...*/ 547 /* 548 * blocked == false is on 549 * blocked == true is off 550 */ 551 if (blocked) 552 set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); 553 else 554 set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); 555 556 return 0; 557} 558 559static int rfkill_wlan_set(void *data, bool blocked) 560{ 561 if (blocked) 562 set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK); 563 else 564 set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK); 565 566 return 0; 567} 568 569static int rfkill_threeg_set(void *data, bool blocked) 570{ 571 if (blocked) 572 set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK); 573 else 574 set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK); 575 576 return 0; 577} 578 579static const struct rfkill_ops rfkill_bluetooth_ops = { 580 .set_block = rfkill_bluetooth_set 581}; 582 583static const struct rfkill_ops rfkill_wlan_ops = { 584 .set_block = rfkill_wlan_set 585}; 586 587static const struct rfkill_ops rfkill_threeg_ops = { 588 .set_block = rfkill_threeg_set 589}; 590 591static void rfkill_cleanup(void) 592{ 593 if (rfk_bluetooth) { 594 rfkill_unregister(rfk_bluetooth); 595 rfkill_destroy(rfk_bluetooth); 596 } 597 598 if (rfk_threeg) { 599 rfkill_unregister(rfk_threeg); 600 rfkill_destroy(rfk_threeg); 601 } 602 603 if (rfk_wlan) { 604 rfkill_unregister(rfk_wlan); 605 rfkill_destroy(rfk_wlan); 606 } 607} 608 609static void msi_update_rfkill(struct work_struct *ignored) 610{ 611 get_wireless_state_ec_standard(); 612 613 if (rfk_wlan) 614 rfkill_set_sw_state(rfk_wlan, !wlan_s); 615 if (rfk_bluetooth) 616 rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s); 617 if (rfk_threeg) 618 rfkill_set_sw_state(rfk_threeg, !threeg_s); 619} 620static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); 621 622static void msi_send_touchpad_key(struct work_struct *ignored) 623{ 624 u8 rdata; 625 int result; 626 627 result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata); 628 if (result < 0) 629 return; 630 631 sparse_keymap_report_event(msi_laptop_input_dev, 632 (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ? 633 KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true); 634} 635static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key); 636 637static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, 638 struct serio *port) 639{ 640 static bool extended; 641 642 if (str & 0x20) 643 return false; 644 645 /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/ 646 if (unlikely(data == 0xe0)) { 647 extended = true; 648 return false; 649 } else if (unlikely(extended)) { 650 extended = false; 651 switch (data) { 652 case 0xE4: 653 schedule_delayed_work(&msi_touchpad_work, 654 round_jiffies_relative(0.5 * HZ)); 655 break; 656 case 0x54: 657 case 0x62: 658 case 0x76: 659 schedule_delayed_work(&msi_rfkill_work, 660 round_jiffies_relative(0.5 * HZ)); 661 break; 662 } 663 } 664 665 return false; 666} 667 668static void msi_init_rfkill(struct work_struct *ignored) 669{ 670 if (rfk_wlan) { 671 rfkill_set_sw_state(rfk_wlan, !wlan_s); 672 rfkill_wlan_set(NULL, !wlan_s); 673 } 674 if (rfk_bluetooth) { 675 rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s); 676 rfkill_bluetooth_set(NULL, !bluetooth_s); 677 } 678 if (rfk_threeg) { 679 rfkill_set_sw_state(rfk_threeg, !threeg_s); 680 rfkill_threeg_set(NULL, !threeg_s); 681 } 682} 683static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill); 684 685static int rfkill_init(struct platform_device *sdev) 686{ 687 /* add rfkill */ 688 int retval; 689 690 /* keep the hardware wireless state */ 691 get_wireless_state_ec_standard(); 692 693 rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev, 694 RFKILL_TYPE_BLUETOOTH, 695 &rfkill_bluetooth_ops, NULL); 696 if (!rfk_bluetooth) { 697 retval = -ENOMEM; 698 goto err_bluetooth; 699 } 700 retval = rfkill_register(rfk_bluetooth); 701 if (retval) 702 goto err_bluetooth; 703 704 rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN, 705 &rfkill_wlan_ops, NULL); 706 if (!rfk_wlan) { 707 retval = -ENOMEM; 708 goto err_wlan; 709 } 710 retval = rfkill_register(rfk_wlan); 711 if (retval) 712 goto err_wlan; 713 714 if (threeg_exists) { 715 rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev, 716 RFKILL_TYPE_WWAN, &rfkill_threeg_ops, NULL); 717 if (!rfk_threeg) { 718 retval = -ENOMEM; 719 goto err_threeg; 720 } 721 retval = rfkill_register(rfk_threeg); 722 if (retval) 723 goto err_threeg; 724 } 725 726 /* schedule to run rfkill state initial */ 727 schedule_delayed_work(&msi_rfkill_init, 728 round_jiffies_relative(1 * HZ)); 729 730 return 0; 731 732err_threeg: 733 rfkill_destroy(rfk_threeg); 734 if (rfk_wlan) 735 rfkill_unregister(rfk_wlan); 736err_wlan: 737 rfkill_destroy(rfk_wlan); 738 if (rfk_bluetooth) 739 rfkill_unregister(rfk_bluetooth); 740err_bluetooth: 741 rfkill_destroy(rfk_bluetooth); 742 743 return retval; 744} 745 746static int msi_laptop_resume(struct platform_device *device) 747{ 748 u8 data; 749 int result; 750 751 if (!load_scm_model) 752 return 0; 753 754 /* set load SCM to disable hardware control by fn key */ 755 result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); 756 if (result < 0) 757 return result; 758 759 result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, 760 data | MSI_STANDARD_EC_SCM_LOAD_MASK); 761 if (result < 0) 762 return result; 763 764 return 0; 765} 766 767static int __init msi_laptop_input_setup(void) 768{ 769 int err; 770 771 msi_laptop_input_dev = input_allocate_device(); 772 if (!msi_laptop_input_dev) 773 return -ENOMEM; 774 775 msi_laptop_input_dev->name = "MSI Laptop hotkeys"; 776 msi_laptop_input_dev->phys = "msi-laptop/input0"; 777 msi_laptop_input_dev->id.bustype = BUS_HOST; 778 779 err = sparse_keymap_setup(msi_laptop_input_dev, 780 msi_laptop_keymap, NULL); 781 if (err) 782 goto err_free_dev; 783 784 err = input_register_device(msi_laptop_input_dev); 785 if (err) 786 goto err_free_keymap; 787 788 return 0; 789 790err_free_keymap: 791 sparse_keymap_free(msi_laptop_input_dev); 792err_free_dev: 793 input_free_device(msi_laptop_input_dev); 794 return err; 795} 796 797static void msi_laptop_input_destroy(void) 798{ 799 sparse_keymap_free(msi_laptop_input_dev); 800 input_unregister_device(msi_laptop_input_dev); 801} 802 803static int load_scm_model_init(struct platform_device *sdev) 804{ 805 u8 data; 806 int result; 807 808 /* allow userland write sysfs file */ 809 dev_attr_bluetooth.store = store_bluetooth; 810 dev_attr_wlan.store = store_wlan; 811 dev_attr_threeg.store = store_threeg; 812 dev_attr_bluetooth.attr.mode |= S_IWUSR; 813 dev_attr_wlan.attr.mode |= S_IWUSR; 814 dev_attr_threeg.attr.mode |= S_IWUSR; 815 816 /* disable hardware control by fn key */ 817 result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); 818 if (result < 0) 819 return result; 820 821 result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, 822 data | MSI_STANDARD_EC_SCM_LOAD_MASK); 823 if (result < 0) 824 return result; 825 826 /* initial rfkill */ 827 result = rfkill_init(sdev); 828 if (result < 0) 829 goto fail_rfkill; 830 831 /* setup input device */ 832 result = msi_laptop_input_setup(); 833 if (result) 834 goto fail_input; 835 836 result = i8042_install_filter(msi_laptop_i8042_filter); 837 if (result) { 838 pr_err("Unable to install key filter\n"); 839 goto fail_filter; 840 } 841 842 return 0; 843 844fail_filter: 845 msi_laptop_input_destroy(); 846 847fail_input: 848 rfkill_cleanup(); 849 850fail_rfkill: 851 852 return result; 853 854} 855 856static int __init msi_init(void) 857{ 858 int ret; 859 860 if (acpi_disabled) 861 return -ENODEV; 862 863 if (force || dmi_check_system(msi_dmi_table)) 864 old_ec_model = 1; 865 866 if (!old_ec_model) 867 get_threeg_exists(); 868 869 if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table)) 870 load_scm_model = 1; 871 872 if (auto_brightness < 0 || auto_brightness > 2) 873 return -EINVAL; 874 875 /* Register backlight stuff */ 876 877 if (acpi_video_backlight_support()) { 878 pr_info("Brightness ignored, must be controlled " 879 "by ACPI video driver\n"); 880 } else { 881 struct backlight_properties props; 882 memset(&props, 0, sizeof(struct backlight_properties)); 883 props.type = BACKLIGHT_PLATFORM; 884 props.max_brightness = MSI_LCD_LEVEL_MAX - 1; 885 msibl_device = backlight_device_register("msi-laptop-bl", NULL, 886 NULL, &msibl_ops, 887 &props); 888 if (IS_ERR(msibl_device)) 889 return PTR_ERR(msibl_device); 890 } 891 892 ret = platform_driver_register(&msipf_driver); 893 if (ret) 894 goto fail_backlight; 895 896 /* Register platform stuff */ 897 898 msipf_device = platform_device_alloc("msi-laptop-pf", -1); 899 if (!msipf_device) { 900 ret = -ENOMEM; 901 goto fail_platform_driver; 902 } 903 904 ret = platform_device_add(msipf_device); 905 if (ret) 906 goto fail_platform_device1; 907 908 if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) { 909 ret = -EINVAL; 910 goto fail_platform_device1; 911 } 912 913 ret = sysfs_create_group(&msipf_device->dev.kobj, 914 &msipf_attribute_group); 915 if (ret) 916 goto fail_platform_device2; 917 918 if (!old_ec_model) { 919 if (threeg_exists) 920 ret = device_create_file(&msipf_device->dev, 921 &dev_attr_threeg); 922 if (ret) 923 goto fail_platform_device2; 924 } 925 926 /* Disable automatic brightness control by default because 927 * this module was probably loaded to do brightness control in 928 * software. */ 929 930 if (auto_brightness != 2) 931 set_auto_brightness(auto_brightness); 932 933 pr_info("driver "MSI_DRIVER_VERSION" successfully loaded.\n"); 934 935 return 0; 936 937fail_platform_device2: 938 939 if (load_scm_model) { 940 i8042_remove_filter(msi_laptop_i8042_filter); 941 cancel_delayed_work_sync(&msi_rfkill_work); 942 rfkill_cleanup(); 943 } 944 platform_device_del(msipf_device); 945 946fail_platform_device1: 947 948 platform_device_put(msipf_device); 949 950fail_platform_driver: 951 952 platform_driver_unregister(&msipf_driver); 953 954fail_backlight: 955 956 backlight_device_unregister(msibl_device); 957 958 return ret; 959} 960 961static void __exit msi_cleanup(void) 962{ 963 if (load_scm_model) { 964 i8042_remove_filter(msi_laptop_i8042_filter); 965 msi_laptop_input_destroy(); 966 cancel_delayed_work_sync(&msi_rfkill_work); 967 rfkill_cleanup(); 968 } 969 970 sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); 971 if (!old_ec_model && threeg_exists) 972 device_remove_file(&msipf_device->dev, &dev_attr_threeg); 973 platform_device_unregister(msipf_device); 974 platform_driver_unregister(&msipf_driver); 975 backlight_device_unregister(msibl_device); 976 977 /* Enable automatic brightness control again */ 978 if (auto_brightness != 2) 979 set_auto_brightness(1); 980 981 pr_info("driver unloaded.\n"); 982} 983 984module_init(msi_init); 985module_exit(msi_cleanup); 986 987MODULE_AUTHOR("Lennart Poettering"); 988MODULE_DESCRIPTION("MSI Laptop Support"); 989MODULE_VERSION(MSI_DRIVER_VERSION); 990MODULE_LICENSE("GPL"); 991 992MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); 993MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*"); 994MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); 995MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); 996MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*"); 997MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*"); 998MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*"); 999MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");