#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".
*/ staticconstchar * 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", "8BAD", "8A42", "8A15"
};
/* 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
*/ staticconstchar * const omen_thermal_profile_force_v0_boards[] = { "8607", "8746", "8747", "8749", "874A", "8748"
};
/* DMI board names of Omen laptops that have a thermal profile timer which will * cause the embedded controller to set the thermal profile back to * "balanced" when reaching zero.
*/ staticconstchar * const omen_timed_thermal_profile_boards[] = { "8BAD", "8A42", "8A15"
};
/* DMI Board names of Victus 16-d1xxx laptops */ staticconstchar * const victus_thermal_profile_boards[] = { "8A25"
};
/* DMI Board names of Victus 16-r1000 and Victus 16-s1000 laptops */ staticconstchar * const victus_s_thermal_profile_boards[] = { "8C99", "8C9C"
};
/* * 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[];
};
/* * 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.
*/ staticconstchar * const tablet_chassis_types[] = { "30", /* Tablet*/ "31", /* Convertible */ "32"/* Detachable */
};
#define DEVICE_MODE_TABLET 0x06
/* map output size to the corresponding WMI method id */ staticinlineint encode_outsize_for_pvsz(int outsize)
{ if (outsize > 4096) return -EINVAL; if (outsize > 1024) return 5; if (outsize > 128) return 4; if (outsize > 4) return 3; if (outsize > 0) return 2; return 1;
}
/* * hp_wmi_perform_query * * query: The commandtype (enum hp_wmi_commandtype) * write: The command (enum hp_wmi_command) * buffer: Buffer used as input and/or output * insize: Size of input buffer * outsize: Size of output buffer * * returns zero on success * an HP WMI query specific error code (which is positive) * -EINVAL if the query was not successful at all * -EINVAL if the output buffer size exceeds buffersize * * Note: The buffersize must at least be the maximum of the input and output * size. E.g. Battery info query is defined to have 1 byte input * and 128 byte output. The caller would do: * buffer = kzalloc(128, GFP_KERNEL); * ret = hp_wmi_perform_query(HPWMI_BATTERY_QUERY, HPWMI_READ, buffer, 1, 128)
*/ staticint hp_wmi_perform_query(int query, enum hp_wmi_command command, void *buffer, int insize, int outsize)
{ struct acpi_buffer input, output = { ACPI_ALLOCATE_BUFFER, NULL }; struct bios_return *bios_return; union acpi_object *obj = NULL; struct bios_args *args = NULL; int mid, actual_insize, actual_outsize;
size_t bios_args_size; int ret;
mid = encode_outsize_for_pvsz(outsize); if (WARN_ON(mid < 0)) return mid;
/* * Calling this hp_wmi_get_fan_count_userdefine_trigger function also enables * and/or maintains the laptop in user defined thermal and fan states, instead * of using a fallback state. After a 120 seconds timeout however, the laptop * goes back to its fallback state.
*/ staticint hp_wmi_get_fan_count_userdefine_trigger(void)
{
u8 fan_data[4] = {}; int ret;
ret = hp_wmi_perform_query(HPWMI_FAN_COUNT_GET_QUERY, HPWMI_GM,
&fan_data, sizeof(u8), sizeof(fan_data)); if (ret != 0) return -EINVAL;
staticint omen_thermal_profile_set(int mode)
{ /* The Omen Control Center actively sets the first byte of the buffer to * 255, so let's mimic this behaviour to be as close as possible to * the original software.
*/ char buffer[2] = {-1, mode}; int ret;
ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM,
&buffer, sizeof(buffer), 0);
ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_SET_QUERY, HPWMI_GM,
&fan_speed, sizeof(fan_speed), 0);
return ret;
}
staticint hp_wmi_fan_speed_max_reset(void)
{ int ret;
ret = hp_wmi_fan_speed_max_set(0); if (ret) return ret;
/* Disabling max fan speed on Victus s1xxx laptops needs a 2nd step: */
ret = hp_wmi_fan_speed_reset(); return ret;
}
staticint hp_wmi_fan_speed_max_get(void)
{ int val = 0, ret;
ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_GET_QUERY, HPWMI_GM,
&val, zero_if_sup(val), sizeof(val));
if (ret) return ret < 0 ? ret : -EINVAL;
return val;
}
staticint __init hp_wmi_bios_2008_later(void)
{ int state = 0; int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, HPWMI_READ, &state,
zero_if_sup(state), sizeof(state)); if (!ret) return 1;
staticint __init hp_wmi_enable_hotkeys(void)
{ int value = 0x6e; int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, HPWMI_WRITE, &value, sizeof(value), 0);
return ret <= 0 ? ret : -EINVAL;
}
staticint hp_wmi_set_block(void *data, bool blocked)
{ enum hp_wmi_radio r = (long)data; int query = BIT(r + 8) | ((!blocked) << r); int ret;
ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_WRITE,
&query, sizeof(query), 0);
static ssize_t postcode_show(struct device *dev, struct device_attribute *attr, char *buf)
{ /* Get the POST error code of previous boot failure. */ int value = hp_wmi_read_int(HPWMI_POSTCODEERROR_QUERY);
ret = kstrtobool(buf, &clear); if (ret) return ret;
if (clear == false) return -EINVAL;
/* Clear the POST error code. It is kept until cleared. */
ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp, sizeof(tmp), 0); if (ret) return ret < 0 ? ret : -EINVAL;
return count;
}
staticint camera_shutter_input_setup(void)
{ int err;
camera_shutter_input_dev = input_allocate_device(); if (!camera_shutter_input_dev) return -ENOMEM;
if (!obj) return; if (obj->type != ACPI_TYPE_BUFFER) {
pr_info("Unknown response received %d\n", obj->type); return;
}
/* * Depending on ACPI version the concatenation of id and event data * inside _WED function will result in a 8 or 16 byte buffer.
*/
location = (u32 *)obj->buffer.pointer; if (obj->buffer.length == 8) {
event_id = *location;
event_data = *(location + 1);
} elseif (obj->buffer.length == 16) {
event_id = *location;
event_data = *(location + 2);
} else {
pr_info("Unknown buffer length %d\n", obj->buffer.length); return;
}
switch (event_id) { case HPWMI_DOCK_EVENT: if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit))
input_report_switch(hp_wmi_input_dev, SW_DOCK,
hp_wmi_get_dock_state()); if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit))
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
hp_wmi_get_tablet_mode());
input_sync(hp_wmi_input_dev); break; case HPWMI_PARK_HDD: break; case HPWMI_SMART_ADAPTER: break; case HPWMI_BEZEL_BUTTON:
key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY); if (key_code < 0) break;
if (!sparse_keymap_report_event(hp_wmi_input_dev,
key_code, 1, true))
pr_info("Unknown key code - 0x%x\n", key_code); break; case HPWMI_FN_P_HOTKEY:
platform_profile_cycle(); break; case HPWMI_OMEN_KEY: if (event_data) /* Only should be true for HP Omen */
key_code = event_data; else
key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY);
if (!sparse_keymap_report_event(hp_wmi_input_dev,
key_code, 1, true))
pr_info("Unknown key code - 0x%x\n", key_code); break; case HPWMI_WIRELESS: if (rfkill2_count) {
hp_wmi_rfkill2_refresh(); break;
}
if (wifi_rfkill)
rfkill_set_states(wifi_rfkill,
hp_wmi_get_sw_state(HPWMI_WIFI),
hp_wmi_get_hw_state(HPWMI_WIFI)); if (bluetooth_rfkill)
rfkill_set_states(bluetooth_rfkill,
hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); if (wwan_rfkill)
rfkill_set_states(wwan_rfkill,
hp_wmi_get_sw_state(HPWMI_WWAN),
hp_wmi_get_hw_state(HPWMI_WWAN)); break; case HPWMI_CPU_BATTERY_THROTTLE:
pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n"); break; case HPWMI_LOCK_SWITCH: break; case HPWMI_LID_SWITCH: break; case HPWMI_SCREEN_ROTATION: break; case HPWMI_COOLSENSE_SYSTEM_MOBILE: break; case HPWMI_COOLSENSE_SYSTEM_HOT: break; case HPWMI_PROXIMITY_SENSOR: break; case HPWMI_BACKLIT_KB_BRIGHTNESS: break; case HPWMI_PEAKSHIFT_PERIOD: break; case HPWMI_BATTERY_CHARGE_PERIOD: break; case HPWMI_SANITIZATION_MODE: break; case HPWMI_CAMERA_TOGGLE: if (!camera_shutter_input_dev) if (camera_shutter_input_setup()) {
pr_err("Failed to setup camera shutter input device\n"); break;
} if (event_data == 0xff)
input_report_switch(camera_shutter_input_dev, SW_CAMERA_LENS_COVER, 1); elseif (event_data == 0xfe)
input_report_switch(camera_shutter_input_dev, SW_CAMERA_LENS_COVER, 0); else
pr_warn("Unknown camera shutter state - 0x%x\n", event_data);
input_sync(camera_shutter_input_dev); break; case HPWMI_SMART_EXPERIENCE_APP: break; default:
pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data); break;
}
}
staticint __init hp_wmi_input_setup(void)
{
acpi_status status; int err, val;
hp_wmi_input_dev = input_allocate_device(); if (!hp_wmi_input_dev) return -ENOMEM;
if (state.count > HPWMI_MAX_RFKILL2_DEVICES) {
pr_warn("unable to parse 0x1b query output\n"); return -EINVAL;
}
for (i = 0; i < state.count; i++) { struct rfkill *rfkill; enum rfkill_type type; char *name;
switch (state.device[i].radio_type) { case HPWMI_WIFI:
type = RFKILL_TYPE_WLAN;
name = "hp-wifi"; break; case HPWMI_BLUETOOTH:
type = RFKILL_TYPE_BLUETOOTH;
name = "hp-bluetooth"; break; case HPWMI_WWAN:
type = RFKILL_TYPE_WWAN;
name = "hp-wwan"; break; case HPWMI_GPS:
type = RFKILL_TYPE_GPS;
name = "hp-gps"; break; default:
pr_warn("unknown device type 0x%x\n",
state.device[i].radio_type); continue;
}
if (!state.device[i].vendor_id) {
pr_warn("zero device %d while %d reported\n",
i, state.count); continue;
}
staticint platform_profile_omen_get_ec(enum platform_profile_option *profile)
{ int tp;
tp = omen_thermal_profile_get(); if (tp < 0) return tp;
switch (tp) { case HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE: case HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE:
*profile = PLATFORM_PROFILE_PERFORMANCE; break; case HP_OMEN_V0_THERMAL_PROFILE_DEFAULT: case HP_OMEN_V1_THERMAL_PROFILE_DEFAULT:
*profile = PLATFORM_PROFILE_BALANCED; break; case HP_OMEN_V0_THERMAL_PROFILE_COOL: case HP_OMEN_V1_THERMAL_PROFILE_COOL:
*profile = PLATFORM_PROFILE_COOL; break; default: return -EINVAL;
}
return 0;
}
staticint platform_profile_omen_get(struct device *dev, enum platform_profile_option *profile)
{ /* * We directly return the stored platform profile, as the embedded * controller will not accept switching to the performance option when * the conditions are not met (e.g. the laptop is not plugged in). * * If we directly return what the EC reports, the platform profile will * immediately "switch back" to normal mode, which is against the * expected behaviour from a userspace point of view, as described in * the Platform Profile Section page of the kernel documentation. * * See also omen_powersource_event.
*/
guard(mutex)(&active_platform_profile_lock);
*profile = active_platform_profile;
/* Retrieving GPU slowdown temperature, in order to keep it unchanged */
ret = victus_s_gpu_thermal_profile_get(¤t_ctgp_state,
¤t_ppab_state,
¤t_dstate,
¤t_gpu_slowdown_temp); if (ret < 0) {
pr_warn("GPU modes not updated, unable to get slowdown temp\n"); return ret;
}
ret = hp_wmi_perform_query(HPWMI_SET_GPU_THERMAL_MODES_QUERY, HPWMI_GM,
&gpu_power_modes, sizeof(gpu_power_modes), 0);
return ret;
}
/* Note: HP_POWER_LIMIT_DEFAULT can be used to restore default PL1 and PL2 */ staticint victus_s_set_cpu_pl1_pl2(u8 pl1, u8 pl2)
{ struct victus_power_limits power_limits; int ret;
/* We need to know both PL1 and PL2 values in order to check them */ if (pl1 == HP_POWER_LIMIT_NO_CHANGE || pl2 == HP_POWER_LIMIT_NO_CHANGE) return -EINVAL;
/* PL2 is not supposed to be lower than PL1 */ if (pl2 < pl1) return -EINVAL;
if (strcmp(event_entry->device_class, ACPI_AC_CLASS) != 0) return NOTIFY_DONE;
pr_debug("Received power source device event\n");
guard(mutex)(&active_platform_profile_lock);
/* * This handler can only be called on Omen and Victus models, so * there's no need to call is_victus_thermal_profile() here.
*/ if (is_omen_thermal_profile())
err = platform_profile_omen_get_ec(&actual_profile); else
err = platform_profile_victus_get_ec(&actual_profile);
if (err < 0) { /* * Although we failed to get the current platform profile, we * still want the other event consumers to process it.
*/
pr_warn("Failed to read current platform profile (%d)\n", err); return NOTIFY_DONE;
}
/* * If we're back on AC and that the user-chosen power profile is * different from what the EC reports, we restore the user-chosen * one.
*/ if (power_supply_is_system_supplied() <= 0 ||
active_platform_profile == actual_profile) {
pr_debug("Platform profile update skipped, conditions unmet\n"); return NOTIFY_DONE;
}
if (is_omen_thermal_profile())
err = platform_profile_omen_set_ec(active_platform_profile); else
err = platform_profile_victus_set_ec(active_platform_profile);
if (err < 0) {
pr_warn("Failed to restore platform profile (%d)\n", err); return NOTIFY_DONE;
}
if (strcmp(event_entry->device_class, ACPI_AC_CLASS) != 0) return NOTIFY_DONE;
pr_debug("Received power source device event\n");
/* * Switching to battery power source while Performance mode is active * needs manual triggering of CPU power limits. Same goes when switching * to AC power source while Performance mode is active. Other modes * however are automatically behaving without any manual action. * Seen on HP 16-s1034nf (board 8C9C) with F.11 and F.13 BIOS versions.
*/
if (active_platform_profile == PLATFORM_PROFILE_PERFORMANCE) {
pr_debug("Triggering CPU PL1/PL2 actualization\n");
err = victus_s_set_cpu_pl1_pl2(HP_POWER_LIMIT_DEFAULT,
HP_POWER_LIMIT_DEFAULT); if (err)
pr_warn("Failed to actualize power limits: %d\n", err);
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
staticint omen_register_powersource_event_handler(void)
{ int err;
/* * call thermal profile write command to ensure that the * firmware correctly sets the OEM variables
*/
err = platform_profile_victus_set_ec(active_platform_profile); if (err < 0) return err;
ops = &platform_profile_victus_ops;
} elseif (is_victus_s_thermal_profile()) { /* * Being unable to retrieve laptop's current thermal profile, * during this setup, we set it to Balanced by default.
*/
active_platform_profile = PLATFORM_PROFILE_BALANCED;
err = platform_profile_victus_s_set_ec(active_platform_profile); if (err < 0) return err;
/* * call thermal profile write command to ensure that the * firmware correctly sets the OEM variables for the DPTF
*/
err = thermal_profile_set(tp); if (err) return err;
ops = &hp_wmi_platform_profile_ops;
}
platform_profile_device = devm_platform_profile_register(&device->dev, "hp-wmi",
NULL, ops); if (IS_ERR(platform_profile_device)) return PTR_ERR(platform_profile_device);
pr_info("Registered as platform profile handler\n");
platform_profile_support = true;
/* * In pre-2009 BIOS, command 1Bh return 0x4 to indicate that * BIOS no longer controls the power for the wireless * devices. All features supported by this command will no * longer be supported.
*/ if (!hp_wmi_bios_2009_later()) { if (hp_wmi_rfkill_setup(device))
hp_wmi_rfkill2_setup(device);
}
err = hp_wmi_hwmon_init();
if (err < 0) return err;
thermal_profile_setup(device);
return 0;
}
staticvoid __exit hp_wmi_bios_remove(struct platform_device *device)
{ int i;
for (i = 0; i < rfkill2_count; i++) {
rfkill_unregister(rfkill2[i].rfkill);
rfkill_destroy(rfkill2[i].rfkill);
}
if (wifi_rfkill) {
rfkill_unregister(wifi_rfkill);
rfkill_destroy(wifi_rfkill);
} if (bluetooth_rfkill) {
rfkill_unregister(bluetooth_rfkill);
rfkill_destroy(bluetooth_rfkill);
} if (wwan_rfkill) {
rfkill_unregister(wwan_rfkill);
rfkill_destroy(wwan_rfkill);
}
}
staticint hp_wmi_resume_handler(struct device *device)
{ /* * Hardware state may have changed while suspended, so trigger * input events for the current state. As this is a switch, * the input layer will only actually pass it on if the state * changed.
*/ if (hp_wmi_input_dev) { if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit))
input_report_switch(hp_wmi_input_dev, SW_DOCK,
hp_wmi_get_dock_state()); if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit))
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
hp_wmi_get_tablet_mode());
input_sync(hp_wmi_input_dev);
}
if (rfkill2_count)
hp_wmi_rfkill2_refresh();
if (wifi_rfkill)
rfkill_set_states(wifi_rfkill,
hp_wmi_get_sw_state(HPWMI_WIFI),
hp_wmi_get_hw_state(HPWMI_WIFI)); if (bluetooth_rfkill)
rfkill_set_states(bluetooth_rfkill,
hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); if (wwan_rfkill)
rfkill_set_states(wwan_rfkill,
hp_wmi_get_sw_state(HPWMI_WWAN),
hp_wmi_get_hw_state(HPWMI_WWAN));
/* * hp_wmi_bios_remove() lives in .exit.text. For drivers registered via * module_platform_driver_probe() this is ok because they cannot get unbound at * runtime. So mark the driver struct with __refdata to prevent modpost * triggering a section mismatch warning.
*/ staticstruct platform_driver hp_wmi_driver __refdata = {
.driver = {
.name = "hp-wmi",
.pm = &hp_wmi_pm_ops,
.dev_groups = hp_wmi_groups,
},
.remove = __exit_p(hp_wmi_bios_remove),
};
static umode_t hp_wmi_hwmon_is_visible(constvoid *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{ switch (type) { case hwmon_pwm: return 0644; case hwmon_fan: if (is_victus_s_thermal_profile()) { if (hp_wmi_get_fan_speed_victus_s(channel) >= 0) return 0444;
} else { if (hp_wmi_get_fan_speed(channel) >= 0) return 0444;
} break; default: return 0;
}
return 0;
}
staticint hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{ int ret;
switch (type) { case hwmon_fan: if (is_victus_s_thermal_profile())
ret = hp_wmi_get_fan_speed_victus_s(channel); else
ret = hp_wmi_get_fan_speed(channel); if (ret < 0) return ret;
*val = ret; return 0; case hwmon_pwm: switch (hp_wmi_fan_speed_max_get()) { case 0: /* 0 is automatic fan, which is 2 for hwmon */
*val = 2; return 0; case 1: /* 1 is max fan, which is 0 * (no fan speed control) for hwmon
*/
*val = 0; return 0; default: /* shouldn't happen */ return -ENODATA;
} default: return -EINVAL;
}
}
staticint hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{ switch (type) { case hwmon_pwm: switch (val) { case 0: if (is_victus_s_thermal_profile())
hp_wmi_get_fan_count_userdefine_trigger(); /* 0 is no fan speed control (max), which is 1 for us */ return hp_wmi_fan_speed_max_set(1); case 2: /* 2 is automatic speed control, which is 0 for us */ if (is_victus_s_thermal_profile()) {
hp_wmi_get_fan_count_userdefine_trigger(); return hp_wmi_fan_speed_max_reset();
} else return hp_wmi_fan_speed_max_set(0); default: /* we don't support manual fan speed control */ return -EINVAL;
} default: return -EOPNOTSUPP;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.