linux/drivers/hwmon/hp-wmi-sensors.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * hwmon driver for HP (and some HP Compaq) business-class computers that
 * report numeric sensor data via Windows Management Instrumentation (WMI).
 *
 * Copyright (C) 2023 James Seo <[email protected]>
 *
 * References:
 * [1] Hewlett-Packard Development Company, L.P.,
 *     "HP Client Management Interface Technical White Paper", 2005. [Online].
 *     Available: https://h20331.www2.hp.com/hpsub/downloads/cmi_whitepaper.pdf
 * [2] Hewlett-Packard Development Company, L.P.,
 *     "HP Retail Manageability", 2012. [Online].
 *     Available: http://h10032.www1.hp.com/ctg/Manual/c03291135.pdf
 * [3] Linux Hardware Project, A. Ponomarenko et al.,
 *     "linuxhw/ACPI - Collect ACPI table dumps", 2018. [Online].
 *     Available: https://github.com/linuxhw/ACPI
 * [4] P. Rohár, "bmfdec - Decompile binary MOF file (BMF) from WMI buffer",
 *     2017. [Online]. Available: https://github.com/pali/bmfdec
 * [5] Microsoft Corporation, "Driver-Defined WMI Data Items", 2017. [Online].
 *     Available: https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/driver-defined-wmi-data-items
 */

#include <linux/acpi.h>
#include <linux/debugfs.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
#include <linux/mutex.h>
#include <linux/nls.h>
#include <linux/units.h>
#include <linux/wmi.h>

#define HP_WMI_EVENT_NAMESPACE
#define HP_WMI_EVENT_CLASS
#define HP_WMI_EVENT_GUID
#define HP_WMI_NUMERIC_SENSOR_GUID
#define HP_WMI_PLATFORM_EVENTS_GUID

/* Patterns for recognizing sensors and matching events to channels. */

#define HP_WMI_PATTERN_SYS_TEMP
#define HP_WMI_PATTERN_SYS_TEMP2
#define HP_WMI_PATTERN_CPU_TEMP
#define HP_WMI_PATTERN_CPU_TEMP2
#define HP_WMI_PATTERN_TEMP_SENSOR
#define HP_WMI_PATTERN_TEMP_ALARM
#define HP_WMI_PATTERN_INTRUSION_ALARM
#define HP_WMI_PATTERN_FAN_ALARM
#define HP_WMI_PATTERN_TEMP
#define HP_WMI_PATTERN_CPU

/* These limits are arbitrary. The WMI implementation may vary by system. */

#define HP_WMI_MAX_STR_SIZE
#define HP_WMI_MAX_PROPERTIES
#define HP_WMI_MAX_INSTANCES

enum hp_wmi_type {};

enum hp_wmi_category {};

enum hp_wmi_severity {};

enum hp_wmi_status {};

enum hp_wmi_units {};

enum hp_wmi_property {};

static const acpi_object_type hp_wmi_property_map[] =;

enum hp_wmi_platform_events_property {};

static const acpi_object_type hp_wmi_platform_events_property_map[] =;

enum hp_wmi_event_property {};

static const acpi_object_type hp_wmi_event_property_map[] =;

static const enum hwmon_sensor_types hp_wmi_hwmon_type_map[] =;

static const u32 hp_wmi_hwmon_attributes[hwmon_max] =;

/*
 * struct hp_wmi_numeric_sensor - a HPBIOS_BIOSNumericSensor instance
 *
 * Two variants of HPBIOS_BIOSNumericSensor are known. The first is specified
 * in [1] and appears to be much more widespread. The second was discovered by
 * decoding BMOF blobs [4], seems to be found only in some newer ZBook systems
 * [3], and has two new properties and a slightly different property order.
 *
 * These differences don't matter on Windows, where WMI object properties are
 * accessed by name. For us, supporting both variants gets ugly and hacky at
 * times. The fun begins now; this struct is defined as per the new variant.
 *
 * Effective MOF definition:
 *
 *   #pragma namespace("\\\\.\\root\\HP\\InstrumentedBIOS");
 *   class HPBIOS_BIOSNumericSensor {
 *     [read] string Name;
 *     [read] string Description;
 *     [read, ValueMap {"0","1","2","3","4","5","6","7","8","9",
 *      "10","11","12"}, Values {"Unknown","Other","Temperature",
 *      "Voltage","Current","Tachometer","Counter","Switch","Lock",
 *      "Humidity","Smoke Detection","Presence","Air Flow"}]
 *     uint32 SensorType;
 *     [read] string OtherSensorType;
 *     [read, ValueMap {"0","1","2","3","4","5","6","7","8","9",
 *      "10","11","12","13","14","15","16","17","18","..",
 *      "0x8000.."}, Values {"Unknown","Other","OK","Degraded",
 *      "Stressed","Predictive Failure","Error",
 *      "Non-Recoverable Error","Starting","Stopping","Stopped",
 *      "In Service","No Contact","Lost Communication","Aborted",
 *      "Dormant","Supporting Entity in Error","Completed",
 *      "Power Mode","DMTF Reserved","Vendor Reserved"}]
 *     uint32 OperationalStatus;
 *     [read] uint32 Size;
 *     [read] string PossibleStates[];
 *     [read] string CurrentState;
 *     [read, ValueMap {"0","1","2","3","4","5","6","7","8","9",
 *      "10","11","12","13","14","15","16","17","18","19","20",
 *      "21","22","23","24","25","26","27","28","29","30","31",
 *      "32","33","34","35","36","37","38","39","40","41","42",
 *      "43","44","45","46","47","48","49","50","51","52","53",
 *      "54","55","56","57","58","59","60","61","62","63","64",
 *      "65"}, Values {"Unknown","Other","Degrees C","Degrees F",
 *      "Degrees K","Volts","Amps","Watts","Joules","Coulombs",
 *      "VA","Nits","Lumens","Lux","Candelas","kPa","PSI",
 *      "Newtons","CFM","RPM","Hertz","Seconds","Minutes",
 *      "Hours","Days","Weeks","Mils","Inches","Feet",
 *      "Cubic Inches","Cubic Feet","Meters","Cubic Centimeters",
 *      "Cubic Meters","Liters","Fluid Ounces","Radians",
 *      "Steradians","Revolutions","Cycles","Gravities","Ounces",
 *      "Pounds","Foot-Pounds","Ounce-Inches","Gauss","Gilberts",
 *      "Henries","Farads","Ohms","Siemens","Moles","Becquerels",
 *      "PPM (parts/million)","Decibels","DbA","DbC","Grays",
 *      "Sieverts","Color Temperature Degrees K","Bits","Bytes",
 *      "Words (data)","DoubleWords","QuadWords","Percentage"}]
 *     uint32 BaseUnits;
 *     [read] sint32 UnitModifier;
 *     [read] uint32 CurrentReading;
 *     [read] uint32 RateUnits;
 *   };
 *
 * Effective MOF definition of old variant [1] (sans redundant info):
 *
 *   class HPBIOS_BIOSNumericSensor {
 *     [read] string Name;
 *     [read] string Description;
 *     [read] uint32 SensorType;
 *     [read] string OtherSensorType;
 *     [read] uint32 OperationalStatus;
 *     [read] string CurrentState;
 *     [read] string PossibleStates[];
 *     [read] uint32 BaseUnits;
 *     [read] sint32 UnitModifier;
 *     [read] uint32 CurrentReading;
 *   };
 */
struct hp_wmi_numeric_sensor {};

/*
 * struct hp_wmi_platform_events - a HPBIOS_PlatformEvents instance
 *
 * Instances of this object reveal the set of possible HPBIOS_BIOSEvent
 * instances for the current system, but it may not always be present.
 *
 * Effective MOF definition:
 *
 *   #pragma namespace("\\\\.\\root\\HP\\InstrumentedBIOS");
 *   class HPBIOS_PlatformEvents {
 *     [read] string Name;
 *     [read] string Description;
 *     [read] string SourceNamespace;
 *     [read] string SourceClass;
 *     [read, ValueMap {"0","1","2","3","4",".."}, Values {
 *      "Unknown","Configuration Change","Button Pressed",
 *      "Sensor","BIOS Settings","Reserved"}]
 *     uint32 Category;
 *     [read, ValueMap{"0","5","10","15","20","25","30",".."},
 *      Values{"Unknown","OK","Degraded/Warning","Minor Failure",
 *      "Major Failure","Critical Failure","Non-recoverable Error",
 *      "DMTF Reserved"}]
 *     uint32 PossibleSeverity;
 *     [read, ValueMap {"0","1","2","3","4","5","6","7","8","9",
 *      "10","11","12","13","14","15","16","17","18","..",
 *      "0x8000.."}, Values {"Unknown","Other","OK","Degraded",
 *      "Stressed","Predictive Failure","Error",
 *      "Non-Recoverable Error","Starting","Stopping","Stopped",
 *      "In Service","No Contact","Lost Communication","Aborted",
 *      "Dormant","Supporting Entity in Error","Completed",
 *      "Power Mode","DMTF Reserved","Vendor Reserved"}]
 *     uint32 PossibleStatus;
 *   };
 */
struct hp_wmi_platform_events {};

/*
 * struct hp_wmi_event - a HPBIOS_BIOSEvent instance
 *
 * Effective MOF definition [1] (corrected below from original):
 *
 *   #pragma namespace("\\\\.\\root\\WMI");
 *   class HPBIOS_BIOSEvent : WMIEvent {
 *     [read] string Name;
 *     [read] string Description;
 *     [read ValueMap {"0","1","2","3","4"}, Values {"Unknown",
 *      "Configuration Change","Button Pressed","Sensor",
 *      "BIOS Settings"}]
 *     uint32 Category;
 *     [read, ValueMap {"0","5","10","15","20","25","30"},
 *      Values {"Unknown","OK","Degraded/Warning",
 *      "Minor Failure","Major Failure","Critical Failure",
 *      "Non-recoverable Error"}]
 *     uint32 Severity;
 *     [read, ValueMap {"0","1","2","3","4","5","6","7","8",
 *      "9","10","11","12","13","14","15","16","17","18","..",
 *      "0x8000.."}, Values {"Unknown","Other","OK","Degraded",
 *      "Stressed","Predictive Failure","Error",
 *      "Non-Recoverable Error","Starting","Stopping","Stopped",
 *      "In Service","No Contact","Lost Communication","Aborted",
 *      "Dormant","Supporting Entity in Error","Completed",
 *      "Power Mode","DMTF Reserved","Vendor Reserved"}]
 *     uint32 Status;
 *   };
 */
struct hp_wmi_event {};

/*
 * struct hp_wmi_info - sensor info
 * @nsensor: numeric sensor properties
 * @instance: its WMI instance number
 * @state: pointer to driver state
 * @has_alarm: whether sensor has an alarm flag
 * @alarm: alarm flag
 * @type: its hwmon sensor type
 * @cached_val: current sensor reading value, scaled for hwmon
 * @last_updated: when these readings were last updated
 */
struct hp_wmi_info {};

/*
 * struct hp_wmi_sensors - driver state
 * @wdev: pointer to the parent WMI device
 * @info_map: sensor info structs by hwmon type and channel number
 * @channel_count: count of hwmon channels by hwmon type
 * @has_intrusion: whether an intrusion sensor is present
 * @intrusion: intrusion flag
 * @lock: mutex to lock polling WMI and changes to driver state
 */
struct hp_wmi_sensors {};

static bool is_raw_wmi_string(const u8 *pointer, u32 length)
{}

static char *convert_raw_wmi_string(const u8 *buf)
{}

/* hp_wmi_strdup - devm_kstrdup, but length-limited */
static char *hp_wmi_strdup(struct device *dev, const char *src)
{}

/* hp_wmi_wstrdup - hp_wmi_strdup, but for a raw WMI string */
static char *hp_wmi_wstrdup(struct device *dev, const u8 *buf)
{}

/*
 * hp_wmi_get_wobj - poll WMI for a WMI object instance
 * @guid: WMI object GUID
 * @instance: WMI object instance number
 *
 * Returns a new WMI object instance on success, or NULL on error.
 * Caller must kfree() the result.
 */
static union acpi_object *hp_wmi_get_wobj(const char *guid, u8 instance)
{}

/* hp_wmi_wobj_instance_count - find count of WMI object instances */
static u8 hp_wmi_wobj_instance_count(const char *guid)
{}

static int check_wobj(const union acpi_object *wobj,
		      const acpi_object_type property_map[], int last_prop)
{}

static int extract_acpi_value(struct device *dev,
			      union acpi_object *element,
			      acpi_object_type type,
			      u32 *out_value, char **out_string)
{}

/*
 * check_numeric_sensor_wobj - validate a HPBIOS_BIOSNumericSensor instance
 * @wobj: pointer to WMI object instance to check
 * @out_size: out pointer to count of possible states
 * @out_is_new: out pointer to whether this is a "new" variant object
 *
 * Returns 0 on success, or a negative error code on error.
 */
static int check_numeric_sensor_wobj(const union acpi_object *wobj,
				     u8 *out_size, bool *out_is_new)
{}

static int
numeric_sensor_is_connected(const struct hp_wmi_numeric_sensor *nsensor)
{}

static int numeric_sensor_has_fault(const struct hp_wmi_numeric_sensor *nsensor)
{}

/* scale_numeric_sensor - scale sensor reading for hwmon */
static long scale_numeric_sensor(const struct hp_wmi_numeric_sensor *nsensor)
{}

/*
 * classify_numeric_sensor - classify a numeric sensor
 * @nsensor: pointer to numeric sensor struct
 *
 * Returns an enum hp_wmi_type value on success,
 * or a negative value if the sensor type is unsupported.
 */
static int classify_numeric_sensor(const struct hp_wmi_numeric_sensor *nsensor)
{}

static int
populate_numeric_sensor_from_wobj(struct device *dev,
				  struct hp_wmi_numeric_sensor *nsensor,
				  union acpi_object *wobj, bool *out_is_new)
{}

/* update_numeric_sensor_from_wobj - update fungible sensor properties */
static void
update_numeric_sensor_from_wobj(struct device *dev,
				struct hp_wmi_numeric_sensor *nsensor,
				const union acpi_object *wobj)
{}

/*
 * check_platform_events_wobj - validate a HPBIOS_PlatformEvents instance
 * @wobj: pointer to WMI object instance to check
 *
 * Returns 0 on success, or a negative error code on error.
 */
static int check_platform_events_wobj(const union acpi_object *wobj)
{}

static int
populate_platform_events_from_wobj(struct device *dev,
				   struct hp_wmi_platform_events *pevents,
				   union acpi_object *wobj)
{}

/*
 * check_event_wobj - validate a HPBIOS_BIOSEvent instance
 * @wobj: pointer to WMI object instance to check
 *
 * Returns 0 on success, or a negative error code on error.
 */
static int check_event_wobj(const union acpi_object *wobj)
{}

static int populate_event_from_wobj(struct device *dev,
				    struct hp_wmi_event *event,
				    union acpi_object *wobj)
{}

/*
 * classify_event - classify an event
 * @name: event name
 * @category: event category
 *
 * Classify instances of both HPBIOS_PlatformEvents and HPBIOS_BIOSEvent from
 * property values. Recognition criteria are based on multiple ACPI dumps [3].
 *
 * Returns an enum hp_wmi_type value on success,
 * or a negative value if the event type is unsupported.
 */
static int classify_event(const char *event_name, u32 category)
{}

/*
 * interpret_info - interpret sensor for hwmon
 * @info: pointer to sensor info struct
 *
 * Should be called after the numeric sensor member has been updated.
 */
static void interpret_info(struct hp_wmi_info *info)
{}

/*
 * hp_wmi_update_info - poll WMI to update sensor info
 * @state: pointer to driver state
 * @info: pointer to sensor info struct
 *
 * Returns 0 on success, or a negative error code on error.
 */
static int hp_wmi_update_info(struct hp_wmi_sensors *state,
			      struct hp_wmi_info *info)
{}

static int basic_string_show(struct seq_file *seqf, void *ignored)
{}
DEFINE_SHOW_ATTRIBUTE();

static int fungible_show(struct seq_file *seqf, enum hp_wmi_property prop)
{}

static int operational_status_show(struct seq_file *seqf, void *ignored)
{}
DEFINE_SHOW_ATTRIBUTE();

static int current_state_show(struct seq_file *seqf, void *ignored)
{}
DEFINE_SHOW_ATTRIBUTE();

static int possible_states_show(struct seq_file *seqf, void *ignored)
{}
DEFINE_SHOW_ATTRIBUTE();

static int unit_modifier_show(struct seq_file *seqf, void *ignored)
{}
DEFINE_SHOW_ATTRIBUTE();

static int current_reading_show(struct seq_file *seqf, void *ignored)
{}
DEFINE_SHOW_ATTRIBUTE();

/* hp_wmi_devm_debugfs_remove - devm callback for debugfs cleanup */
static void hp_wmi_devm_debugfs_remove(void *res)
{}

/* hp_wmi_debugfs_init - create and populate debugfs directory tree */
static void hp_wmi_debugfs_init(struct device *dev, struct hp_wmi_info *info,
				struct hp_wmi_platform_events *pevents,
				u8 icount, u8 pcount, bool is_new)
{}

static umode_t hp_wmi_hwmon_is_visible(const void *drvdata,
				       enum hwmon_sensor_types type,
				       u32 attr, int channel)
{}

static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
			     u32 attr, int channel, long *out_val)
{}

static int hp_wmi_hwmon_read_string(struct device *dev,
				    enum hwmon_sensor_types type, u32 attr,
				    int channel, const char **out_str)
{}

static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
			      u32 attr, int channel, long val)
{}

static const struct hwmon_ops hp_wmi_hwmon_ops =;

static struct hwmon_chip_info hp_wmi_chip_info =;

static struct hp_wmi_info *match_fan_event(struct hp_wmi_sensors *state,
					   const char *event_description)
{}

static u8 match_temp_events(struct hp_wmi_sensors *state,
			    const char *event_description,
			    struct hp_wmi_info *temp_info[])
{}

/* hp_wmi_devm_debugfs_remove - devm callback for WMI event handler removal */
static void hp_wmi_devm_notify_remove(void *ignored)
{}

/* hp_wmi_notify - WMI event notification handler */
static void hp_wmi_notify(u32 value, void *context)
{}

static int init_platform_events(struct device *dev,
				struct hp_wmi_platform_events **out_pevents,
				u8 *out_pcount)
{}

static int init_numeric_sensors(struct hp_wmi_sensors *state,
				struct hp_wmi_info *connected[],
				struct hp_wmi_info **out_info,
				u8 *out_icount, u8 *out_count,
				bool *out_is_new)
{}

static bool find_event_attributes(struct hp_wmi_sensors *state,
				  struct hp_wmi_platform_events *pevents,
				  u8 pevents_count)
{}

static int make_chip_info(struct hp_wmi_sensors *state, bool has_events)
{}

static bool add_event_handler(struct hp_wmi_sensors *state)
{}

static int hp_wmi_sensors_init(struct hp_wmi_sensors *state)
{}

static int hp_wmi_sensors_probe(struct wmi_device *wdev, const void *context)
{}

static const struct wmi_device_id hp_wmi_sensors_id_table[] =;

static struct wmi_driver hp_wmi_sensors_driver =;
module_wmi_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();