Hi
[...]
>> --- a/drivers/platform/x86/asus-wmi.c
>> +++ b/drivers/platform/x86/asus-wmi.c
>> [...]
>> +/*
>> + * Returns as an error if the method output is not a buffer.
>> Typically this
>
> It seems to me it will simply leave the output buffer uninitialized
> if something
> other than ACPI_TYPE_BUFFER and ACPI_TYPE_INTEGER is encountered and
> return 0.
Oops, see below inline reply:
>
>
>> + * means that the method called is unsupported.
>> + */
>> +static int asus_wmi_evaluate_method_buf(u32 method_id,
>> + u32 arg0, u32 arg1, u8 *ret_buffer)
>> +{
>> + 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;
>> + u32 int_tmp = 0;
>> +
>> + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
>> + &input, &output);
>> +
>> + if (ACPI_FAILURE(status))
>> + return -EIO;
>> +
>> + obj = (union acpi_object *)output.pointer;
>> +
>> + if (obj && obj->type == ACPI_TYPE_INTEGER) {
>> + int_tmp = (u32) obj->integer.value;
>> + if (int_tmp == ASUS_WMI_UNSUPPORTED_METHOD)
>> + return -ENODEV;
>> + return int_tmp;
>
> Is anything known about the possible values? You are later
> using it as if it was an errno (e.g. in `custom_fan_check_present()`).
>
> And `obj` is leaked in both of the previous two returns.
The return for the method we're calling in this patch returns 0 if the
input arg has no match.
>
>
>> + }
>> +
>> + if (obj && obj->type == ACPI_TYPE_BUFFER)
>> + memcpy(ret_buffer, obj->buffer.pointer, obj->buffer.length);
>
> I would suggest you add a "size_t size" argument to this function, and
> return -ENOSPC/-ENODATA depending on whether the returned buffer is
> too
> big/small. Maybe return -ENODATA if `obj` is NULL, too.
Got it. So something like this would be suitable?
if (obj && obj->type == ACPI_TYPE_BUFFER)
if (obj->buffer.length < size)
err = -ENOSPC;
if (!obj->buffer.length)
err = -ENODATA;
if (err) {
kfree(obj);
return err;
}
memcpy(ret_buffer, obj->buffer.pointer, obj->buffer.length);
}
if (obj && obj->type == ACPI_TYPE_INTEGER)
int_tmp = (u32) obj->integer.value;
kfree(obj);
if (int_tmp == ASUS_WMI_UNSUPPORTED_METHOD)
return -ENODEV;
/* There is at least one method that returns a 0 with no buffer */
if (obj == NULL || int_tmp == 0)
return -ENODATA;
return 0;
I had something like the following in mind:
int err = 0;
/* ... */
obj = output.pointer;
if (!obj)
return -ENODATA;
switch (obj->type) {
case ACPI_TYPE_BUFFER:
if (obj->buffer.length < size)
err = -ENODATA;
else if (obj->buffer.length > size)
err = -ENOSPC;
else
memcpy(ret_buffer, obj->buffer.pointer, size);
break;
case ACPI_TYPE_INTEGER:
switch (obj->integer.value) {
case ASUS_WMI_UNSUPPORTED_METHOD:
err = -EOPNOTSUPP;
break;
default:
err = -ENODATA;
break;
}
break;
default:
err = -ENODATA;
break;
}
kfree(obj);
return err;
>
>
>> +
>> + kfree(obj);
>> +
>> + return 0;
>> +}
[...]
>> +/*
>> + * Called only by throttle_thermal_policy_write()
>> + */
>
> Am I correct in thinking that the firmware does not actually
> support specifying fan curves for each mode, only a single one,
> and the fan curve switching is done by this driver when
> the performance mode is changed?
I'm not 100% certain on this. The WMI method 0x00110024 takes an arg
0,1,2 which then returns some factory stored fan profiles, these fit
the profiles of ASUS_THROTTLE_THERMAL_POLICY_*, but with 1 and 2
swapped.
Looking at the SET part, it seems to write to a different location than
where the GET is fetching information.
The, unfortunately, that is not as simple as I initially thought...
Because of the fact there are three sets of curves to get, I thought it
would be good for users to be able to set per profile. I don't think
the set is retained in acpi if the profile is switched.
Do you think it would be best to not have the ability to store per
profile in kernel?
If there was a method to set a fan curve, and one to retrieve it,
I would suggest just exposing that via the pwmN_auto_pointM_{pwm,temp}
attributes on a hwmon device, and that the profile-dependent switching
be implemented somewhere else. As far as I see, there is already
existing infrastructure for integrating such a feature [0]
(but please correct me if I'm wrong).
This would simplify the kernel code, add no new ABI, and
potentially provide greater control over policy for the
user space.
How would I choose which profile get to populate the
initial data with if so?
I assume there isn't a method that can query
the current fan curve (or it is unknown)?
[...]
[0]: https://gitlab.com/asus-linux/asusctl
Best regards,
Barnabás Pőcze