Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Este artigo explica a estrutura de uma transferência de controle e como um driver de cliente deve enviar uma solicitação de controle para o dispositivo.
Sobre o ponto de extremidade padrão
Todos os dispositivos USB devem suportar pelo menos um ponto de extremidade chamado ponto de extremidade padrão. Qualquer transferência direcionada ao ponto de extremidade padrão é chamada de transferência de controle. O objetivo de uma transferência de controle é permitir que o host obtenha informações do dispositivo, configure o dispositivo ou execute operações de controle exclusivas para o dispositivo.
Vamos começar estudando essas características do ponto de extremidade padrão.
- O endereço do ponto de extremidade padrão é 0.
- O ponto de extremidade padrão é bidirecional, ou seja, o host pode enviar dados para o ponto de extremidade e receber dados dele dentro de uma transferência.
- O ponto de extremidade padrão está disponível no nível do dispositivo e não está definido em nenhuma interface do dispositivo.
- O ponto de extremidade padrão fica ativo assim que uma conexão é estabelecida entre o host e o dispositivo. Ele fica ativo antes mesmo de uma configuração ser selecionada.
- O tamanho máximo do pacote do ponto de extremidade padrão depende da velocidade do bus do dispositivo. Baixa velocidade, 8 bytes; velocidade total e alta, 64 bytes; SuperSpeed, 512 bytes.
Layout de uma transferência de controle
Como as transferências de controle são transferências de alta prioridade, certa quantidade de largura de banda é reservada no barramento pelo host. Para dispositivos de baixa e velocidade máxima, 10% da largura de banda; 20% para dispositivos de transferência de alta velocidade e SuperSpeed. Agora, vamos examinar o layout de uma transferência de controle.
Uma transferência de controle é dividida em três transações: transação de configuração, transação de dados e transação de status. Cada transação contém três tipos de pacotes: pacote de token, pacote de dados e pacote de handshake.
Certos campos são comuns a todos os pacotes. Estes campos são:
- Campo de sincronização que indica o início do pacote.
- Identificador de pacote (PID) que indica o tipo de pacote, a direção da transação e, no caso de um pacote de handshake, indica o sucesso ou falha da transação.
- O campo EOP indica o fim do pacote.
Outros campos dependem do tipo de pacote.
Pacote de token
Cada transação de configuração começa com um pacote de token. Aqui está a estrutura do pacote. O host sempre envia o pacote de token.
O valor PID indica o tipo do pacote de token. Aqui estão os valores possíveis:
- SETUP: Indica o início de uma transação de configuração em uma transferência de controle.
- IN: Indica que o host está solicitando dados do dispositivo (caso de leitura).
- OUT: Indica que o host está enviando dados para o dispositivo (caso de gravação).
- SOF: Indica o início do fotograma. Este tipo de pacote de token contém um número de quadro de 11 bits. O host envia o pacote SOF. A frequência com que este pacote é enviado depende da velocidade do barramento. Para velocidade máxima, o host envia o pacote a cada 1milissegundo; a cada 125 microssegundos em um ônibus de alta velocidade.
Pacote de dados
Imediatamente após o pacote token está o pacote de dados que contém a carga útil. O número de bytes que cada pacote de dados pode conter depende do tamanho máximo do pacote no ponto final padrão. O pacote de dados pode ser enviado pelo host ou pelo dispositivo, dependendo da direção da transferência.
Pacote de aperto de mão
Imediatamente após o pacote de dados está o pacote de handshake. O PID do pacote indica se o pacote foi ou não recebido pelo host ou pelo dispositivo. O pacote de handshake pode ser enviado pelo host ou pelo dispositivo, dependendo da direção da transferência.
Você pode ver a estrutura de transações e pacotes usando qualquer analisador USB, como Beagle, Ellisys, analisadores de protocolo USB LeCroy. Um dispositivo analisador mostra como os dados são enviados ou recebidos de um dispositivo USB através do fio. Neste exemplo, vamos examinar alguns vestígios capturados por um analisador USB LeCroy. Este exemplo é apenas para informação. Este não é um endosso da Microsoft.
Transação de instalação
O host sempre inicia uma transferência de controle. Ele faz isso enviando uma transação de configuração. Esta transação contém um pacote de token chamado token de configuração seguido por um pacote de dados de 8 bytes. Esta captura de tela mostra um exemplo de transação de configuração.
No rastreamento anterior, o host inicia (indicado por H↓) a transferência de controle enviando o pacote de token de configuração #434. Observe que o PID especifica SETUP indicando um token de instalação. O PID é seguido pelo endereço do dispositivo e pelo endereço do ponto de extremidade. Para transferências de controlo, esse endereço de endpoint é sempre 0.
Em seguida, o host envia o pacote de dados #435. O PID é DATA0 e esse valor é usado para sequenciamento de pacotes (a ser discutido). O PID é seguido por 8 bytes que contém as principais informações sobre essa solicitação. Esses 8 bytes indicam o tipo de solicitação e o tamanho do buffer no qual o dispositivo gravará sua resposta.
Todos os bytes são recebidos na ordem inversa. Conforme descrito na seção 9.3, vemos estes campos e valores:
| Campo | Tamanho | Valor | Descrição |
|---|---|---|---|
| bmRequestType (Consulte 9.3.1 bmRequestType) | 1 | 0x80 | A direção da transferência de dados é do dispositivo para o host (D7 é 1) O pedido é um pedido padrão (D6... D5 é 0) O destinatário do pedido é o DISPOSITIVO (D4 é 0) |
| bPedido (ver secção Ver 9.3.2 e Quadro 9-4) | 1 | 0x06 | O tipo de solicitação é GET_DESCRIPTOR. |
| wValue (Ver Tabela 9-5) | 2 | 0x0100 | O valor da solicitação indica que o tipo de descritor é DEVICE. |
| wIndex (Ver secção 9.3.4) | 2 | 0x0000 | A direção é do host para o dispositivo (D7 é 1) O número do ponto final é 0. |
| wLength (Ver secção 9.3.5) | 2 | 0x0012 | A solicitação é para recuperar 18 bytes. |
Assim, podemos concluir que nesta transferência de controle (leitura), o host envia uma solicitação para recuperar o descritor do dispositivo e especifica 18 bytes como o comprimento da transferência para manter esse descritor. A maneira como o dispositivo envia esses 18 bytes depende da quantidade de dados que o ponto de extremidade padrão pode enviar em uma transação. Essas informações são incluídas no descritor do dispositivo retornado pelo dispositivo na transação de dados.
Em resposta, o dispositivo envia um pacote de handshake (#436 indicado por D↓). Observe que o valor PID é ACK (pacote ACK). Isso indica que o dispositivo reconheceu a transação.
Transação de dados
Agora, vamos ver o que o dispositivo retorna em resposta à solicitação. Os dados reais são transferidos em uma transação de dados.
Aqui está o rastreio da transação de dados.
Ao receber o pacote ACK, o host inicia a transação de dados. Para iniciar a transação, o sistema envia um pacote de token (#450) com a direção IN (chamado token IN).
Em resposta, o dispositivo envia um pacote de dados (#451) que segue o token IN. Este pacote de dados contém o descritor de dispositivo real. O primeiro byte indica o comprimento do descritor do dispositivo, 18 bytes (0x12). O último byte neste pacote de dados indica o tamanho máximo do pacote suportado pelo ponto de extremidade padrão. Neste caso, vemos que o dispositivo pode enviar 8 bytes de cada vez através do seu ponto de extremidade padrão.
Observação
O tamanho máximo do pacote do ponto de extremidade padrão depende da velocidade do dispositivo. O ponto de extremidade padrão de um dispositivo de alta velocidade é de 64 bytes; dispositivo de baixa velocidade é de 8 bytes.
O host reconhece a transação de dados enviando um pacote ACK (#452) para o dispositivo.
Vamos calcular a quantidade de dados retornados. No campo wLength do pacote de dados (#435) na transação de configuração, o host solicitou 18 bytes. Na transação de dados, vemos que apenas os primeiros 8 bytes do descritor do dispositivo foram recebidos do dispositivo. Então, como o host recebe informações armazenadas nos 10 bytes restantes? O dispositivo faz isso em duas transações: primeiro 8 bytes e depois os últimos 2 bytes.
Agora que o host sabe o tamanho máximo do pacote do ponto de extremidade padrão, ele inicia uma nova transação de dados e solicita a próxima parte com base no tamanho do pacote.
Aqui está a próxima transação de dados:
O host inicia a transação de dados anterior enviando um token IN (#463) e solicitando os próximos 8 bytes do dispositivo. O dispositivo responde com um pacote de dados (#464) que contém os próximos 8 bytes do descritor do dispositivo.
Ao receber os 8 bytes, o host envia um pacote ACK (#465) para o dispositivo.
Em seguida, o host solicita os últimos 2 bytes em outra transação de dados da seguinte maneira:
Portanto, vemos que para transferir 18 bytes do dispositivo para o host, o host controla o número de bytes transferidos e iniciou três transações de dados (8+8+2).
Observação
Observe o PID dos pacotes de dados nas transações de dados 19, 23, 26. O PID alterna entre DATA0 e DATA1. Essa sequência é chamada de alternância de dados. Nos casos em que há várias transações de dados, a comutação de dados é usada para verificar a sequência de pacotes. Esse método garante que os pacotes de dados não sejam duplicados ou perdidos.
Ao mapear os pacotes de dados consolidados para a estrutura do descritor do dispositivo (consulte a Tabela 9-8), vemos estes campos e valores:
| Campo | Tamanho | Valor | Descrição |
|---|---|---|---|
| bComprimento | 1 | 0x12 | Comprimento do descritor do dispositivo, que é de 18 bytes. |
| bDescritorType | 1 | 0x01 | O tipo de descritor é dispositivo. |
| bcdUSB | 2 | 0x0100 | O número da versão da especificação é 1.00. |
| bDeviceClass | 1 | 0x00 | A classe de dispositivo é 0. Cada interface na configuração tem as informações da classe. |
| bDeviceSubClass | 1 | 0x00 | A subclasse é 0 porque a classe de dispositivo é 0. |
| bProtocolo | 1 | 0x00 | Protocolo é 0. Este dispositivo não usa nenhum protocolo específico de classe. |
| bMaxPacketSize0 | 1 | 0x08 | O tamanho máximo do pacote do ponto de extremidade é de 8 bytes. |
| idVendor | 2 | 0x0562 | Telex Comunicações. |
| idProduto | 2 | 0x0002 | Microfone USB. |
| bcdDevice | 2 | 0x0100 | Indica o número da liberação do dispositivo. |
| iFabricante | 1 | 0x01 | Cadeia de caracteres do fabricante. |
| iProduto | 1 | 0x02 | Cadeia de caracteres do produto. |
| iSerialNumber | 1 | 0x03 | Número de série. |
| bNumConfigurations | 1 | 0x01 | Número de configurações. |
Ao examinar esses valores, temos algumas informações preliminares sobre o dispositivo. O dispositivo é um microfone USB de baixa velocidade. O tamanho máximo do pacote do ponto de extremidade padrão é de 8 bytes. O dispositivo suporta uma configuração.
Estado da transação
Finalmente, o host conclui a transferência de controle iniciando a última transação: transação de status.
O host inicia a transação com um pacote de token OUT (#481). O objetivo deste pacote é verificar se o dispositivo enviou todos os dados solicitados. Não há nenhum pacote de dados enviado nesta transação de status. O dispositivo responde com um pacote ACK. Se ocorresse um erro, o PID poderia ter sido NAK ou STALL.
Modelos de controlador
- Estrutura de Drivers em Modo Kernel
- Estrutura do driver do modo de usuário
- Introdução ao WinUSB para desenvolvedores
Pré-requisitos
Antes que o driver do cliente possa enumerar pipes, certifique-se de que estes requisitos sejam atendidos:
O driver do cliente deve ter criado o objeto de dispositivo USB de destino do framework.
Se você estiver usando os modelos USB fornecidos com o Microsoft Visual Studio Professional 2012, o código do modelo executa essas tarefas. O código do modelo obtém o identificador para o objeto de dispositivo de destino e armazena-o no contexto do dispositivo.
Driver de cliente KMDF
Um driver de cliente KMDF deve obter um identificador WDFUSBDEVICE chamando o método WdfUsbTargetDeviceCreateWithParameters . Para obter mais informações, consulte "Código-fonte do dispositivo" em Noções básicas sobre a estrutura de código do driver do cliente USB (KMDF).
Driver de cliente UMDF
Um driver de cliente UMDF deve obter um ponteiro IWDFUsbTargetDevice consultando o objeto de dispositivo de destino do framework do UMDF. Para obter mais informações, consulte "IPnpCallbackHardware implementação e tarefas específicas de USB" em Compreender a estrutura do código do driver cliente USB (UMDF).
O aspeto mais importante para uma transferência de controle é formatar o token de configuração adequadamente. Antes de enviar o pedido, reúna este conjunto de informações:
- Direção da solicitação: host para dispositivo ou dispositivo para host.
- Destinatário da solicitação: dispositivo, interface, ponto de extremidade ou outro.
- Categoria de solicitação: padrão, classe ou fornecedor.
- Tipo de solicitação, como uma solicitação de GET_DESCRIPTPOR. Para obter mais informações, consulte a seção 9.5 na especificação USB.
- Valores wValue e wIndex. Esses valores dependem do tipo de solicitação.
Você pode obter todas essas informações a partir da especificação USB oficial.
Se você estiver escrevendo um driver UMDF, obtenha o arquivo de cabeçalho, Usb_hw.h do UMDF Sample Driver for OSR USB Fx2 Learning Kit. Este arquivo de cabeçalho contém macros úteis e estrutura para formatar o pacote de instalação para a transferência de controle.
Todos os drivers UMDF devem se comunicar com um driver de modo kernel para enviar e receber dados de dispositivos. Para um driver USB UMDF, o driver de modo kernel é sempre o driver fornecido pela Microsoft WinUSB (Winusb.sys).
Sempre que um driver UMDF faz uma solicitação para a pilha de drivers USB, o gerenciador de E/S do Windows envia a solicitação para WinUSB. Depois de receber o pedido, o WinUSB processa o pedido ou encaminha-o para a pilha de controladores USB.
Métodos definidos pela Microsoft para enviar solicitações de transferência de controle
Um driver de cliente USB no host inicia a maioria das solicitações de controle para obter informações sobre o dispositivo, configurar o dispositivo ou enviar comandos de controle do fornecedor. Todos esses pedidos podem ser categorizados em:
As solicitações padrão são definidas na especificação USB. O objetivo do envio de solicitações padrão é obter informações sobre o dispositivo, suas configurações, interfaces e pontos de extremidade. O destinatário de cada pedido depende do tipo de pedido. O destinatário pode ser o dispositivo, uma interface ou um ponto de extremidade.
Observação
O destino de qualquer transferência de controle é sempre o ponto de extremidade padrão. O destinatário é a entidade do dispositivo cujas informações (descritor, estado e assim por diante) interessam ao host.
As solicitações podem ser classificadas em: solicitações de configuração, solicitações de recursos e solicitações de status.
- As solicitações de configuração são enviadas para obter informações do dispositivo para que o host possa configurá-lo, como uma solicitação de GET_DESCRIPTOR. Essas solicitações também podem ser solicitações de gravação que são enviadas pelo host para definir uma configuração específica ou uma configuração alternativa no dispositivo.
- As solicitações de recursos são enviadas pelo driver do cliente para habilitar ou desabilitar determinadas configurações de dispositivo booleano suportadas pelo dispositivo, interface ou ponto de extremidade.
- As solicitações de status permitem que o host obtenha ou defina os bits de status definidos por USB de um dispositivo, ponto de extremidade ou interface.
Para obter mais informações, consulte a Seção 9.4 na especificação USB, versão 2.0. Os tipos de solicitação padrão são definidos no arquivo de cabeçalho, Usbspec.h.
As solicitações de classe são definidas por uma especificação de classe de dispositivo específica.
As solicitações do fornecedor são fornecidas pelo fornecedor e dependem das solicitações suportadas pelo dispositivo.
A pilha USB fornecida pela Microsoft lida com toda a comunicação de protocolo com o dispositivo, conforme mostrado nos rastreamentos anteriores. O driver expõe interfaces de driver de dispositivo (DDIs) que permitem que um driver de cliente envie transferências de controle de várias maneiras. Se o driver do cliente for um driver WDF (Windows Driver Foundation), ele poderá chamar rotinas diretamente para enviar os tipos comuns de solicitações de controle. WDF suporta intrinsecamente transferências de controle para ambos KMDF e UMDF.
Certos tipos de solicitações de controle não são expostos por meio do WDF. Para essas solicitações, o driver do cliente pode usar o modelo WDF-hybrid. Esse modelo permite que o driver do cliente crie e formate solicitações no estilo WDM URB e, em seguida, envie essas solicitações usando objetos da estrutura WDF. O modelo híbrido só se aplica aos drivers de modo kernel.
Para controladores UMDF:
Use as macros auxiliares e a estrutura definida em usb_hw.h. Este cabeçalho está incluído com o driver de exemplo UMDF para o Kit de Aprendizagem OSR USB Fx2.
Use esta tabela para determinar a melhor maneira de enviar solicitações de controle para a pilha de drivers USB.
| Se você quiser enviar uma solicitação de controle para... | Para um controlador KMDF... | Para um motorista de UMDF... | Para um driver WDM, crie uma estrutura URB (rotina auxiliar) |
|---|---|---|---|
| CLEAR_FEATURE: Desative determinadas configurações de recursos no dispositivo, suas configurações, interfaces e endpoints. Consulte a secção 9.4.1 na especificação USB. |
|
|
_URB_CONTROL_FEATURE_REQUEST (UsbBuildFeatureRequest) URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT URB_FUNCTION_CLEAR_FEATURE_TO_OTHER |
| GET_CONFIGURATION: Obtenha a configuração USB atual. Consulte a secção 9.4.2 na especificação USB. | O KMDF seleciona a primeira configuração por padrão. Para recuperar o número de configuração definido pelo dispositivo:
|
UMDF seleciona a primeira configuração por padrão. Para recuperar o número de configuração definido pelo dispositivo:
|
_URB_CONTROL_GET_CONFIGURATION_REQUEST FUNÇÃO_URB_OBTER_CONFIGURAÇÃO |
| GET_DESCRIPTOR: Obtenha descritores de dispositivo, configuração, interface e ponto final. Consulte a secção 9.4.3 na especificação USB. Para obter mais informações, consulte Descritores USB. |
Chame estes métodos:
|
Chame estes métodos:
|
_URB_CONTROL_DESCRIPTOR_REQUEST (UsbBuildGetDescriptorRequest) URB_FUNÇÃO_GET_DESCRIPTOR_DE_DISPOSITIVO URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE |
| GET_INTERFACE: Obtenha a configuração alternativa atual para uma interface. Consulte a secção 9.4.4 na especificação USB. |
|
|
_URB_CONTROL_GET_INTERFACE_REQUEST URB_FUNCTION_GET_INTERFACE |
| GET_STATUS: Obtenha bits de status de um dispositivo, ponto de extremidade ou interface. Ver secção 9.4.5. na especificação USB. |
|
|
_URB_CONTROL_GET_STATUS_REQUEST (UsbBuildGetStatusRequest) URB_FUNCTION_GET_STATUS_FROM_DEVICE URB_FUNCTION_GET_STATUS_FROM_INTERFACE URB_FUNCTION_GET_STATUS_FROM_ENDPOINT URB_FUNCTION_GET_STATUS_FROM_OTHER. |
| SET_ADDRESS: Ver secção 9.4.6 na especificação USB. | Esta solicitação é tratada pela pilha de drivers USB; o driver do cliente não pode executar esta operação. | Esta solicitação é tratada pela pilha de drivers USB; o driver do cliente não pode executar esta operação. | Esta solicitação é gerida pela pilha de drivers USB; o driver do cliente não pode executar esta operação. |
| SET_CONFIGURATION: Defina uma configuração. Consulte a secção 9.4.7 na especificação USB. Para obter mais informações, consulte Como selecionar uma configuração para um dispositivo USB. |
Por padrão, o KMDF seleciona a configuração padrão e a primeira configuração alternativa em cada interface. O driver do cliente pode alterar a configuração padrão chamando o método WdfUsbTargetDeviceSelectConfigType e especificando WdfUsbTargetDeviceSelectConfigTypeUrb como a opção de solicitação. Em seguida, deves formatar um URB para essa solicitação e submetê-lo à pilha de controladores USB. | Por padrão, o UMDF seleciona a configuração padrão e a primeira configuração alternativa em cada interface. O driver do cliente não pode alterar a configuração. |
_URB_SELECT_CONFIGURATION (USBD_SelectConfigUrbAllocateAndBuild) FUNÇÃO_URB_SELECIONAR_CONFIGURAÇÃO |
| SET_DESCRIPTOR: Atualize um dispositivo, configuração ou descritor de cadeia de caracteres existente. Consulte a secção 9.4.8 na especificação USB. Esta solicitação não é comumente usada. No entanto, a pilha de drivers USB aceita tal solicitação do driver do cliente. |
|
|
_URB_CONTROL_DESCRIPTOR_REQUEST URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT FUNÇÃO_URB_DEFINIR_DESCRIPTOR_PARA_INTERFACE |
| SET_FEATURE: Ative determinadas configurações de funcionalidades no dispositivo, suas configurações, interfaces e pontos de extremidade. Consulte a secção 9.4.9 na especificação USB. |
|
|
_URB_CONTROL_FEATURE_REQUEST (UsbBuildFeatureRequest) URB_FUNCTION_SET_FEATURE_TO_DEVICE Função URB: Definir Característica na Interface URB_FUNCTION_SET_FEATURE_TO_ENDPOINT URB_FUNCTION_SET_FEATURE_TO_OTHER |
| SET_INTERFACE: Altera a configuração alternativa em uma interface. Consulte a secção 9.4.9 na especificação USB. Para obter mais informações, consulte Como selecionar uma configuração alternativa em uma interface USB. |
WdfUsbTargetDeviceSelectConfig
|
|
_URB_SELECT_INTERFACE (USBD_SelectInterfaceUrbAllocateAndBuild) URB_FUNCTION_SELECT_INTERFACE |
| SYNC_FRAME: Defina e obtenha o número do quadro de sincronização de um ponto de extremidade. Consulte a secção 9.4.10 na especificação USB. | Esta solicitação é tratada pela pilha de drivers USB; o driver do cliente não pode executar esta operação. | Esta solicitação é tratada pela pilha de controladores USB; o driver do cliente não pode executar esta operação. | Esta solicitação é gerida pela pilha de drivers USB; o driver do cliente não pode executar esta operação. |
| Para solicitações específicas de classe de dispositivo e comandos de fornecedor. |
|
|
_URB_CONTROL_VENDOR_OR_CLASS_REQUEST (UsbBuildVendorRequest) URB_FUNCTION_VENDOR_DEVICE URB_FUNCTION_VENDOR_INTERFACE URB_FUNCTION_VENDOR_ENDPOINT Função_URB_Vendedor_Outros URB_FUNÇÃO_CLASSE_DISPOSITIVO URB_FUNCTION_CLASS_INTERFACE URB_FUNCTION_CLASS_ENDPOINT Classe_Função_URB_Outros |
Como enviar uma transferência de controle para comandos de fornecedor - KMDF
Este procedimento mostra como um driver de cliente pode enviar uma transferência de controle. Neste exemplo, o driver do cliente envia um comando vendor que recupera a versão do firmware do dispositivo.
Declare uma constante para o comando do fornecedor. Estude a especificação de hardware e determine o comando do fornecedor que você deseja usar.
Declare uma estrutura WDF_MEMORY_DESCRIPTOR e inicialize-a chamando a macro WDF_MEMORY_DESCRIPTOR_INIT_BUFFER . Esta estrutura receberá a resposta do dispositivo depois que o driver USB concluir a solicitação.
Dependendo se você envia a solicitação de forma síncrona ou assíncrona, especifique suas opções de envio:
Se você enviar a solicitação de forma síncrona chamando WdfUsbTargetDeviceSendControlTransferSynchronously, especifique um valor de tempo limite. Esse valor é importante porque, sem um tempo limite, você pode bloquear o thread indefinidamente.
Para isso, declare uma estrutura WDF_REQUEST_SEND_OPTIONS e inicialize-a chamando a macro WDF_REQUEST_SEND_OPTIONS_INIT . Especifique a opção como WDF_REQUEST_SEND_OPTION_TIMEOUT.
Em seguida, defina o valor de tempo limite chamando a macro WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT .
Se você estiver enviando a solicitação de forma assíncrona, implemente uma rotina de conclusão. Liberte todos os recursos alocados na rotina de conclusão.
Declare uma estrutura WDF_USB_CONTROL_SETUP_PACKET para conter o token de instalação e formatar a estrutura. Para fazer isso, chame a macro WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR para formatar o pacote de instalação. Na chamada, especifique a direção da solicitação, o destinatário, as opções de solicitação enviada (inicializada na etapa 3) e a constante para o comando vendor.
Envie a solicitação chamando WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
Verifique o valor NTSTATUS retornado pela estrutura e inspecione o valor recebido.
Este exemplo de código envia uma solicitação de transferência de controle para um dispositivo USB para recuperar sua versão de firmware. A solicitação é enviada de forma síncrona e o driver do cliente especifica um valor de tempo limite relativo de 5 segundos (em unidades de 100 nanossegundos). O driver armazena a resposta recebida no contexto do dispositivo definido pelo driver.
enum {
USBFX2_GET_FIRMWARE_VERSION = 0x1,
....
} USBFX2_VENDOR_COMMANDS;
#define WDF_TIMEOUT_TO_SEC ((LONGLONG) 1 * 10 * 1000 * 1000) // defined in wdfcore.h
const __declspec(selectany) LONGLONG
DEFAULT_CONTROL_TRANSFER_TIMEOUT = 5 * -1 * WDF_TIMEOUT_TO_SEC;
typedef struct _DEVICE_CONTEXT
{
...
union {
USHORT VersionAsUshort;
struct {
BYTE Minor;
BYTE Major;
} Version;
} Firmware; // Firmware version.
} DEVICE_CONTEXT, *PDEVICE_CONTEXT;
__drv_requiresIRQL(PASSIVE_LEVEL)
VOID GetFirmwareVersion(
__in PDEVICE_CONTEXT DeviceContext
)
{
NTSTATUS status;
WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket;
WDF_REQUEST_SEND_OPTIONS sendOptions;
USHORT firmwareVersion;
WDF_MEMORY_DESCRIPTOR memoryDescriptor;
PAGED_CODE();
firmwareVersion = 0;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, (PVOID) &firmwareVersion, sizeof(firmwareVersion));
WDF_REQUEST_SEND_OPTIONS_INIT(
&sendOptions,
WDF_REQUEST_SEND_OPTION_TIMEOUT
);
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(
&sendOptions,
DEFAULT_CONTROL_TRANSFER_TIMEOUT
);
WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket,
BmRequestDeviceToHost, // Direction of the request
BmRequestToDevice, // Recipient
USBFX2_GET_FIRMWARE_VERSION, // Vendor command
0, // Value
0); // Index
status = WdfUsbTargetDeviceSendControlTransferSynchronously(
DeviceContext->UsbDevice,
WDF_NO_HANDLE, // Optional WDFREQUEST
&sendOptions,
&controlSetupPacket,
&memoryDescriptor, // MemoryDescriptor
NULL); // BytesTransferred
if (!NT_SUCCESS(status))
{
KdPrint(("Device %d: Failed to get device firmware version 0x%x\n", DeviceContext->DeviceNumber, status));
TraceEvents(DeviceContext->DebugLog,
TRACE_LEVEL_ERROR,
DBG_RUN,
"Device %d: Failed to get device firmware version 0x%x\n",
DeviceContext->DeviceNumber,
status);
}
else
{
DeviceContext->Firmware.VersionAsUshort = firmwareVersion;
TraceEvents(DeviceContext->DebugLog,
TRACE_LEVEL_INFORMATION,
DBG_RUN,
"Device %d: Get device firmware version : 0x%x\n",
DeviceContext->DeviceNumber,
firmwareVersion);
}
return;
}
Como enviar uma transferência de controle para GET_STATUS - UMDF
Este procedimento mostra como um driver de cliente pode enviar uma transferência de controle para um comando GET_STATUS. O destinatário da solicitação é o dispositivo e a solicitação obtém informações em bits D1-D0. Para obter mais informações, consulte a Figura 9-4 na especificação USB.
Inclua o ficheiro de cabeçalho Usb_hw.h disponível com o controlador de exemplo UMDF para o OSR USB Fx2 Learning Kit.
Declare uma estrutura WINUSB_CONTROL_SETUP_PACKET .
Inicialize o pacote de instalação chamando a macro auxiliar, WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS.
Especifique BmRequestToDevice como o destinatário.
Especifique 0 no valor Índice .
Chame o método auxiliar SendControlTransferSynchronously para enviar a solicitação de forma síncrona.
O método auxiliar cria a solicitação associando o pacote de instalação inicializado ao objeto de solicitação de estrutura e ao buffer de transferência chamando o método IWDFUsbTargetDevice::FormatRequestForControlTransfer . Em seguida, o método auxiliar envia a solicitação chamando o método IWDFIoRequest::Send . Depois que o método retornar, inspecione o valor retornado.
Para determinar se o estado indica autoalimentado ou ativação remota, use estes valores definidos na enumeração WINUSB_DEVICE_TRAITS:
Este exemplo de código envia uma solicitação de transferência de controle para obter o status do dispositivo. O exemplo envia a solicitação de forma síncrona chamando um método auxiliar chamado SendControlTransferSynchronously.
HRESULT
CDevice::GetDeviceStatus ()
{
HRESULT hr = S_OK;
USHORT deviceStatus;
ULONG bytesTransferred;
TraceEvents(TRACE_LEVEL_INFORMATION,
DRIVER_ALL_INFO,
"%!FUNC!: entry");
// Setup the control packet.
WINUSB_CONTROL_SETUP_PACKET setupPacket;
WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS(
&setupPacket,
BmRequestToDevice,
0);
hr = SendControlTransferSynchronously(
&(setupPacket.WinUsb),
& deviceStatus,
sizeof(USHORT),
&bytesReturned
);
if (SUCCEEDED(hr))
{
if (deviceStatus & USB_GETSTATUS_SELF_POWERED)
{
m_Self_Powered = true;
}
if (deviceStatus & USB_GETSTATUS_REMOTE_WAKEUP_ENABLED)
{
m_remote_wake-enabled = true;
}
}
return hr;
}
O exemplo de código a seguir mostra a implementação do método auxiliar chamado SendControlTransferSynchronously. Esse método envia uma solicitação de forma síncrona.
HRESULT
CDevice::SendControlTransferSynchronously(
_In_ PWINUSB_SETUP_PACKET SetupPacket,
_Inout_ PBYTE Buffer,
_In_ ULONG BufferLength,
_Out_ PULONG LengthTransferred
)
{
HRESULT hr = S_OK;
IWDFIoRequest *pWdfRequest = NULL;
IWDFDriver * FxDriver = NULL;
IWDFMemory * FxMemory = NULL;
IWDFRequestCompletionParams * FxComplParams = NULL;
IWDFUsbRequestCompletionParams * FxUsbComplParams = NULL;
*LengthTransferred = 0;
hr = m_FxDevice->CreateRequest( NULL, //pCallbackInterface
NULL, //pParentObject
&pWdfRequest);
if (SUCCEEDED(hr))
{
m_FxDevice->GetDriver(&FxDriver);
hr = FxDriver->CreatePreallocatedWdfMemory( Buffer,
BufferLength,
NULL, //pCallbackInterface
pWdfRequest, //pParetObject
&FxMemory );
}
if (SUCCEEDED(hr))
{
hr = m_pIUsbTargetDevice->FormatRequestForControlTransfer( pWdfRequest,
SetupPacket,
FxMemory,
NULL); //TransferOffset
}
if (SUCCEEDED(hr))
{
hr = pWdfRequest->Send( m_pIUsbTargetDevice,
WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
0); //Timeout
}
if (SUCCEEDED(hr))
{
pWdfRequest->GetCompletionParams(&FxComplParams);
hr = FxComplParams->GetCompletionStatus();
}
if (SUCCEEDED(hr))
{
HRESULT hrQI = FxComplParams->QueryInterface(IID_PPV_ARGS(&FxUsbComplParams));
WUDF_TEST_DRIVER_ASSERT(SUCCEEDED(hrQI));
WUDF_TEST_DRIVER_ASSERT( WdfUsbRequestTypeDeviceControlTransfer ==
FxUsbComplParams->GetCompletedUsbRequestType() );
FxUsbComplParams->GetDeviceControlTransferParameters( NULL,
LengthTransferred,
NULL,
NULL );
}
SAFE_RELEASE(FxUsbComplParams);
SAFE_RELEASE(FxComplParams);
SAFE_RELEASE(FxMemory);
pWdfRequest->DeleteWdfObject();
SAFE_RELEASE(pWdfRequest);
SAFE_RELEASE(FxDriver);
return hr;
}
Se você estiver usando Winusb.sys como o driver de função para o seu dispositivo, você pode enviar transferências de controle de um aplicativo. Para formatar o pacote de instalação no WinUSB, use as macros e estruturas auxiliares UMDF, descritas na tabela deste artigo. Para enviar a solicitação, chame a função WinUsb_ControlTransfer.