spec(reimaden): add platform support kernel module
This commit is contained in:
parent
8a0ec45d55
commit
5641dc74c5
26 changed files with 2071 additions and 0 deletions
369
spec/reimaden/ac71/events.c
Normal file
369
spec/reimaden/ac71/events.c
Normal file
|
@ -0,0 +1,369 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "pr.h"
|
||||
|
||||
#include <acpi/video.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/leds.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "pdev.h"
|
||||
#include "wmi.h"
|
||||
|
||||
/* ========================================================================== */
|
||||
|
||||
#define KBD_BL_LED_SUFFIX ":" LED_FUNCTION_KBD_BACKLIGHT
|
||||
|
||||
/* ========================================================================== */
|
||||
|
||||
static struct {
|
||||
const char *guid;
|
||||
bool handler_installed;
|
||||
} ac71_wmi_event_guids[] = {
|
||||
{ .guid = AC71_WMI_EVENT0_GUID },
|
||||
{ .guid = AC71_WMI_EVENT1_GUID },
|
||||
{ .guid = AC71_WMI_EVENT2_GUID },
|
||||
};
|
||||
|
||||
static const struct key_entry ac71_wmi_hotkeys[] = {
|
||||
|
||||
/* reported via keyboard controller */
|
||||
{ KE_IGNORE, 0x01, { KEY_CAPSLOCK }},
|
||||
{ KE_IGNORE, 0x02, { KEY_NUMLOCK }},
|
||||
{ KE_IGNORE, 0x03, { KEY_SCROLLLOCK }},
|
||||
|
||||
/* reported via "video bus" */
|
||||
{ KE_IGNORE, 0x14, { KEY_BRIGHTNESSUP }},
|
||||
{ KE_IGNORE, 0x15, { KEY_BRIGHTNESSDOWN }},
|
||||
|
||||
/* reported via keyboard controller */
|
||||
{ KE_IGNORE, 0x35, { KEY_MUTE }},
|
||||
{ KE_IGNORE, 0x36, { KEY_VOLUMEDOWN }},
|
||||
{ KE_IGNORE, 0x37, { KEY_VOLUMEUP }},
|
||||
|
||||
/*
|
||||
* not reported by other means when in manual mode,
|
||||
* handled automatically when it automatic mode
|
||||
*/
|
||||
{ KE_KEY, 0xb1, { KEY_KBDILLUMDOWN }},
|
||||
{ KE_KEY, 0xb2, { KEY_KBDILLUMUP }},
|
||||
{ KE_KEY, 0xb8, { KEY_FN_ESC }},
|
||||
|
||||
{ KE_END }
|
||||
|
||||
};
|
||||
|
||||
/* ========================================================================== */
|
||||
|
||||
static struct input_dev *ac71_input_dev;
|
||||
|
||||
/* ========================================================================== */
|
||||
|
||||
static void toggle_fn_lock_from_event_handler(void)
|
||||
{
|
||||
int status = ac71_fn_lock_get_state();
|
||||
|
||||
if (status < 0)
|
||||
return;
|
||||
|
||||
/* seemingly the returned status in the WMI event handler is not the current */
|
||||
pr_info("setting Fn lock state from %d to %d\n", !status, status);
|
||||
ac71_fn_lock_set_state(status);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_LEDS_BRIGHTNESS_HW_CHANGED)
|
||||
extern struct rw_semaphore leds_list_lock;
|
||||
extern struct list_head leds_list;
|
||||
|
||||
static void emit_keyboard_led_hw_changed(void)
|
||||
{
|
||||
struct led_classdev *led;
|
||||
|
||||
if (down_read_killable(&leds_list_lock))
|
||||
return;
|
||||
|
||||
list_for_each_entry (led, &leds_list, node) {
|
||||
size_t name_length;
|
||||
const char *suffix;
|
||||
|
||||
if (!(led->flags & LED_BRIGHT_HW_CHANGED))
|
||||
continue;
|
||||
|
||||
name_length = strlen(led->name);
|
||||
|
||||
if (name_length < strlen(KBD_BL_LED_SUFFIX))
|
||||
continue;
|
||||
|
||||
suffix = led->name + name_length - strlen(KBD_BL_LED_SUFFIX);
|
||||
|
||||
if (strcmp(suffix, KBD_BL_LED_SUFFIX) == 0) {
|
||||
if (mutex_lock_interruptible(&led->led_access))
|
||||
break;
|
||||
|
||||
if (led_update_brightness(led) >= 0)
|
||||
led_classdev_notify_brightness_hw_changed(led, led->brightness);
|
||||
|
||||
mutex_unlock(&led->led_access);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
up_read(&leds_list_lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ac71_wmi_event_d2_handler(union acpi_object *obj)
|
||||
{
|
||||
bool do_report = true;
|
||||
|
||||
if (!obj || obj->type != ACPI_TYPE_INTEGER)
|
||||
return;
|
||||
|
||||
switch (obj->integer.value) {
|
||||
/* caps lock */
|
||||
case 1:
|
||||
pr_info("caps lock\n");
|
||||
break;
|
||||
|
||||
/* num lock */
|
||||
case 2:
|
||||
pr_info("num lock\n");
|
||||
break;
|
||||
|
||||
/* scroll lock */
|
||||
case 3:
|
||||
pr_info("scroll lock\n");
|
||||
break;
|
||||
|
||||
/* touchpad on */
|
||||
case 4:
|
||||
pr_info("touchpad on\n");
|
||||
break;
|
||||
|
||||
/* touchpad off */
|
||||
case 5:
|
||||
pr_info("touchpad off\n");
|
||||
break;
|
||||
|
||||
/* increase screen brightness */
|
||||
case 20:
|
||||
pr_info("increase screen brightness\n");
|
||||
/* do_report = !acpi_video_handles_brightness_key_presses() */
|
||||
break;
|
||||
|
||||
/* decrease screen brightness */
|
||||
case 21:
|
||||
pr_info("decrease screen brightness\n");
|
||||
/* do_report = !acpi_video_handles_brightness_key_presses() */
|
||||
break;
|
||||
|
||||
/* mute/unmute */
|
||||
case 53:
|
||||
pr_info("toggle mute\n");
|
||||
break;
|
||||
|
||||
/* decrease volume */
|
||||
case 54:
|
||||
pr_info("decrease volume\n");
|
||||
break;
|
||||
|
||||
/* increase volume */
|
||||
case 55:
|
||||
pr_info("increase volume\n");
|
||||
break;
|
||||
|
||||
/* enable super key (win key) lock */
|
||||
case 64:
|
||||
pr_info("enable super key lock\n");
|
||||
break;
|
||||
|
||||
/* decrease volume */
|
||||
case 65:
|
||||
pr_info("disable super key lock\n");
|
||||
break;
|
||||
|
||||
/* enable/disable airplane mode */
|
||||
case 164:
|
||||
pr_info("toggle airplane mode\n");
|
||||
break;
|
||||
|
||||
/* super key (win key) lock state changed */
|
||||
case 165:
|
||||
pr_info("super key lock state changed\n");
|
||||
sysfs_notify(&ac71_platform_dev->dev.kobj, NULL, "super_key_lock");
|
||||
break;
|
||||
|
||||
case 166:
|
||||
pr_info("lightbar state changed\n");
|
||||
break;
|
||||
|
||||
/* fan boost state changed */
|
||||
case 167:
|
||||
pr_info("fan boost state changed\n");
|
||||
break;
|
||||
|
||||
/* charger unplugged/plugged in */
|
||||
case 171:
|
||||
pr_info("AC plugged/unplugged\n");
|
||||
break;
|
||||
|
||||
/* perf mode button pressed */
|
||||
case 176:
|
||||
pr_info("change perf mode\n");
|
||||
/* TODO: should it be handled here? */
|
||||
break;
|
||||
|
||||
/* increase keyboard backlight */
|
||||
case 177:
|
||||
pr_info("keyboard backlight decrease\n");
|
||||
/* TODO: should it be handled here? */
|
||||
break;
|
||||
|
||||
/* decrease keyboard backlight */
|
||||
case 178:
|
||||
pr_info("keyboard backlight increase\n");
|
||||
/* TODO: should it be handled here? */
|
||||
break;
|
||||
|
||||
/* toggle Fn lock (Fn+ESC)*/
|
||||
case 184:
|
||||
pr_info("toggle Fn lock\n");
|
||||
toggle_fn_lock_from_event_handler();
|
||||
sysfs_notify(&ac71_platform_dev->dev.kobj, NULL, "fn_lock");
|
||||
break;
|
||||
|
||||
/* keyboard backlight brightness changed */
|
||||
case 240:
|
||||
pr_info("keyboard backlight changed\n");
|
||||
|
||||
#if IS_ENABLED(CONFIG_LEDS_BRIGHTNESS_HW_CHANGED)
|
||||
emit_keyboard_led_hw_changed();
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_warn("unknown code: %u\n", (unsigned int) obj->integer.value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (do_report && ac71_input_dev)
|
||||
sparse_keymap_report_event(ac71_input_dev,
|
||||
obj->integer.value, 1, true);
|
||||
|
||||
}
|
||||
|
||||
static void ac71_wmi_event_handler(u32 value, void *context)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
|
||||
pr_info("%s(value=%#04x)\n", __func__, (unsigned int) value);
|
||||
status = wmi_get_event_data(value, &response);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("bad WMI event status: %#010x\n", (unsigned int) status);
|
||||
return;
|
||||
}
|
||||
|
||||
obj = response.pointer;
|
||||
|
||||
if (obj) {
|
||||
pr_info("obj->type = %d\n", (int) obj->type);
|
||||
if (obj->type == ACPI_TYPE_INTEGER) {
|
||||
pr_info("int = %u\n", (unsigned int) obj->integer.value);
|
||||
} else if (obj->type == ACPI_TYPE_STRING) {
|
||||
pr_info("string = '%s'\n", obj->string.pointer);
|
||||
} else if (obj->type == ACPI_TYPE_BUFFER) {
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < obj->buffer.length; i++)
|
||||
pr_info("buf[%u] = %#04x\n",
|
||||
(unsigned int) i,
|
||||
(unsigned int) obj->buffer.pointer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
switch (value) {
|
||||
case 0xd2:
|
||||
ac71_wmi_event_d2_handler(obj);
|
||||
break;
|
||||
case 0xd1:
|
||||
case 0xd0:
|
||||
break;
|
||||
}
|
||||
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
static int __init setup_input_dev(void)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
ac71_input_dev = input_allocate_device();
|
||||
if (!ac71_input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ac71_input_dev->name = "AC71 laptop input device";
|
||||
ac71_input_dev->phys = "ac71/input0";
|
||||
ac71_input_dev->id.bustype = BUS_HOST;
|
||||
ac71_input_dev->dev.parent = &ac71_platform_dev->dev;
|
||||
|
||||
err = sparse_keymap_setup(ac71_input_dev, ac71_wmi_hotkeys, NULL);
|
||||
if (err)
|
||||
goto err_free_device;
|
||||
|
||||
err = input_register_device(ac71_input_dev);
|
||||
if (err)
|
||||
goto err_free_device;
|
||||
|
||||
return err;
|
||||
|
||||
err_free_device:
|
||||
input_free_device(ac71_input_dev);
|
||||
ac71_input_dev = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
|
||||
int __init ac71_wmi_events_setup(void)
|
||||
{
|
||||
int err = 0, i;
|
||||
|
||||
(void) setup_input_dev();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ac71_wmi_event_guids); i++) {
|
||||
const char *guid = ac71_wmi_event_guids[i].guid;
|
||||
acpi_status status =
|
||||
wmi_install_notify_handler(guid, ac71_wmi_event_handler, NULL);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_warn("could not install WMI notify handler for '%s': [%#010lx] %s\n",
|
||||
guid, (unsigned long) status, acpi_format_exception(status));
|
||||
} else {
|
||||
ac71_wmi_event_guids[i].handler_installed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void ac71_wmi_events_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ac71_wmi_event_guids); i++) {
|
||||
if (ac71_wmi_event_guids[i].handler_installed) {
|
||||
wmi_remove_notify_handler(ac71_wmi_event_guids[i].guid);
|
||||
ac71_wmi_event_guids[i].handler_installed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ac71_input_dev)
|
||||
input_unregister_device(ac71_input_dev);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue