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 88static int msi_laptop_resume(struct device *device); 89#endif 90static SIMPLE_DEV_PM_OPS(msi_laptop_pm, NULL, msi_laptop_resume); 91 92#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f 93 94static bool force; 95module_param(force, bool, 0); 96MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); 97 98static int auto_brightness; 99module_param(auto_brightness, int, 0); 100MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); 101 102static 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 108static struct input_dev *msi_laptop_input_dev; 109 110static int wlan_s, bluetooth_s, threeg_s; 111static int threeg_exists; 112static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg; 113 114/* MSI laptop quirks */ 115struct 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 140static struct quirk_entry *quirks; 141 142/* Hardware access */ 143 144static 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 158static 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 171static 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 184static 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 203static 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 0; 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 235static 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 253static 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 271static 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 287static int bl_get_brightness(struct backlight_device *b) 288{ 289 return get_lcd_level(); 290} 291 292 293static int bl_update_status(struct backlight_device *b) 294{ 295 return set_lcd_level(b->props.brightness); 296} 297 298static const struct backlight_ops msibl_ops = { 299 .get_brightness = bl_get_brightness, 300 .update_status = bl_update_status, 301}; 302 303static struct backlight_device *msibl_device; 304 305/* Platform device */ 306 307static 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 325static 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 331static 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 349static 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 355static 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 372static 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 378static 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 391static 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 408static 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 421static 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 437static 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 451static 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 465static 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 479static 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 494static 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 508static 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 524static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); 525static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, 526 store_auto_brightness); 527static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); 528static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); 529static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); 530static DEVICE_ATTR(touchpad, 0444, show_touchpad, NULL); 531static DEVICE_ATTR(turbo_mode, 0444, show_turbo, NULL); 532static DEVICE_ATTR(eco_mode, 0444, show_eco, NULL); 533static DEVICE_ATTR(turbo_cooldown, 0444, show_turbo_cooldown, NULL); 534static DEVICE_ATTR(auto_fan, 0644, show_auto_fan, store_auto_fan); 535 536static 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 547static struct attribute *msipf_old_attributes[] = { 548 &dev_attr_lcd_level.attr, 549 &dev_attr_auto_brightness.attr, 550 NULL 551}; 552 553static const struct attribute_group msipf_attribute_group = { 554 .attrs = msipf_attributes 555}; 556 557static const struct attribute_group msipf_old_attribute_group = { 558 .attrs = msipf_old_attributes 559}; 560 561static struct platform_driver msipf_driver = { 562 .driver = { 563 .name = "msi-laptop-pf", 564 .pm = &msi_laptop_pm, 565 }, 566}; 567 568static struct platform_device *msipf_device; 569 570/* Initialization */ 571 572static struct quirk_entry quirk_old_ec_model = { 573 .old_ec_model = true, 574}; 575 576static struct quirk_entry quirk_load_scm_model = { 577 .load_scm_model = true, 578 .ec_delay = true, 579}; 580 581static struct quirk_entry quirk_load_scm_ro_model = { 582 .load_scm_model = true, 583 .ec_read_only = true, 584}; 585 586static 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 595static 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"), 600 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"), 601 DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), 602 DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT") 603 }, 604 .driver_data = &quirk_old_ec_model, 605 .callback = dmi_check_cb 606 }, 607 { 608 .ident = "MSI S271", 609 .matches = { 610 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), 611 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"), 612 DMI_MATCH(DMI_PRODUCT_VERSION, "0581"), 613 DMI_MATCH(DMI_BOARD_NAME, "MS-1058") 614 }, 615 .driver_data = &quirk_old_ec_model, 616 .callback = dmi_check_cb 617 }, 618 { 619 .ident = "MSI S420", 620 .matches = { 621 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), 622 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"), 623 DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), 624 DMI_MATCH(DMI_BOARD_NAME, "MS-1412") 625 }, 626 .driver_data = &quirk_old_ec_model, 627 .callback = dmi_check_cb 628 }, 629 { 630 .ident = "Medion MD96100", 631 .matches = { 632 DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"), 633 DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"), 634 DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), 635 DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT") 636 }, 637 .driver_data = &quirk_old_ec_model, 638 .callback = dmi_check_cb 639 }, 640 { 641 .ident = "MSI N034", 642 .matches = { 643 DMI_MATCH(DMI_SYS_VENDOR, 644 "MICRO-STAR INTERNATIONAL CO., LTD"), 645 DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"), 646 DMI_MATCH(DMI_CHASSIS_VENDOR, 647 "MICRO-STAR INTERNATIONAL CO., LTD") 648 }, 649 .driver_data = &quirk_load_scm_model, 650 .callback = dmi_check_cb 651 }, 652 { 653 .ident = "MSI N051", 654 .matches = { 655 DMI_MATCH(DMI_SYS_VENDOR, 656 "MICRO-STAR INTERNATIONAL CO., LTD"), 657 DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"), 658 DMI_MATCH(DMI_CHASSIS_VENDOR, 659 "MICRO-STAR INTERNATIONAL CO., LTD") 660 }, 661 .driver_data = &quirk_load_scm_model, 662 .callback = dmi_check_cb 663 }, 664 { 665 .ident = "MSI N014", 666 .matches = { 667 DMI_MATCH(DMI_SYS_VENDOR, 668 "MICRO-STAR INTERNATIONAL CO., LTD"), 669 DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"), 670 }, 671 .driver_data = &quirk_load_scm_model, 672 .callback = dmi_check_cb 673 }, 674 { 675 .ident = "MSI CR620", 676 .matches = { 677 DMI_MATCH(DMI_SYS_VENDOR, 678 "Micro-Star International"), 679 DMI_MATCH(DMI_PRODUCT_NAME, "CR620"), 680 }, 681 .driver_data = &quirk_load_scm_model, 682 .callback = dmi_check_cb 683 }, 684 { 685 .ident = "MSI U270", 686 .matches = { 687 DMI_MATCH(DMI_SYS_VENDOR, 688 "Micro-Star International Co., Ltd."), 689 DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"), 690 }, 691 .driver_data = &quirk_load_scm_model, 692 .callback = dmi_check_cb 693 }, 694 { 695 .ident = "MSI U90/U100", 696 .matches = { 697 DMI_MATCH(DMI_SYS_VENDOR, 698 "MICRO-STAR INTERNATIONAL CO., LTD"), 699 DMI_MATCH(DMI_PRODUCT_NAME, "U90/U100"), 700 }, 701 .driver_data = &quirk_load_scm_ro_model, 702 .callback = dmi_check_cb 703 }, 704 { } 705}; 706 707static int rfkill_bluetooth_set(void *data, bool blocked) 708{ 709 /* Do something with blocked...*/ 710 /* 711 * blocked == false is on 712 * blocked == true is off 713 */ 714 int result = set_device_state(blocked ? "0" : "1", 0, 715 MSI_STANDARD_EC_BLUETOOTH_MASK); 716 717 return min(result, 0); 718} 719 720static int rfkill_wlan_set(void *data, bool blocked) 721{ 722 int result = set_device_state(blocked ? "0" : "1", 0, 723 MSI_STANDARD_EC_WLAN_MASK); 724 725 return min(result, 0); 726} 727 728static int rfkill_threeg_set(void *data, bool blocked) 729{ 730 int result = set_device_state(blocked ? "0" : "1", 0, 731 MSI_STANDARD_EC_3G_MASK); 732 733 return min(result, 0); 734} 735 736static const struct rfkill_ops rfkill_bluetooth_ops = { 737 .set_block = rfkill_bluetooth_set 738}; 739 740static const struct rfkill_ops rfkill_wlan_ops = { 741 .set_block = rfkill_wlan_set 742}; 743 744static const struct rfkill_ops rfkill_threeg_ops = { 745 .set_block = rfkill_threeg_set 746}; 747 748static void rfkill_cleanup(void) 749{ 750 if (rfk_bluetooth) { 751 rfkill_unregister(rfk_bluetooth); 752 rfkill_destroy(rfk_bluetooth); 753 } 754 755 if (rfk_threeg) { 756 rfkill_unregister(rfk_threeg); 757 rfkill_destroy(rfk_threeg); 758 } 759 760 if (rfk_wlan) { 761 rfkill_unregister(rfk_wlan); 762 rfkill_destroy(rfk_wlan); 763 } 764} 765 766static bool msi_rfkill_set_state(struct rfkill *rfkill, bool blocked) 767{ 768 if (quirks->ec_read_only) 769 return rfkill_set_hw_state(rfkill, blocked); 770 else 771 return rfkill_set_sw_state(rfkill, blocked); 772} 773 774static void msi_update_rfkill(struct work_struct *ignored) 775{ 776 get_wireless_state_ec_standard(); 777 778 if (rfk_wlan) 779 msi_rfkill_set_state(rfk_wlan, !wlan_s); 780 if (rfk_bluetooth) 781 msi_rfkill_set_state(rfk_bluetooth, !bluetooth_s); 782 if (rfk_threeg) 783 msi_rfkill_set_state(rfk_threeg, !threeg_s); 784} 785static DECLARE_DELAYED_WORK(msi_rfkill_dwork, msi_update_rfkill); 786static DECLARE_WORK(msi_rfkill_work, msi_update_rfkill); 787 788static void msi_send_touchpad_key(struct work_struct *ignored) 789{ 790 u8 rdata; 791 int result; 792 793 result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata); 794 if (result < 0) 795 return; 796 797 sparse_keymap_report_event(msi_laptop_input_dev, 798 (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ? 799 KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true); 800} 801static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key); 802static DECLARE_WORK(msi_touchpad_work, msi_send_touchpad_key); 803 804static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, 805 struct serio *port) 806{ 807 static bool extended; 808 809 if (str & I8042_STR_AUXDATA) 810 return false; 811 812 /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/ 813 if (unlikely(data == 0xe0)) { 814 extended = true; 815 return false; 816 } else if (unlikely(extended)) { 817 extended = false; 818 switch (data) { 819 case 0xE4: 820 if (quirks->ec_delay) { 821 schedule_delayed_work(&msi_touchpad_dwork, 822 round_jiffies_relative(0.5 * HZ)); 823 } else 824 schedule_work(&msi_touchpad_work); 825 break; 826 case 0x54: 827 case 0x62: 828 case 0x76: 829 if (quirks->ec_delay) { 830 schedule_delayed_work(&msi_rfkill_dwork, 831 round_jiffies_relative(0.5 * HZ)); 832 } else 833 schedule_work(&msi_rfkill_work); 834 break; 835 } 836 } 837 838 return false; 839} 840 841static void msi_init_rfkill(struct work_struct *ignored) 842{ 843 if (rfk_wlan) { 844 msi_rfkill_set_state(rfk_wlan, !wlan_s); 845 rfkill_wlan_set(NULL, !wlan_s); 846 } 847 if (rfk_bluetooth) { 848 msi_rfkill_set_state(rfk_bluetooth, !bluetooth_s); 849 rfkill_bluetooth_set(NULL, !bluetooth_s); 850 } 851 if (rfk_threeg) { 852 msi_rfkill_set_state(rfk_threeg, !threeg_s); 853 rfkill_threeg_set(NULL, !threeg_s); 854 } 855} 856static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill); 857 858static int rfkill_init(struct platform_device *sdev) 859{ 860 /* add rfkill */ 861 int retval; 862 863 /* keep the hardware wireless state */ 864 get_wireless_state_ec_standard(); 865 866 rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev, 867 RFKILL_TYPE_BLUETOOTH, 868 &rfkill_bluetooth_ops, NULL); 869 if (!rfk_bluetooth) { 870 retval = -ENOMEM; 871 goto err_bluetooth; 872 } 873 retval = rfkill_register(rfk_bluetooth); 874 if (retval) 875 goto err_bluetooth; 876 877 rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN, 878 &rfkill_wlan_ops, NULL); 879 if (!rfk_wlan) { 880 retval = -ENOMEM; 881 goto err_wlan; 882 } 883 retval = rfkill_register(rfk_wlan); 884 if (retval) 885 goto err_wlan; 886 887 if (threeg_exists) { 888 rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev, 889 RFKILL_TYPE_WWAN, &rfkill_threeg_ops, NULL); 890 if (!rfk_threeg) { 891 retval = -ENOMEM; 892 goto err_threeg; 893 } 894 retval = rfkill_register(rfk_threeg); 895 if (retval) 896 goto err_threeg; 897 } 898 899 /* schedule to run rfkill state initial */ 900 if (quirks->ec_delay) { 901 schedule_delayed_work(&msi_rfkill_init, 902 round_jiffies_relative(1 * HZ)); 903 } else 904 schedule_work(&msi_rfkill_work); 905 906 return 0; 907 908err_threeg: 909 rfkill_destroy(rfk_threeg); 910 if (rfk_wlan) 911 rfkill_unregister(rfk_wlan); 912err_wlan: 913 rfkill_destroy(rfk_wlan); 914 if (rfk_bluetooth) 915 rfkill_unregister(rfk_bluetooth); 916err_bluetooth: 917 rfkill_destroy(rfk_bluetooth); 918 919 return retval; 920} 921 922#ifdef CONFIG_PM_SLEEP 923static int msi_laptop_resume(struct device *device) 924{ 925 u8 data; 926 int result; 927 928 if (!quirks->load_scm_model) 929 return 0; 930 931 /* set load SCM to disable hardware control by fn key */ 932 result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); 933 if (result < 0) 934 return result; 935 936 result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, 937 data | MSI_STANDARD_EC_SCM_LOAD_MASK); 938 if (result < 0) 939 return result; 940 941 return 0; 942} 943#endif 944 945static int __init msi_laptop_input_setup(void) 946{ 947 int err; 948 949 msi_laptop_input_dev = input_allocate_device(); 950 if (!msi_laptop_input_dev) 951 return -ENOMEM; 952 953 msi_laptop_input_dev->name = "MSI Laptop hotkeys"; 954 msi_laptop_input_dev->phys = "msi-laptop/input0"; 955 msi_laptop_input_dev->id.bustype = BUS_HOST; 956 957 err = sparse_keymap_setup(msi_laptop_input_dev, 958 msi_laptop_keymap, NULL); 959 if (err) 960 goto err_free_dev; 961 962 err = input_register_device(msi_laptop_input_dev); 963 if (err) 964 goto err_free_dev; 965 966 return 0; 967 968err_free_dev: 969 input_free_device(msi_laptop_input_dev); 970 return err; 971} 972 973static int __init load_scm_model_init(struct platform_device *sdev) 974{ 975 u8 data; 976 int result; 977 978 if (!quirks->ec_read_only) { 979 /* allow userland write sysfs file */ 980 dev_attr_bluetooth.store = store_bluetooth; 981 dev_attr_wlan.store = store_wlan; 982 dev_attr_threeg.store = store_threeg; 983 dev_attr_bluetooth.attr.mode |= S_IWUSR; 984 dev_attr_wlan.attr.mode |= S_IWUSR; 985 dev_attr_threeg.attr.mode |= S_IWUSR; 986 } 987 988 /* disable hardware control by fn key */ 989 result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); 990 if (result < 0) 991 return result; 992 993 result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, 994 data | MSI_STANDARD_EC_SCM_LOAD_MASK); 995 if (result < 0) 996 return result; 997 998 /* initial rfkill */ 999 result = rfkill_init(sdev); 1000 if (result < 0) 1001 goto fail_rfkill; 1002 1003 /* setup input device */ 1004 result = msi_laptop_input_setup(); 1005 if (result) 1006 goto fail_input; 1007 1008 result = i8042_install_filter(msi_laptop_i8042_filter); 1009 if (result) { 1010 pr_err("Unable to install key filter\n"); 1011 goto fail_filter; 1012 } 1013 1014 return 0; 1015 1016fail_filter: 1017 input_unregister_device(msi_laptop_input_dev); 1018 1019fail_input: 1020 rfkill_cleanup(); 1021 1022fail_rfkill: 1023 1024 return result; 1025 1026} 1027 1028static int __init msi_init(void) 1029{ 1030 int ret; 1031 1032 if (acpi_disabled) 1033 return -ENODEV; 1034 1035 dmi_check_system(msi_dmi_table); 1036 if (!quirks) 1037 /* quirks may be NULL if no match in DMI table */ 1038 quirks = &quirk_load_scm_model; 1039 if (force) 1040 quirks = &quirk_old_ec_model; 1041 1042 if (!quirks->old_ec_model) 1043 get_threeg_exists(); 1044 1045 if (auto_brightness < 0 || auto_brightness > 2) 1046 return -EINVAL; 1047 1048 /* Register backlight stuff */ 1049 if (quirks->old_ec_model && 1050 acpi_video_get_backlight_type() == acpi_backlight_vendor) { 1051 struct backlight_properties props; 1052 memset(&props, 0, sizeof(struct backlight_properties)); 1053 props.type = BACKLIGHT_PLATFORM; 1054 props.max_brightness = MSI_LCD_LEVEL_MAX - 1; 1055 msibl_device = backlight_device_register("msi-laptop-bl", NULL, 1056 NULL, &msibl_ops, 1057 &props); 1058 if (IS_ERR(msibl_device)) 1059 return PTR_ERR(msibl_device); 1060 } 1061 1062 ret = platform_driver_register(&msipf_driver); 1063 if (ret) 1064 goto fail_backlight; 1065 1066 /* Register platform stuff */ 1067 1068 msipf_device = platform_device_alloc("msi-laptop-pf", -1); 1069 if (!msipf_device) { 1070 ret = -ENOMEM; 1071 goto fail_platform_driver; 1072 } 1073 1074 ret = platform_device_add(msipf_device); 1075 if (ret) 1076 goto fail_device_add; 1077 1078 if (quirks->load_scm_model && (load_scm_model_init(msipf_device) < 0)) { 1079 ret = -EINVAL; 1080 goto fail_scm_model_init; 1081 } 1082 1083 ret = sysfs_create_group(&msipf_device->dev.kobj, 1084 &msipf_attribute_group); 1085 if (ret) 1086 goto fail_create_group; 1087 1088 if (!quirks->old_ec_model) { 1089 if (threeg_exists) 1090 ret = device_create_file(&msipf_device->dev, 1091 &dev_attr_threeg); 1092 if (ret) 1093 goto fail_create_attr; 1094 } else { 1095 ret = sysfs_create_group(&msipf_device->dev.kobj, 1096 &msipf_old_attribute_group); 1097 if (ret) 1098 goto fail_create_attr; 1099 1100 /* Disable automatic brightness control by default because 1101 * this module was probably loaded to do brightness control in 1102 * software. */ 1103 1104 if (auto_brightness != 2) 1105 set_auto_brightness(auto_brightness); 1106 } 1107 1108 pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n"); 1109 1110 return 0; 1111 1112fail_create_attr: 1113 sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); 1114fail_create_group: 1115 if (quirks->load_scm_model) { 1116 i8042_remove_filter(msi_laptop_i8042_filter); 1117 cancel_delayed_work_sync(&msi_touchpad_dwork); 1118 input_unregister_device(msi_laptop_input_dev); 1119 cancel_delayed_work_sync(&msi_rfkill_dwork); 1120 cancel_work_sync(&msi_rfkill_work); 1121 rfkill_cleanup(); 1122 } 1123fail_scm_model_init: 1124 platform_device_del(msipf_device); 1125fail_device_add: 1126 platform_device_put(msipf_device); 1127fail_platform_driver: 1128 platform_driver_unregister(&msipf_driver); 1129fail_backlight: 1130 backlight_device_unregister(msibl_device); 1131 1132 return ret; 1133} 1134 1135static void __exit msi_cleanup(void) 1136{ 1137 if (quirks->load_scm_model) { 1138 i8042_remove_filter(msi_laptop_i8042_filter); 1139 cancel_delayed_work_sync(&msi_touchpad_dwork); 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 1162module_init(msi_init); 1163module_exit(msi_cleanup); 1164 1165MODULE_AUTHOR("Lennart Poettering"); 1166MODULE_DESCRIPTION("MSI Laptop Support"); 1167MODULE_VERSION(MSI_DRIVER_VERSION); 1168MODULE_LICENSE("GPL"); 1169 1170MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); 1171MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*"); 1172MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); 1173MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); 1174MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*"); 1175MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*"); 1176MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*"); 1177MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*"); 1178MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*"); 1179MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnU90/U100:*"); 1180