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