10
10
#include <linux/acpi.h>
11
11
#include <linux/bits.h>
12
12
#include <linux/bitfield.h>
13
+ #include <linux/cleanup.h>
13
14
#include <linux/debugfs.h>
14
15
#include <linux/device.h>
15
16
#include <linux/device/driver.h>
16
17
#include <linux/errno.h>
17
18
#include <linux/hwmon.h>
18
19
#include <linux/kernel.h>
19
20
#include <linux/module.h>
21
+ #include <linux/mutex.h>
20
22
#include <linux/printk.h>
21
23
#include <linux/rwsem.h>
22
24
#include <linux/types.h>
@@ -76,8 +78,13 @@ enum msi_wmi_platform_method {
76
78
MSI_PLATFORM_GET_WMI = 0x1d ,
77
79
};
78
80
79
- struct msi_wmi_platform_debugfs_data {
81
+ struct msi_wmi_platform_data {
80
82
struct wmi_device * wdev ;
83
+ struct mutex wmi_lock ; /* Necessary when calling WMI methods */
84
+ };
85
+
86
+ struct msi_wmi_platform_debugfs_data {
87
+ struct msi_wmi_platform_data * data ;
81
88
enum msi_wmi_platform_method method ;
82
89
struct rw_semaphore buffer_lock ; /* Protects debugfs buffer */
83
90
size_t length ;
@@ -132,8 +139,9 @@ static int msi_wmi_platform_parse_buffer(union acpi_object *obj, u8 *output, siz
132
139
return 0 ;
133
140
}
134
141
135
- static int msi_wmi_platform_query (struct wmi_device * wdev , enum msi_wmi_platform_method method ,
136
- u8 * input , size_t input_length , u8 * output , size_t output_length )
142
+ static int msi_wmi_platform_query (struct msi_wmi_platform_data * data ,
143
+ enum msi_wmi_platform_method method , u8 * input ,
144
+ size_t input_length , u8 * output , size_t output_length )
137
145
{
138
146
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER , NULL };
139
147
struct acpi_buffer in = {
@@ -147,9 +155,15 @@ static int msi_wmi_platform_query(struct wmi_device *wdev, enum msi_wmi_platform
147
155
if (!input_length || !output_length )
148
156
return - EINVAL ;
149
157
150
- status = wmidev_evaluate_method (wdev , 0x0 , method , & in , & out );
151
- if (ACPI_FAILURE (status ))
152
- return - EIO ;
158
+ /*
159
+ * The ACPI control method responsible for handling the WMI method calls
160
+ * is not thread-safe. Because of this we have to do the locking ourself.
161
+ */
162
+ scoped_guard (mutex , & data -> wmi_lock ) {
163
+ status = wmidev_evaluate_method (data -> wdev , 0x0 , method , & in , & out );
164
+ if (ACPI_FAILURE (status ))
165
+ return - EIO ;
166
+ }
153
167
154
168
obj = out .pointer ;
155
169
if (!obj )
@@ -170,13 +184,13 @@ static umode_t msi_wmi_platform_is_visible(const void *drvdata, enum hwmon_senso
170
184
static int msi_wmi_platform_read (struct device * dev , enum hwmon_sensor_types type , u32 attr ,
171
185
int channel , long * val )
172
186
{
173
- struct wmi_device * wdev = dev_get_drvdata (dev );
187
+ struct msi_wmi_platform_data * data = dev_get_drvdata (dev );
174
188
u8 input [32 ] = { 0 };
175
189
u8 output [32 ];
176
190
u16 value ;
177
191
int ret ;
178
192
179
- ret = msi_wmi_platform_query (wdev , MSI_PLATFORM_GET_FAN , input , sizeof (input ), output ,
193
+ ret = msi_wmi_platform_query (data , MSI_PLATFORM_GET_FAN , input , sizeof (input ), output ,
180
194
sizeof (output ));
181
195
if (ret < 0 )
182
196
return ret ;
@@ -231,7 +245,7 @@ static ssize_t msi_wmi_platform_write(struct file *fp, const char __user *input,
231
245
return ret ;
232
246
233
247
down_write (& data -> buffer_lock );
234
- ret = msi_wmi_platform_query (data -> wdev , data -> method , payload , data -> length , data -> buffer ,
248
+ ret = msi_wmi_platform_query (data -> data , data -> method , payload , data -> length , data -> buffer ,
235
249
data -> length );
236
250
up_write (& data -> buffer_lock );
237
251
@@ -277,17 +291,17 @@ static void msi_wmi_platform_debugfs_remove(void *data)
277
291
debugfs_remove_recursive (dir );
278
292
}
279
293
280
- static void msi_wmi_platform_debugfs_add (struct wmi_device * wdev , struct dentry * dir ,
294
+ static void msi_wmi_platform_debugfs_add (struct msi_wmi_platform_data * drvdata , struct dentry * dir ,
281
295
const char * name , enum msi_wmi_platform_method method )
282
296
{
283
297
struct msi_wmi_platform_debugfs_data * data ;
284
298
struct dentry * entry ;
285
299
286
- data = devm_kzalloc (& wdev -> dev , sizeof (* data ), GFP_KERNEL );
300
+ data = devm_kzalloc (& drvdata -> wdev -> dev , sizeof (* data ), GFP_KERNEL );
287
301
if (!data )
288
302
return ;
289
303
290
- data -> wdev = wdev ;
304
+ data -> data = drvdata ;
291
305
data -> method = method ;
292
306
init_rwsem (& data -> buffer_lock );
293
307
@@ -298,90 +312,91 @@ static void msi_wmi_platform_debugfs_add(struct wmi_device *wdev, struct dentry
298
312
299
313
entry = debugfs_create_file (name , 0600 , dir , data , & msi_wmi_platform_debugfs_fops );
300
314
if (IS_ERR (entry ))
301
- devm_kfree (& wdev -> dev , data );
315
+ devm_kfree (& drvdata -> wdev -> dev , data );
302
316
}
303
317
304
- static void msi_wmi_platform_debugfs_init (struct wmi_device * wdev )
318
+ static void msi_wmi_platform_debugfs_init (struct msi_wmi_platform_data * data )
305
319
{
306
320
struct dentry * dir ;
307
321
char dir_name [64 ];
308
322
int ret , method ;
309
323
310
- scnprintf (dir_name , ARRAY_SIZE (dir_name ), "%s-%s" , DRIVER_NAME , dev_name (& wdev -> dev ));
324
+ scnprintf (dir_name , ARRAY_SIZE (dir_name ), "%s-%s" , DRIVER_NAME , dev_name (& data -> wdev -> dev ));
311
325
312
326
dir = debugfs_create_dir (dir_name , NULL );
313
327
if (IS_ERR (dir ))
314
328
return ;
315
329
316
- ret = devm_add_action_or_reset (& wdev -> dev , msi_wmi_platform_debugfs_remove , dir );
330
+ ret = devm_add_action_or_reset (& data -> wdev -> dev , msi_wmi_platform_debugfs_remove , dir );
317
331
if (ret < 0 )
318
332
return ;
319
333
320
334
for (method = MSI_PLATFORM_GET_PACKAGE ; method <= MSI_PLATFORM_GET_WMI ; method ++ )
321
- msi_wmi_platform_debugfs_add (wdev , dir , msi_wmi_platform_debugfs_names [method - 1 ],
335
+ msi_wmi_platform_debugfs_add (data , dir , msi_wmi_platform_debugfs_names [method - 1 ],
322
336
method );
323
337
}
324
338
325
- static int msi_wmi_platform_hwmon_init (struct wmi_device * wdev )
339
+ static int msi_wmi_platform_hwmon_init (struct msi_wmi_platform_data * data )
326
340
{
327
341
struct device * hdev ;
328
342
329
- hdev = devm_hwmon_device_register_with_info (& wdev -> dev , "msi_wmi_platform" , wdev ,
343
+ hdev = devm_hwmon_device_register_with_info (& data -> wdev -> dev , "msi_wmi_platform" , data ,
330
344
& msi_wmi_platform_chip_info , NULL );
331
345
332
346
return PTR_ERR_OR_ZERO (hdev );
333
347
}
334
348
335
- static int msi_wmi_platform_ec_init (struct wmi_device * wdev )
349
+ static int msi_wmi_platform_ec_init (struct msi_wmi_platform_data * data )
336
350
{
337
351
u8 input [32 ] = { 0 };
338
352
u8 output [32 ];
339
353
u8 flags ;
340
354
int ret ;
341
355
342
- ret = msi_wmi_platform_query (wdev , MSI_PLATFORM_GET_EC , input , sizeof (input ), output ,
356
+ ret = msi_wmi_platform_query (data , MSI_PLATFORM_GET_EC , input , sizeof (input ), output ,
343
357
sizeof (output ));
344
358
if (ret < 0 )
345
359
return ret ;
346
360
347
361
flags = output [MSI_PLATFORM_EC_FLAGS_OFFSET ];
348
362
349
- dev_dbg (& wdev -> dev , "EC RAM version %lu.%lu\n" ,
363
+ dev_dbg (& data -> wdev -> dev , "EC RAM version %lu.%lu\n" ,
350
364
FIELD_GET (MSI_PLATFORM_EC_MAJOR_MASK , flags ),
351
365
FIELD_GET (MSI_PLATFORM_EC_MINOR_MASK , flags ));
352
- dev_dbg (& wdev -> dev , "EC firmware version %.28s\n" ,
366
+ dev_dbg (& data -> wdev -> dev , "EC firmware version %.28s\n" ,
353
367
& output [MSI_PLATFORM_EC_VERSION_OFFSET ]);
354
368
355
369
if (!(flags & MSI_PLATFORM_EC_IS_TIGERLAKE )) {
356
370
if (!force )
357
371
return - ENODEV ;
358
372
359
- dev_warn (& wdev -> dev , "Loading on a non-Tigerlake platform\n" );
373
+ dev_warn (& data -> wdev -> dev , "Loading on a non-Tigerlake platform\n" );
360
374
}
361
375
362
376
return 0 ;
363
377
}
364
378
365
- static int msi_wmi_platform_init (struct wmi_device * wdev )
379
+ static int msi_wmi_platform_init (struct msi_wmi_platform_data * data )
366
380
{
367
381
u8 input [32 ] = { 0 };
368
382
u8 output [32 ];
369
383
int ret ;
370
384
371
- ret = msi_wmi_platform_query (wdev , MSI_PLATFORM_GET_WMI , input , sizeof (input ), output ,
385
+ ret = msi_wmi_platform_query (data , MSI_PLATFORM_GET_WMI , input , sizeof (input ), output ,
372
386
sizeof (output ));
373
387
if (ret < 0 )
374
388
return ret ;
375
389
376
- dev_dbg (& wdev -> dev , "WMI interface version %u.%u\n" ,
390
+ dev_dbg (& data -> wdev -> dev , "WMI interface version %u.%u\n" ,
377
391
output [MSI_PLATFORM_WMI_MAJOR_OFFSET ],
378
392
output [MSI_PLATFORM_WMI_MINOR_OFFSET ]);
379
393
380
394
if (output [MSI_PLATFORM_WMI_MAJOR_OFFSET ] != MSI_WMI_PLATFORM_INTERFACE_VERSION ) {
381
395
if (!force )
382
396
return - ENODEV ;
383
397
384
- dev_warn (& wdev -> dev , "Loading despite unsupported WMI interface version (%u.%u)\n" ,
398
+ dev_warn (& data -> wdev -> dev ,
399
+ "Loading despite unsupported WMI interface version (%u.%u)\n" ,
385
400
output [MSI_PLATFORM_WMI_MAJOR_OFFSET ],
386
401
output [MSI_PLATFORM_WMI_MINOR_OFFSET ]);
387
402
}
@@ -391,19 +406,31 @@ static int msi_wmi_platform_init(struct wmi_device *wdev)
391
406
392
407
static int msi_wmi_platform_probe (struct wmi_device * wdev , const void * context )
393
408
{
409
+ struct msi_wmi_platform_data * data ;
394
410
int ret ;
395
411
396
- ret = msi_wmi_platform_init (wdev );
412
+ data = devm_kzalloc (& wdev -> dev , sizeof (* data ), GFP_KERNEL );
413
+ if (!data )
414
+ return - ENOMEM ;
415
+
416
+ data -> wdev = wdev ;
417
+ dev_set_drvdata (& wdev -> dev , data );
418
+
419
+ ret = devm_mutex_init (& wdev -> dev , & data -> wmi_lock );
420
+ if (ret < 0 )
421
+ return ret ;
422
+
423
+ ret = msi_wmi_platform_init (data );
397
424
if (ret < 0 )
398
425
return ret ;
399
426
400
- ret = msi_wmi_platform_ec_init (wdev );
427
+ ret = msi_wmi_platform_ec_init (data );
401
428
if (ret < 0 )
402
429
return ret ;
403
430
404
- msi_wmi_platform_debugfs_init (wdev );
431
+ msi_wmi_platform_debugfs_init (data );
405
432
406
- return msi_wmi_platform_hwmon_init (wdev );
433
+ return msi_wmi_platform_hwmon_init (data );
407
434
}
408
435
409
436
static const struct wmi_device_id msi_wmi_platform_id_table [] = {
0 commit comments