mirror of
https://github.com/ckb-next/ckb-next.git
synced 2026-10-10 12:27:21 -04:00
Fix daemon delay handling
While the previous fps delay calculations were correct, they did not take into account the delay of each usb transfer. This resulted in lower framerates and rgb frames being queued and dropped. Since the daemon now supports many more device types and protocols, the fps command handling was moved to the device vtable so that each protocol can set better suited delays. For example bragi doesn't need any.
This commit is contained in:
+11
-22
@@ -82,7 +82,7 @@ static inline long timespec_diff_ns (struct timespec* a, struct timespec* b){
|
||||
int readcmd(usbdevice* kb, char* line){
|
||||
#ifdef FPS_COUNTER
|
||||
// workaround for being able to check if an rgb command was issued
|
||||
bool had_rgb = false;
|
||||
int rgb_cmd_count = 0;
|
||||
#endif
|
||||
const devcmd* vt = &kb->vtable;
|
||||
usbprofile* profile = kb->profile;
|
||||
@@ -100,6 +100,10 @@ int readcmd(usbdevice* kb, char* line){
|
||||
for(int i = 0; i < CMD_COUNT - 1; i++){
|
||||
if(!strcmp(word, cmd_strings[i])){
|
||||
command = i + CMD_FIRST;
|
||||
#ifdef FPS_COUNTER
|
||||
if(command == RGB)
|
||||
rgb_cmd_count++;
|
||||
#endif
|
||||
#ifndef OS_MAC
|
||||
// Layout and mouse acceleration aren't used on Linux; ignore
|
||||
if(command == LAYOUT || command == ACCEL || command == SCROLLSPEED)
|
||||
@@ -186,18 +190,9 @@ int readcmd(usbdevice* kb, char* line){
|
||||
continue;
|
||||
}
|
||||
case FPS: {
|
||||
// USB command delay (2 - 10ms)
|
||||
uint framerate;
|
||||
if(sscanf(word, "%u", &framerate) == 1 && framerate > 0){
|
||||
// Not all devices require the same number of messages per frame; select delay appropriately
|
||||
uint per_frame = IS_MOUSE_DEV(kb) ? 2 : IS_FULLRANGE(kb) ? 14 : 5;
|
||||
uint delay = 1000 / framerate / per_frame;
|
||||
if(delay < 2)
|
||||
delay = 2;
|
||||
else if(delay > 10)
|
||||
delay = 10;
|
||||
kb->usbdelay = delay;
|
||||
}
|
||||
int framerate;
|
||||
if(sscanf(word, "%d", &framerate) == 1 && framerate >= 5)
|
||||
vt->setfps(kb, framerate);
|
||||
continue;
|
||||
}
|
||||
case DITHER: {
|
||||
@@ -245,15 +240,10 @@ int readcmd(usbdevice* kb, char* line){
|
||||
}
|
||||
continue;
|
||||
case HWLOAD: case HWSAVE:{
|
||||
char delay = kb->usbdelay;
|
||||
// Ensure delay of at least 10ms as the device can get overwhelmed otherwise
|
||||
if(delay < 10)
|
||||
kb->usbdelay = 10;
|
||||
// Try to load/save the hardware profile. Reset on failure, disconnect if reset fails.
|
||||
TRY_WITH_RESET(vt->do_io[command](kb, mode, notifynumber, 1, 0));
|
||||
// Re-send the current RGB state as it sometimes gets scrambled
|
||||
TRY_WITH_RESET(vt->updatergb(kb, 1));
|
||||
kb->usbdelay = delay;
|
||||
continue;
|
||||
}
|
||||
case FWUPDATE:
|
||||
@@ -312,9 +302,6 @@ int readcmd(usbdevice* kb, char* line){
|
||||
vt->rgb(kb, mode, -1, i, word);
|
||||
continue;
|
||||
}
|
||||
#ifdef FPS_COUNTER
|
||||
had_rgb = true;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case MACRO:
|
||||
@@ -374,12 +361,14 @@ int readcmd(usbdevice* kb, char* line){
|
||||
memset(kb->encounteredleds, 0, sizeof(kb->encounteredleds));
|
||||
#endif
|
||||
#ifdef FPS_COUNTER
|
||||
if(had_rgb){
|
||||
if(rgb_cmd_count){
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
const long int diff = timespec_diff_ns(&now, &kb->last_rgb);
|
||||
ckb_info("ckb%d: FPS %f", INDEX_OF(kb, keyboard), 1.f / (diff / 1000000000.f));
|
||||
memcpy(&kb->last_rgb, &now, sizeof(struct timespec));
|
||||
if(rgb_cmd_count > 1)
|
||||
ckb_warn("ckb%d: RGB loop behind by %d commands", INDEX_OF(kb, keyboard), rgb_cmd_count - 1);
|
||||
}
|
||||
#endif
|
||||
TRY_WITH_RESET(vt->updatedpi(kb, 0));
|
||||
|
||||
@@ -5,6 +5,12 @@ typedef struct usbdevice_ usbdevice;
|
||||
typedef struct usbmode_ usbmode;
|
||||
typedef enum pollrate_ pollrate_t;
|
||||
|
||||
typedef enum {
|
||||
DELAY_SEND,
|
||||
DELAY_RECV,
|
||||
DELAY_INDICATORS,
|
||||
} delay_type_t;
|
||||
|
||||
// Command operations
|
||||
typedef enum {
|
||||
// Special - handled by readcmd, no device functions
|
||||
@@ -75,7 +81,6 @@ typedef void (*cmdhandler)(usbdevice* kb, usbmode* modeidx, int notifyidx, int k
|
||||
typedef int (*cmdhandler_io)(usbdevice* kb, usbmode* modeidx, int notifyidx, int keyindex, const char* parameter); // Command with hardware I/O - returns zero on success
|
||||
typedef void (*cmdhandler_mac)(usbdevice* kb, usbmode* modeidx, int notifyidx, const char* keys, const char* assignment); // Macro command has a different left-side handler
|
||||
typedef int (*device_io)(usbdevice* kb, void* ptr, int len, int is_recv, const char* file, int line);
|
||||
typedef int (*cmdhandler_poll)(usbdevice* kb, pollrate_t rate);
|
||||
typedef union devcmd {
|
||||
// Commands can be accessed by name or by position
|
||||
cmdhandler do_cmd[CMD_DEV_COUNT];
|
||||
@@ -88,7 +93,7 @@ typedef union devcmd {
|
||||
// firmware.h
|
||||
cmdhandler_io fwupdate;
|
||||
// device.h
|
||||
cmdhandler_poll pollrate;
|
||||
int (*pollrate)(usbdevice* kb, pollrate_t rate);
|
||||
|
||||
// device.h
|
||||
cmdhandler_io active;
|
||||
@@ -150,6 +155,8 @@ typedef union devcmd {
|
||||
device_io read;
|
||||
|
||||
void (*get_battery_info)(usbdevice* kb);
|
||||
void (*delay)(usbdevice* kb, delay_type_t type);
|
||||
void (*setfps)(usbdevice* kb, int fps);
|
||||
};
|
||||
} devcmd;
|
||||
|
||||
|
||||
+24
-3
@@ -219,12 +219,12 @@ int _start_dev(usbdevice* kb, int makeactive){
|
||||
}
|
||||
|
||||
int start_dev(usbdevice* kb, int makeactive){
|
||||
// Force USB interval to 10ms during initial setup phase; return to nominal 5ms after setup completes.
|
||||
kb->usbdelay = 10;
|
||||
// Force USB interval to 10ms during initial setup phase; assume 30FPS afterwards.
|
||||
kb->usbdelay_ns = 10000000L;
|
||||
kb->maxpollrate = POLLRATE_1MS;
|
||||
kb->pollrate = POLLRATE_UNKNOWN;
|
||||
int res = _start_dev(kb, makeactive);
|
||||
kb->usbdelay = USB_DELAY_DEFAULT;
|
||||
kb->vtable.setfps(kb, 30);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -280,3 +280,24 @@ void nxp_get_battery_info(usbdevice* kb){
|
||||
kb->battery_level = nxp_battery_lut[in[4]];
|
||||
kb->battery_status = in[5];
|
||||
}
|
||||
|
||||
void nxp_delay(usbdevice* kb, delay_type_t type){
|
||||
long delay;
|
||||
switch(type){
|
||||
case DELAY_SEND:
|
||||
delay = kb->usbdelay_ns;
|
||||
break;
|
||||
case DELAY_RECV:
|
||||
if(kb->fwversion >= 0x120 || IS_V2_OVERRIDE(kb))
|
||||
return;
|
||||
delay = kb->usbdelay_ns * 10;
|
||||
break;
|
||||
case DELAY_INDICATORS:
|
||||
delay = kb->usbdelay_ns;
|
||||
break;
|
||||
default:
|
||||
ckb_err("Invalid delay type %d", type);
|
||||
delay = 5000000L;
|
||||
}
|
||||
clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec) {.tv_nsec = delay}, NULL);
|
||||
}
|
||||
|
||||
@@ -109,6 +109,13 @@ void clear_input_and_rgb(usbdevice* kb, const int active);
|
||||
void nxp_get_battery_info(usbdevice* kb);
|
||||
void bragi_get_battery_info(usbdevice* kb);
|
||||
|
||||
void legacy_delay(usbdevice* kb, delay_type_t type);
|
||||
void nxp_delay(usbdevice* kb, delay_type_t type);
|
||||
void bragi_delay(usbdevice* kb, delay_type_t type);
|
||||
|
||||
void nxp_mouse_setfps(usbdevice* kb, int fps);
|
||||
void nxp_kb_setfps(usbdevice* kb, int fps);
|
||||
|
||||
// Per-key input settings for device setup
|
||||
// The upper nybble controls input mode. 0x80 generates a normal HID interrupt, 0x40 generates a proprietary interrupt. 0xc0 generates both.
|
||||
// The exceptions are the proprietary Corsair keys, which only report HID input in BIOS mode and only report Corsair input in non-BIOS mode.
|
||||
|
||||
@@ -98,7 +98,6 @@ static inline uint32_t bragi_fwver_bswap(uint32_t fwv){
|
||||
}
|
||||
|
||||
static int start_bragi_common(usbdevice* kb){
|
||||
kb->usbdelay = 10; // This might not be needed, but won't harm
|
||||
kb->pollrate = POLLRATE_UNKNOWN;
|
||||
// Assume 1 ms unless told otherwise
|
||||
kb->maxpollrate = POLLRATE_1MS;
|
||||
@@ -147,8 +146,6 @@ static int start_bragi_common(usbdevice* kb){
|
||||
kb->features |= FEAT_ADJRATE;
|
||||
kb->features &= ~FEAT_HWLOAD;
|
||||
|
||||
kb->usbdelay = USB_DELAY_DEFAULT;
|
||||
|
||||
// Check if the device supports fine or coarse brightness
|
||||
if(bragi_get_property(kb, BRAGI_BRIGHTNESS) >= 0)
|
||||
kb->brightness_mode = BRIGHTNESS_HARDWARE_FINE;
|
||||
@@ -277,3 +274,9 @@ void bragi_get_battery_info(usbdevice* kb){
|
||||
kb->battery_level = chg / 10;
|
||||
kb->battery_status = stat;
|
||||
}
|
||||
|
||||
void bragi_delay(usbdevice* kb, delay_type_t type){
|
||||
// Don't bother with delays in bragi.
|
||||
// Since we use usbrecv for everything, the devices tell us when they are ready to handle another packet.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ int setactive_kb(usbdevice* kb, int active){
|
||||
msg[0][2] = MODE_SOFTWARE;
|
||||
if(!usbsend(kb, msg[0], MSG_SIZE, 1))
|
||||
return -1;
|
||||
DELAY_MEDIUM(kb);
|
||||
DELAY_30MS();
|
||||
// Set input mode on the keys. They must be grouped into packets of 60 bytes (+ 4 bytes header)
|
||||
// Keys are referenced in byte pairs, with the first byte representing the key and the second byte representing the mode.
|
||||
for(int k = 0; k < N_KEYS_HW; ){
|
||||
@@ -57,16 +57,16 @@ int setactive_kb(usbdevice* kb, int active){
|
||||
// Commit new input settings
|
||||
if(!usbsend(kb, msg[2], MSG_SIZE, 1))
|
||||
return -1;
|
||||
DELAY_MEDIUM(kb);
|
||||
DELAY_30MS();
|
||||
} else {
|
||||
// Set the M-keys back into hardware mode, restore hardware RGB profile. It has to be sent twice for some reason.
|
||||
msg[0][2] = MODE_HARDWARE;
|
||||
if(!usbsend(kb, msg[0], MSG_SIZE, 1))
|
||||
return -1;
|
||||
DELAY_MEDIUM(kb);
|
||||
DELAY_30MS();
|
||||
if(!usbsend(kb, msg[0], MSG_SIZE, 1))
|
||||
return -1;
|
||||
DELAY_MEDIUM(kb);
|
||||
DELAY_30MS();
|
||||
#ifdef OS_LINUX
|
||||
// On OSX the default key mappings are fine. On Linux, the G keys will freeze the keyboard. Set the keyboard entirely to HID input.
|
||||
for(int k = 0; k < N_KEYS_HW; ){
|
||||
@@ -99,7 +99,7 @@ int setactive_kb(usbdevice* kb, int active){
|
||||
// Commit new input settings
|
||||
if(!usbsend(kb, msg[2], MSG_SIZE, 1))
|
||||
return -1;
|
||||
DELAY_MEDIUM(kb);
|
||||
DELAY_30MS();
|
||||
#endif
|
||||
}
|
||||
// Update indicator LEDs if the profile contains settings for them
|
||||
@@ -125,7 +125,7 @@ int cmd_idle_kb(usbdevice* kb, usbmode* dummy1, int dummy2, int dummy3, const ch
|
||||
return setactive_kb(kb, 0);
|
||||
}
|
||||
|
||||
void setmodeindex_legacy(usbdevice *kb, int index){
|
||||
void setmodeindex_legacy(usbdevice* kb, int index){
|
||||
switch(index % 3){
|
||||
case 0:
|
||||
nk95cmd(kb, NK95_M1);
|
||||
@@ -138,3 +138,53 @@ void setmodeindex_legacy(usbdevice *kb, int index){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct timespec legacy_delay_ts = {.tv_nsec = 30000000000};
|
||||
void legacy_delay(usbdevice* kb, delay_type_t type){
|
||||
// Fixed 30ms delay should be fine
|
||||
clock_nanosleep(CLOCK_MONOTONIC, 0, &legacy_delay_ts, NULL);
|
||||
}
|
||||
|
||||
void nxp_kb_setfps(usbdevice* kb, int fps){
|
||||
// This is all guesswork
|
||||
//fullrange:
|
||||
// 750us at 60Hz
|
||||
// 800us at 55Hz
|
||||
// 830us at 50Hz
|
||||
// 830us at 45Hz
|
||||
// 1150us at 40Hz
|
||||
// 1500us at 35Hz
|
||||
// 1800us at 30Hz
|
||||
|
||||
if(IS_FULLRANGE(kb)){
|
||||
// Not sure if it's worth handling IS_MONOCHROME_DEV(kb)
|
||||
|
||||
// The keyboards get really sensitive between 40~45FPS.
|
||||
// 1150us is fine for 40FPS but 900us is not enough for 41FPS
|
||||
if(fps >= 55)
|
||||
kb->usbdelay_ns = 750000L;
|
||||
else if(fps >= 47)
|
||||
kb->usbdelay_ns = 800000L;
|
||||
else if(fps > 40)
|
||||
kb->usbdelay_ns = 850000L;
|
||||
else if(fps > 35)
|
||||
kb->usbdelay_ns = 1150000L;
|
||||
else if(fps > 30)
|
||||
kb->usbdelay_ns = 1500000L;
|
||||
else if(fps > 25)
|
||||
kb->usbdelay_ns = 1800000L;
|
||||
else
|
||||
kb->usbdelay_ns = 2500000L;
|
||||
} else {
|
||||
if(fps >= 50)
|
||||
kb->usbdelay_ns = 2650000L;
|
||||
else if(fps >= 40)
|
||||
kb->usbdelay_ns = 2800000L;
|
||||
else if(fps >= 30)
|
||||
kb->usbdelay_ns = 3700000L;
|
||||
else if(fps >= 15)
|
||||
kb->usbdelay_ns = 5500000L;
|
||||
else
|
||||
kb->usbdelay_ns = 10000000L;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,3 +104,11 @@ int cmd_pollrate(usbdevice* kb, pollrate_t rate){
|
||||
kb->pollrate = rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nxp_mouse_setfps(usbdevice* kb, int fps){
|
||||
// Assumes 2 packets at most
|
||||
if(fps > 35)
|
||||
kb->usbdelay_ns = 6000000L;
|
||||
else
|
||||
kb->usbdelay_ns = 10000000L;
|
||||
}
|
||||
|
||||
@@ -115,6 +115,8 @@ const devcmd vtable_keyboard = {
|
||||
.write = nxp_usb_write,
|
||||
.read = nxp_usb_read,
|
||||
.get_battery_info = int0_void_none,
|
||||
.delay = nxp_delay,
|
||||
.setfps = nxp_kb_setfps,
|
||||
};
|
||||
|
||||
const devcmd vtable_keyboard_wireless = {
|
||||
@@ -166,6 +168,8 @@ const devcmd vtable_keyboard_wireless = {
|
||||
.write = nxp_usb_write,
|
||||
.read = nxp_usb_read,
|
||||
.get_battery_info = nxp_get_battery_info,
|
||||
.delay = nxp_delay,
|
||||
.setfps = nxp_kb_setfps,
|
||||
};
|
||||
|
||||
// Legacy keyboard vtable (K70)
|
||||
@@ -218,6 +222,8 @@ const devcmd vtable_keyboard_legacy = {
|
||||
.write = legacy_dev_io,
|
||||
.read = legacy_dev_io,
|
||||
.get_battery_info = int0_void_none,
|
||||
.delay = legacy_delay,
|
||||
.setfps = int1_void_none, // Legacy devices have a fixed delay
|
||||
};
|
||||
|
||||
// RGB mouse vtable
|
||||
@@ -270,6 +276,8 @@ const devcmd vtable_mouse = {
|
||||
.write = nxp_usb_write,
|
||||
.read = nxp_usb_read,
|
||||
.get_battery_info = int0_void_none,
|
||||
.delay = nxp_delay,
|
||||
.setfps = nxp_mouse_setfps,
|
||||
};
|
||||
|
||||
const devcmd vtable_mouse_wireless = {
|
||||
@@ -321,6 +329,8 @@ const devcmd vtable_mouse_wireless = {
|
||||
.write = nxp_usb_write,
|
||||
.read = nxp_usb_read,
|
||||
.get_battery_info = nxp_get_battery_info,
|
||||
.delay = nxp_delay,
|
||||
.setfps = nxp_mouse_setfps,
|
||||
};
|
||||
|
||||
// RGB Mousepad vtable
|
||||
@@ -373,6 +383,8 @@ const devcmd vtable_mousepad = {
|
||||
.write = nxp_usb_write,
|
||||
.read = nxp_usb_read,
|
||||
.get_battery_info = int0_void_none,
|
||||
.delay = nxp_delay,
|
||||
.setfps = nxp_mouse_setfps,
|
||||
};
|
||||
|
||||
// Legacy mouse vtable
|
||||
@@ -425,6 +437,8 @@ const devcmd vtable_mouse_legacy = {
|
||||
.write = legacy_dev_io,
|
||||
.read = legacy_dev_io,
|
||||
.get_battery_info = int0_void_none,
|
||||
.delay = legacy_delay,
|
||||
.setfps = int1_void_none, // Legacy devices have a fixed delay
|
||||
};
|
||||
|
||||
// Bragi vtables
|
||||
@@ -476,6 +490,8 @@ const devcmd vtable_bragi_mouse = {
|
||||
.write = bragi_usb_write,
|
||||
.read = bragi_usb_read,
|
||||
.get_battery_info = bragi_get_battery_info,
|
||||
.delay = bragi_delay,
|
||||
.setfps = int1_void_none, // Bragi devices respond to everything, so no need for delays
|
||||
};
|
||||
|
||||
const devcmd vtable_bragi_keyboard = {
|
||||
@@ -526,6 +542,8 @@ const devcmd vtable_bragi_keyboard = {
|
||||
.write = bragi_usb_write,
|
||||
.read = bragi_usb_read,
|
||||
.get_battery_info = bragi_get_battery_info,
|
||||
.delay = bragi_delay,
|
||||
.setfps = int1_void_none, // Bragi devices respond to everything, so no need for delays
|
||||
};
|
||||
|
||||
const devcmd vtable_bragi_dongle = {
|
||||
@@ -576,4 +594,6 @@ const devcmd vtable_bragi_dongle = {
|
||||
.write = bragi_usb_write,
|
||||
.read = bragi_usb_read,
|
||||
.get_battery_info = int0_void_none,
|
||||
.delay = bragi_delay,
|
||||
.setfps = int1_void_none, // Bragi devices respond to everything, so no need for delays
|
||||
};
|
||||
|
||||
+15
-19
@@ -135,24 +135,27 @@ int getfwversion(usbdevice* kb){
|
||||
}
|
||||
|
||||
#define FW_MAXSIZE (255 * 256)
|
||||
#define FWUPDATE_RET(r) { kb->usbdelay_ns = delay; free(fwdata); return r; }
|
||||
|
||||
// Updates the device's firmware with the specified file. Returns one of the FW_ constants.
|
||||
// Lock the keyboard's main mutex before calling this and unlock it when done.
|
||||
int fwupdate(usbdevice* kb, const char* path, int nnumber){
|
||||
// Force the device to 10ms delay (we need to deliver packets very slowly to make sure it doesn't get overwhelmed)
|
||||
long delay = kb->usbdelay_ns;
|
||||
kb->usbdelay_ns = 10000000L;
|
||||
|
||||
// Read the firmware from the given path
|
||||
char* fwdata = calloc(1, FW_MAXSIZE + 256);
|
||||
int fd = open(path, O_RDONLY);
|
||||
if(fd == -1){
|
||||
ckb_err("Failed to open firmware file %s: %s", path, strerror(errno));
|
||||
free(fwdata);
|
||||
return FW_NOFILE;
|
||||
FWUPDATE_RET(FW_NOFILE);
|
||||
}
|
||||
ssize_t length = read(fd, fwdata, FW_MAXSIZE + 1);
|
||||
if(length <= 0x108 || length > FW_MAXSIZE){
|
||||
ckb_err("Failed to read firmware file %s: %s", path, length <= 0 ? strerror(errno) : "Wrong size");
|
||||
close(fd);
|
||||
free(fwdata);
|
||||
return FW_NOFILE;
|
||||
FWUPDATE_RET(FW_NOFILE);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
@@ -164,13 +167,11 @@ int fwupdate(usbdevice* kb, const char* path, int nnumber){
|
||||
// Check against the actual device
|
||||
if(vendor != kb->vendor || product != kb->product){
|
||||
ckb_err("Firmware file %s doesn't match device (V: %04x P: %04x)", path, vendor, product);
|
||||
free(fwdata);
|
||||
return FW_WRONGDEV;
|
||||
FWUPDATE_RET(FW_WRONGDEV);
|
||||
}
|
||||
ckb_info("Loading firmware version %04x from %s", version, path);
|
||||
nprintf(kb, nnumber, 0, "fwupdate %s 0/%d\n", path, (int)length);
|
||||
// Force the device to 10ms delay (we need to deliver packets very slowly to make sure it doesn't get overwhelmed)
|
||||
kb->usbdelay = 10;
|
||||
|
||||
// Send the firmware messages (256 bytes at a time)
|
||||
uchar data_pkt[7][MSG_SIZE] = {
|
||||
{ CMD_SET, FIELD_FW_START, 0xf0, 0x01, 0 },
|
||||
@@ -205,15 +206,13 @@ int fwupdate(usbdevice* kb, const char* path, int nnumber){
|
||||
if(index == 1){
|
||||
if(!usbsend(kb, data_pkt[0], MSG_SIZE, 1)){
|
||||
ckb_err("Firmware update failed");
|
||||
free(fwdata);
|
||||
return FW_USBFAIL;
|
||||
FWUPDATE_RET(FW_USBFAIL);
|
||||
}
|
||||
// The above packet can take a lot longer to process, so wait for a while
|
||||
sleep(3);
|
||||
clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec) {.tv_sec = 3}, NULL);
|
||||
if(!usbsend(kb, data_pkt[2], MSG_SIZE, npackets - 1)){
|
||||
ckb_err("Firmware update failed");
|
||||
free(fwdata);
|
||||
return FW_USBFAIL;
|
||||
FWUPDATE_RET(FW_USBFAIL);
|
||||
}
|
||||
} else {
|
||||
// If the output ends here, set the length byte appropriately
|
||||
@@ -221,8 +220,7 @@ int fwupdate(usbdevice* kb, const char* path, int nnumber){
|
||||
data_pkt[npackets][2] = length - last;
|
||||
if(!usbsend(kb, data_pkt[1], MSG_SIZE, npackets)){
|
||||
ckb_err("Firmware update failed");
|
||||
free(fwdata);
|
||||
return FW_USBFAIL;
|
||||
FWUPDATE_RET(FW_USBFAIL);
|
||||
}
|
||||
}
|
||||
nprintf(kb, nnumber, 0, "fwupdate %s %d/%d\n", path, output, (int)length);
|
||||
@@ -234,15 +232,13 @@ int fwupdate(usbdevice* kb, const char* path, int nnumber){
|
||||
};
|
||||
if(!usbsend(kb, data_pkt2[0], MSG_SIZE, 2)){
|
||||
ckb_err("Firmware update failed");
|
||||
free(fwdata);
|
||||
return FW_USBFAIL;
|
||||
FWUPDATE_RET(FW_USBFAIL);
|
||||
}
|
||||
// Updated successfully
|
||||
kb->fwversion = version;
|
||||
mkfwnode(kb);
|
||||
ckb_info("Firmware update complete");
|
||||
free(fwdata);
|
||||
return FW_OK;
|
||||
FWUPDATE_RET(FW_OK);
|
||||
}
|
||||
|
||||
int cmd_fwupdate(usbdevice* kb, usbmode* dummy1, int nnumber, int dummy2, const char* path){
|
||||
|
||||
+1
-1
@@ -515,7 +515,7 @@ void updateindicators_kb(usbdevice* kb, int force){
|
||||
kb->ileds = new;
|
||||
kb->hw_ileds_old = hw_new;
|
||||
if(old != new || force){
|
||||
DELAY_SHORT(kb);
|
||||
kb->vtable.delay(kb, DELAY_INDICATORS);
|
||||
|
||||
ushort leds = kb->ileds;
|
||||
int len = 1;
|
||||
|
||||
@@ -13,12 +13,17 @@ static int hwloadmode(usbdevice* kb, hwprofile* hw, int mode){
|
||||
return loadrgb_kb(kb, hw->light + mode, mode);
|
||||
}
|
||||
|
||||
#define HWLOAD_ERR_RET() { kb->usbdelay_ns = delay; free(hw); return -1; }
|
||||
int cmd_hwload_kb(usbdevice* kb, usbmode* dummy1, int dummy2, int apply, const char* dummy3){
|
||||
(void)dummy1;
|
||||
(void)dummy2;
|
||||
(void)dummy3;
|
||||
|
||||
DELAY_LONG(kb);
|
||||
long delay = kb->usbdelay_ns;
|
||||
// Ensure delay of 10ms as the device can get overwhelmed otherwise
|
||||
kb->usbdelay_ns = 10000000L;
|
||||
|
||||
DELAY_100MS();
|
||||
hwprofile* hw = calloc(1, sizeof(hwprofile));
|
||||
// Ask for profile and mode IDs
|
||||
uchar data_pkt[2][MSG_SIZE] = {
|
||||
@@ -29,24 +34,18 @@ int cmd_hwload_kb(usbdevice* kb, usbmode* dummy1, int dummy2, int apply, const c
|
||||
int modes = (IS_K95(kb) ? HWMODE_K95 : HWMODE_K70);
|
||||
for(int i = 0; i <= modes; i++){
|
||||
data_pkt[0][3] = i;
|
||||
if(!usbrecv(kb, data_pkt[0], MSG_SIZE, in_pkt)){
|
||||
free(hw);
|
||||
return -1;
|
||||
}
|
||||
if(!usbrecv(kb, data_pkt[0], MSG_SIZE, in_pkt))
|
||||
HWLOAD_ERR_RET();
|
||||
memcpy(hw->id + i, in_pkt + 4, sizeof(usbid));
|
||||
}
|
||||
// Ask for profile name
|
||||
if(!usbrecv(kb, data_pkt[1], MSG_SIZE, in_pkt)){
|
||||
free(hw);
|
||||
return -1;
|
||||
}
|
||||
if(!usbrecv(kb, data_pkt[1], MSG_SIZE, in_pkt))
|
||||
HWLOAD_ERR_RET();
|
||||
memcpy(hw->name[0], in_pkt + 4, PR_NAME_LEN * 2);
|
||||
// Load modes
|
||||
for(int i = 0; i < modes; i++){
|
||||
if(hwloadmode(kb, hw, i)){
|
||||
free(hw);
|
||||
return -1;
|
||||
}
|
||||
if(hwloadmode(kb, hw, i))
|
||||
HWLOAD_ERR_RET();
|
||||
}
|
||||
// Make the profile active (if requested)
|
||||
if(apply)
|
||||
@@ -54,17 +53,23 @@ int cmd_hwload_kb(usbdevice* kb, usbmode* dummy1, int dummy2, int apply, const c
|
||||
// Free the existing profile (if any)
|
||||
free(kb->hw);
|
||||
kb->hw = hw;
|
||||
DELAY_LONG(kb);
|
||||
DELAY_100MS();
|
||||
kb->usbdelay_ns = delay;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define HWSAVE_RET(e) { kb->usbdelay_ns = delay; return e; }
|
||||
int cmd_hwsave_kb(usbdevice* kb, usbmode* dummy1, int dummy2, int dummy3, const char* dummy4){
|
||||
(void)dummy1;
|
||||
(void)dummy2;
|
||||
(void)dummy3;
|
||||
(void)dummy4;
|
||||
|
||||
DELAY_LONG(kb);
|
||||
long delay = kb->usbdelay_ns;
|
||||
// Ensure delay of 10ms as the device can get overwhelmed otherwise
|
||||
kb->usbdelay_ns = 10000000L;
|
||||
|
||||
DELAY_100MS();
|
||||
hwprofile* hw = kb->hw;
|
||||
if(!hw)
|
||||
hw = kb->hw = calloc(1, sizeof(hwprofile));
|
||||
@@ -80,20 +85,20 @@ int cmd_hwsave_kb(usbdevice* kb, usbmode* dummy1, int dummy2, int dummy3, const
|
||||
data_pkt[0][3] = i;
|
||||
memcpy(data_pkt[0] + 4, hw->name[i], MD_NAME_LEN * 2);
|
||||
if(!usbsend(kb, data_pkt[0], MSG_SIZE, 1))
|
||||
return -1;
|
||||
HWSAVE_RET(-1);
|
||||
}
|
||||
// Save the IDs
|
||||
for(int i = 0; i <= modes; i++){
|
||||
data_pkt[1][3] = i;
|
||||
memcpy(data_pkt[1] + 4, hw->id + i, sizeof(usbid));
|
||||
if(!usbsend(kb, data_pkt[1], MSG_SIZE, 1))
|
||||
return -1;
|
||||
HWSAVE_RET(-1);
|
||||
}
|
||||
// Save the RGB data
|
||||
for(int i = 0; i < modes; i++){
|
||||
if(savergb_kb(kb, hw->light + i, i))
|
||||
return -1;
|
||||
HWSAVE_RET(-1);
|
||||
}
|
||||
DELAY_LONG(kb);
|
||||
return 0;
|
||||
DELAY_100MS();
|
||||
HWSAVE_RET(0);
|
||||
}
|
||||
|
||||
+28
-21
@@ -4,12 +4,17 @@
|
||||
#include "led.h"
|
||||
#include "nxp_proto.h"
|
||||
|
||||
#define HWLOAD_ERR_RET() { kb->usbdelay_ns = delay; free(hw); return -1; }
|
||||
int cmd_hwload_mouse(usbdevice* kb, usbmode* dummy1, int dummy2, int apply, const char* dummy3){
|
||||
(void)dummy1;
|
||||
(void)dummy2;
|
||||
(void)dummy3;
|
||||
|
||||
DELAY_LONG(kb);
|
||||
long delay = kb->usbdelay_ns;
|
||||
// Ensure delay of 10ms as the device can get overwhelmed otherwise
|
||||
kb->usbdelay_ns = 10000000L;
|
||||
|
||||
DELAY_100MS();
|
||||
hwprofile* hw = calloc(1, sizeof(hwprofile));
|
||||
// Ask for profile and mode IDs
|
||||
uchar data_pkt[2][MSG_SIZE] = {
|
||||
@@ -19,46 +24,48 @@ int cmd_hwload_mouse(usbdevice* kb, usbmode* dummy1, int dummy2, int apply, cons
|
||||
uchar in_pkt[MSG_SIZE];
|
||||
for(int i = 0; i <= 1; i++){
|
||||
data_pkt[0][3] = i;
|
||||
if(!usbrecv(kb, data_pkt[0], MSG_SIZE, in_pkt)){
|
||||
free(hw);
|
||||
return -1;
|
||||
}
|
||||
if(!usbrecv(kb, data_pkt[0], MSG_SIZE, in_pkt))
|
||||
HWLOAD_ERR_RET();
|
||||
|
||||
memcpy(hw->id + i, in_pkt + 4, sizeof(usbid));
|
||||
}
|
||||
// Ask for profile and mode names
|
||||
for(int i = 0; i <= 1; i++){
|
||||
data_pkt[1][3] = i;
|
||||
if(!usbrecv(kb, data_pkt[1], MSG_SIZE, in_pkt)){
|
||||
free(hw);
|
||||
return -1;
|
||||
}
|
||||
if(!usbrecv(kb, data_pkt[1], MSG_SIZE, in_pkt))
|
||||
HWLOAD_ERR_RET();
|
||||
memcpy(hw->name[i], in_pkt + 4, PR_NAME_LEN * 2);
|
||||
}
|
||||
|
||||
// Load the RGB and DPI settings
|
||||
if(loadrgb_mouse(kb, hw->light, 0)
|
||||
|| loaddpi(kb, hw->dpi, hw->light)){
|
||||
free(hw);
|
||||
return -1;
|
||||
}
|
||||
|| loaddpi(kb, hw->dpi, hw->light))
|
||||
HWLOAD_ERR_RET();
|
||||
|
||||
// Make the profile active (if requested)
|
||||
if(apply)
|
||||
hwtonative(kb->profile, hw, 1);
|
||||
|
||||
// Free the existing profile (if any)
|
||||
free(kb->hw);
|
||||
kb->hw = hw;
|
||||
DELAY_LONG(kb);
|
||||
DELAY_100MS();
|
||||
kb->usbdelay_ns = delay;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define HWSAVE_RET(e) { kb->usbdelay_ns = delay; return e; }
|
||||
int cmd_hwsave_mouse(usbdevice* kb, usbmode* dummy1, int dummy2, int dummy3, const char* dummy4){
|
||||
(void)dummy1;
|
||||
(void)dummy2;
|
||||
(void)dummy3;
|
||||
(void)dummy4;
|
||||
|
||||
DELAY_LONG(kb);
|
||||
long delay = kb->usbdelay_ns;
|
||||
// Ensure delay of 10ms as the device can get overwhelmed otherwise
|
||||
kb->usbdelay_ns = 10000000L;
|
||||
|
||||
DELAY_100MS();
|
||||
hwprofile* hw = kb->hw;
|
||||
if(!hw)
|
||||
hw = kb->hw = calloc(1, sizeof(hwprofile));
|
||||
@@ -72,21 +79,21 @@ int cmd_hwsave_mouse(usbdevice* kb, usbmode* dummy1, int dummy2, int dummy3, con
|
||||
data_pkt[0][3] = i;
|
||||
memcpy(data_pkt[0] + 4, hw->name[i], MD_NAME_LEN * 2);
|
||||
if(!usbsend(kb, data_pkt[0], MSG_SIZE, 1))
|
||||
return -1;
|
||||
HWSAVE_RET(-1);
|
||||
}
|
||||
// Save the IDs
|
||||
for(int i = 0; i <= 1; i++){
|
||||
data_pkt[1][3] = i;
|
||||
memcpy(data_pkt[1] + 4, hw->id + i, sizeof(usbid));
|
||||
if(!usbsend(kb, data_pkt[1], MSG_SIZE, 1))
|
||||
return -1;
|
||||
HWSAVE_RET(-1);
|
||||
}
|
||||
// Save the RGB data for the non-DPI zones
|
||||
if(savergb_mouse(kb, hw->light, 0))
|
||||
return -1;
|
||||
HWSAVE_RET(-1);
|
||||
// Save the DPI data (also saves RGB for those states)
|
||||
if(savedpi(kb, hw->dpi, hw->light))
|
||||
return -1;
|
||||
DELAY_LONG(kb);
|
||||
return 0;
|
||||
HWSAVE_RET(-1);
|
||||
DELAY_100MS();
|
||||
HWSAVE_RET(0);
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ typedef struct usbdevice_ {
|
||||
// Physical device layout; LAYOUT_NONE if irrelevant, LAYOUT_UNKNOWN if unimplemented.
|
||||
uchar layout;
|
||||
// USB protocol delay (ms)
|
||||
char usbdelay;
|
||||
long usbdelay_ns;
|
||||
// Current input state
|
||||
usbinput input;
|
||||
// Indicator LED state
|
||||
|
||||
+9
-16
@@ -404,16 +404,11 @@ static void* _setupusb(void* context){
|
||||
if(kb->protocol == PROTO_BRAGI)
|
||||
kb->features &= ~FEAT_FWUPDATE;
|
||||
|
||||
kb->usbdelay = USB_DELAY_DEFAULT;
|
||||
|
||||
// Check if the device needs a patched keymap, and if so patch it.
|
||||
patchkeys(kb);
|
||||
|
||||
// Perform OS-specific setup
|
||||
///
|
||||
/// - A fixed 100ms wait is the start.
|
||||
/// <b>Although the DELAY_LONG macro is given a parameter, it is ignored. Occasionally refactor it.</b>
|
||||
DELAY_LONG(kb);
|
||||
DELAY_100MS();
|
||||
|
||||
///
|
||||
/// - The first relevant point is the operating system-specific opening of the interface in os_setupusb().
|
||||
@@ -590,11 +585,11 @@ int revertusb(usbdevice* kb){
|
||||
///
|
||||
int _resetusb(usbdevice* kb, const char* file, int line){
|
||||
// Perform a USB reset
|
||||
DELAY_LONG(kb);
|
||||
DELAY_100MS();
|
||||
int res = os_resetusb(kb, file, line);
|
||||
if(res)
|
||||
return res;
|
||||
DELAY_LONG(kb);
|
||||
DELAY_100MS();
|
||||
// Re-initialize the device
|
||||
if(kb->vtable.start(kb, kb->active) != 0)
|
||||
return -1;
|
||||
@@ -650,7 +645,7 @@ int _usbsend(usbdevice* kb, void* messages, size_t msg_len, int count, const cha
|
||||
for(int i = 0; i < count; i++){
|
||||
// Send each message via the OS function
|
||||
while(1){
|
||||
DELAY_SHORT(kb);
|
||||
kb->vtable.delay(kb, DELAY_SEND);
|
||||
queued_mutex_lock(mmutex(kb)); ///< Synchonization between macro and color information
|
||||
int res = kb->vtable.write(kb, messages + i * msg_len, msg_len, 0, file, line);
|
||||
queued_mutex_unlock(mmutex(kb));
|
||||
@@ -664,7 +659,7 @@ int _usbsend(usbdevice* kb, void* messages, size_t msg_len, int count, const cha
|
||||
if(reset_stop)
|
||||
return 0;
|
||||
// Retry as long as the result is temporary failure
|
||||
DELAY_LONG(kb);
|
||||
DELAY_100MS();
|
||||
}
|
||||
}
|
||||
return total_sent;
|
||||
@@ -678,7 +673,7 @@ int _usbrecv(usbdevice* kb, void* out_msg, size_t msg_len, uchar* in_msg, const
|
||||
for (int try = 0; try < 5; try++) {
|
||||
// Send the output message
|
||||
queued_mutex_lock(mmutex(kb)); ///< Synchonization between macro and color information
|
||||
DELAY_SHORT(kb);
|
||||
kb->vtable.delay(kb, DELAY_SEND);
|
||||
int res = kb->vtable.write(kb, out_msg, msg_len, 1, file, line);
|
||||
queued_mutex_unlock(mmutex(kb));
|
||||
if (res == 0)
|
||||
@@ -687,12 +682,11 @@ int _usbrecv(usbdevice* kb, void* out_msg, size_t msg_len, uchar* in_msg, const
|
||||
// Retry on temporary failure
|
||||
if (reset_stop)
|
||||
return 0;
|
||||
DELAY_LONG(kb);
|
||||
DELAY_100MS();
|
||||
continue;
|
||||
}
|
||||
// Wait for the response
|
||||
if(!(kb->fwversion >= 0x120 || IS_V2_OVERRIDE(kb)) && kb->protocol != PROTO_BRAGI)
|
||||
DELAY_MEDIUM(kb);
|
||||
kb->vtable.delay(kb, DELAY_RECV);
|
||||
res = kb->vtable.read(kb, in_msg, msg_len, 0, file, line);
|
||||
if(res == 0)
|
||||
return 0;
|
||||
@@ -700,8 +694,7 @@ int _usbrecv(usbdevice* kb, void* out_msg, size_t msg_len, uchar* in_msg, const
|
||||
return res;
|
||||
if(reset_stop)
|
||||
return 0;
|
||||
if(!(kb->fwversion >= 0x120 || IS_V2_OVERRIDE(kb)) && kb->protocol != PROTO_BRAGI)
|
||||
DELAY_LONG(kb);
|
||||
DELAY_100MS();
|
||||
}
|
||||
// Give up
|
||||
ckb_err_fn("Too many send/recv failures. Dropping.", file, line);
|
||||
|
||||
+5
-17
@@ -275,24 +275,12 @@ const char* product_str(ushort product);
|
||||
// Devices that use the NXP protocol and have the DPI stage RGB data in the DPI packet
|
||||
#define NXP_RGB_IN_DPI_PKT(kb) ((kb)->vendor == V_CORSAIR && ((kb)->product == P_GLAIVE_PRO || IS_DARK_CORE_NXP(kb)))
|
||||
|
||||
/// USB delays for when the keyboards get picky about timing
|
||||
/// That was the original comment, but it is used anytime.
|
||||
#define DELAY_100MS() \
|
||||
clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec) {.tv_nsec = 100000000}, NULL)
|
||||
|
||||
/// The short delay is used before any send or receive
|
||||
#define DELAY_SHORT(kb) \
|
||||
clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec) {.tv_nsec = ((int) (kb->usbdelay)) * 1000000}, NULL) // base (default: 5ms)
|
||||
|
||||
/// the medium delay is used after sending a command before waiting for the answer.
|
||||
#define DELAY_MEDIUM(kb) \
|
||||
clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec) {.tv_nsec = ((int) (kb->usbdelay)) * 10000000}, NULL) // x10 (default: 50ms)
|
||||
|
||||
/// The longest delay takes place where something went wrong (eg when resetting the device)
|
||||
#define DELAY_LONG(kb) \
|
||||
clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec) {.tv_nsec = 100000000}, NULL) // long, fixed 100ms
|
||||
|
||||
/// This constant is used to initialize \b kb->usbdelay.
|
||||
/// It is used in many places (see macros above) but often also overwritten to the fixed value of 10.
|
||||
#define USB_DELAY_DEFAULT 5
|
||||
// This is used in NXP only. Would be nice if it can be removed.
|
||||
#define DELAY_30MS() \
|
||||
clock_nanosleep(CLOCK_MONOTONIC, 0, &(struct timespec) {.tv_nsec = 30000000}, NULL)
|
||||
|
||||
// This should be removed in the future when we implement autodetection
|
||||
#define USES_BRAGI(vendor, product) ((vendor) == (V_CORSAIR) && ((product) == (P_M55_RGB_PRO) || (product) == (P_IRONCLAW_W_U) || (product) == (P_IRONCLAW_W_D) || (product) == (P_K95_PLATINUM_XT) || (product) == (P_DARK_CORE_RGB_PRO_SE) || (product) == (P_DARK_CORE_RGB_PRO_SE_WL) || (product) == P_HARPOON_WL_U || (product) == P_HARPOON_WL_D || (product) == P_K57_U || (product) == P_K57_D || (product) == P_KATAR_PRO_XT || (product) == P_KATAR_PRO || (product) == P_K60_PRO_RGB || (product) == P_K60_PRO_RGB_LP || (product) == P_K60_PRO_RGB_SE || (product) == P_K60_PRO_MONO || (product) == P_K55_PRO || (product) == P_K55_PRO_XT))
|
||||
|
||||
Reference in New Issue
Block a user