USBDriverKit驱动程序和客户端应用程序之间如何通信?

Myu*_*rik 2 macos ipados driverkit

我们正在 macOS 上试验 DriverKit,而在 iPadOS 上 DriverKit 仍处于测试阶段。我们想要构建一个 iPad 驱动程序,允许我们的 iPad 应用程序与 USB 设备进行通信。

\n

我们做了什么:

\n
    \n
  1. 配置并实现了用作提供程序的驱动程序USBDriverKit::IOUSBHostInterface。当我们将设备插入 USB 端口时,macOS 会自动匹配/启动该驱动程序。接下来我们利用USBDriverKit::IOUSBHostPipe从我们的设备发送/接收数据。我们现在在日志中打印来自设备的数据。
  2. \n
  3. 研究DriverKit 扩展和客户端应用程序之间的通信
  4. \n
  5. 配置并实现了一个基于IOUserClientmacOS 应用程序并允许使用 API 打开通信通道的驱动程序IOServiceOpen。驱动程序有回调将数据传递到 macOS 客户端应用程序。
  6. \n
\n

目前,我们希望组合 2 个驱动程序,并使用回调将从 USB 设备接收到的数据传递到我们的客户端应用程序。不幸的是,我们陷入了困境,因为现在我们有 2 个驱动程序实例:

\n
    \n
  1. 当设备插入时,第一个实例由 macOS 自动运行
  2. \n
  3. virtual kern_return_t NewUserClient(uint32_t type, IOUserClient** userClient)当我们从客户端应用程序连接并调用方法时创建第二个实例。
  4. \n
\n

因此,我们不能使用第二个实例进行 USB 设备通信,因为它有错误的提供程序(IOUserClient),kern_return_t Start(IOService * provider)但我们需要IOUSBHostInterface启动:

\n
\xc2\xa0 \xc2\xa0 ivars->interface = OSDynamicCast(IOUSBHostInterface, provider);\n\xc2\xa0 \xc2\xa0 if(ivars->interface == NULL) {\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 ret = kIOReturnNoDevice;\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 goto Exit;\n\xc2\xa0 \xc2\xa0 }\n
Run Code Online (Sandbox Code Playgroud)\n

我们做错了吗?也许IOUSBHostInterface我们应该从 UserClient 驱动程序手动进行匹配或使用其他方法,而不是自动匹配?

\n

正如我们所知,我们必须在NewUserClient方法中创建一个新的服务实例,并且无法返回操作系统运行的驱动程序:

\n
kern_return_t IMPL(MyDriver, NewUserClient)\n{\n\xc2\xa0 \xc2\xa0 kern_return_t ret = kIOReturnSuccess;\n\xc2\xa0 \xc2\xa0 IOService* client = nullptr;\n\xc2\xa0 \xc2\xa0 ret = Create(this, "UserClientProperties", &client);\n\n\xc2\xa0 \xc2\xa0 if (ret != kIOReturnSuccess)\n\xc2\xa0 \xc2\xa0 {\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 goto Exit;\n\xc2\xa0 \xc2\xa0 }\n\n\xc2\xa0 \xc2\xa0 *userClient = OSDynamicCast(IOUserClient, client);\n\n\xc2\xa0 \xc2\xa0 if (*userClient == NULL)\n\xc2\xa0 \xc2\xa0 {\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 client->release();\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 ret = kIOReturnError;\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 goto Exit;\n\xc2\xa0 \xc2\xa0 }\nExit:\n\xc2\xa0 \xc2\xa0 return ret;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

顺便说一句,也许有更简单的方法将数据从 USB 设备转发到 iPadOS 应用程序?

\n

sls*_*sls 5

我不了解 USBDriverKit 和 iPadOS,但也许我在 macOS 上使用 PCIDriverKit 的有限经验可以作为有用的类比。

我已经为自定义 PCI 设备实现了 DEXT,以及与驱动程序通信以对设备执行操作的用户空间应用程序。

DEXT 由两个类组成(两对.iig.cpp文件):

  • class MyDriver: public IOService
  • class MyDriverClient: public IOUserClient

具有以下内容Info.plist

<dict>
    <key>IOKitPersonalities</key>
    <dict>
        <key>MyDriver</key>
        <dict>
            <key>CFBundleIdentifierKernel</key <string>com.apple.kpi.iokit</string>
            <key>IOClass</key> <string>IOUserService</string>
            <key>IOPCIMatch</key> <string>0xcafebabe</string>
            <key>IOPCITunnelCompatible</key> <true/>
            <key>IOProviderClass</key> <string>IOPCIDevice</string>
            <key>IOUserClass</key> <string>MyDriver</string>
            <key>IOUserServerName</key> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
            <key>MyDriverClientProperties</key>
            <dict>
                <key>IOClass</key>
                <string>IOUserUserClient</string>
                <key>IOUserClass</key>
                <string>MyDriverClient</string>
            </dict>
        </dict>
    </dict>
</dict>
Run Code Online (Sandbox Code Playgroud)

当系统根据IOPCIMatch属性值发现具有匹配供应商/设备 ID 的 PCI 设备时,它:

  1. 在内核中实例化一个IOUserService类(根据IOClass属性的要求)以促进与驻留在用户空间中的 DEXT 的通信
  2. 为 DEXT 生成用户空间进程。
  3. 在该过程中实例化所需的提供程序类(此处:IOPCIDevice根据属性IOProviderClass)。
  4. 实例化主类(此处:MyDriver根据IOUserClass属性),为其提供指向上面创建的指针provider

作为其Start实现的一部分,MyDriver调用RegisterService()使其自身出现在 I/O 注册表中(例如ioreg -l)。

当用户空间应用程序(具有适当的权利)稍后找到注册的MyDriver服务(例如使用IOServiceGetMatchingService())并打开它(IOServiceOpen())时,最终会在 DEXT 端调用MyDriver::NewUserClient()docs),如下所示:

kern_return_t IMPL(MyDriver, NewUserClient)
{
    kern_return_t ret = kIOReturnSuccess;
    IOService* client = nullptr;

    ret = Create(this, "MyDriverClientProperties", &client);
    // ... Error handling

    *userClient = OSDynamicCast(IOUserClient, client);
    // ... Error handling

    // If you need to remember/keep track of the client, you could stash it in the `ivars`, already casted to its true type:
    ivars->client = OSDynamicCast(MyDriverClient, client);

    // ...
}
Run Code Online (Sandbox Code Playgroud)

到底要做什么由提供的属性字典(在 中任意命名的键Create下)决定。在这种情况下,在内核(属性)中创建一个(双-不是拼写错误)对象,并在用户空间(属性)中创建一个对象。MyDriverClientPropertiesInfo.plistIOUserUserClient UserIOClassMyDriverClientIOUserClass

您在问题中的描述似乎表明不然,但至少在我的情况下,客户端对象被MyDriver作为实例提供provider,并且我能够获得对它的引用:

kern_return_t IMPL(MyDriverClient, Start)
{
    kern_return_t ret;
    ret = Start(provider, SUPERDISPATCH);
    // ... Error handling

    ivars->driver = OSDynamicCast(MyDriver, provider);
    // ... Error handling

    // ...
}
Run Code Online (Sandbox Code Playgroud)

稍后,当调用例如MyDriverClient::ExternalMethoddocsivars->driver )时,我可以用来调用该类的功能MyDriver并与底层设备交互。