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