Skip to contentSkip to conversation

hwmon: Introduce uhwmon for userspace implementations

PATCH In review Jihong Min 4 participants 16 messages Oct 9 – Oct 19 1 message not in the archive

Subsystems (3)
lore
Share & tools
lore

Jihong Min

author
#0 lore raw

cover hwmon: Introduce uhwmon for userspace implementations

This series introduces uhwmon, a generic hwmon interface connecting userspace implementations to the kernel. It exposes hwmon sensor and attribute definitions to userspace through <linux/hwmon.h>, allowing userspace drivers to create standard hwmon sysfs attributes. uhwmon forwards reads and writes through /dev/uhwmon. The hwmon core handles numeric text conversion and newlines for kernel and userspace drivers. The series also adds kernel documentation explaining the interface and how to use its UAPI.

In the October 2024 IT87 discussion [1], a read-only hwmon driver configured from userspace was suggested for chips without datasheets and for testing new chips. Guenter Roeck said he would need proposal details, but questioned whether the hwmon sysfs ABI justified new kernel
infrastructure: userspace drivers would still need hardware knowledge and access. He suggested an API between those drivers and libsensors as an alternative.

But I see practical value in retaining hwmon: the use case below already works with unmodified sensors through its existing hwmon interface. A common bridge can similarly let other daemons expose measurements without adding a new input path to existing hwmon applications, reducing duplicated integration work. The idea is conceptually similar to dummy_hcd with a userspace gadget interface.

uhwmon's goal is to let applications using the standard hwmon interface access sensors without mainline drivers, without adding per-device support to userspace libraries or building and loading separate DKMS modules. In theory, projects such as wireview-hwmon [2] could replace their device-specific DKMS module with uhwmon and adapt their daemon to use it. My current use case is bridging the Tapo API [3] to hwmon, using a TAPO P110M smart plug to measure my system's power draw at the wall in place of an ACPI power meter. I have tested this setup in an Ubuntu VM, including live readings, daemon disconnect and recovery, and reboot startup.

[1] https://lkml.iu.edu/2410.2/11841.html
[2] https://github.com/emaspa/wireview-hwmon
[3] https://github.com/mihai-dinculescu/tapo

Jihong Min (5):
hwmon: Export sensor and attribute definitions to userspace
hwmon: Expose attribute validation and string helpers
hwmon: Add a generic interface for userspace implementations
hwmon: Reject truncated attribute names
docs: hwmon: Document the uhwmon userspace interface

 Documentation/hwmon/index.rst                      |   1 +
 Documentation/hwmon/uhwmon.rst                     | 141 +++++
 Documentation/userspace-api/ioctl/ioctl-number.rst |   1 +
 MAINTAINERS                                        |   2 +
 drivers/hwmon/Kconfig                              |   9 +
 drivers/hwmon/Makefile                             |   1 +
 drivers/hwmon/hwmon.c                              |  38 +-
 drivers/hwmon/uhwmon.c                             | 641 +++++++++++++++++++++
 include/linux/hwmon.h                              | 356 +-----------
 include/uapi/linux/hwmon.h                         | 372 ++++++++++++
 include/uapi/linux/uhwmon.h                        |  64 ++
 11 files changed, 1267 insertions(+), 359 deletions(-)
 create mode 100644 Documentation/hwmon/uhwmon.rst
 create mode 100644 drivers/hwmon/uhwmon.c
 create mode 100644 include/uapi/linux/hwmon.h
 create mode 100644 include/uapi/linux/uhwmon.h

base-commit: 7b63ef2d55f24519e7e9e5f4d15dbea03f126e40

Sincerely,
Jihong Min

author
#1 lore raw

PATCH 1/5 hwmon: Export sensor and attribute definitions to userspace

Move sensor types, attribute identifiers and configuration bits to linux/hwmon.h in the UAPI so kernel and userspace drivers share them. Keep existing values unchanged and use userspace-safe bit definitions.

Keep operations, channel structures and registration helpers in the kernel header.

Assisted-by Codex:gpt-6-astra
---
 MAINTAINERS                |   1 +
 include/linux/hwmon.h      | 353 +----------------------------------
 include/uapi/linux/hwmon.h | 372 +++++++++++++++++++++++++++++++++++++
 3 files changed, 374 insertions(+), 352 deletions(-)
 create mode 100644 include/uapi/linux/hwmon.h
MAINTAINERS +1 -0
index 65e8a4b5c90b..f3f0292832bb 100644
@@ -11492,6 +11492,7 @@ F:	Documentation/hwmon/
 F:	drivers/hwmon/
 F:	include/linux/hwmon*.h
 F:	include/trace/events/hwmon*.h
+F:	include/uapi/linux/hwmon.h
 K:	(devm_)?hwmon_device_(un)?register(|_with_groups|_with_info)
 
 HARDWARE RANDOM NUMBER GENERATOR CORE
include/linux/hwmon.h +1 -352

363 lines not on this page · raw message

include/uapi/linux/hwmon.h new file +372 -0

372 lines not on this page · raw message

#2 lore raw

Re: [PATCH 1/5] hwmon: Export sensor and attribute definitions to userspace

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] hwmon: UAPI exposure of hwmon_max sentinel and internal bitmasks --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] hwmon: UAPI exposure of hwmon_max sentinel and internal bitmasks --

commit bbc47a707f27865e8c59d7c4ea18a1b742278dfd
Author: Jihong Min <[email protected]>

hwmon: Export sensor and attribute definitions to userspace

Move sensor types, attribute identifiers and configuration bits to linux/hwmon.h in the UAPI so kernel and userspace drivers share them. Keep existing values unchanged and use userspace-safe bit definitions.

Quoted 6 lines
include/uapi/linux/hwmon.h new file +0 -0
new file mode 100644
index 0000000000000..274f9c9dcb87e
@@ -0,0 +1,372 @@

[ ... ]

include/uapi/linux/hwmon.h line 31

Quoted 13 lines
include/uapi/linux/hwmon.h new file +13 -0
+enum hwmon_sensor_types {
+	hwmon_chip = 0,
+	hwmon_temp = 1,
+	hwmon_in = 2,
+	hwmon_curr = 3,
+	hwmon_power = 4,
+	hwmon_energy = 5,
+	hwmon_energy64 = 6,
+	hwmon_humidity = 7,
+	hwmon_fan = 8,
+	hwmon_pwm = 9,
+	hwmon_intrusion = 10,
+	hwmon_max = 11,
        ^^^^^^^^^^^^^^

[Severity: Medium]
Could exporting this expandable max boundary integer to the stable userspace UAPI header lead to out-of-bounds memory accesses in userspace applications?

If a userspace program statically sizes an array based on hwmon_max, and a future kernel release adds a new sensor type (incrementing hwmon_max), the program could index past the end of its array when receiving the new sensor type.

Quoted 11 lines
include/uapi/linux/hwmon.h new file +11 -0
+};
+
+enum hwmon_chip_attributes {
+	hwmon_chip_temp_reset_history = 0,
+	hwmon_chip_in_reset_history = 1,
+	hwmon_chip_curr_reset_history = 2,
+	hwmon_chip_power_reset_history = 3,
+	hwmon_chip_register_tz = 4,
+	hwmon_chip_update_interval = 5,
+	hwmon_chip_update_interval_us = 6,
+	hwmon_chip_alarms = 7,

include/uapi/linux/hwmon.h line 52

Quoted 10 lines
include/uapi/linux/hwmon.h new file +10 -0
+	hwmon_chip_samples = 8,
+	hwmon_chip_curr_samples = 9,
+	hwmon_chip_in_samples = 10,
+	hwmon_chip_power_samples = 11,
+	hwmon_chip_temp_samples = 12,
+	hwmon_chip_beep_enable = 13,
+	hwmon_chip_pec = 14,
+};
+
+#define HWMON_C_TEMP_RESET_HISTORY _BITUL(hwmon_chip_temp_reset_history)

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[Severity: Medium]
Is it necessary to export these configuration bitmasks to the UAPI?

These bitmasks are strictly used by the kernel-internal struct hwmon_channel_info and provide no value to userspace. Exporting them allows and encourages anti-patterns in userspace.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
maintainer
#3 lore raw

Re: [PATCH 1/5] hwmon: Export sensor and attribute definitions to userspace

In reply to Jihong Min
Quoted 9 lines · Jihong Min

Move sensor types, attribute identifiers and configuration bits to linux/hwmon.h in the UAPI so kernel and userspace drivers share them. Keep existing values unchanged and use userspace-safe bit definitions.

Keep operations, channel structures and registration helpers in the kernel header.

Assisted-by Codex:gpt-6-astra

NACK. Those definitions are kernel internal and subject to change.

Guenter

Quoted 765 lines
---
 MAINTAINERS                |   1 +
 include/linux/hwmon.h      | 353 +----------------------------------
 include/uapi/linux/hwmon.h | 372 +++++++++++++++++++++++++++++++++++++
 3 files changed, 374 insertions(+), 352 deletions(-)
 create mode 100644 include/uapi/linux/hwmon.h
MAINTAINERS +1 -0
index 65e8a4b5c90b..f3f0292832bb 100644
@@ -11492,6 +11492,7 @@ F:	Documentation/hwmon/
 F:	drivers/hwmon/
 F:	include/linux/hwmon*.h
 F:	include/trace/events/hwmon*.h
+F:	include/uapi/linux/hwmon.h
 K:	(devm_)?hwmon_device_(un)?register(|_with_groups|_with_info)
 
 HARDWARE RANDOM NUMBER GENERATOR CORE
include/linux/hwmon.h +1 -352
index dd713e193d0c..d48e0aa82bc5 100644
@@ -14,362 +14,11 @@
 
 #include <linux/bitops.h>
 #include <linux/cleanup.h>
+#include <uapi/linux/hwmon.h>
 
 struct device;
 struct attribute_group;
 
-enum hwmon_sensor_types {
-	hwmon_chip,
-	hwmon_temp,
-	hwmon_in,
-	hwmon_curr,
-	hwmon_power,
-	hwmon_energy,
-	hwmon_energy64,
-	hwmon_humidity,
-	hwmon_fan,
-	hwmon_pwm,
-	hwmon_intrusion,
-	hwmon_max,
-};
-
-enum hwmon_chip_attributes {
-	hwmon_chip_temp_reset_history,
-	hwmon_chip_in_reset_history,
-	hwmon_chip_curr_reset_history,
-	hwmon_chip_power_reset_history,
-	hwmon_chip_register_tz,
-	hwmon_chip_update_interval,
-	hwmon_chip_update_interval_us,
-	hwmon_chip_alarms,
-	hwmon_chip_samples,
-	hwmon_chip_curr_samples,
-	hwmon_chip_in_samples,
-	hwmon_chip_power_samples,
-	hwmon_chip_temp_samples,
-	hwmon_chip_beep_enable,
-	hwmon_chip_pec,
-};
-
-#define HWMON_C_TEMP_RESET_HISTORY	BIT(hwmon_chip_temp_reset_history)
-#define HWMON_C_IN_RESET_HISTORY	BIT(hwmon_chip_in_reset_history)
-#define HWMON_C_CURR_RESET_HISTORY	BIT(hwmon_chip_curr_reset_history)
-#define HWMON_C_POWER_RESET_HISTORY	BIT(hwmon_chip_power_reset_history)
-#define HWMON_C_REGISTER_TZ		BIT(hwmon_chip_register_tz)
-#define HWMON_C_UPDATE_INTERVAL		BIT(hwmon_chip_update_interval)
-#define HWMON_C_UPDATE_INTERVAL_US	BIT(hwmon_chip_update_interval_us)
-#define HWMON_C_ALARMS			BIT(hwmon_chip_alarms)
-#define HWMON_C_SAMPLES			BIT(hwmon_chip_samples)
-#define HWMON_C_CURR_SAMPLES		BIT(hwmon_chip_curr_samples)
-#define HWMON_C_IN_SAMPLES		BIT(hwmon_chip_in_samples)
-#define HWMON_C_POWER_SAMPLES		BIT(hwmon_chip_power_samples)
-#define HWMON_C_TEMP_SAMPLES		BIT(hwmon_chip_temp_samples)
-#define HWMON_C_BEEP_ENABLE		BIT(hwmon_chip_beep_enable)
-#define HWMON_C_PEC			BIT(hwmon_chip_pec)
-
-enum hwmon_temp_attributes {
-	hwmon_temp_enable,
-	hwmon_temp_input,
-	hwmon_temp_type,
-	hwmon_temp_lcrit,
-	hwmon_temp_lcrit_hyst,
-	hwmon_temp_min,
-	hwmon_temp_min_hyst,
-	hwmon_temp_max,
-	hwmon_temp_max_hyst,
-	hwmon_temp_crit,
-	hwmon_temp_crit_hyst,
-	hwmon_temp_emergency,
-	hwmon_temp_emergency_hyst,
-	hwmon_temp_alarm,
-	hwmon_temp_lcrit_alarm,
-	hwmon_temp_min_alarm,
-	hwmon_temp_max_alarm,
-	hwmon_temp_crit_alarm,
-	hwmon_temp_emergency_alarm,
-	hwmon_temp_fault,
-	hwmon_temp_offset,
-	hwmon_temp_label,
-	hwmon_temp_lowest,
-	hwmon_temp_highest,
-	hwmon_temp_reset_history,
-	hwmon_temp_rated_min,
-	hwmon_temp_rated_max,
-	hwmon_temp_beep,
-};
-
-#define HWMON_T_ENABLE		BIT(hwmon_temp_enable)
-#define HWMON_T_INPUT		BIT(hwmon_temp_input)
-#define HWMON_T_TYPE		BIT(hwmon_temp_type)
-#define HWMON_T_LCRIT		BIT(hwmon_temp_lcrit)
-#define HWMON_T_LCRIT_HYST	BIT(hwmon_temp_lcrit_hyst)
-#define HWMON_T_MIN		BIT(hwmon_temp_min)
-#define HWMON_T_MIN_HYST	BIT(hwmon_temp_min_hyst)
-#define HWMON_T_MAX		BIT(hwmon_temp_max)
-#define HWMON_T_MAX_HYST	BIT(hwmon_temp_max_hyst)
-#define HWMON_T_CRIT		BIT(hwmon_temp_crit)
-#define HWMON_T_CRIT_HYST	BIT(hwmon_temp_crit_hyst)
-#define HWMON_T_EMERGENCY	BIT(hwmon_temp_emergency)
-#define HWMON_T_EMERGENCY_HYST	BIT(hwmon_temp_emergency_hyst)
-#define HWMON_T_ALARM		BIT(hwmon_temp_alarm)
-#define HWMON_T_MIN_ALARM	BIT(hwmon_temp_min_alarm)
-#define HWMON_T_MAX_ALARM	BIT(hwmon_temp_max_alarm)
-#define HWMON_T_CRIT_ALARM	BIT(hwmon_temp_crit_alarm)
-#define HWMON_T_LCRIT_ALARM	BIT(hwmon_temp_lcrit_alarm)
-#define HWMON_T_EMERGENCY_ALARM	BIT(hwmon_temp_emergency_alarm)
-#define HWMON_T_FAULT		BIT(hwmon_temp_fault)
-#define HWMON_T_OFFSET		BIT(hwmon_temp_offset)
-#define HWMON_T_LABEL		BIT(hwmon_temp_label)
-#define HWMON_T_LOWEST		BIT(hwmon_temp_lowest)
-#define HWMON_T_HIGHEST		BIT(hwmon_temp_highest)
-#define HWMON_T_RESET_HISTORY	BIT(hwmon_temp_reset_history)
-#define HWMON_T_RATED_MIN	BIT(hwmon_temp_rated_min)
-#define HWMON_T_RATED_MAX	BIT(hwmon_temp_rated_max)
-#define HWMON_T_BEEP		BIT(hwmon_temp_beep)
-
-enum hwmon_in_attributes {
-	hwmon_in_enable,
-	hwmon_in_input,
-	hwmon_in_min,
-	hwmon_in_max,
-	hwmon_in_lcrit,
-	hwmon_in_crit,
-	hwmon_in_average,
-	hwmon_in_lowest,
-	hwmon_in_highest,
-	hwmon_in_reset_history,
-	hwmon_in_label,
-	hwmon_in_alarm,
-	hwmon_in_min_alarm,
-	hwmon_in_max_alarm,
-	hwmon_in_lcrit_alarm,
-	hwmon_in_crit_alarm,
-	hwmon_in_rated_min,
-	hwmon_in_rated_max,
-	hwmon_in_beep,
-	hwmon_in_fault,
-};
-
-#define HWMON_I_ENABLE		BIT(hwmon_in_enable)
-#define HWMON_I_INPUT		BIT(hwmon_in_input)
-#define HWMON_I_MIN		BIT(hwmon_in_min)
-#define HWMON_I_MAX		BIT(hwmon_in_max)
-#define HWMON_I_LCRIT		BIT(hwmon_in_lcrit)
-#define HWMON_I_CRIT		BIT(hwmon_in_crit)
-#define HWMON_I_AVERAGE		BIT(hwmon_in_average)
-#define HWMON_I_LOWEST		BIT(hwmon_in_lowest)
-#define HWMON_I_HIGHEST		BIT(hwmon_in_highest)
-#define HWMON_I_RESET_HISTORY	BIT(hwmon_in_reset_history)
-#define HWMON_I_LABEL		BIT(hwmon_in_label)
-#define HWMON_I_ALARM		BIT(hwmon_in_alarm)
-#define HWMON_I_MIN_ALARM	BIT(hwmon_in_min_alarm)
-#define HWMON_I_MAX_ALARM	BIT(hwmon_in_max_alarm)
-#define HWMON_I_LCRIT_ALARM	BIT(hwmon_in_lcrit_alarm)
-#define HWMON_I_CRIT_ALARM	BIT(hwmon_in_crit_alarm)
-#define HWMON_I_RATED_MIN	BIT(hwmon_in_rated_min)
-#define HWMON_I_RATED_MAX	BIT(hwmon_in_rated_max)
-#define HWMON_I_BEEP		BIT(hwmon_in_beep)
-#define HWMON_I_FAULT		BIT(hwmon_in_fault)
-
-enum hwmon_curr_attributes {
-	hwmon_curr_enable,
-	hwmon_curr_input,
-	hwmon_curr_min,
-	hwmon_curr_max,
-	hwmon_curr_lcrit,
-	hwmon_curr_crit,
-	hwmon_curr_average,
-	hwmon_curr_lowest,
-	hwmon_curr_highest,
-	hwmon_curr_reset_history,
-	hwmon_curr_label,
-	hwmon_curr_alarm,
-	hwmon_curr_min_alarm,
-	hwmon_curr_max_alarm,
-	hwmon_curr_lcrit_alarm,
-	hwmon_curr_crit_alarm,
-	hwmon_curr_rated_min,
-	hwmon_curr_rated_max,
-	hwmon_curr_beep,
-};
-
-#define HWMON_C_ENABLE		BIT(hwmon_curr_enable)
-#define HWMON_C_INPUT		BIT(hwmon_curr_input)
-#define HWMON_C_MIN		BIT(hwmon_curr_min)
-#define HWMON_C_MAX		BIT(hwmon_curr_max)
-#define HWMON_C_LCRIT		BIT(hwmon_curr_lcrit)
-#define HWMON_C_CRIT		BIT(hwmon_curr_crit)
-#define HWMON_C_AVERAGE		BIT(hwmon_curr_average)
-#define HWMON_C_LOWEST		BIT(hwmon_curr_lowest)
-#define HWMON_C_HIGHEST		BIT(hwmon_curr_highest)
-#define HWMON_C_RESET_HISTORY	BIT(hwmon_curr_reset_history)
-#define HWMON_C_LABEL		BIT(hwmon_curr_label)
-#define HWMON_C_ALARM		BIT(hwmon_curr_alarm)
-#define HWMON_C_MIN_ALARM	BIT(hwmon_curr_min_alarm)
-#define HWMON_C_MAX_ALARM	BIT(hwmon_curr_max_alarm)
-#define HWMON_C_LCRIT_ALARM	BIT(hwmon_curr_lcrit_alarm)
-#define HWMON_C_CRIT_ALARM	BIT(hwmon_curr_crit_alarm)
-#define HWMON_C_RATED_MIN	BIT(hwmon_curr_rated_min)
-#define HWMON_C_RATED_MAX	BIT(hwmon_curr_rated_max)
-#define HWMON_C_BEEP		BIT(hwmon_curr_beep)
-
-enum hwmon_power_attributes {
-	hwmon_power_enable,
-	hwmon_power_average,
-	hwmon_power_average_interval,
-	hwmon_power_average_interval_max,
-	hwmon_power_average_interval_min,
-	hwmon_power_average_highest,
-	hwmon_power_average_lowest,
-	hwmon_power_average_max,
-	hwmon_power_average_min,
-	hwmon_power_input,
-	hwmon_power_input_highest,
-	hwmon_power_input_lowest,
-	hwmon_power_reset_history,
-	hwmon_power_accuracy,
-	hwmon_power_cap,
-	hwmon_power_cap_hyst,
-	hwmon_power_cap_max,
-	hwmon_power_cap_min,
-	hwmon_power_min,
-	hwmon_power_max,
-	hwmon_power_crit,
-	hwmon_power_lcrit,
-	hwmon_power_label,
-	hwmon_power_alarm,
-	hwmon_power_cap_alarm,
-	hwmon_power_min_alarm,
-	hwmon_power_max_alarm,
-	hwmon_power_lcrit_alarm,
-	hwmon_power_crit_alarm,
-	hwmon_power_rated_min,
-	hwmon_power_rated_max,
-};
-
-#define HWMON_P_ENABLE			BIT(hwmon_power_enable)
-#define HWMON_P_AVERAGE			BIT(hwmon_power_average)
-#define HWMON_P_AVERAGE_INTERVAL	BIT(hwmon_power_average_interval)
-#define HWMON_P_AVERAGE_INTERVAL_MAX	BIT(hwmon_power_average_interval_max)
-#define HWMON_P_AVERAGE_INTERVAL_MIN	BIT(hwmon_power_average_interval_min)
-#define HWMON_P_AVERAGE_HIGHEST		BIT(hwmon_power_average_highest)
-#define HWMON_P_AVERAGE_LOWEST		BIT(hwmon_power_average_lowest)
-#define HWMON_P_AVERAGE_MAX		BIT(hwmon_power_average_max)
-#define HWMON_P_AVERAGE_MIN		BIT(hwmon_power_average_min)
-#define HWMON_P_INPUT			BIT(hwmon_power_input)
-#define HWMON_P_INPUT_HIGHEST		BIT(hwmon_power_input_highest)
-#define HWMON_P_INPUT_LOWEST		BIT(hwmon_power_input_lowest)
-#define HWMON_P_RESET_HISTORY		BIT(hwmon_power_reset_history)
-#define HWMON_P_ACCURACY		BIT(hwmon_power_accuracy)
-#define HWMON_P_CAP			BIT(hwmon_power_cap)
-#define HWMON_P_CAP_HYST		BIT(hwmon_power_cap_hyst)
-#define HWMON_P_CAP_MAX			BIT(hwmon_power_cap_max)
-#define HWMON_P_CAP_MIN			BIT(hwmon_power_cap_min)
-#define HWMON_P_MIN			BIT(hwmon_power_min)
-#define HWMON_P_MAX			BIT(hwmon_power_max)
-#define HWMON_P_LCRIT			BIT(hwmon_power_lcrit)
-#define HWMON_P_CRIT			BIT(hwmon_power_crit)
-#define HWMON_P_LABEL			BIT(hwmon_power_label)
-#define HWMON_P_ALARM			BIT(hwmon_power_alarm)
-#define HWMON_P_CAP_ALARM		BIT(hwmon_power_cap_alarm)
-#define HWMON_P_MIN_ALARM		BIT(hwmon_power_min_alarm)
-#define HWMON_P_MAX_ALARM		BIT(hwmon_power_max_alarm)
-#define HWMON_P_LCRIT_ALARM		BIT(hwmon_power_lcrit_alarm)
-#define HWMON_P_CRIT_ALARM		BIT(hwmon_power_crit_alarm)
-#define HWMON_P_RATED_MIN		BIT(hwmon_power_rated_min)
-#define HWMON_P_RATED_MAX		BIT(hwmon_power_rated_max)
-
-enum hwmon_energy_attributes {
-	hwmon_energy_enable,
-	hwmon_energy_input,
-	hwmon_energy_label,
-};
-
-#define HWMON_E_ENABLE			BIT(hwmon_energy_enable)
-#define HWMON_E_INPUT			BIT(hwmon_energy_input)
-#define HWMON_E_LABEL			BIT(hwmon_energy_label)
-
-enum hwmon_humidity_attributes {
-	hwmon_humidity_enable,
-	hwmon_humidity_input,
-	hwmon_humidity_label,
-	hwmon_humidity_min,
-	hwmon_humidity_min_hyst,
-	hwmon_humidity_max,
-	hwmon_humidity_max_hyst,
-	hwmon_humidity_alarm,
-	hwmon_humidity_fault,
-	hwmon_humidity_rated_min,
-	hwmon_humidity_rated_max,
-	hwmon_humidity_min_alarm,
-	hwmon_humidity_max_alarm,
-};
-
-#define HWMON_H_ENABLE			BIT(hwmon_humidity_enable)
-#define HWMON_H_INPUT			BIT(hwmon_humidity_input)
-#define HWMON_H_LABEL			BIT(hwmon_humidity_label)
-#define HWMON_H_MIN			BIT(hwmon_humidity_min)
-#define HWMON_H_MIN_HYST		BIT(hwmon_humidity_min_hyst)
-#define HWMON_H_MAX			BIT(hwmon_humidity_max)
-#define HWMON_H_MAX_HYST		BIT(hwmon_humidity_max_hyst)
-#define HWMON_H_ALARM			BIT(hwmon_humidity_alarm)
-#define HWMON_H_FAULT			BIT(hwmon_humidity_fault)
-#define HWMON_H_RATED_MIN		BIT(hwmon_humidity_rated_min)
-#define HWMON_H_RATED_MAX		BIT(hwmon_humidity_rated_max)
-#define HWMON_H_MIN_ALARM		BIT(hwmon_humidity_min_alarm)
-#define HWMON_H_MAX_ALARM		BIT(hwmon_humidity_max_alarm)
-
-enum hwmon_fan_attributes {
-	hwmon_fan_enable,
-	hwmon_fan_input,
-	hwmon_fan_label,
-	hwmon_fan_min,
-	hwmon_fan_max,
-	hwmon_fan_div,
-	hwmon_fan_pulses,
-	hwmon_fan_target,
-	hwmon_fan_alarm,
-	hwmon_fan_min_alarm,
-	hwmon_fan_max_alarm,
-	hwmon_fan_fault,
-	hwmon_fan_beep,
-};
-
-#define HWMON_F_ENABLE			BIT(hwmon_fan_enable)
-#define HWMON_F_INPUT			BIT(hwmon_fan_input)
-#define HWMON_F_LABEL			BIT(hwmon_fan_label)
-#define HWMON_F_MIN			BIT(hwmon_fan_min)
-#define HWMON_F_MAX			BIT(hwmon_fan_max)
-#define HWMON_F_DIV			BIT(hwmon_fan_div)
-#define HWMON_F_PULSES			BIT(hwmon_fan_pulses)
-#define HWMON_F_TARGET			BIT(hwmon_fan_target)
-#define HWMON_F_ALARM			BIT(hwmon_fan_alarm)
-#define HWMON_F_MIN_ALARM		BIT(hwmon_fan_min_alarm)
-#define HWMON_F_MAX_ALARM		BIT(hwmon_fan_max_alarm)
-#define HWMON_F_FAULT			BIT(hwmon_fan_fault)
-#define HWMON_F_BEEP			BIT(hwmon_fan_beep)
-
-enum hwmon_pwm_attributes {
-	hwmon_pwm_input,
-	hwmon_pwm_enable,
-	hwmon_pwm_mode,
-	hwmon_pwm_freq,
-	hwmon_pwm_auto_channels_temp,
-};
-
-#define HWMON_PWM_INPUT			BIT(hwmon_pwm_input)
-#define HWMON_PWM_ENABLE		BIT(hwmon_pwm_enable)
-#define HWMON_PWM_MODE			BIT(hwmon_pwm_mode)
-#define HWMON_PWM_FREQ			BIT(hwmon_pwm_freq)
-#define HWMON_PWM_AUTO_CHANNELS_TEMP	BIT(hwmon_pwm_auto_channels_temp)
-
-enum hwmon_intrusion_attributes {
-	hwmon_intrusion_alarm,
-	hwmon_intrusion_beep,
-};
-#define HWMON_INTRUSION_ALARM		BIT(hwmon_intrusion_alarm)
-#define HWMON_INTRUSION_BEEP		BIT(hwmon_intrusion_beep)
-
 /**
  * struct hwmon_ops - hwmon device operations
  * @visible:	Static visibility. If non-zero, 'is_visible' is ignored.
include/uapi/linux/hwmon.h new file +372 -0
new file mode 100644
index 000000000000..274f9c9dcb87
@@ -0,0 +1,372 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Hardware monitoring sensor types, attributes and configuration bits.
+ *
+ * Copyright (C) 2005 Mark M. Hoffman <[email protected]>
+ * Copyright (C) 2026 Jihong Min <[email protected]>
+ */
+
+#ifndef _UAPI_LINUX_HWMON_H
+#define _UAPI_LINUX_HWMON_H
+
+#include <linux/const.h>
+
+/*
+ * Existing identifiers are ABI: never change their values. Append new attributes
+ * at the end of their enum and new sensor types before hwmon_max.
+ */
+
+enum hwmon_sensor_types {
+	hwmon_chip = 0,
+	hwmon_temp = 1,
+	hwmon_in = 2,
+	hwmon_curr = 3,
+	hwmon_power = 4,
+	hwmon_energy = 5,
+	hwmon_energy64 = 6,
+	hwmon_humidity = 7,
+	hwmon_fan = 8,
+	hwmon_pwm = 9,
+	hwmon_intrusion = 10,
+	hwmon_max = 11,
+};
+
+enum hwmon_chip_attributes {
+	hwmon_chip_temp_reset_history = 0,
+	hwmon_chip_in_reset_history = 1,
+	hwmon_chip_curr_reset_history = 2,
+	hwmon_chip_power_reset_history = 3,
+	hwmon_chip_register_tz = 4,
+	hwmon_chip_update_interval = 5,
+	hwmon_chip_update_interval_us = 6,
+	hwmon_chip_alarms = 7,
+	hwmon_chip_samples = 8,
+	hwmon_chip_curr_samples = 9,
+	hwmon_chip_in_samples = 10,
+	hwmon_chip_power_samples = 11,
+	hwmon_chip_temp_samples = 12,
+	hwmon_chip_beep_enable = 13,
+	hwmon_chip_pec = 14,
+};
+
+#define HWMON_C_TEMP_RESET_HISTORY _BITUL(hwmon_chip_temp_reset_history)
+#define HWMON_C_IN_RESET_HISTORY _BITUL(hwmon_chip_in_reset_history)
+#define HWMON_C_CURR_RESET_HISTORY _BITUL(hwmon_chip_curr_reset_history)
+#define HWMON_C_POWER_RESET_HISTORY _BITUL(hwmon_chip_power_reset_history)
+#define HWMON_C_REGISTER_TZ _BITUL(hwmon_chip_register_tz)
+#define HWMON_C_UPDATE_INTERVAL _BITUL(hwmon_chip_update_interval)
+#define HWMON_C_UPDATE_INTERVAL_US _BITUL(hwmon_chip_update_interval_us)
+#define HWMON_C_ALARMS _BITUL(hwmon_chip_alarms)
+#define HWMON_C_SAMPLES _BITUL(hwmon_chip_samples)
+#define HWMON_C_CURR_SAMPLES _BITUL(hwmon_chip_curr_samples)
+#define HWMON_C_IN_SAMPLES _BITUL(hwmon_chip_in_samples)
+#define HWMON_C_POWER_SAMPLES _BITUL(hwmon_chip_power_samples)
+#define HWMON_C_TEMP_SAMPLES _BITUL(hwmon_chip_temp_samples)
+#define HWMON_C_BEEP_ENABLE _BITUL(hwmon_chip_beep_enable)
+#define HWMON_C_PEC _BITUL(hwmon_chip_pec)
+
+enum hwmon_temp_attributes {
+	hwmon_temp_enable = 0,
+	hwmon_temp_input = 1,
+	hwmon_temp_type = 2,
+	hwmon_temp_lcrit = 3,
+	hwmon_temp_lcrit_hyst = 4,
+	hwmon_temp_min = 5,
+	hwmon_temp_min_hyst = 6,
+	hwmon_temp_max = 7,
+	hwmon_temp_max_hyst = 8,
+	hwmon_temp_crit = 9,
+	hwmon_temp_crit_hyst = 10,
+	hwmon_temp_emergency = 11,
+	hwmon_temp_emergency_hyst = 12,
+	hwmon_temp_alarm = 13,
+	hwmon_temp_lcrit_alarm = 14,
+	hwmon_temp_min_alarm = 15,
+	hwmon_temp_max_alarm = 16,
+	hwmon_temp_crit_alarm = 17,
+	hwmon_temp_emergency_alarm = 18,
+	hwmon_temp_fault = 19,
+	hwmon_temp_offset = 20,
+	hwmon_temp_label = 21,
+	hwmon_temp_lowest = 22,
+	hwmon_temp_highest = 23,
+	hwmon_temp_reset_history = 24,
+	hwmon_temp_rated_min = 25,
+	hwmon_temp_rated_max = 26,
+	hwmon_temp_beep = 27,
+};
+
+#define HWMON_T_ENABLE _BITUL(hwmon_temp_enable)
+#define HWMON_T_INPUT _BITUL(hwmon_temp_input)
+#define HWMON_T_TYPE _BITUL(hwmon_temp_type)
+#define HWMON_T_LCRIT _BITUL(hwmon_temp_lcrit)
+#define HWMON_T_LCRIT_HYST _BITUL(hwmon_temp_lcrit_hyst)
+#define HWMON_T_MIN _BITUL(hwmon_temp_min)
+#define HWMON_T_MIN_HYST _BITUL(hwmon_temp_min_hyst)
+#define HWMON_T_MAX _BITUL(hwmon_temp_max)
+#define HWMON_T_MAX_HYST _BITUL(hwmon_temp_max_hyst)
+#define HWMON_T_CRIT _BITUL(hwmon_temp_crit)
+#define HWMON_T_CRIT_HYST _BITUL(hwmon_temp_crit_hyst)
+#define HWMON_T_EMERGENCY _BITUL(hwmon_temp_emergency)
+#define HWMON_T_EMERGENCY_HYST _BITUL(hwmon_temp_emergency_hyst)
+#define HWMON_T_ALARM _BITUL(hwmon_temp_alarm)
+#define HWMON_T_MIN_ALARM _BITUL(hwmon_temp_min_alarm)
+#define HWMON_T_MAX_ALARM _BITUL(hwmon_temp_max_alarm)
+#define HWMON_T_CRIT_ALARM _BITUL(hwmon_temp_crit_alarm)
+#define HWMON_T_LCRIT_ALARM _BITUL(hwmon_temp_lcrit_alarm)
+#define HWMON_T_EMERGENCY_ALARM _BITUL(hwmon_temp_emergency_alarm)
+#define HWMON_T_FAULT _BITUL(hwmon_temp_fault)
+#define HWMON_T_OFFSET _BITUL(hwmon_temp_offset)
+#define HWMON_T_LABEL _BITUL(hwmon_temp_label)
+#define HWMON_T_LOWEST _BITUL(hwmon_temp_lowest)
+#define HWMON_T_HIGHEST _BITUL(hwmon_temp_highest)
+#define HWMON_T_RESET_HISTORY _BITUL(hwmon_temp_reset_history)
+#define HWMON_T_RATED_MIN _BITUL(hwmon_temp_rated_min)
+#define HWMON_T_RATED_MAX _BITUL(hwmon_temp_rated_max)
+#define HWMON_T_BEEP _BITUL(hwmon_temp_beep)
+
+enum hwmon_in_attributes {
+	hwmon_in_enable = 0,
+	hwmon_in_input = 1,
+	hwmon_in_min = 2,
+	hwmon_in_max = 3,
+	hwmon_in_lcrit = 4,
+	hwmon_in_crit = 5,
+	hwmon_in_average = 6,
+	hwmon_in_lowest = 7,
+	hwmon_in_highest = 8,
+	hwmon_in_reset_history = 9,
+	hwmon_in_label = 10,
+	hwmon_in_alarm = 11,
+	hwmon_in_min_alarm = 12,
+	hwmon_in_max_alarm = 13,
+	hwmon_in_lcrit_alarm = 14,
+	hwmon_in_crit_alarm = 15,
+	hwmon_in_rated_min = 16,
+	hwmon_in_rated_max = 17,
+	hwmon_in_beep = 18,
+	hwmon_in_fault = 19,
+};
+
+#define HWMON_I_ENABLE _BITUL(hwmon_in_enable)
+#define HWMON_I_INPUT _BITUL(hwmon_in_input)
+#define HWMON_I_MIN _BITUL(hwmon_in_min)
+#define HWMON_I_MAX _BITUL(hwmon_in_max)
+#define HWMON_I_LCRIT _BITUL(hwmon_in_lcrit)
+#define HWMON_I_CRIT _BITUL(hwmon_in_crit)
+#define HWMON_I_AVERAGE _BITUL(hwmon_in_average)
+#define HWMON_I_LOWEST _BITUL(hwmon_in_lowest)
+#define HWMON_I_HIGHEST _BITUL(hwmon_in_highest)
+#define HWMON_I_RESET_HISTORY _BITUL(hwmon_in_reset_history)
+#define HWMON_I_LABEL _BITUL(hwmon_in_label)
+#define HWMON_I_ALARM _BITUL(hwmon_in_alarm)
+#define HWMON_I_MIN_ALARM _BITUL(hwmon_in_min_alarm)
+#define HWMON_I_MAX_ALARM _BITUL(hwmon_in_max_alarm)
+#define HWMON_I_LCRIT_ALARM _BITUL(hwmon_in_lcrit_alarm)
+#define HWMON_I_CRIT_ALARM _BITUL(hwmon_in_crit_alarm)
+#define HWMON_I_RATED_MIN _BITUL(hwmon_in_rated_min)
+#define HWMON_I_RATED_MAX _BITUL(hwmon_in_rated_max)
+#define HWMON_I_BEEP _BITUL(hwmon_in_beep)
+#define HWMON_I_FAULT _BITUL(hwmon_in_fault)
+
+enum hwmon_curr_attributes {
+	hwmon_curr_enable = 0,
+	hwmon_curr_input = 1,
+	hwmon_curr_min = 2,
+	hwmon_curr_max = 3,
+	hwmon_curr_lcrit = 4,
+	hwmon_curr_crit = 5,
+	hwmon_curr_average = 6,
+	hwmon_curr_lowest = 7,
+	hwmon_curr_highest = 8,
+	hwmon_curr_reset_history = 9,
+	hwmon_curr_label = 10,
+	hwmon_curr_alarm = 11,
+	hwmon_curr_min_alarm = 12,
+	hwmon_curr_max_alarm = 13,
+	hwmon_curr_lcrit_alarm = 14,
+	hwmon_curr_crit_alarm = 15,
+	hwmon_curr_rated_min = 16,
+	hwmon_curr_rated_max = 17,
+	hwmon_curr_beep = 18,
+};
+
+#define HWMON_C_ENABLE _BITUL(hwmon_curr_enable)
+#define HWMON_C_INPUT _BITUL(hwmon_curr_input)
+#define HWMON_C_MIN _BITUL(hwmon_curr_min)
+#define HWMON_C_MAX _BITUL(hwmon_curr_max)
+#define HWMON_C_LCRIT _BITUL(hwmon_curr_lcrit)
+#define HWMON_C_CRIT _BITUL(hwmon_curr_crit)
+#define HWMON_C_AVERAGE _BITUL(hwmon_curr_average)
+#define HWMON_C_LOWEST _BITUL(hwmon_curr_lowest)
+#define HWMON_C_HIGHEST _BITUL(hwmon_curr_highest)
+#define HWMON_C_RESET_HISTORY _BITUL(hwmon_curr_reset_history)
+#define HWMON_C_LABEL _BITUL(hwmon_curr_label)
+#define HWMON_C_ALARM _BITUL(hwmon_curr_alarm)
+#define HWMON_C_MIN_ALARM _BITUL(hwmon_curr_min_alarm)
+#define HWMON_C_MAX_ALARM _BITUL(hwmon_curr_max_alarm)
+#define HWMON_C_LCRIT_ALARM _BITUL(hwmon_curr_lcrit_alarm)
+#define HWMON_C_CRIT_ALARM _BITUL(hwmon_curr_crit_alarm)
+#define HWMON_C_RATED_MIN _BITUL(hwmon_curr_rated_min)
+#define HWMON_C_RATED_MAX _BITUL(hwmon_curr_rated_max)
+#define HWMON_C_BEEP _BITUL(hwmon_curr_beep)
+
+enum hwmon_power_attributes {
+	hwmon_power_enable = 0,
+	hwmon_power_average = 1,
+	hwmon_power_average_interval = 2,
+	hwmon_power_average_interval_max = 3,
+	hwmon_power_average_interval_min = 4,
+	hwmon_power_average_highest = 5,
+	hwmon_power_average_lowest = 6,
+	hwmon_power_average_max = 7,
+	hwmon_power_average_min = 8,
+	hwmon_power_input = 9,
+	hwmon_power_input_highest = 10,
+	hwmon_power_input_lowest = 11,
+	hwmon_power_reset_history = 12,
+	hwmon_power_accuracy = 13,
+	hwmon_power_cap = 14,
+	hwmon_power_cap_hyst = 15,
+	hwmon_power_cap_max = 16,
+	hwmon_power_cap_min = 17,
+	hwmon_power_min = 18,
+	hwmon_power_max = 19,
+	hwmon_power_crit = 20,
+	hwmon_power_lcrit = 21,
+	hwmon_power_label = 22,
+	hwmon_power_alarm = 23,
+	hwmon_power_cap_alarm = 24,
+	hwmon_power_min_alarm = 25,
+	hwmon_power_max_alarm = 26,
+	hwmon_power_lcrit_alarm = 27,
+	hwmon_power_crit_alarm = 28,
+	hwmon_power_rated_min = 29,
+	hwmon_power_rated_max = 30,
+};
+
+#define HWMON_P_ENABLE _BITUL(hwmon_power_enable)
+#define HWMON_P_AVERAGE _BITUL(hwmon_power_average)
+#define HWMON_P_AVERAGE_INTERVAL _BITUL(hwmon_power_average_interval)
+#define HWMON_P_AVERAGE_INTERVAL_MAX _BITUL(hwmon_power_average_interval_max)
+#define HWMON_P_AVERAGE_INTERVAL_MIN _BITUL(hwmon_power_average_interval_min)
+#define HWMON_P_AVERAGE_HIGHEST _BITUL(hwmon_power_average_highest)
+#define HWMON_P_AVERAGE_LOWEST _BITUL(hwmon_power_average_lowest)
+#define HWMON_P_AVERAGE_MAX _BITUL(hwmon_power_average_max)
+#define HWMON_P_AVERAGE_MIN _BITUL(hwmon_power_average_min)
+#define HWMON_P_INPUT _BITUL(hwmon_power_input)
+#define HWMON_P_INPUT_HIGHEST _BITUL(hwmon_power_input_highest)
+#define HWMON_P_INPUT_LOWEST _BITUL(hwmon_power_input_lowest)
+#define HWMON_P_RESET_HISTORY _BITUL(hwmon_power_reset_history)
+#define HWMON_P_ACCURACY _BITUL(hwmon_power_accuracy)
+#define HWMON_P_CAP _BITUL(hwmon_power_cap)
+#define HWMON_P_CAP_HYST _BITUL(hwmon_power_cap_hyst)
+#define HWMON_P_CAP_MAX _BITUL(hwmon_power_cap_max)
+#define HWMON_P_CAP_MIN _BITUL(hwmon_power_cap_min)
+#define HWMON_P_MIN _BITUL(hwmon_power_min)
+#define HWMON_P_MAX _BITUL(hwmon_power_max)
+#define HWMON_P_LCRIT _BITUL(hwmon_power_lcrit)
+#define HWMON_P_CRIT _BITUL(hwmon_power_crit)
+#define HWMON_P_LABEL _BITUL(hwmon_power_label)
+#define HWMON_P_ALARM _BITUL(hwmon_power_alarm)
+#define HWMON_P_CAP_ALARM _BITUL(hwmon_power_cap_alarm)
+#define HWMON_P_MIN_ALARM _BITUL(hwmon_power_min_alarm)
+#define HWMON_P_MAX_ALARM _BITUL(hwmon_power_max_alarm)
+#define HWMON_P_LCRIT_ALARM _BITUL(hwmon_power_lcrit_alarm)
+#define HWMON_P_CRIT_ALARM _BITUL(hwmon_power_crit_alarm)
+#define HWMON_P_RATED_MIN _BITUL(hwmon_power_rated_min)
+#define HWMON_P_RATED_MAX _BITUL(hwmon_power_rated_max)
+
+enum hwmon_energy_attributes {
+	hwmon_energy_enable = 0,
+	hwmon_energy_input = 1,
+	hwmon_energy_label = 2,
+};
+
+#define HWMON_E_ENABLE _BITUL(hwmon_energy_enable)
+#define HWMON_E_INPUT _BITUL(hwmon_energy_input)
+#define HWMON_E_LABEL _BITUL(hwmon_energy_label)
+
+enum hwmon_humidity_attributes {
+	hwmon_humidity_enable = 0,
+	hwmon_humidity_input = 1,
+	hwmon_humidity_label = 2,
+	hwmon_humidity_min = 3,
+	hwmon_humidity_min_hyst = 4,
+	hwmon_humidity_max = 5,
+	hwmon_humidity_max_hyst = 6,
+	hwmon_humidity_alarm = 7,
+	hwmon_humidity_fault = 8,
+	hwmon_humidity_rated_min = 9,
+	hwmon_humidity_rated_max = 10,
+	hwmon_humidity_min_alarm = 11,
+	hwmon_humidity_max_alarm = 12,
+};
+
+#define HWMON_H_ENABLE _BITUL(hwmon_humidity_enable)
+#define HWMON_H_INPUT _BITUL(hwmon_humidity_input)
+#define HWMON_H_LABEL _BITUL(hwmon_humidity_label)
+#define HWMON_H_MIN _BITUL(hwmon_humidity_min)
+#define HWMON_H_MIN_HYST _BITUL(hwmon_humidity_min_hyst)
+#define HWMON_H_MAX _BITUL(hwmon_humidity_max)
+#define HWMON_H_MAX_HYST _BITUL(hwmon_humidity_max_hyst)
+#define HWMON_H_ALARM _BITUL(hwmon_humidity_alarm)
+#define HWMON_H_FAULT _BITUL(hwmon_humidity_fault)
+#define HWMON_H_RATED_MIN _BITUL(hwmon_humidity_rated_min)
+#define HWMON_H_RATED_MAX _BITUL(hwmon_humidity_rated_max)
+#define HWMON_H_MIN_ALARM _BITUL(hwmon_humidity_min_alarm)
+#define HWMON_H_MAX_ALARM _BITUL(hwmon_humidity_max_alarm)
+
+enum hwmon_fan_attributes {
+	hwmon_fan_enable = 0,
+	hwmon_fan_input = 1,
+	hwmon_fan_label = 2,
+	hwmon_fan_min = 3,
+	hwmon_fan_max = 4,
+	hwmon_fan_div = 5,
+	hwmon_fan_pulses = 6,
+	hwmon_fan_target = 7,
+	hwmon_fan_alarm = 8,
+	hwmon_fan_min_alarm = 9,
+	hwmon_fan_max_alarm = 10,
+	hwmon_fan_fault = 11,
+	hwmon_fan_beep = 12,
+};
+
+#define HWMON_F_ENABLE _BITUL(hwmon_fan_enable)
+#define HWMON_F_INPUT _BITUL(hwmon_fan_input)
+#define HWMON_F_LABEL _BITUL(hwmon_fan_label)
+#define HWMON_F_MIN _BITUL(hwmon_fan_min)
+#define HWMON_F_MAX _BITUL(hwmon_fan_max)
+#define HWMON_F_DIV _BITUL(hwmon_fan_div)
+#define HWMON_F_PULSES _BITUL(hwmon_fan_pulses)
+#define HWMON_F_TARGET _BITUL(hwmon_fan_target)
+#define HWMON_F_ALARM _BITUL(hwmon_fan_alarm)
+#define HWMON_F_MIN_ALARM _BITUL(hwmon_fan_min_alarm)
+#define HWMON_F_MAX_ALARM _BITUL(hwmon_fan_max_alarm)
+#define HWMON_F_FAULT _BITUL(hwmon_fan_fault)
+#define HWMON_F_BEEP _BITUL(hwmon_fan_beep)
+
+enum hwmon_pwm_attributes {
+	hwmon_pwm_input = 0,
+	hwmon_pwm_enable = 1,
+	hwmon_pwm_mode = 2,
+	hwmon_pwm_freq = 3,
+	hwmon_pwm_auto_channels_temp = 4,
+};
+
+#define HWMON_PWM_INPUT _BITUL(hwmon_pwm_input)
+#define HWMON_PWM_ENABLE _BITUL(hwmon_pwm_enable)
+#define HWMON_PWM_MODE _BITUL(hwmon_pwm_mode)
+#define HWMON_PWM_FREQ _BITUL(hwmon_pwm_freq)
+#define HWMON_PWM_AUTO_CHANNELS_TEMP _BITUL(hwmon_pwm_auto_channels_temp)
+
+enum hwmon_intrusion_attributes {
+	hwmon_intrusion_alarm = 0,
+	hwmon_intrusion_beep = 1,
+};
+
+#define HWMON_INTRUSION_ALARM _BITUL(hwmon_intrusion_alarm)
+#define HWMON_INTRUSION_BEEP _BITUL(hwmon_intrusion_beep)
+
+#endif /* _UAPI_LINUX_HWMON_H */
author
#4 lore raw

PATCH 2/5 hwmon: Expose attribute validation and string helpers

In reply to Jihong Min

Expose the core's string classification and sysfs template validation through the kernel hwmon API. Let drivers query these properties without maintaining separate attribute tables.

Reuse the helpers when creating attributes.

Assisted-by Codex:gpt-6-astra
---
 drivers/hwmon/hwmon.c | 30 +++++++++++++++++++++++++-----
 include/linux/hwmon.h |  3 +++
 2 files changed, 28 insertions(+), 5 deletions(-)
drivers/hwmon/hwmon.c +25 -5
index 10d2df3efdfa..bed16cfd4f1c 100644
@@ -502,7 +502,14 @@ static ssize_t hwmon_attr_store(struct device *dev,
 	return count;
 }
 
-static bool is_string_attr(enum hwmon_sensor_types type, u32 attr)
+/**
+ * hwmon_is_string_attr - test whether an attribute returns a string
+ * @type: sensor type
+ * @attr: attribute identifier
+ *
+ * Return: true for string attributes, false otherwise.
+ */
+bool hwmon_is_string_attr(enum hwmon_sensor_types type, u32 attr)
 {
 	return (type == hwmon_temp && attr == hwmon_temp_label) ||
 	       (type == hwmon_in && attr == hwmon_in_label) ||
@@ -513,6 +520,7 @@ static bool is_string_attr(enum hwmon_sensor_types type, u32 attr)
 	       (type == hwmon_humidity && attr == hwmon_humidity_label) ||
 	       (type == hwmon_fan && attr == hwmon_fan_label);
 }
+EXPORT_SYMBOL_GPL(hwmon_is_string_attr);
 
 static const struct attribute *hwmon_genattr(const void *drvdata,
 					     enum hwmon_sensor_types type,
@@ -526,7 +534,7 @@ static const struct attribute *hwmon_genattr(const void *drvdata,
 	struct attribute *a;
 	umode_t mode;
 	const char *name;
-	bool is_string = is_string_attr(type, attr);
+	bool is_string = hwmon_is_string_attr(type, attr);
 
 	mode = hwmon_is_visible(ops, drvdata, type, attr, index);
 	if (!mode)
@@ -776,6 +784,20 @@ static const int __templates_size[] = {
 	[hwmon_intrusion] = ARRAY_SIZE(hwmon_intrusion_attr_templates),
 };
 
+/**
+ * hwmon_is_valid_attr - test whether an attribute has a sysfs template
+ * @type: sensor type
+ * @attr: attribute identifier
+ *
+ * Return: true for supported sysfs attributes, false otherwise.
+ */
+bool hwmon_is_valid_attr(enum hwmon_sensor_types type, u32 attr)
+{
+	return (unsigned int)type < ARRAY_SIZE(__templates) &&
+	       attr < __templates_size[type] && __templates[type][attr];
+}
+EXPORT_SYMBOL_GPL(hwmon_is_valid_attr);
+
 int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
 		       u32 attr, int channel)
 {
@@ -843,14 +865,12 @@ static int hwmon_genattrs(const void *drvdata,
 			  const struct hwmon_channel_info *info)
 {
 	const char * const *templates;
-	int template_size;
 	int i, aindex = 0;
 
 	if (info->type >= ARRAY_SIZE(__templates))
 		return -EINVAL;
 
 	templates = __templates[info->type];
-	template_size = __templates_size[info->type];
 
 	for (i = 0; info->config[i]; i++) {
 		u32 attr_mask = info->config[i];
@@ -861,7 +881,7 @@ static int hwmon_genattrs(const void *drvdata,
 
 			attr = __ffs(attr_mask);
 			attr_mask &= ~BIT(attr);
-			if (attr >= template_size || !templates[attr])
+			if (!hwmon_is_valid_attr(info->type, attr))
 				continue;	/* attribute is invisible */
 			a = hwmon_genattr(drvdata, info->type, attr, i,
 					  templates[attr], ops);
include/linux/hwmon.h +3 -0
index d48e0aa82bc5..b026aa317630 100644
@@ -138,6 +138,9 @@ devm_hwmon_device_register_with_info(struct device *dev,
 
 void hwmon_device_unregister(struct device *dev);
 
+bool hwmon_is_valid_attr(enum hwmon_sensor_types type, u32 attr);
+bool hwmon_is_string_attr(enum hwmon_sensor_types type, u32 attr);
+
 int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
 		       u32 attr, int channel);
 
#5 lore raw

Re: [PATCH 2/5] hwmon: Expose attribute validation and string helpers

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 8 lines

Expose the core's string classification and sysfs template validation through the kernel hwmon API. Let drivers query these properties without maintaining separate attribute tables.

Reuse the helpers when creating attributes.

Assisted-by Codex:gpt-6-astra

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
author
#6 lore raw

PATCH 3/5 hwmon: Add a generic interface for userspace implementations

In reply to Jihong Min

Add /dev/uhwmon so userspace drivers can expose standard hwmon attributes. Use the shared hwmon identifiers directly and forward numeric values or labels through read and write requests.

Reuse hwmon sysfs formatting and notifications. Preserve devices across daemon disconnects and support reconnection or explicit removal.

Assisted-by Codex:gpt-6-astra
---
 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 MAINTAINERS                                   |   1 +
 drivers/hwmon/Kconfig                         |   9 +
 drivers/hwmon/Makefile                        |   1 +
 drivers/hwmon/uhwmon.c                        | 641 ++++++++++++++++++
 include/uapi/linux/uhwmon.h                   |  64 ++
 6 files changed, 717 insertions(+)
 create mode 100644 drivers/hwmon/uhwmon.c
 create mode 100644 include/uapi/linux/uhwmon.h
Documentation/userspace-api/ioctl/ioctl-number.rst +1 -0
index 2fc53093752d..76126b74ebb7 100644
@@ -384,6 +384,7 @@ Code  Seq#    Include File                                             Comments
 0xB8  01-02  uapi/misc/mrvl_cn10k_dpi.h                                Marvell CN10K DPI driver
 0xB8  all    uapi/linux/mshv.h                                         Microsoft Hyper-V /dev/mshv driver
                                                                        <mailto:[email protected]>
+0xB9  00-02  uapi/linux/uhwmon.h                                       Userspace hardware monitoring
 0xBA  00-0F  uapi/linux/liveupdate.h                                   Pasha Tatashin
                                                                        <mailto:[email protected]>
 0xC0  00-0F  linux/usb/iowarrior.h
MAINTAINERS +1 -0
index f3f0292832bb..fb9d9233a8d6 100644
@@ -11493,6 +11493,7 @@ F:	drivers/hwmon/
 F:	include/linux/hwmon*.h
 F:	include/trace/events/hwmon*.h
 F:	include/uapi/linux/hwmon.h
+F:	include/uapi/linux/uhwmon.h
 K:	(devm_)?hwmon_device_(un)?register(|_with_groups|_with_info)
 
 HARDWARE RANDOM NUMBER GENERATOR CORE
drivers/hwmon/Kconfig +9 -0
index fecff8610ea8..dbff7bff035f 100644
@@ -2537,6 +2537,15 @@ config SENSORS_TSC1641
 	  This driver can also be built as a module. If so, the module
 	  will be called tsc1641.
 
+config SENSORS_UHWMON
+	tristate "Userspace hardware monitoring devices"
+	help
+	  Provide a standard interface for userspace hwmon drivers through
+	  /dev/uhwmon.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called uhwmon.
+
 config SENSORS_VEXPRESS
 	tristate "Versatile Express"
 	depends on VEXPRESS_CONFIG
drivers/hwmon/Makefile +1 -0
index 1229b6b3996d..27baa01d8d0f 100644
@@ -242,6 +242,7 @@ obj-$(CONFIG_SENSORS_TMP421)	+= tmp421.o
 obj-$(CONFIG_SENSORS_TMP464)	+= tmp464.o
 obj-$(CONFIG_SENSORS_TMP513)	+= tmp513.o
 obj-$(CONFIG_SENSORS_TSC1641)	+= tsc1641.o
+obj-$(CONFIG_SENSORS_UHWMON)	+= uhwmon.o
 obj-$(CONFIG_SENSORS_VEXPRESS)	+= vexpress-hwmon.o
 obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o
 obj-$(CONFIG_SENSORS_VIA686A)	+= via686a.o
drivers/hwmon/uhwmon.c new file +641 -0

641 lines not on this page · raw message

include/uapi/linux/uhwmon.h new file +64 -0

64 lines not on this page · raw message

#7 lore raw

Re: [PATCH 3/5] hwmon: Add a generic interface for userspace implementations

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] hwmon: O(N^2) attribute validation loop causes CPU soft lockup - [High] sysfs: 1-byte buffer…

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] hwmon: O(N^2) attribute validation loop causes CPU soft lockup
- [High] sysfs: 1-byte buffer overflow in hwmon_attr_show_string due to PAGE_SIZE limit
- [Medium] driver-core: invalid teardown order in uhwmon_exit() destroys parent before children --

commit c2824f78f1fac4c2c5b17a97b2a2b5679a625be3
Author: Jihong Min <[email protected]>

hwmon: Add a generic interface for userspace implementations

Add /dev/uhwmon so userspace drivers can expose standard hwmon attributes. Use the shared hwmon identifiers directly and forward numeric values or labels through read and write requests.

Quoted 27 lines
drivers/hwmon/uhwmon.c new file +19 -0
new file mode 100644
index 0000000000000..1855c87f06d9d
@@ -0,0 +1,641 @@
[ ... ]
+static int uhwmon_create(struct file *file, void __user *arg)
+{
[ ... ]
+	for (i = 0; i < data->num_attrs; i++) {
+		struct uhwmon_attr *attr = &data->attrs[i];
+		u32 type;
+		bool string;
+
+		ret = -EINVAL;
+		if (spec[i].attr >= 32 ||
+		    !hwmon_is_valid_attr(spec[i].type, spec[i].attr) ||
+		    !spec[i].mode || spec[i].mode & ~0644 ||
+		    spec[i].flags & ~UHWMON_ATTR_CONSTANT)
+			goto out;
+		if (spec[i].type == hwmon_chip && spec[i].channel)
+			goto out;
+		/*
+		 * N is a zero-based channel index: channels 0..N need at least
+		 * N + 1 attribute slots, plus a NULL terminator. The core uses
+		 * int counts, so N + 2 <= INT_MAX. The full count is checked below.

drivers/hwmon/uhwmon.c line 314

Quoted 10 lines
drivers/hwmon/uhwmon.c new file +10 -0
+		 */
+		if (spec[i].channel > INT_MAX - 2) {
+			ret = -EOVERFLOW;
+			goto out;
+		}
+		string = hwmon_is_string_attr(spec[i].type, spec[i].attr);
+		if (string) {
+			if (!spec[i].size || spec[i].size > PAGE_SIZE ||
+			    spec[i].mode & 0222 || spec[i].value)
+				goto out;

[Severity: High]
Does this PAGE_SIZE check leave enough room for the newline character appended by the hwmon core?

If a daemon writes a string of length PAGE_SIZE - 1 plus a null terminator, when read via sysfs, the hwmon core passes this string to sprintf(buf, "%s\n", s) where buf is exactly PAGE_SIZE bytes.

This would write PAGE_SIZE - 1 characters, followed by the newline and null terminator, totaling PAGE_SIZE + 1 bytes.

Could this cause a 1-byte overflow and memory corruption?

Quoted 6 lines
drivers/hwmon/uhwmon.c new file +5 -0
+		} else if (spec[i].size || spec[i].text) {
+			goto out;
+		}
[ ... ]
+		type = spec[i].type == hwmon_energy64 ? hwmon_energy :
+							spec[i].type;

drivers/hwmon/uhwmon.c line 353

Quoted 10 lines
drivers/hwmon/uhwmon.c new file +10 -0
+		for (j = 0; j < i; j++) {
+			u32 other_type = spec[j].type == hwmon_energy64 ?
+						 hwmon_energy :
+						 spec[j].type;
+
+			if (type == other_type &&
+			    spec[i].attr == spec[j].attr &&
+			    spec[i].channel == spec[j].channel)
+				goto out;
+		}

[Severity: High]
Can this nested loop cause a CPU soft lockup?

Since uhwmon_create() accepts a user-provided num_attrs bounded only by kmalloc limits (which could exceed 100,000 attributes depending on max slab size), this loop compares every attribute against all preceding attributes to prevent duplicates.

For a large number of attributes, this results in billions of iterations.

As the loop lacks preemption points like cond_resched(), could it hold the CPU exclusively for multiple seconds and trigger the soft lockup watchdog?

Quoted 7 lines
drivers/hwmon/uhwmon.c new file +6 -0
+		attr->spec = spec[i];
+		attr->spec.text = 0;
+		counts[spec[i].type] =
+			max(counts[spec[i].type], spec[i].channel + 1);
+	}
[ ... ]
+static void __exit uhwmon_exit(void)

drivers/hwmon/uhwmon.c line 450

Quoted 10 lines
drivers/hwmon/uhwmon.c new file +10 -0
+{
+	struct uhwmon_device *data, *next;
+
+	misc_deregister(&uhwmon_misc);
+	list_for_each_entry_safe(data, next, &uhwmon_devices, node) {
+		uhwmon_unregister(data);
+		list_del(&data->node);
+		uhwmon_free(data);
+	}
+}

[Severity: Medium]
Is it safe to deregister the parent miscdevice before unregistering the child hwmon devices?

Calling misc_deregister() destroys uhwmon_misc.this_device, which is the parent of all registered hwmon devices. Destroying a parent device before its children violates the driver core's topological ordering and lifecycle rules.

Could this cause sysfs warnings and potential use-after-free when the children subsequently attempt to unregister their sysfs directories from the already-deleted parent?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
#8 lore raw

Re: [PATCH 3/5] hwmon: Add a generic interface for userspace implementations

In reply to Jihong Min

…

Quoted 2 lines

+++ b/drivers/hwmon/uhwmon.c

(quoted diff) +0 -0
@@ -0,0 +1,641 @@

…

Quoted 4 lines
drivers/hwmon/uhwmon.c new file +4 -0
+static void uhwmon_disconnect(struct uhwmon_device *data)
+{
+	mutex_lock(&data->lock);
+	data->online = false;

…

drivers/hwmon/uhwmon.c line 102

Quoted 3 lines
drivers/hwmon/uhwmon.c new file +3 -0
+	mutex_unlock(&data->lock);
+	wake_up_all(&data->wait);
+}

…

Under which circumstances would you become interested to apply a statement like “guard(mutex)(&data->lock);”?
https://elixir.bootlin.com/linux/v7.3-rc6/source/include/linux/mutex.h#L253

Regards,
Markus

author
#9 lore raw

PATCH 4/5 hwmon: Reject truncated attribute names

In reply to Jihong Min

Large channel numbers can produce truncated or conflicting attribute names. Return -EOVERFLOW if a generated name does not fit its buffer.

Assisted-by Codex:gpt-6-astra
---
 drivers/hwmon/hwmon.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/hwmon/hwmon.c +6 -2
index bed16cfd4f1c..288f356980e4 100644
@@ -553,8 +553,12 @@ static const struct attribute *hwmon_genattr(const void *drvdata,
 	if (type == hwmon_chip) {
 		name = template;
 	} else {
-		scnprintf(hattr->name, sizeof(hattr->name), template,
-			  index + hwmon_attr_base(type));
+		if (snprintf(hattr->name, sizeof(hattr->name), template,
+			     index + hwmon_attr_base(type)) >=
+		    sizeof(hattr->name)) {
+			kfree(hattr);
+			return ERR_PTR(-EOVERFLOW);
+		}
 		name = hattr->name;
 	}
 
#10 lore raw

Re: [PATCH 4/5] hwmon: Reject truncated attribute names

Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 5 lines

Large channel numbers can produce truncated or conflicting attribute names. Return -EOVERFLOW if a generated name does not fit its buffer.

Assisted-by Codex:gpt-6-astra

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
author
#11 lore raw

PATCH 5/5 docs: hwmon: Document the uhwmon userspace interface

In reply to Jihong Min

Document the uhwmon UAPI for developers implementing userspace hwmon drivers. Cover device registration, request handling, notifications and device lifetime.

Assisted-by Codex:gpt-6-astra
---
 Documentation/hwmon/index.rst  |   1 +
 Documentation/hwmon/uhwmon.rst | 141 +++++++++++++++++++++++++++++++++
 2 files changed, 142 insertions(+)
 create mode 100644 Documentation/hwmon/uhwmon.rst
Documentation/hwmon/index.rst +1 -0
index 9955a525436a..88c6388e7c5c 100644
@@ -12,6 +12,7 @@ Hardware Monitoring
    submitting-patches
    sysfs-interface
    userspace-tools
+   uhwmon
 
 Hardware Monitoring Kernel Drivers
 ==================================
Documentation/hwmon/uhwmon.rst new file +141 -0

141 lines not on this page · raw message

#12 lore raw

Re: [PATCH 5/5] docs: hwmon: Document the uhwmon userspace interface

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: - [High] hwmon: uhwmon: userfaultfd-based unkillable hangs via copy_to_user under mutex - [High] hwmon…

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] hwmon: uhwmon: userfaultfd-based unkillable hangs via copy_to_user under mutex
- [High] hwmon: uhwmon: name collision logic blocks multiple devices and schema updates
- [High] hwmon: wait_event_killable() in uhwmon_request() allows daemon to hang sysfs
- [Medium] hwmon: uhwmon: uhwmon_exit() destroys parent miscdevice before child hwmon devices --

commit b2f6043289b80f507b7c7bd6ff84bc7129c9ba48
Author: Jihong Min <[email protected]>

docs: hwmon: Document the uhwmon userspace interface

This commit introduces the uhwmon userspace API documentation, which connects userspace drivers to hwmon_ops through the /dev/uhwmon control device. It details how developers can register devices, handle requests, notify changes, and manage device lifetimes.

Quoted 6 lines
drivers/hwmon/uhwmon.c new file +0 -0
new file mode 100644
index 0000000000000..1855c87f06d9d
@@ -0,0 +1,641 @@

[ ... ]

Quoted 3 lines
drivers/hwmon/uhwmon.c new file +3 -0
+static int uhwmon_request(struct uhwmon_device *data, unsigned int index,
+			  u32 op, s64 *value)
+{

[ ... ]

drivers/hwmon/uhwmon.c

Quoted 8 lines
drivers/hwmon/uhwmon.c new file +8 -0
+	ret = wait_event_interruptible(data->wait, READ_ONCE(pending.done));
+	mutex_lock(&data->lock);
+	if (!pending.done && !data->readable) {
+		/* Once delivered, only fatal signals may abort the wait. */
+		mutex_unlock(&data->lock);
+		ret = wait_event_killable(data->wait, READ_ONCE(pending.done));
+		mutex_lock(&data->lock);
+	}

[Severity: High]
Does wait_event_killable() here allow the daemon to hang sysfs?

Because TASK_KILLABLE only responds to fatal signals, monitoring utilities (like sensors or prometheus-node-exporter) that catch SIGTERM or SIGINT will become unkillable and fail to shut down cleanly, delaying system reboots by up to 90 seconds.

Any user with sysfs access can read the sensor, and any bug in the daemon can trigger the hang. Although the daemon requires root to open /dev/uhwmon, a buggy daemon shouldn't cause unkillable kernel deadlocks.

[ ... ]

Quoted 3 lines
drivers/hwmon/uhwmon.c new file +3 -0
+static int uhwmon_create(struct file *file, void __user *arg)
+{
+	struct uhwmon_device *data, *existing;

[ ... ]

drivers/hwmon/uhwmon.c

Quoted 6 lines
drivers/hwmon/uhwmon.c new file +6 -0
+	data = kzalloc_obj(*data);
+	if (!data)
+		return -ENOMEM;
+	mutex_init(&data->lock);
+	init_waitqueue_head(&data->wait);
+	data->name = strndup_user(u64_to_user_ptr(create.name), NAME_MAX + 1);

[Severity: High]
Could this strndup_user() combined with memdup_array_user() below cause an unkillable hang?

uhwmon_create() is called from uhwmon_ioctl() which holds the global uhwmon_control_lock. If a daemon issues an ioctl(UHWMON_CREATE) providing a buffer backed by userfaultfd, the kernel will block indefinitely while holding uhwmon_control_lock.

This freezes all device creation, notification, and release, causing a local denial of service.

[ ... ]

drivers/hwmon/uhwmon.c

Quoted 10 lines
drivers/hwmon/uhwmon.c new file +10 -0
+	list_for_each_entry(existing, &uhwmon_devices, node) {
+		if (strcmp(existing->name, data->name))
+			continue;
+		ret = -EBUSY;
+		if (existing->online)
+			goto out;
+		ret = -EINVAL;
+		if (!uhwmon_same_schema(existing, data))
+			goto out;
+		mutex_lock(&existing->lock);

[Severity: High]
Is it possible that this rigid name-matching logic incorrectly rejects valid registrations?

It appears to block multiple independent devices from sharing the same name by returning -EBUSY, breaking standard hwmon functionality.

Additionally, if a daemon crashes (leaving the device offline) and restarts with a modified schema, UHWMON_CREATE returns -EINVAL without attaching the file descriptor.

This permanently locks out the device name because the daemon cannot issue UHWMON_DESTROY to clean up the old offline device.

[ ... ]

Quoted 3 lines
drivers/hwmon/uhwmon.c new file +3 -0
+static ssize_t uhwmon_read(struct file *file, char __user *buf, size_t count,
+			   loff_t *offset)
+{

[ ... ]

Quoted 2 lines
drivers/hwmon/uhwmon.c new file +2 -0
+		if (mutex_lock_interruptible(&data->lock))
+			return -ERESTARTSYS;

[ ... ]

drivers/hwmon/uhwmon.c

Quoted 8 lines
drivers/hwmon/uhwmon.c new file +8 -0
+	ret = -EFAULT;
+	if (copy_to_user(buf, &data->pending->request,
+			 sizeof(data->pending->request)))
+		goto out;
+	data->readable = false;
+	ret = sizeof(data->pending->request);
+out:
+	mutex_unlock(&data->lock);

[Severity: High]
Can this copy_to_user() while holding data->lock allow userfaultfd to block indefinitely?

If a daemon issues a read() providing a buffer backed by userfaultfd, the kernel will block indefinitely while data->lock is held.

Since hwmon core serializes sysfs accesses, any standard monitoring utility (like sensors) reading the sysfs attribute will invoke uhwmon_request(), which calls mutex_lock(&data->lock) and blocks in TASK_UNINTERRUPTIBLE (D-state).

Standard kernel design strictly forbids holding standard mutexes across userspace memory accesses if those mutexes are shared with system-wide reader processes.

[ ... ]

Quoted 3 lines
drivers/hwmon/uhwmon.c new file +3 -0
+static ssize_t uhwmon_write(struct file *file, const char __user *buf,
+			    size_t count, loff_t *offset)
+{

[ ... ]

Quoted 1 line
drivers/hwmon/uhwmon.c new file +1 -0
+	mutex_lock(&data->lock);

[ ... ]

drivers/hwmon/uhwmon.c

Quoted 7 lines
drivers/hwmon/uhwmon.c new file +7 -0
+		if (size) {
+			if (count < sizeof(reply) + size)
+				goto out;
+			ret = -EFAULT;
+			if (copy_from_user(data->text, buf + sizeof(reply),
+					   size))
+				goto out;

[Severity: High]
Similar to the issue in uhwmon_read(), could this copy_from_user() while holding data->lock also allow a userfaultfd-based unkillable hang?

drivers/hwmon/uhwmon.c

Quoted 4 lines
drivers/hwmon/uhwmon.c new file +4 -0
+			length = strnlen(data->text, size);
+			ret = -EINVAL;
+			if (length == size)
+				goto out;

[Severity: High]
What happens if an invalid string reply is provided here, does it permanently strand the sysfs reader?

If the daemon sends a non-null-terminated string, length == size evaluates to true and the function branches to goto out without setting pending->done = true.

This permanently strands the sysfs reader in the killable wait from uhwmon_request().

Since hwmon core serializes sysfs accesses, this single hung reader deadlocks the entire hwmon device.

[ ... ]

drivers/hwmon/uhwmon.c

Quoted 11 lines
drivers/hwmon/uhwmon.c new file +11 -0
+static void __exit uhwmon_exit(void)
+{
+	struct uhwmon_device *data, *next;
+
+	misc_deregister(&uhwmon_misc);
+	list_for_each_entry_safe(data, next, &uhwmon_devices, node) {
+		uhwmon_unregister(data);
+		list_del(&data->node);
+		uhwmon_free(data);
+	}
+}

[Severity: Medium]
Is it safe for uhwmon_exit() to destroy the parent miscdevice before the child hwmon devices?

uhwmon_create() registers child hwmon devices using uhwmon_misc.this_device as their parent. These child devices persist even after the userspace daemon closes the control file descriptor.

During module unload, calling misc_deregister(&uhwmon_misc) before iterating the uhwmon_devices list to unregister the child hwmon devices leaves dangling child kobjects.

The driver core strictly requires children to be unregistered before their parent. Unregistering the parent first triggers sysfs warnings and use-after-free bugs when the children are subsequently unregistered.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
maintainer
#13 lore raw

Re: [PATCH 5/5] docs: hwmon: Document the uhwmon userspace interface

In reply to Jihong Min
Quoted 6 lines · Jihong Min

Document the uhwmon UAPI for developers implementing userspace hwmon drivers. Cover device registration, request handling, notifications and device lifetime.

Assisted-by Codex:gpt-6-astra

I am not going to review, much less apply, this series. Exposing subsystem internals to userspace is a bad idea and would limit future improvements of the hardware monitoring subsystem. User space file systems are bad enough and cause all kinds of trouble. I don't want to have to deal with the same kind of issues in the hardware monitoring subsystem.

Interested parties should implement kernel hardware monitoring drivers instead.

Guenter

author
#14 lore raw

Re: [PATCH 5/5] docs: hwmon: Document the uhwmon userspace interface

Ok. Then I will withdraw these patch series.

But then let me ask you the initial question that brought this patch
series: Do you think it is at least OK to add kernel drivers for network-based sensors? Or do you think those are also to be subjected to be rejected (or remained in DKMS projects)?

Sincerely,
Jihong Min

Quoted 18 lines · Guenter Roeck
Quoted 6 lines · Jihong Min

Document the uhwmon UAPI for developers implementing userspace hwmon drivers. Cover device registration, request handling, notifications and device lifetime.

Assisted-by Codex:gpt-6-astra

I am not going to review, much less apply, this series. Exposing subsystem internals to userspace is a bad idea and would limit future improvements of the hardware monitoring subsystem. User space file systems are bad enough and cause all kinds of trouble. I don't want to have to deal with the same kind of issues in the hardware monitoring subsystem.

Interested parties should implement kernel hardware monitoring drivers instead.

Guenter

A message that is not in the archive (sent off-list or not yet received): CAAwencqEVCjtnJfmnut2zXXPvuwju5T89FDSpHN3wPfrrJnRdQ@mail.gmail.com

maintainer
#16 lore raw

Re: [PATCH 5/5] docs: hwmon: Document the uhwmon userspace interface

Quoted 4 lines · Jihong Min

Ok. Then I will withdraw these patch series.

But then let me ask you the initial question that brought this patch series: Do you think it is at least OK to add kernel drivers for network-based sensors? Or do you think those are also to be subjected to be rejected (or remained in DKMS projects)?

What is a "network-based sensor" ?

Regarding the references, wireview has a USB interface, and I am quite sure that it should be possible to write a kernel driver for it. There are several hwmon kernel drivers implementing access through USB, so the same should be possible here.

The Tapo API reference points to an "Unofficial Tapo API client", written in AI-guided Rust. Google tells me:

"
There is no official public API documentation released by TP-Link for Tapo smart devices. Because Tapo uses a cloud-centric and encrypted local communication protocol, developers rely entirely on reverse-engineered, community-driven API clients to interact with these devices locally or via the cloud.
"

I have no idea if it is possible to implement a kernel driver for this. There are various hwmon drivers with reverse engineered APIs (such as the Corsair power supply drivers), so relying on reverse engineering is not a problem. I am, however, not going to dig through the above mentioned clients to figure out what the API actually is and how to use it. The guidance provided by Google suggests that it requires per-client credentials (account email and password), which would be problematic.

Either case, it appears that this would be about monitoring remote devices. That is really out of scope for the hardware monitoring subsystem. The hardware monitoring subsystem is monitoring _this_ system. Adding support for monitoring remote devices would be a stretch.

Guenter

Quoted 25 lines

Sincerely,
Jihong Min

On Mon, Oct 19, 2026 at 01:06 Guenter Roeck <[email protected] <mailto:[email protected]>> wrote:

On Sat, Oct 10, 2026 at 12:38:44AM +0900, Jihong Min wrote:
> Document the uhwmon UAPI for developers implementing userspace hwmon > drivers. Cover device registration, request handling, notifications > and device lifetime.
>
> Assisted-by: Codex:gpt-6-astra
> Signed-off-by: Jihong Min <[email protected] <mailto:[email protected]>>

I am not going to review, much less apply, this series. Exposing subsystem internals to userspace is a bad idea and would limit future improvements of the hardware monitoring subsystem. User space file systems are bad enough and cause all kinds of trouble. I don't want to have to deal with the same kind of issues in the hardware monitoring subsystem.

Interested parties should implement kernel hardware monitoring drivers instead.

Guenter