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. I mentioned your thoughts here in our Github discussion: https://github.com/terrycain/razer-drivers/issues/29#issuecomment-243380127 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