Как использовать запрос устройства IOUSBHostDevice / IOUSBHostInterface? - PullRequest
0 голосов
/ 27 января 2020

В настоящее время я экспериментирую с Apple I / O Kit для разработки модуля ядра. Я пытаюсь создать драйвер устройства для беспроводной карты USB (TP-Link WN722N-v1) с помощью IOUSBHostDevice. Мне удалось загрузить kext, и он корректно прощупывает карту, но мне нужно отправить прошивку на карту, что я пытаюсь сделать в probe (). Я не обладаю достаточными знаниями о том, как все происходит, но мне кажется, что мне нужно отправить запрос в конечную точку конфигурации 0, но способы, которые я пробовал, получают ядро ​​pani c, и другие примеры, которые у меня есть, использование устаревшего DeviceRequest без обмена аргументами с новым deviceRequest. У меня есть ссылка для написания прошивки из проекта github "BrcmPatchRAM", но все вилки используют старый IOUSBInterface вместо IOUSBHostInterface. Я искал на этом сайте похожие вопросы, но не нашел ничего полезного в этом вопросе, я также искал документацию веб-сайта разработчика Apple, и она ссылается также на старые способы DeviceRequest. Какой правильный способ переписать это, например:

IOReturn USBInterfaceShim::hciCommand(void* command, UInt16 length)
{
    IOUSBDevRequest request =
    {
        .bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBDevice),
        .bRequest = 0,
        .wValue = 0,
        .wIndex = 0,
        .wLength = length,
        .pData = command
    };
    return m_pInterface->DeviceRequest(&request);
}

Использование некоторой другой реализации этой функции выдает ошибку при загрузке драйвера и говорит, что я отправляю запрос перед вызовом prepare, но я позвонил подготовить перед отправкой запроса.

Вот часть моего грязного кода:

<code><pre>IOReturn AirPort_Atheros9271::pipeCommand(UInt8 requestType, UInt8 command, UInt16 address, IOMemoryDescriptor *buffer)
{
    DeviceRequest request;
    request.bmRequestType = requestType;
    request.bRequest = command;
    request.wValue = address;
    request.wIndex = 0;
    request.wLength = buffer->getLength();

    uint32_t bytesTransferred;
    return fInterface->deviceRequest(request, buffer, bytesTransferred, kUSBHostStandardRequestCompletionTimeout);
}

IOReturn AirPort_Atheros9271::pipeCommand(UInt8 requestType, UInt8 command, UInt16 address, void *buffer, UInt16 length)
{
    DeviceRequest request;
    request.bmRequestType = requestType;
    request.bRequest = command;
    request.wValue = address;
    request.wIndex = 0;
    request.wLength = length;

    uint32_t bytesTransferred;
    return fInterface->deviceRequest(request, buffer, bytesTransferred, kUSBHostStandardRequestCompletionTimeout);
}

bool TL_WN722N::performUpgrade()
{
    IOLog("TL_WN722N::[%04x:%04x]: Performing firmware upgrade.\n", fVendorId, fProductId);

    OSData *firmwareData = OSData::withBytes(ar9271_fw, ar9271_fw_len);
    IOMemoryDescriptor *buffer = IOMemoryDescriptor::withAddress((void*)firmwareData->getBytesNoCopy(), firmwareData->getLength(), kIODirectionIn);
    bool success = true;
    IOLog("TL_WN722N::[%04x:%04x]: I have firmwareData and created the buffer\n", fVendorId, fProductId);
    IOLockLock(fCompletionLock);

    IOReturn result;
    if (buffer!=NULL)
    {
        IOLog("TL_WN722N::[%04x:%04x]: Buffer is not null, now calling prepare()\n", fVendorId, fProductId);
        if ((result = buffer->prepare(kIODirectionNone)) == kIOReturnSuccess)
        {
            IOLog("TL_WN722N::[%04x:%04x]: prepare() called and pass! Piping I - writing firmware\n", fVendorId, fProductId);
            IOLog("TL_WN722N::[%04x:%04x]: Resultado de prepare: Result: %d\n", fVendorId, fProductId,result);

            IOLog("TL_WN722N::[%04x:%04x]: About to excecute pipeCommand\n", fVendorId, fProductId);
            result = pipeCommand(0x40, FIRMWARE_DOWNLOAD, AR9271_FIRMWARE >> 8, buffer); //TODO: EXPLOTION LINE
            if (result>0) {
                IOLog("TL_WN722N::[%04x:%04x]: Resultado de pipeCommand: %d\n", fVendorId, fProductId,result);
            }

            if (result != kIOReturnSuccess)
                IOLog("TL_WN722N::[%04x:%04x]: Unable to write the firmware (0x%08x).\n", fVendorId, fProductId, result);
            else
            {
                if ((result = pipeCommand(0x40, FIRMWARE_DOWNLOAD_COMP, AR9271_FIRMWARE_TEXT >> 8, NULL, 0)) != kIOReturnSuccess)
                    IOLog("TL_WN722N::[%04x:%04x]: Unable to write the firmware complete sequence (0x%08x).\n", fVendorId, fProductId, result);
                else
                {
                    IOLog("TL_WN722N::[%04x:%04x]: Success in writing the firmware sequence.\n", fVendorId, fProductId);
                    success = true;
                }
            }
        }
        else
            IOLog("TL_WN722N::[%04x:%04x]: Failed to prepare write memory buffer (0x%08x).\n", fVendorId, fProductId, result);

        if ((result = buffer->complete()) != kIOReturnSuccess)
            IOLog("TL_WN722N::[%04x:%04x]: Failed to complete write memory buffer (0x%08x).\n", fVendorId, fProductId, result);


    }
    else
        IOLog("TL_WN722N::[%04x:%04x]: Unable to allocate write memory buffer.\n", fVendorId, fProductId);

    IOLockUnlock(fCompletionLock);
    OSSafeReleaseNULL(buffer);
    OSSafeReleaseNULL(firmwareData);
    return success;
}

1 Ответ

0 голосов
/ 28 января 2020

Собираем несколько частей здесь:

Использование какой-либо другой реализации этой функции выдает ошибку при загрузке драйвера и говорит, что я отправляю запрос перед вызовом prepare, но я сделал Вызывается подготовить перед отправкой запроса.

Это дает нам довольно большую подсказку, что ваша проблема связана с буфером памяти. Итак, давайте посмотрим, где происходит создание и prepare():

    IOMemoryDescriptor *buffer = IOMemoryDescriptor::withAddress(
        (void*)firmwareData->getBytesNoCopy(), firmwareData->getLength(),
        kIODirectionIn);
    //  ^^^^^^^^^^^^^^^^

Итак, вы создаете дескриптор памяти ввода (чтения).

        if ((result = buffer->prepare(kIODirectionNone)) == kIOReturnSuccess)

kIODirectionNone просто использует направление создания, поэтому вы готовитесь к чтению данных с устройства.

А затем ввод / вывод:

            result = pipeCommand(0x40, FIRMWARE_DOWNLOAD, AR9271_FIRMWARE >> 8, buffer); //TODO: EXPLOTION LINE
    // --------------------------^^^^

Это означает, что ваш bmRequestType равен 0x40, и следовательно, не установлен бит 0x80. Это означает, что это хост-устройство, т.е. вывод / запись.

Итак, у вас есть несоответствие направления: вы готовите некоторую память для чтения с устройства USB, а затем пытаетесь использовать ее для записи к устройству. Это не сработает.

Еще несколько комментариев:

  • OSData на самом деле не идеальный кандидат для распределения буфера ввода / вывода. Если вы хотите выделить системную память для ввода-вывода, используйте IOBufferMemoryDescriptor. Если ar9271_fw является статически распределенным массивом, вы также можете просто обернуть его, используя IOMemoryDescriptor::withAddress() - это позволяет избежать копирования, выполняемого OSData::withBytes().
  • Вы выполняете ввод / вывод, удерживая IOLockLock(fCompletionLock); замок. Это не очень хорошая идея даже при запуске asyn c I / O, но похоже, что вы используете блокирующую версию deviceRequest? Это почти наверняка не то, что вы хотите: вызов функции может буквально занять секунд для завершения или сбоя. Вы не должны держать замки так долго.
...