int setactive_mouse(usbdevice* kb, int active){ if(NEEDS_FW_UPDATE(kb)) return 0; uchar msg[2][MSG_SIZE] = { { 0x07, 0x04, 0 }, // Disables or enables HW control for DPI and Sniper button { 0x07, 0x40, 0x08, 0, 1, 0x80, 2, 0x80, 3, 0x80, 4, 0x80, 5, 0x80, 6, 0x80, 7, 0x80, 8, 0x80 }, // Enable button input. This is similar to the keyboard input selection, but we want everything to come through the HID reports. }; if(active) // Put the mouse into SW mode msg[0][2] = 2; else // Restore HW mode msg[0][2] = 1; pthread_mutex_lock(imutex(kb)); kb->active = !!active; kb->profile->lastlight.forceupdate = 1; // Clear input memset(&kb->input.keys, 0, sizeof(kb->input.keys)); inputupdate(kb); pthread_mutex_unlock(imutex(kb)); if(!usbsend(kb, msg[0], 1)) return -1; if(active && !usbsend(kb, msg[1], 1)) return -1; return 0; }
int _usbrecv(usbdevice* kb, const uchar* out_msg, uchar* in_msg, const char* file, int line){ // Try a maximum of 3 times for(int try = 0; try < 5; try++){ // Send the output message DELAY_SHORT(kb); int res = os_usbsend(kb, out_msg, 1, file, line); if(res == 0) return 0; else if(res == -1){ // Retry on temporary failure if(reset_stop) return 0; DELAY_LONG(kb); continue; } // Wait for the response DELAY_MEDIUM(kb); res = os_usbrecv(kb, in_msg, file, line); if(res == 0) return 0; else if(res != -1) return res; if(reset_stop || hwload_mode != 2) return 0; DELAY_LONG(kb); } // Give up ckb_err_fn("Too many send/recv failures. Dropping.\n", file, line); return 0; } int closeusb(usbdevice* kb){ pthread_mutex_lock(imutex(kb)); if(kb->handle){ int index = INDEX_OF(kb, keyboard); ckb_info("Disconnecting %s%d\n", devpath, index); os_inputclose(kb); updateconnected(); // Close USB device os_closeusb(kb); } else updateconnected(); rmdevpath(kb); // Wait for thread to close pthread_mutex_unlock(imutex(kb)); pthread_mutex_unlock(dmutex(kb)); pthread_join(kb->thread, 0); pthread_mutex_lock(dmutex(kb)); // Delete the profile and the control path if(!kb->vtable) return 0; kb->vtable->freeprofile(kb); memset(kb, 0, sizeof(usbdevice)); return 0; }
void cmd_notify(usbdevice* kb, usbmode* mode, int nnumber, int keyindex, const char* toggle){ if(keyindex >= N_KEYS_INPUT) return; pthread_mutex_lock(imutex(kb)); if(!strcmp(toggle, "on") || *toggle == 0) SET_KEYBIT(mode->notify[nnumber], keyindex); else if(!strcmp(toggle, "off")) CLEAR_KEYBIT(mode->notify[nnumber], keyindex); pthread_mutex_unlock(imutex(kb)); }
static void* _setupusb(void* context){ usbdevice* kb = context; // Set standard fields short vendor = kb->vendor, product = kb->product; const devcmd* vt = kb->vtable = get_vtable(vendor, product); kb->features = (IS_RGB(vendor, product) ? FEAT_STD_RGB : FEAT_STD_NRGB) & features_mask; if(IS_MOUSE(vendor, product)) kb->features |= FEAT_ADJRATE; if(IS_MONOCHROME(vendor, product)) kb->features |= FEAT_MONOCHROME; kb->usbdelay = USB_DELAY_DEFAULT; // Perform OS-specific setup DELAY_LONG(kb); if(os_setupusb(kb)) goto fail; // Make up a device name and serial if they weren't assigned if(!kb->serial[0]) snprintf(kb->serial, SERIAL_LEN, "%04x:%04x-NoID", kb->vendor, kb->product); if(!kb->name[0]) snprintf(kb->name, KB_NAME_LEN, "%s %s", vendor_str(kb->vendor), product_str(kb->product)); // Set up an input device for key events if(os_inputopen(kb)) goto fail; if(pthread_create(&kb->inputthread, 0, os_inputmain, kb)) goto fail; pthread_detach(kb->inputthread); if(os_setupindicators(kb)) goto fail; // Set up device vt->allocprofile(kb); vt->updateindicators(kb, 1); pthread_mutex_unlock(imutex(kb)); if(vt->start(kb, 0) && usb_tryreset(kb)) goto fail_noinput; // Make /dev path if(mkdevpath(kb)) goto fail_noinput; // Finished. Enter main loop int index = INDEX_OF(kb, keyboard); ckb_info("Setup finished for %s%d\n", devpath, index); updateconnected(); return devmain(kb); fail: pthread_mutex_unlock(imutex(kb)); fail_noinput: closeusb(kb); pthread_mutex_unlock(dmutex(kb)); return 0; }
void* os_inputmain(void* context){ usbdevice* kb = context; int index = INDEX_OF(kb, keyboard); // Schedule async events for the device on this thread CFRunLoopRef runloop = CFRunLoopGetCurrent(); int count = (IS_RGB(kb->vendor, kb->product)) ? 4 : 3; for(int i = 0; i < count; i++){ CFTypeRef eventsource; kern_return_t res = (*kb->handles[i])->getAsyncEventSource(kb->handles[i], &eventsource); if(res != kIOReturnSuccess){ ckb_err("Failed to start input thread for %s%d: %x\n", devpath, index, res); return 0; } if(CFGetTypeID(eventsource) == CFRunLoopSourceGetTypeID()) CFRunLoopAddSource(runloop, (CFRunLoopSourceRef)eventsource, kCFRunLoopDefaultMode); else if(CFGetTypeID(eventsource) == CFRunLoopTimerGetTypeID()) CFRunLoopAddTimer(runloop, (CFRunLoopTimerRef)eventsource, kCFRunLoopDefaultMode); } ckb_info("Starting input thread for %s%d\n", devpath, index); // Start getting reports uint8_t* urbinput[] = { malloc(8), malloc(32), malloc(MSG_SIZE) }; (*kb->handles[0])->setInputReportCallback(kb->handles[0], urbinput[0], 8, intreport, kb, 0); if(IS_RGB(kb->vendor, kb->product)){ (*kb->handles[1])->setInputReportCallback(kb->handles[1], urbinput[1], 21, intreport, kb, 0); (*kb->handles[2])->setInputReportCallback(kb->handles[2], urbinput[2], MSG_SIZE, intreport, kb, 0); } else { (*kb->handles[1])->setInputReportCallback(kb->handles[1], urbinput[1], 4, intreport, kb, 0); (*kb->handles[2])->setInputReportCallback(kb->handles[2], urbinput[2], 15, intreport, kb, 0); } // Run the run loop for up to 2ms at a time, then check for key repeats while(1){ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.002, false); pthread_mutex_lock(imutex(kb)); if(!IS_CONNECTED(kb)){ // Make sure the device hasn't disconnected pthread_mutex_unlock(imutex(kb)); break; } keyretrigger(kb); pthread_mutex_unlock(imutex(kb)); } // Clean up ckb_info("Stopping input thread for %s%d\n", devpath, index); free(urbinput[0]); free(urbinput[1]); free(urbinput[2]); return 0; }
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)); }
void setupusb(usbdevice* kb){ pthread_mutex_lock(imutex(kb)); if(pthread_create(&kb->thread, 0, _setupusb, kb)) ckb_err("Failed to create USB thread\n"); }
void cmd_get(usbdevice* kb, usbmode* mode, int nnumber, int dummy, const char* setting){ pthread_mutex_lock(imutex(kb)); _cmd_get(kb, mode, nnumber, setting); pthread_mutex_unlock(imutex(kb)); }
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; }