我正在尝试使用 IOConnectCallAsyncStructMethod
在 iPadOS 版 DriverKit 中设置客户端和驱动程序之间的回调。
这就是我调用IOConnectCallAsyncStructMethod
ret = IOConnectCallAsyncStructMethod(connection, MessageType_RegisterAsyncCallback, masterPort, asyncRef, kIOAsyncCalloutCount, nullptr, 0, &outputAssignCallback, &outputSize);
其中asyncRef
是:
asyncRef[kIOAsyncCalloutFuncIndex] = (io_user_reference_t)AsyncCallback;
asyncRef[kIOAsyncCalloutRefconIndex] = (io_user_reference_t)nullptr;
和AsyncCallback
是:
static void AsyncCallback(void* refcon, IOReturn result, void** args, uint32_t numArgs)
{
const char* funcName = nullptr;
uint64_t* arrArgs = (uint64_t*)args;
ReadDataStruct* output = (ReadDataStruct*)(arrArgs + 1);
switch (arrArgs[0])
{
case 1:
{
funcName = "'Register Async Callback'";
} break;
case 2:
{
funcName = "'Async Request'";
} break;
default:
{
funcName = "UNKNOWN";
} break;
}
printf("Got callback of %s from dext with returned data ", funcName);
printf("with return code: 0x%08x.\n", result);
// Stop the run loop so our program can return to normal processing.
CFRunLoopStop(globalRunLoop);
}
但是 IOConnectCallAsyncStructMethod
始终返回 kIOReturnBadArgument
并且我可以看到,当该方法:
kern_return_t MyDriverClient::ExternalMethod(uint64_t selector, IOUserClientMethodArguments* arguments, const IOUserClientMethodDispatch* dispatch, OSObject* target, void* reference) {
kern_return_t ret = kIOReturnSuccess;
if (selector < NumberOfExternalMethods)
{
dispatch = &externalMethodChecks[selector];
if (!target)
{
target = this;
}
}
return super::ExternalMethod(selector, arguments, dispatch, target, reference);
被调用,在IOUserClientMethodArguments*参数
中,完成是completion =(OSAction •) NULL
这是我用来检查值的IOUserClientMethodDispatch
:
[ExternalMethodType_RegisterAsyncCallback] =
{
.function = (IOUserClientMethodFunction) &Mk1dDriverClient::StaticRegisterAsyncCallback,
.checkCompletionExists = true,
.checkScalarInputCount = 0,
.checkStructureInputSize = 0,
.checkScalarOutputCount = 0,
.checkStructureOutputSize = sizeof(ReadDataStruct),
},
知道我做错了什么吗?或者还有其他想法吗?
最佳答案
kIOReturnBadArgument
的可能原因:
方法调用中的端口参数看起来很可疑:
IOConnectCallAsyncStructMethod(connection, MessageType_RegisterAsyncCallback, masterPort, …
------------------------------------------------------------------------------^^^^^^^^^^
如果您将 IOKit 主/主端口 ( kIOMasterPortDefault
) 传递到此处,那么这是错误的。该参数的目的是提供一个通知 Mach 端口,该端口将接收异步完成消息。您需要创建一个端口并将其调度到适当的调度队列或运行循环上。我通常使用这样的东西:
// Save this somewhere for the entire time you might receive notification callbacks:
IONotificationPortRef notify_port = IONotificationPortCreate(kIOMasterPortDefault);
// Set the GCD dispatch queue on which we want callbacks called (can be main queue):
IONotificationPortSetDispatchQueue(notify_port, callback_dispatch_queue);
// This is what you pass to each async method call:
mach_port_t callback_port = IONotificationPortGetMachPort(notify_port);
完成通知端口后,请确保使用 IONotificationPortDestroy()
销毁它。 .
看起来您可能正在使用运行循环。在这种情况下,不要调用 IONotificationPortSetDispatchQueue
,您可以使用IONotificationPortGetRunLoopSource
函数来获取通知端口的运行循环源,然后您可以将其安排在CFRunloop
上您正在使用的对象。
有关异步完成参数的一些注意事项:
您还没有发布您的 DriverKit 端 AsyncCompletion()
调用,无论如何这不会导致您立即出现问题,但一旦您修复异步调用本身,它可能会崩溃:
如果您的异步完成仅传递 2 个用户参数,则您在应用程序端使用了错误的回调函数签名。而不是IOAsyncCallback
您必须使用IOAsyncCallback2
表格。
此外,即使您传递 3 个或更多参数,其中 IOAsyncCallback
形式是正确的,我相信这段代码在技术上会由于别名规则而触发未定义的行为:
uint64_t* arrArgs = (uint64_t*)args;
ReadDataStruct* output = (ReadDataStruct*)(arrArgs + 1);
switch (arrArgs[0])
我认为以下内容是正确的:
ReadDataStruct* output = (ReadDataStruct*)(args + 1);
switch ((uintptr_t)args[0])
(不要转换数组指针本身,而是转换每个 void*
元素。)
有关异步输出结构参数的注释
我注意到您的异步方法调用中有一个结构输出参数,其缓冲区看起来相当小。如果您计划在初始ExternalMethod返回后使用DriverKit端的数据更新它,您可能会感到惊讶:输出结构参数不是作为 IOMemoryDescriptor
传递的。将在方法返回时立即复制到应用程序端,而不是在触发异步完成时。
那么如何解决这个问题呢?对于非常小的数据,将其传递到异步完成参数本身中。对于任意大小的字节缓冲区,我知道的唯一方法是确保输出结构参数通过 IOMemoryDescriptor
传递,它可以在驱动程序和应用程序进程之间的共享映射中持久地进行内存映射。好的,如何将它作为内存描述符传递?基本上,输出结构必须大于 4096 字节。是的,这本质上意味着如果您必须使缓冲区变得异常大。
关于c++ - IOUserClientMethodArguments 完成值始终为 NULL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74478546/