Merge tag 'sound-fix-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[sfrench/cifs-2.6.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 {
111         union acpi_object *obj = NULL;
112         struct descriptor_priv *priv;
113         u32 *buffer;
114         int ret;
115
116         obj = wmidev_block_query(wdev, 0);
117         if (!obj) {
118                 dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
119                 ret = -EIO;
120                 goto out;
121         }
122
123         if (obj->type != ACPI_TYPE_BUFFER) {
124                 dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
125                 ret = -EINVAL;
126                 descriptor_valid = ret;
127                 goto out;
128         }
129
130         /* Although it's not technically a failure, this would lead to
131          * unexpected behavior
132          */
133         if (obj->buffer.length != 128) {
134                 dev_err(&wdev->dev,
135                         "Dell descriptor buffer has unexpected length (%d)\n",
136                         obj->buffer.length);
137                 ret = -EINVAL;
138                 descriptor_valid = ret;
139                 goto out;
140         }
141
142         buffer = (u32 *)obj->buffer.pointer;
143
144         if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
145                 dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
146                         buffer);
147                 ret = -EINVAL;
148                 descriptor_valid = ret;
149                 goto out;
150         }
151         descriptor_valid = 0;
152
153         if (buffer[2] != 0 && buffer[2] != 1)
154                 dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n",
155                         (unsigned long) buffer[2]);
156
157         priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv),
158         GFP_KERNEL);
159
160         if (!priv) {
161                 ret = -ENOMEM;
162                 goto out;
163         }
164
165         priv->interface_version = buffer[2];
166         priv->size = buffer[3];
167         priv->hotfix = buffer[4];
168         ret = 0;
169         dev_set_drvdata(&wdev->dev, priv);
170         mutex_lock(&list_mutex);
171         list_add_tail(&priv->list, &wmi_list);
172         mutex_unlock(&list_mutex);
173
174         dev_dbg(&wdev->dev, "Detected Dell WMI interface version %lu, buffer size %lu, hotfix %lu\n",
175                 (unsigned long) priv->interface_version,
176                 (unsigned long) priv->size,
177                 (unsigned long) priv->hotfix);
178
179 out:
180         kfree(obj);
181         return ret;
182 }
183
184 static int dell_wmi_descriptor_remove(struct wmi_device *wdev)
185 {
186         struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev);
187
188         mutex_lock(&list_mutex);
189         list_del(&priv->list);
190         mutex_unlock(&list_mutex);
191         return 0;
192 }
193
194 static const struct wmi_device_id dell_wmi_descriptor_id_table[] = {
195         { .guid_string = DELL_WMI_DESCRIPTOR_GUID },
196         { },
197 };
198
199 static struct wmi_driver dell_wmi_descriptor_driver = {
200         .driver = {
201                 .name = "dell-wmi-descriptor",
202         },
203         .probe = dell_wmi_descriptor_probe,
204         .remove = dell_wmi_descriptor_remove,
205         .id_table = dell_wmi_descriptor_id_table,
206 };
207
208 module_wmi_driver(dell_wmi_descriptor_driver);
209
210 MODULE_DEVICE_TABLE(wmi, dell_wmi_descriptor_id_table);
211 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
212 MODULE_DESCRIPTION("Dell WMI descriptor driver");
213 MODULE_LICENSE("GPL");