2 * WMI methods for use with dell-smbios
4 * Copyright (c) 2017 Dell Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12 #include <linux/dmi.h>
13 #include <linux/list.h>
14 #include <linux/module.h>
15 #include <linux/mutex.h>
16 #include <linux/uaccess.h>
17 #include <linux/wmi.h>
18 #include "dell-smbios.h"
19 #include "dell-wmi-descriptor.h"
21 static DEFINE_MUTEX(call_mutex
);
22 static DEFINE_MUTEX(list_mutex
);
23 static int wmi_supported
;
25 struct misc_bios_flags_structure
{
26 struct dmi_header header
;
29 #define FLAG_HAS_ACPI_WMI 0x02
31 #define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
33 struct wmi_smbios_priv
{
34 struct dell_wmi_smbios_buffer
*buf
;
35 struct list_head list
;
36 struct wmi_device
*wdev
;
40 static LIST_HEAD(wmi_list
);
42 static inline struct wmi_smbios_priv
*get_first_smbios_priv(void)
44 return list_first_entry_or_null(&wmi_list
,
45 struct wmi_smbios_priv
,
49 static int run_smbios_call(struct wmi_device
*wdev
)
51 struct acpi_buffer output
= {ACPI_ALLOCATE_BUFFER
, NULL
};
52 struct wmi_smbios_priv
*priv
;
53 struct acpi_buffer input
;
54 union acpi_object
*obj
;
57 priv
= dev_get_drvdata(&wdev
->dev
);
58 input
.length
= priv
->req_buf_size
- sizeof(u64
);
59 input
.pointer
= &priv
->buf
->std
;
61 dev_dbg(&wdev
->dev
, "evaluating: %u/%u [%x,%x,%x,%x]\n",
62 priv
->buf
->std
.cmd_class
, priv
->buf
->std
.cmd_select
,
63 priv
->buf
->std
.input
[0], priv
->buf
->std
.input
[1],
64 priv
->buf
->std
.input
[2], priv
->buf
->std
.input
[3]);
66 status
= wmidev_evaluate_method(wdev
, 0, 1, &input
, &output
);
67 if (ACPI_FAILURE(status
))
69 obj
= (union acpi_object
*)output
.pointer
;
70 if (obj
->type
!= ACPI_TYPE_BUFFER
) {
71 dev_dbg(&wdev
->dev
, "received type: %d\n", obj
->type
);
72 if (obj
->type
== ACPI_TYPE_INTEGER
)
73 dev_dbg(&wdev
->dev
, "SMBIOS call failed: %llu\n",
77 memcpy(&priv
->buf
->std
, obj
->buffer
.pointer
, obj
->buffer
.length
);
78 dev_dbg(&wdev
->dev
, "result: [%08x,%08x,%08x,%08x]\n",
79 priv
->buf
->std
.output
[0], priv
->buf
->std
.output
[1],
80 priv
->buf
->std
.output
[2], priv
->buf
->std
.output
[3]);
85 int dell_smbios_wmi_call(struct calling_interface_buffer
*buffer
)
87 struct wmi_smbios_priv
*priv
;
92 mutex_lock(&call_mutex
);
93 priv
= get_first_smbios_priv();
99 size
= sizeof(struct calling_interface_buffer
);
100 difference
= priv
->req_buf_size
- sizeof(u64
) - size
;
102 memset(&priv
->buf
->ext
, 0, difference
);
103 memcpy(&priv
->buf
->std
, buffer
, size
);
104 ret
= run_smbios_call(priv
->wdev
);
105 memcpy(buffer
, &priv
->buf
->std
, size
);
107 mutex_unlock(&call_mutex
);
112 static long dell_smbios_wmi_filter(struct wmi_device
*wdev
, unsigned int cmd
,
113 struct wmi_ioctl_buffer
*arg
)
115 struct wmi_smbios_priv
*priv
;
119 case DELL_WMI_SMBIOS_CMD
:
120 mutex_lock(&call_mutex
);
121 priv
= dev_get_drvdata(&wdev
->dev
);
124 goto fail_smbios_cmd
;
126 memcpy(priv
->buf
, arg
, priv
->req_buf_size
);
127 if (dell_smbios_call_filter(&wdev
->dev
, &priv
->buf
->std
)) {
128 dev_err(&wdev
->dev
, "Invalid call %d/%d:%8x\n",
129 priv
->buf
->std
.cmd_class
,
130 priv
->buf
->std
.cmd_select
,
131 priv
->buf
->std
.input
[0]);
133 goto fail_smbios_cmd
;
135 ret
= run_smbios_call(priv
->wdev
);
137 goto fail_smbios_cmd
;
138 memcpy(arg
, priv
->buf
, priv
->req_buf_size
);
140 mutex_unlock(&call_mutex
);
148 static int dell_smbios_wmi_probe(struct wmi_device
*wdev
)
150 struct wmi_smbios_priv
*priv
;
154 ret
= dell_wmi_get_descriptor_valid();
158 priv
= devm_kzalloc(&wdev
->dev
, sizeof(struct wmi_smbios_priv
),
163 /* WMI buffer size will be either 4k or 32k depending on machine */
164 if (!dell_wmi_get_size(&priv
->req_buf_size
))
165 return -EPROBE_DEFER
;
167 /* add in the length object we will use internally with ioctl */
168 priv
->req_buf_size
+= sizeof(u64
);
169 ret
= set_required_buffer_size(wdev
, priv
->req_buf_size
);
173 count
= get_order(priv
->req_buf_size
);
174 priv
->buf
= (void *)__get_free_pages(GFP_KERNEL
, count
);
178 /* ID is used by dell-smbios to set priority of drivers */
180 ret
= dell_smbios_register_device(&wdev
->dev
, &dell_smbios_wmi_call
);
185 dev_set_drvdata(&wdev
->dev
, priv
);
186 mutex_lock(&list_mutex
);
187 list_add_tail(&priv
->list
, &wmi_list
);
188 mutex_unlock(&list_mutex
);
193 free_pages((unsigned long)priv
->buf
, count
);
197 static int dell_smbios_wmi_remove(struct wmi_device
*wdev
)
199 struct wmi_smbios_priv
*priv
= dev_get_drvdata(&wdev
->dev
);
202 mutex_lock(&call_mutex
);
203 mutex_lock(&list_mutex
);
204 list_del(&priv
->list
);
205 mutex_unlock(&list_mutex
);
206 dell_smbios_unregister_device(&wdev
->dev
);
207 count
= get_order(priv
->req_buf_size
);
208 free_pages((unsigned long)priv
->buf
, count
);
209 mutex_unlock(&call_mutex
);
213 static const struct wmi_device_id dell_smbios_wmi_id_table
[] = {
214 { .guid_string
= DELL_WMI_SMBIOS_GUID
},
218 static void __init
parse_b1_table(const struct dmi_header
*dm
)
220 struct misc_bios_flags_structure
*flags
=
221 container_of(dm
, struct misc_bios_flags_structure
, header
);
223 /* 4 bytes header, 8 bytes flags */
226 if (dm
->handle
!= 0xb100)
228 if ((flags
->flags0
& FLAG_HAS_ACPI_WMI
))
232 static void __init
find_b1(const struct dmi_header
*dm
, void *dummy
)
235 case 0xb1: /* misc bios flags */
241 static struct wmi_driver dell_smbios_wmi_driver
= {
243 .name
= "dell-smbios",
245 .probe
= dell_smbios_wmi_probe
,
246 .remove
= dell_smbios_wmi_remove
,
247 .id_table
= dell_smbios_wmi_id_table
,
248 .filter_callback
= dell_smbios_wmi_filter
,
251 static int __init
init_dell_smbios_wmi(void)
253 dmi_walk(find_b1
, NULL
);
258 return wmi_driver_register(&dell_smbios_wmi_driver
);
261 static void __exit
exit_dell_smbios_wmi(void)
263 wmi_driver_unregister(&dell_smbios_wmi_driver
);
266 module_init(init_dell_smbios_wmi
);
267 module_exit(exit_dell_smbios_wmi
);
269 MODULE_ALIAS("wmi:" DELL_WMI_SMBIOS_GUID
);
270 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
271 MODULE_DESCRIPTION("Dell SMBIOS communications over WMI");
272 MODULE_LICENSE("GPL");