Re: [PATCH v4 4/6 RESEND] platform/x86: Add Lenovo Capability Data 01 WMI Driver

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

 



Am 02.04.25 um 22:47 schrieb Derek John Clark:

On Wed, Mar 26, 2025 at 6:29 PM Armin Wolf <W_Armin@xxxxxx> wrote:
Am 17.03.25 um 15:43 schrieb Derek J. Clark:

Adds lenovo-wmi-capdata01 driver which provides the
LENOVO_CAPABILITY_DATA_01 WMI data block that comes on "Other Mode"
enabled hardware. Provides an interface for querying if a given
attribute is supported by the hardware, as well as its default_value,
max_value, min_value, and step increment.

Signed-off-by: Derek J. Clark <derekjohn.clark@xxxxxxxxx>
---
v4:
   - Make driver data a private struct, remove references from Other Mode
     driver.
   - Don't cache data at device initialization. Instead, on component bind,
     cache the data on a member variable of the Other Mode driver data
     passed as a void pointer.
   - Add header file for capdata01 structs.
   - Add new struct to pass capdata01 array data and array length to Other
     Mode.
v3:
- Add as component to lenovo-wmi-other driver.
v2:
- Use devm_kmalloc to ensure driver can be instanced, remove global
    reference.
- Ensure reverse Christmas tree for all variable declarations.
- Remove extra whitespace.
- Use guard(mutex) in all mutex instances, global mutex.
- Use pr_fmt instead of adding the driver name to each pr_err.
- Remove noisy pr_info usage.
- Rename capdata_wmi to lenovo_wmi_cd01_priv and cd01_wmi to priv.
- Use list to get the lenovo_wmi_cd01_priv instance in
    lenovo_wmi_capdata01_get as none of the data provided by the macros
    that will use it can pass a member of the struct for use in
    container_of.
---
   MAINTAINERS                                 |   2 +
   drivers/platform/x86/Kconfig                |   4 +
   drivers/platform/x86/Makefile               |   1 +
   drivers/platform/x86/lenovo-wmi-capdata01.c | 136 ++++++++++++++++++++
   drivers/platform/x86/lenovo-wmi-capdata01.h |  29 +++++
   5 files changed, 172 insertions(+)
   create mode 100644 drivers/platform/x86/lenovo-wmi-capdata01.c
   create mode 100644 drivers/platform/x86/lenovo-wmi-capdata01.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 6dde75922aaf..56ead241a053 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13164,6 +13164,8 @@ L:    platform-driver-x86@xxxxxxxxxxxxxxx
   S:  Maintained
   F:  Documentation/wmi/devices/lenovo-wmi-gamezone.rst
   F:  Documentation/wmi/devices/lenovo-wmi-other.rst
+F:   drivers/platform/x86/lenovo-wmi-capdata01.c
+F:   drivers/platform/x86/lenovo-wmi-capdata01.h
   F:  drivers/platform/x86/lenovo-wmi-events.c
   F:  drivers/platform/x86/lenovo-wmi-events.h
   F:  drivers/platform/x86/lenovo-wmi-helpers.c
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 13b8f4ac5dc5..64663667f0cb 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -467,6 +467,10 @@ config LENOVO_WMI_HELPERS
       tristate
       depends on ACPI_WMI

+config LENOVO_WMI_DATA01
+     tristate
+     depends on ACPI_WMI
+
   config IDEAPAD_LAPTOP
       tristate "Lenovo IdeaPad Laptop Extras"
       depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index fc039839286a..7a35c77221b7 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_THINKPAD_LMI)  += think-lmi.o
   obj-$(CONFIG_YOGABOOK)              += lenovo-yogabook.o
   obj-$(CONFIG_YT2_1380)              += lenovo-yoga-tab2-pro-1380-fastcharger.o
   obj-$(CONFIG_LENOVO_WMI_CAMERA)     += lenovo-wmi-camera.o
+obj-$(CONFIG_LENOVO_WMI_DATA01)      += lenovo-wmi-capdata01.o
   obj-$(CONFIG_LENOVO_WMI_EVENTS)     += lenovo-wmi-events.o
   obj-$(CONFIG_LENOVO_WMI_HELPERS)    += lenovo-wmi-helpers.o

diff --git a/drivers/platform/x86/lenovo-wmi-capdata01.c b/drivers/platform/x86/lenovo-wmi-capdata01.c
new file mode 100644
index 000000000000..b6876611ffd9
--- /dev/null
+++ b/drivers/platform/x86/lenovo-wmi-capdata01.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LENOVO_CAPABILITY_DATA_01 WMI data block driver. This interface provides
+ * information on tunable attributes used by the "Other Mode" WMI interface,
+ * including if it is supported by the hardware, the default_value, max_value,
+ * min_value, and step increment.
+ *
+ * Copyright(C) 2025 Derek J. Clark <derekjohn.clark@xxxxxxxxx>
+ */
+
+#include <linux/cleanup.h>
+#include <linux/component.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/gfp_types.h>
+#include <linux/types.h>
+#include <linux/wmi.h>
+#include "lenovo-wmi-capdata01.h"
Hi,

please also include linux/acpi.h, linux/export.h and linux/module.h.

+
+/* Interface GUIDs */
+#define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154"
+
+struct lwmi_cd01_priv {
+     struct wmi_device *wdev;
+};
+
+/*
/* -> /**

+ * lenovo_cd01_component_bind() - On master bind, caches all capability data on
+ * the master device.
+ * @cd01_dev: Pointer to the capability data 01 parent device.
+ * @om_dev: Pointer to the other mode parent device.
+ * @data: capdata01_list object pointer to return the capability data with.
+ *
+ * Returns: 0, or an error.
+ */
+static int lenovo_cd01_component_bind(struct device *cd01_dev,
+                                   struct device *om_dev, void *data)
+{
+     struct lwmi_cd01_priv *priv = dev_get_drvdata(cd01_dev);
+     int count, idx;
+
+     if (!priv)
+             return -ENODEV;
This check is unnecessary, please drop.

Acked

+
+     count = wmidev_instance_count(priv->wdev);
+
+     if (count == 0)
+             return -EINVAL;
The WMI driver core already ensures that WMI devices with 0 instances are
rejected. Please drop this check.

Good to know, thanks.

+
+     ((struct cd01_list *)data)->count = count;
+     ((struct cd01_list *)data)->data = devm_kmalloc_array(om_dev, count,
+                                                           sizeof(struct capdata01 *),
+                                                           GFP_KERNEL);
Two things:

   - using a local variable with a type of struct cd01_list * results in cleaner source code here

   - using devres is not possible inside the component callbacks, since the lifetime of the component
     device is not necessarily tied to the lifetime of the underlying device.

I suggest you move the whole WMI data querying into lwmi_cd01_probe(), because then you can keep using
devres.

Doing this in probe() puts the list on lwmi_cd01_priv. Should I copy
that data on bind, or pass back a pointer to the cd01 device struct
and use an exported function on cd01 and dev_get_drvdata to access
priv->list->data[idx] when needed? I prefer the latter as this avoids
needing to do devm memory allocation in component/master binds, then I
can check for NULL when accessing and clear the pointer on
master_unbind to avoid calling to a removed device driver.

Passing a pointer is OK, but please only pass a pointer to the struct cd01_list itself,
not the full cd01 device struct. Maybe you can also add a comment explaining that this
pointer will become invalid when unbinding from the component.

Explicitly NULL-ing this pointer upon unbinding seems unnecessary to me.

(snip)

+struct cd01_list {
+     struct capdata01 **data;
+     int count;
+};
In order to save memory you could try something like this:

struct cd01_list {
         size_t count;
         struct capdata01 data[];
};

This way you

1. Avoid the memory fragmentation resulting from multiple memory allocations.

2. Omit two pointers when accessing the data.

You can use struct_size() from linux/overflow.h to calculate the size of such
an array with a trailing flexible array.

Thanks,
Armin Wolf

I think I have this part working in my branch. Using devm_kzalloc also
allows me to omit manually setting NULL in a few cases, which is
cleaner. Is it preferred to use struct_size() directly in the
devm_kzalloc call, or create a separate `size` variable to set the
result to and pass that into the function?

- Derek

In most cases using struct_size() directly inside the devm_kzalloc() call is preferred.

Thanks,
Armin Wolf

+
+int lwmi_cd01_match(struct device *dev, void *data);
+
+#endif /* !_LENOVO_WMI_CAPDATA01_H_ */





[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux