18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * WMI embedded Binary MOF driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2015 Andrew Lutomirski
68c2ecf20Sopenharmony_ci * Copyright (C) 2017 VMware, Inc. All Rights Reserved.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/acpi.h>
128c2ecf20Sopenharmony_ci#include <linux/device.h>
138c2ecf20Sopenharmony_ci#include <linux/fs.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/string.h>
178c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
188c2ecf20Sopenharmony_ci#include <linux/types.h>
198c2ecf20Sopenharmony_ci#include <linux/wmi.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define WMI_BMOF_GUID "05901221-D566-11D1-B2F0-00A0C9062910"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistruct bmof_priv {
248c2ecf20Sopenharmony_ci	union acpi_object *bmofdata;
258c2ecf20Sopenharmony_ci	struct bin_attribute bmof_bin_attr;
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic ssize_t
298c2ecf20Sopenharmony_ciread_bmof(struct file *filp, struct kobject *kobj,
308c2ecf20Sopenharmony_ci	 struct bin_attribute *attr,
318c2ecf20Sopenharmony_ci	 char *buf, loff_t off, size_t count)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct bmof_priv *priv =
348c2ecf20Sopenharmony_ci		container_of(attr, struct bmof_priv, bmof_bin_attr);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (off < 0)
378c2ecf20Sopenharmony_ci		return -EINVAL;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	if (off >= priv->bmofdata->buffer.length)
408c2ecf20Sopenharmony_ci		return 0;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (count > priv->bmofdata->buffer.length - off)
438c2ecf20Sopenharmony_ci		count = priv->bmofdata->buffer.length - off;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	memcpy(buf, priv->bmofdata->buffer.pointer + off, count);
468c2ecf20Sopenharmony_ci	return count;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic int wmi_bmof_probe(struct wmi_device *wdev, const void *context)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct bmof_priv *priv;
528c2ecf20Sopenharmony_ci	int ret;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&wdev->dev, sizeof(struct bmof_priv), GFP_KERNEL);
558c2ecf20Sopenharmony_ci	if (!priv)
568c2ecf20Sopenharmony_ci		return -ENOMEM;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	dev_set_drvdata(&wdev->dev, priv);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	priv->bmofdata = wmidev_block_query(wdev, 0);
618c2ecf20Sopenharmony_ci	if (!priv->bmofdata) {
628c2ecf20Sopenharmony_ci		dev_err(&wdev->dev, "failed to read Binary MOF\n");
638c2ecf20Sopenharmony_ci		return -EIO;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (priv->bmofdata->type != ACPI_TYPE_BUFFER) {
678c2ecf20Sopenharmony_ci		dev_err(&wdev->dev, "Binary MOF is not a buffer\n");
688c2ecf20Sopenharmony_ci		ret = -EIO;
698c2ecf20Sopenharmony_ci		goto err_free;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	sysfs_bin_attr_init(&priv->bmof_bin_attr);
738c2ecf20Sopenharmony_ci	priv->bmof_bin_attr.attr.name = "bmof";
748c2ecf20Sopenharmony_ci	priv->bmof_bin_attr.attr.mode = 0400;
758c2ecf20Sopenharmony_ci	priv->bmof_bin_attr.read = read_bmof;
768c2ecf20Sopenharmony_ci	priv->bmof_bin_attr.size = priv->bmofdata->buffer.length;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	ret = sysfs_create_bin_file(&wdev->dev.kobj, &priv->bmof_bin_attr);
798c2ecf20Sopenharmony_ci	if (ret)
808c2ecf20Sopenharmony_ci		goto err_free;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return 0;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci err_free:
858c2ecf20Sopenharmony_ci	kfree(priv->bmofdata);
868c2ecf20Sopenharmony_ci	return ret;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic int wmi_bmof_remove(struct wmi_device *wdev)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct bmof_priv *priv = dev_get_drvdata(&wdev->dev);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	sysfs_remove_bin_file(&wdev->dev.kobj, &priv->bmof_bin_attr);
948c2ecf20Sopenharmony_ci	kfree(priv->bmofdata);
958c2ecf20Sopenharmony_ci	return 0;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic const struct wmi_device_id wmi_bmof_id_table[] = {
998c2ecf20Sopenharmony_ci	{ .guid_string = WMI_BMOF_GUID },
1008c2ecf20Sopenharmony_ci	{ },
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic struct wmi_driver wmi_bmof_driver = {
1048c2ecf20Sopenharmony_ci	.driver = {
1058c2ecf20Sopenharmony_ci		.name = "wmi-bmof",
1068c2ecf20Sopenharmony_ci	},
1078c2ecf20Sopenharmony_ci	.probe = wmi_bmof_probe,
1088c2ecf20Sopenharmony_ci	.remove = wmi_bmof_remove,
1098c2ecf20Sopenharmony_ci	.id_table = wmi_bmof_id_table,
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cimodule_wmi_driver(wmi_bmof_driver);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(wmi, wmi_bmof_id_table);
1158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrew Lutomirski <luto@kernel.org>");
1168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("WMI embedded Binary MOF driver");
1178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
118