Share via


Hardwareressourcen für User-Mode SPB-Peripherietreiber

Die Codebeispiele in diesem Thema zeigen, wie der UMDF-Treiber (User-Mode Driver Framework ) für ein Peripheriegerät auf einem einfachen Peripheriebus (SPB) die Hardwareressourcen abruft, die für den Betrieb des Geräts erforderlich sind. In diesen Ressourcen enthalten sind die Informationen, die der Treiber verwendet, um eine logische Verbindung mit dem Gerät herzustellen. Zusätzliche Ressourcen können einen Interrupt und mindestens einen GPIO-Eingabe- oder Ausgabepins enthalten. (Ein GPIO-Pin ist ein Pin an einem universellen E/A-Controllergerät, das als Eingabe oder Ausgabe konfiguriert ist. Weitere Informationen finden Sie unter GPIO-Controllertreiber.) Im Gegensatz zu einem Gerät mit Speicherzuordnung benötigt ein mit SPB verbundenes Peripheriegerät keinen Block von Systemspeicheradressen, um seine Register zuzuordnen.

Dieser Treiber implementiert eine IPnpCallbackHardware2-Schnittstelle und registriert diese Schnittstelle beim UMDF während des Aufrufs der IDriverEntry::OnDeviceAdd-Methode des Treibers. Das Framework ruft die Methoden in der IPnpCallbackHardware2-Schnittstelle auf, um den Treiber über Änderungen im Energiezustand des Geräts zu benachrichtigen.

Wenn die Stromversorgung des mit SPB verbundenen Peripheriegeräts wiederhergestellt wird, ruft das Treiberframework die IPnpCallbackHardware2::OnPrepareHardware-Methode auf, um den Treiber zu benachrichtigen, dass dieses Gerät für die Verwendung vorbereitet sein muss. Während dieses Aufrufs empfängt der Treiber zwei Listen von Hardwareressourcen als Eingabeparameter. Der Parameter pWdfResourcesRaw verweist auf die Liste der Rohressourcen, und der Parameter pWdfResourcesTranslated verweist auf die Liste der übersetzten Ressourcen. Beide Parameter sind Zeiger auf IWDFCmResourceList-Objekte . Die übersetzten Ressourcen enthalten die Verbindungs-ID , die der SPB-Peripherietreiber benötigt, um eine logische Verbindung mit dem mit SPB verbundenen Peripheriegerät herzustellen. Weitere Informationen finden Sie unter Verbindungs-IDs für SPB-Peripheriegeräte.

Damit ein UMDF-Peripherietreiber Verbindungs-IDs in seiner Ressourcenliste empfangen kann, muss die INF-Datei, die den Treiber installiert, die folgende Anweisung im WDF-spezifischen DDInstall-Abschnitt enthalten:

UmdfDirectHardwareAccess = AllowDirectHardwareAccess Weitere Informationen zu dieser Direktive finden Sie unter Angeben von WDF-Direktiven in INF-Dateien.

Das folgende Codebeispiel zeigt, wie die OnPrepareHardware-Methode des Treibers die Verbindungs-ID aus dem pWdfResourcesTranslated-Parameter abruft .

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_I2C)
                {
                    if (fConnIdFound == FALSE)
                    {
                        // Save the SPB 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;
    }
}

Im vorherigen Codebeispiel wird die Verbindungs-ID für ein mit SPB verbundenes Peripheriegerät in eine Variable namens connectionIdkopiert. Das folgende Codebeispiel zeigt, wie Sie die Verbindungs-ID in einen Gerätepfadnamen integrieren, der zum Identifizieren des Peripheriegeräts verwendet werden kann.

WCHAR szTargetPath[100];
HRESULT hres;

// Create the device path using the well-known resource hub
// path name and the connection ID.
//
// TODO: Replace this hardcoded string with the appropriate
//       helper method from Reshub.h when available.
hres = StringCbPrintfW(&szTargetPath[0],
                       sizeof(szTargetPath),
                       L"\\\\.\\RESOURCE_HUB\\%0*I64x",
                       (size_t)(sizeof(LARGE_INTEGER) * 2),
                       connectionId.QuadPart);
if (FAILED(hres))
{
     // Error handling
     ...
}

Im vorherigen Codebeispiel wird der Pfadname für das mit SPB verbundene Peripheriegerät in das szTargetPath Array geschrieben. Im folgenden Codebeispiel wird dieser Gerätepfadname verwendet, um ein Dateihandle für das mit SPB verbundene Peripheriegerät zu öffnen.

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

Im vorherigen Codebeispiel ist die pRemoteTarget Variable ein Zeiger auf ein IWDFRemoteTarget-Objekt . Wenn der Aufruf der IWDFRemoteTarget::OpenFileByName-Methode erfolgreich ist, kann der Treiber für das mit SPB verbundene Peripheriegerät das IWDFRemoteTarget-Objekt verwenden, um E/A-Anforderungen an das Peripheriegerät zu senden. Bevor der Treiber eine Lese-, Schreib- oder IOCTL-Anforderung an das Peripheriegerät sendet, ruft der Treiber die IWDFRemoteTarget::FormatRequestForRead-, IWDFRemoteTarget::FormatRequestForWrite- oder IWDFRemoteTarget:FormatRequestForIoctl-Methode auf, um die E/A-Anforderung zu formatieren. Die IWDFRemoteTarget-Schnittstelle erbt diese drei Methoden von der IWDFIoTarget-Schnittstelle . Als Nächstes ruft der Treiber die IWDFIoRequest::Send-Methode auf, um die E/A-Anforderung an das mit SPB verbundene Peripheriegerät zu senden.

Im folgenden Codebeispiel ruft der SPB-Peripherietreiber die Send-Methode auf, um eine IRP_MJ_WRITE-Anforderung an das mit SPB verbundene Peripheriegerät zu senden.

HRESULT hres;
IWDFMemory *pInputMemory = NULL;
IWDFRemoteTarget *pWdfIoRequest = 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
        ...
    }

    // After this call, the parent holds the only reference.
    pWdfMemory->Release();
}

// Format the I/O request for a write operation.
if (SUCCEEDED(hres))
{
    hres = pRemoteTarget->FormatRequestForWrite(pWdfIoRequest,
                                                NULL,
                                                pInputMemory, 
                                                NULL, 
                                                0);
    if (FAILED(hres))
    {
        // Error handling
        ...
    }
}

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

    // Set the I/O completion callback.
    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);
}

Im vorherigen Codebeispiel wird Folgendes ausgeführt:

  1. Die pWdfDevice Variable ist ein Zeiger auf die IWDFDevice-Schnittstelle des Framework-Geräteobjekts, das das mit SPB verbundene Peripheriegerät darstellt. Die IWDFDevice::CreateRequest-Methode erstellt eine E/A-Anforderung und kapselt diese Anforderung in der IWDFIoRequest-Schnittstelle instance, auf die die pWdfIoRequest Variable verweist.
  2. Die pWdfDriver Variable ist ein Zeiger auf die IWDFDriver-Schnittstelle des Frameworktreiberobjekts, das den SPB-Peripherietreiber darstellt. Die pInBuffer Variablen und inBufferSize geben die Adresse und die Größe des Eingabepuffers an, der die Daten für die Schreibanforderung enthält. Die IWDFDriver::CreatePreallocatedWdfMemory-Methode erstellt ein Framework-Speicherobjekt für den Eingabepuffer und bezeichnet das IWDFIoRequest-Objekt , auf das von pWdfIoRequest als übergeordnetes Objekt des Speicherobjekts verwiesen wird (sodass das Speicherobjekt automatisch freigegeben wird, wenn sein übergeordnetes Objekt freigegeben wird). Nachdem der Treiber die Release-Methode aufgerufen hat, um seinen lokalen Verweis auf das Speicherobjekt freizugeben, enthält das übergeordnete Objekt den einzigen Verweis auf dieses Objekt.
  3. Die pWdfRemoteTarget Variable ist der Remotezielzeiger, der aus dem OpenFileByName-Aufruf in einem früheren Codebeispiel abgerufen wurde. Die IWDFRemoteTarget::FormatRequestForWrite-Methode formatiert die E/A-Anforderung für einen Schreibvorgang.
  4. Die fSynchronous Variable ist TRUE , wenn die Schreibanforderung synchron gesendet werden soll, und false , wenn sie asynchron gesendet werden soll. Die pCallback Variable ist ein Zeiger auf eine zuvor erstellte IRequestCallbackRequestCompletion-Schnittstelle . Wenn die Anforderung asynchron gesendet werden soll, registriert der Aufruf der IWDFIoRequest::SetCompletionCallback-Methode diese Schnittstelle. Später wird die IRequestCallbackRequestCompletion::OnCompletion-Methode aufgerufen, um den Treiber zu benachrichtigen, wenn die Anforderung asynchron abgeschlossen wird.
  5. Die Send-Methode sendet die formatierte Schreibanforderung an das mit SPB verbundene Peripheriegerät. Die Flags Variable gibt an, ob die Schreibanforderung synchron oder asynchron gesendet werden soll.
  6. Wenn die Anforderung synchron gesendet wird, löscht die IWDFIoRequest::D eleteWdfObject-Methode sowohl das E/A-Anforderungsobjekt, auf das von pWdfIoRequest verweist, als auch das untergeordnete Objekt, auf das von pInputMemoryverweist. Die IWDFIoRequest-Schnittstelle erbt diese Methode von der IWDFObject-Schnittstelle . Wenn die Anforderung asynchron gesendet wird, sollte der Aufruf der DeleteWdfObject-Methode später in der OnCompletion-Methode des Treibers erfolgen.

Eine alternative Implementierung des vorherigen Codebeispiels kann während der Treiberinitialisierung IWDFIoRequest - und IWDFMemory-Objekte erstellen und dieselben Objekte wiederholt verwenden, anstatt jedes Mal neue Objekte zu erstellen und zu löschen, wenn eine E/A-Anforderung gesendet wird. Weitere Informationen finden Sie unter IWDFIoRequest2::Reuse und IWDFMemory::SetBuffer.

Darüber hinaus kann eine alternative Implementierung den E/A-status Code aus der E/A-Anforderung überprüfen, wenn der Aufruf senden erfolgreich ist. Weitere Informationen finden Sie unter IWDFIoRequest::GetCompletionParams.