// USB device main loop static void* devmain(usbdevice* kb){ readlines_ctx linectx; readlines_ctx_init(&linectx); while(1){ pthread_mutex_lock(dmutex(kb)); // End thread when the handle is removed if(!IS_CONNECTED(kb)) break; // Read from FIFO const char* line; euid_guard_start; int lines = readlines(kb->infifo - 1, linectx, &line); euid_guard_stop; if(lines){ if(readcmd(kb, line)){ // USB transfer failed; destroy device closeusb(kb); break; } } // Update indicator LEDs for this keyboard. These are polled rather than processed during events because they don't update // immediately and may be changed externally by the OS. // (also, they can lock the keyboard if they're sent at the wrong time, at least on some firmwares) kb->vtable->updateindicators(kb, 0); // Wait a little bit and then read again pthread_mutex_unlock(dmutex(kb)); DELAY_SHORT(kb); } pthread_mutex_unlock(dmutex(kb)); readlines_ctx_free(linectx); return 0; }
// USB device main loop static void* devmain(usbdevice* kb){ // dmutex should still be locked when this is called int kbfifo = kb->infifo - 1; readlines_ctx linectx; readlines_ctx_init(&linectx); while(1){ pthread_mutex_unlock(dmutex(kb)); // Read from FIFO const char* line; int lines = readlines(kbfifo, linectx, &line); pthread_mutex_lock(dmutex(kb)); // End thread when the handle is removed if(!IS_CONNECTED(kb)) break; if(lines){ if(readcmd(kb, line)){ // USB transfer failed; destroy device closeusb(kb); break; } } } pthread_mutex_unlock(dmutex(kb)); readlines_ctx_free(linectx); 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; }
static void remove_device(void* context, io_service_t device, uint32_t message_type, void* message_argument){ if(message_type != kIOMessageServiceIsTerminated) return; usbdevice* kb = context; if(kb){ // If the handle is connected to a device, close it pthread_mutex_lock(dmutex(kb)); closeusb(kb); pthread_mutex_unlock(dmutex(kb)); } IOObjectRelease(device); }
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); // 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("%s ready at %s%d\n", kb->name, devpath, index); updateconnected(); pthread_mutex_unlock(dmutex(kb)); return devmain(kb); fail: pthread_mutex_unlock(imutex(kb)); fail_noinput: closeusb(kb); pthread_mutex_unlock(dmutex(kb)); return 0; }
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* _ledthread(void* ctx){ usbdevice* kb = ctx; uchar ileds = 0; // Read LED events from the uinput device struct input_event event; while (read(kb->uinput_kb - 1, &event, sizeof(event)) > 0) { if (event.type == EV_LED && event.code < 8){ char which = 1 << event.code; if(event.value) ileds |= which; else ileds &= ~which; } // Update them if needed pthread_mutex_lock(dmutex(kb)); if(kb->hw_ileds != ileds){ kb->hw_ileds = ileds; kb->vtable->updateindicators(kb, 0); } pthread_mutex_unlock(dmutex(kb)); } return 0; }