1*23902f98SJames Seo // SPDX-License-Identifier: GPL-2.0-or-later 2*23902f98SJames Seo /* 3*23902f98SJames Seo * hwmon driver for HP (and some HP Compaq) business-class computers that 4*23902f98SJames Seo * report numeric sensor data via Windows Management Instrumentation (WMI). 5*23902f98SJames Seo * 6*23902f98SJames Seo * Copyright (C) 2023 James Seo <james@equiv.tech> 7*23902f98SJames Seo * 8*23902f98SJames Seo * References: 9*23902f98SJames Seo * [1] Hewlett-Packard Development Company, L.P., 10*23902f98SJames Seo * "HP Client Management Interface Technical White Paper", 2005. [Online]. 11*23902f98SJames Seo * Available: https://h20331.www2.hp.com/hpsub/downloads/cmi_whitepaper.pdf 12*23902f98SJames Seo * [2] Hewlett-Packard Development Company, L.P., 13*23902f98SJames Seo * "HP Retail Manageability", 2012. [Online]. 14*23902f98SJames Seo * Available: http://h10032.www1.hp.com/ctg/Manual/c03291135.pdf 15*23902f98SJames Seo * [3] Linux Hardware Project, A. Ponomarenko et al., 16*23902f98SJames Seo * "linuxhw/ACPI - Collect ACPI table dumps", 2018. [Online]. 17*23902f98SJames Seo * Available: https://github.com/linuxhw/ACPI 18*23902f98SJames Seo * [4] P. Rohár, "bmfdec - Decompile binary MOF file (BMF) from WMI buffer", 19*23902f98SJames Seo * 2017. [Online]. Available: https://github.com/pali/bmfdec 20*23902f98SJames Seo */ 21*23902f98SJames Seo 22*23902f98SJames Seo #include <linux/acpi.h> 23*23902f98SJames Seo #include <linux/debugfs.h> 24*23902f98SJames Seo #include <linux/hwmon.h> 25*23902f98SJames Seo #include <linux/jiffies.h> 26*23902f98SJames Seo #include <linux/mutex.h> 27*23902f98SJames Seo #include <linux/units.h> 28*23902f98SJames Seo #include <linux/wmi.h> 29*23902f98SJames Seo 30*23902f98SJames Seo #define HP_WMI_EVENT_NAMESPACE "root\\WMI" 31*23902f98SJames Seo #define HP_WMI_EVENT_CLASS "HPBIOS_BIOSEvent" 32*23902f98SJames Seo #define HP_WMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" 33*23902f98SJames Seo #define HP_WMI_NUMERIC_SENSOR_GUID "8F1F6435-9F42-42C8-BADC-0E9424F20C9A" 34*23902f98SJames Seo #define HP_WMI_PLATFORM_EVENTS_GUID "41227C2D-80E1-423F-8B8E-87E32755A0EB" 35*23902f98SJames Seo 36*23902f98SJames Seo /* Patterns for recognizing sensors and matching events to channels. */ 37*23902f98SJames Seo 38*23902f98SJames Seo #define HP_WMI_PATTERN_SYS_TEMP "Chassis Thermal Index" 39*23902f98SJames Seo #define HP_WMI_PATTERN_SYS_TEMP2 "System Ambient Temperature" 40*23902f98SJames Seo #define HP_WMI_PATTERN_CPU_TEMP "CPU Thermal Index" 41*23902f98SJames Seo #define HP_WMI_PATTERN_CPU_TEMP2 "CPU Temperature" 42*23902f98SJames Seo #define HP_WMI_PATTERN_TEMP_SENSOR "Thermal Index" 43*23902f98SJames Seo #define HP_WMI_PATTERN_TEMP_ALARM "Thermal Critical" 44*23902f98SJames Seo #define HP_WMI_PATTERN_INTRUSION_ALARM "Hood Intrusion" 45*23902f98SJames Seo #define HP_WMI_PATTERN_FAN_ALARM "Stall" 46*23902f98SJames Seo #define HP_WMI_PATTERN_TEMP "Temperature" 47*23902f98SJames Seo #define HP_WMI_PATTERN_CPU "CPU" 48*23902f98SJames Seo 49*23902f98SJames Seo /* These limits are arbitrary. The WMI implementation may vary by system. */ 50*23902f98SJames Seo 51*23902f98SJames Seo #define HP_WMI_MAX_STR_SIZE 128U 52*23902f98SJames Seo #define HP_WMI_MAX_PROPERTIES 32U 53*23902f98SJames Seo #define HP_WMI_MAX_INSTANCES 32U 54*23902f98SJames Seo 55*23902f98SJames Seo enum hp_wmi_type { 56*23902f98SJames Seo HP_WMI_TYPE_OTHER = 1, 57*23902f98SJames Seo HP_WMI_TYPE_TEMPERATURE = 2, 58*23902f98SJames Seo HP_WMI_TYPE_VOLTAGE = 3, 59*23902f98SJames Seo HP_WMI_TYPE_CURRENT = 4, 60*23902f98SJames Seo HP_WMI_TYPE_AIR_FLOW = 12, 61*23902f98SJames Seo HP_WMI_TYPE_INTRUSION = 0xabadb01, /* Custom. */ 62*23902f98SJames Seo }; 63*23902f98SJames Seo 64*23902f98SJames Seo enum hp_wmi_category { 65*23902f98SJames Seo HP_WMI_CATEGORY_SENSOR = 3, 66*23902f98SJames Seo }; 67*23902f98SJames Seo 68*23902f98SJames Seo enum hp_wmi_severity { 69*23902f98SJames Seo HP_WMI_SEVERITY_UNKNOWN = 0, 70*23902f98SJames Seo HP_WMI_SEVERITY_OK = 5, 71*23902f98SJames Seo HP_WMI_SEVERITY_DEGRADED_WARNING = 10, 72*23902f98SJames Seo HP_WMI_SEVERITY_MINOR_FAILURE = 15, 73*23902f98SJames Seo HP_WMI_SEVERITY_MAJOR_FAILURE = 20, 74*23902f98SJames Seo HP_WMI_SEVERITY_CRITICAL_FAILURE = 25, 75*23902f98SJames Seo HP_WMI_SEVERITY_NON_RECOVERABLE_ERROR = 30, 76*23902f98SJames Seo }; 77*23902f98SJames Seo 78*23902f98SJames Seo enum hp_wmi_status { 79*23902f98SJames Seo HP_WMI_STATUS_OK = 2, 80*23902f98SJames Seo HP_WMI_STATUS_DEGRADED = 3, 81*23902f98SJames Seo HP_WMI_STATUS_STRESSED = 4, 82*23902f98SJames Seo HP_WMI_STATUS_PREDICTIVE_FAILURE = 5, 83*23902f98SJames Seo HP_WMI_STATUS_ERROR = 6, 84*23902f98SJames Seo HP_WMI_STATUS_NON_RECOVERABLE_ERROR = 7, 85*23902f98SJames Seo HP_WMI_STATUS_NO_CONTACT = 12, 86*23902f98SJames Seo HP_WMI_STATUS_LOST_COMMUNICATION = 13, 87*23902f98SJames Seo HP_WMI_STATUS_ABORTED = 14, 88*23902f98SJames Seo HP_WMI_STATUS_SUPPORTING_ENTITY_IN_ERROR = 16, 89*23902f98SJames Seo 90*23902f98SJames Seo /* Occurs combined with one of "OK", "Degraded", and "Error" [1]. */ 91*23902f98SJames Seo HP_WMI_STATUS_COMPLETED = 17, 92*23902f98SJames Seo }; 93*23902f98SJames Seo 94*23902f98SJames Seo enum hp_wmi_units { 95*23902f98SJames Seo HP_WMI_UNITS_OTHER = 1, 96*23902f98SJames Seo HP_WMI_UNITS_DEGREES_C = 2, 97*23902f98SJames Seo HP_WMI_UNITS_DEGREES_F = 3, 98*23902f98SJames Seo HP_WMI_UNITS_DEGREES_K = 4, 99*23902f98SJames Seo HP_WMI_UNITS_VOLTS = 5, 100*23902f98SJames Seo HP_WMI_UNITS_AMPS = 6, 101*23902f98SJames Seo HP_WMI_UNITS_RPM = 19, 102*23902f98SJames Seo }; 103*23902f98SJames Seo 104*23902f98SJames Seo enum hp_wmi_property { 105*23902f98SJames Seo HP_WMI_PROPERTY_NAME = 0, 106*23902f98SJames Seo HP_WMI_PROPERTY_DESCRIPTION = 1, 107*23902f98SJames Seo HP_WMI_PROPERTY_SENSOR_TYPE = 2, 108*23902f98SJames Seo HP_WMI_PROPERTY_OTHER_SENSOR_TYPE = 3, 109*23902f98SJames Seo HP_WMI_PROPERTY_OPERATIONAL_STATUS = 4, 110*23902f98SJames Seo HP_WMI_PROPERTY_SIZE = 5, 111*23902f98SJames Seo HP_WMI_PROPERTY_POSSIBLE_STATES = 6, 112*23902f98SJames Seo HP_WMI_PROPERTY_CURRENT_STATE = 7, 113*23902f98SJames Seo HP_WMI_PROPERTY_BASE_UNITS = 8, 114*23902f98SJames Seo HP_WMI_PROPERTY_UNIT_MODIFIER = 9, 115*23902f98SJames Seo HP_WMI_PROPERTY_CURRENT_READING = 10, 116*23902f98SJames Seo HP_WMI_PROPERTY_RATE_UNITS = 11, 117*23902f98SJames Seo }; 118*23902f98SJames Seo 119*23902f98SJames Seo static const acpi_object_type hp_wmi_property_map[] = { 120*23902f98SJames Seo [HP_WMI_PROPERTY_NAME] = ACPI_TYPE_STRING, 121*23902f98SJames Seo [HP_WMI_PROPERTY_DESCRIPTION] = ACPI_TYPE_STRING, 122*23902f98SJames Seo [HP_WMI_PROPERTY_SENSOR_TYPE] = ACPI_TYPE_INTEGER, 123*23902f98SJames Seo [HP_WMI_PROPERTY_OTHER_SENSOR_TYPE] = ACPI_TYPE_STRING, 124*23902f98SJames Seo [HP_WMI_PROPERTY_OPERATIONAL_STATUS] = ACPI_TYPE_INTEGER, 125*23902f98SJames Seo [HP_WMI_PROPERTY_SIZE] = ACPI_TYPE_INTEGER, 126*23902f98SJames Seo [HP_WMI_PROPERTY_POSSIBLE_STATES] = ACPI_TYPE_STRING, 127*23902f98SJames Seo [HP_WMI_PROPERTY_CURRENT_STATE] = ACPI_TYPE_STRING, 128*23902f98SJames Seo [HP_WMI_PROPERTY_BASE_UNITS] = ACPI_TYPE_INTEGER, 129*23902f98SJames Seo [HP_WMI_PROPERTY_UNIT_MODIFIER] = ACPI_TYPE_INTEGER, 130*23902f98SJames Seo [HP_WMI_PROPERTY_CURRENT_READING] = ACPI_TYPE_INTEGER, 131*23902f98SJames Seo [HP_WMI_PROPERTY_RATE_UNITS] = ACPI_TYPE_INTEGER, 132*23902f98SJames Seo }; 133*23902f98SJames Seo 134*23902f98SJames Seo enum hp_wmi_platform_events_property { 135*23902f98SJames Seo HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME = 0, 136*23902f98SJames Seo HP_WMI_PLATFORM_EVENTS_PROPERTY_DESCRIPTION = 1, 137*23902f98SJames Seo HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_NAMESPACE = 2, 138*23902f98SJames Seo HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_CLASS = 3, 139*23902f98SJames Seo HP_WMI_PLATFORM_EVENTS_PROPERTY_CATEGORY = 4, 140*23902f98SJames Seo HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_SEVERITY = 5, 141*23902f98SJames Seo HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS = 6, 142*23902f98SJames Seo }; 143*23902f98SJames Seo 144*23902f98SJames Seo static const acpi_object_type hp_wmi_platform_events_property_map[] = { 145*23902f98SJames Seo [HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME] = ACPI_TYPE_STRING, 146*23902f98SJames Seo [HP_WMI_PLATFORM_EVENTS_PROPERTY_DESCRIPTION] = ACPI_TYPE_STRING, 147*23902f98SJames Seo [HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_NAMESPACE] = ACPI_TYPE_STRING, 148*23902f98SJames Seo [HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_CLASS] = ACPI_TYPE_STRING, 149*23902f98SJames Seo [HP_WMI_PLATFORM_EVENTS_PROPERTY_CATEGORY] = ACPI_TYPE_INTEGER, 150*23902f98SJames Seo [HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_SEVERITY] = ACPI_TYPE_INTEGER, 151*23902f98SJames Seo [HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS] = ACPI_TYPE_INTEGER, 152*23902f98SJames Seo }; 153*23902f98SJames Seo 154*23902f98SJames Seo enum hp_wmi_event_property { 155*23902f98SJames Seo HP_WMI_EVENT_PROPERTY_NAME = 0, 156*23902f98SJames Seo HP_WMI_EVENT_PROPERTY_DESCRIPTION = 1, 157*23902f98SJames Seo HP_WMI_EVENT_PROPERTY_CATEGORY = 2, 158*23902f98SJames Seo HP_WMI_EVENT_PROPERTY_SEVERITY = 3, 159*23902f98SJames Seo HP_WMI_EVENT_PROPERTY_STATUS = 4, 160*23902f98SJames Seo }; 161*23902f98SJames Seo 162*23902f98SJames Seo static const acpi_object_type hp_wmi_event_property_map[] = { 163*23902f98SJames Seo [HP_WMI_EVENT_PROPERTY_NAME] = ACPI_TYPE_STRING, 164*23902f98SJames Seo [HP_WMI_EVENT_PROPERTY_DESCRIPTION] = ACPI_TYPE_STRING, 165*23902f98SJames Seo [HP_WMI_EVENT_PROPERTY_CATEGORY] = ACPI_TYPE_INTEGER, 166*23902f98SJames Seo [HP_WMI_EVENT_PROPERTY_SEVERITY] = ACPI_TYPE_INTEGER, 167*23902f98SJames Seo [HP_WMI_EVENT_PROPERTY_STATUS] = ACPI_TYPE_INTEGER, 168*23902f98SJames Seo }; 169*23902f98SJames Seo 170*23902f98SJames Seo static const enum hwmon_sensor_types hp_wmi_hwmon_type_map[] = { 171*23902f98SJames Seo [HP_WMI_TYPE_TEMPERATURE] = hwmon_temp, 172*23902f98SJames Seo [HP_WMI_TYPE_VOLTAGE] = hwmon_in, 173*23902f98SJames Seo [HP_WMI_TYPE_CURRENT] = hwmon_curr, 174*23902f98SJames Seo [HP_WMI_TYPE_AIR_FLOW] = hwmon_fan, 175*23902f98SJames Seo }; 176*23902f98SJames Seo 177*23902f98SJames Seo static const u32 hp_wmi_hwmon_attributes[hwmon_max] = { 178*23902f98SJames Seo [hwmon_chip] = HWMON_C_REGISTER_TZ, 179*23902f98SJames Seo [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_FAULT, 180*23902f98SJames Seo [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL, 181*23902f98SJames Seo [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL, 182*23902f98SJames Seo [hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_FAULT, 183*23902f98SJames Seo [hwmon_intrusion] = HWMON_INTRUSION_ALARM, 184*23902f98SJames Seo }; 185*23902f98SJames Seo 186*23902f98SJames Seo /* 187*23902f98SJames Seo * struct hp_wmi_numeric_sensor - a HPBIOS_BIOSNumericSensor instance 188*23902f98SJames Seo * 189*23902f98SJames Seo * Two variants of HPBIOS_BIOSNumericSensor are known. The first is specified 190*23902f98SJames Seo * in [1] and appears to be much more widespread. The second was discovered by 191*23902f98SJames Seo * decoding BMOF blobs [4], seems to be found only in some newer ZBook systems 192*23902f98SJames Seo * [3], and has two new properties and a slightly different property order. 193*23902f98SJames Seo * 194*23902f98SJames Seo * These differences don't matter on Windows, where WMI object properties are 195*23902f98SJames Seo * accessed by name. For us, supporting both variants gets ugly and hacky at 196*23902f98SJames Seo * times. The fun begins now; this struct is defined as per the new variant. 197*23902f98SJames Seo * 198*23902f98SJames Seo * Effective MOF definition: 199*23902f98SJames Seo * 200*23902f98SJames Seo * #pragma namespace("\\\\.\\root\\HP\\InstrumentedBIOS"); 201*23902f98SJames Seo * class HPBIOS_BIOSNumericSensor { 202*23902f98SJames Seo * [read] string Name; 203*23902f98SJames Seo * [read] string Description; 204*23902f98SJames Seo * [read, ValueMap {"0","1","2","3","4","5","6","7","8","9", 205*23902f98SJames Seo * "10","11","12"}, Values {"Unknown","Other","Temperature", 206*23902f98SJames Seo * "Voltage","Current","Tachometer","Counter","Switch","Lock", 207*23902f98SJames Seo * "Humidity","Smoke Detection","Presence","Air Flow"}] 208*23902f98SJames Seo * uint32 SensorType; 209*23902f98SJames Seo * [read] string OtherSensorType; 210*23902f98SJames Seo * [read, ValueMap {"0","1","2","3","4","5","6","7","8","9", 211*23902f98SJames Seo * "10","11","12","13","14","15","16","17","18","..", 212*23902f98SJames Seo * "0x8000.."}, Values {"Unknown","Other","OK","Degraded", 213*23902f98SJames Seo * "Stressed","Predictive Failure","Error", 214*23902f98SJames Seo * "Non-Recoverable Error","Starting","Stopping","Stopped", 215*23902f98SJames Seo * "In Service","No Contact","Lost Communication","Aborted", 216*23902f98SJames Seo * "Dormant","Supporting Entity in Error","Completed", 217*23902f98SJames Seo * "Power Mode","DMTF Reserved","Vendor Reserved"}] 218*23902f98SJames Seo * uint32 OperationalStatus; 219*23902f98SJames Seo * [read] uint32 Size; 220*23902f98SJames Seo * [read] string PossibleStates[]; 221*23902f98SJames Seo * [read] string CurrentState; 222*23902f98SJames Seo * [read, ValueMap {"0","1","2","3","4","5","6","7","8","9", 223*23902f98SJames Seo * "10","11","12","13","14","15","16","17","18","19","20", 224*23902f98SJames Seo * "21","22","23","24","25","26","27","28","29","30","31", 225*23902f98SJames Seo * "32","33","34","35","36","37","38","39","40","41","42", 226*23902f98SJames Seo * "43","44","45","46","47","48","49","50","51","52","53", 227*23902f98SJames Seo * "54","55","56","57","58","59","60","61","62","63","64", 228*23902f98SJames Seo * "65"}, Values {"Unknown","Other","Degrees C","Degrees F", 229*23902f98SJames Seo * "Degrees K","Volts","Amps","Watts","Joules","Coulombs", 230*23902f98SJames Seo * "VA","Nits","Lumens","Lux","Candelas","kPa","PSI", 231*23902f98SJames Seo * "Newtons","CFM","RPM","Hertz","Seconds","Minutes", 232*23902f98SJames Seo * "Hours","Days","Weeks","Mils","Inches","Feet", 233*23902f98SJames Seo * "Cubic Inches","Cubic Feet","Meters","Cubic Centimeters", 234*23902f98SJames Seo * "Cubic Meters","Liters","Fluid Ounces","Radians", 235*23902f98SJames Seo * "Steradians","Revolutions","Cycles","Gravities","Ounces", 236*23902f98SJames Seo * "Pounds","Foot-Pounds","Ounce-Inches","Gauss","Gilberts", 237*23902f98SJames Seo * "Henries","Farads","Ohms","Siemens","Moles","Becquerels", 238*23902f98SJames Seo * "PPM (parts/million)","Decibels","DbA","DbC","Grays", 239*23902f98SJames Seo * "Sieverts","Color Temperature Degrees K","Bits","Bytes", 240*23902f98SJames Seo * "Words (data)","DoubleWords","QuadWords","Percentage"}] 241*23902f98SJames Seo * uint32 BaseUnits; 242*23902f98SJames Seo * [read] sint32 UnitModifier; 243*23902f98SJames Seo * [read] uint32 CurrentReading; 244*23902f98SJames Seo * [read] uint32 RateUnits; 245*23902f98SJames Seo * }; 246*23902f98SJames Seo * 247*23902f98SJames Seo * Effective MOF definition of old variant [1] (sans redundant info): 248*23902f98SJames Seo * 249*23902f98SJames Seo * class HPBIOS_BIOSNumericSensor { 250*23902f98SJames Seo * [read] string Name; 251*23902f98SJames Seo * [read] string Description; 252*23902f98SJames Seo * [read] uint32 SensorType; 253*23902f98SJames Seo * [read] string OtherSensorType; 254*23902f98SJames Seo * [read] uint32 OperationalStatus; 255*23902f98SJames Seo * [read] string CurrentState; 256*23902f98SJames Seo * [read] string PossibleStates[]; 257*23902f98SJames Seo * [read] uint32 BaseUnits; 258*23902f98SJames Seo * [read] sint32 UnitModifier; 259*23902f98SJames Seo * [read] uint32 CurrentReading; 260*23902f98SJames Seo * }; 261*23902f98SJames Seo */ 262*23902f98SJames Seo struct hp_wmi_numeric_sensor { 263*23902f98SJames Seo const char *name; 264*23902f98SJames Seo const char *description; 265*23902f98SJames Seo u32 sensor_type; 266*23902f98SJames Seo const char *other_sensor_type; /* Explains "Other" SensorType. */ 267*23902f98SJames Seo u32 operational_status; 268*23902f98SJames Seo u8 size; /* Count of PossibleStates[]. */ 269*23902f98SJames Seo const char **possible_states; 270*23902f98SJames Seo const char *current_state; 271*23902f98SJames Seo u32 base_units; 272*23902f98SJames Seo s32 unit_modifier; 273*23902f98SJames Seo u32 current_reading; 274*23902f98SJames Seo u32 rate_units; 275*23902f98SJames Seo }; 276*23902f98SJames Seo 277*23902f98SJames Seo /* 278*23902f98SJames Seo * struct hp_wmi_platform_events - a HPBIOS_PlatformEvents instance 279*23902f98SJames Seo * 280*23902f98SJames Seo * Instances of this object reveal the set of possible HPBIOS_BIOSEvent 281*23902f98SJames Seo * instances for the current system, but it may not always be present. 282*23902f98SJames Seo * 283*23902f98SJames Seo * Effective MOF definition: 284*23902f98SJames Seo * 285*23902f98SJames Seo * #pragma namespace("\\\\.\\root\\HP\\InstrumentedBIOS"); 286*23902f98SJames Seo * class HPBIOS_PlatformEvents { 287*23902f98SJames Seo * [read] string Name; 288*23902f98SJames Seo * [read] string Description; 289*23902f98SJames Seo * [read] string SourceNamespace; 290*23902f98SJames Seo * [read] string SourceClass; 291*23902f98SJames Seo * [read, ValueMap {"0","1","2","3","4",".."}, Values { 292*23902f98SJames Seo * "Unknown","Configuration Change","Button Pressed", 293*23902f98SJames Seo * "Sensor","BIOS Settings","Reserved"}] 294*23902f98SJames Seo * uint32 Category; 295*23902f98SJames Seo * [read, ValueMap{"0","5","10","15","20","25","30",".."}, 296*23902f98SJames Seo * Values{"Unknown","OK","Degraded/Warning","Minor Failure", 297*23902f98SJames Seo * "Major Failure","Critical Failure","Non-recoverable Error", 298*23902f98SJames Seo * "DMTF Reserved"}] 299*23902f98SJames Seo * uint32 PossibleSeverity; 300*23902f98SJames Seo * [read, ValueMap {"0","1","2","3","4","5","6","7","8","9", 301*23902f98SJames Seo * "10","11","12","13","14","15","16","17","18","..", 302*23902f98SJames Seo * "0x8000.."}, Values {"Unknown","Other","OK","Degraded", 303*23902f98SJames Seo * "Stressed","Predictive Failure","Error", 304*23902f98SJames Seo * "Non-Recoverable Error","Starting","Stopping","Stopped", 305*23902f98SJames Seo * "In Service","No Contact","Lost Communication","Aborted", 306*23902f98SJames Seo * "Dormant","Supporting Entity in Error","Completed", 307*23902f98SJames Seo * "Power Mode","DMTF Reserved","Vendor Reserved"}] 308*23902f98SJames Seo * uint32 PossibleStatus; 309*23902f98SJames Seo * }; 310*23902f98SJames Seo */ 311*23902f98SJames Seo struct hp_wmi_platform_events { 312*23902f98SJames Seo const char *name; 313*23902f98SJames Seo const char *description; 314*23902f98SJames Seo const char *source_namespace; 315*23902f98SJames Seo const char *source_class; 316*23902f98SJames Seo u32 category; 317*23902f98SJames Seo u32 possible_severity; 318*23902f98SJames Seo u32 possible_status; 319*23902f98SJames Seo }; 320*23902f98SJames Seo 321*23902f98SJames Seo /* 322*23902f98SJames Seo * struct hp_wmi_event - a HPBIOS_BIOSEvent instance 323*23902f98SJames Seo * 324*23902f98SJames Seo * Effective MOF definition [1] (corrected below from original): 325*23902f98SJames Seo * 326*23902f98SJames Seo * #pragma namespace("\\\\.\\root\\WMI"); 327*23902f98SJames Seo * class HPBIOS_BIOSEvent : WMIEvent { 328*23902f98SJames Seo * [read] string Name; 329*23902f98SJames Seo * [read] string Description; 330*23902f98SJames Seo * [read ValueMap {"0","1","2","3","4"}, Values {"Unknown", 331*23902f98SJames Seo * "Configuration Change","Button Pressed","Sensor", 332*23902f98SJames Seo * "BIOS Settings"}] 333*23902f98SJames Seo * uint32 Category; 334*23902f98SJames Seo * [read, ValueMap {"0","5","10","15","20","25","30"}, 335*23902f98SJames Seo * Values {"Unknown","OK","Degraded/Warning", 336*23902f98SJames Seo * "Minor Failure","Major Failure","Critical Failure", 337*23902f98SJames Seo * "Non-recoverable Error"}] 338*23902f98SJames Seo * uint32 Severity; 339*23902f98SJames Seo * [read, ValueMap {"0","1","2","3","4","5","6","7","8", 340*23902f98SJames Seo * "9","10","11","12","13","14","15","16","17","18","..", 341*23902f98SJames Seo * "0x8000.."}, Values {"Unknown","Other","OK","Degraded", 342*23902f98SJames Seo * "Stressed","Predictive Failure","Error", 343*23902f98SJames Seo * "Non-Recoverable Error","Starting","Stopping","Stopped", 344*23902f98SJames Seo * "In Service","No Contact","Lost Communication","Aborted", 345*23902f98SJames Seo * "Dormant","Supporting Entity in Error","Completed", 346*23902f98SJames Seo * "Power Mode","DMTF Reserved","Vendor Reserved"}] 347*23902f98SJames Seo * uint32 Status; 348*23902f98SJames Seo * }; 349*23902f98SJames Seo */ 350*23902f98SJames Seo struct hp_wmi_event { 351*23902f98SJames Seo const char *name; 352*23902f98SJames Seo const char *description; 353*23902f98SJames Seo u32 category; 354*23902f98SJames Seo }; 355*23902f98SJames Seo 356*23902f98SJames Seo /* 357*23902f98SJames Seo * struct hp_wmi_info - sensor info 358*23902f98SJames Seo * @nsensor: numeric sensor properties 359*23902f98SJames Seo * @instance: its WMI instance number 360*23902f98SJames Seo * @state: pointer to driver state 361*23902f98SJames Seo * @has_alarm: whether sensor has an alarm flag 362*23902f98SJames Seo * @alarm: alarm flag 363*23902f98SJames Seo * @type: its hwmon sensor type 364*23902f98SJames Seo * @cached_val: current sensor reading value, scaled for hwmon 365*23902f98SJames Seo * @last_updated: when these readings were last updated 366*23902f98SJames Seo */ 367*23902f98SJames Seo struct hp_wmi_info { 368*23902f98SJames Seo struct hp_wmi_numeric_sensor nsensor; 369*23902f98SJames Seo u8 instance; 370*23902f98SJames Seo void *state; /* void *: Avoid forward declaration. */ 371*23902f98SJames Seo bool has_alarm; 372*23902f98SJames Seo bool alarm; 373*23902f98SJames Seo enum hwmon_sensor_types type; 374*23902f98SJames Seo long cached_val; 375*23902f98SJames Seo unsigned long last_updated; /* In jiffies. */ 376*23902f98SJames Seo 377*23902f98SJames Seo }; 378*23902f98SJames Seo 379*23902f98SJames Seo /* 380*23902f98SJames Seo * struct hp_wmi_sensors - driver state 381*23902f98SJames Seo * @wdev: pointer to the parent WMI device 382*23902f98SJames Seo * @info_map: sensor info structs by hwmon type and channel number 383*23902f98SJames Seo * @channel_count: count of hwmon channels by hwmon type 384*23902f98SJames Seo * @has_intrusion: whether an intrusion sensor is present 385*23902f98SJames Seo * @intrusion: intrusion flag 386*23902f98SJames Seo * @lock: mutex to lock polling WMI and changes to driver state 387*23902f98SJames Seo */ 388*23902f98SJames Seo struct hp_wmi_sensors { 389*23902f98SJames Seo struct wmi_device *wdev; 390*23902f98SJames Seo struct hp_wmi_info **info_map[hwmon_max]; 391*23902f98SJames Seo u8 channel_count[hwmon_max]; 392*23902f98SJames Seo bool has_intrusion; 393*23902f98SJames Seo bool intrusion; 394*23902f98SJames Seo 395*23902f98SJames Seo struct mutex lock; /* Lock polling WMI and driver state changes. */ 396*23902f98SJames Seo }; 397*23902f98SJames Seo 398*23902f98SJames Seo /* hp_wmi_strdup - devm_kstrdup, but length-limited */ 399*23902f98SJames Seo static char *hp_wmi_strdup(struct device *dev, const char *src) 400*23902f98SJames Seo { 401*23902f98SJames Seo char *dst; 402*23902f98SJames Seo size_t len; 403*23902f98SJames Seo 404*23902f98SJames Seo len = strnlen(src, HP_WMI_MAX_STR_SIZE - 1); 405*23902f98SJames Seo 406*23902f98SJames Seo dst = devm_kmalloc(dev, (len + 1) * sizeof(*dst), GFP_KERNEL); 407*23902f98SJames Seo if (!dst) 408*23902f98SJames Seo return NULL; 409*23902f98SJames Seo 410*23902f98SJames Seo strscpy(dst, src, len + 1); 411*23902f98SJames Seo 412*23902f98SJames Seo return dst; 413*23902f98SJames Seo } 414*23902f98SJames Seo 415*23902f98SJames Seo /* 416*23902f98SJames Seo * hp_wmi_get_wobj - poll WMI for a WMI object instance 417*23902f98SJames Seo * @guid: WMI object GUID 418*23902f98SJames Seo * @instance: WMI object instance number 419*23902f98SJames Seo * 420*23902f98SJames Seo * Returns a new WMI object instance on success, or NULL on error. 421*23902f98SJames Seo * Caller must kfree() the result. 422*23902f98SJames Seo */ 423*23902f98SJames Seo static union acpi_object *hp_wmi_get_wobj(const char *guid, u8 instance) 424*23902f98SJames Seo { 425*23902f98SJames Seo struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; 426*23902f98SJames Seo acpi_status err; 427*23902f98SJames Seo 428*23902f98SJames Seo err = wmi_query_block(guid, instance, &out); 429*23902f98SJames Seo if (ACPI_FAILURE(err)) 430*23902f98SJames Seo return NULL; 431*23902f98SJames Seo 432*23902f98SJames Seo return out.pointer; 433*23902f98SJames Seo } 434*23902f98SJames Seo 435*23902f98SJames Seo /* hp_wmi_wobj_instance_count - find count of WMI object instances */ 436*23902f98SJames Seo static u8 hp_wmi_wobj_instance_count(const char *guid) 437*23902f98SJames Seo { 438*23902f98SJames Seo u8 hi = HP_WMI_MAX_INSTANCES; 439*23902f98SJames Seo union acpi_object *wobj; 440*23902f98SJames Seo u8 lo = 0; 441*23902f98SJames Seo u8 mid; 442*23902f98SJames Seo 443*23902f98SJames Seo while (lo < hi) { 444*23902f98SJames Seo mid = (lo + hi) / 2; 445*23902f98SJames Seo 446*23902f98SJames Seo wobj = hp_wmi_get_wobj(guid, mid); 447*23902f98SJames Seo if (!wobj) { 448*23902f98SJames Seo hi = mid; 449*23902f98SJames Seo continue; 450*23902f98SJames Seo } 451*23902f98SJames Seo 452*23902f98SJames Seo lo = mid + 1; 453*23902f98SJames Seo kfree(wobj); 454*23902f98SJames Seo } 455*23902f98SJames Seo 456*23902f98SJames Seo return lo; 457*23902f98SJames Seo } 458*23902f98SJames Seo 459*23902f98SJames Seo static int check_wobj(const union acpi_object *wobj, 460*23902f98SJames Seo const acpi_object_type property_map[], int last_prop) 461*23902f98SJames Seo { 462*23902f98SJames Seo acpi_object_type type = wobj->type; 463*23902f98SJames Seo acpi_object_type valid_type; 464*23902f98SJames Seo union acpi_object *elements; 465*23902f98SJames Seo u32 elem_count; 466*23902f98SJames Seo int prop; 467*23902f98SJames Seo 468*23902f98SJames Seo if (type != ACPI_TYPE_PACKAGE) 469*23902f98SJames Seo return -EINVAL; 470*23902f98SJames Seo 471*23902f98SJames Seo elem_count = wobj->package.count; 472*23902f98SJames Seo if (elem_count != last_prop + 1) 473*23902f98SJames Seo return -EINVAL; 474*23902f98SJames Seo 475*23902f98SJames Seo elements = wobj->package.elements; 476*23902f98SJames Seo for (prop = 0; prop <= last_prop; prop++) { 477*23902f98SJames Seo type = elements[prop].type; 478*23902f98SJames Seo valid_type = property_map[prop]; 479*23902f98SJames Seo if (type != valid_type) 480*23902f98SJames Seo return -EINVAL; 481*23902f98SJames Seo } 482*23902f98SJames Seo 483*23902f98SJames Seo return 0; 484*23902f98SJames Seo } 485*23902f98SJames Seo 486*23902f98SJames Seo static int extract_acpi_value(struct device *dev, 487*23902f98SJames Seo union acpi_object *element, 488*23902f98SJames Seo acpi_object_type type, 489*23902f98SJames Seo u32 *out_value, char **out_string) 490*23902f98SJames Seo { 491*23902f98SJames Seo switch (type) { 492*23902f98SJames Seo case ACPI_TYPE_INTEGER: 493*23902f98SJames Seo *out_value = element->integer.value; 494*23902f98SJames Seo break; 495*23902f98SJames Seo 496*23902f98SJames Seo case ACPI_TYPE_STRING: 497*23902f98SJames Seo *out_string = hp_wmi_strdup(dev, strim(element->string.pointer)); 498*23902f98SJames Seo if (!*out_string) 499*23902f98SJames Seo return -ENOMEM; 500*23902f98SJames Seo break; 501*23902f98SJames Seo 502*23902f98SJames Seo default: 503*23902f98SJames Seo return -EINVAL; 504*23902f98SJames Seo } 505*23902f98SJames Seo 506*23902f98SJames Seo return 0; 507*23902f98SJames Seo } 508*23902f98SJames Seo 509*23902f98SJames Seo /* 510*23902f98SJames Seo * check_numeric_sensor_wobj - validate a HPBIOS_BIOSNumericSensor instance 511*23902f98SJames Seo * @wobj: pointer to WMI object instance to check 512*23902f98SJames Seo * @out_size: out pointer to count of possible states 513*23902f98SJames Seo * @out_is_new: out pointer to whether this is a "new" variant object 514*23902f98SJames Seo * 515*23902f98SJames Seo * Returns 0 on success, or a negative error code on error. 516*23902f98SJames Seo */ 517*23902f98SJames Seo static int check_numeric_sensor_wobj(const union acpi_object *wobj, 518*23902f98SJames Seo u8 *out_size, bool *out_is_new) 519*23902f98SJames Seo { 520*23902f98SJames Seo acpi_object_type type = wobj->type; 521*23902f98SJames Seo int prop = HP_WMI_PROPERTY_NAME; 522*23902f98SJames Seo acpi_object_type valid_type; 523*23902f98SJames Seo union acpi_object *elements; 524*23902f98SJames Seo u32 elem_count; 525*23902f98SJames Seo int last_prop; 526*23902f98SJames Seo bool is_new; 527*23902f98SJames Seo u8 count; 528*23902f98SJames Seo u32 j; 529*23902f98SJames Seo u32 i; 530*23902f98SJames Seo 531*23902f98SJames Seo if (type != ACPI_TYPE_PACKAGE) 532*23902f98SJames Seo return -EINVAL; 533*23902f98SJames Seo 534*23902f98SJames Seo /* 535*23902f98SJames Seo * elements is a variable-length array of ACPI objects, one for 536*23902f98SJames Seo * each property of the WMI object instance, except that the 537*23902f98SJames Seo * strings in PossibleStates[] are flattened into this array 538*23902f98SJames Seo * as if each individual string were a property by itself. 539*23902f98SJames Seo */ 540*23902f98SJames Seo elements = wobj->package.elements; 541*23902f98SJames Seo 542*23902f98SJames Seo elem_count = wobj->package.count; 543*23902f98SJames Seo if (elem_count <= HP_WMI_PROPERTY_SIZE || 544*23902f98SJames Seo elem_count > HP_WMI_MAX_PROPERTIES) 545*23902f98SJames Seo return -EINVAL; 546*23902f98SJames Seo 547*23902f98SJames Seo type = elements[HP_WMI_PROPERTY_SIZE].type; 548*23902f98SJames Seo switch (type) { 549*23902f98SJames Seo case ACPI_TYPE_INTEGER: 550*23902f98SJames Seo is_new = true; 551*23902f98SJames Seo last_prop = HP_WMI_PROPERTY_RATE_UNITS; 552*23902f98SJames Seo break; 553*23902f98SJames Seo 554*23902f98SJames Seo case ACPI_TYPE_STRING: 555*23902f98SJames Seo is_new = false; 556*23902f98SJames Seo last_prop = HP_WMI_PROPERTY_CURRENT_READING; 557*23902f98SJames Seo break; 558*23902f98SJames Seo 559*23902f98SJames Seo default: 560*23902f98SJames Seo return -EINVAL; 561*23902f98SJames Seo } 562*23902f98SJames Seo 563*23902f98SJames Seo /* 564*23902f98SJames Seo * In general, the count of PossibleStates[] must be > 0. 565*23902f98SJames Seo * Also, the old variant lacks the Size property, so we may need to 566*23902f98SJames Seo * reduce the value of last_prop by 1 when doing arithmetic with it. 567*23902f98SJames Seo */ 568*23902f98SJames Seo if (elem_count < last_prop - !is_new + 1) 569*23902f98SJames Seo return -EINVAL; 570*23902f98SJames Seo 571*23902f98SJames Seo count = elem_count - (last_prop - !is_new); 572*23902f98SJames Seo 573*23902f98SJames Seo for (i = 0; i < elem_count && prop <= last_prop; i++, prop++) { 574*23902f98SJames Seo type = elements[i].type; 575*23902f98SJames Seo valid_type = hp_wmi_property_map[prop]; 576*23902f98SJames Seo if (type != valid_type) 577*23902f98SJames Seo return -EINVAL; 578*23902f98SJames Seo 579*23902f98SJames Seo switch (prop) { 580*23902f98SJames Seo case HP_WMI_PROPERTY_OPERATIONAL_STATUS: 581*23902f98SJames Seo /* Old variant: CurrentState follows OperationalStatus. */ 582*23902f98SJames Seo if (!is_new) 583*23902f98SJames Seo prop = HP_WMI_PROPERTY_CURRENT_STATE - 1; 584*23902f98SJames Seo break; 585*23902f98SJames Seo 586*23902f98SJames Seo case HP_WMI_PROPERTY_SIZE: 587*23902f98SJames Seo /* New variant: Size == count of PossibleStates[]. */ 588*23902f98SJames Seo if (count != elements[i].integer.value) 589*23902f98SJames Seo return -EINVAL; 590*23902f98SJames Seo break; 591*23902f98SJames Seo 592*23902f98SJames Seo case HP_WMI_PROPERTY_POSSIBLE_STATES: 593*23902f98SJames Seo /* PossibleStates[0] has already been type-checked. */ 594*23902f98SJames Seo for (j = 0; i + 1 < elem_count && j + 1 < count; j++) { 595*23902f98SJames Seo type = elements[++i].type; 596*23902f98SJames Seo if (type != valid_type) 597*23902f98SJames Seo return -EINVAL; 598*23902f98SJames Seo } 599*23902f98SJames Seo 600*23902f98SJames Seo /* Old variant: BaseUnits follows PossibleStates[]. */ 601*23902f98SJames Seo if (!is_new) 602*23902f98SJames Seo prop = HP_WMI_PROPERTY_BASE_UNITS - 1; 603*23902f98SJames Seo break; 604*23902f98SJames Seo 605*23902f98SJames Seo case HP_WMI_PROPERTY_CURRENT_STATE: 606*23902f98SJames Seo /* Old variant: PossibleStates[] follows CurrentState. */ 607*23902f98SJames Seo if (!is_new) 608*23902f98SJames Seo prop = HP_WMI_PROPERTY_POSSIBLE_STATES - 1; 609*23902f98SJames Seo break; 610*23902f98SJames Seo } 611*23902f98SJames Seo } 612*23902f98SJames Seo 613*23902f98SJames Seo if (prop != last_prop + 1) 614*23902f98SJames Seo return -EINVAL; 615*23902f98SJames Seo 616*23902f98SJames Seo *out_size = count; 617*23902f98SJames Seo *out_is_new = is_new; 618*23902f98SJames Seo 619*23902f98SJames Seo return 0; 620*23902f98SJames Seo } 621*23902f98SJames Seo 622*23902f98SJames Seo static int 623*23902f98SJames Seo numeric_sensor_is_connected(const struct hp_wmi_numeric_sensor *nsensor) 624*23902f98SJames Seo { 625*23902f98SJames Seo u32 operational_status = nsensor->operational_status; 626*23902f98SJames Seo 627*23902f98SJames Seo return operational_status != HP_WMI_STATUS_NO_CONTACT; 628*23902f98SJames Seo } 629*23902f98SJames Seo 630*23902f98SJames Seo static int numeric_sensor_has_fault(const struct hp_wmi_numeric_sensor *nsensor) 631*23902f98SJames Seo { 632*23902f98SJames Seo u32 operational_status = nsensor->operational_status; 633*23902f98SJames Seo 634*23902f98SJames Seo switch (operational_status) { 635*23902f98SJames Seo case HP_WMI_STATUS_DEGRADED: 636*23902f98SJames Seo case HP_WMI_STATUS_STRESSED: /* e.g. Overload, overtemp. */ 637*23902f98SJames Seo case HP_WMI_STATUS_PREDICTIVE_FAILURE: /* e.g. Fan removed. */ 638*23902f98SJames Seo case HP_WMI_STATUS_ERROR: 639*23902f98SJames Seo case HP_WMI_STATUS_NON_RECOVERABLE_ERROR: 640*23902f98SJames Seo case HP_WMI_STATUS_NO_CONTACT: 641*23902f98SJames Seo case HP_WMI_STATUS_LOST_COMMUNICATION: 642*23902f98SJames Seo case HP_WMI_STATUS_ABORTED: 643*23902f98SJames Seo case HP_WMI_STATUS_SUPPORTING_ENTITY_IN_ERROR: 644*23902f98SJames Seo 645*23902f98SJames Seo /* Assume combination by addition; bitwise OR doesn't make sense. */ 646*23902f98SJames Seo case HP_WMI_STATUS_COMPLETED + HP_WMI_STATUS_DEGRADED: 647*23902f98SJames Seo case HP_WMI_STATUS_COMPLETED + HP_WMI_STATUS_ERROR: 648*23902f98SJames Seo return true; 649*23902f98SJames Seo } 650*23902f98SJames Seo 651*23902f98SJames Seo return false; 652*23902f98SJames Seo } 653*23902f98SJames Seo 654*23902f98SJames Seo /* scale_numeric_sensor - scale sensor reading for hwmon */ 655*23902f98SJames Seo static long scale_numeric_sensor(const struct hp_wmi_numeric_sensor *nsensor) 656*23902f98SJames Seo { 657*23902f98SJames Seo u32 current_reading = nsensor->current_reading; 658*23902f98SJames Seo s32 unit_modifier = nsensor->unit_modifier; 659*23902f98SJames Seo u32 sensor_type = nsensor->sensor_type; 660*23902f98SJames Seo u32 base_units = nsensor->base_units; 661*23902f98SJames Seo s32 target_modifier; 662*23902f98SJames Seo long val; 663*23902f98SJames Seo 664*23902f98SJames Seo /* Fan readings are in RPM units; others are in milliunits. */ 665*23902f98SJames Seo target_modifier = sensor_type == HP_WMI_TYPE_AIR_FLOW ? 0 : -3; 666*23902f98SJames Seo 667*23902f98SJames Seo val = current_reading; 668*23902f98SJames Seo 669*23902f98SJames Seo for (; unit_modifier < target_modifier; unit_modifier++) 670*23902f98SJames Seo val = DIV_ROUND_CLOSEST(val, 10); 671*23902f98SJames Seo 672*23902f98SJames Seo for (; unit_modifier > target_modifier; unit_modifier--) { 673*23902f98SJames Seo if (val > LONG_MAX / 10) { 674*23902f98SJames Seo val = LONG_MAX; 675*23902f98SJames Seo break; 676*23902f98SJames Seo } 677*23902f98SJames Seo val *= 10; 678*23902f98SJames Seo } 679*23902f98SJames Seo 680*23902f98SJames Seo if (sensor_type == HP_WMI_TYPE_TEMPERATURE) { 681*23902f98SJames Seo switch (base_units) { 682*23902f98SJames Seo case HP_WMI_UNITS_DEGREES_F: 683*23902f98SJames Seo val -= MILLI * 32; 684*23902f98SJames Seo val = val <= LONG_MAX / 5 ? 685*23902f98SJames Seo DIV_ROUND_CLOSEST(val * 5, 9) : 686*23902f98SJames Seo DIV_ROUND_CLOSEST(val, 9) * 5; 687*23902f98SJames Seo break; 688*23902f98SJames Seo 689*23902f98SJames Seo case HP_WMI_UNITS_DEGREES_K: 690*23902f98SJames Seo val = milli_kelvin_to_millicelsius(val); 691*23902f98SJames Seo break; 692*23902f98SJames Seo } 693*23902f98SJames Seo } 694*23902f98SJames Seo 695*23902f98SJames Seo return val; 696*23902f98SJames Seo } 697*23902f98SJames Seo 698*23902f98SJames Seo /* 699*23902f98SJames Seo * classify_numeric_sensor - classify a numeric sensor 700*23902f98SJames Seo * @nsensor: pointer to numeric sensor struct 701*23902f98SJames Seo * 702*23902f98SJames Seo * Returns an enum hp_wmi_type value on success, 703*23902f98SJames Seo * or a negative value if the sensor type is unsupported. 704*23902f98SJames Seo */ 705*23902f98SJames Seo static int classify_numeric_sensor(const struct hp_wmi_numeric_sensor *nsensor) 706*23902f98SJames Seo { 707*23902f98SJames Seo u32 sensor_type = nsensor->sensor_type; 708*23902f98SJames Seo u32 base_units = nsensor->base_units; 709*23902f98SJames Seo const char *name = nsensor->name; 710*23902f98SJames Seo 711*23902f98SJames Seo switch (sensor_type) { 712*23902f98SJames Seo case HP_WMI_TYPE_TEMPERATURE: 713*23902f98SJames Seo /* 714*23902f98SJames Seo * Some systems have sensors named "X Thermal Index" in "Other" 715*23902f98SJames Seo * units. Tested CPU sensor examples were found to be in °C, 716*23902f98SJames Seo * albeit perhaps "differently" accurate; e.g. readings were 717*23902f98SJames Seo * reliably -6°C vs. coretemp on a HP Compaq Elite 8300, and 718*23902f98SJames Seo * +8°C on an EliteOne G1 800. But this is still within the 719*23902f98SJames Seo * realm of plausibility for cheaply implemented motherboard 720*23902f98SJames Seo * sensors, and chassis readings were about as expected. 721*23902f98SJames Seo */ 722*23902f98SJames Seo if ((base_units == HP_WMI_UNITS_OTHER && 723*23902f98SJames Seo strstr(name, HP_WMI_PATTERN_TEMP_SENSOR)) || 724*23902f98SJames Seo base_units == HP_WMI_UNITS_DEGREES_C || 725*23902f98SJames Seo base_units == HP_WMI_UNITS_DEGREES_F || 726*23902f98SJames Seo base_units == HP_WMI_UNITS_DEGREES_K) 727*23902f98SJames Seo return HP_WMI_TYPE_TEMPERATURE; 728*23902f98SJames Seo break; 729*23902f98SJames Seo 730*23902f98SJames Seo case HP_WMI_TYPE_VOLTAGE: 731*23902f98SJames Seo if (base_units == HP_WMI_UNITS_VOLTS) 732*23902f98SJames Seo return HP_WMI_TYPE_VOLTAGE; 733*23902f98SJames Seo break; 734*23902f98SJames Seo 735*23902f98SJames Seo case HP_WMI_TYPE_CURRENT: 736*23902f98SJames Seo if (base_units == HP_WMI_UNITS_AMPS) 737*23902f98SJames Seo return HP_WMI_TYPE_CURRENT; 738*23902f98SJames Seo break; 739*23902f98SJames Seo 740*23902f98SJames Seo case HP_WMI_TYPE_AIR_FLOW: 741*23902f98SJames Seo /* 742*23902f98SJames Seo * Strangely, HP considers fan RPM sensor type to be 743*23902f98SJames Seo * "Air Flow" instead of the more intuitive "Tachometer". 744*23902f98SJames Seo */ 745*23902f98SJames Seo if (base_units == HP_WMI_UNITS_RPM) 746*23902f98SJames Seo return HP_WMI_TYPE_AIR_FLOW; 747*23902f98SJames Seo break; 748*23902f98SJames Seo } 749*23902f98SJames Seo 750*23902f98SJames Seo return -EINVAL; 751*23902f98SJames Seo } 752*23902f98SJames Seo 753*23902f98SJames Seo static int 754*23902f98SJames Seo populate_numeric_sensor_from_wobj(struct device *dev, 755*23902f98SJames Seo struct hp_wmi_numeric_sensor *nsensor, 756*23902f98SJames Seo union acpi_object *wobj, bool *out_is_new) 757*23902f98SJames Seo { 758*23902f98SJames Seo int last_prop = HP_WMI_PROPERTY_RATE_UNITS; 759*23902f98SJames Seo int prop = HP_WMI_PROPERTY_NAME; 760*23902f98SJames Seo const char **possible_states; 761*23902f98SJames Seo union acpi_object *element; 762*23902f98SJames Seo acpi_object_type type; 763*23902f98SJames Seo char *string; 764*23902f98SJames Seo bool is_new; 765*23902f98SJames Seo u32 value; 766*23902f98SJames Seo u8 size; 767*23902f98SJames Seo int err; 768*23902f98SJames Seo 769*23902f98SJames Seo err = check_numeric_sensor_wobj(wobj, &size, &is_new); 770*23902f98SJames Seo if (err) 771*23902f98SJames Seo return err; 772*23902f98SJames Seo 773*23902f98SJames Seo possible_states = devm_kcalloc(dev, size, sizeof(*possible_states), 774*23902f98SJames Seo GFP_KERNEL); 775*23902f98SJames Seo if (!possible_states) 776*23902f98SJames Seo return -ENOMEM; 777*23902f98SJames Seo 778*23902f98SJames Seo element = wobj->package.elements; 779*23902f98SJames Seo nsensor->possible_states = possible_states; 780*23902f98SJames Seo nsensor->size = size; 781*23902f98SJames Seo 782*23902f98SJames Seo if (!is_new) 783*23902f98SJames Seo last_prop = HP_WMI_PROPERTY_CURRENT_READING; 784*23902f98SJames Seo 785*23902f98SJames Seo for (; prop <= last_prop; prop++) { 786*23902f98SJames Seo type = hp_wmi_property_map[prop]; 787*23902f98SJames Seo 788*23902f98SJames Seo err = extract_acpi_value(dev, element, type, &value, &string); 789*23902f98SJames Seo if (err) 790*23902f98SJames Seo return err; 791*23902f98SJames Seo 792*23902f98SJames Seo element++; 793*23902f98SJames Seo 794*23902f98SJames Seo switch (prop) { 795*23902f98SJames Seo case HP_WMI_PROPERTY_NAME: 796*23902f98SJames Seo nsensor->name = string; 797*23902f98SJames Seo break; 798*23902f98SJames Seo 799*23902f98SJames Seo case HP_WMI_PROPERTY_DESCRIPTION: 800*23902f98SJames Seo nsensor->description = string; 801*23902f98SJames Seo break; 802*23902f98SJames Seo 803*23902f98SJames Seo case HP_WMI_PROPERTY_SENSOR_TYPE: 804*23902f98SJames Seo if (value > HP_WMI_TYPE_AIR_FLOW) 805*23902f98SJames Seo return -EINVAL; 806*23902f98SJames Seo 807*23902f98SJames Seo nsensor->sensor_type = value; 808*23902f98SJames Seo break; 809*23902f98SJames Seo 810*23902f98SJames Seo case HP_WMI_PROPERTY_OTHER_SENSOR_TYPE: 811*23902f98SJames Seo nsensor->other_sensor_type = string; 812*23902f98SJames Seo break; 813*23902f98SJames Seo 814*23902f98SJames Seo case HP_WMI_PROPERTY_OPERATIONAL_STATUS: 815*23902f98SJames Seo nsensor->operational_status = value; 816*23902f98SJames Seo 817*23902f98SJames Seo /* Old variant: CurrentState follows OperationalStatus. */ 818*23902f98SJames Seo if (!is_new) 819*23902f98SJames Seo prop = HP_WMI_PROPERTY_CURRENT_STATE - 1; 820*23902f98SJames Seo break; 821*23902f98SJames Seo 822*23902f98SJames Seo case HP_WMI_PROPERTY_SIZE: 823*23902f98SJames Seo break; /* Already set. */ 824*23902f98SJames Seo 825*23902f98SJames Seo case HP_WMI_PROPERTY_POSSIBLE_STATES: 826*23902f98SJames Seo *possible_states++ = string; 827*23902f98SJames Seo if (--size) 828*23902f98SJames Seo prop--; 829*23902f98SJames Seo 830*23902f98SJames Seo /* Old variant: BaseUnits follows PossibleStates[]. */ 831*23902f98SJames Seo if (!is_new && !size) 832*23902f98SJames Seo prop = HP_WMI_PROPERTY_BASE_UNITS - 1; 833*23902f98SJames Seo break; 834*23902f98SJames Seo 835*23902f98SJames Seo case HP_WMI_PROPERTY_CURRENT_STATE: 836*23902f98SJames Seo nsensor->current_state = string; 837*23902f98SJames Seo 838*23902f98SJames Seo /* Old variant: PossibleStates[] follows CurrentState. */ 839*23902f98SJames Seo if (!is_new) 840*23902f98SJames Seo prop = HP_WMI_PROPERTY_POSSIBLE_STATES - 1; 841*23902f98SJames Seo break; 842*23902f98SJames Seo 843*23902f98SJames Seo case HP_WMI_PROPERTY_BASE_UNITS: 844*23902f98SJames Seo nsensor->base_units = value; 845*23902f98SJames Seo break; 846*23902f98SJames Seo 847*23902f98SJames Seo case HP_WMI_PROPERTY_UNIT_MODIFIER: 848*23902f98SJames Seo /* UnitModifier is signed. */ 849*23902f98SJames Seo nsensor->unit_modifier = (s32)value; 850*23902f98SJames Seo break; 851*23902f98SJames Seo 852*23902f98SJames Seo case HP_WMI_PROPERTY_CURRENT_READING: 853*23902f98SJames Seo nsensor->current_reading = value; 854*23902f98SJames Seo break; 855*23902f98SJames Seo 856*23902f98SJames Seo case HP_WMI_PROPERTY_RATE_UNITS: 857*23902f98SJames Seo nsensor->rate_units = value; 858*23902f98SJames Seo break; 859*23902f98SJames Seo 860*23902f98SJames Seo default: 861*23902f98SJames Seo return -EINVAL; 862*23902f98SJames Seo } 863*23902f98SJames Seo } 864*23902f98SJames Seo 865*23902f98SJames Seo *out_is_new = is_new; 866*23902f98SJames Seo 867*23902f98SJames Seo return 0; 868*23902f98SJames Seo } 869*23902f98SJames Seo 870*23902f98SJames Seo /* update_numeric_sensor_from_wobj - update fungible sensor properties */ 871*23902f98SJames Seo static void 872*23902f98SJames Seo update_numeric_sensor_from_wobj(struct device *dev, 873*23902f98SJames Seo struct hp_wmi_numeric_sensor *nsensor, 874*23902f98SJames Seo const union acpi_object *wobj) 875*23902f98SJames Seo { 876*23902f98SJames Seo const union acpi_object *elements; 877*23902f98SJames Seo const union acpi_object *element; 878*23902f98SJames Seo const char *string; 879*23902f98SJames Seo bool is_new; 880*23902f98SJames Seo int offset; 881*23902f98SJames Seo u8 size; 882*23902f98SJames Seo int err; 883*23902f98SJames Seo 884*23902f98SJames Seo err = check_numeric_sensor_wobj(wobj, &size, &is_new); 885*23902f98SJames Seo if (err) 886*23902f98SJames Seo return; 887*23902f98SJames Seo 888*23902f98SJames Seo elements = wobj->package.elements; 889*23902f98SJames Seo 890*23902f98SJames Seo element = &elements[HP_WMI_PROPERTY_OPERATIONAL_STATUS]; 891*23902f98SJames Seo nsensor->operational_status = element->integer.value; 892*23902f98SJames Seo 893*23902f98SJames Seo /* 894*23902f98SJames Seo * In general, an index offset is needed after PossibleStates[0]. 895*23902f98SJames Seo * On a new variant, CurrentState is after PossibleStates[]. This is 896*23902f98SJames Seo * not the case on an old variant, but we still need to offset the 897*23902f98SJames Seo * read because CurrentState is where Size would be on a new variant. 898*23902f98SJames Seo */ 899*23902f98SJames Seo offset = is_new ? size - 1 : -2; 900*23902f98SJames Seo 901*23902f98SJames Seo element = &elements[HP_WMI_PROPERTY_CURRENT_STATE + offset]; 902*23902f98SJames Seo string = strim(element->string.pointer); 903*23902f98SJames Seo 904*23902f98SJames Seo if (strcmp(string, nsensor->current_state)) { 905*23902f98SJames Seo devm_kfree(dev, nsensor->current_state); 906*23902f98SJames Seo nsensor->current_state = hp_wmi_strdup(dev, string); 907*23902f98SJames Seo } 908*23902f98SJames Seo 909*23902f98SJames Seo /* Old variant: -2 (not -1) because it lacks the Size property. */ 910*23902f98SJames Seo if (!is_new) 911*23902f98SJames Seo offset = (int)size - 2; /* size is > 0, i.e. may be 1. */ 912*23902f98SJames Seo 913*23902f98SJames Seo element = &elements[HP_WMI_PROPERTY_UNIT_MODIFIER + offset]; 914*23902f98SJames Seo nsensor->unit_modifier = (s32)element->integer.value; 915*23902f98SJames Seo 916*23902f98SJames Seo element = &elements[HP_WMI_PROPERTY_CURRENT_READING + offset]; 917*23902f98SJames Seo nsensor->current_reading = element->integer.value; 918*23902f98SJames Seo } 919*23902f98SJames Seo 920*23902f98SJames Seo /* 921*23902f98SJames Seo * check_platform_events_wobj - validate a HPBIOS_PlatformEvents instance 922*23902f98SJames Seo * @wobj: pointer to WMI object instance to check 923*23902f98SJames Seo * 924*23902f98SJames Seo * Returns 0 on success, or a negative error code on error. 925*23902f98SJames Seo */ 926*23902f98SJames Seo static int check_platform_events_wobj(const union acpi_object *wobj) 927*23902f98SJames Seo { 928*23902f98SJames Seo return check_wobj(wobj, hp_wmi_platform_events_property_map, 929*23902f98SJames Seo HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS); 930*23902f98SJames Seo } 931*23902f98SJames Seo 932*23902f98SJames Seo static int 933*23902f98SJames Seo populate_platform_events_from_wobj(struct device *dev, 934*23902f98SJames Seo struct hp_wmi_platform_events *pevents, 935*23902f98SJames Seo union acpi_object *wobj) 936*23902f98SJames Seo { 937*23902f98SJames Seo int last_prop = HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS; 938*23902f98SJames Seo int prop = HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME; 939*23902f98SJames Seo union acpi_object *element; 940*23902f98SJames Seo acpi_object_type type; 941*23902f98SJames Seo char *string; 942*23902f98SJames Seo u32 value; 943*23902f98SJames Seo int err; 944*23902f98SJames Seo 945*23902f98SJames Seo err = check_platform_events_wobj(wobj); 946*23902f98SJames Seo if (err) 947*23902f98SJames Seo return err; 948*23902f98SJames Seo 949*23902f98SJames Seo element = wobj->package.elements; 950*23902f98SJames Seo 951*23902f98SJames Seo for (; prop <= last_prop; prop++, element++) { 952*23902f98SJames Seo type = hp_wmi_platform_events_property_map[prop]; 953*23902f98SJames Seo 954*23902f98SJames Seo err = extract_acpi_value(dev, element, type, &value, &string); 955*23902f98SJames Seo if (err) 956*23902f98SJames Seo return err; 957*23902f98SJames Seo 958*23902f98SJames Seo switch (prop) { 959*23902f98SJames Seo case HP_WMI_PLATFORM_EVENTS_PROPERTY_NAME: 960*23902f98SJames Seo pevents->name = string; 961*23902f98SJames Seo break; 962*23902f98SJames Seo 963*23902f98SJames Seo case HP_WMI_PLATFORM_EVENTS_PROPERTY_DESCRIPTION: 964*23902f98SJames Seo pevents->description = string; 965*23902f98SJames Seo break; 966*23902f98SJames Seo 967*23902f98SJames Seo case HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_NAMESPACE: 968*23902f98SJames Seo if (strcasecmp(HP_WMI_EVENT_NAMESPACE, string)) 969*23902f98SJames Seo return -EINVAL; 970*23902f98SJames Seo 971*23902f98SJames Seo pevents->source_namespace = string; 972*23902f98SJames Seo break; 973*23902f98SJames Seo 974*23902f98SJames Seo case HP_WMI_PLATFORM_EVENTS_PROPERTY_SOURCE_CLASS: 975*23902f98SJames Seo if (strcasecmp(HP_WMI_EVENT_CLASS, string)) 976*23902f98SJames Seo return -EINVAL; 977*23902f98SJames Seo 978*23902f98SJames Seo pevents->source_class = string; 979*23902f98SJames Seo break; 980*23902f98SJames Seo 981*23902f98SJames Seo case HP_WMI_PLATFORM_EVENTS_PROPERTY_CATEGORY: 982*23902f98SJames Seo pevents->category = value; 983*23902f98SJames Seo break; 984*23902f98SJames Seo 985*23902f98SJames Seo case HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_SEVERITY: 986*23902f98SJames Seo pevents->possible_severity = value; 987*23902f98SJames Seo break; 988*23902f98SJames Seo 989*23902f98SJames Seo case HP_WMI_PLATFORM_EVENTS_PROPERTY_POSSIBLE_STATUS: 990*23902f98SJames Seo pevents->possible_status = value; 991*23902f98SJames Seo break; 992*23902f98SJames Seo 993*23902f98SJames Seo default: 994*23902f98SJames Seo return -EINVAL; 995*23902f98SJames Seo } 996*23902f98SJames Seo } 997*23902f98SJames Seo 998*23902f98SJames Seo return 0; 999*23902f98SJames Seo } 1000*23902f98SJames Seo 1001*23902f98SJames Seo /* 1002*23902f98SJames Seo * check_event_wobj - validate a HPBIOS_BIOSEvent instance 1003*23902f98SJames Seo * @wobj: pointer to WMI object instance to check 1004*23902f98SJames Seo * 1005*23902f98SJames Seo * Returns 0 on success, or a negative error code on error. 1006*23902f98SJames Seo */ 1007*23902f98SJames Seo static int check_event_wobj(const union acpi_object *wobj) 1008*23902f98SJames Seo { 1009*23902f98SJames Seo return check_wobj(wobj, hp_wmi_event_property_map, 1010*23902f98SJames Seo HP_WMI_EVENT_PROPERTY_STATUS); 1011*23902f98SJames Seo } 1012*23902f98SJames Seo 1013*23902f98SJames Seo static int populate_event_from_wobj(struct hp_wmi_event *event, 1014*23902f98SJames Seo union acpi_object *wobj) 1015*23902f98SJames Seo { 1016*23902f98SJames Seo int prop = HP_WMI_EVENT_PROPERTY_NAME; 1017*23902f98SJames Seo union acpi_object *element; 1018*23902f98SJames Seo int err; 1019*23902f98SJames Seo 1020*23902f98SJames Seo err = check_event_wobj(wobj); 1021*23902f98SJames Seo if (err) 1022*23902f98SJames Seo return err; 1023*23902f98SJames Seo 1024*23902f98SJames Seo element = wobj->package.elements; 1025*23902f98SJames Seo 1026*23902f98SJames Seo /* Extracted strings are NOT device-managed copies. */ 1027*23902f98SJames Seo 1028*23902f98SJames Seo for (; prop <= HP_WMI_EVENT_PROPERTY_CATEGORY; prop++, element++) { 1029*23902f98SJames Seo switch (prop) { 1030*23902f98SJames Seo case HP_WMI_EVENT_PROPERTY_NAME: 1031*23902f98SJames Seo event->name = strim(element->string.pointer); 1032*23902f98SJames Seo break; 1033*23902f98SJames Seo 1034*23902f98SJames Seo case HP_WMI_EVENT_PROPERTY_DESCRIPTION: 1035*23902f98SJames Seo event->description = strim(element->string.pointer); 1036*23902f98SJames Seo break; 1037*23902f98SJames Seo 1038*23902f98SJames Seo case HP_WMI_EVENT_PROPERTY_CATEGORY: 1039*23902f98SJames Seo event->category = element->integer.value; 1040*23902f98SJames Seo break; 1041*23902f98SJames Seo 1042*23902f98SJames Seo default: 1043*23902f98SJames Seo return -EINVAL; 1044*23902f98SJames Seo } 1045*23902f98SJames Seo } 1046*23902f98SJames Seo 1047*23902f98SJames Seo return 0; 1048*23902f98SJames Seo } 1049*23902f98SJames Seo 1050*23902f98SJames Seo /* 1051*23902f98SJames Seo * classify_event - classify an event 1052*23902f98SJames Seo * @name: event name 1053*23902f98SJames Seo * @category: event category 1054*23902f98SJames Seo * 1055*23902f98SJames Seo * Classify instances of both HPBIOS_PlatformEvents and HPBIOS_BIOSEvent from 1056*23902f98SJames Seo * property values. Recognition criteria are based on multiple ACPI dumps [3]. 1057*23902f98SJames Seo * 1058*23902f98SJames Seo * Returns an enum hp_wmi_type value on success, 1059*23902f98SJames Seo * or a negative value if the event type is unsupported. 1060*23902f98SJames Seo */ 1061*23902f98SJames Seo static int classify_event(const char *event_name, u32 category) 1062*23902f98SJames Seo { 1063*23902f98SJames Seo if (category != HP_WMI_CATEGORY_SENSOR) 1064*23902f98SJames Seo return -EINVAL; 1065*23902f98SJames Seo 1066*23902f98SJames Seo /* Fan events have Name "X Stall". */ 1067*23902f98SJames Seo if (strstr(event_name, HP_WMI_PATTERN_FAN_ALARM)) 1068*23902f98SJames Seo return HP_WMI_TYPE_AIR_FLOW; 1069*23902f98SJames Seo 1070*23902f98SJames Seo /* Intrusion events have Name "Hood Intrusion". */ 1071*23902f98SJames Seo if (!strcmp(event_name, HP_WMI_PATTERN_INTRUSION_ALARM)) 1072*23902f98SJames Seo return HP_WMI_TYPE_INTRUSION; 1073*23902f98SJames Seo 1074*23902f98SJames Seo /* 1075*23902f98SJames Seo * Temperature events have Name either "Thermal Caution" or 1076*23902f98SJames Seo * "Thermal Critical". Deal only with "Thermal Critical" events. 1077*23902f98SJames Seo * 1078*23902f98SJames Seo * "Thermal Caution" events have Status "Stressed", informing us that 1079*23902f98SJames Seo * the OperationalStatus of the related sensor has become "Stressed". 1080*23902f98SJames Seo * However, this is already a fault condition that will clear itself 1081*23902f98SJames Seo * when the sensor recovers, so we have no further interest in them. 1082*23902f98SJames Seo */ 1083*23902f98SJames Seo if (!strcmp(event_name, HP_WMI_PATTERN_TEMP_ALARM)) 1084*23902f98SJames Seo return HP_WMI_TYPE_TEMPERATURE; 1085*23902f98SJames Seo 1086*23902f98SJames Seo return -EINVAL; 1087*23902f98SJames Seo } 1088*23902f98SJames Seo 1089*23902f98SJames Seo /* 1090*23902f98SJames Seo * interpret_info - interpret sensor for hwmon 1091*23902f98SJames Seo * @info: pointer to sensor info struct 1092*23902f98SJames Seo * 1093*23902f98SJames Seo * Should be called after the numeric sensor member has been updated. 1094*23902f98SJames Seo */ 1095*23902f98SJames Seo static void interpret_info(struct hp_wmi_info *info) 1096*23902f98SJames Seo { 1097*23902f98SJames Seo const struct hp_wmi_numeric_sensor *nsensor = &info->nsensor; 1098*23902f98SJames Seo 1099*23902f98SJames Seo info->cached_val = scale_numeric_sensor(nsensor); 1100*23902f98SJames Seo info->last_updated = jiffies; 1101*23902f98SJames Seo } 1102*23902f98SJames Seo 1103*23902f98SJames Seo /* 1104*23902f98SJames Seo * hp_wmi_update_info - poll WMI to update sensor info 1105*23902f98SJames Seo * @state: pointer to driver state 1106*23902f98SJames Seo * @info: pointer to sensor info struct 1107*23902f98SJames Seo * 1108*23902f98SJames Seo * Returns 0 on success, or a negative error code on error. 1109*23902f98SJames Seo */ 1110*23902f98SJames Seo static int hp_wmi_update_info(struct hp_wmi_sensors *state, 1111*23902f98SJames Seo struct hp_wmi_info *info) 1112*23902f98SJames Seo { 1113*23902f98SJames Seo struct hp_wmi_numeric_sensor *nsensor = &info->nsensor; 1114*23902f98SJames Seo struct device *dev = &state->wdev->dev; 1115*23902f98SJames Seo const union acpi_object *wobj; 1116*23902f98SJames Seo u8 instance = info->instance; 1117*23902f98SJames Seo int ret = 0; 1118*23902f98SJames Seo 1119*23902f98SJames Seo if (time_after(jiffies, info->last_updated + HZ)) { 1120*23902f98SJames Seo mutex_lock(&state->lock); 1121*23902f98SJames Seo 1122*23902f98SJames Seo wobj = hp_wmi_get_wobj(HP_WMI_NUMERIC_SENSOR_GUID, instance); 1123*23902f98SJames Seo if (!wobj) { 1124*23902f98SJames Seo ret = -EIO; 1125*23902f98SJames Seo goto out_unlock; 1126*23902f98SJames Seo } 1127*23902f98SJames Seo 1128*23902f98SJames Seo update_numeric_sensor_from_wobj(dev, nsensor, wobj); 1129*23902f98SJames Seo 1130*23902f98SJames Seo interpret_info(info); 1131*23902f98SJames Seo 1132*23902f98SJames Seo kfree(wobj); 1133*23902f98SJames Seo 1134*23902f98SJames Seo out_unlock: 1135*23902f98SJames Seo mutex_unlock(&state->lock); 1136*23902f98SJames Seo } 1137*23902f98SJames Seo 1138*23902f98SJames Seo return ret; 1139*23902f98SJames Seo } 1140*23902f98SJames Seo 1141*23902f98SJames Seo #if CONFIG_DEBUG_FS 1142*23902f98SJames Seo 1143*23902f98SJames Seo static int basic_string_show(struct seq_file *seqf, void *ignored) 1144*23902f98SJames Seo { 1145*23902f98SJames Seo const char *str = seqf->private; 1146*23902f98SJames Seo 1147*23902f98SJames Seo seq_printf(seqf, "%s\n", str); 1148*23902f98SJames Seo 1149*23902f98SJames Seo return 0; 1150*23902f98SJames Seo } 1151*23902f98SJames Seo DEFINE_SHOW_ATTRIBUTE(basic_string); 1152*23902f98SJames Seo 1153*23902f98SJames Seo static int fungible_show(struct seq_file *seqf, enum hp_wmi_property prop) 1154*23902f98SJames Seo { 1155*23902f98SJames Seo struct hp_wmi_numeric_sensor *nsensor; 1156*23902f98SJames Seo struct hp_wmi_sensors *state; 1157*23902f98SJames Seo struct hp_wmi_info *info; 1158*23902f98SJames Seo int err; 1159*23902f98SJames Seo 1160*23902f98SJames Seo info = seqf->private; 1161*23902f98SJames Seo state = info->state; 1162*23902f98SJames Seo nsensor = &info->nsensor; 1163*23902f98SJames Seo 1164*23902f98SJames Seo err = hp_wmi_update_info(state, info); 1165*23902f98SJames Seo if (err) 1166*23902f98SJames Seo return err; 1167*23902f98SJames Seo 1168*23902f98SJames Seo switch (prop) { 1169*23902f98SJames Seo case HP_WMI_PROPERTY_OPERATIONAL_STATUS: 1170*23902f98SJames Seo seq_printf(seqf, "%u\n", nsensor->operational_status); 1171*23902f98SJames Seo break; 1172*23902f98SJames Seo 1173*23902f98SJames Seo case HP_WMI_PROPERTY_CURRENT_STATE: 1174*23902f98SJames Seo seq_printf(seqf, "%s\n", nsensor->current_state); 1175*23902f98SJames Seo break; 1176*23902f98SJames Seo 1177*23902f98SJames Seo case HP_WMI_PROPERTY_UNIT_MODIFIER: 1178*23902f98SJames Seo seq_printf(seqf, "%d\n", nsensor->unit_modifier); 1179*23902f98SJames Seo break; 1180*23902f98SJames Seo 1181*23902f98SJames Seo case HP_WMI_PROPERTY_CURRENT_READING: 1182*23902f98SJames Seo seq_printf(seqf, "%u\n", nsensor->current_reading); 1183*23902f98SJames Seo break; 1184*23902f98SJames Seo 1185*23902f98SJames Seo default: 1186*23902f98SJames Seo return -EOPNOTSUPP; 1187*23902f98SJames Seo } 1188*23902f98SJames Seo 1189*23902f98SJames Seo return 0; 1190*23902f98SJames Seo } 1191*23902f98SJames Seo 1192*23902f98SJames Seo static int operational_status_show(struct seq_file *seqf, void *ignored) 1193*23902f98SJames Seo { 1194*23902f98SJames Seo return fungible_show(seqf, HP_WMI_PROPERTY_OPERATIONAL_STATUS); 1195*23902f98SJames Seo } 1196*23902f98SJames Seo DEFINE_SHOW_ATTRIBUTE(operational_status); 1197*23902f98SJames Seo 1198*23902f98SJames Seo static int current_state_show(struct seq_file *seqf, void *ignored) 1199*23902f98SJames Seo { 1200*23902f98SJames Seo return fungible_show(seqf, HP_WMI_PROPERTY_CURRENT_STATE); 1201*23902f98SJames Seo } 1202*23902f98SJames Seo DEFINE_SHOW_ATTRIBUTE(current_state); 1203*23902f98SJames Seo 1204*23902f98SJames Seo static int possible_states_show(struct seq_file *seqf, void *ignored) 1205*23902f98SJames Seo { 1206*23902f98SJames Seo struct hp_wmi_numeric_sensor *nsensor = seqf->private; 1207*23902f98SJames Seo u8 i; 1208*23902f98SJames Seo 1209*23902f98SJames Seo for (i = 0; i < nsensor->size; i++) 1210*23902f98SJames Seo seq_printf(seqf, "%s%s", i ? "," : "", 1211*23902f98SJames Seo nsensor->possible_states[i]); 1212*23902f98SJames Seo 1213*23902f98SJames Seo seq_puts(seqf, "\n"); 1214*23902f98SJames Seo 1215*23902f98SJames Seo return 0; 1216*23902f98SJames Seo } 1217*23902f98SJames Seo DEFINE_SHOW_ATTRIBUTE(possible_states); 1218*23902f98SJames Seo 1219*23902f98SJames Seo static int unit_modifier_show(struct seq_file *seqf, void *ignored) 1220*23902f98SJames Seo { 1221*23902f98SJames Seo return fungible_show(seqf, HP_WMI_PROPERTY_UNIT_MODIFIER); 1222*23902f98SJames Seo } 1223*23902f98SJames Seo DEFINE_SHOW_ATTRIBUTE(unit_modifier); 1224*23902f98SJames Seo 1225*23902f98SJames Seo static int current_reading_show(struct seq_file *seqf, void *ignored) 1226*23902f98SJames Seo { 1227*23902f98SJames Seo return fungible_show(seqf, HP_WMI_PROPERTY_CURRENT_READING); 1228*23902f98SJames Seo } 1229*23902f98SJames Seo DEFINE_SHOW_ATTRIBUTE(current_reading); 1230*23902f98SJames Seo 1231*23902f98SJames Seo /* hp_wmi_devm_debugfs_remove - devm callback for debugfs cleanup */ 1232*23902f98SJames Seo static void hp_wmi_devm_debugfs_remove(void *res) 1233*23902f98SJames Seo { 1234*23902f98SJames Seo debugfs_remove_recursive(res); 1235*23902f98SJames Seo } 1236*23902f98SJames Seo 1237*23902f98SJames Seo /* hp_wmi_debugfs_init - create and populate debugfs directory tree */ 1238*23902f98SJames Seo static void hp_wmi_debugfs_init(struct device *dev, struct hp_wmi_info *info, 1239*23902f98SJames Seo struct hp_wmi_platform_events *pevents, 1240*23902f98SJames Seo u8 icount, u8 pcount, bool is_new) 1241*23902f98SJames Seo { 1242*23902f98SJames Seo struct hp_wmi_numeric_sensor *nsensor; 1243*23902f98SJames Seo char buf[HP_WMI_MAX_STR_SIZE]; 1244*23902f98SJames Seo struct dentry *debugfs; 1245*23902f98SJames Seo struct dentry *entries; 1246*23902f98SJames Seo struct dentry *dir; 1247*23902f98SJames Seo int err; 1248*23902f98SJames Seo u8 i; 1249*23902f98SJames Seo 1250*23902f98SJames Seo /* dev_name() gives a not-very-friendly GUID for WMI devices. */ 1251*23902f98SJames Seo scnprintf(buf, sizeof(buf), "hp-wmi-sensors-%u", dev->id); 1252*23902f98SJames Seo 1253*23902f98SJames Seo debugfs = debugfs_create_dir(buf, NULL); 1254*23902f98SJames Seo if (IS_ERR(debugfs)) 1255*23902f98SJames Seo return; 1256*23902f98SJames Seo 1257*23902f98SJames Seo err = devm_add_action_or_reset(dev, hp_wmi_devm_debugfs_remove, 1258*23902f98SJames Seo debugfs); 1259*23902f98SJames Seo if (err) 1260*23902f98SJames Seo return; 1261*23902f98SJames Seo 1262*23902f98SJames Seo entries = debugfs_create_dir("sensor", debugfs); 1263*23902f98SJames Seo 1264*23902f98SJames Seo for (i = 0; i < icount; i++, info++) { 1265*23902f98SJames Seo nsensor = &info->nsensor; 1266*23902f98SJames Seo 1267*23902f98SJames Seo scnprintf(buf, sizeof(buf), "%u", i); 1268*23902f98SJames Seo dir = debugfs_create_dir(buf, entries); 1269*23902f98SJames Seo 1270*23902f98SJames Seo debugfs_create_file("name", 0444, dir, 1271*23902f98SJames Seo (void *)nsensor->name, 1272*23902f98SJames Seo &basic_string_fops); 1273*23902f98SJames Seo 1274*23902f98SJames Seo debugfs_create_file("description", 0444, dir, 1275*23902f98SJames Seo (void *)nsensor->description, 1276*23902f98SJames Seo &basic_string_fops); 1277*23902f98SJames Seo 1278*23902f98SJames Seo debugfs_create_u32("sensor_type", 0444, dir, 1279*23902f98SJames Seo &nsensor->sensor_type); 1280*23902f98SJames Seo 1281*23902f98SJames Seo debugfs_create_file("other_sensor_type", 0444, dir, 1282*23902f98SJames Seo (void *)nsensor->other_sensor_type, 1283*23902f98SJames Seo &basic_string_fops); 1284*23902f98SJames Seo 1285*23902f98SJames Seo debugfs_create_file("operational_status", 0444, dir, 1286*23902f98SJames Seo info, &operational_status_fops); 1287*23902f98SJames Seo 1288*23902f98SJames Seo debugfs_create_file("possible_states", 0444, dir, 1289*23902f98SJames Seo nsensor, &possible_states_fops); 1290*23902f98SJames Seo 1291*23902f98SJames Seo debugfs_create_file("current_state", 0444, dir, 1292*23902f98SJames Seo info, ¤t_state_fops); 1293*23902f98SJames Seo 1294*23902f98SJames Seo debugfs_create_u32("base_units", 0444, dir, 1295*23902f98SJames Seo &nsensor->base_units); 1296*23902f98SJames Seo 1297*23902f98SJames Seo debugfs_create_file("unit_modifier", 0444, dir, 1298*23902f98SJames Seo info, &unit_modifier_fops); 1299*23902f98SJames Seo 1300*23902f98SJames Seo debugfs_create_file("current_reading", 0444, dir, 1301*23902f98SJames Seo info, ¤t_reading_fops); 1302*23902f98SJames Seo 1303*23902f98SJames Seo if (is_new) 1304*23902f98SJames Seo debugfs_create_u32("rate_units", 0444, dir, 1305*23902f98SJames Seo &nsensor->rate_units); 1306*23902f98SJames Seo } 1307*23902f98SJames Seo 1308*23902f98SJames Seo if (!pcount) 1309*23902f98SJames Seo return; 1310*23902f98SJames Seo 1311*23902f98SJames Seo entries = debugfs_create_dir("platform_events", debugfs); 1312*23902f98SJames Seo 1313*23902f98SJames Seo for (i = 0; i < pcount; i++, pevents++) { 1314*23902f98SJames Seo scnprintf(buf, sizeof(buf), "%u", i); 1315*23902f98SJames Seo dir = debugfs_create_dir(buf, entries); 1316*23902f98SJames Seo 1317*23902f98SJames Seo debugfs_create_file("name", 0444, dir, 1318*23902f98SJames Seo (void *)pevents->name, 1319*23902f98SJames Seo &basic_string_fops); 1320*23902f98SJames Seo 1321*23902f98SJames Seo debugfs_create_file("description", 0444, dir, 1322*23902f98SJames Seo (void *)pevents->description, 1323*23902f98SJames Seo &basic_string_fops); 1324*23902f98SJames Seo 1325*23902f98SJames Seo debugfs_create_file("source_namespace", 0444, dir, 1326*23902f98SJames Seo (void *)pevents->source_namespace, 1327*23902f98SJames Seo &basic_string_fops); 1328*23902f98SJames Seo 1329*23902f98SJames Seo debugfs_create_file("source_class", 0444, dir, 1330*23902f98SJames Seo (void *)pevents->source_class, 1331*23902f98SJames Seo &basic_string_fops); 1332*23902f98SJames Seo 1333*23902f98SJames Seo debugfs_create_u32("category", 0444, dir, 1334*23902f98SJames Seo &pevents->category); 1335*23902f98SJames Seo 1336*23902f98SJames Seo debugfs_create_u32("possible_severity", 0444, dir, 1337*23902f98SJames Seo &pevents->possible_severity); 1338*23902f98SJames Seo 1339*23902f98SJames Seo debugfs_create_u32("possible_status", 0444, dir, 1340*23902f98SJames Seo &pevents->possible_status); 1341*23902f98SJames Seo } 1342*23902f98SJames Seo } 1343*23902f98SJames Seo 1344*23902f98SJames Seo #else 1345*23902f98SJames Seo 1346*23902f98SJames Seo static void hp_wmi_debugfs_init(struct device *dev, struct hp_wmi_info *info, 1347*23902f98SJames Seo struct hp_wmi_platform_events *pevents, 1348*23902f98SJames Seo u8 icount, u8 pcount, bool is_new) 1349*23902f98SJames Seo { 1350*23902f98SJames Seo } 1351*23902f98SJames Seo 1352*23902f98SJames Seo #endif 1353*23902f98SJames Seo 1354*23902f98SJames Seo static umode_t hp_wmi_hwmon_is_visible(const void *drvdata, 1355*23902f98SJames Seo enum hwmon_sensor_types type, 1356*23902f98SJames Seo u32 attr, int channel) 1357*23902f98SJames Seo { 1358*23902f98SJames Seo const struct hp_wmi_sensors *state = drvdata; 1359*23902f98SJames Seo const struct hp_wmi_info *info; 1360*23902f98SJames Seo 1361*23902f98SJames Seo if (type == hwmon_intrusion) 1362*23902f98SJames Seo return state->has_intrusion ? 0644 : 0; 1363*23902f98SJames Seo 1364*23902f98SJames Seo if (!state->info_map[type] || !state->info_map[type][channel]) 1365*23902f98SJames Seo return 0; 1366*23902f98SJames Seo 1367*23902f98SJames Seo info = state->info_map[type][channel]; 1368*23902f98SJames Seo 1369*23902f98SJames Seo if ((type == hwmon_temp && attr == hwmon_temp_alarm) || 1370*23902f98SJames Seo (type == hwmon_fan && attr == hwmon_fan_alarm)) 1371*23902f98SJames Seo return info->has_alarm ? 0444 : 0; 1372*23902f98SJames Seo 1373*23902f98SJames Seo return 0444; 1374*23902f98SJames Seo } 1375*23902f98SJames Seo 1376*23902f98SJames Seo static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 1377*23902f98SJames Seo u32 attr, int channel, long *out_val) 1378*23902f98SJames Seo { 1379*23902f98SJames Seo struct hp_wmi_sensors *state = dev_get_drvdata(dev); 1380*23902f98SJames Seo const struct hp_wmi_numeric_sensor *nsensor; 1381*23902f98SJames Seo struct hp_wmi_info *info; 1382*23902f98SJames Seo int err; 1383*23902f98SJames Seo 1384*23902f98SJames Seo if (type == hwmon_intrusion) { 1385*23902f98SJames Seo *out_val = state->intrusion ? 1 : 0; 1386*23902f98SJames Seo 1387*23902f98SJames Seo return 0; 1388*23902f98SJames Seo } 1389*23902f98SJames Seo 1390*23902f98SJames Seo info = state->info_map[type][channel]; 1391*23902f98SJames Seo 1392*23902f98SJames Seo if ((type == hwmon_temp && attr == hwmon_temp_alarm) || 1393*23902f98SJames Seo (type == hwmon_fan && attr == hwmon_fan_alarm)) { 1394*23902f98SJames Seo *out_val = info->alarm ? 1 : 0; 1395*23902f98SJames Seo info->alarm = false; 1396*23902f98SJames Seo 1397*23902f98SJames Seo return 0; 1398*23902f98SJames Seo } 1399*23902f98SJames Seo 1400*23902f98SJames Seo nsensor = &info->nsensor; 1401*23902f98SJames Seo 1402*23902f98SJames Seo err = hp_wmi_update_info(state, info); 1403*23902f98SJames Seo if (err) 1404*23902f98SJames Seo return err; 1405*23902f98SJames Seo 1406*23902f98SJames Seo if ((type == hwmon_temp && attr == hwmon_temp_fault) || 1407*23902f98SJames Seo (type == hwmon_fan && attr == hwmon_fan_fault)) 1408*23902f98SJames Seo *out_val = numeric_sensor_has_fault(nsensor); 1409*23902f98SJames Seo else 1410*23902f98SJames Seo *out_val = info->cached_val; 1411*23902f98SJames Seo 1412*23902f98SJames Seo return 0; 1413*23902f98SJames Seo } 1414*23902f98SJames Seo 1415*23902f98SJames Seo static int hp_wmi_hwmon_read_string(struct device *dev, 1416*23902f98SJames Seo enum hwmon_sensor_types type, u32 attr, 1417*23902f98SJames Seo int channel, const char **out_str) 1418*23902f98SJames Seo { 1419*23902f98SJames Seo const struct hp_wmi_sensors *state = dev_get_drvdata(dev); 1420*23902f98SJames Seo const struct hp_wmi_info *info; 1421*23902f98SJames Seo 1422*23902f98SJames Seo info = state->info_map[type][channel]; 1423*23902f98SJames Seo *out_str = info->nsensor.name; 1424*23902f98SJames Seo 1425*23902f98SJames Seo return 0; 1426*23902f98SJames Seo } 1427*23902f98SJames Seo 1428*23902f98SJames Seo static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 1429*23902f98SJames Seo u32 attr, int channel, long val) 1430*23902f98SJames Seo { 1431*23902f98SJames Seo struct hp_wmi_sensors *state = dev_get_drvdata(dev); 1432*23902f98SJames Seo 1433*23902f98SJames Seo if (val) 1434*23902f98SJames Seo return -EINVAL; 1435*23902f98SJames Seo 1436*23902f98SJames Seo mutex_lock(&state->lock); 1437*23902f98SJames Seo 1438*23902f98SJames Seo state->intrusion = false; 1439*23902f98SJames Seo 1440*23902f98SJames Seo mutex_unlock(&state->lock); 1441*23902f98SJames Seo 1442*23902f98SJames Seo return 0; 1443*23902f98SJames Seo } 1444*23902f98SJames Seo 1445*23902f98SJames Seo static const struct hwmon_ops hp_wmi_hwmon_ops = { 1446*23902f98SJames Seo .is_visible = hp_wmi_hwmon_is_visible, 1447*23902f98SJames Seo .read = hp_wmi_hwmon_read, 1448*23902f98SJames Seo .read_string = hp_wmi_hwmon_read_string, 1449*23902f98SJames Seo .write = hp_wmi_hwmon_write, 1450*23902f98SJames Seo }; 1451*23902f98SJames Seo 1452*23902f98SJames Seo static struct hwmon_chip_info hp_wmi_chip_info = { 1453*23902f98SJames Seo .ops = &hp_wmi_hwmon_ops, 1454*23902f98SJames Seo .info = NULL, 1455*23902f98SJames Seo }; 1456*23902f98SJames Seo 1457*23902f98SJames Seo static struct hp_wmi_info *match_fan_event(struct hp_wmi_sensors *state, 1458*23902f98SJames Seo const char *event_description) 1459*23902f98SJames Seo { 1460*23902f98SJames Seo struct hp_wmi_info **ptr_info = state->info_map[hwmon_fan]; 1461*23902f98SJames Seo u8 fan_count = state->channel_count[hwmon_fan]; 1462*23902f98SJames Seo struct hp_wmi_info *info; 1463*23902f98SJames Seo const char *name; 1464*23902f98SJames Seo u8 i; 1465*23902f98SJames Seo 1466*23902f98SJames Seo /* Fan event has Description "X Speed". Sensor has Name "X[ Speed]". */ 1467*23902f98SJames Seo 1468*23902f98SJames Seo for (i = 0; i < fan_count; i++, ptr_info++) { 1469*23902f98SJames Seo info = *ptr_info; 1470*23902f98SJames Seo name = info->nsensor.name; 1471*23902f98SJames Seo 1472*23902f98SJames Seo if (strstr(event_description, name)) 1473*23902f98SJames Seo return info; 1474*23902f98SJames Seo } 1475*23902f98SJames Seo 1476*23902f98SJames Seo return NULL; 1477*23902f98SJames Seo } 1478*23902f98SJames Seo 1479*23902f98SJames Seo static u8 match_temp_events(struct hp_wmi_sensors *state, 1480*23902f98SJames Seo const char *event_description, 1481*23902f98SJames Seo struct hp_wmi_info *temp_info[]) 1482*23902f98SJames Seo { 1483*23902f98SJames Seo struct hp_wmi_info **ptr_info = state->info_map[hwmon_temp]; 1484*23902f98SJames Seo u8 temp_count = state->channel_count[hwmon_temp]; 1485*23902f98SJames Seo struct hp_wmi_info *info; 1486*23902f98SJames Seo const char *name; 1487*23902f98SJames Seo u8 count = 0; 1488*23902f98SJames Seo bool is_cpu; 1489*23902f98SJames Seo bool is_sys; 1490*23902f98SJames Seo u8 i; 1491*23902f98SJames Seo 1492*23902f98SJames Seo /* Description is either "CPU Thermal Index" or "Chassis Thermal Index". */ 1493*23902f98SJames Seo 1494*23902f98SJames Seo is_cpu = !strcmp(event_description, HP_WMI_PATTERN_CPU_TEMP); 1495*23902f98SJames Seo is_sys = !strcmp(event_description, HP_WMI_PATTERN_SYS_TEMP); 1496*23902f98SJames Seo if (!is_cpu && !is_sys) 1497*23902f98SJames Seo return 0; 1498*23902f98SJames Seo 1499*23902f98SJames Seo /* 1500*23902f98SJames Seo * CPU event: Match one sensor with Name either "CPU Thermal Index" or 1501*23902f98SJames Seo * "CPU Temperature", or multiple with Name(s) "CPU[#] Temperature". 1502*23902f98SJames Seo * 1503*23902f98SJames Seo * Chassis event: Match one sensor with Name either 1504*23902f98SJames Seo * "Chassis Thermal Index" or "System Ambient Temperature". 1505*23902f98SJames Seo */ 1506*23902f98SJames Seo 1507*23902f98SJames Seo for (i = 0; i < temp_count; i++, ptr_info++) { 1508*23902f98SJames Seo info = *ptr_info; 1509*23902f98SJames Seo name = info->nsensor.name; 1510*23902f98SJames Seo 1511*23902f98SJames Seo if ((is_cpu && (!strcmp(name, HP_WMI_PATTERN_CPU_TEMP) || 1512*23902f98SJames Seo !strcmp(name, HP_WMI_PATTERN_CPU_TEMP2))) || 1513*23902f98SJames Seo (is_sys && (!strcmp(name, HP_WMI_PATTERN_SYS_TEMP) || 1514*23902f98SJames Seo !strcmp(name, HP_WMI_PATTERN_SYS_TEMP2)))) { 1515*23902f98SJames Seo temp_info[0] = info; 1516*23902f98SJames Seo return 1; 1517*23902f98SJames Seo } 1518*23902f98SJames Seo 1519*23902f98SJames Seo if (is_cpu && (strstr(name, HP_WMI_PATTERN_CPU) && 1520*23902f98SJames Seo strstr(name, HP_WMI_PATTERN_TEMP))) 1521*23902f98SJames Seo temp_info[count++] = info; 1522*23902f98SJames Seo } 1523*23902f98SJames Seo 1524*23902f98SJames Seo return count; 1525*23902f98SJames Seo } 1526*23902f98SJames Seo 1527*23902f98SJames Seo /* hp_wmi_devm_debugfs_remove - devm callback for WMI event handler removal */ 1528*23902f98SJames Seo static void hp_wmi_devm_notify_remove(void *ignored) 1529*23902f98SJames Seo { 1530*23902f98SJames Seo wmi_remove_notify_handler(HP_WMI_EVENT_GUID); 1531*23902f98SJames Seo } 1532*23902f98SJames Seo 1533*23902f98SJames Seo /* hp_wmi_notify - WMI event notification handler */ 1534*23902f98SJames Seo static void hp_wmi_notify(u32 value, void *context) 1535*23902f98SJames Seo { 1536*23902f98SJames Seo struct hp_wmi_info *temp_info[HP_WMI_MAX_INSTANCES] = {}; 1537*23902f98SJames Seo struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; 1538*23902f98SJames Seo struct hp_wmi_sensors *state = context; 1539*23902f98SJames Seo struct device *dev = &state->wdev->dev; 1540*23902f98SJames Seo struct hp_wmi_info *fan_info; 1541*23902f98SJames Seo struct hp_wmi_event event; 1542*23902f98SJames Seo union acpi_object *wobj; 1543*23902f98SJames Seo acpi_status err; 1544*23902f98SJames Seo int event_type; 1545*23902f98SJames Seo u8 count; 1546*23902f98SJames Seo 1547*23902f98SJames Seo /* 1548*23902f98SJames Seo * The following warning may occur in the kernel log: 1549*23902f98SJames Seo * 1550*23902f98SJames Seo * ACPI Warning: \_SB.WMID._WED: Return type mismatch - 1551*23902f98SJames Seo * found Package, expected Integer/String/Buffer 1552*23902f98SJames Seo * 1553*23902f98SJames Seo * After using [4] to decode BMOF blobs found in [3], careless copying 1554*23902f98SJames Seo * of BIOS code seems the most likely explanation for this warning. 1555*23902f98SJames Seo * HP_WMI_EVENT_GUID refers to \\.\root\WMI\HPBIOS_BIOSEvent on 1556*23902f98SJames Seo * business-class systems, but it refers to \\.\root\WMI\hpqBEvnt on 1557*23902f98SJames Seo * non-business-class systems. Per the existing hp-wmi driver, it 1558*23902f98SJames Seo * looks like an instance of hpqBEvnt delivered as event data may 1559*23902f98SJames Seo * indeed take the form of a raw ACPI_BUFFER on non-business-class 1560*23902f98SJames Seo * systems ("may" because ASL shows some BIOSes do strange things). 1561*23902f98SJames Seo * 1562*23902f98SJames Seo * In any case, we can ignore this warning, because we always validate 1563*23902f98SJames Seo * the event data to ensure it is an ACPI_PACKAGE containing a 1564*23902f98SJames Seo * HPBIOS_BIOSEvent instance. 1565*23902f98SJames Seo */ 1566*23902f98SJames Seo 1567*23902f98SJames Seo mutex_lock(&state->lock); 1568*23902f98SJames Seo 1569*23902f98SJames Seo err = wmi_get_event_data(value, &out); 1570*23902f98SJames Seo if (ACPI_FAILURE(err)) 1571*23902f98SJames Seo goto out_unlock; 1572*23902f98SJames Seo 1573*23902f98SJames Seo wobj = out.pointer; 1574*23902f98SJames Seo 1575*23902f98SJames Seo err = populate_event_from_wobj(&event, wobj); 1576*23902f98SJames Seo if (err) { 1577*23902f98SJames Seo dev_warn(dev, "Bad event data (ACPI type %d)\n", wobj->type); 1578*23902f98SJames Seo goto out_free_wobj; 1579*23902f98SJames Seo } 1580*23902f98SJames Seo 1581*23902f98SJames Seo event_type = classify_event(event.name, event.category); 1582*23902f98SJames Seo switch (event_type) { 1583*23902f98SJames Seo case HP_WMI_TYPE_AIR_FLOW: 1584*23902f98SJames Seo fan_info = match_fan_event(state, event.description); 1585*23902f98SJames Seo if (fan_info) 1586*23902f98SJames Seo fan_info->alarm = true; 1587*23902f98SJames Seo break; 1588*23902f98SJames Seo 1589*23902f98SJames Seo case HP_WMI_TYPE_INTRUSION: 1590*23902f98SJames Seo state->intrusion = true; 1591*23902f98SJames Seo break; 1592*23902f98SJames Seo 1593*23902f98SJames Seo case HP_WMI_TYPE_TEMPERATURE: 1594*23902f98SJames Seo count = match_temp_events(state, event.description, temp_info); 1595*23902f98SJames Seo while (count) 1596*23902f98SJames Seo temp_info[--count]->alarm = true; 1597*23902f98SJames Seo break; 1598*23902f98SJames Seo 1599*23902f98SJames Seo default: 1600*23902f98SJames Seo break; 1601*23902f98SJames Seo } 1602*23902f98SJames Seo 1603*23902f98SJames Seo out_free_wobj: 1604*23902f98SJames Seo kfree(wobj); 1605*23902f98SJames Seo 1606*23902f98SJames Seo out_unlock: 1607*23902f98SJames Seo mutex_unlock(&state->lock); 1608*23902f98SJames Seo } 1609*23902f98SJames Seo 1610*23902f98SJames Seo static int init_platform_events(struct device *dev, 1611*23902f98SJames Seo struct hp_wmi_platform_events **out_pevents, 1612*23902f98SJames Seo u8 *out_pcount) 1613*23902f98SJames Seo { 1614*23902f98SJames Seo struct hp_wmi_platform_events *pevents_arr; 1615*23902f98SJames Seo struct hp_wmi_platform_events *pevents; 1616*23902f98SJames Seo union acpi_object *wobj; 1617*23902f98SJames Seo u8 count; 1618*23902f98SJames Seo int err; 1619*23902f98SJames Seo u8 i; 1620*23902f98SJames Seo 1621*23902f98SJames Seo count = hp_wmi_wobj_instance_count(HP_WMI_PLATFORM_EVENTS_GUID); 1622*23902f98SJames Seo if (!count) { 1623*23902f98SJames Seo *out_pcount = 0; 1624*23902f98SJames Seo 1625*23902f98SJames Seo dev_dbg(dev, "No platform events\n"); 1626*23902f98SJames Seo 1627*23902f98SJames Seo return 0; 1628*23902f98SJames Seo } 1629*23902f98SJames Seo 1630*23902f98SJames Seo pevents_arr = devm_kcalloc(dev, count, sizeof(*pevents), GFP_KERNEL); 1631*23902f98SJames Seo if (!pevents_arr) 1632*23902f98SJames Seo return -ENOMEM; 1633*23902f98SJames Seo 1634*23902f98SJames Seo for (i = 0, pevents = pevents_arr; i < count; i++, pevents++) { 1635*23902f98SJames Seo wobj = hp_wmi_get_wobj(HP_WMI_PLATFORM_EVENTS_GUID, i); 1636*23902f98SJames Seo if (!wobj) 1637*23902f98SJames Seo return -EIO; 1638*23902f98SJames Seo 1639*23902f98SJames Seo err = populate_platform_events_from_wobj(dev, pevents, wobj); 1640*23902f98SJames Seo 1641*23902f98SJames Seo kfree(wobj); 1642*23902f98SJames Seo 1643*23902f98SJames Seo if (err) 1644*23902f98SJames Seo return err; 1645*23902f98SJames Seo } 1646*23902f98SJames Seo 1647*23902f98SJames Seo *out_pevents = pevents_arr; 1648*23902f98SJames Seo *out_pcount = count; 1649*23902f98SJames Seo 1650*23902f98SJames Seo dev_dbg(dev, "Found %u platform events\n", count); 1651*23902f98SJames Seo 1652*23902f98SJames Seo return 0; 1653*23902f98SJames Seo } 1654*23902f98SJames Seo 1655*23902f98SJames Seo static int init_numeric_sensors(struct hp_wmi_sensors *state, 1656*23902f98SJames Seo struct hp_wmi_info *connected[], 1657*23902f98SJames Seo struct hp_wmi_info **out_info, 1658*23902f98SJames Seo u8 *out_icount, u8 *out_count, 1659*23902f98SJames Seo bool *out_is_new) 1660*23902f98SJames Seo { 1661*23902f98SJames Seo struct hp_wmi_info ***info_map = state->info_map; 1662*23902f98SJames Seo u8 *channel_count = state->channel_count; 1663*23902f98SJames Seo struct device *dev = &state->wdev->dev; 1664*23902f98SJames Seo struct hp_wmi_numeric_sensor *nsensor; 1665*23902f98SJames Seo u8 channel_index[hwmon_max] = {}; 1666*23902f98SJames Seo enum hwmon_sensor_types type; 1667*23902f98SJames Seo struct hp_wmi_info *info_arr; 1668*23902f98SJames Seo struct hp_wmi_info *info; 1669*23902f98SJames Seo union acpi_object *wobj; 1670*23902f98SJames Seo u8 count = 0; 1671*23902f98SJames Seo bool is_new; 1672*23902f98SJames Seo u8 icount; 1673*23902f98SJames Seo int wtype; 1674*23902f98SJames Seo int err; 1675*23902f98SJames Seo u8 c; 1676*23902f98SJames Seo u8 i; 1677*23902f98SJames Seo 1678*23902f98SJames Seo icount = hp_wmi_wobj_instance_count(HP_WMI_NUMERIC_SENSOR_GUID); 1679*23902f98SJames Seo if (!icount) 1680*23902f98SJames Seo return -ENODATA; 1681*23902f98SJames Seo 1682*23902f98SJames Seo info_arr = devm_kcalloc(dev, icount, sizeof(*info), GFP_KERNEL); 1683*23902f98SJames Seo if (!info_arr) 1684*23902f98SJames Seo return -ENOMEM; 1685*23902f98SJames Seo 1686*23902f98SJames Seo for (i = 0, info = info_arr; i < icount; i++, info++) { 1687*23902f98SJames Seo wobj = hp_wmi_get_wobj(HP_WMI_NUMERIC_SENSOR_GUID, i); 1688*23902f98SJames Seo if (!wobj) 1689*23902f98SJames Seo return -EIO; 1690*23902f98SJames Seo 1691*23902f98SJames Seo info->instance = i; 1692*23902f98SJames Seo info->state = state; 1693*23902f98SJames Seo nsensor = &info->nsensor; 1694*23902f98SJames Seo 1695*23902f98SJames Seo err = populate_numeric_sensor_from_wobj(dev, nsensor, wobj, 1696*23902f98SJames Seo &is_new); 1697*23902f98SJames Seo 1698*23902f98SJames Seo kfree(wobj); 1699*23902f98SJames Seo 1700*23902f98SJames Seo if (err) 1701*23902f98SJames Seo return err; 1702*23902f98SJames Seo 1703*23902f98SJames Seo if (!numeric_sensor_is_connected(nsensor)) 1704*23902f98SJames Seo continue; 1705*23902f98SJames Seo 1706*23902f98SJames Seo wtype = classify_numeric_sensor(nsensor); 1707*23902f98SJames Seo if (wtype < 0) 1708*23902f98SJames Seo continue; 1709*23902f98SJames Seo 1710*23902f98SJames Seo type = hp_wmi_hwmon_type_map[wtype]; 1711*23902f98SJames Seo 1712*23902f98SJames Seo channel_count[type]++; 1713*23902f98SJames Seo 1714*23902f98SJames Seo info->type = type; 1715*23902f98SJames Seo 1716*23902f98SJames Seo interpret_info(info); 1717*23902f98SJames Seo 1718*23902f98SJames Seo connected[count++] = info; 1719*23902f98SJames Seo } 1720*23902f98SJames Seo 1721*23902f98SJames Seo dev_dbg(dev, "Found %u sensors (%u connected)\n", i, count); 1722*23902f98SJames Seo 1723*23902f98SJames Seo for (i = 0; i < count; i++) { 1724*23902f98SJames Seo info = connected[i]; 1725*23902f98SJames Seo type = info->type; 1726*23902f98SJames Seo c = channel_index[type]++; 1727*23902f98SJames Seo 1728*23902f98SJames Seo if (!info_map[type]) { 1729*23902f98SJames Seo info_map[type] = devm_kcalloc(dev, channel_count[type], 1730*23902f98SJames Seo sizeof(*info_map), 1731*23902f98SJames Seo GFP_KERNEL); 1732*23902f98SJames Seo if (!info_map[type]) 1733*23902f98SJames Seo return -ENOMEM; 1734*23902f98SJames Seo } 1735*23902f98SJames Seo 1736*23902f98SJames Seo info_map[type][c] = info; 1737*23902f98SJames Seo } 1738*23902f98SJames Seo 1739*23902f98SJames Seo *out_info = info_arr; 1740*23902f98SJames Seo *out_icount = icount; 1741*23902f98SJames Seo *out_count = count; 1742*23902f98SJames Seo *out_is_new = is_new; 1743*23902f98SJames Seo 1744*23902f98SJames Seo return 0; 1745*23902f98SJames Seo } 1746*23902f98SJames Seo 1747*23902f98SJames Seo static bool find_event_attributes(struct hp_wmi_sensors *state, 1748*23902f98SJames Seo struct hp_wmi_platform_events *pevents, 1749*23902f98SJames Seo u8 pevents_count) 1750*23902f98SJames Seo { 1751*23902f98SJames Seo /* 1752*23902f98SJames Seo * The existence of this HPBIOS_PlatformEvents instance: 1753*23902f98SJames Seo * 1754*23902f98SJames Seo * { 1755*23902f98SJames Seo * Name = "Rear Chassis Fan0 Stall"; 1756*23902f98SJames Seo * Description = "Rear Chassis Fan0 Speed"; 1757*23902f98SJames Seo * Category = 3; // "Sensor" 1758*23902f98SJames Seo * PossibleSeverity = 25; // "Critical Failure" 1759*23902f98SJames Seo * PossibleStatus = 5; // "Predictive Failure" 1760*23902f98SJames Seo * [...] 1761*23902f98SJames Seo * } 1762*23902f98SJames Seo * 1763*23902f98SJames Seo * means that this HPBIOS_BIOSEvent instance may occur: 1764*23902f98SJames Seo * 1765*23902f98SJames Seo * { 1766*23902f98SJames Seo * Name = "Rear Chassis Fan0 Stall"; 1767*23902f98SJames Seo * Description = "Rear Chassis Fan0 Speed"; 1768*23902f98SJames Seo * Category = 3; // "Sensor" 1769*23902f98SJames Seo * Severity = 25; // "Critical Failure" 1770*23902f98SJames Seo * Status = 5; // "Predictive Failure" 1771*23902f98SJames Seo * } 1772*23902f98SJames Seo * 1773*23902f98SJames Seo * After the event occurs (e.g. because the fan was unplugged), 1774*23902f98SJames Seo * polling the related HPBIOS_BIOSNumericSensor instance gives: 1775*23902f98SJames Seo * 1776*23902f98SJames Seo * { 1777*23902f98SJames Seo * Name = "Rear Chassis Fan0"; 1778*23902f98SJames Seo * Description = "Reports rear chassis fan0 speed"; 1779*23902f98SJames Seo * OperationalStatus = 5; // "Predictive Failure", was 3 ("OK") 1780*23902f98SJames Seo * CurrentReading = 0; 1781*23902f98SJames Seo * [...] 1782*23902f98SJames Seo * } 1783*23902f98SJames Seo * 1784*23902f98SJames Seo * In this example, the hwmon fan channel for "Rear Chassis Fan0" 1785*23902f98SJames Seo * should support the alarm flag and have it be set if the related 1786*23902f98SJames Seo * HPBIOS_BIOSEvent instance occurs. 1787*23902f98SJames Seo * 1788*23902f98SJames Seo * In addition to fan events, temperature (CPU/chassis) and intrusion 1789*23902f98SJames Seo * events are relevant to hwmon [2]. Note that much information in [2] 1790*23902f98SJames Seo * is unreliable; it is referenced in addition to ACPI dumps [3] merely 1791*23902f98SJames Seo * to support the conclusion that sensor and event names/descriptions 1792*23902f98SJames Seo * are systematic enough to allow this driver to match them. 1793*23902f98SJames Seo * 1794*23902f98SJames Seo * Complications and limitations: 1795*23902f98SJames Seo * 1796*23902f98SJames Seo * - Strings are freeform and may vary, cf. sensor Name "CPU0 Fan" 1797*23902f98SJames Seo * on a Z420 vs. "CPU Fan Speed" on an EliteOne 800 G1. 1798*23902f98SJames Seo * - Leading/trailing whitespace is a rare but real possibility [3]. 1799*23902f98SJames Seo * - The HPBIOS_PlatformEvents object may not exist or its instances 1800*23902f98SJames Seo * may show that the system only has e.g. BIOS setting-related 1801*23902f98SJames Seo * events (cf. the ProBook 4540s and ProBook 470 G0 [3]). 1802*23902f98SJames Seo */ 1803*23902f98SJames Seo 1804*23902f98SJames Seo struct hp_wmi_info *temp_info[HP_WMI_MAX_INSTANCES] = {}; 1805*23902f98SJames Seo const char *event_description; 1806*23902f98SJames Seo struct hp_wmi_info *fan_info; 1807*23902f98SJames Seo bool has_events = false; 1808*23902f98SJames Seo const char *event_name; 1809*23902f98SJames Seo u32 event_category; 1810*23902f98SJames Seo int event_type; 1811*23902f98SJames Seo u8 count; 1812*23902f98SJames Seo u8 i; 1813*23902f98SJames Seo 1814*23902f98SJames Seo for (i = 0; i < pevents_count; i++, pevents++) { 1815*23902f98SJames Seo event_name = pevents->name; 1816*23902f98SJames Seo event_description = pevents->description; 1817*23902f98SJames Seo event_category = pevents->category; 1818*23902f98SJames Seo 1819*23902f98SJames Seo event_type = classify_event(event_name, event_category); 1820*23902f98SJames Seo switch (event_type) { 1821*23902f98SJames Seo case HP_WMI_TYPE_AIR_FLOW: 1822*23902f98SJames Seo fan_info = match_fan_event(state, event_description); 1823*23902f98SJames Seo if (!fan_info) 1824*23902f98SJames Seo break; 1825*23902f98SJames Seo 1826*23902f98SJames Seo fan_info->has_alarm = true; 1827*23902f98SJames Seo has_events = true; 1828*23902f98SJames Seo break; 1829*23902f98SJames Seo 1830*23902f98SJames Seo case HP_WMI_TYPE_INTRUSION: 1831*23902f98SJames Seo state->has_intrusion = true; 1832*23902f98SJames Seo has_events = true; 1833*23902f98SJames Seo break; 1834*23902f98SJames Seo 1835*23902f98SJames Seo case HP_WMI_TYPE_TEMPERATURE: 1836*23902f98SJames Seo count = match_temp_events(state, event_description, 1837*23902f98SJames Seo temp_info); 1838*23902f98SJames Seo if (!count) 1839*23902f98SJames Seo break; 1840*23902f98SJames Seo 1841*23902f98SJames Seo while (count) 1842*23902f98SJames Seo temp_info[--count]->has_alarm = true; 1843*23902f98SJames Seo has_events = true; 1844*23902f98SJames Seo break; 1845*23902f98SJames Seo 1846*23902f98SJames Seo default: 1847*23902f98SJames Seo break; 1848*23902f98SJames Seo } 1849*23902f98SJames Seo } 1850*23902f98SJames Seo 1851*23902f98SJames Seo return has_events; 1852*23902f98SJames Seo } 1853*23902f98SJames Seo 1854*23902f98SJames Seo static int make_chip_info(struct hp_wmi_sensors *state, bool has_events) 1855*23902f98SJames Seo { 1856*23902f98SJames Seo const struct hwmon_channel_info **ptr_channel_info; 1857*23902f98SJames Seo struct hp_wmi_info ***info_map = state->info_map; 1858*23902f98SJames Seo u8 *channel_count = state->channel_count; 1859*23902f98SJames Seo struct hwmon_channel_info *channel_info; 1860*23902f98SJames Seo struct device *dev = &state->wdev->dev; 1861*23902f98SJames Seo enum hwmon_sensor_types type; 1862*23902f98SJames Seo u8 type_count = 0; 1863*23902f98SJames Seo u32 *config; 1864*23902f98SJames Seo u32 attr; 1865*23902f98SJames Seo u8 count; 1866*23902f98SJames Seo u8 i; 1867*23902f98SJames Seo 1868*23902f98SJames Seo if (channel_count[hwmon_temp]) 1869*23902f98SJames Seo channel_count[hwmon_chip] = 1; 1870*23902f98SJames Seo 1871*23902f98SJames Seo if (has_events && state->has_intrusion) 1872*23902f98SJames Seo channel_count[hwmon_intrusion] = 1; 1873*23902f98SJames Seo 1874*23902f98SJames Seo for (type = hwmon_chip; type < hwmon_max; type++) 1875*23902f98SJames Seo if (channel_count[type]) 1876*23902f98SJames Seo type_count++; 1877*23902f98SJames Seo 1878*23902f98SJames Seo channel_info = devm_kcalloc(dev, type_count, 1879*23902f98SJames Seo sizeof(*channel_info), GFP_KERNEL); 1880*23902f98SJames Seo if (!channel_info) 1881*23902f98SJames Seo return -ENOMEM; 1882*23902f98SJames Seo 1883*23902f98SJames Seo ptr_channel_info = devm_kcalloc(dev, type_count + 1, 1884*23902f98SJames Seo sizeof(*ptr_channel_info), GFP_KERNEL); 1885*23902f98SJames Seo if (!ptr_channel_info) 1886*23902f98SJames Seo return -ENOMEM; 1887*23902f98SJames Seo 1888*23902f98SJames Seo hp_wmi_chip_info.info = ptr_channel_info; 1889*23902f98SJames Seo 1890*23902f98SJames Seo for (type = hwmon_chip; type < hwmon_max; type++) { 1891*23902f98SJames Seo count = channel_count[type]; 1892*23902f98SJames Seo if (!count) 1893*23902f98SJames Seo continue; 1894*23902f98SJames Seo 1895*23902f98SJames Seo config = devm_kcalloc(dev, count + 1, 1896*23902f98SJames Seo sizeof(*config), GFP_KERNEL); 1897*23902f98SJames Seo if (!config) 1898*23902f98SJames Seo return -ENOMEM; 1899*23902f98SJames Seo 1900*23902f98SJames Seo attr = hp_wmi_hwmon_attributes[type]; 1901*23902f98SJames Seo channel_info->type = type; 1902*23902f98SJames Seo channel_info->config = config; 1903*23902f98SJames Seo memset32(config, attr, count); 1904*23902f98SJames Seo 1905*23902f98SJames Seo *ptr_channel_info++ = channel_info++; 1906*23902f98SJames Seo 1907*23902f98SJames Seo if (!has_events || (type != hwmon_temp && type != hwmon_fan)) 1908*23902f98SJames Seo continue; 1909*23902f98SJames Seo 1910*23902f98SJames Seo attr = type == hwmon_temp ? HWMON_T_ALARM : HWMON_F_ALARM; 1911*23902f98SJames Seo 1912*23902f98SJames Seo for (i = 0; i < count; i++) 1913*23902f98SJames Seo if (info_map[type][i]->has_alarm) 1914*23902f98SJames Seo config[i] |= attr; 1915*23902f98SJames Seo } 1916*23902f98SJames Seo 1917*23902f98SJames Seo return 0; 1918*23902f98SJames Seo } 1919*23902f98SJames Seo 1920*23902f98SJames Seo static bool add_event_handler(struct hp_wmi_sensors *state) 1921*23902f98SJames Seo { 1922*23902f98SJames Seo struct device *dev = &state->wdev->dev; 1923*23902f98SJames Seo int err; 1924*23902f98SJames Seo 1925*23902f98SJames Seo err = wmi_install_notify_handler(HP_WMI_EVENT_GUID, 1926*23902f98SJames Seo hp_wmi_notify, state); 1927*23902f98SJames Seo if (err) { 1928*23902f98SJames Seo dev_info(dev, "Failed to subscribe to WMI event\n"); 1929*23902f98SJames Seo return false; 1930*23902f98SJames Seo } 1931*23902f98SJames Seo 1932*23902f98SJames Seo err = devm_add_action_or_reset(dev, hp_wmi_devm_notify_remove, NULL); 1933*23902f98SJames Seo if (err) 1934*23902f98SJames Seo return false; 1935*23902f98SJames Seo 1936*23902f98SJames Seo return true; 1937*23902f98SJames Seo } 1938*23902f98SJames Seo 1939*23902f98SJames Seo static int hp_wmi_sensors_init(struct hp_wmi_sensors *state) 1940*23902f98SJames Seo { 1941*23902f98SJames Seo struct hp_wmi_info *connected[HP_WMI_MAX_INSTANCES]; 1942*23902f98SJames Seo struct hp_wmi_platform_events *pevents; 1943*23902f98SJames Seo struct device *dev = &state->wdev->dev; 1944*23902f98SJames Seo struct hp_wmi_info *info; 1945*23902f98SJames Seo struct device *hwdev; 1946*23902f98SJames Seo bool has_events; 1947*23902f98SJames Seo bool is_new; 1948*23902f98SJames Seo u8 icount; 1949*23902f98SJames Seo u8 pcount; 1950*23902f98SJames Seo u8 count; 1951*23902f98SJames Seo int err; 1952*23902f98SJames Seo 1953*23902f98SJames Seo err = init_platform_events(dev, &pevents, &pcount); 1954*23902f98SJames Seo if (err) 1955*23902f98SJames Seo return err; 1956*23902f98SJames Seo 1957*23902f98SJames Seo err = init_numeric_sensors(state, connected, &info, 1958*23902f98SJames Seo &icount, &count, &is_new); 1959*23902f98SJames Seo if (err) 1960*23902f98SJames Seo return err; 1961*23902f98SJames Seo 1962*23902f98SJames Seo hp_wmi_debugfs_init(dev, info, pevents, icount, pcount, is_new); 1963*23902f98SJames Seo 1964*23902f98SJames Seo if (!count) 1965*23902f98SJames Seo return 0; /* No connected sensors; debugfs only. */ 1966*23902f98SJames Seo 1967*23902f98SJames Seo has_events = find_event_attributes(state, pevents, pcount); 1968*23902f98SJames Seo 1969*23902f98SJames Seo /* Survive failure to install WMI event handler. */ 1970*23902f98SJames Seo if (has_events && !add_event_handler(state)) 1971*23902f98SJames Seo has_events = false; 1972*23902f98SJames Seo 1973*23902f98SJames Seo err = make_chip_info(state, has_events); 1974*23902f98SJames Seo if (err) 1975*23902f98SJames Seo return err; 1976*23902f98SJames Seo 1977*23902f98SJames Seo hwdev = devm_hwmon_device_register_with_info(dev, "hp_wmi_sensors", 1978*23902f98SJames Seo state, &hp_wmi_chip_info, 1979*23902f98SJames Seo NULL); 1980*23902f98SJames Seo return PTR_ERR_OR_ZERO(hwdev); 1981*23902f98SJames Seo } 1982*23902f98SJames Seo 1983*23902f98SJames Seo static int hp_wmi_sensors_probe(struct wmi_device *wdev, const void *context) 1984*23902f98SJames Seo { 1985*23902f98SJames Seo struct device *dev = &wdev->dev; 1986*23902f98SJames Seo struct hp_wmi_sensors *state; 1987*23902f98SJames Seo 1988*23902f98SJames Seo state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); 1989*23902f98SJames Seo if (!state) 1990*23902f98SJames Seo return -ENOMEM; 1991*23902f98SJames Seo 1992*23902f98SJames Seo state->wdev = wdev; 1993*23902f98SJames Seo 1994*23902f98SJames Seo mutex_init(&state->lock); 1995*23902f98SJames Seo 1996*23902f98SJames Seo dev_set_drvdata(dev, state); 1997*23902f98SJames Seo 1998*23902f98SJames Seo return hp_wmi_sensors_init(state); 1999*23902f98SJames Seo } 2000*23902f98SJames Seo 2001*23902f98SJames Seo static const struct wmi_device_id hp_wmi_sensors_id_table[] = { 2002*23902f98SJames Seo { HP_WMI_NUMERIC_SENSOR_GUID, NULL }, 2003*23902f98SJames Seo {}, 2004*23902f98SJames Seo }; 2005*23902f98SJames Seo 2006*23902f98SJames Seo static struct wmi_driver hp_wmi_sensors_driver = { 2007*23902f98SJames Seo .driver = { .name = "hp-wmi-sensors" }, 2008*23902f98SJames Seo .id_table = hp_wmi_sensors_id_table, 2009*23902f98SJames Seo .probe = hp_wmi_sensors_probe, 2010*23902f98SJames Seo }; 2011*23902f98SJames Seo module_wmi_driver(hp_wmi_sensors_driver); 2012*23902f98SJames Seo 2013*23902f98SJames Seo MODULE_AUTHOR("James Seo <james@equiv.tech>"); 2014*23902f98SJames Seo MODULE_DESCRIPTION("HP WMI Sensors driver"); 2015*23902f98SJames Seo MODULE_LICENSE("GPL"); 2016