int os_resetusb(usbdevice* kb, const char* file, int line){ TEST_RESET(usbunclaim(kb, 1, HAS_FEATURES(kb, FEAT_RGB))); TEST_RESET(ioctl(kb->handle, USBDEVFS_RESET)); TEST_RESET(usbclaim(kb, HAS_FEATURES(kb, FEAT_RGB))); // Success! return 0; }
void os_closeusb(usbdevice* kb){ usbunclaim(kb, 0, HAS_FEATURES(kb, FEAT_RGB)); close(kb->handle); udev_device_unref(kb->udev); kb->handle = 0; kb->udev = 0; kbsyspath[INDEX_OF(kb, keyboard)][0] = 0; }
int revertusb(usbdevice* kb){ if(NEEDS_FW_UPDATE(kb)) return 0; if(!HAS_FEATURES(kb, FEAT_RGB)){ nk95cmd(kb, NK95_HWON); return 0; } if(setactive(kb, 0)) return -1; return 0; }
int _usbinput(usbdevice* kb, uchar* message, const char* file, int line){ if(!IS_CONNECTED(kb) || !HAS_FEATURES(kb, FEAT_RGB)) return -1; CFIndex length = MSG_SIZE; IOReturn res = IOHIDDeviceGetReport(kb->handle, kIOHIDReportTypeFeature, 0, message, &length); kb->lastError = res; if(res != kIOReturnSuccess && res != 0xe0004051){ printf("usbinput (%s:%d): Got return value 0x%x\n", file, line, res); return 0; } if(length != MSG_SIZE) printf("usbinput (%s:%d): Read %d bytes (expected %d)\n", file, line, (int)length, MSG_SIZE); return length; }
int _usbdequeue(usbdevice* kb, const char* file, int line){ if(kb->queuecount == 0 || !kb->handle || !HAS_FEATURES(kb, FEAT_RGB)) return -1; IOReturn res = IOHIDDeviceSetReport(kb->handle, kIOHIDReportTypeFeature, 0, kb->queue[0], MSG_SIZE); // Rotate queue uchar* first = kb->queue[0]; for(int i = 1; i < QUEUE_LEN; i++) kb->queue[i - 1] = kb->queue[i]; kb->queue[QUEUE_LEN - 1] = first; kb->queuecount--; kb->lastError = res; if(res != kIOReturnSuccess && res != 0xe0004051){ // Can't find e0004051 documented, but it seems to be a harmless error, so ignore it. printf("usbdequeue (%s:%d): Got return value 0x%x\n", file, line, res); return 0; } return MSG_SIZE; }
void reportcallback(void* context, IOReturn result, void* sender, IOHIDReportType reporttype, uint32_t reportid, uint8_t* data, CFIndex length){ usbdevice* kb = context; if(HAS_FEATURES(kb, FEAT_RGB)){ switch(length){ case 8: // RGB EP 1: 6KRO (BIOS mode) input hid_translate(kb->kbinput, -1, 8, kb->urbinput); inputupdate(kb); break; case 21: case 5: // RGB EP 2: NKRO (non-BIOS) input. Accept only if keyboard is inactive if(!kb->active){ hid_translate(kb->kbinput, -2, 21, kb->urbinput + 8); inputupdate(kb); } break; case MSG_SIZE: // RGB EP 3: Corsair input inputupdate(kb); break; } } else { switch(length){ case 8: // Non-RGB EP 1: 6KRO input hid_translate(kb->kbinput, 1, 8, kb->urbinput); inputupdate(kb); break; case 4: // Non-RGB EP 2: media keys hid_translate(kb->kbinput, 2, 4, kb->urbinput + 8); inputupdate(kb); break; case 15: // Non-RGB EP 3: NKRO input hid_translate(kb->kbinput, 3, 15, kb->urbinput + 8 + 4); inputupdate(kb); break; } } }
static void intreport(void* context, IOReturn result, void* sender, IOHIDReportType reporttype, uint32_t reportid, uint8_t* data, CFIndex length){ usbdevice* kb = context; pthread_mutex_lock(imutex(kb)); if(IS_MOUSE(kb->vendor, kb->product)){ if(length == 10) hid_mouse_translate(kb->input.keys, &kb->input.rel_x, &kb->input.rel_y, -2, length, data); } else if(HAS_FEATURES(kb, FEAT_RGB)){ switch(length){ case 8: // RGB EP 1: 6KRO (BIOS mode) input hid_kb_translate(kb->input.keys, -1, length, data); break; case 21: case 5: // RGB EP 2: NKRO (non-BIOS) input. Accept only if keyboard is inactive if(!kb->active) hid_kb_translate(kb->input.keys, -2, length, data); break; case MSG_SIZE: // RGB EP 3: Corsair input memcpy(kb->input.keys, data, N_KEYBYTES_KB); break; } } else { switch(length){ case 8: // Non-RGB EP 1: 6KRO input hid_kb_translate(kb->input.keys, 1, length, data); break; case 4: // Non-RGB EP 2: media keys hid_kb_translate(kb->input.keys, 2, length, data); break; case 15: // Non-RGB EP 3: NKRO input hid_kb_translate(kb->input.keys, 3, length, data); break; } } inputupdate(kb); pthread_mutex_unlock(imutex(kb)); }
int usbunclaim(usbdevice* kb, int resetting){ int handle = kb->handle - 1; int count = kb->epcount; for(int i = 0; i < count; i++) ioctl(handle, USBDEVFS_RELEASEINTERFACE, &i); // For RGB keyboards, the kernel driver should only be reconnected to interfaces 0 and 1 (HID), and only if we're not about to do a USB reset. // Reconnecting any of the others causes trouble. if(!resetting){ struct usbdevfs_ioctl ctl = { 0, USBDEVFS_CONNECT, 0 }; ioctl(handle, USBDEVFS_IOCTL, &ctl); ctl.ifno = 1; ioctl(handle, USBDEVFS_IOCTL, &ctl); // Also reconnect iface #2 (HID) for non-RGB keyboards if(!HAS_FEATURES(kb, FEAT_RGB)){ ctl.ifno = 2; ioctl(handle, USBDEVFS_IOCTL, &ctl); } } return 0; }
int os_setupusb(usbdevice* kb){ // Copy device description and serial struct udev_device* dev = kb->udev; const char* name = udev_device_get_sysattr_value(dev, "product"); if(name) strncpy(kb->name, name, KB_NAME_LEN); const char* serial = udev_device_get_sysattr_value(dev, "serial"); if(serial) strncpy(kb->serial, serial, SERIAL_LEN); // 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; ckb_info("Connecting %s (S/N: %s)\n", kb->name, kb->serial); // Claim the USB interfaces if(usbclaim(kb, HAS_FEATURES(kb, FEAT_RGB))){ ckb_err("Failed to claim interface: %s\n", strerror(errno)); return -1; } return 0; }
int os_setupusb(usbdevice* kb){ // Copy device description and serial struct udev_device* dev = kb->udev; const char* name = udev_device_get_sysattr_value(dev, "product"); if(name) strncpy(kb->name, name, KB_NAME_LEN); strtrim(kb->name); const char* serial = udev_device_get_sysattr_value(dev, "serial"); if(serial) strncpy(kb->serial, serial, SERIAL_LEN); strtrim(kb->serial); // 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; int index = INDEX_OF(kb, keyboard); ckb_info("Connecting %s at %s%d\n", kb->name, devpath, index); // Claim the USB interfaces const char* ep_str = udev_device_get_sysattr_value(dev, "bNumInterfaces"); kb->epcount = 0; if(ep_str) sscanf(ep_str, "%d", &kb->epcount); if(kb->epcount == 0){ // This shouldn't happen, but if it does, assume EP count based on what the device is supposed to have kb->epcount = (HAS_FEATURES(kb, FEAT_RGB) ? 4 : 3); ckb_warn("Unable to read endpoint count from udev, assuming %d...\n", kb->epcount); } if(usbclaim(kb)){ ckb_err("Failed to claim interfaces: %s\n", strerror(errno)); return -1; } return 0; }
void os_keypress(usbdevice* kb, int scancode, int down){ if(scancode & SCAN_MOUSE){ if(scancode == BTN_WHEELUP){ if(down) postevent_wheel(kb->event, !!(kb->features & FEAT_MOUSEACCEL), 1); return; } else if(scancode == BTN_WHEELDOWN){ if(down) postevent_wheel(kb->event, !!(kb->features & FEAT_MOUSEACCEL), -1); return; } int button = scancode & ~SCAN_MOUSE; // Reverse or collapse left/right buttons if the system preferences say so int mode; if(IOHIDGetMouseButtonMode(kb->event, &mode) == kIOReturnSuccess){ if(mode == kIOHIDButtonMode_ReverseLeftRightClicks && button == 0) button = 1; else if(mode != kIOHIDButtonMode_EnableRightClick && button == 1) button = 0; } postevent_mb(kb->event, button, down); if(down) kb->mousestate |= (1 << button); else kb->mousestate &= ~(1 << button); return; } // Some boneheaded Apple engineers decided to reverse kVK_ANSI_Grave and kVK_ISO_Section on the 105-key layouts... if(!HAS_ANY_FEATURE(kb, FEAT_LMASK)){ // If the layout hasn't been set yet, it can be auto-detected from certain keys if(scancode == KEY_BACKSLASH_ISO || scancode == KEY_102ND) kb->features |= FEAT_ISO; else if(scancode == KEY_BACKSLASH) kb->features |= FEAT_ANSI; } if(scancode == KEY_BACKSLASH_ISO) scancode = KEY_BACKSLASH; if(HAS_FEATURES(kb, FEAT_ISO)){ // Compensate for key reversal if(scancode == KEY_GRAVE) scancode = KEY_102ND; else if(scancode == KEY_102ND) scancode = KEY_GRAVE; } // Check for modifier keys and update flags int isMod = 0; IOOptionBits mod = 0; if(scancode == KEY_CAPSLOCK){ if(down) kb->modifiers ^= NX_ALPHASHIFTMASK; isMod = 1; } else if(scancode == KEY_LEFTSHIFT) mod = NX_DEVICELSHIFTKEYMASK; else if(scancode == KEY_RIGHTSHIFT) mod = NX_DEVICERSHIFTKEYMASK; else if(scancode == KEY_LEFTCTRL) mod = NX_DEVICELCTLKEYMASK; else if(scancode == KEY_RIGHTCTRL) mod = NX_DEVICERCTLKEYMASK; else if(scancode == KEY_LEFTMETA) mod = NX_DEVICELCMDKEYMASK; else if(scancode == KEY_RIGHTMETA) mod = NX_DEVICERCMDKEYMASK; else if(scancode == KEY_LEFTALT) mod = NX_DEVICELALTKEYMASK; else if(scancode == KEY_RIGHTALT) mod = NX_DEVICERALTKEYMASK; if(mod){ // Update global modifiers if(down) mod |= kb->modifiers; else mod = kb->modifiers & ~mod; if((mod & NX_DEVICELSHIFTKEYMASK) || (mod & NX_DEVICERSHIFTKEYMASK)) mod |= NX_SHIFTMASK; else mod &= ~NX_SHIFTMASK; if((mod & NX_DEVICELCTLKEYMASK) || (mod & NX_DEVICERCTLKEYMASK)) mod |= NX_CONTROLMASK; else mod &= ~NX_CONTROLMASK; if((mod & NX_DEVICELCMDKEYMASK) || (mod & NX_DEVICERCMDKEYMASK)) mod |= NX_COMMANDMASK; else mod &= ~NX_COMMANDMASK; if((mod & NX_DEVICELALTKEYMASK) || (mod & NX_DEVICERALTKEYMASK)) mod |= NX_ALTERNATEMASK; else mod &= ~NX_ALTERNATEMASK; kb->modifiers = mod; kb->lastkeypress = KEY_NONE; isMod = 1; } else if(!isMod){ // For any other key, trigger key repeat if(down){ long repeat = repeattime(kb->event, 1); if(repeat > 0){ kb->lastkeypress = scancode; clock_gettime(CLOCK_MONOTONIC, &kb->keyrepeat); timespec_add(&kb->keyrepeat, repeat); } else kb->lastkeypress = KEY_NONE; } else kb->lastkeypress = KEY_NONE; } postevent_kp(kb->event, kb->modifiers, scancode, down, isMod, 0); }