]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/platform/x86/dell-wmi-descriptor.c
platform/x86: wmi: add context argument to the probe function
[linux.git] / drivers / platform / x86 / dell-wmi-descriptor.c
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 #define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
25
26 struct descriptor_priv {
27         struct list_head list;
28         u32 interface_version;
29         u32 size;
30         u32 hotfix;
31 };
32 static int descriptor_valid = -EPROBE_DEFER;
33 static LIST_HEAD(wmi_list);
34 static DEFINE_MUTEX(list_mutex);
35
36 int dell_wmi_get_descriptor_valid(void)
37 {
38         if (!wmi_has_guid(DELL_WMI_DESCRIPTOR_GUID))
39                 return -ENODEV;
40
41         return descriptor_valid;
42 }
43 EXPORT_SYMBOL_GPL(dell_wmi_get_descriptor_valid);
44
45 bool dell_wmi_get_interface_version(u32 *version)
46 {
47         struct descriptor_priv *priv;
48         bool ret = false;
49
50         mutex_lock(&list_mutex);
51         priv = list_first_entry_or_null(&wmi_list,
52                                         struct descriptor_priv,
53                                         list);
54         if (priv) {
55                 *version = priv->interface_version;
56                 ret = true;
57         }
58         mutex_unlock(&list_mutex);
59         return ret;
60 }
61 EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version);
62
63 bool dell_wmi_get_size(u32 *size)
64 {
65         struct descriptor_priv *priv;
66         bool ret = false;
67
68         mutex_lock(&list_mutex);
69         priv = list_first_entry_or_null(&wmi_list,
70                                         struct descriptor_priv,
71                                         list);
72         if (priv) {
73                 *size = priv->size;
74                 ret = true;
75         }
76         mutex_unlock(&list_mutex);
77         return ret;
78 }
79 EXPORT_SYMBOL_GPL(dell_wmi_get_size);
80
81 bool dell_wmi_get_hotfix(u32 *hotfix)
82 {
83         struct descriptor_priv *priv;
84         bool ret = false;
85
86         mutex_lock(&list_mutex);
87         priv = list_first_entry_or_null(&wmi_list,
88                                         struct descriptor_priv,
89                                         list);
90         if (priv) {
91                 *hotfix = priv->hotfix;
92                 ret = true;
93         }
94         mutex_unlock(&list_mutex);
95         return ret;
96 }
97 EXPORT_SYMBOL_GPL(dell_wmi_get_hotfix);
98
99 /*
100  * Descriptor buffer is 128 byte long and contains:
101  *
102  *       Name             Offset  Length  Value
103  * Vendor Signature          0       4    "DELL"
104  * Object Signature          4       4    " WMI"
105  * WMI Interface Version     8       4    <version>
106  * WMI buffer length        12       4    <length>
107  * WMI hotfix number        16       4    <hotfix>
108  */
109 static int dell_wmi_descriptor_probe(struct wmi_device *wdev,
110                                      const void *context)
111 {
112         union acpi_object *obj = NULL;
113         struct descriptor_priv *priv;
114         u32 *buffer;
115         int ret;
116
117         obj = wmidev_block_query(wdev, 0);
118         if (!obj) {
119                 dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
120                 ret = -EIO;
121                 goto out;
122         }
123
124         if (obj->type != ACPI_TYPE_BUFFER) {
125                 dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
126                 ret = -EINVAL;
127                 descriptor_valid = ret;
128                 goto out;
129         }
130
131         /* Although it's not technically a failure, this would lead to
132          * unexpected behavior
133          */
134         if (obj->buffer.length != 128) {
135                 dev_err(&wdev->dev,
136                         "Dell descriptor buffer has unexpected length (%d)\n",
137                         obj->buffer.length);
138                 ret = -EINVAL;
139                 descriptor_valid = ret;
140                 goto out;
141         }
142
143         buffer = (u32 *)obj->buffer.pointer;
144
145         if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
146                 dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
147                         buffer);
148                 ret = -EINVAL;
149                 descriptor_valid = ret;
150                 goto out;
151         }
152         descriptor_valid = 0;
153
154         if (buffer[2] != 0 && buffer[2] != 1)
155                 dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n",
156                         (unsigned long) buffer[2]);
157
158         priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv),
159         GFP_KERNEL);
160
161         if (!priv) {
162                 ret = -ENOMEM;
163                 goto out;
164         }
165
166         priv->interface_version = buffer[2];
167         priv->size = buffer[3];
168         priv->hotfix = buffer[4];
169         ret = 0;
170         dev_set_drvdata(&wdev->dev, priv);
171         mutex_lock(&list_mutex);
172         list_add_tail(&priv->list, &wmi_list);
173         mutex_unlock(&list_mutex);
174
175         dev_dbg(&wdev->dev, "Detected Dell WMI interface version %lu, buffer size %lu, hotfix %lu\n",
176                 (unsigned long) priv->interface_version,
177                 (unsigned long) priv->size,
178                 (unsigned long) priv->hotfix);
179
180 out:
181         kfree(obj);
182         return ret;
183 }
184
185 static int dell_wmi_descriptor_remove(struct wmi_device *wdev)
186 {
187         struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev);
188
189         mutex_lock(&list_mutex);
190         list_del(&priv->list);
191         mutex_unlock(&list_mutex);
192         return 0;
193 }
194
195 static const struct wmi_device_id dell_wmi_descriptor_id_table[] = {
196         { .guid_string = DELL_WMI_DESCRIPTOR_GUID },
197         { },
198 };
199
200 static struct wmi_driver dell_wmi_descriptor_driver = {
201         .driver = {
202                 .name = "dell-wmi-descriptor",
203         },
204         .probe = dell_wmi_descriptor_probe,
205         .remove = dell_wmi_descriptor_remove,
206         .id_table = dell_wmi_descriptor_id_table,
207 };
208
209 module_wmi_driver(dell_wmi_descriptor_driver);
210
211 MODULE_DEVICE_TABLE(wmi, dell_wmi_descriptor_id_table);
212 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
213 MODULE_DESCRIPTION("Dell WMI descriptor driver");
214 MODULE_LICENSE("GPL");