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.
Dit artikel bevat een overzicht van USB-pijpen en beschrijft de stappen die een USB-clientstuurprogramma nodig heeft om pijpgrepen te verkrijgen van de USB-stuurprogrammastack.
Een USB-eindpunt is een buffer in het apparaat waar het clientstuurprogramma gegevens naar verzendt of ontvangt van. Als u gegevens wilt verzenden of ontvangen, verzendt het clientstuurprogramma een I/O-overdrachtsaanvraag naar de USB-stuurprogrammastack, die de gegevens aan de hostcontroller presenteert. De hostcontroller volgt vervolgens bepaalde protocollen (afhankelijk van het type eindpunt: bulksgewijs, onderbreken of isochronisch) om aanvragen te bouwen die gegevens overdragen naar of van het apparaat. Alle details van de gegevensoverdracht worden geabstraheerd van het clientstuurprogramma. Zolang het clientstuurprogramma een goed opgemaakte aanvraag indient, verwerkt de USB-stuurprogrammastack de aanvraag en draagt de gegevens over naar het apparaat.
Tijdens de apparaatconfiguratie maakt de USB-stuurprogrammastack een USB-pijp (aan de hostzijde) voor elk van de eindpunten van het apparaat die zijn gedefinieerd in de USB-interface en de actieve alternatieve instelling. Een USB-pijp is een communicatiekanaal tussen de hostcontroller en het eindpunt. Voor het clientstuurprogramma is een pijp een logische abstractie van het eindpunt. Als u gegevensoverdrachten wilt verzenden, moet het stuurprogramma de pijpgreep ophalen die is gekoppeld aan het eindpunt dat het doel is voor de overdracht. Pipe handles zijn ook vereist wanneer de driver overdrachten wil afbreken of de pijp opnieuw wil instellen, in geval van foutsituaties.
Alle kenmerken van een pijp zijn afgeleid van de bijbehorende eindpuntdescriptor. Afhankelijk van het type eindpunt wijst de USB-stuurprogrammastack bijvoorbeeld een type toe voor de pijp. Voor een bulkeindpunt maakt de USB-stuurprogrammastack een bulkpijp; voor een isochronisch eindpunt wordt een isochronische pijp gemaakt, enzovoort. Een ander belangrijk kenmerk is de hoeveelheid gegevens die de hostcontroller in een aanvraag naar het eindpuntpunt kan verzenden. Afhankelijk van deze waarde moet het clientstuurprogramma de indeling van de overdrachtsbuffer bepalen.
Windows Driver Foundation (WDF) biedt gespecialiseerde I/O-doelobjecten in Kernel-Mode Driver Framework (KMDF) en User-Mode Driver Framework (UMDF) die veel van de configuratietaken voor het clientstuurprogramma vereenvoudigen. Door deze objecten te gebruiken, kan het clientstuurprogramma informatie over de huidige configuratie ophalen, zoals het aantal interfaces, alternatieve instelling binnen elke interface en hun eindpunten. Een van deze objecten, het doelpijpobject genoemd, voert eindpuntgerelateerde taken uit. In dit artikel wordt beschreven hoe u pijpinformatie kunt verkrijgen met behulp van het doelpijpobject.
Voor WDM-clientstuurprogramma's (Windows Driver Model) retourneert de USB-stuurprogrammastack een matrix van USBD_PIPE_INFORMATION structuren. Het aantal elementen in de matrix is afhankelijk van het aantal eindpunten dat is gedefinieerd voor de actieve alternatieve instelling van een interface in de geselecteerde configuratie. Elk element bevat informatie over de pijp die is gemaakt voor een bepaald eindpunt. Zie How to Select a Configuration for a USB Device (Een configuratie voor een USB-apparaat selecteren) voor meer informatie over het selecteren van een configuratie en het ophalen van de matrix met pijpgegevens.
Wat u moet weten
Voordat het clientstuurprogramma pijpen kan inventariseren, moet u ervoor zorgen dat aan deze vereisten wordt voldaan:
Het clientstuurprogramma moet het USB-doelapparaatobject van het framework hebben aangemaakt.
Als u de USB-sjablonen gebruikt die worden geleverd met Microsoft Visual Studio Professional 2012, voert de sjablooncode deze taken uit. De sjablooncode verkrijgt de ingang naar het doelapparaatobject en slaat deze op in de apparaatcontext.
KMDF-clientstuurprogramma:
Een KMDF-clientstuurprogramma moet een WDFUSBDEVICE-ingang verkrijgen door de methode WdfUsbTargetDeviceCreateWithParameters aan te roepen. Zie 'Broncode van apparaat' in Informatie over de KMDF-(USB-clientstuurprogrammacodestructuur) voor meer informatie.
UMDF-clientstuurprogramma:
Een UMDF-clientstuurprogramma moet een IWDFUsbTargetDevice-aanwijzer verkrijgen door een query uit te voeren op het object van het frameworkdoelapparaat. Zie 'IPnpCallbackHardware-implementatie en USB-specifieke taken' in Understanding the USB client driver code structure (UMDF)voor meer informatie.
Het apparaat moet een actieve configuratie hebben.
Als u USB-sjablonen gebruikt, selecteert de code de eerste configuratie en de standaard alternatieve instelling in elke interface. Zie Een alternatieve instelling selecteren in een USB-interface voor meer informatie over het wijzigen van deze standaardinstelling.
KMDF-clientstuurprogramma:
Een KMDF-clientstuurprogramma moet de WdfUsbTargetDeviceSelectConfig-methode aanroepen.
UMDF-clientstuurprogramma:
Voor een UMDF-clientstuurprogramma selecteert het framework de eerste configuratie en de standaard alternatieve instelling voor elke interface in die configuratie.
USB-pijphandvatten verkrijgen in een KMDF-clientstuurprogramma
Het framework vertegenwoordigt elke pijp, die wordt geopend door de USB-stuurprogrammastack, als een USB-doelpijpobject. Een KMDF-clientstuurprogramma heeft toegang tot de methoden van het doelpijpobject om informatie over de pijp op te halen. Als u gegevensoverdrachten wilt uitvoeren, moet het clientstuurprogramma WDFUSBPIPE-pijpgrepen hebben. Om de pijpgrepen op te halen, moet het stuurprogramma de interfaces en alternatieve instellingen van de actieve configuratie inventariseren en vervolgens de eindpunten opsommen die in elke instelling zijn gedefinieerd. Het uitvoeren van inventarisatiebewerkingen kan duur zijn voor elke gegevensoverdracht. Daarom is een benadering om pijphandvaten te verkrijgen nadat het apparaat is geconfigureerd en deze op te slaan in de door het stuurprogramma gedefinieerde apparaatcontext. Wanneer het stuurprogramma aanvragen voor gegevensoverdracht ontvangt, kan het stuurprogramma de vereiste pipe-ingangen ophalen uit de apparaatcontext en deze gebruiken om de aanvraag te verzenden. Als het clientstuurprogramma de configuratie van het apparaat wijzigt, bijvoorbeeld een alternatieve instelling selecteert, moet het stuurprogramma ook de apparaatcontext vernieuwen met de nieuwe pijpgrepen. Anders kan het besturingsprogramma onbedoeld overdrachtsaanvragen verzenden op verouderde pijplijnhandvatten.
Pijpgrepen zijn niet vereist voor besturingsoverdrachten. Voor het verzenden van aanvragen voor beheeroverdracht roept een WDF-clientstuurprogramma WdfUsbDevicexxxx-methoden aan die beschikbaar worden gesteld door het frameworkapparaatobject. Voor deze methoden is een WDFUSBDEVICE-ingang vereist om besturingsoverdrachten te initiëren die gericht zijn op het standaardeindpunt. Voor dergelijke overdrachten is het I/O-doel voor de aanvraag het standaardeindpunt en wordt vertegenwoordigd door de WDFIOTARGET-ingang, die wordt geabstraheerd door de WDFUSBPIPE-ingang. Op apparaatniveau is de WDFUSBDEVICE-ingang een abstractie van de WDFUSBPIPE-ingang naar het standaardeindpunt.
Zie How to send a USB control transfer. (Een USB-besturingsoverdracht verzenden) voor informatie over het verzenden van besturingsoverdrachten en de KMDF-methoden.
Breid de contextstructuur van uw apparaat uit om pijpgrepen op te slaan.
Als u de eindpunten in uw apparaat kent, kunt u de contextstructuur van uw apparaat uitbreiden door WDFUSBPIPE-leden toe te voegen om de bijbehorende USB-pijpgrepen op te slaan. U kunt bijvoorbeeld de contextstructuur van het apparaat uitbreiden, zoals hier wordt weergegeven:
typedef struct _DEVICE_CONTEXT { WDFUSBDEVICE UsbDevice; WDFUSBINTERFACE UsbInterface; WDFUSBPIPE BulkReadPipe; // Pipe opened for the bulk IN endpoint. WDFUSBPIPE BulkWritePipe; // Pipe opened for the bulk IN endpoint. WDFUSBPIPE InterruptPipe; // Pipe opened for the interrupt IN endpoint. WDFUSBPIPE StreamInPipe; // Pipe opened for stream IN endpoint. WDFUSBPIPE StreamOutPipe; // Pipe opened for stream OUT endpoint. UCHAR NumberConfiguredPipes; // Number of pipes opened. ... ... // Other members. Not shown. } DEVICE_CONTEXT, *PDEVICE_CONTEXT;
Declareer een pijpcontextstructuur.
Elke pijp kan eindpuntgerelateerde kenmerken opslaan in een andere structuur, de pijpcontext genoemd. Net als bij een apparaatcontext is een pijpcontext een gegevensstructuur (gedefinieerd door het clientstuurprogramma) voor het opslaan van informatie over pijpen die zijn gekoppeld aan eindpunten. Tijdens de apparaatconfiguratie geeft het clientstuurprogramma een aanwijzer door naar zijn pijpcontext aan het framework. Het framework wijst, op basis van de grootte van de datastructuur, een blok geheugen toe en slaat een aanwijzer naar die geheugenlocatie op, samen met het USB-doelpijpobject van het framework. Het clientstuurprogramma kan de pointer gebruiken om pijpgegevens te openen en op te slaan in elementen van de pijpcontext.
typedef struct _PIPE_CONTEXT { ULONG MaxPacketSize; ULONG MaxStreamsSupported; PUSBD_STREAM_INFORMATION StreamInfo; } PIPE_CONTEXT, *PPIPE_CONTEXT; WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PIPE_CONTEXT, GetPipeContext)
In dit voorbeeld wordt in de pijpcontext het maximum aantal bytes opgeslagen dat in één overdracht kan worden verzonden. Het clientstuurprogramma kan die waarde gebruiken om de grootte van de overdrachtsbuffer te bepalen. De declaratie bevat ook de WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro, waarmee een inlinefunctie, GetPipeContext, wordt gegenereerd. Het clientstuurprogramma kan die functie aanroepen om een aanwijzer op te halen naar het geheugenblok waarin de pipecontext wordt opgeslagen.
Zie Contextruimte voor frameworkobjecten voor meer informatie over contexten.
Als u een aanwijzer wilt doorgeven aan het framework, initialiseert het clientstuurprogramma eerst de pijpcontext door WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE aan te roepen. Vervolgens wordt een aanwijzer doorgegeven aan de pijpcontext tijdens het aanroepen van WdfUsbTargetDeviceSelectConfig (voor het selecteren van een configuratie) of WdfUsbInterfaceSelectSetting (voor het selecteren van een alternatieve instelling).
Nadat de configuratieaanvraag voor het apparaat is voltooid, inventariseert u de interface en haalt u de pijpgrepen voor de geconfigureerde pijpen op. U hebt deze set gegevens nodig:
- WDFUSBINTERFACE-ingang naar de interface die de huidige instelling bevat. U kunt die handle verkrijgen door de interfaces in de actieve configuratie op te sommen. Als u ook een aanwijzer hebt opgegeven voor een WDF_USB_DEVICE_SELECT_CONFIG_PARAMS structuur in WdfUsbTargetDeviceSelectConfig, kunt u de ingang ophalen van het lid Type.SingleInterface.ConfiguredUsbInterface (voor apparaten met één interface) of het lid Type.MultiInterface.Pairs.UsbInterface (voor apparaat met meerdere interfaces).
- Aantal kanalen dat is geopend voor de eindpunten in de huidige configuratie. U kunt dat nummer verkrijgen via een specifieke interface door de methode WdfUsbInterfaceGetNumConfiguredPipes aan te roepen.
- WDFUSBPIPE-ingangen voor alle geconfigureerde pijpen. U kunt de handle verkrijgen door de methode WdfUsbInterfaceGetConfiguredPipe aan te roepen.
Na het verkrijgen van de pijpgreep kan het clientstuurprogramma methoden aanroepen om het type en de richting van de pijp te bepalen. Het stuurprogramma kan informatie over het eindpunt verkrijgen in een WDF_USB_PIPE_INFORMATION structuur. Het stuurprogramma kan de gevulde structuur verkrijgen door de WdfUsbTargetPipeGetInformation-methode aan te roepen. Het stuurprogramma kan ook een pointer naar de structuur opgeven in de aanroep van WdfUsbInterfaceGetConfiguredPipe.
In het volgende codevoorbeeld worden de pijpen in de huidige instelling opgesomd. Het haalt pijpgrepen op voor de bulk- en interrupteindpunten van het apparaat en slaat deze op in de contextstructuur van het stuurprogramma. Hiermee wordt de maximale pakketgrootte van elk eindpunt opgeslagen in de bijbehorende pijpcontext. Als het eindpunt streams ondersteunt, worden statische streams geopend door OpenStreams-routine aan te roepen. De implementatie van OpenStreams wordt weergegeven in Statische streams openen en sluiten in een USB-bulkeindpunt.
Om te bepalen of een bepaald bulkeindpunt statische streams ondersteunt, onderzoekt het clientstuurprogramma de eindpuntdescriptor. Deze code wordt geïmplementeerd in een helperroutine met de naam RetrieveStreamInfoFromEndpointDesc, weergegeven in het volgende codeblok.
NTSTATUS
FX3EnumeratePipes(
_In_ WDFDEVICE Device)
{
NTSTATUS status;
PDEVICE_CONTEXT pDeviceContext;
UCHAR i;
PPIPE_CONTEXT pipeContext;
WDFUSBPIPE pipe;
WDF_USB_PIPE_INFORMATION pipeInfo;
PAGED_CODE();
pDeviceContext = GetDeviceContext(Device);
// Get the number of pipes in the current alternate setting.
pDeviceContext->NumberConfiguredPipes = WdfUsbInterfaceGetNumConfiguredPipes(
pDeviceContext->UsbInterface);
if (pDeviceContext->NumberConfiguredPipes == 0)
{
status = USBD_STATUS_BAD_NUMBER_OF_ENDPOINTS;
goto Exit;
}
else
{
status = STATUS_SUCCESS;
}
// Enumerate the pipes and get pipe information for each pipe.
for (i = 0; i < pDeviceContext->NumberConfiguredPipes; i++)
{
WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
pipe = WdfUsbInterfaceGetConfiguredPipe(
pDeviceContext->UsbInterface,
i,
&pipeInfo);
if (pipe == NULL)
{
continue;
}
pipeContext = GetPipeContext (pipe);
// If the pipe is a bulk endpoint that supports streams,
// If the host controller supports streams, open streams.
// Use the endpoint as an IN bulk endpoint.
// Store the maximum packet size.
if ((WdfUsbPipeTypeBulk == pipeInfo.PipeType) &&
WdfUsbTargetPipeIsInEndpoint (pipe))
{
// Check if this is a streams IN endpoint. If it is,
// Get the maximum number of streams and store
// the value in the pipe context.
RetrieveStreamInfoFromEndpointDesc (
Device,
pipe);
if ((pipeContext->IsStreamsCapable) &&
(pipeContext->MaxStreamsSupported > 0))
{
status = OpenStreams (
Device,
pipe);
if (status != STATUS_SUCCESS)
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not open streams.");
pDeviceContext->StreamInPipe = NULL;
}
else
{
pDeviceContext->StreamInPipe = pipe;
pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
}
}
else
{
pDeviceContext->BulkReadPipe = pipe;
pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
}
continue;
}
if ((WdfUsbPipeTypeBulk == pipeInfo.PipeType) &&
WdfUsbTargetPipeIsOutEndpoint (pipe))
{
// Check if this is a streams IN endpoint. If it is,
// Get the maximum number of streams and store
// the value in the pipe context.
RetrieveStreamInfoFromEndpointDesc (
Device,
pipe);
if ((pipeContext->IsStreamsCapable) &&
(pipeContext->MaxStreamsSupported > 0))
{
status = OpenStreams (
Device,
pipe);
if (status != STATUS_SUCCESS)
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not open streams.");
pDeviceContext->StreamOutPipe = NULL;
}
else
{
pDeviceContext->StreamOutPipe = pipe;
pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
}
}
else
{
pDeviceContext->BulkWritePipe = pipe;
pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
}
continue;
}
if ((WdfUsbPipeTypeInterrupt == pipeInfo.PipeType) &&
WdfUsbTargetPipeIsInEndpoint (pipe))
{
pDeviceContext->InterruptPipe = pipe;
pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
continue;
}
}
Exit:
return status;
}
In het volgende codevoorbeeld ziet u een helperroutine met de naam RetrieveStreamInfoFromEndpointDesc, die het clientstuurprogramma aanroept tijdens het inventariseren van pipes.
In het volgende codevoorbeeld roept het clientstuurprogramma de voorgaande helperroutine RetrieveStreamInfoFromEndpointDesc aan, terwijl pijpen worden opgesomd. De routine onderzoekt eerst de configuratiedescriptor en parseert deze om eindpuntdescriptoren op te halen. Als de eindpuntdescriptor voor de pijp een USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE descriptor bevat, haalt het stuurprogramma het maximum aantal streams op dat door het eindpunt wordt ondersteund.
/*++
Routine Description:
This routine parses the configuration descriptor and finds the endpoint
with which the specified pipe is associated.
It then retrieves the maximum number of streams supported by the endpoint.
It stores maximum number of streams in the pipe context.
Arguments:
Device - WDFUSBDEVICE handle to the target device object.
The driver obtained that handle in a previous call to
WdfUsbTargetDeviceCreateWithParameters.
Pipe - WDFUSBPIPE handle to the target pipe object.
Return Value:
NTSTATUS
++*/
VOID RetrieveStreamInfoFromEndpointDesc (
WDFDEVICE Device,
WDFUSBPIPE Pipe)
{
PDEVICE_CONTEXT deviceContext = NULL;
PUSB_CONFIGURATION_DESCRIPTOR configDescriptor = NULL;
WDF_USB_PIPE_INFORMATION pipeInfo;
PUSB_COMMON_DESCRIPTOR pCommonDescriptorHeader = NULL;
PUSB_INTERFACE_DESCRIPTOR pInterfaceDescriptor = NULL;
PUSB_ENDPOINT_DESCRIPTOR pEndpointDescriptor = NULL;
PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR pEndpointCompanionDescriptor = NULL;
ULONG maxStreams;
ULONG index;
BOOLEAN found = FALSE;
UCHAR interfaceNumber = 0;
UCHAR alternateSetting = 1;
PPIPE_CONTEXT pipeContext = NULL;
NTSTATUS status;
PAGED_CODE();
deviceContext = GetDeviceContext (Device);
pipeContext = GetPipeContext (Pipe);
// Get the configuration descriptor of the currently selected configuration
status = FX3RetrieveConfigurationDescriptor (
deviceContext->UsbDevice,
&deviceContext->ConfigurationNumber,
&configDescriptor);
if (!NT_SUCCESS (status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not retrieve the configuration descriptor.");
status = USBD_STATUS_INAVLID_CONFIGURATION_DESCRIPTOR;
goto Exit;
}
if (deviceContext->ConfigurationNumber == 1)
{
alternateSetting = 1;
}
else
{
alternateSetting = 0;
}
// Get the Endpoint Address of the pipe
WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
WdfUsbTargetPipeGetInformation (Pipe, &pipeInfo);
// Parse the ConfigurationDescriptor (including all Interface and
// Endpoint Descriptors) and locate a Interface Descriptor which
// matches the InterfaceNumber, AlternateSetting, InterfaceClass,
// InterfaceSubClass, and InterfaceProtocol parameters.
pInterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
configDescriptor,
configDescriptor,
interfaceNumber, //Interface number is 0.
alternateSetting, // Alternate Setting is 1
-1, // InterfaceClass, ignore
-1, // InterfaceSubClass, ignore
-1 // InterfaceProtocol, ignore
);
if (pInterfaceDescriptor == NULL )
{
// USBD_ParseConfigurationDescriptorEx failed to retrieve Interface Descriptor.
goto Exit;
}
pCommonDescriptorHeader = (PUSB_COMMON_DESCRIPTOR) pInterfaceDescriptor;
for(index = 0; index < pInterfaceDescriptor->bNumEndpoints; index++)
{
pCommonDescriptorHeader = USBD_ParseDescriptors(
configDescriptor,
configDescriptor->wTotalLength,
pCommonDescriptorHeader,
USB_ENDPOINT_DESCRIPTOR_TYPE);
if (pCommonDescriptorHeader == NULL)
{
// USBD_ParseDescriptors failed to retrieve Endpoint Descriptor unexpectedly.
goto Exit;
}
pEndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) pCommonDescriptorHeader;
// Search an Endpoint Descriptor that matches the EndpointAddress
if (pEndpointDescriptor->bEndpointAddress == pipeInfo.EndpointAddress)
{
found = TRUE;
break;
}
// Skip the current Endpoint Descriptor and search for the next.
pCommonDescriptorHeader = (PUSB_COMMON_DESCRIPTOR)(((PUCHAR)pCommonDescriptorHeader)
+ pCommonDescriptorHeader->bLength);
}
if (found)
{
// Locate the SuperSpeed Endpoint Companion Descriptor
// associated with the endpoint descriptor
pCommonDescriptorHeader = USBD_ParseDescriptors (
configDescriptor,
configDescriptor->wTotalLength,
pEndpointDescriptor,
USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE);
if (pCommonDescriptorHeader != NULL)
{
pEndpointCompanionDescriptor =
(PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR) pCommonDescriptorHeader;
maxStreams = pEndpointCompanionDescriptor->bmAttributes.Bulk.MaxStreams;
if (maxStreams == 0)
{
pipeContext->MaxStreamsSupported = 0;
pipeContext->IsStreamsCapable = FALSE;
}
else
{
pipeContext->IsStreamsCapable = TRUE;
pipeContext->MaxStreamsSupported = 1 << maxStreams;
}
}
else
{
KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"USBD_ParseDescriptors failed to retrieve SuperSpeed Endpoint Companion Descriptor unexpectedly.\n" ));
}
}
else
{
pipeContext->MaxStreamsSupported = 0;
pipeContext->IsStreamsCapable = FALSE;
}
Exit:
if (configDescriptor)
{
ExFreePoolWithTag (configDescriptor, USBCLIENT_TAG);
}
return;
}
Het verkrijgen van pijpgrepen in een UMDF-clientstuurprogramma
Een UMDF-clientstuurprogramma maakt gebruik van COM-infrastructuur en implementeert COM-callbackklassen die zijn gekoppeld aan frameworkapparaatobjecten. Net als bij een KMDF-stuurprogramma kan een UMDF-clientstuurprogramma alleen sluisgegevens ophalen nadat het apparaat is geconfigureerd. Als u pipe-informatie wilt ophalen, moet het clientstuurprogramma een aanwijzer verkrijgen naar de IWDFUsbTargetPipe-interface van het framework interface-object dat de actieve instelling bevat. Met behulp van de interfaceaanwijzer kan het stuurprogramma de pijpen in die instelling inventariseren om IWDFUsbTargetPipe-interfaceaanwijzers te verkrijgen die worden weergegeven door de doelpijpobjecten van het framework.
Voordat het stuurprogramma begint met het inventariseren van de pijpen, moet het stuurprogramma op de hoogte zijn van de apparaatconfiguratie en de ondersteunde eindpunten. Op basis van deze informatie kan het stuurprogramma pijpobjecten opslaan als variabelen voor klasseleden.
In het volgende codevoorbeeld wordt de USB UMDF-sjabloon uitgebreid die wordt geleverd met Visual Studio Professional 2012. Zie "IPnpCallbackHardware implementation and USB-specific tasks" (IPnpCallbackHardware-implementatie en USB-specifieke taken) in Understanding the USB client driver code structure (UMDF).
Breid de CDevice-klassedeclaratie uit zoals hier wordt weergegeven. In deze voorbeeldcode wordt ervan uitgegaan dat het apparaat het OSR FX2-bord is. Zie USB-apparaatindeling voor informatie over de indeling van de descriptor.
class CMyDevice :
public CComObjectRootEx<CComMultiThreadModel>,
public IPnpCallbackHardware
{
public:
DECLARE_NOT_AGGREGATABLE(CMyDevice)
BEGIN_COM_MAP(CMyDevice)
COM_INTERFACE_ENTRY(IPnpCallbackHardware)
END_COM_MAP()
CMyDevice() :
m_FxDevice(NULL),
m_IoQueue(NULL),
m_FxUsbDevice(NULL)
{
}
~CMyDevice()
{
}
private:
IWDFDevice * m_FxDevice;
CMyIoQueue * m_IoQueue;
IWDFUsbTargetDevice * m_FxUsbDevice;
IWDFUsbInterface * m_pIUsbInterface; //Pointer to the target interface object.
IWDFUsbTargetPipe * m_pIUsbInputPipe; // Pointer to the target pipe object for the bulk IN endpoint.
IWDFUsbTargetPipe * m_pIUsbOutputPipe; // Pointer to the target pipe object for the bulk OUT endpoint.
IWDFUsbTargetPipe * m_pIUsbInterruptPipe; // Pointer to the target pipe object for the interrupt endpoint.
private:
HRESULT
Initialize(
__in IWDFDriver *FxDriver,
__in IWDFDeviceInitialize *FxDeviceInit
);
public:
static
HRESULT
CreateInstanceAndInitialize(
__in IWDFDriver *FxDriver,
__in IWDFDeviceInitialize *FxDeviceInit,
__out CMyDevice **Device
);
HRESULT
Configure(
VOID
);
HRESULT // Declare a helper function to enumerate pipes.
ConfigureUsbPipes(
);
public:
// IPnpCallbackHardware methods
virtual
HRESULT
STDMETHODCALLTYPE
OnPrepareHardware(
__in IWDFDevice *FxDevice
);
virtual
HRESULT
STDMETHODCALLTYPE
OnReleaseHardware(
__in IWDFDevice *FxDevice
);
};
Implementeer in de CDevice-klassedefinitie een helpermethode met de naam CreateUsbIoTargets. Deze methode wordt aangeroepen vanuit de IPnpCallbackHardware::OnPrepareHardware-implementatie nadat het stuurprogramma een aanwijzer heeft verkregen naar het doelapparaatobject.
HRESULT CMyDevice::CreateUsbIoTargets()
{
HRESULT hr;
UCHAR NumEndPoints = 0;
IWDFUsbInterface * pIUsbInterface = NULL;
IWDFUsbTargetPipe * pIUsbPipe = NULL;
if (SUCCEEDED(hr))
{
UCHAR NumInterfaces = pIUsbTargetDevice->GetNumInterfaces();
WUDF_TEST_DRIVER_ASSERT(1 == NumInterfaces);
hr = pIUsbTargetDevice->RetrieveUsbInterface(0, &pIUsbInterface);
if (FAILED(hr))
{
TraceEvents(TRACE_LEVEL_ERROR,
TEST_TRACE_DEVICE,
"%!FUNC! Unable to retrieve USB interface from USB Device I/O Target %!HRESULT!",
hr
);
}
else
{
m_pIUsbInterface = pIUsbInterface;
DriverSafeRelease (pIUsbInterface); //release creation reference
}
}
if (SUCCEEDED(hr))
{
NumEndPoints = pIUsbInterface->GetNumEndPoints();
if (NumEndPoints != NUM_OSRUSB_ENDPOINTS)
{
hr = E_UNEXPECTED;
TraceEvents(TRACE_LEVEL_ERROR,
TEST_TRACE_DEVICE,
"%!FUNC! Has %d endpoints, expected %d, returning %!HRESULT! ",
NumEndPoints,
NUM_OSRUSB_ENDPOINTS,
hr
);
}
}
if (SUCCEEDED(hr))
{
for (UCHAR PipeIndex = 0; PipeIndex < NumEndPoints; PipeIndex++)
{
hr = pIUsbInterface->RetrieveUsbPipeObject(PipeIndex,
&pIUsbPipe);
if (FAILED(hr))
{
TraceEvents(TRACE_LEVEL_ERROR,
TEST_TRACE_DEVICE,
"%!FUNC! Unable to retrieve USB Pipe for PipeIndex %d, %!HRESULT!",
PipeIndex,
hr
);
}
else
{
if ( pIUsbPipe->IsInEndPoint() )
{
if ( UsbdPipeTypeInterrupt == pIUsbPipe->GetType() )
{
m_pIUsbInterruptPipe = pIUsbPipe;
}
else if ( UsbdPipeTypeBulk == pIUsbPipe->GetType() )
{
m_pIUsbInputPipe = pIUsbPipe;
}
else
{
pIUsbPipe->DeleteWdfObject();
}
}
else if ( pIUsbPipe->IsOutEndPoint() && (UsbdPipeTypeBulk == pIUsbPipe->GetType()) )
{
m_pIUsbOutputPipe = pIUsbPipe;
}
else
{
pIUsbPipe->DeleteWdfObject();
}
DriverSafeRelease(pIUsbPipe); //release creation reference
}
}
if (NULL == m_pIUsbInputPipe || NULL == m_pIUsbOutputPipe)
{
hr = E_UNEXPECTED;
TraceEvents(TRACE_LEVEL_ERROR,
TEST_TRACE_DEVICE,
"%!FUNC! Input or output pipe not found, returning %!HRESULT!",
hr
);
}
}
return hr;
}
In UMDF gebruikt het clientstuurprogramma een pipe-index om aanvragen voor gegevensoverdracht te verzenden. Een pijpindex is een getal dat door de USB-stuurprogrammastack wordt toegewezen wanneer er pijpen voor de eindpunten in een instelling worden geopend. Roep de methode**IWDFUsbTargetPipe::GetInformation** aan om de pijpindex te verkrijgen. De methode vult een WINUSB_PIPE_INFORMATION structuur. De waarde PipeId geeft de pijpindex aan.
Een manier om lees- en schrijfbewerkingen uit te voeren op de doelpijp is het aanroepen van IWDFUsbInterface::GetWinUsbHandle om een WinUSB-ingang te verkrijgen en vervolgens WinUSB-functies aan te roepen. Het stuurprogramma kan bijvoorbeeld de functie WinUsb_ReadPipe of WinUsb_WritePipe aanroepen. In deze functie-aanroepen moet het stuurprogramma de pipe-index opgeven. Zie #B0 How to Access a USB Device by Using WinUSB Functions #A1 voor meer informatie.
Pijpgrepen voor WDM-clientstuurprogramma's
Nadat een configuratie is geselecteerd, stelt de USB-stuurprogrammastack een kanaal in naar elk van de eindpunten van het apparaat. De USB-stuurprogrammastack retourneert een matrix van USBD_PIPE_INFORMATION structuren. Het aantal elementen in de matrix is afhankelijk van het aantal eindpunten dat is gedefinieerd voor de actieve alternatieve instelling van een interface in de geselecteerde configuratie. Elk element bevat informatie over de pijp die is gemaakt voor een bepaald eindpunt. Voor meer informatie over het verkrijgen van pijpgrepen, zie Een configuratie voor een USB-apparaat selecteren.
Als u een I/O-overdrachtsaanvraag wilt maken, moet het clientstuurprogramma een ingang hebben voor de pijp die aan dat eindpunt is gekoppeld. Het clientstuurprogramma kan de pijpgreep verkrijgen van het PipeHandle-lid van USBD_PIPE_INFORMATION in de matrix.
Naast de pijpgreep vereist het clientstuurprogramma ook het pijptype. Het clientstuurprogramma kan het pijptype bepalen door het PipeType-lid te onderzoeken.
Op basis van het eindpunttype ondersteunt de USB-stuurprogrammastack verschillende soorten pijpen. Het clientstuurprogramma kan het pijptype bepalen door het PipeType-lid van USBD_PIPE_INFORMATION te onderzoeken. De verschillende pijptypen vereisen verschillende soorten USB-aanvraagblokken (URL's) om I/O-transacties uit te voeren.
Het clientstuurprogramma verzendt vervolgens de URB naar de USB-stuurprogrammastack. De USB-stuurprogrammastack verwerkt de aanvraag en verzendt de opgegeven gegevens naar de aangevraagde doelpijp.
De URB bevat informatie over de aanvraag, zoals de doelpijpgreep, de overdrachtsbuffer en de lengte ervan. Elke structuur binnen de URB-union deelt bepaalde leden: TransferFlags, TransferBuffer, TransferBufferLength en TransferBufferMDL. Er zijn typespecifieke vlaggen in het TransferFlags-lid die overeenkomen met elk URB-type. Voor alle URL's voor gegevensoverdracht geeft de USBD_TRANSFER_DIRECTION_IN vlag in TransferFlags de richting van de overdracht aan. Clientstuurprogramma's stellen de USBD_TRANSFER_DIRECTION_IN vlag in om gegevens van het apparaat te lezen. Stuurprogramma's wissen deze vlag om gegevens naar het apparaat te verzenden. Gegevens kunnen worden gelezen van of geschreven naar een buffer die zich in het geheugen of een MDL bevinden. In beide gevallen geeft het stuurprogramma de grootte van de buffer aan in het Lid TransferBufferLength . Het stuurprogramma biedt een residente buffer in het TransferBuffer-lid en een MDL in het TransferBufferMDL-lid . Welke de bestuurder ook verstrekt, de andere parameter moet NULL zijn.