This repository has been archived by the owner on Aug 16, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrazer.c
197 lines (172 loc) · 6.42 KB
/
razer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
* rzctl(1) -- Razer mouse control utility
* Copyright (c) 2019 Angel <angel@ttm.sh>
*
* 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, version 2.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define _DEFAULT_SOURCE /* required for usleep */
#include <stdlib.h>
#include <string.h>
#include <libusb-1.0/libusb.h>
#include <assert.h>
#include <unistd.h>
#include "razer.h"
static const uint16_t k_supported_vid = 0x1532;
static const uint16_t k_supported_pids[] = { /* please keep this array sorted */
0x0043 /* Razer Deathadder Chroma */
};
/* comparison function for bsearch() */
static int __attribute__((noinline))
search_cmpfn(const void *a, const void *b)
{
/* comparison succeeds if a == b */
return *(uint16_t *) a - *(uint16_t *) b;
}
static uint8_t
report_crc(struct razer_report *report)
{
uint8_t i;
uint8_t crc = 0;
for (i = 2; i < sizeof(struct razer_report) - 2; i++)
crc ^= ((uint8_t *) report)[i];
return crc;
}
int razer_init(struct razer_device *dev)
{
int iface;
if (libusb_open(dev->dev, &(dev->handle)) < 0)
return -1;
if (libusb_get_config_descriptor(dev->dev, 0, &(dev->cfg_desc)) < 0)
return -1;
/* detach kernel drivers associated with interfaces that are to be claimed
* by rzctl */
libusb_set_auto_detach_kernel_driver(dev->handle, 1);
/* claim all available interfaces */
for (iface = 0; iface < dev->cfg_desc->bNumInterfaces; iface++)
libusb_claim_interface(dev->handle, iface);
/* obtain manufacturer and product name strings */
libusb_get_string_descriptor_ascii(dev->handle, dev->desc.iManufacturer,
(unsigned char *) dev->s_manufacturer,
sizeof(dev->s_manufacturer));
libusb_get_string_descriptor_ascii(dev->handle, dev->desc.iProduct,
(unsigned char *) dev->s_product,
sizeof(dev->s_product));
return 0;
}
int razer_deinit(struct razer_device *dev)
{
int iface;
/* release claimed interfaces */
for (iface = 0; iface < dev->cfg_desc->bNumInterfaces; iface++)
libusb_release_interface(dev->handle, iface);
libusb_free_config_descriptor(dev->cfg_desc);
libusb_close(dev->handle);
return 0;
}
/* the caller is responsible of freeing the allocated structure by using the
* razer_free_devices() function */
ssize_t razer_get_devices(struct razer_device **rzdevs)
{
ssize_t ndevs, ndevs_supported = 0;
libusb_device **devs;
int i;
ndevs = libusb_get_device_list(NULL, &devs);
if (ndevs < 1)
return ndevs;
/* allocate space for the array of Razer PIDs */
*rzdevs = malloc(sizeof(struct razer_device) * ndevs);
for (i = 0; i < ndevs; i++) {
struct razer_device *rzdev = (struct razer_device *) (*rzdevs +
sizeof(struct razer_device) *
ndevs_supported);
memset(rzdev, 0, sizeof(struct razer_device));
if (libusb_get_device_descriptor(devs[i], &(rzdev->desc)) < 0)
continue;
if (rzdev->desc.idVendor != k_supported_vid)
continue;
/* look for the product ID on the supported PIDs array */
if (bsearch(&(rzdev->desc.idProduct), k_supported_pids,
sizeof(k_supported_pids) / sizeof(uint16_t),
sizeof(uint16_t), search_cmpfn) == NULL)
continue;
/* this device is supported */
rzdev->dev = devs[i];
ndevs_supported++;
}
libusb_free_device_list(devs, 1);
*rzdevs = realloc(*rzdevs, sizeof(struct razer_device) * ndevs_supported);
return ndevs_supported;
}
int razer_free_devices(struct razer_device **rzdevs)
{
if (!rzdevs || !(*rzdevs))
return -1;
free(*rzdevs);
*rzdevs = NULL;
return 0;
}
int razer_send_report(struct razer_device *dev,
uint8_t cmd_class,
uint8_t cmd_id,
uint8_t *in_args,
uint8_t in_args_len,
uint8_t *out_args,
uint8_t out_args_len)
{
int retval;
uint8_t transaction_id;
struct razer_report report = {0};
transaction_id = rand() % 0xff;
report.transaction_id = transaction_id;
report.cmd_class = cmd_class;
report.cmd_id = cmd_id;
report.args_len = in_args_len;
memcpy(report.args, in_args, in_args_len);
report.crc = report_crc(&report);
/* send feature report */
do {
retval = libusb_control_transfer(
dev->handle,
LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS |
LIBUSB_RECIPIENT_INTERFACE,
HID_SET_REPORT,
(HID_REPORT_TYPE_FEATURE << 8) | 0x00,
0x0000,
(uint8_t *) &report,
sizeof(struct razer_report),
REPORT_TIMEOUT);
} while (retval < 0);
/* HACK: actually wait for the device firmware to be ready */
usleep(REPORT_TIMEOUT);
if (!out_args || !out_args_len)
return retval;
/* obtain response report from device */
do {
retval = libusb_control_transfer(
dev->handle,
LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS |
LIBUSB_RECIPIENT_INTERFACE,
HID_GET_REPORT,
(HID_REPORT_TYPE_FEATURE << 8) | 0x00,
0x0000,
(uint8_t *) &report,
sizeof(struct razer_report),
REPORT_TIMEOUT);
} while (retval < 0);
/* copy response arguments, without overflowing the supplied buffer */
retval = report.args_len > out_args_len ? out_args_len : report.args_len;
memcpy(out_args, report.args, retval);
/* TODO: handle out-of-order input reports and correlate using transaction_id */
assert(report.transaction_id == transaction_id);
return retval;
}