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