0001
0002
0003
0004
0005
0006
0007
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009
0010 #include <linux/acpi.h>
0011 #include <linux/list.h>
0012 #include <linux/module.h>
0013 #include <linux/wmi.h>
0014 #include "dell-wmi-descriptor.h"
0015
0016 #define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
0017
0018 struct descriptor_priv {
0019 struct list_head list;
0020 u32 interface_version;
0021 u32 size;
0022 u32 hotfix;
0023 };
0024 static int descriptor_valid = -EPROBE_DEFER;
0025 static LIST_HEAD(wmi_list);
0026 static DEFINE_MUTEX(list_mutex);
0027
0028 int dell_wmi_get_descriptor_valid(void)
0029 {
0030 if (!wmi_has_guid(DELL_WMI_DESCRIPTOR_GUID))
0031 return -ENODEV;
0032
0033 return descriptor_valid;
0034 }
0035 EXPORT_SYMBOL_GPL(dell_wmi_get_descriptor_valid);
0036
0037 bool dell_wmi_get_interface_version(u32 *version)
0038 {
0039 struct descriptor_priv *priv;
0040 bool ret = false;
0041
0042 mutex_lock(&list_mutex);
0043 priv = list_first_entry_or_null(&wmi_list,
0044 struct descriptor_priv,
0045 list);
0046 if (priv) {
0047 *version = priv->interface_version;
0048 ret = true;
0049 }
0050 mutex_unlock(&list_mutex);
0051 return ret;
0052 }
0053 EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version);
0054
0055 bool dell_wmi_get_size(u32 *size)
0056 {
0057 struct descriptor_priv *priv;
0058 bool ret = false;
0059
0060 mutex_lock(&list_mutex);
0061 priv = list_first_entry_or_null(&wmi_list,
0062 struct descriptor_priv,
0063 list);
0064 if (priv) {
0065 *size = priv->size;
0066 ret = true;
0067 }
0068 mutex_unlock(&list_mutex);
0069 return ret;
0070 }
0071 EXPORT_SYMBOL_GPL(dell_wmi_get_size);
0072
0073 bool dell_wmi_get_hotfix(u32 *hotfix)
0074 {
0075 struct descriptor_priv *priv;
0076 bool ret = false;
0077
0078 mutex_lock(&list_mutex);
0079 priv = list_first_entry_or_null(&wmi_list,
0080 struct descriptor_priv,
0081 list);
0082 if (priv) {
0083 *hotfix = priv->hotfix;
0084 ret = true;
0085 }
0086 mutex_unlock(&list_mutex);
0087 return ret;
0088 }
0089 EXPORT_SYMBOL_GPL(dell_wmi_get_hotfix);
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101 static int dell_wmi_descriptor_probe(struct wmi_device *wdev,
0102 const void *context)
0103 {
0104 union acpi_object *obj = NULL;
0105 struct descriptor_priv *priv;
0106 u32 *buffer;
0107 int ret;
0108
0109 obj = wmidev_block_query(wdev, 0);
0110 if (!obj) {
0111 dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
0112 ret = -EIO;
0113 goto out;
0114 }
0115
0116 if (obj->type != ACPI_TYPE_BUFFER) {
0117 dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
0118 ret = -EINVAL;
0119 descriptor_valid = ret;
0120 goto out;
0121 }
0122
0123
0124
0125
0126 if (obj->buffer.length != 128) {
0127 dev_err(&wdev->dev,
0128 "Dell descriptor buffer has unexpected length (%d)\n",
0129 obj->buffer.length);
0130 ret = -EINVAL;
0131 descriptor_valid = ret;
0132 goto out;
0133 }
0134
0135 buffer = (u32 *)obj->buffer.pointer;
0136
0137 if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
0138 dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
0139 buffer);
0140 ret = -EINVAL;
0141 descriptor_valid = ret;
0142 goto out;
0143 }
0144 descriptor_valid = 0;
0145
0146 if (buffer[2] != 0 && buffer[2] != 1)
0147 dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n",
0148 (unsigned long) buffer[2]);
0149
0150 priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv),
0151 GFP_KERNEL);
0152
0153 if (!priv) {
0154 ret = -ENOMEM;
0155 goto out;
0156 }
0157
0158 priv->interface_version = buffer[2];
0159 priv->size = buffer[3];
0160 priv->hotfix = buffer[4];
0161 ret = 0;
0162 dev_set_drvdata(&wdev->dev, priv);
0163 mutex_lock(&list_mutex);
0164 list_add_tail(&priv->list, &wmi_list);
0165 mutex_unlock(&list_mutex);
0166
0167 dev_dbg(&wdev->dev, "Detected Dell WMI interface version %lu, buffer size %lu, hotfix %lu\n",
0168 (unsigned long) priv->interface_version,
0169 (unsigned long) priv->size,
0170 (unsigned long) priv->hotfix);
0171
0172 out:
0173 kfree(obj);
0174 return ret;
0175 }
0176
0177 static void dell_wmi_descriptor_remove(struct wmi_device *wdev)
0178 {
0179 struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev);
0180
0181 mutex_lock(&list_mutex);
0182 list_del(&priv->list);
0183 mutex_unlock(&list_mutex);
0184 }
0185
0186 static const struct wmi_device_id dell_wmi_descriptor_id_table[] = {
0187 { .guid_string = DELL_WMI_DESCRIPTOR_GUID },
0188 { },
0189 };
0190
0191 static struct wmi_driver dell_wmi_descriptor_driver = {
0192 .driver = {
0193 .name = "dell-wmi-descriptor",
0194 },
0195 .probe = dell_wmi_descriptor_probe,
0196 .remove = dell_wmi_descriptor_remove,
0197 .id_table = dell_wmi_descriptor_id_table,
0198 };
0199
0200 module_wmi_driver(dell_wmi_descriptor_driver);
0201
0202 MODULE_DEVICE_TABLE(wmi, dell_wmi_descriptor_id_table);
0203 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
0204 MODULE_DESCRIPTION("Dell WMI descriptor driver");
0205 MODULE_LICENSE("GPL");