From 6a9a8a2994223a3c2a08385aefa16889092f31a1 Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 5 Oct 2021 18:35:23 +0100 Subject: [PATCH 01/13] Updated version of driver from https://bugzilla.kernel.org/show_bug.cgi\?id\=204807\#c128 --- asus-wmi-sensors.c | 1019 +++++++++++++++++++++++++++++++------------- 1 file changed, 715 insertions(+), 304 deletions(-) diff --git a/asus-wmi-sensors.c b/asus-wmi-sensors.c index 3b741cf..a417ea0 100644 --- a/asus-wmi-sensors.c +++ b/asus-wmi-sensors.c @@ -1,10 +1,13 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Asus WMI sensors HWMON driver + * HWMON driver for ASUS motherboards that publish some sensor values + * via the embedded controller registers * + * Copyright (C) 2021 Eugene Shalygin * Copyright (C) 2018-2019 Ed Brindley */ -#define PLATFORM_DRIVER +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define DRVNAME "asus_wmi_sensors" #include #include @@ -15,49 +18,74 @@ #include #include #include +#include #include -MODULE_AUTHOR("Ed Brindley "); -MODULE_DESCRIPTION("Asus WMI Sensors Driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("3"); - -#define ASUS_HW_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" - -#define CROSSHAIR_6 "CROSSHAIR VI HERO" -#define CROSSHAIR_6_WIFI "ROG CROSSHAIR VI HERO (WI-FI AC)" -#define CROSSHAIR_6_EXTREME "ROG CROSSHAIR VI EXTREME" -#define CROSSHAIR_7 "ROG CROSSHAIR VII HERO" -#define CROSSHAIR_7_WIFI "ROG CROSSHAIR VII HERO (WI-FI)" -#define ZENITH_EXTREME "ROG ZENITH EXTREME" -#define ZENITH_EXTREME_ALPHA "ROG ZENITH EXTREME ALPHA" -#define PRIME_X470_PRO "PRIME X470-PRO" -#define PRIME_X399_A "PRIME X399-A" -#define STRIX_X399_E "ROG STRIX X399-E GAMING" -#define STRIX_B450_E "ROG STRIX B450-E GAMING" -#define STRIX_B450_F "ROG STRIX B450-F GAMING" -#define STRIX_B450_I "ROG STRIX B450-I GAMING" -#define STRIX_X470_I "ROG STRIX X470-I GAMING" -#define STRIX_X470_F "ROG STRIX X470-F GAMING" - -#define METHODID_SENSOR_GET_VALUE 0x52574543 -#define METHODID_SENSOR_UPDATE_BUFFER 0x51574543 -#define METHODID_SENSOR_GET_INFO 0x50574543 -#define METHODID_SENSOR_GET_NUMBER 0x50574572 -#define METHODID_SENSOR_GET_BUFFER_ADDRESS 0x50574573 -#define METHODID_SENSOR_GET_VERSION 0x50574574 +#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" +#define ASUSWMI_METHODID_BLOCK_READ_EC 0x42524543 /* BREC */ +#define ASUSWMI_METHODID_GET_VALUE 0x52574543 +#define ASUSWMI_METHODID_UPDATE_BUFFER 0x51574543 +#define ASUSWMI_METHODID_GET_INFO 0x50574543 +#define ASUSWMI_METHODID_GET_NUMBER 0x50574572 +#define ASUSWMI_METHODID_GET_BUFFER_ADDRESS 0x50574573 +#define ASUSWMI_METHODID_GET_VERSION 0x50574574 #define ASUS_WMI_MAX_STR_SIZE 32 #define HWMON_MAX 9 +#define ASUS_WMI_BLOCK_READ_REGISTERS_MAX 0x10 /* from the ASUS DSDT source */ +/* from the ASUS_WMI_BLOCK_READ_REGISTERS_MAX value */ +#define ASUS_WMI_MAX_BUF_LEN 0x80 +#define MAX_SENSOR_LABEL_LENGTH 0x10 + +#define ASUSWMI_SENSORS_MAX 11 +#define ASUS_EC_KNOWN_EC_REGISTERS 14 +#define HWMON_MAX 9 + +enum asus_wmi_ec_board { + BOARD_R_C8H, // ROG Crosshair VIII Hero + BOARD_R_C8DH, // ROG Crosshair VIII Dark Hero + BOARD_R_C8F, // ROG Crosshair VIII Formula + BOARD_RS_X570_E_G, // ROG STRIX X570-E GAMING + BOARD_RS_B550_E_G, // ROG STRIX B550-E GAMING +}; + +/* boards with EC support */ +static const char *const asus_wmi_ec_boards_names[] = { + [BOARD_R_C8H] = "ROG CROSSHAIR VIII HERO", + [BOARD_R_C8DH] = "ROG CROSSHAIR VIII DARK HERO", + [BOARD_R_C8F] = "ROG CROSSHAIR VIII FORMULA", + [BOARD_RS_X570_E_G] = "ROG STRIX X570-E GAMING", + [BOARD_RS_B550_E_G] = "ROG STRIX B550-E GAMING", +}; + +/* boards with wmi sensors support */ +static const char *const asus_wmi_boards_names[] = { + "ROG CROSSHAIR VII HERO (WI-FI)", + "ROG CROSSHAIR VII HERO", + "ROG CROSSHAIR VI HERO (WI-FI AC)", + "CROSSHAIR VI HERO", + "ROG CROSSHAIR VI EXTREME", + "ROG ZENITH EXTREME", + "ROG ZENITH EXTREME ALPHA", + "PRIME X399-A", + "PRIME X470-PRO", + "ROG STRIX X399-E GAMING", + "ROG STRIX B450-E GAMING", + "ROG STRIX B450-F GAMING", + "ROG STRIX B450-I GAMING", + "ROG STRIX X470-I GAMING", + "ROG STRIX X470-F GAMING", +}; + enum asus_wmi_sensor_class { VOLTAGE = 0x0, TEMPERATURE_C = 0x1, FAN_RPM = 0x2, CURRENT = 0x3, WATER_FLOW = 0x4, - //BOOL = 0x5 //TODO + // BOOL = 0x5 //TODO }; enum asus_wmi_location { @@ -74,7 +102,7 @@ enum asus_wmi_location { enum asus_wmi_type { SIGNED_INT = 0x0, UNSIGNED_INT = 0x1, - //BOOL = 0x2, //TODO + // BOOL = 0x2, //TODO SCALED = 0x3, }; @@ -109,42 +137,163 @@ struct asus_wmi_sensor_info { u32 cached_value; }; -struct asus_wmi_sensors { - #ifdef PLATFORM_DRIVER - struct platform_driver platform_driver; - struct platform_device *platform_device; - #else - struct wmi_driver wmi_driver; - struct wmi_device *wmi_device; - #endif +union asus_wmi_ec_sensor_address { + u32 value; + struct { + u8 index; + u8 bank; + u8 size; + u8 dummy; + } addr; +}; + +struct asus_wmi_ec_sensor_info { + char label[MAX_SENSOR_LABEL_LENGTH]; + enum hwmon_sensor_types type; + union asus_wmi_ec_sensor_address addr; + u32 cached_value; +}; + +struct asus_wmi_ec_info { + struct asus_wmi_ec_sensor_info sensors[ASUSWMI_SENSORS_MAX]; + /* UTF-16 string to pass to BRxx() WMI function */ + char read_arg[((ASUS_WMI_BLOCK_READ_REGISTERS_MAX * 4) + 1) * 2]; + u8 read_buffer[ASUS_WMI_BLOCK_READ_REGISTERS_MAX]; + u8 nr_sensors; /* number of board EC sensors */ + /* number of EC registers to read (sensor might span more than 1 register) */ + u8 nr_registers; + unsigned long last_updated; /* in jiffies */ +}; +struct asus_wmi_wmi_info { u8 buffer; unsigned long source_last_updated[3]; /* in jiffies */ u8 sensor_count; - struct mutex lock; const struct asus_wmi_sensor_info **info[HWMON_MAX]; struct asus_wmi_sensor_info **info_by_id; }; +struct asus_wmi_sensors { + /* lock access to instrnal cache */ + struct mutex lock; + struct asus_wmi_ec_info ec; + struct asus_wmi_wmi_info wmi; + + int ec_board; + int wmi_board; +}; + +struct asus_wmi_data { + int ec_board; + int wmi_board; + int wmi_count; +}; + +static inline union asus_wmi_ec_sensor_address asus_wmi_ec_make_sensor_address(u8 size, + u8 bank, + u8 index) +{ + union asus_wmi_ec_sensor_address res; + + res.value = (size << 16) + (bank << 8) + index; + return res; +} + +static inline void asus_wmi_ec_set_sensor_info(struct asus_wmi_ec_sensor_info *sensor_info, + const char *label, + enum hwmon_sensor_types type, + union asus_wmi_ec_sensor_address addr, + u8 *nr_regs) +{ + sensor_info->type = type; + strcpy(sensor_info->label, label); + sensor_info->cached_value = 0; + sensor_info->addr.value = addr.value; + *nr_regs += sensor_info->addr.addr.size; +} + +static void asus_wmi_ec_fill_board_sensors(struct asus_wmi_ec_info *ec, int board) +{ + struct asus_wmi_ec_sensor_info *si; + + si = ec->sensors; + ec->nr_registers = 0; + + switch (board) { + case BOARD_RS_B550_E_G: + case BOARD_RS_X570_E_G: + case BOARD_R_C8H: + case BOARD_R_C8DH: + case BOARD_R_C8F: + asus_wmi_ec_set_sensor_info(si++, "Chipset", hwmon_temp, + asus_wmi_ec_make_sensor_address(1, 0x00, 0x3A), + &ec->nr_registers); + asus_wmi_ec_set_sensor_info(si++, "CPU", hwmon_temp, + asus_wmi_ec_make_sensor_address(1, 0x00, 0x3B), + &ec->nr_registers); + asus_wmi_ec_set_sensor_info(si++, "Motherboard", hwmon_temp, + asus_wmi_ec_make_sensor_address(1, 0x00, 0x3C), + &ec->nr_registers); + asus_wmi_ec_set_sensor_info(si++, "T_Sensor", hwmon_temp, + asus_wmi_ec_make_sensor_address(1, 0x00, 0x3D), + &ec->nr_registers); + asus_wmi_ec_set_sensor_info(si++, "VRM", hwmon_temp, + asus_wmi_ec_make_sensor_address(1, 0x00, 0x3E), + &ec->nr_registers); + } + + switch (board) { + case BOARD_RS_X570_E_G: + case BOARD_R_C8H: + case BOARD_R_C8DH: + case BOARD_R_C8F: + asus_wmi_ec_set_sensor_info(si++, "CPU_Opt", hwmon_fan, + asus_wmi_ec_make_sensor_address(2, 0x00, 0xB0), + &ec->nr_registers); + asus_wmi_ec_set_sensor_info(si++, "CPU", hwmon_curr, + asus_wmi_ec_make_sensor_address(1, 0x00, 0xF4), + &ec->nr_registers); + } + + switch (board) { + case BOARD_RS_X570_E_G: + case BOARD_R_C8H: + case BOARD_R_C8F: + asus_wmi_ec_set_sensor_info(si++, "Chipset", hwmon_fan, + asus_wmi_ec_make_sensor_address(2, 0x00, 0xB4), + &ec->nr_registers); + } + + switch (board) { + case BOARD_R_C8H: + case BOARD_R_C8DH: + case BOARD_R_C8F: + asus_wmi_ec_set_sensor_info(si++, "Water", hwmon_fan, + asus_wmi_ec_make_sensor_address(2, 0x00, 0xBC), + &ec->nr_registers); + asus_wmi_ec_set_sensor_info(si++, "Water_In", hwmon_temp, + asus_wmi_ec_make_sensor_address(1, 0x01, 0x00), + &ec->nr_registers); + asus_wmi_ec_set_sensor_info(si++, "Water_Out", hwmon_temp, + asus_wmi_ec_make_sensor_address(1, 0x01, 0x01), + &ec->nr_registers); + } + + ec->nr_sensors = si - ec->sensors; +} + /* * Universal method for calling WMI method - * @method_id: - * @args: - * @output: */ -static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *output) +static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *output) { struct acpi_buffer input = {(acpi_size) sizeof(*args), args }; - acpi_status status; - status = wmi_evaluate_method(ASUS_HW_GUID, - 0, - method_id, - &input, output); - if (ACPI_FAILURE(status)) { + + status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0, method_id, &input, output); + if (ACPI_FAILURE(status)) return -EIO; - } return 0; } @@ -152,18 +301,17 @@ static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *ou /* * Gets the version of the ASUS sensors interface implemented */ -static int get_version(u32 *version) +static int asus_wmi_get_version(u32 *version) { u32 args[] = {0, 0, 0}; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - int status = asus_wmi_call_method(METHODID_SENSOR_GET_VERSION, args, &output); + int status = asus_wmi_call_method(ASUSWMI_METHODID_GET_VERSION, args, &output); if (!status) { union acpi_object *obj = (union acpi_object *)output.pointer; - if (obj && obj->type == ACPI_TYPE_INTEGER) { - *version = obj->integer.value; - } + if (obj && obj->type == ACPI_TYPE_INTEGER) + *version = obj->integer.value; } return status; } @@ -171,33 +319,351 @@ static int get_version(u32 *version) /* * Gets the number of sensor items */ -static int get_item_count(u32 *count) +static int asus_wmi_get_item_count(u32 *count) { u32 args[] = {0, 0, 0}; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - int status = asus_wmi_call_method(METHODID_SENSOR_GET_NUMBER, args, &output); + int status = asus_wmi_call_method(ASUSWMI_METHODID_GET_NUMBER, args, &output); if (!status) { union acpi_object *obj = (union acpi_object *)output.pointer; - if (obj && obj->type == ACPI_TYPE_INTEGER) { - *count = obj->integer.value; - } + if (obj && obj->type == ACPI_TYPE_INTEGER) + *count = obj->integer.value; } return status; } +/* + * The next four functions converts to/from BRxx string argument format + * The format of the string is as follows: + * The string consists of two-byte UTF-16 characters + * The value of the very first byte int the string is equal to the total length + * of the next string in bytes, thus excluding the first two-byte character + * The rest of the string encodes pairs of (bank, index) pairs, where both + * values are byte-long (0x00 to 0xFF) + * Numbers are encoded as UTF-16 hex values + */ + +static inline char *asus_wmi_ec_hex_utf_16_le_pack(char *buf, u8 byte) +{ + *buf++ = hex_asc_hi(byte); + *buf++ = 0; + *buf++ = hex_asc_lo(byte); + *buf++ = 0; + return buf; +} + +static void asus_wmi_ec_decode_reply_buffer(const u8 *inp, u8 *out) +{ + u8 len = ACPI_MIN(ASUS_WMI_MAX_BUF_LEN, inp[0] / 4); + const u8 *data = inp + 2; + u8 i; + + for (i = 0; i < len; ++i, data += 4) + out[i] = (hex_to_bin(data[0]) << 4) + hex_to_bin(data[2]); +} + +static void asus_wmi_ec_encode_registers(u16 *registers, u8 len, char *out) +{ + u8 i; + + // assert(len <= 30) + *out++ = len * 8; + *out++ = 0; + for (i = 0; i < len; ++i) { + out = asus_wmi_ec_hex_utf_16_le_pack(out, (registers[i] & 0xFF00) >> 8); + out = asus_wmi_ec_hex_utf_16_le_pack(out, (registers[i] & 0x00FF)); + } +} + +static void asus_wmi_ec_make_block_read_query(struct asus_wmi_ec_info *ec) +{ + u16 registers[ASUS_EC_KNOWN_EC_REGISTERS]; + u8 i, j, register_idx = 0; + + /* if we can get values for all the registers in a single query, + * the query will not change from call to call + */ + if (ec->nr_registers <= ASUS_WMI_BLOCK_READ_REGISTERS_MAX && + ec->read_arg[0] > 0) { + /* no need to update */ + return; + } + + for (i = 0; i < ec->nr_sensors; ++i) { + for (j = 0; j < ec->sensors[i].addr.addr.size; + ++j, ++register_idx) { + registers[register_idx] = + (ec->sensors[i].addr.addr.bank << 8) + + ec->sensors[i].addr.addr.index + j; + } + } + + asus_wmi_ec_encode_registers(registers, ec->nr_registers, ec->read_arg); +} + +static int asus_wmi_ec_block_read(u32 method_id, const char *query, u8 *out) +{ + struct acpi_buffer input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, + NULL }; // TODO use pre-allocated buffer + acpi_status status; + union acpi_object *obj; + + /* the first byte of the BRxx() argument string has to be the string size */ + input.length = (acpi_size)query[0] + 2; + input.pointer = (void *)query; + status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0, method_id, &input, + &output); + + if (ACPI_FAILURE(status)) { + acpi_os_free(output.pointer); + return -EIO; + } + + obj = output.pointer; + if (!obj || obj->type != ACPI_TYPE_BUFFER) { + pr_err("unexpected reply type from ASUS ACPI code"); + acpi_os_free(output.pointer); + return -EIO; + } + asus_wmi_ec_decode_reply_buffer(obj->buffer.pointer, out); + acpi_os_free(output.pointer); + return 0; +} + +static int asus_wmi_ec_update_ec_sensors(struct asus_wmi_ec_info *ec) +{ + struct asus_wmi_ec_sensor_info *si; + u32 value; + int status; + u8 i_sensor, read_reg_ct, i_sensor_register; + + asus_wmi_ec_make_block_read_query(ec); + status = asus_wmi_ec_block_read(ASUSWMI_METHODID_BLOCK_READ_EC, + ec->read_arg, + ec->read_buffer); + if (status) + return status; + + read_reg_ct = 0; + for (i_sensor = 0; i_sensor < ec->nr_sensors; ++i_sensor) { + si = &ec->sensors[i_sensor]; + value = ec->read_buffer[read_reg_ct++]; + for (i_sensor_register = 1; + i_sensor_register < si->addr.addr.size; + ++i_sensor_register) { + value <<= 8; + value += ec->read_buffer[read_reg_ct++]; + } + si->cached_value = value; + } + return 0; +} + +static int asus_wmi_ec_scale_sensor_value(u32 value, int data_type) +{ + switch (data_type) { + case hwmon_curr: + case hwmon_temp: + case hwmon_in: + return value * 1000; + default: + return value; + } +} + +static u8 asus_wmi_ec_find_sensor_index(const struct asus_wmi_ec_info *ec, + enum hwmon_sensor_types type, int channel) +{ + u8 i; + + for (i = 0; i < ec->nr_sensors; ++i) { + if (ec->sensors[i].type == type) { + if (channel == 0) + return i; + + --channel; + } + } + return 0xFF; +} + +static int asus_wmi_ec_get_cached_value_or_update(int sensor_index, + struct asus_wmi_sensors *state, + u32 *value) +{ + int ret; + + if (time_after(jiffies, state->ec.last_updated + HZ)) { + ret = asus_wmi_ec_update_ec_sensors(&state->ec); + + if (ret) { + pr_err("asus_wmi_ec_update_ec_sensors() failure\n"); + return -EIO; + } + + state->ec.last_updated = jiffies; + } + + *value = state->ec.sensors[sensor_index].cached_value; + return 0; +} + +/* + * Now follow the functions that implement the hwmon interface + */ + +static int asus_wmi_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + int ret; + u32 value = 0; + struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); + + u8 sidx = asus_wmi_ec_find_sensor_index(&sensor_data->ec, type, channel); + + mutex_lock(&sensor_data->lock); + + ret = asus_wmi_ec_get_cached_value_or_update(sidx, sensor_data, &value); + mutex_unlock(&sensor_data->lock); + + if (!ret) + *val = asus_wmi_ec_scale_sensor_value(value, sensor_data->ec.sensors[sidx].type); + + return ret; +} + +static int asus_wmi_ec_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); + + u8 sensor_index = asus_wmi_ec_find_sensor_index(&sensor_data->ec, type, channel); + *str = sensor_data->ec.sensors[sensor_index].label; + + return 0; +} + +static umode_t asus_wmi_ec_hwmon_is_visible(const void *drvdata, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + const struct asus_wmi_sensors *sensor_data = drvdata; + + return asus_wmi_ec_find_sensor_index(&sensor_data->ec, type, channel) != 0xFF ? + 0444 : + 0; +} + +static int asus_wmi_hwmon_add_chan_info(struct hwmon_channel_info *asus_wmi_hwmon_chan, + struct device *dev, int num, + enum hwmon_sensor_types type, u32 config) +{ + int i; + u32 *cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL); + + if (!cfg) + return -ENOMEM; + + asus_wmi_hwmon_chan->type = type; + asus_wmi_hwmon_chan->config = cfg; + for (i = 0; i < num; i++, cfg++) + *cfg = config; + + return 0; +} + +static const struct hwmon_ops asus_wmi_ec_hwmon_ops = { + .is_visible = asus_wmi_ec_hwmon_is_visible, + .read = asus_wmi_ec_hwmon_read, + .read_string = asus_wmi_ec_hwmon_read_string, +}; + +static struct hwmon_chip_info asus_wmi_ec_chip_info = { + .ops = &asus_wmi_ec_hwmon_ops, + .info = NULL, +}; + +static int asus_wmi_ec_configure_sensor_setup(struct platform_device *pdev, + struct asus_wmi_sensors *sensor_data) +{ + int i; + int nr_count[HWMON_MAX] = { 0 }, nr_types = 0; + struct device *hwdev; + struct device *dev = &pdev->dev; + struct hwmon_channel_info *asus_wmi_hwmon_chan; + const struct hwmon_channel_info **ptr_asus_wmi_ci; + const struct hwmon_chip_info *chip_info; + const struct asus_wmi_ec_sensor_info *si; + enum hwmon_sensor_types type; + + if (sensor_data->ec_board < 0) + return 0; + + asus_wmi_ec_fill_board_sensors(&sensor_data->ec, sensor_data->ec_board); + + if (!sensor_data->ec.nr_sensors) + return -ENODEV; + + for (i = 0; i < sensor_data->ec.nr_sensors; ++i) { + si = &sensor_data->ec.sensors[i]; + if (!nr_count[si->type]) + ++nr_types; + ++nr_count[si->type]; + } + + if (nr_count[hwmon_temp]) + nr_count[hwmon_chip]++, nr_types++; + + asus_wmi_hwmon_chan = devm_kcalloc(dev, nr_types, + sizeof(*asus_wmi_hwmon_chan), + GFP_KERNEL); + if (!asus_wmi_hwmon_chan) + return -ENOMEM; + + ptr_asus_wmi_ci = devm_kcalloc(dev, nr_types + 1, + sizeof(*ptr_asus_wmi_ci), GFP_KERNEL); + if (!ptr_asus_wmi_ci) + return -ENOMEM; + + asus_wmi_ec_chip_info.info = ptr_asus_wmi_ci; + chip_info = &asus_wmi_ec_chip_info; + + for (type = 0; type < HWMON_MAX; type++) { + if (!nr_count[type]) + continue; + + asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev, + nr_count[type], type, + hwmon_attributes[type]); + *ptr_asus_wmi_ci++ = asus_wmi_hwmon_chan++; + } + + pr_info("%s board has %d EC sensors that span %d registers", + asus_wmi_ec_boards_names[sensor_data->ec_board], + sensor_data->ec.nr_sensors, + sensor_data->ec.nr_registers); + + hwdev = devm_hwmon_device_register_with_info(dev, "asuswmiecsensors", + sensor_data, chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwdev); +} + /* * For a given sensor item returns details e.g. type (voltage/temperature/fan speed etc), bank etc */ -static int info(int index, struct asus_wmi_sensor_info *s) +static int asus_wmi_sensor_info(int index, struct asus_wmi_sensor_info *s) { u32 args[] = {index, 0}; union acpi_object name_obj, data_type_obj, location_obj, source_obj, type_obj; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; - int status = asus_wmi_call_method(METHODID_SENSOR_GET_INFO, args, &output); + int status = asus_wmi_call_method(ASUSWMI_METHODID_GET_INFO, args, &output); if (!status) { s->id = index; @@ -205,89 +671,91 @@ static int info(int index, struct asus_wmi_sensor_info *s) obj = (union acpi_object *)output.pointer; if (obj && obj->type == ACPI_TYPE_PACKAGE) { - if(obj->package.count != 5) { + if (obj->package.count != 5) return 1; - } + name_obj = obj->package.elements[0]; - if (name_obj.type != ACPI_TYPE_STRING) { + if (name_obj.type != ACPI_TYPE_STRING) return 1; - } - strncpy(s->name, name_obj.string.pointer, sizeof s->name - 1); + + strncpy(s->name, name_obj.string.pointer, sizeof(s->name) - 1); data_type_obj = obj->package.elements[1]; - if (data_type_obj.type != ACPI_TYPE_INTEGER) { + if (data_type_obj.type != ACPI_TYPE_INTEGER) return 1; - } + s->data_type = data_type_obj.integer.value; location_obj = obj->package.elements[2]; - if (location_obj.type != ACPI_TYPE_INTEGER) { + if (location_obj.type != ACPI_TYPE_INTEGER) return 1; - } + s->location = location_obj.integer.value; source_obj = obj->package.elements[3]; - if (source_obj.type != ACPI_TYPE_INTEGER) { + if (source_obj.type != ACPI_TYPE_INTEGER) return 1; - } + s->source = source_obj.integer.value; type_obj = obj->package.elements[4]; - if (type_obj.type != ACPI_TYPE_INTEGER) { + if (type_obj.type != ACPI_TYPE_INTEGER) return 1; - } + s->type = type_obj.integer.value; } } return status; } -static int update_buffer(u8 source) +static int asus_wmi_update_buffer(u8 source) { u32 args[] = {source, 0}; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - return asus_wmi_call_method(METHODID_SENSOR_UPDATE_BUFFER, args, &output); + + return asus_wmi_call_method(ASUSWMI_METHODID_UPDATE_BUFFER, args, &output); } -static int get_sensor_value(u8 index, u32 *value) +static int asus_wmi_get_sensor_value(u8 index, u32 *value) { u32 args[] = {index, 0}; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - int status = asus_wmi_call_method(METHODID_SENSOR_GET_VALUE, args, &output); + int status = asus_wmi_call_method(ASUSWMI_METHODID_GET_VALUE, args, &output); if (!status) { union acpi_object *obj = (union acpi_object *)output.pointer; - if (obj && obj->type == ACPI_TYPE_INTEGER) { + if (obj && obj->type == ACPI_TYPE_INTEGER) *value = obj->integer.value; - } } return status; } -static void update_values_for_source(u8 source, struct asus_wmi_sensors *asus_wmi_sensors) { +static void asus_wmi_update_values_for_source(u8 source, struct asus_wmi_sensors *sensor_data) +{ int ret = 0; int value = 0; int i; struct asus_wmi_sensor_info *sensor; - for (i = 0; i < asus_wmi_sensors->sensor_count;i++) { - sensor = asus_wmi_sensors->info_by_id[i]; - if(sensor && sensor->source == source) { - ret = get_sensor_value(sensor->id, &value); - if (!ret) { + for (i = 0; i < sensor_data->wmi.sensor_count; i++) { + sensor = sensor_data->wmi.info_by_id[i]; + if (sensor && sensor->source == source) { + ret = asus_wmi_get_sensor_value(sensor->id, &value); + if (!ret) sensor->cached_value = value; - } } } } -static int scale_sensor_value(u32 value, int data_type) { +static int asus_wmi_scale_sensor_value(u32 value, int data_type) +{ + /* FAN_RPM and WATER_FLOW don't need scaling */ switch (data_type) { case VOLTAGE: return DIV_ROUND_CLOSEST(value, 1000); @@ -296,97 +764,79 @@ static int scale_sensor_value(u32 value, int data_type) { case CURRENT: return value * 1000; } - return value; // FAN_RPM and WATER_FLOW don't need scaling + return value; } -static int get_cached_value_or_update(const struct asus_wmi_sensor_info *sensor, struct asus_wmi_sensors *asus_wmi_sensors, u32 *value) { +static int asus_wmi_get_cached_value_or_update(const struct asus_wmi_sensor_info *sensor, + struct asus_wmi_sensors *sensor_data, + u32 *value) +{ int ret; - if (time_after(jiffies, asus_wmi_sensors->source_last_updated[sensor->source] + HZ)) { - ret = update_buffer(sensor->source); - - if (ret) { - pr_err("update_buffer failure\n"); + if (time_after(jiffies, sensor_data->wmi.source_last_updated[sensor->source] + HZ)) { + ret = asus_wmi_update_buffer(sensor->source); + if (ret) return -EIO; - } - asus_wmi_sensors->buffer = sensor->source; - update_values_for_source(sensor->source, asus_wmi_sensors); - asus_wmi_sensors->source_last_updated[sensor->source] = jiffies; + sensor_data->wmi.buffer = sensor->source; + + asus_wmi_update_values_for_source(sensor->source, sensor_data); + sensor_data->wmi.source_last_updated[sensor->source] = jiffies; } *value = sensor->cached_value; return 0; } -/* +/* * Now follow the functions that implement the hwmon interface */ static int asus_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long *val) + u32 attr, int channel, long *val) { int ret; u32 value = 0; const struct asus_wmi_sensor_info *sensor; - struct asus_wmi_sensors *asus_wmi_sensors = dev_get_drvdata(dev); + struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); - sensor = *(asus_wmi_sensors->info[type] + channel); + sensor = *(sensor_data->wmi.info[type] + channel); - mutex_lock(&asus_wmi_sensors->lock); + mutex_lock(&sensor_data->lock); - ret = get_cached_value_or_update(sensor, asus_wmi_sensors, &value); - mutex_unlock(&asus_wmi_sensors->lock); + ret = asus_wmi_get_cached_value_or_update(sensor, sensor_data, &value); + mutex_unlock(&sensor_data->lock); - if (!ret) { - *val = scale_sensor_value(value, sensor->data_type); - } + if (!ret) + *val = asus_wmi_scale_sensor_value(value, sensor->data_type); return ret; } -static int -asus_wmi_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, const char **str) +static int asus_wmi_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) { const struct asus_wmi_sensor_info *sensor; - struct asus_wmi_sensors *asus_wmi_sensors = dev_get_drvdata(dev); + struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); - sensor = *(asus_wmi_sensors->info[type] + channel); + sensor = *(sensor_data->wmi.info[type] + channel); *str = sensor->name; return 0; } -static umode_t -asus_wmi_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type, - u32 attr, int channel) +static umode_t asus_wmi_hwmon_is_visible(const void *drvdata, + enum hwmon_sensor_types type, u32 attr, + int channel) { const struct asus_wmi_sensor_info *sensor; - const struct asus_wmi_sensors *asus_wmi_sensors = drvdata; + const struct asus_wmi_sensors *sensor_data = drvdata; - sensor = *(asus_wmi_sensors->info[type] + channel); + sensor = *(sensor_data->wmi.info[type] + channel); if (sensor && sensor->name) - return S_IRUGO; - - return 0; -} - -static int asus_wmi_hwmon_add_chan_info(struct hwmon_channel_info *asus_wmi_hwmon_chan, - struct device *dev, int num, - enum hwmon_sensor_types type, u32 config) -{ - int i; - u32 *cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL); - - if (!cfg) - return -ENOMEM; - - asus_wmi_hwmon_chan->type = type; - asus_wmi_hwmon_chan->config = cfg; - for (i = 0; i < num; i++, cfg++) - *cfg = config; + return 0444; return 0; } @@ -402,40 +852,30 @@ static struct hwmon_chip_info asus_wmi_chip_info = { .info = NULL, }; -static int configure_sensor_setup(struct asus_wmi_sensors *asus_wmi_sensors) +static int asus_wmi_configure_sensor_setup(struct platform_device *pdev, + struct asus_wmi_sensors *sensor_data) { int err; int i, idx; int nr_count[HWMON_MAX] = {0}, nr_types = 0; - u32 nr_sensors = 0; - struct device *hwdev; - #ifdef PLATFORM_DRIVER - struct device *dev = &asus_wmi_sensors->platform_device->dev; - #else - struct device *dev = &asus_wmi_sensors->wmi_device->dev; - #endif + struct device *hwdev; + struct device *dev = &pdev->dev; struct hwmon_channel_info *asus_wmi_hwmon_chan; struct asus_wmi_sensor_info *temp_sensor; enum hwmon_sensor_types type; const struct hwmon_channel_info **ptr_asus_wmi_ci; const struct hwmon_chip_info *chip_info; - asus_wmi_sensors->buffer = -1; - mutex_init(&asus_wmi_sensors->lock); + if (sensor_data->wmi.sensor_count <= 0 || sensor_data->wmi_board < 0) + return 0; + sensor_data->wmi.buffer = -1; temp_sensor = devm_kcalloc(dev, 1, sizeof(*temp_sensor), GFP_KERNEL); - if (!temp_sensor) { - pr_err("Alloc fail\n"); + if (!temp_sensor) return -ENOMEM; - } - - get_item_count(&nr_sensors); - asus_wmi_sensors->sensor_count = nr_sensors; - pr_debug("sensor count %u\n", nr_sensors); - - for (i = 0; i < nr_sensors; i++) { - err = info(i, temp_sensor); + for (i = 0; i < sensor_data->wmi.sensor_count; i++) { + err = asus_wmi_sensor_info(i, temp_sensor); if (err) return -EINVAL; @@ -456,53 +896,56 @@ static int configure_sensor_setup(struct asus_wmi_sensors *asus_wmi_sensors) if (nr_count[hwmon_temp]) nr_count[hwmon_chip]++, nr_types++; - asus_wmi_hwmon_chan = devm_kcalloc(dev, nr_types, sizeof(*asus_wmi_hwmon_chan), - GFP_KERNEL); + asus_wmi_hwmon_chan = devm_kcalloc(dev, nr_types, + sizeof(*asus_wmi_hwmon_chan), + GFP_KERNEL); if (!asus_wmi_hwmon_chan) return -ENOMEM; - ptr_asus_wmi_ci = devm_kcalloc(dev, nr_types + 1, sizeof(*ptr_asus_wmi_ci), - GFP_KERNEL); + ptr_asus_wmi_ci = devm_kcalloc(dev, nr_types + 1, + sizeof(*ptr_asus_wmi_ci), GFP_KERNEL); if (!ptr_asus_wmi_ci) return -ENOMEM; asus_wmi_chip_info.info = ptr_asus_wmi_ci; chip_info = &asus_wmi_chip_info; - - asus_wmi_sensors->info_by_id = - devm_kcalloc(dev, nr_sensors, sizeof(*asus_wmi_sensors->info_by_id), GFP_KERNEL); - if (!asus_wmi_sensors->info_by_id) + sensor_data->wmi.info_by_id = devm_kcalloc(dev, sensor_data->wmi.sensor_count, + sizeof(*sensor_data->wmi.info_by_id), + GFP_KERNEL); + + if (!sensor_data->wmi.info_by_id) return -ENOMEM; for (type = 0; type < HWMON_MAX; type++) { if (!nr_count[type]) continue; - asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev, nr_count[type], - type, hwmon_attributes[type]); + asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev, + nr_count[type], type, + hwmon_attributes[type]); *ptr_asus_wmi_ci++ = asus_wmi_hwmon_chan++; - asus_wmi_sensors->info[type] = - devm_kcalloc(dev, nr_count[type], sizeof(*asus_wmi_sensors->info), GFP_KERNEL); - if (!asus_wmi_sensors->info[type]) + sensor_data->wmi.info[type] = devm_kcalloc(dev, + nr_count[type], + sizeof(*sensor_data->wmi.info), + GFP_KERNEL); + if (!sensor_data->wmi.info[type]) return -ENOMEM; } - for (i = nr_sensors - 1; i >= 0 ; i--) { + for (i = sensor_data->wmi.sensor_count - 1; i >= 0 ; i--) { temp_sensor = devm_kzalloc(dev, sizeof(*temp_sensor), GFP_KERNEL); - if (!temp_sensor) { - pr_err("asuswmisensors: Alloc fail\n"); + if (!temp_sensor) return -ENOMEM; - } - err = info(i, temp_sensor); + err = asus_wmi_sensor_info(i, temp_sensor); if (err) { - pr_err("asuswmisensors: sensor error\n"); + pr_err("sensor error\n"); continue; } - pr_debug("asuswmisensors: setting sensor info\n"); + pr_debug("setting sensor info\n"); switch (temp_sensor->data_type) { case TEMPERATURE_C: @@ -512,163 +955,131 @@ static int configure_sensor_setup(struct asus_wmi_sensors *asus_wmi_sensors) case WATER_FLOW: type = asus_data_types[temp_sensor->data_type]; idx = --nr_count[type]; - *(asus_wmi_sensors->info[type] + idx) = temp_sensor; - asus_wmi_sensors->info_by_id[i] = temp_sensor; + *(sensor_data->wmi.info[type] + idx) = temp_sensor; + sensor_data->wmi.info_by_id[i] = temp_sensor; break; } } + pr_info("%s board has %d sensors", + asus_wmi_boards_names[sensor_data->wmi_board], + sensor_data->wmi.sensor_count); + hwdev = devm_hwmon_device_register_with_info(dev, "asuswmisensors", - asus_wmi_sensors, chip_info, + sensor_data, chip_info, NULL); return PTR_ERR_OR_ZERO(hwdev); } -static int is_board_supported(void) { - const char *board_vendor, *board_name, *bios_version; - u32 version = 0; - - board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); - board_name = dmi_get_system_info(DMI_BOARD_NAME); - bios_version = dmi_get_system_info(DMI_BIOS_VERSION); - - if(get_version(&version)) { - pr_err("asuswmisensors: Error getting version\n"); - return -ENODEV; - } - - if (board_vendor && board_name && bios_version) { - pr_info("asuswmisensors: Vendor: %s Board: %s BIOS version: %s WMI version: %u", board_vendor, board_name, bios_version, version); - - if (version >= 3 || (version >= 2 && ( - strcmp(board_name, CROSSHAIR_7_WIFI) == 0 || - strcmp(board_name, CROSSHAIR_7) == 0 || - strcmp(board_name, CROSSHAIR_6_WIFI) == 0 || - strcmp(board_name, CROSSHAIR_6) == 0 || - strcmp(board_name, CROSSHAIR_6_EXTREME) == 0 || - strcmp(board_name, ZENITH_EXTREME) == 0 || - strcmp(board_name, ZENITH_EXTREME_ALPHA) == 0 || - strcmp(board_name, PRIME_X399_A) == 0 || - strcmp(board_name, PRIME_X470_PRO) == 0 || - strcmp(board_name, STRIX_X399_E) == 0 || - strcmp(board_name, STRIX_B450_E) == 0 || - strcmp(board_name, STRIX_B450_F) == 0 || - strcmp(board_name, STRIX_B450_I) == 0 || - strcmp(board_name, STRIX_X470_I) == 0 || - strcmp(board_name, STRIX_X470_F) == 0))) { - - pr_info("asuswmisensors: Supported board"); - return 0; - } - } - pr_info("asuswmisensors: Unsupported board"); - return -ENODEV; -} - -#ifndef PLATFORM_DRIVER - -static int asus_wmi_sensors_probe(struct wmi_device *wdev) +static int asus_wmi_probe(struct platform_device *pdev) { - struct device *dev = &wdev->dev; - struct asus_wmi_sensors *asus_wmi_sensors; - - pr_info("asuswmisensors: WMI GUID matched - probing"); - - if (is_board_supported()) { - return -ENODEV; - } + struct device *dev = &pdev->dev; + struct asus_wmi_data *data = dev_get_platdata(dev); + struct asus_wmi_sensors *sensor_data; + int err; - asus_wmi_sensors = devm_kzalloc(dev, sizeof(struct asus_wmi_sensors), GFP_KERNEL); - if (!asus_wmi_sensors) + sensor_data = devm_kzalloc(dev, sizeof(struct asus_wmi_sensors), + GFP_KERNEL); + if (!sensor_data) return -ENOMEM; - asus_wmi_sensors->wmi_device = wdev; + mutex_init(&sensor_data->lock); + sensor_data->ec_board = data->ec_board; + sensor_data->wmi_board = data->wmi_board; + sensor_data->wmi.sensor_count = data->wmi_count; - dev_set_drvdata(dev, asus_wmi_sensors); - return configure_sensor_setup(asus_wmi_sensors); -} + platform_set_drvdata(pdev, sensor_data); -static int asus_wmi_sensors_remove(struct wmi_device *wdev) -{ - struct asus_wmi_sensors *asus; + /* ec init */ + err = asus_wmi_ec_configure_sensor_setup(pdev, + sensor_data); + if (err) + return err; - asus = dev_get_drvdata(&wdev->dev); + /* old version */ + err = asus_wmi_configure_sensor_setup(pdev, + sensor_data); - return 0; + return err; } -static const struct wmi_device_id asus_wmi_sensors_id_table[] = { - { .guid_string = ASUS_HW_GUID }, - { }, -}; - -static struct wmi_driver asus_wmi_sensors = { +static struct platform_driver asus_wmi_sensors_platform_driver = { .driver = { - .name = "asus-wmi-sensors", + .name = "asus-wmi-sensors", }, - .probe = asus_wmi_sensors_probe, - .remove = asus_wmi_sensors_remove, - .id_table = asus_wmi_sensors_id_table, + .probe = asus_wmi_probe }; -module_wmi_driver(asus_wmi_sensors); -#endif +static struct platform_device *sensors_pdev; -#ifdef PLATFORM_DRIVER -static struct platform_device *asus_wmi_sensors_platform_device; - -static int asus_wmi_probe(struct platform_device *pdev) +static int __init asus_wmi_init(void) { - if (!wmi_has_guid(ASUS_HW_GUID)) { - pr_info("asuswmisensors: ASUSHW GUID not found\n"); - return -ENODEV; - } + const char *board_vendor, *board_name; + u32 version = 0; + struct asus_wmi_data data; - if (is_board_supported()) { - return -ENODEV; - } + data.wmi_board = -1; + data.wmi_count = 0; + data.ec_board = -1; - pr_info("asuswmisensors: ASUS WMI sensors driver loaded\n"); - return 0; -} + board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); + board_name = dmi_get_system_info(DMI_BOARD_NAME); -static struct platform_driver asus_wmi_sensors_platform_driver = { - .driver = { - .name = "asus-wmi-sensors", - }, - .probe = asus_wmi_probe -}; + if (board_vendor && board_name && + !strcmp(board_vendor, "ASUSTeK COMPUTER INC.")) { + if (!wmi_has_guid(ASUSWMI_MONITORING_GUID)) + return -ENODEV; -static int __init asus_wmi_init(void) -{ - struct asus_wmi_sensors *asus_wmi_sensors; + data.ec_board = match_string(asus_wmi_ec_boards_names, + ARRAY_SIZE(asus_wmi_ec_boards_names), + board_name); + data.wmi_board = match_string(asus_wmi_boards_names, + ARRAY_SIZE(asus_wmi_boards_names), + board_name); - asus_wmi_sensors_platform_device = platform_create_bundle(&asus_wmi_sensors_platform_driver, - asus_wmi_probe, - NULL, 0, NULL, 0); + if (data.wmi_board >= 0) { + if (asus_wmi_get_item_count(&data.wmi_count)) + return -ENODEV; - if (IS_ERR(asus_wmi_sensors_platform_device)) - return PTR_ERR(asus_wmi_sensors_platform_device); + if (asus_wmi_get_version(&version)) + return -ENODEV; - asus_wmi_sensors = devm_kzalloc(&asus_wmi_sensors_platform_device->dev, sizeof(struct asus_wmi_sensors), GFP_KERNEL); - if (!asus_wmi_sensors) - return -ENOMEM; + if (data.wmi_count <= 0 || version < 2) { + pr_err("Board: %s WMI wmi version: %u with %d sensors is unsupported\n", + board_name, version, data.wmi_count); + + data.wmi_board = -ENODEV; + } + } + } + + /* Nothing to support */ + if (data.ec_board < 0 && data.wmi_board < 0) + return -ENODEV; - asus_wmi_sensors->platform_device = asus_wmi_sensors_platform_device; - asus_wmi_sensors->platform_driver = asus_wmi_sensors_platform_driver; + sensors_pdev = platform_create_bundle(&asus_wmi_sensors_platform_driver, + asus_wmi_probe, + NULL, 0, + &data, sizeof(struct asus_wmi_data)); - platform_set_drvdata(asus_wmi_sensors->platform_device, asus_wmi_sensors); - - return configure_sensor_setup(asus_wmi_sensors); + if (IS_ERR(sensors_pdev)) + return PTR_ERR(sensors_pdev); + + return 0; } static void __exit asus_wmi_exit(void) - { - platform_device_unregister(asus_wmi_sensors_platform_device); - platform_driver_unregister(&asus_wmi_sensors_platform_driver); - } - - module_init(asus_wmi_init); - module_exit(asus_wmi_exit); - #endif +{ + platform_device_unregister(sensors_pdev); + platform_driver_unregister(&asus_wmi_sensors_platform_driver); +} + +MODULE_AUTHOR("Ed Brindley "); +MODULE_AUTHOR("Eugene Shalygin "); +MODULE_DESCRIPTION("Asus WMI Sensors Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1"); + +module_init(asus_wmi_init); +module_exit(asus_wmi_exit); \ No newline at end of file From 7722079d0ffc4ac227fad30700492fe9ef0b329a Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 7 Oct 2021 18:21:33 +0100 Subject: [PATCH 02/13] v2 as sent to LKML. See https://lore.kernel.org/all/20211006222502.645003-4-pauk.denis@gmail.com/ --- asus-wmi-sensors.c | 720 ++++++++++----------------------------------- 1 file changed, 148 insertions(+), 572 deletions(-) diff --git a/asus-wmi-sensors.c b/asus-wmi-sensors.c index a417ea0..792872b 100644 --- a/asus-wmi-sensors.c +++ b/asus-wmi-sensors.c @@ -1,14 +1,46 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * HWMON driver for ASUS motherboards that publish some sensor values - * via the embedded controller registers + * HWMON driver for ASUS motherboards that provides sensor readouts via WMI + * interface present in the UEFI of the X370/X470/B450/X399 Ryzen motherboards. * * Copyright (C) 2021 Eugene Shalygin * Copyright (C) 2018-2019 Ed Brindley + * + * WMI interface provided: + * CPU Core Voltage, + * CPU SOC Voltage, + * DRAM Voltage, + * VDDP Voltage, + * 1.8V PLL Voltage, + * +12V Voltage, + * +5V Voltage, + * 3VSB Voltage, + * VBAT Voltage, + * AVCC3 Voltage, + * SB 1.05V Voltage, + * CPU Core Voltage, + * CPU SOC Voltage, + * DRAM Voltage, + * CPU Fan, + * Chassis Fan 1, + * Chassis Fan 2, + * Chassis Fan 3, + * HAMP Fan, + * Water Pump, + * CPU OPT, + * Water Flow, + * AIO Pump, + * CPU Temperature, + * CPU Socket Temperature, + * Motherboard Temperature, + * Chipset Temperature, + * Tsensor 1 Temperature, + * CPU VRM Temperature, + * Water In, + * Water Out, + * CPU VRM Output Current. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define DRVNAME "asus_wmi_sensors" - +#include #include #include #include @@ -18,65 +50,35 @@ #include #include #include -#include +#include #include #define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" -#define ASUSWMI_METHODID_BLOCK_READ_EC 0x42524543 /* BREC */ #define ASUSWMI_METHODID_GET_VALUE 0x52574543 #define ASUSWMI_METHODID_UPDATE_BUFFER 0x51574543 #define ASUSWMI_METHODID_GET_INFO 0x50574543 #define ASUSWMI_METHODID_GET_NUMBER 0x50574572 -#define ASUSWMI_METHODID_GET_BUFFER_ADDRESS 0x50574573 #define ASUSWMI_METHODID_GET_VERSION 0x50574574 #define ASUS_WMI_MAX_STR_SIZE 32 -#define HWMON_MAX 9 - -#define ASUS_WMI_BLOCK_READ_REGISTERS_MAX 0x10 /* from the ASUS DSDT source */ -/* from the ASUS_WMI_BLOCK_READ_REGISTERS_MAX value */ -#define ASUS_WMI_MAX_BUF_LEN 0x80 -#define MAX_SENSOR_LABEL_LENGTH 0x10 - -#define ASUSWMI_SENSORS_MAX 11 -#define ASUS_EC_KNOWN_EC_REGISTERS 14 -#define HWMON_MAX 9 - -enum asus_wmi_ec_board { - BOARD_R_C8H, // ROG Crosshair VIII Hero - BOARD_R_C8DH, // ROG Crosshair VIII Dark Hero - BOARD_R_C8F, // ROG Crosshair VIII Formula - BOARD_RS_X570_E_G, // ROG STRIX X570-E GAMING - BOARD_RS_B550_E_G, // ROG STRIX B550-E GAMING -}; - -/* boards with EC support */ -static const char *const asus_wmi_ec_boards_names[] = { - [BOARD_R_C8H] = "ROG CROSSHAIR VIII HERO", - [BOARD_R_C8DH] = "ROG CROSSHAIR VIII DARK HERO", - [BOARD_R_C8F] = "ROG CROSSHAIR VIII FORMULA", - [BOARD_RS_X570_E_G] = "ROG STRIX X570-E GAMING", - [BOARD_RS_B550_E_G] = "ROG STRIX B550-E GAMING", -}; - /* boards with wmi sensors support */ static const char *const asus_wmi_boards_names[] = { - "ROG CROSSHAIR VII HERO (WI-FI)", - "ROG CROSSHAIR VII HERO", - "ROG CROSSHAIR VI HERO (WI-FI AC)", - "CROSSHAIR VI HERO", - "ROG CROSSHAIR VI EXTREME", - "ROG ZENITH EXTREME", - "ROG ZENITH EXTREME ALPHA", + "ROG CROSSHAIR VI HERO", "PRIME X399-A", "PRIME X470-PRO", - "ROG STRIX X399-E GAMING", + "ROG CROSSHAIR VI EXTREME", + "ROG CROSSHAIR VI HERO (WI-FI AC)", + "ROG CROSSHAIR VII HERO", + "ROG CROSSHAIR VII HERO (WI-FI)", "ROG STRIX B450-E GAMING", "ROG STRIX B450-F GAMING", "ROG STRIX B450-I GAMING", - "ROG STRIX X470-I GAMING", + "ROG STRIX X399-E GAMING", "ROG STRIX X470-F GAMING", + "ROG STRIX X470-I GAMING", + "ROG ZENITH EXTREME", + "ROG ZENITH EXTREME ALPHA", }; enum asus_wmi_sensor_class { @@ -85,7 +87,6 @@ enum asus_wmi_sensor_class { FAN_RPM = 0x2, CURRENT = 0x3, WATER_FLOW = 0x4, - // BOOL = 0x5 //TODO }; enum asus_wmi_location { @@ -102,7 +103,6 @@ enum asus_wmi_location { enum asus_wmi_type { SIGNED_INT = 0x0, UNSIGNED_INT = 0x1, - // BOOL = 0x2, //TODO SCALED = 0x3, }; @@ -127,162 +127,48 @@ static u32 hwmon_attributes[] = { [hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL, }; +/** + * struct asus_wmi_sensor_info - sensor info. + * @id: sensor id. + * @data_type: sensor class e.g. voltage, temp etc. + * @location: sensor location. + * @name: sensor name. + * @source: sensor source. + * @type: sensor type signed, unsigned etc. + * @cached_value: cached sensor value. + */ struct asus_wmi_sensor_info { u32 id; - int data_type; // asus_wmi_sensor_class e.g. voltage, temp etc - int location; // asus_wmi_location + int data_type; + int location; char name[ASUS_WMI_MAX_STR_SIZE]; - int source; // asus_wmi_source - int type; // asus_wmi_type signed, unsigned etc - u32 cached_value; -}; - -union asus_wmi_ec_sensor_address { - u32 value; - struct { - u8 index; - u8 bank; - u8 size; - u8 dummy; - } addr; -}; - -struct asus_wmi_ec_sensor_info { - char label[MAX_SENSOR_LABEL_LENGTH]; - enum hwmon_sensor_types type; - union asus_wmi_ec_sensor_address addr; + int source; + int type; u32 cached_value; }; -struct asus_wmi_ec_info { - struct asus_wmi_ec_sensor_info sensors[ASUSWMI_SENSORS_MAX]; - /* UTF-16 string to pass to BRxx() WMI function */ - char read_arg[((ASUS_WMI_BLOCK_READ_REGISTERS_MAX * 4) + 1) * 2]; - u8 read_buffer[ASUS_WMI_BLOCK_READ_REGISTERS_MAX]; - u8 nr_sensors; /* number of board EC sensors */ - /* number of EC registers to read (sensor might span more than 1 register) */ - u8 nr_registers; - unsigned long last_updated; /* in jiffies */ -}; - struct asus_wmi_wmi_info { u8 buffer; unsigned long source_last_updated[3]; /* in jiffies */ u8 sensor_count; - const struct asus_wmi_sensor_info **info[HWMON_MAX]; + const struct asus_wmi_sensor_info **info[hwmon_max]; struct asus_wmi_sensor_info **info_by_id; }; struct asus_wmi_sensors { /* lock access to instrnal cache */ struct mutex lock; - struct asus_wmi_ec_info ec; struct asus_wmi_wmi_info wmi; - int ec_board; int wmi_board; }; struct asus_wmi_data { - int ec_board; int wmi_board; int wmi_count; }; -static inline union asus_wmi_ec_sensor_address asus_wmi_ec_make_sensor_address(u8 size, - u8 bank, - u8 index) -{ - union asus_wmi_ec_sensor_address res; - - res.value = (size << 16) + (bank << 8) + index; - return res; -} - -static inline void asus_wmi_ec_set_sensor_info(struct asus_wmi_ec_sensor_info *sensor_info, - const char *label, - enum hwmon_sensor_types type, - union asus_wmi_ec_sensor_address addr, - u8 *nr_regs) -{ - sensor_info->type = type; - strcpy(sensor_info->label, label); - sensor_info->cached_value = 0; - sensor_info->addr.value = addr.value; - *nr_regs += sensor_info->addr.addr.size; -} - -static void asus_wmi_ec_fill_board_sensors(struct asus_wmi_ec_info *ec, int board) -{ - struct asus_wmi_ec_sensor_info *si; - - si = ec->sensors; - ec->nr_registers = 0; - - switch (board) { - case BOARD_RS_B550_E_G: - case BOARD_RS_X570_E_G: - case BOARD_R_C8H: - case BOARD_R_C8DH: - case BOARD_R_C8F: - asus_wmi_ec_set_sensor_info(si++, "Chipset", hwmon_temp, - asus_wmi_ec_make_sensor_address(1, 0x00, 0x3A), - &ec->nr_registers); - asus_wmi_ec_set_sensor_info(si++, "CPU", hwmon_temp, - asus_wmi_ec_make_sensor_address(1, 0x00, 0x3B), - &ec->nr_registers); - asus_wmi_ec_set_sensor_info(si++, "Motherboard", hwmon_temp, - asus_wmi_ec_make_sensor_address(1, 0x00, 0x3C), - &ec->nr_registers); - asus_wmi_ec_set_sensor_info(si++, "T_Sensor", hwmon_temp, - asus_wmi_ec_make_sensor_address(1, 0x00, 0x3D), - &ec->nr_registers); - asus_wmi_ec_set_sensor_info(si++, "VRM", hwmon_temp, - asus_wmi_ec_make_sensor_address(1, 0x00, 0x3E), - &ec->nr_registers); - } - - switch (board) { - case BOARD_RS_X570_E_G: - case BOARD_R_C8H: - case BOARD_R_C8DH: - case BOARD_R_C8F: - asus_wmi_ec_set_sensor_info(si++, "CPU_Opt", hwmon_fan, - asus_wmi_ec_make_sensor_address(2, 0x00, 0xB0), - &ec->nr_registers); - asus_wmi_ec_set_sensor_info(si++, "CPU", hwmon_curr, - asus_wmi_ec_make_sensor_address(1, 0x00, 0xF4), - &ec->nr_registers); - } - - switch (board) { - case BOARD_RS_X570_E_G: - case BOARD_R_C8H: - case BOARD_R_C8F: - asus_wmi_ec_set_sensor_info(si++, "Chipset", hwmon_fan, - asus_wmi_ec_make_sensor_address(2, 0x00, 0xB4), - &ec->nr_registers); - } - - switch (board) { - case BOARD_R_C8H: - case BOARD_R_C8DH: - case BOARD_R_C8F: - asus_wmi_ec_set_sensor_info(si++, "Water", hwmon_fan, - asus_wmi_ec_make_sensor_address(2, 0x00, 0xBC), - &ec->nr_registers); - asus_wmi_ec_set_sensor_info(si++, "Water_In", hwmon_temp, - asus_wmi_ec_make_sensor_address(1, 0x01, 0x00), - &ec->nr_registers); - asus_wmi_ec_set_sensor_info(si++, "Water_Out", hwmon_temp, - asus_wmi_ec_make_sensor_address(1, 0x01, 0x01), - &ec->nr_registers); - } - - ec->nr_sensors = si - ec->sensors; -} - /* * Universal method for calling WMI method */ @@ -303,414 +189,124 @@ static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *ou */ static int asus_wmi_get_version(u32 *version) { - u32 args[] = {0, 0, 0}; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - int status = asus_wmi_call_method(ASUSWMI_METHODID_GET_VERSION, args, &output); - - if (!status) { - union acpi_object *obj = (union acpi_object *)output.pointer; - - if (obj && obj->type == ACPI_TYPE_INTEGER) - *version = obj->integer.value; - } - return status; -} - -/* - * Gets the number of sensor items - */ -static int asus_wmi_get_item_count(u32 *count) -{ u32 args[] = {0, 0, 0}; - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - int status = asus_wmi_call_method(ASUSWMI_METHODID_GET_NUMBER, args, &output); - - if (!status) { - union acpi_object *obj = (union acpi_object *)output.pointer; - - if (obj && obj->type == ACPI_TYPE_INTEGER) - *count = obj->integer.value; - } - return status; -} - -/* - * The next four functions converts to/from BRxx string argument format - * The format of the string is as follows: - * The string consists of two-byte UTF-16 characters - * The value of the very first byte int the string is equal to the total length - * of the next string in bytes, thus excluding the first two-byte character - * The rest of the string encodes pairs of (bank, index) pairs, where both - * values are byte-long (0x00 to 0xFF) - * Numbers are encoded as UTF-16 hex values - */ - -static inline char *asus_wmi_ec_hex_utf_16_le_pack(char *buf, u8 byte) -{ - *buf++ = hex_asc_hi(byte); - *buf++ = 0; - *buf++ = hex_asc_lo(byte); - *buf++ = 0; - return buf; -} - -static void asus_wmi_ec_decode_reply_buffer(const u8 *inp, u8 *out) -{ - u8 len = ACPI_MIN(ASUS_WMI_MAX_BUF_LEN, inp[0] / 4); - const u8 *data = inp + 2; - u8 i; - - for (i = 0; i < len; ++i, data += 4) - out[i] = (hex_to_bin(data[0]) << 4) + hex_to_bin(data[2]); -} - -static void asus_wmi_ec_encode_registers(u16 *registers, u8 len, char *out) -{ - u8 i; - - // assert(len <= 30) - *out++ = len * 8; - *out++ = 0; - for (i = 0; i < len; ++i) { - out = asus_wmi_ec_hex_utf_16_le_pack(out, (registers[i] & 0xFF00) >> 8); - out = asus_wmi_ec_hex_utf_16_le_pack(out, (registers[i] & 0x00FF)); - } -} - -static void asus_wmi_ec_make_block_read_query(struct asus_wmi_ec_info *ec) -{ - u16 registers[ASUS_EC_KNOWN_EC_REGISTERS]; - u8 i, j, register_idx = 0; - - /* if we can get values for all the registers in a single query, - * the query will not change from call to call - */ - if (ec->nr_registers <= ASUS_WMI_BLOCK_READ_REGISTERS_MAX && - ec->read_arg[0] > 0) { - /* no need to update */ - return; - } - - for (i = 0; i < ec->nr_sensors; ++i) { - for (j = 0; j < ec->sensors[i].addr.addr.size; - ++j, ++register_idx) { - registers[register_idx] = - (ec->sensors[i].addr.addr.bank << 8) + - ec->sensors[i].addr.addr.index + j; - } - } - - asus_wmi_ec_encode_registers(registers, ec->nr_registers, ec->read_arg); -} - -static int asus_wmi_ec_block_read(u32 method_id, const char *query, u8 *out) -{ - struct acpi_buffer input; - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, - NULL }; // TODO use pre-allocated buffer - acpi_status status; union acpi_object *obj; + int err; - /* the first byte of the BRxx() argument string has to be the string size */ - input.length = (acpi_size)query[0] + 2; - input.pointer = (void *)query; - status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0, method_id, &input, - &output); - - if (ACPI_FAILURE(status)) { - acpi_os_free(output.pointer); - return -EIO; - } + err = asus_wmi_call_method(ASUSWMI_METHODID_GET_VERSION, args, &output); + if (err) + return err; obj = output.pointer; - if (!obj || obj->type != ACPI_TYPE_BUFFER) { - pr_err("unexpected reply type from ASUS ACPI code"); - acpi_os_free(output.pointer); + if (!obj || obj->type != ACPI_TYPE_INTEGER) return -EIO; - } - asus_wmi_ec_decode_reply_buffer(obj->buffer.pointer, out); - acpi_os_free(output.pointer); - return 0; -} - -static int asus_wmi_ec_update_ec_sensors(struct asus_wmi_ec_info *ec) -{ - struct asus_wmi_ec_sensor_info *si; - u32 value; - int status; - u8 i_sensor, read_reg_ct, i_sensor_register; - - asus_wmi_ec_make_block_read_query(ec); - status = asus_wmi_ec_block_read(ASUSWMI_METHODID_BLOCK_READ_EC, - ec->read_arg, - ec->read_buffer); - if (status) - return status; - - read_reg_ct = 0; - for (i_sensor = 0; i_sensor < ec->nr_sensors; ++i_sensor) { - si = &ec->sensors[i_sensor]; - value = ec->read_buffer[read_reg_ct++]; - for (i_sensor_register = 1; - i_sensor_register < si->addr.addr.size; - ++i_sensor_register) { - value <<= 8; - value += ec->read_buffer[read_reg_ct++]; - } - si->cached_value = value; - } - return 0; -} - -static int asus_wmi_ec_scale_sensor_value(u32 value, int data_type) -{ - switch (data_type) { - case hwmon_curr: - case hwmon_temp: - case hwmon_in: - return value * 1000; - default: - return value; - } -} - -static u8 asus_wmi_ec_find_sensor_index(const struct asus_wmi_ec_info *ec, - enum hwmon_sensor_types type, int channel) -{ - u8 i; - - for (i = 0; i < ec->nr_sensors; ++i) { - if (ec->sensors[i].type == type) { - if (channel == 0) - return i; - - --channel; - } - } - return 0xFF; -} - -static int asus_wmi_ec_get_cached_value_or_update(int sensor_index, - struct asus_wmi_sensors *state, - u32 *value) -{ - int ret; - - if (time_after(jiffies, state->ec.last_updated + HZ)) { - ret = asus_wmi_ec_update_ec_sensors(&state->ec); - - if (ret) { - pr_err("asus_wmi_ec_update_ec_sensors() failure\n"); - return -EIO; - } - state->ec.last_updated = jiffies; - } + *version = obj->integer.value; - *value = state->ec.sensors[sensor_index].cached_value; return 0; } /* - * Now follow the functions that implement the hwmon interface + * Gets the number of sensor items */ - -static int asus_wmi_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long *val) +static int asus_wmi_get_item_count(u32 *count) { - int ret; - u32 value = 0; - struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); - - u8 sidx = asus_wmi_ec_find_sensor_index(&sensor_data->ec, type, channel); - - mutex_lock(&sensor_data->lock); - - ret = asus_wmi_ec_get_cached_value_or_update(sidx, sensor_data, &value); - mutex_unlock(&sensor_data->lock); - - if (!ret) - *val = asus_wmi_ec_scale_sensor_value(value, sensor_data->ec.sensors[sidx].type); + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + u32 args[] = {0, 0, 0}; + union acpi_object *obj; + int err; - return ret; -} + err = asus_wmi_call_method(ASUSWMI_METHODID_GET_NUMBER, args, &output); + if (err) + return err; -static int asus_wmi_ec_hwmon_read_string(struct device *dev, - enum hwmon_sensor_types type, u32 attr, - int channel, const char **str) -{ - struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); + obj = output.pointer; + if (!obj || obj->type != ACPI_TYPE_INTEGER) + return -EIO; - u8 sensor_index = asus_wmi_ec_find_sensor_index(&sensor_data->ec, type, channel); - *str = sensor_data->ec.sensors[sensor_index].label; + *count = obj->integer.value; return 0; } -static umode_t asus_wmi_ec_hwmon_is_visible(const void *drvdata, - enum hwmon_sensor_types type, u32 attr, - int channel) -{ - const struct asus_wmi_sensors *sensor_data = drvdata; - - return asus_wmi_ec_find_sensor_index(&sensor_data->ec, type, channel) != 0xFF ? - 0444 : - 0; -} - static int asus_wmi_hwmon_add_chan_info(struct hwmon_channel_info *asus_wmi_hwmon_chan, struct device *dev, int num, enum hwmon_sensor_types type, u32 config) { - int i; - u32 *cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL); + u32 *cfg; + cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL); if (!cfg) return -ENOMEM; asus_wmi_hwmon_chan->type = type; asus_wmi_hwmon_chan->config = cfg; - for (i = 0; i < num; i++, cfg++) - *cfg = config; + memset32(cfg, config, num); return 0; } -static const struct hwmon_ops asus_wmi_ec_hwmon_ops = { - .is_visible = asus_wmi_ec_hwmon_is_visible, - .read = asus_wmi_ec_hwmon_read, - .read_string = asus_wmi_ec_hwmon_read_string, -}; - -static struct hwmon_chip_info asus_wmi_ec_chip_info = { - .ops = &asus_wmi_ec_hwmon_ops, - .info = NULL, -}; - -static int asus_wmi_ec_configure_sensor_setup(struct platform_device *pdev, - struct asus_wmi_sensors *sensor_data) -{ - int i; - int nr_count[HWMON_MAX] = { 0 }, nr_types = 0; - struct device *hwdev; - struct device *dev = &pdev->dev; - struct hwmon_channel_info *asus_wmi_hwmon_chan; - const struct hwmon_channel_info **ptr_asus_wmi_ci; - const struct hwmon_chip_info *chip_info; - const struct asus_wmi_ec_sensor_info *si; - enum hwmon_sensor_types type; - - if (sensor_data->ec_board < 0) - return 0; - - asus_wmi_ec_fill_board_sensors(&sensor_data->ec, sensor_data->ec_board); - - if (!sensor_data->ec.nr_sensors) - return -ENODEV; - - for (i = 0; i < sensor_data->ec.nr_sensors; ++i) { - si = &sensor_data->ec.sensors[i]; - if (!nr_count[si->type]) - ++nr_types; - ++nr_count[si->type]; - } - - if (nr_count[hwmon_temp]) - nr_count[hwmon_chip]++, nr_types++; - - asus_wmi_hwmon_chan = devm_kcalloc(dev, nr_types, - sizeof(*asus_wmi_hwmon_chan), - GFP_KERNEL); - if (!asus_wmi_hwmon_chan) - return -ENOMEM; - - ptr_asus_wmi_ci = devm_kcalloc(dev, nr_types + 1, - sizeof(*ptr_asus_wmi_ci), GFP_KERNEL); - if (!ptr_asus_wmi_ci) - return -ENOMEM; - - asus_wmi_ec_chip_info.info = ptr_asus_wmi_ci; - chip_info = &asus_wmi_ec_chip_info; - - for (type = 0; type < HWMON_MAX; type++) { - if (!nr_count[type]) - continue; - - asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev, - nr_count[type], type, - hwmon_attributes[type]); - *ptr_asus_wmi_ci++ = asus_wmi_hwmon_chan++; - } - - pr_info("%s board has %d EC sensors that span %d registers", - asus_wmi_ec_boards_names[sensor_data->ec_board], - sensor_data->ec.nr_sensors, - sensor_data->ec.nr_registers); - - hwdev = devm_hwmon_device_register_with_info(dev, "asuswmiecsensors", - sensor_data, chip_info, NULL); - - return PTR_ERR_OR_ZERO(hwdev); -} - /* * For a given sensor item returns details e.g. type (voltage/temperature/fan speed etc), bank etc */ static int asus_wmi_sensor_info(int index, struct asus_wmi_sensor_info *s) { - u32 args[] = {index, 0}; union acpi_object name_obj, data_type_obj, location_obj, source_obj, type_obj; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + u32 args[] = {index, 0}; union acpi_object *obj; + int err; + + err = asus_wmi_call_method(ASUSWMI_METHODID_GET_INFO, args, &output); + if (err) + return err; - int status = asus_wmi_call_method(ASUSWMI_METHODID_GET_INFO, args, &output); + s->id = index; - if (!status) { - s->id = index; + obj = output.pointer; + if (!obj || obj->type != ACPI_TYPE_PACKAGE) + return -EIO; - obj = (union acpi_object *)output.pointer; + if (obj->package.count != 5) + return 1; - if (obj && obj->type == ACPI_TYPE_PACKAGE) { - if (obj->package.count != 5) - return 1; + name_obj = obj->package.elements[0]; - name_obj = obj->package.elements[0]; + if (name_obj.type != ACPI_TYPE_STRING) + return 1; - if (name_obj.type != ACPI_TYPE_STRING) - return 1; + strncpy(s->name, name_obj.string.pointer, sizeof(s->name) - 1); - strncpy(s->name, name_obj.string.pointer, sizeof(s->name) - 1); + data_type_obj = obj->package.elements[1]; - data_type_obj = obj->package.elements[1]; + if (data_type_obj.type != ACPI_TYPE_INTEGER) + return 1; - if (data_type_obj.type != ACPI_TYPE_INTEGER) - return 1; + s->data_type = data_type_obj.integer.value; - s->data_type = data_type_obj.integer.value; + location_obj = obj->package.elements[2]; - location_obj = obj->package.elements[2]; + if (location_obj.type != ACPI_TYPE_INTEGER) + return 1; - if (location_obj.type != ACPI_TYPE_INTEGER) - return 1; + s->location = location_obj.integer.value; - s->location = location_obj.integer.value; + source_obj = obj->package.elements[3]; - source_obj = obj->package.elements[3]; + if (source_obj.type != ACPI_TYPE_INTEGER) + return 1; - if (source_obj.type != ACPI_TYPE_INTEGER) - return 1; + s->source = source_obj.integer.value; - s->source = source_obj.integer.value; + type_obj = obj->package.elements[4]; - type_obj = obj->package.elements[4]; + if (type_obj.type != ACPI_TYPE_INTEGER) + return 1; - if (type_obj.type != ACPI_TYPE_INTEGER) - return 1; + s->type = type_obj.integer.value; - s->type = type_obj.integer.value; - } - } - return status; + return 0; } static int asus_wmi_update_buffer(u8 source) @@ -723,17 +319,22 @@ static int asus_wmi_update_buffer(u8 source) static int asus_wmi_get_sensor_value(u8 index, u32 *value) { - u32 args[] = {index, 0}; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - int status = asus_wmi_call_method(ASUSWMI_METHODID_GET_VALUE, args, &output); + u32 args[] = {index, 0}; + union acpi_object *obj; + int err; - if (!status) { - union acpi_object *obj = (union acpi_object *)output.pointer; + err = asus_wmi_call_method(ASUSWMI_METHODID_GET_VALUE, args, &output); + if (err) + return err; - if (obj && obj->type == ACPI_TYPE_INTEGER) - *value = obj->integer.value; - } - return status; + obj = output.pointer; + if (!obj || obj->type != ACPI_TYPE_INTEGER) + return -EIO; + + *value = obj->integer.value; + + return 0; } static void asus_wmi_update_values_for_source(u8 source, struct asus_wmi_sensors *sensor_data) @@ -857,7 +458,7 @@ static int asus_wmi_configure_sensor_setup(struct platform_device *pdev, { int err; int i, idx; - int nr_count[HWMON_MAX] = {0}, nr_types = 0; + int nr_count[hwmon_max] = {0}, nr_types = 0; struct device *hwdev; struct device *dev = &pdev->dev; struct hwmon_channel_info *asus_wmi_hwmon_chan; @@ -866,9 +467,6 @@ static int asus_wmi_configure_sensor_setup(struct platform_device *pdev, const struct hwmon_channel_info **ptr_asus_wmi_ci; const struct hwmon_chip_info *chip_info; - if (sensor_data->wmi.sensor_count <= 0 || sensor_data->wmi_board < 0) - return 0; - sensor_data->wmi.buffer = -1; temp_sensor = devm_kcalloc(dev, 1, sizeof(*temp_sensor), GFP_KERNEL); if (!temp_sensor) @@ -917,7 +515,7 @@ static int asus_wmi_configure_sensor_setup(struct platform_device *pdev, if (!sensor_data->wmi.info_by_id) return -ENOMEM; - for (type = 0; type < HWMON_MAX; type++) { + for (type = 0; type < hwmon_max; type++) { if (!nr_count[type]) continue; @@ -940,12 +538,8 @@ static int asus_wmi_configure_sensor_setup(struct platform_device *pdev, return -ENOMEM; err = asus_wmi_sensor_info(i, temp_sensor); - if (err) { - pr_err("sensor error\n"); + if (err) continue; - } - - pr_debug("setting sensor info\n"); switch (temp_sensor->data_type) { case TEMPERATURE_C: @@ -961,23 +555,23 @@ static int asus_wmi_configure_sensor_setup(struct platform_device *pdev, } } - pr_info("%s board has %d sensors", + dev_dbg(&pdev->dev, "%s board has %d sensors", asus_wmi_boards_names[sensor_data->wmi_board], sensor_data->wmi.sensor_count); - hwdev = devm_hwmon_device_register_with_info(dev, "asuswmisensors", - sensor_data, chip_info, - NULL); + hwdev = devm_hwmon_device_register_with_info(dev, KBUILD_MODNAME, + sensor_data, chip_info, NULL); return PTR_ERR_OR_ZERO(hwdev); } static int asus_wmi_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct asus_wmi_data *data = dev_get_platdata(dev); struct asus_wmi_sensors *sensor_data; - int err; + struct device *dev = &pdev->dev; + struct asus_wmi_data *data; + + data = dev_get_platdata(dev); sensor_data = devm_kzalloc(dev, sizeof(struct asus_wmi_sensors), GFP_KERNEL); @@ -985,30 +579,21 @@ static int asus_wmi_probe(struct platform_device *pdev) return -ENOMEM; mutex_init(&sensor_data->lock); - sensor_data->ec_board = data->ec_board; sensor_data->wmi_board = data->wmi_board; sensor_data->wmi.sensor_count = data->wmi_count; platform_set_drvdata(pdev, sensor_data); - /* ec init */ - err = asus_wmi_ec_configure_sensor_setup(pdev, - sensor_data); - if (err) - return err; - - /* old version */ - err = asus_wmi_configure_sensor_setup(pdev, - sensor_data); - - return err; + return asus_wmi_configure_sensor_setup(pdev, + sensor_data); + return 0; } static struct platform_driver asus_wmi_sensors_platform_driver = { .driver = { .name = "asus-wmi-sensors", }, - .probe = asus_wmi_probe + .probe = asus_wmi_probe, }; static struct platform_device *sensors_pdev; @@ -1021,7 +606,6 @@ static int __init asus_wmi_init(void) data.wmi_board = -1; data.wmi_count = 0; - data.ec_board = -1; board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); board_name = dmi_get_system_info(DMI_BOARD_NAME); @@ -1031,9 +615,6 @@ static int __init asus_wmi_init(void) if (!wmi_has_guid(ASUSWMI_MONITORING_GUID)) return -ENODEV; - data.ec_board = match_string(asus_wmi_ec_boards_names, - ARRAY_SIZE(asus_wmi_ec_boards_names), - board_name); data.wmi_board = match_string(asus_wmi_boards_names, ARRAY_SIZE(asus_wmi_boards_names), board_name); @@ -1055,7 +636,7 @@ static int __init asus_wmi_init(void) } /* Nothing to support */ - if (data.ec_board < 0 && data.wmi_board < 0) + if (data.wmi_board < 0) return -ENODEV; sensors_pdev = platform_create_bundle(&asus_wmi_sensors_platform_driver, @@ -1063,23 +644,18 @@ static int __init asus_wmi_init(void) NULL, 0, &data, sizeof(struct asus_wmi_data)); - if (IS_ERR(sensors_pdev)) - return PTR_ERR(sensors_pdev); - - return 0; + return PTR_ERR_OR_ZERO(sensors_pdev); } +module_init(asus_wmi_init); static void __exit asus_wmi_exit(void) { platform_device_unregister(sensors_pdev); platform_driver_unregister(&asus_wmi_sensors_platform_driver); } +module_exit(asus_wmi_exit); MODULE_AUTHOR("Ed Brindley "); MODULE_AUTHOR("Eugene Shalygin "); MODULE_DESCRIPTION("Asus WMI Sensors Driver"); MODULE_LICENSE("GPL"); -MODULE_VERSION("1"); - -module_init(asus_wmi_init); -module_exit(asus_wmi_exit); \ No newline at end of file From 7121e03c4f879e0cbae1dd345246831efc86241e Mon Sep 17 00:00:00 2001 From: ed Date: Sun, 10 Oct 2021 18:43:12 +0100 Subject: [PATCH 03/13] v3 as sent to LKML. See https://lore.kernel.org/all/20211010095216.25115-3-pauk.denis@gmail.com/ --- asus-wmi-sensors.c | 144 +++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 85 deletions(-) diff --git a/asus-wmi-sensors.c b/asus-wmi-sensors.c index 792872b..ac6a995 100644 --- a/asus-wmi-sensors.c +++ b/asus-wmi-sensors.c @@ -3,10 +3,9 @@ * HWMON driver for ASUS motherboards that provides sensor readouts via WMI * interface present in the UEFI of the X370/X470/B450/X399 Ryzen motherboards. * - * Copyright (C) 2021 Eugene Shalygin * Copyright (C) 2018-2019 Ed Brindley * - * WMI interface provided: + * WMI interface provides: * CPU Core Voltage, * CPU SOC Voltage, * DRAM Voltage, @@ -21,15 +20,15 @@ * CPU Core Voltage, * CPU SOC Voltage, * DRAM Voltage, - * CPU Fan, - * Chassis Fan 1, - * Chassis Fan 2, - * Chassis Fan 3, - * HAMP Fan, - * Water Pump, - * CPU OPT, - * Water Flow, - * AIO Pump, + * CPU Fan RPM, + * Chassis Fan 1 RPM, + * Chassis Fan 2 RPM, + * Chassis Fan 3 RPM, + * HAMP Fan RPM, + * Water Pump RPM, + * CPU OPT RPM, + * Water Flow RPM, + * AIO Pump RPM, * CPU Temperature, * CPU Socket Temperature, * Motherboard Temperature, @@ -39,6 +38,7 @@ * Water In, * Water Out, * CPU VRM Output Current. + * */ #include #include @@ -62,24 +62,34 @@ #define ASUS_WMI_MAX_STR_SIZE 32 -/* boards with wmi sensors support */ -static const char *const asus_wmi_boards_names[] = { - "ROG CROSSHAIR VI HERO", - "PRIME X399-A", - "PRIME X470-PRO", - "ROG CROSSHAIR VI EXTREME", - "ROG CROSSHAIR VI HERO (WI-FI AC)", - "ROG CROSSHAIR VII HERO", - "ROG CROSSHAIR VII HERO (WI-FI)", - "ROG STRIX B450-E GAMING", - "ROG STRIX B450-F GAMING", - "ROG STRIX B450-I GAMING", - "ROG STRIX X399-E GAMING", - "ROG STRIX X470-F GAMING", - "ROG STRIX X470-I GAMING", - "ROG ZENITH EXTREME", - "ROG ZENITH EXTREME ALPHA", +#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name) \ + { \ + .matches = { \ + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, \ + "ASUSTeK COMPUTER INC."), \ + DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \ + }, \ + } + +static const struct dmi_system_id asus_wmi_dmi_table[] __initconst = { + DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X399-A"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X470-PRO"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VI EXTREME"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VI HERO"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VI HERO (WI-FI AC)"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VII HERO"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VII HERO (WI-FI)"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-E GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-F GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-I GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X399-E GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-F GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-I GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH EXTREME"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH EXTREME ALPHA"), + {} }; +MODULE_DEVICE_TABLE(dmi, asus_wmi_dmi_table); enum asus_wmi_sensor_class { VOLTAGE = 0x0, @@ -150,7 +160,7 @@ struct asus_wmi_sensor_info { struct asus_wmi_wmi_info { u8 buffer; unsigned long source_last_updated[3]; /* in jiffies */ - u8 sensor_count; + int sensor_count; const struct asus_wmi_sensor_info **info[hwmon_max]; struct asus_wmi_sensor_info **info_by_id; @@ -160,13 +170,6 @@ struct asus_wmi_sensors { /* lock access to instrnal cache */ struct mutex lock; struct asus_wmi_wmi_info wmi; - - int wmi_board; -}; - -struct asus_wmi_data { - int wmi_board; - int wmi_count; }; /* @@ -405,10 +408,8 @@ static int asus_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, sensor = *(sensor_data->wmi.info[type] + channel); mutex_lock(&sensor_data->lock); - ret = asus_wmi_get_cached_value_or_update(sensor, sensor_data, &value); mutex_unlock(&sensor_data->lock); - if (!ret) *val = asus_wmi_scale_sensor_value(value, sensor->data_type); @@ -555,8 +556,7 @@ static int asus_wmi_configure_sensor_setup(struct platform_device *pdev, } } - dev_dbg(&pdev->dev, "%s board has %d sensors", - asus_wmi_boards_names[sensor_data->wmi_board], + dev_dbg(&pdev->dev, "board has %d sensors", sensor_data->wmi.sensor_count); hwdev = devm_hwmon_device_register_with_info(dev, KBUILD_MODNAME, @@ -569,24 +569,35 @@ static int asus_wmi_probe(struct platform_device *pdev) { struct asus_wmi_sensors *sensor_data; struct device *dev = &pdev->dev; - struct asus_wmi_data *data; - - data = dev_get_platdata(dev); + u32 version = 0; sensor_data = devm_kzalloc(dev, sizeof(struct asus_wmi_sensors), GFP_KERNEL); if (!sensor_data) return -ENOMEM; + if (!wmi_has_guid(ASUSWMI_MONITORING_GUID)) + return -ENODEV; + + if (asus_wmi_get_version(&version)) + return -ENODEV; + + if (asus_wmi_get_item_count(&sensor_data->wmi.sensor_count)) + return -ENODEV; + + if (sensor_data->wmi.sensor_count <= 0 || version < 2) { + dev_info(dev, "version: %u with %d sensors is unsupported\n", + version, sensor_data->wmi.sensor_count); + + return -ENODEV; + } + mutex_init(&sensor_data->lock); - sensor_data->wmi_board = data->wmi_board; - sensor_data->wmi.sensor_count = data->wmi_count; platform_set_drvdata(pdev, sensor_data); return asus_wmi_configure_sensor_setup(pdev, sensor_data); - return 0; } static struct platform_driver asus_wmi_sensors_platform_driver = { @@ -600,49 +611,13 @@ static struct platform_device *sensors_pdev; static int __init asus_wmi_init(void) { - const char *board_vendor, *board_name; - u32 version = 0; - struct asus_wmi_data data; - - data.wmi_board = -1; - data.wmi_count = 0; - - board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); - board_name = dmi_get_system_info(DMI_BOARD_NAME); - - if (board_vendor && board_name && - !strcmp(board_vendor, "ASUSTeK COMPUTER INC.")) { - if (!wmi_has_guid(ASUSWMI_MONITORING_GUID)) - return -ENODEV; - - data.wmi_board = match_string(asus_wmi_boards_names, - ARRAY_SIZE(asus_wmi_boards_names), - board_name); - - if (data.wmi_board >= 0) { - if (asus_wmi_get_item_count(&data.wmi_count)) - return -ENODEV; - - if (asus_wmi_get_version(&version)) - return -ENODEV; - - if (data.wmi_count <= 0 || version < 2) { - pr_err("Board: %s WMI wmi version: %u with %d sensors is unsupported\n", - board_name, version, data.wmi_count); - - data.wmi_board = -ENODEV; - } - } - } - - /* Nothing to support */ - if (data.wmi_board < 0) + if (!dmi_check_system(asus_wmi_dmi_table)) return -ENODEV; sensors_pdev = platform_create_bundle(&asus_wmi_sensors_platform_driver, asus_wmi_probe, NULL, 0, - &data, sizeof(struct asus_wmi_data)); + NULL, 0); return PTR_ERR_OR_ZERO(sensors_pdev); } @@ -656,6 +631,5 @@ static void __exit asus_wmi_exit(void) module_exit(asus_wmi_exit); MODULE_AUTHOR("Ed Brindley "); -MODULE_AUTHOR("Eugene Shalygin "); MODULE_DESCRIPTION("Asus WMI Sensors Driver"); MODULE_LICENSE("GPL"); From 3eeb3fe091ad3059d62d19a829e71aeebafbc2c9 Mon Sep 17 00:00:00 2001 From: ed Date: Sun, 10 Oct 2021 23:32:43 +0100 Subject: [PATCH 04/13] v4 of driver as provided to LKML --- asus-wmi-sensors.c | 53 +++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/asus-wmi-sensors.c b/asus-wmi-sensors.c index ac6a995..da0a7eb 100644 --- a/asus-wmi-sensors.c +++ b/asus-wmi-sensors.c @@ -49,7 +49,6 @@ #include #include #include -#include #include #include @@ -71,7 +70,7 @@ }, \ } -static const struct dmi_system_id asus_wmi_dmi_table[] __initconst = { +static const struct dmi_system_id asus_wmi_dmi_table[] = { DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X399-A"), DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X470-PRO"), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VI EXTREME"), @@ -454,14 +453,13 @@ static struct hwmon_chip_info asus_wmi_chip_info = { .info = NULL, }; -static int asus_wmi_configure_sensor_setup(struct platform_device *pdev, +static int asus_wmi_configure_sensor_setup(struct device *dev, struct asus_wmi_sensors *sensor_data) { int err; int i, idx; int nr_count[hwmon_max] = {0}, nr_types = 0; struct device *hwdev; - struct device *dev = &pdev->dev; struct hwmon_channel_info *asus_wmi_hwmon_chan; struct asus_wmi_sensor_info *temp_sensor; enum hwmon_sensor_types type; @@ -556,7 +554,7 @@ static int asus_wmi_configure_sensor_setup(struct platform_device *pdev, } } - dev_dbg(&pdev->dev, "board has %d sensors", + dev_dbg(dev, "board has %d sensors", sensor_data->wmi.sensor_count); hwdev = devm_hwmon_device_register_with_info(dev, KBUILD_MODNAME, @@ -565,12 +563,15 @@ static int asus_wmi_configure_sensor_setup(struct platform_device *pdev, return PTR_ERR_OR_ZERO(hwdev); } -static int asus_wmi_probe(struct platform_device *pdev) +static int asus_wmi_probe(struct wmi_device *wdev, const void *context) { struct asus_wmi_sensors *sensor_data; - struct device *dev = &pdev->dev; + struct device *dev = &wdev->dev; u32 version = 0; + if (!dmi_check_system(asus_wmi_dmi_table)) + return -ENODEV; + sensor_data = devm_kzalloc(dev, sizeof(struct asus_wmi_sensors), GFP_KERNEL); if (!sensor_data) @@ -594,41 +595,25 @@ static int asus_wmi_probe(struct platform_device *pdev) mutex_init(&sensor_data->lock); - platform_set_drvdata(pdev, sensor_data); + dev_set_drvdata(dev, sensor_data); - return asus_wmi_configure_sensor_setup(pdev, + return asus_wmi_configure_sensor_setup(dev, sensor_data); } -static struct platform_driver asus_wmi_sensors_platform_driver = { +static const struct wmi_device_id asus_wmi_id_table[] = { + { ASUSWMI_MONITORING_GUID, NULL }, + { } +}; + +static struct wmi_driver asus_sensors_wmi_driver = { .driver = { - .name = "asus-wmi-sensors", + .name = KBUILD_MODNAME, }, + .id_table = asus_wmi_id_table, .probe = asus_wmi_probe, }; - -static struct platform_device *sensors_pdev; - -static int __init asus_wmi_init(void) -{ - if (!dmi_check_system(asus_wmi_dmi_table)) - return -ENODEV; - - sensors_pdev = platform_create_bundle(&asus_wmi_sensors_platform_driver, - asus_wmi_probe, - NULL, 0, - NULL, 0); - - return PTR_ERR_OR_ZERO(sensors_pdev); -} -module_init(asus_wmi_init); - -static void __exit asus_wmi_exit(void) -{ - platform_device_unregister(sensors_pdev); - platform_driver_unregister(&asus_wmi_sensors_platform_driver); -} -module_exit(asus_wmi_exit); +module_wmi_driver(asus_sensors_wmi_driver); MODULE_AUTHOR("Ed Brindley "); MODULE_DESCRIPTION("Asus WMI Sensors Driver"); From 9313d1051bdd634e440c8e40a87d155af76cf178 Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 12 Oct 2021 14:10:12 +0100 Subject: [PATCH 05/13] v5 of the patch as sent to LKML --- asus-wmi-sensors.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/asus-wmi-sensors.c b/asus-wmi-sensors.c index da0a7eb..a45fbec 100644 --- a/asus-wmi-sensors.c +++ b/asus-wmi-sensors.c @@ -176,6 +176,7 @@ struct asus_wmi_sensors { */ static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *output) { +#if IS_ENABLED(CONFIG_ACPI_WMI) struct acpi_buffer input = {(acpi_size) sizeof(*args), args }; acpi_status status; @@ -184,6 +185,9 @@ static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *ou return -EIO; return 0; +#else + return -EOPNOTSUPP; +#endif } /* @@ -436,7 +440,7 @@ static umode_t asus_wmi_hwmon_is_visible(const void *drvdata, const struct asus_wmi_sensors *sensor_data = drvdata; sensor = *(sensor_data->wmi.info[type] + channel); - if (sensor && sensor->name) + if (sensor) return 0444; return 0; @@ -577,9 +581,6 @@ static int asus_wmi_probe(struct wmi_device *wdev, const void *context) if (!sensor_data) return -ENOMEM; - if (!wmi_has_guid(ASUSWMI_MONITORING_GUID)) - return -ENODEV; - if (asus_wmi_get_version(&version)) return -ENODEV; From 293b6df41ba40d6b44400e08683e91728724ea11 Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 14 Oct 2021 23:12:22 +0100 Subject: [PATCH 06/13] v6 of patch sent to LKML --- asus-wmi-sensors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asus-wmi-sensors.c b/asus-wmi-sensors.c index a45fbec..9fe26e1 100644 --- a/asus-wmi-sensors.c +++ b/asus-wmi-sensors.c @@ -166,7 +166,7 @@ struct asus_wmi_wmi_info { }; struct asus_wmi_sensors { - /* lock access to instrnal cache */ + /* lock access to internal cache */ struct mutex lock; struct asus_wmi_wmi_info wmi; }; From 73252df177f2c0c3724da418818ba89cb0625ee5 Mon Sep 17 00:00:00 2001 From: ed Date: Mon, 18 Oct 2021 20:23:18 +0100 Subject: [PATCH 07/13] v7 of patch see https://lore.kernel.org/all/20211015055808.327453-3-pauk.denis@gmail.com/ --- asus-wmi-sensors.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/asus-wmi-sensors.c b/asus-wmi-sensors.c index 9fe26e1..5f255b5 100644 --- a/asus-wmi-sensors.c +++ b/asus-wmi-sensors.c @@ -52,16 +52,16 @@ #include #include -#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" -#define ASUSWMI_METHODID_GET_VALUE 0x52574543 -#define ASUSWMI_METHODID_UPDATE_BUFFER 0x51574543 -#define ASUSWMI_METHODID_GET_INFO 0x50574543 -#define ASUSWMI_METHODID_GET_NUMBER 0x50574572 -#define ASUSWMI_METHODID_GET_VERSION 0x50574574 +#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" +#define ASUSWMI_METHODID_GET_VALUE 0x52574543 +#define ASUSWMI_METHODID_UPDATE_BUFFER 0x51574543 +#define ASUSWMI_METHODID_GET_INFO 0x50574543 +#define ASUSWMI_METHODID_GET_NUMBER 0x50574572 +#define ASUSWMI_METHODID_GET_VERSION 0x50574574 -#define ASUS_WMI_MAX_STR_SIZE 32 +#define ASUS_WMI_MAX_STR_SIZE 32 -#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name) \ +#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name) \ { \ .matches = { \ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, \ @@ -176,7 +176,6 @@ struct asus_wmi_sensors { */ static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *output) { -#if IS_ENABLED(CONFIG_ACPI_WMI) struct acpi_buffer input = {(acpi_size) sizeof(*args), args }; acpi_status status; @@ -185,9 +184,6 @@ static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *ou return -EIO; return 0; -#else - return -EOPNOTSUPP; -#endif } /* From 4e7ef5767fbebbb5f967562d4165eb082629afd6 Mon Sep 17 00:00:00 2001 From: ed Date: Sat, 23 Oct 2021 22:13:43 +0100 Subject: [PATCH 08/13] v8 changes, Fix codestyle in defines and comments, Call mutex_lock inside of functions. --- asus-wmi-sensors.c | 97 +++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/asus-wmi-sensors.c b/asus-wmi-sensors.c index 5f255b5..45709b6 100644 --- a/asus-wmi-sensors.c +++ b/asus-wmi-sensors.c @@ -38,7 +38,6 @@ * Water In, * Water Out, * CPU VRM Output Current. - * */ #include #include @@ -52,23 +51,22 @@ #include #include -#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" -#define ASUSWMI_METHODID_GET_VALUE 0x52574543 -#define ASUSWMI_METHODID_UPDATE_BUFFER 0x51574543 -#define ASUSWMI_METHODID_GET_INFO 0x50574543 -#define ASUSWMI_METHODID_GET_NUMBER 0x50574572 -#define ASUSWMI_METHODID_GET_VERSION 0x50574574 - -#define ASUS_WMI_MAX_STR_SIZE 32 - -#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name) \ - { \ - .matches = { \ - DMI_EXACT_MATCH(DMI_BOARD_VENDOR, \ - "ASUSTeK COMPUTER INC."), \ - DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \ - }, \ - } +#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" +#define ASUSWMI_METHODID_GET_VALUE 0x52574543 /* RWEC */ +#define ASUSWMI_METHODID_UPDATE_BUFFER 0x51574543 /* QWEC */ +#define ASUSWMI_METHODID_GET_INFO 0x50574543 /* PWEC */ +#define ASUSWMI_METHODID_GET_NUMBER 0x50574572 /* PWEr */ +#define ASUSWMI_METHODID_GET_VERSION 0x50574574 /* PWEt */ + +#define ASUS_WMI_MAX_STR_SIZE 32 + +#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name) { \ + .matches = { \ + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, \ + "ASUSTeK COMPUTER INC."), \ + DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \ + }, \ +} static const struct dmi_system_id asus_wmi_dmi_table[] = { DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X399-A"), @@ -179,7 +177,8 @@ static int asus_wmi_call_method(u32 method_id, u32 *args, struct acpi_buffer *ou struct acpi_buffer input = {(acpi_size) sizeof(*args), args }; acpi_status status; - status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0, method_id, &input, output); + status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0, + method_id, &input, output); if (ACPI_FAILURE(status)) return -EIO; @@ -313,8 +312,8 @@ static int asus_wmi_sensor_info(int index, struct asus_wmi_sensor_info *s) static int asus_wmi_update_buffer(u8 source) { - u32 args[] = {source, 0}; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + u32 args[] = {source, 0}; return asus_wmi_call_method(ASUSWMI_METHODID_UPDATE_BUFFER, args, &output); } @@ -341,10 +340,10 @@ static int asus_wmi_get_sensor_value(u8 index, u32 *value) static void asus_wmi_update_values_for_source(u8 source, struct asus_wmi_sensors *sensor_data) { - int ret = 0; + struct asus_wmi_sensor_info *sensor; int value = 0; + int ret = 0; int i; - struct asus_wmi_sensor_info *sensor; for (i = 0; i < sensor_data->wmi.sensor_count; i++) { sensor = sensor_data->wmi.info_by_id[i]; @@ -361,11 +360,11 @@ static int asus_wmi_scale_sensor_value(u32 value, int data_type) /* FAN_RPM and WATER_FLOW don't need scaling */ switch (data_type) { case VOLTAGE: - return DIV_ROUND_CLOSEST(value, 1000); + return DIV_ROUND_CLOSEST(value, MILLI); case TEMPERATURE_C: - return value * 1000; + return value * MILLI; case CURRENT: - return value * 1000; + return value * MILLI; } return value; } @@ -376,10 +375,14 @@ static int asus_wmi_get_cached_value_or_update(const struct asus_wmi_sensor_info { int ret; + mutex_lock(&sensor_data->lock); + if (time_after(jiffies, sensor_data->wmi.source_last_updated[sensor->source] + HZ)) { ret = asus_wmi_update_buffer(sensor->source); - if (ret) + if (ret) { + mutex_unlock(&sensor_data->lock); return -EIO; + } sensor_data->wmi.buffer = sensor->source; @@ -388,27 +391,25 @@ static int asus_wmi_get_cached_value_or_update(const struct asus_wmi_sensor_info } *value = sensor->cached_value; + + mutex_unlock(&sensor_data->lock); + return 0; } -/* - * Now follow the functions that implement the hwmon interface - */ - +/* Now follow the functions that implement the hwmon interface */ static int asus_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { - int ret; - u32 value = 0; const struct asus_wmi_sensor_info *sensor; + u32 value = 0; + int ret; struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); sensor = *(sensor_data->wmi.info[type] + channel); - mutex_lock(&sensor_data->lock); ret = asus_wmi_get_cached_value_or_update(sensor, sensor_data, &value); - mutex_unlock(&sensor_data->lock); if (!ret) *val = asus_wmi_scale_sensor_value(value, sensor->data_type); @@ -419,8 +420,8 @@ static int asus_wmi_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, const char **str) { - const struct asus_wmi_sensor_info *sensor; struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); + const struct asus_wmi_sensor_info *sensor; sensor = *(sensor_data->wmi.info[type] + channel); *str = sensor->name; @@ -432,8 +433,8 @@ static umode_t asus_wmi_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, int channel) { - const struct asus_wmi_sensor_info *sensor; const struct asus_wmi_sensors *sensor_data = drvdata; + const struct asus_wmi_sensor_info *sensor; sensor = *(sensor_data->wmi.info[type] + channel); if (sensor) @@ -456,15 +457,15 @@ static struct hwmon_chip_info asus_wmi_chip_info = { static int asus_wmi_configure_sensor_setup(struct device *dev, struct asus_wmi_sensors *sensor_data) { - int err; - int i, idx; - int nr_count[hwmon_max] = {0}, nr_types = 0; - struct device *hwdev; + const struct hwmon_channel_info **ptr_asus_wmi_ci; struct hwmon_channel_info *asus_wmi_hwmon_chan; + int nr_count[hwmon_max] = {}, nr_types = 0; struct asus_wmi_sensor_info *temp_sensor; - enum hwmon_sensor_types type; - const struct hwmon_channel_info **ptr_asus_wmi_ci; const struct hwmon_chip_info *chip_info; + enum hwmon_sensor_types type; + struct device *hwdev; + int i, idx; + int err; sensor_data->wmi.buffer = -1; temp_sensor = devm_kcalloc(dev, 1, sizeof(*temp_sensor), GFP_KERNEL); @@ -531,7 +532,7 @@ static int asus_wmi_configure_sensor_setup(struct device *dev, return -ENOMEM; } - for (i = sensor_data->wmi.sensor_count - 1; i >= 0 ; i--) { + for (i = sensor_data->wmi.sensor_count - 1; i >= 0; i--) { temp_sensor = devm_kzalloc(dev, sizeof(*temp_sensor), GFP_KERNEL); if (!temp_sensor) return -ENOMEM; @@ -557,7 +558,7 @@ static int asus_wmi_configure_sensor_setup(struct device *dev, dev_dbg(dev, "board has %d sensors", sensor_data->wmi.sensor_count); - hwdev = devm_hwmon_device_register_with_info(dev, KBUILD_MODNAME, + hwdev = devm_hwmon_device_register_with_info(dev, "asus_wmi_sensors", sensor_data, chip_info, NULL); return PTR_ERR_OR_ZERO(hwdev); @@ -572,8 +573,7 @@ static int asus_wmi_probe(struct wmi_device *wdev, const void *context) if (!dmi_check_system(asus_wmi_dmi_table)) return -ENODEV; - sensor_data = devm_kzalloc(dev, sizeof(struct asus_wmi_sensors), - GFP_KERNEL); + sensor_data = devm_kzalloc(dev, sizeof(*sensor_data), GFP_KERNEL); if (!sensor_data) return -ENOMEM; @@ -594,8 +594,7 @@ static int asus_wmi_probe(struct wmi_device *wdev, const void *context) dev_set_drvdata(dev, sensor_data); - return asus_wmi_configure_sensor_setup(dev, - sensor_data); + return asus_wmi_configure_sensor_setup(dev, sensor_data); } static const struct wmi_device_id asus_wmi_id_table[] = { @@ -605,7 +604,7 @@ static const struct wmi_device_id asus_wmi_id_table[] = { static struct wmi_driver asus_sensors_wmi_driver = { .driver = { - .name = KBUILD_MODNAME, + .name = "asus_wmi_sensors", }, .id_table = asus_wmi_id_table, .probe = asus_wmi_probe, From 81e0f57f3c4e857ffc88255f210b2bcb921890a8 Mon Sep 17 00:00:00 2001 From: ed Date: Sat, 30 Oct 2021 23:12:22 +0100 Subject: [PATCH 09/13] v9 Fix memory leaks in asus_wmi_*(). --- asus-wmi-sensors.c | 243 ++++++++++++++++++++++++++------------------- 1 file changed, 142 insertions(+), 101 deletions(-) diff --git a/asus-wmi-sensors.c b/asus-wmi-sensors.c index 45709b6..99553eb 100644 --- a/asus-wmi-sensors.c +++ b/asus-wmi-sensors.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * HWMON driver for ASUS motherboards that provides sensor readouts via WMI * interface present in the UEFI of the X370/X470/B450/X399 Ryzen motherboards. @@ -6,43 +6,42 @@ * Copyright (C) 2018-2019 Ed Brindley * * WMI interface provides: - * CPU Core Voltage, - * CPU SOC Voltage, - * DRAM Voltage, - * VDDP Voltage, - * 1.8V PLL Voltage, - * +12V Voltage, - * +5V Voltage, - * 3VSB Voltage, - * VBAT Voltage, - * AVCC3 Voltage, - * SB 1.05V Voltage, - * CPU Core Voltage, - * CPU SOC Voltage, - * DRAM Voltage, - * CPU Fan RPM, - * Chassis Fan 1 RPM, - * Chassis Fan 2 RPM, - * Chassis Fan 3 RPM, - * HAMP Fan RPM, - * Water Pump RPM, - * CPU OPT RPM, - * Water Flow RPM, - * AIO Pump RPM, - * CPU Temperature, - * CPU Socket Temperature, - * Motherboard Temperature, - * Chipset Temperature, - * Tsensor 1 Temperature, - * CPU VRM Temperature, - * Water In, - * Water Out, - * CPU VRM Output Current. + * - CPU Core Voltage, + * - CPU SOC Voltage, + * - DRAM Voltage, + * - VDDP Voltage, + * - 1.8V PLL Voltage, + * - +12V Voltage, + * - +5V Voltage, + * - 3VSB Voltage, + * - VBAT Voltage, + * - AVCC3 Voltage, + * - SB 1.05V Voltage, + * - CPU Core Voltage, + * - CPU SOC Voltage, + * - DRAM Voltage, + * - CPU Fan RPM, + * - Chassis Fan 1 RPM, + * - Chassis Fan 2 RPM, + * - Chassis Fan 3 RPM, + * - HAMP Fan RPM, + * - Water Pump RPM, + * - CPU OPT RPM, + * - Water Flow RPM, + * - AIO Pump RPM, + * - CPU Temperature, + * - CPU Socket Temperature, + * - Motherboard Temperature, + * - Chipset Temperature, + * - Tsensor 1 Temperature, + * - CPU VRM Temperature, + * - Water In, + * - Water Out, + * - CPU VRM Output Current. */ + #include #include -#include -#include #include #include #include @@ -51,21 +50,23 @@ #include #include -#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" +#include +#include + +#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" #define ASUSWMI_METHODID_GET_VALUE 0x52574543 /* RWEC */ #define ASUSWMI_METHODID_UPDATE_BUFFER 0x51574543 /* QWEC */ #define ASUSWMI_METHODID_GET_INFO 0x50574543 /* PWEC */ #define ASUSWMI_METHODID_GET_NUMBER 0x50574572 /* PWEr */ #define ASUSWMI_METHODID_GET_VERSION 0x50574574 /* PWEt */ -#define ASUS_WMI_MAX_STR_SIZE 32 +#define ASUS_WMI_MAX_STR_SIZE 32 -#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name) { \ - .matches = { \ - DMI_EXACT_MATCH(DMI_BOARD_VENDOR, \ - "ASUSTeK COMPUTER INC."), \ - DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \ - }, \ +#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name) { \ + .matches = { \ + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), \ + DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \ + }, \ } static const struct dmi_system_id asus_wmi_dmi_table[] = { @@ -89,49 +90,49 @@ static const struct dmi_system_id asus_wmi_dmi_table[] = { MODULE_DEVICE_TABLE(dmi, asus_wmi_dmi_table); enum asus_wmi_sensor_class { - VOLTAGE = 0x0, - TEMPERATURE_C = 0x1, - FAN_RPM = 0x2, - CURRENT = 0x3, - WATER_FLOW = 0x4, + VOLTAGE = 0x0, + TEMPERATURE_C = 0x1, + FAN_RPM = 0x2, + CURRENT = 0x3, + WATER_FLOW = 0x4, }; enum asus_wmi_location { - CPU = 0x0, - CPU_SOC = 0x1, - DRAM = 0x2, - MOTHERBOARD = 0x3, - CHIPSET = 0x4, - AUX = 0x5, - VRM = 0x6, - COOLER = 0x7 + CPU = 0x0, + CPU_SOC = 0x1, + DRAM = 0x2, + MOTHERBOARD = 0x3, + CHIPSET = 0x4, + AUX = 0x5, + VRM = 0x6, + COOLER = 0x7 }; enum asus_wmi_type { - SIGNED_INT = 0x0, - UNSIGNED_INT = 0x1, - SCALED = 0x3, + SIGNED_INT = 0x0, + UNSIGNED_INT = 0x1, + SCALED = 0x3, }; enum asus_wmi_source { - SIO = 0x1, - EC = 0x2 + SIO = 0x1, + EC = 0x2 }; static enum hwmon_sensor_types asus_data_types[] = { - [VOLTAGE] = hwmon_in, - [TEMPERATURE_C] = hwmon_temp, - [FAN_RPM] = hwmon_fan, - [CURRENT] = hwmon_curr, - [WATER_FLOW] = hwmon_fan, + [VOLTAGE] = hwmon_in, + [TEMPERATURE_C] = hwmon_temp, + [FAN_RPM] = hwmon_fan, + [CURRENT] = hwmon_curr, + [WATER_FLOW] = hwmon_fan, }; static u32 hwmon_attributes[] = { - [hwmon_chip] = HWMON_C_REGISTER_TZ, - [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL, - [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL, - [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL, - [hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL, + [hwmon_chip] = HWMON_C_REGISTER_TZ, + [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL, + [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL, + [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL, + [hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL, }; /** @@ -164,9 +165,9 @@ struct asus_wmi_wmi_info { }; struct asus_wmi_sensors { + struct asus_wmi_wmi_info wmi; /* lock access to internal cache */ struct mutex lock; - struct asus_wmi_wmi_info wmi; }; /* @@ -200,12 +201,19 @@ static int asus_wmi_get_version(u32 *version) return err; obj = output.pointer; - if (!obj || obj->type != ACPI_TYPE_INTEGER) + if (!obj) return -EIO; + err = -EIO; + if (obj->type != ACPI_TYPE_INTEGER) + goto out_free_obj; + + err = 0; *version = obj->integer.value; - return 0; +out_free_obj: + ACPI_FREE(obj); + return err; } /* @@ -223,12 +231,19 @@ static int asus_wmi_get_item_count(u32 *count) return err; obj = output.pointer; - if (!obj || obj->type != ACPI_TYPE_INTEGER) + if (!obj) return -EIO; + err = -EIO; + if (obj->type != ACPI_TYPE_INTEGER) + goto out_free_obj; + + err = 0; *count = obj->integer.value; - return 0; +out_free_obj: + ACPI_FREE(obj); + return err; } static int asus_wmi_hwmon_add_chan_info(struct hwmon_channel_info *asus_wmi_hwmon_chan, @@ -266,48 +281,55 @@ static int asus_wmi_sensor_info(int index, struct asus_wmi_sensor_info *s) s->id = index; obj = output.pointer; - if (!obj || obj->type != ACPI_TYPE_PACKAGE) + if (!obj) return -EIO; + err = -EIO; + if (obj->type != ACPI_TYPE_PACKAGE) + goto out_free_obj; + if (obj->package.count != 5) - return 1; + goto out_free_obj; name_obj = obj->package.elements[0]; if (name_obj.type != ACPI_TYPE_STRING) - return 1; + goto out_free_obj; strncpy(s->name, name_obj.string.pointer, sizeof(s->name) - 1); data_type_obj = obj->package.elements[1]; if (data_type_obj.type != ACPI_TYPE_INTEGER) - return 1; + goto out_free_obj; s->data_type = data_type_obj.integer.value; location_obj = obj->package.elements[2]; if (location_obj.type != ACPI_TYPE_INTEGER) - return 1; + goto out_free_obj; s->location = location_obj.integer.value; source_obj = obj->package.elements[3]; if (source_obj.type != ACPI_TYPE_INTEGER) - return 1; + goto out_free_obj; s->source = source_obj.integer.value; type_obj = obj->package.elements[4]; if (type_obj.type != ACPI_TYPE_INTEGER) - return 1; + goto out_free_obj; + err = 0; s->type = type_obj.integer.value; - return 0; +out_free_obj: + ACPI_FREE(obj); + return err; } static int asus_wmi_update_buffer(u8 source) @@ -330,15 +352,22 @@ static int asus_wmi_get_sensor_value(u8 index, u32 *value) return err; obj = output.pointer; - if (!obj || obj->type != ACPI_TYPE_INTEGER) + if (!obj) return -EIO; + err = -EIO; + if (obj->type != ACPI_TYPE_INTEGER) + goto out_free_obj; + + err = 0; *value = obj->integer.value; - return 0; +out_free_obj: + ACPI_FREE(obj); + return err; } -static void asus_wmi_update_values_for_source(u8 source, struct asus_wmi_sensors *sensor_data) +static int asus_wmi_update_values_for_source(u8 source, struct asus_wmi_sensors *sensor_data) { struct asus_wmi_sensor_info *sensor; int value = 0; @@ -349,10 +378,14 @@ static void asus_wmi_update_values_for_source(u8 source, struct asus_wmi_sensors sensor = sensor_data->wmi.info_by_id[i]; if (sensor && sensor->source == source) { ret = asus_wmi_get_sensor_value(sensor->id, &value); - if (!ret) - sensor->cached_value = value; + if (ret) + return ret; + + sensor->cached_value = value; } } + + return 0; } static int asus_wmi_scale_sensor_value(u32 value, int data_type) @@ -360,10 +393,13 @@ static int asus_wmi_scale_sensor_value(u32 value, int data_type) /* FAN_RPM and WATER_FLOW don't need scaling */ switch (data_type) { case VOLTAGE: - return DIV_ROUND_CLOSEST(value, MILLI); + /* value in microVolts */ + return DIV_ROUND_CLOSEST(value, KILO); case TEMPERATURE_C: - return value * MILLI; + /* value in Celsius */ + return value * MILLIDEGREE_PER_DEGREE; case CURRENT: + /* value in Amperes */ return value * MILLI; } return value; @@ -379,22 +415,24 @@ static int asus_wmi_get_cached_value_or_update(const struct asus_wmi_sensor_info if (time_after(jiffies, sensor_data->wmi.source_last_updated[sensor->source] + HZ)) { ret = asus_wmi_update_buffer(sensor->source); - if (ret) { - mutex_unlock(&sensor_data->lock); - return -EIO; - } + if (ret) + goto unlock; sensor_data->wmi.buffer = sensor->source; - asus_wmi_update_values_for_source(sensor->source, sensor_data); + ret = asus_wmi_update_values_for_source(sensor->source, sensor_data); + if (ret) + goto unlock; + sensor_data->wmi.source_last_updated[sensor->source] = jiffies; } *value = sensor->cached_value; +unlock: mutex_unlock(&sensor_data->lock); - return 0; + return ret; } /* Now follow the functions that implement the hwmon interface */ @@ -475,7 +513,7 @@ static int asus_wmi_configure_sensor_setup(struct device *dev, for (i = 0; i < sensor_data->wmi.sensor_count; i++) { err = asus_wmi_sensor_info(i, temp_sensor); if (err) - return -EINVAL; + return err; switch (temp_sensor->data_type) { case TEMPERATURE_C: @@ -519,9 +557,12 @@ static int asus_wmi_configure_sensor_setup(struct device *dev, if (!nr_count[type]) continue; - asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev, - nr_count[type], type, - hwmon_attributes[type]); + err = asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev, + nr_count[type], type, + hwmon_attributes[type]); + if (err) + return err; + *ptr_asus_wmi_ci++ = asus_wmi_hwmon_chan++; sensor_data->wmi.info[type] = devm_kcalloc(dev, From 0f048ae281aba6b0084f3db7c73c8985861573a4 Mon Sep 17 00:00:00 2001 From: ed Date: Sun, 31 Oct 2021 22:21:00 +0000 Subject: [PATCH 10/13] Changes in v10: - Use long for sensor values. - Remove unrequired linux/hwmon-sysfs.h - Change code style in error status return. - Remove unuses wmi.buffer and fix type of source. --- asus-wmi-sensors.c | 75 +++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/asus-wmi-sensors.c b/asus-wmi-sensors.c index 99553eb..67af15d 100644 --- a/asus-wmi-sensors.c +++ b/asus-wmi-sensors.c @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -50,9 +51,6 @@ #include #include -#include -#include - #define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" #define ASUSWMI_METHODID_GET_VALUE 0x52574543 /* RWEC */ #define ASUSWMI_METHODID_UPDATE_BUFFER 0x51574543 /* QWEC */ @@ -152,11 +150,10 @@ struct asus_wmi_sensor_info { char name[ASUS_WMI_MAX_STR_SIZE]; int source; int type; - u32 cached_value; + long cached_value; }; struct asus_wmi_wmi_info { - u8 buffer; unsigned long source_last_updated[3]; /* in jiffies */ int sensor_count; @@ -204,9 +201,10 @@ static int asus_wmi_get_version(u32 *version) if (!obj) return -EIO; - err = -EIO; - if (obj->type != ACPI_TYPE_INTEGER) + if (obj->type != ACPI_TYPE_INTEGER) { + err = -EIO; goto out_free_obj; + } err = 0; *version = obj->integer.value; @@ -234,9 +232,10 @@ static int asus_wmi_get_item_count(u32 *count) if (!obj) return -EIO; - err = -EIO; - if (obj->type != ACPI_TYPE_INTEGER) + if (obj->type != ACPI_TYPE_INTEGER) { + err = -EIO; goto out_free_obj; + } err = 0; *count = obj->integer.value; @@ -284,45 +283,53 @@ static int asus_wmi_sensor_info(int index, struct asus_wmi_sensor_info *s) if (!obj) return -EIO; - err = -EIO; - if (obj->type != ACPI_TYPE_PACKAGE) + if (obj->type != ACPI_TYPE_PACKAGE) { + err = -EIO; goto out_free_obj; + } - if (obj->package.count != 5) + if (obj->package.count != 5) { + err = -EIO; goto out_free_obj; + } name_obj = obj->package.elements[0]; - - if (name_obj.type != ACPI_TYPE_STRING) + if (name_obj.type != ACPI_TYPE_STRING) { + err = -EIO; goto out_free_obj; + } strncpy(s->name, name_obj.string.pointer, sizeof(s->name) - 1); data_type_obj = obj->package.elements[1]; - - if (data_type_obj.type != ACPI_TYPE_INTEGER) + if (data_type_obj.type != ACPI_TYPE_INTEGER) { + err = -EIO; goto out_free_obj; + } s->data_type = data_type_obj.integer.value; location_obj = obj->package.elements[2]; - - if (location_obj.type != ACPI_TYPE_INTEGER) + if (location_obj.type != ACPI_TYPE_INTEGER) { + err = -EIO; goto out_free_obj; + } s->location = location_obj.integer.value; source_obj = obj->package.elements[3]; - - if (source_obj.type != ACPI_TYPE_INTEGER) + if (source_obj.type != ACPI_TYPE_INTEGER) { + err = -EIO; goto out_free_obj; + } s->source = source_obj.integer.value; type_obj = obj->package.elements[4]; - - if (type_obj.type != ACPI_TYPE_INTEGER) + if (type_obj.type != ACPI_TYPE_INTEGER) { + err = -EIO; goto out_free_obj; + } err = 0; s->type = type_obj.integer.value; @@ -332,7 +339,7 @@ static int asus_wmi_sensor_info(int index, struct asus_wmi_sensor_info *s) return err; } -static int asus_wmi_update_buffer(u8 source) +static int asus_wmi_update_buffer(int source) { struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; u32 args[] = {source, 0}; @@ -340,7 +347,7 @@ static int asus_wmi_update_buffer(u8 source) return asus_wmi_call_method(ASUSWMI_METHODID_UPDATE_BUFFER, args, &output); } -static int asus_wmi_get_sensor_value(u8 index, u32 *value) +static int asus_wmi_get_sensor_value(u8 index, long *value) { struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; u32 args[] = {index, 0}; @@ -355,9 +362,10 @@ static int asus_wmi_get_sensor_value(u8 index, u32 *value) if (!obj) return -EIO; - err = -EIO; - if (obj->type != ACPI_TYPE_INTEGER) + if (obj->type != ACPI_TYPE_INTEGER) { + err = -EIO; goto out_free_obj; + } err = 0; *value = obj->integer.value; @@ -370,8 +378,8 @@ static int asus_wmi_get_sensor_value(u8 index, u32 *value) static int asus_wmi_update_values_for_source(u8 source, struct asus_wmi_sensors *sensor_data) { struct asus_wmi_sensor_info *sensor; - int value = 0; - int ret = 0; + long value = 0; + int ret; int i; for (i = 0; i < sensor_data->wmi.sensor_count; i++) { @@ -409,7 +417,7 @@ static int asus_wmi_get_cached_value_or_update(const struct asus_wmi_sensor_info struct asus_wmi_sensors *sensor_data, u32 *value) { - int ret; + int ret = 0; mutex_lock(&sensor_data->lock); @@ -418,8 +426,6 @@ static int asus_wmi_get_cached_value_or_update(const struct asus_wmi_sensor_info if (ret) goto unlock; - sensor_data->wmi.buffer = sensor->source; - ret = asus_wmi_update_values_for_source(sensor->source, sensor_data); if (ret) goto unlock; @@ -448,8 +454,10 @@ static int asus_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, sensor = *(sensor_data->wmi.info[type] + channel); ret = asus_wmi_get_cached_value_or_update(sensor, sensor_data, &value); - if (!ret) - *val = asus_wmi_scale_sensor_value(value, sensor->data_type); + if (ret) + return ret; + + *val = asus_wmi_scale_sensor_value(value, sensor->data_type); return ret; } @@ -505,7 +513,6 @@ static int asus_wmi_configure_sensor_setup(struct device *dev, int i, idx; int err; - sensor_data->wmi.buffer = -1; temp_sensor = devm_kcalloc(dev, 1, sizeof(*temp_sensor), GFP_KERNEL); if (!temp_sensor) return -ENOMEM; From 0c85c92a398f844e6973e389801d4269198f0f01 Mon Sep 17 00:00:00 2001 From: ed Date: Wed, 1 Dec 2021 22:02:51 +0000 Subject: [PATCH 11/13] Integrate buffer overflow fix Original commit message: Smatch detects this array overflow: drivers/hwmon/asus_wmi_sensors.c:569 asus_wmi_configure_sensor_setup() error: buffer overflow 'hwmon_attributes' 8 <= 9 The hwmon_attributes[] array should have "hwmon_max" so that it gets larger when more attributes are added. --- asus-wmi-sensors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asus-wmi-sensors.c b/asus-wmi-sensors.c index 67af15d..c80eee8 100644 --- a/asus-wmi-sensors.c +++ b/asus-wmi-sensors.c @@ -125,7 +125,7 @@ static enum hwmon_sensor_types asus_data_types[] = { [WATER_FLOW] = hwmon_fan, }; -static u32 hwmon_attributes[] = { +static u32 hwmon_attributes[hwmon_max] = { [hwmon_chip] = HWMON_C_REGISTER_TZ, [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL, [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL, From 2e66778bf667bc3e13500cb5c9d0d82b37ce923c Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 17 Feb 2022 21:59:43 +0000 Subject: [PATCH 12/13] Add ROG STRIX B450-F GAMING II - see https://lore.kernel.org/all/20220112214917.11662-1-pauk.denis@gmail.com/ --- asus-wmi-sensors.c | 1 + 1 file changed, 1 insertion(+) diff --git a/asus-wmi-sensors.c b/asus-wmi-sensors.c index c80eee8..8fdcb62 100644 --- a/asus-wmi-sensors.c +++ b/asus-wmi-sensors.c @@ -77,6 +77,7 @@ static const struct dmi_system_id asus_wmi_dmi_table[] = { DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VII HERO (WI-FI)"), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-E GAMING"), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-F GAMING"), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-F GAMING II"), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B450-I GAMING"), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X399-E GAMING"), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-F GAMING"), From 3119f2f252149f501f901f10b57d77e6161ae1a4 Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 17 Feb 2022 22:00:34 +0000 Subject: [PATCH 13/13] Update readme to include details of upstreaming --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index da30196..32b6e20 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,12 @@ # Linux ASUS WMI Sensor driver +This patch has now been integrated with the main kernel tree thanks to Denis Pauk, and will be available in Kernel 5.17 + +See https://patchwork.kernel.org/project/linux-hwmon/list/?submitter=201979&archive=both&state=* +post on LKML with "Update ASUS WMI supported boards" in the subject line and posts in https://bugzilla.kernel.org/attachment.cgi?id=299111 + +The changes made during the kernel review process have been backported into this repo + ## General info Provides a Linux kernel module "asus_wmi_sensors" that provides sensor readouts via ASUS' WMI interface present in the UEFI of some X370/X470/B450/X399 Ryzen motherboards.