root/drivers/platform/x86/dell-wmi-descriptor.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dell_wmi_get_descriptor_valid
  2. dell_wmi_get_interface_version
  3. dell_wmi_get_size
  4. dell_wmi_get_hotfix
  5. dell_wmi_descriptor_probe
  6. dell_wmi_descriptor_remove

   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 
  18 struct descriptor_priv {
  19         struct list_head list;
  20         u32 interface_version;
  21         u32 size;
  22         u32 hotfix;
  23 };
  24 static int descriptor_valid = -EPROBE_DEFER;
  25 static LIST_HEAD(wmi_list);
  26 static DEFINE_MUTEX(list_mutex);
  27 
  28 int 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 }
  35 EXPORT_SYMBOL_GPL(dell_wmi_get_descriptor_valid);
  36 
  37 bool 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 }
  53 EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version);
  54 
  55 bool 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 }
  71 EXPORT_SYMBOL_GPL(dell_wmi_get_size);
  72 
  73 bool 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 }
  89 EXPORT_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  */
 101 static 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 
 172 out:
 173         kfree(obj);
 174         return ret;
 175 }
 176 
 177 static 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 
 187 static const struct wmi_device_id dell_wmi_descriptor_id_table[] = {
 188         { .guid_string = DELL_WMI_DESCRIPTOR_GUID },
 189         { },
 190 };
 191 
 192 static 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 
 201 module_wmi_driver(dell_wmi_descriptor_driver);
 202 
 203 MODULE_DEVICE_TABLE(wmi, dell_wmi_descriptor_id_table);
 204 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
 205 MODULE_DESCRIPTION("Dell WMI descriptor driver");
 206 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */