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]

 



This patch is currently canceled. I'll first implement the improvements
suggested by Greg Kroah-Hartman:

https://github.com/openrazer/openrazer-drivers/issues/2


Am 26.08.2016 um 11:04 schrieb Roland Singer:
> 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
> ---
> 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
> +
> +
> +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
> +
> +
> +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
> +
> +
> +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
> +
> +
> +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
> +
> +
> +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
> +
> +
> +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
> +
> +
> +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
> +
> +
> +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
> 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
> 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>
> +#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);
> +
> +	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]);
> +}
> +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
> --
> 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