#define ASUS_MINI_LED_MODE_MASK 0x03 /* Standard modes for devices with only on/off */ #define ASUS_MINI_LED_OFF 0x00 #define ASUS_MINI_LED_ON 0x01 /* New mode on some devices, define here to clarify remapping later */ #define ASUS_MINI_LED_STRONG_MODE 0x02 /* New modes for devices with 3 mini-led mode types */ #define ASUS_MINI_LED_2024_WEAK 0x00 #define ASUS_MINI_LED_2024_STRONG 0x01 #define ASUS_MINI_LED_2024_OFF 0x02
#define ASUS_USB0_PWR_EC0_CSEE "\\_SB.PCI0.SBRG.EC0.CSEE" /* * The period required to wait after screen off/on/s2idle.check in MS. * Time here greatly impacts the wake behaviour. Used in suspend/wake.
*/ #define ASUS_USB0_PWR_EC0_CSEE_WAIT 600 #define ASUS_USB0_PWR_EC0_CSEE_OFF 0xB7 #define ASUS_USB0_PWR_EC0_CSEE_ON 0xB8
staticbool ashs_present(void)
{ int i = 0; while (ashs_ids[i]) { if (acpi_dev_found(ashs_ids[i++])) returntrue;
} returnfalse;
}
struct bios_args {
u32 arg0;
u32 arg1;
u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
u32 arg3;
u32 arg4; /* Some ROG laptops require a full 5 input args */
u32 arg5;
} __packed;
/* * Struct that's used for all methods called via AGFN. Naming is * identically to the AML code.
*/ struct agfn_args {
u16 mfun; /* probably "Multi-function" to be called */
u16 sfun; /* probably "Sub-function" to be called */
u16 len; /* size of the hole struct, including subfunction fields */
u8 stas; /* not used by now */
u8 err; /* zero on success */
} __packed;
/* struct used for calling fan read and write methods */ struct agfn_fan_args { struct agfn_args agfn; /* common fields */
u8 fan; /* fan number: 0: set auto mode 1: 1st fan */
u32 speed; /* read: RPM/100 - write: 0-255 */
} __packed;
/* * <platform>/ - debugfs root directory * dev_id - current dev_id * ctrl_param - current ctrl_param * method_id - current method_id * devs - call DEVS(dev_id, ctrl_param) and print result * dsts - call DSTS(dev_id) and print result * call - call method_id(dev_id, ctrl_param) and print result
*/ struct asus_wmi_debug { struct dentry *root;
u32 method_id;
u32 dev_id;
u32 ctrl_param;
};
int tablet_switch_event_code;
u32 tablet_switch_dev_id; bool tablet_switch_inverted;
enum fan_type fan_type; enum fan_type gpu_fan_type; enum fan_type mid_fan_type; int fan_pwm_mode; int gpu_fan_pwm_mode; int mid_fan_pwm_mode; int agfn_pwm;
/* * Returns as an error if the method output is not a buffer. Typically this * means that the method called is unsupported.
*/ staticint asus_wmi_evaluate_method_buf(u32 method_id,
u32 arg0, u32 arg1, u8 *ret_buffer, size_t size)
{ struct bios_args args = {
.arg0 = arg0,
.arg1 = arg1,
.arg2 = 0,
}; struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status; union acpi_object *obj; int err = 0;
status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
&input, &output);
pr_debug("%s called (0x%08x) with args: 0x%08x, 0x%08x\n",
__func__, method_id, arg0, arg1); if (ACPI_FAILURE(status)) {
pr_debug("%s, (0x%08x), arg 0x%08x failed: %d\n",
__func__, method_id, arg0, -EIO); return -EIO;
}
obj = (union acpi_object *)output.pointer;
switch (obj->type) { case ACPI_TYPE_BUFFER: if (obj->buffer.length > size) {
err = -ENOSPC; break;
} if (obj->buffer.length == 0) {
err = -ENODATA; break;
}
memcpy(ret_buffer, obj->buffer.pointer, obj->buffer.length); break; case ACPI_TYPE_INTEGER:
err = (u32)obj->integer.value;
if (err == ASUS_WMI_UNSUPPORTED_METHOD)
err = -ENODEV; /* * At least one method returns a 0 with no buffer if no arg * is provided, such as ASUS_WMI_DEVID_CPU_FAN_CURVE
*/ if (err == 0)
err = -ENODATA; break; default:
err = -ENODATA; break;
}
/* * Copy to dma capable address otherwise memory corruption occurs as * bios has to be able to access it.
*/
input.pointer = kmemdup(args.pointer, args.length, GFP_DMA | GFP_KERNEL);
input.length = args.length; if (!input.pointer) return -ENOMEM;
phys_addr = virt_to_phys(input.pointer);
status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_AGFN,
phys_addr, 0, &retval); if (!status)
memcpy(args.pointer, input.pointer, args.length);
result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_DGPU); if (result < 0) return result;
return sysfs_emit(buf, "%d\n", result);
}
/* * A user may be required to store the value twice, typcial store first, then * rescan PCI bus to activate power, then store a second time to save correctly. * The reason for this is that an extra code path in the ACPI is enabled when * the device and bus are powered.
*/ static ssize_t dgpu_disable_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ int result, err;
u32 disable;
struct asus_wmi *asus = dev_get_drvdata(dev);
result = kstrtou32(buf, 10, &disable); if (result) return result;
if (disable > 1) return -EINVAL;
if (asus->gpu_mux_dev) {
result = asus_wmi_get_devstate_simple(asus, asus->gpu_mux_dev); if (result < 0) /* An error here may signal greater failure of GPU handling */ return result; if (!result && disable) {
err = -ENODEV;
pr_warn("Can not disable dGPU when the MUX is in dGPU mode: %d\n", err); return err;
}
}
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_DGPU, disable, &result); if (err) {
pr_warn("Failed to set dgpu disable: %d\n", err); return err;
}
if (result > 1) {
pr_warn("Failed to set dgpu disable (result): 0x%x\n", result); return -EIO;
}
result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_EGPU); if (result < 0) return result;
return sysfs_emit(buf, "%d\n", result);
}
/* The ACPI call to enable the eGPU also disables the internal dGPU */ static ssize_t egpu_enable_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ int result, err;
u32 enable;
struct asus_wmi *asus = dev_get_drvdata(dev);
err = kstrtou32(buf, 10, &enable); if (err) return err;
if (enable > 1) return -EINVAL;
err = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_EGPU_CONNECTED); if (err < 0) {
pr_warn("Failed to get egpu connection status: %d\n", err); return err;
}
if (asus->gpu_mux_dev) {
result = asus_wmi_get_devstate_simple(asus, asus->gpu_mux_dev); if (result < 0) { /* An error here may signal greater failure of GPU handling */
pr_warn("Failed to get gpu mux status: %d\n", result); return result;
} if (!result && enable) {
err = -ENODEV;
pr_warn("Can not enable eGPU when the MUX is in dGPU mode: %d\n", err); return err;
}
}
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_EGPU, enable, &result); if (err) {
pr_warn("Failed to set egpu state: %d\n", err); return err;
}
if (result > 1) {
pr_warn("Failed to set egpu state (retval): 0x%x\n", result); return -EIO;
}
err = kstrtou32(buf, 10, &optimus); if (err) return err;
if (optimus > 1) return -EINVAL;
if (asus->dgpu_disable_available) {
result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_DGPU); if (result < 0) /* An error here may signal greater failure of GPU handling */ return result; if (result && !optimus) {
err = -ENODEV;
pr_warn("Can not switch MUX to dGPU mode when dGPU is disabled: %d\n", err); return err;
}
}
if (asus->egpu_enable_available) {
result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_EGPU); if (result < 0) /* An error here may signal greater failure of GPU handling */ return result; if (result && !optimus) {
err = -ENODEV;
pr_warn("Can not switch MUX to dGPU mode when eGPU is enabled: %d\n", err); return err;
}
}
err = asus_wmi_set_devstate(asus->gpu_mux_dev, optimus, &result); if (err) {
dev_err(dev, "Failed to set GPU MUX mode: %d\n", err); return err;
} /* !1 is considered a fail by ASUS */ if (result != 1) {
dev_warn(dev, "Failed to set GPU MUX mode (result): 0x%x\n", result); return -EIO;
}
/* * The HID driver needs to check MCU version and set this to false if the MCU FW * version is >= the minimum requirements. New FW do not need the hacks.
*/ void set_ally_mcu_hack(enum asus_ally_mcu_hack status)
{
use_ally_mcu_hack = status;
pr_debug("%s Ally MCU suspend quirk\n",
status == ASUS_WMI_ALLY_MCU_HACK_ENABLED ? "Enabled" : "Disabled");
}
EXPORT_SYMBOL_NS_GPL(set_ally_mcu_hack, "ASUS_WMI");
/* * mcu_powersave should be enabled always, as it is fixed in MCU FW versions: * - v313 for Ally X * - v319 for Ally 1 * The HID driver checks MCU versions and so should set this if requirements match
*/ void set_ally_mcu_powersave(bool enabled)
{ int result, err;
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MCU_POWERSAVE, enabled, &result); if (err) {
pr_warn("Failed to set MCU powersave: %d\n", err); return;
} if (result > 1) {
pr_warn("Failed to set MCU powersave (result): 0x%x\n", result); return;
}
staticint asus_wmi_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
{ /* The WMI method does not provide a way to specific a battery, so we * just assume it is the first battery. * Note: On some newer ASUS laptops (Zenbook UM431DA), the primary/first * battery is named BATT.
*/ if (strcmp(battery->desc->name, "BAT0") != 0 &&
strcmp(battery->desc->name, "BAT1") != 0 &&
strcmp(battery->desc->name, "BATC") != 0 &&
strcmp(battery->desc->name, "BATT") != 0) return -ENODEV;
if (device_create_file(&battery->dev,
&dev_attr_charge_control_end_threshold)) return -ENODEV;
/* The charge threshold is only reset when the system is power cycled, * and we can't get the current threshold so let set it to 100% when * a battery is added.
*/
asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, 100, NULL);
charge_end_threshold = 100;
/* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED * subsystem asks, we avoid messing with the Asus ACPI stuff during a * potentially bad time, such as a timer interrupt.
*/ staticvoid tpd_led_update(struct work_struct *work)
{ int ctrl_param; struct asus_wmi *asus;
asus = container_of(work, struct asus_wmi, tpd_led_work);
if (asus->kbd_rgb_dev)
kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_mode_group; if (asus->kbd_rgb_state_available)
kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_state_group;
asus->led_workqueue = create_singlethread_workqueue("led_workqueue"); if (!asus->led_workqueue) return -ENOMEM;
if (read_tpd_led_state(asus) >= 0) {
INIT_WORK(&asus->tpd_led_work, tpd_led_update);
rv = led_classdev_register(&asus->platform_device->dev,
&asus->camera_led); if (rv) goto error;
}
if (asus->oobe_state_available) { /* * Disable OOBE state, so that e.g. the keyboard backlight * works.
*/
rv = asus_wmi_set_devstate(ASUS_WMI_DEVID_OOBE, 1, NULL); if (rv) goto error;
}
if (asus->wlan.rfkill)
rfkill_set_sw_state(asus->wlan.rfkill, blocked);
if (asus->hotplug_slot.ops) {
bus = pci_find_bus(0, 1); if (!bus) {
pr_warn("Unable to find PCI bus 1?\n"); goto out_unlock;
}
if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
pr_err("Unable to read PCI config space?\n"); goto out_unlock;
}
absent = (l == 0xffffffff);
if (blocked != absent) {
pr_warn("BIOS says wireless lan is %s, but the pci device is %s\n",
blocked ? "blocked" : "unblocked",
absent ? "absent" : "present");
pr_warn("skipped wireless hotplug as probably inappropriate for this model\n"); goto out_unlock;
}
if (!blocked) {
dev = pci_get_slot(bus, 0); if (dev) { /* Device already present */
pci_dev_put(dev); goto out_unlock;
}
dev = pci_scan_single_device(bus, 0); if (dev) {
pci_bus_assign_resources(bus);
pci_bus_add_device(dev);
}
} else {
dev = pci_get_slot(bus, 0); if (dev) {
pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev);
}
}
}
/* * We can't call directly asus_rfkill_hotplug because most * of the time WMBC is still being executed and not reetrant. * There is currently no way to tell ACPICA that we want this * method to be serialized, we schedule a asus_rfkill_hotplug * call later, in a safer context.
*/
queue_work(asus->hotplug_workqueue, &asus->hotplug_work);
}
status = acpi_get_handle(NULL, node, &handle); if (ACPI_FAILURE(status)) return -ENODEV;
status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
asus_rfkill_notify, asus); if (ACPI_FAILURE(status))
pr_warn("Failed to register notify on %s\n", node);
/* * If the user bit is set, BIOS can't set and record the wlan status, * it will report the value read from id ASUS_WMI_DEVID_WLAN_LED * while we query the wlan status through WMI(ASUS_WMI_DEVID_WLAN). * So, we have to record wlan status in id ASUS_WMI_DEVID_WLAN_LED * while setting the wlan status through WMI. * This is also the behavior that windows app will do.
*/ if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
priv->asus->driver->wlan_ctrl_by_user)
dev_id = ASUS_WMI_DEVID_WLAN_LED;
/* * This handler is enabled only if hotplug is enabled. * In this case, the asus_wmi_set_devstate() will * trigger a wmi notification and we need to wait * this call to finish before being able to call * any wmi method
*/
mutex_lock(&asus->wmi_lock);
ret = asus_rfkill_set(data, blocked);
mutex_unlock(&asus->wmi_lock); return ret;
}
if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
(asus->driver->quirks->wapf > 0))
rfkill_set_led_trigger_name(*rfkill, "asus-wlan");
rfkill_init_sw_state(*rfkill, !result);
result = rfkill_register(*rfkill); if (result) {
rfkill_destroy(*rfkill);
*rfkill = NULL; return result;
} return 0;
}
staticvoid asus_wmi_rfkill_exit(struct asus_wmi *asus)
{ if (asus->driver->wlan_ctrl_by_user && ashs_present()) return;
asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P7"); if (asus->wlan.rfkill) {
rfkill_unregister(asus->wlan.rfkill);
rfkill_destroy(asus->wlan.rfkill);
asus->wlan.rfkill = NULL;
} /* * Refresh pci hotplug in case the rfkill state was changed after * asus_unregister_rfkill_notifier()
*/
asus_rfkill_hotplug(asus); if (asus->hotplug_slot.ops)
pci_hp_deregister(&asus->hotplug_slot); if (asus->hotplug_workqueue)
destroy_workqueue(asus->hotplug_workqueue);
if (asus->bluetooth.rfkill) {
rfkill_unregister(asus->bluetooth.rfkill);
rfkill_destroy(asus->bluetooth.rfkill);
asus->bluetooth.rfkill = NULL;
} if (asus->wimax.rfkill) {
rfkill_unregister(asus->wimax.rfkill);
rfkill_destroy(asus->wimax.rfkill);
asus->wimax.rfkill = NULL;
} if (asus->wwan3g.rfkill) {
rfkill_unregister(asus->wwan3g.rfkill);
rfkill_destroy(asus->wwan3g.rfkill);
asus->wwan3g.rfkill = NULL;
} if (asus->gps.rfkill) {
rfkill_unregister(asus->gps.rfkill);
rfkill_destroy(asus->gps.rfkill);
asus->gps.rfkill = NULL;
} if (asus->uwb.rfkill) {
rfkill_unregister(asus->uwb.rfkill);
rfkill_destroy(asus->uwb.rfkill);
asus->uwb.rfkill = NULL;
}
}
staticint asus_wmi_rfkill_init(struct asus_wmi *asus)
{ int result = 0;
result = asus_new_rfkill(asus, &asus->wlan, "asus-wlan",
RFKILL_TYPE_WLAN, ASUS_WMI_DEVID_WLAN);
if (result && result != -ENODEV) gotoexit;
result = asus_new_rfkill(asus, &asus->bluetooth, "asus-bluetooth", RFKILL_TYPE_BLUETOOTH,
ASUS_WMI_DEVID_BLUETOOTH);
if (result && result != -ENODEV) gotoexit;
result = asus_new_rfkill(asus, &asus->wimax, "asus-wimax",
RFKILL_TYPE_WIMAX, ASUS_WMI_DEVID_WIMAX);
if (result && result != -ENODEV) gotoexit;
result = asus_new_rfkill(asus, &asus->wwan3g, "asus-wwan3g",
RFKILL_TYPE_WWAN, ASUS_WMI_DEVID_WWAN3G);
if (result && result != -ENODEV) gotoexit;
result = asus_new_rfkill(asus, &asus->gps, "asus-gps",
RFKILL_TYPE_GPS, ASUS_WMI_DEVID_GPS);
if (result && result != -ENODEV) gotoexit;
result = asus_new_rfkill(asus, &asus->uwb, "asus-uwb",
RFKILL_TYPE_UWB, ASUS_WMI_DEVID_UWB);
if (result && result != -ENODEV) gotoexit;
if (!asus->driver->quirks->hotplug_wireless) gotoexit;
result = asus_setup_pci_hotplug(asus); /* * If we get -EBUSY then something else is handling the PCI hotplug - * don't fail in this case
*/ if (result == -EBUSY)
result = 0;
asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P5");
asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P6");
asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P7"); /* * Refresh pci hotplug in case the rfkill state was changed during * setup.
*/
asus_rfkill_hotplug(asus);
exit: if (result && result != -ENODEV)
asus_wmi_rfkill_exit(asus);
err = asus_wmi_get_devstate(asus, asus->mini_led_dev_id, &value); if (err < 0) return err;
value = value & ASUS_MINI_LED_MODE_MASK;
/* * Remap the mode values to match previous generation mini-led. The last gen * WMI 0 == off, while on this version WMI 2 ==off (flipped).
*/ if (asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) { switch (value) { case ASUS_MINI_LED_2024_WEAK:
value = ASUS_MINI_LED_ON; break; case ASUS_MINI_LED_2024_STRONG:
value = ASUS_MINI_LED_STRONG_MODE; break; case ASUS_MINI_LED_2024_OFF:
value = ASUS_MINI_LED_OFF; break;
}
}
result = kstrtou32(buf, 10, &mode); if (result) return result;
if (asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE &&
mode > ASUS_MINI_LED_ON) return -EINVAL; if (asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2 &&
mode > ASUS_MINI_LED_STRONG_MODE) return -EINVAL;
/* * Remap the mode values so expected behaviour is the same as the last * generation of mini-LED with 0 == off, 1 == on.
*/ if (asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) { switch (mode) { case ASUS_MINI_LED_OFF:
mode = ASUS_MINI_LED_2024_OFF; break; case ASUS_MINI_LED_ON:
mode = ASUS_MINI_LED_2024_WEAK; break; case ASUS_MINI_LED_STRONG_MODE:
mode = ASUS_MINI_LED_2024_STRONG; break;
}
}
err = asus_wmi_set_devstate(asus->mini_led_dev_id, mode, &result); if (err) {
pr_warn("Failed to set mini-LED: %d\n", err); return err;
}
if (result > 1) {
pr_warn("Failed to set mini-LED mode (result): 0x%x\n", result); return -EIO;
}
/* * Some devices dont support or have borcken get_als method * but still support set method.
*/ staticvoid asus_wmi_set_als(void)
{
asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
}
/* 1: for setting 1st fan's speed 0: setting auto mode */ if (fan != 1 && fan != 0) return -EINVAL;
status = asus_wmi_evaluate_method_agfn(input);
if (status || args.agfn.err) return -ENXIO;
if (speed && fan == 1)
asus->agfn_pwm = *speed;
return 0;
}
/* * Check if we can read the speed of one fan. If true we assume we can also * control it.
*/ staticbool asus_wmi_has_agfn_fan(struct asus_wmi *asus)
{ int status; int speed;
u32 value;
status = asus_agfn_fan_speed_read(asus, 1, &speed); if (status != 0) returnfalse;
status = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value); if (status != 0) returnfalse;
/* * We need to find a better way, probably using sfun, * bits or spec ... * Currently we disable it if: * - ASUS_WMI_UNSUPPORTED_METHOD is returned * - reverved bits are non-zero * - sfun and presence bit are not set
*/ return !(value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000
|| (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)));
}
staticint asus_fan_set_auto(struct asus_wmi *asus)
{ int status;
u32 retval;
switch (asus->fan_type) { case FAN_TYPE_SPEC83:
status = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL,
0, &retval); if (status) return status;
if (retval != 1) return -EIO; break;
case FAN_TYPE_AGFN:
status = asus_agfn_fan_speed_write(asus, 0, NULL); if (status) return -ENXIO; break;
default: return -ENXIO;
}
/* * Modern models like the G713 also have GPU fan control (this is not AGFN)
*/ if (asus->gpu_fan_type == FAN_TYPE_SPEC83) {
status = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_FAN_CTRL,
0, &retval); if (status) return status;
if (retval != 1) return -EIO;
}
return 0;
}
static ssize_t pwm1_show(struct device *dev, struct device_attribute *attr, char *buf)
{ struct asus_wmi *asus = dev_get_drvdata(dev); int err; int value;
/* If we already set a value then just return it */ if (asus->agfn_pwm >= 0) return sysfs_emit(buf, "%d\n", asus->agfn_pwm);
/* * If we haven't set already set a value through the AGFN interface, * we read a current value through the (now-deprecated) FAN_CTRL device.
*/
err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value); if (err < 0) return err;
value &= 0xFF;
if (value == 1) /* Low Speed */
value = 85; elseif (value == 2)
value = 170; elseif (value == 3)
value = 255; elseif (value) {
pr_err("Unknown fan speed %#x\n", value);
value = -1;
}
return sysfs_emit(buf, "%d\n", value);
}
static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count) { struct asus_wmi *asus = dev_get_drvdata(dev); int value; int state; int ret;
ret = kstrtouint(buf, 10, &value); if (ret) return ret;
value = clamp(value, 0, 255);
state = asus_agfn_fan_speed_write(asus, 1, &value); if (state)
pr_warn("Setting fan speed failed: %d\n", state); else
asus->fan_pwm_mode = ASUS_FAN_CTRL_MANUAL;
return count;
}
static ssize_t fan1_input_show(struct device *dev, struct device_attribute *attr, char *buf)
{ struct asus_wmi *asus = dev_get_drvdata(dev); int value; int ret;
switch (asus->fan_type) { case FAN_TYPE_SPEC83:
ret = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL,
&value); if (ret < 0) return ret;
value &= 0xffff; break;
case FAN_TYPE_AGFN: /* no speed readable on manual mode */ if (asus->fan_pwm_mode == ASUS_FAN_CTRL_MANUAL) return -ENXIO;
ret = asus_agfn_fan_speed_read(asus, 1, &value); if (ret) {
pr_warn("reading fan speed failed: %d\n", ret); return -ENXIO;
} break;
default: return -ENXIO;
}
return sysfs_emit(buf, "%d\n", value < 0 ? -1 : value * 100);
}
/* * Just read back the cached pwm mode. * * For the CPU_FAN device, the spec indicates that we should be * able to read the device status and consult bit 19 to see if we * are in Full On or Automatic mode. However, this does not work * in practice on X532FL at least (the bit is always 0) and there's * also nothing in the DSDT to indicate that this behaviour exists.
*/ return sysfs_emit(buf, "%d\n", asus->fan_pwm_mode);
}
static ssize_t pwm1_enable_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ struct asus_wmi *asus = dev_get_drvdata(dev); int status = 0; int state; int value; int ret;
u32 retval;
ret = kstrtouint(buf, 10, &state); if (ret) return ret;
if (asus->fan_type == FAN_TYPE_SPEC83) { switch (state) { /* standard documented hwmon values */ case ASUS_FAN_CTRL_FULLSPEED:
value = 1; break; case ASUS_FAN_CTRL_AUTO:
value = 0; break; default: return -EINVAL;
}
ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL,
value, &retval); if (ret) return ret;
if (retval != 1) return -EIO;
} elseif (asus->fan_type == FAN_TYPE_AGFN) { switch (state) { case ASUS_FAN_CTRL_MANUAL: break;
case ASUS_FAN_CTRL_AUTO:
status = asus_fan_set_auto(asus); if (status) return status; break;
default: return -EINVAL;
}
}
asus->fan_pwm_mode = state;
/* Must set to disabled if mode is toggled */ if (asus->cpu_fan_curve_available)
asus->custom_fan_curves[FAN_CURVE_DEV_CPU].enabled = false; if (asus->gpu_fan_curve_available)
asus->custom_fan_curves[FAN_CURVE_DEV_GPU].enabled = false; if (asus->mid_fan_curve_available)
asus->custom_fan_curves[FAN_CURVE_DEV_MID].enabled = false;
static ssize_t pwm2_enable_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ struct asus_wmi *asus = dev_get_drvdata(dev); int state; int value; int ret;
u32 retval;
ret = kstrtouint(buf, 10, &state); if (ret) return ret;
switch (state) { /* standard documented hwmon values */ case ASUS_FAN_CTRL_FULLSPEED:
value = 1; break; case ASUS_FAN_CTRL_AUTO:
value = 0; break; default: return -EINVAL;
}
ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_FAN_CTRL,
value, &retval); if (ret) return ret;
static ssize_t pwm3_enable_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ struct asus_wmi *asus = dev_get_drvdata(dev); int state; int value; int ret;
u32 retval;
ret = kstrtouint(buf, 10, &state); if (ret) return ret;
switch (state) { /* standard documented hwmon values */ case ASUS_FAN_CTRL_FULLSPEED:
value = 1; break; case ASUS_FAN_CTRL_AUTO:
value = 0; break; default: return -EINVAL;
}
ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_MID_FAN_CTRL,
value, &retval); if (ret) return ret;
if (err < 0) return 0; /* can't return negative here */
/* * If the temperature value in deci-Kelvin is near the absolute * zero temperature, something is clearly wrong
*/ if (value == 0 || value == 1) return 0;
}
/* Modern models like G713 also have GPU fan control */ if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_FAN_CTRL))
asus->gpu_fan_type = FAN_TYPE_SPEC83;
/* Some models also have a center/middle fan */ if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MID_FAN_CTRL))
asus->mid_fan_type = FAN_TYPE_SPEC83;
if (asus->fan_type == FAN_TYPE_NONE) return -ENODEV;
/* Check if capability exists, and populate defaults */ staticint fan_curve_check_present(struct asus_wmi *asus, bool *available,
u32 fan_dev)
{ int err;
*available = false;
if (asus->fan_type == FAN_TYPE_NONE) return 0;
err = fan_curve_get_factory_default(asus, fan_dev); if (err) { return 0;
}
*available = true; return 0;
}
/* Determine which fan the attribute is for if SENSOR_ATTR */ staticstruct fan_curve_data *fan_curve_attr_select(struct asus_wmi *asus, struct device_attribute *attr)
{ int index = to_sensor_dev_attr(attr)->index;
return &asus->custom_fan_curves[index];
}
/* Determine which fan the attribute is for if SENSOR_ATTR_2 */ staticstruct fan_curve_data *fan_curve_attr_2_select(struct asus_wmi *asus, struct device_attribute *attr)
{ int nr = to_sensor_dev_attr_2(attr)->nr;
if (pwm)
data->percents[index] = value; else
data->temps[index] = value;
/* * Mark as disabled so the user has to explicitly enable to apply a * changed fan curve. This prevents potential lockups from writing out * many changes as one-write-per-change.
*/
data->enabled = false;
switch (value) { case 1:
data->enabled = true; break; case 2:
data->enabled = false; break; /* * Auto + reset the fan curve data to defaults. Make it an explicit * option so that users don't accidentally overwrite a set fan curve.
*/ case 3:
err = fan_curve_get_factory_default(asus, data->device_id); if (err) return err;
data->enabled = false; break; default: return -EINVAL;
}
if (data->enabled) {
err = fan_curve_write(asus, data); if (err) return err;
} else { /* * For machines with throttle this is the only way to reset fans * to default mode of operation (does not erase curve data).
*/ if (asus->throttle_thermal_policy_dev) {
err = throttle_thermal_policy_write(asus); if (err) return err; /* Similar is true for laptops with this fan */
} elseif (asus->fan_type == FAN_TYPE_SPEC83) {
err = asus_fan_set_auto(asus); if (err) return err;
} else { /* Safeguard against fautly ACPI tables */
err = fan_curve_get_factory_default(asus, data->device_id); if (err) return err;
err = fan_curve_write(asus, data); if (err) return err;
}
} return count;
}
/* * Check the char instead of casting attr as there are two attr types * involved here (attr1 and attr2)
*/ if (asus->cpu_fan_curve_available && attr->name[3] == '1') return 0644;
if (asus->gpu_fan_curve_available && attr->name[3] == '2') return 0644;
if (asus->mid_fan_curve_available && attr->name[3] == '3') return 0644;
/* * Must be initialised after throttle_thermal_policy_dev is set as * we check the status of throttle_thermal_policy_dev during init.
*/ staticint asus_wmi_custom_fan_curve_init(struct asus_wmi *asus)
{ struct device *dev = &asus->platform_device->dev; struct device *hwmon; int err;
if (asus->throttle_thermal_policy_dev == ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO) { switch (asus->throttle_thermal_policy_mode) { case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT:
value = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO; break; case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST:
value = ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO; break; case ASUS_THROTTLE_THERMAL_POLICY_SILENT:
value = ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO; break; default: return -EINVAL;
}
} else {
value = asus->throttle_thermal_policy_mode;
}
/* Some machines do not return an error code as a result, so we ignore it */
err = asus_wmi_set_devstate(asus->throttle_thermal_policy_dev, value, NULL);
if (err) {
pr_warn("Failed to set throttle thermal policy: %d\n", err); return err;
}
/* Must set to disabled if mode is toggled */ if (asus->cpu_fan_curve_available)
asus->custom_fan_curves[FAN_CURVE_DEV_CPU].enabled = false; if (asus->gpu_fan_curve_available)
asus->custom_fan_curves[FAN_CURVE_DEV_GPU].enabled = false; if (asus->mid_fan_curve_available)
asus->custom_fan_curves[FAN_CURVE_DEV_MID].enabled = false;
return 0;
}
staticint throttle_thermal_policy_set_default(struct asus_wmi *asus)
{ if (!asus->throttle_thermal_policy_dev) return 0;
result = kstrtou8(buf, 10, &new_mode); if (result < 0) return result;
if (new_mode > PLATFORM_PROFILE_MAX) return -EINVAL;
asus->throttle_thermal_policy_mode = new_mode;
err = throttle_thermal_policy_write(asus); if (err) return err;
/* * Ensure that platform_profile updates userspace with the change to ensure * that platform_profile and throttle_thermal_policy_mode are in sync.
*/
platform_profile_notify(asus->ppdev);
/* * Not an error if a component platform_profile relies on is unavailable * so early return, skipping the setup of platform_profile.
*/ if (!asus->throttle_thermal_policy_dev) return 0;
/* * We need to set the default thermal profile during probe or otherwise * the system will often remain in silent mode, causing low performance.
*/
err = throttle_thermal_policy_set_default(asus); if (err < 0) {
pr_warn("Failed to set default thermal profile\n"); return err;
}
dev_info(dev, "Using throttle_thermal_policy for platform_profile support\n");
asus->ppdev = devm_platform_profile_register(dev, "asus-wmi", asus,
&asus_wmi_platform_profile_ops); if (IS_ERR(asus->ppdev)) {
dev_err(dev, "Failed to register a platform_profile class device\n"); return PTR_ERR(asus->ppdev);
}
staticint read_backlight_power(struct asus_wmi *asus)
{ int ret;
if (asus->driver->quirks->store_backlight_power)
ret = !asus->driver->panel_power; else
ret = asus_wmi_get_devstate_simple(asus,
ASUS_WMI_DEVID_BACKLIGHT);
if (ret < 0) return ret;
return ret ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF;
}
staticint read_brightness_max(struct asus_wmi *asus)
{
u32 retval; int err;
power = read_backlight_power(asus); if (power != -ENODEV && bd->props.power != power) {
ctrl_param = !!(bd->props.power == BACKLIGHT_POWER_ON);
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT,
ctrl_param, NULL); if (asus->driver->quirks->store_backlight_power)
asus->driver->panel_power = bd->props.power;
/* When using scalar brightness, updating the brightness
* will mess with the backlight power */ if (asus->driver->quirks->scalar_panel_brightness) return err;
}
if (asus->driver->quirks->scalar_panel_brightness)
ctrl_param = get_scalar_command(bd); else
ctrl_param = bd->props.brightness;
err = read_screenpad_backlight_power(asus); if (err < 0) return err; /* The device brightness can only be read if powered, so return stored */ if (err == BACKLIGHT_POWER_OFF) return asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN;
power = read_screenpad_backlight_power(asus); if (power < 0) return power;
if (bd->props.power != power) { if (power != BACKLIGHT_POWER_ON) { /* Only brightness > 0 can power it back on */
ctrl_param = asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN;
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT,
ctrl_param, NULL);
} else {
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 0, NULL);
}
} elseif (power == BACKLIGHT_POWER_ON) { /* Only set brightness if powered on or we get invalid/unsync state */
ctrl_param = bd->props.brightness + ASUS_SCREENPAD_BRIGHT_MIN;
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, ctrl_param, NULL);
}
/* Ensure brightness is stored to turn back on with */ if (err == 0)
asus->driver->screenpad_brightness = bd->props.brightness + ASUS_SCREENPAD_BRIGHT_MIN;
staticint asus_screenpad_init(struct asus_wmi *asus)
{ struct backlight_device *bd; struct backlight_properties props; int err, power; int brightness = 0;
power = read_screenpad_backlight_power(asus); if (power < 0) return power;
if (power != BACKLIGHT_POWER_OFF) {
err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &brightness); if (err < 0) return err;
} /* default to an acceptable min brightness on boot if too low */ if (brightness < ASUS_SCREENPAD_BRIGHT_MIN)
brightness = ASUS_SCREENPAD_BRIGHT_DEFAULT;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW; /* ensure this bd is last to be picked */
props.max_brightness = ASUS_SCREENPAD_BRIGHT_MAX - ASUS_SCREENPAD_BRIGHT_MIN;
bd = backlight_device_register("asus_screenpad",
&asus->platform_device->dev, asus,
&asus_screenpad_bl_ops, &props); if (IS_ERR(bd)) {
pr_err("Could not register backlight device\n"); return PTR_ERR(bd);
}
/* INIT enable hotkeys on some models */ if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, &rv))
pr_info("Initialization: %#x\n", rv);
/* We don't know yet what to do with this version... */ if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) {
pr_info("BIOS WMI version: %d.%d\n", rv >> 16, rv & 0xFF);
asus->spec = rv;
}
/* * The SFUN method probably allows the original driver to get the list * of features supported by a given model. For now, 0x0100 or 0x0800 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card. * The significance of others is yet to be found.
*/ if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) {
pr_info("SFUN value: %#x\n", rv);
asus->sfun = rv;
}
/* * Eee PC and Notebooks seems to have different method_id for DSTS, * but it may also be related to the BIOS's SPEC. * Note, on most Eeepc, there is no way to check if a method exist * or note, while on notebooks, they returns 0xFFFFFFFE on failure, * but once again, SPEC may probably be used for that kind of things. * * Additionally at least TUF Gaming series laptops return nothing for * unknown methods, so the detection in this way is not possible. * * There is strong indication that only ACPI WMI devices that have _UID * equal to "ASUSWMI" use DCTS whereas those with "ATK" use DSTS.
*/
wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID); if (!wmi_uid) return -ENODEV;
if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) {
dev_info(dev, "Detected ASUSWMI, use DCTS\n");
asus->dsts_id = ASUS_WMI_METHODID_DCTS;
} else {
dev_info(dev, "Detected %s, not ASUSWMI, use DSTS\n", wmi_uid);
asus->dsts_id = ASUS_WMI_METHODID_DSTS;
}
/* CWAP allow to define the behavior of the Fn+F2 key,
* this method doesn't seems to be present on Eee PCs */ if (asus->driver->quirks->wapf >= 0)
asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
asus->driver->quirks->wapf, NULL);
if (wdrv->detect_quirks)
wdrv->detect_quirks(asus->driver);
err = asus_wmi_platform_init(asus); if (err) goto fail_platform;
if (use_ally_mcu_hack == ASUS_WMI_ALLY_MCU_HACK_INIT) { if (acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE)
&& dmi_check_system(asus_rog_ally_device))
use_ally_mcu_hack = ASUS_WMI_ALLY_MCU_HACK_ENABLED; if (dmi_match(DMI_BOARD_NAME, "RC71")) { /* * These steps ensure the device is in a valid good state, this is * especially important for the Ally 1 after a reboot.
*/
acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE,
ASUS_USB0_PWR_EC0_CSEE_ON);
msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT);
}
}
/* * Work around bios bug - acpi _PTS turns off the wireless led * during suspend. Normally it restores it on resume, but * we should kick it ourselves in case hibernation is aborted.
*/
wlan = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN);
asus_wmi_set_devstate(ASUS_WMI_DEVID_WLAN, wlan, NULL);
}
/* Use only for Ally devices due to the wake_on_ac */ staticstruct acpi_s2idle_dev_ops asus_ally_s2idle_dev_ops = {
.restore = asus_ally_s2idle_restore,
};
staticvoid asus_s2idle_check_register(void)
{ if (acpi_register_lps0_dev(&asus_ally_s2idle_dev_ops))
pr_warn("failed to register LPS0 sleep handler in asus-wmi\n");
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.73Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-26)
¤
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.