Hi, On 2/13/23 19:45, Nikita Kravets wrote: > Hi Hans, > > I've just sent you an updated patch with a better commit message, could you please check it? This looks ready for submission to the mailinglist, please resend it with platform-driver-x86@xxxxxxxxxxxxxxx added to the Cc. Then I will do a detailed review when I can make some time for this. Regards, Hans > > > On Mon, Feb 13, 2023 at 5:42 PM Hans de Goede <hdegoede@xxxxxxxxxx <mailto:hdegoede@xxxxxxxxxx>> wrote: > > Hi Nikita, > > You need to write something of a detailed commit message here. Right > now your commit message is just: > > "Add msi-ec driver, known EC configurations and battery thresholds" > > This should change to something like this: > > """ > platform/x86: Add new msi-ec driver > > Add a new driver to allow various MSI laptops' functionalities to be > controlled from userspace.. > > This driver contains EC memory configurations for different firmware > versions and exports battery charge thresholds to userspace. > > Link: https://github.com/BeardOverflow/msi-ec/ <https://github.com/BeardOverflow/msi-ec/> > Signed-off-by: Nikita Kravets <teackot@xxxxxxxxx <mailto:teackot@xxxxxxxxx>> > """ > > Note the Signed-off-by at the end has a special meaning. > I can only accept patches with such a Signed-off-by line in the commit-message. > > By adding this line you indicate that you are the author of the code and > are submitting it under the existing license for the file which you are > modifying (typically GPL-2.0) or that you have permission from the other > authors to submit it under this license. See: > > https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin <https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin> > > Since the existing msi-ec.c already has a: > > "// SPDX-License-Identifier: GPL-2.0-or-later" > > line at the top, there is no problem here, adding the S-o-b is just > a formality indicating the code was submitted by you to me and then I'll > add my own S-o-b below yours before submitting it to Linus Torvalds. > > If you change your commit message as suggested above (or to something > similar) then git send-email will use the first line for the Subject of > the email and put the rest in the body of the email, above the '---' > line which it puts just before the patch starts. > > Regards, > > Hans > > > > > On 2/8/23 17:58, Nikita Kravets wrote: > > --- > > drivers/platform/x86/Kconfig | 7 + > > drivers/platform/x86/Makefile | 1 + > > drivers/platform/x86/msi-ec.c | 528 ++++++++++++++++++++++++++++++++++ > > drivers/platform/x86/msi-ec.h | 119 ++++++++ > > 4 files changed, 655 insertions(+) > > create mode 100644 drivers/platform/x86/msi-ec.c > > create mode 100644 drivers/platform/x86/msi-ec.h > > > > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig > > index 5692385e2d26..4534d11f9ca5 100644 > > --- a/drivers/platform/x86/Kconfig > > +++ b/drivers/platform/x86/Kconfig > > @@ -644,6 +644,13 @@ config THINKPAD_LMI > > > > source "drivers/platform/x86/intel/Kconfig" > > > > +config MSI_EC > > + tristate "MSI EC Extras" > > + depends on ACPI > > + help > > + This driver allows various MSI laptops' functionalities to be > > + controlled from userspace, including battery charge threshold. > > + > > config MSI_LAPTOP > > tristate "MSI Laptop Extras" > > depends on ACPI > > diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile > > index 1d3d1b02541b..7cc2beca8208 100644 > > --- a/drivers/platform/x86/Makefile > > +++ b/drivers/platform/x86/Makefile > > @@ -71,6 +71,7 @@ obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o > > obj-y += intel/ > > > > # MSI > > +obj-$(CONFIG_MSI_EC) += msi-ec.o > > obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o > > obj-$(CONFIG_MSI_WMI) += msi-wmi.o > > > > diff --git a/drivers/platform/x86/msi-ec.c b/drivers/platform/x86/msi-ec.c > > new file mode 100644 > > index 000000000000..b32106445bf6 > > --- /dev/null > > +++ b/drivers/platform/x86/msi-ec.c > > @@ -0,0 +1,528 @@ > > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > > + > > +/* > > + * msi-ec: MSI laptops' embedded controller driver. > > + * > > + * This driver allows various MSI laptops' functionalities to be > > + * controlled from userspace. > > + * > > + * It contains EC memory configurations for different firmware versions > > + * and exports battery charge thresholds to userspace. > > + * > > + * Copyright (C) 2023 Jose Angel Pastrana <japp0005@xxxxxxxxxxxx <mailto:japp0005@xxxxxxxxxxxx>> > > + * Copyright (C) 2023 Aakash Singh <mail@xxxxxxxxxxxxxxx <mailto:mail@xxxxxxxxxxxxxxx>> > > + * Copyright (C) 2023 Nikita Kravets <teackot@xxxxxxxxx <mailto:teackot@xxxxxxxxx>> > > + */ > > + > > +#include "msi-ec.h" > > + > > +#include <acpi/battery.h> > > +#include <linux/acpi.h> > > +#include <linux/init.h> > > +#include <linux/kernel.h> > > +#include <linux/module.h> > > +#include <linux/platform_device.h> > > +#include <linux/proc_fs.h> > > +#include <linux/seq_file.h> > > + > > +static const char *const SM_ECO_NAME = "eco"; > > +static const char *const SM_COMFORT_NAME = "comfort"; > > +static const char *const SM_SPORT_NAME = "sport"; > > +static const char *const SM_TURBO_NAME = "turbo"; > > + > > +static const char *ALLOWED_FW_0[] __initdata = { > > + "14C1EMS1.101", > > + NULL, > > +}; > > + > > +static struct msi_ec_conf CONF0 __initdata = { > > + .allowed_fw = ALLOWED_FW_0, > > + .charge_control = { > > + .address = 0xef, > > + .offset_start = 0x8a, > > + .offset_end = 0x80, > > + .range_min = 0x8a, > > + .range_max = 0xe4, > > + }, > > + .webcam = { > > + .address = 0x2e, > > + .block_address = 0x2f, > > + .bit = 1, > > + }, > > + .fn_super_swap = { > > + .address = 0xbf, > > + .bit = 4, > > + }, > > + .cooler_boost = { > > + .address = 0x98, > > + .bit = 7, > > + }, > > + .shift_mode = { > > + .address = 0xf2, > > + .modes = { > > + { SM_ECO_NAME, 0xc2 }, > > + { SM_COMFORT_NAME, 0xc1 }, > > + { SM_SPORT_NAME, 0xc0 }, > > + }, > > + .modes_count = 3, > > + }, > > + .super_battery = { > > + .supported = false, > > + }, > > + .fan_mode = { > > + .address = 0xf4, > > + }, > > + .cpu = { > > + .rt_temp_address = 0x68, > > + .rt_fan_speed_address = 0x71, > > + .rt_fan_speed_base_min = 0x19, > > + .rt_fan_speed_base_max = 0x37, > > + .bs_fan_speed_address = 0x89, > > + .bs_fan_speed_base_min = 0x00, > > + .bs_fan_speed_base_max = 0x0f, > > + }, > > + .gpu = { > > + .rt_temp_address = 0x80, > > + .rt_fan_speed_address = 0x89, > > + }, > > + .leds = { > > + .micmute_led_address = 0x2b, > > + .mute_led_address = 0x2c, > > + .bit = 2, > > + }, > > + .kbd_bl = { > > + .bl_mode_address = 0x2c, // ? > > + .bl_modes = { 0x00, 0x08 }, // ? > > + .max_mode = 1, // ? > > + .bl_state_address = 0xf3, > > + .state_base_value = 0x80, > > + .max_state = 3, > > + }, > > +}; > > + > > +static const char *ALLOWED_FW_1[] __initdata = { > > + "17F2EMS1.106", > > + NULL, > > +}; > > + > > +static struct msi_ec_conf CONF1 __initdata = { > > + .allowed_fw = ALLOWED_FW_1, > > + .charge_control = { > > + .address = 0xef, > > + .offset_start = 0x8a, > > + .offset_end = 0x80, > > + .range_min = 0x8a, > > + .range_max = 0xe4, > > + }, > > + .webcam = { > > + .address = 0x2e, > > + .block_address = 0x2f, > > + .bit = 1, > > + }, > > + .fn_super_swap = { > > + .address = 0xbf, > > + .bit = 4, > > + }, > > + .cooler_boost = { > > + .address = 0x98, > > + .bit = 7, > > + }, > > + .shift_mode = { > > + .address = 0xf2, > > + .modes = { > > + { SM_ECO_NAME, 0xc2 }, > > + { SM_COMFORT_NAME, 0xc1 }, > > + { SM_SPORT_NAME, 0xc0 }, > > + { SM_TURBO_NAME, 0xc4 }, > > + }, > > + .modes_count = 4, > > + }, > > + .super_battery = { > > + .supported = false, > > + }, > > + .fan_mode = { > > + .address = 0xf4, > > + }, > > + .cpu = { > > + .rt_temp_address = 0x68, > > + .rt_fan_speed_address = 0x71, > > + .rt_fan_speed_base_min = 0x19, > > + .rt_fan_speed_base_max = 0x37, > > + .bs_fan_speed_address = 0x89, > > + .bs_fan_speed_base_min = 0x00, > > + .bs_fan_speed_base_max = 0x0f, > > + }, > > + .gpu = { > > + .rt_temp_address = 0x80, > > + .rt_fan_speed_address = 0x89, > > + }, > > + .leds = { > > + .micmute_led_address = 0x2b, > > + .mute_led_address = 0x2c, > > + .bit = 2, > > + }, > > + .kbd_bl = { > > + .bl_mode_address = 0x2c, // ? > > + .bl_modes = { 0x00, 0x08 }, // ? > > + .max_mode = 1, // ? > > + .bl_state_address = 0xf3, > > + .state_base_value = 0x80, > > + .max_state = 3, > > + }, > > +}; > > + > > +static const char *ALLOWED_FW_2[] __initdata = { > > + "1552EMS1.118", > > + NULL, > > +}; > > + > > +static struct msi_ec_conf CONF2 __initdata = { > > + .allowed_fw = ALLOWED_FW_2, > > + .charge_control = { > > + .address = 0xd7, > > + .offset_start = 0x8a, > > + .offset_end = 0x80, > > + .range_min = 0x8a, > > + .range_max = 0xe4, > > + }, > > + .webcam = { > > + .address = 0x2e, > > + .block_address = 0x2f, > > + .bit = 1, > > + }, > > + .fn_super_swap = { > > + .address = 0xe8, > > + .bit = 4, > > + }, > > + .cooler_boost = { > > + .address = 0x98, > > + .bit = 7, > > + }, > > + .shift_mode = { > > + .address = 0xf2, > > + .modes = { > > + { SM_ECO_NAME, 0xc2 }, > > + { SM_COMFORT_NAME, 0xc1 }, > > + { SM_SPORT_NAME, 0xc0 }, > > + }, > > + .modes_count = 3, > > + }, > > + .super_battery = { > > + .supported = true, > > + .address = 0xeb, > > + .mask = 0x0f, > > + }, > > + .fan_mode = { > > + .address = 0xd4, > > + }, > > + .cpu = { > > + .rt_temp_address = 0x68, > > + .rt_fan_speed_address = 0x71, > > + .rt_fan_speed_base_min = 0x19, > > + .rt_fan_speed_base_max = 0x37, > > + .bs_fan_speed_address = 0x89, > > + .bs_fan_speed_base_min = 0x00, > > + .bs_fan_speed_base_max = 0x0f, > > + }, > > + .gpu = { > > + .rt_temp_address = 0x80, > > + .rt_fan_speed_address = 0x89, > > + }, > > + .leds = { > > + .micmute_led_address = 0x2c, > > + .mute_led_address = 0x2d, > > + .bit = 1, > > + }, > > + .kbd_bl = { > > + .bl_mode_address = 0x2c, // ? > > + .bl_modes = { 0x00, 0x08 }, // ? > > + .max_mode = 1, // ? > > + .bl_state_address = 0xd3, > > + .state_base_value = 0x80, > > + .max_state = 3, > > + }, > > +}; > > + > > +static const char *ALLOWED_FW_3[] __initdata = { > > + "1592EMS1.111", > > + "E1592IMS.10C", > > + NULL, > > +}; > > + > > +static struct msi_ec_conf CONF3 __initdata = { > > + .allowed_fw = ALLOWED_FW_3, > > + .charge_control = { > > + .address = 0xef, > > + .offset_start = 0x8a, > > + .offset_end = 0x80, > > + .range_min = 0x8a, > > + .range_max = 0xe4, > > + }, > > + .webcam = { > > + .address = 0x2e, > > + .block_address = 0x2f, > > + .bit = 1, > > + }, > > + .fn_super_swap = { > > + .address = 0xe8, > > + .bit = 4, > > + }, > > + .cooler_boost = { > > + .address = 0x98, > > + .bit = 7, > > + }, > > + .shift_mode = { > > + .address = 0xd2, > > + .modes = { > > + { SM_ECO_NAME, 0xc2 }, > > + { SM_COMFORT_NAME, 0xc1 }, > > + { SM_SPORT_NAME, 0xc0 }, > > + }, > > + .modes_count = 3, > > + }, > > + .super_battery = { > > + .supported = true, > > + .address = 0xeb, > > + .mask = 0x0f, > > + }, > > + .fan_mode = { > > + .address = 0xd4, > > + }, > > + .cpu = { > > + .rt_temp_address = 0x68, > > + .rt_fan_speed_address = 0xc9, > > + .rt_fan_speed_base_min = 0x19, > > + .rt_fan_speed_base_max = 0x37, > > + .bs_fan_speed_address = 0x89, // ? > > + .bs_fan_speed_base_min = 0x00, > > + .bs_fan_speed_base_max = 0x0f, > > + }, > > + .gpu = { > > + .rt_temp_address = 0x80, > > + .rt_fan_speed_address = 0x89, > > + }, > > + .leds = { > > + .micmute_led_address = 0x2b, > > + .mute_led_address = 0x2c, > > + .bit = 1, > > + }, > > + .kbd_bl = { > > + .bl_mode_address = 0x2c, // ? > > + .bl_modes = { 0x00, 0x08 }, // ? > > + .max_mode = 1, // ? > > + .bl_state_address = 0xd3, > > + .state_base_value = 0x80, > > + .max_state = 3, > > + }, > > +}; > > + > > +static struct msi_ec_conf *CONFIGURATIONS[] __initdata = { > > + &CONF0, > > + &CONF1, > > + &CONF2, > > + &CONF3, > > + NULL, > > +}; > > + > > +static struct msi_ec_conf conf; // current configuration > > + > > +// ============================================================ // > > +// Helper functions > > +// ============================================================ // > > + > > +static int ec_read_seq(u8 addr, u8 *buf, int len) > > +{ > > + int result; > > + u8 i; > > + for (i = 0; i < len; i++) { > > + result = ec_read(addr + i, buf + i); > > + if (result < 0) > > + return result; > > + } > > + return 0; > > +} > > + > > +static int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1]) > > +{ > > + int result; > > + > > + memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1); > > + result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS, > > + buf, > > + MSI_EC_FW_VERSION_LENGTH); > > + if (result < 0) > > + return result; > > + > > + return MSI_EC_FW_VERSION_LENGTH + 1; > > +} > > + > > +// ============================================================ // > > +// Sysfs power_supply subsystem > > +// ============================================================ // > > + > > +static ssize_t charge_control_threshold_show(u8 offset, struct device *device, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + u8 rdata; > > + int result; > > + > > + result = ec_read(conf.charge_control.address, &rdata); > > + if (result < 0) > > + return result; > > + > > + return sprintf(buf, "%i\n", rdata - offset); > > +} > > + > > +static ssize_t charge_control_threshold_store(u8 offset, struct device *dev, > > + struct device_attribute *attr, > > + const char *buf, size_t count) > > +{ > > + u8 wdata; > > + int result; > > + > > + result = kstrtou8(buf, 10, &wdata); > > + if (result < 0) > > + return result; > > + > > + wdata += offset; > > + if (wdata < conf.charge_control.range_min || > > + wdata > conf.charge_control.range_max) > > + return -EINVAL; > > + > > + result = ec_write(conf.charge_control.address, wdata); > > + if (result < 0) > > + return result; > > + > > + return count; > > +} > > + > > +static ssize_t > > +charge_control_start_threshold_show(struct device *device, > > + struct device_attribute *attr, char *buf) > > +{ > > + return charge_control_threshold_show(conf.charge_control.offset_start, > > + device, attr, buf); > > +} > > + > > +static ssize_t > > +charge_control_start_threshold_store(struct device *dev, > > + struct device_attribute *attr, > > + const char *buf, size_t count) > > +{ > > + return charge_control_threshold_store(conf.charge_control.offset_start, > > + dev, attr, buf, count); > > +} > > + > > +static ssize_t charge_control_end_threshold_show(struct device *device, > > + struct device_attribute *attr, > > + char *buf) > > +{ > > + return charge_control_threshold_show(conf.charge_control.offset_end, > > + device, attr, buf); > > +} > > + > > +static ssize_t charge_control_end_threshold_store(struct device *dev, > > + struct device_attribute *attr, > > + const char *buf, size_t count) > > +{ > > + return charge_control_threshold_store(conf.charge_control.offset_end, > > + dev, attr, buf, count); > > +} > > + > > +static DEVICE_ATTR_RW(charge_control_start_threshold); > > +static DEVICE_ATTR_RW(charge_control_end_threshold); > > + > > +static struct attribute *msi_battery_attrs[] = { > > + &dev_attr_charge_control_start_threshold.attr, > > + &dev_attr_charge_control_end_threshold.attr, > > + NULL, > > +}; > > + > > +ATTRIBUTE_GROUPS(msi_battery); > > + > > +static int msi_battery_add(struct power_supply *battery, > > + struct acpi_battery_hook *hook) > > +{ > > + if (device_add_groups(&battery->dev, msi_battery_groups)) > > + return -ENODEV; > > + return 0; > > +} > > + > > +static int msi_battery_remove(struct power_supply *battery, > > + struct acpi_battery_hook *hook) > > +{ > > + device_remove_groups(&battery->dev, msi_battery_groups); > > + return 0; > > +} > > + > > +static struct acpi_battery_hook battery_hook = { > > + .add_battery = msi_battery_add, > > + .remove_battery = msi_battery_remove, > > + .name = MSI_EC_DRIVER_NAME, > > +}; > > + > > +// ============================================================ // > > +// Module load/unload > > +// ============================================================ // > > + > > +static int __init load_configuration(void) > > +{ > > + int result; > > + > > + // get firmware version > > + u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1]; > > + result = ec_get_firmware_version(fw_version); > > + if (result < 0) { > > + return result; > > + } > > + > > + // load the suitable configuration, if exists > > + for (int i = 0; CONFIGURATIONS[i]; i++) { > > + for (int j = 0; CONFIGURATIONS[i]->allowed_fw[j]; j++) { > > + if (strcmp(CONFIGURATIONS[i]->allowed_fw[j], fw_version) == 0) { > > + memcpy(&conf, CONFIGURATIONS[i], sizeof(struct msi_ec_conf)); > > + conf.allowed_fw = NULL; > > + return 0; > > + } > > + } > > + } > > + > > + pr_err("Your firmware version is not supported!\n"); > > + return -EOPNOTSUPP; > > +} > > + > > +static int __init msi_ec_init(void) > > +{ > > + int result; > > + > > + if (acpi_disabled) { > > + pr_err("Unable to init because ACPI needs to be enabled first!\n"); > > + return -ENODEV; > > + } > > + > > + result = load_configuration(); > > + if (result < 0) > > + return result; > > + > > + battery_hook_register(&battery_hook); > > + > > + pr_info("msi-ec: module_init\n"); > > + return 0; > > +} > > + > > +static void __exit msi_ec_exit(void) > > +{ > > + battery_hook_unregister(&battery_hook); > > + > > + pr_info("msi-ec: module_exit\n"); > > +} > > + > > +MODULE_LICENSE("GPL"); > > +MODULE_AUTHOR("Jose Angel Pastrana <japp0005@xxxxxxxxxxxx <mailto:japp0005@xxxxxxxxxxxx>>"); > > +MODULE_AUTHOR("Aakash Singh <mail@xxxxxxxxxxxxxxx <mailto:mail@xxxxxxxxxxxxxxx>>"); > > +MODULE_AUTHOR("Nikita Kravets <teackot@xxxxxxxxx <mailto:teackot@xxxxxxxxx>>"); > > +MODULE_DESCRIPTION("MSI Embedded Controller"); > > + > > +module_init(msi_ec_init); > > +module_exit(msi_ec_exit); > > diff --git a/drivers/platform/x86/msi-ec.h b/drivers/platform/x86/msi-ec.h > > new file mode 100644 > > index 000000000000..4de6bba363ff > > --- /dev/null > > +++ b/drivers/platform/x86/msi-ec.h > > @@ -0,0 +1,119 @@ > > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > > + > > +/* > > + * msi-ec: MSI laptops' embedded controller driver. > > + * > > + * Copyright (C) 2023 Jose Angel Pastrana <japp0005@xxxxxxxxxxxx <mailto:japp0005@xxxxxxxxxxxx>> > > + * Copyright (C) 2023 Aakash Singh <mail@xxxxxxxxxxxxxxx <mailto:mail@xxxxxxxxxxxxxxx>> > > + * Copyright (C) 2023 Nikita Kravets <teackot@xxxxxxxxx <mailto:teackot@xxxxxxxxx>> > > + */ > > + > > +#ifndef _MSI_EC_H_ > > +#define _MSI_EC_H_ > > + > > +#include <linux/types.h> > > + > > +#define MSI_EC_DRIVER_NAME "msi-ec" > > + > > +// Firmware info addresses are universal > > +#define MSI_EC_FW_VERSION_ADDRESS 0xa0 > > +#define MSI_EC_FW_DATE_ADDRESS 0xac > > +#define MSI_EC_FW_TIME_ADDRESS 0xb4 > > +#define MSI_EC_FW_VERSION_LENGTH 12 > > +#define MSI_EC_FW_DATE_LENGTH 8 > > +#define MSI_EC_FW_TIME_LENGTH 8 > > + > > +struct msi_ec_charge_control_conf { > > + int address; > > + int offset_start; > > + int offset_end; > > + int range_min; > > + int range_max; > > +}; > > + > > +struct msi_ec_webcam_conf { > > + int address; > > + int block_address; > > + int bit; > > +}; > > + > > +struct msi_ec_fn_super_swap_conf { > > + int address; > > + int bit; > > +}; > > + > > +struct msi_ec_cooler_boost_conf { > > + int address; > > + int bit; > > +}; > > + > > +struct msi_ec_mode { > > + const char *name; > > + int value; > > +}; > > + > > +struct msi_ec_shift_mode_conf { > > + int address; > > + struct msi_ec_mode modes[5]; // fixed size for easier hard coding > > + int modes_count; > > +}; > > + > > +struct msi_ec_super_battery_conf { > > + bool supported; > > + int address; > > + int mask; > > +}; > > + > > +struct msi_ec_fan_mode_conf { > > + int address; > > +}; > > + > > +struct msi_ec_cpu_conf { > > + int rt_temp_address; > > + int rt_fan_speed_address; // realtime > > + int rt_fan_speed_base_min; > > + int rt_fan_speed_base_max; > > + int bs_fan_speed_address; // basic > > + int bs_fan_speed_base_min; > > + int bs_fan_speed_base_max; > > +}; > > + > > +struct msi_ec_gpu_conf { > > + int rt_temp_address; > > + int rt_fan_speed_address; // realtime > > +}; > > + > > +struct msi_ec_led_conf { > > + int micmute_led_address; > > + int mute_led_address; > > + int bit; > > +}; > > + > > +#define MSI_EC_KBD_BL_STATE_MASK 0x3 > > +struct msi_ec_kbd_bl_conf { > > + int bl_mode_address; > > + int bl_modes[2]; > > + int max_mode; > > + > > + int bl_state_address; > > + int state_base_value; > > + int max_state; > > +}; > > + > > +struct msi_ec_conf { > > + const char **allowed_fw; > > + > > + struct msi_ec_charge_control_conf charge_control; > > + struct msi_ec_webcam_conf webcam; > > + struct msi_ec_fn_super_swap_conf fn_super_swap; > > + struct msi_ec_cooler_boost_conf cooler_boost; > > + struct msi_ec_shift_mode_conf shift_mode; > > + struct msi_ec_super_battery_conf super_battery; > > + struct msi_ec_fan_mode_conf fan_mode; > > + struct msi_ec_cpu_conf cpu; > > + struct msi_ec_gpu_conf gpu; > > + struct msi_ec_led_conf leds; > > + struct msi_ec_kbd_bl_conf kbd_bl; > > +}; > > + > > +#endif // _MSI_EC_H_ >