Skip to content

Commit 92b8c54

Browse files
Mario Limonciellodvhart
authored andcommitted
platform/x86: dell-wmi-descriptor: split WMI descriptor into it's own driver
All communication on individual GUIDs should occur in separate drivers. Allowing a driver to communicate with the bus to another GUID is just a hack that discourages drivers to adopt the bus model. The information found from the WMI descriptor driver is now exported for use by other drivers. Signed-off-by: Mario Limonciello <[email protected]> Reviewed-by: Edward O'Callaghan <[email protected]> Signed-off-by: Darren Hart (VMware) <[email protected]>
1 parent fa9f924 commit 92b8c54

File tree

6 files changed

+208
-74
lines changed

6 files changed

+208
-74
lines changed

MAINTAINERS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4013,6 +4013,11 @@ M: Pali Rohár <[email protected]>
40134013
S: Maintained
40144014
F: drivers/platform/x86/dell-wmi.c
40154015

4016+
DELL WMI DESCRIPTOR DRIVER
4017+
M: Mario Limonciello <[email protected]>
4018+
S: Maintained
4019+
F: drivers/platform/x86/dell-wmi-descriptor.c
4020+
40164021
DELTA ST MEDIA DRIVER
40174022
M: Hugues Fruchet <[email protected]>
40184023

drivers/platform/x86/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ config DELL_WMI
121121
depends on DMI
122122
depends on INPUT
123123
depends on ACPI_VIDEO || ACPI_VIDEO = n
124+
select DELL_WMI_DESCRIPTOR
124125
select DELL_SMBIOS
125126
select INPUT_SPARSEKMAP
126127
---help---
@@ -129,6 +130,10 @@ config DELL_WMI
129130
To compile this driver as a module, choose M here: the module will
130131
be called dell-wmi.
131132

133+
config DELL_WMI_DESCRIPTOR
134+
tristate
135+
depends on ACPI_WMI
136+
132137
config DELL_WMI_AIO
133138
tristate "WMI Hotkeys for Dell All-In-One series"
134139
depends on ACPI_WMI

drivers/platform/x86/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
1414
obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o
1515
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
1616
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
17+
obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o
1718
obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
1819
obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o
1920
obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Dell WMI descriptor driver
3+
*
4+
* Copyright (C) 2017 Dell Inc. All Rights Reserved.
5+
*
6+
* This program is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 as published
8+
* by the Free Software Foundation.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*/
15+
16+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17+
18+
#include <linux/acpi.h>
19+
#include <linux/list.h>
20+
#include <linux/module.h>
21+
#include <linux/wmi.h>
22+
#include "dell-wmi-descriptor.h"
23+
24+
struct descriptor_priv {
25+
struct list_head list;
26+
u32 interface_version;
27+
u32 size;
28+
};
29+
static LIST_HEAD(wmi_list);
30+
static DEFINE_MUTEX(list_mutex);
31+
32+
bool dell_wmi_get_interface_version(u32 *version)
33+
{
34+
struct descriptor_priv *priv;
35+
bool ret = false;
36+
37+
mutex_lock(&list_mutex);
38+
priv = list_first_entry_or_null(&wmi_list,
39+
struct descriptor_priv,
40+
list);
41+
if (priv) {
42+
*version = priv->interface_version;
43+
ret = true;
44+
}
45+
mutex_unlock(&list_mutex);
46+
return ret;
47+
}
48+
EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version);
49+
50+
bool dell_wmi_get_size(u32 *size)
51+
{
52+
struct descriptor_priv *priv;
53+
bool ret = false;
54+
55+
mutex_lock(&list_mutex);
56+
priv = list_first_entry_or_null(&wmi_list,
57+
struct descriptor_priv,
58+
list);
59+
if (priv) {
60+
*size = priv->size;
61+
ret = true;
62+
}
63+
mutex_unlock(&list_mutex);
64+
return ret;
65+
}
66+
EXPORT_SYMBOL_GPL(dell_wmi_get_size);
67+
68+
/*
69+
* Descriptor buffer is 128 byte long and contains:
70+
*
71+
* Name Offset Length Value
72+
* Vendor Signature 0 4 "DELL"
73+
* Object Signature 4 4 " WMI"
74+
* WMI Interface Version 8 4 <version>
75+
* WMI buffer length 12 4 <length>
76+
*/
77+
static int dell_wmi_descriptor_probe(struct wmi_device *wdev)
78+
{
79+
union acpi_object *obj = NULL;
80+
struct descriptor_priv *priv;
81+
u32 *buffer;
82+
int ret;
83+
84+
obj = wmidev_block_query(wdev, 0);
85+
if (!obj) {
86+
dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
87+
ret = -EIO;
88+
goto out;
89+
}
90+
91+
if (obj->type != ACPI_TYPE_BUFFER) {
92+
dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
93+
ret = -EINVAL;
94+
goto out;
95+
}
96+
97+
/* Although it's not technically a failure, this would lead to
98+
* unexpected behavior
99+
*/
100+
if (obj->buffer.length != 128) {
101+
dev_err(&wdev->dev,
102+
"Dell descriptor buffer has unexpected length (%d)\n",
103+
obj->buffer.length);
104+
ret = -EINVAL;
105+
goto out;
106+
}
107+
108+
buffer = (u32 *)obj->buffer.pointer;
109+
110+
if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
111+
dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
112+
buffer);
113+
ret = -EINVAL;
114+
goto out;
115+
}
116+
117+
if (buffer[2] != 0 && buffer[2] != 1)
118+
dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n",
119+
(unsigned long) buffer[2]);
120+
121+
priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv),
122+
GFP_KERNEL);
123+
124+
priv->interface_version = buffer[2];
125+
priv->size = buffer[3];
126+
ret = 0;
127+
dev_set_drvdata(&wdev->dev, priv);
128+
mutex_lock(&list_mutex);
129+
list_add_tail(&priv->list, &wmi_list);
130+
mutex_unlock(&list_mutex);
131+
132+
dev_dbg(&wdev->dev, "Detected Dell WMI interface version %lu and buffer size %lu\n",
133+
(unsigned long) priv->interface_version,
134+
(unsigned long) priv->size);
135+
136+
out:
137+
kfree(obj);
138+
return ret;
139+
}
140+
141+
static int dell_wmi_descriptor_remove(struct wmi_device *wdev)
142+
{
143+
struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev);
144+
145+
mutex_lock(&list_mutex);
146+
list_del(&priv->list);
147+
mutex_unlock(&list_mutex);
148+
return 0;
149+
}
150+
151+
static const struct wmi_device_id dell_wmi_descriptor_id_table[] = {
152+
{ .guid_string = DELL_WMI_DESCRIPTOR_GUID },
153+
{ },
154+
};
155+
156+
static struct wmi_driver dell_wmi_descriptor_driver = {
157+
.driver = {
158+
.name = "dell-wmi-descriptor",
159+
},
160+
.probe = dell_wmi_descriptor_probe,
161+
.remove = dell_wmi_descriptor_remove,
162+
.id_table = dell_wmi_descriptor_id_table,
163+
};
164+
165+
module_wmi_driver(dell_wmi_descriptor_driver);
166+
167+
MODULE_ALIAS("wmi:" DELL_WMI_DESCRIPTOR_GUID);
168+
MODULE_AUTHOR("Mario Limonciello <[email protected]>");
169+
MODULE_DESCRIPTION("Dell WMI descriptor driver");
170+
MODULE_LICENSE("GPL");
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Dell WMI descriptor driver
3+
*
4+
* Copyright (c) 2017 Dell Inc.
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License version 2 as
8+
* published by the Free Software Foundation.
9+
*/
10+
11+
#ifndef _DELL_WMI_DESCRIPTOR_H_
12+
#define _DELL_WMI_DESCRIPTOR_H_
13+
14+
#include <linux/wmi.h>
15+
16+
#define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
17+
18+
bool dell_wmi_get_interface_version(u32 *version);
19+
bool dell_wmi_get_size(u32 *size);
20+
21+
#endif

drivers/platform/x86/dell-wmi.c

Lines changed: 6 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@
3939
#include <linux/wmi.h>
4040
#include <acpi/video.h>
4141
#include "dell-smbios.h"
42+
#include "dell-wmi-descriptor.h"
4243

4344
MODULE_AUTHOR("Matthew Garrett <[email protected]>");
4445
MODULE_AUTHOR("Pali Rohár <[email protected]>");
4546
MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
4647
MODULE_LICENSE("GPL");
4748

4849
#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
49-
#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
5050

5151
static bool wmi_requires_smbios_request;
5252

@@ -617,75 +617,6 @@ static void dell_wmi_input_destroy(struct wmi_device *wdev)
617617
input_unregister_device(priv->input_dev);
618618
}
619619

620-
/*
621-
* Descriptor buffer is 128 byte long and contains:
622-
*
623-
* Name Offset Length Value
624-
* Vendor Signature 0 4 "DELL"
625-
* Object Signature 4 4 " WMI"
626-
* WMI Interface Version 8 4 <version>
627-
* WMI buffer length 12 4 <length>
628-
*/
629-
static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev)
630-
{
631-
struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
632-
union acpi_object *obj = NULL;
633-
struct wmi_device *desc_dev;
634-
u32 *buffer;
635-
int ret;
636-
637-
desc_dev = wmidev_get_other_guid(wdev, DELL_DESCRIPTOR_GUID);
638-
if (!desc_dev) {
639-
dev_err(&wdev->dev, "Dell WMI descriptor does not exist\n");
640-
return -ENODEV;
641-
}
642-
643-
obj = wmidev_block_query(desc_dev, 0);
644-
if (!obj) {
645-
dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
646-
ret = -EIO;
647-
goto out;
648-
}
649-
650-
if (obj->type != ACPI_TYPE_BUFFER) {
651-
dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
652-
ret = -EINVAL;
653-
goto out;
654-
}
655-
656-
if (obj->buffer.length != 128) {
657-
dev_err(&wdev->dev,
658-
"Dell descriptor buffer has invalid length (%d)\n",
659-
obj->buffer.length);
660-
ret = -EINVAL;
661-
goto out;
662-
}
663-
664-
buffer = (u32 *)obj->buffer.pointer;
665-
666-
if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
667-
dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
668-
buffer);
669-
ret = -EINVAL;
670-
goto out;
671-
}
672-
673-
if (buffer[2] != 0 && buffer[2] != 1)
674-
dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n",
675-
(unsigned long) buffer[2]);
676-
677-
priv->interface_version = buffer[2];
678-
ret = 0;
679-
680-
dev_info(&wdev->dev, "Detected Dell WMI interface version %u\n",
681-
priv->interface_version);
682-
683-
out:
684-
kfree(obj);
685-
put_device(&desc_dev->dev);
686-
return ret;
687-
}
688-
689620
/*
690621
* According to Dell SMBIOS documentation:
691622
*
@@ -721,17 +652,18 @@ static int dell_wmi_events_set_enabled(bool enable)
721652
static int dell_wmi_probe(struct wmi_device *wdev)
722653
{
723654
struct dell_wmi_priv *priv;
724-
int err;
655+
656+
if (!wmi_has_guid(DELL_WMI_DESCRIPTOR_GUID))
657+
return -ENODEV;
725658

726659
priv = devm_kzalloc(
727660
&wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL);
728661
if (!priv)
729662
return -ENOMEM;
730663
dev_set_drvdata(&wdev->dev, priv);
731664

732-
err = dell_wmi_check_descriptor_buffer(wdev);
733-
if (err)
734-
return err;
665+
if (!dell_wmi_get_interface_version(&priv->interface_version))
666+
return -EPROBE_DEFER;
735667

736668
return dell_wmi_input_setup(wdev);
737669
}

0 commit comments

Comments
 (0)