1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * hwmon driver for HP (and some HP Compaq) business-class computers that
4 * report numeric sensor data via Windows Management Instrumentation (WMI).
6 * Copyright (C) 2023 James Seo <james@equiv.tech>
9 * [1] Hewlett-Packard Development Company, L.P.,
10 * "HP Client Management Interface Technical White Paper", 2005. [Online].
11 * Available: https://h20331.www2.hp.com/hpsub/downloads/cmi_whitepaper.pdf
12 * [2] Hewlett-Packard Development Company, L.P.,
13 * "HP Retail Manageability", 2012. [Online].
14 * Available: http://h10032.www1.hp.com/ctg/Manual/c03291135.pdf
15 * [3] Linux Hardware Project, A. Ponomarenko et al.,
16 * "linuxhw/ACPI - Collect ACPI table dumps", 2018. [Online].
17 * Available: https://github.com/linuxhw/ACPI
18 * [4] P. Rohár, "bmfdec - Decompile binary MOF file (BMF) from WMI buffer",
19 * 2017. [Online]. Available: https://github.com/pali/bmfdec
20 * [5] Microsoft Corporation, "Driver-Defined WMI Data Items", 2017. [Online].
21 * Available: https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/driver-defined-wmi-data-items
24 #include <linux/acpi.h>
25 #include <linux/debugfs.h>
26 #include <linux/hwmon.h>
27 #include <linux/jiffies.h>
28 #include <linux/mutex.h>
29 #include <linux/nls.h>
30 #include <linux/units.h>
31 #include <linux/wmi.h>
33 #define HP_WMI_EVENT_NAMESPACE "root\\WMI"
34 #define HP_WMI_EVENT_CLASS "HPBIOS_BIOSEvent"
35 #define HP_WMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
36 #define HP_WMI_NUMERIC_SENSOR_GUID "8F1F6435-9F42-42C8-BADC-0E9424F20C9A"
37 #define HP_WMI_PLATFORM_EVENTS_GUID "41227C2D-80E1-423F-8B8E-87E32755A0EB"
39 /* Patterns for recognizing sensors and matching events to channels. */
41 #define HP_WMI_PATTERN_SYS_TEMP "Chassis Thermal Index"
42 #define HP_WMI_PATTERN_SYS_TEMP2 "System Ambient Temperature"
43 #define HP_WMI_PATTERN_CPU_TEMP "CPU Thermal Index"
44 #define HP_WMI_PATTERN_CPU_TEMP2 "CPU Temperature"
45 #define HP_WMI_PATTERN_TEMP_SENSOR "Thermal Index"
46 #define HP_WMI_PATTERN_TEMP_ALARM "Thermal Critical"
47 #define HP_WMI_PATTERN_INTRUSION_ALARM "Hood Intrusion"
48 #define HP_WMI_PATTERN_FAN_ALARM "Stall"
49 #define HP_WMI_PATTERN_TEMP "Temperature"
50 #define HP_WMI_PATTERN_CPU "CPU"
52 /* These limits are arbitrary. The WMI implementation may vary by system. */
54 #define HP_WMI_MAX_STR_SIZE 128U
55 #define HP_WMI_MAX_PROPERTIES 32U
56 #define HP_WMI_MAX_INSTANCES 32U
59 HP_WMI_TYPE_OTHER
= 1,
60 HP_WMI_TYPE_TEMPERATURE
= 2,
61 HP_WMI_TYPE_VOLTAGE
= 3,
62 HP_WMI_TYPE_CURRENT
= 4,
63 HP_WMI_TYPE_AIR_FLOW
= 12,
64 HP_WMI_TYPE_INTRUSION
= 0xabadb01, /* Custom. */
67 enum hp_wmi_category
{
68 HP_WMI_CATEGORY_SENSOR
= 3,
71 enum hp_wmi_severity
{
72 HP_WMI_SEVERITY_UNKNOWN
= 0,
73 HP_WMI_SEVERITY_OK
= 5,
74 HP_WMI_SEVERITY_DEGRADED_WARNING
= 10,
75 HP_WMI_SEVERITY_MINOR_FAILURE
= 15,
76 HP_WMI_SEVERITY_MAJOR_FAILURE
= 20,
77 HP_WMI_SEVERITY_CRITICAL_FAILURE
= 25,
78 HP_WMI_SEVERITY_NON_RECOVERABLE_ERROR
= 30,
83 HP_WMI_STATUS_DEGRADED
= 3,
84 HP_WMI_STATUS_STRESSED
= 4,
85 HP_WMI_STATUS_PREDICTIVE_FAILURE
= 5,
86 HP_WMI_STATUS_ERROR
= 6,
87 HP_WMI_STATUS_NON_RECOVERABLE_ERROR
= 7,
88 HP_WMI_STATUS_NO_CONTACT
= 12,
89 HP_WMI_STATUS_LOST_COMMUNICATION
= 13,
90 HP_WMI_STATUS_ABORTED
= 14,
91 HP_WMI_STATUS_SUPPORTING_ENTITY_IN_ERROR
= 16,
93 /* Occurs combined with one of "OK", "Degraded", and "Error" [1]. */
94 HP_WMI_STATUS_COMPLETED
= 17,
98 HP_WMI_UNITS_OTHER
= 1,
99 HP_WMI_UNITS_DEGREES_C
= 2,
100 HP_WMI_UNITS_DEGREES_F
= 3,
101 HP_WMI_UNITS_DEGREES_K
= 4,
102 HP_WMI_UNITS_VOLTS
= 5,
103 HP_WMI_UNITS_AMPS
= 6,
104 HP_WMI_UNITS_RPM
= 19,
107 enum hp_wmi_property
{
108 HP_WMI_PROPERTY_NAME
= 0,
109 HP_WMI_PROPERTY_DESCRIPTION
= 1,
110 HP_WMI_PROPERTY_SENSOR_TYPE
= 2,
111 HP_WMI_PROPERTY_OTHER_SENSOR_TYPE
= 3,
112 HP_WMI_PROPERTY_OPERATIONAL_STATUS
= 4,
113 HP_WMI_PROPERTY_SIZE
= 5,
114 HP_WMI_PROPERTY_POSSIBLE_STATES
= 6,
115 HP_WMI_PROPERTY_CURRENT_STATE
= 7,
116 HP_WMI_PROPERTY_BASE_UNITS
= 8,
117 HP_WMI_PROPERTY_UNIT_MODIFIER
= 9,
118 HP_WMI_PROPERTY_CURRENT_READING
= 10,
119 HP_WMI_PROPERTY_RATE_UNITS
= 11,
122 static const acpi_object_type hp_wmi_property_map
[] = {
123 [HP_WMI_PROPERTY_NAME
] = ACPI_TYPE_STRING
,
124 [HP_WMI_PROPERTY_DESCRIPTION
] = ACPI_TYPE_STRING
,
125 [HP_WMI_PROPERTY_SENSOR_TYPE
] = ACPI_TYPE_INTEGER
,
126 [HP_WMI_PROPERTY_OTHER_SENSOR_TYPE
] = ACPI_TYPE_STRING
,
127 [HP_WMI_PROPERTY_OPERATIONAL_STATUS
] = ACPI_TYPE_INTEGER
,
128 [HP_WMI_PROPERTY_SIZE
] = ACPI_TYPE_INTEGER
,
129 [HP_WMI_PROPERTY_POSSIBLE_STATES
] = ACPI_TYPE_STRING
,
130 [HP_WMI_PROPERTY_CURRENT_STATE
] = ACPI_TYPE_STRING
,
131 [HP_WMI_PROPERTY_BASE_UNITS
] = ACPI_TYPE_INTEGER
,
132 [HP_WMI_PROPERTY_UNIT_MODIFIER
] = ACPI_TYPE_INTEGER
,
133 [HP_WMI_PROPERTY_CURRENT_READING
] = ACPI_TYPE_INTEGER
,
134 [HP_WMI_PROPERTY_RATE_UNITS
] = ACPI_TYPE_INTEGER
,
137 enum hp_wmi_platform_events_property
{
138 HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME
= 0,
139 HP_WMI_PLATFORM_EVENTS_PROPERTY_DESCRIPTION
= 1,
140 HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_NAMESPACE
= 2,
141 HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_CLASS
= 3,
142 HP_WMI_PLATFORM_EVENTS_PROPERTY_CATEGORY
= 4,
143 HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_SEVERITY
= 5,
144 HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS
= 6,
147 static const acpi_object_type hp_wmi_platform_events_property_map
[] = {
148 [HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME
] = ACPI_TYPE_STRING
,
149 [HP_WMI_PLATFORM_EVENTS_PROPERTY_DESCRIPTION
] = ACPI_TYPE_STRING
,
150 [HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_NAMESPACE
] = ACPI_TYPE_STRING
,
151 [HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_CLASS
] = ACPI_TYPE_STRING
,
152 [HP_WMI_PLATFORM_EVENTS_PROPERTY_CATEGORY
] = ACPI_TYPE_INTEGER
,
153 [HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_SEVERITY
] = ACPI_TYPE_INTEGER
,
154 [HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS
] = ACPI_TYPE_INTEGER
,
157 enum hp_wmi_event_property
{
158 HP_WMI_EVENT_PROPERTY_NAME
= 0,
159 HP_WMI_EVENT_PROPERTY_DESCRIPTION
= 1,
160 HP_WMI_EVENT_PROPERTY_CATEGORY
= 2,
161 HP_WMI_EVENT_PROPERTY_SEVERITY
= 3,
162 HP_WMI_EVENT_PROPERTY_STATUS
= 4,
165 static const acpi_object_type hp_wmi_event_property_map
[] = {
166 [HP_WMI_EVENT_PROPERTY_NAME
] = ACPI_TYPE_STRING
,
167 [HP_WMI_EVENT_PROPERTY_DESCRIPTION
] = ACPI_TYPE_STRING
,
168 [HP_WMI_EVENT_PROPERTY_CATEGORY
] = ACPI_TYPE_INTEGER
,
169 [HP_WMI_EVENT_PROPERTY_SEVERITY
] = ACPI_TYPE_INTEGER
,
170 [HP_WMI_EVENT_PROPERTY_STATUS
] = ACPI_TYPE_INTEGER
,
173 static const enum hwmon_sensor_types hp_wmi_hwmon_type_map
[] = {
174 [HP_WMI_TYPE_TEMPERATURE
] = hwmon_temp
,
175 [HP_WMI_TYPE_VOLTAGE
] = hwmon_in
,
176 [HP_WMI_TYPE_CURRENT
] = hwmon_curr
,
177 [HP_WMI_TYPE_AIR_FLOW
] = hwmon_fan
,
180 static const u32 hp_wmi_hwmon_attributes
[hwmon_max
] = {
181 [hwmon_chip
] = HWMON_C_REGISTER_TZ
,
182 [hwmon_temp
] = HWMON_T_INPUT
| HWMON_T_LABEL
| HWMON_T_FAULT
,
183 [hwmon_in
] = HWMON_I_INPUT
| HWMON_I_LABEL
,
184 [hwmon_curr
] = HWMON_C_INPUT
| HWMON_C_LABEL
,
185 [hwmon_fan
] = HWMON_F_INPUT
| HWMON_F_LABEL
| HWMON_F_FAULT
,
186 [hwmon_intrusion
] = HWMON_INTRUSION_ALARM
,
190 * struct hp_wmi_numeric_sensor - a HPBIOS_BIOSNumericSensor instance
192 * Two variants of HPBIOS_BIOSNumericSensor are known. The first is specified
193 * in [1] and appears to be much more widespread. The second was discovered by
194 * decoding BMOF blobs [4], seems to be found only in some newer ZBook systems
195 * [3], and has two new properties and a slightly different property order.
197 * These differences don't matter on Windows, where WMI object properties are
198 * accessed by name. For us, supporting both variants gets ugly and hacky at
199 * times. The fun begins now; this struct is defined as per the new variant.
201 * Effective MOF definition:
203 * #pragma namespace("\\\\.\\root\\HP\\InstrumentedBIOS");
204 * class HPBIOS_BIOSNumericSensor {
205 * [read] string Name;
206 * [read] string Description;
207 * [read, ValueMap {"0","1","2","3","4","5","6","7","8","9",
208 * "10","11","12"}, Values {"Unknown","Other","Temperature",
209 * "Voltage","Current","Tachometer","Counter","Switch","Lock",
210 * "Humidity","Smoke Detection","Presence","Air Flow"}]
212 * [read] string OtherSensorType;
213 * [read, ValueMap {"0","1","2","3","4","5","6","7","8","9",
214 * "10","11","12","13","14","15","16","17","18","..",
215 * "0x8000.."}, Values {"Unknown","Other","OK","Degraded",
216 * "Stressed","Predictive Failure","Error",
217 * "Non-Recoverable Error","Starting","Stopping","Stopped",
218 * "In Service","No Contact","Lost Communication","Aborted",
219 * "Dormant","Supporting Entity in Error","Completed",
220 * "Power Mode","DMTF Reserved","Vendor Reserved"}]
221 * uint32 OperationalStatus;
222 * [read] uint32 Size;
223 * [read] string PossibleStates[];
224 * [read] string CurrentState;
225 * [read, ValueMap {"0","1","2","3","4","5","6","7","8","9",
226 * "10","11","12","13","14","15","16","17","18","19","20",
227 * "21","22","23","24","25","26","27","28","29","30","31",
228 * "32","33","34","35","36","37","38","39","40","41","42",
229 * "43","44","45","46","47","48","49","50","51","52","53",
230 * "54","55","56","57","58","59","60","61","62","63","64",
231 * "65"}, Values {"Unknown","Other","Degrees C","Degrees F",
232 * "Degrees K","Volts","Amps","Watts","Joules","Coulombs",
233 * "VA","Nits","Lumens","Lux","Candelas","kPa","PSI",
234 * "Newtons","CFM","RPM","Hertz","Seconds","Minutes",
235 * "Hours","Days","Weeks","Mils","Inches","Feet",
236 * "Cubic Inches","Cubic Feet","Meters","Cubic Centimeters",
237 * "Cubic Meters","Liters","Fluid Ounces","Radians",
238 * "Steradians","Revolutions","Cycles","Gravities","Ounces",
239 * "Pounds","Foot-Pounds","Ounce-Inches","Gauss","Gilberts",
240 * "Henries","Farads","Ohms","Siemens","Moles","Becquerels",
241 * "PPM (parts/million)","Decibels","DbA","DbC","Grays",
242 * "Sieverts","Color Temperature Degrees K","Bits","Bytes",
243 * "Words (data)","DoubleWords","QuadWords","Percentage"}]
245 * [read] sint32 UnitModifier;
246 * [read] uint32 CurrentReading;
247 * [read] uint32 RateUnits;
250 * Effective MOF definition of old variant [1] (sans redundant info):
252 * class HPBIOS_BIOSNumericSensor {
253 * [read] string Name;
254 * [read] string Description;
255 * [read] uint32 SensorType;
256 * [read] string OtherSensorType;
257 * [read] uint32 OperationalStatus;
258 * [read] string CurrentState;
259 * [read] string PossibleStates[];
260 * [read] uint32 BaseUnits;
261 * [read] sint32 UnitModifier;
262 * [read] uint32 CurrentReading;
265 struct hp_wmi_numeric_sensor
{
267 const char *description
;
269 const char *other_sensor_type
; /* Explains "Other" SensorType. */
270 u32 operational_status
;
271 u8 size
; /* Count of PossibleStates[]. */
272 const char **possible_states
;
273 const char *current_state
;
281 * struct hp_wmi_platform_events - a HPBIOS_PlatformEvents instance
283 * Instances of this object reveal the set of possible HPBIOS_BIOSEvent
284 * instances for the current system, but it may not always be present.
286 * Effective MOF definition:
288 * #pragma namespace("\\\\.\\root\\HP\\InstrumentedBIOS");
289 * class HPBIOS_PlatformEvents {
290 * [read] string Name;
291 * [read] string Description;
292 * [read] string SourceNamespace;
293 * [read] string SourceClass;
294 * [read, ValueMap {"0","1","2","3","4",".."}, Values {
295 * "Unknown","Configuration Change","Button Pressed",
296 * "Sensor","BIOS Settings","Reserved"}]
298 * [read, ValueMap{"0","5","10","15","20","25","30",".."},
299 * Values{"Unknown","OK","Degraded/Warning","Minor Failure",
300 * "Major Failure","Critical Failure","Non-recoverable Error",
302 * uint32 PossibleSeverity;
303 * [read, ValueMap {"0","1","2","3","4","5","6","7","8","9",
304 * "10","11","12","13","14","15","16","17","18","..",
305 * "0x8000.."}, Values {"Unknown","Other","OK","Degraded",
306 * "Stressed","Predictive Failure","Error",
307 * "Non-Recoverable Error","Starting","Stopping","Stopped",
308 * "In Service","No Contact","Lost Communication","Aborted",
309 * "Dormant","Supporting Entity in Error","Completed",
310 * "Power Mode","DMTF Reserved","Vendor Reserved"}]
311 * uint32 PossibleStatus;
314 struct hp_wmi_platform_events
{
316 const char *description
;
317 const char *source_namespace
;
318 const char *source_class
;
320 u32 possible_severity
;
325 * struct hp_wmi_event - a HPBIOS_BIOSEvent instance
327 * Effective MOF definition [1] (corrected below from original):
329 * #pragma namespace("\\\\.\\root\\WMI");
330 * class HPBIOS_BIOSEvent : WMIEvent {
331 * [read] string Name;
332 * [read] string Description;
333 * [read ValueMap {"0","1","2","3","4"}, Values {"Unknown",
334 * "Configuration Change","Button Pressed","Sensor",
337 * [read, ValueMap {"0","5","10","15","20","25","30"},
338 * Values {"Unknown","OK","Degraded/Warning",
339 * "Minor Failure","Major Failure","Critical Failure",
340 * "Non-recoverable Error"}]
342 * [read, ValueMap {"0","1","2","3","4","5","6","7","8",
343 * "9","10","11","12","13","14","15","16","17","18","..",
344 * "0x8000.."}, Values {"Unknown","Other","OK","Degraded",
345 * "Stressed","Predictive Failure","Error",
346 * "Non-Recoverable Error","Starting","Stopping","Stopped",
347 * "In Service","No Contact","Lost Communication","Aborted",
348 * "Dormant","Supporting Entity in Error","Completed",
349 * "Power Mode","DMTF Reserved","Vendor Reserved"}]
353 struct hp_wmi_event
{
355 const char *description
;
360 * struct hp_wmi_info - sensor info
361 * @nsensor: numeric sensor properties
362 * @instance: its WMI instance number
363 * @state: pointer to driver state
364 * @has_alarm: whether sensor has an alarm flag
366 * @type: its hwmon sensor type
367 * @cached_val: current sensor reading value, scaled for hwmon
368 * @last_updated: when these readings were last updated
371 struct hp_wmi_numeric_sensor nsensor
;
373 void *state
; /* void *: Avoid forward declaration. */
376 enum hwmon_sensor_types type
;
378 unsigned long last_updated
; /* In jiffies. */
383 * struct hp_wmi_sensors - driver state
384 * @wdev: pointer to the parent WMI device
385 * @info_map: sensor info structs by hwmon type and channel number
386 * @channel_count: count of hwmon channels by hwmon type
387 * @has_intrusion: whether an intrusion sensor is present
388 * @intrusion: intrusion flag
389 * @lock: mutex to lock polling WMI and changes to driver state
391 struct hp_wmi_sensors
{
392 struct wmi_device
*wdev
;
393 struct hp_wmi_info
**info_map
[hwmon_max
];
394 u8 channel_count
[hwmon_max
];
398 struct mutex lock
; /* Lock polling WMI and driver state changes. */
401 static bool is_raw_wmi_string(const u8
*pointer
, u32 length
)
406 /* WMI strings are length-prefixed UTF-16 [5]. */
407 if (length
<= sizeof(*ptr
))
410 length
-= sizeof(*ptr
);
411 ptr
= (const u16
*)pointer
;
414 return len
<= length
&& !(len
& 1);
417 static char *convert_raw_wmi_string(const u8
*buf
)
425 src
= (const wchar_t *)buf
;
427 /* Count UTF-16 code points. Exclude trailing null padding. */
428 cps
= *src
/ sizeof(*src
);
429 while (cps
&& !src
[cps
])
432 /* Each code point becomes up to 3 UTF-8 characters. */
433 len
= min(cps
* 3, HP_WMI_MAX_STR_SIZE
- 1);
435 dst
= kmalloc((len
+ 1) * sizeof(*dst
), GFP_KERNEL
);
439 i
= utf16s_to_utf8s(++src
, cps
, UTF16_LITTLE_ENDIAN
, dst
, len
);
445 /* hp_wmi_strdup - devm_kstrdup, but length-limited */
446 static char *hp_wmi_strdup(struct device
*dev
, const char *src
)
451 len
= strnlen(src
, HP_WMI_MAX_STR_SIZE
- 1);
453 dst
= devm_kmalloc(dev
, (len
+ 1) * sizeof(*dst
), GFP_KERNEL
);
457 strscpy(dst
, src
, len
+ 1);
462 /* hp_wmi_wstrdup - hp_wmi_strdup, but for a raw WMI string */
463 static char *hp_wmi_wstrdup(struct device
*dev
, const u8
*buf
)
468 src
= convert_raw_wmi_string(buf
);
472 dst
= hp_wmi_strdup(dev
, strim(src
)); /* Note: Copy is trimmed. */
480 * hp_wmi_get_wobj - poll WMI for a WMI object instance
481 * @guid: WMI object GUID
482 * @instance: WMI object instance number
484 * Returns a new WMI object instance on success, or NULL on error.
485 * Caller must kfree() the result.
487 static union acpi_object
*hp_wmi_get_wobj(const char *guid
, u8 instance
)
489 struct acpi_buffer out
= { ACPI_ALLOCATE_BUFFER
, NULL
};
492 err
= wmi_query_block(guid
, instance
, &out
);
493 if (ACPI_FAILURE(err
))
499 /* hp_wmi_wobj_instance_count - find count of WMI object instances */
500 static u8
hp_wmi_wobj_instance_count(const char *guid
)
504 count
= wmi_instance_count(guid
);
506 return clamp(count
, 0, (int)HP_WMI_MAX_INSTANCES
);
509 static int check_wobj(const union acpi_object
*wobj
,
510 const acpi_object_type property_map
[], int last_prop
)
512 acpi_object_type type
= wobj
->type
;
513 acpi_object_type valid_type
;
514 union acpi_object
*elements
;
518 if (type
!= ACPI_TYPE_PACKAGE
)
521 elem_count
= wobj
->package
.count
;
522 if (elem_count
!= last_prop
+ 1)
525 elements
= wobj
->package
.elements
;
526 for (prop
= 0; prop
<= last_prop
; prop
++) {
527 type
= elements
[prop
].type
;
528 valid_type
= property_map
[prop
];
529 if (type
!= valid_type
) {
530 if (type
== ACPI_TYPE_BUFFER
&&
531 valid_type
== ACPI_TYPE_STRING
&&
532 is_raw_wmi_string(elements
[prop
].buffer
.pointer
,
533 elements
[prop
].buffer
.length
))
542 static int extract_acpi_value(struct device
*dev
,
543 union acpi_object
*element
,
544 acpi_object_type type
,
545 u32
*out_value
, char **out_string
)
548 case ACPI_TYPE_INTEGER
:
549 *out_value
= element
->integer
.value
;
552 case ACPI_TYPE_STRING
:
553 *out_string
= element
->type
== ACPI_TYPE_BUFFER
?
554 hp_wmi_wstrdup(dev
, element
->buffer
.pointer
) :
555 hp_wmi_strdup(dev
, strim(element
->string
.pointer
));
568 * check_numeric_sensor_wobj - validate a HPBIOS_BIOSNumericSensor instance
569 * @wobj: pointer to WMI object instance to check
570 * @out_size: out pointer to count of possible states
571 * @out_is_new: out pointer to whether this is a "new" variant object
573 * Returns 0 on success, or a negative error code on error.
575 static int check_numeric_sensor_wobj(const union acpi_object
*wobj
,
576 u8
*out_size
, bool *out_is_new
)
578 acpi_object_type type
= wobj
->type
;
579 int prop
= HP_WMI_PROPERTY_NAME
;
580 acpi_object_type valid_type
;
581 union acpi_object
*elements
;
589 if (type
!= ACPI_TYPE_PACKAGE
)
593 * elements is a variable-length array of ACPI objects, one for
594 * each property of the WMI object instance, except that the
595 * strings in PossibleStates[] are flattened into this array
596 * as if each individual string were a property by itself.
598 elements
= wobj
->package
.elements
;
600 elem_count
= wobj
->package
.count
;
601 if (elem_count
<= HP_WMI_PROPERTY_SIZE
||
602 elem_count
> HP_WMI_MAX_PROPERTIES
)
605 type
= elements
[HP_WMI_PROPERTY_SIZE
].type
;
607 case ACPI_TYPE_INTEGER
:
609 last_prop
= HP_WMI_PROPERTY_RATE_UNITS
;
612 case ACPI_TYPE_STRING
:
614 last_prop
= HP_WMI_PROPERTY_CURRENT_READING
;
622 * In general, the count of PossibleStates[] must be > 0.
623 * Also, the old variant lacks the Size property, so we may need to
624 * reduce the value of last_prop by 1 when doing arithmetic with it.
626 if (elem_count
< last_prop
- !is_new
+ 1)
629 count
= elem_count
- (last_prop
- !is_new
);
631 for (i
= 0; i
< elem_count
&& prop
<= last_prop
; i
++, prop
++) {
632 type
= elements
[i
].type
;
633 valid_type
= hp_wmi_property_map
[prop
];
634 if (type
!= valid_type
)
638 case HP_WMI_PROPERTY_OPERATIONAL_STATUS
:
639 /* Old variant: CurrentState follows OperationalStatus. */
641 prop
= HP_WMI_PROPERTY_CURRENT_STATE
- 1;
644 case HP_WMI_PROPERTY_SIZE
:
645 /* New variant: Size == count of PossibleStates[]. */
646 if (count
!= elements
[i
].integer
.value
)
650 case HP_WMI_PROPERTY_POSSIBLE_STATES
:
651 /* PossibleStates[0] has already been type-checked. */
652 for (j
= 0; i
+ 1 < elem_count
&& j
+ 1 < count
; j
++) {
653 type
= elements
[++i
].type
;
654 if (type
!= valid_type
)
658 /* Old variant: BaseUnits follows PossibleStates[]. */
660 prop
= HP_WMI_PROPERTY_BASE_UNITS
- 1;
663 case HP_WMI_PROPERTY_CURRENT_STATE
:
664 /* Old variant: PossibleStates[] follows CurrentState. */
666 prop
= HP_WMI_PROPERTY_POSSIBLE_STATES
- 1;
671 if (prop
!= last_prop
+ 1)
675 *out_is_new
= is_new
;
681 numeric_sensor_is_connected(const struct hp_wmi_numeric_sensor
*nsensor
)
683 u32 operational_status
= nsensor
->operational_status
;
685 return operational_status
!= HP_WMI_STATUS_NO_CONTACT
;
688 static int numeric_sensor_has_fault(const struct hp_wmi_numeric_sensor
*nsensor
)
690 u32 operational_status
= nsensor
->operational_status
;
692 switch (operational_status
) {
693 case HP_WMI_STATUS_DEGRADED
:
694 case HP_WMI_STATUS_STRESSED
: /* e.g. Overload, overtemp. */
695 case HP_WMI_STATUS_PREDICTIVE_FAILURE
: /* e.g. Fan removed. */
696 case HP_WMI_STATUS_ERROR
:
697 case HP_WMI_STATUS_NON_RECOVERABLE_ERROR
:
698 case HP_WMI_STATUS_NO_CONTACT
:
699 case HP_WMI_STATUS_LOST_COMMUNICATION
:
700 case HP_WMI_STATUS_ABORTED
:
701 case HP_WMI_STATUS_SUPPORTING_ENTITY_IN_ERROR
:
703 /* Assume combination by addition; bitwise OR doesn't make sense. */
704 case HP_WMI_STATUS_COMPLETED
+ HP_WMI_STATUS_DEGRADED
:
705 case HP_WMI_STATUS_COMPLETED
+ HP_WMI_STATUS_ERROR
:
712 /* scale_numeric_sensor - scale sensor reading for hwmon */
713 static long scale_numeric_sensor(const struct hp_wmi_numeric_sensor
*nsensor
)
715 u32 current_reading
= nsensor
->current_reading
;
716 s32 unit_modifier
= nsensor
->unit_modifier
;
717 u32 sensor_type
= nsensor
->sensor_type
;
718 u32 base_units
= nsensor
->base_units
;
722 /* Fan readings are in RPM units; others are in milliunits. */
723 target_modifier
= sensor_type
== HP_WMI_TYPE_AIR_FLOW
? 0 : -3;
725 val
= current_reading
;
727 for (; unit_modifier
< target_modifier
; unit_modifier
++)
728 val
= DIV_ROUND_CLOSEST(val
, 10);
730 for (; unit_modifier
> target_modifier
; unit_modifier
--) {
731 if (val
> LONG_MAX
/ 10) {
738 if (sensor_type
== HP_WMI_TYPE_TEMPERATURE
) {
739 switch (base_units
) {
740 case HP_WMI_UNITS_DEGREES_F
:
742 val
= val
<= LONG_MAX
/ 5 ?
743 DIV_ROUND_CLOSEST(val
* 5, 9) :
744 DIV_ROUND_CLOSEST(val
, 9) * 5;
747 case HP_WMI_UNITS_DEGREES_K
:
748 val
= milli_kelvin_to_millicelsius(val
);
757 * classify_numeric_sensor - classify a numeric sensor
758 * @nsensor: pointer to numeric sensor struct
760 * Returns an enum hp_wmi_type value on success,
761 * or a negative value if the sensor type is unsupported.
763 static int classify_numeric_sensor(const struct hp_wmi_numeric_sensor
*nsensor
)
765 u32 sensor_type
= nsensor
->sensor_type
;
766 u32 base_units
= nsensor
->base_units
;
767 const char *name
= nsensor
->name
;
769 switch (sensor_type
) {
770 case HP_WMI_TYPE_TEMPERATURE
:
772 * Some systems have sensors named "X Thermal Index" in "Other"
773 * units. Tested CPU sensor examples were found to be in °C,
774 * albeit perhaps "differently" accurate; e.g. readings were
775 * reliably -6°C vs. coretemp on a HP Compaq Elite 8300, and
776 * +8°C on an EliteOne G1 800. But this is still within the
777 * realm of plausibility for cheaply implemented motherboard
778 * sensors, and chassis readings were about as expected.
780 if ((base_units
== HP_WMI_UNITS_OTHER
&&
781 strstr(name
, HP_WMI_PATTERN_TEMP_SENSOR
)) ||
782 base_units
== HP_WMI_UNITS_DEGREES_C
||
783 base_units
== HP_WMI_UNITS_DEGREES_F
||
784 base_units
== HP_WMI_UNITS_DEGREES_K
)
785 return HP_WMI_TYPE_TEMPERATURE
;
788 case HP_WMI_TYPE_VOLTAGE
:
789 if (base_units
== HP_WMI_UNITS_VOLTS
)
790 return HP_WMI_TYPE_VOLTAGE
;
793 case HP_WMI_TYPE_CURRENT
:
794 if (base_units
== HP_WMI_UNITS_AMPS
)
795 return HP_WMI_TYPE_CURRENT
;
798 case HP_WMI_TYPE_AIR_FLOW
:
800 * Strangely, HP considers fan RPM sensor type to be
801 * "Air Flow" instead of the more intuitive "Tachometer".
803 if (base_units
== HP_WMI_UNITS_RPM
)
804 return HP_WMI_TYPE_AIR_FLOW
;
812 populate_numeric_sensor_from_wobj(struct device
*dev
,
813 struct hp_wmi_numeric_sensor
*nsensor
,
814 union acpi_object
*wobj
, bool *out_is_new
)
816 int last_prop
= HP_WMI_PROPERTY_RATE_UNITS
;
817 int prop
= HP_WMI_PROPERTY_NAME
;
818 const char **possible_states
;
819 union acpi_object
*element
;
820 acpi_object_type type
;
827 err
= check_numeric_sensor_wobj(wobj
, &size
, &is_new
);
831 possible_states
= devm_kcalloc(dev
, size
, sizeof(*possible_states
),
833 if (!possible_states
)
836 element
= wobj
->package
.elements
;
837 nsensor
->possible_states
= possible_states
;
838 nsensor
->size
= size
;
841 last_prop
= HP_WMI_PROPERTY_CURRENT_READING
;
843 for (; prop
<= last_prop
; prop
++) {
844 type
= hp_wmi_property_map
[prop
];
846 err
= extract_acpi_value(dev
, element
, type
, &value
, &string
);
853 case HP_WMI_PROPERTY_NAME
:
854 nsensor
->name
= string
;
857 case HP_WMI_PROPERTY_DESCRIPTION
:
858 nsensor
->description
= string
;
861 case HP_WMI_PROPERTY_SENSOR_TYPE
:
862 if (value
> HP_WMI_TYPE_AIR_FLOW
)
865 nsensor
->sensor_type
= value
;
868 case HP_WMI_PROPERTY_OTHER_SENSOR_TYPE
:
869 nsensor
->other_sensor_type
= string
;
872 case HP_WMI_PROPERTY_OPERATIONAL_STATUS
:
873 nsensor
->operational_status
= value
;
875 /* Old variant: CurrentState follows OperationalStatus. */
877 prop
= HP_WMI_PROPERTY_CURRENT_STATE
- 1;
880 case HP_WMI_PROPERTY_SIZE
:
881 break; /* Already set. */
883 case HP_WMI_PROPERTY_POSSIBLE_STATES
:
884 *possible_states
++ = string
;
888 /* Old variant: BaseUnits follows PossibleStates[]. */
889 if (!is_new
&& !size
)
890 prop
= HP_WMI_PROPERTY_BASE_UNITS
- 1;
893 case HP_WMI_PROPERTY_CURRENT_STATE
:
894 nsensor
->current_state
= string
;
896 /* Old variant: PossibleStates[] follows CurrentState. */
898 prop
= HP_WMI_PROPERTY_POSSIBLE_STATES
- 1;
901 case HP_WMI_PROPERTY_BASE_UNITS
:
902 nsensor
->base_units
= value
;
905 case HP_WMI_PROPERTY_UNIT_MODIFIER
:
906 /* UnitModifier is signed. */
907 nsensor
->unit_modifier
= (s32
)value
;
910 case HP_WMI_PROPERTY_CURRENT_READING
:
911 nsensor
->current_reading
= value
;
914 case HP_WMI_PROPERTY_RATE_UNITS
:
915 nsensor
->rate_units
= value
;
923 *out_is_new
= is_new
;
928 /* update_numeric_sensor_from_wobj - update fungible sensor properties */
930 update_numeric_sensor_from_wobj(struct device
*dev
,
931 struct hp_wmi_numeric_sensor
*nsensor
,
932 const union acpi_object
*wobj
)
934 const union acpi_object
*elements
;
935 const union acpi_object
*element
;
936 const char *new_string
;
944 err
= check_numeric_sensor_wobj(wobj
, &size
, &is_new
);
948 elements
= wobj
->package
.elements
;
950 element
= &elements
[HP_WMI_PROPERTY_OPERATIONAL_STATUS
];
951 nsensor
->operational_status
= element
->integer
.value
;
954 * In general, an index offset is needed after PossibleStates[0].
955 * On a new variant, CurrentState is after PossibleStates[]. This is
956 * not the case on an old variant, but we still need to offset the
957 * read because CurrentState is where Size would be on a new variant.
959 offset
= is_new
? size
- 1 : -2;
961 element
= &elements
[HP_WMI_PROPERTY_CURRENT_STATE
+ offset
];
962 string
= element
->type
== ACPI_TYPE_BUFFER
?
963 convert_raw_wmi_string(element
->buffer
.pointer
) :
964 element
->string
.pointer
;
967 trimmed
= strim(string
);
968 if (strcmp(trimmed
, nsensor
->current_state
)) {
969 new_string
= hp_wmi_strdup(dev
, trimmed
);
971 devm_kfree(dev
, nsensor
->current_state
);
972 nsensor
->current_state
= new_string
;
975 if (element
->type
== ACPI_TYPE_BUFFER
)
979 /* Old variant: -2 (not -1) because it lacks the Size property. */
981 offset
= (int)size
- 2; /* size is > 0, i.e. may be 1. */
983 element
= &elements
[HP_WMI_PROPERTY_UNIT_MODIFIER
+ offset
];
984 nsensor
->unit_modifier
= (s32
)element
->integer
.value
;
986 element
= &elements
[HP_WMI_PROPERTY_CURRENT_READING
+ offset
];
987 nsensor
->current_reading
= element
->integer
.value
;
991 * check_platform_events_wobj - validate a HPBIOS_PlatformEvents instance
992 * @wobj: pointer to WMI object instance to check
994 * Returns 0 on success, or a negative error code on error.
996 static int check_platform_events_wobj(const union acpi_object
*wobj
)
998 return check_wobj(wobj
, hp_wmi_platform_events_property_map
,
999 HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS
);
1003 populate_platform_events_from_wobj(struct device
*dev
,
1004 struct hp_wmi_platform_events
*pevents
,
1005 union acpi_object
*wobj
)
1007 int last_prop
= HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS
;
1008 int prop
= HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME
;
1009 union acpi_object
*element
;
1010 acpi_object_type type
;
1015 err
= check_platform_events_wobj(wobj
);
1019 element
= wobj
->package
.elements
;
1021 for (; prop
<= last_prop
; prop
++, element
++) {
1022 type
= hp_wmi_platform_events_property_map
[prop
];
1024 err
= extract_acpi_value(dev
, element
, type
, &value
, &string
);
1029 case HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME
:
1030 pevents
->name
= string
;
1033 case HP_WMI_PLATFORM_EVENTS_PROPERTY_DESCRIPTION
:
1034 pevents
->description
= string
;
1037 case HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_NAMESPACE
:
1038 if (strcasecmp(HP_WMI_EVENT_NAMESPACE
, string
))
1041 pevents
->source_namespace
= string
;
1044 case HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_CLASS
:
1045 if (strcasecmp(HP_WMI_EVENT_CLASS
, string
))
1048 pevents
->source_class
= string
;
1051 case HP_WMI_PLATFORM_EVENTS_PROPERTY_CATEGORY
:
1052 pevents
->category
= value
;
1055 case HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_SEVERITY
:
1056 pevents
->possible_severity
= value
;
1059 case HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS
:
1060 pevents
->possible_status
= value
;
1072 * check_event_wobj - validate a HPBIOS_BIOSEvent instance
1073 * @wobj: pointer to WMI object instance to check
1075 * Returns 0 on success, or a negative error code on error.
1077 static int check_event_wobj(const union acpi_object
*wobj
)
1079 return check_wobj(wobj
, hp_wmi_event_property_map
,
1080 HP_WMI_EVENT_PROPERTY_STATUS
);
1083 static int populate_event_from_wobj(struct device
*dev
,
1084 struct hp_wmi_event
*event
,
1085 union acpi_object
*wobj
)
1087 int prop
= HP_WMI_EVENT_PROPERTY_NAME
;
1088 union acpi_object
*element
;
1089 acpi_object_type type
;
1094 err
= check_event_wobj(wobj
);
1098 element
= wobj
->package
.elements
;
1100 for (; prop
<= HP_WMI_EVENT_PROPERTY_CATEGORY
; prop
++, element
++) {
1101 type
= hp_wmi_event_property_map
[prop
];
1103 err
= extract_acpi_value(dev
, element
, type
, &value
, &string
);
1108 case HP_WMI_EVENT_PROPERTY_NAME
:
1109 event
->name
= string
;
1112 case HP_WMI_EVENT_PROPERTY_DESCRIPTION
:
1113 event
->description
= string
;
1116 case HP_WMI_EVENT_PROPERTY_CATEGORY
:
1117 event
->category
= value
;
1129 * classify_event - classify an event
1131 * @category: event category
1133 * Classify instances of both HPBIOS_PlatformEvents and HPBIOS_BIOSEvent from
1134 * property values. Recognition criteria are based on multiple ACPI dumps [3].
1136 * Returns an enum hp_wmi_type value on success,
1137 * or a negative value if the event type is unsupported.
1139 static int classify_event(const char *event_name
, u32 category
)
1141 if (category
!= HP_WMI_CATEGORY_SENSOR
)
1144 /* Fan events have Name "X Stall". */
1145 if (strstr(event_name
, HP_WMI_PATTERN_FAN_ALARM
))
1146 return HP_WMI_TYPE_AIR_FLOW
;
1148 /* Intrusion events have Name "Hood Intrusion". */
1149 if (!strcmp(event_name
, HP_WMI_PATTERN_INTRUSION_ALARM
))
1150 return HP_WMI_TYPE_INTRUSION
;
1153 * Temperature events have Name either "Thermal Caution" or
1154 * "Thermal Critical". Deal only with "Thermal Critical" events.
1156 * "Thermal Caution" events have Status "Stressed", informing us that
1157 * the OperationalStatus of the related sensor has become "Stressed".
1158 * However, this is already a fault condition that will clear itself
1159 * when the sensor recovers, so we have no further interest in them.
1161 if (!strcmp(event_name
, HP_WMI_PATTERN_TEMP_ALARM
))
1162 return HP_WMI_TYPE_TEMPERATURE
;
1168 * interpret_info - interpret sensor for hwmon
1169 * @info: pointer to sensor info struct
1171 * Should be called after the numeric sensor member has been updated.
1173 static void interpret_info(struct hp_wmi_info
*info
)
1175 const struct hp_wmi_numeric_sensor
*nsensor
= &info
->nsensor
;
1177 info
->cached_val
= scale_numeric_sensor(nsensor
);
1178 info
->last_updated
= jiffies
;
1182 * hp_wmi_update_info - poll WMI to update sensor info
1183 * @state: pointer to driver state
1184 * @info: pointer to sensor info struct
1186 * Returns 0 on success, or a negative error code on error.
1188 static int hp_wmi_update_info(struct hp_wmi_sensors
*state
,
1189 struct hp_wmi_info
*info
)
1191 struct hp_wmi_numeric_sensor
*nsensor
= &info
->nsensor
;
1192 struct device
*dev
= &state
->wdev
->dev
;
1193 const union acpi_object
*wobj
;
1194 u8 instance
= info
->instance
;
1197 if (time_after(jiffies
, info
->last_updated
+ HZ
)) {
1198 mutex_lock(&state
->lock
);
1200 wobj
= hp_wmi_get_wobj(HP_WMI_NUMERIC_SENSOR_GUID
, instance
);
1206 update_numeric_sensor_from_wobj(dev
, nsensor
, wobj
);
1208 interpret_info(info
);
1213 mutex_unlock(&state
->lock
);
1219 static int basic_string_show(struct seq_file
*seqf
, void *ignored
)
1221 const char *str
= seqf
->private;
1223 seq_printf(seqf
, "%s\n", str
);
1227 DEFINE_SHOW_ATTRIBUTE(basic_string
);
1229 static int fungible_show(struct seq_file
*seqf
, enum hp_wmi_property prop
)
1231 struct hp_wmi_numeric_sensor
*nsensor
;
1232 struct hp_wmi_sensors
*state
;
1233 struct hp_wmi_info
*info
;
1236 info
= seqf
->private;
1237 state
= info
->state
;
1238 nsensor
= &info
->nsensor
;
1240 err
= hp_wmi_update_info(state
, info
);
1245 case HP_WMI_PROPERTY_OPERATIONAL_STATUS
:
1246 seq_printf(seqf
, "%u\n", nsensor
->operational_status
);
1249 case HP_WMI_PROPERTY_CURRENT_STATE
:
1250 seq_printf(seqf
, "%s\n", nsensor
->current_state
);
1253 case HP_WMI_PROPERTY_UNIT_MODIFIER
:
1254 seq_printf(seqf
, "%d\n", nsensor
->unit_modifier
);
1257 case HP_WMI_PROPERTY_CURRENT_READING
:
1258 seq_printf(seqf
, "%u\n", nsensor
->current_reading
);
1268 static int operational_status_show(struct seq_file
*seqf
, void *ignored
)
1270 return fungible_show(seqf
, HP_WMI_PROPERTY_OPERATIONAL_STATUS
);
1272 DEFINE_SHOW_ATTRIBUTE(operational_status
);
1274 static int current_state_show(struct seq_file
*seqf
, void *ignored
)
1276 return fungible_show(seqf
, HP_WMI_PROPERTY_CURRENT_STATE
);
1278 DEFINE_SHOW_ATTRIBUTE(current_state
);
1280 static int possible_states_show(struct seq_file
*seqf
, void *ignored
)
1282 struct hp_wmi_numeric_sensor
*nsensor
= seqf
->private;
1285 for (i
= 0; i
< nsensor
->size
; i
++)
1286 seq_printf(seqf
, "%s%s", i
? "," : "",
1287 nsensor
->possible_states
[i
]);
1289 seq_puts(seqf
, "\n");
1293 DEFINE_SHOW_ATTRIBUTE(possible_states
);
1295 static int unit_modifier_show(struct seq_file
*seqf
, void *ignored
)
1297 return fungible_show(seqf
, HP_WMI_PROPERTY_UNIT_MODIFIER
);
1299 DEFINE_SHOW_ATTRIBUTE(unit_modifier
);
1301 static int current_reading_show(struct seq_file
*seqf
, void *ignored
)
1303 return fungible_show(seqf
, HP_WMI_PROPERTY_CURRENT_READING
);
1305 DEFINE_SHOW_ATTRIBUTE(current_reading
);
1307 /* hp_wmi_devm_debugfs_remove - devm callback for debugfs cleanup */
1308 static void hp_wmi_devm_debugfs_remove(void *res
)
1310 debugfs_remove_recursive(res
);
1313 /* hp_wmi_debugfs_init - create and populate debugfs directory tree */
1314 static void hp_wmi_debugfs_init(struct device
*dev
, struct hp_wmi_info
*info
,
1315 struct hp_wmi_platform_events
*pevents
,
1316 u8 icount
, u8 pcount
, bool is_new
)
1318 struct hp_wmi_numeric_sensor
*nsensor
;
1319 char buf
[HP_WMI_MAX_STR_SIZE
];
1320 struct dentry
*debugfs
;
1321 struct dentry
*entries
;
1326 /* dev_name() gives a not-very-friendly GUID for WMI devices. */
1327 scnprintf(buf
, sizeof(buf
), "hp-wmi-sensors-%u", dev
->id
);
1329 debugfs
= debugfs_create_dir(buf
, NULL
);
1330 if (IS_ERR(debugfs
))
1333 err
= devm_add_action_or_reset(dev
, hp_wmi_devm_debugfs_remove
,
1338 entries
= debugfs_create_dir("sensor", debugfs
);
1340 for (i
= 0; i
< icount
; i
++, info
++) {
1341 nsensor
= &info
->nsensor
;
1343 scnprintf(buf
, sizeof(buf
), "%u", i
);
1344 dir
= debugfs_create_dir(buf
, entries
);
1346 debugfs_create_file("name", 0444, dir
,
1347 (void *)nsensor
->name
,
1348 &basic_string_fops
);
1350 debugfs_create_file("description", 0444, dir
,
1351 (void *)nsensor
->description
,
1352 &basic_string_fops
);
1354 debugfs_create_u32("sensor_type", 0444, dir
,
1355 &nsensor
->sensor_type
);
1357 debugfs_create_file("other_sensor_type", 0444, dir
,
1358 (void *)nsensor
->other_sensor_type
,
1359 &basic_string_fops
);
1361 debugfs_create_file("operational_status", 0444, dir
,
1362 info
, &operational_status_fops
);
1364 debugfs_create_file("possible_states", 0444, dir
,
1365 nsensor
, &possible_states_fops
);
1367 debugfs_create_file("current_state", 0444, dir
,
1368 info
, ¤t_state_fops
);
1370 debugfs_create_u32("base_units", 0444, dir
,
1371 &nsensor
->base_units
);
1373 debugfs_create_file("unit_modifier", 0444, dir
,
1374 info
, &unit_modifier_fops
);
1376 debugfs_create_file("current_reading", 0444, dir
,
1377 info
, ¤t_reading_fops
);
1380 debugfs_create_u32("rate_units", 0444, dir
,
1381 &nsensor
->rate_units
);
1387 entries
= debugfs_create_dir("platform_events", debugfs
);
1389 for (i
= 0; i
< pcount
; i
++, pevents
++) {
1390 scnprintf(buf
, sizeof(buf
), "%u", i
);
1391 dir
= debugfs_create_dir(buf
, entries
);
1393 debugfs_create_file("name", 0444, dir
,
1394 (void *)pevents
->name
,
1395 &basic_string_fops
);
1397 debugfs_create_file("description", 0444, dir
,
1398 (void *)pevents
->description
,
1399 &basic_string_fops
);
1401 debugfs_create_file("source_namespace", 0444, dir
,
1402 (void *)pevents
->source_namespace
,
1403 &basic_string_fops
);
1405 debugfs_create_file("source_class", 0444, dir
,
1406 (void *)pevents
->source_class
,
1407 &basic_string_fops
);
1409 debugfs_create_u32("category", 0444, dir
,
1410 &pevents
->category
);
1412 debugfs_create_u32("possible_severity", 0444, dir
,
1413 &pevents
->possible_severity
);
1415 debugfs_create_u32("possible_status", 0444, dir
,
1416 &pevents
->possible_status
);
1420 static umode_t
hp_wmi_hwmon_is_visible(const void *drvdata
,
1421 enum hwmon_sensor_types type
,
1422 u32 attr
, int channel
)
1424 const struct hp_wmi_sensors
*state
= drvdata
;
1425 const struct hp_wmi_info
*info
;
1427 if (type
== hwmon_intrusion
)
1428 return state
->has_intrusion
? 0644 : 0;
1430 if (!state
->info_map
[type
] || !state
->info_map
[type
][channel
])
1433 info
= state
->info_map
[type
][channel
];
1435 if ((type
== hwmon_temp
&& attr
== hwmon_temp_alarm
) ||
1436 (type
== hwmon_fan
&& attr
== hwmon_fan_alarm
))
1437 return info
->has_alarm
? 0444 : 0;
1442 static int hp_wmi_hwmon_read(struct device
*dev
, enum hwmon_sensor_types type
,
1443 u32 attr
, int channel
, long *out_val
)
1445 struct hp_wmi_sensors
*state
= dev_get_drvdata(dev
);
1446 const struct hp_wmi_numeric_sensor
*nsensor
;
1447 struct hp_wmi_info
*info
;
1450 if (type
== hwmon_intrusion
) {
1451 *out_val
= state
->intrusion
? 1 : 0;
1456 info
= state
->info_map
[type
][channel
];
1458 if ((type
== hwmon_temp
&& attr
== hwmon_temp_alarm
) ||
1459 (type
== hwmon_fan
&& attr
== hwmon_fan_alarm
)) {
1460 *out_val
= info
->alarm
? 1 : 0;
1461 info
->alarm
= false;
1466 nsensor
= &info
->nsensor
;
1468 err
= hp_wmi_update_info(state
, info
);
1472 if ((type
== hwmon_temp
&& attr
== hwmon_temp_fault
) ||
1473 (type
== hwmon_fan
&& attr
== hwmon_fan_fault
))
1474 *out_val
= numeric_sensor_has_fault(nsensor
);
1476 *out_val
= info
->cached_val
;
1481 static int hp_wmi_hwmon_read_string(struct device
*dev
,
1482 enum hwmon_sensor_types type
, u32 attr
,
1483 int channel
, const char **out_str
)
1485 const struct hp_wmi_sensors
*state
= dev_get_drvdata(dev
);
1486 const struct hp_wmi_info
*info
;
1488 info
= state
->info_map
[type
][channel
];
1489 *out_str
= info
->nsensor
.name
;
1494 static int hp_wmi_hwmon_write(struct device
*dev
, enum hwmon_sensor_types type
,
1495 u32 attr
, int channel
, long val
)
1497 struct hp_wmi_sensors
*state
= dev_get_drvdata(dev
);
1502 mutex_lock(&state
->lock
);
1504 state
->intrusion
= false;
1506 mutex_unlock(&state
->lock
);
1511 static const struct hwmon_ops hp_wmi_hwmon_ops
= {
1512 .is_visible
= hp_wmi_hwmon_is_visible
,
1513 .read
= hp_wmi_hwmon_read
,
1514 .read_string
= hp_wmi_hwmon_read_string
,
1515 .write
= hp_wmi_hwmon_write
,
1518 static struct hwmon_chip_info hp_wmi_chip_info
= {
1519 .ops
= &hp_wmi_hwmon_ops
,
1523 static struct hp_wmi_info
*match_fan_event(struct hp_wmi_sensors
*state
,
1524 const char *event_description
)
1526 struct hp_wmi_info
**ptr_info
= state
->info_map
[hwmon_fan
];
1527 u8 fan_count
= state
->channel_count
[hwmon_fan
];
1528 struct hp_wmi_info
*info
;
1532 /* Fan event has Description "X Speed". Sensor has Name "X[ Speed]". */
1534 for (i
= 0; i
< fan_count
; i
++, ptr_info
++) {
1536 name
= info
->nsensor
.name
;
1538 if (strstr(event_description
, name
))
1545 static u8
match_temp_events(struct hp_wmi_sensors
*state
,
1546 const char *event_description
,
1547 struct hp_wmi_info
*temp_info
[])
1549 struct hp_wmi_info
**ptr_info
= state
->info_map
[hwmon_temp
];
1550 u8 temp_count
= state
->channel_count
[hwmon_temp
];
1551 struct hp_wmi_info
*info
;
1558 /* Description is either "CPU Thermal Index" or "Chassis Thermal Index". */
1560 is_cpu
= !strcmp(event_description
, HP_WMI_PATTERN_CPU_TEMP
);
1561 is_sys
= !strcmp(event_description
, HP_WMI_PATTERN_SYS_TEMP
);
1562 if (!is_cpu
&& !is_sys
)
1566 * CPU event: Match one sensor with Name either "CPU Thermal Index" or
1567 * "CPU Temperature", or multiple with Name(s) "CPU[#] Temperature".
1569 * Chassis event: Match one sensor with Name either
1570 * "Chassis Thermal Index" or "System Ambient Temperature".
1573 for (i
= 0; i
< temp_count
; i
++, ptr_info
++) {
1575 name
= info
->nsensor
.name
;
1577 if ((is_cpu
&& (!strcmp(name
, HP_WMI_PATTERN_CPU_TEMP
) ||
1578 !strcmp(name
, HP_WMI_PATTERN_CPU_TEMP2
))) ||
1579 (is_sys
&& (!strcmp(name
, HP_WMI_PATTERN_SYS_TEMP
) ||
1580 !strcmp(name
, HP_WMI_PATTERN_SYS_TEMP2
)))) {
1581 temp_info
[0] = info
;
1585 if (is_cpu
&& (strstr(name
, HP_WMI_PATTERN_CPU
) &&
1586 strstr(name
, HP_WMI_PATTERN_TEMP
)))
1587 temp_info
[count
++] = info
;
1593 /* hp_wmi_devm_debugfs_remove - devm callback for WMI event handler removal */
1594 static void hp_wmi_devm_notify_remove(void *ignored
)
1596 wmi_remove_notify_handler(HP_WMI_EVENT_GUID
);
1599 /* hp_wmi_notify - WMI event notification handler */
1600 static void hp_wmi_notify(union acpi_object
*wobj
, void *context
)
1602 struct hp_wmi_info
*temp_info
[HP_WMI_MAX_INSTANCES
] = {};
1603 struct hp_wmi_sensors
*state
= context
;
1604 struct device
*dev
= &state
->wdev
->dev
;
1605 struct hp_wmi_event event
= {};
1606 struct hp_wmi_info
*fan_info
;
1612 * The following warning may occur in the kernel log:
1614 * ACPI Warning: \_SB.WMID._WED: Return type mismatch -
1615 * found Package, expected Integer/String/Buffer
1617 * After using [4] to decode BMOF blobs found in [3], careless copying
1618 * of BIOS code seems the most likely explanation for this warning.
1619 * HP_WMI_EVENT_GUID refers to \\.\root\WMI\HPBIOS_BIOSEvent on
1620 * business-class systems, but it refers to \\.\root\WMI\hpqBEvnt on
1621 * non-business-class systems. Per the existing hp-wmi driver, it
1622 * looks like an instance of hpqBEvnt delivered as event data may
1623 * indeed take the form of a raw ACPI_BUFFER on non-business-class
1624 * systems ("may" because ASL shows some BIOSes do strange things).
1626 * In any case, we can ignore this warning, because we always validate
1627 * the event data to ensure it is an ACPI_PACKAGE containing a
1628 * HPBIOS_BIOSEvent instance.
1634 mutex_lock(&state
->lock
);
1636 err
= populate_event_from_wobj(dev
, &event
, wobj
);
1638 dev_warn(dev
, "Bad event data (ACPI type %d)\n", wobj
->type
);
1642 event_type
= classify_event(event
.name
, event
.category
);
1643 switch (event_type
) {
1644 case HP_WMI_TYPE_AIR_FLOW
:
1645 fan_info
= match_fan_event(state
, event
.description
);
1647 fan_info
->alarm
= true;
1650 case HP_WMI_TYPE_INTRUSION
:
1651 state
->intrusion
= true;
1654 case HP_WMI_TYPE_TEMPERATURE
:
1655 count
= match_temp_events(state
, event
.description
, temp_info
);
1657 temp_info
[--count
]->alarm
= true;
1665 devm_kfree(dev
, event
.name
);
1666 devm_kfree(dev
, event
.description
);
1668 mutex_unlock(&state
->lock
);
1671 static int init_platform_events(struct device
*dev
,
1672 struct hp_wmi_platform_events
**out_pevents
,
1675 struct hp_wmi_platform_events
*pevents_arr
;
1676 struct hp_wmi_platform_events
*pevents
;
1677 union acpi_object
*wobj
;
1682 count
= hp_wmi_wobj_instance_count(HP_WMI_PLATFORM_EVENTS_GUID
);
1686 dev_dbg(dev
, "No platform events\n");
1691 pevents_arr
= devm_kcalloc(dev
, count
, sizeof(*pevents
), GFP_KERNEL
);
1695 for (i
= 0, pevents
= pevents_arr
; i
< count
; i
++, pevents
++) {
1696 wobj
= hp_wmi_get_wobj(HP_WMI_PLATFORM_EVENTS_GUID
, i
);
1700 err
= populate_platform_events_from_wobj(dev
, pevents
, wobj
);
1708 *out_pevents
= pevents_arr
;
1709 *out_pcount
= count
;
1711 dev_dbg(dev
, "Found %u platform events\n", count
);
1716 static int init_numeric_sensors(struct hp_wmi_sensors
*state
,
1717 struct hp_wmi_info
*connected
[],
1718 struct hp_wmi_info
**out_info
,
1719 u8
*out_icount
, u8
*out_count
,
1722 struct hp_wmi_info
***info_map
= state
->info_map
;
1723 u8
*channel_count
= state
->channel_count
;
1724 struct device
*dev
= &state
->wdev
->dev
;
1725 struct hp_wmi_numeric_sensor
*nsensor
;
1726 u8 channel_index
[hwmon_max
] = {};
1727 enum hwmon_sensor_types type
;
1728 struct hp_wmi_info
*info_arr
;
1729 struct hp_wmi_info
*info
;
1730 union acpi_object
*wobj
;
1739 icount
= hp_wmi_wobj_instance_count(HP_WMI_NUMERIC_SENSOR_GUID
);
1743 info_arr
= devm_kcalloc(dev
, icount
, sizeof(*info
), GFP_KERNEL
);
1747 for (i
= 0, info
= info_arr
; i
< icount
; i
++, info
++) {
1748 wobj
= hp_wmi_get_wobj(HP_WMI_NUMERIC_SENSOR_GUID
, i
);
1753 info
->state
= state
;
1754 nsensor
= &info
->nsensor
;
1756 err
= populate_numeric_sensor_from_wobj(dev
, nsensor
, wobj
,
1764 if (!numeric_sensor_is_connected(nsensor
))
1767 wtype
= classify_numeric_sensor(nsensor
);
1771 type
= hp_wmi_hwmon_type_map
[wtype
];
1773 channel_count
[type
]++;
1777 interpret_info(info
);
1779 connected
[count
++] = info
;
1782 dev_dbg(dev
, "Found %u sensors (%u connected)\n", i
, count
);
1784 for (i
= 0; i
< count
; i
++) {
1785 info
= connected
[i
];
1787 c
= channel_index
[type
]++;
1789 if (!info_map
[type
]) {
1790 info_map
[type
] = devm_kcalloc(dev
, channel_count
[type
],
1793 if (!info_map
[type
])
1797 info_map
[type
][c
] = info
;
1800 *out_info
= info_arr
;
1801 *out_icount
= icount
;
1803 *out_is_new
= is_new
;
1808 static bool find_event_attributes(struct hp_wmi_sensors
*state
,
1809 struct hp_wmi_platform_events
*pevents
,
1813 * The existence of this HPBIOS_PlatformEvents instance:
1816 * Name = "Rear Chassis Fan0 Stall";
1817 * Description = "Rear Chassis Fan0 Speed";
1818 * Category = 3; // "Sensor"
1819 * PossibleSeverity = 25; // "Critical Failure"
1820 * PossibleStatus = 5; // "Predictive Failure"
1824 * means that this HPBIOS_BIOSEvent instance may occur:
1827 * Name = "Rear Chassis Fan0 Stall";
1828 * Description = "Rear Chassis Fan0 Speed";
1829 * Category = 3; // "Sensor"
1830 * Severity = 25; // "Critical Failure"
1831 * Status = 5; // "Predictive Failure"
1834 * After the event occurs (e.g. because the fan was unplugged),
1835 * polling the related HPBIOS_BIOSNumericSensor instance gives:
1838 * Name = "Rear Chassis Fan0";
1839 * Description = "Reports rear chassis fan0 speed";
1840 * OperationalStatus = 5; // "Predictive Failure", was 3 ("OK")
1841 * CurrentReading = 0;
1845 * In this example, the hwmon fan channel for "Rear Chassis Fan0"
1846 * should support the alarm flag and have it be set if the related
1847 * HPBIOS_BIOSEvent instance occurs.
1849 * In addition to fan events, temperature (CPU/chassis) and intrusion
1850 * events are relevant to hwmon [2]. Note that much information in [2]
1851 * is unreliable; it is referenced in addition to ACPI dumps [3] merely
1852 * to support the conclusion that sensor and event names/descriptions
1853 * are systematic enough to allow this driver to match them.
1855 * Complications and limitations:
1857 * - Strings are freeform and may vary, cf. sensor Name "CPU0 Fan"
1858 * on a Z420 vs. "CPU Fan Speed" on an EliteOne 800 G1.
1859 * - Leading/trailing whitespace is a rare but real possibility [3].
1860 * - The HPBIOS_PlatformEvents object may not exist or its instances
1861 * may show that the system only has e.g. BIOS setting-related
1862 * events (cf. the ProBook 4540s and ProBook 470 G0 [3]).
1865 struct hp_wmi_info
*temp_info
[HP_WMI_MAX_INSTANCES
] = {};
1866 const char *event_description
;
1867 struct hp_wmi_info
*fan_info
;
1868 bool has_events
= false;
1869 const char *event_name
;
1875 for (i
= 0; i
< pevents_count
; i
++, pevents
++) {
1876 event_name
= pevents
->name
;
1877 event_description
= pevents
->description
;
1878 event_category
= pevents
->category
;
1880 event_type
= classify_event(event_name
, event_category
);
1881 switch (event_type
) {
1882 case HP_WMI_TYPE_AIR_FLOW
:
1883 fan_info
= match_fan_event(state
, event_description
);
1887 fan_info
->has_alarm
= true;
1891 case HP_WMI_TYPE_INTRUSION
:
1892 state
->has_intrusion
= true;
1896 case HP_WMI_TYPE_TEMPERATURE
:
1897 count
= match_temp_events(state
, event_description
,
1903 temp_info
[--count
]->has_alarm
= true;
1915 static int make_chip_info(struct hp_wmi_sensors
*state
, bool has_events
)
1917 const struct hwmon_channel_info
**ptr_channel_info
;
1918 struct hp_wmi_info
***info_map
= state
->info_map
;
1919 u8
*channel_count
= state
->channel_count
;
1920 struct hwmon_channel_info
*channel_info
;
1921 struct device
*dev
= &state
->wdev
->dev
;
1922 enum hwmon_sensor_types type
;
1929 if (channel_count
[hwmon_temp
])
1930 channel_count
[hwmon_chip
] = 1;
1932 if (has_events
&& state
->has_intrusion
)
1933 channel_count
[hwmon_intrusion
] = 1;
1935 for (type
= hwmon_chip
; type
< hwmon_max
; type
++)
1936 if (channel_count
[type
])
1939 channel_info
= devm_kcalloc(dev
, type_count
,
1940 sizeof(*channel_info
), GFP_KERNEL
);
1944 ptr_channel_info
= devm_kcalloc(dev
, type_count
+ 1,
1945 sizeof(*ptr_channel_info
), GFP_KERNEL
);
1946 if (!ptr_channel_info
)
1949 hp_wmi_chip_info
.info
= ptr_channel_info
;
1951 for (type
= hwmon_chip
; type
< hwmon_max
; type
++) {
1952 count
= channel_count
[type
];
1956 config
= devm_kcalloc(dev
, count
+ 1,
1957 sizeof(*config
), GFP_KERNEL
);
1961 attr
= hp_wmi_hwmon_attributes
[type
];
1962 channel_info
->type
= type
;
1963 channel_info
->config
= config
;
1964 memset32(config
, attr
, count
);
1966 *ptr_channel_info
++ = channel_info
++;
1968 if (!has_events
|| (type
!= hwmon_temp
&& type
!= hwmon_fan
))
1971 attr
= type
== hwmon_temp
? HWMON_T_ALARM
: HWMON_F_ALARM
;
1973 for (i
= 0; i
< count
; i
++)
1974 if (info_map
[type
][i
]->has_alarm
)
1981 static bool add_event_handler(struct hp_wmi_sensors
*state
)
1983 struct device
*dev
= &state
->wdev
->dev
;
1986 err
= wmi_install_notify_handler(HP_WMI_EVENT_GUID
,
1987 hp_wmi_notify
, state
);
1989 dev_info(dev
, "Failed to subscribe to WMI event\n");
1993 err
= devm_add_action_or_reset(dev
, hp_wmi_devm_notify_remove
, NULL
);
2000 static int hp_wmi_sensors_init(struct hp_wmi_sensors
*state
)
2002 struct hp_wmi_info
*connected
[HP_WMI_MAX_INSTANCES
];
2003 struct hp_wmi_platform_events
*pevents
= NULL
;
2004 struct device
*dev
= &state
->wdev
->dev
;
2005 struct hp_wmi_info
*info
;
2006 struct device
*hwdev
;
2014 err
= init_platform_events(dev
, &pevents
, &pcount
);
2018 err
= init_numeric_sensors(state
, connected
, &info
,
2019 &icount
, &count
, &is_new
);
2023 if (IS_ENABLED(CONFIG_DEBUG_FS
))
2024 hp_wmi_debugfs_init(dev
, info
, pevents
, icount
, pcount
, is_new
);
2027 return 0; /* No connected sensors; debugfs only. */
2029 has_events
= find_event_attributes(state
, pevents
, pcount
);
2031 /* Survive failure to install WMI event handler. */
2032 if (has_events
&& !add_event_handler(state
))
2035 err
= make_chip_info(state
, has_events
);
2039 hwdev
= devm_hwmon_device_register_with_info(dev
, "hp_wmi_sensors",
2040 state
, &hp_wmi_chip_info
,
2042 return PTR_ERR_OR_ZERO(hwdev
);
2045 static int hp_wmi_sensors_probe(struct wmi_device
*wdev
, const void *context
)
2047 struct device
*dev
= &wdev
->dev
;
2048 struct hp_wmi_sensors
*state
;
2050 state
= devm_kzalloc(dev
, sizeof(*state
), GFP_KERNEL
);
2056 mutex_init(&state
->lock
);
2058 dev_set_drvdata(dev
, state
);
2060 return hp_wmi_sensors_init(state
);
2063 static const struct wmi_device_id hp_wmi_sensors_id_table
[] = {
2064 { HP_WMI_NUMERIC_SENSOR_GUID
, NULL
},
2068 static struct wmi_driver hp_wmi_sensors_driver
= {
2069 .driver
= { .name
= "hp-wmi-sensors" },
2070 .id_table
= hp_wmi_sensors_id_table
,
2071 .probe
= hp_wmi_sensors_probe
,
2073 module_wmi_driver(hp_wmi_sensors_driver
);
2075 MODULE_AUTHOR("James Seo <james@equiv.tech>");
2076 MODULE_DESCRIPTION("HP WMI Sensors driver");
2077 MODULE_LICENSE("GPL");