首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何创建IOUSBHostPipe::CompleteAsyncIO回调?

如何创建IOUSBHostPipe::CompleteAsyncIO回调?
EN

Stack Overflow用户
提问于 2020-05-25 22:59:50
回答 1查看 341关注 0票数 4

我正在编写一个SystemExtension来与usb设备通信。我最初的计划是创建一个分配必要IOMemoryDescriptor的类Transfer,然后传递我希望Transfer类与之通信的接口。我希望将AsyncIO完成后得到的callback添加到Transfer类中。如果我需要对多个读取进行排队,那么我可以创建这个类的更多实例。在回调completeCallback中,我将解压数据,然后提交另一个read

我用OSTypeAlloc(Transfer)创建了Transfer类。

我面临的问题是创建OSAction失败,堆栈跟踪:

代码语言:javascript
运行
复制
0   libsystem_kernel.dylib          0x000000010df950ce __pthread_kill + 10
1   libsystem_pthread.dylib         0x000000010e07cf6a pthread_kill + 152
2   libsystem_c.dylib               0x000000010df2c3a0 abort + 120
3   com.apple.DriverKit             0x000000010dc9124b __assert_rtn + 102
4   com.apple.DriverKit             0x000000010dc91a34 OSCopyOutObjects(IOUserServer_IVars*, IORPCMessageMach*, IORPCMessage*, bool) (.cold.4) + 35
5   com.apple.DriverKit             0x000000010dc7a2ff OSCopyOutObjects(IOUserServer_IVars*, IORPCMessageMach*, IORPCMessage*, bool) + 328
6   com.apple.DriverKit             0x000000010dc79126 InvokeRemote(IOUserServer_IVars*, unsigned long long, IORPC) + 159
7   com.apple.DriverKit             0x000000010dc796b6 OSMetaClassBase::Invoke(IORPC) + 754
8   com.apple.DriverKit             0x000000010dc90048 OSAction::Create_Call(OSObject*, unsigned long long, unsigned long long, unsigned long, OSAction**) + 212
9   com.apple.DriverKit             0x000000010dc7ca15 OSAction::Create(OSObject*, unsigned long long, unsigned long long, unsigned long, OSAction**) + 37
10  sc.example.MyUserUSBInterfaceDriver 0x000000010dc3bffc Transfer::CreateActioncompleteCallback(unsigned long, OSAction**) + 60 (Transfer.iig.cpp:175)
11  sc.example.MyUserUSBInterfaceDriver 0x000000010dc34e6e Transfer::setup(IOUSBHostInterface*, int, int) + 638 (Transfer.cpp:53)

相反,如果我将回调移动到在附加usb设备时由系统实例化的类中定义、实现和创建(这个类是用键IOUserClass在plist中指定的),那么创建OSAction对象就可以正常工作。

IOUSBHostInterface::Open的调用是从IOUserClass发出的,它将指向IOUserClass的指针作为第一个参数传递给Open。这样做应该没问题吧?或者要求IOService对象也是从AsyncIO接收回调的同一对象。

代码语言:javascript
运行
复制
class Transfer : public OSObject {
public:
  bool init() override;
  void free() override;

  bool setup(IOUSBHostInterface* interface, int endpointAddress, int maxPacketSize) LOCALONLY;
  bool submit() LOCALONLY;

protected:
   virtual void completeCallback(OSAction* action,
                        IOReturn status,
                        uint32_t actualByteCount,
                        uint64_t completionTimestamp) TYPE(IOUSBHostPipe::CompleteAsyncIO);
};
代码语言:javascript
运行
复制
struct Transfer_IVars {
  IOUSBHostInterface* interface;
  int maxPacketSize;
  IOUSBHostPipe* pipe;
  IOBufferMemoryDescriptor* buffer;
  OSAction* ioCompleteCallback;
};

bool Transfer::init() {
  LOG_DEBUG();
  if (!super::init()) {
    LOG_ERROR("super::init failed");
  }

  if (ivars = IONewZero(Transfer_IVars, 1); ivars == nullptr) {
    LOG_ERROR("Allocating ivars failed");
    return false;
  }

  return true;
}

void Transfer::free() {
  LOG_DEBUG();
  IOSafeDeleteNULL(ivars, Transfer_IVars, 1);
  super::free();
}

bool Transfer::setup(IOUSBHostInterface* interface, int endpointAddress,
                     int maxPacketSize) {
  ivars->interface = interface;
  ivars->maxPacketSize = maxPacketSize;

  if (auto ret = interface->CopyPipe(endpointAddress, &ivars->pipe);
      ret != kIOReturnSuccess) {
    LOG_ERROR("Could not copy pipe: %{public}s, endpointAddress: %{public}d",
              kern_return_t_toCStr(ret), endpointAddress);
  }

  if (auto ret =
        interface->CreateIOBuffer(kIOMemoryDirectionIn, maxPacketSize, &ivars->buffer);
      ret != kIOReturnSuccess) {
    LOG_ERROR("CreateIOBuffer failed, ret: %{public}d", ret);
    return false;
  }

  if (auto ret = CreateActioncompleteCallback(0, &ivars->ioCompleteCallback);
      ret != kIOReturnSuccess) {
    LOG_ERROR("Failed to set iocomplete callback, ret: %{public}d", ret);
    return false;
  }
  return true;
}

bool Transfer::submit() {
  if (auto ret = ivars->pipe->AsyncIO(ivars->buffer, ivars->maxPacketSize,
                                      ivars->ioCompleteCallback, 0);
      ret != kIOReturnSuccess) {
    LOG_ERROR("Failed to call AsyncIO, ret: %{public}d", ret);
    return false;
  }
  return true;
}

void IMPL(Transfer, completeCallback) {
  LOG_DEBUG(
    "Complete callback bytecount %{public}d, timestamp: %{public}llu, status %{public}s",
    actualByteCount, completionTimestamp, kern_return_t_toCStr(status));
// TODO Unpack and then schedule another read.
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-05-25 23:42:50

正如您可能已经发现的,这在任何地方都没有文档。然而,从我的实验中,我得到的印象是,OSAction基本上是一个IPC消息的包装器。如果为真,这将意味着操作的目标对象必须在内核和dext上下文中都有表示,以便内核向其发送消息是有意义的。

使用OSTypeAlloc()创建的对象似乎不是这种情况,因为它只是在dext中创建了一个对象。似乎只有使用IOService::Create创建的对象才能在这两种上下文中获得表示。不过,这个函数只适用于IOService子类,这些子类当然是相当重量级的。

我的建议是通过主驱动程序对象或用户客户端对象中的完成方法发送I/O完成,然后在该完成方法中,将调用转发给实际发起I/O的任何对象。通过将sizeof(some_struct)作为创建任意数据的CreateAction…调用的第一个参数传递给创建它的CreateAction…调用,可以将任意数据存储在OSAction中,然后使用

代码语言:javascript
运行
复制
some_struct* my_context = static_cast<some_struct*>(action->GetReference());

你可以在里面放一个普通的函数指针和指向最终目标的指针,或者你真正喜欢的任何东西。

我不认为传递给接口或设备的Open()函数的客户机对象需要与用于I/O回调的OSAction的目标相同。它们都需要是内核/dext阴影对象,但它们可以是不同的对象。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62005112

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档