Partilhar via


Como enviar uma transferência de controle USB

Este artigo explica a estrutura de uma transferência de controle e como um driver cliente deve enviar uma solicitação de controle para o dispositivo.

Sobre o ponto de extremidade padrão

Todos os dispositivos USB devem dar suporte a pelo menos um ponto de extremidade chamado de ponto de extremidade padrão. Qualquer transferência direcionada ao ponto de extremidade padrão é chamada de transferência de controle. A finalidade 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 do 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 endpoint padrão é bidirecional, ou seja, o host pode enviar dados para o endpoint e receber dados dele em uma transferência.
  • O endpoint padrão está disponível no nível do dispositivo e não é definido em nenhuma interface do dispositivo.
  • O endpoint padrão fica ativo assim que uma conexão é estabelecida entre o host e o dispositivo. Ele está ativo antes mesmo de uma configuração ser selecionada.
  • O tamanho máximo do pacote do endpoint padrão depende da velocidade do barramento 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.

Diagrama de uma transferência de controle USB.

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.

Determinados campos são comuns a todos os pacotes. Esses 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 a 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.

Diagrama de um layout de pacote de token.

O valor PID indica o tipo do pacote de token. Estes sã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 quadro. Esse 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 esse pacote é enviado depende da velocidade do barramento. Para velocidade máxima, o host envia o pacote a cada 1 milissegundo; a cada 125 microssegundos em um ônibus de alta velocidade.

Pacote de dados

Imediatamente após o pacote de 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 do ponto de extremidade padrão. O pacote de dados pode ser enviado pelo host ou pelo dispositivo, dependendo da direção da transferência.

Diagrama de um layout de pacote de dados.

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.

Diagrama de um layout de pacote de handshake.

Você pode ver a estrutura de transações e pacotes usando qualquer analisador USB, como analisadores de protocolo USB Beagle, Ellisys, LeCroy. Um dispositivo analisador mostra como os dados são enviados ou recebidos de um dispositivo USB pela rede. Neste exemplo, vamos examinar alguns rastreamentos capturados por um analisador USB LeCroy. Este exemplo é apenas para fins informativos. Este não é um endosso da Microsoft.

Transação de configuração

O host sempre inicia uma transferência de controle. Ele faz isso enviando uma transação de configuração. Essa 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.

Captura de tela de um rastreamento de uma 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 endpoint. Para transferências de controle, esse endereço de ponto de extremidade é 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 em 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 de transferência de dados é do dispositivo para o host (D7 é 1)

A solicitação é uma solicitação padrão (D6... D5 é 0)

O destinatário da solicitação é o DEVICE (D4 é 0)
bSolicitação (Consulte a seção Consulte 9.3.2 e Tabela 9-4) 1 0x06 O tipo de solicitação é GET_DESCRIPTOR.
wValue (consulte a Tabela 9-5) 2 0x0100 O valor da solicitação indica que o tipo de descritor é DEVICE.
wIndex (consulte a seção 9.3.4) 2 0x0000 A direção é do host para o dispositivo (D7 é 1)

O número do ponto final é 0.
wLength (consulte a seçã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 endpoint padrão pode enviar em uma transação. Essas informações são incluídas no descritor de 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 rastreamento para a transação de dados.

Captura de tela que mostra um rastreamento de uma transação de dados de exemplo.

Ao receber o pacote ACK, o host inicia a transação de dados. Para iniciar a transação, ele envia um pacote de token (#450) com a direção IN (chamado de 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. Nesse caso, vemos que o dispositivo pode enviar 8 bytes por vez por meio de seu endpoint padrão.

Observação

O tamanho máximo do pacote do endpoint 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 de 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: 8 bytes e depois os últimos 2 bytes.

Agora que o host sabe o tamanho máximo do pacote do endpoint padrão, o host 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:

Captura de tela que mostra um rastreamento da nova 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:

Captura de tela que mostra um rastreamento da nova transação de dados de exemplo na qual o host solicita os últimos 2 bytes.

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 alternância 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
bLength 1 0x12 Comprimento do descritor do dispositivo, que é de 18 bytes.
bDescriptorType 1 0x01 O tipo de descritor é device.
bcdUSB 2 0x0100 O número da versão da especificação é 1.00.
bClasse de dispositivo 1 0x00 A classe de dispositivo é 0. Cada interface na configuração tem as informações de classe.
bClasse DeviceSub 1 0x00 A subclasse é 0 porque a classe do dispositivo é 0.
bProtocolo 1 0x00 O 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 Comunicações de telex.
idProduto 2 0x0002 Microfone USB.
bcdDispositivo 2 0x0100 Indica o número de versão do dispositivo.
iManufacturer 1 0x01 Corda 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.

Transação de status

Por fim, o host conclui a transferência de controle iniciando a última transação: transação de status.

Captura de tela de um rastreamento de uma transação de dados de exemplo.

O host inicia a transação com um pacote de token OUT (#481). O objetivo desse 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 driver

Pré-requisitos

Antes que o driver cliente possa enumerar pipes, verifique se esses requisitos foram atendidos:

  • O driver cliente deve ter criado o objeto de dispositivo de destino USB da estrutura.

    Se você estiver usando os modelos USB fornecidos com o Microsoft Visual Studio Professional 2012, o código do modelo executará essas tarefas. O código do modelo obtém o identificador para o objeto de dispositivo de destino e armazena no contexto do dispositivo.

Driver do cliente KMDF

Um driver 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 do cliente UMDF

Um driver de cliente UMDF deve obter um ponteiro IWDFUsbTargetDevice consultando o objeto de dispositivo de destino da estrutura. Para obter mais informações, consulte "Implementação de IPnpCallbackHardware e tarefas específicas de USB" em Noções básicas sobre a estrutura de código do driver do cliente USB (UMDF).

O aspecto mais importante para uma transferência de controle é formatar o token de instalação adequadamente. Antes de enviar a solicitação, 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, endpoint ou outro.
  • Categoria de solicitação: padrão, classe ou fornecedor.
  • Tipo de solicitação, como uma solicitação 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 na especificação oficial do USB.

Se você estiver escrevendo um driver UMDF, obtenha o arquivo de cabeçalho Usb_hw.h do Driver de Exemplo UMDF para OSR USB Fx2 Learning Kit. Este arquivo de cabeçalho contém macros e estrutura úteis para formatar o pacote de configuraçã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 do modo kernel é sempre o driver WinUSB (Winusb.sys) fornecido pela Microsoft.

Sempre que um driver UMDF faz uma solicitação para a pilha de driver USB, o gerenciador de E/S do Windows envia a solicitação para o WinUSB. Depois de receber a solicitação, o WinUSB processa a solicitação ou a encaminha para a pilha de driver 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. Todas essas solicitações podem ser categorizadas 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 endpoints. O destinatário de cada solicitação depende do tipo de solicitação. 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, status e assim por diante) o host está interessado.

    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 GET_DESCRIPTOR. Essas solicitações também podem ser solicitações de gravaçã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 cliente para habilitar ou desabilitar determinadas configurações de dispositivo booliano compatíveis com o dispositivo, a interface ou um 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, endpoint 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 DDIs (interfaces de driver de dispositivo) que permitem que um driver cliente envie transferências de controle de várias maneiras. Se o driver cliente for um driver WDF (Windows Driver Foundation), ele poderá chamar rotinas diretamente para enviar os tipos comuns de solicitações de controle. O WDF dá suporte a transferências de controle intrinsecamente para KMDF e UMDF.

Determinados tipos de solicitações de controle não são expostos por meio do WDF. Para essas solicitações, o driver cliente pode usar o modelo híbrido do WDF. Esse modelo permite que o driver cliente crie e formate solicitações no estilo URB do WDM e, em seguida, envie essas solicitações usando objetos de estrutura do WDF. O modelo híbrido só se aplica a drivers no modo kernel.

Para drivers UMDF:

Use as macros auxiliares e a estrutura definidas em usb_hw.h. Esse cabeçalho está incluído no 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 driver USB. Se você não conseguir visualizar esta tabela, consulte a tabela neste artigo.

Se você deseja enviar uma solicitação de controle para... Para um motorista KMDF... Para um driver 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 seção 9.4.1 na especificação USB.
  1. Declare um pacote de instalação. Veja a estrutura WDF_USB_CONTROL_SETUP_PACKET .
  2. Inicialize o pacote de instalação chamando WDF_USB_CONTROL_SETUP_PACKET_INIT_FEATURE.
  3. Especifique um valor de destinatário definido em WDF_USB_BMREQUEST_RECIPIENT.
  4. Especifique o seletor de recursos (wValue). Consulte USB_FEATURE_XXX constantes em Usbspec.h. Consulte também a Tabela 9-6 na especificação USB.
  5. Defina SetFeature como FALSE.
  6. Envie a solicitação chamando WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Declare um pacote de instalação. Veja a estrutura WINUSB_CONTROL_SETUP_PACKET declarada em usb_hw.h.
  2. Inicialize o pacote de instalação chamando a macro auxiliar, WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE, definida em usb_hw.h.
  3. Especifique um valor de destinatário definido em WINUSB_BMREQUEST_RECIPIENT.
  4. Especifique o seletor de recursos (wValue). Consulte USB_FEATURE_XXX constantes em Usbspec.h. Consulte também a Tabela 9-6 na especificação USB.
  5. Defina SetFeature como FALSE.
  6. Crie 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.
  7. Envie a solicitação chamando o método IWDFIoRequest::Send .
_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 seçã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:

  1. Formate um WDF_USB_CONTROL_SETUP_PACKET e defina seu membro bRequest como USB_REQUEST_GET_CONFIGURATION.
  2. Envie a solicitação chamando WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
O UMDF seleciona a primeira configuração por padrão. Para recuperar o número de configuração definido pelo dispositivo:

  1. Declare um pacote de instalação. Veja a estrutura WINUSB_CONTROL_SETUP_PACKET declarada em usb_hw.h.
  2. Inicialize o pacote de instalação chamando a macro auxiliar, WINUSB_CONTROL_SETUP_PACKET_INIT, definida em usb_hw.h.
  3. Especifique BmRequestToDevice como a direção, BmRequestToDevice como o destinatário e USB_REQUEST_GET_CONFIGURATION como a solicitação.
  4. Crie 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.
  5. Envie a solicitação chamando o método IWDFIoRequest::Send .
  6. Receba o número de configuração no buffer de transferência. Acesse esse buffer chamando métodos IWDFMemory.
_URB_CONTROL_GET_CONFIGURATION_REQUEST

URB_FUNCTION_GET_CONFIGURATION
GET_DESCRIPTOR: Obtenha descritores de dispositivo, configuração, interface e ponto de extremidade. Consulte a seçã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_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE

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 seção 9.4.4 na especificação USB.

  1. Obtenha um identificador WDFUSBINTERFACE para o objeto de interface de destino chamando o método WdfUsbTargetDeviceGetInterface .
  2. Chame o método WdfUsbInterfaceGetConfiguredSettingIndex .
  1. Obtenha um ponteiro IWDFUsbInterface para o objeto de interface de destino.
  2. Chame o método IWDFUsbInterface::GetConfiguredSettingIndex .
_URB_CONTROL_GET_INTERFACE_REQUEST

URB_FUNCTION_GET_INTERFACE
GET_STATUS: Obtenha bits de status de um dispositivo, endpoint ou interface. Ver secção 9.4.5. na especificação USB.
  1. Declare um pacote de instalação. Veja a estrutura WDF_USB_CONTROL_SETUP_PACKET .
  2. Inicialize o pacote de instalação chamando WDF_USB_CONTROL_SETUP_PACKET_INIT_GET_STATUS.
  3. Especifique o valor do destinatário definido no WDF_USB_BMREQUEST_RECIPIENT.
  4. Especifique qual status você deseja obter: dispositivo, interface ou ponto de extremidade (wIndex).
  5. Envie a solicitação chamando WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Declare um pacote de instalação. Veja a estrutura WINUSB_CONTROL_SETUP_PACKET declarada em usb_hw.h.
  2. Inicialize o pacote de instalação chamando a macro auxiliar, WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS, definida em usb_hw.h.
  3. Especifique um valor de destinatário definido em WINUSB_BMREQUEST_RECIPIENT.
  4. Especifique qual status você deseja obter: dispositivo, interface ou ponto de extremidade (wIndex).
  5. Crie 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.
  6. Envie a solicitação chamando o método IWDFIoRequest::Send .
  7. Receba o valor de status no buffer de transferência. Acesse esse buffer chamando métodos IWDFMemory.
  8. Para determinar se o status indica ativação remota e autoalimentada, use os valores definidos na enumeração WINUSB_DEVICE_TRAITS:
_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: Consulte a seção 9.4.6 na especificação USB. Essa solicitação é tratada pela pilha de driver USB; O driver cliente não pode executar essa operação. Essa solicitação é tratada pela pilha de driver USB; O driver cliente não pode executar essa operação. Essa solicitação é tratada pela pilha de driver USB; O driver cliente não pode executar essa operação.
SET_CONFIGURATION: Defina uma configuração. Consulte a seçã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 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, você deve formatar um URB para essa solicitação e enviá-lo para a pilha de driver USB. Por padrão, o UMDF seleciona a configuração padrão e a primeira configuração alternativa em cada interface. O driver cliente não pode alterar a configuração. _URB_SELECT_CONFIGURATION

(USBD_SelectConfigUrbAllocateAndBuild)

URB_FUNCTION_SELECT_CONFIGURATION
SET_DESCRIPTOR: Atualize um dispositivo, configuração ou descritor de cadeia de caracteres existente. Consulte a seção 9.4.8 na especificação USB.

Essa solicitação não é comumente usada. No entanto, a pilha de driver USB aceita essa solicitação do driver cliente.
  1. Aloque e crie um URB para a solicitação.
  2. Especifique as informações de transferência em uma estrutura _URB_CONTROL_DESCRIPTOR_REQUEST .
  3. Envie a solicitação chamando WdfUsbTargetDeviceFormatRequestForUrb ou WdfUsbTargetDeviceSendUrbSynchronously .
  1. Declare um pacote de instalação. Veja a estrutura WINUSB_CONTROL_SETUP_PACKET declarada em usb_hw.h.
  2. Especifique as informações de transferência de acordo com a especificação USB.
  3. Crie 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.
  4. Envie a solicitação chamando o método IWDFIoRequest::Send .
_URB_CONTROL_DESCRIPTOR_REQUEST

URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE

URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT

URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE
SET_FEATURE: Habilite determinadas configurações de recursos no dispositivo, suas configurações, interfaces e endpoints. Consulte a seção 9.4.9 na especificação USB.
  1. Declare um pacote de instalação. Veja a estrutura WDF_USB_CONTROL_SETUP_PACKET .
  2. Inicialize o pacote de instalação chamando WDF_USB_CONTROL_SETUP_PACKET_INIT_FEATURE.
  3. Especifique o valor do destinatário (dispositivo, interface, ponto de extremidade) definido em WDF_USB_BMREQUEST_RECIPIENT.
  4. Especifique o seletor de recursos (wValue). Consulte USB_FEATURE_XXX constantes em Usbspec.h. Consulte também a Tabela 9-6 na especificação USB.
  5. Defina SetFeature como TRUE
  6. Envie a solicitação chamando WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Declare um pacote de instalação. Veja a estrutura WINUSB_CONTROL_SETUP_PACKET declarada em usb_hw.h.
  2. Inicialize o pacote de instalação chamando a macro auxiliar, WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE, definida em usb_hw.h.
  3. Especifique um valor de destinatário definido em WINUSB_BMREQUEST_RECIPIENT.
  4. Especifique o seletor de recursos (wValue). Consulte USB_FEATURE_XXX constantes em Usbspec.h. Consulte também a Tabela 9-6 na especificação USB.
  5. Defina SetFeature como TRUE.
  6. Crie 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.
  7. Envie a solicitação chamando o método IWDFIoRequest::Send .
_URB_CONTROL_FEATURE_REQUEST

(UsbBuildFeatureRequest)

URB_FUNCTION_SET_FEATURE_TO_DEVICE

URB_FUNCTION_SET_FEATURE_TO_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 seçã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
  1. Obtenha um identificador WDFUSBINTERFACE para o objeto de interface de destino.
  2. Chame o método WdfUsbInterfaceSelectSetting .
  1. Obtenha um ponteiro IWDFUsbInterface para o objeto de interface de destino.
  2. Chame o método IWDFUsbInterface::SelectSetting .
_URB_SELECT_INTERFACE

(USBD_SelectInterfaceUrbAllocateAndBuild)

URB_FUNCTION_SELECT_INTERFACE
SYNC_FRAME: Defina e obtenha o número do quadro de sincronização do endpoint. Consulte a seção 9.4.10 na especificação USB. Essa solicitação é tratada pela pilha de driver USB; O driver cliente não pode executar essa operação. Essa solicitação é tratada pela pilha de driver USB; O driver cliente não pode executar essa operação. Essa solicitação é tratada pela pilha de driver USB; O driver cliente não pode executar essa operação.
Para solicitações específicas de classe de dispositivo e comandos de fornecedor.
  1. Declare um pacote de instalação. Veja a estrutura WDF_USB_CONTROL_SETUP_PACKET .
  2. Inicialize o pacote de instalação chamando solicitações específicas de WDF_USB_CONTROL_SETUP_PACKET_INIT_CLASS ou WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR para comandos do fornecedor.
  3. Especifique o valor do destinatário (dispositivo, interface, ponto de extremidade) definido em WDF_USB_BMREQUEST_RECIPIENT.
  4. Envie a solicitação chamando WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Declare um pacote de instalação. Veja a estrutura WINUSB_CONTROL_SETUP_PACKET declarada em usb_hw.h.
  2. Inicialize o pacote de instalação chamando a macro auxiliar, WINUSB_CONTROL_SETUP_PACKET_INIT_CLASS ou WINUSB_CONTROL_SETUP_PACKET_INIT_VENDOR, definida em usb_hw.h.
  3. Especifique a direção (consulte a enumeração WINUSB_BMREQUEST_DIRECTION ), o destinatário (consulte a enumeração WINUSB_BMREQUEST_RECIPIENT ) e a solicitação, conforme descrito na classe ou na especificação de hardware.
  4. Crie 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.
  5. Envie a solicitação chamando o método IWDFIoRequest::Send .
  6. Receba as informações do dispositivo no buffer de transferência. Acesse esse buffer chamando métodos IWDFMemory.
_URB_CONTROL_VENDOR_OR_CLASS_REQUEST

(UsbBuildVendorRequest)

URB_FUNCTION_VENDOR_DEVICE

URB_FUNCTION_VENDOR_INTERFACE

URB_FUNCTION_VENDOR_ENDPOINT

URB_FUNCTION_VENDOR_OTHER

URB_FUNCTION_CLASS_DEVICE

URB_FUNCTION_CLASS_INTERFACE

URB_FUNCTION_CLASS_ENDPOINT

URB_FUNCTION_CLASS_OTHER

Como enviar uma transferência de controle para comandos do fornecedor - KMDF

Este procedimento mostra como um driver cliente pode enviar uma transferência de controle. Neste exemplo, o driver cliente envia um comando vendor que recupera a versão do firmware do dispositivo.

  1. Declare uma constante para o comando vendor. Estude a especificação de hardware e determine o comando do fornecedor que você deseja usar.

  2. Declare uma estrutura WDF_MEMORY_DESCRIPTOR e inicialize-a chamando a macro WDF_MEMORY_DESCRIPTOR_INIT_BUFFER . Essa estrutura receberá a resposta do dispositivo depois que o driver USB concluir a solicitação.

  3. Dependendo se você enviar a solicitação de forma síncrona ou assíncrona, especifique suas opções de envio:

  4. Declare uma estrutura WDF_USB_CONTROL_SETUP_PACKET para conter o token de instalação e formate a estrutura. Para fazer isso, chame a macro WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR para formatar o pacote de configuração. Na chamada, especifique a direção da solicitação, o destinatário, as opções de solicitação enviada (inicializadas na etapa 3) e a constante do comando vendor.

  5. Envie a solicitação chamando WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.

  6. 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 a um dispositivo USB para recuperar sua versão de firmware. A solicitação é enviada de forma síncrona e o driver 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 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 nos bits D1-D0. Para obter mais informações, consulte a Figura 9-4 na especificação USB.

  1. Inclua o arquivo de cabeçalho Usb_hw.h disponível com o Driver de Exemplo UMDF para OSR USB Fx2 Learning Kit.

  2. Declare uma estrutura WINUSB_CONTROL_SETUP_PACKET .

  3. Inicialize o pacote de instalação chamando a macro auxiliar, WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS.

  4. Especifique BmRequestToDevice como o destinatário.

  5. Especifique 0 no valor Index.

  6. 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 o 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.

  7. Para determinar se o status indica ativação remota autoalimentada, 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 driver de função para seu dispositivo, poderá enviar transferências de controle de um aplicativo. Para formatar o pacote de instalação no WinUSB, use as macros e estruturas auxiliares do UMDF, descritas na tabela deste artigo. Para enviar a solicitação, chame WinUsb_ControlTransfer função.