Поделиться через


Подключение драйвера периферийных устройств UMDF к последовательному порту

Драйвер UMDF для периферийного устройства на последовательном порту, управляемом SerCx2, требует определенных аппаратных ресурсов для работы устройства. В этих ресурсах содержатся сведения, необходимые драйверу для открытия логического подключения к последовательному порту. Дополнительные ресурсы могут включать прерывание и один или несколько входных или выходных контактов GPIO.

Этот драйвер реализует интерфейс IPnpCallbackHardware2 и регистрирует этот интерфейс в платформе драйверов Windows во время вызова метода IDriverEntry::OnDeviceAdd драйвера. Платформа вызывает методы в интерфейсе IPnpCallbackHardware2 , чтобы уведомить драйвер об изменениях в состоянии питания устройства.

После того как последовательно подключенное периферийное устройство перейдет в неинициализированное состояние питания устройства D0, платформа драйвера вызывает метод IPnpCallbackHardware2::OnPrepareHardware драйвера, чтобы сообщить драйверу, что нужно подготовить это устройство к использованию. Во время этого вызова драйвер получает два списка аппаратных ресурсов в качестве входных параметров. Параметр pWdfResourcesRaw указывает на список необработанных ресурсов, а параметр pWdfResourcesTranslated — на список переведенных ресурсов. Оба параметра являются указателями на объекты IWDFCmResourceList . Переведенные ресурсы включают идентификатор подключения, необходимый для установки логического подключения к устройству с последовательным подключением.

Чтобы разрешить драйверу периферийных устройств UMDF получать идентификаторы подключений в списке ресурсов, INF-файл, устанавливающий драйвер, должен содержать следующую директиву в разделе DDInstall для конкретного WDF:

UmdfDirectHardwareAccess = AllowDirectHardwareAccess Дополнительные сведения об этой директиве см. в разделе Указание директив WDF в INF-файлах. Пример INX-файла (используемого для сборки соответствующего INF-файла), использующего эту директиву, см. в разделе SpbAccelerometer в примерах драйверов WDK.

В следующем примере кода показано, как метод OnPrepareHardware драйвера получает идентификатор подключения из параметра pWdfResourcesTranslated .

BOOLEAN fConnectIdFound = FALSE;
BOOLEAN fDuplicateFound = FALSE;
LARGE_INTEGER connectionId = 0;
ULONG resourceCount;

resourceCount = pWdfResourcesTranslated->GetCount();

// Loop through the resources and save the relevant ones.
for (ULONG ix = 0; ix < resourceCount; ix++)
{
    PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;

    pDescriptor = pWdfResourcesTranslated->GetDescriptor(ix);

    if (pDescriptor == NULL)
    {
        hr = E_POINTER;
        break;
    }

    // Determine the resource type.
    switch (pDescriptor->Type)
    { 
        case CmResourceTypeConnection:
            {
                // Check against the expected connection types.
                UCHAR Class = pDescriptor->u.Connection.Class;
                UCHAR Type = pDescriptor->u.Connection.Type;

                if (Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL)
                {
                    if (Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_UART)
                    {
                        if (fConnIdFound == FALSE)
                        {
                            // Save the serial controller's connection ID.
                            connectionId.LowPart = pDescriptor->u.Connection.IdLowPart;
                            connectionId.HighPart = pDescriptor->u.Connection.IdHighPart;
                            fConnectIdFound = TRUE;
                        }
                        else
                        {
                            fDuplicateFound = TRUE;
                        }
                    }
                }

                if (Class == CM_RESOURCE_CONNECTION_CLASS_GPIO)
                {
                    // Check for GPIO pin resource.
                    ...
                }
            }
            break;

        case CmResourceTypeInterrupt:
            {
                // Check for interrupt resources.
                ...
            }
            break;

        default:
            // Ignore all other resource descriptors.
            break;
    }
}

В приведенном выше примере кода идентификатор подключения для последовательно подключенного периферийного устройства копируется в переменную с именем connectionId. В следующем примере кода показано, как включить идентификатор подключения в имя пути устройства, который можно использовать для идентификации последовательного контроллера, к которому подключено периферийное устройство.

WCHAR szTargetPath[100];
HRESULT hres;

// Create the device path using the well-known resource hub
// path name and the connection ID.
//
hres = StringCbPrintfW(&szTargetPath[0],
                       sizeof(DevicePath),
                       L"\\\\.\\RESOURCE_HUB\\%0*I64x",
                       (size_t)(sizeof(LARGE_INTEGER) * 2),
                       connectionId.QuadPart);
if (FAILED(hres))
{
     // Error handling
     ...
}

В приведенном выше примере кода имя пути устройства для последовательного контроллера записывается в szTargetPath массив . В следующем примере кода это имя пути используется для открытия дескриптора файла для последовательного контроллера.

UMDF_IO_TARGET_OPEN_PARAMS openParams;

openParams.dwShareMode = 0;
openParams.dwCreationDisposition = OPEN_EXISTING;
openParams.dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED;
hres = pRemoteTarget->OpenFileByName(&szTargetPath[0],
                                     (GENERIC_READ | GENERIC_WRITE),
                                     &openParams);
if (FAILED(hres))
{
    // Error handling
    ...
}

В предыдущем примере pRemoteTarget кода параметр является указателем на объект IWDFRemoteTarget . Если вызов метода IWDFRemoteTarget::OpenFileByName завершается успешно, драйвер для последовательно подключенного периферийного устройства может использовать объект IWDFRemoteTarget для отправки запросов ввода-вывода последовательному контроллеру.

Чтобы отправить запрос на чтение или запись на периферийное устройство, драйвер сначала вызывает метод IWDFRemoteTarget::FormatRequestForRead или IWDFRemoteTarget::FormatRequestForWrite для форматирования запроса. (Интерфейс IWDFRemoteTarget наследует эти два метода от интерфейса IWDFIoTarget .)

Чтобы отправить запрос элемента управления вводом-выводом на последовательный контроллер, драйвер сначала вызывает метод IWDFRemoteTarget::FormatRequestForIoctl для форматирования запроса. (Интерфейс IWDFRemoteTarget наследует этот метод от интерфейса IWDFIoTarget .) Затем драйвер вызывает метод IWDFIoRequest::Send для отправки запроса управления вводом-выводом на последовательно подключенное периферийное устройство.

В следующем примере кода периферийный драйвер отправляет запрос на управление вводом-выводом последовательному контроллеру.

HRESULT hres;
IWDFMemory *pInputMemory = NULL;

// Create a new I/O request.
if (SUCCEEDED(hres))
{
    hres = pWdfDevice->CreateRequest(NULL, 
                                     pWdfDevice, 
                                     &pWdfIoRequest);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

// Allocate memory for the input buffer.
if (SUCCEEDED(hres))
{
    hres = pWdfDriver->CreatePreallocatedWdfMemory(pInBuffer, 
                                                   inBufferSize, 
                                                   NULL,
                                                   pWdfIoRequest,
                                                   &pInputMemory);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

// Format the request to be an I/O control request.
if (SUCCEEDED(hres))
{
    hres = pRemoteTarget->FormatRequestForIoctl(pWdfIoRequest,
                                                ioctlCode,
                                                NULL,
                                                pInputMemory, 
                                                NULL, 
                                                NULL,
                                                NULL);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

// Send the request to the serial controller.
if (SUCCEEDED(hres))
{
    ULONG Flags = fSynchronous ? WDF_REQUEST_SEND_OPTION_SYNCHRONOUS : 0;

    if (!fSynchronous)
    {
        pWdfIoRequest->SetCompletionCallback(pCallback, NULL); 
    }

    hres = pWdfIoRequest->Send(pRemoteTarget, Flags, 0);

    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

if (fSynchronous || FAILED(hres))
{
    pWdfIoRequest->DeleteWdfObject();
    SAFE_RELEASE(pWdfIoRequest);
}

В приведенном выше примере кода выполняется следующее:

  1. Переменная pWdfDevice является указателем на интерфейс IWDFDevice объекта устройства платформы, представляющего последовательно подключенное периферийное устройство. Метод IWDFDevice::CreateRequest создает запрос ввода-вывода и инкапсулирует этот запрос в экземпляре интерфейса IWDFIoRequest , на который указывает pWdfIoRequest параметр . Запрос ввода-вывода будет удален позже (см. шаг 6). Эта реализация несколько неэффективна, так как она создает и удаляет объект запроса для каждого отправляемого запроса ввода-вывода. Более эффективным подходом является повторное использование одного и того же объекта запроса для ряда запросов ввода-вывода. Дополнительные сведения см. в разделе Повторное использованием объектов запросов платформы.

  2. Переменная pWdfDriver является указателем на интерфейс IWDFDriver объекта драйвера платформы, представляющего драйвер периферийного устройства. Переменные pInBuffer и inBufferSize указывают адрес и размер входного буфера для запроса элемента управления вводом-выводом. Метод IWDFDriver::CreatePreallocatedWdfMemory создает объект памяти платформы для входного буфера и назначает объект IWDFIoRequest , на который указывает pWdfIoRequest , родительским объектом объекта памяти.

  3. Переменная pWdfRemoteTarget является удаленным целевым указателем, полученным из вызова OpenFileByName в предыдущем примере кода. Метод IWDFRemoteTarget::FormatRequestForIoctl форматирует запрос для операции управления вводом-выводом. Переменной ioctlCode присваивается один из кодов элементов управления вводом-выводом, перечисленных в таблице в разделе Интерфейс запроса последовательного ввода-вывода.

  4. Переменная fSynchronous имеет значение TRUE , если запрос управления вводом-выводом отправляется синхронно, и false , если он должен быть отправлен асинхронно. Переменная pCallback является указателем на ранее созданный интерфейс IRequestCallbackRequestCompletion . Если запрос отправляется асинхронно, вызов метода IWDFIoRequest::SetCompletionCallback регистрирует этот интерфейс. Позже вызывается метод IRequestCallbackRequestCompletion::OnCompletion для уведомления драйвера о асинхронном завершении запроса.

  5. Метод Send отправляет отформатированный запрос на запись на последовательно подключенное периферийное устройство. Переменная Flags указывает, должен ли запрос на запись отправляться синхронно или асинхронно.

  6. Если запрос отправляется синхронно, метод IWDFIoRequest::D eleteWdfObject удаляет объект запроса ввода-вывода, на который pWdfIoRequest указывает , и дочерний объект, на который pInputMemoryуказывает . Интерфейс IWDFIoRequest наследует этот метод от интерфейса IWDFObject . Если запрос отправляется асинхронно, вызов метода DeleteWdfObject должен произойти позже в методе OnCompletion драйвера.