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 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); }
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; }
int makedevpath(int index){ usbdevice* kb = keyboard + index; // Create the control path char path[strlen(devpath) + 2]; snprintf(path, sizeof(path), "%s%d", devpath, index); if(rm_recursive(path) != 0 && errno != ENOENT){ printf("Error: Unable to delete %s: %s\n", path, strerror(errno)); return -1; } if(mkdir(path, S_READDIR) != 0){ rm_recursive(path); printf("Error: Unable to create %s: %s\n", path, strerror(errno)); return -1; } // Create command FIFO char fifopath[sizeof(path) + 4]; snprintf(fifopath, sizeof(fifopath), "%s/cmd", path); if(mkfifo(fifopath, S_READWRITE) != 0 || (kb->fifo = open(fifopath, O_RDONLY | O_NONBLOCK)) <= 0){ rm_recursive(path); printf("Error: Unable to create %s: %s\n", fifopath, strerror(errno)); return -1; } if(kb->model == -1){ // Root keyboard: write a list of devices updateconnected(); } else { // Write the model and serial to files (doesn't apply to root keyboard) char mpath[sizeof(path) + 6], spath[sizeof(path) + 7]; snprintf(mpath, sizeof(mpath), "%s/model", path); snprintf(spath, sizeof(spath), "%s/serial", path); FILE* mfile = fopen(mpath, "w"); if(mfile){ fputs(kb->name, mfile); fputc('\n', mfile); fclose(mfile); chmod(mpath, S_READ); } else { printf("Warning: Unable to create %s: %s\n", mpath, strerror(errno)); } FILE* sfile = fopen(spath, "w"); if(sfile){ fputs(kb->setting.serial, sfile); fputc('\n', sfile); fclose(sfile); chmod(spath, S_READ); } else { printf("Warning: Unable to create %s: %s\n", spath, strerror(errno)); } } return 0; }
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; }