From 134c6d983137764818ff2896486e33647d40815f Mon Sep 17 00:00:00 2001 From: Christian Loritz Date: Fri, 8 Jun 2018 11:56:32 +0200 Subject: [PATCH] Initial commit --- .gitignore | 18 ++ Makefile | 23 ++ README.md | 178 +++++++++++++ dkms.conf | 10 + src/tuxedo_keyboard.c | 565 ++++++++++++++++++++++++++++++++++++++++++ src/tuxedo_keyboard.h | 244 ++++++++++++++++++ 6 files changed, 1038 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 dkms.conf create mode 100644 src/tuxedo_keyboard.c create mode 100644 src/tuxedo_keyboard.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9983fb5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old + +.vscode/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f327190 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +obj-m := ./src/tuxedo_keyboard.o + +tuxedo_tuxedo-objs := ./src/tuxedo_keyboard.o + +PWD := $(shell pwd) +KDIR := /lib/modules/$(shell uname -r)/build + +all: + make -C $(KDIR) M=$(PWD) modules + +clean: + make -C $(KDIR) M=$(PWD) clean + +install: + make -C $(KDIR) M=$(PWD) modules_install + +dkmsadd: + cp -R . /usr/src/tuxedo_keyboard-1 + dkms add -m tuxedo_keyboard -v 1 + +dkmsremove: + dkms remove -m tuxedo_keyboard -v 1 --all + rm -rf /usr/src/tuxedo_keyboard-1 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f71a98a --- /dev/null +++ b/README.md @@ -0,0 +1,178 @@ +# Table of Content +- Description +- Building and Install +- Install +- Using +- Sysfs +- Kernel Parameter +- Modes + +# Description +TUXEDO Computers Kernel module for keyboard backlighting. + +Additions +- Sysfs interface to control the brightness, mode, color, on/off state +- DKMS Ready +- Full RGB Color Support + +# Building and Install + +## Dependencies +- make +- gcc +- linux-headers +- dkms (Only when use this module with the DKMS functionality) + +## Build the Module + +```sh +make clean && make +``` + +## dkms + +### Add as DKMS Module + +At first point add the Module +```sh +make clean +sudo cp -R . /usr/src/tuxedo_keyboard-1 +sudo dkms add -m tuxedo_keyboard -v 1 +``` + +The secound step is compile the module +```sh +sudo dkms build -m tuxedo_keyboard -v 1 +``` + +Install the DKMS module +```sh +sudo dkms install -m tuxedo_keyboard -v 1 +``` + +Load the module with modprobe +```sh +modprobe tuxedo_keyboard +``` + +### Remove DKMS Module + +Remove the dkms module +```sh +sudo dkms remove -m tuxedo_keyboard -v 1 --all +``` + +Remove the source +```sh +sudo rm -rf /usr/src/tuxedo_keyboard-1 +``` + +# Using + +## modprobe + +```sh +modprobe tuxedo_keyboard +``` + +## Load at Boot-Up + +Add Module to /etc/modules +```sh +sudo su +echo tuxedo_keyboard >> /etc/modules +``` + +Default Parameters at start. +In this example we start the kernel module with +- mode 0 (Custom / Default Mode) +- red color for the left side of keyboard +- green color for the center of keyboard +- blue color for the right side of keyboard + +```sh +sudo su +echo "options tuxedo_keyboard mode=0 color_left=0xFF0000 color_center=0x00FF00 color_right=0x0000FF" > /etc/modprobe.d/tuxedo_keyboard.conf +``` + +# Sysfs + +## General +Path: /sys/devices/platform/tuxedo_keyboard + +## color_left +Allowed Values: Hex-Value (e.g. 0xFF0000 for the Color Red) +Description: Set the color of the left Keyboard Side + +## color_center +Allowed Values: Hex-Value (e.g. 0xFF0000 for the Color Red) +Description: Set the color of the center of Keyboard + +## color_right +Allowed Values: Hex-Value (e.g. 0xFF0000 for the Color Red) +Description: Set the color of the right Keyboard Side + +## color_extra +Allowed Values: Hex-Value (e.g. 0xFF0000 for the Color Red) +Description: Set the color of the extra region (if exist) of the Keyboard + +## brightness +Allowed Values: 0 - 255 +Description: Set the brightness of the Keyboard + +## mode +Allowed Values: 0 - 7 +Description: Set the mode of the Keyboard. A list with the modes is under Modes + +## state +Allowed Values: 0, 1 +Description: Set the State of keyboard, 0 is keyboard is off and 1 is keyboard is on + +## extra +Allowed Values: 0, 1 +Description: Only get the information, if the keyboard have the extra region + +# Kernel Parameter + +## Using +```sh +sudo modprobe tuxedo_keyboard +``` + +## color_left +Set the color of the left Keyboard Side + +## color_center +Set the color of the left Keyboard Side + +## color_right +Set the color of the left Keyboard Side + +## color_extra +Set the color of the left Keyboard extra region (Only when is a supported keyboard) + +## mode +Set the mode (on/off) of keyboard + +## brightness +Set the brightness of keyboard + +## state + +# Modes + +## CUSTOM + +## BREATHE + +## CYCLE + +## DANCE + +## FLASH + +## RANDOM_COLOR + +## TEMPO + +## WAVE \ No newline at end of file diff --git a/dkms.conf b/dkms.conf new file mode 100644 index 0000000..cfac8ac --- /dev/null +++ b/dkms.conf @@ -0,0 +1,10 @@ +PACKAGE_NAME=tuxedo_keyboard +PACKAGE_VERSION=1 + +DEST_MODULE_LOCATION=/extra +BUILT_MODULE_NAME=tuxedo_keyboard +BUILT_MODULE_LOCATION=src/ + +MAKE="make -C . KERNELDIR=/lib/modules/${kernelver}/build" +CLEAN="make -C src/ clean" +AUTOINSTALL="yes" \ No newline at end of file diff --git a/src/tuxedo_keyboard.c b/src/tuxedo_keyboard.c new file mode 100644 index 0000000..6bf031c --- /dev/null +++ b/src/tuxedo_keyboard.c @@ -0,0 +1,565 @@ +/* +* tuxedo_keyboard.c +* +* Copyright (C) TUXEDO Computers GmbH +* +* 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. +*/ + +#include "tuxedo_keyboard.h" + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Christian Loritz / TUXEDO Computer GmbH "); +MODULE_DESCRIPTION("TUXEDO Computer Keyboard Backlight Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0.0"); + +// Sysfs Interface for the keyboard state (ON / OFF) +static ssize_t show_state_fs(struct device *child, struct device_attribute *attr, char *buffer) +{ + return sprintf(buffer, "%d\n", keyboard.state); +} + +static ssize_t set_state_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size) +{ + unsigned int val; + int ret = kstrtouint(buffer, 0, &val); + + if (ret) + { + return ret; + } + + val = clamp_t(u8, val, 0, 1); + + set_kb_state(val); + + return ret ? : size; +} + +// Sysfs Interface for the color of the left side (Color as hexvalue) +static ssize_t show_color_left_fs(struct device *child, struct device_attribute *attr, char *buffer) +{ + return sprintf(buffer, "%06x\n", keyboard.color.left); +} + +static ssize_t set_color_left_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size) +{ + return set_color_region(buffer, size, REGION_LEFT); +} + +// Sysfs Interface for the color of the center (Color as hexvalue) +static ssize_t show_color_center_fs(struct device *child, struct device_attribute *attr, char *buffer) +{ + return sprintf(buffer, "%06x\n", keyboard.color.center); +} + +static ssize_t set_color_center_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size) +{ + return set_color_region(buffer, size, REGION_CENTER); +} + +// Sysfs Interface for the color of the right side (Color as hexvalue) +static ssize_t show_color_right_fs(struct device *child, struct device_attribute *attr, char *buffer) +{ + return sprintf(buffer, "%06x\n", keyboard.color.right); +} + +static ssize_t set_color_right_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size) +{ + return set_color_region(buffer, size, REGION_RIGHT); +} + +// Sysfs Interface for the color of the extra region (Color as hexvalue) +static ssize_t show_color_extra_fs(struct device *child, struct device_attribute *attr, char *buffer) +{ + return sprintf(buffer, "%06x\n", keyboard.color.extra); +} + +static ssize_t set_color_extra_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size) +{ + return set_color_region(buffer, size, REGION_EXTRA); +} + +// Sysfs Interface for the keyboard brightness (unsigned int) +static ssize_t show_brightness_fs(struct device *child, struct device_attribute *attr, char *buffer) +{ + return sprintf(buffer, "%d\n", keyboard.brightness); +} + +static ssize_t set_brightness_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size) +{ + unsigned int val; + int ret = kstrtouint(buffer, 0, &val); + + if (ret) + { + return ret; + } + + val = clamp_t(u8, val, BRIGHTNESS_MIN, BRIGHTNESS_MAX); + set_brightness(val); + + return ret ? : size; +} + +// Sysfs Interface for the keyboard mode +static ssize_t show_mode_fs(struct device *child, struct device_attribute *attr, char *buffer) +{ + return sprintf(buffer, "%d\n", keyboard.mode); +} + +static ssize_t set_mode_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size) +{ + unsigned int val; + int ret = kstrtouint(buffer, 0, &val); + + if (ret) + { + return ret; + } + + val = clamp_t(u8, val, 0, ARRAY_SIZE(modes) - 1); + set_mode(val); + + return ret ? : size; +} + +// Sysfs Interface for if the keyboard has extra region +static ssize_t show_hasextra_fs(struct device *child, struct device_attribute *attr, char *buffer) +{ + return sprintf(buffer, "%d\n", keyboard.has_extra); +} + +static int __init tuxdeo_keyboard_init(void) +{ + int err; + + if (!wmi_has_guid(CLEVO_EVENT_GUID)) + { + TUXEDO_ERROR("No known WMI event notification GUID found\n"); + return -ENODEV; + } + + if (!wmi_has_guid(CLEVO_GET_GUID)) + { + TUXEDO_ERROR("No known WMI control method GUID found\n"); + return -ENODEV; + } + + TUXEDO_INFO("Model '%s' found\n", dmi_get_system_info(DMI_PRODUCT_NAME)); + + tuxedo_platform_device = platform_create_bundle(&tuxedo_platform_driver, tuxedo_wmi_probe, NULL, 0, NULL, 0); + if (unlikely(IS_ERR(tuxedo_platform_device))) + { + TUXEDO_ERROR("Can not init Platform driver"); + return PTR_ERR(tuxedo_platform_device); + } + + err = tuxedo_input_init(); + if (unlikely(err)) + { + TUXEDO_ERROR("Could not register input device\n"); + } + + if (device_create_file(&tuxedo_platform_device->dev, &dev_attr_state) != 0) + { + TUXEDO_ERROR("Sysfs attribute creation failed for state\n"); + } + + if (device_create_file(&tuxedo_platform_device->dev, &dev_attr_color_left) != 0) + { + TUXEDO_ERROR("Sysfs attribute creation failed for color left\n"); + } + + if (device_create_file(&tuxedo_platform_device->dev, &dev_attr_color_center) != 0) + { + TUXEDO_ERROR("Sysfs attribute creation failed for color center\n"); + } + + if (device_create_file(&tuxedo_platform_device->dev, &dev_attr_color_right) != 0) + { + TUXEDO_ERROR("Sysfs attribute creation failed for color right\n"); + } + + if(set_color(REGION_EXTRA, KB_COLOR_DEFAULT) == 0) + { + TUXEDO_DEBUG("Keyboard does not support EXTRA Color"); + keyboard.has_extra = 0; + } + else + { + keyboard.has_extra = 1; + if (device_create_file(&tuxedo_platform_device->dev, &dev_attr_color_extra) != 0) + { + TUXEDO_ERROR("Sysfs attribute creation failed for color extra\n"); + } + + set_color(REGION_EXTRA, param_color_extra); + } + + if (device_create_file(&tuxedo_platform_device->dev, &dev_attr_extra) != 0) + { + TUXEDO_ERROR("Sysfs attribute creation failed for extra information flag\n"); + } + + if (device_create_file(&tuxedo_platform_device->dev, &dev_attr_mode) != 0) + { + TUXEDO_ERROR("Sysfs attribute creation failed for mode\n"); + } + + if (device_create_file(&tuxedo_platform_device->dev, &dev_attr_brightness) != 0) + { + TUXEDO_ERROR("Sysfs attribute creation failed for brightness\n"); + } + + keyboard.color.left = param_color_left; + keyboard.color.center = param_color_center; + keyboard.color.right = param_color_right; + keyboard.color.extra = param_color_extra; + + set_color(REGION_LEFT, param_color_left); + set_color(REGION_CENTER, param_color_center); + set_color(REGION_RIGHT, param_color_right); + + set_mode(param_mode); + set_brightness(param_brightness); + set_kb_state(param_state); + + return 0; +} + +static void __exit tuxdeo_keyboard_exit(void) +{ + tuxedo_input_exit(); + + device_remove_file(&tuxedo_platform_device->dev, &dev_attr_state); + device_remove_file(&tuxedo_platform_device->dev, &dev_attr_color_left); + device_remove_file(&tuxedo_platform_device->dev, &dev_attr_color_center); + device_remove_file(&tuxedo_platform_device->dev, &dev_attr_color_right); + device_remove_file(&tuxedo_platform_device->dev, &dev_attr_extra); + device_remove_file(&tuxedo_platform_device->dev, &dev_attr_mode); + device_remove_file(&tuxedo_platform_device->dev, &dev_attr_brightness); + + if(keyboard.has_extra == 1) + { + device_remove_file(&tuxedo_platform_device->dev, &dev_attr_color_extra); + } + + platform_device_unregister(tuxedo_platform_device); + + platform_driver_unregister(&tuxedo_platform_driver); + + TUXEDO_DEBUG("exit"); +} + +static int __init tuxedo_input_init(void) +{ + int err; + + tuxedo_input_device = input_allocate_device(); + if (unlikely(!tuxedo_input_device)) + { + TUXEDO_ERROR("Error allocating input device\n"); + return -ENOMEM; + } + + tuxedo_input_device->name = "TUXEDO Keyboard"; + tuxedo_input_device->phys = DRIVER_NAME "/input0"; + tuxedo_input_device->id.bustype = BUS_HOST; + tuxedo_input_device->dev.parent = &tuxedo_platform_device->dev; + + set_bit(EV_KEY, tuxedo_input_device->evbit); + + err = input_register_device(tuxedo_input_device); + if (unlikely(err)) + { + TUXEDO_ERROR("Error registering input device\n"); + goto err_free_input_device; + } + + return 0; + +err_free_input_device: + input_free_device(tuxedo_input_device); + + return err; +} + +static void __exit tuxedo_input_exit(void) +{ + if (unlikely(!tuxedo_input_device)) + { + return; + } + + input_unregister_device(tuxedo_input_device); + { + tuxedo_input_device = NULL; + } +} + +static int tuxedo_wmi_probe(struct platform_device *dev) +{ + int status; + + status = wmi_install_notify_handler(CLEVO_EVENT_GUID, tuxedo_wmi_notify, NULL); + TUXEDO_DEBUG("clevo_xsm_wmi_probe status: (%0#6x)", status); + + if (unlikely(ACPI_FAILURE(status))) + { + TUXEDO_ERROR("Could not register WMI notify handler (%0#6x)\n", status); + return -EIO; + } + + tuxedo_evaluate_method(GET_AP, 0, NULL); + + return 0; +} + +static int tuxedo_wmi_remove(struct platform_device *dev) +{ + wmi_remove_notify_handler(CLEVO_EVENT_GUID); + return 0; +} + +static int tuxedo_wmi_resume(struct platform_device *dev) +{ + tuxedo_evaluate_method(GET_AP, 0, NULL); + + return 0; +} + +static void tuxedo_wmi_notify(u32 value, void *context) +{ + u32 event; + + tuxedo_evaluate_method(GET_EVENT, 0, &event); + TUXEDO_DEBUG("WMI event (%0#6x)\n", event); + + switch(event) + { + case WMI_CODE_DECREASE_BACKLIGHT: + if(keyboard.brightness == BRIGHTNESS_MIN || (keyboard.brightness - 25) < BRIGHTNESS_MIN) + { + set_brightness(BRIGHTNESS_MIN); + } + else + { + set_brightness(keyboard.brightness - 25); + } + + break; + + case WMI_CODE_INCREASE_BACKLIGHT: + if(keyboard.brightness == BRIGHTNESS_MAX || (keyboard.brightness + 25) > BRIGHTNESS_MAX) + { + set_brightness(BRIGHTNESS_MAX); + } + else + { + set_brightness(keyboard.brightness + 25); + } + + break; + + case WMI_CODE_NEXT_MODE: + set_mode((keyboard.mode + 1) > (ARRAY_SIZE(modes) - 1) ? 0 : (keyboard.mode + 1)); + break; + + case WMI_CODE_TOGGLE_STATE: + set_kb_state(keyboard.state == 0 ? 1 : 0); + break; + + default: + break; + } +} + +static void set_mode(u8 mode) +{ + TUXEDO_INFO("set_mode on %s", modes[mode].name); + + if(!tuxedo_evaluate_method(SET_KB_LED, modes[mode].value, NULL)) + { + keyboard.mode = mode; + } + + if(mode == 0) + { + set_color(REGION_LEFT, keyboard.color.left); + set_color(REGION_CENTER, keyboard.color.center); + set_color(REGION_RIGHT, keyboard.color.right); + + if(keyboard.has_extra == 1) + { + set_color(REGION_EXTRA, keyboard.color.extra); + } + } +} + +static void set_brightness(u8 brightness) +{ + TUXEDO_INFO("Set brightness on %d", brightness); + if (!tuxedo_evaluate_method(SET_KB_LED, 0xF4000000 | brightness, NULL)) + { + keyboard.brightness = brightness; + } +} + +static void set_kb_state(u8 state) +{ + u32 cmd = 0xE0000000; + TUXEDO_INFO("Set keyboard state on: %d\n", state); + + if(state == 0) + { + cmd |= 0x003001; + } + else + { + cmd |= 0x07F001; + } + + if (!tuxedo_evaluate_method(SET_KB_LED, cmd, NULL)) + { + keyboard.state = state; + } +} + +static int tuxedo_evaluate_method(u32 method_id, u32 arg, u32 *retval) +{ + struct acpi_buffer in = { (acpi_size) sizeof(arg), &arg }; + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + u32 tmp; + + TUXEDO_DEBUG("evaluate method: %0#4x IN : %0#6x\n", method_id, arg); + + status = wmi_evaluate_method(CLEVO_GET_GUID, 0x00, method_id, &in, &out); + + if (unlikely(ACPI_FAILURE(status))) + { + TUXEDO_ERROR("evaluate method error"); + goto exit; + } + + obj = (union acpi_object *) out.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + { + tmp = (u32) obj->integer.value; + } + else + { + tmp = 0; + } + + TUXEDO_DEBUG("%0#4x OUT: %0#6x (IN: %0#6x)\n", method_id, tmp, arg); + + if (likely(retval)) + { + *retval = tmp; + } + + kfree(obj); + +exit: + if (unlikely(ACPI_FAILURE(status))) + { + return -EIO; + } + + return 0; +} + +static int set_color(u32 region, u32 color) +{ + u32 cset = ((color & 0x0000FF) << 16) | ((color & 0xFF0000) >> 8) | ((color & 0x00FF00) >> 8); + u32 cmd = region | cset; + + TUXEDO_DEBUG("Set Color '%08x' for region '%08x'", color, region); + + return tuxedo_evaluate_method(SET_KB_LED, cmd, NULL); +} + +static int set_color_region(const char *buffer, size_t size, u32 region) +{ + u32 val; + int ret = kstrtouint(buffer, 0, &val); + + if (ret) + { + return ret; + } + + if(!set_color(region, val)) + { + switch(region) + { + case REGION_LEFT: + keyboard.color.left = val; + break; + case REGION_CENTER: + keyboard.color.center = val; + break; + case REGION_RIGHT: + keyboard.color.right = val; + break; + case REGION_EXTRA: + keyboard.color.extra = val; + break; + } + } + + return ret ? : size; +} + +static int mode_validator(const char *val, const struct kernel_param *kp) +{ + int mode = 0; + int ret; + + ret = kstrtoint(val, 10, &mode); + if (ret != 0 || mode < 0 || mode > (ARRAY_SIZE(modes) - 1)) + { + return -EINVAL; + } + + return param_set_int(val, kp); +} + +static int brightness_validator(const char *val, const struct kernel_param *kp) +{ + int brightness = 0; + int ret; + + ret = kstrtoint(val, 10, &brightness); + if (ret != 0 || brightness < BRIGHTNESS_MIN || brightness > BRIGHTNESS_MAX) + { + return -EINVAL; + } + + return param_set_int(val, kp); +} + +module_init(tuxdeo_keyboard_init); +module_exit(tuxdeo_keyboard_exit); diff --git a/src/tuxedo_keyboard.h b/src/tuxedo_keyboard.h new file mode 100644 index 0000000..3df18e4 --- /dev/null +++ b/src/tuxedo_keyboard.h @@ -0,0 +1,244 @@ +/* +* tuxedo_keyboard.h +* +* Copyright (C) TUXEDO Computers GmbH +* +* 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_KEYBOARD_H +#define _TUXEDO_KEYBOARD_H + +#define DRIVER_NAME "tuxedo_keyboard" +#define pr_fmt(fmt) DRIVER_NAME ": " fmt + +#include +#include +#include + +#define __TUXEDO_PR(lvl, fmt, ...) do { pr_##lvl(fmt, ##__VA_ARGS__); } while (0) +#define TUXEDO_INFO(fmt, ...) __TUXEDO_PR(info, fmt, ##__VA_ARGS__) +#define TUXEDO_ERROR(fmt, ...) __TUXEDO_PR(err, fmt, ##__VA_ARGS__) +#define TUXEDO_DEBUG(fmt, ...) __TUXEDO_PR(debug, "[%s:%u] " fmt, __func__, __LINE__, ##__VA_ARGS__) + +#define BRIGHTNESS_MIN 0 +#define BRIGHTNESS_MAX 255 +#define BRIGHTNESS_DEFAULT BRIGHTNESS_MAX + +#define KB_MODE_DEFAULT 0 // CUSTOM Mode + +#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 REGION_LEFT 0xF0000000 +#define REGION_CENTER 0xF1000000 +#define REGION_RIGHT 0xF2000000 +#define REGION_EXTRA 0xF3000000 + +#define KEYBOARD_BRIGHTNESS 0xF4000000 + +#define COLOR_BLACK 0x000000 +#define COLOR_RED 0xFF0000 +#define COLOR_GREEN 0x00FF00 +#define COLOR_BLUE 0x0000FF +#define COLOR_YELLOW 0xFFFF00 +#define COLOR_MAGENTA 0xFF00FF +#define COLOR_CYAN 0x00FFFF +#define COLOR_WHITE 0xFFFFFF + +#define KB_COLOR_DEFAULT COLOR_BLUE // Default Color Blue +#define DEFAULT_MODE 0 + +// Method IDs for CLEVO_GET +#define GET_EVENT 0x01 +#define GET_AP 0x46 +#define SET_KB_LED 0x67 + +// WMI Codes +#define WMI_CODE_DECREASE_BACKLIGHT 0x81 +#define WMI_CODE_INCREASE_BACKLIGHT 0x82 +#define WMI_CODE_NEXT_MODE 0x83 +#define WMI_CODE_TOGGLE_STATE 0x9F + +#define STEP_BRIGHTNESS_STEP 25 + +// Module Parameter Values +//static bool + +// Sysfs Interface Methods +// Sysfs Interface for the keyboard state (ON / OFF) +static ssize_t show_state_fs(struct device *child, struct device_attribute *attr, char *buffer); +static ssize_t set_state_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size); + +// Sysfs Interface for the color of the left side (Color as hexvalue) +static ssize_t show_color_left_fs(struct device *child, struct device_attribute *attr, char *buffer); +static ssize_t set_color_left_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size); + +// Sysfs Interface for the color of the center (Color as hexvalue) +static ssize_t show_color_center_fs(struct device *child, struct device_attribute *attr, char *buffer); +static ssize_t set_color_center_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size); + +// Sysfs Interface for the color of the right side (Color as hexvalue) +static ssize_t show_color_right_fs(struct device *child, struct device_attribute *attr, char *buffer); +static ssize_t set_color_right_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size); + +// Sysfs Interface for the color of the extra region (Color as hexvalue) +static ssize_t show_color_extra_fs(struct device *child, struct device_attribute *attr, char *buffer); +static ssize_t set_color_extra_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size); + +// Sysfs Interface for the keyboard brightness (unsigned int) +static ssize_t show_brightness_fs(struct device *child, struct device_attribute *attr, char *buffer); +static ssize_t set_brightness_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size); + +// Sysfs Interface for the keyboard mode +static ssize_t show_mode_fs(struct device *child, struct device_attribute *attr, char *buffer); +static ssize_t set_mode_fs(struct device *child, struct device_attribute *attr, const char *buffer, size_t size); + +// Sysfs Interface for if the keyboard has extra region +static ssize_t show_hasextra_fs(struct device *child, struct device_attribute *attr, char *buffer); + +// Keyboard struct +static struct +{ + u8 has_extra; + u8 state; + + struct + { + u32 left; + u32 center; + u32 right; + u32 extra; + } color; + + u8 brightness; + u8 mode; +} keyboard = { + .has_extra = 0, + .mode = DEFAULT_MODE, + .state = 1, + .brightness = BRIGHTNESS_DEFAULT, + .color = { + .left = KB_COLOR_DEFAULT, + .center = KB_COLOR_DEFAULT, + .right = KB_COLOR_DEFAULT, + .extra = KB_COLOR_DEFAULT, + } +}; + +static struct +{ + u8 key; + u32 value; + const char *const name; +} modes[] = { + { .key = 0, .value = 0 , .name = "CUSTOM" }, + { .key = 1, .value = 0x1002a000, .name = "BREATHE" }, + { .key = 2, .value = 0x33010000, .name = "CYCLE" }, + { .key = 3, .value = 0x80000000, .name = "DANCE" }, + { .key = 4, .value = 0xA0000000, .name = "FLASH" }, + { .key = 5, .value = 0x70000000, .name = "RANDOM_COLOR" }, + { .key = 6, .value = 0x90000000, .name = "TEMPO" }, + { .key = 7, .value = 0xB0000000, .name = "WAVE" } +}; + +struct platform_device *tuxedo_platform_device; +static struct input_dev *tuxedo_input_device; + +// Init and Exit methods +static int __init tuxdeo_keyboard_init(void); +static void __exit tuxdeo_keyboard_exit(void); + +static int __init tuxedo_input_init(void); +static void __exit tuxedo_input_exit(void); + +// Methods for controlling the Keyboard +static void set_brightness(u8 brightness); +static void set_kb_state(u8 state); +static void set_mode(u8 mode); +static int set_color(u32 region, u32 color); + +static int set_color_region(const char *buffer, size_t size, u32 region); + +static int tuxedo_wmi_remove(struct platform_device *dev); +static int tuxedo_wmi_resume(struct platform_device *dev); +static int tuxedo_wmi_probe(struct platform_device *dev); +static void tuxedo_wmi_notify(u32 value, void *context); + +static int tuxedo_evaluate_method(u32 method_id, u32 arg, u32 *retval); + +static struct platform_driver tuxedo_platform_driver = { + .remove = tuxedo_wmi_remove, + .resume = tuxedo_wmi_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +// Param Validators +static int mode_validator(const char *val, const struct kernel_param *kp); +static const struct kernel_param_ops param_ops_mode_ops = { + .set = mode_validator, + .get = param_get_int, +}; + +static int brightness_validator(const char *val, const struct kernel_param *kp); +static const struct kernel_param_ops param_ops_brightness_ops = { + .set = brightness_validator, + .get = param_get_int, +}; + +// Params Variables +static uint param_color_left = KB_COLOR_DEFAULT; +module_param_named(color_left, param_color_left, uint, S_IRUSR); +MODULE_PARM_DESC(color_left, "Color for the Left Section"); + +static uint param_color_center = KB_COLOR_DEFAULT; +module_param_named(color_center, param_color_center, uint, S_IRUSR); +MODULE_PARM_DESC(color_center, "Color for the Center Section"); + +static uint param_color_right = KB_COLOR_DEFAULT; +module_param_named(color_right, param_color_right, uint, S_IRUSR); +MODULE_PARM_DESC(color_right, "Color for the Right Right"); + +static uint param_color_extra = KB_COLOR_DEFAULT; +module_param_named(color_extra, param_color_extra, uint, S_IRUSR); +MODULE_PARM_DESC(color_extra, "Color for the Extra Section"); + +static ushort param_mode = DEFAULT_MODE; +module_param_cb(mode, ¶m_ops_mode_ops, ¶m_mode, S_IRUSR); +MODULE_PARM_DESC(mode, "Set the mode"); + +static ushort param_brightness = BRIGHTNESS_DEFAULT; +module_param_cb(brightness, ¶m_ops_brightness_ops, ¶m_brightness, S_IRUSR); +MODULE_PARM_DESC(brightness, "Set the Keyboard Brightness"); + +static bool param_state = true; +module_param_named(state, param_state, bool, S_IRUSR); +MODULE_PARM_DESC(state, "Set the State of the Keyboard TRUE = ON | FALSE = OFF"); + +// Sysfs device Attributes +static DEVICE_ATTR(state, 0644, show_state_fs, set_state_fs); +static DEVICE_ATTR(color_left, 0644, show_color_left_fs, set_color_left_fs); +static DEVICE_ATTR(color_center, 0644, show_color_center_fs, set_color_center_fs); +static DEVICE_ATTR(color_right, 0644, show_color_right_fs, set_color_right_fs); +static DEVICE_ATTR(color_extra, 0644, show_color_extra_fs, set_color_extra_fs); +static DEVICE_ATTR(brightness, 0644, show_brightness_fs, set_brightness_fs); +static DEVICE_ATTR(mode, 0644, show_mode_fs, set_mode_fs); +static DEVICE_ATTR(extra, 0444, show_hasextra_fs, NULL); + +#endif