Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.17 213 lines 5.2 kB view raw
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 26struct descriptor_priv { 27 struct list_head list; 28 u32 interface_version; 29 u32 size; 30 u32 hotfix; 31}; 32static int descriptor_valid = -EPROBE_DEFER; 33static LIST_HEAD(wmi_list); 34static DEFINE_MUTEX(list_mutex); 35 36int 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} 43EXPORT_SYMBOL_GPL(dell_wmi_get_descriptor_valid); 44 45bool 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} 61EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version); 62 63bool 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} 79EXPORT_SYMBOL_GPL(dell_wmi_get_size); 80 81bool 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} 97EXPORT_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 */ 109static 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 179out: 180 kfree(obj); 181 return ret; 182} 183 184static 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 194static const struct wmi_device_id dell_wmi_descriptor_id_table[] = { 195 { .guid_string = DELL_WMI_DESCRIPTOR_GUID }, 196 { }, 197}; 198 199static 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 208module_wmi_driver(dell_wmi_descriptor_driver); 209 210MODULE_ALIAS("wmi:" DELL_WMI_DESCRIPTOR_GUID); 211MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); 212MODULE_DESCRIPTION("Dell WMI descriptor driver"); 213MODULE_LICENSE("GPL");