mirror of
https://github.com/wessel-novacustom/clevo-keyboard.git
synced 2024-11-15 03:34:01 +01:00
Merge branch 'uw-interface-modularization'
This commit is contained in:
commit
22d450a144
3
Makefile
3
Makefile
|
@ -19,7 +19,8 @@
|
||||||
obj-m := ./src/tuxedo_keyboard.o \
|
obj-m := ./src/tuxedo_keyboard.o \
|
||||||
./src/clevo_wmi.o \
|
./src/clevo_wmi.o \
|
||||||
./src/clevo_acpi.o \
|
./src/clevo_acpi.o \
|
||||||
./src/tuxedo_io/tuxedo_io.o
|
./src/tuxedo_io/tuxedo_io.o \
|
||||||
|
./src/uniwill_wmi.o
|
||||||
|
|
||||||
PWD := $(shell pwd)
|
PWD := $(shell pwd)
|
||||||
KDIR := /lib/modules/$(shell uname -r)/build
|
KDIR := /lib/modules/$(shell uname -r)/build
|
||||||
|
|
|
@ -58,11 +58,13 @@ case "$1" in
|
||||||
|
|
||||||
echo "(Re)load modules if possible"
|
echo "(Re)load modules if possible"
|
||||||
rmmod tuxedo_io > /dev/null 2>&1 || true
|
rmmod tuxedo_io > /dev/null 2>&1 || true
|
||||||
|
rmmod uniwill_wmi > /dev/null 2>&1 || true
|
||||||
rmmod clevo_wmi > /dev/null 2>&1 || true
|
rmmod clevo_wmi > /dev/null 2>&1 || true
|
||||||
rmmod clevo_acpi > /dev/null 2>&1 || true
|
rmmod clevo_acpi > /dev/null 2>&1 || true
|
||||||
rmmod tuxedo_keyboard > /dev/null 2>&1 || true
|
rmmod tuxedo_keyboard > /dev/null 2>&1 || true
|
||||||
|
|
||||||
modprobe tuxedo_keyboard > /dev/null 2>&1 || true
|
modprobe tuxedo_keyboard > /dev/null 2>&1 || true
|
||||||
|
modprobe uniwill_wmi > /dev/null 2>&1 || true
|
||||||
modprobe clevo_wmi > /dev/null 2>&1 || true
|
modprobe clevo_wmi > /dev/null 2>&1 || true
|
||||||
modprobe clevo_acpi > /dev/null 2>&1 || true
|
modprobe clevo_acpi > /dev/null 2>&1 || true
|
||||||
modprobe tuxedo_io > /dev/null 2>&1 || true
|
modprobe tuxedo_io > /dev/null 2>&1 || true
|
||||||
|
|
|
@ -17,6 +17,10 @@ DEST_MODULE_LOCATION[3]="/kernel/lib/"
|
||||||
BUILT_MODULE_NAME[3]="tuxedo_io"
|
BUILT_MODULE_NAME[3]="tuxedo_io"
|
||||||
BUILT_MODULE_LOCATION[3]="src/tuxedo_io"
|
BUILT_MODULE_LOCATION[3]="src/tuxedo_io"
|
||||||
|
|
||||||
|
DEST_MODULE_LOCATION[4]="/kernel/lib/"
|
||||||
|
BUILT_MODULE_NAME[4]="uniwill_wmi"
|
||||||
|
BUILT_MODULE_LOCATION[4]="src/"
|
||||||
|
|
||||||
MAKE[0]="make KDIR=/lib/modules/${kernelver}/build"
|
MAKE[0]="make KDIR=/lib/modules/${kernelver}/build"
|
||||||
CLEAN="make clean"
|
CLEAN="make clean"
|
||||||
AUTOINSTALL="yes"
|
AUTOINSTALL="yes"
|
||||||
|
|
|
@ -101,7 +101,7 @@ u32 clevo_acpi_interface_method_call(u8 cmd, u32 arg, u32 *result_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct clevo_interface_t clevo_acpi_interface = {
|
struct clevo_interface_t clevo_acpi_interface = {
|
||||||
.string_id = "clevo_acpi",
|
.string_id = CLEVO_INTERFACE_ACPI_STRID,
|
||||||
.method_call = clevo_acpi_interface_method_call,
|
.method_call = clevo_acpi_interface_method_call,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -123,9 +123,6 @@ static int clevo_acpi_add(struct acpi_device *device)
|
||||||
// Add this interface
|
// Add this interface
|
||||||
clevo_keyboard_add_interface(&clevo_acpi_interface);
|
clevo_keyboard_add_interface(&clevo_acpi_interface);
|
||||||
|
|
||||||
// Initiate clevo keyboard, if not already loaded by other interface driver
|
|
||||||
clevo_keyboard_init();
|
|
||||||
|
|
||||||
pr_info("interface initialized\n");
|
pr_info("interface initialized\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -195,7 +192,7 @@ module_acpi_driver(clevo_acpi_driver);
|
||||||
|
|
||||||
MODULE_AUTHOR("TUXEDO Computers GmbH <tux@tuxedocomputers.com>");
|
MODULE_AUTHOR("TUXEDO Computers GmbH <tux@tuxedocomputers.com>");
|
||||||
MODULE_DESCRIPTION("Driver for Clevo ACPI interface");
|
MODULE_DESCRIPTION("Driver for Clevo ACPI interface");
|
||||||
MODULE_VERSION("0.0.2");
|
MODULE_VERSION("0.0.3");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(acpi, clevo_acpi_device_ids);
|
MODULE_DEVICE_TABLE(acpi, clevo_acpi_device_ids);
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this software. If not, see <https://www.gnu.org/licenses/>.
|
* along with this software. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#ifndef TUXEDO_INTERFACES_H
|
#ifndef CLEVO_INTERFACES_H
|
||||||
#define TUXEDO_INTERFACES_H
|
#define CLEVO_INTERFACES_H
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
@ -48,8 +48,6 @@
|
||||||
#define CLEVO_CMD_OPT 0x79
|
#define CLEVO_CMD_OPT 0x79
|
||||||
#define CLEVO_OPT_SUBCMD_SET_PERF_PROF 0x19
|
#define CLEVO_OPT_SUBCMD_SET_PERF_PROF 0x19
|
||||||
|
|
||||||
int clevo_keyboard_init(void);
|
|
||||||
|
|
||||||
struct clevo_interface_t {
|
struct clevo_interface_t {
|
||||||
char *string_id;
|
char *string_id;
|
||||||
void (*event_callb)(u32);
|
void (*event_callb)(u32);
|
||||||
|
@ -65,6 +63,9 @@ u32 clevo_get_active_interface_id(char **id_str);
|
||||||
MODULE_ALIAS("wmi:" CLEVO_WMI_EVENT_GUID); \
|
MODULE_ALIAS("wmi:" CLEVO_WMI_EVENT_GUID); \
|
||||||
MODULE_ALIAS("wmi:" CLEVO_WMI_METHOD_GUID);
|
MODULE_ALIAS("wmi:" CLEVO_WMI_METHOD_GUID);
|
||||||
|
|
||||||
|
#define CLEVO_INTERFACE_WMI_STRID "clevo_wmi"
|
||||||
|
#define CLEVO_INTERFACE_ACPI_STRID "clevo_acpi"
|
||||||
|
|
||||||
#define MODULE_ALIAS_CLEVO_ACPI() \
|
#define MODULE_ALIAS_CLEVO_ACPI() \
|
||||||
MODULE_ALIAS("acpi*:" CLEVO_ACPI_RESOURCE_HID ":*");
|
MODULE_ALIAS("acpi*:" CLEVO_ACPI_RESOURCE_HID ":*");
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,8 @@
|
||||||
#define CLEVO_EVENT_RFKILL1 0x85
|
#define CLEVO_EVENT_RFKILL1 0x85
|
||||||
#define CLEVO_EVENT_RFKILL2 0x86
|
#define CLEVO_EVENT_RFKILL2 0x86
|
||||||
|
|
||||||
|
struct tuxedo_keyboard_driver clevo_keyboard_driver;
|
||||||
|
|
||||||
static struct clevo_interfaces_t {
|
static struct clevo_interfaces_t {
|
||||||
struct clevo_interface_t *wmi;
|
struct clevo_interface_t *wmi;
|
||||||
struct clevo_interface_t *acpi;
|
struct clevo_interface_t *acpi;
|
||||||
|
@ -70,7 +72,7 @@ u32 clevo_keyboard_add_interface(struct clevo_interface_t *new_interface)
|
||||||
{
|
{
|
||||||
mutex_lock(&clevo_keyboard_interface_modification_lock);
|
mutex_lock(&clevo_keyboard_interface_modification_lock);
|
||||||
|
|
||||||
if (strcmp(new_interface->string_id, "clevo_wmi") == 0) {
|
if (strcmp(new_interface->string_id, CLEVO_INTERFACE_WMI_STRID) == 0) {
|
||||||
clevo_interfaces.wmi = new_interface;
|
clevo_interfaces.wmi = new_interface;
|
||||||
clevo_interfaces.wmi->event_callb = clevo_keyboard_event_callb;
|
clevo_interfaces.wmi->event_callb = clevo_keyboard_event_callb;
|
||||||
|
|
||||||
|
@ -82,7 +84,7 @@ u32 clevo_keyboard_add_interface(struct clevo_interface_t *new_interface)
|
||||||
active_clevo_interface = clevo_interfaces.wmi;
|
active_clevo_interface = clevo_interfaces.wmi;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (strcmp(new_interface->string_id, "clevo_acpi") == 0) {
|
} else if (strcmp(new_interface->string_id, CLEVO_INTERFACE_ACPI_STRID) == 0) {
|
||||||
clevo_interfaces.acpi = new_interface;
|
clevo_interfaces.acpi = new_interface;
|
||||||
clevo_interfaces.acpi->event_callb = clevo_keyboard_event_callb;
|
clevo_interfaces.acpi->event_callb = clevo_keyboard_event_callb;
|
||||||
|
|
||||||
|
@ -98,6 +100,9 @@ u32 clevo_keyboard_add_interface(struct clevo_interface_t *new_interface)
|
||||||
|
|
||||||
mutex_unlock(&clevo_keyboard_interface_modification_lock);
|
mutex_unlock(&clevo_keyboard_interface_modification_lock);
|
||||||
|
|
||||||
|
if (active_clevo_interface != NULL)
|
||||||
|
tuxedo_keyboard_init_driver(&clevo_keyboard_driver);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(clevo_keyboard_add_interface);
|
EXPORT_SYMBOL(clevo_keyboard_add_interface);
|
||||||
|
@ -106,17 +111,20 @@ u32 clevo_keyboard_remove_interface(struct clevo_interface_t *interface)
|
||||||
{
|
{
|
||||||
mutex_lock(&clevo_keyboard_interface_modification_lock);
|
mutex_lock(&clevo_keyboard_interface_modification_lock);
|
||||||
|
|
||||||
if (strcmp(interface->string_id, "clevo_wmi") == 0) {
|
if (strcmp(interface->string_id, CLEVO_INTERFACE_WMI_STRID) == 0) {
|
||||||
clevo_interfaces.wmi = NULL;
|
clevo_interfaces.wmi = NULL;
|
||||||
} else if (strcmp(interface->string_id, "clevo_acpi") == 0) {
|
} else if (strcmp(interface->string_id, CLEVO_INTERFACE_ACPI_STRID) == 0) {
|
||||||
clevo_interfaces.acpi = NULL;
|
clevo_interfaces.acpi = NULL;
|
||||||
} else {
|
} else {
|
||||||
mutex_unlock(&clevo_keyboard_interface_modification_lock);
|
mutex_unlock(&clevo_keyboard_interface_modification_lock);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (active_clevo_interface == interface)
|
if (active_clevo_interface == interface) {
|
||||||
|
tuxedo_keyboard_remove_driver(&clevo_keyboard_driver);
|
||||||
active_clevo_interface = NULL;
|
active_clevo_interface = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
mutex_unlock(&clevo_keyboard_interface_modification_lock);
|
mutex_unlock(&clevo_keyboard_interface_modification_lock);
|
||||||
|
|
||||||
|
@ -124,8 +132,6 @@ u32 clevo_keyboard_remove_interface(struct clevo_interface_t *interface)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(clevo_keyboard_remove_interface);
|
EXPORT_SYMBOL(clevo_keyboard_remove_interface);
|
||||||
|
|
||||||
struct tuxedo_keyboard_driver clevo_keyboard_driver;
|
|
||||||
|
|
||||||
static struct key_entry clevo_keymap[] = {
|
static struct key_entry clevo_keymap[] = {
|
||||||
// Keyboard backlight (RGB versions)
|
// Keyboard backlight (RGB versions)
|
||||||
{ KE_KEY, CLEVO_EVENT_DECREASE_BACKLIGHT, { KEY_KBDILLUMDOWN } },
|
{ KE_KEY, CLEVO_EVENT_DECREASE_BACKLIGHT, { KEY_KBDILLUMDOWN } },
|
||||||
|
@ -299,7 +305,7 @@ static ssize_t show_hasextra_fs(struct device *child,
|
||||||
u32 clevo_evaluate_method(u8 cmd, u32 arg, u32 *result)
|
u32 clevo_evaluate_method(u8 cmd, u32 arg, u32 *result)
|
||||||
{
|
{
|
||||||
if (IS_ERR_OR_NULL(active_clevo_interface)) {
|
if (IS_ERR_OR_NULL(active_clevo_interface)) {
|
||||||
pr_err("clevo_keyboard: no active interface\n");
|
pr_err("clevo_keyboard: no active interface while attempting cmd %02x arg %08x\n", cmd, arg);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
return active_clevo_interface->method_call(cmd, arg, result);
|
return active_clevo_interface->method_call(cmd, arg, result);
|
||||||
|
@ -705,9 +711,65 @@ void clevo_keyboard_write_state(void)
|
||||||
set_enabled(kbd_led_state.enabled);
|
set_enabled(kbd_led_state.enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int clevo_keyboard_probe_only_init(struct platform_device *dev)
|
/**
|
||||||
|
* strstr version of dmi_match
|
||||||
|
*/
|
||||||
|
static bool dmi_string_in(enum dmi_field f, const char *str)
|
||||||
|
{
|
||||||
|
const char *info = dmi_get_system_info(f);
|
||||||
|
|
||||||
|
if (info == NULL || str == NULL)
|
||||||
|
return info == str;
|
||||||
|
|
||||||
|
return strstr(info, str) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int clevo_keyboard_init(void)
|
||||||
|
{
|
||||||
|
bool performance_profile_set_workaround;
|
||||||
|
|
||||||
|
// Init state from params
|
||||||
|
kbd_led_state.color.left = param_color_left;
|
||||||
|
kbd_led_state.color.center = param_color_center;
|
||||||
|
kbd_led_state.color.right = param_color_right;
|
||||||
|
kbd_led_state.color.extra = param_color_extra;
|
||||||
|
|
||||||
|
kbd_led_state.blinking_pattern = param_blinking_pattern;
|
||||||
|
|
||||||
|
if (param_brightness > BRIGHTNESS_MAX) param_brightness = BRIGHTNESS_DEFAULT;
|
||||||
|
kbd_led_state.brightness = param_brightness;
|
||||||
|
|
||||||
|
kbd_led_state.enabled = param_state;
|
||||||
|
|
||||||
|
clevo_keyboard_write_state();
|
||||||
|
|
||||||
|
// Workaround for firmware issue not setting selected performance profile.
|
||||||
|
// Explicitly set "performance" perf. profile on init regardless of what is chosen
|
||||||
|
// for these devices (Aura, XP14, IBS14v5)
|
||||||
|
performance_profile_set_workaround = false
|
||||||
|
|| dmi_string_in(DMI_BOARD_NAME, "AURA1501")
|
||||||
|
|| dmi_string_in(DMI_BOARD_NAME, "EDUBOOK1502")
|
||||||
|
|| dmi_string_in(DMI_BOARD_NAME, "NL5xRU")
|
||||||
|
|| dmi_string_in(DMI_BOARD_NAME, "NV4XMB,ME,MZ")
|
||||||
|
|| dmi_string_in(DMI_BOARD_NAME, "L140CU")
|
||||||
|
|| dmi_string_in(DMI_BOARD_NAME, "NS50MU")
|
||||||
|
|| dmi_string_in(DMI_BOARD_NAME, "PCX0DX")
|
||||||
|
|| dmi_string_in(DMI_BOARD_NAME, "PCx0Dx_GN20")
|
||||||
|
|| dmi_string_in(DMI_BOARD_NAME, "L14xMU")
|
||||||
|
;
|
||||||
|
if (performance_profile_set_workaround) {
|
||||||
|
TUXEDO_INFO("Performance profile 'performance' set workaround applied\n");
|
||||||
|
clevo_evaluate_method(0x79, 0x19000002, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int clevo_keyboard_probe(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
clevo_keyboard_init_device_interface(dev);
|
clevo_keyboard_init_device_interface(dev);
|
||||||
|
clevo_keyboard_init();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -759,66 +821,8 @@ static struct platform_driver platform_driver_clevo = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tuxedo_keyboard_driver clevo_keyboard_driver_v2 = {
|
struct tuxedo_keyboard_driver clevo_keyboard_driver = {
|
||||||
.platform_driver = &platform_driver_clevo,
|
.platform_driver = &platform_driver_clevo,
|
||||||
.probe = clevo_keyboard_probe_only_init,
|
.probe = clevo_keyboard_probe,
|
||||||
.key_map = clevo_keymap,
|
.key_map = clevo_keymap,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* strstr version of dmi_match
|
|
||||||
*/
|
|
||||||
static bool dmi_string_in(enum dmi_field f, const char *str)
|
|
||||||
{
|
|
||||||
const char *info = dmi_get_system_info(f);
|
|
||||||
|
|
||||||
if (info == NULL || str == NULL)
|
|
||||||
return info == str;
|
|
||||||
|
|
||||||
return strstr(info, str) != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int clevo_keyboard_init(void)
|
|
||||||
{
|
|
||||||
bool performance_profile_set_workaround;
|
|
||||||
|
|
||||||
if (IS_ERR_OR_NULL(tuxedo_keyboard_init_driver(&clevo_keyboard_driver_v2)))
|
|
||||||
return -EEXIST;
|
|
||||||
|
|
||||||
// Init state from params
|
|
||||||
kbd_led_state.color.left = param_color_left;
|
|
||||||
kbd_led_state.color.center = param_color_center;
|
|
||||||
kbd_led_state.color.right = param_color_right;
|
|
||||||
kbd_led_state.color.extra = param_color_extra;
|
|
||||||
|
|
||||||
kbd_led_state.blinking_pattern = param_blinking_pattern;
|
|
||||||
|
|
||||||
if (param_brightness > BRIGHTNESS_MAX) param_brightness = BRIGHTNESS_DEFAULT;
|
|
||||||
kbd_led_state.brightness = param_brightness;
|
|
||||||
|
|
||||||
kbd_led_state.enabled = param_state;
|
|
||||||
|
|
||||||
clevo_keyboard_write_state();
|
|
||||||
|
|
||||||
// Workaround for firmware issue not setting selected performance profile.
|
|
||||||
// Explicitly set "performance" perf. profile on init regardless of what is chosen
|
|
||||||
// for these devices (Aura, XP14, IBS14v5)
|
|
||||||
performance_profile_set_workaround = false
|
|
||||||
|| dmi_string_in(DMI_BOARD_NAME, "AURA1501")
|
|
||||||
|| dmi_string_in(DMI_BOARD_NAME, "EDUBOOK1502")
|
|
||||||
|| dmi_string_in(DMI_BOARD_NAME, "NL5xRU")
|
|
||||||
|| dmi_string_in(DMI_BOARD_NAME, "NV4XMB,ME,MZ")
|
|
||||||
|| dmi_string_in(DMI_BOARD_NAME, "L140CU")
|
|
||||||
|| dmi_string_in(DMI_BOARD_NAME, "NS50MU")
|
|
||||||
|| dmi_string_in(DMI_BOARD_NAME, "PCX0DX")
|
|
||||||
|| dmi_string_in(DMI_BOARD_NAME, "PCx0Dx_GN20")
|
|
||||||
|| dmi_string_in(DMI_BOARD_NAME, "L14xMU")
|
|
||||||
;
|
|
||||||
if (performance_profile_set_workaround) {
|
|
||||||
TUXEDO_INFO("Performance profile 'performance' set workaround applied\n");
|
|
||||||
clevo_evaluate_method(0x79, 0x19000002, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(clevo_keyboard_init);
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ u32 clevo_wmi_interface_method_call(u8 cmd, u32 arg, u32 *result_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct clevo_interface_t clevo_wmi_interface = {
|
struct clevo_interface_t clevo_wmi_interface = {
|
||||||
.string_id = "clevo_wmi",
|
.string_id = CLEVO_INTERFACE_WMI_STRID,
|
||||||
.method_call = clevo_wmi_interface_method_call,
|
.method_call = clevo_wmi_interface_method_call,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,9 +110,6 @@ static int clevo_wmi_probe(struct wmi_device *wdev, const void *dummy_context)
|
||||||
// Add this interface
|
// Add this interface
|
||||||
clevo_keyboard_add_interface(&clevo_wmi_interface);
|
clevo_keyboard_add_interface(&clevo_wmi_interface);
|
||||||
|
|
||||||
// Initiate clevo keyboard, if not already loaded by other interface driver
|
|
||||||
clevo_keyboard_init();
|
|
||||||
|
|
||||||
pr_info("interface initialized\n");
|
pr_info("interface initialized\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -125,6 +122,7 @@ static void clevo_wmi_remove(struct wmi_device *wdev)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
pr_debug("clevo_wmi driver remove\n");
|
pr_debug("clevo_wmi driver remove\n");
|
||||||
|
clevo_keyboard_remove_interface(&clevo_wmi_interface);
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0)
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
|
@ -149,7 +147,10 @@ static const struct wmi_device_id clevo_wmi_device_ids[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct wmi_driver clevo_wmi_driver = {
|
static struct wmi_driver clevo_wmi_driver = {
|
||||||
.driver = { .name = "clevo_wmi", .owner = THIS_MODULE },
|
.driver = {
|
||||||
|
.name = CLEVO_INTERFACE_WMI_STRID,
|
||||||
|
.owner = THIS_MODULE
|
||||||
|
},
|
||||||
.id_table = clevo_wmi_device_ids,
|
.id_table = clevo_wmi_device_ids,
|
||||||
.probe = clevo_wmi_probe,
|
.probe = clevo_wmi_probe,
|
||||||
.remove = clevo_wmi_remove,
|
.remove = clevo_wmi_remove,
|
||||||
|
@ -160,7 +161,7 @@ module_wmi_driver(clevo_wmi_driver);
|
||||||
|
|
||||||
MODULE_AUTHOR("TUXEDO Computers GmbH <tux@tuxedocomputers.com>");
|
MODULE_AUTHOR("TUXEDO Computers GmbH <tux@tuxedocomputers.com>");
|
||||||
MODULE_DESCRIPTION("Driver for Clevo WMI interface");
|
MODULE_DESCRIPTION("Driver for Clevo WMI interface");
|
||||||
MODULE_VERSION("0.0.3");
|
MODULE_VERSION("0.0.4");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(wmi, clevo_wmi_device_ids);
|
MODULE_DEVICE_TABLE(wmi, clevo_wmi_device_ids);
|
||||||
|
|
|
@ -1,359 +0,0 @@
|
||||||
/*!
|
|
||||||
* Copyright (c) 2020 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
|
||||||
*
|
|
||||||
* This file is part of tuxedo-io.
|
|
||||||
*
|
|
||||||
* tuxedo-io is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this software. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#ifndef TONGFANG_WMI_H
|
|
||||||
#define TONGFANG_WMI_H
|
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
|
||||||
#include <linux/wmi.h>
|
|
||||||
#include <linux/mutex.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
|
|
||||||
#define UNIWILL_WMI_MGMT_GUID_BA "ABBC0F6D-8EA1-11D1-00A0-C90629100000"
|
|
||||||
#define UNIWILL_WMI_MGMT_GUID_BB "ABBC0F6E-8EA1-11D1-00A0-C90629100000"
|
|
||||||
#define UNIWILL_WMI_MGMT_GUID_BC "ABBC0F6F-8EA1-11D1-00A0-C90629100000"
|
|
||||||
|
|
||||||
#define UNIWILL_WMI_EVENT_GUID_0 "ABBC0F70-8EA1-11D1-00A0-C90629100000"
|
|
||||||
#define UNIWILL_WMI_EVENT_GUID_1 "ABBC0F71-8EA1-11D1-00A0-C90629100000"
|
|
||||||
#define UNIWILL_WMI_EVENT_GUID_2 "ABBC0F72-8EA1-11D1-00A0-C90629100000"
|
|
||||||
|
|
||||||
#define UNIWILL_EC_REG_LDAT 0x8a
|
|
||||||
#define UNIWILL_EC_REG_HDAT 0x8b
|
|
||||||
#define UNIWILL_EC_REG_FLAGS 0x8c
|
|
||||||
#define UNIWILL_EC_REG_CMDL 0x8d
|
|
||||||
#define UNIWILL_EC_REG_CMDH 0x8e
|
|
||||||
|
|
||||||
#define UNIWILL_EC_BIT_RFLG 0
|
|
||||||
#define UNIWILL_EC_BIT_WFLG 1
|
|
||||||
#define UNIWILL_EC_BIT_BFLG 2
|
|
||||||
#define UNIWILL_EC_BIT_CFLG 3
|
|
||||||
#define UNIWILL_EC_BIT_DRDY 7
|
|
||||||
|
|
||||||
#define UW_EC_WAIT_CYCLES 0x50
|
|
||||||
|
|
||||||
union uw_ec_read_return {
|
|
||||||
u32 dword;
|
|
||||||
struct {
|
|
||||||
u8 data_low;
|
|
||||||
u8 data_high;
|
|
||||||
} bytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
union uw_ec_write_return {
|
|
||||||
u32 dword;
|
|
||||||
struct {
|
|
||||||
u8 addr_low;
|
|
||||||
u8 addr_high;
|
|
||||||
u8 data_low;
|
|
||||||
u8 data_high;
|
|
||||||
} bytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool uniwill_ec_direct = true;
|
|
||||||
|
|
||||||
DEFINE_MUTEX(uniwill_ec_lock);
|
|
||||||
|
|
||||||
static u32 uw_wmi_ec_evaluate(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, u8 read_flag, u32 *return_buffer)
|
|
||||||
{
|
|
||||||
acpi_status status;
|
|
||||||
union acpi_object *out_acpi;
|
|
||||||
u32 e_result = 0;
|
|
||||||
|
|
||||||
// Kernel buffer for input argument
|
|
||||||
u32 *wmi_arg = (u32 *) kmalloc(sizeof(u32)*10, GFP_KERNEL);
|
|
||||||
// Byte reference to the input buffer
|
|
||||||
u8 *wmi_arg_bytes = (u8 *) wmi_arg;
|
|
||||||
|
|
||||||
u8 wmi_instance = 0x00;
|
|
||||||
u32 wmi_method_id = 0x04;
|
|
||||||
struct acpi_buffer wmi_in = { (acpi_size) sizeof(wmi_arg), wmi_arg};
|
|
||||||
struct acpi_buffer wmi_out = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
||||||
|
|
||||||
mutex_lock(&uniwill_ec_lock);
|
|
||||||
|
|
||||||
// Zero input buffer
|
|
||||||
memset(wmi_arg, 0x00, 10 * sizeof(u32));
|
|
||||||
|
|
||||||
// Configure the input buffer
|
|
||||||
wmi_arg_bytes[0] = addr_low;
|
|
||||||
wmi_arg_bytes[1] = addr_high;
|
|
||||||
wmi_arg_bytes[2] = data_low;
|
|
||||||
wmi_arg_bytes[3] = data_high;
|
|
||||||
|
|
||||||
if (read_flag != 0) {
|
|
||||||
wmi_arg_bytes[5] = 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = wmi_evaluate_method(UNIWILL_WMI_MGMT_GUID_BC, wmi_instance, wmi_method_id, &wmi_in, &wmi_out);
|
|
||||||
out_acpi = (union acpi_object *) wmi_out.pointer;
|
|
||||||
|
|
||||||
if (out_acpi && out_acpi->type == ACPI_TYPE_BUFFER) {
|
|
||||||
memcpy(return_buffer, out_acpi->buffer.pointer, out_acpi->buffer.length);
|
|
||||||
} /* else if (out_acpi && out_acpi->type == ACPI_TYPE_INTEGER) {
|
|
||||||
e_result = (u32) out_acpi->integer.value;
|
|
||||||
}*/
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
pr_err("uniwill_wmi.h: Error evaluating method\n");
|
|
||||||
e_result = -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
kfree(out_acpi);
|
|
||||||
kfree(wmi_arg);
|
|
||||||
|
|
||||||
mutex_unlock(&uniwill_ec_lock);
|
|
||||||
|
|
||||||
return e_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EC address read through WMI
|
|
||||||
*/
|
|
||||||
static u32 uw_ec_read_addr_wmi(u8 addr_low, u8 addr_high, union uw_ec_read_return *output)
|
|
||||||
{
|
|
||||||
u32 uw_data[10];
|
|
||||||
u32 ret = uw_wmi_ec_evaluate(addr_low, addr_high, 0x00, 0x00, 1, uw_data);
|
|
||||||
output->dword = uw_data[0];
|
|
||||||
// pr_debug("addr: 0x%02x%02x value: %0#4x (high: %0#4x) result: %d\n", addr_high, addr_low, output->bytes.data_low, output->bytes.data_high, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EC address write through WMI
|
|
||||||
*/
|
|
||||||
static u32 uw_ec_write_addr_wmi(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, union uw_ec_write_return *output)
|
|
||||||
{
|
|
||||||
u32 uw_data[10];
|
|
||||||
u32 ret = uw_wmi_ec_evaluate(addr_low, addr_high, data_low, data_high, 0, uw_data);
|
|
||||||
output->dword = uw_data[0];
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Direct EC address read
|
|
||||||
*/
|
|
||||||
static u32 uw_ec_read_addr_direct(u8 addr_low, u8 addr_high, union uw_ec_read_return *output)
|
|
||||||
{
|
|
||||||
u32 result;
|
|
||||||
u8 tmp, count, flags;
|
|
||||||
|
|
||||||
mutex_lock(&uniwill_ec_lock);
|
|
||||||
|
|
||||||
ec_write(UNIWILL_EC_REG_LDAT, addr_low);
|
|
||||||
ec_write(UNIWILL_EC_REG_HDAT, addr_high);
|
|
||||||
|
|
||||||
flags = (0 << UNIWILL_EC_BIT_DRDY) | (1 << UNIWILL_EC_BIT_RFLG);
|
|
||||||
ec_write(UNIWILL_EC_REG_FLAGS, flags);
|
|
||||||
|
|
||||||
// Wait for ready flag
|
|
||||||
count = UW_EC_WAIT_CYCLES;
|
|
||||||
ec_read(UNIWILL_EC_REG_FLAGS, &tmp);
|
|
||||||
while (((tmp & (1 << UNIWILL_EC_BIT_DRDY)) == 0) && count != 0) {
|
|
||||||
msleep(1);
|
|
||||||
ec_read(UNIWILL_EC_REG_FLAGS, &tmp);
|
|
||||||
count -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count != 0) {
|
|
||||||
output->dword = 0;
|
|
||||||
ec_read(UNIWILL_EC_REG_CMDL, &tmp);
|
|
||||||
output->bytes.data_low = tmp;
|
|
||||||
ec_read(UNIWILL_EC_REG_CMDH, &tmp);
|
|
||||||
output->bytes.data_high = tmp;
|
|
||||||
result = 0;
|
|
||||||
} else {
|
|
||||||
output->dword = 0xfefefefe;
|
|
||||||
result = -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
ec_write(UNIWILL_EC_REG_FLAGS, 0x00);
|
|
||||||
|
|
||||||
mutex_unlock(&uniwill_ec_lock);
|
|
||||||
|
|
||||||
// pr_debug("addr: 0x%02x%02x value: %0#4x result: %d\n", addr_high, addr_low, output->bytes.data_low, result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 uw_ec_write_addr_direct(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, union uw_ec_write_return *output)
|
|
||||||
{
|
|
||||||
u32 result = 0;
|
|
||||||
u8 tmp, count, flags;
|
|
||||||
|
|
||||||
mutex_lock(&uniwill_ec_lock);
|
|
||||||
|
|
||||||
ec_write(UNIWILL_EC_REG_LDAT, addr_low);
|
|
||||||
ec_write(UNIWILL_EC_REG_HDAT, addr_high);
|
|
||||||
ec_write(UNIWILL_EC_REG_CMDL, data_low);
|
|
||||||
ec_write(UNIWILL_EC_REG_CMDH, data_high);
|
|
||||||
|
|
||||||
flags = (0 << UNIWILL_EC_BIT_DRDY) | (1 << UNIWILL_EC_BIT_WFLG);
|
|
||||||
ec_write(UNIWILL_EC_REG_FLAGS, flags);
|
|
||||||
|
|
||||||
// Wait for ready flag
|
|
||||||
count = UW_EC_WAIT_CYCLES;
|
|
||||||
ec_read(UNIWILL_EC_REG_FLAGS, &tmp);
|
|
||||||
while (((tmp & (1 << UNIWILL_EC_BIT_DRDY)) == 0) && count != 0) {
|
|
||||||
msleep(1);
|
|
||||||
ec_read(UNIWILL_EC_REG_FLAGS, &tmp);
|
|
||||||
count -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replicate wmi output depending on success
|
|
||||||
if (count != 0) {
|
|
||||||
output->bytes.addr_low = addr_low;
|
|
||||||
output->bytes.addr_high = addr_high;
|
|
||||||
output->bytes.data_low = data_low;
|
|
||||||
output->bytes.data_high = data_high;
|
|
||||||
result = 0;
|
|
||||||
} else {
|
|
||||||
output->dword = 0xfefefefe;
|
|
||||||
result = -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
ec_write(UNIWILL_EC_REG_FLAGS, 0x00);
|
|
||||||
|
|
||||||
mutex_unlock(&uniwill_ec_lock);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 uw_ec_read_addr(u8 addr_low, u8 addr_high, union uw_ec_read_return *output)
|
|
||||||
{
|
|
||||||
if (uniwill_ec_direct) {
|
|
||||||
return uw_ec_read_addr_direct(addr_low, addr_high, output);
|
|
||||||
} else {
|
|
||||||
return uw_ec_read_addr_wmi(addr_low, addr_high, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(uw_ec_read_addr);
|
|
||||||
|
|
||||||
u32 uw_ec_write_addr(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, union uw_ec_write_return *output)
|
|
||||||
{
|
|
||||||
if (uniwill_ec_direct) {
|
|
||||||
return uw_ec_write_addr_direct(addr_low, addr_high, data_low, data_high, output);
|
|
||||||
} else {
|
|
||||||
return uw_ec_write_addr_wmi(addr_low, addr_high, data_low, data_high, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(uw_ec_write_addr);
|
|
||||||
|
|
||||||
static u32 uniwill_identify(void)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
|
|
||||||
// Look for for GUIDs used on uniwill devices
|
|
||||||
status =
|
|
||||||
wmi_has_guid(UNIWILL_WMI_EVENT_GUID_0) &&
|
|
||||||
wmi_has_guid(UNIWILL_WMI_EVENT_GUID_1) &&
|
|
||||||
wmi_has_guid(UNIWILL_WMI_EVENT_GUID_2) &&
|
|
||||||
wmi_has_guid(UNIWILL_WMI_MGMT_GUID_BA) &&
|
|
||||||
wmi_has_guid(UNIWILL_WMI_MGMT_GUID_BB) &&
|
|
||||||
wmi_has_guid(UNIWILL_WMI_MGMT_GUID_BC);
|
|
||||||
|
|
||||||
if (!status)
|
|
||||||
{
|
|
||||||
pr_debug("probe: At least one Uniwill GUID missing\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uniwill_init(void)
|
|
||||||
{
|
|
||||||
u32 i;
|
|
||||||
union uw_ec_read_return reg_read_return;
|
|
||||||
union uw_ec_write_return reg_write_return;
|
|
||||||
|
|
||||||
// FIXME Hard set balanced profile until we have implemented a way to
|
|
||||||
// switch it while tuxedo_io is loaded
|
|
||||||
uw_ec_write_addr(0x51, 0x07, 0x00, 0x00, ®_write_return);
|
|
||||||
|
|
||||||
// Set manual-mode fan-curve in 0x0743 - 0x0747
|
|
||||||
// Some kind of default fan-curve is stored in 0x0786 - 0x078a: Using it to initialize manual-mode fan-curve
|
|
||||||
for (i = 0; i < 5; ++i) {
|
|
||||||
uw_ec_read_addr(0x86 + i, 0x07, ®_read_return);
|
|
||||||
uw_ec_write_addr(0x43 + i, 0x07, reg_read_return.bytes.data_low, 0x00, ®_write_return);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable manual mode
|
|
||||||
uw_ec_write_addr(0x41, 0x07, 0x01, 0x00, ®_write_return);
|
|
||||||
|
|
||||||
// Zero second fan temp for detection
|
|
||||||
uw_ec_write_addr(0x4f, 0x04, 0x00, 0x00, ®_write_return);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uniwill_exit(void)
|
|
||||||
{
|
|
||||||
union uw_ec_write_return reg_write_return;
|
|
||||||
|
|
||||||
// Disable manual mode
|
|
||||||
uw_ec_write_addr(0x41, 0x07, 0x00, 0x00, ®_write_return);
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 uw_set_fan(u32 fan_index, u8 fan_speed)
|
|
||||||
{
|
|
||||||
u8 reg_low, reg_high = 0x18;
|
|
||||||
u32 i;
|
|
||||||
union uw_ec_read_return reg_read_return;
|
|
||||||
union uw_ec_write_return reg_write_return;
|
|
||||||
u8 low_reg_fan0 = 0x04;
|
|
||||||
u8 low_reg_fan1 = 0x09;
|
|
||||||
|
|
||||||
if (fan_index == 0)
|
|
||||||
reg_low = low_reg_fan0;
|
|
||||||
else if (fan_index == 1)
|
|
||||||
reg_low = low_reg_fan1;
|
|
||||||
else
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
// Check current mode
|
|
||||||
uw_ec_read_addr(0x51, 0x07, ®_read_return);
|
|
||||||
if (!(reg_read_return.bytes.data_low & 0x40)) {
|
|
||||||
// If not "full fan mode" (i.e. 0x40 bit set) switch to it (required for fancontrol)
|
|
||||||
uw_ec_write_addr(0x51, 0x07, reg_read_return.bytes.data_low | 0x40, 0x00, ®_write_return);
|
|
||||||
// Attempt to write both fans as quick as possible before complete ramp-up
|
|
||||||
pr_debug("prevent ramp-up start\n");
|
|
||||||
for (i = 0; i < 10; ++i) {
|
|
||||||
uw_ec_write_addr(low_reg_fan0, reg_high, fan_speed & 0xff, 0x00, ®_write_return);
|
|
||||||
uw_ec_write_addr(low_reg_fan1, reg_high, fan_speed & 0xff, 0x00, ®_write_return);
|
|
||||||
msleep(10);
|
|
||||||
}
|
|
||||||
pr_debug("prevent ramp-up done\n");
|
|
||||||
} else {
|
|
||||||
// Otherwise just set the chosen fan
|
|
||||||
uw_ec_write_addr(reg_low, reg_high, fan_speed & 0xff, 0x00, ®_write_return);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 uw_set_fan_auto(void)
|
|
||||||
{
|
|
||||||
union uw_ec_read_return reg_read_return;
|
|
||||||
union uw_ec_write_return reg_write_return;
|
|
||||||
|
|
||||||
// Get current mode
|
|
||||||
uw_ec_read_addr(0x51, 0x07, ®_read_return);
|
|
||||||
// Switch off "full fan mode" (i.e. unset 0x40 bit)
|
|
||||||
uw_ec_write_addr(0x51, 0x07, reg_read_return.bytes.data_low & 0xbf, 0x00, ®_write_return);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -27,7 +27,7 @@
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include "../clevo_interfaces.h"
|
#include "../clevo_interfaces.h"
|
||||||
#include "tongfang_wmi.h"
|
#include "../uniwill_interfaces.h"
|
||||||
#include "tuxedo_io_ioctl.h"
|
#include "tuxedo_io_ioctl.h"
|
||||||
|
|
||||||
MODULE_DESCRIPTION("Hardware interface for TUXEDO laptops");
|
MODULE_DESCRIPTION("Hardware interface for TUXEDO laptops");
|
||||||
|
@ -50,6 +50,11 @@ static u32 clevo_identify(void)
|
||||||
return clevo_get_active_interface_id(NULL) == 0 ? 1 : 0;
|
return clevo_get_active_interface_id(NULL) == 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 uniwill_identify(void)
|
||||||
|
{
|
||||||
|
return uniwill_get_active_interface_id(NULL) == 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*static int fop_open(struct inode *inode, struct file *file)
|
/*static int fop_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -151,11 +156,61 @@ static long clevo_ioctl_interface(struct file *file, unsigned int cmd, unsigned
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 uw_set_fan(u32 fan_index, u8 fan_speed)
|
||||||
|
{
|
||||||
|
u32 i;
|
||||||
|
u8 mode_data;
|
||||||
|
u16 addr_fan0 = 0x1804;
|
||||||
|
u16 addr_fan1 = 0x1809;
|
||||||
|
u16 addr_for_fan;
|
||||||
|
|
||||||
|
if (fan_index == 0)
|
||||||
|
addr_for_fan = addr_fan0;
|
||||||
|
else if (fan_index == 1)
|
||||||
|
addr_for_fan = addr_fan1;
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
// Check current mode
|
||||||
|
uniwill_read_ec_ram(0x0751, &mode_data);
|
||||||
|
if (!(mode_data & 0x40)) {
|
||||||
|
// If not "full fan mode" (i.e. 0x40 bit set) switch to it (required for fancontrol)
|
||||||
|
uniwill_write_ec_ram(0x0751, mode_data | 0x40);
|
||||||
|
// Attempt to write both fans as quick as possible before complete ramp-up
|
||||||
|
pr_debug("prevent ramp-up start\n");
|
||||||
|
for (i = 0; i < 10; ++i) {
|
||||||
|
uniwill_write_ec_ram(addr_fan0, fan_speed & 0xff);
|
||||||
|
uniwill_write_ec_ram(addr_fan1, fan_speed & 0xff);
|
||||||
|
msleep(10);
|
||||||
|
}
|
||||||
|
pr_debug("prevent ramp-up done\n");
|
||||||
|
} else {
|
||||||
|
// Otherwise just set the chosen fan
|
||||||
|
uniwill_write_ec_ram(addr_for_fan, fan_speed & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 uw_set_fan_auto(void)
|
||||||
|
{
|
||||||
|
u8 mode_data;
|
||||||
|
// Get current mode
|
||||||
|
uniwill_read_ec_ram(0x0751, &mode_data);
|
||||||
|
// Switch off "full fan mode" (i.e. unset 0x40 bit)
|
||||||
|
uniwill_write_ec_ram(0x0751, mode_data & 0xbf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static long uniwill_ioctl_interface(struct file *file, unsigned int cmd, unsigned long arg)
|
static long uniwill_ioctl_interface(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
u32 result = 0;
|
u32 result = 0;
|
||||||
u32 copy_result;
|
u32 copy_result;
|
||||||
u32 argument;
|
u32 argument;
|
||||||
|
u8 byte_data;
|
||||||
|
const char str_no_if[] = "";
|
||||||
|
char *str_uniwill_if;
|
||||||
union uw_ec_read_return reg_read_return;
|
union uw_ec_read_return reg_read_return;
|
||||||
union uw_ec_write_return reg_write_return;
|
union uw_ec_write_return reg_write_return;
|
||||||
|
|
||||||
|
@ -169,47 +224,57 @@ static long uniwill_ioctl_interface(struct file *file, unsigned int cmd, unsigne
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
case R_UW_HW_IF_STR:
|
||||||
|
if (uniwill_get_active_interface_id(&str_uniwill_if) == 0) {
|
||||||
|
copy_result = copy_to_user((char *) arg, str_uniwill_if, strlen(str_uniwill_if) + 1);
|
||||||
|
} else {
|
||||||
|
copy_result = copy_to_user((char *) arg, str_no_if, strlen(str_no_if) + 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case R_UW_FANSPEED:
|
case R_UW_FANSPEED:
|
||||||
uw_ec_read_addr(0x04, 0x18, ®_read_return);
|
uniwill_read_ec_ram(0x1804, &byte_data);
|
||||||
result = reg_read_return.bytes.data_low;
|
result = byte_data;
|
||||||
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
||||||
break;
|
break;
|
||||||
case R_UW_FANSPEED2:
|
case R_UW_FANSPEED2:
|
||||||
uw_ec_read_addr(0x09, 0x18, ®_read_return);
|
uniwill_read_ec_ram(0x1809, &byte_data);
|
||||||
result = reg_read_return.bytes.data_low;
|
result = byte_data;
|
||||||
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
||||||
break;
|
break;
|
||||||
case R_UW_FAN_TEMP:
|
case R_UW_FAN_TEMP:
|
||||||
uw_ec_read_addr(0x3e, 0x04, ®_read_return);
|
uniwill_read_ec_ram(0x043e, &byte_data);
|
||||||
result = reg_read_return.bytes.data_low;
|
result = byte_data;
|
||||||
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
||||||
break;
|
break;
|
||||||
case R_UW_FAN_TEMP2:
|
case R_UW_FAN_TEMP2:
|
||||||
uw_ec_read_addr(0x4f, 0x04, ®_read_return);
|
uniwill_read_ec_ram(0x044f, &byte_data);
|
||||||
result = reg_read_return.bytes.data_low;
|
result = byte_data;
|
||||||
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
||||||
break;
|
break;
|
||||||
case R_UW_MODE:
|
case R_UW_MODE:
|
||||||
uw_ec_read_addr(0x51, 0x07, ®_read_return);
|
uniwill_read_ec_ram(0x0751, &byte_data);
|
||||||
result = reg_read_return.bytes.data_low;
|
result = byte_data;
|
||||||
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
||||||
break;
|
break;
|
||||||
case R_UW_MODE_ENABLE:
|
case R_UW_MODE_ENABLE:
|
||||||
uw_ec_read_addr(0x41, 0x07, ®_read_return);
|
uniwill_read_ec_ram(0x0741, &byte_data);
|
||||||
result = reg_read_return.bytes.data_low;
|
result = byte_data;
|
||||||
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
||||||
break;
|
break;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
case R_TF_BC:
|
case R_TF_BC:
|
||||||
copy_result = copy_from_user(&uw_arg, (void *) arg, sizeof(uw_arg));
|
copy_result = copy_from_user(&uw_arg, (void *) arg, sizeof(uw_arg));
|
||||||
|
reg_read_return.dword = 0;
|
||||||
|
result = uniwill_read_ec_ram((uw_arg[1] << 8) | uw_arg[0], ®_read_return.bytes.data_low);
|
||||||
|
copy_result = copy_to_user((void *) arg, ®_read_return.dword, sizeof(reg_read_return.dword));
|
||||||
// pr_info("R_TF_BC args [%0#2x, %0#2x, %0#2x, %0#2x]\n", uw_arg[0], uw_arg[1], uw_arg[2], uw_arg[3]);
|
// pr_info("R_TF_BC args [%0#2x, %0#2x, %0#2x, %0#2x]\n", uw_arg[0], uw_arg[1], uw_arg[2], uw_arg[3]);
|
||||||
if (uniwill_ec_direct) {
|
/*if (uniwill_ec_direct) {
|
||||||
result = uw_ec_read_addr_direct(uw_arg[0], uw_arg[1], ®_read_return);
|
result = uw_ec_read_addr_direct(uw_arg[0], uw_arg[1], ®_read_return);
|
||||||
copy_result = copy_to_user((void *) arg, ®_read_return.dword, sizeof(reg_read_return.dword));
|
copy_result = copy_to_user((void *) arg, ®_read_return.dword, sizeof(reg_read_return.dword));
|
||||||
} else {
|
} else {
|
||||||
result = uw_wmi_ec_evaluate(uw_arg[0], uw_arg[1], uw_arg[2], uw_arg[3], 1, uw_result);
|
result = uw_wmi_ec_evaluate(uw_arg[0], uw_arg[1], uw_arg[2], uw_arg[3], 1, uw_result);
|
||||||
copy_result = copy_to_user((void *) arg, &uw_result, sizeof(uw_result));
|
copy_result = copy_to_user((void *) arg, &uw_result, sizeof(uw_result));
|
||||||
}
|
}*/
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -227,29 +292,32 @@ static long uniwill_ioctl_interface(struct file *file, unsigned int cmd, unsigne
|
||||||
break;
|
break;
|
||||||
case W_UW_MODE:
|
case W_UW_MODE:
|
||||||
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument));
|
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument));
|
||||||
uw_ec_write_addr(0x51, 0x07, argument & 0xff, 0x00, ®_write_return);
|
uniwill_write_ec_ram(0x0751, argument & 0xff);
|
||||||
break;
|
break;
|
||||||
case W_UW_MODE_ENABLE:
|
case W_UW_MODE_ENABLE:
|
||||||
// Note: Is for the moment set and cleared on init/exit of module (uniwill mode)
|
// Note: Is for the moment set and cleared on init/exit of module (uniwill mode)
|
||||||
/*
|
/*
|
||||||
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument));
|
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument));
|
||||||
uw_ec_write_addr(0x41, 0x07, argument & 0x01, 0x00, ®_write_return);
|
uniwill_write_ec_ram(0x0741, argument & 0x01);
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
case W_UW_FANAUTO:
|
case W_UW_FANAUTO:
|
||||||
uw_set_fan_auto();
|
uw_set_fan_auto();
|
||||||
break;
|
break;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
case W_TF_BC:
|
case W_TF_BC:
|
||||||
|
reg_write_return.dword = 0;
|
||||||
copy_result = copy_from_user(&uw_arg, (void *) arg, sizeof(uw_arg));
|
copy_result = copy_from_user(&uw_arg, (void *) arg, sizeof(uw_arg));
|
||||||
if (uniwill_ec_direct) {
|
uniwill_write_ec_ram((uw_arg[1] << 8) | uw_arg[0], uw_arg[2]);
|
||||||
|
copy_result = copy_to_user((void *) arg, ®_write_return.dword, sizeof(reg_write_return.dword));
|
||||||
|
/*if (uniwill_ec_direct) {
|
||||||
result = uw_ec_write_addr_direct(uw_arg[0], uw_arg[1], uw_arg[2], uw_arg[3], ®_write_return);
|
result = uw_ec_write_addr_direct(uw_arg[0], uw_arg[1], uw_arg[2], uw_arg[3], ®_write_return);
|
||||||
copy_result = copy_to_user((void *) arg, ®_write_return.dword, sizeof(reg_write_return.dword));
|
copy_result = copy_to_user((void *) arg, ®_write_return.dword, sizeof(reg_write_return.dword));
|
||||||
} else {
|
} else {
|
||||||
result = uw_wmi_ec_evaluate(uw_arg[0], uw_arg[1], uw_arg[2], uw_arg[3], 0, uw_result);
|
result = uw_wmi_ec_evaluate(uw_arg[0], uw_arg[1], uw_arg[2], uw_arg[3], 0, uw_result);
|
||||||
copy_result = copy_to_user((void *) arg, &uw_result, sizeof(uw_result));
|
copy_result = copy_to_user((void *) arg, &uw_result, sizeof(uw_result));
|
||||||
reg_write_return.dword = uw_result[0];
|
reg_write_return.dword = uw_result[0];
|
||||||
}
|
}*/
|
||||||
/*pr_info("data_high %0#2x\n", reg_write_return.bytes.data_high);
|
/*pr_info("data_high %0#2x\n", reg_write_return.bytes.data_high);
|
||||||
pr_info("data_low %0#2x\n", reg_write_return.bytes.data_low);
|
pr_info("data_low %0#2x\n", reg_write_return.bytes.data_low);
|
||||||
pr_info("addr_high %0#2x\n", reg_write_return.bytes.addr_high);
|
pr_info("addr_high %0#2x\n", reg_write_return.bytes.addr_high);
|
||||||
|
@ -293,8 +361,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
static struct file_operations fops_dev = {
|
static struct file_operations fops_dev = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.unlocked_ioctl = fop_ioctl
|
.unlocked_ioctl = fop_ioctl
|
||||||
// .open = fop_open,
|
// .open = fop_open,
|
||||||
// .release = fop_release
|
// .release = fop_release
|
||||||
};
|
};
|
||||||
|
|
||||||
struct class *tuxedo_io_device_class;
|
struct class *tuxedo_io_device_class;
|
||||||
|
@ -308,11 +376,7 @@ static int __init tuxedo_io_init(void)
|
||||||
|
|
||||||
// Hardware identification
|
// Hardware identification
|
||||||
id_check_clevo = clevo_identify();
|
id_check_clevo = clevo_identify();
|
||||||
id_check_uniwill = uniwill_identify() == 0 ? 1 : 0;
|
id_check_uniwill = uniwill_identify();
|
||||||
|
|
||||||
if (id_check_uniwill == 1) {
|
|
||||||
uniwill_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (id_check_clevo == 0 && id_check_uniwill == 0) {
|
if (id_check_clevo == 0 && id_check_uniwill == 0) {
|
||||||
|
@ -340,10 +404,6 @@ static int __init tuxedo_io_init(void)
|
||||||
|
|
||||||
static void __exit tuxedo_io_exit(void)
|
static void __exit tuxedo_io_exit(void)
|
||||||
{
|
{
|
||||||
if (id_check_uniwill == 1) {
|
|
||||||
uniwill_exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
device_destroy(tuxedo_io_device_class, tuxedo_io_device_handle);
|
device_destroy(tuxedo_io_device_class, tuxedo_io_device_handle);
|
||||||
class_destroy(tuxedo_io_device_class);
|
class_destroy(tuxedo_io_device_class);
|
||||||
cdev_del(&tuxedo_io_cdev);
|
cdev_del(&tuxedo_io_cdev);
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Read
|
// Read
|
||||||
|
#define R_UW_HW_IF_STR _IOR(MAGIC_READ_UW, 0x00, char*)
|
||||||
#define R_UW_FANSPEED _IOR(MAGIC_READ_UW, 0x10, int32_t*)
|
#define R_UW_FANSPEED _IOR(MAGIC_READ_UW, 0x10, int32_t*)
|
||||||
#define R_UW_FANSPEED2 _IOR(MAGIC_READ_UW, 0x11, int32_t*)
|
#define R_UW_FANSPEED2 _IOR(MAGIC_READ_UW, 0x11, int32_t*)
|
||||||
#define R_UW_FAN_TEMP _IOR(MAGIC_READ_UW, 0x12, int32_t*)
|
#define R_UW_FAN_TEMP _IOR(MAGIC_READ_UW, 0x12, int32_t*)
|
||||||
|
|
|
@ -28,15 +28,9 @@ MODULE_DESCRIPTION("TUXEDO Computers keyboard & keyboard backlight Driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_VERSION("3.0.8");
|
MODULE_VERSION("3.0.8");
|
||||||
|
|
||||||
MODULE_ALIAS("wmi:" UNIWILL_WMI_EVENT_GUID_0);
|
|
||||||
MODULE_ALIAS("wmi:" UNIWILL_WMI_EVENT_GUID_1);
|
|
||||||
MODULE_ALIAS("wmi:" UNIWILL_WMI_EVENT_GUID_2);
|
|
||||||
|
|
||||||
static DEFINE_MUTEX(tuxedo_keyboard_init_driver_lock);
|
static DEFINE_MUTEX(tuxedo_keyboard_init_driver_lock);
|
||||||
|
|
||||||
static struct tuxedo_keyboard_driver *driver_list[] = {
|
// static struct tuxedo_keyboard_driver *driver_list[] = { };
|
||||||
&uniwill_keyboard_driver
|
|
||||||
};
|
|
||||||
|
|
||||||
static int tuxedo_input_init(const struct key_entry key_map[])
|
static int tuxedo_input_init(const struct key_entry key_map[])
|
||||||
{
|
{
|
||||||
|
@ -134,44 +128,48 @@ static void __exit tuxedo_input_exit(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init tuxedo_keyboard_init(void)
|
void tuxedo_keyboard_remove_driver(struct tuxedo_keyboard_driver *tk_driver)
|
||||||
{
|
{
|
||||||
int i;
|
bool specified_driver_differ_from_used =
|
||||||
int num_drivers = sizeof(driver_list) / sizeof(*driver_list);
|
tk_driver != NULL &&
|
||||||
TUXEDO_INFO("Model '%s' found\n",
|
(
|
||||||
dmi_get_system_info(DMI_PRODUCT_NAME));
|
strcmp(
|
||||||
|
tk_driver->platform_driver->driver.name,
|
||||||
|
current_driver->platform_driver->driver.name
|
||||||
|
) != 0
|
||||||
|
);
|
||||||
|
|
||||||
// Attempt to load each available driver
|
if (specified_driver_differ_from_used)
|
||||||
// Associated probe decides if it fits
|
return;
|
||||||
// Driver from first successful probe is used
|
|
||||||
|
|
||||||
i = 0;
|
TUXEDO_DEBUG("tuxedo_input_exit()\n");
|
||||||
while (IS_ERR_OR_NULL(tuxedo_platform_device) && i < num_drivers) {
|
tuxedo_input_exit();
|
||||||
current_driver = driver_list[i];
|
TUXEDO_DEBUG("platform_device_unregister()\n");
|
||||||
tuxedo_keyboard_init_driver(current_driver);
|
if (!IS_ERR_OR_NULL(tuxedo_platform_device)) {
|
||||||
++i;
|
platform_device_unregister(tuxedo_platform_device);
|
||||||
|
tuxedo_platform_device = NULL;
|
||||||
}
|
}
|
||||||
|
TUXEDO_DEBUG("platform_driver_unregister()\n");
|
||||||
if (IS_ERR_OR_NULL(tuxedo_platform_device)) {
|
if (!IS_ERR_OR_NULL(current_driver)) {
|
||||||
TUXEDO_DEBUG("No matching hardware found on init\n");
|
platform_driver_unregister(current_driver->platform_driver);
|
||||||
current_driver = NULL;
|
current_driver = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(tuxedo_keyboard_remove_driver);
|
||||||
|
|
||||||
|
static int __init tuxedo_keyboard_init(void)
|
||||||
|
{
|
||||||
|
TUXEDO_INFO("module init\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit tuxedo_keyboard_exit(void)
|
static void __exit tuxedo_keyboard_exit(void)
|
||||||
{
|
{
|
||||||
TUXEDO_DEBUG("tuxedo_input_exit()\n");
|
TUXEDO_INFO("module exit\n");
|
||||||
tuxedo_input_exit();
|
|
||||||
TUXEDO_DEBUG("platform_device_unregister()\n");
|
|
||||||
if (!IS_ERR_OR_NULL(tuxedo_platform_device))
|
|
||||||
platform_device_unregister(tuxedo_platform_device);
|
|
||||||
TUXEDO_DEBUG("platform_driver_unregister()\n");
|
|
||||||
if (!IS_ERR_OR_NULL(current_driver))
|
|
||||||
platform_driver_unregister(current_driver->platform_driver);
|
|
||||||
|
|
||||||
TUXEDO_DEBUG("exit\n");
|
if (tuxedo_platform_device != NULL)
|
||||||
|
tuxedo_keyboard_remove_driver(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(tuxedo_keyboard_init);
|
module_init(tuxedo_keyboard_init);
|
||||||
|
|
|
@ -56,6 +56,7 @@ static struct input_dev *tuxedo_input_device = NULL;
|
||||||
static struct tuxedo_keyboard_driver *current_driver = NULL;
|
static struct tuxedo_keyboard_driver *current_driver = NULL;
|
||||||
|
|
||||||
struct platform_device *tuxedo_keyboard_init_driver(struct tuxedo_keyboard_driver *tk_driver);
|
struct platform_device *tuxedo_keyboard_init_driver(struct tuxedo_keyboard_driver *tk_driver);
|
||||||
|
void tuxedo_keyboard_remove_driver(struct tuxedo_keyboard_driver *tk_driver);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basically a copy of the existing report event but doesn't report unknown events
|
* Basically a copy of the existing report event but doesn't report unknown events
|
||||||
|
|
73
src/uniwill_interfaces.h
Normal file
73
src/uniwill_interfaces.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*!
|
||||||
|
* Copyright (c) 2021 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
||||||
|
*
|
||||||
|
* This file is part of tuxedo-keyboard.
|
||||||
|
*
|
||||||
|
* tuxedo-keyboard is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifndef UNIWILL_INTERFACES_H
|
||||||
|
#define UNIWILL_INTERFACES_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#define UNIWILL_WMI_MGMT_GUID_BA "ABBC0F6D-8EA1-11D1-00A0-C90629100000"
|
||||||
|
#define UNIWILL_WMI_MGMT_GUID_BB "ABBC0F6E-8EA1-11D1-00A0-C90629100000"
|
||||||
|
#define UNIWILL_WMI_MGMT_GUID_BC "ABBC0F6F-8EA1-11D1-00A0-C90629100000"
|
||||||
|
|
||||||
|
#define UNIWILL_WMI_EVENT_GUID_0 "ABBC0F70-8EA1-11D1-00A0-C90629100000"
|
||||||
|
#define UNIWILL_WMI_EVENT_GUID_1 "ABBC0F71-8EA1-11D1-00A0-C90629100000"
|
||||||
|
#define UNIWILL_WMI_EVENT_GUID_2 "ABBC0F72-8EA1-11D1-00A0-C90629100000"
|
||||||
|
|
||||||
|
#define MODULE_ALIAS_UNIWILL_WMI() \
|
||||||
|
MODULE_ALIAS("wmi:" UNIWILL_WMI_EVENT_GUID_2); \
|
||||||
|
MODULE_ALIAS("wmi:" UNIWILL_WMI_MGMT_GUID_BC);
|
||||||
|
|
||||||
|
#define UNIWILL_INTERFACE_WMI_STRID "uniwill_wmi"
|
||||||
|
|
||||||
|
typedef u32 (uniwill_read_ec_ram_t)(u16, u8*);
|
||||||
|
typedef u32 (uniwill_write_ec_ram_t)(u16, u8);
|
||||||
|
typedef void (uniwill_event_callb_t)(u32);
|
||||||
|
|
||||||
|
struct uniwill_interface_t {
|
||||||
|
char *string_id;
|
||||||
|
uniwill_event_callb_t *event_callb;
|
||||||
|
uniwill_read_ec_ram_t *read_ec_ram;
|
||||||
|
uniwill_write_ec_ram_t *write_ec_ram;
|
||||||
|
};
|
||||||
|
|
||||||
|
u32 uniwill_add_interface(struct uniwill_interface_t *new_interface);
|
||||||
|
u32 uniwill_remove_interface(struct uniwill_interface_t *interface);
|
||||||
|
uniwill_read_ec_ram_t uniwill_read_ec_ram;
|
||||||
|
uniwill_write_ec_ram_t uniwill_write_ec_ram;
|
||||||
|
u32 uniwill_get_active_interface_id(char **id_str);
|
||||||
|
|
||||||
|
union uw_ec_read_return {
|
||||||
|
u32 dword;
|
||||||
|
struct {
|
||||||
|
u8 data_low;
|
||||||
|
u8 data_high;
|
||||||
|
} bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
union uw_ec_write_return {
|
||||||
|
u32 dword;
|
||||||
|
struct {
|
||||||
|
u8 addr_low;
|
||||||
|
u8 addr_high;
|
||||||
|
u8 data_low;
|
||||||
|
u8 data_high;
|
||||||
|
} bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,5 +1,5 @@
|
||||||
/*!
|
/*!
|
||||||
* Copyright (c) 2020 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
* Copyright (c) 2020-2021 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
||||||
*
|
*
|
||||||
* This file is part of tuxedo-keyboard.
|
* This file is part of tuxedo-keyboard.
|
||||||
*
|
*
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include "uw_io.h"
|
#include "uniwill_interfaces.h"
|
||||||
|
|
||||||
#define UNIWILL_WMI_MGMT_GUID_BA "ABBC0F6D-8EA1-11D1-00A0-C90629100000"
|
#define UNIWILL_WMI_MGMT_GUID_BA "ABBC0F6D-8EA1-11D1-00A0-C90629100000"
|
||||||
#define UNIWILL_WMI_MGMT_GUID_BB "ABBC0F6E-8EA1-11D1-00A0-C90629100000"
|
#define UNIWILL_WMI_MGMT_GUID_BB "ABBC0F6E-8EA1-11D1-00A0-C90629100000"
|
||||||
|
@ -88,6 +88,98 @@ static struct key_entry uniwill_wmi_keymap[] = {
|
||||||
{ KE_END, 0 }
|
{ KE_END, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct uniwill_interfaces_t {
|
||||||
|
struct uniwill_interface_t *wmi;
|
||||||
|
} uniwill_interfaces = { .wmi = NULL };
|
||||||
|
|
||||||
|
uniwill_event_callb_t uniwill_event_callb;
|
||||||
|
|
||||||
|
u32 uniwill_read_ec_ram(u16 address, u8 *data)
|
||||||
|
{
|
||||||
|
u32 status;
|
||||||
|
|
||||||
|
if (!IS_ERR_OR_NULL(uniwill_interfaces.wmi))
|
||||||
|
status = uniwill_interfaces.wmi->read_ec_ram(address, data);
|
||||||
|
else {
|
||||||
|
pr_err("no active interface while read addr 0x%04x\n", address);
|
||||||
|
status = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(uniwill_read_ec_ram);
|
||||||
|
|
||||||
|
u32 uniwill_write_ec_ram(u16 address, u8 data)
|
||||||
|
{
|
||||||
|
u32 status;
|
||||||
|
|
||||||
|
if (!IS_ERR_OR_NULL(uniwill_interfaces.wmi))
|
||||||
|
status = uniwill_interfaces.wmi->write_ec_ram(address, data);
|
||||||
|
else {
|
||||||
|
pr_err("no active interface while write addr 0x%04x data 0x%02x\n", address, data);
|
||||||
|
status = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(uniwill_write_ec_ram);
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(uniwill_interface_modification_lock);
|
||||||
|
|
||||||
|
u32 uniwill_add_interface(struct uniwill_interface_t *interface)
|
||||||
|
{
|
||||||
|
mutex_lock(&uniwill_interface_modification_lock);
|
||||||
|
|
||||||
|
if (strcmp(interface->string_id, UNIWILL_INTERFACE_WMI_STRID) == 0)
|
||||||
|
uniwill_interfaces.wmi = interface;
|
||||||
|
else {
|
||||||
|
TUXEDO_DEBUG("trying to add unknown interface\n");
|
||||||
|
mutex_unlock(&uniwill_interface_modification_lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
interface->event_callb = uniwill_event_callb;
|
||||||
|
|
||||||
|
mutex_unlock(&uniwill_interface_modification_lock);
|
||||||
|
|
||||||
|
// Initialize driver if not already present
|
||||||
|
tuxedo_keyboard_init_driver(&uniwill_keyboard_driver);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(uniwill_add_interface);
|
||||||
|
|
||||||
|
u32 uniwill_remove_interface(struct uniwill_interface_t *interface)
|
||||||
|
{
|
||||||
|
mutex_lock(&uniwill_interface_modification_lock);
|
||||||
|
|
||||||
|
if (strcmp(interface->string_id, UNIWILL_INTERFACE_WMI_STRID) == 0) {
|
||||||
|
// Remove driver if last interface is removed
|
||||||
|
tuxedo_keyboard_remove_driver(&uniwill_keyboard_driver);
|
||||||
|
|
||||||
|
uniwill_interfaces.wmi = NULL;
|
||||||
|
} else {
|
||||||
|
mutex_unlock(&uniwill_interface_modification_lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&uniwill_interface_modification_lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(uniwill_remove_interface);
|
||||||
|
|
||||||
|
u32 uniwill_get_active_interface_id(char **id_str)
|
||||||
|
{
|
||||||
|
if (IS_ERR_OR_NULL(uniwill_interfaces.wmi))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (!IS_ERR_OR_NULL(id_str))
|
||||||
|
*id_str = uniwill_interfaces.wmi->string_id;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(uniwill_get_active_interface_id);
|
||||||
|
|
||||||
static void key_event_work(struct work_struct *work)
|
static void key_event_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
sparse_keymap_report_known_event(
|
sparse_keymap_report_known_event(
|
||||||
|
@ -130,16 +222,16 @@ static int keyboard_notifier_callb(struct notifier_block *nb, unsigned long code
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct notifier_block keyboard_notifier_block = {
|
static struct notifier_block keyboard_notifier_block = {
|
||||||
.notifier_call = keyboard_notifier_callb
|
.notifier_call = keyboard_notifier_callb
|
||||||
};
|
};
|
||||||
|
|
||||||
static u8 uniwill_read_kbd_bl_enabled(void)
|
static u8 uniwill_read_kbd_bl_enabled(void)
|
||||||
{
|
{
|
||||||
union uw_ec_read_return reg_read_return;
|
u8 backlight_data;
|
||||||
u8 enabled = 0xff;
|
u8 enabled = 0xff;
|
||||||
|
|
||||||
__uw_ec_read_addr(0x8c, 0x07, ®_read_return);
|
uniwill_read_ec_ram(0x078c, &backlight_data);
|
||||||
enabled = (reg_read_return.bytes.data_low >> 1) & 0x01;
|
enabled = (backlight_data >> 1) & 0x01;
|
||||||
enabled = !enabled;
|
enabled = !enabled;
|
||||||
|
|
||||||
return enabled;
|
return enabled;
|
||||||
|
@ -147,24 +239,22 @@ static u8 uniwill_read_kbd_bl_enabled(void)
|
||||||
|
|
||||||
static void uniwill_write_kbd_bl_enable(u8 enable)
|
static void uniwill_write_kbd_bl_enable(u8 enable)
|
||||||
{
|
{
|
||||||
union uw_ec_read_return reg_read_return;
|
u8 backlight_data;
|
||||||
union uw_ec_write_return reg_write_return;
|
|
||||||
u8 write_value = 0;
|
|
||||||
enable = enable & 0x01;
|
enable = enable & 0x01;
|
||||||
|
|
||||||
__uw_ec_read_addr(0x8c, 0x07, ®_read_return);
|
uniwill_read_ec_ram(0x078c, &backlight_data);
|
||||||
write_value = reg_read_return.bytes.data_low & ~(1 << 1);
|
backlight_data = backlight_data & ~(1 << 1);
|
||||||
write_value |= (!enable << 1);
|
backlight_data |= (!enable << 1);
|
||||||
__uw_ec_write_addr(0x8c, 0x07, write_value, 0x00, ®_write_return);
|
uniwill_write_ec_ram(0x078c, backlight_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static u32 uniwill_read_kbd_bl_br_state(u8 *brightness_state)
|
/*static u32 uniwill_read_kbd_bl_br_state(u8 *brightness_state)
|
||||||
{
|
{
|
||||||
union uw_ec_read_return reg_read_return;
|
u8 backlight_data;
|
||||||
u32 result;
|
u32 result;
|
||||||
|
|
||||||
__uw_ec_read_addr(0x8c, 0x07, ®_read_return);
|
uniwill_read_ec_ram(0x078c, &backlight_data);
|
||||||
*brightness_state = (reg_read_return.bytes.data_low & 0xf0) >> 4;
|
*brightness_state = (backlight_data & 0xf0) >> 4;
|
||||||
result = 0;
|
result = 0;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -172,15 +262,12 @@ static void uniwill_write_kbd_bl_enable(u8 enable)
|
||||||
|
|
||||||
static u32 uniwill_read_kbd_bl_rgb(u8 *red, u8 *green, u8 *blue)
|
static u32 uniwill_read_kbd_bl_rgb(u8 *red, u8 *green, u8 *blue)
|
||||||
{
|
{
|
||||||
union uw_ec_read_return reg_read_return;
|
|
||||||
u32 result;
|
u32 result;
|
||||||
|
|
||||||
__uw_ec_read_addr(0x03, 0x18, ®_read_return);
|
uniwill_read_ec_ram(0x1803, red);
|
||||||
*red = reg_read_return.bytes.data_low;
|
uniwill_read_ec_ram(0x1805, green);
|
||||||
__uw_ec_read_addr(0x05, 0x18, ®_read_return);
|
uniwill_read_ec_ram(0x1808, blue);
|
||||||
*green = reg_read_return.bytes.data_low;
|
|
||||||
__uw_ec_read_addr(0x08, 0x18, ®_read_return);
|
|
||||||
*blue = reg_read_return.bytes.data_low;
|
|
||||||
result = 0;
|
result = 0;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -188,16 +275,13 @@ static u32 uniwill_read_kbd_bl_rgb(u8 *red, u8 *green, u8 *blue)
|
||||||
|
|
||||||
static void uniwill_write_kbd_bl_rgb(u8 red, u8 green, u8 blue)
|
static void uniwill_write_kbd_bl_rgb(u8 red, u8 green, u8 blue)
|
||||||
{
|
{
|
||||||
union uw_ec_write_return reg_write_return;
|
|
||||||
|
|
||||||
// Write the colors
|
|
||||||
if (red > 0xc8) red = 0xc8;
|
if (red > 0xc8) red = 0xc8;
|
||||||
if (green > 0xc8) green = 0xc8;
|
if (green > 0xc8) green = 0xc8;
|
||||||
if (blue > 0xc8) blue = 0xc8;
|
if (blue > 0xc8) blue = 0xc8;
|
||||||
__uw_ec_write_addr(0x03, 0x18, red, 0x00, ®_write_return);
|
uniwill_write_ec_ram(0x1803, red);
|
||||||
__uw_ec_write_addr(0x05, 0x18, green, 0x00, ®_write_return);
|
uniwill_write_ec_ram(0x1805, green);
|
||||||
__uw_ec_write_addr(0x08, 0x18, blue, 0x00, ®_write_return);
|
uniwill_write_ec_ram(0x1808, blue);
|
||||||
TUXEDO_DEBUG("Wrote color [%0#4x, %0#4x, %0#4x]\n", red, green, blue);
|
TUXEDO_DEBUG("Wrote kbd color [%0#4x, %0#4x, %0#4x]\n", red, green, blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uniwill_write_kbd_bl_state(void) {
|
static void uniwill_write_kbd_bl_state(void) {
|
||||||
|
@ -223,95 +307,57 @@ static void uniwill_write_kbd_bl_state(void) {
|
||||||
|
|
||||||
static void uniwill_write_kbd_bl_reset(void)
|
static void uniwill_write_kbd_bl_reset(void)
|
||||||
{
|
{
|
||||||
union uw_ec_write_return reg_write_return;
|
uniwill_write_ec_ram(0x078c, 0x10);
|
||||||
|
|
||||||
__uw_ec_write_addr(0x8c, 0x07, 0x10, 0x00, ®_write_return);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uniwill_wmi_handle_event(u32 value, void *context, u32 guid_nr)
|
void uniwill_event_callb(u32 code)
|
||||||
{
|
{
|
||||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
if (uniwill_keyboard_driver.input_device != NULL)
|
||||||
union acpi_object *obj;
|
if (!sparse_keymap_report_known_event(uniwill_keyboard_driver.input_device, code, 1, true)) {
|
||||||
|
TUXEDO_DEBUG("Unknown code - %d (%0#6x)\n", code, code);
|
||||||
|
}
|
||||||
|
|
||||||
acpi_status status;
|
// Special key combination when mode change key is pressed
|
||||||
int code;
|
if (code == 0xb0) {
|
||||||
|
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTMETA, 1);
|
||||||
status = wmi_get_event_data(value, &response);
|
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTALT, 1);
|
||||||
if (status != AE_OK) {
|
input_report_key(uniwill_keyboard_driver.input_device, KEY_F6, 1);
|
||||||
TUXEDO_ERROR("uniwill handle event -> bad event status\n");
|
input_sync(uniwill_keyboard_driver.input_device);
|
||||||
return;
|
input_report_key(uniwill_keyboard_driver.input_device, KEY_F6, 0);
|
||||||
|
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTALT, 0);
|
||||||
|
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTMETA, 0);
|
||||||
|
input_sync(uniwill_keyboard_driver.input_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = (union acpi_object *) response.pointer;
|
// Keyboard backlight brightness toggle
|
||||||
if (obj) {
|
if (uniwill_kbd_bl_type_rgb_single_color) {
|
||||||
if (obj->type == ACPI_TYPE_INTEGER) {
|
switch (code) {
|
||||||
code = obj->integer.value;
|
case UNIWILL_OSD_KB_LED_LEVEL0:
|
||||||
if (!sparse_keymap_report_known_event(uniwill_keyboard_driver.input_device, code, 1, true)) {
|
kbd_led_state_uw.brightness = 0x00;
|
||||||
TUXEDO_DEBUG("[Ev %d] Unknown key - %d (%0#6x)\n", guid_nr, code, code);
|
uniwill_write_kbd_bl_state();
|
||||||
}
|
break;
|
||||||
|
case UNIWILL_OSD_KB_LED_LEVEL1:
|
||||||
// Special key combination when mode change key is pressed
|
kbd_led_state_uw.brightness = 0x20;
|
||||||
if (code == 0xb0) {
|
uniwill_write_kbd_bl_state();
|
||||||
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTMETA, 1);
|
break;
|
||||||
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTALT, 1);
|
case UNIWILL_OSD_KB_LED_LEVEL2:
|
||||||
input_report_key(uniwill_keyboard_driver.input_device, KEY_F6, 1);
|
kbd_led_state_uw.brightness = 0x50;
|
||||||
input_sync(uniwill_keyboard_driver.input_device);
|
uniwill_write_kbd_bl_state();
|
||||||
input_report_key(uniwill_keyboard_driver.input_device, KEY_F6, 0);
|
break;
|
||||||
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTALT, 0);
|
case UNIWILL_OSD_KB_LED_LEVEL3:
|
||||||
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTMETA, 0);
|
kbd_led_state_uw.brightness = 0x80;
|
||||||
input_sync(uniwill_keyboard_driver.input_device);
|
uniwill_write_kbd_bl_state();
|
||||||
}
|
break;
|
||||||
|
case UNIWILL_OSD_KB_LED_LEVEL4:
|
||||||
// Keyboard backlight brightness toggle
|
kbd_led_state_uw.brightness = 0xc8;
|
||||||
if (uniwill_kbd_bl_type_rgb_single_color) {
|
uniwill_write_kbd_bl_state();
|
||||||
switch (code) {
|
break;
|
||||||
case UNIWILL_OSD_KB_LED_LEVEL0:
|
// Also refresh keyboard state on cable switch event
|
||||||
kbd_led_state_uw.brightness = 0x00;
|
case UNIWILL_OSD_DC_ADAPTER_CHANGE:
|
||||||
uniwill_write_kbd_bl_state();
|
uniwill_write_kbd_bl_state();
|
||||||
break;
|
break;
|
||||||
case UNIWILL_OSD_KB_LED_LEVEL1:
|
|
||||||
kbd_led_state_uw.brightness = 0x20;
|
|
||||||
uniwill_write_kbd_bl_state();
|
|
||||||
break;
|
|
||||||
case UNIWILL_OSD_KB_LED_LEVEL2:
|
|
||||||
kbd_led_state_uw.brightness = 0x50;
|
|
||||||
uniwill_write_kbd_bl_state();
|
|
||||||
break;
|
|
||||||
case UNIWILL_OSD_KB_LED_LEVEL3:
|
|
||||||
kbd_led_state_uw.brightness = 0x80;
|
|
||||||
uniwill_write_kbd_bl_state();
|
|
||||||
break;
|
|
||||||
case UNIWILL_OSD_KB_LED_LEVEL4:
|
|
||||||
kbd_led_state_uw.brightness = 0xc8;
|
|
||||||
uniwill_write_kbd_bl_state();
|
|
||||||
break;
|
|
||||||
// Also refresh keyboard state on cable switch event
|
|
||||||
case UNIWILL_OSD_DC_ADAPTER_CHANGE:
|
|
||||||
uniwill_write_kbd_bl_state();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TUXEDO_DEBUG("[Ev %d] Unknown event type - %d (%0#6x)\n", guid_nr, obj->type, obj->type);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uniwill_wmi_notify0(u32 value, void *context)
|
|
||||||
{
|
|
||||||
uniwill_wmi_handle_event(value, context, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uniwill_wmi_notify1(u32 value, void *context)
|
|
||||||
{
|
|
||||||
uniwill_wmi_handle_event(value, context, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uniwill_wmi_notify2(u32 value, void *context)
|
|
||||||
{
|
|
||||||
uniwill_wmi_handle_event(value, context, 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t uw_brightness_show(struct device *child,
|
static ssize_t uw_brightness_show(struct device *child,
|
||||||
|
@ -518,107 +564,42 @@ static int uw_kbd_bl_init(struct platform_device *dev)
|
||||||
|
|
||||||
static void uniwill_write_lightbar_rgb(u8 red, u8 green, u8 blue)
|
static void uniwill_write_lightbar_rgb(u8 red, u8 green, u8 blue)
|
||||||
{
|
{
|
||||||
union uw_ec_write_return reg_write_return;
|
if (red <= UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS) {
|
||||||
|
uniwill_write_ec_ram(0x0749, red);
|
||||||
uw_ec_write_func *__uw_ec_write_addr;
|
}
|
||||||
|
if (green <= UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS) {
|
||||||
__uw_ec_write_addr = symbol_get(uw_ec_write_addr);
|
uniwill_write_ec_ram(0x074a, green);
|
||||||
|
}
|
||||||
if (__uw_ec_write_addr) {
|
if (blue <= UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS) {
|
||||||
if (red <= UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS) {
|
uniwill_write_ec_ram(0x074b, blue);
|
||||||
__uw_ec_write_addr(0x49, 0x07, red, 0x00, ®_write_return);
|
|
||||||
}
|
|
||||||
if (green <= UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS) {
|
|
||||||
__uw_ec_write_addr(0x4a, 0x07, green, 0x00, ®_write_return);
|
|
||||||
}
|
|
||||||
if (blue <= UNIWILL_LIGHTBAR_LED_MAX_BRIGHTNESS) {
|
|
||||||
__uw_ec_write_addr(0x4b, 0x07, blue, 0x00, ®_write_return);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__uw_ec_write_addr) symbol_put(uw_ec_write_addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uniwill_read_lightbar_rgb(u8 *red, u8 *green, u8 *blue)
|
static void uniwill_read_lightbar_rgb(u8 *red, u8 *green, u8 *blue)
|
||||||
{
|
{
|
||||||
int status;
|
uniwill_read_ec_ram(0x0749, red);
|
||||||
union uw_ec_read_return reg_read_return;
|
uniwill_read_ec_ram(0x074a, green);
|
||||||
|
uniwill_read_ec_ram(0x074b, blue);
|
||||||
uw_ec_read_func *__uw_ec_read_addr;
|
|
||||||
|
|
||||||
__uw_ec_read_addr = symbol_get(uw_ec_read_addr);
|
|
||||||
|
|
||||||
if (__uw_ec_read_addr) {
|
|
||||||
__uw_ec_read_addr(0x49, 0x07, ®_read_return);
|
|
||||||
*red = reg_read_return.bytes.data_low;
|
|
||||||
__uw_ec_read_addr(0x4a, 0x07, ®_read_return);
|
|
||||||
*green = reg_read_return.bytes.data_low;
|
|
||||||
__uw_ec_read_addr(0x4b, 0x07, ®_read_return);
|
|
||||||
*blue = reg_read_return.bytes.data_low;
|
|
||||||
status = 0;
|
|
||||||
} else {
|
|
||||||
status = -EIO;
|
|
||||||
TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (__uw_ec_read_addr) symbol_put(uw_ec_read_addr);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uniwill_write_lightbar_animation(bool animation_status)
|
static void uniwill_write_lightbar_animation(bool animation_status)
|
||||||
{
|
{
|
||||||
union uw_ec_write_return reg_write_return;
|
|
||||||
union uw_ec_read_return reg_read_return;
|
|
||||||
|
|
||||||
u8 value;
|
u8 value;
|
||||||
|
|
||||||
uw_ec_write_func *__uw_ec_write_addr;
|
uniwill_read_ec_ram(0x0748, &value);
|
||||||
uw_ec_read_func *__uw_ec_read_addr;
|
if (animation_status) {
|
||||||
|
value |= 0x80;
|
||||||
__uw_ec_write_addr = symbol_get(uw_ec_write_addr);
|
|
||||||
__uw_ec_read_addr = symbol_get(uw_ec_read_addr);
|
|
||||||
|
|
||||||
if (__uw_ec_write_addr && __uw_ec_read_addr) {
|
|
||||||
__uw_ec_read_addr(0x48, 0x07, ®_read_return);
|
|
||||||
value = reg_read_return.bytes.data_low;
|
|
||||||
if (animation_status) {
|
|
||||||
value |= 0x80;
|
|
||||||
} else {
|
|
||||||
value &= ~0x80;
|
|
||||||
}
|
|
||||||
__uw_ec_write_addr(0x48, 0x07, value, 0x00, ®_write_return);
|
|
||||||
} else {
|
} else {
|
||||||
TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n");
|
value &= ~0x80;
|
||||||
}
|
}
|
||||||
|
uniwill_write_ec_ram(0x0748, value);
|
||||||
if (__uw_ec_write_addr) symbol_put(uw_ec_write_addr);
|
|
||||||
if (__uw_ec_read_addr) symbol_put(uw_ec_read_addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uniwill_read_lightbar_animation(bool *animation_status)
|
static void uniwill_read_lightbar_animation(bool *animation_status)
|
||||||
{
|
{
|
||||||
int status;
|
u8 lightbar_animation_data;
|
||||||
union uw_ec_read_return reg_read_return;
|
uniwill_read_ec_ram(0x0748, &lightbar_animation_data);
|
||||||
|
*animation_status = (lightbar_animation_data & 0x80) > 0;
|
||||||
uw_ec_read_func *__uw_ec_read_addr;
|
|
||||||
|
|
||||||
__uw_ec_read_addr = symbol_get(uw_ec_read_addr);
|
|
||||||
|
|
||||||
if (__uw_ec_read_addr) {
|
|
||||||
__uw_ec_read_addr(0x48, 0x07, ®_read_return);
|
|
||||||
*animation_status = (reg_read_return.bytes.data_low & 0x80) > 0;
|
|
||||||
status = 0;
|
|
||||||
} else {
|
|
||||||
status = -EIO;
|
|
||||||
TUXEDO_DEBUG("tuxedo-cc-wmi symbols not found\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (__uw_ec_read_addr) symbol_put(uw_ec_read_addr);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lightbar_set_blocking(struct led_classdev *led_cdev, enum led_brightness brightness)
|
static int lightbar_set_blocking(struct led_classdev *led_cdev, enum led_brightness brightness)
|
||||||
|
@ -716,7 +697,7 @@ static int uw_lightbar_init(struct platform_device *dev)
|
||||||
|| dmi_match(DMI_PRODUCT_NAME, "A60 MUV")
|
|| dmi_match(DMI_PRODUCT_NAME, "A60 MUV")
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
||||||
|| dmi_match(DMI_PRODUCT_SKU, "STELLARIS1XI03")
|
|| dmi_match(DMI_PRODUCT_SKU, "STELLARIS1XI03")
|
||||||
|| dmi_match(DMI_PRODUCT_SKU, "STELLARIS1XA03")
|
|| dmi_match(DMI_PRODUCT_SKU, "STELLARIS1XA03")
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
|
@ -752,40 +733,27 @@ static int uw_lightbar_remove(struct platform_device *dev)
|
||||||
|
|
||||||
static int uniwill_keyboard_probe(struct platform_device *dev)
|
static int uniwill_keyboard_probe(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
|
u32 i;
|
||||||
|
u8 data;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
// Look for for GUIDs used on uniwill devices
|
// FIXME Hard set balanced profile until we have implemented a way to
|
||||||
status =
|
// switch it while tuxedo_io is loaded
|
||||||
wmi_has_guid(UNIWILL_WMI_EVENT_GUID_0) &&
|
// uw_ec_write_addr(0x51, 0x07, 0x00, 0x00, ®_write_return);
|
||||||
wmi_has_guid(UNIWILL_WMI_EVENT_GUID_1) &&
|
uniwill_write_ec_ram(0x0751, 0x00);
|
||||||
wmi_has_guid(UNIWILL_WMI_EVENT_GUID_2) &&
|
|
||||||
wmi_has_guid(UNIWILL_WMI_MGMT_GUID_BA) &&
|
// Set manual-mode fan-curve in 0x0743 - 0x0747
|
||||||
wmi_has_guid(UNIWILL_WMI_MGMT_GUID_BB) &&
|
// Some kind of default fan-curve is stored in 0x0786 - 0x078a: Using it to initialize manual-mode fan-curve
|
||||||
wmi_has_guid(UNIWILL_WMI_MGMT_GUID_BC);
|
for (i = 0; i < 5; ++i) {
|
||||||
|
uniwill_read_ec_ram(0x0786 + i, &data);
|
||||||
if (!status) {
|
uniwill_write_ec_ram(0x0743 + i, data);
|
||||||
TUXEDO_DEBUG("probe: At least one Uniwill GUID missing\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to add event handlers
|
// Enable manual mode
|
||||||
status = wmi_install_notify_handler(UNIWILL_WMI_EVENT_GUID_0, uniwill_wmi_notify0, NULL);
|
uniwill_write_ec_ram(0x0741, 0x01);
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
TUXEDO_ERROR("probe: Failed to install uniwill notify handler 0\n");
|
|
||||||
goto err_remove_notifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = wmi_install_notify_handler(UNIWILL_WMI_EVENT_GUID_1, uniwill_wmi_notify1, NULL);
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
TUXEDO_ERROR("probe: Failed to install uniwill notify handler 1\n");
|
|
||||||
goto err_remove_notifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = wmi_install_notify_handler(UNIWILL_WMI_EVENT_GUID_2, uniwill_wmi_notify2, NULL);
|
// Zero second fan temp for detection
|
||||||
if (ACPI_FAILURE(status)) {
|
uniwill_write_ec_ram(0x044f, 0x00);
|
||||||
TUXEDO_ERROR("probe: Failed to install uniwill notify handler 2\n");
|
|
||||||
goto err_remove_notifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = register_keyboard_notifier(&keyboard_notifier_block);
|
status = register_keyboard_notifier(&keyboard_notifier_block);
|
||||||
|
|
||||||
|
@ -795,13 +763,6 @@ static int uniwill_keyboard_probe(struct platform_device *dev)
|
||||||
uw_lightbar_loaded = (status >= 0);
|
uw_lightbar_loaded = (status >= 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_remove_notifiers:
|
|
||||||
wmi_remove_notify_handler(UNIWILL_WMI_EVENT_GUID_0);
|
|
||||||
wmi_remove_notify_handler(UNIWILL_WMI_EVENT_GUID_1);
|
|
||||||
wmi_remove_notify_handler(UNIWILL_WMI_EVENT_GUID_2);
|
|
||||||
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uniwill_keyboard_remove(struct platform_device *dev)
|
static int uniwill_keyboard_remove(struct platform_device *dev)
|
||||||
|
@ -817,15 +778,15 @@ static int uniwill_keyboard_remove(struct platform_device *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
unregister_keyboard_notifier(&keyboard_notifier_block);
|
unregister_keyboard_notifier(&keyboard_notifier_block);
|
||||||
wmi_remove_notify_handler(UNIWILL_WMI_EVENT_GUID_0);
|
|
||||||
wmi_remove_notify_handler(UNIWILL_WMI_EVENT_GUID_1);
|
|
||||||
wmi_remove_notify_handler(UNIWILL_WMI_EVENT_GUID_2);
|
|
||||||
|
|
||||||
del_timer(&uw_kbd_bl_init_timer);
|
del_timer(&uw_kbd_bl_init_timer);
|
||||||
|
|
||||||
if (uw_lightbar_loaded)
|
if (uw_lightbar_loaded)
|
||||||
uw_lightbar_remove(dev);
|
uw_lightbar_remove(dev);
|
||||||
|
|
||||||
|
// Disable manual mode
|
||||||
|
uniwill_write_ec_ram(0x0741, 0x00);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
354
src/uniwill_wmi.c
Normal file
354
src/uniwill_wmi.c
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
/*!
|
||||||
|
* Copyright (c) 2021 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
||||||
|
*
|
||||||
|
* This file is part of tuxedo-keyboard.
|
||||||
|
*
|
||||||
|
* tuxedo-keyboard is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/wmi.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include "uniwill_interfaces.h"
|
||||||
|
|
||||||
|
#define UNIWILL_EC_REG_LDAT 0x8a
|
||||||
|
#define UNIWILL_EC_REG_HDAT 0x8b
|
||||||
|
#define UNIWILL_EC_REG_FLAGS 0x8c
|
||||||
|
#define UNIWILL_EC_REG_CMDL 0x8d
|
||||||
|
#define UNIWILL_EC_REG_CMDH 0x8e
|
||||||
|
|
||||||
|
#define UNIWILL_EC_BIT_RFLG 0
|
||||||
|
#define UNIWILL_EC_BIT_WFLG 1
|
||||||
|
#define UNIWILL_EC_BIT_BFLG 2
|
||||||
|
#define UNIWILL_EC_BIT_CFLG 3
|
||||||
|
#define UNIWILL_EC_BIT_DRDY 7
|
||||||
|
|
||||||
|
#define UW_EC_WAIT_CYCLES 0x50
|
||||||
|
|
||||||
|
static bool uniwill_ec_direct = true;
|
||||||
|
|
||||||
|
DEFINE_MUTEX(uniwill_ec_lock);
|
||||||
|
|
||||||
|
static u32 uw_wmi_ec_evaluate(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, u8 read_flag, u32 *return_buffer)
|
||||||
|
{
|
||||||
|
acpi_status status;
|
||||||
|
union acpi_object *out_acpi;
|
||||||
|
u32 e_result = 0;
|
||||||
|
|
||||||
|
// Kernel buffer for input argument
|
||||||
|
u32 *wmi_arg = (u32 *) kmalloc(sizeof(u32)*10, GFP_KERNEL);
|
||||||
|
// Byte reference to the input buffer
|
||||||
|
u8 *wmi_arg_bytes = (u8 *) wmi_arg;
|
||||||
|
|
||||||
|
u8 wmi_instance = 0x00;
|
||||||
|
u32 wmi_method_id = 0x04;
|
||||||
|
struct acpi_buffer wmi_in = { (acpi_size) sizeof(wmi_arg), wmi_arg};
|
||||||
|
struct acpi_buffer wmi_out = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
|
|
||||||
|
mutex_lock(&uniwill_ec_lock);
|
||||||
|
|
||||||
|
// Zero input buffer
|
||||||
|
memset(wmi_arg, 0x00, 10 * sizeof(u32));
|
||||||
|
|
||||||
|
// Configure the input buffer
|
||||||
|
wmi_arg_bytes[0] = addr_low;
|
||||||
|
wmi_arg_bytes[1] = addr_high;
|
||||||
|
wmi_arg_bytes[2] = data_low;
|
||||||
|
wmi_arg_bytes[3] = data_high;
|
||||||
|
|
||||||
|
if (read_flag != 0) {
|
||||||
|
wmi_arg_bytes[5] = 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = wmi_evaluate_method(UNIWILL_WMI_MGMT_GUID_BC, wmi_instance, wmi_method_id, &wmi_in, &wmi_out);
|
||||||
|
out_acpi = (union acpi_object *) wmi_out.pointer;
|
||||||
|
|
||||||
|
if (out_acpi && out_acpi->type == ACPI_TYPE_BUFFER) {
|
||||||
|
memcpy(return_buffer, out_acpi->buffer.pointer, out_acpi->buffer.length);
|
||||||
|
} /* else if (out_acpi && out_acpi->type == ACPI_TYPE_INTEGER) {
|
||||||
|
e_result = (u32) out_acpi->integer.value;
|
||||||
|
}*/
|
||||||
|
if (ACPI_FAILURE(status)) {
|
||||||
|
pr_err("uniwill_wmi.h: Error evaluating method\n");
|
||||||
|
e_result = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(out_acpi);
|
||||||
|
kfree(wmi_arg);
|
||||||
|
|
||||||
|
mutex_unlock(&uniwill_ec_lock);
|
||||||
|
|
||||||
|
return e_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EC address read through WMI
|
||||||
|
*/
|
||||||
|
static u32 uw_ec_read_addr_wmi(u8 addr_low, u8 addr_high, union uw_ec_read_return *output)
|
||||||
|
{
|
||||||
|
u32 uw_data[10];
|
||||||
|
u32 ret = uw_wmi_ec_evaluate(addr_low, addr_high, 0x00, 0x00, 1, uw_data);
|
||||||
|
output->dword = uw_data[0];
|
||||||
|
// pr_debug("addr: 0x%02x%02x value: %0#4x (high: %0#4x) result: %d\n", addr_high, addr_low, output->bytes.data_low, output->bytes.data_high, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EC address write through WMI
|
||||||
|
*/
|
||||||
|
static u32 uw_ec_write_addr_wmi(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, union uw_ec_write_return *output)
|
||||||
|
{
|
||||||
|
u32 uw_data[10];
|
||||||
|
u32 ret = uw_wmi_ec_evaluate(addr_low, addr_high, data_low, data_high, 0, uw_data);
|
||||||
|
output->dword = uw_data[0];
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Direct EC address read
|
||||||
|
*/
|
||||||
|
static u32 uw_ec_read_addr_direct(u8 addr_low, u8 addr_high, union uw_ec_read_return *output)
|
||||||
|
{
|
||||||
|
u32 result;
|
||||||
|
u8 tmp, count, flags;
|
||||||
|
|
||||||
|
mutex_lock(&uniwill_ec_lock);
|
||||||
|
|
||||||
|
ec_write(UNIWILL_EC_REG_LDAT, addr_low);
|
||||||
|
ec_write(UNIWILL_EC_REG_HDAT, addr_high);
|
||||||
|
|
||||||
|
flags = (0 << UNIWILL_EC_BIT_DRDY) | (1 << UNIWILL_EC_BIT_RFLG);
|
||||||
|
ec_write(UNIWILL_EC_REG_FLAGS, flags);
|
||||||
|
|
||||||
|
// Wait for ready flag
|
||||||
|
count = UW_EC_WAIT_CYCLES;
|
||||||
|
ec_read(UNIWILL_EC_REG_FLAGS, &tmp);
|
||||||
|
while (((tmp & (1 << UNIWILL_EC_BIT_DRDY)) == 0) && count != 0) {
|
||||||
|
msleep(1);
|
||||||
|
ec_read(UNIWILL_EC_REG_FLAGS, &tmp);
|
||||||
|
count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count != 0) {
|
||||||
|
output->dword = 0;
|
||||||
|
ec_read(UNIWILL_EC_REG_CMDL, &tmp);
|
||||||
|
output->bytes.data_low = tmp;
|
||||||
|
ec_read(UNIWILL_EC_REG_CMDH, &tmp);
|
||||||
|
output->bytes.data_high = tmp;
|
||||||
|
result = 0;
|
||||||
|
} else {
|
||||||
|
output->dword = 0xfefefefe;
|
||||||
|
result = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
ec_write(UNIWILL_EC_REG_FLAGS, 0x00);
|
||||||
|
|
||||||
|
mutex_unlock(&uniwill_ec_lock);
|
||||||
|
|
||||||
|
// pr_debug("addr: 0x%02x%02x value: %0#4x result: %d\n", addr_high, addr_low, output->bytes.data_low, result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 uw_ec_write_addr_direct(u8 addr_low, u8 addr_high, u8 data_low, u8 data_high, union uw_ec_write_return *output)
|
||||||
|
{
|
||||||
|
u32 result = 0;
|
||||||
|
u8 tmp, count, flags;
|
||||||
|
|
||||||
|
mutex_lock(&uniwill_ec_lock);
|
||||||
|
|
||||||
|
ec_write(UNIWILL_EC_REG_LDAT, addr_low);
|
||||||
|
ec_write(UNIWILL_EC_REG_HDAT, addr_high);
|
||||||
|
ec_write(UNIWILL_EC_REG_CMDL, data_low);
|
||||||
|
ec_write(UNIWILL_EC_REG_CMDH, data_high);
|
||||||
|
|
||||||
|
flags = (0 << UNIWILL_EC_BIT_DRDY) | (1 << UNIWILL_EC_BIT_WFLG);
|
||||||
|
ec_write(UNIWILL_EC_REG_FLAGS, flags);
|
||||||
|
|
||||||
|
// Wait for ready flag
|
||||||
|
count = UW_EC_WAIT_CYCLES;
|
||||||
|
ec_read(UNIWILL_EC_REG_FLAGS, &tmp);
|
||||||
|
while (((tmp & (1 << UNIWILL_EC_BIT_DRDY)) == 0) && count != 0) {
|
||||||
|
msleep(1);
|
||||||
|
ec_read(UNIWILL_EC_REG_FLAGS, &tmp);
|
||||||
|
count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replicate wmi output depending on success
|
||||||
|
if (count != 0) {
|
||||||
|
output->bytes.addr_low = addr_low;
|
||||||
|
output->bytes.addr_high = addr_high;
|
||||||
|
output->bytes.data_low = data_low;
|
||||||
|
output->bytes.data_high = data_high;
|
||||||
|
result = 0;
|
||||||
|
} else {
|
||||||
|
output->dword = 0xfefefefe;
|
||||||
|
result = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
ec_write(UNIWILL_EC_REG_FLAGS, 0x00);
|
||||||
|
|
||||||
|
mutex_unlock(&uniwill_ec_lock);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 uw_wmi_read_ec_ram(u16 addr, u8 *data)
|
||||||
|
{
|
||||||
|
u32 result;
|
||||||
|
u8 addr_low, addr_high;
|
||||||
|
union uw_ec_read_return output;
|
||||||
|
|
||||||
|
if (IS_ERR_OR_NULL(data))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
addr_low = addr & 0xff;
|
||||||
|
addr_high = (addr >> 8) & 0xff;
|
||||||
|
|
||||||
|
if (uniwill_ec_direct) {
|
||||||
|
result = uw_ec_read_addr_direct(addr_low, addr_high, &output);
|
||||||
|
} else {
|
||||||
|
result = uw_ec_read_addr_wmi(addr_low, addr_high, &output);
|
||||||
|
}
|
||||||
|
|
||||||
|
*data = output.bytes.data_low;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 uw_wmi_write_ec_ram(u16 addr, u8 data)
|
||||||
|
{
|
||||||
|
u32 result;
|
||||||
|
u8 addr_low, addr_high, data_low, data_high;
|
||||||
|
union uw_ec_write_return output;
|
||||||
|
|
||||||
|
addr_low = addr & 0xff;
|
||||||
|
addr_high = (addr >> 8) & 0xff;
|
||||||
|
data_low = data;
|
||||||
|
data_high = 0x00;
|
||||||
|
|
||||||
|
if (uniwill_ec_direct)
|
||||||
|
result = uw_ec_write_addr_direct(addr_low, addr_high, data_low, data_high, &output);
|
||||||
|
else
|
||||||
|
result = uw_ec_write_addr_wmi(addr_low, addr_high, data_low, data_high, &output);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct uniwill_interface_t uniwill_wmi_interface = {
|
||||||
|
.string_id = UNIWILL_INTERFACE_WMI_STRID,
|
||||||
|
.read_ec_ram = uw_wmi_read_ec_ram,
|
||||||
|
.write_ec_ram = uw_wmi_write_ec_ram
|
||||||
|
};
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0)
|
||||||
|
static int uniwill_wmi_probe(struct wmi_device *wdev)
|
||||||
|
#else
|
||||||
|
static int uniwill_wmi_probe(struct wmi_device *wdev, const void *dummy_context)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
// Look for for GUIDs used on uniwill devices
|
||||||
|
status =
|
||||||
|
wmi_has_guid(UNIWILL_WMI_EVENT_GUID_0) &&
|
||||||
|
wmi_has_guid(UNIWILL_WMI_EVENT_GUID_1) &&
|
||||||
|
wmi_has_guid(UNIWILL_WMI_EVENT_GUID_2) &&
|
||||||
|
wmi_has_guid(UNIWILL_WMI_MGMT_GUID_BA) &&
|
||||||
|
wmi_has_guid(UNIWILL_WMI_MGMT_GUID_BB) &&
|
||||||
|
wmi_has_guid(UNIWILL_WMI_MGMT_GUID_BC);
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
pr_debug("probe: At least one Uniwill GUID missing\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
uniwill_add_interface(&uniwill_wmi_interface);
|
||||||
|
|
||||||
|
pr_info("interface initialized\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0)
|
||||||
|
static int uniwill_wmi_remove(struct wmi_device *wdev)
|
||||||
|
#else
|
||||||
|
static void uniwill_wmi_remove(struct wmi_device *wdev)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
pr_debug("uniwill_wmi driver remove\n");
|
||||||
|
uniwill_remove_interface(&uniwill_wmi_interface);
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0)
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uniwill_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
|
||||||
|
{
|
||||||
|
u32 code;
|
||||||
|
|
||||||
|
if (!IS_ERR_OR_NULL(uniwill_wmi_interface.event_callb)) {
|
||||||
|
if (obj) {
|
||||||
|
if (obj->type == ACPI_TYPE_INTEGER) {
|
||||||
|
code = obj->integer.value;
|
||||||
|
// Execute registered callback
|
||||||
|
uniwill_wmi_interface.event_callb(code);
|
||||||
|
} else {
|
||||||
|
pr_debug("unknown event type - %d (%0#6x)\n", obj->type, obj->type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pr_debug("expected ACPI object doesn't exist\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pr_debug("no registered callback\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wmi_device_id uniwill_wmi_device_ids[] = {
|
||||||
|
// Listing one should be enough, for a driver that "takes care of all anyways"
|
||||||
|
// also prevents probe (and handling) per "device"
|
||||||
|
{ .guid_string = UNIWILL_WMI_EVENT_GUID_2 },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wmi_driver uniwill_wmi_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = UNIWILL_INTERFACE_WMI_STRID,
|
||||||
|
.owner = THIS_MODULE
|
||||||
|
},
|
||||||
|
.id_table = uniwill_wmi_device_ids,
|
||||||
|
.probe = uniwill_wmi_probe,
|
||||||
|
.remove = uniwill_wmi_remove,
|
||||||
|
.notify = uniwill_wmi_notify,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_wmi_driver(uniwill_wmi_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("TUXEDO Computers GmbH <tux@tuxedocomputers.com>");
|
||||||
|
MODULE_DESCRIPTION("Driver for Uniwill WMI interface");
|
||||||
|
MODULE_VERSION("0.0.1");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If set to true, the module will use the replicated WMI functions
|
||||||
|
* (direct ec_read/ec_write) to read and write to the EC RAM instead
|
||||||
|
* of the original. Since the original functions, in all observed cases,
|
||||||
|
* use excessive delays, they are not preferred.
|
||||||
|
*/
|
||||||
|
module_param_cb(ec_direct_io, ¶m_ops_bool, &uniwill_ec_direct, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
|
||||||
|
MODULE_PARM_DESC(ec_direct_io, "Do not use WMI methods to read/write EC RAM (default: true).");
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(wmi, uniwill_wmi_device_ids);
|
||||||
|
MODULE_ALIAS_UNIWILL_WMI();
|
101
src/uw_io.h
101
src/uw_io.h
|
@ -1,101 +0,0 @@
|
||||||
/*!
|
|
||||||
* Copyright (c) 2020 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
|
||||||
*
|
|
||||||
* This file is part of tuxedo-keyboard.
|
|
||||||
*
|
|
||||||
* tuxedo-keyboard is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this software. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#ifndef UW_IO_H
|
|
||||||
#define UW_IO_H
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
|
|
||||||
union uw_ec_read_return {
|
|
||||||
u32 dword;
|
|
||||||
struct {
|
|
||||||
u8 data_low;
|
|
||||||
u8 data_high;
|
|
||||||
} bytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
union uw_ec_write_return {
|
|
||||||
u32 dword;
|
|
||||||
struct {
|
|
||||||
u8 addr_low;
|
|
||||||
u8 addr_high;
|
|
||||||
u8 data_low;
|
|
||||||
u8 data_high;
|
|
||||||
} bytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef u32 (uw_ec_read_func)(u8, u8, union uw_ec_read_return *);
|
|
||||||
typedef u32 (uw_ec_write_func)(u8, u8, u8, u8, union uw_ec_write_return *);
|
|
||||||
|
|
||||||
extern uw_ec_read_func uw_ec_read_addr;
|
|
||||||
extern uw_ec_write_func uw_ec_write_addr;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* uw ec read from tccwmi for now stuffed in this wrapper for simplification
|
|
||||||
*
|
|
||||||
* @returns The result of the tccwmi function, or -ENOENT
|
|
||||||
* if tccwmi symbol is not accessible
|
|
||||||
*/
|
|
||||||
static u32 __uw_ec_read_addr(u8 addr_low, u8 addr_high,
|
|
||||||
union uw_ec_read_return *output)
|
|
||||||
{
|
|
||||||
uw_ec_read_func *symbol_uw_ec_read_addr;
|
|
||||||
u32 result = 0;
|
|
||||||
symbol_uw_ec_read_addr = symbol_get(uw_ec_read_addr);
|
|
||||||
if (symbol_uw_ec_read_addr) {
|
|
||||||
result = symbol_uw_ec_read_addr(addr_low, addr_high, output);
|
|
||||||
} else {
|
|
||||||
pr_debug("tuxedo-cc-wmi symbols not found\n");
|
|
||||||
result = -ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (symbol_uw_ec_read_addr)
|
|
||||||
symbol_put(uw_ec_read_addr);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* uw ec write from tccwmi for now stuffed in this wrapper for simplification
|
|
||||||
*
|
|
||||||
* @returns The result of the tccwmi function, or -ENOENT
|
|
||||||
* if tccwmi symbol is not accessible
|
|
||||||
*/
|
|
||||||
static u32 __uw_ec_write_addr(u8 addr_low, u8 addr_high, u8 data_low,
|
|
||||||
u8 data_high, union uw_ec_write_return *output)
|
|
||||||
{
|
|
||||||
uw_ec_write_func *symbol_uw_ec_write_addr;
|
|
||||||
u32 result = 0;
|
|
||||||
symbol_uw_ec_write_addr = symbol_get(uw_ec_write_addr);
|
|
||||||
|
|
||||||
if (symbol_uw_ec_write_addr) {
|
|
||||||
result = symbol_uw_ec_write_addr(addr_low, addr_high, data_low,
|
|
||||||
data_high, output);
|
|
||||||
} else {
|
|
||||||
pr_debug("tuxedo-cc-wmi symbols not found\n");
|
|
||||||
result = -ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (symbol_uw_ec_write_addr)
|
|
||||||
symbol_put(uw_ec_write_addr);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -96,11 +96,13 @@ for POSTINST in /usr/lib/dkms/common.postinst /usr/share/%{module}/postinst; do
|
||||||
echo "(Re)load modules if possible"
|
echo "(Re)load modules if possible"
|
||||||
|
|
||||||
rmmod tuxedo_io > /dev/null 2>&1 || true
|
rmmod tuxedo_io > /dev/null 2>&1 || true
|
||||||
|
rmmod uniwill_wmi > /dev/null 2>&1 || true
|
||||||
rmmod clevo_wmi > /dev/null 2>&1 || true
|
rmmod clevo_wmi > /dev/null 2>&1 || true
|
||||||
rmmod clevo_acpi > /dev/null 2>&1 || true
|
rmmod clevo_acpi > /dev/null 2>&1 || true
|
||||||
rmmod tuxedo_keyboard > /dev/null 2>&1 || true
|
rmmod tuxedo_keyboard > /dev/null 2>&1 || true
|
||||||
|
|
||||||
modprobe tuxedo_keyboard > /dev/null 2>&1 || true
|
modprobe tuxedo_keyboard > /dev/null 2>&1 || true
|
||||||
|
modprobe uniwill_wmi > /dev/null 2>&1 || true
|
||||||
modprobe clevo_wmi > /dev/null 2>&1 || true
|
modprobe clevo_wmi > /dev/null 2>&1 || true
|
||||||
modprobe clevo_acpi > /dev/null 2>&1 || true
|
modprobe clevo_acpi > /dev/null 2>&1 || true
|
||||||
modprobe tuxedo_io > /dev/null 2>&1 || true
|
modprobe tuxedo_io > /dev/null 2>&1 || true
|
||||||
|
|
Loading…
Reference in a new issue