Re: [PATCH 1/1] input: hid-razer driver: add support for Razer devices

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

 



Good point. I will work on a small kernel module to set the
FN keys (similar to hid-apple), enable macro keys and some 
other settings which might be useful. So it's possible to
set some of these settings without requiring to install
a full-blown project managing these things...

Thanks for your help! I'll report back as soon as I am ready.

Roland

Am 30.08.2016 um 15:43 schrieb Benjamin Tissoires:
> On Aug 30 2016 or thereabouts, Roland Singer wrote:
>> Hi Benjamin,
>>
>> thank you for your hints and your thoughts about using hidraw!
>> I must admit, that you're absolutely right and this really doesn't
>> belong into the kernel. I'll stop the development of the OpenRazer
>> kernel driver and work on an userspace hidraw implementation.
> 
> Well, you should still keep your current work at hand. If you find some
> features useful at boot (like disable the LEDs, enable macro keys,
> Fn lock or anything else that doesn't require a user-space API), I'd be
> happy to review a patch for these.
> 
>>
>> I mentioned your thoughts here in our Github discussion:
>> https://github.com/terrycain/razer-drivers/issues/29#issuecomment-243380127
> 
> Thanks!
> 
> Cheers,
> Benjamin
> 
>>
>> Cheers,
>> Roland
>>
>>
>> Am 29.08.2016 um 17:46 schrieb Benjamin Tissoires:
>>> Hi Roland,
>>>
>>> On Aug 26 2016 or thereabouts, Roland Singer wrote:
>>>> From: Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>>
>>>> This driver adds support for Razer devices. It supports custom keyboard
>>>> and laptop lightning modes, special macro keys and various settings.
>>>> It has been tested with the Razer Blade 14 2016 laptop,
>>>> Razer Blade Stealth 2016 laptop and the BlackWidow Chroma keyboard.
>>>>
>>>> It supports brightness control, lid logo control, FN mode control,
>>>> all lighting modes, set custom key colors and macro keys.
>>>>
>>>> Signed-off-by: Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> ---
>>>> This driver is the initial release and will be extended with further
>>>> Razer device support. Common functionalities have been extracted
>>>> to the razer-common module.
>>>> This driver belongs to the new OpenRazer Project, which also provides
>>>> userland tools to configure and handle Razer devices.
>>>> Reference: https://github.com/openrazer
>>>
>>> I saw your other email, but I still wants to make some high-level review
>>> so that you don't spend too much time in code you will need to remove.
>>>
>>> So here they come:
>>> Generally, it is better to keep the kernel interface to a minimum. It's
>>> OK to enable macro keys from the kernel, but setting the color of the
>>> keyboard is something which should be done IMO in userspace only through
>>> hidraw.
>>>
>>> The reasons are:
>>> - maintaining a kernel API is a pain
>>> - you basically can't change it while it's there given that you might
>>>   have users using it
>>> - roccat used to have a kernel API, and the latest userspace version
>>>   of the tool went away from this given that it was too much of a pain
>>>   and using hidraw allows a lot more flexibility
>>> - we have standard API for LED, and you are not using them
>>> - add some more which would convince you :)
>>>
>>> Please have a look at what hidraw offers and think twice before adding a
>>> sysfs API for a functionality (I'll give you my ideas below). You can
>>> see what we are doing for Logitech and some others in libratbag
>>> (github.com/libratbag/libratbag) or what roccat (the open project, not
>>> the company) did.
>>>
>>>> ---
>>>> diff --git a/Documentation/ABI/testing/sysfs-driver-hid-razer b/Documentation/ABI/testing/sysfs-driver-hid-razer
>>>> new file mode 100644
>>>> index 0000000..594ca18
>>>> --- /dev/null
>>>> +++ b/Documentation/ABI/testing/sysfs-driver-hid-razer
>>>> @@ -0,0 +1,190 @@
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/device_type
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Returns a friendly device type string.
>>>> +		This file is readonly.
>>>> +Users:		https://github.com/openrazer
>>>
>>> Do we need that? (can't this be appended to the device name or something
>>> similar)
>>>
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/get_serial
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Returns the serial number from the device as string.
>>>> +		This file is readonly.
>>>> +Users:		https://github.com/openrazer
>>>> +
>>>
>>> This would fit in the .uniq field of struct input for instance.
>>>
>>> Plus the name is wrong. "serial" is better.
>>>
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/get_firmware_version
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Returns the firmware version from the device as string.
>>>> +		This file is readonly.
>>>> +Users:		https://github.com/openrazer
>>>
>>> Usually we just output it in the dmesg. If your tool needs to retrieve
>>> it, you have the request handy in userspace, so there might not be a
>>> strong need here either.
>>>
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/brightness
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	When read, this file returns the brightness value from 0-255.
>>>> +		When written, this file sets the brightness to the ASCII number
>>>> +		written to this file. Values from 0-255.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>
>>> Please refrain from using custom sysfs when dealing with LEDs. We have
>>> the kernel LED API, which is basic, but it should be sufficient for most
>>> needs.
>>> If you need to do more fancy configurations, then maybe this belongs to
>>> userspace.
>>>
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/fn_mode
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	When read, this file returns the current set FN mode.
>>>> +		When written, this file sets the FN mode to the ASCII number
>>>> +		written to this file.
>>>> +
>>>> +		ASCII VALUE   DESCRIPTION
>>>> +		0             The F-keys work as normal F-keys
>>>> +		1             The F-keys act as if the FN key is held
>>>> +
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>
>>> I'd keep this one, or I would let userspace set the expected fn mode at login.
>>>
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/set_logo
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Sets the logo lighting state to the ASCII number written to this file.
>>>> +
>>>> +		ASCII VALUE   STATE
>>>> +		0             off
>>>> +		1             on
>>>> +
>>>> +		This file is writeonly.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>
>>> Definitively userspace thingy. I mean, the API. I would be happy if you
>>> set a specific mode in the driver at probe to not annoy users using the
>>> VT only.
>>>
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/set_key_colors
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Set the colors of all keys of the keyboard. 3 bytes per color.
>>>> +		This file is writeonly.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:			https://github.com/openrazer
>>>
>>> If you need, use three kernel LED api.
>>> But honestly, do you need to set this at boot in the kernel? If your
>>> tool is required to change a setting, that's a good sign that the
>>> parameter should not be in the kernel.
>>>
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/get_key_rows
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Returns the amount of key rows as number.
>>>> +		This file is readonly.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>
>>> This one and the one after seems tightly linked to the LED setting and
>>> should IMO be done in userspace.
>>>
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/get_key_columns
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Returns the amount of key columns as number.
>>>> +		This file is readonly.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/mode_none
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	When written to, disables keyboard effects / turns the keyboard LEDs off.
>>>> +		This file is writeonly.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/mode_static
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Set the keyboard to static mode when 3 RGB bytes are written.
>>>> +		This file is writeonly.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/mode_custom
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Sets the keyboard to custom mode whenever the file is written to.
>>>> +		Custom colors previously set with set_key_colors are shown.
>>>> +		This file is writeonly.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/mode_wave
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Sets the keyboard to wave mode.
>>>> +
>>>> +		ASCII VALUE   DESCRIPTION
>>>> +		1             wave effect is displayed moving left across the keyboard.
>>>> +		2             wave effect is displayed moving right across the keyboard.
>>>> +
>>>> +		This file is writeonly.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/mode_spectrum
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Sets the keyboard to spectrum mode whenever the file is written to.
>>>> +		This file is writeonly.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/mode_reactive
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Sets the keyboard to reactive mode.
>>>> +		A speed byte and 3 RGB bytes should be written.
>>>> +
>>>> +		VALUE SPEED
>>>> +		1     Short
>>>> +		2     Medium
>>>> +		3     Long
>>>> +
>>>> +		This file is writeonly.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/mode_starlight
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Sets the keyboard to starlight mode.
>>>> +		Requires at least one byte representing the effect speed.
>>>> +
>>>> +		Mode 1: single color. 3 RGB bytes.
>>>> +		Mode 2: two colors. 6 RGB bytes.
>>>> +		Mode 3: random colors. Anything else passed.
>>>> +
>>>> +		VALUE SPEED
>>>> +		1     Short
>>>> +		2     Medium
>>>> +		3     Long
>>>> +
>>>> +		This file is writeonly.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>> +
>>>> +
>>>> +What:		/sys/bus/hid/drivers/hid-razer/<hid-bus>:<vendor-id>:<product-id>.<num>/mode_breath
>>>> +Date:		August 2016
>>>> +Contact:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +Description:	Sets the keyboard to breath mode.
>>>> +		Breathing mode has 3 modes of operation.
>>>> +
>>>> +		Mode 1 fading in and out using a single color. 3 RGB bytes.
>>>> +		Mode 2 is fading in and out between two colors. 6 RGB bytes.
>>>> +		Mode 3 is fading in and out between random colors. Anything else passed.
>>>> +
>>>> +		This file is writeonly.
>>>> +		This file is optional and exists if the device supports this.
>>>> +Users:		https://github.com/openrazer
>>>
>>> All those mode_* will be a pain for you to maintain. When a new device
>>> comes in, you'll have to update the kernel for it, wait for your users
>>> to have it, update your tool and maintain old versions of the modes.
>>>
>>> If Razer decides to add an other mode, you'll have to add a new API, and
>>> if the breathing changes, you might need to add a new
>>> mode_breathing_2...
>>>
>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>> index a306795..594789a 100644
>>>> --- a/MAINTAINERS
>>>> +++ b/MAINTAINERS
>>>> @@ -9771,6 +9771,13 @@ L:	linux-wireless@xxxxxxxxxxxxxxx
>>>>  S:	Orphan
>>>>  F:	drivers/net/wireless/ray*
>>>>  
>>>> +RAZER DRIVERS
>>>> +M:	Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> +W:	https://github.com/openrazer
>>>> +S:	Maintained
>>>> +F:	drivers/hid/hid-razer*
>>>> +F:	Documentation/ABI/*/sysfs-driver-hid-razer*
>>>> +
>>>>  RCUTORTURE MODULE
>>>>  M:	Josh Triplett <josh@xxxxxxxxxxxxxxxx>
>>>>  M:	"Paul E. McKenney" <paulmck@xxxxxxxxxxxxxxxxxx>
>>>> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
>>>> index 78ac481..5e74a0e 100644
>>>> --- a/drivers/hid/Kconfig
>>>> +++ b/drivers/hid/Kconfig
>>>> @@ -702,6 +702,14 @@ config HID_PRIMAX
>>>>  	Support for Primax devices that are not fully compliant with the
>>>>  	HID standard.
>>>>  
>>>> +config HID_RAZER
>>>> +	tristate "Razer device support"
>>>> +	depends on USB_HID
>>>> +	---help---
>>>> +	Support for Razer devices.
>>>> +	Supports custom mouse, keyboard and laptop lightning modes, special keys
>>>> +	and various settings. This driver is required for the OpenRazer project.
>>>> +
>>>>  config HID_ROCCAT
>>>>  	tristate "Roccat device support"
>>>>  	depends on USB_HID
>>>> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
>>>> index fc4b2aa..30fbc8c 100644
>>>> --- a/drivers/hid/Makefile
>>>> +++ b/drivers/hid/Makefile
>>>> @@ -78,6 +78,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-common.o hid-razer.o
>>>>  obj-$(CONFIG_HID_ROCCAT)	+= hid-roccat.o hid-roccat-common.o \
>>>>  	hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
>>>>  	hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \
>>>> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
>>>> index 4ed9a4f..ee76e46 100644
>>>> --- a/drivers/hid/hid-ids.h
>>>> +++ b/drivers/hid/hid-ids.h
>>>> @@ -1085,4 +1085,9 @@
>>>>  #define USB_DEVICE_ID_RAPHNET_2NES2SNES	0x0002
>>>>  #define USB_DEVICE_ID_RAPHNET_4NES4SNES	0x0003
>>>>  
>>>> +#define USB_VENDOR_ID_RAZER                     0x1532
>>>> +#define USB_DEVICE_ID_RAZER_BLADE_STEALTH_2016  0x0205
>>>> +#define USB_DEVICE_ID_RAZER_BLADE_14_2016       0x020F
>>>> +#define USB_DEVICE_ID_RAZER_BLACKWIDOW_CHROMA   0x0203
>>>> +
>>>>  #endif
>>>> diff --git a/drivers/hid/hid-razer-common.c b/drivers/hid/hid-razer-common.c
>>>
>>> What's the point of having 2 files? Roccat was special in a way but we
>>> now prefer having only one file per manufacturer.
>>>
>>>> new file mode 100644
>>>> index 0000000..2f3406d
>>>> --- /dev/null
>>>> +++ b/drivers/hid/hid-razer-common.c
>>>> @@ -0,0 +1,296 @@
>>>> +/*
>>>> + * Razer Kernel Drivers
>>>> + * Copyright (c) 2016 Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> + * Based on Tim Theede <pez2001@xxxxxxxxxxxxxxxxx> razer_chroma_drivers project.
>>>> + *
>>>> + * 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.
>>>> + */
>>>> +
>>>> +#include <linux/printk.h>
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/slab.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/init.h>
>>>> +#include <linux/usb/input.h>
>>>
>>> eeeeks
>>>
>>> HID is transport agnostic, you shouldn't have to include anything usb
>>> related. (unless the device is not using HID after all for configuration)
>>>
>>>> +#include <linux/hid.h>
>>>> +
>>>> +#include "hid-razer-common.h"
>>>> +
>>>> +//###########################//
>>>> +//### Version Information ###//
>>>> +//###########################//
>>>> +
>>>> +MODULE_AUTHOR("Roland Singer <roland.singer@xxxxxxxxxxxxx>");
>>>> +MODULE_DESCRIPTION("USB Razer common driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> +MODULE_VERSION("1.0.0");
>>>> +
>>>> +//##########################//
>>>> +//### Exported Functions ###//
>>>> +//##########################//
>>>> +
>>>> +/*
>>>> + * Initialize a razer device struct.
>>>> + */
>>>> +int razer_init_device(struct razer_device *razer_dev,
>>>> +		      struct usb_device *usb_dev)
>>>> +{
>>>> +	razer_dev->data     = NULL;
>>>> +	razer_dev->usb_dev  = usb_dev;
>>>> +	mutex_init(&razer_dev->lock);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(razer_init_device);
>>>> +
>>>> +/*
>>>> + * Get an initialised razer report
>>>> + */
>>>> +struct razer_report razer_new_report(void)
>>>> +{
>>>> +	struct razer_report new_report;
>>>> +
>>>> +	memset(&new_report, 0, sizeof(new_report));
>>>> +
>>>> +	new_report.status             = RAZER_STATUS_NEW_COMMAND;
>>>> +	new_report.transaction_id     = 0xFF;
>>>> +	new_report.remaining_packets  = 0x00;
>>>> +	new_report.protocol_type      = 0x00;
>>>> +	new_report.reserved           = 0x00;
>>>> +	new_report.command_class      = 0x00;
>>>> +	new_report.command_id         = 0x00;
>>>> +	new_report.data_size          = 0x00;
>>>> +
>>>> +	return new_report;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(razer_new_report);
>>>> +
>>>> +/*
>>>> + * Send an USB control report to the device.
>>>> + * Returns 0 on success.
>>>> + */
>>>> +int _razer_send(struct razer_device *razer_dev, struct razer_report *report)
>>>> +{
>>>> +	const uint size = sizeof(*report);
>>>> +	char *buf;
>>>> +	int len;
>>>> +
>>>> +	buf = kmemdup(report, size, GFP_KERNEL);
>>>> +	if (!buf)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	len =
>>>> +	usb_control_msg(razer_dev->usb_dev,
>>>> +			usb_sndctrlpipe(razer_dev->usb_dev, 0),
>>>> +			HID_REQ_SET_REPORT,                        // Request
>>>> +			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
>>>> +			0x300,                                     // Value
>>>> +			razer_dev->report_index,                   // Index
>>>> +			buf,                                       // Data
>>>> +			size,                                      // Length
>>>> +			USB_CTRL_SET_TIMEOUT);
>>>
>>> Why not using hid_hw_request?
>>>
>>>> +
>>>> +	usleep_range(600, 800);
>>>> +
>>>> +	kfree(buf);
>>>> +
>>>> +	return ((len < 0) ? len : ((len != size) ? -EIO : 0));
>>>> +}
>>>> +
>>>> +int razer_send(struct razer_device *razer_dev, struct razer_report *report)
>>>> +{
>>>> +	int retval;
>>>> +
>>>> +	mutex_lock(&razer_dev->lock);
>>>> +	retval = _razer_send(razer_dev, report);
>>>> +	mutex_unlock(&razer_dev->lock);
>>>> +
>>>> +	return retval;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(razer_send);
>>>> +
>>>> +/*
>>>> + * Get a response from the razer device.
>>>> + * Returns 0 on success.
>>>> + */
>>>> +int _razer_receive(struct razer_device *razer_dev, struct razer_report *report)
>>>> +{
>>>> +	const uint size = sizeof(*report);
>>>> +	int len;
>>>> +
>>>> +	memset(report, 0, size);
>>>> +
>>>> +	len =
>>>> +	usb_control_msg(razer_dev->usb_dev,
>>>> +			usb_rcvctrlpipe(razer_dev->usb_dev, 0),
>>>> +			HID_REQ_GET_REPORT,                          // Request
>>>> +			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
>>>> +			0x300,                                       // Value
>>>> +			razer_dev->report_index,                     // Index
>>>> +			report,                                      // Data
>>>> +			size,
>>>> +			USB_CTRL_SET_TIMEOUT);
>>>> +
>>>> +	usleep_range(600, 800);
>>>> +
>>>> +	return ((len < 0) ? len : ((len != size) ? -EIO : 0));
>>>> +}
>>>> +
>>>> +int razer_receive(struct razer_device *razer_dev, struct razer_report *report)
>>>> +{
>>>> +	int retval;
>>>> +
>>>> +	mutex_lock(&razer_dev->lock);
>>>> +	retval = _razer_receive(razer_dev, report);
>>>> +	mutex_unlock(&razer_dev->lock);
>>>> +
>>>> +	return retval;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(razer_receive);
>>>> +
>>>> +/*
>>>> + * Send a report and wait for a response.
>>>> + * Returns 0 on success.
>>>> + */
>>>> +int _razer_send_with_response(struct razer_device *razer_dev,
>>>> +			      struct razer_report *request_r,
>>>> +			      struct razer_report *response_r)
>>>> +{
>>>> +	int retval, r;
>>>> +
>>>> +	retval = _razer_send(razer_dev, request_r);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	// Retry 40 times when busy -> 125 milliseconds -> max 5 seconds wait
>>>> +	for (r = 0; r < 40; r++) {
>>>> +		retval = _razer_receive(razer_dev, response_r);
>>>> +		if (retval != 0)
>>>> +			return retval;
>>>> +
>>>> +		if (response_r->command_class != request_r->command_class ||
>>>> +		    response_r->command_id != request_r->command_id) {
>>>> +			dev_err(&razer_dev->usb_dev->dev,
>>>> +				"razer_send_with_response: "
>>>> +				"response commands do not match: "
>>>> +				"Request Class: %d "
>>>> +				"Request ID: %d "
>>>> +				"Response Class: %d "
>>>> +				"Response ID: %d\n",
>>>> +				request_r->command_class,
>>>> +				request_r->command_id,
>>>> +				response_r->command_class,
>>>> +				response_r->command_id);
>>>> +			return -EINVAL;
>>>> +		}
>>>> +
>>>> +		switch (response_r->status) {
>>>> +		case RAZER_STATUS_SUCCESS:
>>>> +			return 0;
>>>> +
>>>> +		case RAZER_STATUS_BUSY:
>>>> +			msleep(125);
>>>> +			continue;
>>>> +
>>>> +		case RAZER_STATUS_FAILURE:
>>>> +		case RAZER_STATUS_TIMEOUT:
>>>> +		case RAZER_STATUS_NOT_SUPPORTED:
>>>> +			return -EINVAL;
>>>> +
>>>> +		default:
>>>> +			dev_err(&razer_dev->usb_dev->dev,
>>>> +				"razer_send_with_response: "
>>>> +				"unknown response status 0x%x\n",
>>>> +				response_r->status);
>>>> +			return -EINVAL;
>>>> +		}
>>>> +	}
>>>> +
>>>> +	dev_err(&razer_dev->usb_dev->dev, "razer_send_with_response: "
>>>> +		"request failed: device is busy\n");
>>>> +
>>>> +	return -EBUSY;
>>>> +}
>>>> +
>>>> +int razer_send_with_response(struct razer_device *razer_dev,
>>>> +			     struct razer_report *request_report,
>>>> +			     struct razer_report *response_report)
>>>> +{
>>>> +	int retval;
>>>> +
>>>> +	mutex_lock(&razer_dev->lock);
>>>> +	retval = _razer_send_with_response(razer_dev,
>>>> +					   request_report, response_report);
>>>> +	mutex_unlock(&razer_dev->lock);
>>>> +
>>>> +	return retval;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(razer_send_with_response);
>>>> +
>>>> +/*
>>>> + * Send a report, wait for a response and check the response status.
>>>> + * Returns 0 on success.
>>>> + */
>>>> +int razer_send_check_response(struct razer_device *razer_dev,
>>>> +			      struct razer_report *request_report)
>>>> +{
>>>> +	struct razer_report response_report;
>>>> +
>>>> +	return razer_send_with_response(razer_dev,
>>>> +					request_report, &response_report);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(razer_send_check_response);
>>>> +
>>>> +/*
>>>> + * Calculate the checksum for the usb message
>>>> + *
>>>> + * Checksum byte is stored in the 2nd last byte in the messages payload.
>>>> + * The checksum is generated by XORing all the bytes in the report starting
>>>> + * at byte number 2 (0 based) and ending at byte 88.
>>>> + */
>>>> +unsigned char razer_calculate_crc(struct razer_report *report)
>>>> +{
>>>> +	// Second to last byte of report is a simple checksum.
>>>> +	// Just xor all bytes up with overflow and you are done.
>>>> +	unsigned char crc = 0;
>>>> +	unsigned char *_report = (unsigned char *)report;
>>>> +	unsigned int i;
>>>> +
>>>> +	for (i = 2; i < 88; i++)
>>>> +		crc ^= _report[i];
>>>> +
>>>> +	return crc;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(razer_calculate_crc);
>>>> +
>>>> +/*
>>>> + * Detailed error print
>>>> + */
>>>> +void razer_print_err_report(struct razer_report *report,
>>>> +			    char *driver_name, char *message)
>>>> +{
>>>> +	printk(KERN_WARNING "%s: %s. Status: %02x Transaction ID: %02x"
>>>> +	       " Data Size: %02x Command Class: %02x Command ID: %02x"
>>>> +	       " Params: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
>>>> +	       driver_name,
>>>> +	       message,
>>>> +	       report->status,
>>>> +	       report->transaction_id,
>>>> +	       report->data_size,
>>>> +	       report->command_class,
>>>> +	       report->command_id,
>>>> +	       report->arguments[0], report->arguments[1],
>>>> +	       report->arguments[2], report->arguments[3],
>>>> +	       report->arguments[4], report->arguments[5],
>>>> +	       report->arguments[6], report->arguments[7],
>>>> +	       report->arguments[8], report->arguments[9]);
>>>
>>> %10ph would help you here, instead of using all of the bytes one by one.
>>>
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(razer_print_err_report);
>>>> diff --git a/drivers/hid/hid-razer-common.h b/drivers/hid/hid-razer-common.h
>>>> new file mode 100644
>>>> index 0000000..ec5d191
>>>> --- /dev/null
>>>> +++ b/drivers/hid/hid-razer-common.h
>>>> @@ -0,0 +1,96 @@
>>>> +/*
>>>> + * Razer Kernel Drivers
>>>> + * Copyright (c) 2016 Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> + * Based on Tim Theede <pez2001@xxxxxxxxxxxxxxxxx> razer_chroma_drivers project.
>>>> + *
>>>> + * 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.
>>>> + */
>>>> +
>>>> +#ifndef __HID_RAZER_COMMON_H
>>>> +#define __HID_RAZER_COMMON_H
>>>> +
>>>> +#include <linux/usb.h>
>>>> +#include <linux/types.h>
>>>> +
>>>> +//#############//
>>>> +//### Types ###//
>>>> +//#############//
>>>> +
>>>> +enum razer_status {
>>>> +	RAZER_STATUS_NEW_COMMAND   = 0x00,
>>>> +	RAZER_STATUS_BUSY          = 0x01,
>>>> +	RAZER_STATUS_SUCCESS       = 0x02,
>>>> +	RAZER_STATUS_FAILURE       = 0x03,
>>>> +	RAZER_STATUS_TIMEOUT       = 0x04,
>>>> +	RAZER_STATUS_NOT_SUPPORTED = 0x05
>>>> +};
>>>> +
>>>> +struct razer_device {
>>>> +	struct usb_device *usb_dev;
>>>> +	struct mutex      lock;          // Synchronize usb access.
>>>> +	uint              report_index;  // The report index to use.
>>>> +	void              *data;         // Optional custom data.
>>>> +};
>>>> +
>>>> +struct razer_rgb {
>>>> +	unsigned char r, g, b;
>>>> +};
>>>> +
>>>> +// Report send or received from the device.
>>>> +// transaction_id: Used to group request-response.
>>>> +// remaining_packets: Number of remaining packets in the sequence (Big Endian).
>>>> +// protocol_type: Always 0x0.
>>>> +// data_size:     Size of payload, cannot be greater than 80.
>>>> +//                90 = header (8B) + data + CRC (1B) + Reserved (1B)
>>>> +// command_id:    Type of command being issued.
>>>> +// command_class: Type of command being send. Direction 0 is Host->Device.
>>>> +//                Direction 1 is Device->Host. AKA Get LED 0x80, Set LED 0x00
>>>> +// crc:           xor'ed bytes of report
>>>> +// reserved:      Is always 0x0.
>>>> +struct razer_report {
>>>> +	unsigned char   status;
>>>> +	unsigned char   transaction_id;
>>>> +	unsigned short  remaining_packets;
>>>> +	unsigned char   protocol_type;
>>>> +	unsigned char   data_size;
>>>> +	unsigned char   command_class;
>>>> +	unsigned char   command_id;
>>>> +	unsigned char   arguments[80];
>>>> +	unsigned char   crc;
>>>> +	unsigned char   reserved;
>>>> +};
>>>> +
>>>> +//#################//
>>>> +//### Functions ###//
>>>> +//#################//
>>>> +
>>>> +int razer_init_device(struct razer_device *razer_dev,
>>>> +		      struct usb_device *usb_dev);
>>>> +
>>>> +struct razer_report razer_new_report(void);
>>>> +
>>>> +int razer_send(struct razer_device *razer_dev, struct razer_report *report);
>>>> +
>>>> +int razer_receive(struct razer_device *razer_dev, struct razer_report *report);
>>>> +
>>>> +int razer_send_with_response(struct razer_device *razer_dev,
>>>> +			     struct razer_report *request_report,
>>>> +			     struct razer_report *response_report);
>>>> +
>>>> +int razer_send_check_response(struct razer_device *razer_dev,
>>>> +			      struct razer_report *request_report);
>>>> +
>>>> +unsigned char razer_calculate_crc(struct razer_report *report);
>>>> +
>>>> +void razer_print_err_report(struct razer_report *report,
>>>> +			    char *driver_name, char *message);
>>>> +
>>>> +#endif // __HID_RAZER_COMMON_H
>>>> diff --git a/drivers/hid/hid-razer.c b/drivers/hid/hid-razer.c
>>>> new file mode 100644
>>>> index 0000000..9cbc53f
>>>> --- /dev/null
>>>> +++ b/drivers/hid/hid-razer.c
>>>> @@ -0,0 +1,1430 @@
>>>> +/*
>>>> + * Razer Kernel Drivers
>>>> + * Copyright (c) 2016 Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> + * Based on Tim Theede <pez2001@xxxxxxxxxxxxxxxxx> razer_chroma_drivers project.
>>>> + *
>>>> + * 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.
>>>> + */
>>>> +
>>>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>>>> +
>>>> +#include <linux/printk.h>
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/slab.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/init.h>
>>>> +#include <linux/usb/input.h>
>>>> +#include <linux/hid.h>
>>>> +#include <linux/dmi.h>
>>>> +
>>>> +#include "hid-ids.h"
>>>> +#include "hid-razer-common.h"
>>>> +#include "hid-razer.h"
>>>> +
>>>> +//###########################//
>>>> +//### Version Information ###//
>>>> +//###########################//
>>>> +
>>>> +MODULE_AUTHOR("Roland Singer <roland.singer@xxxxxxxxxxxxx>");
>>>> +MODULE_DESCRIPTION("USB HID Razer Driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> +MODULE_VERSION("1.0.0");
>>>> +
>>>> +//########################//
>>>> +//### Helper functions ###//
>>>> +//########################//
>>>> +
>>>> +// Get the firmware version.
>>>> +int razer_get_firmware_version(struct razer_device *razer_dev,
>>>> +			       unsigned char *fw_string)
>>>> +{
>>>> +	int retval;
>>>> +	struct razer_report response_r;
>>>> +	struct razer_report request_r = razer_new_report();
>>>> +
>>>> +	request_r.command_class = 0x00;
>>>> +	request_r.command_id    = 0x81;
>>>> +	request_r.data_size     = 0x00;
>>>> +	request_r.crc           = razer_calculate_crc(&request_r);
>>>> +
>>>> +	retval = razer_send_with_response(razer_dev, &request_r, &response_r);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&response_r, KBUILD_MODNAME,
>>>> +				       "get_firmware_version: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	retval = sprintf(fw_string, "v%d.%d", response_r.arguments[0],
>>>> +			 response_r.arguments[1]);
>>>> +	if (retval <= 0) {
>>>> +		pr_warn("get_firmware_version: failed to compose string");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Get brightness of the keyboard.
>>>> +int razer_get_brightness(struct razer_device *razer_dev)
>>>> +{
>>>> +	int retval;
>>>> +	struct usb_device *usb_dev          = razer_dev->usb_dev;
>>>> +	struct razer_report request_report  = razer_new_report();
>>>> +	struct razer_report response_report;
>>>> +	int response_value_index            = 1;
>>>> +	const unsigned int product_id       = usb_dev->descriptor.idProduct;
>>>> +
>>>> +	request_report.command_class = 0x0E;
>>>> +	request_report.command_id    = 0x84;
>>>> +	request_report.data_size     = 0x01;
>>>> +	request_report.arguments[0]  = 0x01;     // LED Class
>>>> +
>>>> +	// Device support.
>>>> +	if (product_id == USB_DEVICE_ID_RAZER_BLACKWIDOW_CHROMA) {
>>>> +		request_report.command_class    = 0x03;
>>>> +		request_report.command_id       = 0x83;
>>>> +		request_report.arguments[1]     = 0x05;     // Backlight LED
>>>> +		request_report.data_size        = 0x02;
>>>> +		response_value_index            = 2;
>>>> +	}
>>>> +
>>>> +	request_report.crc = razer_calculate_crc(&request_report);
>>>> +
>>>> +	retval = razer_send_with_response(razer_dev,
>>>> +					  &request_report, &response_report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&response_report, KBUILD_MODNAME,
>>>> +				       "get_brightness: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return response_report.arguments[response_value_index];
>>>> +}
>>>> +
>>>> +// Set the keyboard brightness.
>>>> +int razer_set_brightness(struct razer_device *razer_dev,
>>>> +			 unsigned char brightness)
>>>> +{
>>>> +	int retval;
>>>> +	struct usb_device *usb_dev    = razer_dev->usb_dev;
>>>> +	struct razer_report report    = razer_new_report();
>>>> +	const unsigned int product_id = usb_dev->descriptor.idProduct;
>>>> +
>>>> +	report.command_class = 0x0E;
>>>> +	report.command_id    = 0x04;
>>>> +	report.data_size     = 0x02;
>>>> +	report.arguments[0]  = 0x01;
>>>> +	report.arguments[1]  = brightness;
>>>> +
>>>> +	// Device support.
>>>> +	if (product_id == USB_DEVICE_ID_RAZER_BLACKWIDOW_CHROMA) {
>>>> +		report.command_class    = 0x03;
>>>> +		report.command_id       = 0x03;
>>>> +		report.arguments[1]     = 0x05;     // Backlight LED
>>>> +		report.arguments[2]     = brightness;
>>>> +		report.data_size        = 0x03;
>>>> +	}
>>>> +
>>>> +	report.crc = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "set_brightness: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Set the logo lighting state (on/off only)
>>>> +int razer_set_logo(struct razer_device *razer_dev, unsigned char state)
>>>> +{
>>>> +	int retval;
>>>> +	struct razer_report report = razer_new_report();
>>>> +
>>>> +	if (state != 0 && state != 1) {
>>>> +		pr_warn("set_logo: logo lighting state must be either 0 or 1: "
>>>> +			"got: %d\n", state);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	report.command_class = 0x03;
>>>> +	report.command_id    = 0x00;
>>>> +	report.data_size     = 0x03;
>>>> +	report.arguments[0]  = 0x01;     // LED Class
>>>> +	report.arguments[1]  = 0x04;     // LED ID, Logo
>>>> +	report.arguments[2]  = state;    // State
>>>> +	report.crc           = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "set_logo: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Toggle FN key
>>>> +int razer_set_fn_mode(struct razer_device *razer_dev, unsigned char state)
>>>> +{
>>>> +	int retval;
>>>> +	struct razer_data *data     = razer_dev->data;
>>>> +	struct razer_report report  = razer_new_report();
>>>> +
>>>> +	if (state != 0 && state != 1) {
>>>> +		pr_warn("fn_mode: must be either 0 or 1: got: %d\n", state);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	report.command_class = 0x02;
>>>> +	report.command_id    = 0x06;
>>>> +	report.data_size     = 0x02;
>>>> +	report.arguments[0]  = 0x00;
>>>> +	report.arguments[1]  = state; // State
>>>> +	report.crc           = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "fn_mode: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	// Save the new fn mode state.
>>>> +	data->fn_mode_state = (char)state;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Returns the row count of the keyboard.
>>>> +// On error a value smaller than 0 is returned.
>>>> +int razer_get_rows(struct usb_device *usb_dev)
>>>> +{
>>>> +	switch (usb_dev->descriptor.idProduct) {
>>>> +	case USB_DEVICE_ID_RAZER_BLADE_STEALTH_2016:
>>>> +		return RAZER_STEALTH_2016_ROWS;
>>>> +
>>>> +	case USB_DEVICE_ID_RAZER_BLADE_14_2016:
>>>> +		return RAZER_BLADE_14_2016_ROWS;
>>>> +
>>>> +	case USB_DEVICE_ID_RAZER_BLACKWIDOW_CHROMA:
>>>> +		return RAZER_BLACKWIDOW_CHROMA_ROWS;
>>>> +
>>>> +	default:
>>>> +		return -EINVAL;
>>>> +	}
>>>> +}
>>>> +
>>>> +// Returns the column count of the keyboard.
>>>> +// On error a value smaller than 0 is returned.
>>>> +int razer_get_columns(struct usb_device *usb_dev)
>>>> +{
>>>> +	switch (usb_dev->descriptor.idProduct) {
>>>> +	case USB_DEVICE_ID_RAZER_BLADE_STEALTH_2016:
>>>> +		return RAZER_STEALTH_2016_COLUMNS;
>>>> +
>>>> +	case USB_DEVICE_ID_RAZER_BLADE_14_2016:
>>>> +		return RAZER_BLADE_14_2016_COLUMNS;
>>>> +
>>>> +	case USB_DEVICE_ID_RAZER_BLACKWIDOW_CHROMA:
>>>> +		return RAZER_BLACKWIDOW_CHROMA_COLUMNS;
>>>> +
>>>> +	default:
>>>> +		return -EINVAL;
>>>> +	}
>>>> +}
>>>> +
>>>> +// Set the key colors for a specific row. Takes in an array of RGB bytes.
>>>> +int razer_set_key_row(struct razer_device *razer_dev, unsigned char row_index,
>>>> +		      unsigned char *row_cols, size_t row_cols_len)
>>>> +{
>>>> +	int retval;
>>>> +	int rows                        = razer_get_rows(razer_dev->usb_dev);
>>>> +	int columns                     = razer_get_columns(razer_dev->usb_dev);
>>>> +	size_t row_cols_required_len    = columns * 3;
>>>> +	struct razer_report report      = razer_new_report();
>>>> +
>>>> +	if (rows < 0 || columns < 0) {
>>>> +		pr_warn("set_key_row: unsupported device\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	// Validate the input.
>>>> +	if (row_index >= rows) {
>>>> +		pr_warn("set_key_row: invalid row index: %d\n", row_index);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +	if (row_cols_len != row_cols_required_len) {
>>>> +		pr_warn("set_key_row: wrong amount of RGB data provided: "
>>>> +			"%lu of %lu\n", row_cols_len, row_cols_required_len);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	report.command_class  = 0x03;
>>>> +	report.command_id     = 0x0B;
>>>> +	report.data_size      = row_cols_required_len + 4;
>>>> +	report.transaction_id = 0x80;         // Set a custom transaction ID.
>>>> +	report.arguments[0]   = 0xFF;         // Frame ID
>>>> +	report.arguments[1]   = row_index;    // Row
>>>> +	report.arguments[2]   = 0x00;         // Start Index
>>>> +	report.arguments[3]   = columns - 1;  // End Index (Calc to end of row)
>>>> +	memcpy(&report.arguments[4], row_cols, row_cols_required_len);
>>>> +	report.crc            = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "set_key_row: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return retval;
>>>> +}
>>>> +
>>>> +// Set the key colors for the complete keyboard. Takes in an array of RGB bytes.
>>>> +int razer_set_key_colors(struct razer_device *razer_dev,
>>>> +			 unsigned char *row_cols, size_t row_cols_len)
>>>> +{
>>>> +	int i, retval;
>>>> +	int rows                        = razer_get_rows(razer_dev->usb_dev);
>>>> +	int columns                     = razer_get_columns(razer_dev->usb_dev);
>>>> +	size_t row_cols_required_len    = columns * 3 * rows;
>>>> +
>>>> +	if (columns < 0 || rows < 0 || row_cols_required_len < 0) {
>>>> +		pr_warn("set_key_colors: unsupported device\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	// Validate the input.
>>>> +	if (row_cols_len != row_cols_required_len) {
>>>> +		pr_warn("set_key_colors: wrong amount of RGB data provided: "
>>>> +			"%lu of %lu\n", row_cols_len, row_cols_required_len);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	for (i = 0; i < rows; i++) {
>>>> +		retval =
>>>> +		razer_set_key_row(razer_dev, i,
>>>> +				  (unsigned char *)&row_cols[i * columns * 3],
>>>> +				   columns * 3);
>>>> +		if (retval != 0) {
>>>> +			pr_warn("set_key_colors: failed to set colors for row: "
>>>> +				"%d\n", i);
>>>> +			return retval;
>>>> +		}
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Enable keyboard macro keys.
>>>> +// Keycodes for the macro keys are 191-195 for M1-M5.
>>>> +int razer_activate_macro_keys(struct razer_device *razer_dev)
>>>> +{
>>>> +	int retval;
>>>> +	struct razer_report report = razer_new_report();
>>>> +
>>>> +	report.command_class = 0x00;
>>>> +	report.command_id    = 0x04;
>>>> +	report.data_size     = 0x02;
>>>> +	report.arguments[0]  = 0x02;
>>>> +	report.arguments[1]  = 0x04;
>>>> +	report.crc           = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "activate_macro_keys: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Disable any keyboard effect
>>>> +int razer_set_none_mode(struct razer_device *razer_dev)
>>>> +{
>>>> +	int retval;
>>>> +	struct razer_report report = razer_new_report();
>>>> +
>>>> +	report.command_class = 0x03;
>>>> +	report.command_id    = 0x0A;
>>>> +	report.data_size     = 0x01;
>>>> +	report.arguments[0]  = 0x00; // Effect ID
>>>> +	report.crc           = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "none_mode: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Set static effect on the keyboard
>>>> +int razer_set_static_mode(struct razer_device *razer_dev,
>>>> +			  struct razer_rgb *color)
>>>> +{
>>>> +	int retval;
>>>> +	struct razer_report report = razer_new_report();
>>>> +
>>>> +	report.command_class = 0x03;
>>>> +	report.command_id    = 0x0A;
>>>> +	report.data_size     = 0x04;
>>>> +	report.arguments[0]  = 0x06;     // Effect ID
>>>> +	report.arguments[1]  = color->r;
>>>> +	report.arguments[2]  = color->g;
>>>> +	report.arguments[3]  = color->b;
>>>> +	report.crc           = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "static_mode: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Set custom effect on the keyboard
>>>> +int razer_set_custom_mode(struct razer_device *razer_dev)
>>>> +{
>>>> +	int retval;
>>>> +	struct razer_report report = razer_new_report();
>>>> +
>>>> +	report.command_class = 0x03;
>>>> +	report.command_id    = 0x0A;
>>>> +	report.data_size     = 0x02;
>>>> +	report.arguments[0]  = 0x05; // Effect ID
>>>> +	report.arguments[1]  = 0x00; // Data frame ID
>>>> +	report.crc           = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "custom_mode: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Set the wave effect on the keyboard
>>>> +int razer_set_wave_mode(struct razer_device *razer_dev,
>>>> +			unsigned char direction)
>>>> +{
>>>> +	int retval;
>>>> +	struct razer_report report = razer_new_report();
>>>> +
>>>> +	if (direction != 1 && direction != 2) {
>>>> +		pr_warn("wave_mode: wave direction must be 1 or 2: got: %d\n",
>>>> +			direction);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	report.command_class = 0x03;
>>>> +	report.command_id    = 0x0A;
>>>> +	report.data_size     = 0x02;
>>>> +	report.arguments[0]  = 0x01; // Effect ID
>>>> +	report.arguments[1]  = direction; // Direction
>>>> +	report.crc           = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "wave_mode: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Set spectrum effect on the keyboard
>>>> +int razer_set_spectrum_mode(struct razer_device *razer_dev)
>>>> +{
>>>> +	int retval;
>>>> +	struct razer_report report = razer_new_report();
>>>> +
>>>> +	report.command_class = 0x03;
>>>> +	report.command_id    = 0x0A;
>>>> +	report.data_size     = 0x01;
>>>> +	report.arguments[0]  = 0x04; // Effect ID
>>>> +	report.crc           = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "spectrum_mode: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Set reactive effect on the keyboard
>>>> +int razer_set_reactive_mode(struct razer_device *razer_dev,
>>>> +			    unsigned char speed, struct razer_rgb *color)
>>>> +{
>>>> +	int retval = 0;
>>>> +	struct razer_report report = razer_new_report();
>>>> +
>>>> +	if (speed <= 0 || speed >= 4) {
>>>> +		pr_warn("reactive_mode: speed must be within 1-3: got: %d\n",
>>>> +			speed);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	report.command_class = 0x03;
>>>> +	report.command_id    = 0x0A;
>>>> +	report.data_size     = 0x05;
>>>> +	report.arguments[0]  = 0x02;     // Effect ID
>>>> +	report.arguments[1]  = speed;    // Speed
>>>> +	report.arguments[2]  = color->r;
>>>> +	report.arguments[3]  = color->g;
>>>> +	report.arguments[4]  = color->b;
>>>> +	report.crc           = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "reactive_mode: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Set the starlight effect on the keyboard.
>>>> +// Possible speed values: 1-3.
>>>> +// 1. Random color mode: set both colors to NULL
>>>> +// 2. One color mode: Set color1 and color2 to NULL
>>>> +// 3. Two color mode: Set both colors
>>>> +int razer_set_starlight_mode(struct razer_device *razer_dev,
>>>> +			     unsigned char speed, struct razer_rgb *color1,
>>>> +			     struct razer_rgb *color2)
>>>> +{
>>>> +	int retval;
>>>> +	struct razer_report report = razer_new_report();
>>>> +
>>>> +	if (speed <= 0 || speed >= 4) {
>>>> +		pr_warn("starlight_mode: speed must be within 1-3: got: %d\n",
>>>> +			speed);
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	report.command_class = 0x03;
>>>> +	report.command_id    = 0x0A;
>>>> +	report.arguments[0]  = 0x19;     // Effect ID
>>>> +	report.arguments[2]  = speed;    // Speed
>>>> +
>>>> +	if (!color1 && !color2) {
>>>> +		report.arguments[1] = 0x03;       // Random colors
>>>> +		report.data_size    = 0x03;
>>>> +	} else if (color1 && !color2) {
>>>> +		report.arguments[1] = 0x01;       // One color
>>>> +		report.arguments[3] = color1->r;
>>>> +		report.arguments[4] = color1->g;
>>>> +		report.arguments[5] = color1->b;
>>>> +		report.data_size    = 0x06;
>>>> +	} else if (color1 && color2) {
>>>> +		report.arguments[1] = 0x02;       // Two colors
>>>> +		report.arguments[3] = color1->r;
>>>> +		report.arguments[4] = color1->g;
>>>> +		report.arguments[5] = color1->b;
>>>> +		report.arguments[6] = color2->r;
>>>> +		report.arguments[7] = color2->g;
>>>> +		report.arguments[8] = color2->b;
>>>> +		report.data_size    = 0x09;
>>>> +	} else {
>>>> +		pr_warn("starlight_mode: invalid colors set\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	report.crc = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "starlight_mode: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +// Set breath effect on the keyboard.
>>>> +// 1. Random color mode: set both colors to NULL
>>>> +// 2. One color mode: Set color1 and color2 to NULL
>>>> +// 3. Two color mode: Set both colors
>>>> +int razer_set_breath_mode(struct razer_device *razer_dev,
>>>> +			  struct razer_rgb *color1, struct razer_rgb *color2)
>>>> +{
>>>> +	int retval;
>>>> +	struct razer_report report = razer_new_report();
>>>> +
>>>> +	report.command_class = 0x03;
>>>> +	report.command_id    = 0x0A;
>>>> +	report.arguments[0]  = 0x03; // Effect ID
>>>> +
>>>> +	if (!color1 && !color2) {
>>>> +		report.arguments[1] = 0x03;       // Random colors
>>>> +		report.data_size    = 0x02;
>>>> +	} else if (color1 && !color2) {
>>>> +		report.arguments[1] = 0x01;       // One color
>>>> +		report.arguments[2] = color1->r;
>>>> +		report.arguments[3] = color1->g;
>>>> +		report.arguments[4] = color1->b;
>>>> +		report.data_size    = 0x05;
>>>> +	} else if (color1 && color2) {
>>>> +		report.arguments[1] = 0x02;       // Two colors
>>>> +		report.arguments[2] = color1->r;
>>>> +		report.arguments[3] = color1->g;
>>>> +		report.arguments[4] = color1->b;
>>>> +		report.arguments[5] = color2->r;
>>>> +		report.arguments[6] = color2->g;
>>>> +		report.arguments[7] = color2->b;
>>>> +		report.data_size    = 0x08;
>>>> +	} else {
>>>> +		pr_warn("breath_mode: invalid colors set\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	report.crc = razer_calculate_crc(&report);
>>>> +
>>>> +	retval = razer_send_check_response(razer_dev, &report);
>>>> +	if (retval != 0) {
>>>> +		razer_print_err_report(&report, KBUILD_MODNAME,
>>>> +				       "breath_mode: request failed");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +//#########################//
>>>> +//### Device Attributes ###//
>>>> +//#########################//
>>>> +
>>>> +/*
>>>> + * Read device file "get_serial"
>>>> + * Gets the serial number from the device.
>>>> + * Returns a string.
>>>> + */
>>>> +static ssize_t razer_attr_read_get_serial(struct device *dev,
>>>> +					  struct device_attribute *attr,
>>>> +					  char *buf)
>>>> +{
>>>> +	char serial_string[100] = "";
>>>> +
>>>> +	// Stealth and the Blade does not have serial via USB,
>>>> +	// so get it from the DMI table.
>>>> +	strcpy(serial_string, dmi_get_system_info(DMI_PRODUCT_SERIAL));
>>>> +	return sprintf(buf, "%s\n", &serial_string[0]);
>>>> +}
>>>> +
>>>> +/**
>>>> + * Read device file "get_firmware_version"
>>>> + * Gets the firmware version from the device.
>>>> + * Returns a string.
>>>> + */
>>>> +static ssize_t
>>>> +razer_attr_read_get_firmware_version(struct device *dev,
>>>> +				     struct device_attribute *attr,
>>>> +				     char *buf)
>>>> +{
>>>> +	struct razer_device *razer_dev  = dev_get_drvdata(dev);
>>>> +	char fw_string[100]             = "";
>>>> +	int retval;
>>>> +
>>>> +	retval = razer_get_firmware_version(razer_dev, &fw_string[0]);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return sprintf(buf, "%s\n", &fw_string[0]);
>>>> +}
>>>> +
>>>> +/*
>>>> + * Read device file "get_key_rows"
>>>> + * Returns the amount of key rows.
>>>> + */
>>>> +static ssize_t razer_attr_read_get_key_rows(struct device *dev,
>>>> +					    struct device_attribute *attr,
>>>> +					    char *buf)
>>>> +{
>>>> +	struct usb_interface *intf  = to_usb_interface(dev->parent);
>>>> +	struct usb_device *usb_dev  = interface_to_usbdev(intf);
>>>> +	int rows                    = razer_get_rows(usb_dev);
>>>> +
>>>> +	return sprintf(buf, "%d\n", rows);
>>>> +}
>>>> +
>>>> +/*
>>>> + * Read device file "get_key_columns"
>>>> + * Returns the amount of key columns.
>>>> + */
>>>> +static ssize_t razer_attr_read_get_key_columns(struct device *dev,
>>>> +					       struct device_attribute *attr,
>>>> +					       char *buf)
>>>> +{
>>>> +	struct usb_interface *intf  = to_usb_interface(dev->parent);
>>>> +	struct usb_device *usb_dev  = interface_to_usbdev(intf);
>>>> +	int columns                 = razer_get_columns(usb_dev);
>>>> +
>>>> +	return sprintf(buf, "%d\n", columns);
>>>> +}
>>>> +
>>>> +/**
>>>> + * Read device file "device_type"
>>>> + * Returns a friendly device type string.
>>>> + */
>>>> +static ssize_t razer_attr_read_device_type(struct device *dev,
>>>> +					   struct device_attribute *attr,
>>>> +					   char *buf)
>>>> +{
>>>> +	struct usb_interface *intf = to_usb_interface(dev->parent);
>>>> +	struct usb_device *usb_dev = interface_to_usbdev(intf);
>>>> +
>>>> +	char *device_type;
>>>> +
>>>> +	switch (usb_dev->descriptor.idProduct) {
>>>> +	case USB_DEVICE_ID_RAZER_BLADE_STEALTH_2016:
>>>> +		device_type = "Razer Blade Stealth 2016\n";
>>>> +		break;
>>>> +
>>>> +	case USB_DEVICE_ID_RAZER_BLADE_14_2016:
>>>> +		device_type = "Razer Blade 14 2016\n";
>>>> +		break;
>>>> +
>>>> +	case USB_DEVICE_ID_RAZER_BLACKWIDOW_CHROMA:
>>>> +		device_type = "Razer BlackWidow Chroma\n";
>>>> +		break;
>>>> +
>>>> +	default:
>>>> +		device_type = "Unknown Device\n";
>>>> +	}
>>>> +
>>>> +	return sprintf(buf, device_type);
>>>> +}
>>>> +
>>>> +/*
>>>> + * Read device file "brightness"
>>>> + * Returns the brightness value from 0-255.
>>>> + */
>>>> +static ssize_t razer_attr_read_brightness(struct device *dev,
>>>> +					  struct device_attribute *attr,
>>>> +					  char *buf)
>>>> +{
>>>> +	struct razer_device *razer_dev  = dev_get_drvdata(dev);
>>>> +	int brightness                  = razer_get_brightness(razer_dev);
>>>> +
>>>> +	return sprintf(buf, "%d\n", brightness);
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write device file "brightness"
>>>> + * Sets the brightness to the ASCII number written to this file.
>>>> + * Values from 0-255.
>>>> + */
>>>> +static ssize_t razer_attr_write_brightness(struct device *dev,
>>>> +					   struct device_attribute *attr,
>>>> +					   const char *buf, size_t count)
>>>> +{
>>>> +	struct razer_device *razer_dev  = dev_get_drvdata(dev);
>>>> +	unsigned long temp;
>>>> +	int retval;
>>>> +
>>>> +	retval = kstrtoul(buf, 10, &temp);
>>>> +	if (retval != 0) {
>>>> +		pr_warn("set brightness requires an ASCII number\n");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	retval = razer_set_brightness(razer_dev, (unsigned char)temp);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return count;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write device file "set_logo"
>>>> + * Sets the logo lighting state to the ASCII number written to this file.
>>>> + * 0 = off
>>>> + * 1 = on
>>>> + */
>>>> +static ssize_t razer_attr_write_set_logo(struct device *dev,
>>>> +					 struct device_attribute *attr,
>>>> +					 const char *buf, size_t count)
>>>> +{
>>>> +	struct razer_device *razer_dev  = dev_get_drvdata(dev);
>>>> +	unsigned long temp;
>>>> +	int retval;
>>>> +
>>>> +	retval = kstrtoul(buf, 10, &temp);
>>>> +	if (retval != 0) {
>>>> +		pr_warn("set_logo: requires an ASCII number\n");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	retval = razer_set_logo(razer_dev, (unsigned char)temp);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return count;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write device file "fn_mode"
>>>> + * Sets the FN mode to the ASCII number written to this file.
>>>> + * If 0 the F-keys work as normal F-keys
>>>> + * If 1 the F-keys act as if the FN key is held
>>>> + */
>>>> +static ssize_t razer_attr_write_fn_mode(struct device *dev,
>>>> +					struct device_attribute *attr,
>>>> +					const char *buf, size_t count)
>>>> +{
>>>> +	struct razer_device *razer_dev = dev_get_drvdata(dev);
>>>> +	unsigned long temp;
>>>> +	int retval;
>>>> +
>>>> +	retval = kstrtoul(buf, 10, &temp);
>>>> +	if (retval != 0) {
>>>> +		pr_warn("set fn_mode requires an ASCII number\n");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	retval = razer_set_fn_mode(razer_dev, (unsigned char)temp);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return count;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Read device file "fn_mode"
>>>> + * Returns the current fn mode state.
>>>> + */
>>>> +static ssize_t razer_attr_read_fn_mode(struct device *dev,
>>>> +				       struct device_attribute *attr, char *buf)
>>>> +{
>>>> +	struct razer_device *razer_dev  = dev_get_drvdata(dev);
>>>> +	struct razer_data *data         = razer_dev->data;
>>>> +
>>>> +	return sprintf(buf, "%d\n", data->fn_mode_state);
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write device file "set_key_colors"
>>>> + * Set the colors of all keys of the keyboard. 3 bytes per color.
>>>> + */
>>>> +static ssize_t razer_attr_write_set_key_colors(struct device *dev,
>>>> +					       struct device_attribute *attr,
>>>> +					       const char *buf, size_t count)
>>>> +{
>>>> +	struct razer_device *razer_dev = dev_get_drvdata(dev);
>>>> +	int retval;
>>>> +
>>>> +	retval = razer_set_key_colors(razer_dev,
>>>> +				      (unsigned char *)&buf[0], count);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	retval = razer_set_custom_mode(razer_dev);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return count;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write device file "mode_none"
>>>> + * Disable keyboard effects / turns the keyboard LEDs off.
>>>> + */
>>>> +static ssize_t razer_attr_write_mode_none(struct device *dev,
>>>> +					  struct device_attribute *attr,
>>>> +					  const char *buf, size_t count)
>>>> +{
>>>> +	struct razer_device *razer_dev = dev_get_drvdata(dev);
>>>> +	int retval;
>>>> +
>>>> +	retval = razer_set_none_mode(razer_dev);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return count;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write device file "mode_static"
>>>> + * Set the keyboard to static mode when 3 RGB bytes are written.
>>>> + */
>>>> +static ssize_t razer_attr_write_mode_static(struct device *dev,
>>>> +					    struct device_attribute *attr,
>>>> +					    const char *buf, size_t count)
>>>> +{
>>>> +	struct razer_device *razer_dev = dev_get_drvdata(dev);
>>>> +	int retval;
>>>> +
>>>> +	if (count != 3) {
>>>> +		pr_warn("mode static requires 3 RGB bytes\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	retval = razer_set_static_mode(razer_dev, (struct razer_rgb *)&buf[0]);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return count;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write device file "mode_custom"
>>>> + * Sets the keyboard to custom mode whenever the file is written to.
>>>> + */
>>>> +static ssize_t razer_attr_write_mode_custom(struct device *dev,
>>>> +					    struct device_attribute *attr,
>>>> +					    const char *buf, size_t count)
>>>> +{
>>>> +	struct razer_device *razer_dev = dev_get_drvdata(dev);
>>>> +	int retval;
>>>> +
>>>> +	retval = razer_set_custom_mode(razer_dev);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return count;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write device file "mode_wave"
>>>> + * If the ASCII number 1 is written then the wave effect is displayed
>>>> + * moving left across the keyboard.
>>>> + * If the ASCII number 2 is written then the wave effect goes right.
>>>> + */
>>>> +static ssize_t razer_attr_write_mode_wave(struct device *dev,
>>>> +					  struct device_attribute *attr,
>>>> +					  const char *buf, size_t count)
>>>> +{
>>>> +	struct razer_device *razer_dev  = dev_get_drvdata(dev);
>>>> +	unsigned long temp;
>>>> +	int retval;
>>>> +
>>>> +	retval = kstrtoul(buf, 10, &temp);
>>>> +	if (retval != 0) {
>>>> +		pr_warn("mode_wave: requires an ASCII number\n");
>>>> +		return retval;
>>>> +	}
>>>> +
>>>> +	retval = razer_set_wave_mode(razer_dev, temp);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return count;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write device file "mode_spectrum"
>>>> + * Specrum effect mode is activated whenever the file is written to.
>>>> + */
>>>> +static ssize_t razer_attr_write_mode_spectrum(struct device *dev,
>>>> +					      struct device_attribute *attr,
>>>> +					      const char *buf, size_t count)
>>>> +{
>>>> +	struct razer_device *razer_dev = dev_get_drvdata(dev);
>>>> +	int retval;
>>>> +
>>>> +	retval = razer_set_spectrum_mode(razer_dev);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return count;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write device file "mode_reactive"
>>>> + * Sets reactive mode when this file is written to.
>>>> + * A speed byte and 3 RGB bytes should be written.
>>>> + * The speed must be within 1-3
>>>> + * 1 Short, 2 Medium, 3 Long
>>>> + */
>>>> +static ssize_t razer_attr_write_mode_reactive(struct device *dev,
>>>> +					      struct device_attribute *attr,
>>>> +					      const char *buf, size_t count)
>>>> +{
>>>> +	struct razer_device *razer_dev = dev_get_drvdata(dev);
>>>> +	int retval;
>>>> +
>>>> +	if (count != 4) {
>>>> +		pr_warn("mode reactive requires one speed byte followed by 3 RGB bytes\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	retval = razer_set_reactive_mode(razer_dev,
>>>> +					 (unsigned char)buf[0],
>>>> +					 (struct razer_rgb *)&buf[1]);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return count;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write device file "mode_starlight"
>>>> + * Requires at least one byte representing the effect speed.
>>>> + * The speed must be within 1-3
>>>> + * 1 Short, 2 Medium, 3 Long
>>>> + *
>>>> + * Starlight mode has 3 modes of operation:
>>>> + *   Mode 1: single color. 3 RGB bytes.
>>>> + *   Mode 2: two colors. 6 RGB bytes.
>>>> + *   Mode 3: random colors. Anything else passed.
>>>> + */
>>>> +static ssize_t razer_attr_write_mode_starlight(struct device *dev,
>>>> +					       struct device_attribute *attr,
>>>> +					       const char *buf, size_t count)
>>>> +{
>>>> +	struct razer_device *razer_dev  = dev_get_drvdata(dev);
>>>> +	struct razer_rgb *color1        = NULL;
>>>> +	struct razer_rgb *color2        = NULL;
>>>> +	int retval;
>>>> +
>>>> +	if (count < 1) {
>>>> +		pr_warn("mode starlight requires one speed byte\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	if (count == 4) {
>>>> +		// Single color mode
>>>> +		color1 = (struct razer_rgb *)&buf[1];
>>>> +	} else if (count == 7) {
>>>> +		// Dual color mode
>>>> +		color1 = (struct razer_rgb *)&buf[1];
>>>> +		color2 = (struct razer_rgb *)&buf[4];
>>>> +	}
>>>> +
>>>> +	retval = razer_set_starlight_mode(razer_dev, (unsigned char)buf[0],
>>>> +					  color1, color2);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return count;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Write device file "mode_breath"
>>>> + * Breathing mode has 3 modes of operation.
>>>> + * Mode 1 fading in and out using a single color. 3 RGB bytes.
>>>> + * Mode 2 is fading in and out between two colors. 6 RGB bytes.
>>>> + * Mode 3 is fading in and out between random colors. Anything else passed.
>>>> + */
>>>> +static ssize_t razer_attr_write_mode_breath(struct device *dev,
>>>> +					    struct device_attribute *attr,
>>>> +					    const char *buf, size_t count)
>>>> +{
>>>> +	struct razer_device *razer_dev  = dev_get_drvdata(dev);
>>>> +	struct razer_rgb *color1        = NULL;
>>>> +	struct razer_rgb *color2        = NULL;
>>>> +	int retval;
>>>> +
>>>> +	if (count == 3) {
>>>> +		// Single color mode
>>>> +		color1 = (struct razer_rgb *)&buf[0];
>>>> +	} else if (count == 6) {
>>>> +		// Dual color mode
>>>> +		color1 = (struct razer_rgb *)&buf[0];
>>>> +		color2 = (struct razer_rgb *)&buf[3];
>>>> +	}
>>>> +
>>>> +	retval = razer_set_breath_mode(razer_dev, color1, color2);
>>>> +	if (retval != 0)
>>>> +		return retval;
>>>> +
>>>> +	return count;
>>>> +}
>>>> +
>>>> +//######################################//
>>>> +//### Set up the device driver files ###//
>>>> +//######################################//
>>>> +
>>>> +/*
>>>> + * Read only is 0444
>>>> + * Write only is 0220
>>>> + * Read and write is 0664
>>>> + */
>>>> +
>>>> +static DEVICE_ATTR(get_serial,              0444, razer_attr_read_get_serial,           NULL);
>>>> +static DEVICE_ATTR(get_firmware_version,    0444, razer_attr_read_get_firmware_version, NULL);
>>>> +static DEVICE_ATTR(device_type,             0444, razer_attr_read_device_type,          NULL);
>>>> +static DEVICE_ATTR(brightness,              0664, razer_attr_read_brightness, razer_attr_write_brightness);
>>>> +
>>>> +static DEVICE_ATTR(fn_mode,         0664, razer_attr_read_fn_mode, razer_attr_write_fn_mode);
>>>> +static DEVICE_ATTR(set_logo,        0220, NULL, razer_attr_write_set_logo);
>>>> +static DEVICE_ATTR(set_key_colors,  0220, NULL, razer_attr_write_set_key_colors);
>>>> +static DEVICE_ATTR(get_key_rows,    0444, razer_attr_read_get_key_rows, NULL);
>>>> +static DEVICE_ATTR(get_key_columns, 0444, razer_attr_read_get_key_columns, NULL);
>>>> +
>>>> +static DEVICE_ATTR(mode_none,      0220, NULL, razer_attr_write_mode_none);
>>>> +static DEVICE_ATTR(mode_static,    0220, NULL, razer_attr_write_mode_static);
>>>> +static DEVICE_ATTR(mode_custom,    0220, NULL, razer_attr_write_mode_custom);
>>>> +static DEVICE_ATTR(mode_wave,      0220, NULL, razer_attr_write_mode_wave);
>>>> +static DEVICE_ATTR(mode_spectrum,  0220, NULL, razer_attr_write_mode_spectrum);
>>>> +static DEVICE_ATTR(mode_reactive,  0220, NULL, razer_attr_write_mode_reactive);
>>>> +static DEVICE_ATTR(mode_breath,    0220, NULL, razer_attr_write_mode_breath);
>>>> +static DEVICE_ATTR(mode_starlight, 0220, NULL, razer_attr_write_mode_starlight);
>>>> +
>>>> +//#############################//
>>>> +//### Driver Main Functions ###//
>>>> +//#############################//
>>>> +
>>>> +/*
>>>> + * Initialize a razer_data struct.
>>>> + */
>>>> +int razer_init_data(struct razer_data *data)
>>>> +{
>>>> +	// Set all values to an unset state.
>>>> +	data->macro_keys_state  = -1;
>>>> +	data->fn_mode_state     = -1;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Sets the device to the specific states if set.
>>>> + */
>>>> +int razer_load_states(struct razer_device *razer_dev)
>>>> +{
>>>> +	int retval;
>>>> +	struct razer_data *data = razer_dev->data;
>>>> +
>>>> +	if (!data)
>>>> +		return 0;
>>>> +
>>>> +	// Enable the macro keys if required.
>>>> +	if (data->macro_keys_state == 1) {
>>>> +		retval = razer_activate_macro_keys(razer_dev);
>>>> +		if (retval != 0)
>>>> +			return retval;
>>>> +	}
>>>> +
>>>> +	// Set the FN mode if required.
>>>> +	if (data->fn_mode_state >= 0) {
>>>> +		retval = razer_set_fn_mode(razer_dev,
>>>> +					   (unsigned char)data->fn_mode_state);
>>>> +		if (retval != 0)
>>>> +			return retval;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Probe method is ran whenever a device is bound to the driver.
>>>> + */
>>>> +static int razer_probe(struct hid_device *hdev,
>>>> +		       const struct hid_device_id *id)
>>>> +{
>>>> +	int retval;
>>>> +	struct device *dev              = &hdev->dev;
>>>> +	struct usb_interface *intf      = to_usb_interface(dev->parent);
>>>> +	struct usb_device *usb_dev      = interface_to_usbdev(intf);
>>>> +	struct razer_device *razer_dev;
>>>> +	struct razer_data *data;
>>>> +	const unsigned int product_id   = usb_dev->descriptor.idProduct;
>>>> +
>>>> +	razer_dev = kzalloc(sizeof(*razer_dev), GFP_KERNEL);
>>>> +	if (!razer_dev) {
>>>> +		hid_err(hdev, "can't alloc razer device descriptor\n");
>>>> +		return -ENOMEM;
>>>> +	}
>>>> +
>>>> +	retval = razer_init_device(razer_dev, usb_dev);
>>>> +	if (retval != 0) {
>>>> +		hid_err(hdev, "failed to initialize razer device descriptor\n");
>>>> +		goto exit_free_razer_dev;
>>>> +	}
>>>> +
>>>> +	data = kzalloc(sizeof(*data), GFP_KERNEL);
>>>> +	if (!data) {
>>>> +		hid_err(hdev, "can't alloc razer data\n");
>>>> +		retval = -ENOMEM;
>>>> +		goto exit_free_razer_dev;
>>>> +	}
>>>> +
>>>> +	retval = razer_init_data(data);
>>>> +	if (retval != 0) {
>>>> +		hid_err(hdev, "failed to initialize razer data\n");
>>>> +		goto exit_free;
>>>> +	}
>>>> +
>>>> +	dev_set_drvdata(dev, razer_dev);
>>>> +	razer_dev->data         = data;
>>>> +	razer_dev->report_index = RAZER_DEFAULT_REPORT_INDEX;
>>>> +
>>>> +	// Default files
>>>> +	retval = device_create_file(dev, &dev_attr_get_serial);
>>>> +	if (retval)
>>>> +		goto exit_free;
>>>> +	retval = device_create_file(dev, &dev_attr_get_firmware_version);
>>>> +	if (retval)
>>>> +		goto exit_free;
>>>> +	retval = device_create_file(dev, &dev_attr_device_type);
>>>> +	if (retval)
>>>> +		goto exit_free;
>>>> +	retval = device_create_file(dev, &dev_attr_brightness);
>>>> +	if (retval)
>>>> +		goto exit_free;
>>>> +
>>>> +	// Custom files depending on the device support.
>>>> +	// #############################################
>>>> +	if (product_id == USB_DEVICE_ID_RAZER_BLADE_STEALTH_2016 ||
>>>> +	    product_id == USB_DEVICE_ID_RAZER_BLADE_14_2016) {
>>>> +		// Set the default fn mode state.
>>>> +		data->fn_mode_state = 1;
>>>> +
>>>> +		retval = device_create_file(dev, &dev_attr_fn_mode);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_get_key_rows);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_get_key_columns);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_set_logo);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_set_key_colors);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +
>>>> +		// Modes
>>>> +		retval = device_create_file(dev, &dev_attr_mode_none);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_static);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_custom);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_wave);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_spectrum);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_reactive);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_breath);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_starlight);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +	} else if (product_id == USB_DEVICE_ID_RAZER_BLACKWIDOW_CHROMA) {
>>>> +		// Enable the macro keys state.
>>>> +		data->macro_keys_state = 1;
>>>> +
>>>> +		retval = device_create_file(dev, &dev_attr_get_key_rows);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_get_key_columns);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_set_key_colors);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +
>>>> +		// Modes
>>>> +		retval = device_create_file(dev, &dev_attr_mode_none);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_static);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_custom);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_wave);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_spectrum);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_reactive);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +		retval = device_create_file(dev, &dev_attr_mode_breath);
>>>> +		if (retval)
>>>> +			goto exit_free;
>>>> +	}
>>>> +
>>>> +	retval = hid_parse(hdev);
>>>> +	if (retval)    {
>>>> +		hid_err(hdev, "parse failed\n");
>>>> +		goto exit_free;
>>>> +	}
>>>> +	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
>>>> +	if (retval) {
>>>> +		hid_err(hdev, "hw start failed\n");
>>>> +		goto exit_free;
>>>> +	}
>>>> +
>>>> +	usb_disable_autosuspend(usb_dev);
>>>> +
>>>> +	// Finally load any set states. Ignore errors. They are not fatal.
>>>> +	// Errors will be logged.
>>>> +	razer_load_states(razer_dev);
>>>> +
>>>> +	return 0;
>>>> +exit_free:
>>>> +	kfree(data);
>>>> +exit_free_razer_dev:
>>>> +	kfree(razer_dev);
>>>> +	return retval;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Driver unbind function.
>>>> + */
>>>> +static void razer_disconnect(struct hid_device *hdev)
>>>> +{
>>>> +	struct device *dev              = &hdev->dev;
>>>> +	struct usb_interface *intf      = to_usb_interface(dev->parent);
>>>> +	struct usb_device *usb_dev      = interface_to_usbdev(intf);
>>>> +	struct razer_device *razer_dev  = dev_get_drvdata(dev);
>>>> +	struct razer_data *data         = razer_dev->data;
>>>> +	const unsigned int product_id   = usb_dev->descriptor.idProduct;
>>>> +
>>>> +	// Remove the default files
>>>> +	device_remove_file(dev, &dev_attr_get_firmware_version);
>>>> +	device_remove_file(dev, &dev_attr_get_serial);
>>>> +	device_remove_file(dev, &dev_attr_device_type);
>>>> +	device_remove_file(dev, &dev_attr_brightness);
>>>> +
>>>> +	// Custom files depending on the device support.
>>>> +	// #############################################
>>>> +	if (product_id == USB_DEVICE_ID_RAZER_BLADE_STEALTH_2016 ||
>>>> +	    product_id == USB_DEVICE_ID_RAZER_BLADE_14_2016) {
>>>> +		device_remove_file(dev, &dev_attr_fn_mode);
>>>> +		device_remove_file(dev, &dev_attr_get_key_rows);
>>>> +		device_remove_file(dev, &dev_attr_get_key_columns);
>>>> +		device_remove_file(dev, &dev_attr_set_logo);
>>>> +		device_remove_file(dev, &dev_attr_set_key_colors);
>>>> +
>>>> +		// Modes
>>>> +		device_remove_file(dev, &dev_attr_mode_none);
>>>> +		device_remove_file(dev, &dev_attr_mode_static);
>>>> +		device_remove_file(dev, &dev_attr_mode_custom);
>>>> +		device_remove_file(dev, &dev_attr_mode_wave);
>>>> +		device_remove_file(dev, &dev_attr_mode_spectrum);
>>>> +		device_remove_file(dev, &dev_attr_mode_reactive);
>>>> +		device_remove_file(dev, &dev_attr_mode_breath);
>>>> +		device_remove_file(dev, &dev_attr_mode_starlight);
>>>> +	} else if (product_id == USB_DEVICE_ID_RAZER_BLACKWIDOW_CHROMA) {
>>>> +		device_remove_file(dev, &dev_attr_get_key_rows);
>>>> +		device_remove_file(dev, &dev_attr_get_key_columns);
>>>> +		device_remove_file(dev, &dev_attr_set_key_colors);
>>>> +
>>>> +		// Modes
>>>> +		device_remove_file(dev, &dev_attr_mode_none);
>>>> +		device_remove_file(dev, &dev_attr_mode_static);
>>>> +		device_remove_file(dev, &dev_attr_mode_custom);
>>>> +		device_remove_file(dev, &dev_attr_mode_wave);
>>>> +		device_remove_file(dev, &dev_attr_mode_spectrum);
>>>> +		device_remove_file(dev, &dev_attr_mode_reactive);
>>>> +		device_remove_file(dev, &dev_attr_mode_breath);
>>>> +	}
>>>> +
>>>> +	hid_hw_stop(hdev);
>>>> +	kfree(razer_dev);
>>>> +	kfree(data);
>>>> +	dev_info(dev, "razer device disconnected\n");
>>>> +}
>>>> +
>>>> +//################################//
>>>> +//### Power Management Support ###//
>>>> +//################################//
>>>> +
>>>> +#ifdef CONFIG_PM
>>>> +
>>>> +/*
>>>> + * Called when the device is going to be suspended.
>>>> + */
>>>> +static int razer_suspend(struct hid_device *hdev, pm_message_t message)
>>>> +{
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Called when the device is being resumed by the system.
>>>> + */
>>>> +static int razer_resume(struct hid_device *hdev)
>>>> +{
>>>> +	struct device *dev              = &hdev->dev;
>>>> +	struct razer_device *razer_dev  = dev_get_drvdata(dev);
>>>> +
>>>> +	// Load any set states. Ignore errors. They are not fatal.
>>>> +	// Errors will be logged.
>>>> +	razer_load_states(razer_dev);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Called when the suspended device has been reset instead of being resumed.
>>>> + */
>>>> +static int razer_reset_resume(struct hid_device *hdev)
>>>> +{
>>>> +	return razer_resume(hdev);
>>>> +}
>>>> +
>>>> +#endif
>>>> +
>>>> +//###############################//
>>>> +//### Device ID mapping table ###//
>>>> +//###############################//
>>>> +
>>>> +static const struct hid_device_id razer_devices[] = {
>>>> +	{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_STEALTH_2016) },
>>>> +	{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14_2016) },
>>>> +	{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLACKWIDOW_CHROMA) },
>>>> +	{ }
>>>> +};
>>>> +
>>>> +MODULE_DEVICE_TABLE(hid, razer_devices);
>>>> +
>>>> +//############################################//
>>>> +//### Describes the contents of the driver ###//
>>>> +//############################################//
>>>> +
>>>> +static struct hid_driver razer_driver = {
>>>> +	.name           = "hid-razer",
>>>> +	.id_table       = razer_devices,
>>>> +
>>>> +#ifdef CONFIG_PM
>>>> +	.suspend        = razer_suspend,
>>>> +	.resume         = razer_resume,
>>>> +	.reset_resume   = razer_reset_resume,
>>>> +#endif
>>>> +
>>>> +	.probe          = razer_probe,
>>>> +	.remove         = razer_disconnect
>>>> +};
>>>> +
>>>> +module_hid_driver(razer_driver);
>>>> diff --git a/drivers/hid/hid-razer.h b/drivers/hid/hid-razer.h
>>>> new file mode 100644
>>>> index 0000000..1a1c92e
>>>> --- /dev/null
>>>> +++ b/drivers/hid/hid-razer.h
>>>> @@ -0,0 +1,48 @@
>>>> +/*
>>>> + * Razer Kernel Drivers
>>>> + * Copyright (c) 2016 Roland Singer <roland.singer@xxxxxxxxxxxxx>
>>>> + * Based on Tim Theede <pez2001@xxxxxxxxxxxxxxxxx> razer_chroma_drivers project.
>>>> + *
>>>> + * 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.
>>>> + */
>>>> +
>>>> +#ifndef __HID_RAZER_H
>>>> +#define __HID_RAZER_H
>>>> +
>>>> +#include <linux/types.h>
>>>> +
>>>> +//#################//
>>>> +//### Constants ###//
>>>> +//#################//
>>>> +
>>>> +// Report indexes.
>>>> +#define RAZER_DEFAULT_REPORT_INDEX  0x02
>>>> +
>>>> +// Keyboard rows and columns:
>>>> +#define RAZER_STEALTH_2016_ROWS     0x06
>>>> +#define RAZER_STEALTH_2016_COLUMNS  0x10
>>>> +
>>>> +#define RAZER_BLADE_14_2016_ROWS    0x06
>>>> +#define RAZER_BLADE_14_2016_COLUMNS 0x10
>>>> +
>>>> +#define RAZER_BLACKWIDOW_CHROMA_ROWS    0x06
>>>> +#define RAZER_BLACKWIDOW_CHROMA_COLUMNS 0x16
>>>> +
>>>> +//#############//
>>>> +//### Types ###//
>>>> +//#############//
>>>> +
>>>> +struct razer_data {
>>>> +	char macro_keys_state;
>>>> +	char fn_mode_state;
>>>> +};
>>>> +
>>>> +#endif // __HID_RAZER_H
>>>
>>> This is not a full review, but I hope you will find it useful anyway.
>>>
>>> Cheers,
>>> Benjamin
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-input" in
>>> the body of a message to majordomo@xxxxxxxxxxxxxxx
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[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