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