1 /*-*-linux-c-*-*/ 2 3 /* 4 Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de> 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301, USA. 20 */ 21 22 /* 23 * msi-laptop.c - MSI S270 laptop support. This laptop is sold under 24 * various brands, including "Cytron/TCM/Medion/Tchibo MD96100". 25 * 26 * Driver also supports S271, S420 models. 27 * 28 * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/: 29 * 30 * lcd_level - Screen brightness: contains a single integer in the 31 * range 0..8. (rw) 32 * 33 * auto_brightness - Enable automatic brightness control: contains 34 * either 0 or 1. If set to 1 the hardware adjusts the screen 35 * brightness automatically when the power cord is 36 * plugged/unplugged. (rw) 37 * 38 * wlan - WLAN subsystem enabled: contains either 0 or 1. (ro) 39 * 40 * bluetooth - Bluetooth subsystem enabled: contains either 0 or 1 41 * Please note that this file is constantly 0 if no Bluetooth 42 * hardware is available. (ro) 43 * 44 * In addition to these platform device attributes the driver 45 * registers itself in the Linux backlight control subsystem and is 46 * available to userspace under /sys/class/backlight/msi-laptop-bl/. 47 * 48 * This driver might work on other laptops produced by MSI. If you 49 * want to try it you can pass force=1 as argument to the module which 50 * will force it to load even when the DMI data doesn't identify the 51 * laptop as MSI S270. YMMV. 52 */ 53 54 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 55 56 #include <linux/module.h> 57 #include <linux/kernel.h> 58 #include <linux/init.h> 59 #include <linux/acpi.h> 60 #include <linux/dmi.h> 61 #include <linux/backlight.h> 62 #include <linux/platform_device.h> 63 #include <linux/rfkill.h> 64 #include <linux/i8042.h> 65 #include <linux/input.h> 66 #include <linux/input/sparse-keymap.h> 67 68 #define MSI_DRIVER_VERSION "0.5" 69 70 #define MSI_LCD_LEVEL_MAX 9 71 72 #define MSI_EC_COMMAND_WIRELESS 0x10 73 #define MSI_EC_COMMAND_LCD_LEVEL 0x11 74 75 #define MSI_STANDARD_EC_COMMAND_ADDRESS 0x2e 76 #define MSI_STANDARD_EC_BLUETOOTH_MASK (1 << 0) 77 #define MSI_STANDARD_EC_WEBCAM_MASK (1 << 1) 78 #define MSI_STANDARD_EC_WLAN_MASK (1 << 3) 79 #define MSI_STANDARD_EC_3G_MASK (1 << 4) 80 81 /* For set SCM load flag to disable BIOS fn key */ 82 #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d 83 #define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) 84 85 #define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4 86 #define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4) 87 88 static int msi_laptop_resume(struct platform_device *device); 89 90 #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f 91 92 static int force; 93 module_param(force, bool, 0); 94 MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); 95 96 static int auto_brightness; 97 module_param(auto_brightness, int, 0); 98 MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); 99 100 static 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 106 static struct input_dev *msi_laptop_input_dev; 107 108 static bool old_ec_model; 109 static int wlan_s, bluetooth_s, threeg_s; 110 static 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 */ 122 static bool load_scm_model; 123 static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg; 124 125 /* Hardware access */ 126 127 static 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 141 static 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 154 static 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 167 static 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 186 static 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 215 static 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 233 static 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 251 static 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 267 static int bl_get_brightness(struct backlight_device *b) 268 { 269 return get_lcd_level(); 270 } 271 272 273 static int bl_update_status(struct backlight_device *b) 274 { 275 return set_lcd_level(b->props.brightness); 276 } 277 278 static const struct backlight_ops msibl_ops = { 279 .get_brightness = bl_get_brightness, 280 .update_status = bl_update_status, 281 }; 282 283 static struct backlight_device *msibl_device; 284 285 /* Platform device */ 286 287 static 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 305 static 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 311 static 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 329 static 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 335 static 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 352 static 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 358 static 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 371 static 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 388 static 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 401 static 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 417 static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); 418 static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, 419 store_auto_brightness); 420 static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); 421 static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); 422 static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); 423 424 static 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 432 static struct attribute_group msipf_attribute_group = { 433 .attrs = msipf_attributes 434 }; 435 436 static struct platform_driver msipf_driver = { 437 .driver = { 438 .name = "msi-laptop-pf", 439 .owner = THIS_MODULE, 440 }, 441 .resume = msi_laptop_resume, 442 }; 443 444 static struct platform_device *msipf_device; 445 446 /* Initialization */ 447 448 static 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 454 static 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 500 static 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 553 static 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 568 static 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 578 static 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 588 static const struct rfkill_ops rfkill_bluetooth_ops = { 589 .set_block = rfkill_bluetooth_set 590 }; 591 592 static const struct rfkill_ops rfkill_wlan_ops = { 593 .set_block = rfkill_wlan_set 594 }; 595 596 static const struct rfkill_ops rfkill_threeg_ops = { 597 .set_block = rfkill_threeg_set 598 }; 599 600 static 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 618 static 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 } 629 static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); 630 631 static 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 } 644 static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key); 645 646 static 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 677 static 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 } 692 static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill); 693 694 static 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 741 err_threeg: 742 rfkill_destroy(rfk_threeg); 743 if (rfk_wlan) 744 rfkill_unregister(rfk_wlan); 745 err_wlan: 746 rfkill_destroy(rfk_wlan); 747 if (rfk_bluetooth) 748 rfkill_unregister(rfk_bluetooth); 749 err_bluetooth: 750 rfkill_destroy(rfk_bluetooth); 751 752 return retval; 753 } 754 755 static 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 776 static 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 799 err_free_keymap: 800 sparse_keymap_free(msi_laptop_input_dev); 801 err_free_dev: 802 input_free_device(msi_laptop_input_dev); 803 return err; 804 } 805 806 static 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 812 static 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 853 fail_filter: 854 msi_laptop_input_destroy(); 855 856 fail_input: 857 rfkill_cleanup(); 858 859 fail_rfkill: 860 861 return result; 862 863 } 864 865 static 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 945 fail_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 954 fail_platform_device1: 955 956 platform_device_put(msipf_device); 957 958 fail_platform_driver: 959 960 platform_driver_unregister(&msipf_driver); 961 962 fail_backlight: 963 964 backlight_device_unregister(msibl_device); 965 966 return ret; 967 } 968 969 static 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 992 module_init(msi_init); 993 module_exit(msi_cleanup); 994 995 MODULE_AUTHOR("Lennart Poettering"); 996 MODULE_DESCRIPTION("MSI Laptop Support"); 997 MODULE_VERSION(MSI_DRIVER_VERSION); 998 MODULE_LICENSE("GPL"); 999 1000 MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); 1001 MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*"); 1002 MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); 1003 MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); 1004 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*"); 1005 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*"); 1006 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*"); 1007 MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*"); 1008 MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*"); 1009