mirror of
https://github.com/wessel-novacustom/clevo-keyboard.git
synced 2024-11-15 03:34:01 +01:00
commit
7cb2ccc59d
5
Makefile
5
Makefile
|
@ -16,7 +16,10 @@
|
||||||
# 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/>.
|
||||||
#
|
#
|
||||||
obj-m := ./src/tuxedo_keyboard.o
|
obj-m := ./src/tuxedo_keyboard.o \
|
||||||
|
./src/clevo_wmi.o \
|
||||||
|
./src/clevo_acpi.o \
|
||||||
|
./src/tuxedo_io/tuxedo_io.o
|
||||||
|
|
||||||
PWD := $(shell pwd)
|
PWD := $(shell pwd)
|
||||||
KDIR := /lib/modules/$(shell uname -r)/build
|
KDIR := /lib/modules/$(shell uname -r)/build
|
||||||
|
|
|
@ -3,8 +3,8 @@ Version: x.x.x
|
||||||
Section: admin
|
Section: admin
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Depends: dkms (>= 1.95)
|
Depends: dkms (>= 1.95)
|
||||||
Conflicts: tuxedo-keyboard-dkms (<= 2.0.0), tuxedo-wmi-dkms (<= 1.5.1), tuxedo-xp-xc-touchpad-key-fix, tuxedo-touchpad-fix (<= 1.0.8), tuxedo-xp-xc-airplane-mode-fix (<= 0.6)
|
Conflicts: tuxedo-keyboard-dkms (<= 2.0.0), tuxedo-wmi-dkms (<= 1.5.1), tuxedo-xp-xc-touchpad-key-fix, tuxedo-touchpad-fix (<= 1.0.8), tuxedo-xp-xc-airplane-mode-fix (<= 0.6), tuxedo-cc-wmi
|
||||||
Replaces: tuxedo-keyboard-dkms (<= 2.0.0), tuxedo-wmi-dkms (<= 1.5.1), tuxedo-xp-xc-touchpad-key-fix, tuxedo-touchpad-fix (<= 1.0.8), tuxedo-xp-xc-airplane-mode-fix (<= 0.6)
|
Replaces: tuxedo-keyboard-dkms (<= 2.0.0), tuxedo-wmi-dkms (<= 1.5.1), tuxedo-xp-xc-touchpad-key-fix, tuxedo-touchpad-fix (<= 1.0.8), tuxedo-xp-xc-airplane-mode-fix (<= 0.6), tuxedo-cc-wmi
|
||||||
Maintainer: TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
Maintainer: TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
||||||
Homepage: https://www.tuxedocomputers.com
|
Homepage: https://www.tuxedocomputers.com
|
||||||
Architecture: all
|
Architecture: all
|
||||||
|
|
|
@ -42,8 +42,36 @@ case "$1" in
|
||||||
dkms_configure
|
dkms_configure
|
||||||
|
|
||||||
# Attempt to (re-)load module immediately, fail silently if not possible at this stage
|
# Attempt to (re-)load module immediately, fail silently if not possible at this stage
|
||||||
rmmod ${module} > /dev/null 2>&1 || true
|
|
||||||
modprobe ${module} > /dev/null 2>&1 || true
|
# Also stop tccd service if running before
|
||||||
|
echo "Check tccd running status"
|
||||||
|
if systemctl is-active --quiet tccd.service; then
|
||||||
|
TCCD_RUNNING=true
|
||||||
|
else
|
||||||
|
TCCD_RUNNING=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $TCCD_RUNNING; then
|
||||||
|
echo "Stop tccd temporarily"
|
||||||
|
systemctl stop tccd 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "(Re)load modules if possible"
|
||||||
|
rmmod tuxedo_io > /dev/null 2>&1 || true
|
||||||
|
rmmod clevo_wmi > /dev/null 2>&1 || true
|
||||||
|
rmmod clevo_acpi > /dev/null 2>&1 || true
|
||||||
|
rmmod tuxedo_keyboard > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
modprobe tuxedo_keyboard > /dev/null 2>&1 || true
|
||||||
|
modprobe clevo_wmi > /dev/null 2>&1 || true
|
||||||
|
modprobe clevo_acpi > /dev/null 2>&1 || true
|
||||||
|
modprobe tuxedo_io > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# Restart tccd after reload if it was running
|
||||||
|
if $TCCD_RUNNING; then
|
||||||
|
echo "Start tccd again"
|
||||||
|
systemctl start tccd 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
# Install default config if none exist already
|
# Install default config if none exist already
|
||||||
if [ ! -f "/etc/modprobe.d/tuxedo_keyboard.conf" ]; then
|
if [ ! -f "/etc/modprobe.d/tuxedo_keyboard.conf" ]; then
|
||||||
|
|
12
dkms.conf
12
dkms.conf
|
@ -5,6 +5,18 @@ DEST_MODULE_LOCATION[0]="/kernel/lib/"
|
||||||
BUILT_MODULE_NAME[0]="tuxedo_keyboard"
|
BUILT_MODULE_NAME[0]="tuxedo_keyboard"
|
||||||
BUILT_MODULE_LOCATION[0]="src/"
|
BUILT_MODULE_LOCATION[0]="src/"
|
||||||
|
|
||||||
|
DEST_MODULE_LOCATION[1]="/kernel/lib/"
|
||||||
|
BUILT_MODULE_NAME[1]="clevo_wmi"
|
||||||
|
BUILT_MODULE_LOCATION[1]="src/"
|
||||||
|
|
||||||
|
DEST_MODULE_LOCATION[2]="/kernel/lib/"
|
||||||
|
BUILT_MODULE_NAME[2]="clevo_acpi"
|
||||||
|
BUILT_MODULE_LOCATION[2]="src/"
|
||||||
|
|
||||||
|
DEST_MODULE_LOCATION[3]="/kernel/lib/"
|
||||||
|
BUILT_MODULE_NAME[3]="tuxedo_io"
|
||||||
|
BUILT_MODULE_LOCATION[3]="src/tuxedo_io"
|
||||||
|
|
||||||
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"
|
||||||
|
|
202
src/clevo_acpi.c
Normal file
202
src/clevo_acpi.c
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
/*
|
||||||
|
* clevo_acpi.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include "clevo_interfaces.h"
|
||||||
|
|
||||||
|
#define DRIVER_NAME "clevo_acpi"
|
||||||
|
|
||||||
|
struct clevo_acpi_driver_data_t {
|
||||||
|
struct acpi_device *adev;
|
||||||
|
struct clevo_interface_t *clevo_interface;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct clevo_acpi_driver_data_t *active_driver_data = NULL;
|
||||||
|
|
||||||
|
static u32 clevo_acpi_evaluate(struct acpi_device *device, u8 cmd, u32 arg, u32 *result)
|
||||||
|
{
|
||||||
|
u32 status;
|
||||||
|
acpi_handle handle;
|
||||||
|
u64 dsm_rev_dummy = 0x00; // Dummy 0 value since not used
|
||||||
|
u64 dsm_func = cmd;
|
||||||
|
// Integer package data for argument
|
||||||
|
union acpi_object dsm_argv4_package_data[] = {
|
||||||
|
{
|
||||||
|
.integer.type = ACPI_TYPE_INTEGER,
|
||||||
|
.integer.value = arg
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Package argument
|
||||||
|
union acpi_object dsm_argv4 = {
|
||||||
|
.package.type = ACPI_TYPE_PACKAGE,
|
||||||
|
.package.count = 1,
|
||||||
|
.package.elements = dsm_argv4_package_data
|
||||||
|
};
|
||||||
|
|
||||||
|
union acpi_object *out_obj;
|
||||||
|
|
||||||
|
guid_t clevo_acpi_dsm_uuid;
|
||||||
|
|
||||||
|
status = guid_parse(CLEVO_ACPI_DSM_UUID, &clevo_acpi_dsm_uuid);
|
||||||
|
if (status < 0)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
handle = acpi_device_handle(device);
|
||||||
|
if (handle == NULL)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
out_obj = acpi_evaluate_dsm(handle, &clevo_acpi_dsm_uuid, dsm_rev_dummy, dsm_func, &dsm_argv4);
|
||||||
|
if (!out_obj) {
|
||||||
|
pr_err("failed to evaluate _DSM\n");
|
||||||
|
status = -1;
|
||||||
|
} else {
|
||||||
|
if (out_obj->type == ACPI_TYPE_INTEGER) {
|
||||||
|
if (!IS_ERR_OR_NULL(result))
|
||||||
|
*result = (u32) out_obj->integer.value;
|
||||||
|
pr_debug("evaluate _DSM cmd: %0#4x arg: %0#10x\n", cmd, arg);
|
||||||
|
} else {
|
||||||
|
pr_err("unknown output from _DSM\n");
|
||||||
|
status = -ENODATA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ACPI_FREE(out_obj);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 clevo_acpi_interface_method_call(u8 cmd, u32 arg, u32 *result_value)
|
||||||
|
{
|
||||||
|
u32 status = 0;
|
||||||
|
|
||||||
|
if (!IS_ERR_OR_NULL(active_driver_data)) {
|
||||||
|
status = clevo_acpi_evaluate(active_driver_data->adev, cmd, arg, result_value);
|
||||||
|
} else {
|
||||||
|
pr_err("acpi method call exec, no driver data found\n");
|
||||||
|
pr_err("..for method_call: %0#4x arg: %0#10x\n", cmd, arg);
|
||||||
|
status = -ENODATA;
|
||||||
|
}
|
||||||
|
// pr_debug("clevo_acpi method_call: %0#4x arg: %0#10x result: %0#10x\n", cmd, arg, !IS_ERR_OR_NULL(result_value) ? *result_value : 0);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct clevo_interface_t clevo_acpi_interface = {
|
||||||
|
.string_id = "clevo_acpi",
|
||||||
|
.method_call = clevo_acpi_interface_method_call,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int clevo_acpi_add(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
struct clevo_acpi_driver_data_t *driver_data;
|
||||||
|
|
||||||
|
driver_data = devm_kzalloc(&device->dev, sizeof(*driver_data), GFP_KERNEL);
|
||||||
|
if (!driver_data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
driver_data->adev = device;
|
||||||
|
driver_data->clevo_interface = &clevo_acpi_interface;
|
||||||
|
|
||||||
|
active_driver_data = driver_data;
|
||||||
|
|
||||||
|
pr_debug("clevo_acpi driver add\n");
|
||||||
|
|
||||||
|
// Initiate clevo keyboard, if not already loaded by other interface driver
|
||||||
|
clevo_keyboard_init();
|
||||||
|
|
||||||
|
// Add this interface
|
||||||
|
clevo_keyboard_add_interface(&clevo_acpi_interface);
|
||||||
|
|
||||||
|
pr_info("interface initialized\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int clevo_acpi_remove(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
pr_debug("clevo_acpi driver remove\n");
|
||||||
|
clevo_keyboard_remove_interface(&clevo_acpi_interface);
|
||||||
|
active_driver_data = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clevo_acpi_notify(struct acpi_device *device, u32 event)
|
||||||
|
{
|
||||||
|
u32 event_value;
|
||||||
|
// struct clevo_acpi_driver_data_t *clevo_acpi_driver_data;
|
||||||
|
|
||||||
|
clevo_acpi_evaluate(device, 0x01, 0, &event_value);
|
||||||
|
pr_debug("clevo_acpi event: %0#6x, clevo event value: %0#6x\n", event, event_value);
|
||||||
|
|
||||||
|
// clevo_acpi_driver_data = container_of(&device, struct clevo_acpi_driver_data_t, adev);
|
||||||
|
if (!IS_ERR_OR_NULL(clevo_acpi_interface.event_callb)) {
|
||||||
|
// Execute registered callback
|
||||||
|
clevo_acpi_interface.event_callb(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int driver_suspend_callb(struct device *dev)
|
||||||
|
{
|
||||||
|
pr_debug("driver suspend\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int driver_resume_callb(struct device *dev)
|
||||||
|
{
|
||||||
|
pr_debug("driver resume\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(clevo_driver_pm_ops, driver_suspend_callb, driver_resume_callb);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct acpi_device_id clevo_acpi_device_ids[] = {
|
||||||
|
{CLEVO_ACPI_RESOURCE_HID, 0},
|
||||||
|
{"", 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct acpi_driver clevo_acpi_driver = {
|
||||||
|
.name = DRIVER_NAME,
|
||||||
|
.class = DRIVER_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.ids = clevo_acpi_device_ids,
|
||||||
|
.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
|
||||||
|
.ops = {
|
||||||
|
.add = clevo_acpi_add,
|
||||||
|
.remove = clevo_acpi_remove,
|
||||||
|
.notify = clevo_acpi_notify,
|
||||||
|
},
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
.drv.pm = &clevo_driver_pm_ops
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
module_acpi_driver(clevo_acpi_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("TUXEDO Computers GmbH <tux@tuxedocomputers.com>");
|
||||||
|
MODULE_DESCRIPTION("Driver for Clevo ACPI interface");
|
||||||
|
MODULE_VERSION("0.0.1");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(acpi, clevo_acpi_device_ids);
|
73
src/clevo_interfaces.h
Normal file
73
src/clevo_interfaces.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* clevo_interfaces.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#ifndef TUXEDO_INTERFACES_H
|
||||||
|
#define TUXEDO_INTERFACES_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#define CLEVO_WMI_EVENT_GUID "ABBC0F6B-8EA1-11D1-00A0-C90629100000"
|
||||||
|
#define CLEVO_WMI_EMAIL_GUID "ABBC0F6C-8EA1-11D1-00A0-C90629100000"
|
||||||
|
#define CLEVO_WMI_METHOD_GUID "ABBC0F6D-8EA1-11D1-00A0-C90629100000"
|
||||||
|
|
||||||
|
#define CLEVO_ACPI_RESOURCE_HID "CLV0001"
|
||||||
|
#define CLEVO_ACPI_DSM_UUID "93f224e4-fbdc-4bbf-add6-db71bdc0afad"
|
||||||
|
|
||||||
|
// The clevo get commands expect no parameters
|
||||||
|
#define CLEVO_CMD_GET_FANINFO1 0x63
|
||||||
|
#define CLEVO_CMD_GET_FANINFO2 0x64
|
||||||
|
#define CLEVO_CMD_GET_FANINFO3 0x6e
|
||||||
|
|
||||||
|
#define CLEVO_CMD_GET_WEBCAM_SW 0x06
|
||||||
|
#define CLEVO_CMD_GET_FLIGHTMODE_SW 0x07
|
||||||
|
#define CLEVO_CMD_GET_TOUCHPAD_SW 0x09
|
||||||
|
|
||||||
|
// The clevo set commands expect a parameter
|
||||||
|
#define CLEVO_CMD_SET_FANSPEED_VALUE 0x68
|
||||||
|
#define CLEVO_CMD_SET_FANSPEED_AUTO 0x69
|
||||||
|
|
||||||
|
#define CLEVO_CMD_SET_WEBCAM_SW 0x22
|
||||||
|
#define CLEVO_CMD_SET_FLIGHTMODE_SW 0x20
|
||||||
|
#define CLEVO_CMD_SET_TOUCHPAD_SW 0x2a
|
||||||
|
|
||||||
|
int clevo_keyboard_init(void);
|
||||||
|
|
||||||
|
struct clevo_interface_t {
|
||||||
|
char *string_id;
|
||||||
|
void (*event_callb)(u32);
|
||||||
|
u32 (*method_call)(u8, u32, u32*);
|
||||||
|
};
|
||||||
|
|
||||||
|
u32 clevo_keyboard_add_interface(struct clevo_interface_t *new_interface);
|
||||||
|
u32 clevo_keyboard_remove_interface(struct clevo_interface_t *interface);
|
||||||
|
u32 clevo_evaluate_method(u8 cmd, u32 arg, u32 *result);
|
||||||
|
u32 clevo_get_active_interface_id(char **id_str);
|
||||||
|
|
||||||
|
#define MODULE_ALIAS_CLEVO_WMI() \
|
||||||
|
MODULE_ALIAS("wmi:" CLEVO_WMI_EVENT_GUID); \
|
||||||
|
MODULE_ALIAS("wmi:" CLEVO_WMI_METHOD_GUID);
|
||||||
|
|
||||||
|
#define MODULE_ALIAS_CLEVO_ACPI() \
|
||||||
|
MODULE_ALIAS("acpi*:" CLEVO_ACPI_RESOURCE_HID ":*");
|
||||||
|
|
||||||
|
#define MODULE_ALIAS_CLEVO_INTERFACES() \
|
||||||
|
MODULE_ALIAS_CLEVO_WMI(); \
|
||||||
|
MODULE_ALIAS_CLEVO_ACPI();
|
||||||
|
|
||||||
|
#endif
|
|
@ -18,10 +18,7 @@
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
#include "tuxedo_keyboard_common.h"
|
#include "tuxedo_keyboard_common.h"
|
||||||
|
#include "clevo_interfaces.h"
|
||||||
#define CLEVO_EVENT_GUID "ABBC0F6B-8EA1-11D1-00A0-C90629100000"
|
|
||||||
#define CLEVO_EMAIL_GUID "ABBC0F6C-8EA1-11D1-00A0-C90629100000"
|
|
||||||
#define CLEVO_GET_GUID "ABBC0F6D-8EA1-11D1-00A0-C90629100000"
|
|
||||||
|
|
||||||
#define BRIGHTNESS_MIN 0
|
#define BRIGHTNESS_MIN 0
|
||||||
#define BRIGHTNESS_MAX 255
|
#define BRIGHTNESS_MAX 255
|
||||||
|
@ -37,53 +34,114 @@
|
||||||
#define KB_COLOR_DEFAULT 0xFFFFFF // White
|
#define KB_COLOR_DEFAULT 0xFFFFFF // White
|
||||||
#define DEFAULT_BLINKING_PATTERN 0
|
#define DEFAULT_BLINKING_PATTERN 0
|
||||||
|
|
||||||
// Submethod IDs for the CLEVO_GET WMI method
|
// Submethod IDs for the CLEVO_GET interface method
|
||||||
#define WMI_SUBMETHOD_ID_GET_EVENT 0x01
|
#define CLEVO_METHOD_ID_GET_EVENT 0x01
|
||||||
#define WMI_SUBMETHOD_ID_GET_AP 0x46
|
#define CLEVO_METHOD_ID_GET_AP 0x46
|
||||||
#define WMI_SUBMETHOD_ID_SET_KB_LEDS 0x67 /* used to set color, brightness,
|
#define CLEVO_METHOD_ID_SET_KB_LEDS 0x67 /* used to set color, brightness,
|
||||||
blinking pattern, etc. */
|
blinking pattern, etc. */
|
||||||
|
|
||||||
|
|
||||||
// WMI Event Codes
|
// Clevo event codes
|
||||||
#define WMI_KEYEVENT_CODE_DECREASE_BACKLIGHT 0x81
|
#define CLEVO_EVENT_DECREASE_BACKLIGHT 0x81
|
||||||
#define WMI_KEYEVENT_CODE_INCREASE_BACKLIGHT 0x82
|
#define CLEVO_EVENT_INCREASE_BACKLIGHT 0x82
|
||||||
#define WMI_KEYEVENT_CODE_NEXT_BLINKING_PATTERN 0x83
|
#define CLEVO_EVENT_NEXT_BLINKING_PATTERN 0x83
|
||||||
#define WMI_KEYEVENT_CODE_TOGGLE_STATE 0x9F
|
#define CLEVO_EVENT_TOGGLE_STATE 0x9F
|
||||||
|
|
||||||
#define WMI_KEYEVENT_CODE_CYCLE_BRIGHTNESS 0x8A
|
#define CLEVO_EVENT_CYCLE_BRIGHTNESS 0x8A
|
||||||
#define WMI_KEYEVENT_CODE_TOUCHPAD_TOGGLE 0x5D
|
#define CLEVO_EVENT_TOUCHPAD_TOGGLE 0x5D
|
||||||
#define WMI_KEYEVENT_CODE_TOUCHPAD_OFF 0xFC
|
#define CLEVO_EVENT_TOUCHPAD_OFF 0xFC
|
||||||
#define WMI_KEYEVENT_CODE_TOUCHPAD_ON 0xFD
|
#define CLEVO_EVENT_TOUCHPAD_ON 0xFD
|
||||||
|
|
||||||
#define WMI_KEYEVENT_CODE_RFKILL1 0x85
|
#define CLEVO_EVENT_RFKILL1 0x85
|
||||||
#define WMI_KEYEVENT_CODE_RFKILL2 0x86
|
#define CLEVO_EVENT_RFKILL2 0x86
|
||||||
|
|
||||||
|
static struct clevo_interfaces_t {
|
||||||
|
struct clevo_interface_t *wmi;
|
||||||
|
struct clevo_interface_t *acpi;
|
||||||
|
} clevo_interfaces;
|
||||||
|
|
||||||
|
struct clevo_interface_t *active_clevo_interface;
|
||||||
|
|
||||||
|
void clevo_keyboard_write_state(void);
|
||||||
|
void clevo_keyboard_event_callb(u32 event);
|
||||||
|
|
||||||
|
u32 clevo_keyboard_add_interface(struct clevo_interface_t *new_interface)
|
||||||
|
{
|
||||||
|
if (strcmp(new_interface->string_id, "clevo_wmi") == 0) {
|
||||||
|
clevo_interfaces.wmi = new_interface;
|
||||||
|
clevo_interfaces.wmi->event_callb = clevo_keyboard_event_callb;
|
||||||
|
|
||||||
|
// Only use wmi if there is no other current interface
|
||||||
|
if (ZERO_OR_NULL_PTR(active_clevo_interface)) {
|
||||||
|
pr_debug("enable wmi events\n");
|
||||||
|
clevo_interfaces.wmi->method_call(0x46, 0, NULL);
|
||||||
|
|
||||||
|
active_clevo_interface = clevo_interfaces.wmi;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (strcmp(new_interface->string_id, "clevo_acpi") == 0) {
|
||||||
|
clevo_interfaces.acpi = new_interface;
|
||||||
|
clevo_interfaces.acpi->event_callb = clevo_keyboard_event_callb;
|
||||||
|
|
||||||
|
pr_debug("enable acpi events (takes priority)\n");
|
||||||
|
clevo_interfaces.acpi->method_call(0x46, 0, NULL);
|
||||||
|
active_clevo_interface = clevo_interfaces.acpi;
|
||||||
|
} else {
|
||||||
|
// Not recognized interface
|
||||||
|
pr_err("unrecognized interface\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
clevo_keyboard_write_state();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(clevo_keyboard_add_interface);
|
||||||
|
|
||||||
|
u32 clevo_keyboard_remove_interface(struct clevo_interface_t *interface)
|
||||||
|
{
|
||||||
|
if (strcmp(interface->string_id, "clevo_wmi") == 0) {
|
||||||
|
clevo_interfaces.wmi = NULL;
|
||||||
|
} else if (strcmp(interface->string_id, "clevo_acpi") == 0) {
|
||||||
|
clevo_interfaces.acpi = NULL;
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active_clevo_interface == interface)
|
||||||
|
active_clevo_interface = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(clevo_keyboard_remove_interface);
|
||||||
|
|
||||||
struct tuxedo_keyboard_driver clevo_keyboard_driver;
|
struct tuxedo_keyboard_driver clevo_keyboard_driver;
|
||||||
|
|
||||||
static struct key_entry clevo_wmi_keymap[] = {
|
static struct key_entry clevo_keymap[] = {
|
||||||
// Keyboard backlight (RGB versions)
|
// Keyboard backlight (RGB versions)
|
||||||
{ KE_KEY, WMI_KEYEVENT_CODE_DECREASE_BACKLIGHT, { KEY_KBDILLUMDOWN } },
|
{ KE_KEY, CLEVO_EVENT_DECREASE_BACKLIGHT, { KEY_KBDILLUMDOWN } },
|
||||||
{ KE_KEY, WMI_KEYEVENT_CODE_INCREASE_BACKLIGHT, { KEY_KBDILLUMUP } },
|
{ KE_KEY, CLEVO_EVENT_INCREASE_BACKLIGHT, { KEY_KBDILLUMUP } },
|
||||||
{ KE_KEY, WMI_KEYEVENT_CODE_TOGGLE_STATE, { KEY_KBDILLUMTOGGLE } },
|
{ KE_KEY, CLEVO_EVENT_TOGGLE_STATE, { KEY_KBDILLUMTOGGLE } },
|
||||||
{ KE_KEY, WMI_KEYEVENT_CODE_NEXT_BLINKING_PATTERN, { KEY_LIGHTS_TOGGLE } },
|
{ KE_KEY, CLEVO_EVENT_NEXT_BLINKING_PATTERN, { KEY_LIGHTS_TOGGLE } },
|
||||||
// Single cycle key (white only versions)
|
// Single cycle key (white only versions)
|
||||||
{ KE_KEY, WMI_KEYEVENT_CODE_CYCLE_BRIGHTNESS, { KEY_KBDILLUMUP } },
|
{ KE_KEY, CLEVO_EVENT_CYCLE_BRIGHTNESS, { KEY_KBDILLUMUP } },
|
||||||
|
|
||||||
// Touchpad
|
// Touchpad
|
||||||
// The weirdly named touchpad toggle key that is implemented as KEY_F21 "everywhere"
|
// The weirdly named touchpad toggle key that is implemented as KEY_F21 "everywhere"
|
||||||
// (instead of KEY_TOUCHPAD_TOGGLE or on/off)
|
// (instead of KEY_TOUCHPAD_TOGGLE or on/off)
|
||||||
// Most "new" devices just provide one toggle event
|
// Most "new" devices just provide one toggle event
|
||||||
{ KE_KEY, WMI_KEYEVENT_CODE_TOUCHPAD_TOGGLE, { KEY_F21 } },
|
{ KE_KEY, CLEVO_EVENT_TOUCHPAD_TOGGLE, { KEY_F21 } },
|
||||||
// Some "old" devices produces on/off events
|
// Some "old" devices produces on/off events
|
||||||
{ KE_KEY, WMI_KEYEVENT_CODE_TOUCHPAD_OFF, { KEY_F21 } },
|
{ KE_KEY, CLEVO_EVENT_TOUCHPAD_OFF, { KEY_F21 } },
|
||||||
{ KE_KEY, WMI_KEYEVENT_CODE_TOUCHPAD_ON, { KEY_F21 } },
|
{ KE_KEY, CLEVO_EVENT_TOUCHPAD_ON, { KEY_F21 } },
|
||||||
// The alternative key events (currently not used)
|
// The alternative key events (currently not used)
|
||||||
//{ KE_KEY, WMI_KEYEVENT_CODE_TOUCHPAD_OFF, { KEY_TOUCHPAD_OFF } },
|
// { KE_KEY, CLEVO_EVENT_TOUCHPAD_OFF, { KEY_TOUCHPAD_OFF } },
|
||||||
//{ KE_KEY, WMI_KEYEVENT_CODE_TOUCHPAD_ON, { KEY_TOUCHPAD_ON } },
|
// { KE_KEY, CLEVO_EVENT_TOUCHPAD_ON, { KEY_TOUCHPAD_ON } },
|
||||||
//{ KE_KEY, WMI_KEYEVENT_CODE_TOUCHPAD_TOGGLE, { KEY_TOUCHPAD_TOGGLE } },
|
// { KE_KEY, CLEVO_EVENT_TOUCHPAD_TOGGLE, { KEY_TOUCHPAD_TOGGLE } },
|
||||||
|
|
||||||
{ KE_KEY, WMI_KEYEVENT_CODE_RFKILL1, { KEY_RFKILL } }, // Still needed by some devices
|
// Rfkill still needed by some devices
|
||||||
{ KE_IGNORE, WMI_KEYEVENT_CODE_RFKILL2, { KEY_RFKILL } }, // Older rfkill event
|
{ KE_KEY, CLEVO_EVENT_RFKILL1, { KEY_RFKILL } },
|
||||||
|
{ KE_IGNORE, CLEVO_EVENT_RFKILL2, { KEY_RFKILL } }, // Older rfkill event
|
||||||
// Note: Volume events need to be ignored as to not interfere with built-in functionality
|
// Note: Volume events need to be ignored as to not interfere with built-in functionality
|
||||||
{ KE_IGNORE, 0xfa, { KEY_UNKNOWN } }, // Appears by volume up/down
|
{ KE_IGNORE, 0xfa, { KEY_UNKNOWN } }, // Appears by volume up/down
|
||||||
{ KE_IGNORE, 0xfb, { KEY_UNKNOWN } }, // Appears by mute toggle
|
{ KE_IGNORE, 0xfb, { KEY_UNKNOWN } }, // Appears by mute toggle
|
||||||
|
@ -229,47 +287,33 @@ static ssize_t show_hasextra_fs(struct device *child,
|
||||||
return sprintf(buffer, "%d\n", kbd_led_state.has_extra);
|
return sprintf(buffer, "%d\n", kbd_led_state.has_extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int evaluate_wmi_method_clevo(u32 submethod_id, u32 submethod_arg, u32 * retval)
|
u32 clevo_evaluate_method(u8 cmd, u32 arg, u32 *result)
|
||||||
{
|
{
|
||||||
struct acpi_buffer acpi_input = { (acpi_size) sizeof(submethod_arg), &submethod_arg };
|
if (IS_ERR_OR_NULL(active_clevo_interface)) {
|
||||||
struct acpi_buffer acpi_output = { ACPI_ALLOCATE_BUFFER, NULL };
|
pr_err("clevo_keyboard: no active interface\n");
|
||||||
union acpi_object *obj;
|
return -ENODEV;
|
||||||
acpi_status status;
|
|
||||||
u32 wmi_output;
|
|
||||||
|
|
||||||
TUXEDO_DEBUG("evaluate wmi method: %0#4x IN : %0#6x\n", submethod_id, submethod_arg);
|
|
||||||
|
|
||||||
status = wmi_evaluate_method(CLEVO_GET_GUID, 0x00, submethod_id,
|
|
||||||
&acpi_input, &acpi_output);
|
|
||||||
|
|
||||||
if (unlikely(ACPI_FAILURE(status))) {
|
|
||||||
TUXEDO_ERROR("evaluate_wmi_method error");
|
|
||||||
return -EIO;
|
|
||||||
}
|
}
|
||||||
|
return active_clevo_interface->method_call(cmd, arg, result);
|
||||||
obj = (union acpi_object *)acpi_output.pointer;
|
|
||||||
if (obj && obj->type == ACPI_TYPE_INTEGER) {
|
|
||||||
wmi_output = (u32) obj->integer.value;
|
|
||||||
} else {
|
|
||||||
wmi_output = 0;
|
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(clevo_evaluate_method);
|
||||||
|
|
||||||
TUXEDO_DEBUG("WMI submethod %0#4x output: %0#6x (input: %0#6x)\n",
|
u32 clevo_get_active_interface_id(char **id_str)
|
||||||
submethod_id, wmi_output, submethod_arg);
|
{
|
||||||
|
if (IS_ERR_OR_NULL(active_clevo_interface))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
if (likely(retval)) { /* if no NULL pointer */
|
if (!IS_ERR_OR_NULL(id_str))
|
||||||
*retval = wmi_output;
|
*id_str = active_clevo_interface->string_id;
|
||||||
}
|
|
||||||
|
|
||||||
kfree(obj);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(clevo_get_active_interface_id);
|
||||||
|
|
||||||
static void set_brightness(u8 brightness)
|
static void set_brightness(u8 brightness)
|
||||||
{
|
{
|
||||||
TUXEDO_INFO("Set brightness on %d", brightness);
|
TUXEDO_INFO("Set brightness on %d", brightness);
|
||||||
if (!evaluate_wmi_method_clevo
|
if (!clevo_evaluate_method
|
||||||
(WMI_SUBMETHOD_ID_SET_KB_LEDS, 0xF4000000 | brightness, NULL)) {
|
(CLEVO_METHOD_ID_SET_KB_LEDS, 0xF4000000 | brightness, NULL)) {
|
||||||
kbd_led_state.brightness = brightness;
|
kbd_led_state.brightness = brightness;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,7 +347,7 @@ static int set_enabled_cmd(u8 state)
|
||||||
cmd |= 0x07F001;
|
cmd |= 0x07F001;
|
||||||
}
|
}
|
||||||
|
|
||||||
return evaluate_wmi_method_clevo(WMI_SUBMETHOD_ID_SET_KB_LEDS, cmd, NULL);
|
return clevo_evaluate_method(CLEVO_METHOD_ID_SET_KB_LEDS, cmd, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_enabled(u8 state)
|
static void set_enabled(u8 state)
|
||||||
|
@ -335,11 +379,11 @@ static int set_color(u32 region, u32 color)
|
||||||
u32 cset =
|
u32 cset =
|
||||||
((color & 0x0000FF) << 16) | ((color & 0xFF0000) >> 8) |
|
((color & 0x0000FF) << 16) | ((color & 0xFF0000) >> 8) |
|
||||||
((color & 0x00FF00) >> 8);
|
((color & 0x00FF00) >> 8);
|
||||||
u32 wmi_submethod_arg = region | cset;
|
u32 clevo_submethod_arg = region | cset;
|
||||||
|
|
||||||
TUXEDO_DEBUG("Set Color '%08x' for region '%08x'", color, region);
|
TUXEDO_DEBUG("Set Color '%08x' for region '%08x'", color, region);
|
||||||
|
|
||||||
return evaluate_wmi_method_clevo(WMI_SUBMETHOD_ID_SET_KB_LEDS, wmi_submethod_arg, NULL);
|
return clevo_evaluate_method(CLEVO_METHOD_ID_SET_KB_LEDS, clevo_submethod_arg, NULL);
|
||||||
}
|
}
|
||||||
static int set_color_code_region(u32 region, u32 colorcode)
|
static int set_color_code_region(u32 region, u32 colorcode)
|
||||||
{
|
{
|
||||||
|
@ -455,8 +499,8 @@ static void set_blinking_pattern(u8 blinkling_pattern)
|
||||||
{
|
{
|
||||||
TUXEDO_INFO("set_mode on %s", blinking_patterns[blinkling_pattern].name);
|
TUXEDO_INFO("set_mode on %s", blinking_patterns[blinkling_pattern].name);
|
||||||
|
|
||||||
if (!evaluate_wmi_method_clevo(WMI_SUBMETHOD_ID_SET_KB_LEDS, blinking_patterns[blinkling_pattern].value, NULL)) {
|
if (!clevo_evaluate_method(CLEVO_METHOD_ID_SET_KB_LEDS, blinking_patterns[blinkling_pattern].value, NULL)) {
|
||||||
// wmi method was succesfull so update ur internal state struct
|
// method was succesfull so update ur internal state struct
|
||||||
kbd_led_state.blinking_pattern = blinkling_pattern;
|
kbd_led_state.blinking_pattern = blinkling_pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,15 +562,14 @@ static int brightness_validator(const char *value,
|
||||||
return param_set_int(value, brightness_param);
|
return param_set_int(value, brightness_param);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clevo_wmi_notify(u32 value, void *context)
|
void clevo_keyboard_event_callb(u32 event)
|
||||||
{
|
{
|
||||||
u32 key_event;
|
u32 key_event = event;
|
||||||
|
|
||||||
evaluate_wmi_method_clevo(WMI_SUBMETHOD_ID_GET_EVENT, 0, &key_event);
|
// TUXEDO_DEBUG("clevo event: %0#6x\n", event);
|
||||||
TUXEDO_DEBUG("WMI event (%0#6x)\n", key_event);
|
|
||||||
|
|
||||||
switch (key_event) {
|
switch (key_event) {
|
||||||
case WMI_KEYEVENT_CODE_DECREASE_BACKLIGHT:
|
case CLEVO_EVENT_DECREASE_BACKLIGHT:
|
||||||
if (kbd_led_state.brightness == BRIGHTNESS_MIN
|
if (kbd_led_state.brightness == BRIGHTNESS_MIN
|
||||||
|| (kbd_led_state.brightness - 25) < BRIGHTNESS_MIN) {
|
|| (kbd_led_state.brightness - 25) < BRIGHTNESS_MIN) {
|
||||||
set_brightness(BRIGHTNESS_MIN);
|
set_brightness(BRIGHTNESS_MIN);
|
||||||
|
@ -536,7 +579,7 @@ static void clevo_wmi_notify(u32 value, void *context)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WMI_KEYEVENT_CODE_INCREASE_BACKLIGHT:
|
case CLEVO_EVENT_INCREASE_BACKLIGHT:
|
||||||
if (kbd_led_state.brightness == BRIGHTNESS_MAX
|
if (kbd_led_state.brightness == BRIGHTNESS_MAX
|
||||||
|| (kbd_led_state.brightness + 25) > BRIGHTNESS_MAX) {
|
|| (kbd_led_state.brightness + 25) > BRIGHTNESS_MAX) {
|
||||||
set_brightness(BRIGHTNESS_MAX);
|
set_brightness(BRIGHTNESS_MAX);
|
||||||
|
@ -546,16 +589,16 @@ static void clevo_wmi_notify(u32 value, void *context)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// case WMI_CODE_NEXT_BLINKING_PATTERN:
|
// case CLEVO_EVENT_NEXT_BLINKING_PATTERN:
|
||||||
// set_blinking_pattern((kbd_led_state.blinking_pattern + 1) >
|
// set_blinking_pattern((kbd_led_state.blinking_pattern + 1) >
|
||||||
// (ARRAY_SIZE(blinking_patterns) - 1) ? 0 : (kbd_led_state.blinking_pattern + 1));
|
// (ARRAY_SIZE(blinking_patterns) - 1) ? 0 : (kbd_led_state.blinking_pattern + 1));
|
||||||
// break;
|
// break;
|
||||||
|
|
||||||
case WMI_KEYEVENT_CODE_NEXT_BLINKING_PATTERN:
|
case CLEVO_EVENT_NEXT_BLINKING_PATTERN:
|
||||||
set_next_color_whole_kb();
|
set_next_color_whole_kb();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WMI_KEYEVENT_CODE_TOGGLE_STATE:
|
case CLEVO_EVENT_TOGGLE_STATE:
|
||||||
set_enabled(kbd_led_state.enabled == 0 ? 1 : 0);
|
set_enabled(kbd_led_state.enabled == 0 ? 1 : 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -563,10 +606,9 @@ static void clevo_wmi_notify(u32 value, void *context)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clevo_keyboard_driver.input_device != NULL) {
|
if (current_driver != NULL && current_driver->input_device != NULL) {
|
||||||
if (!sparse_keymap_report_known_event(
|
if (!sparse_keymap_report_known_event(
|
||||||
clevo_keyboard_driver.input_device, key_event, 1,
|
current_driver->input_device, key_event, 1, true)) {
|
||||||
true)) {
|
|
||||||
TUXEDO_DEBUG("Unknown key - %d (%0#6x)\n", key_event,
|
TUXEDO_DEBUG("Unknown key - %d (%0#6x)\n", key_event,
|
||||||
key_event);
|
key_event);
|
||||||
}
|
}
|
||||||
|
@ -584,44 +626,8 @@ static DEVICE_ATTR(brightness, 0644, show_brightness_fs, set_brightness_fs);
|
||||||
static DEVICE_ATTR(mode, 0644, show_blinking_patterns_fs, set_blinking_pattern_fs);
|
static DEVICE_ATTR(mode, 0644, show_blinking_patterns_fs, set_blinking_pattern_fs);
|
||||||
static DEVICE_ATTR(extra, 0444, show_hasextra_fs, NULL);
|
static DEVICE_ATTR(extra, 0444, show_hasextra_fs, NULL);
|
||||||
|
|
||||||
static int clevo_keyboard_probe(struct platform_device *dev)
|
static void clevo_keyboard_init_device_interface(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
int status, ret;
|
|
||||||
|
|
||||||
if (!wmi_has_guid(CLEVO_EVENT_GUID)) {
|
|
||||||
TUXEDO_DEBUG("probe: Clevo event guid missing\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wmi_has_guid(CLEVO_GET_GUID)) {
|
|
||||||
TUXEDO_DEBUG("probe: Clevo method guid missing\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since the WMI GUIDs aren't unique let's (at least)
|
|
||||||
// check the return of some "known existing general" method
|
|
||||||
status = evaluate_wmi_method_clevo(0x52, 0, &ret);
|
|
||||||
if (status < 0) {
|
|
||||||
TUXEDO_DEBUG("probe: Clevo GUIDs present but method call failed\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
if (ret == 0xffffffff) {
|
|
||||||
TUXEDO_DEBUG("probe: Clevo GUIDs present but method returned unexpected value\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = wmi_install_notify_handler(CLEVO_EVENT_GUID, clevo_wmi_notify,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (unlikely(ACPI_FAILURE(status))) {
|
|
||||||
TUXEDO_ERROR("Could not register WMI notify handler (%0#6x)\n",
|
|
||||||
status);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable WMI events
|
|
||||||
evaluate_wmi_method_clevo(WMI_SUBMETHOD_ID_GET_AP, 0, NULL);
|
|
||||||
|
|
||||||
// Setup sysfs
|
// Setup sysfs
|
||||||
if (device_create_file(&dev->dev, &dev_attr_state) != 0) {
|
if (device_create_file(&dev->dev, &dev_attr_state) != 0) {
|
||||||
TUXEDO_ERROR("Sysfs attribute file creation failed for state\n");
|
TUXEDO_ERROR("Sysfs attribute file creation failed for state\n");
|
||||||
|
@ -682,7 +688,10 @@ static int clevo_keyboard_probe(struct platform_device *dev)
|
||||||
kbd_led_state.color.center = param_color_center;
|
kbd_led_state.color.center = param_color_center;
|
||||||
kbd_led_state.color.right = param_color_right;
|
kbd_led_state.color.right = param_color_right;
|
||||||
kbd_led_state.color.extra = param_color_extra;
|
kbd_led_state.color.extra = param_color_extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clevo_keyboard_write_state(void)
|
||||||
|
{
|
||||||
// Write state
|
// Write state
|
||||||
set_color(REGION_LEFT, param_color_left);
|
set_color(REGION_LEFT, param_color_left);
|
||||||
set_color(REGION_CENTER, param_color_center);
|
set_color(REGION_CENTER, param_color_center);
|
||||||
|
@ -692,14 +701,16 @@ static int clevo_keyboard_probe(struct platform_device *dev)
|
||||||
if (param_brightness > BRIGHTNESS_MAX) param_brightness = BRIGHTNESS_DEFAULT;
|
if (param_brightness > BRIGHTNESS_MAX) param_brightness = BRIGHTNESS_DEFAULT;
|
||||||
set_brightness(param_brightness);
|
set_brightness(param_brightness);
|
||||||
set_enabled(param_state);
|
set_enabled(param_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int clevo_keyboard_probe_only_init(struct platform_device *dev)
|
||||||
|
{
|
||||||
|
clevo_keyboard_init_device_interface(dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int clevo_keyboard_remove(struct platform_device *dev)
|
static void clevo_keyboard_remove_device_interface(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
wmi_remove_notify_handler(CLEVO_EVENT_GUID);
|
|
||||||
|
|
||||||
device_remove_file(&dev->dev, &dev_attr_state);
|
device_remove_file(&dev->dev, &dev_attr_state);
|
||||||
device_remove_file(&dev->dev, &dev_attr_color_left);
|
device_remove_file(&dev->dev, &dev_attr_color_left);
|
||||||
device_remove_file(&dev->dev, &dev_attr_color_center);
|
device_remove_file(&dev->dev, &dev_attr_color_center);
|
||||||
|
@ -711,7 +722,11 @@ static int clevo_keyboard_remove(struct platform_device *dev)
|
||||||
if (kbd_led_state.has_extra == 1) {
|
if (kbd_led_state.has_extra == 1) {
|
||||||
device_remove_file(&dev->dev, &dev_attr_color_extra);
|
device_remove_file(&dev->dev, &dev_attr_color_extra);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int clevo_keyboard_remove(struct platform_device *dev)
|
||||||
|
{
|
||||||
|
clevo_keyboard_remove_device_interface(dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,7 +739,7 @@ static int clevo_keyboard_suspend(struct platform_device *dev, pm_message_t stat
|
||||||
|
|
||||||
static int clevo_keyboard_resume(struct platform_device *dev)
|
static int clevo_keyboard_resume(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
evaluate_wmi_method_clevo(WMI_SUBMETHOD_ID_GET_AP, 0, NULL);
|
clevo_evaluate_method(CLEVO_METHOD_ID_GET_AP, 0, NULL);
|
||||||
|
|
||||||
set_color(REGION_LEFT, kbd_led_state.color.left);
|
set_color(REGION_LEFT, kbd_led_state.color.left);
|
||||||
set_color(REGION_CENTER, kbd_led_state.color.center);
|
set_color(REGION_CENTER, kbd_led_state.color.center);
|
||||||
|
@ -750,8 +765,15 @@ static struct platform_driver platform_driver_clevo = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tuxedo_keyboard_driver clevo_keyboard_driver = {
|
struct tuxedo_keyboard_driver clevo_keyboard_driver_v2 = {
|
||||||
.platform_driver = &platform_driver_clevo,
|
.platform_driver = &platform_driver_clevo,
|
||||||
.probe = clevo_keyboard_probe,
|
.probe = clevo_keyboard_probe_only_init,
|
||||||
.key_map = clevo_wmi_keymap,
|
.key_map = clevo_keymap,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int clevo_keyboard_init(void)
|
||||||
|
{
|
||||||
|
tuxedo_keyboard_init_driver(&clevo_keyboard_driver_v2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(clevo_keyboard_init);
|
||||||
|
|
162
src/clevo_wmi.c
Normal file
162
src/clevo_wmi.c
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* clevo_wmi.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/wmi.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include "clevo_interfaces.h"
|
||||||
|
|
||||||
|
static int clevo_wmi_evaluate(u32 wmi_method_id, u32 wmi_arg, u32 *result)
|
||||||
|
{
|
||||||
|
struct acpi_buffer acpi_buffer_in = { (acpi_size)sizeof(wmi_arg),
|
||||||
|
&wmi_arg };
|
||||||
|
struct acpi_buffer acpi_buffer_out = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
|
union acpi_object *acpi_result;
|
||||||
|
acpi_status status_acpi;
|
||||||
|
u32 return_status = 0;
|
||||||
|
|
||||||
|
status_acpi =
|
||||||
|
wmi_evaluate_method(CLEVO_WMI_METHOD_GUID, 0x00, wmi_method_id,
|
||||||
|
&acpi_buffer_in, &acpi_buffer_out);
|
||||||
|
|
||||||
|
if (unlikely(ACPI_FAILURE(status_acpi))) {
|
||||||
|
pr_err("failed to evaluate wmi method\n");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
acpi_result = (union acpi_object *)acpi_buffer_out.pointer;
|
||||||
|
if (!acpi_result) {
|
||||||
|
pr_err("failed to evaluate WMI method\n");
|
||||||
|
return_status = -1;
|
||||||
|
} else {
|
||||||
|
if (acpi_result->type == ACPI_TYPE_INTEGER) {
|
||||||
|
if (!IS_ERR_OR_NULL(result)) {
|
||||||
|
*result = (u32)acpi_result->integer.value;
|
||||||
|
pr_debug(
|
||||||
|
"evaluate wmi cmd: %0#4x arg: %0#10x\n",
|
||||||
|
wmi_method_id, wmi_arg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pr_err("unknown output from wmi method\n");
|
||||||
|
return_status = -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(acpi_result);
|
||||||
|
|
||||||
|
return return_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 clevo_wmi_interface_method_call(u8 cmd, u32 arg, u32 *result_value)
|
||||||
|
{
|
||||||
|
return clevo_wmi_evaluate(cmd, arg, result_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct clevo_interface_t clevo_wmi_interface = {
|
||||||
|
.string_id = "clevo_wmi",
|
||||||
|
.method_call = clevo_wmi_interface_method_call,
|
||||||
|
};
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0)
|
||||||
|
static int clevo_wmi_probe(struct wmi_device *wdev)
|
||||||
|
#else
|
||||||
|
static int clevo_wmi_probe(struct wmi_device *wdev, const void *dummy_context)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
u32 status, ret;
|
||||||
|
|
||||||
|
pr_debug("clevo_wmi driver probe\n");
|
||||||
|
|
||||||
|
if (!wmi_has_guid(CLEVO_WMI_EVENT_GUID)) {
|
||||||
|
pr_debug("probe: Clevo event guid missing\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wmi_has_guid(CLEVO_WMI_METHOD_GUID)) {
|
||||||
|
pr_debug("probe: Clevo method guid missing\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the WMI GUIDs aren't unique let's (at least)
|
||||||
|
// check the return of some "known existing general" method
|
||||||
|
status = clevo_wmi_evaluate(0x52, 0, &ret);
|
||||||
|
if (status < 0) {
|
||||||
|
pr_debug("probe: Clevo GUIDs present but method call failed\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
if (ret == 0xffffffff) {
|
||||||
|
pr_debug(
|
||||||
|
"probe: Clevo GUIDs present but method returned unexpected value\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate clevo keyboard, if not already loaded by other interface driver
|
||||||
|
clevo_keyboard_init();
|
||||||
|
|
||||||
|
// Add this interface
|
||||||
|
clevo_keyboard_add_interface(&clevo_wmi_interface);
|
||||||
|
|
||||||
|
pr_info("interface initialized\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int clevo_wmi_remove(struct wmi_device *wdev)
|
||||||
|
{
|
||||||
|
pr_debug("clevo_wmi driver remove\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clevo_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy)
|
||||||
|
{
|
||||||
|
u32 event_value;
|
||||||
|
clevo_wmi_evaluate(0x01, 0, &event_value);
|
||||||
|
pr_debug("clevo_wmi notify\n");
|
||||||
|
if (!IS_ERR_OR_NULL(clevo_wmi_interface.event_callb)) {
|
||||||
|
// Execute registered callback
|
||||||
|
clevo_wmi_interface.event_callb(event_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wmi_device_id clevo_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 = CLEVO_WMI_EVENT_GUID },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wmi_driver clevo_wmi_driver = {
|
||||||
|
.driver = { .name = "clevo_wmi", .owner = THIS_MODULE },
|
||||||
|
.id_table = clevo_wmi_device_ids,
|
||||||
|
.probe = clevo_wmi_probe,
|
||||||
|
.remove = clevo_wmi_remove,
|
||||||
|
.notify = clevo_wmi_notify,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_wmi_driver(clevo_wmi_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("TUXEDO Computers GmbH <tux@tuxedocomputers.com>");
|
||||||
|
MODULE_DESCRIPTION("Driver for Clevo WMI interface");
|
||||||
|
MODULE_VERSION("0.0.1");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(wmi, clevo_wmi_device_ids);
|
||||||
|
MODULE_ALIAS_CLEVO_WMI();
|
291
src/tuxedo_io/tongfang_wmi.h
Normal file
291
src/tuxedo_io/tongfang_wmi.h
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
/*!
|
||||||
|
* Copyright (c) 2020 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
||||||
|
*
|
||||||
|
* This file is part of tuxedo-cc-wmi.
|
||||||
|
*
|
||||||
|
* tuxedo-cc-wmi 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/>.
|
||||||
|
*/
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
union uw_ec_write_return reg_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);
|
||||||
|
}
|
361
src/tuxedo_io/tuxedo_io.c
Normal file
361
src/tuxedo_io/tuxedo_io.c
Normal file
|
@ -0,0 +1,361 @@
|
||||||
|
/*!
|
||||||
|
* Copyright (c) 2019-2020 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
||||||
|
*
|
||||||
|
* This file is part of tuxedo-cc-wmi.
|
||||||
|
*
|
||||||
|
* tuxedo-cc-wmi 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/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/ioctl.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/cdev.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include "../clevo_interfaces.h"
|
||||||
|
#include "tongfang_wmi.h"
|
||||||
|
#include "tuxedo_io_ioctl.h"
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Hardware interface for TUXEDO laptops");
|
||||||
|
MODULE_AUTHOR("TUXEDO Computers GmbH <tux@tuxedocomputers.com>");
|
||||||
|
MODULE_VERSION("0.2.0");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
MODULE_ALIAS_CLEVO_INTERFACES();
|
||||||
|
MODULE_ALIAS("wmi:" CLEVO_WMI_METHOD_GUID);
|
||||||
|
MODULE_ALIAS("wmi:" UNIWILL_WMI_MGMT_GUID_BA);
|
||||||
|
MODULE_ALIAS("wmi:" UNIWILL_WMI_MGMT_GUID_BB);
|
||||||
|
MODULE_ALIAS("wmi:" UNIWILL_WMI_MGMT_GUID_BC);
|
||||||
|
|
||||||
|
// Initialized in module init, global for ioctl interface
|
||||||
|
static u32 id_check_clevo;
|
||||||
|
static u32 id_check_uniwill;
|
||||||
|
|
||||||
|
static u32 clevo_identify(void)
|
||||||
|
{
|
||||||
|
return clevo_get_active_interface_id(NULL) == 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*static int fop_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fop_release(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
static long clevo_ioctl_interface(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
u32 result = 0, status;
|
||||||
|
u32 copy_result;
|
||||||
|
u32 argument = (u32) arg;
|
||||||
|
|
||||||
|
const char str_no_if[] = "";
|
||||||
|
char *str_clevo_if;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case R_CL_HW_IF_STR:
|
||||||
|
if (clevo_get_active_interface_id(&str_clevo_if) == 0) {
|
||||||
|
copy_result = copy_to_user((char *) arg, str_clevo_if, strlen(str_clevo_if) + 1);
|
||||||
|
} else {
|
||||||
|
copy_result = copy_to_user((char *) arg, str_no_if, strlen(str_no_if) + 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R_CL_FANINFO1:
|
||||||
|
status = clevo_evaluate_method(CLEVO_CMD_GET_FANINFO1, 0, &result);
|
||||||
|
copy_result = copy_to_user((int32_t *) arg, &result, sizeof(result));
|
||||||
|
break;
|
||||||
|
case R_CL_FANINFO2:
|
||||||
|
status = clevo_evaluate_method(CLEVO_CMD_GET_FANINFO2, 0, &result);
|
||||||
|
copy_result = copy_to_user((int32_t *) arg, &result, sizeof(result));
|
||||||
|
break;
|
||||||
|
case R_CL_FANINFO3:
|
||||||
|
status = clevo_evaluate_method(CLEVO_CMD_GET_FANINFO3, 0, &result);
|
||||||
|
copy_result = copy_to_user((int32_t *) arg, &result, sizeof(result));
|
||||||
|
break;
|
||||||
|
/*case R_CL_FANINFO4:
|
||||||
|
status = clevo_evaluate_method(CLEVO_CMD_GET_FANINFO4, 0);
|
||||||
|
copy_to_user((int32_t *) arg, &result, sizeof(result));
|
||||||
|
break;*/
|
||||||
|
case R_CL_WEBCAM_SW:
|
||||||
|
status = clevo_evaluate_method(CLEVO_CMD_GET_WEBCAM_SW, 0, &result);
|
||||||
|
copy_result = copy_to_user((int32_t *) arg, &result, sizeof(result));
|
||||||
|
break;
|
||||||
|
case R_CL_FLIGHTMODE_SW:
|
||||||
|
status = clevo_evaluate_method(CLEVO_CMD_GET_FLIGHTMODE_SW, 0, &result);
|
||||||
|
copy_result = copy_to_user((int32_t *) arg, &result, sizeof(result));
|
||||||
|
break;
|
||||||
|
case R_CL_TOUCHPAD_SW:
|
||||||
|
status = clevo_evaluate_method(CLEVO_CMD_GET_TOUCHPAD_SW, 0, &result);
|
||||||
|
copy_result = copy_to_user((int32_t *) arg, &result, sizeof(result));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case W_CL_FANSPEED:
|
||||||
|
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument));
|
||||||
|
clevo_evaluate_method(CLEVO_CMD_SET_FANSPEED_VALUE, argument, &result);
|
||||||
|
// Note: Delay needed to let hardware catch up with the written value.
|
||||||
|
// No known ready flag. If the value is read too soon, the old value
|
||||||
|
// will still be read out.
|
||||||
|
// (Theoretically needed for other methods as well.)
|
||||||
|
// Can it be lower? 50ms is too low
|
||||||
|
msleep(100);
|
||||||
|
break;
|
||||||
|
case W_CL_FANAUTO:
|
||||||
|
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument));
|
||||||
|
clevo_evaluate_method(CLEVO_CMD_SET_FANSPEED_AUTO, argument, &result);
|
||||||
|
break;
|
||||||
|
case W_CL_WEBCAM_SW:
|
||||||
|
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument));
|
||||||
|
status = clevo_evaluate_method(CLEVO_CMD_GET_WEBCAM_SW, 0, &result);
|
||||||
|
// Only set status if it isn't already the right value
|
||||||
|
// (workaround for old and/or buggy WMI interfaces that toggle on write)
|
||||||
|
if ((argument & 0x01) != (result & 0x01)) {
|
||||||
|
clevo_evaluate_method(CLEVO_CMD_SET_WEBCAM_SW, argument, &result);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case W_CL_FLIGHTMODE_SW:
|
||||||
|
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument));
|
||||||
|
clevo_evaluate_method(CLEVO_CMD_SET_FLIGHTMODE_SW, argument, &result);
|
||||||
|
break;
|
||||||
|
case W_CL_TOUCHPAD_SW:
|
||||||
|
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument));
|
||||||
|
clevo_evaluate_method(CLEVO_CMD_SET_TOUCHPAD_SW, argument, &result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long uniwill_ioctl_interface(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
u32 result = 0;
|
||||||
|
u32 copy_result;
|
||||||
|
u32 argument;
|
||||||
|
union uw_ec_read_return reg_read_return;
|
||||||
|
union uw_ec_write_return reg_write_return;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
u32 uw_arg[10];
|
||||||
|
u32 uw_result[10];
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 10; ++i) {
|
||||||
|
uw_result[i] = 0xdeadbeef;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case R_UW_FANSPEED:
|
||||||
|
uw_ec_read_addr(0x04, 0x18, ®_read_return);
|
||||||
|
result = reg_read_return.bytes.data_low;
|
||||||
|
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
||||||
|
break;
|
||||||
|
case R_UW_FANSPEED2:
|
||||||
|
uw_ec_read_addr(0x09, 0x18, ®_read_return);
|
||||||
|
result = reg_read_return.bytes.data_low;
|
||||||
|
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
||||||
|
break;
|
||||||
|
case R_UW_FAN_TEMP:
|
||||||
|
uw_ec_read_addr(0x3e, 0x04, ®_read_return);
|
||||||
|
result = reg_read_return.bytes.data_low;
|
||||||
|
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
||||||
|
break;
|
||||||
|
case R_UW_FAN_TEMP2:
|
||||||
|
uw_ec_read_addr(0x4f, 0x04, ®_read_return);
|
||||||
|
result = reg_read_return.bytes.data_low;
|
||||||
|
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
||||||
|
break;
|
||||||
|
case R_UW_MODE:
|
||||||
|
uw_ec_read_addr(0x51, 0x07, ®_read_return);
|
||||||
|
result = reg_read_return.bytes.data_low;
|
||||||
|
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
||||||
|
break;
|
||||||
|
case R_UW_MODE_ENABLE:
|
||||||
|
uw_ec_read_addr(0x41, 0x07, ®_read_return);
|
||||||
|
result = reg_read_return.bytes.data_low;
|
||||||
|
copy_result = copy_to_user((void *) arg, &result, sizeof(result));
|
||||||
|
break;
|
||||||
|
#ifdef DEBUG
|
||||||
|
case R_TF_BC:
|
||||||
|
copy_result = copy_from_user(&uw_arg, (void *) arg, sizeof(uw_arg));
|
||||||
|
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) {
|
||||||
|
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));
|
||||||
|
} else {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case W_UW_FANSPEED:
|
||||||
|
// Get fan speed argument
|
||||||
|
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument));
|
||||||
|
|
||||||
|
// Check current mode
|
||||||
|
uw_ec_read_addr(0x51, 0x07, ®_read_return);
|
||||||
|
if (reg_read_return.bytes.data_low != 0x40) {
|
||||||
|
// If not "full fan mode" (ie. 0x40) switch to it (required for fancontrol)
|
||||||
|
uw_ec_write_addr(0x51, 0x07, 0x40, 0x00, ®_write_return);
|
||||||
|
}
|
||||||
|
// Set speed
|
||||||
|
uw_ec_write_addr(0x04, 0x18, argument & 0xff, 0x00, ®_write_return);
|
||||||
|
break;
|
||||||
|
case W_UW_FANSPEED2:
|
||||||
|
// Get fan speed argument
|
||||||
|
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument));
|
||||||
|
|
||||||
|
// Check current mode
|
||||||
|
uw_ec_read_addr(0x51, 0x07, ®_read_return);
|
||||||
|
if (reg_read_return.bytes.data_low != 0x40) {
|
||||||
|
// If not "full fan mode" (ie. 0x40) switch to it (required for fancontrol)
|
||||||
|
uw_ec_write_addr(0x51, 0x07, 0x40, 0x00, ®_write_return);
|
||||||
|
}
|
||||||
|
// Set speed
|
||||||
|
uw_ec_write_addr(0x09, 0x18, argument & 0xff, 0x00, ®_write_return);
|
||||||
|
break;
|
||||||
|
case W_UW_MODE:
|
||||||
|
copy_result = copy_from_user(&argument, (int32_t *) arg, sizeof(argument));
|
||||||
|
uw_ec_write_addr(0x51, 0x07, argument & 0xff, 0x00, ®_write_return);
|
||||||
|
break;
|
||||||
|
case W_UW_MODE_ENABLE:
|
||||||
|
// 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));
|
||||||
|
uw_ec_write_addr(0x41, 0x07, argument & 0x01, 0x00, ®_write_return);
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
#ifdef DEBUG
|
||||||
|
case W_TF_BC:
|
||||||
|
copy_result = copy_from_user(&uw_arg, (void *) arg, sizeof(uw_arg));
|
||||||
|
if (uniwill_ec_direct) {
|
||||||
|
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));
|
||||||
|
} else {
|
||||||
|
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));
|
||||||
|
reg_write_return.dword = uw_result[0];
|
||||||
|
}
|
||||||
|
/*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("addr_high %0#2x\n", reg_write_return.bytes.addr_high);
|
||||||
|
pr_info("addr_low %0#2x\n", reg_write_return.bytes.addr_low);*/
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
u32 status;
|
||||||
|
// u32 result = 0;
|
||||||
|
u32 copy_result;
|
||||||
|
|
||||||
|
const char *module_version = THIS_MODULE->version;
|
||||||
|
switch (cmd) {
|
||||||
|
case R_MOD_VERSION:
|
||||||
|
copy_result = copy_to_user((char *) arg, module_version, strlen(module_version) + 1);
|
||||||
|
break;
|
||||||
|
// Hardware id checks, 1 = positive, 0 = negative
|
||||||
|
case R_HWCHECK_CL:
|
||||||
|
id_check_clevo = clevo_identify();
|
||||||
|
copy_result = copy_to_user((void *) arg, (void *) &id_check_clevo, sizeof(id_check_clevo));
|
||||||
|
break;
|
||||||
|
case R_HWCHECK_UW:
|
||||||
|
copy_result = copy_to_user((void *) arg, (void *) &id_check_uniwill, sizeof(id_check_uniwill));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = clevo_ioctl_interface(file, cmd, arg);
|
||||||
|
if (status != 0) return status;
|
||||||
|
status = uniwill_ioctl_interface(file, cmd, arg);
|
||||||
|
if (status != 0) return status;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct file_operations fops_dev = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.unlocked_ioctl = fop_ioctl
|
||||||
|
// .open = fop_open,
|
||||||
|
// .release = fop_release
|
||||||
|
};
|
||||||
|
|
||||||
|
struct class *tuxedo_io_device_class;
|
||||||
|
dev_t tuxedo_io_device_handle;
|
||||||
|
|
||||||
|
static struct cdev tuxedo_io_cdev;
|
||||||
|
|
||||||
|
static int __init tuxedo_io_init(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
// Hardware identification
|
||||||
|
id_check_clevo = clevo_identify();
|
||||||
|
id_check_uniwill = uniwill_identify() == 0 ? 1 : 0;
|
||||||
|
|
||||||
|
if (id_check_uniwill == 1) {
|
||||||
|
uniwill_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (id_check_clevo == 0 && id_check_uniwill == 0) {
|
||||||
|
pr_debug("No matching hardware found\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
err = alloc_chrdev_region(&tuxedo_io_device_handle, 0, 1, "tuxedo_io_cdev");
|
||||||
|
if (err != 0) {
|
||||||
|
pr_err("Failed to allocate chrdev region\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
cdev_init(&tuxedo_io_cdev, &fops_dev);
|
||||||
|
err = (cdev_add(&tuxedo_io_cdev, tuxedo_io_device_handle, 1));
|
||||||
|
if (err < 0) {
|
||||||
|
pr_err("Failed to add cdev\n");
|
||||||
|
unregister_chrdev_region(tuxedo_io_device_handle, 1);
|
||||||
|
}
|
||||||
|
tuxedo_io_device_class = class_create(THIS_MODULE, "tuxedo_io");
|
||||||
|
device_create(tuxedo_io_device_class, NULL, tuxedo_io_device_handle, NULL, "tuxedo_io");
|
||||||
|
pr_debug("Module init successful\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit tuxedo_io_exit(void)
|
||||||
|
{
|
||||||
|
if (id_check_uniwill == 1) {
|
||||||
|
uniwill_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
device_destroy(tuxedo_io_device_class, tuxedo_io_device_handle);
|
||||||
|
class_destroy(tuxedo_io_device_class);
|
||||||
|
cdev_del(&tuxedo_io_cdev);
|
||||||
|
unregister_chrdev_region(tuxedo_io_device_handle, 1);
|
||||||
|
pr_debug("Module exit\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(tuxedo_io_init);
|
||||||
|
module_exit(tuxedo_io_exit);
|
87
src/tuxedo_io/tuxedo_io_ioctl.h
Normal file
87
src/tuxedo_io/tuxedo_io_ioctl.h
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
/*!
|
||||||
|
* Copyright (c) 2019-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 TUXEDO_IO_IOCTL_H
|
||||||
|
#define TUXEDO_IO_IOCTL_H
|
||||||
|
|
||||||
|
#define IOCTL_MAGIC 0xEC
|
||||||
|
|
||||||
|
#define MAGIC_READ_CL IOCTL_MAGIC + 1
|
||||||
|
#define MAGIC_WRITE_CL IOCTL_MAGIC + 2
|
||||||
|
|
||||||
|
#define MAGIC_READ_UW IOCTL_MAGIC + 3
|
||||||
|
#define MAGIC_WRITE_UW IOCTL_MAGIC + 4
|
||||||
|
|
||||||
|
|
||||||
|
// General
|
||||||
|
#define R_MOD_VERSION _IOR(IOCTL_MAGIC, 0x00, char*)
|
||||||
|
|
||||||
|
#define R_HWCHECK_CL _IOR(IOCTL_MAGIC, 0x05, int32_t*)
|
||||||
|
#define R_HWCHECK_UW _IOR(IOCTL_MAGIC, 0x06, int32_t*)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clevo interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Read
|
||||||
|
#define R_CL_HW_IF_STR _IOR(MAGIC_READ_CL, 0x00, char*)
|
||||||
|
#define R_CL_FANINFO1 _IOR(MAGIC_READ_CL, 0x10, int32_t*)
|
||||||
|
#define R_CL_FANINFO2 _IOR(MAGIC_READ_CL, 0x11, int32_t*)
|
||||||
|
#define R_CL_FANINFO3 _IOR(MAGIC_READ_CL, 0x12, int32_t*)
|
||||||
|
// #define R_FANINFO4 _IOR(MAGIC_READ_CL, 0x04, int32_t*)
|
||||||
|
|
||||||
|
#define R_CL_WEBCAM_SW _IOR(MAGIC_READ_CL, 0x13, int32_t*)
|
||||||
|
#define R_CL_FLIGHTMODE_SW _IOR(MAGIC_READ_CL, 0x14, int32_t*)
|
||||||
|
#define R_CL_TOUCHPAD_SW _IOR(MAGIC_READ_CL, 0x15, int32_t*)
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define R_TF_BC _IOW(MAGIC_READ_CL, 0x91, uint32_t*)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Write
|
||||||
|
#define W_CL_FANSPEED _IOW(MAGIC_WRITE_CL, 0x10, int32_t*)
|
||||||
|
#define W_CL_FANAUTO _IOW(MAGIC_WRITE_CL, 0x11, int32_t*)
|
||||||
|
|
||||||
|
#define W_CL_WEBCAM_SW _IOW(MAGIC_WRITE_CL, 0x12, int32_t*)
|
||||||
|
#define W_CL_FLIGHTMODE_SW _IOW(MAGIC_WRITE_CL, 0x13, int32_t*)
|
||||||
|
#define W_CL_TOUCHPAD_SW _IOW(MAGIC_WRITE_CL, 0x14, int32_t*)
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define W_TF_BC _IOW(MAGIC_WRITE_CL, 0x91, uint32_t*)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uniwill interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Read
|
||||||
|
#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_FAN_TEMP _IOR(MAGIC_READ_UW, 0x12, int32_t*)
|
||||||
|
#define R_UW_FAN_TEMP2 _IOR(MAGIC_READ_UW, 0x13, int32_t*)
|
||||||
|
|
||||||
|
#define R_UW_MODE _IOR(MAGIC_READ_UW, 0x14, int32_t*)
|
||||||
|
#define R_UW_MODE_ENABLE _IOR(MAGIC_READ_UW, 0x15, int32_t*)
|
||||||
|
|
||||||
|
// Write
|
||||||
|
#define W_UW_FANSPEED _IOW(MAGIC_WRITE_UW, 0x10, int32_t*)
|
||||||
|
#define W_UW_FANSPEED2 _IOW(MAGIC_WRITE_UW, 0x11, int32_t*)
|
||||||
|
#define W_UW_MODE _IOW(MAGIC_WRITE_UW, 0x12, int32_t*)
|
||||||
|
#define W_UW_MODE_ENABLE _IOW(MAGIC_WRITE_UW, 0x13, int32_t*)
|
||||||
|
|
||||||
|
#endif
|
|
@ -27,10 +27,7 @@
|
||||||
MODULE_AUTHOR("TUXEDO Computers GmbH <tux@tuxedocomputers.com>");
|
MODULE_AUTHOR("TUXEDO Computers GmbH <tux@tuxedocomputers.com>");
|
||||||
MODULE_DESCRIPTION("TUXEDO Computers keyboard & keyboard backlight Driver");
|
MODULE_DESCRIPTION("TUXEDO Computers keyboard & keyboard backlight Driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_VERSION("2.1.0");
|
MODULE_VERSION("3.0.0");
|
||||||
|
|
||||||
MODULE_ALIAS("wmi:" CLEVO_EVENT_GUID);
|
|
||||||
MODULE_ALIAS("wmi:" CLEVO_GET_GUID);
|
|
||||||
|
|
||||||
MODULE_ALIAS("wmi:" UNIWILL_WMI_EVENT_GUID_0);
|
MODULE_ALIAS("wmi:" UNIWILL_WMI_EVENT_GUID_0);
|
||||||
MODULE_ALIAS("wmi:" UNIWILL_WMI_EVENT_GUID_1);
|
MODULE_ALIAS("wmi:" UNIWILL_WMI_EVENT_GUID_1);
|
||||||
|
@ -39,7 +36,6 @@ MODULE_ALIAS("wmi:" UNIWILL_WMI_EVENT_GUID_2);
|
||||||
MODULE_SOFTDEP("pre: tuxedo-cc-wmi");
|
MODULE_SOFTDEP("pre: tuxedo-cc-wmi");
|
||||||
|
|
||||||
static struct tuxedo_keyboard_driver *driver_list[] = {
|
static struct tuxedo_keyboard_driver *driver_list[] = {
|
||||||
&clevo_keyboard_driver,
|
|
||||||
&uniwill_keyboard_driver
|
&uniwill_keyboard_driver
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -80,6 +76,44 @@ err_free_input_device:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct platform_device *tuxedo_keyboard_init_driver(struct tuxedo_keyboard_driver *tk_driver)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
TUXEDO_DEBUG("init driver start\n");
|
||||||
|
|
||||||
|
// If already initiated don't do anything further
|
||||||
|
if (!IS_ERR_OR_NULL(tuxedo_platform_device)) {
|
||||||
|
return tuxedo_platform_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
TUXEDO_DEBUG("create platform bundle\n");
|
||||||
|
|
||||||
|
tuxedo_platform_device = platform_create_bundle(
|
||||||
|
tk_driver->platform_driver, tk_driver->probe, NULL, 0, NULL, 0);
|
||||||
|
|
||||||
|
if (IS_ERR_OR_NULL(tuxedo_platform_device))
|
||||||
|
return tuxedo_platform_device;
|
||||||
|
|
||||||
|
TUXEDO_DEBUG("platform device created\n");
|
||||||
|
|
||||||
|
TUXEDO_DEBUG("initialize input device\n");
|
||||||
|
if (tk_driver->key_map != NULL) {
|
||||||
|
err = tuxedo_input_init(tk_driver->key_map);
|
||||||
|
if (unlikely(err)) {
|
||||||
|
TUXEDO_ERROR("Could not register input device\n");
|
||||||
|
tk_driver->input_device = NULL;
|
||||||
|
} else {
|
||||||
|
TUXEDO_DEBUG("input device registered\n");
|
||||||
|
tk_driver->input_device = tuxedo_input_device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_driver = tk_driver;
|
||||||
|
|
||||||
|
return tuxedo_platform_device;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(tuxedo_keyboard_init_driver);
|
||||||
|
|
||||||
static void __exit tuxedo_input_exit(void)
|
static void __exit tuxedo_input_exit(void)
|
||||||
{
|
{
|
||||||
if (unlikely(!tuxedo_input_device)) {
|
if (unlikely(!tuxedo_input_device)) {
|
||||||
|
@ -94,7 +128,7 @@ static void __exit tuxedo_input_exit(void)
|
||||||
|
|
||||||
static int __init tuxdeo_keyboard_init(void)
|
static int __init tuxdeo_keyboard_init(void)
|
||||||
{
|
{
|
||||||
int i, err;
|
int i;
|
||||||
int num_drivers = sizeof(driver_list) / sizeof(*driver_list);
|
int num_drivers = sizeof(driver_list) / sizeof(*driver_list);
|
||||||
TUXEDO_INFO("Model '%s' found\n",
|
TUXEDO_INFO("Model '%s' found\n",
|
||||||
dmi_get_system_info(DMI_PRODUCT_NAME));
|
dmi_get_system_info(DMI_PRODUCT_NAME));
|
||||||
|
@ -106,25 +140,13 @@ static int __init tuxdeo_keyboard_init(void)
|
||||||
i = 0;
|
i = 0;
|
||||||
while (IS_ERR_OR_NULL(tuxedo_platform_device) && i < num_drivers) {
|
while (IS_ERR_OR_NULL(tuxedo_platform_device) && i < num_drivers) {
|
||||||
current_driver = driver_list[i];
|
current_driver = driver_list[i];
|
||||||
tuxedo_platform_device = platform_create_bundle(
|
tuxedo_keyboard_init_driver(current_driver);
|
||||||
current_driver->platform_driver,
|
|
||||||
current_driver->probe, NULL, 0, NULL, 0);
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_ERR_OR_NULL(tuxedo_platform_device)) {
|
if (IS_ERR_OR_NULL(tuxedo_platform_device)) {
|
||||||
TUXEDO_ERROR("No matching hardware found\n");
|
TUXEDO_DEBUG("No matching hardware found on init\n");
|
||||||
return -ENODEV;
|
current_driver = NULL;
|
||||||
}
|
|
||||||
|
|
||||||
if (current_driver->key_map != NULL) {
|
|
||||||
err = tuxedo_input_init(current_driver->key_map);
|
|
||||||
if (unlikely(err)) {
|
|
||||||
TUXEDO_ERROR("Could not register input device\n");
|
|
||||||
current_driver->input_device = NULL;
|
|
||||||
} else {
|
|
||||||
current_driver->input_device = tuxedo_input_device;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -132,13 +154,16 @@ static int __init tuxdeo_keyboard_init(void)
|
||||||
|
|
||||||
static void __exit tuxdeo_keyboard_exit(void)
|
static void __exit tuxdeo_keyboard_exit(void)
|
||||||
{
|
{
|
||||||
|
TUXEDO_DEBUG("tuxedo_input_exit()\n");
|
||||||
tuxedo_input_exit();
|
tuxedo_input_exit();
|
||||||
|
TUXEDO_DEBUG("platform_device_unregister()\n");
|
||||||
|
if (!IS_ERR_OR_NULL(tuxedo_platform_device))
|
||||||
platform_device_unregister(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);
|
platform_driver_unregister(current_driver->platform_driver);
|
||||||
|
|
||||||
TUXEDO_DEBUG("exit");
|
TUXEDO_DEBUG("exit\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(tuxdeo_keyboard_init);
|
module_init(tuxdeo_keyboard_init);
|
||||||
|
|
|
@ -50,11 +50,13 @@ struct tuxedo_keyboard_driver {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global module devices
|
// Global module devices
|
||||||
static struct platform_device *tuxedo_platform_device;
|
static struct platform_device *tuxedo_platform_device = NULL;
|
||||||
static struct input_dev *tuxedo_input_device;
|
static struct input_dev *tuxedo_input_device = NULL;
|
||||||
|
|
||||||
// Currently chosen driver
|
// Currently chosen driver
|
||||||
static struct tuxedo_keyboard_driver *current_driver;
|
static struct tuxedo_keyboard_driver *current_driver = NULL;
|
||||||
|
|
||||||
|
struct platform_device *tuxedo_keyboard_init_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
|
||||||
|
|
|
@ -89,7 +89,7 @@ static struct key_entry uniwill_wmi_keymap[] = {
|
||||||
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(
|
||||||
current_driver->input_device,
|
uniwill_keyboard_driver.input_device,
|
||||||
UNIWILL_OSD_TOUCHPADWORKAROUND,
|
UNIWILL_OSD_TOUCHPADWORKAROUND,
|
||||||
1,
|
1,
|
||||||
true
|
true
|
||||||
|
@ -244,20 +244,20 @@ static void uniwill_wmi_handle_event(u32 value, void *context, u32 guid_nr)
|
||||||
if (obj) {
|
if (obj) {
|
||||||
if (obj->type == ACPI_TYPE_INTEGER) {
|
if (obj->type == ACPI_TYPE_INTEGER) {
|
||||||
code = obj->integer.value;
|
code = obj->integer.value;
|
||||||
if (!sparse_keymap_report_known_event(current_driver->input_device, code, 1, true)) {
|
if (!sparse_keymap_report_known_event(uniwill_keyboard_driver.input_device, code, 1, true)) {
|
||||||
TUXEDO_DEBUG("[Ev %d] Unknown key - %d (%0#6x)\n", guid_nr, code, code);
|
TUXEDO_DEBUG("[Ev %d] Unknown key - %d (%0#6x)\n", guid_nr, code, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special key combination when mode change key is pressed
|
// Special key combination when mode change key is pressed
|
||||||
if (code == 0xb0) {
|
if (code == 0xb0) {
|
||||||
input_report_key(current_driver->input_device, KEY_LEFTMETA, 1);
|
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTMETA, 1);
|
||||||
input_report_key(current_driver->input_device, KEY_LEFTALT, 1);
|
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTALT, 1);
|
||||||
input_report_key(current_driver->input_device, KEY_F6, 1);
|
input_report_key(uniwill_keyboard_driver.input_device, KEY_F6, 1);
|
||||||
input_sync(current_driver->input_device);
|
input_sync(uniwill_keyboard_driver.input_device);
|
||||||
input_report_key(current_driver->input_device, KEY_F6, 0);
|
input_report_key(uniwill_keyboard_driver.input_device, KEY_F6, 0);
|
||||||
input_report_key(current_driver->input_device, KEY_LEFTALT, 0);
|
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTALT, 0);
|
||||||
input_report_key(current_driver->input_device, KEY_LEFTMETA, 0);
|
input_report_key(uniwill_keyboard_driver.input_device, KEY_LEFTMETA, 0);
|
||||||
input_sync(current_driver->input_device);
|
input_sync(uniwill_keyboard_driver.input_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keyboard backlight brightness toggle
|
// Keyboard backlight brightness toggle
|
||||||
|
|
|
@ -31,6 +31,7 @@ Provides: tuxedo_keyboard = %{version}-%{release}
|
||||||
Obsoletes: tuxedo_keyboard < %{version}-%{release}
|
Obsoletes: tuxedo_keyboard < %{version}-%{release}
|
||||||
Obsoletes: tuxedo-xp-xc-touchpad-key-fix
|
Obsoletes: tuxedo-xp-xc-touchpad-key-fix
|
||||||
Obsoletes: tuxedo-touchpad-fix <= 1.0.1
|
Obsoletes: tuxedo-touchpad-fix <= 1.0.1
|
||||||
|
Obsoletes: tuxedo-cc-wmi
|
||||||
Requires: dkms >= 1.95
|
Requires: dkms >= 1.95
|
||||||
BuildRoot: %{_tmppath}
|
BuildRoot: %{_tmppath}
|
||||||
Packager: Tomte <tux@tuxedocomputers.com>
|
Packager: Tomte <tux@tuxedocomputers.com>
|
||||||
|
@ -71,14 +72,48 @@ for POSTINST in /usr/lib/dkms/common.postinst /usr/share/%{module}/postinst; do
|
||||||
if [ -f $POSTINST ]; then
|
if [ -f $POSTINST ]; then
|
||||||
$POSTINST %{module} %{version} /usr/share/%{module}
|
$POSTINST %{module} %{version} /usr/share/%{module}
|
||||||
RET=$?
|
RET=$?
|
||||||
rmmod %{module} > /dev/null 2>&1 || true
|
|
||||||
modprobe %{module} > /dev/null 2>&1 || true
|
# Attempt to (re-)load module immediately, fail silently if not possible at this stage
|
||||||
|
|
||||||
|
# Also stop tccd service if running before
|
||||||
|
echo "Check tccd running status"
|
||||||
|
if systemctl is-active --quiet tccd.service; then
|
||||||
|
TCCD_RUNNING=true
|
||||||
|
else
|
||||||
|
TCCD_RUNNING=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $TCCD_RUNNING; then
|
||||||
|
echo "Stop tccd temporarily"
|
||||||
|
systemctl stop tccd 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
% Explicitly unload old tuxedo_cc_wmi if loaded at this point
|
||||||
|
rmmod tuxedo_cc_wmi > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo "(Re)load modules if possible"
|
||||||
|
|
||||||
|
rmmod tuxedo_io > /dev/null 2>&1 || true
|
||||||
|
rmmod clevo_wmi > /dev/null 2>&1 || true
|
||||||
|
rmmod clevo_acpi > /dev/null 2>&1 || true
|
||||||
|
rmmod tuxedo_keyboard > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
modprobe tuxedo_keyboard > /dev/null 2>&1 || true
|
||||||
|
modprobe clevo_wmi > /dev/null 2>&1 || true
|
||||||
|
modprobe clevo_acpi > /dev/null 2>&1 || true
|
||||||
|
modprobe tuxedo_io > /dev/null 2>&1 || true
|
||||||
|
|
||||||
# Install default config if none exist already
|
# Install default config if none exist already
|
||||||
if [ ! -f "/etc/modprobe.d/tuxedo_keyboard.conf" ]; then
|
if [ ! -f "/etc/modprobe.d/tuxedo_keyboard.conf" ]; then
|
||||||
cp -f /usr/share/tuxedo-keyboard/tuxedo_keyboard.conf /etc/modprobe.d/tuxedo_keyboard.conf
|
cp -f /usr/share/tuxedo-keyboard/tuxedo_keyboard.conf /etc/modprobe.d/tuxedo_keyboard.conf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Restart tccd after reload if it was running
|
||||||
|
if $TCCD_RUNNING; then
|
||||||
|
echo "Start tccd again"
|
||||||
|
systemctl start tccd 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
exit $RET
|
exit $RET
|
||||||
fi
|
fi
|
||||||
echo "WARNING: $POSTINST does not exist."
|
echo "WARNING: $POSTINST does not exist."
|
||||||
|
|
Loading…
Reference in a new issue