void udevenum(){ struct udev_enumerate* enumerator = udev_enumerate_new(udev); udev_enumerate_add_match_subsystem(enumerator, "usb"); udev_enumerate_add_match_sysattr(enumerator, "idVendor", V_CORSAIR_STR); udev_enumerate_scan_devices(enumerator); struct udev_list_entry* devices, *dev_list_entry; devices = udev_enumerate_get_list_entry(enumerator); udev_list_entry_foreach(dev_list_entry, devices){ const char* path = udev_list_entry_get_name(dev_list_entry); struct udev_device* dev = udev_device_new_from_syspath(udev, path); // If the device matches a recognized device ID, open it const char* product = udev_device_get_sysattr_value(dev, "idProduct"); if(!strcmp(product, P_K70_STR)){ pthread_mutex_lock(&kblistmutex); openusb(dev, 70); pthread_mutex_unlock(&kblistmutex); continue; } if(!strcmp(product, P_K95_STR)){ pthread_mutex_lock(&kblistmutex); openusb(dev, 95); pthread_mutex_unlock(&kblistmutex); continue; } // Free the device if it wasn't used udev_device_unref(dev); } udev_enumerate_unref(enumerator); }
void usbadd(void* context, IOReturn result, void* sender, IOHIDDeviceRef device){ if(CFGetTypeID(device) != IOHIDDeviceGetTypeID()) return; // Get the model and serial number long idvendor = V_CORSAIR, idproduct = usbgetvalue(device, CFSTR(kIOHIDProductIDKey)); char serial[SERIAL_LEN]; CFTypeRef cfserial = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDSerialNumberKey)); if(!cfserial || CFGetTypeID(cfserial) != CFStringGetTypeID() || !CFStringGetCString(cfserial, serial, SERIAL_LEN, kCFStringEncodingASCII)) // If the serial can't be read, make one up snprintf(serial, SERIAL_LEN, "%04x:%x04-NoID", (uint)idvendor, (uint)idproduct); // For non-RGB models, get the firmware version here as well long fwversion = 0; if(!IS_RGB(idvendor, idproduct)) fwversion = usbgetvalue(device, CFSTR(kIOHIDVersionNumberKey)); pthread_mutex_lock(&kblistmutex); // A single keyboard will generate multiple match events, so each handle has to be added to the board separately. // Look for any partially-set up boards matching this serial number int index = -1; for(int i = 1; i < DEV_MAX; i++){ if(!strcmp(keyboard[i].profile.serial, serial) && keyboard[i].handle == INCOMPLETE){ index = i; break; } } // If none was found, grab the first free device if(index == -1){ for(int i = 1; i < DEV_MAX; i++){ if(!keyboard[i].handle){ // Mark the device as in use and print out a message index = i; keyboard[i].handle = INCOMPLETE; keyboard[i].fwversion = fwversion; strcpy(keyboard[i].profile.serial, serial); CFTypeRef cfname = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); if(cfname && CFGetTypeID(cfname) == CFStringGetTypeID()) CFStringGetCString(cfname, keyboard[i].name, NAME_LEN, kCFStringEncodingASCII); printf("Connecting %s (S/N: %s)\n", keyboard[i].name, keyboard[i].profile.serial); break; } } } if(index == -1){ printf("Error: No free devices\n"); pthread_mutex_unlock(&kblistmutex); return; } usbdevice* kb = keyboard + index; // There's no direct way to tell which of the four handles this is, but there's a workaround // Each handle has a unique maximum packet size combination, so use that to place them long input = usbgetvalue(device, CFSTR(kIOHIDMaxInputReportSizeKey)); long output = usbgetvalue(device, CFSTR(kIOHIDMaxOutputReportSizeKey)); long feature = usbgetvalue(device, CFSTR(kIOHIDMaxFeatureReportSizeKey)); // Handle 0 is for BIOS mode input (RGB) or non-RGB key input if(input == 8 && output == 1 && feature == 0) kb->handles[0] = device; // Handle 1 is for standard HID input (RGB) or media keys (non-RGB) else if((input == 21 && output == 1 && feature == 1) || (input == 4 && output == 0 && feature == 0)) kb->handles[1] = device; // 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 kb->handles[2] = device; // 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 kb->handles[3] = device; else printf("Warning: Got unknown handle (I: %d, O: %d, F: %d)\n", (int)input, (int)output, (int)feature); // If all handles have been set up, finish initializing the keyboard if(kb->handles[0] && kb->handles[1] && kb->handles[2] && (kb->handles[3] || !IS_RGB(idvendor, idproduct))) openusb(kb, (short)idvendor, (short)idproduct); pthread_mutex_unlock(&kblistmutex); }
void* udevmain(void* context){ // Enumerate all currently connected devices udevenum(); if(connectstatus & 2) // If a device failed to connect, enumerate again to make sure we reconnect it (if possible) udevenum(); // Done scanning. Enter a loop to poll for device updates struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev, "udev"); udev_monitor_filter_add_match_subsystem_devtype(monitor, "usb", 0); udev_monitor_enable_receiving(monitor); // Get an fd for the monitor int fd = udev_monitor_get_fd(monitor); fd_set fds; while(udev){ FD_ZERO(&fds); FD_SET(fd, &fds); // Block until an event is read if(select(fd + 1, &fds, 0, 0, 0) > 0 && FD_ISSET(fd, &fds)){ struct udev_device* dev = udev_monitor_receive_device(monitor); if(dev){ const char* action = udev_device_get_action(dev); if(action && !strcmp(action, "add")){ // Device added. Check vendor and product ID and add the device if it matches. const char* vendor = udev_device_get_sysattr_value(dev, "idVendor"); if(vendor && !strcmp(vendor, V_CORSAIR_STR)){ const char* product = udev_device_get_sysattr_value(dev, "idProduct"); if(product && !strcmp(product, P_K70_STR)){ pthread_mutex_lock(&kblistmutex); openusb(dev, 70); pthread_mutex_unlock(&kblistmutex); } else if(product && !strcmp(product, P_K95_STR)){ pthread_mutex_lock(&kblistmutex); openusb(dev, 95); pthread_mutex_unlock(&kblistmutex); } else // Free the device if it wasn't used udev_device_unref(dev); } else udev_device_unref(dev); // Wait a little bit and then re-enumerate devices. Sometimes the keyboard doesn't get picked up right away. usleep(100000); connectstatus = 0; udevenum(); if(connectstatus & 2) udevenum(); } else if(!strcmp(action, "remove")){ // Device removed. Look for it in our list of keyboards pthread_mutex_lock(&kblistmutex); const char* path = udev_device_get_syspath(dev); for(int i = 1; i < DEV_MAX; i++){ if(keyboard[i].udev && !strcmp(path, udev_device_get_syspath(keyboard[i].udev))){ pthread_mutex_lock(&keyboard[i].mutex); closeusb(i); break; } } pthread_mutex_unlock(&kblistmutex); } } } } udev_monitor_unref(monitor); return 0; }
void usbadd(void* context, IOReturn result, void* sender, IOHIDDeviceRef device){ if(CFGetTypeID(device) != IOHIDDeviceGetTypeID()) return; // Get the model and serial number long idproduct = usbgetvalue(device, CFSTR(kIOHIDProductIDKey)); int model; if(idproduct == P_K70) model = 70; else if(idproduct == P_K95) model = 95; else return; char serial[SERIAL_LEN]; CFTypeRef cfserial = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDSerialNumberKey)); if(!cfserial || CFGetTypeID(cfserial) != CFStringGetTypeID() || !CFStringGetCString(cfserial, serial, SERIAL_LEN, kCFStringEncodingASCII)) return; pthread_mutex_lock(&kblistmutex); // A single keyboard will generate 4 match events, so each handle has to be added to the board separately. // Look for any partially-set up boards matching this serial number int index = -1; for(int i = 1; i < DEV_MAX; i++){ if(!strcmp(keyboard[i].profile.serial, serial) && keyboard[i].handle == INCOMPLETE){ index = i; break; } } // If none was found, grab the first free device if(index == -1){ for(int i = 1; i < DEV_MAX; i++){ if(!keyboard[i].handle){ // Mark the device as in use and print out a message index = i; keyboard[i].handle = INCOMPLETE; keyboard[i].model = model; strcpy(keyboard[i].profile.serial, serial); CFTypeRef cfname = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); if(cfname && CFGetTypeID(cfname) == CFStringGetTypeID()) CFStringGetCString(cfname, keyboard[i].name, SERIAL_LEN, kCFStringEncodingASCII); printf("Connecting %s (S/N: %s)\n", keyboard[i].name, keyboard[i].profile.serial); break; } } } if(index == -1){ printf("Error: No free devices\n"); pthread_mutex_unlock(&kblistmutex); return; } usbdevice* kb = keyboard + index; // There's no direct way to tell which of the four handles this is, but there's a workaround // Each handle has a unique maximum packet size combination, so use that to place them long input = usbgetvalue(device, CFSTR(kIOHIDMaxInputReportSizeKey)); long output = usbgetvalue(device, CFSTR(kIOHIDMaxOutputReportSizeKey)); long feature = usbgetvalue(device, CFSTR(kIOHIDMaxFeatureReportSizeKey)); // Handle 0 is unused if(input == 8 && output == 1 && feature == 0) kb->handles[0] = device; // Handle 1 is for HID inputs (ignored by ckb) else if(input == 21 && output == 1 && feature == 1) kb->handles[1] = device; // Handle 2 is for Corsair inputs else if(input == 64 && output == 0 && feature == 0) kb->handles[2] = device; // Handle 3 is for controlling the device else if(input == 0 && output == 0 && feature == 64) kb->handles[3] = device; else printf("Warning: Got unknown handle (I: %d, O: %d, F: %d)\n", (int)input, (int)output, (int)feature); // If all handles have been set up, finish initializing the keyboard if(kb->handles[0] && kb->handles[1] && kb->handles[2] && kb->handles[3]) openusb(kb); pthread_mutex_unlock(&kblistmutex); }