1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * hwmon driver for HP (and some HP Compaq) business-class computers that
4 * report numeric sensor data via Windows Management Instrumentation (WMI).
5 *
6 * Copyright (C) 2023 James Seo <james@equiv.tech>
7 *
8 * References:
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
22 */
23
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>
32
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"
38
39/* Patterns for recognizing sensors and matching events to channels. */
40
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"
51
52/* These limits are arbitrary. The WMI implementation may vary by system. */
53
54#define HP_WMI_MAX_STR_SIZE 128U
55#define HP_WMI_MAX_PROPERTIES 32U
56#define HP_WMI_MAX_INSTANCES 32U
57
58enum hp_wmi_type {
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. */
65};
66
67enum hp_wmi_category {
68 HP_WMI_CATEGORY_SENSOR = 3,
69};
70
71enum 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,
79};
80
81enum hp_wmi_status {
82 HP_WMI_STATUS_OK = 2,
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,
92
93 /* Occurs combined with one of "OK", "Degraded", and "Error" [1]. */
94 HP_WMI_STATUS_COMPLETED = 17,
95};
96
97enum hp_wmi_units {
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,
105};
106
107enum 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,
120};
121
122static 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,
135};
136
137enum 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,
145};
146
147static 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,
155};
156
157enum 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,
163};
164
165static 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,
171};
172
173static 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,
178};
179
180static 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,
187};
188
189/*
190 * struct hp_wmi_numeric_sensor - a HPBIOS_BIOSNumericSensor instance
191 *
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.
196 *
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.
200 *
201 * Effective MOF definition:
202 *
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"}]
211 * uint32 SensorType;
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"}]
244 * uint32 BaseUnits;
245 * [read] sint32 UnitModifier;
246 * [read] uint32 CurrentReading;
247 * [read] uint32 RateUnits;
248 * };
249 *
250 * Effective MOF definition of old variant [1] (sans redundant info):
251 *
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;
263 * };
264 */
265struct hp_wmi_numeric_sensor {
266 const char *name;
267 const char *description;
268 u32 sensor_type;
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;
274 u32 base_units;
275 s32 unit_modifier;
276 u32 current_reading;
277 u32 rate_units;
278};
279
280/*
281 * struct hp_wmi_platform_events - a HPBIOS_PlatformEvents instance
282 *
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.
285 *
286 * Effective MOF definition:
287 *
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"}]
297 * uint32 Category;
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",
301 * "DMTF Reserved"}]
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;
312 * };
313 */
314struct hp_wmi_platform_events {
315 const char *name;
316 const char *description;
317 const char *source_namespace;
318 const char *source_class;
319 u32 category;
320 u32 possible_severity;
321 u32 possible_status;
322};
323
324/*
325 * struct hp_wmi_event - a HPBIOS_BIOSEvent instance
326 *
327 * Effective MOF definition [1] (corrected below from original):
328 *
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",
335 * "BIOS Settings"}]
336 * uint32 Category;
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"}]
341 * uint32 Severity;
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"}]
350 * uint32 Status;
351 * };
352 */
353struct hp_wmi_event {
354 const char *name;
355 const char *description;
356 u32 category;
357};
358
359/*
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
365 * @alarm: 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
369 */
370struct hp_wmi_info {
371 struct hp_wmi_numeric_sensor nsensor;
372 u8 instance;
373 void *state; /* void *: Avoid forward declaration. */
374 bool has_alarm;
375 bool alarm;
376 enum hwmon_sensor_types type;
377 long cached_val;
378 unsigned long last_updated; /* In jiffies. */
379
380};
381
382/*
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
390 */
391struct hp_wmi_sensors {
392 struct wmi_device *wdev;
393 struct hp_wmi_info **info_map[hwmon_max];
394 u8 channel_count[hwmon_max];
395 bool has_intrusion;
396 bool intrusion;
397
398 struct mutex lock; /* Lock polling WMI and driver state changes. */
399};
400
401static bool is_raw_wmi_string(const u8 *pointer, u32 length)
402{
403 const u16 *ptr;
404 u16 len;
405
406 /* WMI strings are length-prefixed UTF-16 [5]. */
407 if (length <= sizeof(*ptr))
408 return false;
409
410 length -= sizeof(*ptr);
411 ptr = (const u16 *)pointer;
412 len = *ptr;
413
414 return len <= length && !(len & 1);
415}
416
417static char *convert_raw_wmi_string(const u8 *buf)
418{
419 const wchar_t *src;
420 unsigned int cps;
421 unsigned int len;
422 char *dst;
423 int i;
424
425 src = (const wchar_t *)buf;
426
427 /* Count UTF-16 code points. Exclude trailing null padding. */
428 cps = *src / sizeof(*src);
429 while (cps && !src[cps])
430 cps--;
431
432 /* Each code point becomes up to 3 UTF-8 characters. */
433 len = min(cps * 3, HP_WMI_MAX_STR_SIZE - 1);
434
435 dst = kmalloc(size: (len + 1) * sizeof(*dst), GFP_KERNEL);
436 if (!dst)
437 return NULL;
438
439 i = utf16s_to_utf8s(pwcs: ++src, len: cps, endian: UTF16_LITTLE_ENDIAN, s: dst, maxlen: len);
440 dst[i] = '\0';
441
442 return dst;
443}
444
445/* hp_wmi_strdup - devm_kstrdup, but length-limited */
446static char *hp_wmi_strdup(struct device *dev, const char *src)
447{
448 char *dst;
449 size_t len;
450
451 len = strnlen(p: src, HP_WMI_MAX_STR_SIZE - 1);
452
453 dst = devm_kmalloc(dev, size: (len + 1) * sizeof(*dst), GFP_KERNEL);
454 if (!dst)
455 return NULL;
456
457 strscpy(dst, src, len + 1);
458
459 return dst;
460}
461
462/* hp_wmi_wstrdup - hp_wmi_strdup, but for a raw WMI string */
463static char *hp_wmi_wstrdup(struct device *dev, const u8 *buf)
464{
465 char *src;
466 char *dst;
467
468 src = convert_raw_wmi_string(buf);
469 if (!src)
470 return NULL;
471
472 dst = hp_wmi_strdup(dev, src: strim(src)); /* Note: Copy is trimmed. */
473
474 kfree(objp: src);
475
476 return dst;
477}
478
479/*
480 * hp_wmi_get_wobj - poll WMI for a WMI object instance
481 * @guid: WMI object GUID
482 * @instance: WMI object instance number
483 *
484 * Returns a new WMI object instance on success, or NULL on error.
485 * Caller must kfree() the result.
486 */
487static union acpi_object *hp_wmi_get_wobj(const char *guid, u8 instance)
488{
489 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
490 acpi_status err;
491
492 err = wmi_query_block(guid, instance, out: &out);
493 if (ACPI_FAILURE(err))
494 return NULL;
495
496 return out.pointer;
497}
498
499/* hp_wmi_wobj_instance_count - find count of WMI object instances */
500static u8 hp_wmi_wobj_instance_count(const char *guid)
501{
502 int count;
503
504 count = wmi_instance_count(guid);
505
506 return clamp(count, 0, (int)HP_WMI_MAX_INSTANCES);
507}
508
509static int check_wobj(const union acpi_object *wobj,
510 const acpi_object_type property_map[], int last_prop)
511{
512 acpi_object_type type = wobj->type;
513 acpi_object_type valid_type;
514 union acpi_object *elements;
515 u32 elem_count;
516 int prop;
517
518 if (type != ACPI_TYPE_PACKAGE)
519 return -EINVAL;
520
521 elem_count = wobj->package.count;
522 if (elem_count != last_prop + 1)
523 return -EINVAL;
524
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(pointer: elements[prop].buffer.pointer,
533 length: elements[prop].buffer.length))
534 continue;
535 return -EINVAL;
536 }
537 }
538
539 return 0;
540}
541
542static int extract_acpi_value(struct device *dev,
543 union acpi_object *element,
544 acpi_object_type type,
545 u32 *out_value, char **out_string)
546{
547 switch (type) {
548 case ACPI_TYPE_INTEGER:
549 *out_value = element->integer.value;
550 break;
551
552 case ACPI_TYPE_STRING:
553 *out_string = element->type == ACPI_TYPE_BUFFER ?
554 hp_wmi_wstrdup(dev, buf: element->buffer.pointer) :
555 hp_wmi_strdup(dev, src: strim(element->string.pointer));
556 if (!*out_string)
557 return -ENOMEM;
558 break;
559
560 default:
561 return -EINVAL;
562 }
563
564 return 0;
565}
566
567/*
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
572 *
573 * Returns 0 on success, or a negative error code on error.
574 */
575static int check_numeric_sensor_wobj(const union acpi_object *wobj,
576 u8 *out_size, bool *out_is_new)
577{
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;
582 u32 elem_count;
583 int last_prop;
584 bool is_new;
585 u8 count;
586 u32 j;
587 u32 i;
588
589 if (type != ACPI_TYPE_PACKAGE)
590 return -EINVAL;
591
592 /*
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.
597 */
598 elements = wobj->package.elements;
599
600 elem_count = wobj->package.count;
601 if (elem_count <= HP_WMI_PROPERTY_SIZE ||
602 elem_count > HP_WMI_MAX_PROPERTIES)
603 return -EINVAL;
604
605 type = elements[HP_WMI_PROPERTY_SIZE].type;
606 switch (type) {
607 case ACPI_TYPE_INTEGER:
608 is_new = true;
609 last_prop = HP_WMI_PROPERTY_RATE_UNITS;
610 break;
611
612 case ACPI_TYPE_STRING:
613 is_new = false;
614 last_prop = HP_WMI_PROPERTY_CURRENT_READING;
615 break;
616
617 default:
618 return -EINVAL;
619 }
620
621 /*
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.
625 */
626 if (elem_count < last_prop - !is_new + 1)
627 return -EINVAL;
628
629 count = elem_count - (last_prop - !is_new);
630
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)
635 return -EINVAL;
636
637 switch (prop) {
638 case HP_WMI_PROPERTY_OPERATIONAL_STATUS:
639 /* Old variant: CurrentState follows OperationalStatus. */
640 if (!is_new)
641 prop = HP_WMI_PROPERTY_CURRENT_STATE - 1;
642 break;
643
644 case HP_WMI_PROPERTY_SIZE:
645 /* New variant: Size == count of PossibleStates[]. */
646 if (count != elements[i].integer.value)
647 return -EINVAL;
648 break;
649
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)
655 return -EINVAL;
656 }
657
658 /* Old variant: BaseUnits follows PossibleStates[]. */
659 if (!is_new)
660 prop = HP_WMI_PROPERTY_BASE_UNITS - 1;
661 break;
662
663 case HP_WMI_PROPERTY_CURRENT_STATE:
664 /* Old variant: PossibleStates[] follows CurrentState. */
665 if (!is_new)
666 prop = HP_WMI_PROPERTY_POSSIBLE_STATES - 1;
667 break;
668 }
669 }
670
671 if (prop != last_prop + 1)
672 return -EINVAL;
673
674 *out_size = count;
675 *out_is_new = is_new;
676
677 return 0;
678}
679
680static int
681numeric_sensor_is_connected(const struct hp_wmi_numeric_sensor *nsensor)
682{
683 u32 operational_status = nsensor->operational_status;
684
685 return operational_status != HP_WMI_STATUS_NO_CONTACT;
686}
687
688static int numeric_sensor_has_fault(const struct hp_wmi_numeric_sensor *nsensor)
689{
690 u32 operational_status = nsensor->operational_status;
691
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:
702
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:
706 return true;
707 }
708
709 return false;
710}
711
712/* scale_numeric_sensor - scale sensor reading for hwmon */
713static long scale_numeric_sensor(const struct hp_wmi_numeric_sensor *nsensor)
714{
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;
719 s32 target_modifier;
720 long val;
721
722 /* Fan readings are in RPM units; others are in milliunits. */
723 target_modifier = sensor_type == HP_WMI_TYPE_AIR_FLOW ? 0 : -3;
724
725 val = current_reading;
726
727 for (; unit_modifier < target_modifier; unit_modifier++)
728 val = DIV_ROUND_CLOSEST(val, 10);
729
730 for (; unit_modifier > target_modifier; unit_modifier--) {
731 if (val > LONG_MAX / 10) {
732 val = LONG_MAX;
733 break;
734 }
735 val *= 10;
736 }
737
738 if (sensor_type == HP_WMI_TYPE_TEMPERATURE) {
739 switch (base_units) {
740 case HP_WMI_UNITS_DEGREES_F:
741 val -= MILLI * 32;
742 val = val <= LONG_MAX / 5 ?
743 DIV_ROUND_CLOSEST(val * 5, 9) :
744 DIV_ROUND_CLOSEST(val, 9) * 5;
745 break;
746
747 case HP_WMI_UNITS_DEGREES_K:
748 val = milli_kelvin_to_millicelsius(t: val);
749 break;
750 }
751 }
752
753 return val;
754}
755
756/*
757 * classify_numeric_sensor - classify a numeric sensor
758 * @nsensor: pointer to numeric sensor struct
759 *
760 * Returns an enum hp_wmi_type value on success,
761 * or a negative value if the sensor type is unsupported.
762 */
763static int classify_numeric_sensor(const struct hp_wmi_numeric_sensor *nsensor)
764{
765 u32 sensor_type = nsensor->sensor_type;
766 u32 base_units = nsensor->base_units;
767 const char *name = nsensor->name;
768
769 switch (sensor_type) {
770 case HP_WMI_TYPE_TEMPERATURE:
771 /*
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.
779 */
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;
786 break;
787
788 case HP_WMI_TYPE_VOLTAGE:
789 if (base_units == HP_WMI_UNITS_VOLTS)
790 return HP_WMI_TYPE_VOLTAGE;
791 break;
792
793 case HP_WMI_TYPE_CURRENT:
794 if (base_units == HP_WMI_UNITS_AMPS)
795 return HP_WMI_TYPE_CURRENT;
796 break;
797
798 case HP_WMI_TYPE_AIR_FLOW:
799 /*
800 * Strangely, HP considers fan RPM sensor type to be
801 * "Air Flow" instead of the more intuitive "Tachometer".
802 */
803 if (base_units == HP_WMI_UNITS_RPM)
804 return HP_WMI_TYPE_AIR_FLOW;
805 break;
806 }
807
808 return -EINVAL;
809}
810
811static int
812populate_numeric_sensor_from_wobj(struct device *dev,
813 struct hp_wmi_numeric_sensor *nsensor,
814 union acpi_object *wobj, bool *out_is_new)
815{
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;
821 char *string;
822 bool is_new;
823 u32 value;
824 u8 size;
825 int err;
826
827 err = check_numeric_sensor_wobj(wobj, out_size: &size, out_is_new: &is_new);
828 if (err)
829 return err;
830
831 possible_states = devm_kcalloc(dev, n: size, size: sizeof(*possible_states),
832 GFP_KERNEL);
833 if (!possible_states)
834 return -ENOMEM;
835
836 element = wobj->package.elements;
837 nsensor->possible_states = possible_states;
838 nsensor->size = size;
839
840 if (!is_new)
841 last_prop = HP_WMI_PROPERTY_CURRENT_READING;
842
843 for (; prop <= last_prop; prop++) {
844 type = hp_wmi_property_map[prop];
845
846 err = extract_acpi_value(dev, element, type, out_value: &value, out_string: &string);
847 if (err)
848 return err;
849
850 element++;
851
852 switch (prop) {
853 case HP_WMI_PROPERTY_NAME:
854 nsensor->name = string;
855 break;
856
857 case HP_WMI_PROPERTY_DESCRIPTION:
858 nsensor->description = string;
859 break;
860
861 case HP_WMI_PROPERTY_SENSOR_TYPE:
862 if (value > HP_WMI_TYPE_AIR_FLOW)
863 return -EINVAL;
864
865 nsensor->sensor_type = value;
866 break;
867
868 case HP_WMI_PROPERTY_OTHER_SENSOR_TYPE:
869 nsensor->other_sensor_type = string;
870 break;
871
872 case HP_WMI_PROPERTY_OPERATIONAL_STATUS:
873 nsensor->operational_status = value;
874
875 /* Old variant: CurrentState follows OperationalStatus. */
876 if (!is_new)
877 prop = HP_WMI_PROPERTY_CURRENT_STATE - 1;
878 break;
879
880 case HP_WMI_PROPERTY_SIZE:
881 break; /* Already set. */
882
883 case HP_WMI_PROPERTY_POSSIBLE_STATES:
884 *possible_states++ = string;
885 if (--size)
886 prop--;
887
888 /* Old variant: BaseUnits follows PossibleStates[]. */
889 if (!is_new && !size)
890 prop = HP_WMI_PROPERTY_BASE_UNITS - 1;
891 break;
892
893 case HP_WMI_PROPERTY_CURRENT_STATE:
894 nsensor->current_state = string;
895
896 /* Old variant: PossibleStates[] follows CurrentState. */
897 if (!is_new)
898 prop = HP_WMI_PROPERTY_POSSIBLE_STATES - 1;
899 break;
900
901 case HP_WMI_PROPERTY_BASE_UNITS:
902 nsensor->base_units = value;
903 break;
904
905 case HP_WMI_PROPERTY_UNIT_MODIFIER:
906 /* UnitModifier is signed. */
907 nsensor->unit_modifier = (s32)value;
908 break;
909
910 case HP_WMI_PROPERTY_CURRENT_READING:
911 nsensor->current_reading = value;
912 break;
913
914 case HP_WMI_PROPERTY_RATE_UNITS:
915 nsensor->rate_units = value;
916 break;
917
918 default:
919 return -EINVAL;
920 }
921 }
922
923 *out_is_new = is_new;
924
925 return 0;
926}
927
928/* update_numeric_sensor_from_wobj - update fungible sensor properties */
929static void
930update_numeric_sensor_from_wobj(struct device *dev,
931 struct hp_wmi_numeric_sensor *nsensor,
932 const union acpi_object *wobj)
933{
934 const union acpi_object *elements;
935 const union acpi_object *element;
936 const char *new_string;
937 char *trimmed;
938 char *string;
939 bool is_new;
940 int offset;
941 u8 size;
942 int err;
943
944 err = check_numeric_sensor_wobj(wobj, out_size: &size, out_is_new: &is_new);
945 if (err)
946 return;
947
948 elements = wobj->package.elements;
949
950 element = &elements[HP_WMI_PROPERTY_OPERATIONAL_STATUS];
951 nsensor->operational_status = element->integer.value;
952
953 /*
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.
958 */
959 offset = is_new ? size - 1 : -2;
960
961 element = &elements[HP_WMI_PROPERTY_CURRENT_STATE + offset];
962 string = element->type == ACPI_TYPE_BUFFER ?
963 convert_raw_wmi_string(buf: element->buffer.pointer) :
964 element->string.pointer;
965
966 if (string) {
967 trimmed = strim(string);
968 if (strcmp(trimmed, nsensor->current_state)) {
969 new_string = hp_wmi_strdup(dev, src: trimmed);
970 if (new_string) {
971 devm_kfree(dev, p: nsensor->current_state);
972 nsensor->current_state = new_string;
973 }
974 }
975 if (element->type == ACPI_TYPE_BUFFER)
976 kfree(objp: string);
977 }
978
979 /* Old variant: -2 (not -1) because it lacks the Size property. */
980 if (!is_new)
981 offset = (int)size - 2; /* size is > 0, i.e. may be 1. */
982
983 element = &elements[HP_WMI_PROPERTY_UNIT_MODIFIER + offset];
984 nsensor->unit_modifier = (s32)element->integer.value;
985
986 element = &elements[HP_WMI_PROPERTY_CURRENT_READING + offset];
987 nsensor->current_reading = element->integer.value;
988}
989
990/*
991 * check_platform_events_wobj - validate a HPBIOS_PlatformEvents instance
992 * @wobj: pointer to WMI object instance to check
993 *
994 * Returns 0 on success, or a negative error code on error.
995 */
996static int check_platform_events_wobj(const union acpi_object *wobj)
997{
998 return check_wobj(wobj, property_map: hp_wmi_platform_events_property_map,
999 last_prop: HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS);
1000}
1001
1002static int
1003populate_platform_events_from_wobj(struct device *dev,
1004 struct hp_wmi_platform_events *pevents,
1005 union acpi_object *wobj)
1006{
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;
1011 char *string;
1012 u32 value;
1013 int err;
1014
1015 err = check_platform_events_wobj(wobj);
1016 if (err)
1017 return err;
1018
1019 element = wobj->package.elements;
1020
1021 for (; prop <= last_prop; prop++, element++) {
1022 type = hp_wmi_platform_events_property_map[prop];
1023
1024 err = extract_acpi_value(dev, element, type, out_value: &value, out_string: &string);
1025 if (err)
1026 return err;
1027
1028 switch (prop) {
1029 case HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME:
1030 pevents->name = string;
1031 break;
1032
1033 case HP_WMI_PLATFORM_EVENTS_PROPERTY_DESCRIPTION:
1034 pevents->description = string;
1035 break;
1036
1037 case HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_NAMESPACE:
1038 if (strcasecmp(HP_WMI_EVENT_NAMESPACE, s2: string))
1039 return -EINVAL;
1040
1041 pevents->source_namespace = string;
1042 break;
1043
1044 case HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_CLASS:
1045 if (strcasecmp(HP_WMI_EVENT_CLASS, s2: string))
1046 return -EINVAL;
1047
1048 pevents->source_class = string;
1049 break;
1050
1051 case HP_WMI_PLATFORM_EVENTS_PROPERTY_CATEGORY:
1052 pevents->category = value;
1053 break;
1054
1055 case HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_SEVERITY:
1056 pevents->possible_severity = value;
1057 break;
1058
1059 case HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS:
1060 pevents->possible_status = value;
1061 break;
1062
1063 default:
1064 return -EINVAL;
1065 }
1066 }
1067
1068 return 0;
1069}
1070
1071/*
1072 * check_event_wobj - validate a HPBIOS_BIOSEvent instance
1073 * @wobj: pointer to WMI object instance to check
1074 *
1075 * Returns 0 on success, or a negative error code on error.
1076 */
1077static int check_event_wobj(const union acpi_object *wobj)
1078{
1079 return check_wobj(wobj, property_map: hp_wmi_event_property_map,
1080 last_prop: HP_WMI_EVENT_PROPERTY_STATUS);
1081}
1082
1083static int populate_event_from_wobj(struct device *dev,
1084 struct hp_wmi_event *event,
1085 union acpi_object *wobj)
1086{
1087 int prop = HP_WMI_EVENT_PROPERTY_NAME;
1088 union acpi_object *element;
1089 acpi_object_type type;
1090 char *string;
1091 u32 value;
1092 int err;
1093
1094 err = check_event_wobj(wobj);
1095 if (err)
1096 return err;
1097
1098 element = wobj->package.elements;
1099
1100 for (; prop <= HP_WMI_EVENT_PROPERTY_CATEGORY; prop++, element++) {
1101 type = hp_wmi_event_property_map[prop];
1102
1103 err = extract_acpi_value(dev, element, type, out_value: &value, out_string: &string);
1104 if (err)
1105 return err;
1106
1107 switch (prop) {
1108 case HP_WMI_EVENT_PROPERTY_NAME:
1109 event->name = string;
1110 break;
1111
1112 case HP_WMI_EVENT_PROPERTY_DESCRIPTION:
1113 event->description = string;
1114 break;
1115
1116 case HP_WMI_EVENT_PROPERTY_CATEGORY:
1117 event->category = value;
1118 break;
1119
1120 default:
1121 return -EINVAL;
1122 }
1123 }
1124
1125 return 0;
1126}
1127
1128/*
1129 * classify_event - classify an event
1130 * @name: event name
1131 * @category: event category
1132 *
1133 * Classify instances of both HPBIOS_PlatformEvents and HPBIOS_BIOSEvent from
1134 * property values. Recognition criteria are based on multiple ACPI dumps [3].
1135 *
1136 * Returns an enum hp_wmi_type value on success,
1137 * or a negative value if the event type is unsupported.
1138 */
1139static int classify_event(const char *event_name, u32 category)
1140{
1141 if (category != HP_WMI_CATEGORY_SENSOR)
1142 return -EINVAL;
1143
1144 /* Fan events have Name "X Stall". */
1145 if (strstr(event_name, HP_WMI_PATTERN_FAN_ALARM))
1146 return HP_WMI_TYPE_AIR_FLOW;
1147
1148 /* Intrusion events have Name "Hood Intrusion". */
1149 if (!strcmp(event_name, HP_WMI_PATTERN_INTRUSION_ALARM))
1150 return HP_WMI_TYPE_INTRUSION;
1151
1152 /*
1153 * Temperature events have Name either "Thermal Caution" or
1154 * "Thermal Critical". Deal only with "Thermal Critical" events.
1155 *
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.
1160 */
1161 if (!strcmp(event_name, HP_WMI_PATTERN_TEMP_ALARM))
1162 return HP_WMI_TYPE_TEMPERATURE;
1163
1164 return -EINVAL;
1165}
1166
1167/*
1168 * interpret_info - interpret sensor for hwmon
1169 * @info: pointer to sensor info struct
1170 *
1171 * Should be called after the numeric sensor member has been updated.
1172 */
1173static void interpret_info(struct hp_wmi_info *info)
1174{
1175 const struct hp_wmi_numeric_sensor *nsensor = &info->nsensor;
1176
1177 info->cached_val = scale_numeric_sensor(nsensor);
1178 info->last_updated = jiffies;
1179}
1180
1181/*
1182 * hp_wmi_update_info - poll WMI to update sensor info
1183 * @state: pointer to driver state
1184 * @info: pointer to sensor info struct
1185 *
1186 * Returns 0 on success, or a negative error code on error.
1187 */
1188static int hp_wmi_update_info(struct hp_wmi_sensors *state,
1189 struct hp_wmi_info *info)
1190{
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;
1195 int ret = 0;
1196
1197 if (time_after(jiffies, info->last_updated + HZ)) {
1198 mutex_lock(&state->lock);
1199
1200 wobj = hp_wmi_get_wobj(HP_WMI_NUMERIC_SENSOR_GUID, instance);
1201 if (!wobj) {
1202 ret = -EIO;
1203 goto out_unlock;
1204 }
1205
1206 update_numeric_sensor_from_wobj(dev, nsensor, wobj);
1207
1208 interpret_info(info);
1209
1210 kfree(objp: wobj);
1211
1212out_unlock:
1213 mutex_unlock(lock: &state->lock);
1214 }
1215
1216 return ret;
1217}
1218
1219static int basic_string_show(struct seq_file *seqf, void *ignored)
1220{
1221 const char *str = seqf->private;
1222
1223 seq_printf(m: seqf, fmt: "%s\n", str);
1224
1225 return 0;
1226}
1227DEFINE_SHOW_ATTRIBUTE(basic_string);
1228
1229static int fungible_show(struct seq_file *seqf, enum hp_wmi_property prop)
1230{
1231 struct hp_wmi_numeric_sensor *nsensor;
1232 struct hp_wmi_sensors *state;
1233 struct hp_wmi_info *info;
1234 int err;
1235
1236 info = seqf->private;
1237 state = info->state;
1238 nsensor = &info->nsensor;
1239
1240 err = hp_wmi_update_info(state, info);
1241 if (err)
1242 return err;
1243
1244 switch (prop) {
1245 case HP_WMI_PROPERTY_OPERATIONAL_STATUS:
1246 seq_printf(m: seqf, fmt: "%u\n", nsensor->operational_status);
1247 break;
1248
1249 case HP_WMI_PROPERTY_CURRENT_STATE:
1250 seq_printf(m: seqf, fmt: "%s\n", nsensor->current_state);
1251 break;
1252
1253 case HP_WMI_PROPERTY_UNIT_MODIFIER:
1254 seq_printf(m: seqf, fmt: "%d\n", nsensor->unit_modifier);
1255 break;
1256
1257 case HP_WMI_PROPERTY_CURRENT_READING:
1258 seq_printf(m: seqf, fmt: "%u\n", nsensor->current_reading);
1259 break;
1260
1261 default:
1262 return -EOPNOTSUPP;
1263 }
1264
1265 return 0;
1266}
1267
1268static int operational_status_show(struct seq_file *seqf, void *ignored)
1269{
1270 return fungible_show(seqf, prop: HP_WMI_PROPERTY_OPERATIONAL_STATUS);
1271}
1272DEFINE_SHOW_ATTRIBUTE(operational_status);
1273
1274static int current_state_show(struct seq_file *seqf, void *ignored)
1275{
1276 return fungible_show(seqf, prop: HP_WMI_PROPERTY_CURRENT_STATE);
1277}
1278DEFINE_SHOW_ATTRIBUTE(current_state);
1279
1280static int possible_states_show(struct seq_file *seqf, void *ignored)
1281{
1282 struct hp_wmi_numeric_sensor *nsensor = seqf->private;
1283 u8 i;
1284
1285 for (i = 0; i < nsensor->size; i++)
1286 seq_printf(m: seqf, fmt: "%s%s", i ? "," : "",
1287 nsensor->possible_states[i]);
1288
1289 seq_puts(m: seqf, s: "\n");
1290
1291 return 0;
1292}
1293DEFINE_SHOW_ATTRIBUTE(possible_states);
1294
1295static int unit_modifier_show(struct seq_file *seqf, void *ignored)
1296{
1297 return fungible_show(seqf, prop: HP_WMI_PROPERTY_UNIT_MODIFIER);
1298}
1299DEFINE_SHOW_ATTRIBUTE(unit_modifier);
1300
1301static int current_reading_show(struct seq_file *seqf, void *ignored)
1302{
1303 return fungible_show(seqf, prop: HP_WMI_PROPERTY_CURRENT_READING);
1304}
1305DEFINE_SHOW_ATTRIBUTE(current_reading);
1306
1307/* hp_wmi_devm_debugfs_remove - devm callback for debugfs cleanup */
1308static void hp_wmi_devm_debugfs_remove(void *res)
1309{
1310 debugfs_remove_recursive(dentry: res);
1311}
1312
1313/* hp_wmi_debugfs_init - create and populate debugfs directory tree */
1314static 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)
1317{
1318 struct hp_wmi_numeric_sensor *nsensor;
1319 char buf[HP_WMI_MAX_STR_SIZE];
1320 struct dentry *debugfs;
1321 struct dentry *entries;
1322 struct dentry *dir;
1323 int err;
1324 u8 i;
1325
1326 /* dev_name() gives a not-very-friendly GUID for WMI devices. */
1327 scnprintf(buf, size: sizeof(buf), fmt: "hp-wmi-sensors-%u", dev->id);
1328
1329 debugfs = debugfs_create_dir(name: buf, NULL);
1330 if (IS_ERR(ptr: debugfs))
1331 return;
1332
1333 err = devm_add_action_or_reset(dev, hp_wmi_devm_debugfs_remove,
1334 debugfs);
1335 if (err)
1336 return;
1337
1338 entries = debugfs_create_dir(name: "sensor", parent: debugfs);
1339
1340 for (i = 0; i < icount; i++, info++) {
1341 nsensor = &info->nsensor;
1342
1343 scnprintf(buf, size: sizeof(buf), fmt: "%u", i);
1344 dir = debugfs_create_dir(name: buf, parent: entries);
1345
1346 debugfs_create_file(name: "name", mode: 0444, parent: dir,
1347 data: (void *)nsensor->name,
1348 fops: &basic_string_fops);
1349
1350 debugfs_create_file(name: "description", mode: 0444, parent: dir,
1351 data: (void *)nsensor->description,
1352 fops: &basic_string_fops);
1353
1354 debugfs_create_u32(name: "sensor_type", mode: 0444, parent: dir,
1355 value: &nsensor->sensor_type);
1356
1357 debugfs_create_file(name: "other_sensor_type", mode: 0444, parent: dir,
1358 data: (void *)nsensor->other_sensor_type,
1359 fops: &basic_string_fops);
1360
1361 debugfs_create_file(name: "operational_status", mode: 0444, parent: dir,
1362 data: info, fops: &operational_status_fops);
1363
1364 debugfs_create_file(name: "possible_states", mode: 0444, parent: dir,
1365 data: nsensor, fops: &possible_states_fops);
1366
1367 debugfs_create_file(name: "current_state", mode: 0444, parent: dir,
1368 data: info, fops: &current_state_fops);
1369
1370 debugfs_create_u32(name: "base_units", mode: 0444, parent: dir,
1371 value: &nsensor->base_units);
1372
1373 debugfs_create_file(name: "unit_modifier", mode: 0444, parent: dir,
1374 data: info, fops: &unit_modifier_fops);
1375
1376 debugfs_create_file(name: "current_reading", mode: 0444, parent: dir,
1377 data: info, fops: &current_reading_fops);
1378
1379 if (is_new)
1380 debugfs_create_u32(name: "rate_units", mode: 0444, parent: dir,
1381 value: &nsensor->rate_units);
1382 }
1383
1384 if (!pcount)
1385 return;
1386
1387 entries = debugfs_create_dir(name: "platform_events", parent: debugfs);
1388
1389 for (i = 0; i < pcount; i++, pevents++) {
1390 scnprintf(buf, size: sizeof(buf), fmt: "%u", i);
1391 dir = debugfs_create_dir(name: buf, parent: entries);
1392
1393 debugfs_create_file(name: "name", mode: 0444, parent: dir,
1394 data: (void *)pevents->name,
1395 fops: &basic_string_fops);
1396
1397 debugfs_create_file(name: "description", mode: 0444, parent: dir,
1398 data: (void *)pevents->description,
1399 fops: &basic_string_fops);
1400
1401 debugfs_create_file(name: "source_namespace", mode: 0444, parent: dir,
1402 data: (void *)pevents->source_namespace,
1403 fops: &basic_string_fops);
1404
1405 debugfs_create_file(name: "source_class", mode: 0444, parent: dir,
1406 data: (void *)pevents->source_class,
1407 fops: &basic_string_fops);
1408
1409 debugfs_create_u32(name: "category", mode: 0444, parent: dir,
1410 value: &pevents->category);
1411
1412 debugfs_create_u32(name: "possible_severity", mode: 0444, parent: dir,
1413 value: &pevents->possible_severity);
1414
1415 debugfs_create_u32(name: "possible_status", mode: 0444, parent: dir,
1416 value: &pevents->possible_status);
1417 }
1418}
1419
1420static umode_t hp_wmi_hwmon_is_visible(const void *drvdata,
1421 enum hwmon_sensor_types type,
1422 u32 attr, int channel)
1423{
1424 const struct hp_wmi_sensors *state = drvdata;
1425 const struct hp_wmi_info *info;
1426
1427 if (type == hwmon_intrusion)
1428 return state->has_intrusion ? 0644 : 0;
1429
1430 if (!state->info_map[type] || !state->info_map[type][channel])
1431 return 0;
1432
1433 info = state->info_map[type][channel];
1434
1435 if ((type == hwmon_temp && attr == hwmon_temp_alarm) ||
1436 (type == hwmon_fan && attr == hwmon_fan_alarm))
1437 return info->has_alarm ? 0444 : 0;
1438
1439 return 0444;
1440}
1441
1442static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
1443 u32 attr, int channel, long *out_val)
1444{
1445 struct hp_wmi_sensors *state = dev_get_drvdata(dev);
1446 const struct hp_wmi_numeric_sensor *nsensor;
1447 struct hp_wmi_info *info;
1448 int err;
1449
1450 if (type == hwmon_intrusion) {
1451 *out_val = state->intrusion ? 1 : 0;
1452
1453 return 0;
1454 }
1455
1456 info = state->info_map[type][channel];
1457
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;
1462
1463 return 0;
1464 }
1465
1466 nsensor = &info->nsensor;
1467
1468 err = hp_wmi_update_info(state, info);
1469 if (err)
1470 return err;
1471
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);
1475 else
1476 *out_val = info->cached_val;
1477
1478 return 0;
1479}
1480
1481static int hp_wmi_hwmon_read_string(struct device *dev,
1482 enum hwmon_sensor_types type, u32 attr,
1483 int channel, const char **out_str)
1484{
1485 const struct hp_wmi_sensors *state = dev_get_drvdata(dev);
1486 const struct hp_wmi_info *info;
1487
1488 info = state->info_map[type][channel];
1489 *out_str = info->nsensor.name;
1490
1491 return 0;
1492}
1493
1494static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
1495 u32 attr, int channel, long val)
1496{
1497 struct hp_wmi_sensors *state = dev_get_drvdata(dev);
1498
1499 if (val)
1500 return -EINVAL;
1501
1502 mutex_lock(&state->lock);
1503
1504 state->intrusion = false;
1505
1506 mutex_unlock(lock: &state->lock);
1507
1508 return 0;
1509}
1510
1511static 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,
1516};
1517
1518static struct hwmon_chip_info hp_wmi_chip_info = {
1519 .ops = &hp_wmi_hwmon_ops,
1520 .info = NULL,
1521};
1522
1523static struct hp_wmi_info *match_fan_event(struct hp_wmi_sensors *state,
1524 const char *event_description)
1525{
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;
1529 const char *name;
1530 u8 i;
1531
1532 /* Fan event has Description "X Speed". Sensor has Name "X[ Speed]". */
1533
1534 for (i = 0; i < fan_count; i++, ptr_info++) {
1535 info = *ptr_info;
1536 name = info->nsensor.name;
1537
1538 if (strstr(event_description, name))
1539 return info;
1540 }
1541
1542 return NULL;
1543}
1544
1545static u8 match_temp_events(struct hp_wmi_sensors *state,
1546 const char *event_description,
1547 struct hp_wmi_info *temp_info[])
1548{
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;
1552 const char *name;
1553 u8 count = 0;
1554 bool is_cpu;
1555 bool is_sys;
1556 u8 i;
1557
1558 /* Description is either "CPU Thermal Index" or "Chassis Thermal Index". */
1559
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)
1563 return 0;
1564
1565 /*
1566 * CPU event: Match one sensor with Name either "CPU Thermal Index" or
1567 * "CPU Temperature", or multiple with Name(s) "CPU[#] Temperature".
1568 *
1569 * Chassis event: Match one sensor with Name either
1570 * "Chassis Thermal Index" or "System Ambient Temperature".
1571 */
1572
1573 for (i = 0; i < temp_count; i++, ptr_info++) {
1574 info = *ptr_info;
1575 name = info->nsensor.name;
1576
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;
1582 return 1;
1583 }
1584
1585 if (is_cpu && (strstr(name, HP_WMI_PATTERN_CPU) &&
1586 strstr(name, HP_WMI_PATTERN_TEMP)))
1587 temp_info[count++] = info;
1588 }
1589
1590 return count;
1591}
1592
1593/* hp_wmi_devm_debugfs_remove - devm callback for WMI event handler removal */
1594static void hp_wmi_devm_notify_remove(void *ignored)
1595{
1596 wmi_remove_notify_handler(HP_WMI_EVENT_GUID);
1597}
1598
1599/* hp_wmi_notify - WMI event notification handler */
1600static void hp_wmi_notify(u32 value, void *context)
1601{
1602 struct hp_wmi_info *temp_info[HP_WMI_MAX_INSTANCES] = {};
1603 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
1604 struct hp_wmi_sensors *state = context;
1605 struct device *dev = &state->wdev->dev;
1606 struct hp_wmi_event event = {};
1607 struct hp_wmi_info *fan_info;
1608 union acpi_object *wobj;
1609 acpi_status err;
1610 int event_type;
1611 u8 count;
1612
1613 /*
1614 * The following warning may occur in the kernel log:
1615 *
1616 * ACPI Warning: \_SB.WMID._WED: Return type mismatch -
1617 * found Package, expected Integer/String/Buffer
1618 *
1619 * After using [4] to decode BMOF blobs found in [3], careless copying
1620 * of BIOS code seems the most likely explanation for this warning.
1621 * HP_WMI_EVENT_GUID refers to \\.\root\WMI\HPBIOS_BIOSEvent on
1622 * business-class systems, but it refers to \\.\root\WMI\hpqBEvnt on
1623 * non-business-class systems. Per the existing hp-wmi driver, it
1624 * looks like an instance of hpqBEvnt delivered as event data may
1625 * indeed take the form of a raw ACPI_BUFFER on non-business-class
1626 * systems ("may" because ASL shows some BIOSes do strange things).
1627 *
1628 * In any case, we can ignore this warning, because we always validate
1629 * the event data to ensure it is an ACPI_PACKAGE containing a
1630 * HPBIOS_BIOSEvent instance.
1631 */
1632
1633 mutex_lock(&state->lock);
1634
1635 err = wmi_get_event_data(event: value, out: &out);
1636 if (ACPI_FAILURE(err))
1637 goto out_unlock;
1638
1639 wobj = out.pointer;
1640
1641 err = populate_event_from_wobj(dev, event: &event, wobj);
1642 if (err) {
1643 dev_warn(dev, "Bad event data (ACPI type %d)\n", wobj->type);
1644 goto out_free_wobj;
1645 }
1646
1647 event_type = classify_event(event_name: event.name, category: event.category);
1648 switch (event_type) {
1649 case HP_WMI_TYPE_AIR_FLOW:
1650 fan_info = match_fan_event(state, event_description: event.description);
1651 if (fan_info)
1652 fan_info->alarm = true;
1653 break;
1654
1655 case HP_WMI_TYPE_INTRUSION:
1656 state->intrusion = true;
1657 break;
1658
1659 case HP_WMI_TYPE_TEMPERATURE:
1660 count = match_temp_events(state, event_description: event.description, temp_info);
1661 while (count)
1662 temp_info[--count]->alarm = true;
1663 break;
1664
1665 default:
1666 break;
1667 }
1668
1669out_free_wobj:
1670 kfree(objp: wobj);
1671
1672 devm_kfree(dev, p: event.name);
1673 devm_kfree(dev, p: event.description);
1674
1675out_unlock:
1676 mutex_unlock(lock: &state->lock);
1677}
1678
1679static int init_platform_events(struct device *dev,
1680 struct hp_wmi_platform_events **out_pevents,
1681 u8 *out_pcount)
1682{
1683 struct hp_wmi_platform_events *pevents_arr;
1684 struct hp_wmi_platform_events *pevents;
1685 union acpi_object *wobj;
1686 u8 count;
1687 int err;
1688 u8 i;
1689
1690 count = hp_wmi_wobj_instance_count(HP_WMI_PLATFORM_EVENTS_GUID);
1691 if (!count) {
1692 *out_pcount = 0;
1693
1694 dev_dbg(dev, "No platform events\n");
1695
1696 return 0;
1697 }
1698
1699 pevents_arr = devm_kcalloc(dev, n: count, size: sizeof(*pevents), GFP_KERNEL);
1700 if (!pevents_arr)
1701 return -ENOMEM;
1702
1703 for (i = 0, pevents = pevents_arr; i < count; i++, pevents++) {
1704 wobj = hp_wmi_get_wobj(HP_WMI_PLATFORM_EVENTS_GUID, instance: i);
1705 if (!wobj)
1706 return -EIO;
1707
1708 err = populate_platform_events_from_wobj(dev, pevents, wobj);
1709
1710 kfree(objp: wobj);
1711
1712 if (err)
1713 return err;
1714 }
1715
1716 *out_pevents = pevents_arr;
1717 *out_pcount = count;
1718
1719 dev_dbg(dev, "Found %u platform events\n", count);
1720
1721 return 0;
1722}
1723
1724static int init_numeric_sensors(struct hp_wmi_sensors *state,
1725 struct hp_wmi_info *connected[],
1726 struct hp_wmi_info **out_info,
1727 u8 *out_icount, u8 *out_count,
1728 bool *out_is_new)
1729{
1730 struct hp_wmi_info ***info_map = state->info_map;
1731 u8 *channel_count = state->channel_count;
1732 struct device *dev = &state->wdev->dev;
1733 struct hp_wmi_numeric_sensor *nsensor;
1734 u8 channel_index[hwmon_max] = {};
1735 enum hwmon_sensor_types type;
1736 struct hp_wmi_info *info_arr;
1737 struct hp_wmi_info *info;
1738 union acpi_object *wobj;
1739 u8 count = 0;
1740 bool is_new;
1741 u8 icount;
1742 int wtype;
1743 int err;
1744 u8 c;
1745 u8 i;
1746
1747 icount = hp_wmi_wobj_instance_count(HP_WMI_NUMERIC_SENSOR_GUID);
1748 if (!icount)
1749 return -ENODATA;
1750
1751 info_arr = devm_kcalloc(dev, n: icount, size: sizeof(*info), GFP_KERNEL);
1752 if (!info_arr)
1753 return -ENOMEM;
1754
1755 for (i = 0, info = info_arr; i < icount; i++, info++) {
1756 wobj = hp_wmi_get_wobj(HP_WMI_NUMERIC_SENSOR_GUID, instance: i);
1757 if (!wobj)
1758 return -EIO;
1759
1760 info->instance = i;
1761 info->state = state;
1762 nsensor = &info->nsensor;
1763
1764 err = populate_numeric_sensor_from_wobj(dev, nsensor, wobj,
1765 out_is_new: &is_new);
1766
1767 kfree(objp: wobj);
1768
1769 if (err)
1770 return err;
1771
1772 if (!numeric_sensor_is_connected(nsensor))
1773 continue;
1774
1775 wtype = classify_numeric_sensor(nsensor);
1776 if (wtype < 0)
1777 continue;
1778
1779 type = hp_wmi_hwmon_type_map[wtype];
1780
1781 channel_count[type]++;
1782
1783 info->type = type;
1784
1785 interpret_info(info);
1786
1787 connected[count++] = info;
1788 }
1789
1790 dev_dbg(dev, "Found %u sensors (%u connected)\n", i, count);
1791
1792 for (i = 0; i < count; i++) {
1793 info = connected[i];
1794 type = info->type;
1795 c = channel_index[type]++;
1796
1797 if (!info_map[type]) {
1798 info_map[type] = devm_kcalloc(dev, n: channel_count[type],
1799 size: sizeof(*info_map),
1800 GFP_KERNEL);
1801 if (!info_map[type])
1802 return -ENOMEM;
1803 }
1804
1805 info_map[type][c] = info;
1806 }
1807
1808 *out_info = info_arr;
1809 *out_icount = icount;
1810 *out_count = count;
1811 *out_is_new = is_new;
1812
1813 return 0;
1814}
1815
1816static bool find_event_attributes(struct hp_wmi_sensors *state,
1817 struct hp_wmi_platform_events *pevents,
1818 u8 pevents_count)
1819{
1820 /*
1821 * The existence of this HPBIOS_PlatformEvents instance:
1822 *
1823 * {
1824 * Name = "Rear Chassis Fan0 Stall";
1825 * Description = "Rear Chassis Fan0 Speed";
1826 * Category = 3; // "Sensor"
1827 * PossibleSeverity = 25; // "Critical Failure"
1828 * PossibleStatus = 5; // "Predictive Failure"
1829 * [...]
1830 * }
1831 *
1832 * means that this HPBIOS_BIOSEvent instance may occur:
1833 *
1834 * {
1835 * Name = "Rear Chassis Fan0 Stall";
1836 * Description = "Rear Chassis Fan0 Speed";
1837 * Category = 3; // "Sensor"
1838 * Severity = 25; // "Critical Failure"
1839 * Status = 5; // "Predictive Failure"
1840 * }
1841 *
1842 * After the event occurs (e.g. because the fan was unplugged),
1843 * polling the related HPBIOS_BIOSNumericSensor instance gives:
1844 *
1845 * {
1846 * Name = "Rear Chassis Fan0";
1847 * Description = "Reports rear chassis fan0 speed";
1848 * OperationalStatus = 5; // "Predictive Failure", was 3 ("OK")
1849 * CurrentReading = 0;
1850 * [...]
1851 * }
1852 *
1853 * In this example, the hwmon fan channel for "Rear Chassis Fan0"
1854 * should support the alarm flag and have it be set if the related
1855 * HPBIOS_BIOSEvent instance occurs.
1856 *
1857 * In addition to fan events, temperature (CPU/chassis) and intrusion
1858 * events are relevant to hwmon [2]. Note that much information in [2]
1859 * is unreliable; it is referenced in addition to ACPI dumps [3] merely
1860 * to support the conclusion that sensor and event names/descriptions
1861 * are systematic enough to allow this driver to match them.
1862 *
1863 * Complications and limitations:
1864 *
1865 * - Strings are freeform and may vary, cf. sensor Name "CPU0 Fan"
1866 * on a Z420 vs. "CPU Fan Speed" on an EliteOne 800 G1.
1867 * - Leading/trailing whitespace is a rare but real possibility [3].
1868 * - The HPBIOS_PlatformEvents object may not exist or its instances
1869 * may show that the system only has e.g. BIOS setting-related
1870 * events (cf. the ProBook 4540s and ProBook 470 G0 [3]).
1871 */
1872
1873 struct hp_wmi_info *temp_info[HP_WMI_MAX_INSTANCES] = {};
1874 const char *event_description;
1875 struct hp_wmi_info *fan_info;
1876 bool has_events = false;
1877 const char *event_name;
1878 u32 event_category;
1879 int event_type;
1880 u8 count;
1881 u8 i;
1882
1883 for (i = 0; i < pevents_count; i++, pevents++) {
1884 event_name = pevents->name;
1885 event_description = pevents->description;
1886 event_category = pevents->category;
1887
1888 event_type = classify_event(event_name, category: event_category);
1889 switch (event_type) {
1890 case HP_WMI_TYPE_AIR_FLOW:
1891 fan_info = match_fan_event(state, event_description);
1892 if (!fan_info)
1893 break;
1894
1895 fan_info->has_alarm = true;
1896 has_events = true;
1897 break;
1898
1899 case HP_WMI_TYPE_INTRUSION:
1900 state->has_intrusion = true;
1901 has_events = true;
1902 break;
1903
1904 case HP_WMI_TYPE_TEMPERATURE:
1905 count = match_temp_events(state, event_description,
1906 temp_info);
1907 if (!count)
1908 break;
1909
1910 while (count)
1911 temp_info[--count]->has_alarm = true;
1912 has_events = true;
1913 break;
1914
1915 default:
1916 break;
1917 }
1918 }
1919
1920 return has_events;
1921}
1922
1923static int make_chip_info(struct hp_wmi_sensors *state, bool has_events)
1924{
1925 const struct hwmon_channel_info **ptr_channel_info;
1926 struct hp_wmi_info ***info_map = state->info_map;
1927 u8 *channel_count = state->channel_count;
1928 struct hwmon_channel_info *channel_info;
1929 struct device *dev = &state->wdev->dev;
1930 enum hwmon_sensor_types type;
1931 u8 type_count = 0;
1932 u32 *config;
1933 u32 attr;
1934 u8 count;
1935 u8 i;
1936
1937 if (channel_count[hwmon_temp])
1938 channel_count[hwmon_chip] = 1;
1939
1940 if (has_events && state->has_intrusion)
1941 channel_count[hwmon_intrusion] = 1;
1942
1943 for (type = hwmon_chip; type < hwmon_max; type++)
1944 if (channel_count[type])
1945 type_count++;
1946
1947 channel_info = devm_kcalloc(dev, n: type_count,
1948 size: sizeof(*channel_info), GFP_KERNEL);
1949 if (!channel_info)
1950 return -ENOMEM;
1951
1952 ptr_channel_info = devm_kcalloc(dev, n: type_count + 1,
1953 size: sizeof(*ptr_channel_info), GFP_KERNEL);
1954 if (!ptr_channel_info)
1955 return -ENOMEM;
1956
1957 hp_wmi_chip_info.info = ptr_channel_info;
1958
1959 for (type = hwmon_chip; type < hwmon_max; type++) {
1960 count = channel_count[type];
1961 if (!count)
1962 continue;
1963
1964 config = devm_kcalloc(dev, n: count + 1,
1965 size: sizeof(*config), GFP_KERNEL);
1966 if (!config)
1967 return -ENOMEM;
1968
1969 attr = hp_wmi_hwmon_attributes[type];
1970 channel_info->type = type;
1971 channel_info->config = config;
1972 memset32(s: config, v: attr, n: count);
1973
1974 *ptr_channel_info++ = channel_info++;
1975
1976 if (!has_events || (type != hwmon_temp && type != hwmon_fan))
1977 continue;
1978
1979 attr = type == hwmon_temp ? HWMON_T_ALARM : HWMON_F_ALARM;
1980
1981 for (i = 0; i < count; i++)
1982 if (info_map[type][i]->has_alarm)
1983 config[i] |= attr;
1984 }
1985
1986 return 0;
1987}
1988
1989static bool add_event_handler(struct hp_wmi_sensors *state)
1990{
1991 struct device *dev = &state->wdev->dev;
1992 int err;
1993
1994 err = wmi_install_notify_handler(HP_WMI_EVENT_GUID,
1995 handler: hp_wmi_notify, data: state);
1996 if (err) {
1997 dev_info(dev, "Failed to subscribe to WMI event\n");
1998 return false;
1999 }
2000
2001 err = devm_add_action_or_reset(dev, hp_wmi_devm_notify_remove, NULL);
2002 if (err)
2003 return false;
2004
2005 return true;
2006}
2007
2008static int hp_wmi_sensors_init(struct hp_wmi_sensors *state)
2009{
2010 struct hp_wmi_info *connected[HP_WMI_MAX_INSTANCES];
2011 struct hp_wmi_platform_events *pevents = NULL;
2012 struct device *dev = &state->wdev->dev;
2013 struct hp_wmi_info *info;
2014 struct device *hwdev;
2015 bool has_events;
2016 bool is_new;
2017 u8 icount;
2018 u8 pcount;
2019 u8 count;
2020 int err;
2021
2022 err = init_platform_events(dev, out_pevents: &pevents, out_pcount: &pcount);
2023 if (err)
2024 return err;
2025
2026 err = init_numeric_sensors(state, connected, out_info: &info,
2027 out_icount: &icount, out_count: &count, out_is_new: &is_new);
2028 if (err)
2029 return err;
2030
2031 if (IS_ENABLED(CONFIG_DEBUG_FS))
2032 hp_wmi_debugfs_init(dev, info, pevents, icount, pcount, is_new);
2033
2034 if (!count)
2035 return 0; /* No connected sensors; debugfs only. */
2036
2037 has_events = find_event_attributes(state, pevents, pevents_count: pcount);
2038
2039 /* Survive failure to install WMI event handler. */
2040 if (has_events && !add_event_handler(state))
2041 has_events = false;
2042
2043 err = make_chip_info(state, has_events);
2044 if (err)
2045 return err;
2046
2047 hwdev = devm_hwmon_device_register_with_info(dev, name: "hp_wmi_sensors",
2048 drvdata: state, info: &hp_wmi_chip_info,
2049 NULL);
2050 return PTR_ERR_OR_ZERO(ptr: hwdev);
2051}
2052
2053static int hp_wmi_sensors_probe(struct wmi_device *wdev, const void *context)
2054{
2055 struct device *dev = &wdev->dev;
2056 struct hp_wmi_sensors *state;
2057
2058 state = devm_kzalloc(dev, size: sizeof(*state), GFP_KERNEL);
2059 if (!state)
2060 return -ENOMEM;
2061
2062 state->wdev = wdev;
2063
2064 mutex_init(&state->lock);
2065
2066 dev_set_drvdata(dev, data: state);
2067
2068 return hp_wmi_sensors_init(state);
2069}
2070
2071static const struct wmi_device_id hp_wmi_sensors_id_table[] = {
2072 { HP_WMI_NUMERIC_SENSOR_GUID, NULL },
2073 {},
2074};
2075
2076static struct wmi_driver hp_wmi_sensors_driver = {
2077 .driver = { .name = "hp-wmi-sensors" },
2078 .id_table = hp_wmi_sensors_id_table,
2079 .probe = hp_wmi_sensors_probe,
2080};
2081module_wmi_driver(hp_wmi_sensors_driver);
2082
2083MODULE_AUTHOR("James Seo <james@equiv.tech>");
2084MODULE_DESCRIPTION("HP WMI Sensors driver");
2085MODULE_LICENSE("GPL");
2086

source code of linux/drivers/hwmon/hp-wmi-sensors.c