cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

msi-laptop.c (28101B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*-*-linux-c-*-*/
      3
      4/*
      5  Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
      6
      7 */
      8
      9/*
     10 * msi-laptop.c - MSI S270 laptop support. This laptop is sold under
     11 * various brands, including "Cytron/TCM/Medion/Tchibo MD96100".
     12 *
     13 * Driver also supports S271, S420 models.
     14 *
     15 * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/:
     16 *
     17 *   lcd_level - Screen brightness: contains a single integer in the
     18 *   range 0..8. (rw)
     19 *
     20 *   auto_brightness - Enable automatic brightness control: contains
     21 *   either 0 or 1. If set to 1 the hardware adjusts the screen
     22 *   brightness automatically when the power cord is
     23 *   plugged/unplugged. (rw)
     24 *
     25 *   wlan - WLAN subsystem enabled: contains either 0 or 1. (ro)
     26 *
     27 *   bluetooth - Bluetooth subsystem enabled: contains either 0 or 1
     28 *   Please note that this file is constantly 0 if no Bluetooth
     29 *   hardware is available. (ro)
     30 *
     31 * In addition to these platform device attributes the driver
     32 * registers itself in the Linux backlight control subsystem and is
     33 * available to userspace under /sys/class/backlight/msi-laptop-bl/.
     34 *
     35 * This driver might work on other laptops produced by MSI. If you
     36 * want to try it you can pass force=1 as argument to the module which
     37 * will force it to load even when the DMI data doesn't identify the
     38 * laptop as MSI S270. YMMV.
     39 */
     40
     41#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     42
     43#include <linux/module.h>
     44#include <linux/kernel.h>
     45#include <linux/init.h>
     46#include <linux/acpi.h>
     47#include <linux/dmi.h>
     48#include <linux/backlight.h>
     49#include <linux/platform_device.h>
     50#include <linux/rfkill.h>
     51#include <linux/i8042.h>
     52#include <linux/input.h>
     53#include <linux/input/sparse-keymap.h>
     54#include <acpi/video.h>
     55
     56#define MSI_DRIVER_VERSION "0.5"
     57
     58#define MSI_LCD_LEVEL_MAX 9
     59
     60#define MSI_EC_COMMAND_WIRELESS 0x10
     61#define MSI_EC_COMMAND_LCD_LEVEL 0x11
     62
     63#define MSI_STANDARD_EC_COMMAND_ADDRESS	0x2e
     64#define MSI_STANDARD_EC_BLUETOOTH_MASK	(1 << 0)
     65#define MSI_STANDARD_EC_WEBCAM_MASK	(1 << 1)
     66#define MSI_STANDARD_EC_WLAN_MASK	(1 << 3)
     67#define MSI_STANDARD_EC_3G_MASK		(1 << 4)
     68
     69/* For set SCM load flag to disable BIOS fn key */
     70#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS	0x2d
     71#define MSI_STANDARD_EC_SCM_LOAD_MASK		(1 << 0)
     72
     73#define MSI_STANDARD_EC_FUNCTIONS_ADDRESS	0xe4
     74/* Power LED is orange - Turbo mode */
     75#define MSI_STANDARD_EC_TURBO_MASK		(1 << 1)
     76/* Power LED is green - ECO mode */
     77#define MSI_STANDARD_EC_ECO_MASK		(1 << 3)
     78/* Touchpad is turned on */
     79#define MSI_STANDARD_EC_TOUCHPAD_MASK		(1 << 4)
     80/* If this bit != bit 1, turbo mode can't be toggled */
     81#define MSI_STANDARD_EC_TURBO_COOLDOWN_MASK	(1 << 7)
     82
     83#define MSI_STANDARD_EC_FAN_ADDRESS		0x33
     84/* If zero, fan rotates at maximal speed */
     85#define MSI_STANDARD_EC_AUTOFAN_MASK		(1 << 0)
     86
     87#ifdef CONFIG_PM_SLEEP
     88static int msi_laptop_resume(struct device *device);
     89#endif
     90static SIMPLE_DEV_PM_OPS(msi_laptop_pm, NULL, msi_laptop_resume);
     91
     92#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS	0x2f
     93
     94static bool force;
     95module_param(force, bool, 0);
     96MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
     97
     98static int auto_brightness;
     99module_param(auto_brightness, int, 0);
    100MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
    101
    102static const struct key_entry msi_laptop_keymap[] = {
    103	{KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} },	/* Touch Pad On */
    104	{KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */
    105	{KE_END, 0}
    106};
    107
    108static struct input_dev *msi_laptop_input_dev;
    109
    110static int wlan_s, bluetooth_s, threeg_s;
    111static int threeg_exists;
    112static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
    113
    114/* MSI laptop quirks */
    115struct quirk_entry {
    116	bool old_ec_model;
    117
    118	/* Some MSI 3G netbook only have one fn key to control
    119	 * Wlan/Bluetooth/3G, those netbook will load the SCM (windows app) to
    120	 * disable the original Wlan/Bluetooth control by BIOS when user press
    121	 * fn key, then control Wlan/Bluetooth/3G by SCM (software control by
    122	 * OS). Without SCM, user cann't on/off 3G module on those 3G netbook.
    123	 * On Linux, msi-laptop driver will do the same thing to disable the
    124	 * original BIOS control, then might need use HAL or other userland
    125	 * application to do the software control that simulate with SCM.
    126	 * e.g. MSI N034 netbook
    127	 */
    128	bool load_scm_model;
    129
    130	/* Some MSI laptops need delay before reading from EC */
    131	bool ec_delay;
    132
    133	/* Some MSI Wind netbooks (e.g. MSI Wind U100) need loading SCM to get
    134	 * some features working (e.g. ECO mode), but we cannot change
    135	 * Wlan/Bluetooth state in software and we can only read its state.
    136	 */
    137	bool ec_read_only;
    138};
    139
    140static struct quirk_entry *quirks;
    141
    142/* Hardware access */
    143
    144static int set_lcd_level(int level)
    145{
    146	u8 buf[2];
    147
    148	if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
    149		return -EINVAL;
    150
    151	buf[0] = 0x80;
    152	buf[1] = (u8) (level*31);
    153
    154	return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf),
    155			      NULL, 0);
    156}
    157
    158static int get_lcd_level(void)
    159{
    160	u8 wdata = 0, rdata;
    161	int result;
    162
    163	result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
    164				&rdata, 1);
    165	if (result < 0)
    166		return result;
    167
    168	return (int) rdata / 31;
    169}
    170
    171static int get_auto_brightness(void)
    172{
    173	u8 wdata = 4, rdata;
    174	int result;
    175
    176	result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
    177				&rdata, 1);
    178	if (result < 0)
    179		return result;
    180
    181	return !!(rdata & 8);
    182}
    183
    184static int set_auto_brightness(int enable)
    185{
    186	u8 wdata[2], rdata;
    187	int result;
    188
    189	wdata[0] = 4;
    190
    191	result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1,
    192				&rdata, 1);
    193	if (result < 0)
    194		return result;
    195
    196	wdata[0] = 0x84;
    197	wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
    198
    199	return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2,
    200			      NULL, 0);
    201}
    202
    203static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
    204{
    205	int status;
    206	u8 wdata = 0, rdata;
    207	int result;
    208
    209	if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
    210		return -EINVAL;
    211
    212	if (quirks->ec_read_only)
    213		return -EOPNOTSUPP;
    214
    215	/* read current device state */
    216	result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
    217	if (result < 0)
    218		return result;
    219
    220	if (!!(rdata & mask) != status) {
    221		/* reverse device bit */
    222		if (rdata & mask)
    223			wdata = rdata & ~mask;
    224		else
    225			wdata = rdata | mask;
    226
    227		result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);
    228		if (result < 0)
    229			return result;
    230	}
    231
    232	return count;
    233}
    234
    235static int get_wireless_state(int *wlan, int *bluetooth)
    236{
    237	u8 wdata = 0, rdata;
    238	int result;
    239
    240	result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1);
    241	if (result < 0)
    242		return result;
    243
    244	if (wlan)
    245		*wlan = !!(rdata & 8);
    246
    247	if (bluetooth)
    248		*bluetooth = !!(rdata & 128);
    249
    250	return 0;
    251}
    252
    253static int get_wireless_state_ec_standard(void)
    254{
    255	u8 rdata;
    256	int result;
    257
    258	result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
    259	if (result < 0)
    260		return result;
    261
    262	wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK);
    263
    264	bluetooth_s = !!(rdata & MSI_STANDARD_EC_BLUETOOTH_MASK);
    265
    266	threeg_s = !!(rdata & MSI_STANDARD_EC_3G_MASK);
    267
    268	return 0;
    269}
    270
    271static int get_threeg_exists(void)
    272{
    273	u8 rdata;
    274	int result;
    275
    276	result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata);
    277	if (result < 0)
    278		return result;
    279
    280	threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK);
    281
    282	return 0;
    283}
    284
    285/* Backlight device stuff */
    286
    287static int bl_get_brightness(struct backlight_device *b)
    288{
    289	return get_lcd_level();
    290}
    291
    292
    293static int bl_update_status(struct backlight_device *b)
    294{
    295	return set_lcd_level(b->props.brightness);
    296}
    297
    298static const struct backlight_ops msibl_ops = {
    299	.get_brightness = bl_get_brightness,
    300	.update_status  = bl_update_status,
    301};
    302
    303static struct backlight_device *msibl_device;
    304
    305/* Platform device */
    306
    307static ssize_t show_wlan(struct device *dev,
    308	struct device_attribute *attr, char *buf)
    309{
    310
    311	int ret, enabled = 0;
    312
    313	if (quirks->old_ec_model) {
    314		ret = get_wireless_state(&enabled, NULL);
    315	} else {
    316		ret = get_wireless_state_ec_standard();
    317		enabled = wlan_s;
    318	}
    319	if (ret < 0)
    320		return ret;
    321
    322	return sprintf(buf, "%i\n", enabled);
    323}
    324
    325static ssize_t store_wlan(struct device *dev,
    326	struct device_attribute *attr, const char *buf, size_t count)
    327{
    328	return set_device_state(buf, count, MSI_STANDARD_EC_WLAN_MASK);
    329}
    330
    331static ssize_t show_bluetooth(struct device *dev,
    332	struct device_attribute *attr, char *buf)
    333{
    334
    335	int ret, enabled = 0;
    336
    337	if (quirks->old_ec_model) {
    338		ret = get_wireless_state(NULL, &enabled);
    339	} else {
    340		ret = get_wireless_state_ec_standard();
    341		enabled = bluetooth_s;
    342	}
    343	if (ret < 0)
    344		return ret;
    345
    346	return sprintf(buf, "%i\n", enabled);
    347}
    348
    349static ssize_t store_bluetooth(struct device *dev,
    350	struct device_attribute *attr, const char *buf, size_t count)
    351{
    352	return set_device_state(buf, count, MSI_STANDARD_EC_BLUETOOTH_MASK);
    353}
    354
    355static ssize_t show_threeg(struct device *dev,
    356	struct device_attribute *attr, char *buf)
    357{
    358
    359	int ret;
    360
    361	/* old msi ec not support 3G */
    362	if (quirks->old_ec_model)
    363		return -ENODEV;
    364
    365	ret = get_wireless_state_ec_standard();
    366	if (ret < 0)
    367		return ret;
    368
    369	return sprintf(buf, "%i\n", threeg_s);
    370}
    371
    372static ssize_t store_threeg(struct device *dev,
    373	struct device_attribute *attr, const char *buf, size_t count)
    374{
    375	return set_device_state(buf, count, MSI_STANDARD_EC_3G_MASK);
    376}
    377
    378static ssize_t show_lcd_level(struct device *dev,
    379	struct device_attribute *attr, char *buf)
    380{
    381
    382	int ret;
    383
    384	ret = get_lcd_level();
    385	if (ret < 0)
    386		return ret;
    387
    388	return sprintf(buf, "%i\n", ret);
    389}
    390
    391static ssize_t store_lcd_level(struct device *dev,
    392	struct device_attribute *attr, const char *buf, size_t count)
    393{
    394
    395	int level, ret;
    396
    397	if (sscanf(buf, "%i", &level) != 1 ||
    398	    (level < 0 || level >= MSI_LCD_LEVEL_MAX))
    399		return -EINVAL;
    400
    401	ret = set_lcd_level(level);
    402	if (ret < 0)
    403		return ret;
    404
    405	return count;
    406}
    407
    408static ssize_t show_auto_brightness(struct device *dev,
    409	struct device_attribute *attr, char *buf)
    410{
    411
    412	int ret;
    413
    414	ret = get_auto_brightness();
    415	if (ret < 0)
    416		return ret;
    417
    418	return sprintf(buf, "%i\n", ret);
    419}
    420
    421static ssize_t store_auto_brightness(struct device *dev,
    422	struct device_attribute *attr, const char *buf, size_t count)
    423{
    424
    425	int enable, ret;
    426
    427	if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
    428		return -EINVAL;
    429
    430	ret = set_auto_brightness(enable);
    431	if (ret < 0)
    432		return ret;
    433
    434	return count;
    435}
    436
    437static ssize_t show_touchpad(struct device *dev,
    438	struct device_attribute *attr, char *buf)
    439{
    440
    441	u8 rdata;
    442	int result;
    443
    444	result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
    445	if (result < 0)
    446		return result;
    447
    448	return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK));
    449}
    450
    451static ssize_t show_turbo(struct device *dev,
    452	struct device_attribute *attr, char *buf)
    453{
    454
    455	u8 rdata;
    456	int result;
    457
    458	result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
    459	if (result < 0)
    460		return result;
    461
    462	return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TURBO_MASK));
    463}
    464
    465static ssize_t show_eco(struct device *dev,
    466	struct device_attribute *attr, char *buf)
    467{
    468
    469	u8 rdata;
    470	int result;
    471
    472	result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
    473	if (result < 0)
    474		return result;
    475
    476	return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_ECO_MASK));
    477}
    478
    479static ssize_t show_turbo_cooldown(struct device *dev,
    480	struct device_attribute *attr, char *buf)
    481{
    482
    483	u8 rdata;
    484	int result;
    485
    486	result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
    487	if (result < 0)
    488		return result;
    489
    490	return sprintf(buf, "%i\n", (!!(rdata & MSI_STANDARD_EC_TURBO_MASK)) |
    491		(!!(rdata & MSI_STANDARD_EC_TURBO_COOLDOWN_MASK) << 1));
    492}
    493
    494static ssize_t show_auto_fan(struct device *dev,
    495	struct device_attribute *attr, char *buf)
    496{
    497
    498	u8 rdata;
    499	int result;
    500
    501	result = ec_read(MSI_STANDARD_EC_FAN_ADDRESS, &rdata);
    502	if (result < 0)
    503		return result;
    504
    505	return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_AUTOFAN_MASK));
    506}
    507
    508static ssize_t store_auto_fan(struct device *dev,
    509	struct device_attribute *attr, const char *buf, size_t count)
    510{
    511
    512	int enable, result;
    513
    514	if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
    515		return -EINVAL;
    516
    517	result = ec_write(MSI_STANDARD_EC_FAN_ADDRESS, enable);
    518	if (result < 0)
    519		return result;
    520
    521	return count;
    522}
    523
    524static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
    525static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness,
    526		   store_auto_brightness);
    527static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
    528static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
    529static DEVICE_ATTR(threeg, 0444, show_threeg, NULL);
    530static DEVICE_ATTR(touchpad, 0444, show_touchpad, NULL);
    531static DEVICE_ATTR(turbo_mode, 0444, show_turbo, NULL);
    532static DEVICE_ATTR(eco_mode, 0444, show_eco, NULL);
    533static DEVICE_ATTR(turbo_cooldown, 0444, show_turbo_cooldown, NULL);
    534static DEVICE_ATTR(auto_fan, 0644, show_auto_fan, store_auto_fan);
    535
    536static struct attribute *msipf_attributes[] = {
    537	&dev_attr_bluetooth.attr,
    538	&dev_attr_wlan.attr,
    539	&dev_attr_touchpad.attr,
    540	&dev_attr_turbo_mode.attr,
    541	&dev_attr_eco_mode.attr,
    542	&dev_attr_turbo_cooldown.attr,
    543	&dev_attr_auto_fan.attr,
    544	NULL
    545};
    546
    547static struct attribute *msipf_old_attributes[] = {
    548	&dev_attr_lcd_level.attr,
    549	&dev_attr_auto_brightness.attr,
    550	NULL
    551};
    552
    553static const struct attribute_group msipf_attribute_group = {
    554	.attrs = msipf_attributes
    555};
    556
    557static const struct attribute_group msipf_old_attribute_group = {
    558	.attrs = msipf_old_attributes
    559};
    560
    561static struct platform_driver msipf_driver = {
    562	.driver = {
    563		.name = "msi-laptop-pf",
    564		.pm = &msi_laptop_pm,
    565	},
    566};
    567
    568static struct platform_device *msipf_device;
    569
    570/* Initialization */
    571
    572static struct quirk_entry quirk_old_ec_model = {
    573	.old_ec_model = true,
    574};
    575
    576static struct quirk_entry quirk_load_scm_model = {
    577	.load_scm_model = true,
    578	.ec_delay = true,
    579};
    580
    581static struct quirk_entry quirk_load_scm_ro_model = {
    582	.load_scm_model = true,
    583	.ec_read_only = true,
    584};
    585
    586static int dmi_check_cb(const struct dmi_system_id *dmi)
    587{
    588	pr_info("Identified laptop model '%s'\n", dmi->ident);
    589
    590	quirks = dmi->driver_data;
    591
    592	return 1;
    593}
    594
    595static const struct dmi_system_id msi_dmi_table[] __initconst = {
    596	{
    597		.ident = "MSI S270",
    598		.matches = {
    599			DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
    600			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
    601			DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
    602			DMI_MATCH(DMI_CHASSIS_VENDOR,
    603				  "MICRO-STAR INT'L CO.,LTD")
    604		},
    605		.driver_data = &quirk_old_ec_model,
    606		.callback = dmi_check_cb
    607	},
    608	{
    609		.ident = "MSI S271",
    610		.matches = {
    611			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
    612			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
    613			DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
    614			DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
    615		},
    616		.driver_data = &quirk_old_ec_model,
    617		.callback = dmi_check_cb
    618	},
    619	{
    620		.ident = "MSI S420",
    621		.matches = {
    622			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
    623			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
    624			DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
    625			DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
    626		},
    627		.driver_data = &quirk_old_ec_model,
    628		.callback = dmi_check_cb
    629	},
    630	{
    631		.ident = "Medion MD96100",
    632		.matches = {
    633			DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
    634			DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
    635			DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
    636			DMI_MATCH(DMI_CHASSIS_VENDOR,
    637				  "MICRO-STAR INT'L CO.,LTD")
    638		},
    639		.driver_data = &quirk_old_ec_model,
    640		.callback = dmi_check_cb
    641	},
    642	{
    643		.ident = "MSI N034",
    644		.matches = {
    645			DMI_MATCH(DMI_SYS_VENDOR,
    646				"MICRO-STAR INTERNATIONAL CO., LTD"),
    647			DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"),
    648			DMI_MATCH(DMI_CHASSIS_VENDOR,
    649			"MICRO-STAR INTERNATIONAL CO., LTD")
    650		},
    651		.driver_data = &quirk_load_scm_model,
    652		.callback = dmi_check_cb
    653	},
    654	{
    655		.ident = "MSI N051",
    656		.matches = {
    657			DMI_MATCH(DMI_SYS_VENDOR,
    658				"MICRO-STAR INTERNATIONAL CO., LTD"),
    659			DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"),
    660			DMI_MATCH(DMI_CHASSIS_VENDOR,
    661			"MICRO-STAR INTERNATIONAL CO., LTD")
    662		},
    663		.driver_data = &quirk_load_scm_model,
    664		.callback = dmi_check_cb
    665	},
    666	{
    667		.ident = "MSI N014",
    668		.matches = {
    669			DMI_MATCH(DMI_SYS_VENDOR,
    670				"MICRO-STAR INTERNATIONAL CO., LTD"),
    671			DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),
    672		},
    673		.driver_data = &quirk_load_scm_model,
    674		.callback = dmi_check_cb
    675	},
    676	{
    677		.ident = "MSI CR620",
    678		.matches = {
    679			DMI_MATCH(DMI_SYS_VENDOR,
    680				"Micro-Star International"),
    681			DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),
    682		},
    683		.driver_data = &quirk_load_scm_model,
    684		.callback = dmi_check_cb
    685	},
    686	{
    687		.ident = "MSI U270",
    688		.matches = {
    689			DMI_MATCH(DMI_SYS_VENDOR,
    690				"Micro-Star International Co., Ltd."),
    691			DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"),
    692		},
    693		.driver_data = &quirk_load_scm_model,
    694		.callback = dmi_check_cb
    695	},
    696	{
    697		.ident = "MSI U90/U100",
    698		.matches = {
    699			DMI_MATCH(DMI_SYS_VENDOR,
    700				"MICRO-STAR INTERNATIONAL CO., LTD"),
    701			DMI_MATCH(DMI_PRODUCT_NAME, "U90/U100"),
    702		},
    703		.driver_data = &quirk_load_scm_ro_model,
    704		.callback = dmi_check_cb
    705	},
    706	{ }
    707};
    708
    709static int rfkill_bluetooth_set(void *data, bool blocked)
    710{
    711	/* Do something with blocked...*/
    712	/*
    713	 * blocked == false is on
    714	 * blocked == true is off
    715	 */
    716	int result = set_device_state(blocked ? "0" : "1", 0,
    717			MSI_STANDARD_EC_BLUETOOTH_MASK);
    718
    719	return min(result, 0);
    720}
    721
    722static int rfkill_wlan_set(void *data, bool blocked)
    723{
    724	int result = set_device_state(blocked ? "0" : "1", 0,
    725			MSI_STANDARD_EC_WLAN_MASK);
    726
    727	return min(result, 0);
    728}
    729
    730static int rfkill_threeg_set(void *data, bool blocked)
    731{
    732	int result = set_device_state(blocked ? "0" : "1", 0,
    733			MSI_STANDARD_EC_3G_MASK);
    734
    735	return min(result, 0);
    736}
    737
    738static const struct rfkill_ops rfkill_bluetooth_ops = {
    739	.set_block = rfkill_bluetooth_set
    740};
    741
    742static const struct rfkill_ops rfkill_wlan_ops = {
    743	.set_block = rfkill_wlan_set
    744};
    745
    746static const struct rfkill_ops rfkill_threeg_ops = {
    747	.set_block = rfkill_threeg_set
    748};
    749
    750static void rfkill_cleanup(void)
    751{
    752	if (rfk_bluetooth) {
    753		rfkill_unregister(rfk_bluetooth);
    754		rfkill_destroy(rfk_bluetooth);
    755	}
    756
    757	if (rfk_threeg) {
    758		rfkill_unregister(rfk_threeg);
    759		rfkill_destroy(rfk_threeg);
    760	}
    761
    762	if (rfk_wlan) {
    763		rfkill_unregister(rfk_wlan);
    764		rfkill_destroy(rfk_wlan);
    765	}
    766}
    767
    768static bool msi_rfkill_set_state(struct rfkill *rfkill, bool blocked)
    769{
    770	if (quirks->ec_read_only)
    771		return rfkill_set_hw_state(rfkill, blocked);
    772	else
    773		return rfkill_set_sw_state(rfkill, blocked);
    774}
    775
    776static void msi_update_rfkill(struct work_struct *ignored)
    777{
    778	get_wireless_state_ec_standard();
    779
    780	if (rfk_wlan)
    781		msi_rfkill_set_state(rfk_wlan, !wlan_s);
    782	if (rfk_bluetooth)
    783		msi_rfkill_set_state(rfk_bluetooth, !bluetooth_s);
    784	if (rfk_threeg)
    785		msi_rfkill_set_state(rfk_threeg, !threeg_s);
    786}
    787static DECLARE_DELAYED_WORK(msi_rfkill_dwork, msi_update_rfkill);
    788static DECLARE_WORK(msi_rfkill_work, msi_update_rfkill);
    789
    790static void msi_send_touchpad_key(struct work_struct *ignored)
    791{
    792	u8 rdata;
    793	int result;
    794
    795	result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
    796	if (result < 0)
    797		return;
    798
    799	sparse_keymap_report_event(msi_laptop_input_dev,
    800		(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
    801		KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
    802}
    803static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key);
    804static DECLARE_WORK(msi_touchpad_work, msi_send_touchpad_key);
    805
    806static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
    807				struct serio *port)
    808{
    809	static bool extended;
    810
    811	if (str & I8042_STR_AUXDATA)
    812		return false;
    813
    814	/* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/
    815	if (unlikely(data == 0xe0)) {
    816		extended = true;
    817		return false;
    818	} else if (unlikely(extended)) {
    819		extended = false;
    820		switch (data) {
    821		case 0xE4:
    822			if (quirks->ec_delay) {
    823				schedule_delayed_work(&msi_touchpad_dwork,
    824					round_jiffies_relative(0.5 * HZ));
    825			} else
    826				schedule_work(&msi_touchpad_work);
    827			break;
    828		case 0x54:
    829		case 0x62:
    830		case 0x76:
    831			if (quirks->ec_delay) {
    832				schedule_delayed_work(&msi_rfkill_dwork,
    833					round_jiffies_relative(0.5 * HZ));
    834			} else
    835				schedule_work(&msi_rfkill_work);
    836			break;
    837		}
    838	}
    839
    840	return false;
    841}
    842
    843static void msi_init_rfkill(struct work_struct *ignored)
    844{
    845	if (rfk_wlan) {
    846		rfkill_set_sw_state(rfk_wlan, !wlan_s);
    847		rfkill_wlan_set(NULL, !wlan_s);
    848	}
    849	if (rfk_bluetooth) {
    850		rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
    851		rfkill_bluetooth_set(NULL, !bluetooth_s);
    852	}
    853	if (rfk_threeg) {
    854		rfkill_set_sw_state(rfk_threeg, !threeg_s);
    855		rfkill_threeg_set(NULL, !threeg_s);
    856	}
    857}
    858static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill);
    859
    860static int rfkill_init(struct platform_device *sdev)
    861{
    862	/* add rfkill */
    863	int retval;
    864
    865	/* keep the hardware wireless state */
    866	get_wireless_state_ec_standard();
    867
    868	rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev,
    869				RFKILL_TYPE_BLUETOOTH,
    870				&rfkill_bluetooth_ops, NULL);
    871	if (!rfk_bluetooth) {
    872		retval = -ENOMEM;
    873		goto err_bluetooth;
    874	}
    875	retval = rfkill_register(rfk_bluetooth);
    876	if (retval)
    877		goto err_bluetooth;
    878
    879	rfk_wlan = rfkill_alloc("msi-wlan", &sdev->dev, RFKILL_TYPE_WLAN,
    880				&rfkill_wlan_ops, NULL);
    881	if (!rfk_wlan) {
    882		retval = -ENOMEM;
    883		goto err_wlan;
    884	}
    885	retval = rfkill_register(rfk_wlan);
    886	if (retval)
    887		goto err_wlan;
    888
    889	if (threeg_exists) {
    890		rfk_threeg = rfkill_alloc("msi-threeg", &sdev->dev,
    891				RFKILL_TYPE_WWAN, &rfkill_threeg_ops, NULL);
    892		if (!rfk_threeg) {
    893			retval = -ENOMEM;
    894			goto err_threeg;
    895		}
    896		retval = rfkill_register(rfk_threeg);
    897		if (retval)
    898			goto err_threeg;
    899	}
    900
    901	/* schedule to run rfkill state initial */
    902	if (quirks->ec_delay) {
    903		schedule_delayed_work(&msi_rfkill_init,
    904			round_jiffies_relative(1 * HZ));
    905	} else
    906		schedule_work(&msi_rfkill_work);
    907
    908	return 0;
    909
    910err_threeg:
    911	rfkill_destroy(rfk_threeg);
    912	if (rfk_wlan)
    913		rfkill_unregister(rfk_wlan);
    914err_wlan:
    915	rfkill_destroy(rfk_wlan);
    916	if (rfk_bluetooth)
    917		rfkill_unregister(rfk_bluetooth);
    918err_bluetooth:
    919	rfkill_destroy(rfk_bluetooth);
    920
    921	return retval;
    922}
    923
    924#ifdef CONFIG_PM_SLEEP
    925static int msi_laptop_resume(struct device *device)
    926{
    927	u8 data;
    928	int result;
    929
    930	if (!quirks->load_scm_model)
    931		return 0;
    932
    933	/* set load SCM to disable hardware control by fn key */
    934	result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
    935	if (result < 0)
    936		return result;
    937
    938	result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
    939		data | MSI_STANDARD_EC_SCM_LOAD_MASK);
    940	if (result < 0)
    941		return result;
    942
    943	return 0;
    944}
    945#endif
    946
    947static int __init msi_laptop_input_setup(void)
    948{
    949	int err;
    950
    951	msi_laptop_input_dev = input_allocate_device();
    952	if (!msi_laptop_input_dev)
    953		return -ENOMEM;
    954
    955	msi_laptop_input_dev->name = "MSI Laptop hotkeys";
    956	msi_laptop_input_dev->phys = "msi-laptop/input0";
    957	msi_laptop_input_dev->id.bustype = BUS_HOST;
    958
    959	err = sparse_keymap_setup(msi_laptop_input_dev,
    960		msi_laptop_keymap, NULL);
    961	if (err)
    962		goto err_free_dev;
    963
    964	err = input_register_device(msi_laptop_input_dev);
    965	if (err)
    966		goto err_free_dev;
    967
    968	return 0;
    969
    970err_free_dev:
    971	input_free_device(msi_laptop_input_dev);
    972	return err;
    973}
    974
    975static int __init load_scm_model_init(struct platform_device *sdev)
    976{
    977	u8 data;
    978	int result;
    979
    980	if (!quirks->ec_read_only) {
    981		/* allow userland write sysfs file  */
    982		dev_attr_bluetooth.store = store_bluetooth;
    983		dev_attr_wlan.store = store_wlan;
    984		dev_attr_threeg.store = store_threeg;
    985		dev_attr_bluetooth.attr.mode |= S_IWUSR;
    986		dev_attr_wlan.attr.mode |= S_IWUSR;
    987		dev_attr_threeg.attr.mode |= S_IWUSR;
    988	}
    989
    990	/* disable hardware control by fn key */
    991	result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
    992	if (result < 0)
    993		return result;
    994
    995	result = ec_write(MSI_STANDARD_EC_SCM_LOAD_ADDRESS,
    996		data | MSI_STANDARD_EC_SCM_LOAD_MASK);
    997	if (result < 0)
    998		return result;
    999
   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
   1018fail_filter:
   1019	input_unregister_device(msi_laptop_input_dev);
   1020
   1021fail_input:
   1022	rfkill_cleanup();
   1023
   1024fail_rfkill:
   1025
   1026	return result;
   1027
   1028}
   1029
   1030static 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
   1115fail_create_attr:
   1116	sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
   1117fail_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	}
   1124fail_scm_model_init:
   1125	platform_device_del(msipf_device);
   1126fail_device_add:
   1127	platform_device_put(msipf_device);
   1128fail_platform_driver:
   1129	platform_driver_unregister(&msipf_driver);
   1130fail_backlight:
   1131	backlight_device_unregister(msibl_device);
   1132
   1133	return ret;
   1134}
   1135
   1136static 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
   1162module_init(msi_init);
   1163module_exit(msi_cleanup);
   1164
   1165MODULE_AUTHOR("Lennart Poettering");
   1166MODULE_DESCRIPTION("MSI Laptop Support");
   1167MODULE_VERSION(MSI_DRIVER_VERSION);
   1168MODULE_LICENSE("GPL");
   1169
   1170MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
   1171MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
   1172MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
   1173MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
   1174MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*");
   1175MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
   1176MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
   1177MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");
   1178MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*");
   1179MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnU90/U100:*");