aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/msi-laptop.c
diff options
context:
space:
mode:
authorLee, Chun-Yi <jlee@novell.com>2010-01-21 11:15:59 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2010-03-07 20:04:53 -0500
commit472ea12d1a265c868e1605abb59bf21f595eedfc (patch)
tree423796857593af71f52b60ce43a45548e6321370 /drivers/platform/x86/msi-laptop.c
parentfc0dc4c926e6b7afe680aecf809f48eef3faa182 (diff)
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>
Diffstat (limited to 'drivers/platform/x86/msi-laptop.c')
-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);