Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*-*-linux-c-*-*/
0003 
0004 /*
0005   Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
0006 
0007  */
0008 
0009 /*
0010  * msi-laptop.c - MSI S270 laptop support. This laptop is sold under
0011  * various brands, including "Cytron/TCM/Medion/Tchibo MD96100".
0012  *
0013  * Driver also supports S271, S420 models.
0014  *
0015  * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/:
0016  *
0017  *   lcd_level - Screen brightness: contains a single integer in the
0018  *   range 0..8. (rw)
0019  *
0020  *   auto_brightness - Enable automatic brightness control: contains
0021  *   either 0 or 1. If set to 1 the hardware adjusts the screen
0022  *   brightness automatically when the power cord is
0023  *   plugged/unplugged. (rw)
0024  *
0025  *   wlan - WLAN subsystem enabled: contains either 0 or 1. (ro)
0026  *
0027  *   bluetooth - Bluetooth subsystem enabled: contains either 0 or 1
0028  *   Please note that this file is constantly 0 if no Bluetooth
0029  *   hardware is available. (ro)
0030  *
0031  * In addition to these platform device attributes the driver
0032  * registers itself in the Linux backlight control subsystem and is
0033  * available to userspace under /sys/class/backlight/msi-laptop-bl/.
0034  *
0035  * This driver might work on other laptops produced by MSI. If you
0036  * want to try it you can pass force=1 as argument to the module which
0037  * will force it to load even when the DMI data doesn't identify the
0038  * laptop as MSI S270. YMMV.
0039  */
0040 
0041 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0042 
0043 #include <linux/module.h>
0044 #include <linux/kernel.h>
0045 #include <linux/init.h>
0046 #include <linux/acpi.h>
0047 #include <linux/dmi.h>
0048 #include <linux/backlight.h>
0049 #include <linux/platform_device.h>
0050 #include <linux/rfkill.h>
0051 #include <linux/i8042.h>
0052 #include <linux/input.h>
0053 #include <linux/input/sparse-keymap.h>
0054 #include <acpi/video.h>
0055 
0056 #define MSI_DRIVER_VERSION "0.5"
0057 
0058 #define MSI_LCD_LEVEL_MAX 9
0059 
0060 #define MSI_EC_COMMAND_WIRELESS 0x10
0061 #define MSI_EC_COMMAND_LCD_LEVEL 0x11
0062 
0063 #define MSI_STANDARD_EC_COMMAND_ADDRESS 0x2e
0064 #define MSI_STANDARD_EC_BLUETOOTH_MASK  (1 << 0)
0065 #define MSI_STANDARD_EC_WEBCAM_MASK (1 << 1)
0066 #define MSI_STANDARD_EC_WLAN_MASK   (1 << 3)
0067 #define MSI_STANDARD_EC_3G_MASK     (1 << 4)
0068 
0069 /* For set SCM load flag to disable BIOS fn key */
0070 #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS    0x2d
0071 #define MSI_STANDARD_EC_SCM_LOAD_MASK       (1 << 0)
0072 
0073 #define MSI_STANDARD_EC_FUNCTIONS_ADDRESS   0xe4
0074 /* Power LED is orange - Turbo mode */
0075 #define MSI_STANDARD_EC_TURBO_MASK      (1 << 1)
0076 /* Power LED is green - ECO mode */
0077 #define MSI_STANDARD_EC_ECO_MASK        (1 << 3)
0078 /* Touchpad is turned on */
0079 #define MSI_STANDARD_EC_TOUCHPAD_MASK       (1 << 4)
0080 /* If this bit != bit 1, turbo mode can't be toggled */
0081 #define MSI_STANDARD_EC_TURBO_COOLDOWN_MASK (1 << 7)
0082 
0083 #define MSI_STANDARD_EC_FAN_ADDRESS     0x33
0084 /* If zero, fan rotates at maximal speed */
0085 #define MSI_STANDARD_EC_AUTOFAN_MASK        (1 << 0)
0086 
0087 #ifdef CONFIG_PM_SLEEP
0088 static int msi_laptop_resume(struct device *device);
0089 #endif
0090 static SIMPLE_DEV_PM_OPS(msi_laptop_pm, NULL, msi_laptop_resume);
0091 
0092 #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS  0x2f
0093 
0094 static bool force;
0095 module_param(force, bool, 0);
0096 MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
0097 
0098 static int auto_brightness;
0099 module_param(auto_brightness, int, 0);
0100 MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
0101 
0102 static const struct key_entry msi_laptop_keymap[] = {
0103     {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} },  /* Touch Pad On */
0104     {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */
0105     {KE_END, 0}
0106 };
0107 
0108 static struct input_dev *msi_laptop_input_dev;
0109 
0110 static int wlan_s, bluetooth_s, threeg_s;
0111 static int threeg_exists;
0112 static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
0113 
0114 /* MSI laptop quirks */
0115 struct quirk_entry {
0116     bool old_ec_model;
0117 
0118     /* Some MSI 3G netbook only have one fn key to control
0119      * Wlan/Bluetooth/3G, those netbook will load the SCM (windows app) to
0120      * disable the original Wlan/Bluetooth control by BIOS when user press
0121      * fn key, then control Wlan/Bluetooth/3G by SCM (software control by
0122      * OS). Without SCM, user cann't on/off 3G module on those 3G netbook.
0123      * On Linux, msi-laptop driver will do the same thing to disable the
0124      * original BIOS control, then might need use HAL or other userland
0125      * application to do the software control that simulate with SCM.
0126      * e.g. MSI N034 netbook
0127      */
0128     bool load_scm_model;
0129 
0130     /* Some MSI laptops need delay before reading from EC */
0131     bool ec_delay;
0132 
0133     /* Some MSI Wind netbooks (e.g. MSI Wind U100) need loading SCM to get
0134      * some features working (e.g. ECO mode), but we cannot change
0135      * Wlan/Bluetooth state in software and we can only read its state.
0136      */
0137     bool ec_read_only;
0138 };
0139 
0140 static struct quirk_entry *quirks;
0141 
0142 /* Hardware access */
0143 
0144 static int set_lcd_level(int level)
0145 {
0146     u8 buf[2];
0147 
0148     if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
0149         return -EINVAL;
0150 
0151     buf[0] = 0x80;
0152     buf[1] = (u8) (level*31);
0153 
0154     return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf),
0155                   NULL, 0);
0156 }
0157 
0158 static int get_lcd_level(void)
0159 {
0160     u8 wdata = 0, rdata;
0161     int result;
0162 
0163     result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
0164                 &rdata, 1);
0165     if (result < 0)
0166         return result;
0167 
0168     return (int) rdata / 31;
0169 }
0170 
0171 static int get_auto_brightness(void)
0172 {
0173     u8 wdata = 4, rdata;
0174     int result;
0175 
0176     result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
0177                 &rdata, 1);
0178     if (result < 0)
0179         return result;
0180 
0181     return !!(rdata & 8);
0182 }
0183 
0184 static int set_auto_brightness(int enable)
0185 {
0186     u8 wdata[2], rdata;
0187     int result;
0188 
0189     wdata[0] = 4;
0190 
0191     result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1,
0192                 &rdata, 1);
0193     if (result < 0)
0194         return result;
0195 
0196     wdata[0] = 0x84;
0197     wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
0198 
0199     return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2,
0200                   NULL, 0);
0201 }
0202 
0203 static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
0204 {
0205     int status;
0206     u8 wdata = 0, rdata;
0207     int result;
0208 
0209     if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
0210         return -EINVAL;
0211 
0212     if (quirks->ec_read_only)
0213         return -EOPNOTSUPP;
0214 
0215     /* read current device state */
0216     result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
0217     if (result < 0)
0218         return result;
0219 
0220     if (!!(rdata & mask) != status) {
0221         /* reverse device bit */
0222         if (rdata & mask)
0223             wdata = rdata & ~mask;
0224         else
0225             wdata = rdata | mask;
0226 
0227         result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);
0228         if (result < 0)
0229             return result;
0230     }
0231 
0232     return count;
0233 }
0234 
0235 static int get_wireless_state(int *wlan, int *bluetooth)
0236 {
0237     u8 wdata = 0, rdata;
0238     int result;
0239 
0240     result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1);
0241     if (result < 0)
0242         return result;
0243 
0244     if (wlan)
0245         *wlan = !!(rdata & 8);
0246 
0247     if (bluetooth)
0248         *bluetooth = !!(rdata & 128);
0249 
0250     return 0;
0251 }
0252 
0253 static int get_wireless_state_ec_standard(void)
0254 {
0255     u8 rdata;
0256     int result;
0257 
0258     result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
0259     if (result < 0)
0260         return result;
0261 
0262     wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK);
0263 
0264     bluetooth_s = !!(rdata & MSI_STANDARD_EC_BLUETOOTH_MASK);
0265 
0266     threeg_s = !!(rdata & MSI_STANDARD_EC_3G_MASK);
0267 
0268     return 0;
0269 }
0270 
0271 static int get_threeg_exists(void)
0272 {
0273     u8 rdata;
0274     int result;
0275 
0276     result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata);
0277     if (result < 0)
0278         return result;
0279 
0280     threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK);
0281 
0282     return 0;
0283 }
0284 
0285 /* Backlight device stuff */
0286 
0287 static int bl_get_brightness(struct backlight_device *b)
0288 {
0289     return get_lcd_level();
0290 }
0291 
0292 
0293 static int bl_update_status(struct backlight_device *b)
0294 {
0295     return set_lcd_level(b->props.brightness);
0296 }
0297 
0298 static const struct backlight_ops msibl_ops = {
0299     .get_brightness = bl_get_brightness,
0300     .update_status  = bl_update_status,
0301 };
0302 
0303 static struct backlight_device *msibl_device;
0304 
0305 /* Platform device */
0306 
0307 static ssize_t show_wlan(struct device *dev,
0308     struct device_attribute *attr, char *buf)
0309 {
0310 
0311     int ret, enabled = 0;
0312 
0313     if (quirks->old_ec_model) {
0314         ret = get_wireless_state(&enabled, NULL);
0315     } else {
0316         ret = get_wireless_state_ec_standard();
0317         enabled = wlan_s;
0318     }
0319     if (ret < 0)
0320         return ret;
0321 
0322     return sprintf(buf, "%i\n", enabled);
0323 }
0324 
0325 static ssize_t store_wlan(struct device *dev,
0326     struct device_attribute *attr, const char *buf, size_t count)
0327 {
0328     return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK);
0329 }
0330 
0331 static ssize_t show_bluetooth(struct device *dev,
0332     struct device_attribute *attr, char *buf)
0333 {
0334 
0335     int ret, enabled = 0;
0336 
0337     if (quirks->old_ec_model) {
0338         ret = get_wireless_state(NULL, &enabled);
0339     } else {
0340         ret = get_wireless_state_ec_standard();
0341         enabled = bluetooth_s;
0342     }
0343     if (ret < 0)
0344         return ret;
0345 
0346     return sprintf(buf, "%i\n", enabled);
0347 }
0348 
0349 static ssize_t store_bluetooth(struct device *dev,
0350     struct device_attribute *attr, const char *buf, size_t count)
0351 {
0352     return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK);
0353 }
0354 
0355 static ssize_t show_threeg(struct device *dev,
0356     struct device_attribute *attr, char *buf)
0357 {
0358 
0359     int ret;
0360 
0361     /* old msi ec not support 3G */
0362     if (quirks->old_ec_model)
0363         return -ENODEV;
0364 
0365     ret = get_wireless_state_ec_standard();
0366     if (ret < 0)
0367         return ret;
0368 
0369     return sprintf(buf, "%i\n", threeg_s);
0370 }
0371 
0372 static ssize_t store_threeg(struct device *dev,
0373     struct device_attribute *attr, const char *buf, size_t count)
0374 {
0375     return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK);
0376 }
0377 
0378 static ssize_t show_lcd_level(struct device *dev,
0379     struct device_attribute *attr, char *buf)
0380 {
0381 
0382     int ret;
0383 
0384     ret = get_lcd_level();
0385     if (ret < 0)
0386         return ret;
0387 
0388     return sprintf(buf, "%i\n", ret);
0389 }
0390 
0391 static ssize_t store_lcd_level(struct device *dev,
0392     struct device_attribute *attr, const char *buf, size_t count)
0393 {
0394 
0395     int level, ret;
0396 
0397     if (sscanf(buf, "%i", &level) != 1 ||
0398         (level < 0 || level >= MSI_LCD_LEVEL_MAX))
0399         return -EINVAL;
0400 
0401     ret = set_lcd_level(level);
0402     if (ret < 0)
0403         return ret;
0404 
0405     return count;
0406 }
0407 
0408 static ssize_t show_auto_brightness(struct device *dev,
0409     struct device_attribute *attr, char *buf)
0410 {
0411 
0412     int ret;
0413 
0414     ret = get_auto_brightness();
0415     if (ret < 0)
0416         return ret;
0417 
0418     return sprintf(buf, "%i\n", ret);
0419 }
0420 
0421 static ssize_t store_auto_brightness(struct device *dev,
0422     struct device_attribute *attr, const char *buf, size_t count)
0423 {
0424 
0425     int enable, ret;
0426 
0427     if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
0428         return -EINVAL;
0429 
0430     ret = set_auto_brightness(enable);
0431     if (ret < 0)
0432         return ret;
0433 
0434     return count;
0435 }
0436 
0437 static ssize_t show_touchpad(struct device *dev,
0438     struct device_attribute *attr, char *buf)
0439 {
0440 
0441     u8 rdata;
0442     int result;
0443 
0444     result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
0445     if (result < 0)
0446         return result;
0447 
0448     return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK));
0449 }
0450 
0451 static ssize_t show_turbo(struct device *dev,
0452     struct device_attribute *attr, char *buf)
0453 {
0454 
0455     u8 rdata;
0456     int result;
0457 
0458     result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
0459     if (result < 0)
0460         return result;
0461 
0462     return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TURBO_MASK));
0463 }
0464 
0465 static ssize_t show_eco(struct device *dev,
0466     struct device_attribute *attr, char *buf)
0467 {
0468 
0469     u8 rdata;
0470     int result;
0471 
0472     result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
0473     if (result < 0)
0474         return result;
0475 
0476     return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_ECO_MASK));
0477 }
0478 
0479 static ssize_t show_turbo_cooldown(struct device *dev,
0480     struct device_attribute *attr, char *buf)
0481 {
0482 
0483     u8 rdata;
0484     int result;
0485 
0486     result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
0487     if (result < 0)
0488         return result;
0489 
0490     return sprintf(buf, "%i\n", (!!(rdata & MSI_STANDARD_EC_TURBO_MASK)) |
0491         (!!(rdata & MSI_STANDARD_EC_TURBO_COOLDOWN_MASK) << 1));
0492 }
0493 
0494 static ssize_t show_auto_fan(struct device *dev,
0495     struct device_attribute *attr, char *buf)
0496 {
0497 
0498     u8 rdata;
0499     int result;
0500 
0501     result = ec_read(MSI_STANDARD_EC_FAN_ADDRESS, &rdata);
0502     if (result < 0)
0503         return result;
0504 
0505     return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_AUTOFAN_MASK));
0506 }
0507 
0508 static ssize_t store_auto_fan(struct device *dev,
0509     struct device_attribute *attr, const char *buf, size_t count)
0510 {
0511 
0512     int enable, result;
0513 
0514     if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
0515         return -EINVAL;
0516 
0517     result = ec_write(MSI_STANDARD_EC_FAN_ADDRESS, enable);
0518     if (result < 0)
0519         return result;
0520 
0521     return count;
0522 }
0523 
0524 static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
0525 static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness,
0526            store_auto_brightness);
0527 static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
0528 static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
0529 static DEVICE_ATTR(threeg, 0444, show_threeg, NULL);
0530 static DEVICE_ATTR(touchpad, 0444, show_touchpad, NULL);
0531 static DEVICE_ATTR(turbo_mode, 0444, show_turbo, NULL);
0532 static DEVICE_ATTR(eco_mode, 0444, show_eco, NULL);
0533 static DEVICE_ATTR(turbo_cooldown, 0444, show_turbo_cooldown, NULL);
0534 static DEVICE_ATTR(auto_fan, 0644, show_auto_fan, store_auto_fan);
0535 
0536 static struct attribute *msipf_attributes[] = {
0537     &dev_attr_bluetooth.attr,
0538     &dev_attr_wlan.attr,
0539     &dev_attr_touchpad.attr,
0540     &dev_attr_turbo_mode.attr,
0541     &dev_attr_eco_mode.attr,
0542     &dev_attr_turbo_cooldown.attr,
0543     &dev_attr_auto_fan.attr,
0544     NULL
0545 };
0546 
0547 static struct attribute *msipf_old_attributes[] = {
0548     &dev_attr_lcd_level.attr,
0549     &dev_attr_auto_brightness.attr,
0550     NULL
0551 };
0552 
0553 static const struct attribute_group msipf_attribute_group = {
0554     .attrs = msipf_attributes
0555 };
0556 
0557 static const struct attribute_group msipf_old_attribute_group = {
0558     .attrs = msipf_old_attributes
0559 };
0560 
0561 static struct platform_driver msipf_driver = {
0562     .driver = {
0563         .name = "msi-laptop-pf",
0564         .pm = &msi_laptop_pm,
0565     },
0566 };
0567 
0568 static struct platform_device *msipf_device;
0569 
0570 /* Initialization */
0571 
0572 static struct quirk_entry quirk_old_ec_model = {
0573     .old_ec_model = true,
0574 };
0575 
0576 static struct quirk_entry quirk_load_scm_model = {
0577     .load_scm_model = true,
0578     .ec_delay = true,
0579 };
0580 
0581 static struct quirk_entry quirk_load_scm_ro_model = {
0582     .load_scm_model = true,
0583     .ec_read_only = true,
0584 };
0585 
0586 static int dmi_check_cb(const struct dmi_system_id *dmi)
0587 {
0588     pr_info("Identified laptop model '%s'\n", dmi->ident);
0589 
0590     quirks = dmi->driver_data;
0591 
0592     return 1;
0593 }
0594 
0595 static const struct dmi_system_id msi_dmi_table[] __initconst = {
0596     {
0597         .ident = "MSI S270",
0598         .matches = {
0599             DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
0600             DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
0601             DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
0602             DMI_MATCH(DMI_CHASSIS_VENDOR,
0603                   "MICRO-STAR INT'L CO.,LTD")
0604         },
0605         .driver_data = &quirk_old_ec_model,
0606         .callback = dmi_check_cb
0607     },
0608     {
0609         .ident = "MSI S271",
0610         .matches = {
0611             DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
0612             DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
0613             DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
0614             DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
0615         },
0616         .driver_data = &quirk_old_ec_model,
0617         .callback = dmi_check_cb
0618     },
0619     {
0620         .ident = "MSI S420",
0621         .matches = {
0622             DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
0623             DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
0624             DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
0625             DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
0626         },
0627         .driver_data = &quirk_old_ec_model,
0628         .callback = dmi_check_cb
0629     },
0630     {
0631         .ident = "Medion MD96100",
0632         .matches = {
0633             DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
0634             DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
0635             DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
0636             DMI_MATCH(DMI_CHASSIS_VENDOR,
0637                   "MICRO-STAR INT'L CO.,LTD")
0638         },
0639         .driver_data = &quirk_old_ec_model,
0640         .callback = dmi_check_cb
0641     },
0642     {
0643         .ident = "MSI N034",
0644         .matches = {
0645             DMI_MATCH(DMI_SYS_VENDOR,
0646                 "MICRO-STAR INTERNATIONAL CO., LTD"),
0647             DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"),
0648             DMI_MATCH(DMI_CHASSIS_VENDOR,
0649             "MICRO-STAR INTERNATIONAL CO., LTD")
0650         },
0651         .driver_data = &quirk_load_scm_model,
0652         .callback = dmi_check_cb
0653     },
0654     {
0655         .ident = "MSI N051",
0656         .matches = {
0657             DMI_MATCH(DMI_SYS_VENDOR,
0658                 "MICRO-STAR INTERNATIONAL CO., LTD"),
0659             DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"),
0660             DMI_MATCH(DMI_CHASSIS_VENDOR,
0661             "MICRO-STAR INTERNATIONAL CO., LTD")
0662         },
0663         .driver_data = &quirk_load_scm_model,
0664         .callback = dmi_check_cb
0665     },
0666     {
0667         .ident = "MSI N014",
0668         .matches = {
0669             DMI_MATCH(DMI_SYS_VENDOR,
0670                 "MICRO-STAR INTERNATIONAL CO., LTD"),
0671             DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),
0672         },
0673         .driver_data = &quirk_load_scm_model,
0674         .callback = dmi_check_cb
0675     },
0676     {
0677         .ident = "MSI CR620",
0678         .matches = {
0679             DMI_MATCH(DMI_SYS_VENDOR,
0680                 "Micro-Star International"),
0681             DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),
0682         },
0683         .driver_data = &quirk_load_scm_model,
0684         .callback = dmi_check_cb
0685     },
0686     {
0687         .ident = "MSI U270",
0688         .matches = {
0689             DMI_MATCH(DMI_SYS_VENDOR,
0690                 "Micro-Star International Co., Ltd."),
0691             DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"),
0692         },
0693         .driver_data = &quirk_load_scm_model,
0694         .callback = dmi_check_cb
0695     },
0696     {
0697         .ident = "MSI U90/U100",
0698         .matches = {
0699             DMI_MATCH(DMI_SYS_VENDOR,
0700                 "MICRO-STAR INTERNATIONAL CO., LTD"),
0701             DMI_MATCH(DMI_PRODUCT_NAME, "U90/U100"),
0702         },
0703         .driver_data = &quirk_load_scm_ro_model,
0704         .callback = dmi_check_cb
0705     },
0706     { }
0707 };
0708 
0709 static int rfkill_bluetooth_set(void *data, bool blocked)
0710 {
0711     /* Do something with blocked...*/
0712     /*
0713      * blocked == false is on
0714      * blocked == true is off
0715      */
0716     int result = set_device_state(blocked ? "0" : "1", 0,
0717             MSI_STANDARD_EC_BLUETOOTH_MASK);
0718 
0719     return min(result, 0);
0720 }
0721 
0722 static int rfkill_wlan_set(void *data, bool blocked)
0723 {
0724     int result = set_device_state(blocked ? "0" : "1", 0,
0725             MSI_STANDARD_EC_WLAN_MASK);
0726 
0727     return min(result, 0);
0728 }
0729 
0730 static int rfkill_threeg_set(void *data, bool blocked)
0731 {
0732     int result = set_device_state(blocked ? "0" : "1", 0,
0733             MSI_STANDARD_EC_3G_MASK);
0734 
0735     return min(result, 0);
0736 }
0737 
0738 static const struct rfkill_ops rfkill_bluetooth_ops = {
0739     .set_block = rfkill_bluetooth_set
0740 };
0741 
0742 static const struct rfkill_ops rfkill_wlan_ops = {
0743     .set_block = rfkill_wlan_set
0744 };
0745 
0746 static const struct rfkill_ops rfkill_threeg_ops = {
0747     .set_block = rfkill_threeg_set
0748 };
0749 
0750 static void rfkill_cleanup(void)
0751 {
0752     if (rfk_bluetooth) {
0753         rfkill_unregister(rfk_bluetooth);
0754         rfkill_destroy(rfk_bluetooth);
0755     }
0756 
0757     if (rfk_threeg) {
0758         rfkill_unregister(rfk_threeg);
0759         rfkill_destroy(rfk_threeg);
0760     }
0761 
0762     if (rfk_wlan) {
0763         rfkill_unregister(rfk_wlan);
0764         rfkill_destroy(rfk_wlan);
0765     }
0766 }
0767 
0768 static bool msi_rfkill_set_state(struct rfkill *rfkill, bool blocked)
0769 {
0770     if (quirks->ec_read_only)
0771         return rfkill_set_hw_state(rfkill, blocked);
0772     else
0773         return rfkill_set_sw_state(rfkill, blocked);
0774 }
0775 
0776 static void msi_update_rfkill(struct work_struct *ignored)
0777 {
0778     get_wireless_state_ec_standard();
0779 
0780     if (rfk_wlan)
0781         msi_rfkill_set_state(rfk_wlan, !wlan_s);
0782     if (rfk_bluetooth)
0783         msi_rfkill_set_state(rfk_bluetooth, !bluetooth_s);
0784     if (rfk_threeg)
0785         msi_rfkill_set_state(rfk_threeg, !threeg_s);
0786 }
0787 static DECLARE_DELAYED_WORK(msi_rfkill_dwork, msi_update_rfkill);
0788 static DECLARE_WORK(msi_rfkill_work, msi_update_rfkill);
0789 
0790 static void msi_send_touchpad_key(struct work_struct *ignored)
0791 {
0792     u8 rdata;
0793     int result;
0794 
0795     result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
0796     if (result < 0)
0797         return;
0798 
0799     sparse_keymap_report_event(msi_laptop_input_dev,
0800         (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
0801         KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
0802 }
0803 static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key);
0804 static DECLARE_WORK(msi_touchpad_work, msi_send_touchpad_key);
0805 
0806 static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
0807                 struct serio *port)
0808 {
0809     static bool extended;
0810 
0811     if (str & I8042_STR_AUXDATA)
0812         return false;
0813 
0814     /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/
0815     if (unlikely(data == 0xe0)) {
0816         extended = true;
0817         return false;
0818     } else if (unlikely(extended)) {
0819         extended = false;
0820         switch (data) {
0821         case 0xE4:
0822             if (quirks->ec_delay) {
0823                 schedule_delayed_work(&msi_touchpad_dwork,
0824                     round_jiffies_relative(0.5 * HZ));
0825             } else
0826                 schedule_work(&msi_touchpad_work);
0827             break;
0828         case 0x54:
0829         case 0x62:
0830         case 0x76:
0831             if (quirks->ec_delay) {
0832                 schedule_delayed_work(&msi_rfkill_dwork,
0833                     round_jiffies_relative(0.5 * HZ));
0834             } else
0835                 schedule_work(&msi_rfkill_work);
0836             break;
0837         }
0838     }
0839 
0840     return false;
0841 }
0842 
0843 static void msi_init_rfkill(struct work_struct *ignored)
0844 {
0845     if (rfk_wlan) {
0846         rfkill_set_sw_state(rfk_wlan, !wlan_s);
0847         rfkill_wlan_set(NULL, !wlan_s);
0848     }
0849     if (rfk_bluetooth) {
0850         rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
0851         rfkill_bluetooth_set(NULL, !bluetooth_s);
0852     }
0853     if (rfk_threeg) {
0854         rfkill_set_sw_state(rfk_threeg, !threeg_s);
0855         rfkill_threeg_set(NULL, !threeg_s);
0856     }
0857 }
0858 static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill);
0859 
0860 static int rfkill_init(struct platform_device *sdev)
0861 {
0862     /* add rfkill */
0863     int retval;
0864 
0865     /* keep the hardware wireless state */
0866     get_wireless_state_ec_standard();
0867 
0868     rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev,
0869                 RFKILL_TYPE_BLUETOOTH,
0870                 &rfkill_bluetooth_ops, NULL);
0871     if (!rfk_bluetooth) {
0872         retval = -ENOMEM;
0873         goto err_bluetooth;
0874     }
0875     retval = rfkill_register(rfk_bluetooth);
0876     if (retval)
0877         goto err_bluetooth;
0878 
0879     rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN,
0880                 &rfkill_wlan_ops, NULL);
0881     if (!rfk_wlan) {
0882         retval = -ENOMEM;
0883         goto err_wlan;
0884     }
0885     retval = rfkill_register(rfk_wlan);
0886     if (retval)
0887         goto err_wlan;
0888 
0889     if (threeg_exists) {
0890         rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev,
0891                 RFKILL_TYPE_WWAN, &rfkill_threeg_ops, NULL);
0892         if (!rfk_threeg) {
0893             retval = -ENOMEM;
0894             goto err_threeg;
0895         }
0896         retval = rfkill_register(rfk_threeg);
0897         if (retval)
0898             goto err_threeg;
0899     }
0900 
0901     /* schedule to run rfkill state initial */
0902     if (quirks->ec_delay) {
0903         schedule_delayed_work(&msi_rfkill_init,
0904             round_jiffies_relative(1 * HZ));
0905     } else
0906         schedule_work(&msi_rfkill_work);
0907 
0908     return 0;
0909 
0910 err_threeg:
0911     rfkill_destroy(rfk_threeg);
0912     if (rfk_wlan)
0913         rfkill_unregister(rfk_wlan);
0914 err_wlan:
0915     rfkill_destroy(rfk_wlan);
0916     if (rfk_bluetooth)
0917         rfkill_unregister(rfk_bluetooth);
0918 err_bluetooth:
0919     rfkill_destroy(rfk_bluetooth);
0920 
0921     return retval;
0922 }
0923 
0924 #ifdef CONFIG_PM_SLEEP
0925 static int msi_laptop_resume(struct device *device)
0926 {
0927     u8 data;
0928     int result;
0929 
0930     if (!quirks->load_scm_model)
0931         return 0;
0932 
0933     /* set load SCM to disable hardware control by fn key */
0934     result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
0935     if (result < 0)
0936         return result;
0937 
0938     result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
0939         data | MSI_STANDARD_EC_SCM_LOAD_MASK);
0940     if (result < 0)
0941         return result;
0942 
0943     return 0;
0944 }
0945 #endif
0946 
0947 static int __init msi_laptop_input_setup(void)
0948 {
0949     int err;
0950 
0951     msi_laptop_input_dev = input_allocate_device();
0952     if (!msi_laptop_input_dev)
0953         return -ENOMEM;
0954 
0955     msi_laptop_input_dev->name = "MSI Laptop hotkeys";
0956     msi_laptop_input_dev->phys = "msi-laptop/input0";
0957     msi_laptop_input_dev->id.bustype = BUS_HOST;
0958 
0959     err = sparse_keymap_setup(msi_laptop_input_dev,
0960         msi_laptop_keymap, NULL);
0961     if (err)
0962         goto err_free_dev;
0963 
0964     err = input_register_device(msi_laptop_input_dev);
0965     if (err)
0966         goto err_free_dev;
0967 
0968     return 0;
0969 
0970 err_free_dev:
0971     input_free_device(msi_laptop_input_dev);
0972     return err;
0973 }
0974 
0975 static int __init load_scm_model_init(struct platform_device *sdev)
0976 {
0977     u8 data;
0978     int result;
0979 
0980     if (!quirks->ec_read_only) {
0981         /* allow userland write sysfs file  */
0982         dev_attr_bluetooth.store = store_bluetooth;
0983         dev_attr_wlan.store = store_wlan;
0984         dev_attr_threeg.store = store_threeg;
0985         dev_attr_bluetooth.attr.mode |= S_IWUSR;
0986         dev_attr_wlan.attr.mode |= S_IWUSR;
0987         dev_attr_threeg.attr.mode |= S_IWUSR;
0988     }
0989 
0990     /* disable hardware control by fn key */
0991     result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
0992     if (result < 0)
0993         return result;
0994 
0995     result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
0996         data | MSI_STANDARD_EC_SCM_LOAD_MASK);
0997     if (result < 0)
0998         return result;
0999 
1000     /* initial rfkill */
1001     result = rfkill_init(sdev);
1002     if (result < 0)
1003         goto fail_rfkill;
1004 
1005     /* setup input device */
1006     result = msi_laptop_input_setup();
1007     if (result)
1008         goto fail_input;
1009 
1010     result = i8042_install_filter(msi_laptop_i8042_filter);
1011     if (result) {
1012         pr_err("Unable to install key filter\n");
1013         goto fail_filter;
1014     }
1015 
1016     return 0;
1017 
1018 fail_filter:
1019     input_unregister_device(msi_laptop_input_dev);
1020 
1021 fail_input:
1022     rfkill_cleanup();
1023 
1024 fail_rfkill:
1025 
1026     return result;
1027 
1028 }
1029 
1030 static int __init msi_init(void)
1031 {
1032     int ret;
1033 
1034     if (acpi_disabled)
1035         return -ENODEV;
1036 
1037     dmi_check_system(msi_dmi_table);
1038     if (!quirks)
1039         /* quirks may be NULL if no match in DMI table */
1040         quirks = &quirk_load_scm_model;
1041     if (force)
1042         quirks = &quirk_old_ec_model;
1043 
1044     if (!quirks->old_ec_model)
1045         get_threeg_exists();
1046 
1047     if (auto_brightness < 0 || auto_brightness > 2)
1048         return -EINVAL;
1049 
1050     /* Register backlight stuff */
1051 
1052     if (quirks->old_ec_model ||
1053         acpi_video_get_backlight_type() == acpi_backlight_vendor) {
1054         struct backlight_properties props;
1055         memset(&props, 0, sizeof(struct backlight_properties));
1056         props.type = BACKLIGHT_PLATFORM;
1057         props.max_brightness = MSI_LCD_LEVEL_MAX - 1;
1058         msibl_device = backlight_device_register("msi-laptop-bl", NULL,
1059                              NULL, &msibl_ops,
1060                              &props);
1061         if (IS_ERR(msibl_device))
1062             return PTR_ERR(msibl_device);
1063     }
1064 
1065     ret = platform_driver_register(&msipf_driver);
1066     if (ret)
1067         goto fail_backlight;
1068 
1069     /* Register platform stuff */
1070 
1071     msipf_device = platform_device_alloc("msi-laptop-pf", -1);
1072     if (!msipf_device) {
1073         ret = -ENOMEM;
1074         goto fail_platform_driver;
1075     }
1076 
1077     ret = platform_device_add(msipf_device);
1078     if (ret)
1079         goto fail_device_add;
1080 
1081     if (quirks->load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
1082         ret = -EINVAL;
1083         goto fail_scm_model_init;
1084     }
1085 
1086     ret = sysfs_create_group(&msipf_device->dev.kobj,
1087                  &msipf_attribute_group);
1088     if (ret)
1089         goto fail_create_group;
1090 
1091     if (!quirks->old_ec_model) {
1092         if (threeg_exists)
1093             ret = device_create_file(&msipf_device->dev,
1094                         &dev_attr_threeg);
1095         if (ret)
1096             goto fail_create_attr;
1097     } else {
1098         ret = sysfs_create_group(&msipf_device->dev.kobj,
1099                      &msipf_old_attribute_group);
1100         if (ret)
1101             goto fail_create_attr;
1102 
1103         /* Disable automatic brightness control by default because
1104          * this module was probably loaded to do brightness control in
1105          * software. */
1106 
1107         if (auto_brightness != 2)
1108             set_auto_brightness(auto_brightness);
1109     }
1110 
1111     pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n");
1112 
1113     return 0;
1114 
1115 fail_create_attr:
1116     sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
1117 fail_create_group:
1118     if (quirks->load_scm_model) {
1119         i8042_remove_filter(msi_laptop_i8042_filter);
1120         cancel_delayed_work_sync(&msi_rfkill_dwork);
1121         cancel_work_sync(&msi_rfkill_work);
1122         rfkill_cleanup();
1123     }
1124 fail_scm_model_init:
1125     platform_device_del(msipf_device);
1126 fail_device_add:
1127     platform_device_put(msipf_device);
1128 fail_platform_driver:
1129     platform_driver_unregister(&msipf_driver);
1130 fail_backlight:
1131     backlight_device_unregister(msibl_device);
1132 
1133     return ret;
1134 }
1135 
1136 static void __exit msi_cleanup(void)
1137 {
1138     if (quirks->load_scm_model) {
1139         i8042_remove_filter(msi_laptop_i8042_filter);
1140         input_unregister_device(msi_laptop_input_dev);
1141         cancel_delayed_work_sync(&msi_rfkill_dwork);
1142         cancel_work_sync(&msi_rfkill_work);
1143         rfkill_cleanup();
1144     }
1145 
1146     sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
1147     if (!quirks->old_ec_model && threeg_exists)
1148         device_remove_file(&msipf_device->dev, &dev_attr_threeg);
1149     platform_device_unregister(msipf_device);
1150     platform_driver_unregister(&msipf_driver);
1151     backlight_device_unregister(msibl_device);
1152 
1153     if (quirks->old_ec_model) {
1154         /* Enable automatic brightness control again */
1155         if (auto_brightness != 2)
1156             set_auto_brightness(1);
1157     }
1158 
1159     pr_info("driver unloaded\n");
1160 }
1161 
1162 module_init(msi_init);
1163 module_exit(msi_cleanup);
1164 
1165 MODULE_AUTHOR("Lennart Poettering");
1166 MODULE_DESCRIPTION("MSI Laptop Support");
1167 MODULE_VERSION(MSI_DRIVER_VERSION);
1168 MODULE_LICENSE("GPL");
1169 
1170 MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
1171 MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
1172 MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
1173 MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
1174 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*");
1175 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
1176 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
1177 MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");
1178 MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*");
1179 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnU90/U100:*");