mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

The TUXEDO Sirius 16 Gen1 and TUXEDO Sirius 16 Gen2 devices have a per-key controllable RGB keyboard backlight. The firmware API for it is implemented via WMI. To make the backlight userspace configurable this driver emulates a LampArray HID device and translates the input from hidraw to the corresponding WMI calls. This is a new approach as the leds subsystem lacks a suitable UAPI for per-key keyboard backlights, and like this no new UAPI needs to be established. The handle_* functions an corresponding structs are named based on the HID spec: HID Usage Tables 1.6 -> 26 Lighting And Illumination Page (0x59) Signed-off-by: Werner Sembach <wse@tuxedocomputers.com> Link: https://lore.kernel.org/r/20250425210043.342288-2-wse@tuxedocomputers.com Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
91 lines
2.4 KiB
C
91 lines
2.4 KiB
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);
|
|
}
|