#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);
}
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.