blob: 3204c408e261dd77d2998eb2f2febd8f41b6bce5 [file] [log] [blame]
Mario Limonciello92b8c542017-11-01 14:25:27 -05001/*
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
24struct descriptor_priv {
25 struct list_head list;
26 u32 interface_version;
27 u32 size;
28};
29static LIST_HEAD(wmi_list);
30static DEFINE_MUTEX(list_mutex);
31
32bool dell_wmi_get_interface_version(u32 *version)
33{
34 struct descriptor_priv *priv;
35 bool ret = false;
36
37 mutex_lock(&list_mutex);
38 priv = list_first_entry_or_null(&wmi_list,
39 struct descriptor_priv,
40 list);
41 if (priv) {
42 *version = priv->interface_version;
43 ret = true;
44 }
45 mutex_unlock(&list_mutex);
46 return ret;
47}
48EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version);
49
50bool dell_wmi_get_size(u32 *size)
51{
52 struct descriptor_priv *priv;
53 bool ret = false;
54
55 mutex_lock(&list_mutex);
56 priv = list_first_entry_or_null(&wmi_list,
57 struct descriptor_priv,
58 list);
59 if (priv) {
60 *size = priv->size;
61 ret = true;
62 }
63 mutex_unlock(&list_mutex);
64 return ret;
65}
66EXPORT_SYMBOL_GPL(dell_wmi_get_size);
67
68/*
69 * Descriptor buffer is 128 byte long and contains:
70 *
71 * Name Offset Length Value
72 * Vendor Signature 0 4 "DELL"
73 * Object Signature 4 4 " WMI"
74 * WMI Interface Version 8 4 <version>
75 * WMI buffer length 12 4 <length>
76 */
77static int dell_wmi_descriptor_probe(struct wmi_device *wdev)
78{
79 union acpi_object *obj = NULL;
80 struct descriptor_priv *priv;
81 u32 *buffer;
82 int ret;
83
84 obj = wmidev_block_query(wdev, 0);
85 if (!obj) {
86 dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
87 ret = -EIO;
88 goto out;
89 }
90
91 if (obj->type != ACPI_TYPE_BUFFER) {
92 dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
93 ret = -EINVAL;
94 goto out;
95 }
96
97 /* Although it's not technically a failure, this would lead to
98 * unexpected behavior
99 */
100 if (obj->buffer.length != 128) {
101 dev_err(&wdev->dev,
102 "Dell descriptor buffer has unexpected length (%d)\n",
103 obj->buffer.length);
104 ret = -EINVAL;
105 goto out;
106 }
107
108 buffer = (u32 *)obj->buffer.pointer;
109
110 if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
111 dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
112 buffer);
113 ret = -EINVAL;
114 goto out;
115 }
116
117 if (buffer[2] != 0 && buffer[2] != 1)
118 dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n",
119 (unsigned long) buffer[2]);
120
121 priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv),
122 GFP_KERNEL);
123
124 priv->interface_version = buffer[2];
125 priv->size = buffer[3];
126 ret = 0;
127 dev_set_drvdata(&wdev->dev, priv);
128 mutex_lock(&list_mutex);
129 list_add_tail(&priv->list, &wmi_list);
130 mutex_unlock(&list_mutex);
131
132 dev_dbg(&wdev->dev, "Detected Dell WMI interface version %lu and buffer size %lu\n",
133 (unsigned long) priv->interface_version,
134 (unsigned long) priv->size);
135
136out:
137 kfree(obj);
138 return ret;
139}
140
141static int dell_wmi_descriptor_remove(struct wmi_device *wdev)
142{
143 struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev);
144
145 mutex_lock(&list_mutex);
146 list_del(&priv->list);
147 mutex_unlock(&list_mutex);
148 return 0;
149}
150
151static const struct wmi_device_id dell_wmi_descriptor_id_table[] = {
152 { .guid_string = DELL_WMI_DESCRIPTOR_GUID },
153 { },
154};
155
156static struct wmi_driver dell_wmi_descriptor_driver = {
157 .driver = {
158 .name = "dell-wmi-descriptor",
159 },
160 .probe = dell_wmi_descriptor_probe,
161 .remove = dell_wmi_descriptor_remove,
162 .id_table = dell_wmi_descriptor_id_table,
163};
164
165module_wmi_driver(dell_wmi_descriptor_driver);
166
167MODULE_ALIAS("wmi:" DELL_WMI_DESCRIPTOR_GUID);
168MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
169MODULE_DESCRIPTION("Dell WMI descriptor driver");
170MODULE_LICENSE("GPL");