brintos

brintos / linux-shallow public Read only

0
0
Text · 4.7 KiB · c2a1802 Raw
206 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Dell WMI descriptor driver4 *5 * Copyright (C) 2017 Dell Inc. All Rights Reserved.6 */7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt9 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  Value95 * 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 to124	 * unexpected behavior125	 */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 void 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}185 186static const struct wmi_device_id dell_wmi_descriptor_id_table[] = {187	{ .guid_string = DELL_WMI_DESCRIPTOR_GUID },188	{ },189};190 191static struct wmi_driver dell_wmi_descriptor_driver = {192	.driver = {193		.name = "dell-wmi-descriptor",194	},195	.probe = dell_wmi_descriptor_probe,196	.remove = dell_wmi_descriptor_remove,197	.id_table = dell_wmi_descriptor_id_table,198};199 200module_wmi_driver(dell_wmi_descriptor_driver);201 202MODULE_DEVICE_TABLE(wmi, dell_wmi_descriptor_id_table);203MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");204MODULE_DESCRIPTION("Dell WMI descriptor driver");205MODULE_LICENSE("GPL");206