Conectando um driver periférico UMDF a uma porta serial
O driver UMDF para um dispositivo periférico em uma porta serial gerenciada pelo SerCx2 requer determinados recursos de hardware para operar o dispositivo. Incluídas nesses recursos estão as informações de que o driver precisa para abrir uma conexão lógica com a porta serial. Recursos adicionais podem incluir uma interrupção e um ou mais pinos de entrada ou saída gpio.
Esse driver implementa uma interface IPnpCallbackHardware2 e registra essa interface com a estrutura de driver do Windows durante a chamada para o método IDriverEntry::OnDeviceAdd do driver. A estrutura chama os métodos na interface IPnpCallbackHardware2 para notificar o driver de alterações no estado de energia do dispositivo.
Depois que o dispositivo periférico conectado serialmente entra em um estado de energia do dispositivo D0 não inicializado, a estrutura do driver chama o método IPnpCallbackHardware2::OnPrepareHardware do driver para instruir o driver a preparar esse dispositivo para uso. Durante essa chamada, o driver recebe duas listas de recursos de hardware como parâmetros de entrada. O parâmetro pWdfResourcesRaw aponta para a lista de recursos brutos e o parâmetro pWdfResourcesTranslated aponta para a lista de recursos traduzidos. Ambos os parâmetros são ponteiros para objetos IWDFCmResourceList . Os recursos traduzidos incluem a ID de conexão que o driver periférico precisa para estabelecer a conexão lógica com o dispositivo periférico serialmente conectado.
Para permitir que um driver periférico UMDF receba IDs de conexão em sua lista de recursos, o arquivo INF que instala o driver deve incluir a seguinte diretiva em sua seção DDInstall específica do WDF :
UmdfDirectHardwareAccess = AllowDirectHardwareAccess Para obter mais informações sobre essa diretiva, consulte Especificando diretivas WDF em arquivos INF. Para obter um exemplo de um arquivo INX (usado para criar o arquivo INF correspondente) que usa essa diretiva, consulte o SpbAccelerometer nos exemplos de driver do WDK.
O exemplo de código a seguir mostra como o método OnPrepareHardware do driver obtém a ID de conexão do 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;
}
}
O exemplo de código anterior copia a ID de conexão do dispositivo periférico serialmente conectado em uma variável chamada connectionId
. O exemplo de código a seguir mostra como incorporar a ID de conexão em um nome de caminho do dispositivo que pode ser usado para identificar o controlador serial ao qual o dispositivo periférico está conectado.
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
...
}
O exemplo de código anterior grava o nome do caminho do dispositivo para o controlador serial na szTargetPath
matriz. O exemplo de código a seguir usa esse nome de caminho para abrir um identificador de arquivo para o controlador serial.
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
...
}
No exemplo de código anterior, o pRemoteTarget
parâmetro é um ponteiro para um objeto IWDFRemoteTarget . Se a chamada para o método IWDFRemoteTarget::OpenFileByName for bem-sucedida, o driver do dispositivo periférico serialmente conectado poderá usar o objeto IWDFRemoteTarget para enviar solicitações de E/S ao controlador serial.
Para enviar uma solicitação de leitura ou gravação para o dispositivo periférico, o driver primeiro chama o método IWDFRemoteTarget::FormatRequestForRead ou IWDFRemoteTarget::FormatRequestForWrite para formatar a solicitação. (A interface IWDFRemoteTarget herda esses dois métodos da interface IWDFIoTarget .)
Para enviar uma solicitação de controle de E/S para o controlador serial, o driver primeiro chama o método IWDFRemoteTarget::FormatRequestForIoctl para formatar a solicitação. (A interface IWDFRemoteTarget herda esse método da interface IWDFIoTarget .) Em seguida, o driver chama o método IWDFIoRequest::Send para enviar a solicitação de controle de E/S para o dispositivo periférico serialmente conectado.
No exemplo de código a seguir, o driver periférico envia uma solicitação de controle de E/S para o controlador serial.
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);
}
O exemplo de código anterior faz o seguinte:
A
pWdfDevice
variável é um ponteiro para a interface IWDFDevice do objeto de dispositivo de estrutura que representa o dispositivo periférico serialmente conectado. O método IWDFDevice::CreateRequest cria uma solicitação de E/S e encapsula essa solicitação na instância da interface IWDFIoRequest que é apontada pelopWdfIoRequest
parâmetro . A solicitação de E/S é excluída posteriormente (consulte a etapa 6). Essa implementação é um pouco ineficiente porque cria e exclui um objeto de solicitação para cada solicitação de E/S enviada. Uma abordagem mais eficiente é reutilizar o mesmo objeto de solicitação para uma série de solicitações de E/S. Para obter mais informações, consulte Reutilizando objetos de solicitação de estrutura.A
pWdfDriver
variável é um ponteiro para a interface IWDFDriver do objeto de driver de estrutura que representa o driver periférico. AspInBuffer
variáveis einBufferSize
especificam o endereço e o tamanho do buffer de entrada para a solicitação de controle de E/S. O método IWDFDriver::CreatePreallocatedWdfMemory cria um objeto de memória de estrutura para o buffer de entrada e designa o objeto IWDFIoRequest apontado porpWdfIoRequest
como o objeto pai do objeto de memória.A
pWdfRemoteTarget
variável é o ponteiro de destino remoto obtido da chamada OpenFileByName em um exemplo de código anterior. O método IWDFRemoteTarget::FormatRequestForIoctl formata a solicitação para uma operação de controle de E/S. AioctlCode
variável é definida como um dos códigos de controle de E/S listados na tabela na Interface de Solicitação de E/S Serial.A
fSynchronous
variável será TRUE se a solicitação de controle de E/S for enviada de forma síncrona e for FALSE se for enviada de forma assíncrona. ApCallback
variável é um ponteiro para uma interface IRequestCallbackRequestCompletion criada anteriormente. Se a solicitação for enviada de forma assíncrona, a chamada para o método IWDFIoRequest::SetCompletionCallback registrará essa interface. Posteriormente, o método IRequestCallbackRequestCompletion::OnCompletion é chamado para notificar o driver quando a solicitação for concluída de forma assíncrona.O método Send envia a solicitação de gravação formatada para o dispositivo periférico serialmente conectado. A
Flags
variável indica se a solicitação de gravação deve ser enviada de forma síncrona ou assíncrona.Se a solicitação for enviada de forma síncrona, o método IWDFIoRequest::D eleteWdfObject excluirá o objeto de solicitação de E/S apontado por
pWdfIoRequest
e o objeto filho apontado porpInputMemory
. A interface IWDFIoRequest herda esse método da interface IWDFObject . Se a solicitação for enviada de forma assíncrona, a chamada para o método DeleteWdfObject deverá ocorrer posteriormente, no método OnCompletion do driver.