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

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.

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.

Esquema de layout do 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.

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 do layout de um pacote de handshake.

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.

Captura de ecrã de um rastreio de uma transação de instalaçã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.

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, 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:

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

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 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

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.
  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 as constantes USB_FEATURE_XXX no 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. Ver 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). Veja 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 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:

  1. Formate um WDF_USB_CONTROL_SETUP_PACKET e defina seu membro bRequest como USB_REQUEST_GET_CONFIGURATION.
  2. Envie o pedido chamando WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
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 configuração. Ver 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. Obtenha o número de configuração no buffer de transferência. Acesse esse buffer chamando métodos IWDFMemory .
_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.

  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, ponto de extremidade ou interface. Ver secção 9.4.5. na especificação USB.
  1. Declare um pacote de configuraçã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 de destinatário definido em 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 configuração. Ver 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 autoalimentado ou ativação remota, 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: 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.
  1. Aloque e construa um URB para a solicitação.
  2. Especifique a informação de transferência numa estrutura _URB_CONTROL_DESCRIPTOR_REQUEST.
  3. Envie a solicitação chamando WdfUsbTargetDeviceFormatRequestForUrb ou WdfUsbTargetDeviceSendUrbSynchronously .
  1. Declare um pacote de configuração. Ver 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

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.
  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). Veja as constantes USB_FEATURE_XXX em Usbspec.h. Consulte também a Tabela 9-6 na especificação USB.
  5. Definir SetFeature como TRUE
  6. Envie a solicitação chamando WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Declare um pacote de configuração. Ver 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). Veja 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

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
  1. Obtenha um identificador WDFUSBINTERFACE para o objeto de interface de destino.
  2. Chame o método WdfUsbInterfaceSelectSetting .
  1. Obtenha um ponteiro para o objeto de interface de destino IWDFUsbInterface.
  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 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.
  1. Declare um pacote de configuração. Veja a estrutura WDF_USB_CONTROL_SETUP_PACKET.
  2. Inicialize o pacote de configuração chamando solicitações específicas do WDF_USB_CONTROL_SETUP_PACKET_INIT_CLASS ou WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR para comandos de 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 configuração. Ver 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

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.

  1. Declare uma constante para o comando do fornecedor. 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 . Esta estrutura receberá a resposta do dispositivo depois que o driver USB concluir a solicitação.

  3. Dependendo se você envia 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 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.

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

  1. Inclua o ficheiro de cabeçalho Usb_hw.h disponível com o controlador de exemplo UMDF para o 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 Índice .

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

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