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