// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HP WMI hotkeys
*
* Copyright (C) 2008 Red Hat <mjg@redhat.com>
* Copyright (C) 2010, 2011 Anssi Hannula <anssi.hannula@iki.fi>
*
* Portions based on wistron_btns.c:
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
* Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
* Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/platform_device.h>
#include <linux/platform_profile.h>
#include <linux/hwmon.h>
#include <linux/acpi.h>
#include <linux/rfkill.h>
#include <linux/string.h>
#include <linux/dmi.h>
MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95
#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required
/* DMI board names of devices that should use the omen specific path for
* thermal profiles.
* This was obtained by taking a look in the windows omen command center
* app and parsing a json file that they use to figure out what capabilities
* the device should have.
* A device is considered an omen if the DisplayName in that list contains
* "OMEN", and it can use the thermal profile stuff if the "Feature" array
* contains "PerformanceControl".
*/
static const char * const omen_thermal_profile_boards[] = {
"84DA", "84DB", "84DC", "8574", "8575", "860A", "87B5", "8572", "8573",
"8600", "8601", "8602", "8605", "8606", "8607", "8746", "8747", "8749",
"874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C",
"88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD",
"88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912",
"8917", "8918", "8949", "894A", "89EB"
};
/* DMI Board names of Omen laptops that are specifically set to be thermal
* profile version 0 by the Omen Command Center app, regardless of what
* the get system design information WMI call returns
*/
static const char *const omen_thermal_profile_force_v0_boards[] = {
"8607", "8746", "8747", "8749", "874A", "8748"
};
enum hp_wmi_radio {
HPWMI_WIFI = 0x0,
HPWMI_BLUETOOTH = 0x1,
HPWMI_WWAN = 0x2,
HPWMI_GPS = 0x3,
};
enum hp_wmi_event_ids {
HPWMI_DOCK_EVENT = 0x01,
HPWMI_PARK_HDD = 0x02,
HPWMI_SMART_ADAPTER = 0x03,
HPWMI_BEZEL_BUTTON = 0x04,
HPWMI_WIRELESS = 0x05,
HPWMI_CPU_BATTERY_THROTTLE = 0x06,
HPWMI_LOCK_SWITCH = 0x07,
HPWMI_LID_SWITCH = 0x08,
HPWMI_SCREEN_ROTATION = 0x09,
HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A,
HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B,
HPWMI_PROXIMITY_SENSOR = 0x0C,
HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D,
HPWMI_PEAKSHIFT_PERIOD = 0x0F,
HPWMI_BATTERY_CHARGE_PERIOD = 0x10,
HPWMI_SANITIZATION_MODE = 0x17,
};
/*
* struct bios_args buffer is dynamically allocated. New WMI command types
* were introduced that exceeds 128-byte data size. Changes to handle
* the data size allocation scheme were kept in hp_wmi_perform_qurey function.
*/
struct bios_args {
u32 signature;
u32 command;
u32 commandtype;
u32 datasize;
u8 data[];
};
enum hp_wmi_commandtype {
HPWMI_DISPLAY_QUERY = 0x01,
HPWMI_HDDTEMP_QUERY = 0x02,
HPWMI_ALS_QUERY = 0x03,
HPWMI_HARDWARE_QUERY = 0x04,
HPWMI_WIRELESS_QUERY = 0x05,
HPWMI_BATTERY_QUERY = 0x07,
HPWMI_BIOS_QUERY = 0x09,
HPWMI_FEATURE_QUERY = 0x0b,
HPWMI_HOTKEY_QUERY = 0x0c,
HPWMI_FEATURE2_QUERY = 0x0d,
HPWMI_WIRELESS2_QUERY = 0x1b,
HPWMI_POSTCODEERROR_QUERY = 0x2a,
HPWMI_SYSTEM_DEVICE_MODE = 0x40,
HPWMI_THERMAL_PROFILE_QUERY = 0x4c,
};
enum hp_wmi_gm_commandtype {
HPWMI_FAN_SPEED_GET_QUERY = 0x11,
HPWMI_SET_PERFORMANCE_MODE = 0x1A,
HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26,
HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27,
HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28,
};
enum hp_wmi_command {
HPWMI_READ = 0x01,
HPWMI_WRITE = 0x02,
HPWMI_ODM = 0x03,
HPWMI_GM = 0x20008,
};
enum hp_wmi_hardware_mask {
HPWMI_DOCK_MASK = 0x01,
HPWMI_TABLET_MASK = 0x04,
};
struct bios_return {
u32 sigpass;
u32 return_code;
};
enum hp_return_value {
HPWMI_RET_WRONG_SIGNATURE = 0x02,
HPWMI_RET_UNKNOWN_COMMAND = 0x03,
HPWMI_RET_UNKNOWN_CMDTYPE = 0x04,
HPWMI_RET_INVALID_PARAMETERS = 0x05,
};
enum hp_wireless2_bits {
HPWMI_POWER_STATE = 0x01,
HPWMI_POWER_SOFT = 0x02,
HPWMI_POWER_BIOS = 0x04,
HPWMI_POWER_HARD = 0x08,
HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD,
};
enum hp_thermal_profile_omen_v0 {
HP_OMEN_V0_THERMAL_PROFILE_DEFAULT = 0x00,
HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE = 0x01,
HP_OMEN_V0_THERMAL_PROFILE_COOL = 0x02,
};
enum hp_thermal_profile_omen_v1 {
HP_OMEN_V1_THERMAL_PROFILE_DEFAULT = 0x30,
HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE = 0x31,
HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50,
};
enum hp_thermal_profile {
HP_THERMAL_PROFILE_PERFORMANCE = 0x00,
HP_THERMAL_PROFILE_DEFAULT = 0x01,
HP_THERMAL_PROFILE_COOL = 0x02
};
#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW)
#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
struct bios_rfkill2_device_state {
u8 radio_type;
u8 bus_type;
u16 vendor_id;
u16 product_id;
u16 subsys_vendor_id;
u16 subsys_product_id;
u8 rfkill_id;
u8 power;
u8 unknown[4];
};
/* 7 devices fit into the 128 byte buffer */
#define HPWMI_MAX_RFKILL2_DEVICES 7
struct bios_rfkill2_state {
u8 unknown[7];
u8 count;
u8 pad[8];
struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES];
};
static const struct key_entry hp_wmi_keymap[] = {
{ KE_KEY, 0x02, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY, 0x20e6, { KEY_PROG1 } },
{ KE_KEY, 0x20e8, { KEY_MEDIA } },
{ KE_KEY, 0x2142, { KEY_MEDIA } },
{ KE_KEY, 0x213b, { KEY_INFO } },
{ KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } },
{ KE_KEY, 0x216a, { KEY_SETUP } },
{ KE_KEY, 0x231b, { KEY_HELP } },
{ KE_END, 0 }
};
static struct input_dev *hp_wmi_input_dev;
static struct platform_device *hp_wmi_platform_dev;
static struct platform_profile_handler platform_profile_handler;
static bool platform_profile_support;
static bool zero_insize_support;
static struct rfkill *wifi_rfkill;
static struct rfkill *bluetooth_rfkill;
static struct rfkill *wwan_rfkill;
struct rfkill2_device {
u8 id;
int num;
struct rfkill *rfkill;
};
static int rfkill2_count;
static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
/*
* Chassis Types values were obtained from SMBIOS reference
* specification version 3.00. A complete list of system enclosures
* and chassis types is available on Table 17.
*/
static const char * const tablet_chassis_types[] = {
"30", /* Tablet*/
"31", /* Convertible */
"32" /* Detachable */
};
#define DEVICE_MODE_TABLET 0x06
/* map output size to the corresponding WMI method id */
static inline int encode_outsize_for_pvsz(int outsize)
{
if (outsize > 4096)
return -EINVAL;
if (outsize > 1024)
|