1164 lines
34 KiB
C++
1164 lines
34 KiB
C++
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
usbdev.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains the implementation of IUsbDevice
|
|
except for the routine involved with device enumeration.
|
|
|
|
Environment:
|
|
|
|
XBOX kernel.
|
|
|
|
Notes:
|
|
|
|
|
|
Revision History:
|
|
|
|
03-24-00: Create (mitchd)
|
|
06-12-00: renamed to usbdev and changed to implement in c++.
|
|
|
|
--*/
|
|
|
|
//
|
|
// Pull in OS headers
|
|
//
|
|
#define _XAPI_
|
|
extern "C" {
|
|
#include <ntos.h>
|
|
}
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <xtl.h>
|
|
#include <xapidrv.h>
|
|
//#include <xapidrv.h>
|
|
|
|
//
|
|
// Setup the debug information for this file (see ..\inc\debug.h)
|
|
//
|
|
#define MODULE_POOL_TAG 'DBSU'
|
|
#include <debug.h>
|
|
DEFINE_USB_DEBUG_FUNCTIONS("USBD");
|
|
|
|
|
|
//
|
|
// Pull in remaining usb headers
|
|
//
|
|
#include <usbd.h>
|
|
|
|
VOID
|
|
USBD_SubmitSynchronousRequestComplete(
|
|
IN PURB Urb,
|
|
IN PKEVENT Event
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Completion routine used by USBD_STATUS IUsbDevice::SubmitRequest,
|
|
when blocking on an asynchronous request.
|
|
|
|
Arguments:
|
|
|
|
Urb - URB that completed.
|
|
Event - Pointer to event that caller is blocking on.
|
|
|
|
Return Value:
|
|
|
|
USBD_STATUS_SUCCESS - Or wide variety of possible errors.
|
|
The possible errors for each URB can be found where the URB
|
|
functions are defined.
|
|
|
|
--*/
|
|
{
|
|
UNREFERENCED_PARAMETER(Urb);
|
|
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
|
|
return;
|
|
}
|
|
|
|
|
|
USBD_STATUS IUsbDevice::SubmitRequest(PURB Urb)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Class drivers call this function to submit URBs to the HCD.
|
|
This routine picks off the URBs that it handles or preprocesses
|
|
and then passes the URBs down to the HCD if necessary.
|
|
|
|
Arguments:
|
|
|
|
Urb - URB to handle
|
|
|
|
Return Value:
|
|
|
|
USBD_STATUS_SUCCESS - Or wide variety of possible errors.
|
|
The possible errors for each URB can be found where the URB
|
|
functions are defined.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN block = FALSE;
|
|
USBD_STATUS usbdStatus;
|
|
KEVENT SyncEvent;
|
|
BOOLEAN submitRequestHere = TRUE;
|
|
//
|
|
// Check if this is an aynschrnous request and we need
|
|
// to block until done.
|
|
//
|
|
if(
|
|
(Urb->Header.Function&URB_FUNCTION_ASYNCHRONOUS) &&
|
|
(NULL == Urb->Header.CompleteProc)
|
|
)
|
|
{
|
|
block = TRUE;
|
|
ASSERT_LESS_THAN_DISPATCH_LEVEL();
|
|
//
|
|
// Initialize an event, and setup completion
|
|
// routine that will set the event.
|
|
//
|
|
|
|
KeInitializeEvent(&SyncEvent, NotificationEvent, FALSE);
|
|
Urb->Header.CompleteProc = (PURB_COMPLETE_PROC)USBD_SubmitSynchronousRequestComplete;
|
|
Urb->Header.CompleteContext = (PVOID)&SyncEvent;
|
|
}
|
|
//
|
|
// There are couple of URBs we need to preprocess
|
|
//
|
|
switch(Urb->Header.Function)
|
|
{
|
|
case URB_FUNCTION_OPEN_DEFAULT_ENDPOINT:
|
|
submitRequestHere = FALSE;
|
|
usbdStatus = OpenDefaultEndpoint(Urb);
|
|
break;
|
|
case URB_FUNCTION_CLOSE_DEFAULT_ENDPOINT:
|
|
submitRequestHere = FALSE;
|
|
usbdStatus = CloseDefaultEndpoint(Urb);
|
|
break;
|
|
case URB_FUNCTION_OPEN_ENDPOINT:
|
|
Urb->OpenEndpoint.FunctionAddress = m_Address;
|
|
Urb->OpenEndpoint.DataToggleBits = &m_DataToggleBits;
|
|
Urb->OpenEndpoint.LowSpeed = GetLowSpeed();
|
|
break;
|
|
case URB_FUNCTION_CLOSE_ENDPOINT:
|
|
Urb->CloseEndpoint.DataToggleBits = &m_DataToggleBits;
|
|
break;
|
|
case URB_FUNCTION_ISOCH_OPEN_ENDPOINT:
|
|
Urb->IsochOpenEndpoint.FunctionAddress = m_Address;
|
|
break;
|
|
//case URB_FUNCTION_RESET_PORT:
|
|
case URB_FUNCTION_CONTROL_TRANSFER:
|
|
//
|
|
// Resolve the endpoint handle for the default endpoint.
|
|
//
|
|
if(NULL == Urb->ControlTransfer.EndpointHandle)
|
|
{
|
|
ASSERT(m_DefaultEndpoint);
|
|
Urb->ControlTransfer.EndpointHandle = m_DefaultEndpoint;
|
|
}
|
|
//
|
|
// If this is a set configuration, clear the cached toggle
|
|
// bits.
|
|
//
|
|
if(USB_REQUEST_SET_CONFIGURATION == Urb->ControlTransfer.SetupPacket.bRequest)
|
|
{
|
|
m_DataToggleBits = 0;
|
|
}
|
|
break;
|
|
}
|
|
//
|
|
// Pass URB on to HCD (Open and close default endpoint, do it
|
|
// before getting here).
|
|
if(submitRequestHere)
|
|
{
|
|
usbdStatus = HCD_SubmitRequest(USBD_GetHCDExtension(m_HostController), Urb);
|
|
|
|
}
|
|
//
|
|
// Check if we need to wait.
|
|
//
|
|
if(block)
|
|
{
|
|
if(USBD_PENDING(usbdStatus))
|
|
{
|
|
KeWaitForSingleObject(&SyncEvent, Executive, KernelMode, FALSE, NULL);
|
|
usbdStatus = Urb->Header.Status;
|
|
}
|
|
Urb->Header.CompleteProc = NULL;
|
|
Urb->Header.CompleteContext = NULL;
|
|
}
|
|
return usbdStatus;
|
|
}
|
|
|
|
USBD_STATUS IUsbDevice::CancelRequest(PURB Urb)
|
|
{
|
|
return HCD_CancelRequest(USBD_GetHCDExtension(m_HostController), Urb);
|
|
}
|
|
|
|
|
|
PVOID IUsbDevice::GetExtension() const
|
|
/*++
|
|
Routine Description:
|
|
Retrieves the value of a the class driver defined
|
|
extension.
|
|
Return Value:
|
|
Previously set class driver extension.
|
|
--*/
|
|
{
|
|
return m_ClassDriverExtension;
|
|
}
|
|
PVOID IUsbDevice::SetExtension(PVOID Extension)
|
|
/*++
|
|
Routine Description:
|
|
Allows a the class driver to set an extension.
|
|
Return Value:
|
|
Previous value for class driver extension.
|
|
--*/
|
|
{
|
|
PVOID temp = m_ClassDriverExtension;
|
|
m_ClassDriverExtension = Extension;
|
|
return temp;
|
|
}
|
|
|
|
BOOLEAN IUsbDevice::IsHardwareConnected() const
|
|
/*++
|
|
Routine Description:
|
|
|
|
Retrieves whether the hardware is still connected.
|
|
|
|
The implmentation depends on the fact that the very first
|
|
step when getting DeviceDisconnected call from the HUB
|
|
is to orphan the device node.
|
|
|
|
Return Value:
|
|
|
|
Previously set class driver extension.
|
|
|
|
--*/
|
|
{
|
|
if(UDN_TYPE_INTERFACE == m_Type)
|
|
{
|
|
return (GetParent()->GetParent()) ? TRUE : FALSE;
|
|
} else
|
|
{
|
|
return GetParent() ? TRUE : FALSE;
|
|
}
|
|
}
|
|
|
|
UCHAR IUsbDevice::GetInterfaceNumber() const
|
|
/*++
|
|
Routine Description:
|
|
Returns the interface number for a device
|
|
Return Value:
|
|
The interface number that was originally
|
|
in the interface descriptor.
|
|
--*/
|
|
{
|
|
ASSERT((UDN_TYPE_INTERFACE == m_Type) || (UDN_TYPE_INTERFACE_FUNCTION == m_Type));
|
|
return this->m_bInterfaceNumber;
|
|
}
|
|
|
|
void IUsbDevice::SetClassSpecificType(UCHAR ClassSpecificType)
|
|
/*++
|
|
Routine Description:
|
|
Class driver calls this to set the class specific byte in the USB_DEVICE_TYPE
|
|
returned for this device.
|
|
Return Value:
|
|
none.
|
|
--*/
|
|
{
|
|
m_ClassSpecificType = ClassSpecificType;
|
|
}
|
|
|
|
ULONG IUsbDevice::Win32FromUsbdStatus(USBD_STATUS UsbdStatus)
|
|
/*++
|
|
Routine Description:
|
|
Returns a Win32 error code (NOT NT_STATUS) given a USBD_STATUS code.
|
|
Comment:
|
|
This makes good sense because XID exposes an API directly,
|
|
we may need to provide a separate function to convert to NT_STATUS
|
|
for class drivers with layers above them.
|
|
--*/
|
|
{
|
|
switch(UsbdStatus)
|
|
{
|
|
case USBD_STATUS_SUCCESS:
|
|
return ERROR_SUCCESS;
|
|
case USBD_STATUS_PENDING:
|
|
return ERROR_IO_PENDING;
|
|
case USBD_STATUS_ERROR:
|
|
case USBD_STATUS_HALTED:
|
|
case USBD_STATUS_CRC:
|
|
case USBD_STATUS_BTSTUFF:
|
|
case USBD_STATUS_DATA_TOGGLE_MISMATCH:
|
|
case USBD_STATUS_STALL_PID:
|
|
case USBD_STATUS_DEV_NOT_RESPONDING:
|
|
case USBD_STATUS_PID_CHECK_FAILURE:
|
|
case USBD_STATUS_UNEXPECTED_PID:
|
|
case USBD_STATUS_DATA_OVERRUN:
|
|
case USBD_STATUS_DATA_UNDERRUN:
|
|
case USBD_STATUS_RESERVED1:
|
|
case USBD_STATUS_RESERVED2:
|
|
case USBD_STATUS_BUFFER_OVERRUN:
|
|
case USBD_STATUS_BUFFER_UNDERRUN:
|
|
case USBD_STATUS_NOT_ACCESSED:
|
|
case USBD_STATUS_FIFO:
|
|
return ERROR_IO_DEVICE;
|
|
case USBD_STATUS_NO_MEMORY:
|
|
return ERROR_OUTOFMEMORY;
|
|
case USBD_STATUS_NO_BANDWIDTH:
|
|
return ERROR_NO_SYSTEM_RESOURCES;
|
|
case USBD_STATUS_CANCELED:
|
|
return ERROR_CANCELLED;
|
|
default:
|
|
return ERROR_GEN_FAILURE;
|
|
}
|
|
}
|
|
|
|
NTSTATUS IUsbDevice::NtStatusFromUsbdStatus(USBD_STATUS UsbdStatus)
|
|
/*++
|
|
Routine Description:
|
|
Returns a NTSTATUS error code given a USBD_STATUS code.
|
|
Comment:
|
|
--*/
|
|
{
|
|
switch(UsbdStatus)
|
|
{
|
|
case USBD_STATUS_SUCCESS:
|
|
return STATUS_SUCCESS;
|
|
case USBD_STATUS_PENDING:
|
|
return STATUS_PENDING;
|
|
case USBD_STATUS_ERROR:
|
|
case USBD_STATUS_HALTED:
|
|
case USBD_STATUS_CRC:
|
|
case USBD_STATUS_BTSTUFF:
|
|
case USBD_STATUS_DATA_TOGGLE_MISMATCH:
|
|
case USBD_STATUS_STALL_PID:
|
|
case USBD_STATUS_DEV_NOT_RESPONDING:
|
|
case USBD_STATUS_PID_CHECK_FAILURE:
|
|
case USBD_STATUS_UNEXPECTED_PID:
|
|
case USBD_STATUS_DATA_OVERRUN:
|
|
case USBD_STATUS_DATA_UNDERRUN:
|
|
case USBD_STATUS_RESERVED1:
|
|
case USBD_STATUS_RESERVED2:
|
|
case USBD_STATUS_BUFFER_OVERRUN:
|
|
case USBD_STATUS_BUFFER_UNDERRUN:
|
|
case USBD_STATUS_NOT_ACCESSED:
|
|
case USBD_STATUS_FIFO:
|
|
return STATUS_IO_DEVICE_ERROR;
|
|
case USBD_STATUS_NO_BANDWIDTH:
|
|
case USBD_STATUS_NO_MEMORY:
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
case USBD_STATUS_CANCELED:
|
|
return STATUS_CANCELLED;
|
|
default:
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
const USB_DEVICE_DESCRIPTOR8 *IUsbDevice::GetDeviceDescriptor() const
|
|
/*++
|
|
Routine Description:
|
|
Provides Class Driver with access to first eight bytes of device descriptor.
|
|
--*/
|
|
{
|
|
ASSERT(IsEnumTime());
|
|
return &g_DeviceTree.m_DeviceDescriptor;
|
|
}
|
|
|
|
const USB_CONFIGURATION_DESCRIPTOR *IUsbDevice::GetConfigurationDescriptor() const
|
|
/*++
|
|
Routine Description:
|
|
Provides Class Driver with access to Configuration descriptor.
|
|
Only available to Interface Class Drivers at enum time.
|
|
--*/
|
|
{
|
|
ASSERT(IsEnumTime());
|
|
ASSERT((UDN_TYPE_INTERFACE == m_Type)||(UDN_TYPE_INTERFACE_FUNCTION == m_Type));
|
|
return (PUSB_CONFIGURATION_DESCRIPTOR)g_DeviceTree.m_ConfigurationDescriptorBuffer;
|
|
}
|
|
|
|
const USB_INTERFACE_DESCRIPTOR *IUsbDevice::GetInterfaceDescriptor() const
|
|
/*++
|
|
Routine Description:
|
|
Provides Class Driver with access to Configuration descriptor.
|
|
Only available to Interface Class Drivers at enum time.
|
|
--*/
|
|
{
|
|
ASSERT(IsEnumTime());
|
|
ASSERT((UDN_TYPE_INTERFACE == m_Type)||(UDN_TYPE_INTERFACE_FUNCTION == m_Type));
|
|
return g_DeviceTree.m_InterfaceDescriptor;
|
|
}
|
|
|
|
|
|
const USB_ENDPOINT_DESCRIPTOR *IUsbDevice::GetEndpointDescriptor(UCHAR EndpointType, BOOLEAN InDirection, UCHAR Index) const
|
|
/*++
|
|
Routine Description:
|
|
Searches the class drivers interface for an endpoint.
|
|
Only available to Interface Class Drivers at enum time.
|
|
Arguments:
|
|
EndpointType - from usb100.h one of:
|
|
USB_ENDPOINT_TYPE_CONTROL
|
|
USB_ENDPOINT_TYPE_ISOCHRONOUS
|
|
USB_ENDPOINT_TYPE_BULK
|
|
USB_ENDPOINT_TYPE_INTERRUPT
|
|
Direction - True if the endpoint direction is in. Ignored for USB_ENDPOINT_TYPE_CONTROL.
|
|
Index - Zero based index to endpoint of that type and direction.
|
|
e.g. Find the first bulk-out endpoint: GetEndpointDescriptor(USB_ENDPOINT_TYPE_BULK, FALSE, 0).
|
|
Find the second bulk-out endpoint: GetEndpointDescriptor(USB_ENDPOINT_TYPE_BULK, FALSE, 1).
|
|
Return Value:
|
|
Pointer to endpoint descriptor for matching endpoint.
|
|
NULL if endpoint not found.
|
|
--*/
|
|
{
|
|
ASSERT(IsEnumTime());
|
|
ASSERT((UDN_TYPE_INTERFACE == m_Type)||(UDN_TYPE_INTERFACE_FUNCTION == m_Type));
|
|
|
|
PUCHAR currentParseLocation = (PUCHAR)g_DeviceTree.m_InterfaceDescriptor;
|
|
PUCHAR endOfCongifuration = g_DeviceTree.m_ConfigurationDescriptorBuffer +
|
|
((PUSB_CONFIGURATION_DESCRIPTOR)g_DeviceTree.m_ConfigurationDescriptorBuffer)->wTotalLength;
|
|
PUSB_ENDPOINT_DESCRIPTOR endpointDescriptor = NULL;
|
|
do
|
|
{
|
|
if(0==((PUSB_COMMON_DESCRIPTOR)currentParseLocation)->bLength) break;
|
|
currentParseLocation += ((PUSB_COMMON_DESCRIPTOR)currentParseLocation)->bLength;
|
|
if(currentParseLocation >= endOfCongifuration) break;
|
|
|
|
if(((PUSB_COMMON_DESCRIPTOR)currentParseLocation)->bDescriptorType == USB_ENDPOINT_DESCRIPTOR_TYPE)
|
|
{
|
|
PUSB_ENDPOINT_DESCRIPTOR temp = (PUSB_ENDPOINT_DESCRIPTOR)currentParseLocation;
|
|
if((temp->bmAttributes&USB_ENDPOINT_TYPE_MASK) == EndpointType)
|
|
{
|
|
if(
|
|
(EndpointType != USB_ENDPOINT_TYPE_CONTROL) &&
|
|
//When comparing BOOLEANs, compare their logical NOT's
|
|
(!USB_ENDPOINT_DIRECTION_IN(temp->bEndpointAddress) == !InDirection)
|
|
){
|
|
if(0 == Index--)
|
|
{
|
|
endpointDescriptor = temp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} while(((PUSB_COMMON_DESCRIPTOR)currentParseLocation)->bDescriptorType != USB_INTERFACE_DESCRIPTOR_TYPE);
|
|
return endpointDescriptor;
|
|
}
|
|
|
|
ULONG IUsbDevice::GetPort() const
|
|
{
|
|
return m_ExternalPort;
|
|
}
|
|
|
|
USBD_STATUS IUsbDevice::OpenDefaultEndpoint(PURB Urb)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Default endpoints are owned by USBD not by the class
|
|
drivers. However, USBD only uses the default endpoint
|
|
during enumeration and then closes it. If a class driver
|
|
needs to send commands over the default endpoint is must
|
|
send an URB_FUNCTION_OPEN_DEFAULT_ENDPOINT, which ends
|
|
up in this routine.
|
|
|
|
For UDN_TYPE_INTERFACE, there is one default endpoint
|
|
shared between multiple interfaces (i.e. class drivers).
|
|
The default endpoint may be open for the parent ofd type
|
|
UDN_TYPE_COMPOSITE_FUNCTION, and therefore for at least one
|
|
of the siblings. In this case, we can handle the URB competely
|
|
here. If it is not open for the parent we need to open the
|
|
default endpoint for the parent first.
|
|
|
|
For UDN_TYPE_INTERFACE_FUNCTION, UDN_TYPE_FUNCTION, and
|
|
UDN_TYPE_HUB the default endpoint is not shared. We just
|
|
need to copy some info out of the node into the URB before
|
|
passing it down.
|
|
|
|
Fortunately, OpenDefaultEndpoint is inherently synchronous so
|
|
we can do post-processing without blocking.
|
|
|
|
This routine does not use preallocating, because it
|
|
should never be called during enumeration.
|
|
|
|
Arguments:
|
|
|
|
Urb - URB to handle
|
|
|
|
Return Value:
|
|
|
|
USBD_STATUS_SUCCESS
|
|
USBD_STATUS_NO_MEMORY - If there is not enough memory for the endpoint.
|
|
|
|
--*/
|
|
{
|
|
USBD_STATUS status;
|
|
IUsbDevice *parent=NULL;
|
|
//
|
|
// If this assertion is hit, someone is trying to open
|
|
// a default endpoint that is already open.
|
|
//
|
|
ASSERT(NULL == m_DefaultEndpoint);
|
|
|
|
//
|
|
// Handle UDN_TYPE_INTERFACE.
|
|
//
|
|
if(UDN_TYPE_INTERFACE == m_Type)
|
|
{
|
|
parent = GetParent();
|
|
ASSERT(parent);
|
|
if(parent->m_DefaultEndpoint)
|
|
{
|
|
//
|
|
// The parent was open just copy the endpoint handle.
|
|
//
|
|
m_DefaultEndpoint = parent->m_DefaultEndpoint;
|
|
USB_DBG_TRACE_PRINT(("Open Default endpoint for UDN_TYPE_INTERFACE already open using parent's"));
|
|
//
|
|
// Even inherently synchronous URBs need completion
|
|
//
|
|
Urb->Header.Status = USBD_STATUS_SUCCESS;
|
|
USBD_CompleteRequest(Urb);
|
|
return USBD_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We actually need to open an endpoint, which means we need
|
|
// to finish filling out the URB with stuff from the node.
|
|
//
|
|
Urb->OpenEndpoint.FunctionAddress = m_Address;
|
|
Urb->OpenEndpoint.EndpointAddress = 0;
|
|
Urb->OpenEndpoint.EndpointType = USB_ENDPOINT_TYPE_CONTROL;
|
|
Urb->OpenEndpoint.MaxPacketSize = m_MaxPacket0;
|
|
Urb->OpenEndpoint.LowSpeed = GetLowSpeed();
|
|
Urb->OpenEndpoint.Hdr.Function = URB_FUNCTION_OPEN_ENDPOINT;
|
|
Urb->OpenEndpoint.DataToggleBits = NULL;
|
|
|
|
//
|
|
// Pass URB on to HCD
|
|
//
|
|
status = HCD_SubmitRequest(USBD_GetHCDExtension(m_HostController), Urb);
|
|
|
|
//
|
|
// If the endpoint successfully opened, store away the handle.
|
|
//
|
|
if(USBD_SUCCESS(status))
|
|
{
|
|
m_DefaultEndpoint = Urb->OpenEndpoint.EndpointHandle;
|
|
if(parent)
|
|
{
|
|
parent->m_DefaultEndpoint = Urb->OpenEndpoint.EndpointHandle;
|
|
}
|
|
ASSERT(m_DefaultEndpoint);
|
|
} else
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
//
|
|
// Don't show caller default endpoint.
|
|
//
|
|
Urb->OpenEndpoint.EndpointHandle = NULL;
|
|
//
|
|
// HCD completed the URB so we shouldn't.
|
|
//
|
|
return status;
|
|
}
|
|
|
|
|
|
USBD_STATUS IUsbDevice::CloseDefaultEndpoint(PURB Urb)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This routine is called in response to URB_CLOSE_DEFAULT_ENDPOINT and it
|
|
undoes IUsbDevice::OpenDefaultEndpoint.
|
|
|
|
Arguments:
|
|
|
|
Urb - URB to handle
|
|
|
|
Return Value:
|
|
|
|
USBD_STATUS_SUCCESS or USBD_STATUS_PENDING
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Mark the default endpoint close, but cache the handle
|
|
// in a local variable.
|
|
//
|
|
PVOID endpoint = m_DefaultEndpoint;
|
|
m_DefaultEndpoint = NULL;
|
|
//
|
|
// Handle UDN_TYPE_INTERFACE.
|
|
//
|
|
if(UDN_TYPE_INTERFACE == m_Type)
|
|
{
|
|
IUsbDevice *sibling;
|
|
//
|
|
// Now see if any of the siblings still have the endpoint open.
|
|
// If even one has it open, we can complete the URB
|
|
// and exit.
|
|
//
|
|
sibling = GetParent()->GetFirstChild();
|
|
while(sibling)
|
|
{
|
|
//
|
|
// We don't check for NULL. Why? Look at the note in IUsbDevice::AddComplete.
|
|
//
|
|
if(sibling->m_DefaultEndpoint == endpoint)
|
|
{
|
|
Urb->Header.Status = USBD_STATUS_SUCCESS;
|
|
USBD_CompleteRequest(Urb);
|
|
return USBD_STATUS_SUCCESS;
|
|
}
|
|
//
|
|
// Try next sibling
|
|
//
|
|
sibling = sibling->GetSibling();
|
|
}
|
|
//
|
|
// If we made it through the loop then close
|
|
// endpoint for real
|
|
//
|
|
GetParent()->m_DefaultEndpoint = NULL;
|
|
}
|
|
|
|
//
|
|
// Munge URB and pass on to HCD
|
|
//
|
|
Urb->CloseEndpoint.Hdr.Function = URB_FUNCTION_CLOSE_ENDPOINT;
|
|
Urb->CloseEndpoint.EndpointHandle = endpoint;
|
|
Urb->OpenEndpoint.DataToggleBits = NULL;
|
|
return HCD_SubmitRequest(USBD_GetHCDExtension(m_HostController), Urb);
|
|
}
|
|
|
|
IUsbDevice *IUsbDevice::FindChild(UCHAR PortNumber) const
|
|
/*++
|
|
Returns a pointer to a child device given its port number. NULL if not found.
|
|
--*/
|
|
{
|
|
|
|
// This should only be called for hubs, root_hubs and composite devices.
|
|
ASSERT( (UDN_TYPE_HUB == m_Type) || (UDN_TYPE_ROOT_HUB == m_Type) || (UDN_TYPE_COMPOSITE_FUNCTION == m_Type));
|
|
|
|
IUsbDevice *device = GetFirstChild();
|
|
while( device && (device->m_PortNumber & ~UDN_LOWSPEED_PORT) != (PortNumber & ~UDN_LOWSPEED_PORT))
|
|
{
|
|
device = device->GetSibling();
|
|
}
|
|
return device;
|
|
}
|
|
|
|
void IUsbDevice::InsertChild(IUsbDevice *child)
|
|
/*++
|
|
Inserts a device as a child of this device.
|
|
It is important to make it the last child,
|
|
because FindChild finds the first matching
|
|
child. If there is a remove and a reinsert
|
|
between enumeration stages, there could be
|
|
two matching children, one pending removal.
|
|
So it is imporant to add from the opposite end
|
|
as removal (i.e. finding , since we remove
|
|
what we find).
|
|
--*/
|
|
{
|
|
// This should only be called for hubs, root_hubs and composite devices.
|
|
ASSERT( (UDN_TYPE_HUB == m_Type) || (UDN_TYPE_ROOT_HUB == m_Type) || (UDN_TYPE_COMPOSITE_FUNCTION == m_Type));
|
|
ASSERT(child >= g_DeviceTree.m_Devices);
|
|
//
|
|
// Get the nodeIndex and debug assert that it is valid
|
|
//
|
|
UCHAR nodeIndex = child - g_DeviceTree.m_Devices;
|
|
ASSERT(UDN_INVALID_NODE_INDEX > nodeIndex);
|
|
|
|
//
|
|
// We know the childs parent and sibling now, so fill it in.
|
|
//
|
|
child->m_Sibling = UDN_INVALID_NODE_INDEX;
|
|
child->m_Parent = this - g_DeviceTree.m_Devices;
|
|
|
|
|
|
IUsbDevice *existingChild = GetFirstChild();
|
|
|
|
if(!existingChild)
|
|
//
|
|
// If there are no children, then it is the first.
|
|
//
|
|
{
|
|
m_FirstChild = nodeIndex;
|
|
} else
|
|
//
|
|
// Otherwise, we need to insert at the end
|
|
//
|
|
{
|
|
//
|
|
// Make sure we are not inserting the child twice
|
|
//
|
|
ASSERT(existingChild!=child);
|
|
|
|
//
|
|
// walk to the end.
|
|
//
|
|
while(UDN_INVALID_NODE_INDEX != existingChild->m_Sibling)
|
|
{
|
|
existingChild = existingChild->GetSibling();
|
|
//
|
|
// Make sure we are not inserting the child twice
|
|
//
|
|
ASSERT(existingChild!=child);
|
|
}
|
|
//
|
|
// attach to the end
|
|
//
|
|
existingChild->m_Sibling = nodeIndex;
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN IUsbDevice::RemoveChild(IUsbDevice *child)
|
|
/*++
|
|
Removes a device from the list of children.
|
|
Returns TRUE if the are still more children.
|
|
--*/
|
|
{
|
|
// This should only be called for hubs, root_hubs and composite devices.
|
|
ASSERT( (UDN_TYPE_HUB == m_Type) || (UDN_TYPE_ROOT_HUB == m_Type) || (UDN_TYPE_COMPOSITE_FUNCTION == m_Type));
|
|
|
|
IUsbDevice *prevChild = GetFirstChild();
|
|
BOOLEAN moreChildren = TRUE;
|
|
if(prevChild == child)
|
|
{
|
|
m_FirstChild = child->m_Sibling;
|
|
if(m_FirstChild == UDN_INVALID_NODE_INDEX) moreChildren = FALSE;
|
|
} else
|
|
{
|
|
|
|
while(prevChild && (prevChild->GetSibling() != child))
|
|
{
|
|
prevChild = prevChild->GetSibling();
|
|
}
|
|
ASSERT(prevChild);
|
|
if(prevChild)
|
|
{
|
|
prevChild->m_Sibling = child->m_Sibling;
|
|
}
|
|
}
|
|
child->m_Sibling = UDN_INVALID_NODE_INDEX;
|
|
child->m_Parent = UDN_INVALID_NODE_INDEX;
|
|
return moreChildren;
|
|
}
|
|
|
|
PNP_CLASS_ID IUsbDevice::GetClassId() const
|
|
{
|
|
PNP_CLASS_ID classId;
|
|
if(0xFF != m_ClassSpecificType)
|
|
{
|
|
classId.AsLong = m_ClassDriver->ClassId.AsLong;
|
|
classId.USB.bClassSpecificType = m_ClassSpecificType;
|
|
} else {
|
|
classId.AsLong = -1;
|
|
}
|
|
return classId;
|
|
}
|
|
|
|
|
|
BOOLEAN IUsbDevice::IsEnumTime() const
|
|
/*++
|
|
Test to see if this device is currently being enumerated.
|
|
This routine gets called in DEBUG mode for lots of calls
|
|
to IUsbDevice from the ClassDriver. It is also used
|
|
in the retail build to decide whether or not to use a pre-allocated
|
|
an endpoint on open.
|
|
--*/
|
|
{
|
|
//
|
|
// If you are not at DISPATCH_LEVEL you could
|
|
// leave EnumTime whenever, so effectively it
|
|
// is not EnumTime.
|
|
//
|
|
if(DISPATCH_LEVEL != KeGetCurrentIrql()) return FALSE;
|
|
//
|
|
// If nobody is being enumerated than we are not
|
|
// being enumerated.
|
|
//
|
|
if(!g_DeviceTree.m_InProgress) return FALSE;
|
|
|
|
//
|
|
// If we are UDN_TYPE_INTERFACE, then really it
|
|
// is our parent being enumerated. We are than at enum
|
|
// time if:
|
|
// a) Our default endpoint non-NULL and different from
|
|
// our parents.
|
|
// b) Our previous sibling default endpoint is NULL or the same
|
|
// same as our parents.
|
|
// This condition implies that our parent is g_DeviceTree.m_CurrentEnum
|
|
// we will double check this in debug builds only.
|
|
//
|
|
// We will decline to check b), because it is tough, and it would
|
|
// imply that a class driver got a handle to us prior to receiving
|
|
// AddDevice. This driver doesn't call IsEnumTime for its own
|
|
// purposes anyway.
|
|
//
|
|
if(UDN_TYPE_INTERFACE == m_Type)
|
|
{
|
|
ASSERT(GetParent()); //Serious error for UDN_TYPE_INTERFACE not to have a parent.
|
|
if(
|
|
(NULL == m_DefaultEndpoint) ||
|
|
(GetParent()->m_DefaultEndpoint == m_DefaultEndpoint)
|
|
) return FALSE;
|
|
ASSERT(g_DeviceTree.m_CurrentEnum == GetParent());
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// So we are not of type UDN_TYPE_INTERFACE, which means
|
|
// that we are at EnumTime iff we are g_DeviceTree.m_CurrentEnum.
|
|
//
|
|
if(g_DeviceTree.m_CurrentEnum != this) return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifndef SILVER //SetExternalPort for BLACK builds
|
|
|
|
#define MAX_TREE_INDEX 5
|
|
void IUsbDevice::SetExternalPort()
|
|
/*++
|
|
This routine calculates the external port number which includes
|
|
the slot.
|
|
|
|
0-3 is for a direct connect device or a top slot, and 16-19 if for a device
|
|
that connects through the bottom slot.
|
|
--*/
|
|
{
|
|
//
|
|
// Build Parent Device Array
|
|
//
|
|
IUsbDevice *pParentArrayBuffer[MAX_TREE_INDEX+1];
|
|
IUsbDevice **pParentArray;
|
|
UINT parentIndex = MAX_TREE_INDEX;
|
|
UINT deviceIndex;
|
|
pParentArrayBuffer[MAX_TREE_INDEX] = this;
|
|
do
|
|
{
|
|
parentIndex--;
|
|
ASSERT(parentIndex >= 0);
|
|
pParentArrayBuffer[parentIndex] = pParentArrayBuffer[parentIndex+1]->GetParent();
|
|
} while(UDN_TYPE_ROOT_HUB != pParentArrayBuffer[parentIndex]->m_Type);
|
|
|
|
//
|
|
// Adjust pParentArray so that index 0 is the root hub
|
|
// and deviceIndex is the device itself.
|
|
//
|
|
pParentArray = pParentArrayBuffer + parentIndex;
|
|
deviceIndex = MAX_TREE_INDEX - parentIndex;
|
|
|
|
// This should not be possible
|
|
ASSERT(0!=deviceIndex);
|
|
|
|
//
|
|
// Call the appropriate configuration routine
|
|
//
|
|
if(XBOX_HW_FLAG_INTERNAL_USB_HUB & XboxHardwareInfo->Flags)
|
|
{
|
|
SetExternalPortWithHub(pParentArray, deviceIndex);
|
|
} else
|
|
{
|
|
SetExternalPortWithoutHub(pParentArray, deviceIndex);
|
|
}
|
|
}
|
|
|
|
|
|
void IUsbDevice::SetExternalPortWithHub(IUsbDevice **pParentArray, UINT DeviceIndex)
|
|
{
|
|
|
|
//
|
|
// We expect the device at index 1 to be a hub.
|
|
// Otherwise, someone is screwing with the configuration.
|
|
//
|
|
if(UDN_TYPE_HUB!=pParentArray[1]->m_Type)
|
|
{
|
|
m_ExternalPort = XDEVICE_ILLEGAL_PORT;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// That first hub must be plugged into port 1 of the
|
|
// root hub.
|
|
//
|
|
if(1!=pParentArray[1]->GetHubPort())
|
|
{
|
|
m_ExternalPort = XDEVICE_ILLEGAL_PORT;
|
|
return;
|
|
}
|
|
|
|
//If this is the internal hub we are done,
|
|
if(1==DeviceIndex)
|
|
{
|
|
m_ExternalPort = 0;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Otherwise, we can call the SetExternalPortWithoutHub
|
|
// after splicing the internal hub out of the topology.
|
|
//
|
|
pParentArray[1] = pParentArray[0];
|
|
SetExternalPortWithoutHub(pParentArray+1, DeviceIndex-1);
|
|
}
|
|
|
|
void IUsbDevice::SetExternalPortWithoutHub(IUsbDevice **pParentArray, UINT DeviceIndex)
|
|
{
|
|
//
|
|
// In general the port is the hub port that the device at index 1
|
|
// is plugged into.
|
|
//
|
|
m_ExternalPort = (pParentArray[1]->GetHubPort()-1);
|
|
if(m_ExternalPort >= XGetPortCount())
|
|
{
|
|
m_ExternalPort = XDEVICE_ILLEGAL_PORT;
|
|
return;
|
|
}
|
|
// A board layout problem on the motherboard (and on the daughter
|
|
// hub to work around a USB drive level issue on the MCP rev B01)
|
|
// requires us to swap ports 0 and 1 with ports 2 and 3. We can
|
|
// do this by XOR with 2. This assumes that there is no port 5.
|
|
//
|
|
m_ExternalPort ^= 2;
|
|
|
|
//
|
|
// If the device is a hub, its parent must be the root hub.
|
|
//
|
|
if(UDN_TYPE_HUB==pParentArray[DeviceIndex]->m_Type)
|
|
{
|
|
if(1 == DeviceIndex) return;
|
|
m_ExternalPort = XDEVICE_ILLEGAL_PORT;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If the device index is not plugged directly into the box
|
|
// (equivalent to the DeviceIndex being greater than 1)
|
|
// then the slot is determined by the HubPort that the device at
|
|
// DeviceIndex 2 is inserted into. Port 1 is direct connect, port 2
|
|
// is top slot and port 3 is bottom slot. Only port 3 changes m_ExternalPort
|
|
// (We need to reject anything plugged into another slot.)
|
|
//
|
|
if(DeviceIndex>1)
|
|
{
|
|
ULONG SlotPort = (pParentArray[2]->GetHubPort())-1;
|
|
//
|
|
// Adjust m_ExternalPort with the slot information
|
|
//
|
|
if(SlotPort > 3)
|
|
{
|
|
m_ExternalPort = XDEVICE_ILLEGAL_PORT;
|
|
} else if(SlotPort == 2)
|
|
{
|
|
m_ExternalPort += 16;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
#else //SetExternalPort for SILVER builds
|
|
void IUsbDevice::SetExternalPort()
|
|
/*++
|
|
This routine calculates the external port number. The external
|
|
port number is 0-3 for devices that plug directly in to XBOX.
|
|
This corresponds to the labels 1 through 4 on the outside of
|
|
the box. If the device is in the bottom slot of a game controller
|
|
16 is added to the external port number.
|
|
|
|
This code supports three configuration based on #define's
|
|
|
|
USB_HOST_CONTROLLER_CONFIGURATION is to one of four
|
|
values.
|
|
|
|
USB_SINGLE_HOST_CONTROLLER
|
|
Ports one to four on thre root host, become external ports 0-3.
|
|
If externa; port four check for external hub, thus the external
|
|
port could be greater.
|
|
|
|
USB_DUAL_HOST_CONTROLLER_XDK
|
|
The first Host Controllers' ports one and two, become external ports
|
|
0 and 1. The second Host Controllers' ports one and two
|
|
become external ports 2 and 3.
|
|
|
|
If external port three check for external hub, thus
|
|
external port could be greater.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Build list of device.
|
|
//
|
|
IUsbDevice *pParentArrayBuffer[5];
|
|
IUsbDevice **pParentArray;
|
|
int parentIndex = 4;
|
|
pParentArrayBuffer[4] = this;
|
|
do
|
|
{
|
|
parentIndex--;
|
|
ASSERT(parentIndex >= 0);
|
|
pParentArrayBuffer[parentIndex] = pParentArrayBuffer[parentIndex+1]->GetParent();
|
|
} while(UDN_TYPE_ROOT_HUB != pParentArrayBuffer[parentIndex]->m_Type);
|
|
|
|
//
|
|
// Adjust pParentArray so that index 0, is the Root Hub
|
|
//
|
|
|
|
//pParentArray = &pParentArrayBuffer[parentIndex];
|
|
pParentArray = pParentArrayBuffer + parentIndex;
|
|
int maxIndex = 4 - parentIndex;
|
|
|
|
//
|
|
// If the index is less than two, this is
|
|
// a hub, or some strange configuration
|
|
//
|
|
|
|
if(maxIndex < 1) return;
|
|
|
|
//
|
|
// USB_SINGLE_HOST_CONTROLLER
|
|
//
|
|
|
|
#if (USB_HOST_CONTROLLER_CONFIGURATION==USB_SINGLE_HOST_CONTROLLER)
|
|
ASSERT(1==pParentArray[0]->m_HostController->ControllerNumber); //It should always be host controller 1
|
|
m_ExternalPort = pParentArray[1]->GetHubPort()-1;
|
|
#endif //(USB_HOST_CONTROLLER_CONFIGURATION = USB_SINGLE_HOST_CONTROLLER)
|
|
|
|
//
|
|
// USB_DUAL_HOST_CONTROLLER_XDK
|
|
//
|
|
|
|
#if (USB_HOST_CONTROLLER_CONFIGURATION==USB_DUAL_HOST_CONTROLLER_XDK)
|
|
m_ExternalPort = pParentArray[1]->GetHubPort()-1;
|
|
if(2==pParentArray[0]->m_HostController->ControllerNumber)
|
|
{
|
|
m_ExternalPort += 2;
|
|
}
|
|
#endif //(USBD_HOST_CONTROLLER_CONFIGURATION = USB_DUAL_HOST_CONTROLLER_XDK)
|
|
|
|
int functionIndex = 2;
|
|
|
|
//
|
|
// At this point the external port should be 0-3, (or 4 if it is a dual
|
|
// host controller for DVT) if not it is in an illegal port.
|
|
//
|
|
if(m_ExternalPort > 3)
|
|
{
|
|
m_ExternalPort = XDEVICE_ILLEGAL_PORT;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Deal with the possiblity of an external hub
|
|
//
|
|
if(
|
|
(UDN_TYPE_HUB == pParentArray[1]->m_Type) &&
|
|
(UDN_HUB_TYPE_EXTERNAL == pParentArray[1]->m_ClassSpecificType)
|
|
)
|
|
{
|
|
if(3 == m_ExternalPort)
|
|
{
|
|
//
|
|
// If there are no more nodes, we are being
|
|
// called on the external hub, we are done.
|
|
//
|
|
|
|
if(maxIndex < 2) return;
|
|
m_ExternalPort = pParentArray[2]->GetHubPort()+2;
|
|
functionIndex = 3;
|
|
} else
|
|
{
|
|
m_ExternalPort = XDEVICE_ILLEGAL_PORT;
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Exclude functions directly
|
|
// connected to the box.
|
|
//
|
|
#ifndef USB_ENABLE_DIRECT_CONNECT
|
|
if(UDN_TYPE_ROOT_HUB == pParentArray[functionIndex-1]->m_Type)
|
|
{
|
|
m_ExternalPort = XDEVICE_ILLEGAL_PORT;
|
|
return;
|
|
}
|
|
if(UDN_TYPE_COMPOSITE_FUNCTION == pParentArray[functionIndex-1]->m_Type)
|
|
{
|
|
if(UDN_TYPE_ROOT_HUB == pParentArray[functionIndex-2]->m_Type)
|
|
{
|
|
m_ExternalPort = XDEVICE_ILLEGAL_PORT;
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// We may be evaluating the parent of
|
|
// a function, or a device plugged
|
|
// directly into the box.
|
|
//
|
|
|
|
if(functionIndex > maxIndex)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check for bottom slot (or illegal slot)
|
|
//
|
|
BOOLEAN fBottomSlot = FALSE;
|
|
//
|
|
// If the parent is a hub, then the slot is our port number
|
|
// 1 or 2 = top, 3 = bottom, > 3 is illegal.
|
|
if(UDN_TYPE_HUB == pParentArray[functionIndex-1]->m_Type)
|
|
{
|
|
if(pParentArray[functionIndex]->GetHubPort() == 3)
|
|
{
|
|
fBottomSlot = TRUE;
|
|
} else if(pParentArray[functionIndex]->GetHubPort() > 3)
|
|
{
|
|
m_ExternalPort = XDEVICE_ILLEGAL_PORT;
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the parent is a composite, e.g. Hawk, then the slot
|
|
// is based on our parent port.
|
|
//
|
|
else if(UDN_TYPE_COMPOSITE_FUNCTION == pParentArray[functionIndex-1]->m_Type)
|
|
{
|
|
if(UDN_TYPE_HUB == pParentArray[functionIndex-2]->m_Type)
|
|
{
|
|
if(pParentArray[functionIndex-1]->GetHubPort() == 3)
|
|
{
|
|
fBottomSlot = TRUE;
|
|
} else if(pParentArray[functionIndex-1]->GetHubPort() > 3)
|
|
{
|
|
m_ExternalPort = XDEVICE_ILLEGAL_PORT;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(fBottomSlot)
|
|
{
|
|
m_ExternalPort += 16;
|
|
}
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
|
|
|