diff --git a/drivers/razer/Makefile b/drivers/razer/Makefile new file mode 100644 index 0000000..a8c060c --- /dev/null +++ b/drivers/razer/Makefile @@ -0,0 +1,13 @@ +CC=gcc +CFLAGS=$(shell pkg-config --cflags libusb-1.0) -O3 -Wall -shared +LDFLAGS=$(shell pkg-config --libs libusb-1.0) + +FINAL_ASSETS=$(BUILD_DIR)/drivers/assets/razer-viper_mini.png +BUILD_DIR=../../build/ + +all: $(BUILD_DIR)/drivers/razer.so $(FINAL_ASSETS) + +$(BUILD_DIR)/drivers/razer.so: razer.c | $(BUILD_DIR)/drivers/ + $(CC) $(CFLAGS) -o $@ $^ +$(BUILD_DIR)/drivers/assets/%.png: %.png | $(BUILD_DIR)/drivers/assets + cp $^ $@ diff --git a/drivers/razer/razer-viper_mini.png b/drivers/razer/razer-viper_mini.png new file mode 100644 index 0000000..b346ce7 Binary files /dev/null and b/drivers/razer/razer-viper_mini.png differ diff --git a/drivers/razer/razer.c b/drivers/razer/razer.c new file mode 100644 index 0000000..9cd0bac --- /dev/null +++ b/drivers/razer/razer.c @@ -0,0 +1,106 @@ +#include "razer.h" + +static uint8_t compute_crc(void* report) +{ + uint8_t* r = report; + + uint8_t crc = 0; + for(size_t i = 2; i < REPORT_SIZE - sizeof(report_footer_t); i++) + { + crc ^= r[i]; + } + + return crc; +} + +static int send_command(libusb_device_handle* hand, uint8_t command_class, uint8_t command, uint8_t size, void* report) +{ + report_header_t* head = report; + head->report_size = size; + head->command_class = command_class; + head->command_id = command; + + report_footer_t* footer = report + REPORT_SIZE - sizeof(report_footer_t); + footer->crc = compute_crc(report); + + // Send command + int res = libusb_control_transfer(hand, + 0x21, // request type + 0x09, // request (SET_REPORT) + 0x300, // wValue (FEATURE << 8 | REPORT(0)) + 0x0, // wIndex = 0x0 : interface + report, + REPORT_SIZE, // wLength = 90 + 0 /* timeout*/); + + if(res <= 0) return res; + + // Wait for the mouse to catch the report + usleep(100); + + // Get response report + res = libusb_control_transfer(hand, + 0xa1, // request type + 0x01, // request (GET_REPORT) + 0x300, // wValue (FEATURE << 8 | REPORT(0)) + 0x0, // wIndex = 0x0 : interface + report, + REPORT_SIZE, // wLength = 90 + 0); + + return res; +} + +void driver_init(void) {} + +uint32_t driver_getkey(void) +{ + static int count = 0; + count++; + + // Register ALL razer compatible devices + uint16_t id = 0; + switch(count) + { + case 1: + id = VIPER_MINI_PRODUCT_ID; + break; + default: + id = 0; + } + + if(id == 0) return 0; + return (VENDOR_ID << 16) | id; +} + +char* driver_get_name(void* handle) +{ + libusb_device* dev = handle; + + struct libusb_device_descriptor desc; + libusb_get_device_descriptor(dev, &desc); + + switch(desc.idProduct) + { + case VIPER_MINI_PRODUCT_ID: + return VIPER_MINI_NAME; + default: + return "Unknown Razer mice"; + } +} + +char* driver_get_image(void* handle) +{ + libusb_device* dev = handle; + + struct libusb_device_descriptor desc; + libusb_get_device_descriptor(dev, &desc); + + switch(desc.idProduct) + { + case VIPER_MINI_PRODUCT_ID: + return VIPER_MINI_IMAGE; + default: + return ""; + } +} diff --git a/drivers/razer/razer.h b/drivers/razer/razer.h new file mode 100644 index 0000000..eca5b02 --- /dev/null +++ b/drivers/razer/razer.h @@ -0,0 +1,148 @@ +#ifndef RAZER_H +#define RAZER_H + +#include +#include +#include + +#define VENDOR_ID 0x1532 + +#define REPORT_SIZE 90 + +/* Devices */ +#include "viper_mini.h" + +typedef struct REPORT_HEADER +{ + uint8_t status; + uint8_t transaction_id; + + uint8_t remaining_packets_high; + uint8_t remaining_packets_low; + uint8_t protocol_type; // 0x0 + uint8_t report_size; + + uint8_t command_class; + uint8_t command_id; +} __attribute__((packed)) report_header_t; + +typedef struct REPORT_FOOTER +{ + uint8_t crc; // Control, xored bytes of report + uint8_t reserved; +} __attribute__((packed)) report_footer_t; + +/* Commands of the BASIC (0x0) class */ +#define COMMAND_CLASS_BASIC 0x0 + +struct REPORT_FIRMWARE_VERSION +{ + report_header_t header; + + uint8_t version_major; + uint8_t version_minor; + + uint8_t zeros[REPORT_SIZE - sizeof(report_header_t) - sizeof(report_footer_t) - 2]; + + report_footer_t footer; +} __attribute__((packed)); +#define REPORT_SIZE_FIRMWARE_VERSION 0x2 +#define COMMAND_REPORT_FIRMWARE_VERSION 0x81 +static_assert(sizeof(struct REPORT_FIRMWARE_VERSION) == REPORT_SIZE, "REPORT_FIRMWARE_VERSION is not of the right size"); + +struct REPORT_POLLING_RATE +{ + report_header_t header; + + // There is another report for up to 8000Hz polling + // 0x01: 1000Hz, 0x2: 500Hz, 0x3: 125Hz + uint8_t polling_rate; + + uint8_t zeros[REPORT_SIZE - sizeof(report_header_t) - sizeof(report_footer_t) - 1]; + + report_footer_t footer; +} __attribute__((packed)); +static_assert(sizeof(struct REPORT_POLLING_RATE) == REPORT_SIZE, "REPORT_POLLING_RATE is not of the right size"); +#define REPORT_SIZE_POLLING_RATE 0x1 +#define COMMAND_REPORT_POLLING_RATE 0x85 + +/* Commands of the DPI (0x4) class */ +#define COMMAND_CLASS_DPI 0x4 + +struct REPORT_DPI +{ + report_header_t header; + + uint8_t variable_storage; + uint8_t dpi_x_high; + uint8_t dpi_x_low; + uint8_t dpi_y_high; + uint8_t dpi_y_low; + uint8_t unknown_0; + uint8_t unknown_1; + + uint8_t zeros[REPORT_SIZE - sizeof(report_header_t) - sizeof(report_footer_t) - 7]; + + report_footer_t footer; +} __attribute__((packed)); +static_assert(sizeof(struct REPORT_DPI) == REPORT_SIZE, "REPORT_DPI is not of the right size"); +#define REPORT_SIZE_DPI 0x07 +#define COMMAND_REPORT_DPI 0x85 + +typedef struct DPI_STAGE +{ + uint8_t stage_index; + uint8_t dpi_x_high; + uint8_t dpi_x_low; + uint8_t dpi_y_high; + uint8_t dpi_y_low; + uint8_t unknown_0; + uint8_t unknown_1; +} __attribute__((packed)) dpi_stage_t; +struct REPORT_DPI_STAGES +{ + report_header_t header; + + uint8_t variable_storage; + uint8_t active_dpi_stage; + uint8_t dpi_stages_count; + + dpi_stage_t stages[5]; + + uint8_t zeros[REPORT_SIZE - sizeof(report_header_t) - sizeof(report_footer_t) - 38]; + + report_footer_t footer; +} __attribute__((packed)); +static_assert(sizeof(struct REPORT_DPI_STAGES) == REPORT_SIZE, "REPORT_DPI_STAGES is not of the right size"); +#define REPORT_SIZE_DPI_STAGES 0x26 +#define COMMAND_REPORT_DPI_STAGES 0x86 + +/* Commands of the BATTERY (0x7) class */ +#define COMMAND_CLASS_BATTERY 0x7 + +struct REPORT_BATTERY_LEVEL +{ + report_header_t header; + + uint8_t ignored; + uint8_t battery_level; // 0-255 + + report_footer_t footer; +} __attribute__((packed)); +#define REPORT_SIZE_BATTERY_LEVEL 0x2 +#define COMMAND_REPORT_BATTERY_LEVEL 0x80 + +struct REPORT_BATTERY_STATUS +{ + report_header_t header; + + uint8_t ignored; + uint8_t charging; // 0: not charging, 1:charging + + report_footer_t footer; +} __attribute__((packed)); +#define REPORT_SIZE_BATTERY_STATUS 0x2 +#define COMMAND_REPORT_BATTERY_STATUS 0x84 + + +#endif diff --git a/drivers/razer/viper_mini.h b/drivers/razer/viper_mini.h new file mode 100644 index 0000000..918053d --- /dev/null +++ b/drivers/razer/viper_mini.h @@ -0,0 +1,8 @@ +#ifndef VIPER_MINI_H +#define VIPER_MINI_H + +#define VIPER_MINI_PRODUCT_ID 0x008a +#define VIPER_MINI_NAME "Viper Mini" +#define VIPER_MINI_IMAGE "razer-viper_mini.png" + +#endif