summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/tuxedo/nb04/wmi_util.c
blob: e894690da1a833ea0c4749e20cf1240d337a2556 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * This code gives functions to avoid code duplication while interacting with
 * the TUXEDO NB04 wmi interfaces.
 *
 * Copyright (C) 2024-2025 Werner Sembach <wse@tuxedocomputers.com>
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/cleanup.h>
#include <linux/wmi.h>

#include "wmi_util.h"

static int __wmi_method_acpi_object_out(struct wmi_device *wdev,
					u32 wmi_method_id,
					u8 *in,
					acpi_size in_len,
					union acpi_object **out)
{
	struct acpi_buffer acpi_buffer_in = { in_len, in };
	struct acpi_buffer acpi_buffer_out = { ACPI_ALLOCATE_BUFFER, NULL };

	dev_dbg(&wdev->dev, "Evaluate WMI method: %u in:\n", wmi_method_id);
	print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, in, in_len);

	acpi_status status = wmidev_evaluate_method(wdev, 0, wmi_method_id,
						    &acpi_buffer_in,
						    &acpi_buffer_out);
	if (ACPI_FAILURE(status)) {
		dev_err(&wdev->dev, "Failed to evaluate WMI method.\n");
		return -EIO;
	}
	if (!acpi_buffer_out.pointer) {
		dev_err(&wdev->dev, "Unexpected empty out buffer.\n");
		return -ENODATA;
	}

	*out = acpi_buffer_out.pointer;

	return 0;
}

static int __wmi_method_buffer_out(struct wmi_device *wdev,
				   u32 wmi_method_id,
				   u8 *in,
				   acpi_size in_len,
				   u8 *out,
				   acpi_size out_len)
{
	int ret;

	union acpi_object *acpi_object_out __free(kfree) = NULL;

	ret = __wmi_method_acpi_object_out(wdev, wmi_method_id,
					   in, in_len,
					   &acpi_object_out);
	if (ret)
		return ret;

	if (acpi_object_out->type != ACPI_TYPE_BUFFER) {
		dev_err(&wdev->dev, "Unexpected out buffer type. Expected: %u Got: %u\n",
			ACPI_TYPE_BUFFER, acpi_object_out->type);
		return -EIO;
	}
	if (acpi_object_out->buffer.length < out_len) {
		dev_err(&wdev->dev, "Unexpected out buffer length.\n");
		return -EIO;
	}

	memcpy(out, acpi_object_out->buffer.pointer, out_len);

	return 0;
}

int tux_wmi_xx_8in_80out(struct wmi_device *wdev,
			 enum tux_wmi_xx_8in_80out_methods method,
			 union tux_wmi_xx_8in_80out_in_t *in,
			 union tux_wmi_xx_8in_80out_out_t *out)
{
	return __wmi_method_buffer_out(wdev, method, in->raw, 8, out->raw, 80);
}

int tux_wmi_xx_496in_80out(struct wmi_device *wdev,
			   enum tux_wmi_xx_496in_80out_methods method,
			   union tux_wmi_xx_496in_80out_in_t *in,
			   union tux_wmi_xx_496in_80out_out_t *out)
{
	return __wmi_method_buffer_out(wdev, method, in->raw, 496, out->raw, 80);
}