ckb-next  beta-v0.2.8 at branch testing
ckb-next driver for corsair devices
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
led_keyboard.c
Go to the documentation of this file.
1 #include <stdint.h>
2 
3 #include "led.h"
4 #include "notify.h"
5 #include "profile.h"
6 #include "usb.h"
7 
8 // Define an ordered dithering table by using bit reversion.
9 #define BR1(x) ((((x) & 0xaa) >> 1) | (((x) & 0x55) << 1))
10 #define BR2(x) (((BR1(x) & 0xcc) >> 2) | ((BR1(x) & 0x33) << 2))
11 #define BR4(x) (((BR2(x) & 0xf0) >> 4) | ((BR2(x) & 0x0f) << 4))
12 #define O0(i) BR4(i),
13 #define O1(i) O0(i) O0((i) + 1)
14 #define O2(i) O1(i) O1((i) + 2)
15 #define O3(i) O2(i) O2((i) + 4)
16 #define O4(i) O3(i) O3((i) + 8)
17 #define O5(i) O4(i) O4((i) + 16)
18 #define O6(i) O5(i) O5((i) + 32)
19 #define O7(i) O6(i) O6((i) + 64)
20 #define O8(i) O7(i) O7((i) + 127)
21 
22 static uchar bit_reverse_table[256] = { O8(0) };
23 
24 static uchar ordered8to3(int index, uchar value){
25  int m = value * 7;
26  int b = m / 255;
27  if((m % 255) > bit_reverse_table[index & 0xff])
28  b++;
29  return b;
30 }
31 
32 static uchar quantize8to3(int index, uchar value){
33  return value >> 5;
34 }
35 
36 static void makergb_512(const lighting* light, uchar data_pkt[5][MSG_SIZE],
37  uchar (*ditherfn)(int, uchar)){
38  uchar r[N_KEYS_HW / 2], g[N_KEYS_HW / 2], b[N_KEYS_HW / 2];
39  // Compress RGB values to a 512-color palette
40  for(int i = 0; i < N_KEYS_HW; i += 2){
41  char r1 = ditherfn(i, light->r[i]), r2 = ditherfn(i + 1, light->r[i + 1]);
42  char g1 = ditherfn(i, light->g[i]), g2 = ditherfn(i + 1, light->g[i + 1]);
43  char b1 = ditherfn(i, light->b[i]), b2 = ditherfn(i + 1, light->b[i + 1]);
44  r[i / 2] = (7 - r2) << 4 | (7 - r1);
45  g[i / 2] = (7 - g2) << 4 | (7 - g1);
46  b[i / 2] = (7 - b2) << 4 | (7 - b1);
47  }
48  memcpy(data_pkt[0] + 4, r, 60);
49  memcpy(data_pkt[1] + 4, r + 60, 12);
50  memcpy(data_pkt[1] + 16, g, 48);
51  memcpy(data_pkt[2] + 4, g + 48, 24);
52  memcpy(data_pkt[2] + 28, b, 36);
53  memcpy(data_pkt[3] + 4, b + 36, 36);
54 }
55 
56 static void makergb_full(const lighting* light, uchar data_pkt[12][MSG_SIZE]){
57  const uchar* r = light->r, *g = light->g, *b = light->b;
58  // Red
59  memcpy(data_pkt[0] + 4, r, 60);
60  memcpy(data_pkt[1] + 4, r + 60, 60);
61  memcpy(data_pkt[2] + 4, r + 120, 24);
62  // Green (final R packet is blank)
63  memcpy(data_pkt[4] + 4, g, 60);
64  memcpy(data_pkt[5] + 4, g + 60, 60);
65  memcpy(data_pkt[6] + 4, g + 120, 24);
66  // Blue (final G packet is blank)
67  memcpy(data_pkt[8] + 4, b, 60);
68  memcpy(data_pkt[9] + 4, b + 60, 60);
69  memcpy(data_pkt[10] + 4, b + 120, 24);
70 }
71 
72 static int rgbcmp(const lighting* lhs, const lighting* rhs){
73  // Compare two light structures, ignore mouse zones
74  return memcmp(lhs->r, rhs->r, N_KEYS_HW) || memcmp(lhs->g, rhs->g, N_KEYS_HW) || memcmp(lhs->b, rhs->b, N_KEYS_HW);
75 }
76 
77 int updatergb_kb(usbdevice* kb, int force){
78  if(!kb->active)
79  return 0;
80  lighting* lastlight = &kb->profile->lastlight;
81  lighting* newlight = &kb->profile->currentmode->light;
82  // Don't do anything if the lighting hasn't changed
83  if(!force && !lastlight->forceupdate && !newlight->forceupdate
84  && !rgbcmp(lastlight, newlight) && lastlight->sidelight == newlight->sidelight) // strafe sidelights
85  return 0;
86  lastlight->forceupdate = newlight->forceupdate = 0;
87 
88  if(IS_FULLRANGE(kb)){
89  // Update strafe sidelights if necessary
90  if(lastlight->sidelight != newlight->sidelight) {
91  uchar data_pkt[2][MSG_SIZE] = {
92  { 0x07, 0x05, 0x08, 0x00, 0x00 },
93  { 0x07, 0x05, 0x02, 0, 0x03 }
94  };
95  if (newlight->sidelight)
96  data_pkt[0][4]=1; // turn on
97  if(!usbsend(kb, data_pkt[0], 2))
98  return -1;
99  }
100  // 16.8M color lighting works fine on strafe and is the only way it actually works
101  uchar data_pkt[12][MSG_SIZE] = {
102  // Red
103  { 0x7f, 0x01, 0x3c, 0 },
104  { 0x7f, 0x02, 0x3c, 0 },
105  { 0x7f, 0x03, 0x18, 0 },
106  { 0x07, 0x28, 0x01, 0x03, 0x01, 0},
107  // Green
108  { 0x7f, 0x01, 0x3c, 0 },
109  { 0x7f, 0x02, 0x3c, 0 },
110  { 0x7f, 0x03, 0x18, 0 },
111  { 0x07, 0x28, 0x02, 0x03, 0x01, 0},
112  // Blue
113  { 0x7f, 0x01, 0x3c, 0 },
114  { 0x7f, 0x02, 0x3c, 0 },
115  { 0x7f, 0x03, 0x18, 0 },
116  { 0x07, 0x28, 0x03, 0x03, 0x02, 0}
117  };
118  makergb_full(newlight, data_pkt);
119  if(!usbsend(kb, data_pkt[0], 12))
120  return -1;
121  } else {
122  // On older keyboards it looks flickery and causes lighting glitches, so we don't use it.
123  uchar data_pkt[5][MSG_SIZE] = {
124  { 0x7f, 0x01, 60, 0 },
125  { 0x7f, 0x02, 60, 0 },
126  { 0x7f, 0x03, 60, 0 },
127  { 0x7f, 0x04, 36, 0 },
128  { 0x07, 0x27, 0x00, 0x00, 0xD8 }
129  };
130  makergb_512(newlight, data_pkt, kb->dither ? ordered8to3 : quantize8to3);
131  if(!usbsend(kb, data_pkt[0], 5))
132  return -1;
133  }
134 
135  memcpy(lastlight, newlight, sizeof(lighting));
136  return 0;
137 }
138 
139 int savergb_kb(usbdevice* kb, lighting* light, int mode){
140  if(kb->fwversion >= 0x0120){
141  uchar data_pkt[12][MSG_SIZE] = {
142  // Red
143  { 0x7f, 0x01, 60, 0 },
144  { 0x7f, 0x02, 60, 0 },
145  { 0x7f, 0x03, 24, 0 },
146  { 0x07, 0x14, 0x03, 0x01, 0x01, mode + 1, 0x01 },
147  // Green
148  { 0x7f, 0x01, 60, 0 },
149  { 0x7f, 0x02, 60, 0 },
150  { 0x7f, 0x03, 24, 0 },
151  { 0x07, 0x14, 0x03, 0x01, 0x01, mode + 1, 0x02 },
152  // Blue
153  { 0x7f, 0x01, 60, 0 },
154  { 0x7f, 0x02, 60, 0 },
155  { 0x7f, 0x03, 24, 0 },
156  { 0x07, 0x14, 0x03, 0x01, 0x01, mode + 1, 0x03 }
157  };
158  makergb_full(light, data_pkt);
159  if(!usbsend(kb, data_pkt[0], 12))
160  return -1;
161  if (IS_STRAFE(kb)){ // end save
162  uchar save_end_pkt[MSG_SIZE] = { 0x07, 0x14, 0x04, 0x01, 0x01 };
163  if(!usbsend(kb, save_end_pkt, 1))
164  return -1;
165  }
166  } else {
167  uchar data_pkt[5][MSG_SIZE] = {
168  { 0x7f, 0x01, 60, 0 },
169  { 0x7f, 0x02, 60, 0 },
170  { 0x7f, 0x03, 60, 0 },
171  { 0x7f, 0x04, 36, 0 },
172  { 0x07, 0x14, 0x02, 0x00, 0x01, mode + 1 }
173  };
174  makergb_512(light, data_pkt, kb->dither ? ordered8to3 : quantize8to3);
175  if(!usbsend(kb, data_pkt[0], 5))
176  return -1;
177  }
178  return 0;
179 }
180 
181 int loadrgb_kb(usbdevice* kb, lighting* light, int mode){
182  if(kb->fwversion >= 0x0120){
183  uchar data_pkt[12][MSG_SIZE] = {
184  { 0x0e, 0x14, 0x03, 0x01, 0x01, mode + 1, 0x01 },
185  { 0xff, 0x01, 60, 0 },
186  { 0xff, 0x02, 60, 0 },
187  { 0xff, 0x03, 24, 0 },
188  { 0x0e, 0x14, 0x03, 0x01, 0x01, mode + 1, 0x02 },
189  { 0xff, 0x01, 60, 0 },
190  { 0xff, 0x02, 60, 0 },
191  { 0xff, 0x03, 24, 0 },
192  { 0x0e, 0x14, 0x03, 0x01, 0x01, mode + 1, 0x03 },
193  { 0xff, 0x01, 60, 0 },
194  { 0xff, 0x02, 60, 0 },
195  { 0xff, 0x03, 24, 0 },
196  };
197  uchar in_pkt[4][MSG_SIZE] = {
198  { 0x0e, 0x14, 0x03, 0x01 },
199  { 0xff, 0x01, 60, 0 },
200  { 0xff, 0x02, 60, 0 },
201  { 0xff, 0x03, 24, 0 },
202  };
203 
209 
210  uchar cmp_pkt[4][4] = {
211  { 0x0e, 0x14, 0x03, 0x01 },
212  { 0x0e, 0xff, 0x01, 60 },
213  { 0x0e, 0xff, 0x02, 60 },
214  { 0x0e, 0xff, 0x03, 24 },
215  };
217  uchar* colors[3] = { light->r, light->g, light->b };
218  for(int clr = 0; clr < 3; clr++){
219  for(int i = 0; i < 4; i++){
220  if(!usbrecv(kb, data_pkt[i + clr * 4], in_pkt[i]))
221  return -1;
222 
223  uchar* comparePacket = data_pkt[i + clr * 4];
224  if ((kb->fwversion >= 0x205)
228  || ((kb->fwversion >= 0x204)
229  && ((kb->product == P_K70_LUX_NRGB) || (kb->product == P_K70_LUX)))) {
230  comparePacket = cmp_pkt[i];
231  }
232 
233  if (memcmp(in_pkt[i], comparePacket, 4)) {
234  ckb_err("Bad input header\n");
235  ckb_err("color = %d, i = %d, mode = %d\nOutput (Request): %2.2x %2.2x %2.2x %2.2x\nInput(Reply): %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n", clr, i, mode,
236  comparePacket[0], comparePacket[1], comparePacket[2], comparePacket[3],
237  in_pkt[i][0], in_pkt[i][1], in_pkt[i][2], in_pkt[i][3], in_pkt[i][4], in_pkt[i][5], in_pkt[i][6], in_pkt[i][7]);
238  in_pkt[2][0] = 0x99;
239  in_pkt[2][1] = 0x99;
240  in_pkt[2][2] = 0x99;
241  in_pkt[2][3] = 0x99;
242  usbrecv(kb, in_pkt[2], in_pkt[2]); // just to find it in the wireshark log
243  return -1;
244  }
245  }
246  // Copy colors to lighting. in_pkt[0] is irrelevant.
247  memcpy(colors[clr], in_pkt[1] + 4, 60);
248  memcpy(colors[clr] + 60, in_pkt[2] + 4, 60);
249  memcpy(colors[clr] + 120, in_pkt[3] + 4, 24);
250  }
251  } else {
252  uchar data_pkt[5][MSG_SIZE] = {
253  { 0x0e, 0x14, 0x02, 0x01, 0x01, mode + 1, 0 },
254  { 0xff, 0x01, 60, 0 },
255  { 0xff, 0x02, 60, 0 },
256  { 0xff, 0x03, 60, 0 },
257  { 0xff, 0x04, 36, 0 },
258  };
259  uchar in_pkt[4][MSG_SIZE] = {
260  { 0xff, 0x01, 60, 0 },
261  { 0xff, 0x02, 60, 0 },
262  { 0xff, 0x03, 60, 0 },
263  { 0xff, 0x04, 36, 0 },
264  };
265  // Write initial packet
266  if(!usbsend(kb, data_pkt[0], 1))
267  return -1;
268  // Read colors
269  for(int i = 1; i < 5; i++){
270  if(!usbrecv(kb, data_pkt[i],in_pkt[i - 1]))
271  return -1;
272  if(memcmp(in_pkt[i - 1], data_pkt[i], 4)){
273  ckb_err("Bad input header\n");
274  return -1;
275  }
276  }
277  // Copy the data back to the mode
278  uint8_t mr[N_KEYS_HW / 2], mg[N_KEYS_HW / 2], mb[N_KEYS_HW / 2];
279  memcpy(mr, in_pkt[0] + 4, 60);
280  memcpy(mr + 60, in_pkt[1] + 4, 12);
281  memcpy(mg, in_pkt[1] + 16, 48);
282  memcpy(mg + 48, in_pkt[2] + 4, 24);
283  memcpy(mb, in_pkt[2] + 28, 36);
284  memcpy(mb + 36, in_pkt[3] + 4, 36);
285  // Unpack LED data to 8bpc format
286  for(int i = 0; i < N_KEYS_HW; i++){
287  int i_2 = i / 2;
288  uint8_t r, g, b;
289 
290  // 3-bit intensities stored in alternate nybbles.
291  if (i & 1) {
292  r = 7 - (mr[i_2] >> 4);
293  g = 7 - (mg[i_2] >> 4);
294  b = 7 - (mb[i_2] >> 4);
295  } else {
296  r = 7 - (mr[i_2] & 0x0F);
297  g = 7 - (mg[i_2] & 0x0F);
298  b = 7 - (mb[i_2] & 0x0F);
299  }
300  // Scale 3-bit values up to 8 bits.
301  light->r[i] = r << 5 | r << 2 | r >> 1;
302  light->g[i] = g << 5 | g << 2 | g >> 1;
303  light->b[i] = b << 5 | b << 2 | b >> 1;
304  }
305  }
306  return 0;
307 }
#define IS_FULLRANGE(kb)
Full color range (16.8M) vs partial color range (512)
Definition: usb.h:138
#define MSG_SIZE
Definition: structures.h:176
uchar sidelight
Definition: structures.h:78
lighting lastlight
Definition: structures.h:107
uchar b[152+11]
Definition: structures.h:76
usbprofile * profile
Definition: structures.h:221
usbmode * currentmode
Definition: structures.h:105
static uchar quantize8to3(int index, uchar value)
Definition: led_keyboard.c:32
#define ckb_err(fmt, args...)
Definition: includes.h:49
ushort fwversion
Definition: structures.h:239
#define O8(i)
Definition: led_keyboard.c:20
static uchar bit_reverse_table[256]
Definition: led_keyboard.c:22
char dither
Definition: structures.h:249
char active
Definition: structures.h:231
int loadrgb_kb(usbdevice *kb, lighting *light, int mode)
Definition: led_keyboard.c:181
#define N_KEYS_HW
Definition: keymap.h:24
lighting light
Definition: structures.h:84
static void makergb_full(const lighting *light, uchar data_pkt[12][64])
Definition: led_keyboard.c:56
unsigned char uchar
Definition: includes.h:24
static int rgbcmp(const lighting *lhs, const lighting *rhs)
Definition: led_keyboard.c:72
uchar forceupdate
Definition: structures.h:77
short product
Definition: structures.h:237
uchar g[152+11]
Definition: structures.h:75
static uchar ordered8to3(int index, uchar value)
Definition: led_keyboard.c:24
#define IS_STRAFE(kb)
Definition: usb.h:77
#define usbrecv(kb, out_msg, in_msg)
usbrecv macro is used to wrap _usbrecv() with debugging information (file and lineno) ...
Definition: usb.h:256
Definitions for using USB interface.
uchar r[152+11]
Definition: structures.h:74
int savergb_kb(usbdevice *kb, lighting *light, int mode)
Definition: led_keyboard.c:139
static void makergb_512(const lighting *light, uchar data_pkt[5][64], uchar(*ditherfn)(int, uchar))
Definition: led_keyboard.c:36
#define P_K70_LUX
Definition: usb.h:55
#define P_K70_LUX_NRGB
Definition: usb.h:57
#define usbsend(kb, messages, count)
usbsend macro is used to wrap _usbsend() with debugging information (file and lineno) ...
Definition: usb.h:239
int updatergb_kb(usbdevice *kb, int force)
Definition: led_keyboard.c:77