1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * WMI embedded Binary MOF driver |
4 | * |
5 | * Copyright (c) 2015 Andrew Lutomirski |
6 | * Copyright (C) 2017 VMware, Inc. All Rights Reserved. |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | |
11 | #include <linux/acpi.h> |
12 | #include <linux/device.h> |
13 | #include <linux/fs.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | #include <linux/string.h> |
17 | #include <linux/sysfs.h> |
18 | #include <linux/types.h> |
19 | #include <linux/wmi.h> |
20 | |
21 | #define WMI_BMOF_GUID "05901221-D566-11D1-B2F0-00A0C9062910" |
22 | |
23 | struct bmof_priv { |
24 | union acpi_object *bmofdata; |
25 | struct bin_attribute bmof_bin_attr; |
26 | }; |
27 | |
28 | static ssize_t read_bmof(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, |
29 | char *buf, loff_t off, size_t count) |
30 | { |
31 | struct bmof_priv *priv = container_of(attr, struct bmof_priv, bmof_bin_attr); |
32 | |
33 | return memory_read_from_buffer(to: buf, count, ppos: &off, from: priv->bmofdata->buffer.pointer, |
34 | available: priv->bmofdata->buffer.length); |
35 | } |
36 | |
37 | static int wmi_bmof_probe(struct wmi_device *wdev, const void *context) |
38 | { |
39 | struct bmof_priv *priv; |
40 | int ret; |
41 | |
42 | priv = devm_kzalloc(dev: &wdev->dev, size: sizeof(struct bmof_priv), GFP_KERNEL); |
43 | if (!priv) |
44 | return -ENOMEM; |
45 | |
46 | dev_set_drvdata(dev: &wdev->dev, data: priv); |
47 | |
48 | priv->bmofdata = wmidev_block_query(wdev, instance: 0); |
49 | if (!priv->bmofdata) { |
50 | dev_err(&wdev->dev, "failed to read Binary MOF\n" ); |
51 | return -EIO; |
52 | } |
53 | |
54 | if (priv->bmofdata->type != ACPI_TYPE_BUFFER) { |
55 | dev_err(&wdev->dev, "Binary MOF is not a buffer\n" ); |
56 | ret = -EIO; |
57 | goto err_free; |
58 | } |
59 | |
60 | sysfs_bin_attr_init(&priv->bmof_bin_attr); |
61 | priv->bmof_bin_attr.attr.name = "bmof" ; |
62 | priv->bmof_bin_attr.attr.mode = 0400; |
63 | priv->bmof_bin_attr.read = read_bmof; |
64 | priv->bmof_bin_attr.size = priv->bmofdata->buffer.length; |
65 | |
66 | ret = device_create_bin_file(dev: &wdev->dev, attr: &priv->bmof_bin_attr); |
67 | if (ret) |
68 | goto err_free; |
69 | |
70 | return 0; |
71 | |
72 | err_free: |
73 | kfree(objp: priv->bmofdata); |
74 | return ret; |
75 | } |
76 | |
77 | static void wmi_bmof_remove(struct wmi_device *wdev) |
78 | { |
79 | struct bmof_priv *priv = dev_get_drvdata(dev: &wdev->dev); |
80 | |
81 | device_remove_bin_file(dev: &wdev->dev, attr: &priv->bmof_bin_attr); |
82 | kfree(objp: priv->bmofdata); |
83 | } |
84 | |
85 | static const struct wmi_device_id wmi_bmof_id_table[] = { |
86 | { .guid_string = WMI_BMOF_GUID }, |
87 | { }, |
88 | }; |
89 | |
90 | static struct wmi_driver wmi_bmof_driver = { |
91 | .driver = { |
92 | .name = "wmi-bmof" , |
93 | }, |
94 | .probe = wmi_bmof_probe, |
95 | .remove = wmi_bmof_remove, |
96 | .id_table = wmi_bmof_id_table, |
97 | }; |
98 | |
99 | module_wmi_driver(wmi_bmof_driver); |
100 | |
101 | MODULE_DEVICE_TABLE(wmi, wmi_bmof_id_table); |
102 | MODULE_AUTHOR("Andrew Lutomirski <luto@kernel.org>" ); |
103 | MODULE_DESCRIPTION("WMI embedded Binary MOF driver" ); |
104 | MODULE_LICENSE("GPL" ); |
105 | |