Condividi tramite


Come inviare un trasferimento di controllo USB

Questo articolo illustra la struttura di un trasferimento di controllo e il modo in cui un driver client deve inviare una richiesta di controllo al dispositivo.

Informazioni sull'endpoint predefinito

Tutti i dispositivi USB devono supportare almeno un endpoint denominato endpoint predefinito. Qualsiasi trasferimento destinato all'endpoint predefinito è denominato trasferimento di controllo. Lo scopo di un trasferimento di controllo è consentire all'host di ottenere informazioni sul dispositivo, configurare il dispositivo o eseguire operazioni di controllo univoche per il dispositivo.

Per iniziare, studiare queste caratteristiche dell'endpoint predefinito.

  • L'indirizzo dell'endpoint predefinito è 0.
  • L'endpoint predefinito è bidirezionale, ovvero l'host può inviare dati all'endpoint e ricevere dati da esso entro un trasferimento.
  • L'endpoint predefinito è disponibile a livello di dispositivo e non è definito in alcuna interfaccia del dispositivo.
  • L'endpoint predefinito è attivo non appena viene stabilita una connessione tra l'host e il dispositivo. È attivo anche prima che venga selezionata una configurazione.
  • La dimensione massima del pacchetto dell'endpoint predefinito dipende dalla velocità del bus del dispositivo. Bassa velocità, 8 byte; pieno e ad alta velocità, 64 byte; SuperSpeed, 512 byte.

Layout di un trasferimento di controllo

Poiché i trasferimenti di controllo sono trasferimenti con priorità elevata, una certa quantità di larghezza di banda viene riservata sul bus dall'host. Per i dispositivi a bassa e piena velocità, il 10% della larghezza di banda; 20% per i dispositivi ad alta velocità e SuperSpeed. Esaminiamo ora il layout di un trasferimento di controllo.

Diagramma di un trasferimento di controllo USB.

Un trasferimento di controllo è suddiviso in tre transazioni: transazione di configurazione, transazione di dati e transazione di stato. Ogni transazione contiene tre tipi di pacchetti: pacchetto di token, pacchetto di dati e pacchetto handshake.

Alcuni campi sono comuni a tutti i pacchetti. Questi campi sono:

  • Campo di sincronizzazione che indica l'inizio del pacchetto.
  • Identificatore di pacchetto (PID) che indica il tipo di pacchetto, la direzione della transazione e, nel caso di un pacchetto handshake, indica l'esito positivo o negativo della transazione.
  • Il campo EOP indica la fine del pacchetto.

Altri campi dipendono dal tipo di pacchetto.

Pacchetto di token

Ogni transazione di installazione inizia con un pacchetto di token. Ecco la struttura del pacchetto. L'host invia sempre il pacchetto di token.

Diagramma del layout di un pacchetto di token.

Il valore PID indica il tipo del pacchetto di token. Ecco i valori possibili:

  • SETUP: indica l'inizio di una transazione di installazione in un trasferimento di controllo.
  • IN: indica che l'host richiede dati dal dispositivo (caso di lettura).
  • OUT: indica che l'host invia dati al dispositivo (caso di scrittura).
  • SOF: indica l'inizio del frame. Questo tipo di pacchetto di token contiene un numero di frame a 11 bit. L'host invia il pacchetto sof. La frequenza con cui questo pacchetto viene inviato dipende dalla velocità del bus. Per tutta la velocità, l'host invia il pacchetto ogni 1millisecondo; ogni 125 microsecondi su un autobus ad alta velocità.

Pacchetto di dati

Immediatamente dopo il pacchetto di token è il pacchetto di dati che contiene il payload. Il numero di byte che ogni pacchetto di dati può contenere dipende dalle dimensioni massime del pacchetto dell'endpoint predefinito. Il pacchetto di dati può essere inviato dall'host o dal dispositivo, a seconda della direzione del trasferimento.

Diagramma di un layout di pacchetti di dati.

Pacchetto handshake

Subito dopo il pacchetto di dati è il pacchetto handshake. Il PID del pacchetto indica se il pacchetto è stato ricevuto dall'host o dal dispositivo. Il pacchetto handshake può essere inviato dall'host o dal dispositivo, a seconda della direzione del trasferimento.

Diagramma di un layout di pacchetto handshake.

È possibile visualizzare la struttura delle transazioni e dei pacchetti usando qualsiasi analizzatore USB, ad esempio Beagle, Ellisys, analizzatori di protocolli USB LeCroy. Un dispositivo analizzatore mostra come i dati vengono inviati o ricevuti da un dispositivo USB in rete. In questo esempio verranno esaminate alcune tracce acquisite da un analizzatore USB LeCroy. Questo esempio è solo per informazioni. Non si tratta di un'approvazione da parte di Microsoft.

Configurare la transazione

L'host avvia sempre un trasferimento di controllo. A tale scopo, inviare una transazione di installazione. Questa transazione contiene un pacchetto di token denominato token di installazione seguito da un pacchetto di dati a 8 byte. Questa schermata mostra una transazione di configurazione di esempio.

Screenshot di una traccia di una transazione di installazione.

Nella traccia precedente, l'host avvia (indicato da H++) il trasferimento del controllo inviando il pacchetto di token di installazione #434. Si noti che il PID specifica SETUP che indica un token di installazione. Il PID è seguito dall'indirizzo del dispositivo e dall'indirizzo dell'endpoint. Per i trasferimenti di controllo, l'indirizzo dell'endpoint è sempre 0.

Successivamente, l'host invia il pacchetto di dati #435. Il PID è DATA0 e tale valore viene usato per la sequenziazione di pacchetti (da discutere). Il PID è seguito da 8 byte che contiene le informazioni principali su questa richiesta. Questi 8 byte indicano il tipo di richiesta e le dimensioni del buffer in cui il dispositivo scriverà la risposta.

Tutti i byte vengono ricevuti in ordine inverso. Come descritto nella sezione 9.3, vengono visualizzati i campi e i valori seguenti:

Campo Dimensione valore Descrizione
bmRequestType (vedere 9.3.1 bmRequestType) 1 0x80 La direzione del trasferimento dei dati è dal dispositivo all'host (D7 è 1)

La richiesta è una richiesta standard (D6... D5 è 0)

Il destinatario della richiesta è il DISPOSITIVO (D4 è 0)
bRequest (vedere la sezione Vedere 9.3.2 e tabella 9-4) 1 0x06 Il tipo di richiesta è GET_DESCRIPTOR.
wValue (vedere la tabella 9-5) 2 0x0100 Il valore della richiesta indica che il tipo di descrittore è DEVICE.
wIndex (vedere la sezione 9.3.4) 2 0x0000 La direzione è dall'host al dispositivo (D7 è 1)

Il numero di endpoint è 0.
wLength (vedere la sezione 9.3.5) 2 0x0012 La richiesta consiste nel recuperare 18 byte.

È quindi possibile concludere che in questo trasferimento di controllo (lettura) l'host invia una richiesta per recuperare il descrittore del dispositivo e specifica 18 byte come lunghezza di trasferimento per contenere tale descrittore. Il modo in cui il dispositivo invia questi 18 byte dipende dalla quantità di dati che l'endpoint predefinito può inviare in una transazione. Tali informazioni sono incluse nel descrittore del dispositivo restituito dal dispositivo nella transazione di dati.

In risposta, il dispositivo invia un pacchetto handshake (#436 indicato da D++). Si noti che il valore PID è ACK (pacchetto ACK). Ciò indica che il dispositivo ha riconosciuto la transazione.

Transazione dati

A questo punto, vediamo cosa restituisce il dispositivo in risposta alla richiesta. I dati effettivi vengono trasferiti in una transazione di dati.

Ecco la traccia per la transazione di dati.

Screenshot che mostra una traccia di una transazione di dati di esempio.

Dopo aver ricevuto il pacchetto ACK, l'host avvia la transazione di dati. Per avviare la transazione, invia un pacchetto di token (#450) con la direzione IN (denominata token IN).

In risposta, il dispositivo invia un pacchetto di dati (#451) che segue il token IN. Questo pacchetto di dati contiene il descrittore effettivo del dispositivo. Il primo byte indica la lunghezza del descrittore del dispositivo, 18 byte (0x12). L'ultimo byte in questo pacchetto di dati indica le dimensioni massime del pacchetto supportate dall'endpoint predefinito. In questo caso, si noterà che il dispositivo può inviare 8 byte alla volta tramite l'endpoint predefinito.

Nota

La dimensione massima del pacchetto dell'endpoint predefinito dipende dalla velocità del dispositivo. L'endpoint predefinito di un dispositivo ad alta velocità è di 64 byte; il dispositivo a bassa velocità è di 8 byte.

L'host riconosce la transazione di dati inviando un pacchetto ACK (#452) al dispositivo.

Calcoliamo ora la quantità di dati restituiti. Nel campo wLength del pacchetto di dati (#435) nella transazione di installazione l'host ha richiesto 18 byte. Nella transazione di dati si noterà che dal dispositivo sono stati ricevuti solo i primi 8 byte del descrittore del dispositivo. In che modo l'host riceve le informazioni archiviate nei 10 byte rimanenti? Il dispositivo esegue questa operazione in due transazioni: 8 byte e quindi ultimi 2 byte.

Ora che l'host conosce le dimensioni massime del pacchetto dell'endpoint predefinito, l'host avvia una nuova transazione di dati e richiede la parte successiva in base alle dimensioni del pacchetto.

Ecco la transazione dati successiva:

Screenshot che mostra una traccia della nuova transazione di dati.

L'host avvia la transazione di dati precedente inviando un token IN (#463) e richiedendo i successivi 8 byte dal dispositivo. Il dispositivo risponde con un pacchetto di dati (#464) che contiene i successivi 8 byte del descrittore del dispositivo.

Dopo aver ricevuto i 8 byte, l'host invia un pacchetto ACK (#465) al dispositivo.

Successivamente, l'host richiede gli ultimi 2 byte in un'altra transazione di dati come indicato di seguito:

Screenshot che mostra una traccia della nuova transazione di dati di esempio in cui l'host richiede gli ultimi 2 byte.

Si noterà quindi che per trasferire 18 byte dal dispositivo all'host, l'host tiene traccia del numero di byte trasferiti e avviato tre transazioni di dati (8+8+2).

Nota

Si noti il PID dei pacchetti di dati nelle transazioni di dati 19, 23, 26. Il PID si alterna tra DATA0 e DATA1. Questa sequenza viene chiamata attivazione/disattivazione dei dati. Nei casi in cui sono presenti più transazioni di dati, viene usato l'interruttore dei dati per verificare la sequenza di pacchetti. Questo metodo garantisce che i pacchetti di dati non vengano duplicati o persi.

Eseguendo il mapping dei pacchetti di dati consolidati alla struttura del descrittore di dispositivo (vedere la tabella 9-8), vengono visualizzati i campi e i valori seguenti:

Campo Dimensione valore Descrizione
bLength 1 0x12 Lunghezza del descrittore del dispositivo, ovvero 18 byte.
bDescriptorType 1 0x01 Il tipo di descrittore è device.
bcdUSB 2 0x0100 Il numero di versione della specifica è 1.00.
bDeviceClass 1 0x00 La classe device è 0. Ogni interfaccia nella configurazione contiene le informazioni sulla classe.
bDeviceSubClass 1 0x00 La sottoclasse è 0 perché la classe del dispositivo è 0.
bProtocol 1 0x00 Il protocollo è 0. Questo dispositivo non usa protocolli specifici della classe.
bMaxPacketSize0 1 0x08 La dimensione massima del pacchetto dell'endpoint è di 8 byte.
idVendor 2 0x0562 Comunicazioni telex.
idProduct 2 0x0002 Microfono USB.
bcdDevice 2 0x0100 Indica il numero di versione del dispositivo.
iManufacturer 1 0x01 Stringa del produttore.
iProduct 1 0x02 Stringa del prodotto.
iSerialNumber 1 0x03 Numero di serie.
bNumConfigurations 1 0x01 Numero di configurazioni.

Esaminando questi valori sono disponibili alcune informazioni preliminari sul dispositivo. Il dispositivo è un microfono USB a bassa velocità. La dimensione massima del pacchetto dell'endpoint predefinito è di 8 byte. Il dispositivo supporta una configurazione.

Transazione di stato

Infine, l'host completa il trasferimento del controllo avviando l'ultima transazione: transazione di stato.

Screenshot di una traccia di una transazione di dati di esempio.

L'host avvia la transazione con un pacchetto di token OUT (#481). Lo scopo di questo pacchetto è verificare che il dispositivo abbia inviato tutti i dati richiesti. Nessun pacchetto di dati inviato in questa transazione di stato. Il dispositivo risponde con un pacchetto ACK. Se si è verificato un errore, il PID potrebbe essere stato NAK o STALL.

Modelli di driver

Prerequisiti

Prima che il driver client possa enumerare le pipe, assicurarsi che siano soddisfatti questi requisiti:

  • Il driver client deve aver creato l'oggetto dispositivo di destinazione USB del framework.

    Se si usano i modelli USB forniti con Microsoft Visual Studio Professional 2012, il codice del modello esegue tali attività. Il codice del modello ottiene l'handle per l'oggetto dispositivo di destinazione e archivia nel contesto del dispositivo.

Driver client KMDF

Un driver client KMDF deve ottenere un handle WDFUSBDEVICE chiamando il metodo WdfUsbTargetDeviceCreateWithParameters . Per altre informazioni, vedere "Codice sorgente del dispositivo" in Informazioni sulla struttura del codice del driver client USB (KMDF).

Driver client UMDF

Un driver client UMDF deve ottenere un puntatore IWDFUsbTargetDevice eseguendo una query sull'oggetto dispositivo di destinazione del framework. Per altre informazioni, vedere "Implementazione IPnpCallbackHardware e attività specifiche dell'USB" in Informazioni sulla struttura del codice del driver client USB (UMDF).

L'aspetto più importante per un trasferimento di controllo consiste nel formattare il token di installazione in modo appropriato. Prima di inviare la richiesta, raccogliere questo set di informazioni:

  • Direzione della richiesta: ospitare da host a dispositivo o dispositivo da ospitare.
  • Destinatario della richiesta: dispositivo, interfaccia, endpoint o altro.
  • Categoria di richiesta: standard, classe o fornitore.
  • Tipo di richiesta, ad esempio una richiesta di GET_DESCRIPTPOR. Per altre informazioni, vedere la sezione 9.5 nella specifica USB.
  • valori wValue e wIndex . Questi valori dipendono dal tipo di richiesta.

Puoi ottenere tutte queste informazioni dalla specifica USB ufficiale.

Se si scrive un driver UMDF, ottenere il file di intestazione, Usb_hw.h dal driver di esempio UMDF per OSR USB Fx2 Learning Kit. Questo file di intestazione contiene macro e struttura utili per la formattazione del pacchetto di installazione per il trasferimento del controllo.

Tutti i driver UMDF devono comunicare con un driver in modalità kernel per inviare e ricevere dati dai dispositivi. Per un driver USB UMDF, il driver in modalità kernel è sempre il driver WinUSB fornito da Microsoft (Winusb.sys).

Ogni volta che un driver UMDF effettua una richiesta per lo stack di driver USB, gestione I/O di Windows invia la richiesta a WinUSB. Dopo aver ricevuto la richiesta, WinUSB elabora la richiesta o la inoltra allo stack di driver USB.

Metodi definiti da Microsoft per l'invio di richieste di trasferimento dei controlli

Un driver client USB nell'host avvia la maggior parte delle richieste di controllo per ottenere informazioni sul dispositivo, configurare il dispositivo o inviare comandi di controllo fornitore. Tutte queste richieste possono essere classificate in:

  • Le richieste standard sono definite nella specifica USB. Lo scopo dell'invio di richieste standard è ottenere informazioni sul dispositivo, sulle relative configurazioni, interfacce ed endpoint. Il destinatario di ogni richiesta dipende dal tipo di richiesta. Il destinatario può essere il dispositivo, un'interfaccia o un endpoint.

    Nota

    La destinazione di qualsiasi trasferimento di controllo è sempre l'endpoint predefinito. Il destinatario è l'entità del dispositivo le cui informazioni (descrittore, stato e così via) l'host è interessato.

    Le richieste possono essere ulteriormente classificate in: richieste di configurazione, richieste di funzionalità e richieste di stato.

    • Le richieste di configurazione vengono inviate per ottenere informazioni dal dispositivo in modo che l'host possa configurarlo, ad esempio una richiesta di GET_DESCRIPTOR. Queste richieste possono anche essere richieste di scrittura inviate dall'host per impostare una configurazione specifica o un'impostazione alternativa nel dispositivo.
    • Le richieste di funzionalità vengono inviate dal driver client per abilitare o disabilitare determinate impostazioni del dispositivo booleane supportate dal dispositivo, dall'interfaccia o da un endpoint.
    • Le richieste di stato consentono all'host di ottenere o impostare i bit di stato definiti tramite USB di un dispositivo, un endpoint o un'interfaccia.

    Per altre informazioni, vedere la sezione 9.4 nella specifica USB versione 2.0. I tipi di richiesta standard sono definiti nel file di intestazione Usbspec.h.

  • Le richieste di classe sono definite da una specifica della classe del dispositivo.

  • Le richieste fornitore vengono fornite dal fornitore e dipendono dalle richieste supportate dal dispositivo.

Lo stack USB fornito da Microsoft gestisce tutte le comunicazioni di protocollo con il dispositivo, come illustrato nelle tracce precedenti. Il driver espone le interfacce del driver di dispositivo (DDI) che consentono a un driver client di inviare trasferimenti di controllo in molti modi. Se il driver client è un driver Windows Driver Foundation (WDF), può chiamare routine direttamente per inviare i tipi comuni di richieste di controllo. WDF supporta i trasferimenti di controllo intrinsecamente per KMDF e UMDF.

Alcuni tipi di richieste di controllo non vengono esposti tramite WDF. Per queste richieste, il driver client può usare il modello WDF-hybrid. Questo modello consente al driver client di compilare e formattare richieste in stile WDM XSM e quindi inviare tali richieste usando oggetti framework WDF. Il modello ibrido si applica solo ai driver in modalità kernel.

Per i driver UMDF:

Usare le macro helper e la struttura definite in usb_hw.h. Questa intestazione è inclusa nel driver di esempio UMDF per OSR USB Fx2 Learning Kit.

Usare questa tabella per determinare il modo migliore per inviare richieste di controllo allo stack di driver USB. Se non è possibile visualizzare questa tabella, vedere la tabella in questo articolo.

Se si desidera inviare una richiesta di controllo a... Per un driver KMDF... Per un driver UMDF... Per un driver WDM, creare una struttura ODBC (routine helper)
CLEAR_FEATURE: disabilitare determinate impostazioni di funzionalità nel dispositivo, le relative configurazioni, interfacce ed endpoint. Vedere la sezione 9.4.1 nella specifica USB.
  1. Dichiarare un pacchetto di installazione. Vedere la struttura WDF_USB_CONTROL_SETUP_PACKET.
  2. Inizializzare il pacchetto di installazione chiamando WDF_USB_CONTROL_SETUP_PACKET_INIT_FEATURE.
  3. Specificare un valore del destinatario definito in WDF_USB_BMREQUEST_RECIPIENT.
  4. Specificare il selettore di funzionalità (wValue). Vedere USB_FEATURE_XXX costanti in Usbspec.h. Vedere anche la tabella 9-6 nella specifica USB.
  5. Impostare SetFeature su FALSE.
  6. Inviare la richiesta chiamando WdfUsbTargetDeviceSendControlTransferSynchronously o WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Dichiarare un pacchetto di installazione. Vedere la struttura WINUSB_CONTROL_SETUP_PACKET dichiarata in usb_hw.h.
  2. Inizializzare il pacchetto di installazione chiamando la macro helper, WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE, definita in usb_hw.h.
  3. Specificare un valore del destinatario definito in WINUSB_BMREQUEST_RECIPIENT.
  4. Specificare il selettore di funzionalità (wValue). Vedere USB_FEATURE_XXX costanti in Usbspec.h. Vedere anche la tabella 9-6 nella specifica USB.
  5. Impostare SetFeature su FALSE.
  6. Compilare la richiesta associando il pacchetto di installazione inizializzato all'oggetto richiesta framework e al buffer di trasferimento chiamando il metodo IWDFUsbTargetDevice::FormatRequestForControlTransfer.
  7. Inviare la richiesta chiamando il metodo 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: ottenere la configurazione USB corrente. Vedere la sezione 9.4.2 nella specifica USB. KmDF seleziona la prima configurazione per impostazione predefinita. Per recuperare il numero di configurazione definito dal dispositivo:

  1. Formattare un WDF_USB_CONTROL_SETUP_PACKET e impostarne il membro bRequest su USB_REQUEST_GET_CONFIGURATION.
  2. Inviare la richiesta chiamando WdfUsbTargetDeviceSendControlTransferSynchronously o WdfUsbTargetDeviceFormatRequestForControlTransfer.
UMDF seleziona la prima configurazione per impostazione predefinita. Per recuperare il numero di configurazione definito dal dispositivo:

  1. Dichiarare un pacchetto di installazione. Vedere la struttura WINUSB_CONTROL_SETUP_PACKET dichiarata in usb_hw.h.
  2. Inizializzare il pacchetto di installazione chiamando la macro helper, WINUSB_CONTROL_SETUP_PACKET_INIT, definita in usb_hw.h.
  3. Specificare BmRequestToDevice come direzione, BmRequestToDevice come destinatario e USB_REQUEST_GET_CONFIGURATION come richiesta.
  4. Compilare la richiesta associando il pacchetto di installazione inizializzato all'oggetto richiesta framework e al buffer di trasferimento chiamando il metodo IWDFUsbTargetDevice::FormatRequestForControlTransfer.
  5. Inviare la richiesta chiamando il metodo IWDFIoRequest::Send .
  6. Ricevere il numero di configurazione nel buffer di trasferimento. Accedere a tale buffer chiamando i metodi IWDFMemory.
_URB_CONTROL_GET_CONFIGURATION_REQUEST

URB_FUNCTION_GET_CONFIGURATION
GET_DESCRIPTOR: ottenere descrittori di dispositivo, configurazione, interfaccia e endpoint. Vedere la sezione 9.4.3 nella specifica USB.

Per altre informazioni, vedere Descrittori USB.
Chiamare questi metodi:

Chiamare questi metodi:

_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: ottenere l'impostazione alternativa corrente per un'interfaccia. Vedere la sezione 9.4.4 nella specifica USB.

  1. Ottenere un handle WDFUSBINTERFACE per l'oggetto interfaccia di destinazione chiamando il metodo WdfUsbTargetDeviceGetInterface.
  2. Chiamare il metodo WdfUsbInterfaceGetConfiguredSettingIndex.
  1. Ottenere un puntatore IWDFUsbInterface all'oggetto interfaccia di destinazione.
  2. Chiamare il metodo IWDFUsbInterface::GetConfiguredSettingIndex.
_URB_CONTROL_GET_INTERFACE_REQUEST

URB_FUNCTION_GET_INTERFACE
GET_STATUS: ottenere i bit di stato da un dispositivo, un endpoint o un'interfaccia. Vedere la sezione 9.4.5. nella specifica USB.
  1. Dichiarare un pacchetto di installazione. Vedere la struttura WDF_USB_CONTROL_SETUP_PACKET.
  2. Inizializzare il pacchetto di installazione chiamando WDF_USB_CONTROL_SETUP_PACKET_INIT_GET_STATUS.
  3. Specificare il valore del destinatario definito in WDF_USB_BMREQUEST_RECIPIENT.
  4. Specificare lo stato da ottenere: dispositivo, interfaccia o endpoint (wIndex).
  5. Inviare la richiesta chiamando WdfUsbTargetDeviceSendControlTransferSynchronously o WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Dichiarare un pacchetto di installazione. Vedere la struttura WINUSB_CONTROL_SETUP_PACKET dichiarata in usb_hw.h.
  2. Inizializzare il pacchetto di installazione chiamando la macro helper, WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS, definita in usb_hw.h.
  3. Specificare un valore del destinatario definito in WINUSB_BMREQUEST_RECIPIENT.
  4. Specificare lo stato da ottenere: dispositivo, interfaccia o endpoint (wIndex).
  5. Compilare la richiesta associando il pacchetto di installazione inizializzato all'oggetto richiesta framework e al buffer di trasferimento chiamando il metodo IWDFUsbTargetDevice::FormatRequestForControlTransfer.
  6. Inviare la richiesta chiamando il metodo IWDFIoRequest::Send .
  7. Ricevere il valore di stato nel buffer di trasferimento. Accedere a tale buffer chiamando i metodi IWDFMemory.
  8. Per determinare se lo stato indica la riattivazione remota, usare i valori definiti nell'enumerazione 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: vedere la sezione 9.4.6 nella specifica USB. Questa richiesta viene gestita dallo stack di driver USB; Il driver client non può eseguire questa operazione. Questa richiesta viene gestita dallo stack di driver USB; Il driver client non può eseguire questa operazione. Questa richiesta viene gestita dallo stack di driver USB; Il driver client non può eseguire questa operazione.
SET_CONFIGURATION: impostare una configurazione. Vedere la sezione 9.4.7 nella specifica USB.

Per altre informazioni, vedere Come selezionare una configurazione per un dispositivo USB.
Per impostazione predefinita, KMDF seleziona la configurazione predefinita e la prima impostazione alternativa in ogni interfaccia. Il driver client può modificare la configurazione predefinita chiamando il metodo WdfUsbTargetDeviceSelectConfigType e specificando WdfUsbTargetDeviceSelectConfigTypeUrb come opzione di richiesta. È quindi necessario formattare un VALORE DI CLASSE per questa richiesta e inviarlo allo stack di driver USB. Per impostazione predefinita, UMDF seleziona la configurazione predefinita e la prima impostazione alternativa in ogni interfaccia. Il driver client non può modificare la configurazione. _URB_SELECT_CONFIGURATION

(USBD_SelectConfigUrbAllocateAndBuild)

URB_FUNCTION_SELECT_CONFIGURATION
SET_DESCRIPTOR: aggiornare un descrittore di dispositivo, configurazione o stringa esistente. Vedere la sezione 9.4.8 nella specifica USB.

Questa richiesta non viene comunemente usata. Tuttavia, lo stack di driver USB accetta tale richiesta dal driver client.
  1. Allocare e compilare un ELEMENTO RUNTIME per la richiesta.
  2. Specificare le informazioni di trasferimento in una struttura _URB_CONTROL_DESCRIPTOR_REQUEST.
  3. Inviare la richiesta chiamando WdfUsbTargetDeviceFormatRequestForUrb o WdfUsbTargetDeviceSendUrbSynchronously .
  1. Dichiarare un pacchetto di installazione. Vedere la struttura WINUSB_CONTROL_SETUP_PACKET dichiarata in usb_hw.h.
  2. Specificare le informazioni di trasferimento in base alla specifica USB.
  3. Compilare la richiesta associando il pacchetto di installazione inizializzato all'oggetto richiesta framework e al buffer di trasferimento chiamando il metodo IWDFUsbTargetDevice::FormatRequestForControlTransfer.
  4. Inviare la richiesta chiamando il metodo 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: abilitare determinate impostazioni di funzionalità nel dispositivo, le relative configurazioni, interfacce ed endpoint. Vedere la sezione 9.4.9 nella specifica USB.
  1. Dichiarare un pacchetto di installazione. Vedere la struttura WDF_USB_CONTROL_SETUP_PACKET.
  2. Inizializzare il pacchetto di installazione chiamando WDF_USB_CONTROL_SETUP_PACKET_INIT_FEATURE.
  3. Specificare il valore del destinatario (dispositivo, interfaccia, endpoint) definito in WDF_USB_BMREQUEST_RECIPIENT.
  4. Specificare il selettore di funzionalità (wValue). Vedere USB_FEATURE_XXX costanti in Usbspec.h. Vedere anche la tabella 9-6 nella specifica USB.
  5. Impostare SetFeature su TRUE
  6. Inviare la richiesta chiamando WdfUsbTargetDeviceSendControlTransferSynchronously o WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Dichiarare un pacchetto di installazione. Vedere la struttura WINUSB_CONTROL_SETUP_PACKET dichiarata in usb_hw.h.
  2. Inizializzare il pacchetto di installazione chiamando la macro helper, WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE, definita in usb_hw.h.
  3. Specificare un valore del destinatario definito in WINUSB_BMREQUEST_RECIPIENT.
  4. Specificare il selettore di funzionalità (wValue). Vedere USB_FEATURE_XXX costanti in Usbspec.h. Vedere anche la tabella 9-6 nella specifica USB.
  5. Impostare SetFeature su TRUE.
  6. Compilare la richiesta associando il pacchetto di installazione inizializzato all'oggetto richiesta framework e al buffer di trasferimento chiamando il metodo IWDFUsbTargetDevice::FormatRequestForControlTransfer.
  7. Inviare la richiesta chiamando il metodo 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: modifica l'impostazione alternativa in un'interfaccia. Vedere la sezione 9.4.9 nella specifica USB.

Per altre informazioni, vedere Come selezionare un'impostazione alternativa in un'interfaccia USB.
WdfUsbTargetDeviceSelectConfig
  1. Ottenere un handle WDFUSBINTERFACE per l'oggetto interfaccia di destinazione.
  2. Chiamare il metodo WdfUsbInterfaceSelectSetting.
  1. Ottenere un puntatore IWDFUsbInterface all'oggetto interfaccia di destinazione.
  2. Chiamare il metodo IWDFUsbInterface::SelectSetting.
_URB_SELECT_INTERFACE

(USBD_SelectInterfaceUrbAllocateAndBuild)

URB_FUNCTION_SELECT_INTERFACE
SYNC_FRAME: impostare e ottenere il numero di frame di sincronizzazione dell'endpoint e . Vedere la sezione 9.4.10 nella specifica USB. Questa richiesta viene gestita dallo stack di driver USB; Il driver client non può eseguire questa operazione. Questa richiesta viene gestita dallo stack di driver USB; Il driver client non può eseguire questa operazione. Questa richiesta viene gestita dallo stack di driver USB; Il driver client non può eseguire questa operazione.
Per le richieste specifiche della classe del dispositivo e i comandi del fornitore.
  1. Dichiarare un pacchetto di installazione. Vedere la struttura WDF_USB_CONTROL_SETUP_PACKET.
  2. Inizializzare il pacchetto di installazione chiamando WDF_USB_CONTROL_SETUP_PACKET_INIT_CLASS richieste specifiche o WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR per i comandi del fornitore.
  3. Specificare il valore del destinatario (dispositivo, interfaccia, endpoint) definito in WDF_USB_BMREQUEST_RECIPIENT.
  4. Inviare la richiesta chiamando WdfUsbTargetDeviceSendControlTransferSynchronously o WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Dichiarare un pacchetto di installazione. Vedere la struttura WINUSB_CONTROL_SETUP_PACKET dichiarata in usb_hw.h.
  2. Inizializzare il pacchetto di installazione chiamando la macro helper, WINUSB_CONTROL_SETUP_PACKET_INIT_CLASS o WINUSB_CONTROL_SETUP_PACKET_INIT_VENDOR, definita in usb_hw.h.
  3. Specificare la direzione (vedere l'enumerazione WINUSB_BMREQUEST_DIRECTION ), il destinatario ( vedere l'enumerazione WINUSB_BMREQUEST_RECIPIENT ) e la richiesta, come descritto nella classe o nella specifica hardware.
  4. Compilare la richiesta associando il pacchetto di installazione inizializzato all'oggetto richiesta framework e al buffer di trasferimento chiamando il metodo IWDFUsbTargetDevice::FormatRequestForControlTransfer.
  5. Inviare la richiesta chiamando il metodo IWDFIoRequest::Send .
  6. Ricevere le informazioni dal dispositivo nel buffer di trasferimento. Accedere a tale buffer chiamando i metodi 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

Come inviare un trasferimento di controllo per i comandi del fornitore - KMDF

Questa procedura illustra come un driver client può inviare un trasferimento di controllo. In questo esempio, il driver client invia un comando fornitore che recupera la versione del firmware dal dispositivo.

  1. Dichiarare una costante per il comando fornitore. Studiare la specifica hardware e determinare il comando fornitore che si vuole usare.

  2. Dichiarare una struttura WDF_MEMORY_DESCRIPTOR e inizializzarla chiamando la macro WDF_MEMORY_DESCRIPTOR_INIT_BUFFER . Questa struttura riceverà la risposta dal dispositivo dopo che il driver USB completa la richiesta.

  3. A seconda che si invii la richiesta in modo sincrono o asincrono, specificare le opzioni di invio:

  4. Dichiarare una struttura WDF_USB_CONTROL_SETUP_PACKET per contenere il token di installazione e formattare la struttura. A tale scopo, chiamare la macro WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR per formattare il pacchetto di installazione. Nella chiamata specificare la direzione della richiesta, il destinatario, le opzioni di richiesta inviata (inizializzate nel passaggio 3) e la costante per il comando fornitore.

  5. Inviare la richiesta chiamando WdfUsbTargetDeviceSendControlTransferSynchronously o WdfUsbTargetDeviceFormatRequestForControlTransfer.

  6. Controllare il valore NTSTATUS restituito dal framework ed esaminare il valore ricevuto.

Questo esempio di codice invia una richiesta di trasferimento del controllo a un dispositivo USB per recuperare la versione del firmware. La richiesta viene inviata in modo sincrono e il driver client specifica un valore di timeout relativo di 5 secondi (in unità da 100 nanosecondi). Il driver archivia la risposta ricevuta nel contesto di dispositivo definito dal 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;
}

Come inviare un trasferimento di controllo per GET_STATUS - UMDF

Questa procedura illustra come un driver client può inviare un trasferimento di controllo per un comando GET_STATUS. Il destinatario della richiesta è il dispositivo e la richiesta ottiene informazioni in bit D1-D0. Per altre informazioni, vedere la figura 9-4 nella specifica USB.

  1. Includere il file di intestazione Usb_hw.h disponibile con il driver di esempio UMDF per OSR USB Fx2 Learning Kit.

  2. Dichiarare una struttura WINUSB_CONTROL_SETUP_PACKET .

  3. Inizializzare il pacchetto di installazione chiamando la macro helper WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS.

  4. Specificare BmRequestToDevice come destinatario.

  5. Specificare 0 nel valore index .

  6. Chiamare il metodo helper SendControlTransferSynchronously per inviare la richiesta in modo sincrono.

    Il metodo helper compila la richiesta associando il pacchetto di installazione inizializzato all'oggetto richiesta framework e al buffer di trasferimento chiamando il metodo IWDFUsbTargetDevice::FormatRequestForControlTransfer . Il metodo helper invia quindi la richiesta chiamando il metodo IWDFIoRequest::Send . Al termine del metodo, controllare il valore restituito.

  7. Per determinare se lo stato indica la riattivazione remota e self-powered, usare questi valori definiti nell'enumerazione WINUSB_DEVICE_TRAITS :

Questo esempio di codice invia una richiesta di trasferimento del controllo a un oggetto per ottenere lo stato del dispositivo. L'esempio invia la richiesta in modo sincrono chiamando un metodo helper denominato 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;
 }

L'esempio di codice seguente illustra l'implementazione del metodo helper denominato SendControlTransferSynchronously. Questo metodo invia una richiesta in modo sincrono.

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 si usa Winusb.sys come driver di funzione per il dispositivo, è possibile inviare trasferimenti di controllo da un'applicazione. Per formattare il pacchetto di installazione in WinUSB, usare le macro e le strutture helper UMDF descritte nella tabella in questo articolo. Per inviare la richiesta, chiamare WinUsb_ControlTransfer funzione.