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 58 #include <linux/dmi.h> 59 59 #include <linux/backlight.h> 60 60 #include <linux/platform_device.h> 61 + #include <linux/rfkill.h> 61 62 62 63 #define MSI_DRIVER_VERSION "0.5" 63 64 ··· 73 72 #define MSI_STANDARD_EC_WLAN_MASK (1 << 3) 74 73 #define MSI_STANDARD_EC_3G_MASK (1 << 4) 75 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 + 76 79 static int force; 77 80 module_param(force, bool, 0); 78 81 MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); ··· 87 82 88 83 static bool old_ec_model; 89 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; 90 98 91 99 /* Hardware access */ 92 100 ··· 155 137 wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); 156 138 157 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; 158 169 } 159 170 160 171 static int get_wireless_state(int *wlan, int *bluetooth) ··· 262 215 return sprintf(buf, "%i\n", enabled); 263 216 } 264 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 + 265 224 static ssize_t show_bluetooth(struct device *dev, 266 225 struct device_attribute *attr, char *buf) 267 226 { ··· 286 233 return sprintf(buf, "%i\n", enabled); 287 234 } 288 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 + 289 242 static ssize_t show_threeg(struct device *dev, 290 243 struct device_attribute *attr, char *buf) 291 244 { ··· 307 248 return ret; 308 249 309 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); 310 257 } 311 258 312 259 static ssize_t show_lcd_level(struct device *dev, ··· 452 387 { } 453 388 }; 454 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 + 455 553 static int __init msi_init(void) 456 554 { 457 555 int ret; ··· 624 396 625 397 if (force || dmi_check_system(msi_dmi_table)) 626 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; 627 402 628 403 if (auto_brightness < 0 || auto_brightness > 2) 629 404 return -EINVAL; ··· 659 428 ret = platform_device_add(msipf_device); 660 429 if (ret) 661 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 + } 662 436 663 437 ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group); 664 438 if (ret) ··· 714 478 platform_device_unregister(msipf_device); 715 479 platform_driver_unregister(&msipf_driver); 716 480 backlight_device_unregister(msibl_device); 481 + 482 + rfkill_cleanup(); 717 483 718 484 /* Enable automatic brightness control again */ 719 485 if (auto_brightness != 2)