Connessione di un driver di periferica UMDF a una porta seriale
Il driver UMDF per un dispositivo periferico in una porta seriale gestita da SerCx2 richiede alcune risorse hardware per il funzionamento del dispositivo. Incluse in queste risorse sono le informazioni necessarie al driver per aprire una connessione logica alla porta seriale. Altre risorse possono includere un interrupt e uno o più pin di input o output GPIO.
Questo driver implementa un'interfaccia IPnpCallbackHardware2 e registra questa interfaccia con il framework driver di Windows durante la chiamata al metodo IDriverEntry::OnDeviceAdd del driver. Il framework chiama i metodi nell'interfaccia IPnpCallbackHardware2 per notificare al driver le modifiche nello stato di alimentazione del dispositivo.
Dopo che il dispositivo periferico connesso in modo seriale entra in uno stato di alimentazione del dispositivo D0 non inizializzato, il framework driver chiama il metodo IPnpCallbackHardware2::OnPrepareHardware per indicare al driver di preparare il dispositivo per l'uso. Durante questa chiamata, il driver riceve due elenchi di risorse hardware come parametri di input. Il parametro pWdfResourcesRaw punta all'elenco di risorse non elaborate e il parametro pWdfResourcesTranslated punta all'elenco di risorse tradotte. Entrambi i parametri sono puntatori agli oggetti IWDFCmResourceList . Le risorse tradotte includono l'ID connessione che il driver periferico deve stabilire la connessione logica al dispositivo periferico connesso in modo seriale.
Per consentire a un driver di periferica UMDF di ricevere GLI ID connessione nell'elenco delle risorse, il file INF che installa il driver deve includere la direttiva seguente nella sezione DDInstall specifica di WDF:
UmdfDirectHardwareAccess = AllowDirectHardwareAccess Per altre informazioni su questa direttiva, vedere Specifica delle direttive WDF nei file INF. Per un esempio di file INX (usato per compilare il file INF corrispondente) che usa questa direttiva, vedere gli esempi di SpbAccelerometer negli esempi di driver WDK.
Nell'esempio di codice seguente viene illustrato come il metodo OnPrepareHardware del driver ottiene l'ID connessione dal parametro 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;
}
}
L'esempio di codice precedente copia l'ID connessione per il dispositivo periferico connesso in modo seriale in una variabile denominata connectionId
. Nell'esempio di codice seguente viene illustrato come incorporare l'ID connessione in un nome di percorso del dispositivo che può essere usato per identificare il controller seriale a cui è connesso il dispositivo periferico.
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
...
}
L'esempio di codice precedente scrive il nome del percorso del dispositivo per il controller seriale nella szTargetPath
matrice. Nell'esempio di codice seguente viene usato questo nome di percorso per aprire un handle di file per il controller seriale.
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
...
}
Nell'esempio di codice precedente, il pRemoteTarget
parametro è un puntatore a un oggetto IWDFRemoteTarget . Se la chiamata al metodo IWDFRemoteTarget::OpenFileByName ha esito positivo, il driver per il dispositivo periferico connesso in serie può utilizzare l'oggetto IWDFRemoteTarget per inviare richieste di I/O al controller seriale.
Per inviare una richiesta di lettura o scrittura al dispositivo periferico, il driver chiama innanzitutto il metodo IWDFRemoteTarget::FormatRequestForRead o IWDFRemoteTarget::FormatRequestForWrite dell'oggetto per formattare la richiesta. L'interfaccia IWDFRemoteTarget eredita questi due metodi dall'interfaccia IWDFIoTarget .
Per inviare una richiesta di controllo I/O al controller seriale, il driver chiama innanzitutto il metodo IWDFRemoteTarget::FormatRequestForIoctl per formattare la richiesta. L'interfaccia IWDFRemoteTarget eredita questo metodo dall'interfaccia IWDFIoTarget . Successivamente, il driver chiama il metodo IWDFIoRequest::Send per inviare la richiesta di controllo I/O al dispositivo periferico connesso in modo seriale.
Nell'esempio di codice seguente il driver periferica invia una richiesta di controllo di I/O al controller seriale.
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);
}
L'esempio di codice precedente esegue le operazioni seguenti:
La
pWdfDevice
variabile è un puntatore all'interfaccia IWDFDevice dell'oggetto dispositivo framework che rappresenta il dispositivo periferico connesso in modo seriale. Il metodo IWDFDevice::CreateRequest crea una richiesta di I/O e incapsula questa richiesta nell'istanza dell'interfaccia IWDFIoRequest a cui punta ilpWdfIoRequest
parametro . La richiesta di I/O viene eliminata in un secondo momento (vedere il passaggio 6). Questa implementazione è un po' inefficiente perché crea e quindi elimina un oggetto richiesta per ogni richiesta di I/O inviata. Un approccio più efficiente consiste nel riutilizzare lo stesso oggetto richiesta per una serie di richieste di I/O. Per altre informazioni, vedere Riutilizzo degli oggetti richiesta framework.La
pWdfDriver
variabile è un puntatore all'interfaccia IWDFDriver dell'oggetto driver framework che rappresenta il driver periferico. LepInBuffer
variabili einBufferSize
specificano l'indirizzo e le dimensioni del buffer di input per la richiesta di controllo di I/O. Il metodo IWDFDriver::CreatePreallocatedWdfMemory crea un oggetto memoria framework per il buffer di input e designa l'oggetto IWDFIoRequest apWdfIoRequest
cui punta come oggetto padre dell'oggetto memory.La
pWdfRemoteTarget
variabile è il puntatore di destinazione remoto ottenuto dalla chiamata OpenFileByName in un esempio di codice precedente. Il metodo IWDFRemoteTarget::FormatRequestForIoctl formatta la richiesta per un'operazione di controllo I/O. LaioctlCode
variabile è impostata su uno dei codici di controllo di I/O elencati nella tabella in Interfaccia richiesta di I/O seriale.La
fSynchronous
variabile è TRUE se la richiesta di controllo di I/O deve essere inviata in modo sincrono ed è FALSE se deve essere inviata in modo asincrono. LapCallback
variabile è un puntatore a un'interfaccia IRequestCallbackRequestCompletion creata in precedenza. Se la richiesta deve essere inviata in modo asincrono, la chiamata al metodo IWDFIoRequest::SetCompletionCallback registra questa interfaccia. Successivamente, il metodo IRequestCallbackRequestCompletion::OnCompletion viene chiamato per notificare al driver quando la richiesta viene completata in modo asincrono.Il metodo Send invia la richiesta di scrittura formattata al dispositivo periferico connesso in modo seriale. La
Flags
variabile indica se la richiesta di scrittura deve essere inviata in modo sincrono o asincrono.Se la richiesta viene inviata in modo sincrono, il metodo IWDFIoRequest::D eleteWdfObject elimina sia l'oggetto richiesta di I/O a
pWdfIoRequest
cui punta e l'oggetto figlio apInputMemory
cui punta . L'interfaccia IWDFIoRequest eredita questo metodo dall'interfaccia IWDFObject . Se la richiesta viene inviata in modo asincrono, la chiamata al metodo DeleteWdfObject deve essere eseguita in un secondo momento, nel metodo OnCompletion del driver.