We implemented communication between Swift App and DriverKit driver using IOConnectCallAsyncStructMethod.
void USBDriverClass::registerAsyncCallback(){
// Async required variables
notificationPort = NULL;
machNotificationPort = NULL;
runLoopSource = NULL;
// Async initialization
globalRunLoop = CFRunLoopGetMain();
CFRetain(globalRunLoop);
notificationPort = IONotificationPortCreate(kIOMainPortDefault);
if (notificationPort == NULL)
{
printf("Failed to create notification port for application.\n");
}
machNotificationPort = IONotificationPortGetMachPort(notificationPort);
if (machNotificationPort == 0)
{
printf("Failed to get mach notification port for application.\n");
}
runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
if (runLoopSource == NULL)
{
printf("Failed to get run loop for application.\n");
}
// Establish our notifications in the run loop, so we can get callbacks.
CFRunLoopAddSource(globalRunLoop, runLoopSource, kCFRunLoopDefaultMode);
// Establish our "AsyncCallback" function as the function that will be called by our Dext when it calls its "AsyncCompletion" function.
// We'll use kIOAsyncCalloutFuncIndex and kIOAsyncCalloutRefconIndex to define the parameters for our async callback
// This is your callback function. Check the definition for more details.
asyncRef[kIOAsyncCalloutFuncIndex] = (io_user_reference_t)AsyncCallback;
// Use this for context on the return. For example you might pass "this". But since this example is entirely static, we'll skip that step.
asyncRef[kIOAsyncCalloutRefconIndex] = (io_user_reference_t)NULL;
kern_return_t ret = kIOReturnSuccess;
uint8_t words = 4;
size_t inputSize = sizeof(StructA);
StructA structA;
structA.tag = 1;
structA.length = 0;
structA.values[0] = 0x106000;
structA.values[1] = words * sizeof(uint32_t);
size_t outputSize = sizeof(OutputData);
OutputData data;
printf("registerAsyncCallback called");
ret = IOConnectCallAsyncStructMethod(connection, MessageType_RegisterAsyncCallback, machNotificationPort, asyncRef, kIOAsyncCalloutCount, &structA, inputSize, &data, &outputSize);
if (ret != kIOReturnSuccess)
{
printf("IOConnectCallAsyncStructMethod failed with error: 0x%08x.\n", ret);
}
}
And when we get data from device we send data back from Driver to Swift app
ivars->mUSBProbeDriverClient->AsyncCompletion(ivars->streamingDataCallback, kIOReturnSuccess, asyncData, 16);
Driver should transfer data to swift app asynchronously when it's provided by USB device so we can not transfer struct synchronously.
Async call back has limitation of 16 of uint64_t only and our application require larger data transfer.
The logical way to transfer data using a memory buffer and as per Apple documentation they are providing this using IODataQueueDispatchSource. But they have not provided any example or showed how to use this.
How to use fill buffer and use it in swift app using IODataQueueDispatchSource?