162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * WMI embedded Binary MOF driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2015 Andrew Lutomirski
662306a36Sopenharmony_ci * Copyright (C) 2017 VMware, Inc. All Rights Reserved.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/acpi.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/fs.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/string.h>
1762306a36Sopenharmony_ci#include <linux/sysfs.h>
1862306a36Sopenharmony_ci#include <linux/types.h>
1962306a36Sopenharmony_ci#include <linux/wmi.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define WMI_BMOF_GUID "05901221-D566-11D1-B2F0-00A0C9062910"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct bmof_priv {
2462306a36Sopenharmony_ci	union acpi_object *bmofdata;
2562306a36Sopenharmony_ci	struct bin_attribute bmof_bin_attr;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic ssize_t read_bmof(struct file *filp, struct kobject *kobj, struct bin_attribute *attr,
2962306a36Sopenharmony_ci			 char *buf, loff_t off, size_t count)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct bmof_priv *priv = container_of(attr, struct bmof_priv, bmof_bin_attr);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	return memory_read_from_buffer(buf, count, &off, priv->bmofdata->buffer.pointer,
3462306a36Sopenharmony_ci				       priv->bmofdata->buffer.length);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int wmi_bmof_probe(struct wmi_device *wdev, const void *context)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct bmof_priv *priv;
4062306a36Sopenharmony_ci	int ret;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	priv = devm_kzalloc(&wdev->dev, sizeof(struct bmof_priv), GFP_KERNEL);
4362306a36Sopenharmony_ci	if (!priv)
4462306a36Sopenharmony_ci		return -ENOMEM;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	dev_set_drvdata(&wdev->dev, priv);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	priv->bmofdata = wmidev_block_query(wdev, 0);
4962306a36Sopenharmony_ci	if (!priv->bmofdata) {
5062306a36Sopenharmony_ci		dev_err(&wdev->dev, "failed to read Binary MOF\n");
5162306a36Sopenharmony_ci		return -EIO;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (priv->bmofdata->type != ACPI_TYPE_BUFFER) {
5562306a36Sopenharmony_ci		dev_err(&wdev->dev, "Binary MOF is not a buffer\n");
5662306a36Sopenharmony_ci		ret = -EIO;
5762306a36Sopenharmony_ci		goto err_free;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	sysfs_bin_attr_init(&priv->bmof_bin_attr);
6162306a36Sopenharmony_ci	priv->bmof_bin_attr.attr.name = "bmof";
6262306a36Sopenharmony_ci	priv->bmof_bin_attr.attr.mode = 0400;
6362306a36Sopenharmony_ci	priv->bmof_bin_attr.read = read_bmof;
6462306a36Sopenharmony_ci	priv->bmof_bin_attr.size = priv->bmofdata->buffer.length;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	ret = device_create_bin_file(&wdev->dev, &priv->bmof_bin_attr);
6762306a36Sopenharmony_ci	if (ret)
6862306a36Sopenharmony_ci		goto err_free;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return 0;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci err_free:
7362306a36Sopenharmony_ci	kfree(priv->bmofdata);
7462306a36Sopenharmony_ci	return ret;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void wmi_bmof_remove(struct wmi_device *wdev)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct bmof_priv *priv = dev_get_drvdata(&wdev->dev);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	device_remove_bin_file(&wdev->dev, &priv->bmof_bin_attr);
8262306a36Sopenharmony_ci	kfree(priv->bmofdata);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic const struct wmi_device_id wmi_bmof_id_table[] = {
8662306a36Sopenharmony_ci	{ .guid_string = WMI_BMOF_GUID },
8762306a36Sopenharmony_ci	{ },
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic struct wmi_driver wmi_bmof_driver = {
9162306a36Sopenharmony_ci	.driver = {
9262306a36Sopenharmony_ci		.name = "wmi-bmof",
9362306a36Sopenharmony_ci	},
9462306a36Sopenharmony_ci	.probe = wmi_bmof_probe,
9562306a36Sopenharmony_ci	.remove = wmi_bmof_remove,
9662306a36Sopenharmony_ci	.id_table = wmi_bmof_id_table,
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cimodule_wmi_driver(wmi_bmof_driver);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(wmi, wmi_bmof_id_table);
10262306a36Sopenharmony_ciMODULE_AUTHOR("Andrew Lutomirski <luto@kernel.org>");
10362306a36Sopenharmony_ciMODULE_DESCRIPTION("WMI embedded Binary MOF driver");
10462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
105