Re: [PATCH] RFC: HID: razer: Add a driver for Razer Keyboards

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello Linus,

First of all it's very nice of you to try to improve the support for Razer devices on Linux.

Second, as I got fed up with several limitations of OpenRazer, I started basically a rewrite[1] (so the end goal is nearly the same functionality + more) written in C++ and using HIDAPI (so hidraw on Linux) instead of the kernel driver<->sysfs<->user space daemon combo.
I think it would be best to have the key input devices handled in the kernel and the game controller idea is a good idea in my opinion (at least a better one than we currently have[2])

Other than that I wouldn't put too much functionality into the kernel driver as new devices are being released all the time and it would probably take very long for those patches to arrive at users of non-rolling distros.
Exposing half the functionality is not a good way to go in my opinion as the other half would have to implemented in user space then where we are maintaining 2 components again.

I think these are most of the general comments I have.

Luca

[1] https://github.com/z3ntu/razer_test
[2] Currently we remap several special buttons on the keyboard (e.g. brightness down, macro keys) to something like F20-F24. Unfortunately at least one of those seems to clash with XF86MicMute and is triggering the microphone mute functionality.

On November 28, 2018 11:24:43 PM GMT+01:00, Linus Walleij <linus.walleij@xxxxxxxxxx> wrote:
>The Razer gaming accessories are a pretty popular keyboard
>brand. The keyboards as such work pretty well with the standard
>HID interface driver, but will miss out on a bunch of extra
>functionality.
>
>The extra functions have been supported for years by an
>out-of-tree project, OpenRazer. However out-of-tree code
>annoys me because special keys and features don't "just
>work" out of the box with the mainline kernel.
>
>So what about we start to bring this support into the kernel
>properly.
>
>This is a first shot that only support the keyboards, so as
>to limit the scope a bit.
>
>It is tested on the Razer Ornata Chroma and Razer BlackWidow
>Elite, because that's all I have.
>
>The driver registers an additional input device to funnel the
>special keys (these report themselves as "game controller"
>events) and register LEDs with the LED subsystem for the
>backlight and special LEDs on the Razer keyboards.
>
>The "game" LED goes on/off in response to pushing the
>special "game" key (sometimes prefixed with the Fn key).
>This on some keyboards also has the side effect of inhibiting
>the "super" key (Windows) while active. Apparently gamers
>often accidentally push the super key. The HID USB device
>reports two individual keyboard interfaces: one for common
>mode and one for game mode. Some developer noted it is
>a bit over the top to have a separate HID device for a
>game mode that only inhibits a single key, but that is
>how it works.
>
>The "macro" LED goes to on when pressed, the macro is
>then supposed to be recorded, second time it is pressed
>the LED starts flashing meaning it is time to store the
>macro, and after pressing a key where the macro is
>stored, the LED goes off. ESC can abort macro recording.
>The LED is all handled in the driver, and each time we press
>the macro key the standard Linux key KEY_MACRO is reported
>to userspace, that should take care of the actual macro
>recording.
>
>The special keys to increase/decrease backlight on the
>keyboard itself are handled directly in the driver.
>The input subsystem only supports KEY_LIGHTS_TOGGLE, but
>I guess if people prefer we could add KEY_LIGHTS_UP
>and KEY_LIGHTS_DOWN and handle this in userspace,
>looping back to the backlight. I am uncertain about that.
>
>The special matrix effects are fun but (IMHO) pointless to
>abstract further than a plain selection item. They are
>controlled by a sysfs file named "matrix_effect" in the
>class device for the "backlight" LED like this:
>
> > cd /sys/class/leds/razer:rgb:backlight
> > cat matrix_effect
> > [none] static spectrum wave reactive breathing starlight ripple fire
> > echo wave > matrix_effect
>
>TODO:
>- No support for non-keyboard devices. People can send
>  patches.
>- There is no RGB LED color control for the backlight or
>  anything else. Just leave it for now.
>- There is no individual LED control: each key on the
>  keyboard can be assigned a custom color, but we do
>  not support this yet.
>- I imagine RGB LED control need some work in the LED
>  subsystem or alternatively custom sysfs files.
>- I kept some sysfs files from openrazer giving the product
>  name, firmware version and serial number, albeit the usage
>  is dubious. Should I just drop them?
>- Userspace that does something reasonable with KEY_MACRO
>- Userspace that plays with matrix_effects so users don't
>  have to use sysfs.
>- Left some debug code in etc. Well it is an RFC patch
>  after all.
>
>Cc: Terry Cain <terry@xxxxxxxxxxxxxxxxx>
>Cc: Luca Weiss <luca@xxxxxxxxx>
>Cc: Tim Theede <pez2001@xxxxxxxxxxxxxxxxx>
>Cc: Adam Honse <calcprogrammer1@xxxxxxxxx>
>Cc: Steve Kondik <shade@xxxxxxxxxxx>
>Cc: Ian Kumlien <ian.kumlien@xxxxxxxxx>
>Signed-off-by: Linus Walleij <linus.walleij@xxxxxxxxxx>
>---
>This patch requires the LED patch:
>"leds: core: Support blocking HW blink operations" to
>compile if you want to test it:
>https://patches.linaro.org/patch/152135/
>---
> drivers/hid/Kconfig     |    8 +
> drivers/hid/Makefile    |    1 +
> drivers/hid/hid-ids.h   |   38 +-
> drivers/hid/hid-razer.c | 2679 +++++++++++++++++++++++++++++++++++++++
> 4 files changed, 2724 insertions(+), 2 deletions(-)
> create mode 100644 drivers/hid/hid-razer.c
>
>diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
>index 41e9935fc584..00e89ff7489e 100644
>--- a/drivers/hid/Kconfig
>+++ b/drivers/hid/Kconfig
>@@ -807,6 +807,14 @@ config HID_PRIMAX
> 	Support for Primax devices that are not fully compliant with the
> 	HID standard.
> 
>+config HID_RAZER
>+	tristate "Razer gaming accessories"
>+	depends on USB_HID
>+	depends on NEW_LEDS
>+	depends on LEDS_CLASS
>+	---help---
>+	Support for Razer gaming keyboards, mice etc
>+
> config HID_RETRODE
> 	tristate "Retrode 2 USB adapter for vintage video games"
> 	depends on USB_HID
>diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
>index 896a51ce7ce0..8d97cb060d89 100644
>--- a/drivers/hid/Makefile
>+++ b/drivers/hid/Makefile
>@@ -88,6 +88,7 @@ hid-picolcd-$(CONFIG_DEBUG_FS)		+=
>hid-picolcd_debugfs.o
> 
> obj-$(CONFIG_HID_PLANTRONICS)	+= hid-plantronics.o
> obj-$(CONFIG_HID_PRIMAX)	+= hid-primax.o
>+obj-$(CONFIG_HID_RAZER)		+= hid-razer.o
> obj-$(CONFIG_HID_REDRAGON)	+= hid-redragon.o
> obj-$(CONFIG_HID_RETRODE)	+= hid-retrode.o
> obj-$(CONFIG_HID_ROCCAT)	+= hid-roccat.o hid-roccat-common.o \
>diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
>index c0d668944dbe..f1eb8b52e63a 100644
>--- a/drivers/hid/hid-ids.h
>+++ b/drivers/hid/hid-ids.h
>@@ -930,8 +930,42 @@
> #define I2C_VENDOR_ID_RAYDIUM		0x2386
> #define I2C_PRODUCT_ID_RAYDIUM_4B33	0x4b33
> 
>-#define USB_VENDOR_ID_RAZER            0x1532
>-#define USB_DEVICE_ID_RAZER_BLADE_14   0x011D
>+#define USB_VENDOR_ID_RAZER					0x1532
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_ULTIMATE_2012		0x010D
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_STEALTH_EDITION		0x010E
>+#define USB_DEVICE_ID_RAZER_ANANSI				0x010F
>+#define USB_DEVICE_ID_RAZER_NOSTROMO				0x0111
>+#define USB_DEVICE_ID_RAZER_ORBWEAVER				0x0113
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_ULTIMATE_2013		0x011A
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_STEALTH			0x011B
>+#define USB_DEVICE_ID_RAZER_BLADE_14				0x011D
>+#define USB_DEVICE_ID_RAZER_TARTARUS				0x0201
>+#define USB_DEVICE_ID_RAZER_DEATHSTALKER_EXPERT			0x0202
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_CHROMA			0x0203
>+#define USB_DEVICE_ID_RAZER_DEATHSTALKER_CHROMA			0x0204
>+#define USB_DEVICE_ID_RAZER_BLADE_STEALTH			0x0205
>+#define USB_DEVICE_ID_RAZER_ORBWEAVER_CHROMA			0x0207
>+#define USB_DEVICE_ID_RAZER_TARTARUS_CHROMA			0x0208
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_CHROMA_TE		0x0209
>+#define USB_DEVICE_ID_RAZER_BLADE_QHD				0x020F
>+#define USB_DEVICE_ID_RAZER_BLADE_PRO_LATE_2016			0x0210
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_OVERWATCH		0x0211
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_ULTIMATE_2016		0x0214
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_X_CHROMA			0x0216
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_X_ULTIMATE		0x0217
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_X_CHROMA_TE		0x021A
>+#define USB_DEVICE_ID_RAZER_ORNATA_CHROMA			0x021E
>+#define USB_DEVICE_ID_RAZER_ORNATA				0x021F
>+#define USB_DEVICE_ID_RAZER_BLADE_STEALTH_LATE_2016		0x0220
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_CHROMA_V2		0x0221
>+#define USB_DEVICE_ID_RAZER_BLADE_LATE_2016			0x0224
>+#define USB_DEVICE_ID_RAZER_BLADE_PRO_2017			0x0225
>+#define USB_DEVICE_ID_RAZER_BLACKWIDOW_ELITE			0x0228
>+#define USB_DEVICE_ID_RAZER_CYNOSA_CHROMA			0x022A
>+#define USB_DEVICE_ID_RAZER_BLADE_STEALTH_MID_2017		0x022D
>+#define USB_DEVICE_ID_RAZER_BLADE_PRO_2017_FULLHD		0x022F
>+#define USB_DEVICE_ID_RAZER_BLADE_STEALTH_LATE_2017		0x0232
>+#define USB_DEVICE_ID_RAZER_BLADE_2018				0x0233
> 
> #define USB_VENDOR_ID_REALTEK		0x0bda
> #define USB_DEVICE_ID_REALTEK_READER	0x0152
>diff --git a/drivers/hid/hid-razer.c b/drivers/hid/hid-razer.c
>new file mode 100644
>index 000000000000..e7bbfdb4c093
>--- /dev/null
>+++ b/drivers/hid/hid-razer.c
>@@ -0,0 +1,2679 @@
>+// SPDX-License-Identifier: GPL-2.0+
>+/*
>+ * HID driver for Razer gaming accessories.
>+ * Copyright (c) 2018 Linus Walleij <linus.walleij@xxxxxxxxxx>
>+ *
>+ * Based on know-how from the out-of-tree Razer accesories driver:
>+ * Copyright (c) Terry Cain <terry@xxxxxxxxxxxxxxxxx>
>+ * Copyright (c) Luca Weiss <luca@xxxxxxxxx>
>+ * Copyright (c) Tim Theede <pez2001@xxxxxxxxxxxxxxxxx>
>+ * Copyright (c) Adam Honse <calcprogrammer1@xxxxxxxxx>
>+ * Copyright (c) Steve Kondik <shade@xxxxxxxxxxx>
>+ *
>+ * The Razer accessories share a common protocol accessed over the
>+ * USB HID mouse interface with HID_REQ_[SET|GET]_REPORT control
>+ * messages. The message is identical in both directions (to and
>+ * from the device) and consists of 0x5a bytes with the following
>+ * layout:
>+ *
>+ * Byte offset:     Content:
>+ * 0x00             Status (0x00 when sending)
>+ * 0x01             Transaction ID (usually 0xff or 0x3f)
>+ * 0x02             Remaining packets HI byte (big endian)
>+ * 0x03             Remaining packest LO byte (big endian)
>+ * 0x04             Protocol type (always 0x00)
>+ * 0x05             Data size (number of bytes used in the payload)
>+ * 0x06             Command class
>+ * 0x07             Command ID
>+ * 0x08 .. 0x57     Argument (payload)
>+ * 0x58             CRC sum (0x00 XOR bytes 0x02..0x57)
>+ * 0x59             Reserved/unused
>+ */
>+#include <linux/device.h>
>+#include <linux/hid.h>
>+#include <linux/module.h>
>+#include <linux/usb.h>
>+#include <linux/string.h>
>+#include <linux/random.h>
>+#include <linux/sysfs.h>
>+#include <linux/leds.h>
>+#include <linux/dmi.h>
>+#include <linux/mutex.h>
>+#include <linux/bitops.h>
>+#include <linux/bitmap.h>
>+
>+#include "hid-ids.h"
>+
>+/*
>+ * The upstream kernel driver version is bumped to 3.0.0 compared
>+ * to the old out-of-tree driver so that userspace programs have
>+ * a chance to deal with this.
>+ */
>+#define DRIVER_VERSION "3.0.0"
>+
>+/*
>+ * Waiting constants for USB control messages
>+ * some survive at 600us but keep it safe at 900
>+ */
>+#define RAZER_WAIT_MIN_US	900
>+#define RAZER_WAIT_MAX_US	1000
>+
>+#define RAZER_USB_REPORT_LEN	0x5A
>+
>+#define RAZER_CMD_GET_LED_STATE		0x80
>+#define RAZER_CMD_GET_FW_VER		0x81
>+#define RAZER_CMD_GET_SERIAL		0x82
>+#define RAZER_CMD_GET_LED_EFFECT	0x82
>+#define RAZER_CMD_GET_BRIGHTNESS	0x83
>+#define RAZER_CMD_GET_VARIABLE		0x84
>+#define RAZER_CMD_GET_LAYOUT		0x86
>+/* Length 4 reads something that is 01 00 09 00 */
>+#define RAZER_CMD_UNKNOWN_87		0x87
>+
>+#define RAZER_CMD_SET_LED_STATE		0x00
>+/* Length 5 writes 00 07 34 a0 e1 set RGB on LED 7 ? */
>+#define RAZER_CMD_UNKNOWN_01		0x01
>+#define RAZER_CMD_SET_LED_EFFECT	0x02
>+#define RAZER_CMD_SET_LED_EFFECT_ANANSI	0x04
>+#define RAZER_CMD_SET_BRIGHTNESS	0x03
>+#define RAZER_CMD_SET_VARIABLE		0x04
>+#define RAZER_CMD_SET_EFFECT		0x0a
>+
>+/* Response types for commands on the control interface */
>+#define RAZER_CMD_BUSY	 	 0x01
>+#define RAZER_CMD_SUCCESSFUL	 0x02
>+#define RAZER_CMD_FAILURE	 0x03
>+#define RAZER_CMD_TIMEOUT	 0x04
>+#define RAZER_CMD_NOT_SUPPORTED	 0x05
>+
>+enum razer_mode {
>+	RAZER_MODE_NORMAL,
>+	RAZER_MODE_FACTORY,
>+	RAZER_MODE_DRIVER,
>+	RAZER_MODE_UNKNOWN,
>+};
>+
>+enum razer_led_type {
>+	RAZER_LED_SCROLL_WHEEL,
>+	RAZER_LED_BATTERY,
>+	RAZER_LED_LOGO,
>+	RAZER_LED_BACKLIGHT,
>+	RAZER_LED_BACKLIGHT_BLADE,
>+	RAZER_LED_BACKLIGHT_STULT,
>+	RAZER_LED_MACRO,
>+	RAZER_LED_MACRO_ANANSI,
>+	RAZER_LED_GAME,
>+	RAZER_LED_RED_PROFILE,
>+	RAZER_LED_GREEN_PROFILE,
>+	RAZER_LED_BLUE_PROFILE,
>+};
>+
>+enum razer_macro_state {
>+	RAZER_MACRO_OFF,
>+	RAZER_MACRO_RECORD,
>+	RAZER_MACRO_STORE,
>+};
>+
>+enum razer_matrix_effect {
>+	RAZER_EFFECT_NONE,
>+	RAZER_EFFECT_WAVE,
>+	RAZER_EFFECT_SPECTRUM,
>+	RAZER_EFFECT_REACTIVE,
>+	RAZER_EFFECT_STATIC,
>+	RAZER_EFFECT_BREATHING, /* Also known as "pulsate" */
>+	RAZER_EFFECT_STARLIGHT,
>+	RAZER_EFFECT_RIPPLE,
>+	RAZER_EFFECT_FIRE,
>+	/*
>+	 * FIXME: missing effects: audio meter, ambient awareness,
>+	 * wheel ... please find these.
>+	 */
>+};
>+
>+struct razer;
>+
>+/**
>+ * struct razer_led - Information for a Razer LED
>+ */
>+struct razer_led_info {
>+	/**
>+	 * @name: LED name
>+	 */
>+	const char *name;
>+	/**
>+	 * @color: LED color
>+	 */
>+	const char *color;
>+	/**
>+	 * @type: Razer LED type
>+	 */
>+	enum razer_led_type type;
>+};
>+
>+/**
>+ * struct razer_raw_keymap - maps a raw Razer key event to a Linux key
>+ */
>+struct razer_raw_keymap {
>+	/**
>+	 * @name: Name of this key (mostly for debugging)
>+	 */
>+	const char *name;
>+	/**
>+	 * @is_fn: This is the magical fn key, it will not be translated
>+	 * to a Linux key, but keypresses will result in state change
>+	 */
>+	bool is_fn;
>+	/**
>+	 * @razerkey: The Razer key code reported in the raw event
>+	 */
>+	u8 razer_key;
>+	/**
>+	 * @linuxkey: The Linux key code to be reported upward
>+	 */
>+	unsigned int linux_key;
>+};
>+
>+/**
>+ * struct razer_fn_keymap - maps a Razer key while holding fn
>+ * (mostly F1..F8) to a Linux key
>+ */
>+struct razer_fn_keymap {
>+	/**
>+	 * @name: Name of this key (mostly for debugging)
>+	 */
>+	const char *name;
>+	/**
>+	 * @from_key: The Linux key code on the keyboard pressed while
>+	 * holding fn
>+	 */
>+	unsigned int from_key;
>+	/**
>+	 * @to_key: The Linux key code to be reported upward
>+	 */
>+	unsigned int to_key;
>+	/**
>+	 * @is_macro: This is the macro key
>+	 */
>+	bool is_macro;
>+	/**
>+	 * @is_game: This is the game key
>+	 */
>+	bool is_game;
>+	/**
>+	 * @is_bl_down: This is the backlight down key
>+	 */
>+	bool is_bl_down;
>+	/**
>+	 * @is_bl_up: This the backlight up key
>+	 */
>+	bool is_bl_up;
>+	/**
>+	 * @is_profile: This is the profile toggle key (not present on all
>+	 * keyboards)
>+	 */
>+	bool is_profile;
>+	/**
>+	 * @is_razereffect: This is the Razer effect toggle key (not present
>+	 * on all keyboards)
>+	 */
>+	bool is_razereffect;
>+};
>+
>+/**
>+ * struct razer_keyboard - Product information for a Razer keyboard
>+ */
>+struct razer_keyboard {
>+	/**
>+	 * @name: Razer product name
>+	 */
>+	const char *name;
>+	/**
>+	 * @req_res_index_2: The control message index to request device
>+	 * status on the control interface of the device uses index 2.
>+	 * This is an odd outlier: all devices except one use index 1.
>+	 */
>+	bool req_res_index_2;
>+	/**
>+	 * @is_blade: This is a laptop blade laptop keyboard, meaning it is
>+	 * embedded inside one of the Razer gaming laptops
>+	 */
>+	bool is_blade;
>+	/**
>+	 * @is_bw_stealth: This is the BlackWidow Stealth keyboard which has
>+	 * a slightly deviant key mapping for the special Razer keys
>+	 */
>+	bool is_bw_stealth;
>+	/**
>+	 * @volume_wheel: This keyboard has a volume wheel we need to
>+	 * translate from mouse wheel events to volume up/down events.
>+	 */
>+	bool volume_wheel;
>+	/**
>+	 * @extended_effects: This keyboard use extended effect settings in
>+	 * command class 0x0f rather than 0x03.
>+	 */
>+	bool extended_effects;
>+	/**
>+	 * @leds: array of LEDs on this device
>+	 */
>+	const struct razer_led_info *leds;
>+	/**
>+	 * @effects: keyboard backlight effects on this device
>+	 */
>+	const enum razer_matrix_effect *effects;
>+	/**
>+	 * @raw_keymap: map between raw Razer keyboard events and Linux keys
>+	 */
>+	const struct razer_raw_keymap *raw_keymap;
>+	/**
>+	 * @fn_keymap: map for keys pressed while holding down the fn key
>+	 */
>+	const struct razer_fn_keymap *fn_keymap;
>+};
>+
>+/**
>+ * struct razer_led - Information for a Razer LED
>+ */
>+struct razer_led {
>+	/**
>+	 * @led_info: Razer LED info pointer
>+	 */
>+	const struct razer_led_info *info;
>+	/**
>+	 * @r: pointer to main state struct
>+	 */
>+	struct razer *r;
>+	/**
>+	 * @led: LED class device for the Razer LED
>+	 */
>+	struct led_classdev led;
>+};
>+
>+/**
>+ * struct razer_control - State container for control interface
>+ *
>+ * This struct will only be attached to the control interface of the
>Razer
>+ * device.
>+ */
>+struct razer_control {
>+	/**
>+	 * @lock: locks a USB control message transaction
>+	 */
>+	struct mutex lock;
>+	/**
>+	 * @request_buf: buffer to store a report request
>+	 */
>+	u8 request_buf[RAZER_USB_REPORT_LEN];
>+	/**
>+	 * @response_buf: buffer to store the response from a requested
>+	 * report
>+	 */
>+	u8 response_buf[RAZER_USB_REPORT_LEN];
>+	/**
>+	 * @fw_major: major firmware version
>+	 */
>+	u8 fw_major;
>+	/**
>+	 * @fw_minor: minor firmware version
>+	 */
>+	u8 fw_minor;
>+	/**
>+	 * @serial: serial number, DMI serial number can be up to 50 chars
>+	 * and NULL, normal serial numbers are just 22 characters
>+	 */
>+	char serial[51];
>+	/**
>+	 * @mode: the current mode of the device
>+	 */
>+	enum razer_mode mode;
>+	/**
>+	 * @layout: the layout on this keyboard
>+	 */
>+	u8 layout;
>+	/**
>+	 * @backlight: handle to the backlight LED on this keyboard
>+	 */
>+	struct razer_led *backlight;
>+	/**
>+	 * @backlight_brightness: current backlight brightness
>+	 */
>+	enum led_brightness backlight_brightness;
>+	/**
>+	 * @game_mode: if the keyboard is in game mode
>+	 */
>+	bool game_mode;
>+	/**
>+	 * @game_led: handle to the LED that indicates GAME mode on this
>+	 * keyboard
>+	 */
>+	struct razer_led *gameled;
>+	/**
>+	 * @macro_state: the macro key state
>+	 */
>+	enum razer_macro_state macro_state;
>+	/**
>+	 * @macro_led: handle to the LED that indicates MACRO mode on this
>+	 * keyboard
>+	 */
>+	struct razer_led *macroled;
>+	/**
>+	 * @matrix_effect: currently selected matrix effect
>+	 */
>+	enum razer_matrix_effect matrix_effect;
>+	/**
>+	 * @input: input device for the special Razer keys
>+	 */
>+	struct input_dev *input;
>+};
>+
>+/**
>+ * struct razer - State containter for a Razer device
>+ *
>+ * This state container is attached to each interface of the Razer
>device, the control
>+ * interface will contain additional information as well.
>+ */
>+struct razer {
>+	/**
>+	 * @dev: pointer to the parent device
>+	 */
>+	struct device *dev;
>+	/**
>+	 * @hdev: pointer to the HID input device
>+	 */
>+	struct hid_device *hdev;
>+	/**
>+	 * @product: Razer product variant information
>+	 */
>+	const struct razer_keyboard *product;
>+	/**
>+	 * @uif: the interface used on the USB bus
>+	 */
>+	struct usb_interface *uif;
>+	/**
>+	 * @udev: USB device for this device
>+	 */
>+	struct usb_device *udev;
>+	/**
>+	 * @control: the razer control interface state, this will be NULL
>+	 * if this interface is not the control interface
>+	 */
>+	struct razer_control *control;
>+	/**
>+	 * @fn_pressed: Magical fn key is pressed or not
>+	 */
>+	bool fn_pressed;
>+	/**
>+	 * @active_keys: Bitmap of Razer special keys active right now
>+	 * we assume sizeof(unsigned long) will be enough for all
>+	 * custom keys.
>+	 */
>+	unsigned long active_keys;
>+};
>+
>+/**
>+ * razer_get_control() - Get the control interface for a Razer device
>+ *
>+ * We keep a pointer to the control interface in the driver data of
>the
>+ * USB device, and that is how we get at it, unless we're instantiated
>+ * on the control interface itself.
>+ */
>+struct razer_control *razer_get_control(struct razer *r)
>+{
>+	if (r->control)
>+		return r->control;
>+	return dev_get_drvdata(&r->udev->dev);
>+}
>+
>+static void razer_usb_marshal_request(struct razer *r, u8 cmd_class,
>u8 cmd_id, u8 cmd_size)
>+{
>+	struct razer_control *rc = razer_get_control(r);
>+	u8 crc = 0;
>+	unsigned int i;
>+
>+	/* Marshal (serialize) the request */
>+
>+	rc->request_buf[0] = 0x00; /* Status */
>+	/*
>+	 * It appears that the OpenRazer project is spending lots of time
>+	 * trying to hammer this transaction ID to 0x3f on some products, and
>+	 * in some logs the transaction ID 0x1f appears. In my
>(non-exhaustive)
>+	 * tests I have not found that the devices care one bit about the
>+	 * transaction ID, but if you think that your device is not working
>+	 * because the transaction ID is not the same as in the log, go ahead
>+	 * and patch this and see if it helps, who knows.
>+	 */
>+	rc->request_buf[1] = 0xff; /* Transaction ID */
>+	/*
>+	 * I suspect that the only time the remaining packets make any sense
>+	 * is in firmware updates
>+	 */
>+	rc->request_buf[2] = 0x00; /* Remaining packets HI */
>+	rc->request_buf[3] = 0x00; /* Remaining packets LO */
>+	rc->request_buf[4] = 0x00; /* Protocol type (always 0) */
>+	rc->request_buf[5] = cmd_size; /* Data size */
>+	rc->request_buf[6] = cmd_class; /* Command class */
>+	rc->request_buf[7] = cmd_id; /* Command ID */
>+
>+	/* Bytes 8 .. 88 are arguments */
>+
>+	/*
>+	 * Second to last byte of the request or response is a simple
>+	 * checksum: just XOR all bytes at index 2..88 up with overflow
>+	 * and you are done
>+	 */
>+	for(i = 2; i < (RAZER_USB_REPORT_LEN - 2); i++)
>+		crc ^= rc->request_buf[i];
>+
>+	rc->request_buf[88] = crc; /* CRC */
>+	rc->request_buf[89] = 0x00; /* Reserved */
>+}
>+
>+static int razer_usb_check_response(struct razer *r)
>+{
>+	struct razer_control *rc = razer_get_control(r);
>+	u8 status = rc->response_buf[0];
>+	/* Notice big endian format */
>+	u16 req_rp = rc->request_buf[2] << 8 | rc->request_buf[3];
>+	u16 res_rp = rc->response_buf[2] << 8 | rc->response_buf[3];
>+	u8 req_cc = rc->request_buf[6];
>+	u8 res_cc = rc->response_buf[6];
>+	u8 req_id = rc->request_buf[7];
>+	u8 res_id = rc->response_buf[7];
>+
>+	/* First sanity check */
>+	if ((req_rp != res_rp) ||
>+	    (req_cc != res_cc) ||
>+	    (req_id != res_id)) {
>+		dev_err(r->dev, "request does not match response\n");
>+		return -EIO;
>+	}
>+
>+	switch (status) {
>+	case RAZER_CMD_BUSY:
>+		dev_err(r->dev, "command 0x%02x device is busy\n", req_id);
>+		return -EIO;
>+	case RAZER_CMD_FAILURE:
>+		dev_err(r->dev, "command 0x%02x failed\n", req_id);
>+		return -EIO;
>+	case RAZER_CMD_TIMEOUT:
>+		dev_err(r->dev, "command 0x%02x timed out\n", req_id);
>+		return -EIO;
>+	case RAZER_CMD_NOT_SUPPORTED:
>+		dev_err(r->dev, "command 0x%02x not supported\n", req_id);
>+		return -EIO;
>+	default:
>+		dev_dbg(r->dev, "command 0x%02x successful\n", req_id);
>+	}
>+
>+	return 0;
>+}
>+
>+static int razer_usb_request_response(struct razer *r)
>+{
>+	struct razer_control *rc = razer_get_control(r);
>+	int req_res_index;
>+	int ret;
>+
>+	/*
>+	 * All Razer products except an odd one requests a report index 1.
>+	 * One product requests on index 2, I wonder what is on index 1
>+	 * on that product.
>+	 */
>+	if (r->product->req_res_index_2)
>+		req_res_index = 2;
>+	else
>+		req_res_index = 1;
>+
>+	/* Send the request to the device */
>+	ret = usb_control_msg(r->udev,
>+			      usb_rcvctrlpipe(r->udev, 0),
>+			      HID_REQ_SET_REPORT,
>+			      USB_TYPE_CLASS | USB_RECIP_INTERFACE |
>+			      USB_DIR_OUT,
>+			      0x300, /* value */
>+			      req_res_index, /* index */
>+			      rc->request_buf, /* data */
>+			      RAZER_USB_REPORT_LEN,
>+			      USB_CTRL_SET_TIMEOUT);
>+
>+	if (ret != RAZER_USB_REPORT_LEN) {
>+		dev_err(r->dev, "failed request, sent bytes: %d\n", ret);
>+		return -EINVAL;
>+	}
>+
>+	/* Wait after each USB message so as not to stress the interface */
>+	usleep_range(RAZER_WAIT_MIN_US, RAZER_WAIT_MAX_US);
>+
>+	/* Ask for the response */
>+	ret = usb_control_msg(r->udev,
>+			      usb_rcvctrlpipe(r->udev, 0),
>+			      HID_REQ_GET_REPORT,
>+			      USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
>+			      0x300, /* value */
>+			      req_res_index, /* index */
>+			      rc->response_buf, /* data */
>+			      RAZER_USB_REPORT_LEN,
>+			      USB_CTRL_SET_TIMEOUT);
>+
>+	/* Wait after each USB message so as not to stress the interface */
>+	usleep_range(RAZER_WAIT_MIN_US, RAZER_WAIT_MAX_US);
>+
>+	/* Apparently this happens on some devices */
>+	if (ret != RAZER_USB_REPORT_LEN) {
>+		dev_err(r->dev, "short report, report length: 0x%02x\n", ret);
>+		return -EIO;
>+	}
>+
>+	/* Response now contains the RAZER_USB_REPORT_LEN bytes */
>+	return razer_usb_check_response(r);
>+}
>+
>+/**
>+ * razer_send_command() - Sends a command to the keyboard and get the
>response
>+ *
>+ * This will lock the transaction pipe, marshal a request, get the
>response and
>+ * check the result of a command with up to 80 arguments.
>+ */
>+static int razer_send_command(struct razer *r, u8 cmd_class, u8
>cmd_id,
>+			      u8 num_args, u8 *buf)
>+{
>+	struct razer_control *rc = razer_get_control(r);
>+	int ret;
>+
>+	if (num_args > 80) {
>+		dev_err(r->dev, "too many arguments\n");
>+		return -EIO;
>+	}
>+
>+	mutex_lock(&rc->lock);
>+
>+	/* Zero buffer, copy over arguments */
>+	memset(rc->request_buf, 0, sizeof(rc->request_buf));
>+	memcpy(&rc->request_buf[8], buf, num_args);
>+
>+	razer_usb_marshal_request(r, cmd_class, cmd_id, num_args);
>+
>+	ret = razer_usb_request_response(r);
>+	if (ret) {
>+		mutex_unlock(&rc->lock);
>+		return ret;
>+	}
>+
>+	memcpy(buf, &rc->response_buf[8], num_args);
>+
>+	mutex_unlock(&rc->lock);
>+	return 0;
>+}
>+
>+/**
>+ * razer_set_device_mode() - set what mode the device will operate in
>+ *
>+ * Factory mode (0x02) will make M1-5 and FN emit normal keystrokes.
>+ */
>+static int razer_set_device_mode(struct razer *r, enum razer_mode
>mode)
>+{
>+	struct razer_control *rc = razer_get_control(r);
>+	u8 arg[2];
>+	int ret;
>+
>+	/* Blade laptops are in the mode they are */
>+	if (r->product->is_blade)
>+		return 0;
>+
>+	switch (mode) {
>+	case RAZER_MODE_NORMAL:
>+		arg[0] = 0x00;
>+		break;
>+	case RAZER_MODE_FACTORY:
>+		arg[0] = 0x02;
>+		break;
>+	case RAZER_MODE_DRIVER:
>+		arg[0] = 0x03;
>+		break;
>+	default:
>+		dev_err(r->dev, "illegal mode\n");
>+		return -EINVAL;
>+	}
>+
>+	arg[1] = 0x00;
>+	ret = razer_send_command(r, 0, RAZER_CMD_SET_VARIABLE, 2, arg);
>+	if (ret) {
>+		dev_err(r->dev, "set mode request failed\n");
>+		return ret;
>+	}
>+
>+	rc->mode = mode;
>+	return 0;
>+}
>+
>+bool razer_led_is_backlight(struct razer_led *rled)
>+{
>+	const struct razer_led_info *rinfo = rled->info;
>+
>+	return rinfo->type == RAZER_LED_BACKLIGHT ||
>+		rinfo->type == RAZER_LED_BACKLIGHT_BLADE ||
>+		rinfo->type == RAZER_LED_BACKLIGHT_STULT;
>+}
>+
>+int razer_led_blink_set(struct razer *r,
>+			const struct razer_led_info *rinfo,
>+			bool on)
>+{
>+	u8 arg[4];
>+	int ret;
>+
>+	switch (rinfo->type) {
>+	case RAZER_LED_MACRO:
>+		arg[0] = 1; /* VARSTORE? */
>+		arg[1] = 0x07; /* Macro LED */
>+		arg[2] = on ? 1 : 0; /* effects 0..5 */
>+		ret = razer_send_command(r, 3, RAZER_CMD_SET_LED_EFFECT,
>+					 3, arg);
>+		break;
>+	case RAZER_LED_MACRO_ANANSI:
>+		arg[0] = 0;
>+		arg[1] = 0x07; /* Macro LED */
>+		arg[2] = on ? 1 : 0; /* effects 0..5 */
>+		ret = razer_send_command(r, 3, RAZER_CMD_SET_LED_EFFECT,
>+					 3, arg);
>+		if (ret)
>+			return ret;
>+		/* The Anansi needs extra persuation */
>+		arg[0] = 0;
>+		arg[1] = 0x07; /* Macro LED */
>+		arg[2] = 0x05;
>+		arg[3] = 0x05;
>+		ret = razer_send_command(r, 3, RAZER_CMD_SET_LED_EFFECT_ANANSI,
>+					 4, arg);
>+		break;
>+	default:
>+		ret = -EINVAL;
>+		dev_err(r->dev, "can't %s blinking on led %s\n",
>+			on ? "enable" : "disable", rinfo->name);
>+		break;
>+	}
>+
>+	return ret;
>+}
>+
>+int razer_led_blink(struct led_classdev *cled,
>+		    unsigned long *delay_on,
>+		    unsigned long *delay_off)
>+{
>+	struct razer_led *rled = container_of(cled, struct razer_led, led);
>+	const struct razer_led_info *rinfo = rled->info;
>+	struct razer *r = rled->r;
>+
>+	/* This call should always turn the blinking ON */
>+	return razer_led_blink_set(r, rinfo, true);
>+}
>+
>+static int razer_led_set(struct led_classdev *cled,
>+			 enum led_brightness br)
>+{
>+	struct razer_led *rled = container_of(cled, struct razer_led, led);
>+	const struct razer_led_info *rinfo = rled->info;
>+	struct razer *r = rled->r;
>+	u8 arg[3];
>+	int ret;
>+
>+	/*
>+	 * Argument format for 3 arguments:
>+	 * 0: 1 = VARSTORAGE (variable storage)
>+	 * 1: LED ID
>+	 * 2: brightness
>+	 */
>+	switch (rinfo->type) {
>+	case RAZER_LED_BACKLIGHT:
>+		arg[0] = 1;
>+		arg[1] = 0x05; /* 0x00 also work a lot of the time */
>+		arg[2] = br;
>+		if (r->product->extended_effects)
>+			ret = razer_send_command(r, 0x0f,
>+						 RAZER_CMD_SET_VARIABLE,
>+						 3, arg);
>+		else
>+			ret = razer_send_command(r, 3,
>+						 RAZER_CMD_SET_BRIGHTNESS,
>+						 3, arg);
>+		if (ret)
>+			break;
>+		return 0;
>+	case RAZER_LED_BACKLIGHT_BLADE:
>+		/* The blades only have two arguments: LED ID and brightness */
>+		arg[0] = 1;
>+		arg[1] = br;
>+		ret = razer_send_command(r, 0x0e, RAZER_CMD_SET_VARIABLE,
>+					 2, arg);
>+		if (ret)
>+			break;
>+		return 0;
>+	case RAZER_LED_BACKLIGHT_STULT:
>+	case RAZER_LED_LOGO:
>+		arg[0] = 1;
>+		arg[1] = 0x04;
>+		arg[2] = br;
>+		ret = razer_send_command(r, 3, RAZER_CMD_SET_BRIGHTNESS,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return 0;
>+	case RAZER_LED_SCROLL_WHEEL:
>+		arg[0] = 1;
>+		arg[1] = 0x01;
>+		arg[2] = br;
>+		ret = razer_send_command(r, 3, RAZER_CMD_SET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return 0;
>+	case RAZER_LED_BATTERY:
>+		arg[0] = 1;
>+		arg[1] = 0x03;
>+		arg[2] = br;
>+		ret = razer_send_command(r, 3, RAZER_CMD_SET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return 0;
>+	case RAZER_LED_GAME:
>+		arg[0] = 1;
>+		arg[1] = 0x08;
>+		arg[2] = br;
>+		ret = razer_send_command(r, 3, RAZER_CMD_SET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return 0;
>+	case RAZER_LED_MACRO:
>+	case RAZER_LED_MACRO_ANANSI:
>+		/* First turn off the blinking if set to off */
>+		if (br == LED_OFF) {
>+			ret = razer_led_blink_set(r, rinfo, false);
>+			if (ret)
>+				return ret;
>+		}
>+
>+		arg[0] = 1;
>+		arg[1] = 0x07;
>+		arg[2] = br;
>+		ret = razer_send_command(r, 3, RAZER_CMD_SET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return 0;
>+	case RAZER_LED_RED_PROFILE:
>+		arg[0] = 1;
>+		arg[1] = 0x0c;
>+		arg[2] = br;
>+		ret = razer_send_command(r, 3, RAZER_CMD_SET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return 0;
>+	case RAZER_LED_GREEN_PROFILE:
>+		arg[0] = 1;
>+		arg[1] = 0x0d;
>+		arg[2] = br;
>+		ret = razer_send_command(r, 3, RAZER_CMD_SET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return 0;
>+	case RAZER_LED_BLUE_PROFILE:
>+		arg[0] = 1;
>+		arg[1] = 0x0e;
>+		arg[2] = br;
>+		ret = razer_send_command(r, 3, RAZER_CMD_SET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return 0;
>+	default:
>+		dev_err(r->dev, "unknown LED type\n");
>+		ret = -EINVAL;
>+		break;
>+	}
>+
>+	dev_err(r->dev, "LED %s brightness set failed\n", rinfo->name);
>+	return ret;
>+}
>+
>+static enum led_brightness razer_led_get(struct led_classdev *cled)
>+{
>+	struct razer_led *rled = container_of(cled, struct razer_led, led);
>+	const struct razer_led_info *rinfo = rled->info;
>+	struct razer *r = rled->r;
>+	u8 arg[3];
>+	int ret;
>+
>+	arg[2] = 0;
>+	switch (rinfo->type) {
>+	case RAZER_LED_BACKLIGHT:
>+		arg[0] = 1;
>+		arg[1] = 0x05;
>+		if (r->product->extended_effects)
>+			ret = razer_send_command(r, 0x0f,
>+						 RAZER_CMD_GET_VARIABLE,
>+						 3, arg);
>+		else
>+			ret = razer_send_command(r, 3,
>+						 RAZER_CMD_GET_BRIGHTNESS,
>+						 3, arg);
>+		if (ret)
>+			break;
>+		return arg[2];
>+	case RAZER_LED_BACKLIGHT_BLADE:
>+		arg[0] = 1;
>+		arg[1] = 0;
>+		/* The blade laptops use only two arguments */
>+		ret = razer_send_command(r, 0x0e, RAZER_CMD_GET_VARIABLE,
>+					 2, arg);
>+		if (ret)
>+			break;
>+		return arg[1];
>+	case RAZER_LED_BACKLIGHT_STULT:
>+	case RAZER_LED_LOGO:
>+		arg[0] = 1;
>+		arg[1] = 0x04;
>+		ret = razer_send_command(r, 3, RAZER_CMD_GET_BRIGHTNESS,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return arg[2];
>+	case RAZER_LED_SCROLL_WHEEL:
>+		arg[0] = 1;
>+		arg[1] = 0x01;
>+		ret = razer_send_command(r, 3, RAZER_CMD_GET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return arg[2];
>+	case RAZER_LED_BATTERY:
>+		arg[0] = 1;
>+		arg[1] = 0x03;
>+		ret = razer_send_command(r, 3, RAZER_CMD_GET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return arg[2];
>+	case RAZER_LED_GAME:
>+		arg[0] = 1;
>+		arg[1] = 0x08;
>+		ret = razer_send_command(r, 3, RAZER_CMD_GET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return arg[2];
>+	case RAZER_LED_MACRO:
>+	case RAZER_LED_MACRO_ANANSI:
>+		arg[0] = 1;
>+		arg[1] = 0x07;
>+		ret = razer_send_command(r, 3, RAZER_CMD_GET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return arg[2];
>+	case RAZER_LED_RED_PROFILE:
>+		arg[0] = 1;
>+		arg[1] = 0x0c;
>+		ret = razer_send_command(r, 3, RAZER_CMD_GET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return arg[2];
>+	case RAZER_LED_GREEN_PROFILE:
>+		arg[0] = 1;
>+		arg[1] = 0x0d;
>+		ret = razer_send_command(r, 3, RAZER_CMD_GET_LED_STATE,
>+					 3, arg);
>+		if (ret)
>+			break;
>+		return arg[2];
>+	case RAZER_LED_BLUE_PROFILE:
>+		arg[0]




[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux