Как создать обратный вызов IOUSBHostPipe :: CompleteAsyncIO? - PullRequest
3 голосов
/ 25 мая 2020

Я пишу SystenExtension для связи с USB-устройством. Мой первоначальный план - создать класс Transfer, который выделяет необходимый IOMemoryDescriptor , а затем передать интерфейс, с которым я хочу, чтобы класс Transfer мог взаимодействовать. Я хотел бы, чтобы обратный вызов , полученный в результате завершения AsyncIO , был передан в класс Transfer. Если мне нужно поставить в очередь несколько чтений, я могу создать больше экземпляров этого класса. В обратном вызове completeCallback я бы распаковал данные, а затем отправил бы другой чтение .

Я создаю класс Transfer с OSTypeAlloc(Transfer).

Проблема, с которой я столкнулся, заключается в том, что создание OSAction не удается с этой трассировкой стека:

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), то создание объекта OSAction работает нормально.

Вызов IOUSBHostInterface::Open создается из IOUserClass передачи указателя на IOUserClass в качестве первого аргумента Open. Можно ли это сделать? Или требуется, чтобы объект IOService также был тем же объектом, который получает обратные вызовы от AsyncIO.

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);
};
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.
}

1 Ответ

1 голос
/ 25 мая 2020

Как вы, наверное, обнаружили, это нигде не задокументировано. Однако из моих экспериментов у меня сложилось впечатление, что OSAction в основном является оболочкой для сообщений IP C. Если это правда, это будет означать, что целевой объект действия должен иметь представление как в контексте ядра, так и в контексте декста, чтобы ядро ​​имело смысл отправлять ему сообщение.

Это не похоже на быть в случае с объектами, созданными с помощью OSTypeAlloc(), так как это просто создает объект в дексте. Кажется, что только объекты, созданные с использованием IOService::Create, получают представления в обоих контекстах. Эта функция работает только для подклассов IOService, которые, конечно, довольно тяжеловесны.

Я бы порекомендовал отправлять ваши завершения ввода-вывода с помощью метода завершения в вашем основном объекте драйвера или пользовательском клиентском объекте, и в этом методе завершения перенаправить вызов тому, что действительно вызвало ввод-вывод. Вы можете сохранить произвольные данные в OSAction, передав sizeof(some_struct) в качестве первого аргумента вашего CreateAction… вызова, который его создает, а затем получить указатель на эту структуру, используя

some_struct* my_context = static_cast<some_struct*>(action->GetReference());

Вы можете поместить обычный указатель на функцию и указатель на конечную цель там, или что угодно, что вам действительно нравится. target для OSAction, используемого для обратного вызова ввода-вывода. Оба они должны быть теневыми объектами ядра / декста, но это могут быть разные объекты.

...