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