Initial commit

This commit is contained in:
Přemysl Eric Janouch 2013-06-09 17:19:00 +02:00
commit 97616b9600
5 changed files with 534 additions and 0 deletions

57
CMakeLists.txt Normal file
View File

@ -0,0 +1,57 @@
cmake_minimum_required (VERSION 2.8.5)
project (razer-bw-te-ctl C)
set (project_VERSION "1.0")
find_package (PkgConfig REQUIRED)
pkg_check_modules (dependencies REQUIRED libusb-1.0)
include_directories (${dependencies_INCLUDE_DIRS})
include (GNUInstallDirs)
configure_file (${PROJECT_SOURCE_DIR}/config.h.in
${PROJECT_BINARY_DIR}/config.h)
include_directories (${PROJECT_BINARY_DIR})
add_executable (${PROJECT_NAME} ${PROJECT_NAME}.c)
target_link_libraries (${PROJECT_NAME} ${dependencies_LIBRARIES})
install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
find_program (HELP2MAN_EXECUTABLE help2man)
if (NOT HELP2MAN_EXECUTABLE)
message (FATAL_ERROR "help2man not found")
endif ()
foreach (page ${PROJECT_NAME})
set (page_output "${PROJECT_BINARY_DIR}/${page}.1")
list (APPEND project_MAN_PAGES "${page_output}")
add_custom_command (OUTPUT ${page_output}
COMMAND ${HELP2MAN_EXECUTABLE} -N
"${PROJECT_BINARY_DIR}/${page}" -o ${page_output}
DEPENDS ${PROJECT_NAME}
COMMENT "Generating man page for ${page}" VERBATIM)
endforeach ()
add_custom_target (docs ALL DEPENDS ${project_MAN_PAGES})
foreach (page ${project_MAN_PAGES})
string (REGEX MATCH "\\.([0-9])" manpage_suffix "${page}")
install (FILES "${page}"
DESTINATION "${CMAKE_INSTALL_MANDIR}/man${CMAKE_MATCH_1}")
endforeach ()
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY
"Razer BlackWidow Tournament Edition control utility")
set (CPACK_PACKAGE_VERSION ${project_VERSION})
set (CPACK_PACKAGE_VENDOR "Premysl Janouch")
set (CPACK_PACKAGE_CONTACT "Přemysl Janouch <p.janouch@gmail.com>")
set (CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
set (CPACK_GENERATOR "TGZ;ZIP")
set (CPACK_PACKAGE_FILE_NAME
"${PROJECT_NAME}-${project_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
set (CPACK_SOURCE_GENERATOR "TGZ;ZIP")
set (CPACK_SOURCE_IGNORE_FILES "/\\\\.git;/build;/CMakeLists.txt.user")
set (CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${project_VERSION}")
set (CPACK_SET_DESTDIR TRUE)
include (CPack)

15
LICENSE Normal file
View File

@ -0,0 +1,15 @@
Copyright (c) 2013, Přemysl Janouch <p.janouch@gmail.com>
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

28
README Normal file
View File

@ -0,0 +1,28 @@
razer-bw-te-ctl
This program makes it possible to change the configuration of your Razer
BlackWidow Tournament Edition keybooard from within Linux, *BSD or any other
POSIX-compatible system supported by libusb.
Make sure to let the Windows Razer Synapse tool upgrade the firmware to the
newest version before running the program. There might be some issues otherwise
due to protocol changes, although I don't really deem it very probable.
Run `razer-bw-te-ctl --help' or `man razer-bw-te-ctl' for usage information.
Installation
============
Build dependencies: cmake >= 2.8.5, help2man, libusb >= 1.0
$ git clone git://github.com/pjanouch/razer-bw-te-ctl.git
$ cd razer-bw-te-ctl
$ mkdir build
$ cd build
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr
# make install
Note that there's no "make uninstall".
For Debian-based distros, you can do the following instead of the last step:
$ cpack -G DEB
# dpkg -i razer-bw-te-ctl-*.deb

7
config.h.in Normal file
View File

@ -0,0 +1,7 @@
#ifndef CONFIG_H
#define CONFIG_H
#define PROJECT_NAME "${CMAKE_PROJECT_NAME}"
#define PROJECT_VERSION "${project_VERSION}"
#endif // ! CONFIG_H

427
razer-bw-te-ctl.c Normal file
View File

@ -0,0 +1,427 @@
/*
* razer-bw-te-ctl.c: Razer BlackWidow Tournament Edition control utility
*
* Everything has been reverse-engineered via Wireshark/usbmon and VirtualBox.
*
* Copyright (c) 2013, Přemysl Janouch <p.janouch@gmail.com>
* All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <getopt.h>
#include <strings.h>
#include <libusb.h>
#include "config.h"
// --- Utilities ---------------------------------------------------------------
/** Search for a device with given vendor and product ID. */
static libusb_device_handle *
find_device (int vendor, int product, int *error)
{
libusb_device **list;
libusb_device *found = NULL;
libusb_device_handle *handle = NULL;
int err = 0;
ssize_t cnt = libusb_get_device_list (NULL, &list);
if (cnt < 0)
goto out;
ssize_t i = 0;
for (i = 0; i < cnt; i++)
{
libusb_device *device = list[i];
struct libusb_device_descriptor desc;
if (libusb_get_device_descriptor (device, &desc))
continue;
if (desc.idVendor == vendor && desc.idProduct == product)
{
found = device;
break;
}
}
if (found)
{
err = libusb_open (found, &handle);
if (err)
goto out_free;
}
out_free:
libusb_free_device_list(list, 1);
out:
if (error != NULL && err != 0)
*error = err;
return handle;
}
// --- Device configuration ----------------------------------------------------
#define USB_VENDOR_RAZER 0x1532
#define USB_PRODUCT_RAZER_BW_TE 0x011c
#define USB_GET_REPORT 0x01
#define USB_SET_REPORT 0x09
#define BW_CTL_IFACE 0
/** Razer logo backlight mode. */
enum bw_led_mode
{
LED_BRIGHTNESS = 0,
LED_BLINK,
LED_PULSATE
};
/** Overall device configuration. */
struct bw_config
{
enum bw_led_mode led_mode;
unsigned led_brightness;
unsigned macro_led_on : 1;
unsigned macro_led_blinking : 1;
unsigned gaming_mode : 1;
};
/** Send a command to the mouse via SET_REPORT. */
static int
bw_send_command (libusb_device_handle *device,
unsigned char *data, uint16_t length)
{
unsigned char packet[90] = { 0x00 };
assert (length <= sizeof packet - 5);
memcpy (packet + 5, data, length);
unsigned char checksum = 0;
while (length--)
checksum ^= data[length];
packet[sizeof packet - 2] = checksum;
// XXX wIndex should actually be 0x0002 but that doesn't work
int result = libusb_control_transfer (device, LIBUSB_ENDPOINT_OUT
| LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
USB_SET_REPORT, 0x0300, 0x0000, packet, sizeof packet, 0);
return result < 0 ? result : 0;
}
/** Set Razer logo backlight mode. */
static int
bw_set_led_mode (libusb_device_handle *device, enum bw_led_mode mode)
{
unsigned char cmd[] = { 0x03, 0x03, 0x02, 0x01, 0x04, mode };
return bw_send_command (device, cmd, sizeof cmd);
}
/** Set Razer logo backlight brightness. */
static int
bw_set_led_brightness (libusb_device_handle *device, unsigned char brightness)
{
unsigned char cmd[] = { 0x03, 0x03, 0x03, 0x01, 0x04, brightness };
return bw_send_command (device, cmd, sizeof cmd);
}
/** Set the on/off state of the macro LED. */
static int
bw_set_macro_led (libusb_device_handle *device, bool on)
{
unsigned char cmd[] = { 0x03, 0x03, 0x00, 0x00, 0x07, on };
return bw_send_command (device, cmd, sizeof cmd);
}
/** Set whether the macro LED should blink. */
static int
bw_set_macro_led_blinking (libusb_device_handle *device, bool blinking)
{
unsigned char cmd[] = { 0x03, 0x03, 0x02, 0x00, 0x07, blinking };
return bw_send_command (device, cmd, sizeof cmd);
}
/** Set the gaming mode (whether the Windows key should be ignored). */
static int
bw_set_gaming_mode (libusb_device_handle *device, bool on)
{
unsigned char cmd[] = { 0x03, 0x03, 0x00, 0x01, 0x08, on };
return bw_send_command (device, cmd, sizeof cmd);
}
// --- Control utility ---------------------------------------------------------
struct options
{
unsigned set_led_mode : 1;
unsigned set_led_brightness : 1;
unsigned set_macro_led_on : 1;
unsigned set_macro_led_blinking : 1;
unsigned set_gaming_mode : 1;
};
static void
show_usage (const char *program_name)
{
printf ("Usage: %s [OPTION]...\n", program_name);
printf ("Configure Razer BlackWidow Tournament Edition devices.\n\n");
printf (" -h, --help Show this help\n");
printf (" --version Show program version and exit\n");
printf (" --led-mode X Set the mode of the Razer logo LED"
" (can be 'normal', 'blink' or 'pulsate')\n");
printf (" --led-brightness X Set Razer logo LED brightness"
" (from 0 to 255)\n");
printf (" --macro-led X Set the macro LED mode"
" ('off', 'on' or 'blink')\n");
printf (" --gaming-mode BOOL Set whether the Windows key is ignored\n");
printf ("\n");
}
static void
parse_options (int argc, char *argv[],
struct options *options, struct bw_config *new_config)
{
static struct option long_opts[] =
{
{ "help", no_argument, 0, 'h' },
{ "version", no_argument, 0, 'V' },
{ "led-mode", required_argument, 0, 'l' },
{ "led-brightness", required_argument, 0, 'L' },
{ "macro-led", required_argument, 0, 'm' },
{ "gaming-mode", required_argument, 0, 'g' },
{ 0, 0, 0, 0 }
};
if (argc == 1)
{
show_usage (argv[0]);
exit (EXIT_FAILURE);
}
int c;
while ((c = getopt_long (argc, argv, "h", long_opts, NULL)) != -1)
{
switch (c)
{
case 'h':
show_usage (argv[0]);
exit (EXIT_SUCCESS);
case 'V':
printf (PROJECT_NAME " " PROJECT_VERSION "\n");
exit (EXIT_SUCCESS);
case 'l':
if (!strcasecmp (optarg, "normal"))
new_config->led_mode = LED_BRIGHTNESS;
else if (!strcasecmp (optarg, "blink"))
new_config->led_mode = LED_BLINK;
else if (!strcasecmp (optarg, "pulsate"))
new_config->led_mode = LED_PULSATE;
else
{
fprintf (stderr, "Error: invalid LED mode: %s\n", optarg);
exit (EXIT_FAILURE);
}
options->set_led_mode = true;
break;
case 'L':
{
char *end;
long bri = strtol (optarg, &end, 10);
if (!*optarg || *end || bri < 0 || bri > 255)
{
fprintf (stderr, "Error: invalid LED brightness value\n");
exit (EXIT_FAILURE);
}
options->set_led_brightness = true;
break;
}
case 'm':
if (!strcasecmp (optarg, "off"))
{
new_config->macro_led_on = false;
new_config->macro_led_blinking = false;
}
else if (!strcasecmp (optarg, "blink"))
{
new_config->macro_led_on = true;
new_config->macro_led_blinking = true;
}
else if (!strcasecmp (optarg, "on"))
{
new_config->macro_led_on = true;
new_config->macro_led_blinking = false;
}
else
{
fprintf (stderr, "Error: invalid macro LED mode: %s\n", optarg);
exit (EXIT_FAILURE);
}
options->set_macro_led_blinking = true;
options->set_macro_led_on = true;
break;
case 'g':
if (!strcasecmp (optarg, "true")
|| !strcasecmp (optarg, "on")
|| !strcasecmp (optarg, "yes"))
new_config->gaming_mode = true;
else if (!strcasecmp (optarg, "false")
|| !strcasecmp (optarg, "off")
|| !strcasecmp (optarg, "no"))
new_config->gaming_mode = false;
else
{
fprintf (stderr, "Error: invalid gaming mode"
" setting: %s\n", optarg);
exit (EXIT_FAILURE);
}
options->set_gaming_mode = true;
break;
case '?':
exit (EXIT_FAILURE);
}
}
if (optind < argc)
{
fprintf (stderr, "Error: extra parameters\n");
exit (EXIT_FAILURE);
}
}
static int
apply_options (libusb_device_handle *device,
struct options *options, struct bw_config *new_config)
{
int result;
if (options->set_led_mode)
if ((result = bw_set_led_mode
(device, new_config->led_mode)))
return result;
if (options->set_led_brightness)
if ((result = bw_set_led_brightness
(device, new_config->led_brightness)))
return result;
if (options->set_macro_led_on)
if ((result = bw_set_macro_led
(device, new_config->macro_led_on)))
return result;
if (options->set_macro_led_blinking)
if ((result = bw_set_macro_led_blinking
(device, new_config->macro_led_blinking)))
return result;
if (options->set_gaming_mode)
if ((result = bw_set_gaming_mode
(device, new_config->gaming_mode)))
return result;
return 0;
}
#define ERROR(label, ...) \
do { \
fprintf (stderr, "Error: " __VA_ARGS__); \
status = 1; \
goto label; \
} while (0)
int
main (int argc, char *argv[])
{
struct options options = { 0 };
struct bw_config new_config = { 0 };
parse_options (argc, argv, &options, &new_config);
int result, status = 0;
result = libusb_init (NULL);
if (result)
ERROR (error_0, "libusb initialisation failed: %s\n",
libusb_error_name (result));
result = 0;
libusb_device_handle *device = find_device
(USB_VENDOR_RAZER, USB_PRODUCT_RAZER_BW_TE, &result);
if (!device)
{
if (result)
ERROR (error_1, "couldn't open device: %s\n",
libusb_error_name (result));
else
ERROR (error_1, "no suitable device found\n");
}
bool reattach_driver = false;
result = libusb_kernel_driver_active (device, BW_CTL_IFACE);
switch (result)
{
case 0:
case LIBUSB_ERROR_NOT_SUPPORTED:
break;
case 1:
reattach_driver = true;
result = libusb_detach_kernel_driver (device, BW_CTL_IFACE);
if (result)
ERROR (error_2, "couldn't detach kernel driver: %s\n",
libusb_error_name (result));
break;
default:
ERROR (error_2, "coudn't detect kernel driver presence: %s\n",
libusb_error_name (result));
}
result = libusb_claim_interface (device, BW_CTL_IFACE);
if (result)
ERROR (error_3, "couldn't claim interface: %s\n",
libusb_error_name (result));
result = apply_options (device, &options, &new_config);
if (result)
ERROR (error_4, "operation failed: %s\n",
libusb_error_name (result));
error_4:
result = libusb_release_interface (device, BW_CTL_IFACE);
if (result)
ERROR (error_3, "couldn't release interface: %s\n",
libusb_error_name (result));
error_3:
if (reattach_driver)
{
result = libusb_attach_kernel_driver (device, BW_CTL_IFACE);
if (result)
ERROR (error_2, "couldn't reattach kernel driver: %s\n",
libusb_error_name (result));
}
error_2:
libusb_close (device);
error_1:
libusb_exit (NULL);
error_0:
return status;
}