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