mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-09-02 19:26:32 +00:00
[SDK][WDFLDR] Add kmdf loader driver [SDK][WDF] Add kmdf drver init static library [SDK][WDF] Modify kmdf driver for working with wdfldr driver [SDK][CDROM] Cdrom driver dynamically linking with kmdf [SDK][WDF] Add kmdfdriver module type [SDK][WDF][USBDEX][NTOSKRNL_VISTA] Fully enable KMDF [KMDF][WDFLDR][WDF01000] Fix Windows 10 Compatibility WDFLDR and WDF01000 [WDF01000] NO_KERNEL_LIST_ENTRY_CHECKS for wdf01000 3rd party code This PR is an accumulation of three peoples work, with the goal of the trying to get WDF to work like it should. This has been tested in combination with some extra NT6+ ntoskrnl against multiple drivers. --------- Co-authored-by: Max Korostil <[email protected]> Co-authored-by: Victor Perevertkin <[email protected]> Co-authored-by: Adam Słaboń <[email protected]> Co-authored-by: Hermès BÉLUSCA - MAÏTO <[email protected]>
1172 lines
31 KiB
C++
1172 lines
31 KiB
C++
/*++
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
FxUsbInterface.cpp
|
|
|
|
Abstract:
|
|
|
|
Author:
|
|
|
|
Environment:
|
|
|
|
Both kernel and user mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "fxusbpch.hpp"
|
|
|
|
extern "C" {
|
|
#if defined(EVENT_TRACING)
|
|
#include "FxUsbInterface.tmh"
|
|
#endif
|
|
}
|
|
|
|
FxUsbInterface::FxUsbInterface(
|
|
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
|
|
__in FxUsbDevice* UsbDevice,
|
|
__in PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor
|
|
) :
|
|
FxNonPagedObject(FX_TYPE_USB_INTERFACE ,sizeof(FxUsbInterface), FxDriverGlobals),
|
|
m_UsbDevice(UsbDevice)
|
|
{
|
|
m_UsbDevice->ADDREF(this);
|
|
|
|
m_InterfaceNumber = InterfaceDescriptor->bInterfaceNumber;
|
|
m_Protocol = InterfaceDescriptor->bInterfaceProtocol;
|
|
m_Class = InterfaceDescriptor->bInterfaceClass;
|
|
m_SubClass = InterfaceDescriptor->bInterfaceSubClass;
|
|
|
|
m_CurAlternateSetting = 0;
|
|
m_NumSettings = 0;
|
|
m_NumberOfConfiguredPipes = 0;
|
|
m_ConfiguredPipes = NULL;
|
|
m_Settings = NULL;
|
|
|
|
MarkNoDeleteDDI(ObjectDoNotLock);
|
|
}
|
|
|
|
FxUsbInterface::~FxUsbInterface()
|
|
{
|
|
ULONG i;
|
|
|
|
m_UsbDevice->RemoveDeletedInterface(this);
|
|
|
|
for (i = 0; i < m_NumberOfConfiguredPipes; i++) {
|
|
ASSERT(m_ConfiguredPipes[i] == NULL);
|
|
}
|
|
|
|
if (m_ConfiguredPipes != NULL) {
|
|
FxPoolFree(m_ConfiguredPipes);
|
|
m_ConfiguredPipes = NULL;
|
|
}
|
|
|
|
m_NumberOfConfiguredPipes = 0;
|
|
|
|
if (m_Settings != NULL) {
|
|
FxPoolFree(m_Settings);
|
|
m_Settings = NULL;
|
|
}
|
|
|
|
//
|
|
// Release the reference taken in the constructor
|
|
//
|
|
m_UsbDevice->RELEASE(this);
|
|
}
|
|
|
|
VOID
|
|
FxUsbInterface::CleanUpAndDelete(
|
|
__in BOOLEAN Failure
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Deletes the pipes contained on the interface.
|
|
|
|
Arguments:
|
|
Failure - if TRUE, the pipes were never exposed to the client, so any attributes
|
|
are cleared before deletion (via DeleteFromFailedCreate)
|
|
|
|
Assumes:
|
|
FxUsbDevice::InterfaceIterationLock is held by the caller
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
FxUsbPipe** pPipes;
|
|
ULONG numPipes;
|
|
KIRQL irql;
|
|
ULONG i;
|
|
|
|
//
|
|
// Capture the values, clear them out of the object and then clean them up
|
|
// outside of the lock.
|
|
//
|
|
m_UsbDevice->Lock(&irql);
|
|
|
|
pPipes = m_ConfiguredPipes;
|
|
numPipes = m_NumberOfConfiguredPipes;
|
|
|
|
m_ConfiguredPipes = NULL;
|
|
m_NumberOfConfiguredPipes = 0;
|
|
|
|
m_UsbDevice->Unlock(irql);
|
|
|
|
if (pPipes != NULL) {
|
|
for (i = 0; i < numPipes; i++) {
|
|
if (pPipes[i] != NULL) {
|
|
if (Failure) {
|
|
//
|
|
// FxIoTarget::Remove will be called in FxIoTarget::Dispose()
|
|
//
|
|
pPipes[i]->DeleteFromFailedCreate();
|
|
}
|
|
else {
|
|
pPipes[i]->DeleteObject();
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// No more pointers to delete, break out of the loop
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
FxPoolFree(pPipes);
|
|
pPipes = NULL;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FxUsbInterface::RemoveDeletedPipe(
|
|
__in FxUsbPipe* Pipe
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
if (m_ConfiguredPipes == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < m_NumberOfConfiguredPipes; i++) {
|
|
if (m_ConfiguredPipes[i] == Pipe) {
|
|
m_ConfiguredPipes[i] = NULL;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FxUsbInterface::SetInfo(
|
|
__in PUSBD_INTERFACE_INFORMATION InterfaceInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Captures the alternate from the interface information into this structure
|
|
and then assigns info to all the created pipes.
|
|
|
|
Arguments:
|
|
InterfaceInfo - info to capture
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
UCHAR i;
|
|
|
|
ASSERT(m_InterfaceNumber == InterfaceInfo->InterfaceNumber);
|
|
|
|
m_CurAlternateSetting = InterfaceInfo->AlternateSetting;
|
|
|
|
for (i = 0; i < m_NumberOfConfiguredPipes ; i++) {
|
|
m_ConfiguredPipes[i]->InitPipe(&InterfaceInfo->Pipes[i],
|
|
InterfaceInfo->InterfaceNumber,
|
|
this);
|
|
}
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxUsbInterface::CreateSettings(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
1) Find the max number of settings for the interface
|
|
2) Allocate an array for the settings
|
|
3) Create a setting object for each setting and initialize
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PUSB_INTERFACE_DESCRIPTOR pDescriptor;
|
|
ULONG size;
|
|
UCHAR i;
|
|
|
|
//
|
|
// No need to validate the size of the interface descriptor since FxUsbDevice::CreateInterfaces
|
|
// has already done so
|
|
//
|
|
pDescriptor = (PUSB_INTERFACE_DESCRIPTOR) FxUsbFindDescriptorType(
|
|
m_UsbDevice->m_ConfigDescriptor,
|
|
m_UsbDevice->m_ConfigDescriptor->wTotalLength,
|
|
m_UsbDevice->m_ConfigDescriptor,
|
|
USB_INTERFACE_DESCRIPTOR_TYPE
|
|
);
|
|
|
|
//
|
|
// Calculate the number of settings for this interface
|
|
//
|
|
while (pDescriptor != NULL) {
|
|
if (m_InterfaceNumber == pDescriptor->bInterfaceNumber) {
|
|
m_NumSettings++;
|
|
}
|
|
pDescriptor = (PUSB_INTERFACE_DESCRIPTOR) FxUsbFindDescriptorType(
|
|
m_UsbDevice->m_ConfigDescriptor,
|
|
m_UsbDevice->m_ConfigDescriptor->wTotalLength,
|
|
WDF_PTR_ADD_OFFSET(pDescriptor, pDescriptor->bLength),
|
|
USB_INTERFACE_DESCRIPTOR_TYPE
|
|
);
|
|
}
|
|
size = sizeof(FxUsbInterfaceSetting) * m_NumSettings;
|
|
m_Settings = (FxUsbInterfaceSetting *) FxPoolAllocate(
|
|
GetDriverGlobals(), NonPagedPool, size);
|
|
|
|
if (m_Settings == NULL) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
|
|
"Could not allocate memory for %d settings for bInterfaceNumber %d "
|
|
"(Protocol %d, Class %d, SubClass %d), %!STATUS!",
|
|
m_NumSettings, m_InterfaceNumber, m_Protocol, m_Class, m_SubClass,
|
|
STATUS_INSUFFICIENT_RESOURCES);
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(m_Settings, size);
|
|
|
|
//
|
|
// Add all the settings for this interface
|
|
//
|
|
pDescriptor = (PUSB_INTERFACE_DESCRIPTOR) FxUsbFindDescriptorType(
|
|
m_UsbDevice->m_ConfigDescriptor,
|
|
m_UsbDevice->m_ConfigDescriptor->wTotalLength,
|
|
m_UsbDevice->m_ConfigDescriptor,
|
|
USB_INTERFACE_DESCRIPTOR_TYPE
|
|
);
|
|
|
|
while (pDescriptor != NULL) {
|
|
if (m_InterfaceNumber == pDescriptor->bInterfaceNumber) {
|
|
if (pDescriptor->bAlternateSetting < m_NumSettings) {
|
|
m_Settings[pDescriptor->bAlternateSetting].InterfaceDescriptor = pDescriptor;
|
|
}
|
|
else {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
|
|
"Interface Number %d does not have contiguous alternate settings,"
|
|
"expected %d settings, found alt setting %d, %!STATUS!",
|
|
pDescriptor->bInterfaceNumber, m_NumSettings,
|
|
pDescriptor->bAlternateSetting, STATUS_INVALID_DEVICE_REQUEST);
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
}
|
|
|
|
pDescriptor = (PUSB_INTERFACE_DESCRIPTOR) FxUsbFindDescriptorType(
|
|
m_UsbDevice->m_ConfigDescriptor,
|
|
m_UsbDevice->m_ConfigDescriptor->wTotalLength,
|
|
WDF_PTR_ADD_OFFSET(pDescriptor, pDescriptor->bLength),
|
|
USB_INTERFACE_DESCRIPTOR_TYPE
|
|
);
|
|
}
|
|
|
|
for (i = 0; i < m_NumSettings; i++) {
|
|
|
|
if (m_Settings[i].InterfaceDescriptor == NULL) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
|
|
"Interface Number %d does not have contiguous alternate settings,"
|
|
"expected consecutive %d settings, but alt setting %d missing "
|
|
"%!STATUS!", m_InterfaceNumber, m_NumSettings, i,
|
|
STATUS_INVALID_DEVICE_REQUEST);
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
//
|
|
// Only validate the endpoints if the interface reports it has some. We don't
|
|
// want to validate EP descriptors that may be after the interface descriptor
|
|
// that are never used because bNumEndpoints doesn't indicate they are present.
|
|
//
|
|
if (m_Settings[i].InterfaceDescriptor->bNumEndpoints > 0) {
|
|
PVOID pRelativeEnd;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Validate that each endpoint descriptor is the correct size for this alt setting.
|
|
// We will use the next inteface descriptor as the end, and if this is the last
|
|
// interface descriptor, use the end of the config descriptor as our end.
|
|
//
|
|
pRelativeEnd = FxUsbFindDescriptorType(
|
|
m_UsbDevice->m_ConfigDescriptor,
|
|
m_UsbDevice->m_ConfigDescriptor->wTotalLength,
|
|
m_Settings[i].InterfaceDescriptor,
|
|
USB_INTERFACE_DESCRIPTOR_TYPE
|
|
);
|
|
|
|
if (pRelativeEnd == NULL) {
|
|
//
|
|
// This is the last alt setting in the config descriptor, use the end of the
|
|
// config descriptor as our end
|
|
//
|
|
pRelativeEnd = WDF_PTR_ADD_OFFSET(m_UsbDevice->m_ConfigDescriptor,
|
|
m_UsbDevice->m_ConfigDescriptor->wTotalLength);
|
|
}
|
|
|
|
//
|
|
// Limit the number of endpoints validated to bNumEndpoints. In theory
|
|
// there could be EP descriptors after N EP descriptors that are never
|
|
// used, thus we don't want to risk valdiating them and failing them
|
|
// (ie an app compat concern, in a perfect world we would validate them)
|
|
//
|
|
status = FxUsbValidateDescriptorType(
|
|
GetDriverGlobals(),
|
|
m_UsbDevice->m_ConfigDescriptor,
|
|
m_Settings[i].InterfaceDescriptor,
|
|
pRelativeEnd,
|
|
USB_ENDPOINT_DESCRIPTOR_TYPE,
|
|
sizeof(USB_ENDPOINT_DESCRIPTOR),
|
|
FxUsbValidateDescriptorOpAtLeast,
|
|
m_Settings[i].InterfaceDescriptor->bNumEndpoints
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
|
|
"Interface Number %d does not have a valid endpoint descriptor,"
|
|
"%!STATUS!", m_InterfaceNumber, status);
|
|
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxUsbInterface::SelectSettingByIndex(
|
|
__in PWDF_OBJECT_ATTRIBUTES PipesAttributes,
|
|
__in UCHAR SettingIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Setlects a setting by alternate setting index.
|
|
|
|
Arguments:
|
|
PipesAttributes - optional attributes to apply to each of the created pipes
|
|
|
|
SettingIndex - alternate setting index to use
|
|
|
|
Return Value:
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
|
|
PURB urb;
|
|
#elif (FX_CORE_MODE == FX_CORE_USER_MODE)
|
|
UMURB urb;
|
|
PUSB_INTERFACE_DESCRIPTOR interfaceDesc;
|
|
#endif
|
|
FxUsbInterfaceSetting entry;
|
|
UCHAR numEP;
|
|
NTSTATUS status;
|
|
USHORT size;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// We should have at least 1 setting on the interface
|
|
//
|
|
ASSERT(m_NumSettings != 0);
|
|
|
|
//
|
|
// If m_NumberOfConfiguredPipes == 0 then it also tells us that
|
|
// the interface wasn't configured. So it can keep track of configuredness
|
|
// of the interface. Could there be a case when the selected setting has 0
|
|
// EP's. Due to the above case we use m_InterfaceConfigured.
|
|
//
|
|
if (IsInterfaceConfigured() && m_CurAlternateSetting == SettingIndex) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Check for an invalid alternate setting
|
|
//
|
|
if (SettingIndex >= m_NumSettings){
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
RtlCopyMemory(&entry, &m_Settings[SettingIndex], sizeof(entry));
|
|
|
|
//
|
|
// Create the configured pipes
|
|
//
|
|
numEP = entry.InterfaceDescriptor->bNumEndpoints;
|
|
|
|
size = GET_SELECT_INTERFACE_REQUEST_SIZE(numEP);
|
|
|
|
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
|
|
urb = (PURB) FxPoolAllocate(GetDriverGlobals(), NonPagedPool, size);
|
|
|
|
if (urb == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else {
|
|
FormatSelectSettingUrb(urb, numEP, SettingIndex);
|
|
|
|
status = SelectSetting(PipesAttributes, urb);
|
|
|
|
FxPoolFree(urb);
|
|
urb = NULL;
|
|
}
|
|
#elif (FX_CORE_MODE == FX_CORE_USER_MODE)
|
|
RtlZeroMemory(&urb, sizeof(UMURB));
|
|
|
|
urb.UmUrbSelectInterface.Hdr.InterfaceHandle = m_WinUsbHandle;
|
|
urb.UmUrbSelectInterface.Hdr.Function = UMURB_FUNCTION_SELECT_INTERFACE;
|
|
urb.UmUrbSelectInterface.Hdr.Length = sizeof(_UMURB_SELECT_INTERFACE);
|
|
|
|
urb.UmUrbSelectInterface.AlternateSetting = SettingIndex;
|
|
|
|
status = m_UsbDevice->SendSyncUmUrb(&urb, 2);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
RtlZeroMemory(&urb, sizeof(UMURB));
|
|
|
|
urb.UmUrbInterfaceInformation.Hdr.InterfaceHandle = m_WinUsbHandle;
|
|
urb.UmUrbInterfaceInformation.Hdr.Function = UMURB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE;
|
|
urb.UmUrbInterfaceInformation.Hdr.Length = sizeof(_UMURB_INTERFACE_INFORMATION);
|
|
|
|
urb.UmUrbInterfaceInformation.AlternateSetting = SettingIndex;
|
|
|
|
status = m_UsbDevice->SendSyncUmUrb(&urb, 2);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
interfaceDesc = &urb.UmUrbInterfaceInformation.UsbInterfaceDescriptor;
|
|
|
|
m_Settings[SettingIndex].InterfaceDescriptorAlloc = *interfaceDesc;
|
|
|
|
m_CurAlternateSetting = SettingIndex;
|
|
|
|
MakeAndConfigurePipes(PipesAttributes, interfaceDesc->bNumEndpoints);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxUsbInterface::SelectSettingByDescriptor(
|
|
__in PWDF_OBJECT_ATTRIBUTES PipesAttributes,
|
|
__in PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Selects an alternate setting by using a USB interface descriptor
|
|
|
|
Arguments:
|
|
PipesAttributes - optional attributes to apply to each of the created pipes
|
|
|
|
Return Value:
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PURB urb;
|
|
NTSTATUS status;
|
|
USHORT size;
|
|
|
|
if (IsInterfaceConfigured() &&
|
|
(m_CurAlternateSetting == InterfaceDescriptor->bAlternateSetting)) {
|
|
//
|
|
// Don't do anything
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (InterfaceDescriptor->bInterfaceNumber != m_InterfaceNumber) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
|
|
"WDFUSBINTERFACE %p has interface num %d, select setting by "
|
|
"descriptor specified interface num %d, %!STATUS!",
|
|
GetHandle(), m_InterfaceNumber,
|
|
InterfaceDescriptor->bInterfaceNumber, status
|
|
);
|
|
|
|
return status;
|
|
}
|
|
|
|
size = GET_SELECT_INTERFACE_REQUEST_SIZE(InterfaceDescriptor->bNumEndpoints);
|
|
|
|
urb = (PURB) FxPoolAllocate(GetDriverGlobals(), NonPagedPool, size);
|
|
|
|
if (urb == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else {
|
|
FormatSelectSettingUrb(
|
|
urb,
|
|
InterfaceDescriptor->bNumEndpoints,
|
|
InterfaceDescriptor->bAlternateSetting
|
|
);
|
|
|
|
status = SelectSetting(PipesAttributes, urb);
|
|
|
|
FxPoolFree(urb);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxUsbInterface::CheckAndSelectSettingByIndex(
|
|
__in UCHAR SettingIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Checks if the give SettingIndex is the current alternate setting
|
|
and if not, selects that setting
|
|
|
|
Arguments:
|
|
SettingIndex - Alternate setting
|
|
|
|
Return Value:
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
if (GetConfiguredSettingIndex() != SettingIndex) {
|
|
return SelectSettingByIndex(NULL,
|
|
SettingIndex);
|
|
}
|
|
else {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
FxUsbInterface::SelectSetting(
|
|
__in PWDF_OBJECT_ATTRIBUTES PipesAttributes,
|
|
__in PURB Urb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Worker function which all top level SelectSetting DDIs call into. This will
|
|
1) preallocate all required objects so that we can return failure before
|
|
we make any undoable changes to the device's state
|
|
2) send the select interface URB to the device
|
|
3) initialize all created objects
|
|
|
|
Arguments:
|
|
PipesAttributes - optional attributes to apply to all created pipes
|
|
|
|
Urb - Urb to send to the device
|
|
|
|
Return Value:
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
FxSyncRequest request(GetDriverGlobals(), NULL);
|
|
LIST_ENTRY pendHead;
|
|
FxUsbPipe* pPipe;
|
|
NTSTATUS status;
|
|
UCHAR iPipe, numPipes;
|
|
WDF_REQUEST_SEND_OPTIONS options;
|
|
FxUsbPipe ** ppPipes;
|
|
ULONG size;
|
|
|
|
//
|
|
// FxSyncRequest always succeesds for KM but can fail for UM.
|
|
//
|
|
status = request.Initialize();
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
|
|
"Failed to initialize FxSyncRequest");
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Subtract the size of the embedded pipe.
|
|
//
|
|
const ULONG interfaceStructSize = sizeof(Urb->UrbSelectInterface.Interface) -
|
|
sizeof(USBD_PIPE_INFORMATION);
|
|
|
|
//
|
|
// This check will happen twice for SelectSettingByInterface/Descriptor.
|
|
//
|
|
// We could just do it here, but the above two functions will unnecessarily
|
|
// have to build an URB.
|
|
//
|
|
if (IsInterfaceConfigured() &&
|
|
m_CurAlternateSetting ==
|
|
Urb->UrbSelectInterface.Interface.AlternateSetting) {
|
|
//
|
|
// don't do anything
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
InitializeListHead(&pendHead);
|
|
numPipes = 0;
|
|
ppPipes = NULL;
|
|
|
|
if (Urb->UrbSelectInterface.Hdr.Length < interfaceStructSize) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
|
|
"Urb header length 0x%x is less than expected 0x%x"
|
|
"%!STATUS!", Urb->UrbSelectInterface.Hdr.Length, interfaceStructSize,status
|
|
);
|
|
return status;
|
|
}
|
|
|
|
status = request.m_TrueRequest->ValidateTarget(m_UsbDevice);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Urb->UrbSelectInterface.Interface.NumberOfPipes is set when the URB
|
|
// completes. So, we must compute the number of pipes being requested based
|
|
// on the size of the structure and its Length (as set by the caller).
|
|
// To calculate the number of pipes we need to account for the
|
|
// embedded pipe in the structure.
|
|
//
|
|
numPipes = (UCHAR) ((Urb->UrbSelectInterface.Interface.Length -
|
|
interfaceStructSize) /
|
|
sizeof(USBD_PIPE_INFORMATION)
|
|
);
|
|
|
|
if (numPipes > 0) {
|
|
size = numPipes * sizeof(FxUsbPipe *);
|
|
}
|
|
else {
|
|
//
|
|
// It is valid to have an interface with zero pipes in it. In that
|
|
// case, we just allocate one entry so that we have a valid array
|
|
// and keep the remaining code simple.
|
|
//
|
|
size = sizeof(FxUsbPipe*);
|
|
}
|
|
|
|
//
|
|
// If the interface is already configured don't do anything with the old
|
|
// settings till we allocate new.
|
|
//
|
|
ppPipes = (FxUsbPipe **) FxPoolAllocate(GetDriverGlobals(), NonPagedPool, size);
|
|
|
|
if (ppPipes == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
|
|
"Unable to allocate memory %!STATUS!"
|
|
, status);
|
|
goto Done;
|
|
}
|
|
|
|
RtlZeroMemory(ppPipes, size);
|
|
|
|
for (iPipe = 0; iPipe < numPipes; iPipe++) {
|
|
ppPipes[iPipe] = new (GetDriverGlobals(), PipesAttributes)
|
|
FxUsbPipe(GetDriverGlobals(),m_UsbDevice);
|
|
|
|
if (ppPipes[iPipe] == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
|
|
"Unable to allocate memory for the pipes %!STATUS!", status);
|
|
goto Done;
|
|
}
|
|
|
|
pPipe = ppPipes[iPipe];
|
|
|
|
status = pPipe->Init(m_UsbDevice->m_Device);
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
|
|
"Init pipe failed %!STATUS!", status);
|
|
goto Done;
|
|
}
|
|
|
|
status = pPipe->Commit(PipesAttributes, NULL, this);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
|
|
"Commit pipe failed %!STATUS!", status);
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
if (IsInterfaceConfigured()) {
|
|
//
|
|
// Delete the old pipes
|
|
//
|
|
m_UsbDevice->CleanupInterfacePipesAndDelete(this);
|
|
}
|
|
|
|
|
|
WDF_REQUEST_SEND_OPTIONS_INIT(&options,
|
|
WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE);
|
|
|
|
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&options, WDF_REL_TIMEOUT_IN_SEC(2));
|
|
|
|
FxFormatUsbRequest(request.m_TrueRequest, Urb, FxUrbTypeLegacy, NULL);
|
|
status = m_UsbDevice->SubmitSync(request.m_TrueRequest, &options, NULL);
|
|
|
|
//
|
|
// If select interface URB fails we are at the point of no return and we
|
|
// will end up with no configured pipes.
|
|
//
|
|
if (NT_SUCCESS(status)) {
|
|
SetNumConfiguredPipes(numPipes);
|
|
SetConfiguredPipes(ppPipes);
|
|
SetInfo(&Urb->UrbSelectInterface.Interface);
|
|
}
|
|
|
|
Done:
|
|
if (!NT_SUCCESS(status)) {
|
|
if (ppPipes != NULL) {
|
|
ASSERT(ppPipes != m_ConfiguredPipes);
|
|
|
|
for (iPipe = 0; iPipe < numPipes; iPipe++) {
|
|
if (ppPipes[iPipe] != NULL) {
|
|
ppPipes[iPipe]->DeleteFromFailedCreate();
|
|
}
|
|
}
|
|
|
|
FxPoolFree(ppPipes);
|
|
ppPipes = NULL;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
FxUsbInterface::FormatSelectSettingUrb(
|
|
__in_bcount(GET_SELECT_INTERFACE_REQUEST_SIZE(NumEndpoints)) PURB Urb,
|
|
__in USHORT NumEndpoints,
|
|
__in UCHAR SettingNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Format a URB for selecting an interface's new setting. Note this will setup
|
|
the URB header and all pipes' information in the URB's array of pipe infos.
|
|
|
|
This function exists as a method of FxUsbDevice instead of FxUsbInterface
|
|
because in the case of a select config where we manually select setting 0
|
|
on interfaces 2...N we don't have an FxUsbInterface pointer
|
|
|
|
Arguments:
|
|
Urb - the URB to format. It is assumed the caller allocated a large enough
|
|
URB to contain NumEndpoints
|
|
|
|
NumEndpoints - number of endpoints in the new setting
|
|
|
|
InterfaceNum - interface number for the interface
|
|
|
|
SettingNumber - setting number on the interface
|
|
|
|
--*/
|
|
{
|
|
ULONG defaultMaxTransferSize;
|
|
USHORT size;
|
|
UCHAR i;
|
|
|
|
size = GET_SELECT_INTERFACE_REQUEST_SIZE(NumEndpoints);
|
|
|
|
RtlZeroMemory(Urb, size);
|
|
|
|
//
|
|
// Setup the URB, format the request, and send it
|
|
//
|
|
UsbBuildSelectInterfaceRequest(Urb,
|
|
size,
|
|
m_UsbDevice->m_ConfigHandle,
|
|
m_InterfaceNumber,
|
|
SettingNumber);
|
|
|
|
defaultMaxTransferSize = m_UsbDevice->GetDefaultMaxTransferSize();
|
|
|
|
Urb->UrbSelectInterface.Interface.Length =
|
|
GET_USBD_INTERFACE_SIZE(NumEndpoints);
|
|
|
|
Urb->UrbSelectInterface.Interface.NumberOfPipes = NumEndpoints;
|
|
|
|
for (i = 0; i < NumEndpoints; i++) {
|
|
|
|
//
|
|
// Make sure that the Interface Length conveys the exact number of EP's
|
|
//
|
|
ASSERT(
|
|
&Urb->UrbSelectInterface.Interface.Pipes[i] <
|
|
WDF_PTR_ADD_OFFSET(&Urb->UrbSelectInterface.Interface,
|
|
Urb->UrbSelectInterface.Interface.Length)
|
|
);
|
|
|
|
Urb->UrbSelectInterface.Interface.Pipes[i].PipeFlags = 0x0;
|
|
Urb->UrbSelectInterface.Interface.Pipes[i].MaximumTransferSize =
|
|
defaultMaxTransferSize;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FxUsbInterface::GetEndpointInformation(
|
|
__in UCHAR SettingIndex,
|
|
__in UCHAR EndpointIndex,
|
|
__in PWDF_USB_PIPE_INFORMATION PipeInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The layout of the config descriptor is such that each interface+setting pair
|
|
is followed by the endpoints for that interface+setting pair. Keep track of
|
|
the index.
|
|
|
|
Arguments:
|
|
SettingIndex - alternate setting to get info for
|
|
|
|
EndpointIndex - index into the number endpoints for this interface+setting
|
|
|
|
PipeInfo - Info to return
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PUCHAR pEnd, pCur;
|
|
PUSB_INTERFACE_DESCRIPTOR pInterfaceDesc;
|
|
PUSB_ENDPOINT_DESCRIPTOR pEndpointDesc;
|
|
UCHAR curEndpointIndex;
|
|
BOOLEAN endPointFound;
|
|
|
|
pInterfaceDesc = NULL;
|
|
curEndpointIndex = 0;
|
|
endPointFound = FALSE;
|
|
UNREFERENCED_PARAMETER(endPointFound);
|
|
|
|
//
|
|
// Extract the interface descriptor for the alternate setting for the interface
|
|
//
|
|
pInterfaceDesc = GetSettingDescriptor(SettingIndex);
|
|
|
|
if (pInterfaceDesc == NULL) {
|
|
return;
|
|
}
|
|
|
|
pEnd = (PUCHAR) WDF_PTR_ADD_OFFSET(
|
|
m_UsbDevice->m_ConfigDescriptor,
|
|
m_UsbDevice->m_ConfigDescriptor->wTotalLength
|
|
);
|
|
|
|
//
|
|
// Start from the descriptor after current one
|
|
//
|
|
pCur = (PUCHAR) WDF_PTR_ADD_OFFSET(pInterfaceDesc, pInterfaceDesc->bLength);
|
|
|
|
//
|
|
// Iterate through the list of EP descriptors following the interface descriptor
|
|
// we just found and get the endpoint descriptor which matches the endpoint
|
|
// index or we hit another interface descriptor
|
|
//
|
|
// We have already validated that the descriptor headers are well formed and within
|
|
// the config descriptor bounds
|
|
//
|
|
while (pCur < pEnd) {
|
|
PUSB_COMMON_DESCRIPTOR pCommonDesc = (PUSB_COMMON_DESCRIPTOR) pCur;
|
|
|
|
//
|
|
// If we hit the next interface no way we can find the EndPoint
|
|
//
|
|
if (pCommonDesc->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE) {
|
|
break;
|
|
}
|
|
|
|
if (pCommonDesc->bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE) {
|
|
//
|
|
// Size of pEndpointDesc has been validated by CreateSettings() and
|
|
// is within the config descriptor
|
|
//
|
|
pEndpointDesc = (PUSB_ENDPOINT_DESCRIPTOR) pCommonDesc;
|
|
|
|
if (EndpointIndex == curEndpointIndex) {
|
|
CopyEndpointFieldsFromDescriptor(PipeInfo,
|
|
pEndpointDesc,
|
|
SettingIndex);
|
|
break;
|
|
}
|
|
|
|
curEndpointIndex++;
|
|
}
|
|
|
|
//
|
|
// Advance past this descriptor
|
|
//
|
|
pCur += pCommonDesc->bLength;
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
FxUsbInterface::DetermineDefaultMaxTransferSize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Returns the maximum transfer size of an endpoint
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
max transfer size
|
|
|
|
--*/
|
|
{
|
|
if (m_UsbDevice->m_Traits & WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED) {
|
|
return FxUsbPipeHighSpeedMaxTransferSize;
|
|
}
|
|
else {
|
|
return FxUsbPipeLowSpeedMaxTransferSize;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FxUsbInterface::CopyEndpointFieldsFromDescriptor(
|
|
__in PWDF_USB_PIPE_INFORMATION PipeInfo,
|
|
__in PUSB_ENDPOINT_DESCRIPTOR EndpointDesc,
|
|
__in UCHAR SettingIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Copy informatoin out of the usb endpoint descriptor into this object
|
|
|
|
Arguments:
|
|
PipeInfo - information to return
|
|
|
|
EndpointDesc - descriptor to copy from
|
|
|
|
SettingIndex - alternate setting this information is for
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PipeInfo->MaximumPacketSize = EndpointDesc->wMaxPacketSize;
|
|
PipeInfo->EndpointAddress = EndpointDesc->bEndpointAddress;
|
|
PipeInfo->Interval = EndpointDesc->bInterval;
|
|
|
|
//
|
|
// Extract the lower 2 bits which contain the EP type
|
|
//
|
|
PipeInfo->PipeType = FxUsbPipe::_UsbdPipeTypeToWdf(
|
|
(USBD_PIPE_TYPE) (EndpointDesc->bmAttributes & 0x03)
|
|
);
|
|
|
|
//
|
|
// Filling in a default value since the EndpointDescriptor doesn't contain it
|
|
//
|
|
if (PipeInfo->PipeType == WdfUsbPipeTypeControl) {
|
|
PipeInfo->MaximumTransferSize = FxUsbPipeControlMaxTransferSize;
|
|
}
|
|
else {
|
|
PipeInfo->MaximumTransferSize = DetermineDefaultMaxTransferSize();
|
|
}
|
|
|
|
PipeInfo->SettingIndex = SettingIndex;
|
|
}
|
|
|
|
WDFUSBPIPE
|
|
FxUsbInterface::GetConfiguredPipe(
|
|
__in UCHAR PipeIndex,
|
|
__out_opt PWDF_USB_PIPE_INFORMATION PipeInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Return the WDFUSBPIPE for the given index
|
|
|
|
Arguments:
|
|
PipeIndex - index into the number of configured pipes for the interface
|
|
|
|
PipeInfo - optional information to return about the returned pipe
|
|
|
|
Return Value:
|
|
valid WDFUSBPIPE handle or NULL on error
|
|
|
|
--*/
|
|
{
|
|
if (PipeIndex >= m_NumberOfConfiguredPipes) {
|
|
return NULL;
|
|
}
|
|
else {
|
|
if (PipeInfo != NULL) {
|
|
m_ConfiguredPipes[PipeIndex]->GetInformation(PipeInfo);
|
|
}
|
|
|
|
return m_ConfiguredPipes[PipeIndex]->GetHandle();
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FxUsbInterface::GetDescriptor(
|
|
__in PUSB_INTERFACE_DESCRIPTOR UsbInterfaceDescriptor,
|
|
__in UCHAR SettingIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Copies the descriptor back to the caller
|
|
|
|
Arguments:
|
|
UsbInterfaceDescriptor - descriptor pointer to fill in
|
|
|
|
SettingIndex - alternate setting that the caller is interested in
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if (SettingIndex >= m_NumSettings) {
|
|
RtlZeroMemory(UsbInterfaceDescriptor,
|
|
sizeof(*UsbInterfaceDescriptor));
|
|
}
|
|
else {
|
|
RtlCopyMemory(UsbInterfaceDescriptor,
|
|
m_Settings[SettingIndex].InterfaceDescriptor,
|
|
sizeof(*UsbInterfaceDescriptor));
|
|
}
|
|
}
|
|
|
|
UCHAR
|
|
FxUsbInterface::GetConfiguredSettingIndex(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Returns the currently configured setting index for the interface
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return Value:
|
|
Currently configured Index
|
|
|
|
--*/
|
|
|
|
{
|
|
if (IsInterfaceConfigured()) {
|
|
return m_CurAlternateSetting;
|
|
}
|
|
else {
|
|
DoTraceLevelMessage(
|
|
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
|
|
"WDFUSBINTERFACE %p not configured, cannot retrieve configured "
|
|
"setting index", GetHandle());
|
|
|
|
FxVerifierDbgBreakPoint(GetDriverGlobals());
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
UCHAR
|
|
FxUsbInterface::GetNumEndpoints(
|
|
__in UCHAR SettingIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Returns the number of endpoints on a given alternate interface
|
|
|
|
Arguments:
|
|
SettingIndex - index of the alternate setting
|
|
|
|
Return Value:
|
|
Number of endpoints or 0 on error
|
|
|
|
--*/
|
|
{
|
|
if (SettingIndex >= m_NumSettings) {
|
|
return 0;
|
|
}
|
|
else {
|
|
return m_Settings[SettingIndex].InterfaceDescriptor->bNumEndpoints;
|
|
}
|
|
}
|
|
|
|
PUSB_INTERFACE_DESCRIPTOR
|
|
FxUsbInterface::GetSettingDescriptor(
|
|
__in UCHAR Setting
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Returns the device's interface descriptor for the given alternate setting
|
|
|
|
Arguments:
|
|
Setting - AlternateSetting desired
|
|
|
|
Return Value:
|
|
USB interface descriptor or NULL on failure
|
|
|
|
--*/
|
|
{
|
|
UCHAR i;
|
|
|
|
for (i = 0; i < m_NumSettings; i++) {
|
|
if (m_Settings[i].InterfaceDescriptor->bAlternateSetting == Setting) {
|
|
return m_Settings[i].InterfaceDescriptor;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|