162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * hwmon driver for HP (and some HP Compaq) business-class computers that 462306a36Sopenharmony_ci * report numeric sensor data via Windows Management Instrumentation (WMI). 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2023 James Seo <james@equiv.tech> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * References: 962306a36Sopenharmony_ci * [1] Hewlett-Packard Development Company, L.P., 1062306a36Sopenharmony_ci * "HP Client Management Interface Technical White Paper", 2005. [Online]. 1162306a36Sopenharmony_ci * Available: https://h20331.www2.hp.com/hpsub/downloads/cmi_whitepaper.pdf 1262306a36Sopenharmony_ci * [2] Hewlett-Packard Development Company, L.P., 1362306a36Sopenharmony_ci * "HP Retail Manageability", 2012. [Online]. 1462306a36Sopenharmony_ci * Available: http://h10032.www1.hp.com/ctg/Manual/c03291135.pdf 1562306a36Sopenharmony_ci * [3] Linux Hardware Project, A. Ponomarenko et al., 1662306a36Sopenharmony_ci * "linuxhw/ACPI - Collect ACPI table dumps", 2018. [Online]. 1762306a36Sopenharmony_ci * Available: https://github.com/linuxhw/ACPI 1862306a36Sopenharmony_ci * [4] P. Rohár, "bmfdec - Decompile binary MOF file (BMF) from WMI buffer", 1962306a36Sopenharmony_ci * 2017. [Online]. Available: https://github.com/pali/bmfdec 2062306a36Sopenharmony_ci * [5] Microsoft Corporation, "Driver-Defined WMI Data Items", 2017. [Online]. 2162306a36Sopenharmony_ci * Available: https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/driver-defined-wmi-data-items 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/acpi.h> 2562306a36Sopenharmony_ci#include <linux/debugfs.h> 2662306a36Sopenharmony_ci#include <linux/hwmon.h> 2762306a36Sopenharmony_ci#include <linux/jiffies.h> 2862306a36Sopenharmony_ci#include <linux/mutex.h> 2962306a36Sopenharmony_ci#include <linux/nls.h> 3062306a36Sopenharmony_ci#include <linux/units.h> 3162306a36Sopenharmony_ci#include <linux/wmi.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define HP_WMI_EVENT_NAMESPACE "root\\WMI" 3462306a36Sopenharmony_ci#define HP_WMI_EVENT_CLASS "HPBIOS_BIOSEvent" 3562306a36Sopenharmony_ci#define HP_WMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" 3662306a36Sopenharmony_ci#define HP_WMI_NUMERIC_SENSOR_GUID "8F1F6435-9F42-42C8-BADC-0E9424F20C9A" 3762306a36Sopenharmony_ci#define HP_WMI_PLATFORM_EVENTS_GUID "41227C2D-80E1-423F-8B8E-87E32755A0EB" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* Patterns for recognizing sensors and matching events to channels. */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define HP_WMI_PATTERN_SYS_TEMP "Chassis Thermal Index" 4262306a36Sopenharmony_ci#define HP_WMI_PATTERN_SYS_TEMP2 "System Ambient Temperature" 4362306a36Sopenharmony_ci#define HP_WMI_PATTERN_CPU_TEMP "CPU Thermal Index" 4462306a36Sopenharmony_ci#define HP_WMI_PATTERN_CPU_TEMP2 "CPU Temperature" 4562306a36Sopenharmony_ci#define HP_WMI_PATTERN_TEMP_SENSOR "Thermal Index" 4662306a36Sopenharmony_ci#define HP_WMI_PATTERN_TEMP_ALARM "Thermal Critical" 4762306a36Sopenharmony_ci#define HP_WMI_PATTERN_INTRUSION_ALARM "Hood Intrusion" 4862306a36Sopenharmony_ci#define HP_WMI_PATTERN_FAN_ALARM "Stall" 4962306a36Sopenharmony_ci#define HP_WMI_PATTERN_TEMP "Temperature" 5062306a36Sopenharmony_ci#define HP_WMI_PATTERN_CPU "CPU" 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* These limits are arbitrary. The WMI implementation may vary by system. */ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define HP_WMI_MAX_STR_SIZE 128U 5562306a36Sopenharmony_ci#define HP_WMI_MAX_PROPERTIES 32U 5662306a36Sopenharmony_ci#define HP_WMI_MAX_INSTANCES 32U 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cienum hp_wmi_type { 5962306a36Sopenharmony_ci HP_WMI_TYPE_OTHER = 1, 6062306a36Sopenharmony_ci HP_WMI_TYPE_TEMPERATURE = 2, 6162306a36Sopenharmony_ci HP_WMI_TYPE_VOLTAGE = 3, 6262306a36Sopenharmony_ci HP_WMI_TYPE_CURRENT = 4, 6362306a36Sopenharmony_ci HP_WMI_TYPE_AIR_FLOW = 12, 6462306a36Sopenharmony_ci HP_WMI_TYPE_INTRUSION = 0xabadb01, /* Custom. */ 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cienum hp_wmi_category { 6862306a36Sopenharmony_ci HP_WMI_CATEGORY_SENSOR = 3, 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cienum hp_wmi_severity { 7262306a36Sopenharmony_ci HP_WMI_SEVERITY_UNKNOWN = 0, 7362306a36Sopenharmony_ci HP_WMI_SEVERITY_OK = 5, 7462306a36Sopenharmony_ci HP_WMI_SEVERITY_DEGRADED_WARNING = 10, 7562306a36Sopenharmony_ci HP_WMI_SEVERITY_MINOR_FAILURE = 15, 7662306a36Sopenharmony_ci HP_WMI_SEVERITY_MAJOR_FAILURE = 20, 7762306a36Sopenharmony_ci HP_WMI_SEVERITY_CRITICAL_FAILURE = 25, 7862306a36Sopenharmony_ci HP_WMI_SEVERITY_NON_RECOVERABLE_ERROR = 30, 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cienum hp_wmi_status { 8262306a36Sopenharmony_ci HP_WMI_STATUS_OK = 2, 8362306a36Sopenharmony_ci HP_WMI_STATUS_DEGRADED = 3, 8462306a36Sopenharmony_ci HP_WMI_STATUS_STRESSED = 4, 8562306a36Sopenharmony_ci HP_WMI_STATUS_PREDICTIVE_FAILURE = 5, 8662306a36Sopenharmony_ci HP_WMI_STATUS_ERROR = 6, 8762306a36Sopenharmony_ci HP_WMI_STATUS_NON_RECOVERABLE_ERROR = 7, 8862306a36Sopenharmony_ci HP_WMI_STATUS_NO_CONTACT = 12, 8962306a36Sopenharmony_ci HP_WMI_STATUS_LOST_COMMUNICATION = 13, 9062306a36Sopenharmony_ci HP_WMI_STATUS_ABORTED = 14, 9162306a36Sopenharmony_ci HP_WMI_STATUS_SUPPORTING_ENTITY_IN_ERROR = 16, 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* Occurs combined with one of "OK", "Degraded", and "Error" [1]. */ 9462306a36Sopenharmony_ci HP_WMI_STATUS_COMPLETED = 17, 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cienum hp_wmi_units { 9862306a36Sopenharmony_ci HP_WMI_UNITS_OTHER = 1, 9962306a36Sopenharmony_ci HP_WMI_UNITS_DEGREES_C = 2, 10062306a36Sopenharmony_ci HP_WMI_UNITS_DEGREES_F = 3, 10162306a36Sopenharmony_ci HP_WMI_UNITS_DEGREES_K = 4, 10262306a36Sopenharmony_ci HP_WMI_UNITS_VOLTS = 5, 10362306a36Sopenharmony_ci HP_WMI_UNITS_AMPS = 6, 10462306a36Sopenharmony_ci HP_WMI_UNITS_RPM = 19, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cienum hp_wmi_property { 10862306a36Sopenharmony_ci HP_WMI_PROPERTY_NAME = 0, 10962306a36Sopenharmony_ci HP_WMI_PROPERTY_DESCRIPTION = 1, 11062306a36Sopenharmony_ci HP_WMI_PROPERTY_SENSOR_TYPE = 2, 11162306a36Sopenharmony_ci HP_WMI_PROPERTY_OTHER_SENSOR_TYPE = 3, 11262306a36Sopenharmony_ci HP_WMI_PROPERTY_OPERATIONAL_STATUS = 4, 11362306a36Sopenharmony_ci HP_WMI_PROPERTY_SIZE = 5, 11462306a36Sopenharmony_ci HP_WMI_PROPERTY_POSSIBLE_STATES = 6, 11562306a36Sopenharmony_ci HP_WMI_PROPERTY_CURRENT_STATE = 7, 11662306a36Sopenharmony_ci HP_WMI_PROPERTY_BASE_UNITS = 8, 11762306a36Sopenharmony_ci HP_WMI_PROPERTY_UNIT_MODIFIER = 9, 11862306a36Sopenharmony_ci HP_WMI_PROPERTY_CURRENT_READING = 10, 11962306a36Sopenharmony_ci HP_WMI_PROPERTY_RATE_UNITS = 11, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic const acpi_object_type hp_wmi_property_map[] = { 12362306a36Sopenharmony_ci [HP_WMI_PROPERTY_NAME] = ACPI_TYPE_STRING, 12462306a36Sopenharmony_ci [HP_WMI_PROPERTY_DESCRIPTION] = ACPI_TYPE_STRING, 12562306a36Sopenharmony_ci [HP_WMI_PROPERTY_SENSOR_TYPE] = ACPI_TYPE_INTEGER, 12662306a36Sopenharmony_ci [HP_WMI_PROPERTY_OTHER_SENSOR_TYPE] = ACPI_TYPE_STRING, 12762306a36Sopenharmony_ci [HP_WMI_PROPERTY_OPERATIONAL_STATUS] = ACPI_TYPE_INTEGER, 12862306a36Sopenharmony_ci [HP_WMI_PROPERTY_SIZE] = ACPI_TYPE_INTEGER, 12962306a36Sopenharmony_ci [HP_WMI_PROPERTY_POSSIBLE_STATES] = ACPI_TYPE_STRING, 13062306a36Sopenharmony_ci [HP_WMI_PROPERTY_CURRENT_STATE] = ACPI_TYPE_STRING, 13162306a36Sopenharmony_ci [HP_WMI_PROPERTY_BASE_UNITS] = ACPI_TYPE_INTEGER, 13262306a36Sopenharmony_ci [HP_WMI_PROPERTY_UNIT_MODIFIER] = ACPI_TYPE_INTEGER, 13362306a36Sopenharmony_ci [HP_WMI_PROPERTY_CURRENT_READING] = ACPI_TYPE_INTEGER, 13462306a36Sopenharmony_ci [HP_WMI_PROPERTY_RATE_UNITS] = ACPI_TYPE_INTEGER, 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cienum hp_wmi_platform_events_property { 13862306a36Sopenharmony_ci HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME = 0, 13962306a36Sopenharmony_ci HP_WMI_PLATFORM_EVENTS_PROPERTY_DESCRIPTION = 1, 14062306a36Sopenharmony_ci HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_NAMESPACE = 2, 14162306a36Sopenharmony_ci HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_CLASS = 3, 14262306a36Sopenharmony_ci HP_WMI_PLATFORM_EVENTS_PROPERTY_CATEGORY = 4, 14362306a36Sopenharmony_ci HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_SEVERITY = 5, 14462306a36Sopenharmony_ci HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS = 6, 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic const acpi_object_type hp_wmi_platform_events_property_map[] = { 14862306a36Sopenharmony_ci [HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME] = ACPI_TYPE_STRING, 14962306a36Sopenharmony_ci [HP_WMI_PLATFORM_EVENTS_PROPERTY_DESCRIPTION] = ACPI_TYPE_STRING, 15062306a36Sopenharmony_ci [HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_NAMESPACE] = ACPI_TYPE_STRING, 15162306a36Sopenharmony_ci [HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_CLASS] = ACPI_TYPE_STRING, 15262306a36Sopenharmony_ci [HP_WMI_PLATFORM_EVENTS_PROPERTY_CATEGORY] = ACPI_TYPE_INTEGER, 15362306a36Sopenharmony_ci [HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_SEVERITY] = ACPI_TYPE_INTEGER, 15462306a36Sopenharmony_ci [HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS] = ACPI_TYPE_INTEGER, 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cienum hp_wmi_event_property { 15862306a36Sopenharmony_ci HP_WMI_EVENT_PROPERTY_NAME = 0, 15962306a36Sopenharmony_ci HP_WMI_EVENT_PROPERTY_DESCRIPTION = 1, 16062306a36Sopenharmony_ci HP_WMI_EVENT_PROPERTY_CATEGORY = 2, 16162306a36Sopenharmony_ci HP_WMI_EVENT_PROPERTY_SEVERITY = 3, 16262306a36Sopenharmony_ci HP_WMI_EVENT_PROPERTY_STATUS = 4, 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic const acpi_object_type hp_wmi_event_property_map[] = { 16662306a36Sopenharmony_ci [HP_WMI_EVENT_PROPERTY_NAME] = ACPI_TYPE_STRING, 16762306a36Sopenharmony_ci [HP_WMI_EVENT_PROPERTY_DESCRIPTION] = ACPI_TYPE_STRING, 16862306a36Sopenharmony_ci [HP_WMI_EVENT_PROPERTY_CATEGORY] = ACPI_TYPE_INTEGER, 16962306a36Sopenharmony_ci [HP_WMI_EVENT_PROPERTY_SEVERITY] = ACPI_TYPE_INTEGER, 17062306a36Sopenharmony_ci [HP_WMI_EVENT_PROPERTY_STATUS] = ACPI_TYPE_INTEGER, 17162306a36Sopenharmony_ci}; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic const enum hwmon_sensor_types hp_wmi_hwmon_type_map[] = { 17462306a36Sopenharmony_ci [HP_WMI_TYPE_TEMPERATURE] = hwmon_temp, 17562306a36Sopenharmony_ci [HP_WMI_TYPE_VOLTAGE] = hwmon_in, 17662306a36Sopenharmony_ci [HP_WMI_TYPE_CURRENT] = hwmon_curr, 17762306a36Sopenharmony_ci [HP_WMI_TYPE_AIR_FLOW] = hwmon_fan, 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic const u32 hp_wmi_hwmon_attributes[hwmon_max] = { 18162306a36Sopenharmony_ci [hwmon_chip] = HWMON_C_REGISTER_TZ, 18262306a36Sopenharmony_ci [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_FAULT, 18362306a36Sopenharmony_ci [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL, 18462306a36Sopenharmony_ci [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL, 18562306a36Sopenharmony_ci [hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_FAULT, 18662306a36Sopenharmony_ci [hwmon_intrusion] = HWMON_INTRUSION_ALARM, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* 19062306a36Sopenharmony_ci * struct hp_wmi_numeric_sensor - a HPBIOS_BIOSNumericSensor instance 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * Two variants of HPBIOS_BIOSNumericSensor are known. The first is specified 19362306a36Sopenharmony_ci * in [1] and appears to be much more widespread. The second was discovered by 19462306a36Sopenharmony_ci * decoding BMOF blobs [4], seems to be found only in some newer ZBook systems 19562306a36Sopenharmony_ci * [3], and has two new properties and a slightly different property order. 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * These differences don't matter on Windows, where WMI object properties are 19862306a36Sopenharmony_ci * accessed by name. For us, supporting both variants gets ugly and hacky at 19962306a36Sopenharmony_ci * times. The fun begins now; this struct is defined as per the new variant. 20062306a36Sopenharmony_ci * 20162306a36Sopenharmony_ci * Effective MOF definition: 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * #pragma namespace("\\\\.\\root\\HP\\InstrumentedBIOS"); 20462306a36Sopenharmony_ci * class HPBIOS_BIOSNumericSensor { 20562306a36Sopenharmony_ci * [read] string Name; 20662306a36Sopenharmony_ci * [read] string Description; 20762306a36Sopenharmony_ci * [read, ValueMap {"0","1","2","3","4","5","6","7","8","9", 20862306a36Sopenharmony_ci * "10","11","12"}, Values {"Unknown","Other","Temperature", 20962306a36Sopenharmony_ci * "Voltage","Current","Tachometer","Counter","Switch","Lock", 21062306a36Sopenharmony_ci * "Humidity","Smoke Detection","Presence","Air Flow"}] 21162306a36Sopenharmony_ci * uint32 SensorType; 21262306a36Sopenharmony_ci * [read] string OtherSensorType; 21362306a36Sopenharmony_ci * [read, ValueMap {"0","1","2","3","4","5","6","7","8","9", 21462306a36Sopenharmony_ci * "10","11","12","13","14","15","16","17","18","..", 21562306a36Sopenharmony_ci * "0x8000.."}, Values {"Unknown","Other","OK","Degraded", 21662306a36Sopenharmony_ci * "Stressed","Predictive Failure","Error", 21762306a36Sopenharmony_ci * "Non-Recoverable Error","Starting","Stopping","Stopped", 21862306a36Sopenharmony_ci * "In Service","No Contact","Lost Communication","Aborted", 21962306a36Sopenharmony_ci * "Dormant","Supporting Entity in Error","Completed", 22062306a36Sopenharmony_ci * "Power Mode","DMTF Reserved","Vendor Reserved"}] 22162306a36Sopenharmony_ci * uint32 OperationalStatus; 22262306a36Sopenharmony_ci * [read] uint32 Size; 22362306a36Sopenharmony_ci * [read] string PossibleStates[]; 22462306a36Sopenharmony_ci * [read] string CurrentState; 22562306a36Sopenharmony_ci * [read, ValueMap {"0","1","2","3","4","5","6","7","8","9", 22662306a36Sopenharmony_ci * "10","11","12","13","14","15","16","17","18","19","20", 22762306a36Sopenharmony_ci * "21","22","23","24","25","26","27","28","29","30","31", 22862306a36Sopenharmony_ci * "32","33","34","35","36","37","38","39","40","41","42", 22962306a36Sopenharmony_ci * "43","44","45","46","47","48","49","50","51","52","53", 23062306a36Sopenharmony_ci * "54","55","56","57","58","59","60","61","62","63","64", 23162306a36Sopenharmony_ci * "65"}, Values {"Unknown","Other","Degrees C","Degrees F", 23262306a36Sopenharmony_ci * "Degrees K","Volts","Amps","Watts","Joules","Coulombs", 23362306a36Sopenharmony_ci * "VA","Nits","Lumens","Lux","Candelas","kPa","PSI", 23462306a36Sopenharmony_ci * "Newtons","CFM","RPM","Hertz","Seconds","Minutes", 23562306a36Sopenharmony_ci * "Hours","Days","Weeks","Mils","Inches","Feet", 23662306a36Sopenharmony_ci * "Cubic Inches","Cubic Feet","Meters","Cubic Centimeters", 23762306a36Sopenharmony_ci * "Cubic Meters","Liters","Fluid Ounces","Radians", 23862306a36Sopenharmony_ci * "Steradians","Revolutions","Cycles","Gravities","Ounces", 23962306a36Sopenharmony_ci * "Pounds","Foot-Pounds","Ounce-Inches","Gauss","Gilberts", 24062306a36Sopenharmony_ci * "Henries","Farads","Ohms","Siemens","Moles","Becquerels", 24162306a36Sopenharmony_ci * "PPM (parts/million)","Decibels","DbA","DbC","Grays", 24262306a36Sopenharmony_ci * "Sieverts","Color Temperature Degrees K","Bits","Bytes", 24362306a36Sopenharmony_ci * "Words (data)","DoubleWords","QuadWords","Percentage"}] 24462306a36Sopenharmony_ci * uint32 BaseUnits; 24562306a36Sopenharmony_ci * [read] sint32 UnitModifier; 24662306a36Sopenharmony_ci * [read] uint32 CurrentReading; 24762306a36Sopenharmony_ci * [read] uint32 RateUnits; 24862306a36Sopenharmony_ci * }; 24962306a36Sopenharmony_ci * 25062306a36Sopenharmony_ci * Effective MOF definition of old variant [1] (sans redundant info): 25162306a36Sopenharmony_ci * 25262306a36Sopenharmony_ci * class HPBIOS_BIOSNumericSensor { 25362306a36Sopenharmony_ci * [read] string Name; 25462306a36Sopenharmony_ci * [read] string Description; 25562306a36Sopenharmony_ci * [read] uint32 SensorType; 25662306a36Sopenharmony_ci * [read] string OtherSensorType; 25762306a36Sopenharmony_ci * [read] uint32 OperationalStatus; 25862306a36Sopenharmony_ci * [read] string CurrentState; 25962306a36Sopenharmony_ci * [read] string PossibleStates[]; 26062306a36Sopenharmony_ci * [read] uint32 BaseUnits; 26162306a36Sopenharmony_ci * [read] sint32 UnitModifier; 26262306a36Sopenharmony_ci * [read] uint32 CurrentReading; 26362306a36Sopenharmony_ci * }; 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_cistruct hp_wmi_numeric_sensor { 26662306a36Sopenharmony_ci const char *name; 26762306a36Sopenharmony_ci const char *description; 26862306a36Sopenharmony_ci u32 sensor_type; 26962306a36Sopenharmony_ci const char *other_sensor_type; /* Explains "Other" SensorType. */ 27062306a36Sopenharmony_ci u32 operational_status; 27162306a36Sopenharmony_ci u8 size; /* Count of PossibleStates[]. */ 27262306a36Sopenharmony_ci const char **possible_states; 27362306a36Sopenharmony_ci const char *current_state; 27462306a36Sopenharmony_ci u32 base_units; 27562306a36Sopenharmony_ci s32 unit_modifier; 27662306a36Sopenharmony_ci u32 current_reading; 27762306a36Sopenharmony_ci u32 rate_units; 27862306a36Sopenharmony_ci}; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/* 28162306a36Sopenharmony_ci * struct hp_wmi_platform_events - a HPBIOS_PlatformEvents instance 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci * Instances of this object reveal the set of possible HPBIOS_BIOSEvent 28462306a36Sopenharmony_ci * instances for the current system, but it may not always be present. 28562306a36Sopenharmony_ci * 28662306a36Sopenharmony_ci * Effective MOF definition: 28762306a36Sopenharmony_ci * 28862306a36Sopenharmony_ci * #pragma namespace("\\\\.\\root\\HP\\InstrumentedBIOS"); 28962306a36Sopenharmony_ci * class HPBIOS_PlatformEvents { 29062306a36Sopenharmony_ci * [read] string Name; 29162306a36Sopenharmony_ci * [read] string Description; 29262306a36Sopenharmony_ci * [read] string SourceNamespace; 29362306a36Sopenharmony_ci * [read] string SourceClass; 29462306a36Sopenharmony_ci * [read, ValueMap {"0","1","2","3","4",".."}, Values { 29562306a36Sopenharmony_ci * "Unknown","Configuration Change","Button Pressed", 29662306a36Sopenharmony_ci * "Sensor","BIOS Settings","Reserved"}] 29762306a36Sopenharmony_ci * uint32 Category; 29862306a36Sopenharmony_ci * [read, ValueMap{"0","5","10","15","20","25","30",".."}, 29962306a36Sopenharmony_ci * Values{"Unknown","OK","Degraded/Warning","Minor Failure", 30062306a36Sopenharmony_ci * "Major Failure","Critical Failure","Non-recoverable Error", 30162306a36Sopenharmony_ci * "DMTF Reserved"}] 30262306a36Sopenharmony_ci * uint32 PossibleSeverity; 30362306a36Sopenharmony_ci * [read, ValueMap {"0","1","2","3","4","5","6","7","8","9", 30462306a36Sopenharmony_ci * "10","11","12","13","14","15","16","17","18","..", 30562306a36Sopenharmony_ci * "0x8000.."}, Values {"Unknown","Other","OK","Degraded", 30662306a36Sopenharmony_ci * "Stressed","Predictive Failure","Error", 30762306a36Sopenharmony_ci * "Non-Recoverable Error","Starting","Stopping","Stopped", 30862306a36Sopenharmony_ci * "In Service","No Contact","Lost Communication","Aborted", 30962306a36Sopenharmony_ci * "Dormant","Supporting Entity in Error","Completed", 31062306a36Sopenharmony_ci * "Power Mode","DMTF Reserved","Vendor Reserved"}] 31162306a36Sopenharmony_ci * uint32 PossibleStatus; 31262306a36Sopenharmony_ci * }; 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_cistruct hp_wmi_platform_events { 31562306a36Sopenharmony_ci const char *name; 31662306a36Sopenharmony_ci const char *description; 31762306a36Sopenharmony_ci const char *source_namespace; 31862306a36Sopenharmony_ci const char *source_class; 31962306a36Sopenharmony_ci u32 category; 32062306a36Sopenharmony_ci u32 possible_severity; 32162306a36Sopenharmony_ci u32 possible_status; 32262306a36Sopenharmony_ci}; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/* 32562306a36Sopenharmony_ci * struct hp_wmi_event - a HPBIOS_BIOSEvent instance 32662306a36Sopenharmony_ci * 32762306a36Sopenharmony_ci * Effective MOF definition [1] (corrected below from original): 32862306a36Sopenharmony_ci * 32962306a36Sopenharmony_ci * #pragma namespace("\\\\.\\root\\WMI"); 33062306a36Sopenharmony_ci * class HPBIOS_BIOSEvent : WMIEvent { 33162306a36Sopenharmony_ci * [read] string Name; 33262306a36Sopenharmony_ci * [read] string Description; 33362306a36Sopenharmony_ci * [read ValueMap {"0","1","2","3","4"}, Values {"Unknown", 33462306a36Sopenharmony_ci * "Configuration Change","Button Pressed","Sensor", 33562306a36Sopenharmony_ci * "BIOS Settings"}] 33662306a36Sopenharmony_ci * uint32 Category; 33762306a36Sopenharmony_ci * [read, ValueMap {"0","5","10","15","20","25","30"}, 33862306a36Sopenharmony_ci * Values {"Unknown","OK","Degraded/Warning", 33962306a36Sopenharmony_ci * "Minor Failure","Major Failure","Critical Failure", 34062306a36Sopenharmony_ci * "Non-recoverable Error"}] 34162306a36Sopenharmony_ci * uint32 Severity; 34262306a36Sopenharmony_ci * [read, ValueMap {"0","1","2","3","4","5","6","7","8", 34362306a36Sopenharmony_ci * "9","10","11","12","13","14","15","16","17","18","..", 34462306a36Sopenharmony_ci * "0x8000.."}, Values {"Unknown","Other","OK","Degraded", 34562306a36Sopenharmony_ci * "Stressed","Predictive Failure","Error", 34662306a36Sopenharmony_ci * "Non-Recoverable Error","Starting","Stopping","Stopped", 34762306a36Sopenharmony_ci * "In Service","No Contact","Lost Communication","Aborted", 34862306a36Sopenharmony_ci * "Dormant","Supporting Entity in Error","Completed", 34962306a36Sopenharmony_ci * "Power Mode","DMTF Reserved","Vendor Reserved"}] 35062306a36Sopenharmony_ci * uint32 Status; 35162306a36Sopenharmony_ci * }; 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_cistruct hp_wmi_event { 35462306a36Sopenharmony_ci const char *name; 35562306a36Sopenharmony_ci const char *description; 35662306a36Sopenharmony_ci u32 category; 35762306a36Sopenharmony_ci}; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* 36062306a36Sopenharmony_ci * struct hp_wmi_info - sensor info 36162306a36Sopenharmony_ci * @nsensor: numeric sensor properties 36262306a36Sopenharmony_ci * @instance: its WMI instance number 36362306a36Sopenharmony_ci * @state: pointer to driver state 36462306a36Sopenharmony_ci * @has_alarm: whether sensor has an alarm flag 36562306a36Sopenharmony_ci * @alarm: alarm flag 36662306a36Sopenharmony_ci * @type: its hwmon sensor type 36762306a36Sopenharmony_ci * @cached_val: current sensor reading value, scaled for hwmon 36862306a36Sopenharmony_ci * @last_updated: when these readings were last updated 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_cistruct hp_wmi_info { 37162306a36Sopenharmony_ci struct hp_wmi_numeric_sensor nsensor; 37262306a36Sopenharmony_ci u8 instance; 37362306a36Sopenharmony_ci void *state; /* void *: Avoid forward declaration. */ 37462306a36Sopenharmony_ci bool has_alarm; 37562306a36Sopenharmony_ci bool alarm; 37662306a36Sopenharmony_ci enum hwmon_sensor_types type; 37762306a36Sopenharmony_ci long cached_val; 37862306a36Sopenharmony_ci unsigned long last_updated; /* In jiffies. */ 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci}; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci/* 38362306a36Sopenharmony_ci * struct hp_wmi_sensors - driver state 38462306a36Sopenharmony_ci * @wdev: pointer to the parent WMI device 38562306a36Sopenharmony_ci * @info_map: sensor info structs by hwmon type and channel number 38662306a36Sopenharmony_ci * @channel_count: count of hwmon channels by hwmon type 38762306a36Sopenharmony_ci * @has_intrusion: whether an intrusion sensor is present 38862306a36Sopenharmony_ci * @intrusion: intrusion flag 38962306a36Sopenharmony_ci * @lock: mutex to lock polling WMI and changes to driver state 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_cistruct hp_wmi_sensors { 39262306a36Sopenharmony_ci struct wmi_device *wdev; 39362306a36Sopenharmony_ci struct hp_wmi_info **info_map[hwmon_max]; 39462306a36Sopenharmony_ci u8 channel_count[hwmon_max]; 39562306a36Sopenharmony_ci bool has_intrusion; 39662306a36Sopenharmony_ci bool intrusion; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci struct mutex lock; /* Lock polling WMI and driver state changes. */ 39962306a36Sopenharmony_ci}; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic bool is_raw_wmi_string(const u8 *pointer, u32 length) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci const u16 *ptr; 40462306a36Sopenharmony_ci u16 len; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* WMI strings are length-prefixed UTF-16 [5]. */ 40762306a36Sopenharmony_ci if (length <= sizeof(*ptr)) 40862306a36Sopenharmony_ci return false; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci length -= sizeof(*ptr); 41162306a36Sopenharmony_ci ptr = (const u16 *)pointer; 41262306a36Sopenharmony_ci len = *ptr; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return len <= length && !(len & 1); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic char *convert_raw_wmi_string(const u8 *buf) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci const wchar_t *src; 42062306a36Sopenharmony_ci unsigned int cps; 42162306a36Sopenharmony_ci unsigned int len; 42262306a36Sopenharmony_ci char *dst; 42362306a36Sopenharmony_ci int i; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci src = (const wchar_t *)buf; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Count UTF-16 code points. Exclude trailing null padding. */ 42862306a36Sopenharmony_ci cps = *src / sizeof(*src); 42962306a36Sopenharmony_ci while (cps && !src[cps]) 43062306a36Sopenharmony_ci cps--; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Each code point becomes up to 3 UTF-8 characters. */ 43362306a36Sopenharmony_ci len = min(cps * 3, HP_WMI_MAX_STR_SIZE - 1); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci dst = kmalloc((len + 1) * sizeof(*dst), GFP_KERNEL); 43662306a36Sopenharmony_ci if (!dst) 43762306a36Sopenharmony_ci return NULL; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci i = utf16s_to_utf8s(++src, cps, UTF16_LITTLE_ENDIAN, dst, len); 44062306a36Sopenharmony_ci dst[i] = '\0'; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return dst; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/* hp_wmi_strdup - devm_kstrdup, but length-limited */ 44662306a36Sopenharmony_cistatic char *hp_wmi_strdup(struct device *dev, const char *src) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci char *dst; 44962306a36Sopenharmony_ci size_t len; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci len = strnlen(src, HP_WMI_MAX_STR_SIZE - 1); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci dst = devm_kmalloc(dev, (len + 1) * sizeof(*dst), GFP_KERNEL); 45462306a36Sopenharmony_ci if (!dst) 45562306a36Sopenharmony_ci return NULL; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci strscpy(dst, src, len + 1); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return dst; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci/* hp_wmi_wstrdup - hp_wmi_strdup, but for a raw WMI string */ 46362306a36Sopenharmony_cistatic char *hp_wmi_wstrdup(struct device *dev, const u8 *buf) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci char *src; 46662306a36Sopenharmony_ci char *dst; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci src = convert_raw_wmi_string(buf); 46962306a36Sopenharmony_ci if (!src) 47062306a36Sopenharmony_ci return NULL; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci dst = hp_wmi_strdup(dev, strim(src)); /* Note: Copy is trimmed. */ 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci kfree(src); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return dst; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/* 48062306a36Sopenharmony_ci * hp_wmi_get_wobj - poll WMI for a WMI object instance 48162306a36Sopenharmony_ci * @guid: WMI object GUID 48262306a36Sopenharmony_ci * @instance: WMI object instance number 48362306a36Sopenharmony_ci * 48462306a36Sopenharmony_ci * Returns a new WMI object instance on success, or NULL on error. 48562306a36Sopenharmony_ci * Caller must kfree() the result. 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_cistatic union acpi_object *hp_wmi_get_wobj(const char *guid, u8 instance) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; 49062306a36Sopenharmony_ci acpi_status err; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci err = wmi_query_block(guid, instance, &out); 49362306a36Sopenharmony_ci if (ACPI_FAILURE(err)) 49462306a36Sopenharmony_ci return NULL; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return out.pointer; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci/* hp_wmi_wobj_instance_count - find count of WMI object instances */ 50062306a36Sopenharmony_cistatic u8 hp_wmi_wobj_instance_count(const char *guid) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci int count; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci count = wmi_instance_count(guid); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return clamp(count, 0, (int)HP_WMI_MAX_INSTANCES); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int check_wobj(const union acpi_object *wobj, 51062306a36Sopenharmony_ci const acpi_object_type property_map[], int last_prop) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci acpi_object_type type = wobj->type; 51362306a36Sopenharmony_ci acpi_object_type valid_type; 51462306a36Sopenharmony_ci union acpi_object *elements; 51562306a36Sopenharmony_ci u32 elem_count; 51662306a36Sopenharmony_ci int prop; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (type != ACPI_TYPE_PACKAGE) 51962306a36Sopenharmony_ci return -EINVAL; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci elem_count = wobj->package.count; 52262306a36Sopenharmony_ci if (elem_count != last_prop + 1) 52362306a36Sopenharmony_ci return -EINVAL; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci elements = wobj->package.elements; 52662306a36Sopenharmony_ci for (prop = 0; prop <= last_prop; prop++) { 52762306a36Sopenharmony_ci type = elements[prop].type; 52862306a36Sopenharmony_ci valid_type = property_map[prop]; 52962306a36Sopenharmony_ci if (type != valid_type) { 53062306a36Sopenharmony_ci if (type == ACPI_TYPE_BUFFER && 53162306a36Sopenharmony_ci valid_type == ACPI_TYPE_STRING && 53262306a36Sopenharmony_ci is_raw_wmi_string(elements[prop].buffer.pointer, 53362306a36Sopenharmony_ci elements[prop].buffer.length)) 53462306a36Sopenharmony_ci continue; 53562306a36Sopenharmony_ci return -EINVAL; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int extract_acpi_value(struct device *dev, 54362306a36Sopenharmony_ci union acpi_object *element, 54462306a36Sopenharmony_ci acpi_object_type type, 54562306a36Sopenharmony_ci u32 *out_value, char **out_string) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci switch (type) { 54862306a36Sopenharmony_ci case ACPI_TYPE_INTEGER: 54962306a36Sopenharmony_ci *out_value = element->integer.value; 55062306a36Sopenharmony_ci break; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci case ACPI_TYPE_STRING: 55362306a36Sopenharmony_ci *out_string = element->type == ACPI_TYPE_BUFFER ? 55462306a36Sopenharmony_ci hp_wmi_wstrdup(dev, element->buffer.pointer) : 55562306a36Sopenharmony_ci hp_wmi_strdup(dev, strim(element->string.pointer)); 55662306a36Sopenharmony_ci if (!*out_string) 55762306a36Sopenharmony_ci return -ENOMEM; 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci default: 56162306a36Sopenharmony_ci return -EINVAL; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci/* 56862306a36Sopenharmony_ci * check_numeric_sensor_wobj - validate a HPBIOS_BIOSNumericSensor instance 56962306a36Sopenharmony_ci * @wobj: pointer to WMI object instance to check 57062306a36Sopenharmony_ci * @out_size: out pointer to count of possible states 57162306a36Sopenharmony_ci * @out_is_new: out pointer to whether this is a "new" variant object 57262306a36Sopenharmony_ci * 57362306a36Sopenharmony_ci * Returns 0 on success, or a negative error code on error. 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_cistatic int check_numeric_sensor_wobj(const union acpi_object *wobj, 57662306a36Sopenharmony_ci u8 *out_size, bool *out_is_new) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci acpi_object_type type = wobj->type; 57962306a36Sopenharmony_ci int prop = HP_WMI_PROPERTY_NAME; 58062306a36Sopenharmony_ci acpi_object_type valid_type; 58162306a36Sopenharmony_ci union acpi_object *elements; 58262306a36Sopenharmony_ci u32 elem_count; 58362306a36Sopenharmony_ci int last_prop; 58462306a36Sopenharmony_ci bool is_new; 58562306a36Sopenharmony_ci u8 count; 58662306a36Sopenharmony_ci u32 j; 58762306a36Sopenharmony_ci u32 i; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (type != ACPI_TYPE_PACKAGE) 59062306a36Sopenharmony_ci return -EINVAL; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* 59362306a36Sopenharmony_ci * elements is a variable-length array of ACPI objects, one for 59462306a36Sopenharmony_ci * each property of the WMI object instance, except that the 59562306a36Sopenharmony_ci * strings in PossibleStates[] are flattened into this array 59662306a36Sopenharmony_ci * as if each individual string were a property by itself. 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_ci elements = wobj->package.elements; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci elem_count = wobj->package.count; 60162306a36Sopenharmony_ci if (elem_count <= HP_WMI_PROPERTY_SIZE || 60262306a36Sopenharmony_ci elem_count > HP_WMI_MAX_PROPERTIES) 60362306a36Sopenharmony_ci return -EINVAL; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci type = elements[HP_WMI_PROPERTY_SIZE].type; 60662306a36Sopenharmony_ci switch (type) { 60762306a36Sopenharmony_ci case ACPI_TYPE_INTEGER: 60862306a36Sopenharmony_ci is_new = true; 60962306a36Sopenharmony_ci last_prop = HP_WMI_PROPERTY_RATE_UNITS; 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci case ACPI_TYPE_STRING: 61362306a36Sopenharmony_ci is_new = false; 61462306a36Sopenharmony_ci last_prop = HP_WMI_PROPERTY_CURRENT_READING; 61562306a36Sopenharmony_ci break; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci default: 61862306a36Sopenharmony_ci return -EINVAL; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* 62262306a36Sopenharmony_ci * In general, the count of PossibleStates[] must be > 0. 62362306a36Sopenharmony_ci * Also, the old variant lacks the Size property, so we may need to 62462306a36Sopenharmony_ci * reduce the value of last_prop by 1 when doing arithmetic with it. 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_ci if (elem_count < last_prop - !is_new + 1) 62762306a36Sopenharmony_ci return -EINVAL; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci count = elem_count - (last_prop - !is_new); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci for (i = 0; i < elem_count && prop <= last_prop; i++, prop++) { 63262306a36Sopenharmony_ci type = elements[i].type; 63362306a36Sopenharmony_ci valid_type = hp_wmi_property_map[prop]; 63462306a36Sopenharmony_ci if (type != valid_type) 63562306a36Sopenharmony_ci return -EINVAL; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci switch (prop) { 63862306a36Sopenharmony_ci case HP_WMI_PROPERTY_OPERATIONAL_STATUS: 63962306a36Sopenharmony_ci /* Old variant: CurrentState follows OperationalStatus. */ 64062306a36Sopenharmony_ci if (!is_new) 64162306a36Sopenharmony_ci prop = HP_WMI_PROPERTY_CURRENT_STATE - 1; 64262306a36Sopenharmony_ci break; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci case HP_WMI_PROPERTY_SIZE: 64562306a36Sopenharmony_ci /* New variant: Size == count of PossibleStates[]. */ 64662306a36Sopenharmony_ci if (count != elements[i].integer.value) 64762306a36Sopenharmony_ci return -EINVAL; 64862306a36Sopenharmony_ci break; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci case HP_WMI_PROPERTY_POSSIBLE_STATES: 65162306a36Sopenharmony_ci /* PossibleStates[0] has already been type-checked. */ 65262306a36Sopenharmony_ci for (j = 0; i + 1 < elem_count && j + 1 < count; j++) { 65362306a36Sopenharmony_ci type = elements[++i].type; 65462306a36Sopenharmony_ci if (type != valid_type) 65562306a36Sopenharmony_ci return -EINVAL; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* Old variant: BaseUnits follows PossibleStates[]. */ 65962306a36Sopenharmony_ci if (!is_new) 66062306a36Sopenharmony_ci prop = HP_WMI_PROPERTY_BASE_UNITS - 1; 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci case HP_WMI_PROPERTY_CURRENT_STATE: 66462306a36Sopenharmony_ci /* Old variant: PossibleStates[] follows CurrentState. */ 66562306a36Sopenharmony_ci if (!is_new) 66662306a36Sopenharmony_ci prop = HP_WMI_PROPERTY_POSSIBLE_STATES - 1; 66762306a36Sopenharmony_ci break; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (prop != last_prop + 1) 67262306a36Sopenharmony_ci return -EINVAL; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci *out_size = count; 67562306a36Sopenharmony_ci *out_is_new = is_new; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci return 0; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic int 68162306a36Sopenharmony_cinumeric_sensor_is_connected(const struct hp_wmi_numeric_sensor *nsensor) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci u32 operational_status = nsensor->operational_status; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return operational_status != HP_WMI_STATUS_NO_CONTACT; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int numeric_sensor_has_fault(const struct hp_wmi_numeric_sensor *nsensor) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci u32 operational_status = nsensor->operational_status; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci switch (operational_status) { 69362306a36Sopenharmony_ci case HP_WMI_STATUS_DEGRADED: 69462306a36Sopenharmony_ci case HP_WMI_STATUS_STRESSED: /* e.g. Overload, overtemp. */ 69562306a36Sopenharmony_ci case HP_WMI_STATUS_PREDICTIVE_FAILURE: /* e.g. Fan removed. */ 69662306a36Sopenharmony_ci case HP_WMI_STATUS_ERROR: 69762306a36Sopenharmony_ci case HP_WMI_STATUS_NON_RECOVERABLE_ERROR: 69862306a36Sopenharmony_ci case HP_WMI_STATUS_NO_CONTACT: 69962306a36Sopenharmony_ci case HP_WMI_STATUS_LOST_COMMUNICATION: 70062306a36Sopenharmony_ci case HP_WMI_STATUS_ABORTED: 70162306a36Sopenharmony_ci case HP_WMI_STATUS_SUPPORTING_ENTITY_IN_ERROR: 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* Assume combination by addition; bitwise OR doesn't make sense. */ 70462306a36Sopenharmony_ci case HP_WMI_STATUS_COMPLETED + HP_WMI_STATUS_DEGRADED: 70562306a36Sopenharmony_ci case HP_WMI_STATUS_COMPLETED + HP_WMI_STATUS_ERROR: 70662306a36Sopenharmony_ci return true; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return false; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci/* scale_numeric_sensor - scale sensor reading for hwmon */ 71362306a36Sopenharmony_cistatic long scale_numeric_sensor(const struct hp_wmi_numeric_sensor *nsensor) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci u32 current_reading = nsensor->current_reading; 71662306a36Sopenharmony_ci s32 unit_modifier = nsensor->unit_modifier; 71762306a36Sopenharmony_ci u32 sensor_type = nsensor->sensor_type; 71862306a36Sopenharmony_ci u32 base_units = nsensor->base_units; 71962306a36Sopenharmony_ci s32 target_modifier; 72062306a36Sopenharmony_ci long val; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* Fan readings are in RPM units; others are in milliunits. */ 72362306a36Sopenharmony_ci target_modifier = sensor_type == HP_WMI_TYPE_AIR_FLOW ? 0 : -3; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci val = current_reading; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci for (; unit_modifier < target_modifier; unit_modifier++) 72862306a36Sopenharmony_ci val = DIV_ROUND_CLOSEST(val, 10); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci for (; unit_modifier > target_modifier; unit_modifier--) { 73162306a36Sopenharmony_ci if (val > LONG_MAX / 10) { 73262306a36Sopenharmony_ci val = LONG_MAX; 73362306a36Sopenharmony_ci break; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci val *= 10; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (sensor_type == HP_WMI_TYPE_TEMPERATURE) { 73962306a36Sopenharmony_ci switch (base_units) { 74062306a36Sopenharmony_ci case HP_WMI_UNITS_DEGREES_F: 74162306a36Sopenharmony_ci val -= MILLI * 32; 74262306a36Sopenharmony_ci val = val <= LONG_MAX / 5 ? 74362306a36Sopenharmony_ci DIV_ROUND_CLOSEST(val * 5, 9) : 74462306a36Sopenharmony_ci DIV_ROUND_CLOSEST(val, 9) * 5; 74562306a36Sopenharmony_ci break; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci case HP_WMI_UNITS_DEGREES_K: 74862306a36Sopenharmony_ci val = milli_kelvin_to_millicelsius(val); 74962306a36Sopenharmony_ci break; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci return val; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci/* 75762306a36Sopenharmony_ci * classify_numeric_sensor - classify a numeric sensor 75862306a36Sopenharmony_ci * @nsensor: pointer to numeric sensor struct 75962306a36Sopenharmony_ci * 76062306a36Sopenharmony_ci * Returns an enum hp_wmi_type value on success, 76162306a36Sopenharmony_ci * or a negative value if the sensor type is unsupported. 76262306a36Sopenharmony_ci */ 76362306a36Sopenharmony_cistatic int classify_numeric_sensor(const struct hp_wmi_numeric_sensor *nsensor) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci u32 sensor_type = nsensor->sensor_type; 76662306a36Sopenharmony_ci u32 base_units = nsensor->base_units; 76762306a36Sopenharmony_ci const char *name = nsensor->name; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci switch (sensor_type) { 77062306a36Sopenharmony_ci case HP_WMI_TYPE_TEMPERATURE: 77162306a36Sopenharmony_ci /* 77262306a36Sopenharmony_ci * Some systems have sensors named "X Thermal Index" in "Other" 77362306a36Sopenharmony_ci * units. Tested CPU sensor examples were found to be in °C, 77462306a36Sopenharmony_ci * albeit perhaps "differently" accurate; e.g. readings were 77562306a36Sopenharmony_ci * reliably -6°C vs. coretemp on a HP Compaq Elite 8300, and 77662306a36Sopenharmony_ci * +8°C on an EliteOne G1 800. But this is still within the 77762306a36Sopenharmony_ci * realm of plausibility for cheaply implemented motherboard 77862306a36Sopenharmony_ci * sensors, and chassis readings were about as expected. 77962306a36Sopenharmony_ci */ 78062306a36Sopenharmony_ci if ((base_units == HP_WMI_UNITS_OTHER && 78162306a36Sopenharmony_ci strstr(name, HP_WMI_PATTERN_TEMP_SENSOR)) || 78262306a36Sopenharmony_ci base_units == HP_WMI_UNITS_DEGREES_C || 78362306a36Sopenharmony_ci base_units == HP_WMI_UNITS_DEGREES_F || 78462306a36Sopenharmony_ci base_units == HP_WMI_UNITS_DEGREES_K) 78562306a36Sopenharmony_ci return HP_WMI_TYPE_TEMPERATURE; 78662306a36Sopenharmony_ci break; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci case HP_WMI_TYPE_VOLTAGE: 78962306a36Sopenharmony_ci if (base_units == HP_WMI_UNITS_VOLTS) 79062306a36Sopenharmony_ci return HP_WMI_TYPE_VOLTAGE; 79162306a36Sopenharmony_ci break; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci case HP_WMI_TYPE_CURRENT: 79462306a36Sopenharmony_ci if (base_units == HP_WMI_UNITS_AMPS) 79562306a36Sopenharmony_ci return HP_WMI_TYPE_CURRENT; 79662306a36Sopenharmony_ci break; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci case HP_WMI_TYPE_AIR_FLOW: 79962306a36Sopenharmony_ci /* 80062306a36Sopenharmony_ci * Strangely, HP considers fan RPM sensor type to be 80162306a36Sopenharmony_ci * "Air Flow" instead of the more intuitive "Tachometer". 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ci if (base_units == HP_WMI_UNITS_RPM) 80462306a36Sopenharmony_ci return HP_WMI_TYPE_AIR_FLOW; 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return -EINVAL; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic int 81262306a36Sopenharmony_cipopulate_numeric_sensor_from_wobj(struct device *dev, 81362306a36Sopenharmony_ci struct hp_wmi_numeric_sensor *nsensor, 81462306a36Sopenharmony_ci union acpi_object *wobj, bool *out_is_new) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci int last_prop = HP_WMI_PROPERTY_RATE_UNITS; 81762306a36Sopenharmony_ci int prop = HP_WMI_PROPERTY_NAME; 81862306a36Sopenharmony_ci const char **possible_states; 81962306a36Sopenharmony_ci union acpi_object *element; 82062306a36Sopenharmony_ci acpi_object_type type; 82162306a36Sopenharmony_ci char *string; 82262306a36Sopenharmony_ci bool is_new; 82362306a36Sopenharmony_ci u32 value; 82462306a36Sopenharmony_ci u8 size; 82562306a36Sopenharmony_ci int err; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci err = check_numeric_sensor_wobj(wobj, &size, &is_new); 82862306a36Sopenharmony_ci if (err) 82962306a36Sopenharmony_ci return err; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci possible_states = devm_kcalloc(dev, size, sizeof(*possible_states), 83262306a36Sopenharmony_ci GFP_KERNEL); 83362306a36Sopenharmony_ci if (!possible_states) 83462306a36Sopenharmony_ci return -ENOMEM; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci element = wobj->package.elements; 83762306a36Sopenharmony_ci nsensor->possible_states = possible_states; 83862306a36Sopenharmony_ci nsensor->size = size; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (!is_new) 84162306a36Sopenharmony_ci last_prop = HP_WMI_PROPERTY_CURRENT_READING; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci for (; prop <= last_prop; prop++) { 84462306a36Sopenharmony_ci type = hp_wmi_property_map[prop]; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci err = extract_acpi_value(dev, element, type, &value, &string); 84762306a36Sopenharmony_ci if (err) 84862306a36Sopenharmony_ci return err; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci element++; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci switch (prop) { 85362306a36Sopenharmony_ci case HP_WMI_PROPERTY_NAME: 85462306a36Sopenharmony_ci nsensor->name = string; 85562306a36Sopenharmony_ci break; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci case HP_WMI_PROPERTY_DESCRIPTION: 85862306a36Sopenharmony_ci nsensor->description = string; 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci case HP_WMI_PROPERTY_SENSOR_TYPE: 86262306a36Sopenharmony_ci if (value > HP_WMI_TYPE_AIR_FLOW) 86362306a36Sopenharmony_ci return -EINVAL; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci nsensor->sensor_type = value; 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci case HP_WMI_PROPERTY_OTHER_SENSOR_TYPE: 86962306a36Sopenharmony_ci nsensor->other_sensor_type = string; 87062306a36Sopenharmony_ci break; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci case HP_WMI_PROPERTY_OPERATIONAL_STATUS: 87362306a36Sopenharmony_ci nsensor->operational_status = value; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci /* Old variant: CurrentState follows OperationalStatus. */ 87662306a36Sopenharmony_ci if (!is_new) 87762306a36Sopenharmony_ci prop = HP_WMI_PROPERTY_CURRENT_STATE - 1; 87862306a36Sopenharmony_ci break; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci case HP_WMI_PROPERTY_SIZE: 88162306a36Sopenharmony_ci break; /* Already set. */ 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci case HP_WMI_PROPERTY_POSSIBLE_STATES: 88462306a36Sopenharmony_ci *possible_states++ = string; 88562306a36Sopenharmony_ci if (--size) 88662306a36Sopenharmony_ci prop--; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* Old variant: BaseUnits follows PossibleStates[]. */ 88962306a36Sopenharmony_ci if (!is_new && !size) 89062306a36Sopenharmony_ci prop = HP_WMI_PROPERTY_BASE_UNITS - 1; 89162306a36Sopenharmony_ci break; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci case HP_WMI_PROPERTY_CURRENT_STATE: 89462306a36Sopenharmony_ci nsensor->current_state = string; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* Old variant: PossibleStates[] follows CurrentState. */ 89762306a36Sopenharmony_ci if (!is_new) 89862306a36Sopenharmony_ci prop = HP_WMI_PROPERTY_POSSIBLE_STATES - 1; 89962306a36Sopenharmony_ci break; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci case HP_WMI_PROPERTY_BASE_UNITS: 90262306a36Sopenharmony_ci nsensor->base_units = value; 90362306a36Sopenharmony_ci break; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci case HP_WMI_PROPERTY_UNIT_MODIFIER: 90662306a36Sopenharmony_ci /* UnitModifier is signed. */ 90762306a36Sopenharmony_ci nsensor->unit_modifier = (s32)value; 90862306a36Sopenharmony_ci break; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci case HP_WMI_PROPERTY_CURRENT_READING: 91162306a36Sopenharmony_ci nsensor->current_reading = value; 91262306a36Sopenharmony_ci break; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci case HP_WMI_PROPERTY_RATE_UNITS: 91562306a36Sopenharmony_ci nsensor->rate_units = value; 91662306a36Sopenharmony_ci break; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci default: 91962306a36Sopenharmony_ci return -EINVAL; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci *out_is_new = is_new; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci return 0; 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci/* update_numeric_sensor_from_wobj - update fungible sensor properties */ 92962306a36Sopenharmony_cistatic void 93062306a36Sopenharmony_ciupdate_numeric_sensor_from_wobj(struct device *dev, 93162306a36Sopenharmony_ci struct hp_wmi_numeric_sensor *nsensor, 93262306a36Sopenharmony_ci const union acpi_object *wobj) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci const union acpi_object *elements; 93562306a36Sopenharmony_ci const union acpi_object *element; 93662306a36Sopenharmony_ci const char *new_string; 93762306a36Sopenharmony_ci char *trimmed; 93862306a36Sopenharmony_ci char *string; 93962306a36Sopenharmony_ci bool is_new; 94062306a36Sopenharmony_ci int offset; 94162306a36Sopenharmony_ci u8 size; 94262306a36Sopenharmony_ci int err; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci err = check_numeric_sensor_wobj(wobj, &size, &is_new); 94562306a36Sopenharmony_ci if (err) 94662306a36Sopenharmony_ci return; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci elements = wobj->package.elements; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci element = &elements[HP_WMI_PROPERTY_OPERATIONAL_STATUS]; 95162306a36Sopenharmony_ci nsensor->operational_status = element->integer.value; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* 95462306a36Sopenharmony_ci * In general, an index offset is needed after PossibleStates[0]. 95562306a36Sopenharmony_ci * On a new variant, CurrentState is after PossibleStates[]. This is 95662306a36Sopenharmony_ci * not the case on an old variant, but we still need to offset the 95762306a36Sopenharmony_ci * read because CurrentState is where Size would be on a new variant. 95862306a36Sopenharmony_ci */ 95962306a36Sopenharmony_ci offset = is_new ? size - 1 : -2; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci element = &elements[HP_WMI_PROPERTY_CURRENT_STATE + offset]; 96262306a36Sopenharmony_ci string = element->type == ACPI_TYPE_BUFFER ? 96362306a36Sopenharmony_ci convert_raw_wmi_string(element->buffer.pointer) : 96462306a36Sopenharmony_ci element->string.pointer; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (string) { 96762306a36Sopenharmony_ci trimmed = strim(string); 96862306a36Sopenharmony_ci if (strcmp(trimmed, nsensor->current_state)) { 96962306a36Sopenharmony_ci new_string = hp_wmi_strdup(dev, trimmed); 97062306a36Sopenharmony_ci if (new_string) { 97162306a36Sopenharmony_ci devm_kfree(dev, nsensor->current_state); 97262306a36Sopenharmony_ci nsensor->current_state = new_string; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci if (element->type == ACPI_TYPE_BUFFER) 97662306a36Sopenharmony_ci kfree(string); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* Old variant: -2 (not -1) because it lacks the Size property. */ 98062306a36Sopenharmony_ci if (!is_new) 98162306a36Sopenharmony_ci offset = (int)size - 2; /* size is > 0, i.e. may be 1. */ 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci element = &elements[HP_WMI_PROPERTY_UNIT_MODIFIER + offset]; 98462306a36Sopenharmony_ci nsensor->unit_modifier = (s32)element->integer.value; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci element = &elements[HP_WMI_PROPERTY_CURRENT_READING + offset]; 98762306a36Sopenharmony_ci nsensor->current_reading = element->integer.value; 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci/* 99162306a36Sopenharmony_ci * check_platform_events_wobj - validate a HPBIOS_PlatformEvents instance 99262306a36Sopenharmony_ci * @wobj: pointer to WMI object instance to check 99362306a36Sopenharmony_ci * 99462306a36Sopenharmony_ci * Returns 0 on success, or a negative error code on error. 99562306a36Sopenharmony_ci */ 99662306a36Sopenharmony_cistatic int check_platform_events_wobj(const union acpi_object *wobj) 99762306a36Sopenharmony_ci{ 99862306a36Sopenharmony_ci return check_wobj(wobj, hp_wmi_platform_events_property_map, 99962306a36Sopenharmony_ci HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS); 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic int 100362306a36Sopenharmony_cipopulate_platform_events_from_wobj(struct device *dev, 100462306a36Sopenharmony_ci struct hp_wmi_platform_events *pevents, 100562306a36Sopenharmony_ci union acpi_object *wobj) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci int last_prop = HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS; 100862306a36Sopenharmony_ci int prop = HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME; 100962306a36Sopenharmony_ci union acpi_object *element; 101062306a36Sopenharmony_ci acpi_object_type type; 101162306a36Sopenharmony_ci char *string; 101262306a36Sopenharmony_ci u32 value; 101362306a36Sopenharmony_ci int err; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci err = check_platform_events_wobj(wobj); 101662306a36Sopenharmony_ci if (err) 101762306a36Sopenharmony_ci return err; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci element = wobj->package.elements; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci for (; prop <= last_prop; prop++, element++) { 102262306a36Sopenharmony_ci type = hp_wmi_platform_events_property_map[prop]; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci err = extract_acpi_value(dev, element, type, &value, &string); 102562306a36Sopenharmony_ci if (err) 102662306a36Sopenharmony_ci return err; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci switch (prop) { 102962306a36Sopenharmony_ci case HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME: 103062306a36Sopenharmony_ci pevents->name = string; 103162306a36Sopenharmony_ci break; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci case HP_WMI_PLATFORM_EVENTS_PROPERTY_DESCRIPTION: 103462306a36Sopenharmony_ci pevents->description = string; 103562306a36Sopenharmony_ci break; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci case HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_NAMESPACE: 103862306a36Sopenharmony_ci if (strcasecmp(HP_WMI_EVENT_NAMESPACE, string)) 103962306a36Sopenharmony_ci return -EINVAL; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci pevents->source_namespace = string; 104262306a36Sopenharmony_ci break; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci case HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_CLASS: 104562306a36Sopenharmony_ci if (strcasecmp(HP_WMI_EVENT_CLASS, string)) 104662306a36Sopenharmony_ci return -EINVAL; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci pevents->source_class = string; 104962306a36Sopenharmony_ci break; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci case HP_WMI_PLATFORM_EVENTS_PROPERTY_CATEGORY: 105262306a36Sopenharmony_ci pevents->category = value; 105362306a36Sopenharmony_ci break; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci case HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_SEVERITY: 105662306a36Sopenharmony_ci pevents->possible_severity = value; 105762306a36Sopenharmony_ci break; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci case HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS: 106062306a36Sopenharmony_ci pevents->possible_status = value; 106162306a36Sopenharmony_ci break; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci default: 106462306a36Sopenharmony_ci return -EINVAL; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci return 0; 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci/* 107262306a36Sopenharmony_ci * check_event_wobj - validate a HPBIOS_BIOSEvent instance 107362306a36Sopenharmony_ci * @wobj: pointer to WMI object instance to check 107462306a36Sopenharmony_ci * 107562306a36Sopenharmony_ci * Returns 0 on success, or a negative error code on error. 107662306a36Sopenharmony_ci */ 107762306a36Sopenharmony_cistatic int check_event_wobj(const union acpi_object *wobj) 107862306a36Sopenharmony_ci{ 107962306a36Sopenharmony_ci return check_wobj(wobj, hp_wmi_event_property_map, 108062306a36Sopenharmony_ci HP_WMI_EVENT_PROPERTY_STATUS); 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic int populate_event_from_wobj(struct device *dev, 108462306a36Sopenharmony_ci struct hp_wmi_event *event, 108562306a36Sopenharmony_ci union acpi_object *wobj) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci int prop = HP_WMI_EVENT_PROPERTY_NAME; 108862306a36Sopenharmony_ci union acpi_object *element; 108962306a36Sopenharmony_ci acpi_object_type type; 109062306a36Sopenharmony_ci char *string; 109162306a36Sopenharmony_ci u32 value; 109262306a36Sopenharmony_ci int err; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci err = check_event_wobj(wobj); 109562306a36Sopenharmony_ci if (err) 109662306a36Sopenharmony_ci return err; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci element = wobj->package.elements; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci for (; prop <= HP_WMI_EVENT_PROPERTY_CATEGORY; prop++, element++) { 110162306a36Sopenharmony_ci type = hp_wmi_event_property_map[prop]; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci err = extract_acpi_value(dev, element, type, &value, &string); 110462306a36Sopenharmony_ci if (err) 110562306a36Sopenharmony_ci return err; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci switch (prop) { 110862306a36Sopenharmony_ci case HP_WMI_EVENT_PROPERTY_NAME: 110962306a36Sopenharmony_ci event->name = string; 111062306a36Sopenharmony_ci break; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci case HP_WMI_EVENT_PROPERTY_DESCRIPTION: 111362306a36Sopenharmony_ci event->description = string; 111462306a36Sopenharmony_ci break; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci case HP_WMI_EVENT_PROPERTY_CATEGORY: 111762306a36Sopenharmony_ci event->category = value; 111862306a36Sopenharmony_ci break; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci default: 112162306a36Sopenharmony_ci return -EINVAL; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci return 0; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci/* 112962306a36Sopenharmony_ci * classify_event - classify an event 113062306a36Sopenharmony_ci * @name: event name 113162306a36Sopenharmony_ci * @category: event category 113262306a36Sopenharmony_ci * 113362306a36Sopenharmony_ci * Classify instances of both HPBIOS_PlatformEvents and HPBIOS_BIOSEvent from 113462306a36Sopenharmony_ci * property values. Recognition criteria are based on multiple ACPI dumps [3]. 113562306a36Sopenharmony_ci * 113662306a36Sopenharmony_ci * Returns an enum hp_wmi_type value on success, 113762306a36Sopenharmony_ci * or a negative value if the event type is unsupported. 113862306a36Sopenharmony_ci */ 113962306a36Sopenharmony_cistatic int classify_event(const char *event_name, u32 category) 114062306a36Sopenharmony_ci{ 114162306a36Sopenharmony_ci if (category != HP_WMI_CATEGORY_SENSOR) 114262306a36Sopenharmony_ci return -EINVAL; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci /* Fan events have Name "X Stall". */ 114562306a36Sopenharmony_ci if (strstr(event_name, HP_WMI_PATTERN_FAN_ALARM)) 114662306a36Sopenharmony_ci return HP_WMI_TYPE_AIR_FLOW; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci /* Intrusion events have Name "Hood Intrusion". */ 114962306a36Sopenharmony_ci if (!strcmp(event_name, HP_WMI_PATTERN_INTRUSION_ALARM)) 115062306a36Sopenharmony_ci return HP_WMI_TYPE_INTRUSION; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci /* 115362306a36Sopenharmony_ci * Temperature events have Name either "Thermal Caution" or 115462306a36Sopenharmony_ci * "Thermal Critical". Deal only with "Thermal Critical" events. 115562306a36Sopenharmony_ci * 115662306a36Sopenharmony_ci * "Thermal Caution" events have Status "Stressed", informing us that 115762306a36Sopenharmony_ci * the OperationalStatus of the related sensor has become "Stressed". 115862306a36Sopenharmony_ci * However, this is already a fault condition that will clear itself 115962306a36Sopenharmony_ci * when the sensor recovers, so we have no further interest in them. 116062306a36Sopenharmony_ci */ 116162306a36Sopenharmony_ci if (!strcmp(event_name, HP_WMI_PATTERN_TEMP_ALARM)) 116262306a36Sopenharmony_ci return HP_WMI_TYPE_TEMPERATURE; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci return -EINVAL; 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci/* 116862306a36Sopenharmony_ci * interpret_info - interpret sensor for hwmon 116962306a36Sopenharmony_ci * @info: pointer to sensor info struct 117062306a36Sopenharmony_ci * 117162306a36Sopenharmony_ci * Should be called after the numeric sensor member has been updated. 117262306a36Sopenharmony_ci */ 117362306a36Sopenharmony_cistatic void interpret_info(struct hp_wmi_info *info) 117462306a36Sopenharmony_ci{ 117562306a36Sopenharmony_ci const struct hp_wmi_numeric_sensor *nsensor = &info->nsensor; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci info->cached_val = scale_numeric_sensor(nsensor); 117862306a36Sopenharmony_ci info->last_updated = jiffies; 117962306a36Sopenharmony_ci} 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci/* 118262306a36Sopenharmony_ci * hp_wmi_update_info - poll WMI to update sensor info 118362306a36Sopenharmony_ci * @state: pointer to driver state 118462306a36Sopenharmony_ci * @info: pointer to sensor info struct 118562306a36Sopenharmony_ci * 118662306a36Sopenharmony_ci * Returns 0 on success, or a negative error code on error. 118762306a36Sopenharmony_ci */ 118862306a36Sopenharmony_cistatic int hp_wmi_update_info(struct hp_wmi_sensors *state, 118962306a36Sopenharmony_ci struct hp_wmi_info *info) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci struct hp_wmi_numeric_sensor *nsensor = &info->nsensor; 119262306a36Sopenharmony_ci struct device *dev = &state->wdev->dev; 119362306a36Sopenharmony_ci const union acpi_object *wobj; 119462306a36Sopenharmony_ci u8 instance = info->instance; 119562306a36Sopenharmony_ci int ret = 0; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci if (time_after(jiffies, info->last_updated + HZ)) { 119862306a36Sopenharmony_ci mutex_lock(&state->lock); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci wobj = hp_wmi_get_wobj(HP_WMI_NUMERIC_SENSOR_GUID, instance); 120162306a36Sopenharmony_ci if (!wobj) { 120262306a36Sopenharmony_ci ret = -EIO; 120362306a36Sopenharmony_ci goto out_unlock; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci update_numeric_sensor_from_wobj(dev, nsensor, wobj); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci interpret_info(info); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci kfree(wobj); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ciout_unlock: 121362306a36Sopenharmony_ci mutex_unlock(&state->lock); 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci return ret; 121762306a36Sopenharmony_ci} 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_cistatic int basic_string_show(struct seq_file *seqf, void *ignored) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci const char *str = seqf->private; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci seq_printf(seqf, "%s\n", str); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci return 0; 122662306a36Sopenharmony_ci} 122762306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(basic_string); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cistatic int fungible_show(struct seq_file *seqf, enum hp_wmi_property prop) 123062306a36Sopenharmony_ci{ 123162306a36Sopenharmony_ci struct hp_wmi_numeric_sensor *nsensor; 123262306a36Sopenharmony_ci struct hp_wmi_sensors *state; 123362306a36Sopenharmony_ci struct hp_wmi_info *info; 123462306a36Sopenharmony_ci int err; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci info = seqf->private; 123762306a36Sopenharmony_ci state = info->state; 123862306a36Sopenharmony_ci nsensor = &info->nsensor; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci err = hp_wmi_update_info(state, info); 124162306a36Sopenharmony_ci if (err) 124262306a36Sopenharmony_ci return err; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci switch (prop) { 124562306a36Sopenharmony_ci case HP_WMI_PROPERTY_OPERATIONAL_STATUS: 124662306a36Sopenharmony_ci seq_printf(seqf, "%u\n", nsensor->operational_status); 124762306a36Sopenharmony_ci break; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci case HP_WMI_PROPERTY_CURRENT_STATE: 125062306a36Sopenharmony_ci seq_printf(seqf, "%s\n", nsensor->current_state); 125162306a36Sopenharmony_ci break; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci case HP_WMI_PROPERTY_UNIT_MODIFIER: 125462306a36Sopenharmony_ci seq_printf(seqf, "%d\n", nsensor->unit_modifier); 125562306a36Sopenharmony_ci break; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci case HP_WMI_PROPERTY_CURRENT_READING: 125862306a36Sopenharmony_ci seq_printf(seqf, "%u\n", nsensor->current_reading); 125962306a36Sopenharmony_ci break; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci default: 126262306a36Sopenharmony_ci return -EOPNOTSUPP; 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci return 0; 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_cistatic int operational_status_show(struct seq_file *seqf, void *ignored) 126962306a36Sopenharmony_ci{ 127062306a36Sopenharmony_ci return fungible_show(seqf, HP_WMI_PROPERTY_OPERATIONAL_STATUS); 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(operational_status); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic int current_state_show(struct seq_file *seqf, void *ignored) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci return fungible_show(seqf, HP_WMI_PROPERTY_CURRENT_STATE); 127762306a36Sopenharmony_ci} 127862306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(current_state); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_cistatic int possible_states_show(struct seq_file *seqf, void *ignored) 128162306a36Sopenharmony_ci{ 128262306a36Sopenharmony_ci struct hp_wmi_numeric_sensor *nsensor = seqf->private; 128362306a36Sopenharmony_ci u8 i; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci for (i = 0; i < nsensor->size; i++) 128662306a36Sopenharmony_ci seq_printf(seqf, "%s%s", i ? "," : "", 128762306a36Sopenharmony_ci nsensor->possible_states[i]); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci seq_puts(seqf, "\n"); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci return 0; 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(possible_states); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_cistatic int unit_modifier_show(struct seq_file *seqf, void *ignored) 129662306a36Sopenharmony_ci{ 129762306a36Sopenharmony_ci return fungible_show(seqf, HP_WMI_PROPERTY_UNIT_MODIFIER); 129862306a36Sopenharmony_ci} 129962306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(unit_modifier); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_cistatic int current_reading_show(struct seq_file *seqf, void *ignored) 130262306a36Sopenharmony_ci{ 130362306a36Sopenharmony_ci return fungible_show(seqf, HP_WMI_PROPERTY_CURRENT_READING); 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(current_reading); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci/* hp_wmi_devm_debugfs_remove - devm callback for debugfs cleanup */ 130862306a36Sopenharmony_cistatic void hp_wmi_devm_debugfs_remove(void *res) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci debugfs_remove_recursive(res); 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci/* hp_wmi_debugfs_init - create and populate debugfs directory tree */ 131462306a36Sopenharmony_cistatic void hp_wmi_debugfs_init(struct device *dev, struct hp_wmi_info *info, 131562306a36Sopenharmony_ci struct hp_wmi_platform_events *pevents, 131662306a36Sopenharmony_ci u8 icount, u8 pcount, bool is_new) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci struct hp_wmi_numeric_sensor *nsensor; 131962306a36Sopenharmony_ci char buf[HP_WMI_MAX_STR_SIZE]; 132062306a36Sopenharmony_ci struct dentry *debugfs; 132162306a36Sopenharmony_ci struct dentry *entries; 132262306a36Sopenharmony_ci struct dentry *dir; 132362306a36Sopenharmony_ci int err; 132462306a36Sopenharmony_ci u8 i; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci /* dev_name() gives a not-very-friendly GUID for WMI devices. */ 132762306a36Sopenharmony_ci scnprintf(buf, sizeof(buf), "hp-wmi-sensors-%u", dev->id); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci debugfs = debugfs_create_dir(buf, NULL); 133062306a36Sopenharmony_ci if (IS_ERR(debugfs)) 133162306a36Sopenharmony_ci return; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci err = devm_add_action_or_reset(dev, hp_wmi_devm_debugfs_remove, 133462306a36Sopenharmony_ci debugfs); 133562306a36Sopenharmony_ci if (err) 133662306a36Sopenharmony_ci return; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci entries = debugfs_create_dir("sensor", debugfs); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci for (i = 0; i < icount; i++, info++) { 134162306a36Sopenharmony_ci nsensor = &info->nsensor; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci scnprintf(buf, sizeof(buf), "%u", i); 134462306a36Sopenharmony_ci dir = debugfs_create_dir(buf, entries); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci debugfs_create_file("name", 0444, dir, 134762306a36Sopenharmony_ci (void *)nsensor->name, 134862306a36Sopenharmony_ci &basic_string_fops); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci debugfs_create_file("description", 0444, dir, 135162306a36Sopenharmony_ci (void *)nsensor->description, 135262306a36Sopenharmony_ci &basic_string_fops); 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci debugfs_create_u32("sensor_type", 0444, dir, 135562306a36Sopenharmony_ci &nsensor->sensor_type); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci debugfs_create_file("other_sensor_type", 0444, dir, 135862306a36Sopenharmony_ci (void *)nsensor->other_sensor_type, 135962306a36Sopenharmony_ci &basic_string_fops); 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci debugfs_create_file("operational_status", 0444, dir, 136262306a36Sopenharmony_ci info, &operational_status_fops); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci debugfs_create_file("possible_states", 0444, dir, 136562306a36Sopenharmony_ci nsensor, &possible_states_fops); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci debugfs_create_file("current_state", 0444, dir, 136862306a36Sopenharmony_ci info, ¤t_state_fops); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci debugfs_create_u32("base_units", 0444, dir, 137162306a36Sopenharmony_ci &nsensor->base_units); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci debugfs_create_file("unit_modifier", 0444, dir, 137462306a36Sopenharmony_ci info, &unit_modifier_fops); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci debugfs_create_file("current_reading", 0444, dir, 137762306a36Sopenharmony_ci info, ¤t_reading_fops); 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (is_new) 138062306a36Sopenharmony_ci debugfs_create_u32("rate_units", 0444, dir, 138162306a36Sopenharmony_ci &nsensor->rate_units); 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci if (!pcount) 138562306a36Sopenharmony_ci return; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci entries = debugfs_create_dir("platform_events", debugfs); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci for (i = 0; i < pcount; i++, pevents++) { 139062306a36Sopenharmony_ci scnprintf(buf, sizeof(buf), "%u", i); 139162306a36Sopenharmony_ci dir = debugfs_create_dir(buf, entries); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci debugfs_create_file("name", 0444, dir, 139462306a36Sopenharmony_ci (void *)pevents->name, 139562306a36Sopenharmony_ci &basic_string_fops); 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci debugfs_create_file("description", 0444, dir, 139862306a36Sopenharmony_ci (void *)pevents->description, 139962306a36Sopenharmony_ci &basic_string_fops); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci debugfs_create_file("source_namespace", 0444, dir, 140262306a36Sopenharmony_ci (void *)pevents->source_namespace, 140362306a36Sopenharmony_ci &basic_string_fops); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci debugfs_create_file("source_class", 0444, dir, 140662306a36Sopenharmony_ci (void *)pevents->source_class, 140762306a36Sopenharmony_ci &basic_string_fops); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci debugfs_create_u32("category", 0444, dir, 141062306a36Sopenharmony_ci &pevents->category); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci debugfs_create_u32("possible_severity", 0444, dir, 141362306a36Sopenharmony_ci &pevents->possible_severity); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci debugfs_create_u32("possible_status", 0444, dir, 141662306a36Sopenharmony_ci &pevents->possible_status); 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic umode_t hp_wmi_hwmon_is_visible(const void *drvdata, 142162306a36Sopenharmony_ci enum hwmon_sensor_types type, 142262306a36Sopenharmony_ci u32 attr, int channel) 142362306a36Sopenharmony_ci{ 142462306a36Sopenharmony_ci const struct hp_wmi_sensors *state = drvdata; 142562306a36Sopenharmony_ci const struct hp_wmi_info *info; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci if (type == hwmon_intrusion) 142862306a36Sopenharmony_ci return state->has_intrusion ? 0644 : 0; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci if (!state->info_map[type] || !state->info_map[type][channel]) 143162306a36Sopenharmony_ci return 0; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci info = state->info_map[type][channel]; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci if ((type == hwmon_temp && attr == hwmon_temp_alarm) || 143662306a36Sopenharmony_ci (type == hwmon_fan && attr == hwmon_fan_alarm)) 143762306a36Sopenharmony_ci return info->has_alarm ? 0444 : 0; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci return 0444; 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_cistatic int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 144362306a36Sopenharmony_ci u32 attr, int channel, long *out_val) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci struct hp_wmi_sensors *state = dev_get_drvdata(dev); 144662306a36Sopenharmony_ci const struct hp_wmi_numeric_sensor *nsensor; 144762306a36Sopenharmony_ci struct hp_wmi_info *info; 144862306a36Sopenharmony_ci int err; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci if (type == hwmon_intrusion) { 145162306a36Sopenharmony_ci *out_val = state->intrusion ? 1 : 0; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci return 0; 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci info = state->info_map[type][channel]; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci if ((type == hwmon_temp && attr == hwmon_temp_alarm) || 145962306a36Sopenharmony_ci (type == hwmon_fan && attr == hwmon_fan_alarm)) { 146062306a36Sopenharmony_ci *out_val = info->alarm ? 1 : 0; 146162306a36Sopenharmony_ci info->alarm = false; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci return 0; 146462306a36Sopenharmony_ci } 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci nsensor = &info->nsensor; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci err = hp_wmi_update_info(state, info); 146962306a36Sopenharmony_ci if (err) 147062306a36Sopenharmony_ci return err; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci if ((type == hwmon_temp && attr == hwmon_temp_fault) || 147362306a36Sopenharmony_ci (type == hwmon_fan && attr == hwmon_fan_fault)) 147462306a36Sopenharmony_ci *out_val = numeric_sensor_has_fault(nsensor); 147562306a36Sopenharmony_ci else 147662306a36Sopenharmony_ci *out_val = info->cached_val; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci return 0; 147962306a36Sopenharmony_ci} 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_cistatic int hp_wmi_hwmon_read_string(struct device *dev, 148262306a36Sopenharmony_ci enum hwmon_sensor_types type, u32 attr, 148362306a36Sopenharmony_ci int channel, const char **out_str) 148462306a36Sopenharmony_ci{ 148562306a36Sopenharmony_ci const struct hp_wmi_sensors *state = dev_get_drvdata(dev); 148662306a36Sopenharmony_ci const struct hp_wmi_info *info; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci info = state->info_map[type][channel]; 148962306a36Sopenharmony_ci *out_str = info->nsensor.name; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci return 0; 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 149562306a36Sopenharmony_ci u32 attr, int channel, long val) 149662306a36Sopenharmony_ci{ 149762306a36Sopenharmony_ci struct hp_wmi_sensors *state = dev_get_drvdata(dev); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci if (val) 150062306a36Sopenharmony_ci return -EINVAL; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci mutex_lock(&state->lock); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci state->intrusion = false; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci mutex_unlock(&state->lock); 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci return 0; 150962306a36Sopenharmony_ci} 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_cistatic const struct hwmon_ops hp_wmi_hwmon_ops = { 151262306a36Sopenharmony_ci .is_visible = hp_wmi_hwmon_is_visible, 151362306a36Sopenharmony_ci .read = hp_wmi_hwmon_read, 151462306a36Sopenharmony_ci .read_string = hp_wmi_hwmon_read_string, 151562306a36Sopenharmony_ci .write = hp_wmi_hwmon_write, 151662306a36Sopenharmony_ci}; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_cistatic struct hwmon_chip_info hp_wmi_chip_info = { 151962306a36Sopenharmony_ci .ops = &hp_wmi_hwmon_ops, 152062306a36Sopenharmony_ci .info = NULL, 152162306a36Sopenharmony_ci}; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_cistatic struct hp_wmi_info *match_fan_event(struct hp_wmi_sensors *state, 152462306a36Sopenharmony_ci const char *event_description) 152562306a36Sopenharmony_ci{ 152662306a36Sopenharmony_ci struct hp_wmi_info **ptr_info = state->info_map[hwmon_fan]; 152762306a36Sopenharmony_ci u8 fan_count = state->channel_count[hwmon_fan]; 152862306a36Sopenharmony_ci struct hp_wmi_info *info; 152962306a36Sopenharmony_ci const char *name; 153062306a36Sopenharmony_ci u8 i; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci /* Fan event has Description "X Speed". Sensor has Name "X[ Speed]". */ 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci for (i = 0; i < fan_count; i++, ptr_info++) { 153562306a36Sopenharmony_ci info = *ptr_info; 153662306a36Sopenharmony_ci name = info->nsensor.name; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci if (strstr(event_description, name)) 153962306a36Sopenharmony_ci return info; 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci return NULL; 154362306a36Sopenharmony_ci} 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_cistatic u8 match_temp_events(struct hp_wmi_sensors *state, 154662306a36Sopenharmony_ci const char *event_description, 154762306a36Sopenharmony_ci struct hp_wmi_info *temp_info[]) 154862306a36Sopenharmony_ci{ 154962306a36Sopenharmony_ci struct hp_wmi_info **ptr_info = state->info_map[hwmon_temp]; 155062306a36Sopenharmony_ci u8 temp_count = state->channel_count[hwmon_temp]; 155162306a36Sopenharmony_ci struct hp_wmi_info *info; 155262306a36Sopenharmony_ci const char *name; 155362306a36Sopenharmony_ci u8 count = 0; 155462306a36Sopenharmony_ci bool is_cpu; 155562306a36Sopenharmony_ci bool is_sys; 155662306a36Sopenharmony_ci u8 i; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci /* Description is either "CPU Thermal Index" or "Chassis Thermal Index". */ 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci is_cpu = !strcmp(event_description, HP_WMI_PATTERN_CPU_TEMP); 156162306a36Sopenharmony_ci is_sys = !strcmp(event_description, HP_WMI_PATTERN_SYS_TEMP); 156262306a36Sopenharmony_ci if (!is_cpu && !is_sys) 156362306a36Sopenharmony_ci return 0; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci /* 156662306a36Sopenharmony_ci * CPU event: Match one sensor with Name either "CPU Thermal Index" or 156762306a36Sopenharmony_ci * "CPU Temperature", or multiple with Name(s) "CPU[#] Temperature". 156862306a36Sopenharmony_ci * 156962306a36Sopenharmony_ci * Chassis event: Match one sensor with Name either 157062306a36Sopenharmony_ci * "Chassis Thermal Index" or "System Ambient Temperature". 157162306a36Sopenharmony_ci */ 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci for (i = 0; i < temp_count; i++, ptr_info++) { 157462306a36Sopenharmony_ci info = *ptr_info; 157562306a36Sopenharmony_ci name = info->nsensor.name; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci if ((is_cpu && (!strcmp(name, HP_WMI_PATTERN_CPU_TEMP) || 157862306a36Sopenharmony_ci !strcmp(name, HP_WMI_PATTERN_CPU_TEMP2))) || 157962306a36Sopenharmony_ci (is_sys && (!strcmp(name, HP_WMI_PATTERN_SYS_TEMP) || 158062306a36Sopenharmony_ci !strcmp(name, HP_WMI_PATTERN_SYS_TEMP2)))) { 158162306a36Sopenharmony_ci temp_info[0] = info; 158262306a36Sopenharmony_ci return 1; 158362306a36Sopenharmony_ci } 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci if (is_cpu && (strstr(name, HP_WMI_PATTERN_CPU) && 158662306a36Sopenharmony_ci strstr(name, HP_WMI_PATTERN_TEMP))) 158762306a36Sopenharmony_ci temp_info[count++] = info; 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci return count; 159162306a36Sopenharmony_ci} 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci/* hp_wmi_devm_debugfs_remove - devm callback for WMI event handler removal */ 159462306a36Sopenharmony_cistatic void hp_wmi_devm_notify_remove(void *ignored) 159562306a36Sopenharmony_ci{ 159662306a36Sopenharmony_ci wmi_remove_notify_handler(HP_WMI_EVENT_GUID); 159762306a36Sopenharmony_ci} 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci/* hp_wmi_notify - WMI event notification handler */ 160062306a36Sopenharmony_cistatic void hp_wmi_notify(u32 value, void *context) 160162306a36Sopenharmony_ci{ 160262306a36Sopenharmony_ci struct hp_wmi_info *temp_info[HP_WMI_MAX_INSTANCES] = {}; 160362306a36Sopenharmony_ci struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; 160462306a36Sopenharmony_ci struct hp_wmi_sensors *state = context; 160562306a36Sopenharmony_ci struct device *dev = &state->wdev->dev; 160662306a36Sopenharmony_ci struct hp_wmi_event event = {}; 160762306a36Sopenharmony_ci struct hp_wmi_info *fan_info; 160862306a36Sopenharmony_ci union acpi_object *wobj; 160962306a36Sopenharmony_ci acpi_status err; 161062306a36Sopenharmony_ci int event_type; 161162306a36Sopenharmony_ci u8 count; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci /* 161462306a36Sopenharmony_ci * The following warning may occur in the kernel log: 161562306a36Sopenharmony_ci * 161662306a36Sopenharmony_ci * ACPI Warning: \_SB.WMID._WED: Return type mismatch - 161762306a36Sopenharmony_ci * found Package, expected Integer/String/Buffer 161862306a36Sopenharmony_ci * 161962306a36Sopenharmony_ci * After using [4] to decode BMOF blobs found in [3], careless copying 162062306a36Sopenharmony_ci * of BIOS code seems the most likely explanation for this warning. 162162306a36Sopenharmony_ci * HP_WMI_EVENT_GUID refers to \\.\root\WMI\HPBIOS_BIOSEvent on 162262306a36Sopenharmony_ci * business-class systems, but it refers to \\.\root\WMI\hpqBEvnt on 162362306a36Sopenharmony_ci * non-business-class systems. Per the existing hp-wmi driver, it 162462306a36Sopenharmony_ci * looks like an instance of hpqBEvnt delivered as event data may 162562306a36Sopenharmony_ci * indeed take the form of a raw ACPI_BUFFER on non-business-class 162662306a36Sopenharmony_ci * systems ("may" because ASL shows some BIOSes do strange things). 162762306a36Sopenharmony_ci * 162862306a36Sopenharmony_ci * In any case, we can ignore this warning, because we always validate 162962306a36Sopenharmony_ci * the event data to ensure it is an ACPI_PACKAGE containing a 163062306a36Sopenharmony_ci * HPBIOS_BIOSEvent instance. 163162306a36Sopenharmony_ci */ 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci mutex_lock(&state->lock); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci err = wmi_get_event_data(value, &out); 163662306a36Sopenharmony_ci if (ACPI_FAILURE(err)) 163762306a36Sopenharmony_ci goto out_unlock; 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci wobj = out.pointer; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci err = populate_event_from_wobj(dev, &event, wobj); 164262306a36Sopenharmony_ci if (err) { 164362306a36Sopenharmony_ci dev_warn(dev, "Bad event data (ACPI type %d)\n", wobj->type); 164462306a36Sopenharmony_ci goto out_free_wobj; 164562306a36Sopenharmony_ci } 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci event_type = classify_event(event.name, event.category); 164862306a36Sopenharmony_ci switch (event_type) { 164962306a36Sopenharmony_ci case HP_WMI_TYPE_AIR_FLOW: 165062306a36Sopenharmony_ci fan_info = match_fan_event(state, event.description); 165162306a36Sopenharmony_ci if (fan_info) 165262306a36Sopenharmony_ci fan_info->alarm = true; 165362306a36Sopenharmony_ci break; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci case HP_WMI_TYPE_INTRUSION: 165662306a36Sopenharmony_ci state->intrusion = true; 165762306a36Sopenharmony_ci break; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci case HP_WMI_TYPE_TEMPERATURE: 166062306a36Sopenharmony_ci count = match_temp_events(state, event.description, temp_info); 166162306a36Sopenharmony_ci while (count) 166262306a36Sopenharmony_ci temp_info[--count]->alarm = true; 166362306a36Sopenharmony_ci break; 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci default: 166662306a36Sopenharmony_ci break; 166762306a36Sopenharmony_ci } 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ciout_free_wobj: 167062306a36Sopenharmony_ci kfree(wobj); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci devm_kfree(dev, event.name); 167362306a36Sopenharmony_ci devm_kfree(dev, event.description); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ciout_unlock: 167662306a36Sopenharmony_ci mutex_unlock(&state->lock); 167762306a36Sopenharmony_ci} 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_cistatic int init_platform_events(struct device *dev, 168062306a36Sopenharmony_ci struct hp_wmi_platform_events **out_pevents, 168162306a36Sopenharmony_ci u8 *out_pcount) 168262306a36Sopenharmony_ci{ 168362306a36Sopenharmony_ci struct hp_wmi_platform_events *pevents_arr; 168462306a36Sopenharmony_ci struct hp_wmi_platform_events *pevents; 168562306a36Sopenharmony_ci union acpi_object *wobj; 168662306a36Sopenharmony_ci u8 count; 168762306a36Sopenharmony_ci int err; 168862306a36Sopenharmony_ci u8 i; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci count = hp_wmi_wobj_instance_count(HP_WMI_PLATFORM_EVENTS_GUID); 169162306a36Sopenharmony_ci if (!count) { 169262306a36Sopenharmony_ci *out_pcount = 0; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci dev_dbg(dev, "No platform events\n"); 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci return 0; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci pevents_arr = devm_kcalloc(dev, count, sizeof(*pevents), GFP_KERNEL); 170062306a36Sopenharmony_ci if (!pevents_arr) 170162306a36Sopenharmony_ci return -ENOMEM; 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci for (i = 0, pevents = pevents_arr; i < count; i++, pevents++) { 170462306a36Sopenharmony_ci wobj = hp_wmi_get_wobj(HP_WMI_PLATFORM_EVENTS_GUID, i); 170562306a36Sopenharmony_ci if (!wobj) 170662306a36Sopenharmony_ci return -EIO; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci err = populate_platform_events_from_wobj(dev, pevents, wobj); 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci kfree(wobj); 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci if (err) 171362306a36Sopenharmony_ci return err; 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci *out_pevents = pevents_arr; 171762306a36Sopenharmony_ci *out_pcount = count; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci dev_dbg(dev, "Found %u platform events\n", count); 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci return 0; 172262306a36Sopenharmony_ci} 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_cistatic int init_numeric_sensors(struct hp_wmi_sensors *state, 172562306a36Sopenharmony_ci struct hp_wmi_info *connected[], 172662306a36Sopenharmony_ci struct hp_wmi_info **out_info, 172762306a36Sopenharmony_ci u8 *out_icount, u8 *out_count, 172862306a36Sopenharmony_ci bool *out_is_new) 172962306a36Sopenharmony_ci{ 173062306a36Sopenharmony_ci struct hp_wmi_info ***info_map = state->info_map; 173162306a36Sopenharmony_ci u8 *channel_count = state->channel_count; 173262306a36Sopenharmony_ci struct device *dev = &state->wdev->dev; 173362306a36Sopenharmony_ci struct hp_wmi_numeric_sensor *nsensor; 173462306a36Sopenharmony_ci u8 channel_index[hwmon_max] = {}; 173562306a36Sopenharmony_ci enum hwmon_sensor_types type; 173662306a36Sopenharmony_ci struct hp_wmi_info *info_arr; 173762306a36Sopenharmony_ci struct hp_wmi_info *info; 173862306a36Sopenharmony_ci union acpi_object *wobj; 173962306a36Sopenharmony_ci u8 count = 0; 174062306a36Sopenharmony_ci bool is_new; 174162306a36Sopenharmony_ci u8 icount; 174262306a36Sopenharmony_ci int wtype; 174362306a36Sopenharmony_ci int err; 174462306a36Sopenharmony_ci u8 c; 174562306a36Sopenharmony_ci u8 i; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci icount = hp_wmi_wobj_instance_count(HP_WMI_NUMERIC_SENSOR_GUID); 174862306a36Sopenharmony_ci if (!icount) 174962306a36Sopenharmony_ci return -ENODATA; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci info_arr = devm_kcalloc(dev, icount, sizeof(*info), GFP_KERNEL); 175262306a36Sopenharmony_ci if (!info_arr) 175362306a36Sopenharmony_ci return -ENOMEM; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci for (i = 0, info = info_arr; i < icount; i++, info++) { 175662306a36Sopenharmony_ci wobj = hp_wmi_get_wobj(HP_WMI_NUMERIC_SENSOR_GUID, i); 175762306a36Sopenharmony_ci if (!wobj) 175862306a36Sopenharmony_ci return -EIO; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci info->instance = i; 176162306a36Sopenharmony_ci info->state = state; 176262306a36Sopenharmony_ci nsensor = &info->nsensor; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci err = populate_numeric_sensor_from_wobj(dev, nsensor, wobj, 176562306a36Sopenharmony_ci &is_new); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci kfree(wobj); 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci if (err) 177062306a36Sopenharmony_ci return err; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci if (!numeric_sensor_is_connected(nsensor)) 177362306a36Sopenharmony_ci continue; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci wtype = classify_numeric_sensor(nsensor); 177662306a36Sopenharmony_ci if (wtype < 0) 177762306a36Sopenharmony_ci continue; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci type = hp_wmi_hwmon_type_map[wtype]; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci channel_count[type]++; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci info->type = type; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci interpret_info(info); 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci connected[count++] = info; 178862306a36Sopenharmony_ci } 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci dev_dbg(dev, "Found %u sensors (%u connected)\n", i, count); 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci for (i = 0; i < count; i++) { 179362306a36Sopenharmony_ci info = connected[i]; 179462306a36Sopenharmony_ci type = info->type; 179562306a36Sopenharmony_ci c = channel_index[type]++; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci if (!info_map[type]) { 179862306a36Sopenharmony_ci info_map[type] = devm_kcalloc(dev, channel_count[type], 179962306a36Sopenharmony_ci sizeof(*info_map), 180062306a36Sopenharmony_ci GFP_KERNEL); 180162306a36Sopenharmony_ci if (!info_map[type]) 180262306a36Sopenharmony_ci return -ENOMEM; 180362306a36Sopenharmony_ci } 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci info_map[type][c] = info; 180662306a36Sopenharmony_ci } 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci *out_info = info_arr; 180962306a36Sopenharmony_ci *out_icount = icount; 181062306a36Sopenharmony_ci *out_count = count; 181162306a36Sopenharmony_ci *out_is_new = is_new; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci return 0; 181462306a36Sopenharmony_ci} 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_cistatic bool find_event_attributes(struct hp_wmi_sensors *state, 181762306a36Sopenharmony_ci struct hp_wmi_platform_events *pevents, 181862306a36Sopenharmony_ci u8 pevents_count) 181962306a36Sopenharmony_ci{ 182062306a36Sopenharmony_ci /* 182162306a36Sopenharmony_ci * The existence of this HPBIOS_PlatformEvents instance: 182262306a36Sopenharmony_ci * 182362306a36Sopenharmony_ci * { 182462306a36Sopenharmony_ci * Name = "Rear Chassis Fan0 Stall"; 182562306a36Sopenharmony_ci * Description = "Rear Chassis Fan0 Speed"; 182662306a36Sopenharmony_ci * Category = 3; // "Sensor" 182762306a36Sopenharmony_ci * PossibleSeverity = 25; // "Critical Failure" 182862306a36Sopenharmony_ci * PossibleStatus = 5; // "Predictive Failure" 182962306a36Sopenharmony_ci * [...] 183062306a36Sopenharmony_ci * } 183162306a36Sopenharmony_ci * 183262306a36Sopenharmony_ci * means that this HPBIOS_BIOSEvent instance may occur: 183362306a36Sopenharmony_ci * 183462306a36Sopenharmony_ci * { 183562306a36Sopenharmony_ci * Name = "Rear Chassis Fan0 Stall"; 183662306a36Sopenharmony_ci * Description = "Rear Chassis Fan0 Speed"; 183762306a36Sopenharmony_ci * Category = 3; // "Sensor" 183862306a36Sopenharmony_ci * Severity = 25; // "Critical Failure" 183962306a36Sopenharmony_ci * Status = 5; // "Predictive Failure" 184062306a36Sopenharmony_ci * } 184162306a36Sopenharmony_ci * 184262306a36Sopenharmony_ci * After the event occurs (e.g. because the fan was unplugged), 184362306a36Sopenharmony_ci * polling the related HPBIOS_BIOSNumericSensor instance gives: 184462306a36Sopenharmony_ci * 184562306a36Sopenharmony_ci * { 184662306a36Sopenharmony_ci * Name = "Rear Chassis Fan0"; 184762306a36Sopenharmony_ci * Description = "Reports rear chassis fan0 speed"; 184862306a36Sopenharmony_ci * OperationalStatus = 5; // "Predictive Failure", was 3 ("OK") 184962306a36Sopenharmony_ci * CurrentReading = 0; 185062306a36Sopenharmony_ci * [...] 185162306a36Sopenharmony_ci * } 185262306a36Sopenharmony_ci * 185362306a36Sopenharmony_ci * In this example, the hwmon fan channel for "Rear Chassis Fan0" 185462306a36Sopenharmony_ci * should support the alarm flag and have it be set if the related 185562306a36Sopenharmony_ci * HPBIOS_BIOSEvent instance occurs. 185662306a36Sopenharmony_ci * 185762306a36Sopenharmony_ci * In addition to fan events, temperature (CPU/chassis) and intrusion 185862306a36Sopenharmony_ci * events are relevant to hwmon [2]. Note that much information in [2] 185962306a36Sopenharmony_ci * is unreliable; it is referenced in addition to ACPI dumps [3] merely 186062306a36Sopenharmony_ci * to support the conclusion that sensor and event names/descriptions 186162306a36Sopenharmony_ci * are systematic enough to allow this driver to match them. 186262306a36Sopenharmony_ci * 186362306a36Sopenharmony_ci * Complications and limitations: 186462306a36Sopenharmony_ci * 186562306a36Sopenharmony_ci * - Strings are freeform and may vary, cf. sensor Name "CPU0 Fan" 186662306a36Sopenharmony_ci * on a Z420 vs. "CPU Fan Speed" on an EliteOne 800 G1. 186762306a36Sopenharmony_ci * - Leading/trailing whitespace is a rare but real possibility [3]. 186862306a36Sopenharmony_ci * - The HPBIOS_PlatformEvents object may not exist or its instances 186962306a36Sopenharmony_ci * may show that the system only has e.g. BIOS setting-related 187062306a36Sopenharmony_ci * events (cf. the ProBook 4540s and ProBook 470 G0 [3]). 187162306a36Sopenharmony_ci */ 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci struct hp_wmi_info *temp_info[HP_WMI_MAX_INSTANCES] = {}; 187462306a36Sopenharmony_ci const char *event_description; 187562306a36Sopenharmony_ci struct hp_wmi_info *fan_info; 187662306a36Sopenharmony_ci bool has_events = false; 187762306a36Sopenharmony_ci const char *event_name; 187862306a36Sopenharmony_ci u32 event_category; 187962306a36Sopenharmony_ci int event_type; 188062306a36Sopenharmony_ci u8 count; 188162306a36Sopenharmony_ci u8 i; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci for (i = 0; i < pevents_count; i++, pevents++) { 188462306a36Sopenharmony_ci event_name = pevents->name; 188562306a36Sopenharmony_ci event_description = pevents->description; 188662306a36Sopenharmony_ci event_category = pevents->category; 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci event_type = classify_event(event_name, event_category); 188962306a36Sopenharmony_ci switch (event_type) { 189062306a36Sopenharmony_ci case HP_WMI_TYPE_AIR_FLOW: 189162306a36Sopenharmony_ci fan_info = match_fan_event(state, event_description); 189262306a36Sopenharmony_ci if (!fan_info) 189362306a36Sopenharmony_ci break; 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci fan_info->has_alarm = true; 189662306a36Sopenharmony_ci has_events = true; 189762306a36Sopenharmony_ci break; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci case HP_WMI_TYPE_INTRUSION: 190062306a36Sopenharmony_ci state->has_intrusion = true; 190162306a36Sopenharmony_ci has_events = true; 190262306a36Sopenharmony_ci break; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci case HP_WMI_TYPE_TEMPERATURE: 190562306a36Sopenharmony_ci count = match_temp_events(state, event_description, 190662306a36Sopenharmony_ci temp_info); 190762306a36Sopenharmony_ci if (!count) 190862306a36Sopenharmony_ci break; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci while (count) 191162306a36Sopenharmony_ci temp_info[--count]->has_alarm = true; 191262306a36Sopenharmony_ci has_events = true; 191362306a36Sopenharmony_ci break; 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci default: 191662306a36Sopenharmony_ci break; 191762306a36Sopenharmony_ci } 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci return has_events; 192162306a36Sopenharmony_ci} 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_cistatic int make_chip_info(struct hp_wmi_sensors *state, bool has_events) 192462306a36Sopenharmony_ci{ 192562306a36Sopenharmony_ci const struct hwmon_channel_info **ptr_channel_info; 192662306a36Sopenharmony_ci struct hp_wmi_info ***info_map = state->info_map; 192762306a36Sopenharmony_ci u8 *channel_count = state->channel_count; 192862306a36Sopenharmony_ci struct hwmon_channel_info *channel_info; 192962306a36Sopenharmony_ci struct device *dev = &state->wdev->dev; 193062306a36Sopenharmony_ci enum hwmon_sensor_types type; 193162306a36Sopenharmony_ci u8 type_count = 0; 193262306a36Sopenharmony_ci u32 *config; 193362306a36Sopenharmony_ci u32 attr; 193462306a36Sopenharmony_ci u8 count; 193562306a36Sopenharmony_ci u8 i; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci if (channel_count[hwmon_temp]) 193862306a36Sopenharmony_ci channel_count[hwmon_chip] = 1; 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci if (has_events && state->has_intrusion) 194162306a36Sopenharmony_ci channel_count[hwmon_intrusion] = 1; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci for (type = hwmon_chip; type < hwmon_max; type++) 194462306a36Sopenharmony_ci if (channel_count[type]) 194562306a36Sopenharmony_ci type_count++; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci channel_info = devm_kcalloc(dev, type_count, 194862306a36Sopenharmony_ci sizeof(*channel_info), GFP_KERNEL); 194962306a36Sopenharmony_ci if (!channel_info) 195062306a36Sopenharmony_ci return -ENOMEM; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci ptr_channel_info = devm_kcalloc(dev, type_count + 1, 195362306a36Sopenharmony_ci sizeof(*ptr_channel_info), GFP_KERNEL); 195462306a36Sopenharmony_ci if (!ptr_channel_info) 195562306a36Sopenharmony_ci return -ENOMEM; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci hp_wmi_chip_info.info = ptr_channel_info; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci for (type = hwmon_chip; type < hwmon_max; type++) { 196062306a36Sopenharmony_ci count = channel_count[type]; 196162306a36Sopenharmony_ci if (!count) 196262306a36Sopenharmony_ci continue; 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci config = devm_kcalloc(dev, count + 1, 196562306a36Sopenharmony_ci sizeof(*config), GFP_KERNEL); 196662306a36Sopenharmony_ci if (!config) 196762306a36Sopenharmony_ci return -ENOMEM; 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci attr = hp_wmi_hwmon_attributes[type]; 197062306a36Sopenharmony_ci channel_info->type = type; 197162306a36Sopenharmony_ci channel_info->config = config; 197262306a36Sopenharmony_ci memset32(config, attr, count); 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci *ptr_channel_info++ = channel_info++; 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci if (!has_events || (type != hwmon_temp && type != hwmon_fan)) 197762306a36Sopenharmony_ci continue; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci attr = type == hwmon_temp ? HWMON_T_ALARM : HWMON_F_ALARM; 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci for (i = 0; i < count; i++) 198262306a36Sopenharmony_ci if (info_map[type][i]->has_alarm) 198362306a36Sopenharmony_ci config[i] |= attr; 198462306a36Sopenharmony_ci } 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci return 0; 198762306a36Sopenharmony_ci} 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_cistatic bool add_event_handler(struct hp_wmi_sensors *state) 199062306a36Sopenharmony_ci{ 199162306a36Sopenharmony_ci struct device *dev = &state->wdev->dev; 199262306a36Sopenharmony_ci int err; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci err = wmi_install_notify_handler(HP_WMI_EVENT_GUID, 199562306a36Sopenharmony_ci hp_wmi_notify, state); 199662306a36Sopenharmony_ci if (err) { 199762306a36Sopenharmony_ci dev_info(dev, "Failed to subscribe to WMI event\n"); 199862306a36Sopenharmony_ci return false; 199962306a36Sopenharmony_ci } 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci err = devm_add_action_or_reset(dev, hp_wmi_devm_notify_remove, NULL); 200262306a36Sopenharmony_ci if (err) 200362306a36Sopenharmony_ci return false; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci return true; 200662306a36Sopenharmony_ci} 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_cistatic int hp_wmi_sensors_init(struct hp_wmi_sensors *state) 200962306a36Sopenharmony_ci{ 201062306a36Sopenharmony_ci struct hp_wmi_info *connected[HP_WMI_MAX_INSTANCES]; 201162306a36Sopenharmony_ci struct hp_wmi_platform_events *pevents = NULL; 201262306a36Sopenharmony_ci struct device *dev = &state->wdev->dev; 201362306a36Sopenharmony_ci struct hp_wmi_info *info; 201462306a36Sopenharmony_ci struct device *hwdev; 201562306a36Sopenharmony_ci bool has_events; 201662306a36Sopenharmony_ci bool is_new; 201762306a36Sopenharmony_ci u8 icount; 201862306a36Sopenharmony_ci u8 pcount; 201962306a36Sopenharmony_ci u8 count; 202062306a36Sopenharmony_ci int err; 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci err = init_platform_events(dev, &pevents, &pcount); 202362306a36Sopenharmony_ci if (err) 202462306a36Sopenharmony_ci return err; 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci err = init_numeric_sensors(state, connected, &info, 202762306a36Sopenharmony_ci &icount, &count, &is_new); 202862306a36Sopenharmony_ci if (err) 202962306a36Sopenharmony_ci return err; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_DEBUG_FS)) 203262306a36Sopenharmony_ci hp_wmi_debugfs_init(dev, info, pevents, icount, pcount, is_new); 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci if (!count) 203562306a36Sopenharmony_ci return 0; /* No connected sensors; debugfs only. */ 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci has_events = find_event_attributes(state, pevents, pcount); 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci /* Survive failure to install WMI event handler. */ 204062306a36Sopenharmony_ci if (has_events && !add_event_handler(state)) 204162306a36Sopenharmony_ci has_events = false; 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci err = make_chip_info(state, has_events); 204462306a36Sopenharmony_ci if (err) 204562306a36Sopenharmony_ci return err; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci hwdev = devm_hwmon_device_register_with_info(dev, "hp_wmi_sensors", 204862306a36Sopenharmony_ci state, &hp_wmi_chip_info, 204962306a36Sopenharmony_ci NULL); 205062306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(hwdev); 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_cistatic int hp_wmi_sensors_probe(struct wmi_device *wdev, const void *context) 205462306a36Sopenharmony_ci{ 205562306a36Sopenharmony_ci struct device *dev = &wdev->dev; 205662306a36Sopenharmony_ci struct hp_wmi_sensors *state; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 205962306a36Sopenharmony_ci if (!state) 206062306a36Sopenharmony_ci return -ENOMEM; 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci state->wdev = wdev; 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci mutex_init(&state->lock); 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci dev_set_drvdata(dev, state); 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci return hp_wmi_sensors_init(state); 206962306a36Sopenharmony_ci} 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_cistatic const struct wmi_device_id hp_wmi_sensors_id_table[] = { 207262306a36Sopenharmony_ci { HP_WMI_NUMERIC_SENSOR_GUID, NULL }, 207362306a36Sopenharmony_ci {}, 207462306a36Sopenharmony_ci}; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_cistatic struct wmi_driver hp_wmi_sensors_driver = { 207762306a36Sopenharmony_ci .driver = { .name = "hp-wmi-sensors" }, 207862306a36Sopenharmony_ci .id_table = hp_wmi_sensors_id_table, 207962306a36Sopenharmony_ci .probe = hp_wmi_sensors_probe, 208062306a36Sopenharmony_ci}; 208162306a36Sopenharmony_cimodule_wmi_driver(hp_wmi_sensors_driver); 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ciMODULE_AUTHOR("James Seo <james@equiv.tech>"); 208462306a36Sopenharmony_ciMODULE_DESCRIPTION("HP WMI Sensors driver"); 208562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2086