aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/platform/x86/msi-laptop.c238
1 files changed, 238 insertions, 0 deletions
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
index 1784d5588d41..ff21d1acf3be 100644
--- a/drivers/platform/x86/msi-laptop.c
+++ b/drivers/platform/x86/msi-laptop.c
@@ -58,6 +58,7 @@
58#include <linux/dmi.h> 58#include <linux/dmi.h>
59#include <linux/backlight.h> 59#include <linux/backlight.h>
60#include <linux/platform_device.h> 60#include <linux/platform_device.h>
61#include <linux/rfkill.h>
61 62
62#define MSI_DRIVER_VERSION "0.5" 63#define MSI_DRIVER_VERSION "0.5"
63 64
@@ -72,6 +73,10 @@
72#define MSI_STANDARD_EC_WLAN_MASK (1 << 3) 73#define MSI_STANDARD_EC_WLAN_MASK (1 << 3)
73#define MSI_STANDARD_EC_3G_MASK (1 << 4) 74#define MSI_STANDARD_EC_3G_MASK (1 << 4)
74 75
76/* For set SCM load flag to disable BIOS fn key */
77#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d
78#define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0)
79
75static int force; 80static int force;
76module_param(force, bool, 0); 81module_param(force, bool, 0);
77MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); 82MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
@@ -83,6 +88,19 @@ MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disab
83static bool old_ec_model; 88static bool old_ec_model;
84static int wlan_s, bluetooth_s, threeg_s; 89static int wlan_s, bluetooth_s, threeg_s;
85 90
91/* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G,
92 * those netbook will load the SCM (windows app) to disable the original
93 * Wlan/Bluetooth control by BIOS when user press fn key, then control
94 * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user
95 * cann't on/off 3G module on those 3G netbook.
96 * On Linux, msi-laptop driver will do the same thing to disable the
97 * original BIOS control, then might need use HAL or other userland
98 * application to do the software control that simulate with SCM.
99 * e.g. MSI N034 netbook
100 */
101static bool load_scm_model;
102static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
103
86/* Hardware access */ 104/* Hardware access */
87 105
88static int set_lcd_level(int level) 106static int set_lcd_level(int level)
@@ -139,6 +157,35 @@ static int set_auto_brightness(int enable)
139 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1); 157 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1);
140} 158}
141 159
160static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
161{
162 int status;
163 u8 wdata = 0, rdata;
164 int result;
165
166 if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
167 return -EINVAL;
168
169 /* read current device state */
170 result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
171 if (result < 0)
172 return -EINVAL;
173
174 if (!!(rdata & mask) != status) {
175 /* reverse device bit */
176 if (rdata & mask)
177 wdata = rdata & ~mask;
178 else
179 wdata = rdata | mask;
180
181 result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);
182 if (result < 0)
183 return -EINVAL;
184 }
185
186 return count;
187}
188
142static int get_wireless_state(int *wlan, int *bluetooth) 189static int get_wireless_state(int *wlan, int *bluetooth)
143{ 190{
144 u8 wdata = 0, rdata; 191 u8 wdata = 0, rdata;
@@ -215,6 +262,12 @@ static ssize_t show_wlan(struct device *dev,
215 return sprintf(buf, "%i\n", enabled); 262 return sprintf(buf, "%i\n", enabled);
216} 263}
217 264
265static ssize_t store_wlan(struct device *dev,
266 struct device_attribute *attr, const char *buf, size_t count)
267{
268 return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK);
269}
270
218static ssize_t show_bluetooth(struct device *dev, 271static ssize_t show_bluetooth(struct device *dev,
219 struct device_attribute *attr, char *buf) 272 struct device_attribute *attr, char *buf)
220{ 273{
@@ -233,6 +286,12 @@ static ssize_t show_bluetooth(struct device *dev,
233 return sprintf(buf, "%i\n", enabled); 286 return sprintf(buf, "%i\n", enabled);
234} 287}
235 288
289static ssize_t store_bluetooth(struct device *dev,
290 struct device_attribute *attr, const char *buf, size_t count)
291{
292 return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK);
293}
294
236static ssize_t show_threeg(struct device *dev, 295static ssize_t show_threeg(struct device *dev,
237 struct device_attribute *attr, char *buf) 296 struct device_attribute *attr, char *buf)
238{ 297{
@@ -250,6 +309,12 @@ static ssize_t show_threeg(struct device *dev,
250 return sprintf(buf, "%i\n", threeg_s); 309 return sprintf(buf, "%i\n", threeg_s);
251} 310}
252 311
312static ssize_t store_threeg(struct device *dev,
313 struct device_attribute *attr, const char *buf, size_t count)
314{
315 return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK);
316}
317
253static ssize_t show_lcd_level(struct device *dev, 318static ssize_t show_lcd_level(struct device *dev,
254 struct device_attribute *attr, char *buf) 319 struct device_attribute *attr, char *buf)
255{ 320{
@@ -387,6 +452,169 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
387 { } 452 { }
388}; 453};
389 454
455static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
456 {
457 .ident = "MSI N034",
458 .matches = {
459 DMI_MATCH(DMI_SYS_VENDOR,
460 "MICRO-STAR INTERNATIONAL CO., LTD"),
461 DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"),
462 DMI_MATCH(DMI_CHASSIS_VENDOR,
463 "MICRO-STAR INTERNATIONAL CO., LTD")
464 },
465 .callback = dmi_check_cb
466 },
467 { }
468};
469
470static int rfkill_bluetooth_set(void *data, bool blocked)
471{
472 /* Do something with blocked...*/
473 /*
474 * blocked == false is on
475 * blocked == true is off
476 */
477 if (blocked)
478 set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
479 else
480 set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
481
482 return 0;
483}
484
485static int rfkill_wlan_set(void *data, bool blocked)
486{
487 if (blocked)
488 set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK);
489 else
490 set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK);
491
492 return 0;
493}
494
495static int rfkill_threeg_set(void *data, bool blocked)
496{
497 if (blocked)
498 set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK);
499 else
500 set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK);
501
502 return 0;
503}
504
505static struct rfkill_ops rfkill_bluetooth_ops = {
506 .set_block = rfkill_bluetooth_set
507};
508
509static struct rfkill_ops rfkill_wlan_ops = {
510 .set_block = rfkill_wlan_set
511};
512
513static struct rfkill_ops rfkill_threeg_ops = {
514 .set_block = rfkill_threeg_set
515};
516
517static void rfkill_cleanup(void)
518{
519 if (rfk_bluetooth) {
520 rfkill_unregister(rfk_bluetooth);
521 rfkill_destroy(rfk_bluetooth);
522 }
523
524 if (rfk_threeg) {
525 rfkill_unregister(rfk_threeg);
526 rfkill_destroy(rfk_threeg);
527 }
528
529 if (rfk_wlan) {
530 rfkill_unregister(rfk_wlan);
531 rfkill_destroy(rfk_wlan);
532 }
533}
534
535static int rfkill_init(struct platform_device *sdev)
536{
537 /* add rfkill */
538 int retval;
539
540 rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev,
541 RFKILL_TYPE_BLUETOOTH,
542 &rfkill_bluetooth_ops, NULL);
543 if (!rfk_bluetooth) {
544 retval = -ENOMEM;
545 goto err_bluetooth;
546 }
547 retval = rfkill_register(rfk_bluetooth);
548 if (retval)
549 goto err_bluetooth;
550
551 rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN,
552 &rfkill_wlan_ops, NULL);
553 if (!rfk_wlan) {
554 retval = -ENOMEM;
555 goto err_wlan;
556 }
557 retval = rfkill_register(rfk_wlan);
558 if (retval)
559 goto err_wlan;
560
561 rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev, RFKILL_TYPE_WWAN,
562 &rfkill_threeg_ops, NULL);
563 if (!rfk_threeg) {
564 retval = -ENOMEM;
565 goto err_threeg;
566 }
567 retval = rfkill_register(rfk_threeg);
568 if (retval)
569 goto err_threeg;
570
571 return 0;
572
573err_threeg:
574 rfkill_destroy(rfk_threeg);
575 if (rfk_wlan)
576 rfkill_unregister(rfk_wlan);
577err_wlan:
578 rfkill_destroy(rfk_wlan);
579 if (rfk_bluetooth)
580 rfkill_unregister(rfk_bluetooth);
581err_bluetooth:
582 rfkill_destroy(rfk_bluetooth);
583
584 return retval;
585}
586
587static int load_scm_model_init(struct platform_device *sdev)
588{
589 u8 data;
590 int result;
591
592 /* allow userland write sysfs file */
593 dev_attr_bluetooth.store = store_bluetooth;
594 dev_attr_wlan.store = store_wlan;
595 dev_attr_threeg.store = store_threeg;
596 dev_attr_bluetooth.attr.mode |= S_IWUSR;
597 dev_attr_wlan.attr.mode |= S_IWUSR;
598 dev_attr_threeg.attr.mode |= S_IWUSR;
599
600 /* disable hardware control by fn key */
601 result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
602 if (result < 0)
603 return result;
604
605 result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
606 data | MSI_STANDARD_EC_SCM_LOAD_MASK);
607 if (result < 0)
608 return result;
609
610 /* initial rfkill */
611 result = rfkill_init(sdev);
612 if (result < 0)
613 return result;
614
615 return 0;
616}
617
390static int __init msi_init(void) 618static int __init msi_init(void)
391{ 619{
392 int ret; 620 int ret;
@@ -397,6 +625,9 @@ static int __init msi_init(void)
397 if (force || dmi_check_system(msi_dmi_table)) 625 if (force || dmi_check_system(msi_dmi_table))
398 old_ec_model = 1; 626 old_ec_model = 1;
399 627
628 if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table))
629 load_scm_model = 1;
630
400 if (auto_brightness < 0 || auto_brightness > 2) 631 if (auto_brightness < 0 || auto_brightness > 2)
401 return -EINVAL; 632 return -EINVAL;
402 633
@@ -429,6 +660,11 @@ static int __init msi_init(void)
429 if (ret) 660 if (ret)
430 goto fail_platform_device1; 661 goto fail_platform_device1;
431 662
663 if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
664 ret = -EINVAL;
665 goto fail_platform_device1;
666 }
667
432 ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group); 668 ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group);
433 if (ret) 669 if (ret)
434 goto fail_platform_device2; 670 goto fail_platform_device2;
@@ -479,6 +715,8 @@ static void __exit msi_cleanup(void)
479 platform_driver_unregister(&msipf_driver); 715 platform_driver_unregister(&msipf_driver);
480 backlight_device_unregister(msibl_device); 716 backlight_device_unregister(msibl_device);
481 717
718 rfkill_cleanup();
719
482 /* Enable automatic brightness control again */ 720 /* Enable automatic brightness control again */
483 if (auto_brightness != 2) 721 if (auto_brightness != 2)
484 set_auto_brightness(1); 722 set_auto_brightness(1);