Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Een USB-apparaat toont de mogelijkheden in de vorm van een reeks interfaces, een USB-configuratie genoemd. Elke interface bestaat uit een of meer alternatieve instellingen en elke alternatieve instelling bestaat uit een set eindpunten. In dit onderwerp worden de verschillende descriptors beschreven die zijn gekoppeld aan een USB-configuratie.
Een USB-configuratie wordt beschreven in een configuratiedescriptor (zie USB_CONFIGURATION_DESCRIPTOR structuur). Een configuratiedescriptor bevat informatie over de configuratie en de bijbehorende interfaces, alternatieve instellingen en hun eindpunten. Elke interfacedescriptor of alternatieve instelling wordt beschreven in een USB_INTERFACE_DESCRIPTOR structuur. In een configuratie wordt elke interface-descriptor gevolgd in het geheugen door alle eindpunt-descriptoren voor de interface en alternatieve instellingen. Elke eindpuntdescriptor wordt opgeslagen in een USB_ENDPOINT_DESCRIPTOR structuur.
Denk bijvoorbeeld aan een USB-webcamapparaat dat wordt beschreven in de indeling van het USB-apparaat. Het apparaat ondersteunt een configuratie met twee interfaces en de eerste interface (index 0) ondersteunt twee alternatieve instellingen.
In het volgende voorbeeld ziet u de configuratiedescriptor voor het USB-webcamapparaat:
Configuration Descriptor:
wTotalLength: 0x02CA
bNumInterfaces: 0x02
bConfigurationValue: 0x01
iConfiguration: 0x00
bmAttributes: 0x80 (Bus Powered )
MaxPower: 0xFA (500 mA)
Het veld bConfigurationValue geeft het getal aan voor de configuratie die is gedefinieerd in de firmware van het apparaat. Het clientstuurprogramma gebruikt die numerieke waarde om een actieve configuratie te selecteren. Voor meer informatie over USB-apparaatconfiguratie, zie Hoe selecteer je een configuratie voor een USB-apparaat. Een USB-configuratie geeft ook bepaalde energiekenmerken aan. De bmAttributes bevat een bitmasker dat aangeeft of de configuratie de functie voor extern ontwaken ondersteunt en of het apparaat busgevoed of zelfgevoed is. In het veld MaxPower wordt het maximale vermogen (in milliamp-eenheden) opgegeven dat het apparaat kan trekken van de host, wanneer het apparaat bus-aangedreven is. De configuratiedescriptor geeft ook het totale aantal interfaces (bNumInterfaces) aan dat het apparaat ondersteunt.
In het volgende voorbeeld ziet u de interfacedescriptor voor alternatieve instelling 0 van interface 0 voor het webcamapparaat:
Interface Descriptor:
bInterfaceNumber: 0x00
bAlternateSetting: 0x00
bNumEndpoints: 0x01
bInterfaceClass: 0x0E
bInterfaceSubClass: 0x02
bInterfaceProtocol: 0x00
iInterface: 0x02
0x0409: "Microsoft LifeCam VX-5000"
0x0409: "Microsoft LifeCam VX-5000"
In het voorgaande voorbeeld noteert u de veldwaarden bInterfaceNumber en bAlternateSetting . Deze velden bevatten indexwaarden die een clientstuurprogramma gebruikt om de interface en een van de alternatieve instellingen te activeren. Voor activering verzendt het stuurprogramma een select-interface-aanvraag naar de USB-stuurprogrammastack. De stuurprogrammastack bouwt vervolgens een standaard besturingsaanvraag (SET INTERFACE) en verzendt deze naar het apparaat. Let op het veld bInterfaceClass . De interfacedescriptor of de descriptor voor een van de alternatieve instellingen geeft een klassecode, subklasse en protocol op. De waarde van 0x0E geeft aan dat de interface voor de videoapparaatklasse is. Let ook op het veld iInterface . Deze waarde geeft aan dat er twee tekenreeksdescriptors zijn toegevoegd aan de interfacedescriptor. Tekenreeksdescriptors bevatten Unicode-beschrijvingen die worden gebruikt tijdens de inventarisatie van het apparaat om de functionaliteit te identificeren. Voor meer informatie over stringdescriptors, zie USB-stringdescriptors.
Elk eindpunt in een interface beschrijft één stroom invoer of uitvoer voor het apparaat. Een apparaat dat streams voor verschillende soorten functies ondersteunt, heeft meerdere interfaces. Een apparaat dat ondersteuning biedt voor verschillende streams die betrekking hebben op een functie, kan meerdere eindpunten op één interface ondersteunen.
Alle typen eindpunten (met uitzondering van het standaardeindpunt) moeten eindpuntdescriptors opgeven, zodat de host informatie over het eindpunt kan ophalen. Een eindpuntdescriptor bevat informatie, zoals het adres, het type, de richting en de hoeveelheid gegevens die het eindpunt kan verwerken. De gegevensoverdracht naar het eindpunt zijn gebaseerd op die informatie.
In het volgende voorbeeld ziet u een eindpuntdescriptor voor het webcamapparaat:
Endpoint Descriptor:
bEndpointAddress: 0x82 IN
bmAttributes: 0x01
wMaxPacketSize: 0x0080 (128)
bInterval: 0x01
Het veld bEndpointAddress geeft het unieke eindpuntadres op dat het eindpuntnummer (Bits 3..0) en de richting van het eindpunt (Bit 7) bevat. Door deze waarden in het voorgaande voorbeeld te lezen, kunnen we bepalen dat de descriptor een IN-eindpunt beschrijft waarvan het eindpuntnummer 2 is. Het kenmerk bmAttributes geeft aan dat het eindpunttype isochronisch. Het wMaxPacketSizefield geeft het maximum aantal bytes aan dat het eindpunt in één transactie kan verzenden of ontvangen. Bits 12..11 geven het totale aantal transacties aan dat per microframe kan worden verzonden. De bInterval geeft aan hoe vaak het eindpunt gegevens kan verzenden of ontvangen.
Hoe de configuratiedescriptor te krijgen
De configuratiedescriptor wordt verkregen van het apparaat via een standaardapparaataanvraag (GET_DESCRIPTOR), die wordt verzonden als een controleoverdracht door de USB-stuurprogrammastack. Een USB-clientstuurprogramma kan de aanvraag op een van de volgende manieren initiëren:
Als het apparaat slechts één configuratie ondersteunt, is de eenvoudigste manier om de door het framework geleverde WdfUsbTargetDeviceRetrieveConfigDescriptor-methode aan te roepen.
Voor een apparaat dat meerdere configuraties ondersteunt, moet het stuurprogramma een URB indienen als het clientstuurprogramma de descriptor van een andere configuratie dan de eerste wil ophalen. Als u een URB wilt verzenden, moet het stuurprogramma de URB toewijzen, formatteren en vervolgens verzenden naar de USB-stuurprogrammastack.
Als u de URB wilt toewijzen, moet het clientstuurprogramma de WdfUsbTargetDeviceCreateUrb-methode aanroepen. De methode ontvangt een pointer naar een URB die is toegewezen door de USB driver stack.
Het clientstuurprogramma kan de UsbBuildGetDescriptorRequest macro gebruiken om de URB op te maken. De macro stelt alle benodigde informatie in de URB in, zoals het door het apparaat gedefinieerde configuratienummer waarvoor de descriptor moet worden opgehaald. De functie URB is ingesteld op URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE (zie _URB_CONTROL_DESCRIPTOR_REQUEST) en het type descriptor is ingesteld op USB_CONFIGURATION_DESCRIPTOR_TYPE. Met behulp van de informatie in de URB bouwt de USB-stuurprogrammastack een standaard controleaanvraag en verzendt deze naar het apparaat.
Als u de URB wilt verzenden, moet het clientstuurprogramma een WDF-aanvraagobject gebruiken. Als u het aanvraagobject asynchroon naar de USB-stuurprogrammastack wilt verzenden, moet het stuurprogramma de methode **WdfRequestSend** aanroepen. Als u deze synchroon wilt verzenden, roept u de methode WdfUsbTargetDeviceSendUrbSynchronously aan .
WDM-stuurprogramma's: Een Windows Driver Model -clientstuurprogramma (WDM) kan alleen de configuratiedescriptor ophalen door een URB in te dienen. Als u de URB wilt toewijzen, moet het stuurprogramma de USBD_UrbAllocate routine aanroepen. Als u de URB wilt opmaken, moet het stuurprogramma de macro UsbBuildGetDescriptorRequest aanroepen. Als u de URB wilt verzenden, moet het stuurprogramma de URB koppelen aan een IRP en de IRP verzenden naar de USB-stuurprogrammastack. Zie Een URB verzenden voor meer informatie.
Binnen een USB-configuratie zijn het aantal interfaces en de bijbehorende alternatieve instellingen variabel. Daarom is het moeilijk om de grootte van de buffer te voorspellen die nodig is om de configuratiedescriptor op te slaan. Het clientstuurprogramma moet al die informatie in twee stappen verzamelen. Bepaal eerst welke groottebuffer vereist is voor het opslaan van alle configuratiedescriptor en geef vervolgens een aanvraag uit om de volledige descriptor op te halen. Een clientstuurprogramma kan de grootte op een van de volgende manieren verkrijgen:
Voer de volgende stappen uit om de configuratiedescriptor te verkrijgen door WdfUsbTargetDeviceRetrieveConfigDescriptor aan te roepen:
- Haal de grootte van de buffer op die is vereist om alle configuratiegegevens op te slaan door WdfUsbTargetDeviceRetrieveConfigDescriptor aan te roepen. Het stuurprogramma moet NULL doorgeven in de buffer en een variabele om de grootte van de buffer vast te houden.
- Wijs een grotere buffer toe op basis van de grootte die is ontvangen via de vorige aanroep WdfUsbTargetDeviceRetrieveConfigDescriptor .
- Roep WdfUsbTargetDeviceRetrieveConfigDescriptor opnieuw aan en geef een aanwijzer op naar de nieuwe buffer die is toegewezen in stap 2.
NTSTATUS RetrieveDefaultConfigurationDescriptor (
_In_ WDFUSBDEVICE UsbDevice,
_Out_ PUSB_CONFIGURATION_DESCRIPTOR *ConfigDescriptor
)
{
NTSTATUS ntStatus = -1;
USHORT sizeConfigDesc;
PUSB_CONFIGURATION_DESCRIPTOR fullConfigDesc = NULL;
PAGED_CODE();
*ConfigDescriptor = NULL;
ntStatus = WdfUsbTargetDeviceRetrieveConfigDescriptor (
UsbDevice,
NULL,
&sizeConfigDesc);
if (sizeConfigDesc == 0)
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not retrieve the configuration descriptor size.");
goto Exit;
}
else
{
fullConfigDesc = (PUSB_CONFIGURATION_DESCRIPTOR) ExAllocatePoolWithTag (
NonPagedPool,
sizeConfigDesc,
USBCLIENT_TAG);
if (!fullConfigDesc)
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
}
RtlZeroMemory (fullConfigDesc, sizeConfigDesc);
ntStatus = WdfUsbTargetDeviceRetrieveConfigDescriptor (
UsbDevice,
fullConfigDesc,
&sizeConfigDesc);
if (!NT_SUCCESS(ntStatus))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not retrieve the configuration descriptor.");
goto Exit;
}
*ConfigDescriptor = fullConfigDesc;
Exit:
return ntStatus;
}
Voer de volgende stappen uit om de configuratiedescriptor te verkrijgen door een URB in te dienen:
- Wijs een URB toe door de WdfUsbTargetDeviceCreateUrb-methode aan te roepen.
- Roep de macro UsbBuildGetDescriptorRequest aan om de URB op te maken. De overdrachtsbuffer van de URB moet verwijzen naar een buffer die groot genoeg is om een USB_CONFIGURATION_DESCRIPTOR structuur vast te houden.
- Verzend de URB als een WDF-aanvraagobject door WdfRequestSend of WdfUsbTargetDeviceSendUrbSynchronously aan te roepen.
- Nadat de aanvraag is voltooid, controleert u het element wTotalLength van USB_CONFIGURATION_DESCRIPTOR. Deze waarde geeft de grootte aan van de buffer die is vereist om een volledige configuratiedescriptor te bevatten.
- Wijs een grotere buffer toe op basis van de grootte die is opgehaald in wTotalLength.
- Geef dezelfde aanvraag met de grotere buffer.
De volgende voorbeeldcode toont de Aanroep UsbBuildGetDescriptorRequest voor een aanvraag voor het ophalen van configuratiegegevens voor de i-th-configuratie:
NTSTATUS FX3_RetrieveConfigurationDescriptor (
_In_ WDFUSBDEVICE UsbDevice,
_In_ PUCHAR ConfigurationIndex,
_Out_ PUSB_CONFIGURATION_DESCRIPTOR *ConfigDescriptor
)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
USB_CONFIGURATION_DESCRIPTOR configDesc;
PUSB_CONFIGURATION_DESCRIPTOR fullConfigDesc = NULL;
PURB urb = NULL;
WDFMEMORY urbMemory = NULL;
PAGED_CODE();
RtlZeroMemory (&configDesc, sizeof(USB_CONFIGURATION_DESCRIPTOR));
*ConfigDescriptor = NULL;
// Allocate an URB for the get-descriptor request.
// WdfUsbTargetDeviceCreateUrb returns the address of the
// newly allocated URB and the WDFMemory object that
// contains the URB.
ntStatus = WdfUsbTargetDeviceCreateUrb (
UsbDevice,
NULL,
&urbMemory,
&urb);
if (!NT_SUCCESS (ntStatus))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not allocate URB for an open-streams request.");
goto Exit;
}
// Format the URB.
UsbBuildGetDescriptorRequest (
urb, // Points to the URB to be formatted
(USHORT) sizeof( struct _URB_CONTROL_DESCRIPTOR_REQUEST ), // Size of the URB.
USB_CONFIGURATION_DESCRIPTOR_TYPE, // Type of descriptor
*ConfigurationIndex, // Index of the configuration
0, // Not used for configuration descriptors
&configDesc, // Points to a USB_CONFIGURATION_DESCRIPTOR structure
NULL, // Not required because we are providing a buffer not MDL
sizeof(USB_CONFIGURATION_DESCRIPTOR), // Size of the USB_CONFIGURATION_DESCRIPTOR structure.
NULL // Reserved.
);
// Send the request synchronously.
ntStatus = WdfUsbTargetDeviceSendUrbSynchronously (
UsbDevice,
NULL,
NULL,
urb);
if (configDesc.wTotalLength == 0)
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not retrieve the configuration descriptor size.");
ntStatus = USBD_STATUS_INAVLID_CONFIGURATION_DESCRIPTOR;
goto Exit;
}
// Allocate memory based on the retrieved size.
// The allocated memory is released by the caller.
fullConfigDesc = (PUSB_CONFIGURATION_DESCRIPTOR) ExAllocatePoolWithTag (
NonPagedPool,
configDesc.wTotalLength,
USBCLIENT_TAG);
RtlZeroMemory (fullConfigDesc, configDesc.wTotalLength);
if (!fullConfigDesc)
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
// Format the URB.
UsbBuildGetDescriptorRequest (
urb,
(USHORT) sizeof( struct _URB_CONTROL_DESCRIPTOR_REQUEST ),
USB_CONFIGURATION_DESCRIPTOR_TYPE,
*ConfigurationIndex,
0,
fullConfigDesc,
NULL,
configDesc.wTotalLength,
NULL
);
// Send the request again.
ntStatus = WdfUsbTargetDeviceSendUrbSynchronously (
UsbDevice,
NULL,
NULL,
urb);
if ((fullConfigDesc->wTotalLength == 0) || !NT_SUCCESS (ntStatus))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not retrieve the configuration descriptor.");
ntStatus = USBD_STATUS_INAVLID_CONFIGURATION_DESCRIPTOR;
goto Exit;
}
// Return to the caller.
*ConfigDescriptor = fullConfigDesc;
Exit:
if (urbMemory)
{
WdfObjectDelete (urbMemory);
}
return ntStatus;
}
Wanneer het apparaat de configuratiedescriptor retourneert, wordt de aanvraagbuffer gevuld met interfacedescriptors voor alle alternatieve instellingen en eindpuntdescriptors voor alle eindpunten binnen een bepaalde alternatieve instelling. Voor het apparaat dat wordt beschreven in de indeling van het USB-apparaat, ziet u in het volgende diagram hoe configuratiegegevens in het geheugen worden ingedeeld.
Het op nul gebaseerde bInterfaceNumber-lid van USB_INTERFACE_DESCRIPTOR onderscheidt interfaces binnen een configuratie. Voor een bepaalde interface maakt het op nul gebaseerde bAlternateSetting-lid onderscheid tussen alternatieve instellingen van de interface. Het apparaat retourneert interfacedescriptors in volgorde van bInterfaceNumber-waarden en vervolgens in volgorde van bAlternateSetting-waarden .
Als u wilt zoeken naar een bepaalde interfacedescriptor binnen de configuratie, kan het clientstuurprogramma USBD_ParseConfigurationDescriptorEx aanroepen. In de aanroep biedt het clientstuurprogramma een beginpositie binnen de configuratie. Desgewenst kan het stuurprogramma een interfacenummer, een alternatieve instelling, een klasse, een subklasse of een protocol opgeven. De routine retourneert een aanwijzer naar de volgende overeenkomende interfacedescriptor.
Als u een configuratiedescriptor wilt onderzoeken voor een eindpunt of tekenreeksdescriptor, gebruikt u de USBD_ParseDescriptors routine. De aanroeper biedt een beginpositie binnen de configuratie en een descriptortype, zoals USB_STRING_DESCRIPTOR_TYPE of USB_ENDPOINT_DESCRIPTOR_TYPE. De routine retourneert een aanwijzer naar de volgende overeenkomende descriptor.