root/drivers/platform/x86/tuxedo/nb04/wmi_util.c
// 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);
}