我正在尝试使用IOConnectCallAsyncStructMethod
来设置客户端和驱动程序之间的回调。
这就是我所说的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* arguments
中,完成是completion =(OSAction •) NULL
这是我用来检查值的IOUserClientMethodDispatch
:
[ExternalMethodType_RegisterAsyncCallback] =
{
.function = (IOUserClientMethodFunction) &Mk1dDriverClient::StaticRegisterAsyncCallback,
.checkCompletionExists = true,
.checkScalarInputCount = 0,
.checkStructureInputSize = 0,
.checkScalarOutputCount = 0,
.checkStructureOutputSize = sizeof(ReadDataStruct),
},
你知道我做错了什么吗?或者其他什么想法?
1条答案
按热度按时间rryofs0p1#
kIOReturnBadArgument
的可能原因:方法调用中的端口参数看起来很可疑:
如果你在这里传递IOKit main/master端口(
kIOMasterPortDefault
),那是错误的。这个参数的目的是提供一个通知Mach端口,它将接收异步完成消息。你将需要创建一个端口,并在适当的调度队列或runloop上调度它。我通常使用如下的方式:完成通知端口后,确保使用
IONotificationPortDestroy()
销毁它。看起来你可能正在使用runloops,在这种情况下,你可以使用
IONotificationPortGetRunLoopSource
函数来获取通知端口的runloops源,而不是调用IONotificationPortSetDispatchQueue
,然后你可以在正在使用的CFRunloop
对象上调度它。关于异步完成参数的一些注意事项:
您还没有发布您的DriverKit端
AsyncCompletion()
调用,无论如何,这不会直接导致您的问题,但一旦您修复异步调用本身,可能会爆炸:如果你的异步补全只传递了2个用户参数,你在应用端使用了错误的回调函数签名。你必须使用
IOAsyncCallback2
而不是IOAsyncCallback
形式。此外,即使您传递了3个或更多的参数,其中
IOAsyncCallback
形式是正确的,我相信由于别名规则,此代码在技术上会触发未定义的行为:我认为以下是正确的:
(Don(不能转换数组指针本身,请转换每个
void*
元素。)有关异步输出结构参数的说明
我注意到您在异步方法调用中有一个struct输出参数,它的缓冲区看起来相当小。如果您打算在初始ExternalMethod返回 * 之后 * 用DriverKit端的数据更新它,您可能会感到惊讶:未作为
IOMemoryDescriptor
传递的输出结构参数将在方法返回时立即复制到应用程序端,而不是在触发异步完成时。那么,如何解决这个问题呢?对于非常小的数据,在异步完成参数本身中传递它。对于任意大小的字节缓冲区,我所知道的唯一方法是确保输出结构参数通过
IOMemoryDescriptor
传递,它可以在驱动程序和应用程序进程之间的共享Map中持久地进行内存Map。好吧,如何将它作为内存描述符传递呢?基本上,输出结构必须大于4096字节。2是的,这实际上意味着如果你不得不让你的缓冲区变得异常的大。