We implemented communication between Swift App and DriverKit driver using IOConnectCallAsyncStructMethod
. So in Swift App we have following code to setup AsyncDataCallback:
func AsyncDataCallback(refcon: UnsafeMutableRawPointer?, result: IOReturn, args: UnsafeMutablePointer<UnsafeMutableRawPointer?>?, numArgs: UInt32) -> Void {
// handle args
}
var asyncRef: [io_user_reference_t] = .init(repeating: 0, count: 8)
asyncRef[kIOAsyncCalloutFuncIndex] = unsafeBitCast(AsyncDataCallback as IOAsyncCallback, to: UInt64.self)
asyncRef[kIOAsyncCalloutRefconIndex] = unsafeBitCast(self, to: UInt64.self)
......
let notificationPort = IONotificationPortCreate(kIOMasterPortDefault)
let machNotificationPort = IONotificationPortGetMachPort(notificationPort)
let runLoopSource = IONotificationPortGetRunLoopSource(notificationPort)!
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource.takeUnretainedValue(), CFRunLoopMode.defaultMode)
var input = "someinput".map { UInt8($0.asciiValue!) }
let inputSize = input.count
IOConnectCallAsyncStructMethod(
connection,
123,
machNotificationPort,
&asyncRef,
UInt32(kIOAsyncCalloutCount),
&input,
inputSize,
nil,
nil
)
In the driver's ExternalMethod
we save callback in ivars:
ivars->callbackAction = arguments->completion;
ivars->callbackAction->retain();
Next we start communication with our device in a separate thread and execute callback when we get data from device:
SendDataAsync(void *data, uint32_t dataLength) {
const int asyncDataCount = 16;
if (ivars->callbackAction != nullptr) {
Log("SendDataAsync() - got %u bytes", dataLength);
uint64_t asyncData[asyncDataCount] = { };
asyncData[0] = dataLength;
memcpy(asyncData + 1, data, dataLength);
AsyncCompletion(ivars->callbackAction, kIOReturnSuccess, asyncData, asyncDataCount);
}
Limitation 1: Our device produces data packet every 10 ms. So driver gets 100 data packets per second. Receive rate is good and we verified it by logs. However we see some delay in AsyncCallback in Swift App. It looks like async calls are queued since we see that Swift App gets callbacks for a few seconds when we stopped to send data from Driver. We measured receive rate in Swift App and it is about 50 data packets per second.
So what's a minimum call rate for AsyncCompletion
? Is it higher than 10 ms?
Maybe there is other more efficient way to asynchronously pass data from Driver to Swift App?
Limitation 2:
We thought we can buffer data packets and decrease AsyncCompletion
call rate. However asyncData
could be only 16 of uint64_t by declaration typedef uint64_t IOUserClientAsyncArgumentsArray[16];
. Size of our data packer is 112 bytes that perfectly fits to max args
size(8*16 = 128 bytes). So we can't cache and send 2 data packets.
How we can avoid this limitation and send more data via AsyncCompletion
?
Is there any other API for asynchronous communication that allows send more data back?