Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Dell WMI descriptor driver
0004  *
0005  * Copyright (C) 2017 Dell Inc. All Rights Reserved.
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  * Descriptor buffer is 128 byte long and contains:
0093  *
0094  *       Name             Offset  Length  Value
0095  * Vendor Signature          0       4    "DELL"
0096  * Object Signature          4       4    " WMI"
0097  * WMI Interface Version     8       4    <version>
0098  * WMI buffer length        12       4    <length>
0099  * WMI hotfix number        16       4    <hotfix>
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     /* Although it's not technically a failure, this would lead to
0124      * unexpected behavior
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");