From 8bef88b2651340e0e87c6b7dfda82a37f6a215c1 Mon Sep 17 00:00:00 2001 From: ccMSC Date: Fri, 24 Apr 2015 22:16:10 -0600 Subject: [PATCH] Firmware v1.20 support --- src/ckb-daemon/devnode.c | 28 +-- src/ckb-daemon/devnode.h | 2 +- src/ckb-daemon/firmware.c | 6 +- src/ckb-daemon/input.c | 10 +- src/ckb-daemon/input.h | 10 +- src/ckb-daemon/input_linux.c | 2 +- src/ckb-daemon/led.c | 326 +++++++++++++++++++++++------------ src/ckb-daemon/led.h | 16 +- src/ckb-daemon/main.c | 48 +++--- src/ckb-daemon/notify.c | 6 +- src/ckb-daemon/notify.h | 2 +- src/ckb-daemon/profile.c | 18 +- src/ckb-daemon/profile.h | 2 +- src/ckb-daemon/structures.h | 10 +- src/ckb-daemon/usb.h | 6 +- src/ckb-daemon/usb_linux.c | 27 +-- src/ckb-daemon/usb_mac.c | 15 +- 17 files changed, 325 insertions(+), 209 deletions(-) diff --git a/src/ckb-daemon/devnode.c b/src/ckb-daemon/devnode.c index b1ef503..7d0dba2 100644 --- a/src/ckb-daemon/devnode.c +++ b/src/ckb-daemon/devnode.c @@ -320,8 +320,6 @@ unsigned readlines(int fd, const char** input){ void readcmd(usbdevice* kb, const char* line){ usbdevice* kb0 = kb; - if(IS_CONNECTED(kb)) - pthread_mutex_lock(&kb->mutex); char* word = malloc(strlen(line) + 1); int wordlen; const char* newline = 0; @@ -350,10 +348,6 @@ void readcmd(usbdevice* kb, const char* line){ // Send the RGB command to the last device if its colors changed if(kb != prevkb){ updatergb(prevkb, 0); - if(IS_CONNECTED(prevkb)) - pthread_mutex_unlock(&prevkb->mutex); - if(IS_CONNECTED(kb)) - pthread_mutex_lock(&kb->mutex); } } // Check for a command word @@ -504,10 +498,6 @@ void readcmd(usbdevice* kb, const char* line){ // Send the RGB command to the last device if its colors changed if(kb != prevkb){ updatergb(prevkb, 0); - if(IS_CONNECTED(prevkb)) - pthread_mutex_unlock(&prevkb->mutex); - if(IS_CONNECTED(kb)) - pthread_mutex_lock(&kb->mutex); } } continue; @@ -620,7 +610,7 @@ void readcmd(usbdevice* kb, const char* line){ continue; case NAME: case IOFF: case ION: case IAUTO: case INOTIFY: // All of the above just parse the whole word - handler(mode, keymap, notifynumber, 0, word); + handler(kb, mode, keymap, notifynumber, 0, word); continue; case PROFILENAME: // Profile name is the same, but takes a different parameter @@ -642,20 +632,20 @@ void readcmd(usbdevice* kb, const char* line){ // RGB command has a special response for "on", "off", and a hex constant int r, g, b; if(!strcmp(word, "on")){ - cmd_rgbon(mode); + cmd_rgbon(kb, mode); continue; } else if(!strcmp(word, "off")){ - cmd_rgboff(mode); + cmd_rgboff(kb, mode); continue; } else if(sscanf(word, "%02x%02x%02x", &r, &g, &b) == 3){ for(int i = 0; i < N_KEYS; i++) - cmd_rgb(mode, keymap, notifynumber, i, word); + cmd_rgb(kb, mode, keymap, notifynumber, i, word); continue; } } case MACRO: if(!strcmp(word, "clear")){ // Macro has a special clear command - cmd_macroclear(mode); + cmd_macroclear(kb, mode); continue; } break; @@ -682,7 +672,7 @@ void readcmd(usbdevice* kb, const char* line){ // Macros have a separate left-side handler if(command == MACRO){ word[left] = 0; - cmd_macro(mode, keymap, word, right); + cmd_macro(kb, mode, keymap, word, right); continue; } // Scan the left side for key names and run the request command @@ -693,16 +683,16 @@ void readcmd(usbdevice* kb, const char* line){ if(!strcmp(keyname, "all")){ // Set all keys for(int i = 0; i < N_KEYS; i++) - handler(mode, keymap, notifynumber, i, right); + handler(kb, mode, keymap, notifynumber, i, right); } else if((sscanf(keyname, "#%d", &keycode) && keycode >= 0 && keycode < N_KEYS) || (sscanf(keyname, "#x%x", &keycode) && keycode >= 0 && keycode < N_KEYS)){ // Set a key numerically - handler(mode, keymap, notifynumber, keycode, right); + handler(kb, mode, keymap, notifynumber, keycode, right); } else { // Find this key in the keymap for(unsigned i = 0; i < N_KEYS; i++){ if(keymap[i].name && !strcmp(keyname, keymap[i].name)){ - handler(mode, keymap, notifynumber, i, right); + handler(kb, mode, keymap, notifynumber, i, right); break; } } diff --git a/src/ckb-daemon/devnode.h b/src/ckb-daemon/devnode.h index b1b8de2..ba5fb6d 100644 --- a/src/ckb-daemon/devnode.h +++ b/src/ckb-daemon/devnode.h @@ -73,7 +73,7 @@ typedef enum { FWUPDATE } cmd; -typedef void (*cmdhandler)(usbmode*, const key*, int, int, const char*); +typedef void (*cmdhandler)(usbdevice*, usbmode*, const key*, int, int, const char*); // Reads input from the command FIFO void readcmd(usbdevice* kb, const char* line); diff --git a/src/ckb-daemon/firmware.c b/src/ckb-daemon/firmware.c index 2b836c2..776af18 100644 --- a/src/ckb-daemon/firmware.c +++ b/src/ckb-daemon/firmware.c @@ -23,8 +23,12 @@ int getfwversion(usbdevice* kb){ // Wait for the response DELAY_SHORT; uchar in_pkt[MSG_SIZE]; - if(!usbinput(kb, in_pkt) || in_pkt[0] != 0x0e || in_pkt[1] != 0x01) + if(!usbinput(kb, in_pkt)) return -1; + if(in_pkt[0] != 0x0e || in_pkt[1] != 0x01){ + printf("Error: %s:%d: Bad input header\n", __FILE_NOPATH__, __LINE__); + return -1; + } short vendor, product, version, bootloader; // Copy the vendor ID, product ID, version, and poll rate from the firmware data memcpy(&version, in_pkt + 8, 2); diff --git a/src/ckb-daemon/input.c b/src/ckb-daemon/input.c index 2a7e29e..18054ab 100644 --- a/src/ckb-daemon/input.c +++ b/src/ckb-daemon/input.c @@ -154,7 +154,7 @@ void closebind(keybind* bind){ memset(bind, 0, sizeof(*bind)); } -void cmd_bind(usbmode* mode, const key* keymap, int dummy, int keyindex, const char* to){ +void cmd_bind(usbdevice* kb, usbmode* mode, const key* keymap, int dummy, int keyindex, const char* to){ // Find the key to bind to int tocode = 0; if(sscanf(to, "#x%ux", &tocode) != 1 && sscanf(to, "#%u", &tocode) == 1){ @@ -170,15 +170,15 @@ void cmd_bind(usbmode* mode, const key* keymap, int dummy, int keyindex, const c } } -void cmd_unbind(usbmode* mode, const key* keymap, int dummy, int keyindex, const char* to){ +void cmd_unbind(usbdevice* kb, usbmode* mode, const key* keymap, int dummy, int keyindex, const char* to){ mode->bind.base[keyindex] = KEY_UNBOUND; } -void cmd_rebind(usbmode* mode, const key* keymap, int dummy, int keyindex, const char* to){ +void cmd_rebind(usbdevice* kb, usbmode* mode, const key* keymap, int dummy, int keyindex, const char* to){ mode->bind.base[keyindex] = keymap[keyindex].scan; } -void cmd_macro(usbmode* mode, const key* keymap, const char* keys, const char* assignment){ +void cmd_macro(usbdevice* kb, usbmode* mode, const key* keymap, const char* keys, const char* assignment){ keybind* bind = &mode->bind; if(bind->macrocount >= MACRO_MAX) return; @@ -277,7 +277,7 @@ void cmd_macro(usbmode* mode, const key* keymap, const char* keys, const char* a bind->macros = realloc(bind->macros, (bind->macrocap += 16) * sizeof(keymacro)); } -void cmd_macroclear(usbmode* mode){ +void cmd_macroclear(usbdevice* kb, usbmode* mode){ keybind* bind = &mode->bind; for(int i = 0; i < bind->macrocount; i++) free(bind->macros[i].actions); diff --git a/src/ckb-daemon/input.h b/src/ckb-daemon/input.h index 2ae7cb1..585330f 100644 --- a/src/ckb-daemon/input.h +++ b/src/ckb-daemon/input.h @@ -20,15 +20,15 @@ void initbind(keybind* bind, const key* keymap); void closebind(keybind* bind); // Binds a key -void cmd_bind(usbmode* mode, const key* keymap, int dummy, int keyindex, const char* to); +void cmd_bind(usbdevice* kb, usbmode* mode, const key* keymap, int dummy, int keyindex, const char* to); // Unbinds a key -void cmd_unbind(usbmode* mode, const key* keymap, int dummy, int keyindex, const char* ignored); +void cmd_unbind(usbdevice* kb, usbmode* mode, const key* keymap, int dummy, int keyindex, const char* ignored); // Resets a key binding -void cmd_rebind(usbmode* mode, const key* keymap, int dummy, int keyindex, const char* ignored); +void cmd_rebind(usbdevice* kb, usbmode* mode, const key* keymap, int dummy, int keyindex, const char* ignored); // Creates or updates a macro -void cmd_macro(usbmode* mode, const key* keymap, const char* keys, const char* assignment); +void cmd_macro(usbdevice* kb, usbmode* mode, const key* keymap, const char* keys, const char* assignment); // Clears all macros -void cmd_macroclear(usbmode* mode); +void cmd_macroclear(usbdevice* kb, usbmode* mode); #ifdef OS_LINUX // Is a key a modifier? diff --git a/src/ckb-daemon/input_linux.c b/src/ckb-daemon/input_linux.c index 3d4275d..84ad85a 100644 --- a/src/ckb-daemon/input_linux.c +++ b/src/ckb-daemon/input_linux.c @@ -174,7 +174,7 @@ void os_updateindicators(usbdevice* kb, int force){ ileds = (ileds & ~mode->ioff) | mode->ion; if(force || ileds != kb->ileds){ kb->ileds = ileds; - struct usbdevfs_ctrltransfer transfer = { 0x21, 0x09, 0x0200, 0x00, 1, 500, &kb->ileds }; + struct usbdevfs_ctrltransfer transfer = { 0x21, 0x09, 0x0200, 0x00, 1, 5000, &kb->ileds }; ioctl(kb->handle, USBDEVFS_CONTROL, &transfer); } } diff --git a/src/ckb-daemon/led.c b/src/ckb-daemon/led.c index 1aab81d..84d7c5c 100644 --- a/src/ckb-daemon/led.c +++ b/src/ckb-daemon/led.c @@ -4,15 +4,22 @@ void initrgb(keylight* light){ // Allocate colors. Default to all white. + memset(light, 0xFF, sizeof(keylight)); light->enabled = 1; - memset(light->r, 0, sizeof(light->r)); - memset(light->g, 0, sizeof(light->g)); - memset(light->b, 0, sizeof(light->b)); } -void makergb(const keylight* light, uchar data_pkt[5][MSG_SIZE], int forceon){ +void makergb_512(const keylight* light, uchar data_pkt[5][MSG_SIZE], int forceon){ if(forceon || light->enabled){ - const char* r = light->r, *g = light->g, *b = light->b; + uchar r[N_KEYS / 2], g[N_KEYS / 2], b[N_KEYS / 2]; + // Compress RGB values to a 512-color palette + for(int i = 0; i < N_KEYS; i += 2){ + char r1 = light->r[i], r2 = light->r[i + 1]; + char g1 = light->g[i], g2 = light->g[i + 1]; + char b1 = light->b[i], b2 = light->b[i + 1]; + r[i / 2] = (7 - (r2 >> 5)) << 4 | (7 - (r1 >> 5)); + g[i / 2] = (7 - (g2 >> 5)) << 4 | (7 - (g1 >> 5)); + b[i / 2] = (7 - (b2 >> 5)) << 4 | (7 - (b1 >> 5)); + } memcpy(data_pkt[0] + 4, r, 60); memcpy(data_pkt[1] + 4, r + 60, 12); memcpy(data_pkt[1] + 16, g, 48); @@ -27,6 +34,24 @@ void makergb(const keylight* light, uchar data_pkt[5][MSG_SIZE], int forceon){ } } +void makergb_full(const keylight* light, uchar data_pkt[12][MSG_SIZE], int forceon){ + if(forceon || light->enabled){ + const uchar* r = light->r, *g = light->g, *b = light->b; + // Red + memcpy(data_pkt[0] + 4, r, 60); + memcpy(data_pkt[1] + 4, r + 60, 60); + memcpy(data_pkt[2] + 4, r + 120, 24); + // Green (final R packet is blank) + memcpy(data_pkt[4] + 4, g, 60); + memcpy(data_pkt[5] + 4, g + 60, 60); + memcpy(data_pkt[6] + 4, g + 120, 24); + // Blue (final G packet is blank) + memcpy(data_pkt[8] + 4, b, 60); + memcpy(data_pkt[9] + 4, b + 60, 60); + memcpy(data_pkt[10] + 4, b + 120, 24); + } +} + void updatergb(usbdevice* kb, int force){ if(!IS_CONNECTED(kb) || !HAS_FEATURES(kb, FEAT_RGB) || !kb->active) return; @@ -35,69 +60,187 @@ void updatergb(usbdevice* kb, int force){ keylight* newlight = &kb->profile.currentmode->light; if(!force && ((!lastlight->enabled && !newlight->enabled) || !memcmp(lastlight, newlight, sizeof(keylight)))) return; - memcpy(lastlight, newlight, sizeof(keylight)); - uchar data_pkt[5][MSG_SIZE] = { - { 0x7f, 0x01, 60, 0 }, - { 0x7f, 0x02, 60, 0 }, - { 0x7f, 0x03, 60, 0 }, - { 0x7f, 0x04, 36, 0 }, - { 0x07, 0x27, 0x00, 0x00, 0xD8 } - }; + /*if(kb->fwversion >= 0x0120){ + uchar data_pkt[12][MSG_SIZE] = { + // Red + { 0x7f, 0x01, 60, 0 }, + { 0x7f, 0x02, 60, 0 }, + { 0x7f, 0x03, 24, 0 }, + { 0x07, 0x28, 0x01, 0x00, 0x01, 0x01}, + // Green + { 0x7f, 0x01, 60, 0 }, + { 0x7f, 0x02, 60, 0 }, + { 0x7f, 0x03, 24, 0 }, + { 0x07, 0x28, 0x02, 0x00, 0x01, 0x01}, + // Blue + { 0x7f, 0x01, 60, 0 }, + { 0x7f, 0x02, 60, 0 }, + { 0x7f, 0x03, 24, 0 }, + { 0x07, 0x28, 0x03, 0x00, 0x02, 0x01} + }; + makergb_full(newlight, data_pkt, 0); + if(usbqueue(kb, data_pkt[0], 12)) + return; + } else {*/ + // 16.8M color lighting causes flickering and color glitches. Don't use it for this. + // Maybe in a future version this can be re-added as an advanced feature. + uchar data_pkt[5][MSG_SIZE] = { + { 0x7f, 0x01, 60, 0 }, + { 0x7f, 0x02, 60, 0 }, + { 0x7f, 0x03, 60, 0 }, + { 0x7f, 0x04, 36, 0 }, + { 0x07, 0x27, 0x00, 0x00, 0xD8 } + }; + makergb_512(newlight, data_pkt, 0); + if(usbqueue(kb, data_pkt[0], 5)) + return; + //} - makergb(newlight, data_pkt, 0); - usbqueue(kb, data_pkt[0], 5); + memcpy(lastlight, newlight, sizeof(keylight)); } void savergb(usbdevice* kb, int mode){ - uchar data_pkt[5][MSG_SIZE] = { - { 0x7f, 0x01, 60, 0 }, - { 0x7f, 0x02, 60, 0 }, - { 0x7f, 0x03, 60, 0 }, - { 0x7f, 0x04, 36, 0 }, - { 0x07, 0x14, 0x02, 0x00, 0x01, mode + 1 } - }; - - makergb(&kb->profile.mode[mode].light, data_pkt, 1); - usbqueue(kb, data_pkt[0], 5); + if(kb->fwversion >= 0x0120){ + uchar data_pkt[12][MSG_SIZE] = { + // Red + { 0x7f, 0x01, 60, 0 }, + { 0x7f, 0x02, 60, 0 }, + { 0x7f, 0x03, 24, 0 }, + { 0x07, 0x14, 0x03, 0x01, 0x01, mode + 1, 0x01 }, + // Green + { 0x7f, 0x01, 60, 0 }, + { 0x7f, 0x02, 60, 0 }, + { 0x7f, 0x03, 24, 0 }, + { 0x07, 0x14, 0x03, 0x01, 0x01, mode + 1, 0x02 }, + // Blue + { 0x7f, 0x01, 60, 0 }, + { 0x7f, 0x02, 60, 0 }, + { 0x7f, 0x03, 24, 0 }, + { 0x07, 0x14, 0x03, 0x01, 0x01, mode + 1, 0x03 } + }; + makergb_full(&kb->profile.mode[mode].light, data_pkt, 0); + usbqueue(kb, data_pkt[0], 12); + } else { + uchar data_pkt[5][MSG_SIZE] = { + { 0x7f, 0x01, 60, 0 }, + { 0x7f, 0x02, 60, 0 }, + { 0x7f, 0x03, 60, 0 }, + { 0x7f, 0x04, 36, 0 }, + { 0x07, 0x14, 0x02, 0x00, 0x01, mode + 1 } + }; + makergb_512(&kb->profile.mode[mode].light, data_pkt, 0); + usbqueue(kb, data_pkt[0], 5); + } } int loadrgb(usbdevice* kb, keylight* light, int mode){ - uchar data_pkt[5][MSG_SIZE] = { - { 0x0e, 0x14, 0x02, 0x01, 0x01, mode + 1, 0 }, - { 0xff, 0x01, 60, 0 }, - { 0xff, 0x02, 60, 0 }, - { 0xff, 0x03, 60, 0 }, - { 0xff, 0x04, 36, 0 }, - }; - uchar in_pkt[4][MSG_SIZE] = { - { 0xff, 0x01, 60, 0 }, - { 0xff, 0x02, 60, 0 }, - { 0xff, 0x03, 60, 0 }, - { 0xff, 0x04, 36, 0 }, - }; - usbqueue(kb, data_pkt[0], 1); - DELAY_SHORT; - if(!usbdequeue(kb)) - return -1; - for(int i = 1; i < 5; i++){ - usbqueue(kb, data_pkt[i], 1); + if(kb->fwversion >= 0x0120){ + uchar data_pkt[12][MSG_SIZE] = { + { 0x0e, 0x14, 0x03, 0x01, 0x01, mode + 1, 0x01 }, + { 0xff, 0x01, 60, 0 }, + { 0xff, 0x02, 60, 0 }, + { 0xff, 0x03, 24, 0 }, + { 0x0e, 0x14, 0x03, 0x01, 0x01, mode + 1, 0x02 }, + { 0xff, 0x01, 60, 0 }, + { 0xff, 0x02, 60, 0 }, + { 0xff, 0x03, 24, 0 }, + { 0x0e, 0x14, 0x03, 0x01, 0x01, mode + 1, 0x03 }, + { 0xff, 0x01, 60, 0 }, + { 0xff, 0x02, 60, 0 }, + { 0xff, 0x03, 24, 0 }, + }; + uchar in_pkt[4][MSG_SIZE] = { + { 0x0e, 0x14, 0x03, 0x01 }, + { 0xff, 0x01, 60, 0 }, + { 0xff, 0x02, 60, 0 }, + { 0xff, 0x03, 24, 0 }, + }; + // Read colors + uchar* colors[3] = { light->r, light->g, light->b }; + for(int clr = 0; clr < 3; clr++){ + for(int i = 0; i < 4; i++){ + usbqueue(kb, data_pkt[i + clr * 4], 1); + DELAY_MEDIUM; + if(!usbdequeue(kb)) + return -1; + // Wait for the response. Make sure the first four bytes match + DELAY_MEDIUM; + if(!usbinput(kb, in_pkt[i])) + return -1; + if(memcmp(in_pkt[i], data_pkt[i], 4)){ + printf("Error: %s:%d: Bad input header\n", __FILE_NOPATH__, __LINE__); + return -1; + } + } + // Copy colors to lighting. in_pkt[0] is irrelevant. + memcpy(colors[clr], in_pkt[1] + 4, 60); + memcpy(colors[clr] + 60, in_pkt[2] + 4, 60); + memcpy(colors[clr] + 120, in_pkt[3] + 4, 24); + } + } else { + uchar data_pkt[5][MSG_SIZE] = { + { 0x0e, 0x14, 0x02, 0x01, 0x01, mode + 1, 0 }, + { 0xff, 0x01, 60, 0 }, + { 0xff, 0x02, 60, 0 }, + { 0xff, 0x03, 60, 0 }, + { 0xff, 0x04, 36, 0 }, + }; + uchar in_pkt[4][MSG_SIZE] = { + { 0xff, 0x01, 60, 0 }, + { 0xff, 0x02, 60, 0 }, + { 0xff, 0x03, 60, 0 }, + { 0xff, 0x04, 36, 0 }, + }; + // Write initial packet + usbqueue(kb, data_pkt[0], 1); DELAY_SHORT; if(!usbdequeue(kb)) return -1; - // Wait for the response. Make sure the first four bytes match - DELAY_SHORT; - if(!usbinput(kb, in_pkt[i - 1]) || memcmp(in_pkt[i - 1], data_pkt[i], 4)) - return -1; + // Read colors + for(int i = 1; i < 5; i++){ + usbqueue(kb, data_pkt[i], 1); + DELAY_SHORT; + if(!usbdequeue(kb)) + return -1; + // Wait for the response. Make sure the first four bytes match + DELAY_SHORT; + if(!usbinput(kb, in_pkt[i - 1])) + return -1; + if(memcmp(in_pkt[i - 1], data_pkt[i], 4)){ + printf("Error: %s:%d: Bad input header\n", __FILE_NOPATH__, __LINE__); + return -1; + } + } + // Copy the data back to the mode + uchar mr[N_KEYS / 2], mg[N_KEYS / 2], mb[N_KEYS / 2]; + memcpy(mr, in_pkt[0] + 4, 60); + memcpy(mr + 60, in_pkt[1] + 4, 12); + memcpy(mg, in_pkt[1] + 16, 48); + memcpy(mg + 48, in_pkt[2] + 4, 24); + memcpy(mb, in_pkt[2] + 28, 36); + memcpy(mb + 36, in_pkt[3] + 4, 36); + // Unpack LED data to 8bpc format + for(int i = 0; i < N_KEYS; i++){ + uchar r, g, b; + if(i & 1){ + r = (7 - ((mr[i / 2] & 0xF0) >> 4)) << 5; + g = (7 - ((mg[i / 2] & 0xF0) >> 4)) << 5; + b = (7 - ((mb[i / 2] & 0xF0) >> 4)) << 5; + } else { + r = (7 - (mr[i / 2] & 0x0F)) << 5; + g = (7 - (mg[i / 2] & 0x0F)) << 5; + b = (7 - (mb[i / 2] & 0x0F)) << 5; + } + // Convert 0xe0 to 0xff (white color) + if(r == 0xe0) r = 0xff; + if(g == 0xe0) g = 0xff; + if(b == 0xe0) b = 0xff; + light->r[i] = r; + light->g[i] = g; + light->b[i] = b; + } } - // Copy the data back to the mode - char* r = light->r, *g = light->g, *b = light->b; - memcpy(r, in_pkt[0] + 4, 60); - memcpy(r + 60, in_pkt[1] + 4, 12); - memcpy(g, in_pkt[1] + 16, 48); - memcpy(g + 48, in_pkt[2] + 4, 24); - memcpy(b, in_pkt[2] + 28, 36); - memcpy(b + 36, in_pkt[3] + 4, 36); light->enabled = 1; return 0; } @@ -115,35 +258,21 @@ int has_key(const char* name, int model){ return 1; } -char* printrgb(keylight* light, const key* keymap, int kbmodel){ +char* printrgb(usbdevice* kb, keylight* light, const key* keymap){ + int kbmodel = kb->model; int length = 0; - // Unpack LED data back to 8bpc format. uchar r[N_KEYS], g[N_KEYS], b[N_KEYS]; - char* mr = light->r; - char* mg = light->g; - char* mb = light->b; + uchar* mr = light->r; + uchar* mg = light->g; + uchar* mb = light->b; for(int i = 0; i < N_KEYS; i++){ - // Translate the RGB index to a key index using the key map + // Translate the key index to an RGB index using the key map int k = keymap[i].led; if(k < 0) continue; - if(k & 1){ - r[i] = (7 - ((mr[k / 2] & 0xF0) >> 4)) << 5; - g[i] = (7 - ((mg[k / 2] & 0xF0) >> 4)) << 5; - b[i] = (7 - ((mb[k / 2] & 0xF0) >> 4)) << 5; - } else { - r[i] = (7 - (mr[k / 2] & 0x0F)) << 5; - g[i] = (7 - (mg[k / 2] & 0x0F)) << 5; - b[i] = (7 - (mb[k / 2] & 0x0F)) << 5; - } - - // Convert 0xe0 to 0xff (white color) - if(r[i] == 0xe0) - r[i] = 0xff; - if(g[i] == 0xe0) - g[i] = 0xff; - if(b[i] == 0xe0) - b[i] = 0xff; + r[i] = mr[k]; + g[i] = mg[k]; + b[i] = mb[k]; } // Make a buffer to track key names and to filter out duplicates char names[N_KEYS][11]; @@ -199,38 +328,23 @@ char* printrgb(keylight* light, const key* keymap, int kbmodel){ return buffer; } -void cmd_rgboff(usbmode* mode){ +void cmd_rgboff(usbdevice* kb, usbmode* mode){ mode->light.enabled = 0; } -void cmd_rgbon(usbmode* mode){ +void cmd_rgbon(usbdevice* kb, usbmode* mode){ mode->light.enabled = 1; } -void cmd_rgb(usbmode* mode, const key* keymap, int dummy, int keyindex, const char* code){ +void cmd_rgb(usbdevice* kb, usbmode* mode, const key* keymap, int dummy, int keyindex, const char* code){ int index = keymap[keyindex].led; if(index < 0) return; - unsigned int r, g, b; - if(sscanf(code, "%2x%2x%2x", &r, &g, &b) == 3){ - if(r > 255) - r = 255; - if(g > 255) - g = 255; - if(b > 255) - b = 255; - char* mr = mode->light.r; - char* mg = mode->light.g; - char* mb = mode->light.b; - if(index & 1){ - mr[index / 2] = (mr[index / 2] & 0x0F) | ((7 - (r >> 5)) << 4); - mg[index / 2] = (mg[index / 2] & 0x0F) | ((7 - (g >> 5)) << 4); - mb[index / 2] = (mb[index / 2] & 0x0F) | ((7 - (b >> 5)) << 4); - } else { - mr[index / 2] = (mr[index / 2] & 0xF0) | (7 - (r >> 5)); - mg[index / 2] = (mg[index / 2] & 0xF0) | (7 - (g >> 5)); - mb[index / 2] = (mb[index / 2] & 0xF0) | (7 - (b >> 5)); - } + uchar r, g, b; + if(sscanf(code, "%2hhx%2hhx%2hhx", &r, &g, &b) == 3){ + mode->light.r[index] = r; + mode->light.g[index] = g; + mode->light.b[index] = b; } } @@ -248,28 +362,28 @@ static uchar iselect(const char* led){ return result; } -void cmd_ioff(usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* led){ +void cmd_ioff(usbdevice* kb, usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* led){ uchar bits = iselect(led); // Add the bits to ioff, remove them from ion mode->ioff |= bits; mode->ion &= ~bits; } -void cmd_ion(usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* led){ +void cmd_ion(usbdevice* kb, usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* led){ uchar bits = iselect(led); // Remove the bits from ioff, add them to ion mode->ioff &= ~bits; mode->ion |= bits; } -void cmd_iauto(usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* led){ +void cmd_iauto(usbdevice* kb, usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* led){ uchar bits = iselect(led); // Remove the bits from both ioff and ion mode->ioff &= ~bits; mode->ion &= ~bits; } -void cmd_inotify(usbmode* mode, const key* keymap, int nnumber, int dummy, const char* led){ +void cmd_inotify(usbdevice* kb, usbmode* mode, const key* keymap, int nnumber, int dummy, const char* led){ uchar bits = iselect(led); if(strstr(led, ":off")) // Turn notifications for these bits off diff --git a/src/ckb-daemon/led.h b/src/ckb-daemon/led.h index f978f1d..6f04023 100644 --- a/src/ckb-daemon/led.h +++ b/src/ckb-daemon/led.h @@ -15,23 +15,23 @@ int loadrgb(usbdevice* kb, keylight* light, int mode); // Generates data for an RGB command to match the given RGB data. Returns a string like "ff0000" or "w:ff0000 a:00ff00 ..." // The result must be freed later. -char* printrgb(keylight* light, const key* keymap, int kbmodel); +char* printrgb(usbdevice* kb, keylight* light, const key* keymap); // Turns LEDs off -void cmd_rgboff(usbmode* mode); +void cmd_rgboff(usbdevice* kb, usbmode* mode); // Turns LEDs on -void cmd_rgbon(usbmode* mode); +void cmd_rgbon(usbdevice* kb, usbmode* mode); // Updates an LED color -void cmd_rgb(usbmode* mode, const key* keymap, int dummy, int keyindex, const char* code); +void cmd_rgb(usbdevice* kb, usbmode* mode, const key* keymap, int dummy, int keyindex, const char* code); // Turns an indicator off permanently -void cmd_ioff(usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* led); +void cmd_ioff(usbdevice* kb, usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* led); // Turns an indicator on permanently -void cmd_ion(usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* led); +void cmd_ion(usbdevice* kb, usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* led); // Sets an indicator to automatic mode -void cmd_iauto(usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* led); +void cmd_iauto(usbdevice* kb, usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* led); // Sets indicator notifications -void cmd_inotify(usbmode* mode, const key* keymap, int nnumber, int dummy, const char* led); +void cmd_inotify(usbdevice* kb, usbmode* mode, const key* keymap, int nnumber, int dummy, const char* led); // Daemon frame rate extern volatile unsigned fps; diff --git a/src/ckb-daemon/main.c b/src/ckb-daemon/main.c index 26e0914..ccc580d 100644 --- a/src/ckb-daemon/main.c +++ b/src/ckb-daemon/main.c @@ -217,25 +217,19 @@ int main(int argc, char** argv){ // Start the signal handling thread pthread_create(&sigthread, 0, sigmain, 0); - int frame = 0; + int v120 = 0; struct timespec time, nexttime; while(1){ clock_gettime(CLOCK_MONOTONIC, &time); - // No need to run most of these functions on every single frame - if(!frame){ - // Process FIFOs - pthread_mutex_lock(&kblistmutex); - for(int i = 0; i < DEV_MAX; i++){ - if(keyboard[i].infifo){ - const char* line; - if(readlines(keyboard[i].infifo, &line)) - readcmd(keyboard + i, line); - } - } - } else - pthread_mutex_lock(&kblistmutex); + pthread_mutex_lock(&kblistmutex); + // Process commands for root controller + if(keyboard[0].infifo){ + const char* line; + if(readlines(keyboard[0].infifo, &line)) + readcmd(keyboard, line); + } // Run the USB queue. Messages must be queued because sending multiple messages at the same time can cause the interface to freeze - for(int i = 1; i < DEV_MAX; i++){ + for(int i = 0; i < DEV_MAX; i++){ if(IS_CONNECTED(keyboard + i)){ pthread_mutex_lock(&keyboard[i].mutex); if(usbdequeue(keyboard + i) == 0 @@ -243,10 +237,21 @@ int main(int argc, char** argv){ // If it failed and couldn't be reset, close the keyboard closeusb(keyboard + i); } else { - // Update indicator LEDs for this keyboard. These are polled rather than processed during events because they don't update - // immediately and may be changed externally by the OS. - if(!frame) + if(keyboard[i].queuecount == 0){ + // Process FIFOs + for(int i = 0; i < DEV_MAX; i++){ + if(keyboard[i].infifo){ + const char* line; + if(readlines(keyboard[i].infifo, &line)) + readcmd(keyboard + i, line); + if(keyboard[i].fwversion >= 0x0120) + v120 = 1; + } + } + // Update indicator LEDs for this keyboard. These are polled rather than processed during events because they don't update + // immediately and may be changed externally by the OS. updateindicators(keyboard + i, 0); + } pthread_mutex_unlock(&keyboard[i].mutex); } } @@ -254,15 +259,14 @@ int main(int argc, char** argv){ pthread_mutex_unlock(&kblistmutex); // Sleep for long enough to achieve the desired frame rate (5 packets per frame). memcpy(&nexttime, &time, sizeof(time)); - timespec_add(&nexttime, 1000000000 / fps / 5); - // Don't ever sleep for less than 1.5ms. It can lock the keyboard. Restart the sleep if it gets interrupted. + timespec_add(&nexttime, 1000000000 / fps / (v120 ? 12 : 5)); + // Don't ever sleep for less than 100µs. It can lock the keyboard. Restart the sleep if it gets interrupted. clock_gettime(CLOCK_MONOTONIC, &time); - timespec_add(&time, 1500000); + timespec_add(&time, 100000); if(timespec_gt(nexttime, time)) while(clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &nexttime, 0) == EINTR); else while(clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &time, 0) == EINTR); - frame = (frame + 1) % 5; } quit(); return 0; diff --git a/src/ckb-daemon/notify.c b/src/ckb-daemon/notify.c index ac63389..af9dd98 100644 --- a/src/ckb-daemon/notify.c +++ b/src/ckb-daemon/notify.c @@ -84,7 +84,7 @@ void nprintind(usbdevice* kb, int nnumber, int led, int on){ nprintf(kb, nnumber, 0, "i %c%s\n", on ? '+' : '-', name); } -void cmd_notify(usbmode* mode, const key* keymap, int nnumber, int keyindex, const char* toggle){ +void cmd_notify(usbdevice* kb, usbmode* mode, const key* keymap, int nnumber, int keyindex, const char* toggle){ if(!strcmp(toggle, "on") || *toggle == 0) SET_KEYBIT(mode->notify[nnumber], keyindex); else if(!strcmp(toggle, "off")) @@ -133,7 +133,7 @@ void getinfo(usbdevice* kb, usbmode* mode, int nnumber, const char* setting){ return; } else if(!strcmp(setting, ":rgb")){ // Get the current RGB settings - char* rgb = printrgb(&mode->light, profile->keymap, kb->model); + char* rgb = printrgb(kb, &mode->light, profile->keymap); nprintf(kb, nnumber, mode, "rgb %s\n", rgb); free(rgb); return; @@ -152,7 +152,7 @@ void getinfo(usbdevice* kb, usbmode* mode, int nnumber, const char* setting){ // Make sure the mode number is valid HWMODE_OR_RETURN(kb, index); // Get the mode from the hardware store - char* rgb = printrgb(kb->hw->light + index, profile->keymap, kb->model); + char* rgb = printrgb(kb, kb->hw->light + index, profile->keymap); nprintf(kb, nnumber, mode, "hwrgb %s\n", rgb); free(rgb); return; diff --git a/src/ckb-daemon/notify.h b/src/ckb-daemon/notify.h index 31b8438..04655a1 100644 --- a/src/ckb-daemon/notify.h +++ b/src/ckb-daemon/notify.h @@ -19,7 +19,7 @@ void nprintkey(usbdevice* kb, int nnumber, const key* keymap, int keyindex, int void nprintind(usbdevice* kb, int nnumber, int led, int on); // Enables or disables notification for a key -void cmd_notify(usbmode* mode, const key* keymap, int nnumber, int keyindex, const char* toggle); +void cmd_notify(usbdevice* kb, usbmode* mode, const key* keymap, int nnumber, int keyindex, const char* toggle); // Gets info about a particular setting. void getinfo(usbdevice* kb, usbmode* mode, int nnumber, const char* setting); diff --git a/src/ckb-daemon/profile.c b/src/ckb-daemon/profile.c index 70964dc..3cf6023 100644 --- a/src/ckb-daemon/profile.c +++ b/src/ckb-daemon/profile.c @@ -83,7 +83,7 @@ void urlencode2(char* dst, const char* src){ *dst = '\0'; } -void cmd_setmodename(usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* name){ +void cmd_setmodename(usbdevice* kb, usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* name){ if(!utf8to16) utf8to16 = iconv_open("UTF-16LE", "UTF-8"); memset(mode->name, 0, sizeof(mode->name)); @@ -303,7 +303,7 @@ int hwloadprofile(usbdevice* kb, int apply){ return -1; } // Wait for the response - DELAY_SHORT; + DELAY_MEDIUM; if(!usbinput(kb, in_pkt)){ free(hw); return -1; @@ -318,7 +318,7 @@ int hwloadprofile(usbdevice* kb, int apply){ return -1; } // Wait for the response - DELAY_SHORT; + DELAY_MEDIUM; if(!usbinput(kb, in_pkt)){ free(hw); return -1; @@ -373,13 +373,13 @@ int hwsaveprofile(usbdevice* kb){ usbqueue(kb, data_pkt[1], 1); } // Save the RGB data - for(int i = 0; i < modes; i++) + for(int i = 0; i < modes; i++){ savergb(kb, i); - // Send all of the queued messages to the hardware. Return failure if any aren't sent. - while(kb->queuecount > 0){ - DELAY_SHORT; - if(!usbdequeue(kb)) - return -1; + while(kb->queuecount > 0){ + DELAY_MEDIUM; + if(!usbdequeue(kb)) + return -1; + } } DELAY_LONG; return 0; diff --git a/src/ckb-daemon/profile.h b/src/ckb-daemon/profile.h index e9951fc..e105806 100644 --- a/src/ckb-daemon/profile.h +++ b/src/ckb-daemon/profile.h @@ -7,7 +7,7 @@ usbmode* getusbmode(int id, usbprofile* profile, const key* keymap); // Sets a mode's name -void cmd_setmodename(usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* name); +void cmd_setmodename(usbdevice* kb, usbmode* mode, const key* keymap, int dummy1, int dummy2, const char* name); // Sets a profile's name void setprofilename(usbprofile* profile, const char* name); // Gets a mode's name. Returns a URL-encoded UTF-8 buffer that needs to be freed later. diff --git a/src/ckb-daemon/structures.h b/src/ckb-daemon/structures.h index b6d8796..dbb0764 100644 --- a/src/ckb-daemon/structures.h +++ b/src/ckb-daemon/structures.h @@ -49,9 +49,11 @@ typedef struct { // Lighting structure for a mode typedef struct { - char r[N_KEYS / 2]; - char g[N_KEYS / 2]; - char b[N_KEYS / 2]; + struct { + uchar r[N_KEYS]; + uchar g[N_KEYS]; + uchar b[N_KEYS]; + }; char enabled; } keylight; @@ -133,7 +135,7 @@ typedef struct { // Structure for tracking keyboard devices #define NAME_LEN 33 -#define QUEUE_LEN 40 +#define QUEUE_LEN 64 #define MSG_SIZE 64 typedef struct { // I/O devices diff --git a/src/ckb-daemon/usb.h b/src/ckb-daemon/usb.h index 2ae7324..7b3d0c2 100644 --- a/src/ckb-daemon/usb.h +++ b/src/ckb-daemon/usb.h @@ -24,9 +24,9 @@ #define IS_RGB(vendor, product) ((product) != (P_K70_NRGB) && (product) != (P_K95_NRGB)) // USB delays for when the keyboards get picky about timing -#define DELAY_SHORT usleep(3000) -#define DELAY_MEDIUM usleep(10000) -#define DELAY_LONG usleep(100000) +#define DELAY_SHORT usleep(2000) +#define DELAY_MEDIUM usleep(20000) +#define DELAY_LONG usleep(200000) // Start the USB system. Returns 0 on success int usbinit(); diff --git a/src/ckb-daemon/usb_linux.c b/src/ckb-daemon/usb_linux.c index 54d5d24..6f07a25 100644 --- a/src/ckb-daemon/usb_linux.c +++ b/src/ckb-daemon/usb_linux.c @@ -9,8 +9,14 @@ int _usbdequeue(usbdevice* kb, const char* file, int line){ if(kb->queuecount == 0 || !kb->handle || !HAS_FEATURES(kb, FEAT_RGB)) return -1; - struct usbdevfs_ctrltransfer transfer = { 0x21, 0x09, 0x0300, 0x03, MSG_SIZE, 500, kb->queue[0] }; - int res = ioctl(kb->handle, USBDEVFS_CONTROL, &transfer); + int res; + if(kb->fwversion >= 0x120){ + struct usbdevfs_bulktransfer transfer = { 3, MSG_SIZE, 5000, kb->queue[0] }; + res = ioctl(kb->handle, USBDEVFS_BULK, &transfer); + } else { + struct usbdevfs_ctrltransfer transfer = { 0x21, 0x09, 0x0300, 0x03, MSG_SIZE, 5000, kb->queue[0] }; + res = ioctl(kb->handle, USBDEVFS_CONTROL, &transfer); + } if(res <= 0){ printf("usbdequeue (%s:%d): %s\n", file, line, res ? strerror(-res) : "No data written"); return 0; @@ -29,7 +35,7 @@ int _usbdequeue(usbdevice* kb, const char* file, int line){ int _usbinput(usbdevice* kb, uchar* message, const char* file, int line){ if(!IS_CONNECTED(kb) || !HAS_FEATURES(kb, FEAT_RGB)) return -1; - struct usbdevfs_ctrltransfer transfer = { 0xa1, 0x01, 0x0300, 0x03, MSG_SIZE, 500, message }; + struct usbdevfs_ctrltransfer transfer = { 0xa1, 0x01, 0x0300, 0x03, MSG_SIZE, 5000, message }; int res = ioctl(kb->handle, USBDEVFS_CONTROL, &transfer); if(res <= 0){ printf("usbinput (%s:%d): %s\n", file, line, res ? strerror(-res) : "No data read"); @@ -264,15 +270,12 @@ int openusb(struct udev_device* dev, short vendor, short product){ else snprintf(kb->profile.serial, SERIAL_LEN, "%04x:%04x-NoID", vendor, product); printf("Connecting %s (S/N: %s)\n", kb->name, kb->profile.serial); - // If the keyboard is a non-RGB model, the FW version needs to be copied here too - if(!IS_RGB(vendor, product)){ - const char* firmware = udev_device_get_sysattr_value(dev, "bcdDevice"); - if(firmware) - // Scan the version number as a hex constant for consistency with RGB devices - sscanf(firmware, "%hx", &kb->fwversion); - else - kb->fwversion = 0; - } + // Copy firmware version (needed to determine USB protocol) + const char* firmware = udev_device_get_sysattr_value(dev, "bcdDevice"); + if(firmware) + sscanf(firmware, "%hx", &kb->fwversion); + else + kb->fwversion = 0; // Claim the USB interfaces if(usbclaim(kb, IS_RGB(vendor, product))){ diff --git a/src/ckb-daemon/usb_mac.c b/src/ckb-daemon/usb_mac.c index b174726..82c22e0 100644 --- a/src/ckb-daemon/usb_mac.c +++ b/src/ckb-daemon/usb_mac.c @@ -32,7 +32,7 @@ int _usbinput(usbdevice* kb, uchar* message, const char* file, int line){ CFIndex length = MSG_SIZE; IOReturn res = IOHIDDeviceGetReport(kb->handle, kIOHIDReportTypeFeature, 0, message, &length); kb->lastError = res; - if(res != kIOReturnSuccess){ + if(res != kIOReturnSuccess && res != 0xe0004051){ // Can't find e0004051 documented, but it seems to be a harmless error, so ignore it. printf("usbinput (%s:%d): Got return value 0x%x\n", file, line, res); return 0; } @@ -59,7 +59,7 @@ void closehandle(usbdevice* kb){ int os_resetusb(usbdevice* kb, const char* file, int line){ // Don't try if the keyboard was disconnected - if(kb->lastError == kIOReturnBadArgument) + if(kb->lastError == kIOReturnBadArgument || kb->lastError == kIOReturnNotOpen) return -2; // This does more harm than good... //if(!IOHIDDeviceSetProperty(kb->handle, CFSTR(kIOHIDResetKey), kCFBooleanTrue)) @@ -124,10 +124,7 @@ void reportcallback(void* context, IOReturn result, void* sender, IOHIDReportTyp } void openusb(usbdevice* kb, short vendor, short product){ - // The driver sometimes isn't completely ready yet, so give it a short delay - sleep(1); - - kb->lastkeypress = -1; + kb->lastkeypress = KEY_NONE; if(IS_RGB(vendor, product)) // Handle 3 is the control handle kb->handle = kb->handles[3]; @@ -238,10 +235,12 @@ void usbadd(void* context, IOReturn result, void* sender, IOHIDDeviceRef device) || (input == 4 && output == 0 && feature == 0)) kb->handles[1] = device; // Handle 2 is for Corsair inputs, unused on non-RGB - else if((input == 64 || input == 15) && output == 0 && feature == 0) + else if(((input == 64 || input == 15) && output == 0 && feature == 0) + || (input == 64 && output == 64 && feature == 0)) // FW >= 1.20 kb->handles[2] = device; // Handle 3 is for controlling the device (only exists for RGB) - else if(input == 0 && output == 0 && feature == 64) + else if((input == 0 && output == 0 && feature == 64) + || (input == 64 && output == 64 && feature == 64)) // FW >= 1.20 kb->handles[3] = device; else printf("Warning: Got unknown handle (I: %d, O: %d, F: %d)\n", (int)input, (int)output, (int)feature);