Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Un dispositivo USB expone sus capacidades en forma de una serie de interfaces denominadas configuración USB. Cada interfaz consta de una o varias configuraciones alternativas y cada configuración alternativa se compone de un conjunto de puntos de conexión. En este tema se describen los distintos descriptores asociados a una configuración USB.
Una configuración USB se describe en un descriptor de configuración (consulte USB_CONFIGURATION_DESCRIPTOR estructura). Un descriptor de configuración contiene información sobre la configuración y sus interfaces, opciones alternativas y sus puntos de conexión. Cada descriptor de interfaz o configuración alternativa se describe en una estructura de USB_INTERFACE_DESCRIPTOR . En una configuración, cada descriptor de interfaz es seguido en memoria por todos los descriptores de punto de conexión para la interfaz y su configuración alternativa. Cada descriptor de punto de conexión se almacena en una estructura USB_ENDPOINT_DESCRIPTOR .
Por ejemplo, consideren un dispositivo webcam USB descrito en Diseño de dispositivo USB. El dispositivo admite una configuración con dos interfaces y la primera interfaz (índice 0) admite dos opciones alternativas.
En el ejemplo siguiente se muestra el descriptor de configuración para el dispositivo webcam USB:
Configuration Descriptor:
wTotalLength: 0x02CA
bNumInterfaces: 0x02
bConfigurationValue: 0x01
iConfiguration: 0x00
bmAttributes: 0x80 (Bus Powered )
MaxPower: 0xFA (500 mA)
El campo bConfigurationValue indica el número de la configuración definida en el firmware del dispositivo. El controlador cliente usa ese valor numérico para seleccionar una configuración activa. Para obtener más información sobre la configuración del dispositivo USB, consulte Cómo seleccionar una configuración para un dispositivo USB. Una configuración USB también indica ciertas características de potencia. BmAttributes contiene una máscara de bits que indica si la configuración admite la característica de reactivación remota y si el dispositivo está alimentado por bus o autopropulsado. El campo MaxPower especifica la potencia máxima (en unidades de miliamp) que el dispositivo puede extraer del host, cuando el dispositivo está alimentado por bus. El descriptor de configuración también indica el número total de interfaces (bNumInterfaces) que admite el dispositivo.
En el ejemplo siguiente se muestra el descriptor de interfaz para la configuración alternativa 0 de interfaz 0 para el dispositivo webcam:
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"
En el ejemplo anterior, tenga en cuenta los valores de campo bInterfaceNumber y bAlternateSetting . Esos campos contienen valores de índice que usa un controlador cliente para activar la interfaz y una de sus opciones alternativas. Para la activación, el controlador envía una solicitud de selección de interfaz a la pila del controlador USB. A continuación, la pila de controladores compila una solicitud de control estándar (SET INTERFACE) y la envía al dispositivo. Anote el campo bInterfaceClass . El descriptor de interfaz o el descriptor de cualquiera de sus valores alternativos especifican un código de clase, una subclase y un protocolo. El valor de 0x0E indica que la interfaz es para la clase de dispositivo de vídeo. Además, observe el campo iInterface . Ese valor indica que hay dos descriptores de cadena anexados al descriptor de interfaz. Los descriptores de cadena contienen descripciones Unicode que se usan durante la enumeración del dispositivo para identificar la funcionalidad. Para obtener más información sobre los descriptores de cadena, vea Descriptores de cadena USB.
Cada punto de conexión, en una interfaz, describe una única secuencia de entrada o salida para el dispositivo. Un dispositivo que admite secuencias para diferentes tipos de funciones tiene varias interfaces. Un dispositivo que admite varios flujos que pertenecen a una función puede admitir varios puntos de conexión en una sola interfaz.
Todos los tipos de puntos de conexión (excepto el punto de conexión predeterminado) deben proporcionar descriptores de punto de conexión para que el host pueda obtener información sobre el punto de conexión. Un descriptor de punto de conexión incluye información, como su dirección, tipo, sentido y la cantidad de datos que puede manejar el punto de conexión. Las transferencias de datos al punto de conexión se basan en esa información.
En el ejemplo siguiente se muestra un descriptor de punto de conexión para el dispositivo webcam:
Endpoint Descriptor:
bEndpointAddress: 0x82 IN
bmAttributes: 0x01
wMaxPacketSize: 0x0080 (128)
bInterval: 0x01
El campo bEndpointAddress especifica la dirección del punto de conexión único que contiene el número de punto de conexión (Bits 3..0) y la dirección del punto de conexión (Bit 7). Al leer esos valores en el ejemplo anterior, podemos determinar que el descriptor describe un punto de conexión IN cuyo número de punto de conexión es 2. El atributo bmAttributes indica que el tipo de punto de conexión es isócrono. wMaxPacketSizefield indica el número máximo de bytes que el punto de conexión puede enviar o recibir en una sola transacción. Los bits 12..11 indican el número total de transacciones que se pueden enviar por microframe. bInterval indica la frecuencia con la que el punto de conexión puede enviar o recibir datos.
Obtención del descriptor de configuración
El descriptor de configuración se obtiene del dispositivo a través de una solicitud de dispositivo estándar (GET_DESCRIPTOR), que se envía como transferencia de control por la pila de controladores USB. Un controlador de cliente USB puede iniciar la solicitud de una de las maneras siguientes:
Si el dispositivo solo admite una configuración, la manera más sencilla es llamar al método WdfUsbTargetDeviceRetrieveConfigDescriptor proporcionado por el marco.
Para un dispositivo que admita varias configuraciones, si el controlador cliente quiere obtener el descriptor de la configuración que no sea la primera, el controlador debe enviar un URB. Para enviar un URB, el controlador debe asignar, dar formato y, a continuación, enviar el URB a la pila de controladores USB.
Para asignar el URB, el controlador cliente debe llamar al método WdfUsbTargetDeviceCreateUrb . El método recibe un puntero a un URB asignado por la pila de controladores USB.
Para dar formato al URB, el controlador cliente puede usar la macro UsbBuildGetDescriptorRequest . La macro establece toda la información necesaria en el URB, como el número de configuración definido por el dispositivo para el cual se va a recuperar el descriptor. La función URB se establece en URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE (vea _URB_CONTROL_DESCRIPTOR_REQUEST) y el tipo de descriptor se establece en USB_CONFIGURATION_DESCRIPTOR_TYPE. Mediante el uso de la información contenida en el URB, la pila del controlador USB crea una solicitud de control estándar y la envía al dispositivo.
Para enviar el URB, el controlador cliente debe usar un objeto de solicitud WDF. Para enviar el objeto de solicitud a la pila de controladores USB de forma asincrónica, el controlador debe llamar al método **WdfRequestSend**. Para enviarlo de forma sincrónica, llame al método WdfUsbTargetDeviceSendUrbSynchronously .
Controladores WDM: Un controlador cliente de Windows Driver Model (WDM) solo puede obtener el descriptor de configuración mediante el envío de un URB. Para asignar el URB, el controlador debe llamar a la rutina USBD_UrbAllocate . Para dar formato al URB, el controlador debe llamar a la macro UsbBuildGetDescriptorRequest . Para enviar el URB, el controlador debe asociar el URB a un IRP y enviar el IRP a la pila del controlador USB. Para obtener más información, vea Cómo enviar un URB.
Dentro de una configuración USB, el número de interfaces y sus valores alternativos son variables. Por lo tanto, es difícil predecir el tamaño del búfer necesario para contener el descriptor de configuración. El controlador cliente debe recopilar toda esa información en dos pasos. En primer lugar, determine qué búfer de tamaño se necesita para contener todo el descriptor de configuración y, a continuación, emita una solicitud para recuperar todo el descriptor. Un controlador cliente puede obtener el tamaño de una de las maneras siguientes:
Para obtener el descriptor de configuración llamando a WdfUsbTargetDeviceRetrieveConfigDescriptor, realice estos pasos:
- Obtenga el tamaño del búfer necesario para contener toda la información de configuración llamando a WdfUsbTargetDeviceRetrieveConfigDescriptor. El controlador debe pasar NULL en el búfer y una variable para contener el tamaño del búfer.
- Asigne un búfer mayor en función del tamaño recibido a través de la llamada WdfUsbTargetDeviceRetrieveConfigDescriptor anterior.
- Llame de nuevo a WdfUsbTargetDeviceRetrieveConfigDescriptor y especifique un puntero al nuevo búfer asignado en el paso 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;
}
Para obtener el descriptor de configuración mediante el envío de un URB, realice estos pasos:
- Asigne un URB llamando al método WdfUsbTargetDeviceCreateUrb .
- Dé formato al URB llamando a la macro UsbBuildGetDescriptorRequest . El búfer de transferencia del URB debe apuntar a un búfer lo suficientemente grande como para contener una estructura de USB_CONFIGURATION_DESCRIPTOR .
- Envíe el URB como un objeto de solicitud WDF llamando a WdfRequestSend o WdfUsbTargetDeviceSendUrbSynchronously.
- Una vez completada la solicitud, compruebe el miembro wTotalLength de USB_CONFIGURATION_DESCRIPTOR. Ese valor indica el tamaño del búfer necesario para contener un descriptor de configuración completo.
- Asignar un búfer más grande según el tamaño obtenido en wTotalLength.
- Realice la misma solicitud con el búfer más grande.
En el código de ejemplo siguiente se muestra la llamada UsbBuildGetDescriptorRequest para obtener información de configuración para la configuración de i-th:
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;
}
Cuando el dispositivo devuelve el descriptor de configuración, el búfer de solicitudes se rellena con descriptores de interfaz para todas las opciones alternativas y descriptores de punto de conexión para todos los puntos de conexión dentro de una configuración alternativa determinada. En el caso del dispositivo descrito en Diseño de dispositivo USB, en el diagrama siguiente se muestra cómo se proporciona información de configuración en memoria.
El miembro bInterfaceNumber de base cero de USB_INTERFACE_DESCRIPTOR distingue interfaces dentro de una configuración. Para una interfaz determinada, el miembro bAlternateSetting basado en cero distingue entre la configuración alternativa de la interfaz. El dispositivo devuelve descriptores de interfaz en orden de valores bInterfaceNumber y, a continuación, en orden de valores bAlternateSetting .
Para buscar un descriptor de interfaz determinado dentro de la configuración, el controlador cliente puede llamar a USBD_ParseConfigurationDescriptorEx. En la llamada, el controlador cliente proporciona una posición inicial dentro de la configuración. Opcionalmente, el controlador puede especificar un número de interfaz, una configuración alternativa, una clase, una subclase o un protocolo. La rutina devuelve un puntero al siguiente descriptor de interfaz coincidente.
Para examinar un descriptor de configuración para un punto de conexión o descriptor de cadena, use la rutina USBD_ParseDescriptors . El autor de la llamada proporciona una posición inicial dentro de la configuración y un tipo de descriptor, como USB_STRING_DESCRIPTOR_TYPE o USB_ENDPOINT_DESCRIPTOR_TYPE. La rutina devuelve un puntero al siguiente descriptor coincidente.