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