Re: [PATCH] Add IdeaPad WMI Fn Keys driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Philipp,

On 11/10/22 21:02, Philipp Jungkamp wrote:
> On Mon, 2022-09-19 at 08:54 +0100, Hans de Goede wrote:
>> Hi again,
>>
>> On 9/11/22 17:18, Hans de Goede wrote:
>>> Hi Philipp,
>>>
>>> On 9/11/22 18:04, Philipp Jungkamp wrote:
>>>> Create an input device for WMI events corresponding to some
>>>> special
>>>> keys on the 'Lenovo Yoga' line.
>>>>
>>>> This include the 3 keys to the right on the 'Lenovo Yoga9 14IAP7'
>>>> and
>>>> additionally the 'Lenovo Support' and 'Lenovo Favorites' (star
>>>> with 'S'
>>>> inside) in the fn key row as well as the event emitted on 'Fn+R'
>>>> which
>>>> toggles between 60Hz and 90Hz display refresh rate on windows.
>>>>
>>>> Signed-off-by: Philipp Jungkamp <p.jungkamp@xxxxxxx>
>>>> ---
>>>> I found this patch by poking in the DSDT. I have not submitted
>>>> any
>>>> notable patches yet and hope you can help me improve in case I
>>>> make
>>>> unfortunate choices during submission.
>>>
>>> No worries at a first glance (I have not looked at this in any
>>> detail yet) this looks pretty good for a first submission.
>>>
>>> And thank you for contributing to the Linux kernel!
>>>
>>>
>>>> Philipp Jungkamp
>>>>
>>>>  drivers/platform/x86/Kconfig       |  13 +++
>>>>  drivers/platform/x86/Makefile      |   1 +
>>>>  drivers/platform/x86/ideapad-wmi.c | 153
>>>> +++++++++++++++++++++++++++++
>>>>  3 files changed, 167 insertions(+)
>>>>  create mode 100644 drivers/platform/x86/ideapad-wmi.c
>>>>
>>>> diff --git a/drivers/platform/x86/Kconfig
>>>> b/drivers/platform/x86/Kconfig
>>>> index f2f98e942cf2..e7c5148e5cb4 100644
>>>> --- a/drivers/platform/x86/Kconfig
>>>> +++ b/drivers/platform/x86/Kconfig
>>>> @@ -140,6 +140,19 @@ config YOGABOOK_WMI
>>>>           To compile this driver as a module, choose M here: the
>>>> module will
>>>>           be called lenovo-yogabook-wmi.
>>>>
>>>> +config IDEAPAD_WMI
>>>> +       tristate "Lenovo IdeaPad WMI Fn Keys"
>>>> +       depends on ACPI_WMI
>>>> +       depends on INPUT
>>>> +       select INPUT_SPARSEKMAP
>>>> +       help
>>>> +         Say Y here if you want to receive key presses from some
>>>> lenovo
>>>> +         specific keys. (Star Key, Support Key, Virtual
>>>> Background,
>>>> +         Dark Mode Toggle, ...)
>>>> +
>>>> +         To compile this driver as a module, choose M here: the
>>>> module will
>>>> +         be called ideapad-wmi.
>>>> +
>>>>  config ACERHDF
>>>>         tristate "Acer Aspire One temperature and fan driver"
>>>>         depends on ACPI && THERMAL
>>>> diff --git a/drivers/platform/x86/Makefile
>>>> b/drivers/platform/x86/Makefile
>>>> index 5a428caa654a..d8bec884d6bc 100644
>>>> --- a/drivers/platform/x86/Makefile
>>>> +++ b/drivers/platform/x86/Makefile
>>>> @@ -16,6 +16,7 @@ obj-
>>>> $(CONFIG_PEAQ_WMI)                        += peaq-wmi.o
>>>>  obj-$(CONFIG_XIAOMI_WMI)               += xiaomi-wmi.o
>>>>  obj-$(CONFIG_GIGABYTE_WMI)             += gigabyte-wmi.o
>>>>  obj-$(CONFIG_YOGABOOK_WMI)             += lenovo-yogabook-wmi.o
>>>> +obj-$(CONFIG_IDEAPAD_WMI)              += ideapad-wmi.o
>>>>
>>>>  # Acer
>>>>  obj-$(CONFIG_ACERHDF)          += acerhdf.o
>>>> diff --git a/drivers/platform/x86/ideapad-wmi.c
>>>> b/drivers/platform/x86/ideapad-wmi.c
>>>> new file mode 100644
>>>> index 000000000000..38f7b3d0c171
>>>> --- /dev/null
>>>> +++ b/drivers/platform/x86/ideapad-wmi.c
>>>> @@ -0,0 +1,153 @@
>>>> +// SPDX-License-Identifier: GPL-2.0-or-later
>>>> +/*
>>>> + * ideapad-wmi.c - Ideapad WMI fn keys driver
>>>> + *
>>>> + * Copyright (C) 2022 Philipp Jungkamp <p.jungkamp@xxxxxxx>
>>>> + */
>>>> +
>>>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>>>> +
>>>> +#include <linux/acpi.h>
>>>> +#include <linux/input.h>
>>>> +#include <linux/input/sparse-keymap.h>
>>>> +#include <linux/list.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/wmi.h>
>>>> +
>>>> +#define IDEAPAD_FN_KEY_EVENT_GUID      "8FC0DE0C-B4E4-43FD-B0F3-
>>>> 8871711C1294"
>>>
>>> At a first hunch (basically huh, don't we have a driver for that
>>> already?) I grepped through the kernel sources and found:
>>>
>>> drivers/platform/x86/ideapad-laptop.c
>>>
>>> can you see if you can make things work with that driver?
>>
>> So I have taken a quick look at this and it seems to me that this
>> really should be able to work with the existing ideapad-laptop.c code
>> ?
>>
>> For starters you could add a debug printk / dev_info to this block,
>>
>> #if IS_ENABLED(CONFIG_ACPI_WMI)
>>         for (i = 0; i < ARRAY_SIZE(ideapad_wmi_fnesc_events); i++) {
>>                 status =
>> wmi_install_notify_handler(ideapad_wmi_fnesc_events[i],
>>                                                    
>> ideapad_wmi_notify, priv);
>>                 if (ACPI_SUCCESS(status)) {
>>                         priv->fnesc_guid =
>> ideapad_wmi_fnesc_events[i];
>>                         break;
>>                 }
>>         }
>>
>>         if (ACPI_FAILURE(status) && status != AE_NOT_EXIST) {
>>                 err = -EIO;
>>                 goto notification_failed_wmi;
>>         }
>> #endif
>>
>> checking which event GUID ideapad-laptop binds to on your laptop.
>> Assuming that
>> it does bind to the GUID this driver is binding to too, then it would
>> be
>> a matter of extending the existing ideapad_wmi_notify() to do the
>> same
>> as your notify function in this stand-alone driver. Note you can get
>> the
>> the equivalend of the union acpi_object *data argument in your wmi
>> handler
>> by calling wmi_get_event_data().
>>
>> Regards,
>>
>> Hans
>>
> 
> Hello Hans,
> 
> I did actually start by doing that.
> The problem lies with the wmi_get_event_data() function not working
> for my ACPI.
> 
> wmi_get_event_data() takes the event notify_id as input and is supposed
> to give the data associated with the event back. This occures by
> calling _WED on the first WMI block that contains said notify_id.
> 
> drivers/platform/x86/wmi.c:657:
> 	list_for_each_entry(wblock, &wmi_block_list, list) {
> 		struct guid_block *gblock = &wblock->gblock;
> 
> 		if ((gblock->flags & ACPI_WMI_EVENT) && gblock-
>> notify_id == event)
> 			return get_event_data(wblock, out);
> 	}
> 
> The ACPI of the Lenovo Yoga 9 14IAP7 contains multiple WMI event
> blocks hat contain the same notify_id 0xD0.
> 
> Here are two of the four WMI objects found in the DSDT:
> 
> in _WDG of block WMIY:
> 	06129D99-6083-4164-81AD-F092F9D773A6:
> 		notify_id: 0xD0
> 		instance_count: 1
> 		flags: 0x8 ACPI_WMI_EVENT
> 
> in _WDG of block WMIU:
> 	8FC0DE0C-B4E4-43FD-B0F3-8871711C1294:
> 		notify_id: 0xD0
> 		instance_count: 1
> 		flags: 0x8 ACPI_WMI_EVENT
> 
> These event block belong to different WMI devices and report
> unrelated values from different _WED handlers. WMIY for example
> triggers its event on "mode changes", e.g. laptop/tablet/tent based
> on the accelometers/hinge.
> WMIU is the WMI block with the event which reports the special keys
> I'm interested in.
> 
> WMIY comes before WMIU in the wmi_block_list.
> Calling wmi_get_event_data() in ideapad_wmi_notify() calls the wrong
> _WED (the one of WMIY) and thus returns the wrong event data.
> 
> I noticed that the wmi_driver interface does not incur the problem
> with the event because it binds to a wmi_block and calls the _WED
> directly without searching through other WMI devices.

Right, I see in that case I guess that using the wmi_driver
interface does make sense.

> I thought of changing the signature of wmi_get_event_data() to include
> the GUID of the correct WMI block, but chose wmi_driver instead.
> Would you consider adding a wmi_get_event_data_for_guid() function to
> the wmi module and using that in the ideapad_wmi_notify function to be
> a better solution than the one in the patch presented here?

So the problem is that ideapad-laptop is using the old interface
and it does:

                status = wmi_install_notify_handler(ideapad_wmi_fnesc_events[i],
                                                    ideapad_wmi_notify, priv);

And then the code to call that notifier in wmi.c goes like this:

        if (test_bit(WMI_PROBED, &wblock->flags) && wblock->dev.dev.driver) {
                struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
                struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL };
                acpi_status status;

                if (!driver->no_notify_data) {
                        status = get_event_data(wblock, &evdata);
                        if (ACPI_FAILURE(status)) {
                                dev_warn(&wblock->dev.dev, "failed to get event data\n");
                                return;
                        }
                }

                if (driver->notify)
                        driver->notify(&wblock->dev, evdata.pointer);

                kfree(evdata.pointer);
        } else if (wblock->handler) {
                /* Legacy handler */
                wblock->handler(event, wblock->handler_data);
        }

So if we move ahead with your new style wmi-driver for GUID 
8FC0DE0C-B4E4-43FD-B0F3-8871711C1294.

Then we hit the "true" path of the if and the legacy handler
(registered by ideapad-laptop) will never trigger.

I guess you could argue that this is a feature since your
new-style driver "replaces" ideapad-laptop, but ending up with
2 drivers for ideapad laptops / for GUID
8FC0DE0C-B4E4-43FD-B0F3-8871711C1294 and them having them have
to coordinate to decide which one to load sounds less then ideal.

So I think that your idea to add a wmi_get_event_data_for_guid()
function to wmi.c is actually a good idea.

Can you give that a try please and see how that goes ?

Regards,

Hans






[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux