// creates a queue for a device, creates and opens device interface if required static IOReturn HIDCreateQueue( IOHIDDeviceRef inIOHIDDeviceRef ) { IOReturn result = kIOReturnSuccess; if ( inIOHIDDeviceRef ) { assert( IOHIDDeviceGetTypeID() == CFGetTypeID( inIOHIDDeviceRef ) ); // do we already have a queue? IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef ); if ( tIOHIDQueueRef ) { // (yes) assert( IOHIDQueueGetTypeID() == CFGetTypeID( tIOHIDQueueRef ) ); } else { tIOHIDQueueRef = IOHIDQueueCreate( kCFAllocatorDefault, inIOHIDDeviceRef, kDeviceQueueSize, kIOHIDOptionsTypeNone ); if ( tIOHIDQueueRef ) { // did that work HIDReportErrorNum( "Failed to create queue via create", result ); } else { result = kIOReturnSuccess; } } } else { HIDReportErrorNum( "HID device ref does not exist for queue creation", result ); } return result; } /* HIDCreateQueue */
Boolean HIDIsValidDevice(IOHIDDeviceRef inIOHIDDeviceRef) { Boolean result = false; // assume failure (pessimist!) if (inIOHIDDeviceRef) { if (CFGetTypeID(inIOHIDDeviceRef) == IOHIDDeviceGetTypeID()) { result = true; } } return (result); } // HIDIsValidDevice
static Boolean IOHIDDevice_GetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, uint32_t *outValue) { Boolean result = false; if (inIOHIDDeviceRef) { assert(IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef)); CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey); if (tCFTypeRef) { // if this is a number if (CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef)) { // get it's value result = CFNumberGetValue((CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, outValue); } } } return (result); } // IOHIDDevice_GetUInt32Property
// --------------------------------- // removes element for queue, if last element in queue will release queue and closes device interface int HIDDequeueElement( IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef ) { IOReturn result = kIOReturnSuccess; if ( inIOHIDDeviceRef ) { assert( IOHIDDeviceGetTypeID() == CFGetTypeID( inIOHIDDeviceRef ) ); if ( inIOHIDElementRef ) { assert( IOHIDElementGetTypeID() == CFGetTypeID( inIOHIDElementRef ) ); IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef ); if ( tIOHIDQueueRef ) { // stop queue IOHIDQueueStop( tIOHIDQueueRef ); // de-queue element if ( IOHIDQueueContainsElement( tIOHIDQueueRef, inIOHIDElementRef ) ) { IOHIDQueueRemoveElement( tIOHIDQueueRef, inIOHIDElementRef ); } // release device queue and close interface if queue empty if ( HIDIsDeviceQueueEmpty( inIOHIDDeviceRef ) ) { result = HIDDisposeReleaseQueue( inIOHIDDeviceRef ); if ( kIOReturnSuccess != result ) { HIDReportErrorNum( "Failed to dispose and release queue.", result ); } } else { // not empty so restart queue IOHIDQueueStart( tIOHIDQueueRef ); } } else { HIDReportError( "No queue for device passed to HIDDequeueElement." ); if ( kIOReturnSuccess == result ) { result = kIOReturnError; } } } else { HIDReportError( "NULL element passed to HIDDequeueElement." ); result = kIOReturnBadArgument; } } else { HIDReportError( "NULL device passed to HIDDequeueElement." ); result = kIOReturnBadArgument; } return result; } /* HIDDequeueElement */
// queues specific element, performing any device queue set up required // queue is started and ready to return events on exit from this function int HIDQueueElement( IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef ) { IOReturn result = kIOReturnSuccess; if ( inIOHIDDeviceRef ) { assert( IOHIDDeviceGetTypeID() == CFGetTypeID( inIOHIDDeviceRef ) ); if ( inIOHIDElementRef ) { assert( IOHIDElementGetTypeID() == CFGetTypeID( inIOHIDElementRef ) ); IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef ); if ( !tIOHIDQueueRef ) { // if no queue create queue result = HIDCreateQueue( inIOHIDDeviceRef ); if ( kIOReturnSuccess == result ) { tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef ); } } if ( tIOHIDQueueRef ) { // stop queue IOHIDQueueStop( tIOHIDQueueRef ); // queue element if ( !IOHIDQueueContainsElement( tIOHIDQueueRef, inIOHIDElementRef ) ) { IOHIDQueueAddElement( tIOHIDQueueRef, inIOHIDElementRef ); } // restart queue IOHIDQueueStart( tIOHIDQueueRef ); } else { HIDReportError( "No queue for device passed to HIDQueueElement." ); if ( kIOReturnSuccess == result ) { result = kIOReturnError; } } } else { HIDReportError( "NULL element passed to HIDQueueElement." ); result = kIOReturnBadArgument; } } else { HIDReportError( "NULL device passed to HIDQueueElement." ); result = kIOReturnBadArgument; } return result; } /* HIDQueueElement */
static Boolean IOHIDDevice_GetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, void **outValue) { Boolean result = false; if (inIOHIDDeviceRef) { assert(IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef)); CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey); if (tCFTypeRef) { // if this is a number if (CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef)) { // get it's value #ifdef __LP64__ result = CFNumberGetValue((CFNumberRef) tCFTypeRef, kCFNumberSInt64Type, outValue); #else result = CFNumberGetValue((CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, outValue); #endif // ifdef __LP64__ } } } return (result); } // IOHIDDevice_GetPtrProperty
// --------------------------------- // returns true if queue is empty false otherwise // error if no device, empty if no queue static unsigned char HIDIsDeviceQueueEmpty( IOHIDDeviceRef inIOHIDDeviceRef ) { if ( inIOHIDDeviceRef ) { // need device and queue assert( IOHIDDeviceGetTypeID() == CFGetTypeID( inIOHIDDeviceRef ) ); IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue( inIOHIDDeviceRef ); if ( tIOHIDQueueRef ) { IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement( inIOHIDDeviceRef, kHIDElementTypeIO ); while ( tIOHIDElementRef ) { if ( IOHIDQueueContainsElement( tIOHIDQueueRef, tIOHIDElementRef ) ) { return false; } tIOHIDElementRef = HIDGetNextDeviceElement( tIOHIDElementRef, kHIDElementTypeIO ); } } else { HIDReportError( "NULL device passed to HIDIsDeviceQueueEmpty." ); } } else { HIDReportError( "NULL device passed to HIDIsDeviceQueueEmpty." ); } return true; } /* HIDIsDeviceQueueEmpty */
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); }
CFArrayRef IOHIDDevice_GetUsagePairs(IOHIDDeviceRef inIOHIDDeviceRef) { assert(IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef)); return (IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsagePairsKey))); }
CFStringRef IOHIDDevice_GetSerialNumber(IOHIDDeviceRef inIOHIDDeviceRef) { assert(IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef)); return (IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDSerialNumberKey))); }
CFStringRef IOHIDDevice_GetProduct(IOHIDDeviceRef inIOHIDDeviceRef) { assert(IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef)); return (IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDProductKey))); } // IOHIDDevice_GetProduct
CFStringRef IOHIDDevice_GetManufacturer(IOHIDDeviceRef inIOHIDDeviceRef) { assert(IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef)); return (IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDManufacturerKey))); } // IOHIDDevice_GetManufacturer
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); }
// get the first element of device passed in as parameter // returns NULL if no list exists or device does not exists or is NULL IOHIDElementRef HIDGetFirstDeviceElement( IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask ) { IOHIDElementRef result = NULL; if ( inIOHIDDeviceRef ) { assert( IOHIDDeviceGetTypeID() == CFGetTypeID( inIOHIDDeviceRef ) ); gElementCFArrayRef = IOHIDDeviceCopyMatchingElements( inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone ); if ( gElementCFArrayRef ) { CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef ); for ( idx = 0; idx < cnt; idx++ ) { IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx ); if ( !tIOHIDElementRef ) { continue; } IOHIDElementType type = IOHIDElementGetType( tIOHIDElementRef ); switch ( type ) { case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: case kIOHIDElementTypeInput_Axis: case kIOHIDElementTypeInput_ScanCodes: { if ( typeMask & kHIDElementTypeInput ) { result = tIOHIDElementRef; } break; } case kIOHIDElementTypeOutput: { if ( typeMask & kHIDElementTypeOutput ) { result = tIOHIDElementRef; } break; } case kIOHIDElementTypeFeature: { if ( typeMask & kHIDElementTypeFeature ) { result = tIOHIDElementRef; } break; } case kIOHIDElementTypeCollection: { if ( typeMask & kHIDElementTypeCollection ) { result = tIOHIDElementRef; } break; } } // switch ( type ) if ( result ) { break; // DONE! } } // next idx CFRelease( gElementCFArrayRef ); gElementCFArrayRef = NULL; } // if ( gElementCFArrayRef ) } // if ( inIOHIDDeviceRef ) return result; } /* HIDGetFirstDeviceElement */