void openusb(usbdevice* kb){ // The driver sometimes isn't completely ready yet, so give it a short delay sleep(1); // Handle 3 is the control handle kb->lastkeypress = -1; kb->handle = kb->handles[3]; // Set up the device int setup = setupusb(kb); if(setup == -1){ closehandle(kb); pthread_mutex_unlock(&kb->mutex); pthread_mutex_destroy(&kb->mutex); pthread_mutex_destroy(&kb->keymutex); return; } else if(setup){ // If the setup had a communication error, wait a bit and try again. sleep(1); resetusb(kb); } // Start handling HID reports for the Corsair input IOHIDDeviceRegisterInputReportCallback(kb->handles[2], kb->intinput, MSG_SIZE, reportcallback, kb); // Register for close notification IOHIDDeviceRegisterRemovalCallback(kb->handle, usbremove, kb); // Update connected updateconnected(); notifyconnect(kb, 1); int index = INDEX_OF(kb, keyboard); printf("Device ready at %s%d\n", devpath, index); pthread_mutex_unlock(&kb->mutex); }
int usbadd(struct udev_device* dev, short vendor, short product){ const char* path = udev_device_get_devnode(dev); const char* syspath = udev_device_get_syspath(dev); if(!path || !syspath || path[0] == 0 || syspath[0] == 0){ ckb_err("Failed to get device path\n"); return -1; } // Find a free USB slot for(int index = 1; index < DEV_MAX; index++){ usbdevice* kb = keyboard + index; if(pthread_mutex_trylock(dmutex(kb))){ // If the mutex is locked then the device is obviously in use, so keep going if(!strcmp(syspath, kbsyspath[index])){ // Make sure this existing keyboard doesn't have the same syspath (this shouldn't happen) return 0; } continue; } if(!IS_CONNECTED(kb)){ // Open the sysfs device kb->handle = open(path, O_RDWR); if(kb->handle <= 0){ ckb_err("Failed to open USB device: %s\n", strerror(errno)); kb->handle = 0; pthread_mutex_unlock(dmutex(kb)); return -1; } else { // Set up device kb->udev = dev; kb->vendor = vendor; kb->product = product; strncpy(kbsyspath[index], syspath, FILENAME_MAX); // Mutex remains locked setupusb(kb); return 0; } } pthread_mutex_unlock(dmutex(kb)); } ckb_err("No free devices\n"); return -1; }
void openusb(usbdevice* kb, short vendor, short product){ kb->lastkeypress = KEY_NONE; if(IS_RGB(vendor, product)) // Handle 3 is the control handle kb->handle = kb->handles[3]; else // Non RGB keyboards don't have one, so just use 0 kb->handle = kb->handles[0]; // Set up the device int setup = setupusb(kb, vendor, product); if(setup == -1 || (setup && usb_tryreset(kb))){ closehandle(kb); pthread_mutex_unlock(&kb->mutex); pthread_mutex_destroy(&kb->mutex); pthread_mutex_destroy(&kb->keymutex); return; } // Start handling HID reports for the input IOHIDDeviceRegisterInputReportCallback(kb->handles[0], kb->urbinput, 8, reportcallback, kb); if(IS_RGB(vendor, product)) IOHIDDeviceRegisterInputReportCallback(kb->handles[1], kb->urbinput + 8, 21, reportcallback, kb); else IOHIDDeviceRegisterInputReportCallback(kb->handles[1], kb->urbinput + 8, 4, reportcallback, kb); if(IS_RGB(vendor, product)) IOHIDDeviceRegisterInputReportCallback(kb->handles[2], kb->kbinput, MSG_SIZE, reportcallback, kb); else IOHIDDeviceRegisterInputReportCallback(kb->handles[2], kb->urbinput + 8 + 4, 15, reportcallback, kb); // Register for close notification IOHIDDeviceRegisterRemovalCallback(kb->handle, usbremove, kb); // Update connected updateconnected(); notifyconnect(kb, 1); int index = INDEX_OF(kb, keyboard); printf("Device ready at %s%d\n", devpath, index); pthread_mutex_unlock(&kb->mutex); }
int openusb(struct udev_device* dev, int model){ // Make sure it's not connected yet const char* path = udev_device_get_devnode(dev); for(int i = 1; i < DEV_MAX; i++){ if(keyboard[i].udev && !strcmp(path, udev_device_get_devnode(keyboard[i].udev))) return 0; } // Find a free USB slot for(int index = 1; index < DEV_MAX; index++){ usbdevice* kb = keyboard + index; if(!IS_ACTIVE(kb)){ // Open the sysfs device kb->udev = dev; kb->handle = open(path, O_RDWR); kb->model = model; if(kb->handle <= 0){ printf("Error: Failed to open USB device: %s\n", strerror(errno)); closehandle(kb); connectstatus |= 2; return -1; } // Copy device description and serial strncpy(kb->name, udev_device_get_sysattr_value(dev, "product"), NAME_LEN); strncpy(kb->setting.serial, udev_device_get_sysattr_value(dev, "serial"), SERIAL_LEN); printf("Connecting %s (S/N: %s)\n", kb->name, kb->setting.serial); // A USB reset is almost always required in order for it to work correctly printf("Resetting device\n"); if(ioctl(kb->handle, USBDEVFS_RESET, 0)){ printf("Reset failed (%s). Disconnecting.\n", strerror(errno)); closehandle(kb); connectstatus |= 2; return -1; } // Claim the USB interfaces if(usbclaim(kb)){ printf("Error: Failed to claim interface: %s\n", strerror(errno)); closehandle(kb); connectstatus |= 2; return -1; } // Put the M-keys (K95) as well as the Brightness/Lock keys into software-controlled mode. This packet disables their // hardware-based functions. unsigned char datapkt[MSG_SIZE] = { 0x07, 0x04, 0x02 }; struct usbdevfs_ctrltransfer transfer = { 0x21, 0x09, 0x0300, 0x03, MSG_SIZE, 500, datapkt }; // This packet doesn't always succeed, so reset the device if that happens if(ioctl(kb->handle, USBDEVFS_CONTROL, &transfer) != MSG_SIZE){ printf("Couldn't talk to device (%s), trying to reset again...\n", strerror(errno)); if(resetusb(kb) || ioctl(kb->handle, USBDEVFS_CONTROL, &transfer) != MSG_SIZE){ printf("Reset failed (%s). Disconnecting.\n", strerror(errno)); closehandle(kb); connectstatus |= 2; return -1; } } // Set up the device if(setupusb(index)){ closehandle(kb); connectstatus |= 2; return -1; } // Set up the interrupt transfers. kb->INPUT_TEST = 0; setint(kb); // We should receive an interrupt transfer shortly after setting them up. If it doesn't happen, the device // isn't working correctly and needs to be reset int received = 0; for(int wait = 0; wait < 10; wait++){ usleep(50000); if(kb->INPUT_TEST){ received = 1; break; } } if(!received){ printf("Didn't get input, trying to reset again...\n"); if(resetusb(kb)){ printf("Reset failed (%s). Disconnecting.\n", strerror(errno)); closehandle(kb); connectstatus |= 2; return -1; } } updateconnected(); printf("Device ready at %s%d\n", devpath, index); connectstatus |= 1; return 0; } } printf("Error: No free devices\n"); return -1; }
usbdevice* usbadd(hid_dev_t handle, io_object_t** rm_notify){ // Get the model and serial number long idvendor = V_CORSAIR, idproduct = usbgetlong(handle, CFSTR(kIOHIDProductIDKey)); // Each keyboard generates multiple match events (one for each endpoint) // Use the location ID key to group the handles together long location = usbgetlong(handle, CFSTR(kIOHIDLocationIDKey)); // Look for any partially-set up boards matching this device int index = -1; for(int i = 1; i < DEV_MAX; i++){ if(pthread_mutex_trylock(devmutex + i)) // If the mutex is locked then the device is obviously set up already, keep going continue; if(keyboard[i].handle == INCOMPLETE && keyboard[i].vendor == idvendor && keyboard[i].product == idproduct && keyboard[i].location_id == location){ // Matched; continue setting up this device index = i; // Device mutex remains locked break; } pthread_mutex_unlock(devmutex + i); } // If none was found, grab the first free device if(index == -1){ for(int i = 1; i < DEV_MAX; i++){ if(pthread_mutex_trylock(devmutex + i)) continue; if(!keyboard[i].handle){ // Mark the device as in use and print out a message index = i; keyboard[i].handle = INCOMPLETE; keyboard[i].location_id = location; keyboard[i].vendor = idvendor; keyboard[i].product = idproduct; // Read the serial number and name usbgetstr(handle, CFSTR(kIOHIDSerialNumberKey), keyboard[i].serial, SERIAL_LEN); usbgetstr(handle, CFSTR(kIOHIDProductKey), keyboard[i].name, KB_NAME_LEN); ckb_info("Connecting %s (S/N: %s)\n", keyboard[i].name, keyboard[i].serial); // Device mutex remains locked break; } pthread_mutex_unlock(devmutex + i); } } if(index == -1){ ckb_err("No free devices\n"); return 0; } usbdevice* kb = keyboard + index; // There's no direct way to tell which of the endpoints this is, but there's a workaround // Each handle has a unique maximum packet size combination, so use that to place them long input = usbgetlong(handle, CFSTR(kIOHIDMaxInputReportSizeKey)); long output = usbgetlong(handle, CFSTR(kIOHIDMaxOutputReportSizeKey)); long feature = usbgetlong(handle, CFSTR(kIOHIDMaxFeatureReportSizeKey)); int handle_idx; // Handle 0 is for BIOS mode input (RGB) or non-RGB key input if(((input == 8 && output == 1) // Keyboards || (input == 7 && output == 0)) // Mice && feature == 0) handle_idx = 0; // Handle 1 is for standard HID input (RGB) or media keys (non-RGB) else if(((input == 21 || input == 10) && output == 1 && feature == 1) || (input == 4 && output == 0 && feature == 0)) handle_idx = 1; // Handle 2 is for Corsair inputs, unused on non-RGB else if(((input == 64 || input == 15) && output == 0 && feature == 0) || (input == 64 && output == 64 && feature == 0)) // FW >= 1.20 handle_idx = 2; // Handle 3 is for controlling the device (only exists for RGB) else if((input == 0 && output == 0 && feature == 64) || (input == 64 && output == 64 && feature == 64)) // FW >= 1.20 handle_idx = 3; else { ckb_warn("Got unknown handle (I: %d, O: %d, F: %d)\n", (int)input, (int)output, (int)feature); return 0; } // If all handles have been set up, finish initializing the keyboard kb->handles[handle_idx] = handle; if(kb->handles[0] && kb->handles[1] && kb->handles[2] && (kb->handles[3] || !IS_RGB(idvendor, idproduct))) setupusb(kb); else pthread_mutex_unlock(devmutex + index); *rm_notify = kb->rm_notify + handle_idx; return kb; }