msi-laptop: Support some MSI 3G netbook that is need load SCM

Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G,
those netbook will load the SCM (windows app) to disable the original
Wlan/Bluetooth control by BIOS when user press fn key, then control
Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user
cann't on/off 3G module on those 3G netbook.
On Linux, msi-laptop driver will do the same thing to disable the
original BIOS control, then might need use HAL or other userland
application to do the software control that simulate with SCM.
e.g. MSI N034 netbook

Signed-off-by: Lee, Chun-Yi <jlee@novell.com>
Cc: Lennart Poettering <mzxreary@0pointer.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by Lee, Chun-Yi and committed by Greg Kroah-Hartman 472ea12d fc0dc4c9

+238
+238
drivers/platform/x86/msi-laptop.c
··· 58 #include <linux/dmi.h> 59 #include <linux/backlight.h> 60 #include <linux/platform_device.h> 61 62 #define MSI_DRIVER_VERSION "0.5" 63 ··· 73 #define MSI_STANDARD_EC_WLAN_MASK (1 << 3) 74 #define MSI_STANDARD_EC_3G_MASK (1 << 4) 75 76 static int force; 77 module_param(force, bool, 0); 78 MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); ··· 87 88 static bool old_ec_model; 89 static int wlan_s, bluetooth_s, threeg_s; 90 91 /* Hardware access */ 92 ··· 155 wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); 156 157 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1); 158 } 159 160 static int get_wireless_state(int *wlan, int *bluetooth) ··· 262 return sprintf(buf, "%i\n", enabled); 263 } 264 265 static ssize_t show_bluetooth(struct device *dev, 266 struct device_attribute *attr, char *buf) 267 { ··· 286 return sprintf(buf, "%i\n", enabled); 287 } 288 289 static ssize_t show_threeg(struct device *dev, 290 struct device_attribute *attr, char *buf) 291 { ··· 307 return ret; 308 309 return sprintf(buf, "%i\n", threeg_s); 310 } 311 312 static ssize_t show_lcd_level(struct device *dev, ··· 452 { } 453 }; 454 455 static int __init msi_init(void) 456 { 457 int ret; ··· 624 625 if (force || dmi_check_system(msi_dmi_table)) 626 old_ec_model = 1; 627 628 if (auto_brightness < 0 || auto_brightness > 2) 629 return -EINVAL; ··· 659 ret = platform_device_add(msipf_device); 660 if (ret) 661 goto fail_platform_device1; 662 663 ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group); 664 if (ret) ··· 714 platform_device_unregister(msipf_device); 715 platform_driver_unregister(&msipf_driver); 716 backlight_device_unregister(msibl_device); 717 718 /* Enable automatic brightness control again */ 719 if (auto_brightness != 2)
··· 58 #include <linux/dmi.h> 59 #include <linux/backlight.h> 60 #include <linux/platform_device.h> 61 + #include <linux/rfkill.h> 62 63 #define MSI_DRIVER_VERSION "0.5" 64 ··· 72 #define MSI_STANDARD_EC_WLAN_MASK (1 << 3) 73 #define MSI_STANDARD_EC_3G_MASK (1 << 4) 74 75 + /* For set SCM load flag to disable BIOS fn key */ 76 + #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d 77 + #define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) 78 + 79 static int force; 80 module_param(force, bool, 0); 81 MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); ··· 82 83 static bool old_ec_model; 84 static int wlan_s, bluetooth_s, threeg_s; 85 + 86 + /* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G, 87 + * those netbook will load the SCM (windows app) to disable the original 88 + * Wlan/Bluetooth control by BIOS when user press fn key, then control 89 + * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user 90 + * cann't on/off 3G module on those 3G netbook. 91 + * On Linux, msi-laptop driver will do the same thing to disable the 92 + * original BIOS control, then might need use HAL or other userland 93 + * application to do the software control that simulate with SCM. 94 + * e.g. MSI N034 netbook 95 + */ 96 + static bool load_scm_model; 97 + static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg; 98 99 /* Hardware access */ 100 ··· 137 wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); 138 139 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1); 140 + } 141 + 142 + static ssize_t set_device_state(const char *buf, size_t count, u8 mask) 143 + { 144 + int status; 145 + u8 wdata = 0, rdata; 146 + int result; 147 + 148 + if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1)) 149 + return -EINVAL; 150 + 151 + /* read current device state */ 152 + result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata); 153 + if (result < 0) 154 + return -EINVAL; 155 + 156 + if (!!(rdata & mask) != status) { 157 + /* reverse device bit */ 158 + if (rdata & mask) 159 + wdata = rdata & ~mask; 160 + else 161 + wdata = rdata | mask; 162 + 163 + result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata); 164 + if (result < 0) 165 + return -EINVAL; 166 + } 167 + 168 + return count; 169 } 170 171 static int get_wireless_state(int *wlan, int *bluetooth) ··· 215 return sprintf(buf, "%i\n", enabled); 216 } 217 218 + static ssize_t store_wlan(struct device *dev, 219 + struct device_attribute *attr, const char *buf, size_t count) 220 + { 221 + return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK); 222 + } 223 + 224 static ssize_t show_bluetooth(struct device *dev, 225 struct device_attribute *attr, char *buf) 226 { ··· 233 return sprintf(buf, "%i\n", enabled); 234 } 235 236 + static ssize_t store_bluetooth(struct device *dev, 237 + struct device_attribute *attr, const char *buf, size_t count) 238 + { 239 + return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK); 240 + } 241 + 242 static ssize_t show_threeg(struct device *dev, 243 struct device_attribute *attr, char *buf) 244 { ··· 248 return ret; 249 250 return sprintf(buf, "%i\n", threeg_s); 251 + } 252 + 253 + static ssize_t store_threeg(struct device *dev, 254 + struct device_attribute *attr, const char *buf, size_t count) 255 + { 256 + return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK); 257 } 258 259 static ssize_t show_lcd_level(struct device *dev, ··· 387 { } 388 }; 389 390 + static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = { 391 + { 392 + .ident = "MSI N034", 393 + .matches = { 394 + DMI_MATCH(DMI_SYS_VENDOR, 395 + "MICRO-STAR INTERNATIONAL CO., LTD"), 396 + DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"), 397 + DMI_MATCH(DMI_CHASSIS_VENDOR, 398 + "MICRO-STAR INTERNATIONAL CO., LTD") 399 + }, 400 + .callback = dmi_check_cb 401 + }, 402 + { } 403 + }; 404 + 405 + static int rfkill_bluetooth_set(void *data, bool blocked) 406 + { 407 + /* Do something with blocked...*/ 408 + /* 409 + * blocked == false is on 410 + * blocked == true is off 411 + */ 412 + if (blocked) 413 + set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); 414 + else 415 + set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); 416 + 417 + return 0; 418 + } 419 + 420 + static int rfkill_wlan_set(void *data, bool blocked) 421 + { 422 + if (blocked) 423 + set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK); 424 + else 425 + set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK); 426 + 427 + return 0; 428 + } 429 + 430 + static int rfkill_threeg_set(void *data, bool blocked) 431 + { 432 + if (blocked) 433 + set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK); 434 + else 435 + set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK); 436 + 437 + return 0; 438 + } 439 + 440 + static struct rfkill_ops rfkill_bluetooth_ops = { 441 + .set_block = rfkill_bluetooth_set 442 + }; 443 + 444 + static struct rfkill_ops rfkill_wlan_ops = { 445 + .set_block = rfkill_wlan_set 446 + }; 447 + 448 + static struct rfkill_ops rfkill_threeg_ops = { 449 + .set_block = rfkill_threeg_set 450 + }; 451 + 452 + static void rfkill_cleanup(void) 453 + { 454 + if (rfk_bluetooth) { 455 + rfkill_unregister(rfk_bluetooth); 456 + rfkill_destroy(rfk_bluetooth); 457 + } 458 + 459 + if (rfk_threeg) { 460 + rfkill_unregister(rfk_threeg); 461 + rfkill_destroy(rfk_threeg); 462 + } 463 + 464 + if (rfk_wlan) { 465 + rfkill_unregister(rfk_wlan); 466 + rfkill_destroy(rfk_wlan); 467 + } 468 + } 469 + 470 + static int rfkill_init(struct platform_device *sdev) 471 + { 472 + /* add rfkill */ 473 + int retval; 474 + 475 + rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev, 476 + RFKILL_TYPE_BLUETOOTH, 477 + &rfkill_bluetooth_ops, NULL); 478 + if (!rfk_bluetooth) { 479 + retval = -ENOMEM; 480 + goto err_bluetooth; 481 + } 482 + retval = rfkill_register(rfk_bluetooth); 483 + if (retval) 484 + goto err_bluetooth; 485 + 486 + rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN, 487 + &rfkill_wlan_ops, NULL); 488 + if (!rfk_wlan) { 489 + retval = -ENOMEM; 490 + goto err_wlan; 491 + } 492 + retval = rfkill_register(rfk_wlan); 493 + if (retval) 494 + goto err_wlan; 495 + 496 + rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev, RFKILL_TYPE_WWAN, 497 + &rfkill_threeg_ops, NULL); 498 + if (!rfk_threeg) { 499 + retval = -ENOMEM; 500 + goto err_threeg; 501 + } 502 + retval = rfkill_register(rfk_threeg); 503 + if (retval) 504 + goto err_threeg; 505 + 506 + return 0; 507 + 508 + err_threeg: 509 + rfkill_destroy(rfk_threeg); 510 + if (rfk_wlan) 511 + rfkill_unregister(rfk_wlan); 512 + err_wlan: 513 + rfkill_destroy(rfk_wlan); 514 + if (rfk_bluetooth) 515 + rfkill_unregister(rfk_bluetooth); 516 + err_bluetooth: 517 + rfkill_destroy(rfk_bluetooth); 518 + 519 + return retval; 520 + } 521 + 522 + static int load_scm_model_init(struct platform_device *sdev) 523 + { 524 + u8 data; 525 + int result; 526 + 527 + /* allow userland write sysfs file */ 528 + dev_attr_bluetooth.store = store_bluetooth; 529 + dev_attr_wlan.store = store_wlan; 530 + dev_attr_threeg.store = store_threeg; 531 + dev_attr_bluetooth.attr.mode |= S_IWUSR; 532 + dev_attr_wlan.attr.mode |= S_IWUSR; 533 + dev_attr_threeg.attr.mode |= S_IWUSR; 534 + 535 + /* disable hardware control by fn key */ 536 + result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); 537 + if (result < 0) 538 + return result; 539 + 540 + result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, 541 + data | MSI_STANDARD_EC_SCM_LOAD_MASK); 542 + if (result < 0) 543 + return result; 544 + 545 + /* initial rfkill */ 546 + result = rfkill_init(sdev); 547 + if (result < 0) 548 + return result; 549 + 550 + return 0; 551 + } 552 + 553 static int __init msi_init(void) 554 { 555 int ret; ··· 396 397 if (force || dmi_check_system(msi_dmi_table)) 398 old_ec_model = 1; 399 + 400 + if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table)) 401 + load_scm_model = 1; 402 403 if (auto_brightness < 0 || auto_brightness > 2) 404 return -EINVAL; ··· 428 ret = platform_device_add(msipf_device); 429 if (ret) 430 goto fail_platform_device1; 431 + 432 + if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) { 433 + ret = -EINVAL; 434 + goto fail_platform_device1; 435 + } 436 437 ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group); 438 if (ret) ··· 478 platform_device_unregister(msipf_device); 479 platform_driver_unregister(&msipf_driver); 480 backlight_device_unregister(msibl_device); 481 + 482 + rfkill_cleanup(); 483 484 /* Enable automatic brightness control again */ 485 if (auto_brightness != 2)