Conexión de un controlador periférico UMDF a un puerto serie
El controlador UMDF para un dispositivo periférico en un puerto serie administrado por SerCx2 requiere determinados recursos de hardware para operar el dispositivo. Incluido en estos recursos es la información que el controlador necesita para abrir una conexión lógica al puerto serie. Los recursos adicionales pueden incluir una interrupción y uno o varios patillas de entrada o salida de GPIO.
Este controlador implementa una interfaz IPnpCallbackHardware2 y registra esta interfaz con el marco de controladores de Windows durante la llamada al método IDriverEntry::OnDeviceAdd del controlador. El marco llama a los métodos de la interfaz IPnpCallbackHardware2 para notificar al controlador los cambios en el estado de alimentación del dispositivo.
Una vez que el dispositivo periférico conectado en serie entra en un estado de alimentación del dispositivo D0 sin inicializar, el marco de controladores llama al método IPnpCallbackHardware2::OnPrepareHardware del controlador para indicar al controlador que prepare este dispositivo para su uso. Durante esta llamada, el controlador recibe dos listas de recursos de hardware como parámetros de entrada. El parámetro pWdfResourcesRaw apunta a la lista de recursos sin procesar y el parámetro pWdfResourcesTranslated apunta a la lista de recursos traducidos. Ambos parámetros son punteros a objetos IWDFCmResourceList . Los recursos traducidos incluyen el identificador de conexión que el controlador periférico necesita para establecer la conexión lógica con el dispositivo periférico conectado en serie.
Para permitir que un controlador periférico UMDF reciba identificadores de conexión en su lista de recursos, el archivo INF que instala el controlador debe incluir la siguiente directiva en la sección DDInstall específica de WDF:
UmdfDirectHardwareAccess = AllowDirectHardwareAccess Para obtener más información sobre esta directiva, vea Especificar directivas WDF en archivos INF. Para obtener un ejemplo de un archivo INX (que se usa para compilar el archivo INF correspondiente) que usa esta directiva, consulte el SpbAccelerometer en los ejemplos del controlador WDK.
En el ejemplo de código siguiente se muestra cómo el método OnPrepareHardware del controlador obtiene el identificador de conexión del parámetro 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;
}
}
En el ejemplo de código anterior se copia el identificador de conexión del dispositivo periférico conectado en serie en una variable denominada connectionId
. En el ejemplo de código siguiente se muestra cómo incorporar el identificador de conexión en un nombre de ruta de acceso de dispositivo que se puede usar para identificar el controlador serie al que está conectado el dispositivo periférico.
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
...
}
En el ejemplo de código anterior se escribe el nombre de la ruta de acceso del dispositivo para el controlador serie en la szTargetPath
matriz. En el ejemplo de código siguiente se usa este nombre de ruta de acceso para abrir un identificador de archivo en el controlador serie.
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
...
}
En el ejemplo de código anterior, el pRemoteTarget
parámetro es un puntero a un objeto IWDFRemoteTarget . Si la llamada al método IWDFRemoteTarget::OpenFileByName se realiza correctamente, el controlador del dispositivo periférico conectado en serie puede usar el objeto IWDFRemoteTarget para enviar solicitudes de E/S al controlador serie.
Para enviar una solicitud de lectura o escritura al dispositivo periférico, el controlador llama primero al método IWDFRemoteTarget::FormatRequestForRead o IWDFRemoteTarget::FormatRequestForWrite para dar formato a la solicitud. (La interfaz IWDFRemoteTarget hereda estos dos métodos de la interfaz IWDFIoTarget ).
Para enviar una solicitud de control de E/S al controlador serie, el controlador llama primero al método IWDFRemoteTarget::FormatRequestForIoctl para dar formato a la solicitud. (La interfaz IWDFRemoteTarget hereda este método de la interfaz IWDFIoTarget ). A continuación, el controlador llama al método IWDFIoRequest::Send para enviar la solicitud de control de E/S al dispositivo periférico conectado en serie.
En el ejemplo de código siguiente, el controlador periférico envía una solicitud de control de E/S al controlador serie.
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);
}
El ejemplo de código anterior hace lo siguiente:
La
pWdfDevice
variable es un puntero a la interfaz IWDFDevice del objeto de dispositivo de marco que representa el dispositivo periférico conectado en serie. El método IWDFDevice::CreateRequest crea una solicitud de E/S y encapsula esta solicitud en la instancia de interfaz IWDFIoRequest a la que apunta elpWdfIoRequest
parámetro . La solicitud de E/S se elimina más adelante (consulte el paso 6). Esta implementación es algo ineficaz porque crea y, a continuación, elimina un objeto de solicitud para cada solicitud de E/S que se envía. Un enfoque más eficaz consiste en reutilizar el mismo objeto de solicitud para una serie de solicitudes de E/S. Para obtener más información, consulte Reutilización de objetos de solicitud de marco.La
pWdfDriver
variable es un puntero a la interfaz IWDFDriver del objeto del controlador de marco que representa el controlador periférico. LaspInBuffer
variables yinBufferSize
especifican la dirección y el tamaño del búfer de entrada para la solicitud de control de E/S. El método IWDFDriver::CreatePreallocatedWdfMemory crea un objeto de memoria de marco para el búfer de entrada y designa el objeto IWDFIoRequest al que apuntapWdfIoRequest
como objeto primario del objeto de memoria.La
pWdfRemoteTarget
variable es el puntero de destino remoto que se obtuvo de la llamada OpenFileByName en un ejemplo de código anterior. El método IWDFRemoteTarget::FormatRequestForIoctl da formato a la solicitud de una operación de control de E/S. LaioctlCode
variable se establece en uno de los códigos de control de E/S enumerados en la tabla de interfaz de solicitud de E/S serie.La
fSynchronous
variable es TRUE si la solicitud de control de E/S se va a enviar de forma sincrónica y es FALSE si se va a enviar de forma asincrónica. LapCallback
variable es un puntero a una interfaz IRequestCallbackRequestCompletion creada anteriormente. Si la solicitud se va a enviar de forma asincrónica, la llamada al método IWDFIoRequest::SetCompletionCallback registra esta interfaz. Más adelante, se llama al método IRequestCallbackRequestCompletion::OnCompletion para notificar al controlador cuando se completa la solicitud de forma asincrónica.El método Send envía la solicitud de escritura con formato al dispositivo periférico conectado en serie. La
Flags
variable indica si la solicitud de escritura se va a enviar de forma sincrónica o asincrónica.Si la solicitud se envía de forma sincrónica, el método IWDFIoRequest::D eleteWdfObject elimina el objeto de solicitud de E/S al que apunta
pWdfIoRequest
y el objeto secundario alpInputMemory
que apunta . La interfaz IWDFIoRequest hereda este método de la interfaz IWDFObject . Si la solicitud se envía de forma asincrónica, la llamada al método DeleteWdfObject debe producirse más adelante, en el método OnCompletion del controlador.