// 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; }
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; }
int _usbsend(usbdevice* kb, const uchar* messages, int count, const char* file, int line){ int total_sent = 0; for(int i = 0; i < count; i++){ // Send each message via the OS function while(1){ DELAY_SHORT(kb); int res = os_usbsend(kb, messages + i * MSG_SIZE, 0, file, line); if(res == 0) return 0; else if(res != -1){ total_sent += res; break; } // Stop immediately if the program is shutting down or hardware load is set to tryonce if(reset_stop || hwload_mode != 2) return 0; // Retry as long as the result is temporary failure DELAY_LONG(kb); } } return total_sent; }
void os_updateindicators(usbdevice* kb, int force){ // Set NumLock on permanently char ileds = 1; // Set Caps Lock if enabled. Unlike Linux, OSX keyboards have independent caps lock states, so // we use the last-assigned value rather than fetching it from the system if(kb->modifiers & kCGEventFlagMaskAlphaShift) ileds |= 2; kb->hw_ileds = ileds; if(kb->active){ usbmode* mode = kb->profile->currentmode; ileds = (ileds & ~mode->ioff) | mode->ion; } if(force || ileds != kb->ileds){ kb->ileds = ileds; // Get a list of LED elements from handle 0 long ledpage = kHIDPage_LEDs; const void* keys[] = { CFSTR(kIOHIDElementUsagePageKey) }; const void* values[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &ledpage) }; CFDictionaryRef matching = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease(values[0]); CFArrayRef leds; kern_return_t res = (*kb->handles[0])->copyMatchingElements(kb->handles[0], matching, &leds, 0); CFRelease(matching); if(res != kIOReturnSuccess) return; // Iterate through them and update the LEDs which have changed DELAY_SHORT(kb); CFIndex count = CFArrayGetCount(leds); for(CFIndex i = 0; i < count; i++){ IOHIDElementRef led = (void*)CFArrayGetValueAtIndex(leds, i); uint32_t usage = IOHIDElementGetUsage(led); IOHIDValueRef value = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault, led, 0, !!(ileds & (1 << (usage - 1)))); (*kb->handles[0])->setValue(kb->handles[0], led, value, 5000, 0, 0, 0); CFRelease(value); } CFRelease(leds); } }