Skip to content

Commit 4ff1a02

Browse files
Rongronggg9ij-intel
authored andcommitted
platform/x86: lenovo-wmi-{capdata,other}: Support multiple Capability Data
The current implementation are heavily bound to capdata01. Rewrite it so that it is suitable to utilize other Capability Data as well. No functional change intended. Signed-off-by: Rong Zhang <i@rong.moe> Reviewed-by: Derek J. Clark <derekjohn.clark@gmail.com> Tested-by: Derek J. Clark <derekjohn.clark@gmail.com> Link: https://patch.msgid.link/20260120182104.163424-4-i@rong.moe Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
1 parent f28d76b commit 4ff1a02

3 files changed

Lines changed: 196 additions & 60 deletions

File tree

drivers/platform/x86/lenovo/wmi-capdata.c

Lines changed: 179 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,29 @@
1212
*
1313
* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
1414
* - Initial implementation (formerly named lenovo-wmi-capdata01)
15+
*
16+
* Copyright (C) 2025 Rong Zhang <i@rong.moe>
17+
* - Unified implementation
1518
*/
1619

20+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
21+
1722
#include <linux/acpi.h>
23+
#include <linux/bug.h>
1824
#include <linux/cleanup.h>
1925
#include <linux/component.h>
2026
#include <linux/container_of.h>
2127
#include <linux/device.h>
28+
#include <linux/dev_printk.h>
29+
#include <linux/err.h>
2230
#include <linux/export.h>
2331
#include <linux/gfp_types.h>
2432
#include <linux/module.h>
2533
#include <linux/mutex.h>
2634
#include <linux/mutex_types.h>
2735
#include <linux/notifier.h>
2836
#include <linux/overflow.h>
37+
#include <linux/stddef.h>
2938
#include <linux/types.h>
3039
#include <linux/wmi.h>
3140

@@ -36,6 +45,23 @@
3645
#define ACPI_AC_CLASS "ac_adapter"
3746
#define ACPI_AC_NOTIFY_STATUS 0x80
3847

48+
enum lwmi_cd_type {
49+
LENOVO_CAPABILITY_DATA_01,
50+
};
51+
52+
#define LWMI_CD_TABLE_ITEM(_type) \
53+
[_type] = { \
54+
.name = #_type, \
55+
.type = _type, \
56+
}
57+
58+
static const struct lwmi_cd_info {
59+
const char *name;
60+
enum lwmi_cd_type type;
61+
} lwmi_cd_table[] = {
62+
LWMI_CD_TABLE_ITEM(LENOVO_CAPABILITY_DATA_01),
63+
};
64+
3965
struct lwmi_cd_priv {
4066
struct notifier_block acpi_nb; /* ACPI events */
4167
struct wmi_device *wdev;
@@ -44,15 +70,63 @@ struct lwmi_cd_priv {
4470

4571
struct cd_list {
4672
struct mutex list_mutex; /* list R/W mutex */
73+
enum lwmi_cd_type type;
4774
u8 count;
48-
struct capdata01 data[];
75+
76+
union {
77+
DECLARE_FLEX_ARRAY(struct capdata01, cd01);
78+
};
4979
};
5080

81+
static struct wmi_driver lwmi_cd_driver;
82+
83+
/**
84+
* lwmi_cd_match() - Match rule for the master driver.
85+
* @dev: Pointer to the capability data parent device.
86+
* @type: Pointer to capability data type (enum lwmi_cd_type *) to match.
87+
*
88+
* Return: int.
89+
*/
90+
static int lwmi_cd_match(struct device *dev, void *type)
91+
{
92+
struct lwmi_cd_priv *priv;
93+
94+
if (dev->driver != &lwmi_cd_driver.driver)
95+
return false;
96+
97+
priv = dev_get_drvdata(dev);
98+
return priv->list->type == *(enum lwmi_cd_type *)type;
99+
}
100+
101+
/**
102+
* lwmi_cd_match_add_all() - Add all match rule for the master driver.
103+
* @master: Pointer to the master device.
104+
* @matchptr: Pointer to the returned component_match pointer.
105+
*
106+
* Adds all component matches to the list stored in @matchptr for the @master
107+
* device. @matchptr must be initialized to NULL.
108+
*/
109+
void lwmi_cd_match_add_all(struct device *master, struct component_match **matchptr)
110+
{
111+
int i;
112+
113+
if (WARN_ON(*matchptr))
114+
return;
115+
116+
for (i = 0; i < ARRAY_SIZE(lwmi_cd_table); i++) {
117+
component_match_add(master, matchptr, lwmi_cd_match,
118+
(void *)&lwmi_cd_table[i].type);
119+
if (IS_ERR(*matchptr))
120+
return;
121+
}
122+
}
123+
EXPORT_SYMBOL_NS_GPL(lwmi_cd_match_add_all, "LENOVO_WMI_CAPDATA");
124+
51125
/**
52126
* lwmi_cd_component_bind() - Bind component to master device.
53127
* @cd_dev: Pointer to the lenovo-wmi-capdata driver parent device.
54128
* @om_dev: Pointer to the lenovo-wmi-other driver parent device.
55-
* @data: cd_list object pointer used to return the capability data.
129+
* @data: lwmi_cd_binder object pointer used to return the capability data.
56130
*
57131
* On lenovo-wmi-other's master bind, provide a pointer to the local capdata
58132
* list. This is used to call lwmi_cd*_get_data to look up attribute data
@@ -64,9 +138,15 @@ static int lwmi_cd_component_bind(struct device *cd_dev,
64138
struct device *om_dev, void *data)
65139
{
66140
struct lwmi_cd_priv *priv = dev_get_drvdata(cd_dev);
67-
struct cd_list **cd_list = data;
141+
struct lwmi_cd_binder *binder = data;
68142

69-
*cd_list = priv->list;
143+
switch (priv->list->type) {
144+
case LENOVO_CAPABILITY_DATA_01:
145+
binder->cd01_list = priv->list;
146+
break;
147+
default:
148+
return -EINVAL;
149+
}
70150

71151
return 0;
72152
}
@@ -75,31 +155,36 @@ static const struct component_ops lwmi_cd_component_ops = {
75155
.bind = lwmi_cd_component_bind,
76156
};
77157

78-
/**
79-
* lwmi_cd01_get_data - Get the data of the specified attribute
158+
/*
159+
* lwmi_cd*_get_data - Get the data of the specified attribute
80160
* @list: The lenovo-wmi-capdata pointer to its cd_list struct.
81161
* @attribute_id: The capdata attribute ID to be found.
82-
* @output: Pointer to a capdata01 struct to return the data.
162+
* @output: Pointer to a capdata* struct to return the data.
83163
*
84-
* Retrieves the capability data 01 struct pointer for the given
85-
* attribute for its specified thermal mode.
164+
* Retrieves the capability data struct pointer for the given
165+
* attribute.
86166
*
87167
* Return: 0 on success, or -EINVAL.
88168
*/
89-
int lwmi_cd01_get_data(struct cd_list *list, u32 attribute_id, struct capdata01 *output)
90-
{
91-
u8 idx;
92-
93-
guard(mutex)(&list->list_mutex);
94-
for (idx = 0; idx < list->count; idx++) {
95-
if (list->data[idx].id != attribute_id)
96-
continue;
97-
memcpy(output, &list->data[idx], sizeof(list->data[idx]));
98-
return 0;
169+
#define DEF_LWMI_CDXX_GET_DATA(_cdxx, _cd_type, _output_t) \
170+
int lwmi_##_cdxx##_get_data(struct cd_list *list, u32 attribute_id, _output_t *output) \
171+
{ \
172+
u8 idx; \
173+
\
174+
if (WARN_ON(list->type != _cd_type)) \
175+
return -EINVAL; \
176+
\
177+
guard(mutex)(&list->list_mutex); \
178+
for (idx = 0; idx < list->count; idx++) { \
179+
if (list->_cdxx[idx].id != attribute_id) \
180+
continue; \
181+
memcpy(output, &list->_cdxx[idx], sizeof(list->_cdxx[idx])); \
182+
return 0; \
183+
} \
184+
return -EINVAL; \
99185
}
100186

101-
return -EINVAL;
102-
}
187+
DEF_LWMI_CDXX_GET_DATA(cd01, LENOVO_CAPABILITY_DATA_01, struct capdata01);
103188
EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CAPDATA");
104189

105190
/**
@@ -112,22 +197,32 @@ EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CAPDATA");
112197
*/
113198
static int lwmi_cd_cache(struct lwmi_cd_priv *priv)
114199
{
200+
size_t size;
115201
int idx;
202+
void *p;
203+
204+
switch (priv->list->type) {
205+
case LENOVO_CAPABILITY_DATA_01:
206+
p = &priv->list->cd01[0];
207+
size = sizeof(priv->list->cd01[0]);
208+
break;
209+
default:
210+
return -EINVAL;
211+
}
116212

117213
guard(mutex)(&priv->list->list_mutex);
118-
for (idx = 0; idx < priv->list->count; idx++) {
214+
for (idx = 0; idx < priv->list->count; idx++, p += size) {
119215
union acpi_object *ret_obj __free(kfree) = NULL;
120216

121217
ret_obj = wmidev_block_query(priv->wdev, idx);
122218
if (!ret_obj)
123219
return -ENODEV;
124220

125221
if (ret_obj->type != ACPI_TYPE_BUFFER ||
126-
ret_obj->buffer.length < sizeof(priv->list->data[idx]))
222+
ret_obj->buffer.length < size)
127223
continue;
128224

129-
memcpy(&priv->list->data[idx], ret_obj->buffer.pointer,
130-
ret_obj->buffer.length);
225+
memcpy(p, ret_obj->buffer.pointer, size);
131226
}
132227

133228
return 0;
@@ -136,20 +231,28 @@ static int lwmi_cd_cache(struct lwmi_cd_priv *priv)
136231
/**
137232
* lwmi_cd_alloc() - Allocate a cd_list struct in drvdata
138233
* @priv: lenovo-wmi-capdata driver data.
234+
* @type: The type of capability data.
139235
*
140236
* Allocate a cd_list struct large enough to contain data from all WMI data
141237
* blocks provided by the interface.
142238
*
143239
* Return: 0 on success, or an error.
144240
*/
145-
static int lwmi_cd_alloc(struct lwmi_cd_priv *priv)
241+
static int lwmi_cd_alloc(struct lwmi_cd_priv *priv, enum lwmi_cd_type type)
146242
{
147243
struct cd_list *list;
148244
size_t list_size;
149245
int count, ret;
150246

151247
count = wmidev_instance_count(priv->wdev);
152-
list_size = struct_size(list, data, count);
248+
249+
switch (type) {
250+
case LENOVO_CAPABILITY_DATA_01:
251+
list_size = struct_size(list, cd01, count);
252+
break;
253+
default:
254+
return -EINVAL;
255+
}
153256

154257
list = devm_kzalloc(&priv->wdev->dev, list_size, GFP_KERNEL);
155258
if (!list)
@@ -159,6 +262,7 @@ static int lwmi_cd_alloc(struct lwmi_cd_priv *priv)
159262
if (ret)
160263
return ret;
161264

265+
list->type = type;
162266
list->count = count;
163267
priv->list = list;
164268

@@ -168,18 +272,19 @@ static int lwmi_cd_alloc(struct lwmi_cd_priv *priv)
168272
/**
169273
* lwmi_cd_setup() - Cache all WMI data block information
170274
* @priv: lenovo-wmi-capdata driver data.
275+
* @type: The type of capability data.
171276
*
172277
* Allocate a cd_list struct large enough to contain data from all WMI data
173278
* blocks provided by the interface. Then loop through each data block and
174279
* cache the data.
175280
*
176281
* Return: 0 on success, or an error code.
177282
*/
178-
static int lwmi_cd_setup(struct lwmi_cd_priv *priv)
283+
static int lwmi_cd_setup(struct lwmi_cd_priv *priv, enum lwmi_cd_type type)
179284
{
180285
int ret;
181286

182-
ret = lwmi_cd_alloc(priv);
287+
ret = lwmi_cd_alloc(priv, type);
183288
if (ret)
184289
return ret;
185290

@@ -235,40 +340,72 @@ static void lwmi_cd01_unregister(void *data)
235340

236341
static int lwmi_cd_probe(struct wmi_device *wdev, const void *context)
237342
{
343+
const struct lwmi_cd_info *info = context;
238344
struct lwmi_cd_priv *priv;
239345
int ret;
240346

347+
if (!info)
348+
return -EINVAL;
349+
241350
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
242351
if (!priv)
243352
return -ENOMEM;
244353

245354
priv->wdev = wdev;
246355
dev_set_drvdata(&wdev->dev, priv);
247356

248-
ret = lwmi_cd_setup(priv);
357+
ret = lwmi_cd_setup(priv, info->type);
249358
if (ret)
250-
return ret;
359+
goto out;
251360

252-
priv->acpi_nb.notifier_call = lwmi_cd01_notifier_call;
361+
switch (info->type) {
362+
case LENOVO_CAPABILITY_DATA_01:
363+
priv->acpi_nb.notifier_call = lwmi_cd01_notifier_call;
253364

254-
ret = register_acpi_notifier(&priv->acpi_nb);
255-
if (ret)
256-
return ret;
365+
ret = register_acpi_notifier(&priv->acpi_nb);
366+
if (ret)
367+
goto out;
257368

258-
ret = devm_add_action_or_reset(&wdev->dev, lwmi_cd01_unregister, &priv->acpi_nb);
259-
if (ret)
260-
return ret;
369+
ret = devm_add_action_or_reset(&wdev->dev, lwmi_cd01_unregister,
370+
&priv->acpi_nb);
371+
if (ret)
372+
goto out;
261373

262-
return component_add(&wdev->dev, &lwmi_cd_component_ops);
374+
ret = component_add(&wdev->dev, &lwmi_cd_component_ops);
375+
goto out;
376+
default:
377+
return -EINVAL;
378+
}
379+
out:
380+
if (ret) {
381+
dev_err(&wdev->dev, "failed to register %s: %d\n",
382+
info->name, ret);
383+
} else {
384+
dev_dbg(&wdev->dev, "registered %s with %u items\n",
385+
info->name, priv->list->count);
386+
}
387+
return ret;
263388
}
264389

265390
static void lwmi_cd_remove(struct wmi_device *wdev)
266391
{
267-
component_del(&wdev->dev, &lwmi_cd_component_ops);
392+
struct lwmi_cd_priv *priv = dev_get_drvdata(&wdev->dev);
393+
394+
switch (priv->list->type) {
395+
case LENOVO_CAPABILITY_DATA_01:
396+
component_del(&wdev->dev, &lwmi_cd_component_ops);
397+
break;
398+
default:
399+
WARN_ON(1);
400+
}
268401
}
269402

403+
#define LWMI_CD_WDEV_ID(_type) \
404+
.guid_string = _type##_GUID, \
405+
.context = &lwmi_cd_table[_type],
406+
270407
static const struct wmi_device_id lwmi_cd_id_table[] = {
271-
{ LENOVO_CAPABILITY_DATA_01_GUID, NULL },
408+
{ LWMI_CD_WDEV_ID(LENOVO_CAPABILITY_DATA_01) },
272409
{}
273410
};
274411

@@ -283,22 +420,10 @@ static struct wmi_driver lwmi_cd_driver = {
283420
.no_singleton = true,
284421
};
285422

286-
/**
287-
* lwmi_cd01_match() - Match rule for the master driver.
288-
* @dev: Pointer to the capability data 01 parent device.
289-
* @data: Unused void pointer for passing match criteria.
290-
*
291-
* Return: int.
292-
*/
293-
int lwmi_cd01_match(struct device *dev, void *data)
294-
{
295-
return dev->driver == &lwmi_cd_driver.driver;
296-
}
297-
EXPORT_SYMBOL_NS_GPL(lwmi_cd01_match, "LENOVO_WMI_CAPDATA");
298-
299423
module_wmi_driver(lwmi_cd_driver);
300424

301425
MODULE_DEVICE_TABLE(wmi, lwmi_cd_id_table);
302426
MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
427+
MODULE_AUTHOR("Rong Zhang <i@rong.moe>");
303428
MODULE_DESCRIPTION("Lenovo Capability Data WMI Driver");
304429
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)