void* os_inputmain(void* context){ usbdevice* kb = context; int fd = kb->handle; short vendor = kb->vendor, product = kb->product; int index = INDEX_OF(kb, keyboard); ckb_info("Starting input thread for %s%d\n", devpath, index); // Monitor input transfers on all endpoints int urbcount = 3; struct usbdevfs_urb urbs[urbcount]; memset(urbs, 0, sizeof(urbs)); urbs[0].buffer_length = 8; if(IS_RGB(vendor, product)){ if(IS_MOUSE(vendor, product)) urbs[1].buffer_length = 10; else urbs[1].buffer_length = 21; urbs[2].buffer_length = MSG_SIZE; urbs[3].buffer_length = MSG_SIZE; } else { urbs[1].buffer_length = 4; urbs[2].buffer_length = 15; } // Submit URBs for(int i = 0; i < urbcount; i++){ urbs[i].type = USBDEVFS_URB_TYPE_INTERRUPT; urbs[i].endpoint = 0x80 | (i + 1); urbs[i].buffer = malloc(urbs[i].buffer_length); ioctl(fd, USBDEVFS_SUBMITURB, urbs + i); } // Start monitoring input while(1){ struct usbdevfs_urb* urb = 0; if(ioctl(fd, USBDEVFS_REAPURB, &urb)){ if(errno == ENODEV || errno == ENOENT || errno == ESHUTDOWN) // Stop the thread if the handle closes break; else if(errno == EPIPE && urb){ // On EPIPE, clear halt on the endpoint ioctl(fd, USBDEVFS_CLEAR_HALT, &urb->endpoint); // Re-submit the URB if(urb) ioctl(fd, USBDEVFS_SUBMITURB, urb); urb = 0; } } if(urb){ // Process input (if any) pthread_mutex_lock(imutex(kb)); if(IS_MOUSE(vendor, product)){ if(urb->endpoint == 0x82){ // RGB mouse input hid_mouse_translate(kb->input.keys, &kb->input.rel_x, &kb->input.rel_y, -(urb->endpoint & 0xF), urb->actual_length, urb->buffer); } } else if(IS_RGB(vendor, product)){ switch(urb->endpoint){ case 0x81: // RGB EP 1: 6KRO (BIOS mode) input hid_kb_translate(kb->input.keys, -1, urb->actual_length, urb->buffer); break; case 0x82: // RGB EP 2: NKRO (non-BIOS) input. Accept only if keyboard is inactive if(!kb->active) hid_kb_translate(kb->input.keys, -2, urb->actual_length, urb->buffer); break; case 0x83: // RGB EP 3: Corsair input memcpy(kb->input.keys, urb->buffer, N_KEYBYTES_KB); break; } } else // Non-RGB input hid_kb_translate(kb->input.keys, urb->endpoint & 0xF, urb->actual_length, urb->buffer); inputupdate(kb); pthread_mutex_unlock(imutex(kb)); // Re-submit the URB ioctl(fd, USBDEVFS_SUBMITURB, urb); urb = 0; } } // Clean up ckb_info("Stopping input thread for %s%d\n", devpath, index); for(int i = 0; i < urbcount; i++){ ioctl(fd, USBDEVFS_DISCARDURB, urbs + i); free(urbs[i].buffer); } return 0; }
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; }