[USBOHCI]

- Set up periodic threshold (90 %)
- Add function to retrieve interrupt endpoints
- Add function to retrieve specific descriptors from interface. These are used by HID devices such as mice / keyboards
- Add function to retrieve interrupt interval
- Enqueue all endpoint descriptors at the end of the associated queue
- Only notify hardware of insertion when it is an bulk / control request
- Scan interrupt endpoint list to find the completed transfer descriptor
- Add debugging function to print out linked endpoint descriptors
- Interrupt transfers are now implemented. 
- Tested in Windows XP SP3 + Vbox 4.04 + Microsoft 5-Button Mouse. The HID mouse installs, initializes and starts up. Unfortunately the mouse does not work as expected yet

svn path=/branches/usb-bringup/; revision=51922
This commit is contained in:
Johannes Anderwald 2011-05-26 12:37:18 +00:00
parent 667b425b6c
commit 9aff3e967c
6 changed files with 297 additions and 30 deletions

View file

@ -61,6 +61,7 @@ public:
NTSTATUS GetDeviceDetails(PUSHORT VendorId, PUSHORT DeviceId, PULONG NumberOfPorts, PULONG Speed);
NTSTATUS GetBulkHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor);
NTSTATUS GetControlHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor);
NTSTATUS GetInterruptEndpointDescriptors(struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptor);
VOID HeadEndpointDescriptorModified(ULONG HeadType);
NTSTATUS GetDMA(OUT struct IDMAMemoryManager **m_DmaManager);
@ -113,16 +114,16 @@ protected:
PHYSICAL_ADDRESS m_HCCAPhysicalAddress; // hcca physical address
POHCI_ENDPOINT_DESCRIPTOR m_ControlEndpointDescriptor; // dummy control endpoint descriptor
POHCI_ENDPOINT_DESCRIPTOR m_BulkEndpointDescriptor; // dummy control endpoint descriptor
POHCI_ENDPOINT_DESCRIPTOR m_IsoEndpointDescriptor; // iso endpoint descriptor
POHCI_ENDPOINT_DESCRIPTOR m_IsoEndpointDescriptor; // iso endpoint descriptor
POHCI_ENDPOINT_DESCRIPTOR m_InterruptEndpoints[OHCI_STATIC_ENDPOINT_COUNT]; // endpoints for interrupt / iso transfers
ULONG m_NumberOfPorts; // number of ports
OHCI_PORT_STATUS m_PortStatus[OHCI_MAX_PORT_COUNT]; // port change status
PDMAMEMORYMANAGER m_MemoryManager; // memory manager
HD_INIT_CALLBACK* m_SCECallBack; // status change callback routine
PVOID m_SCEContext; // status change callback routine context
BOOLEAN m_DoorBellRingInProgress; // door bell ring in progress
WORK_QUEUE_ITEM m_StatusChangeWorkItem; // work item for status change callback
ULONG m_SyncFramePhysAddr; // periodic frame list physical address
ULONG m_IntervalValue; // periodic interval value
};
//=================================================================================================
@ -489,7 +490,7 @@ CUSBHardwareDevice::GetUSBQueue(
NTSTATUS
CUSBHardwareDevice::StartController(void)
{
ULONG Control, NumberOfPorts, Index, Descriptor, FrameInterval, Periodic, IntervalValue;
ULONG Control, NumberOfPorts, Index, Descriptor, FrameInterval, Periodic;
//
// first write address of HCCA
@ -545,16 +546,19 @@ CUSBHardwareDevice::StartController(void)
//
// get frame interval
//
//FrameInterval = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET)) & OHCI_FRAME_INTERVAL_TOGGLE) ^ OHCI_FRAME_INTERVAL_TOGGLE;
//FrameInterval |= OHCI_FSMPS(IntervalValue) | IntervalValue;
FrameInterval = (READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET)) & OHCI_FRAME_INTERVAL_TOGGLE) ^ OHCI_FRAME_INTERVAL_TOGGLE;
FrameInterval |= OHCI_FSMPS(m_IntervalValue) | m_IntervalValue;
//
// write frame interval
//
//WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval);
// 90% periodic
//Periodic = OHCI_PERIODIC(intervalValue);
WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + 0x40 /*OHCI_PERIODIC_START_OFFSET*/), 0x3E67);
WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET), FrameInterval);
//
// 90 % periodic
//
Periodic = OHCI_PERIODIC(m_IntervalValue);
WRITE_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_PERIODIC_START_OFFSET), Periodic);
//
@ -686,6 +690,14 @@ CUSBHardwareDevice::GetBulkHeadEndpointDescriptor(
return STATUS_SUCCESS;
}
NTSTATUS
CUSBHardwareDevice::GetInterruptEndpointDescriptors(
struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptor)
{
*OutDescriptor = m_InterruptEndpoints;
return STATUS_SUCCESS;
}
VOID
CUSBHardwareDevice::HeadEndpointDescriptorModified(
ULONG Type)
@ -846,7 +858,7 @@ NTSTATUS
CUSBHardwareDevice::StopController(void)
{
ULONG Control, Reset;
ULONG Index;
ULONG Index, FrameInterval;
//
// first turn off all interrupts
@ -878,6 +890,16 @@ CUSBHardwareDevice::StopController(void)
//
KeStallExecutionProcessor(100);
//
// read from interval
//
FrameInterval = READ_REGISTER_ULONG((PULONG)((PUCHAR)m_Base + OHCI_FRAME_INTERVAL_OFFSET));
//
// store interval value for later
//
m_IntervalValue = OHCI_GET_INTERVAL_VALUE(FrameInterval);
//
// now reset controller
//

View file

@ -90,6 +90,19 @@
#define OHCI_RH_NO_OVER_CURRENT_PROTECTION 0x1000
#define OHCI_RH_GET_POWER_ON_TO_POWER_GOOD_TIME(s) ((s) >> 24)
//
// Frame interval register (section 7.3.1)
//
#define OHCI_FRAME_INTERVAL_OFFSET 0x34
#define OHCI_GET_INTERVAL_VALUE(s) ((s) & 0x3fff)
#define OHCI_GET_FS_LARGEST_DATA_PACKET(s) (((s) >> 16) & 0x7fff)
#define OHCI_FRAME_INTERVAL_TOGGLE 0x80000000
//
// periodic start register
//
#define OHCI_PERIODIC_START_OFFSET 0x40
#define OHCI_PERIODIC(i) ((i) * 9 / 10)
//
// Root Hub Descriptor B register (section 7.4.2)

View file

@ -60,6 +60,7 @@ public:
VOID SetNotification(PVOID CallbackContext, PRH_INIT_CALLBACK CallbackRoutine);
// internal ioctl routines
NTSTATUS HandleGetDescriptor(IN OUT PIRP Irp, PURB Urb);
NTSTATUS HandleGetDescriptorFromInterface(IN OUT PIRP Irp, PURB Urb);
NTSTATUS HandleClassDevice(IN OUT PIRP Irp, PURB Urb);
NTSTATUS HandleGetStatusFromDevice(IN OUT PIRP Irp, PURB Urb);
NTSTATUS HandleSelectConfiguration(IN OUT PIRP Irp, PURB Urb);
@ -1223,6 +1224,55 @@ CHubController::HandleClassDevice(
return Status;
}
//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleGetDescriptorFromInterface(
IN OUT PIRP Irp,
IN OUT PURB Urb)
{
PUSBDEVICE UsbDevice;
USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup;
NTSTATUS Status;
//
// sanity check
//
ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength);
ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer);
//
// check if this is a valid usb device handle
//
ASSERT(ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)));
//
// get device
//
UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);
//
// generate setup packet
//
CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR;
CtrlSetup.wValue.LowByte = Urb->UrbControlDescriptorRequest.Index;
CtrlSetup.wValue.HiByte = Urb->UrbControlDescriptorRequest.DescriptorType;
CtrlSetup.wIndex.W = Urb->UrbControlDescriptorRequest.LanguageId;
CtrlSetup.wLength = (USHORT)Urb->UrbControlDescriptorRequest.TransferBufferLength;
CtrlSetup.bmRequestType.B = 0x81;
//
// submit setup packet
//
Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer);
ASSERT(Status == STATUS_SUCCESS);
//
// done
//
return Status;
}
//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleGetDescriptor(
@ -1492,8 +1542,8 @@ CHubController::HandleClassInterface(
//
// sanity check
//
PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer);
PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength);
//ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer || Urb->UrbControlVendorClassRequest.TransferBufferMDL);
//ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength);
PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle);
//
@ -1520,7 +1570,7 @@ CHubController::HandleClassInterface(
//
// initialize setup packet
//
CtrlSetup.bmRequestType.B = 0xa1; //FIXME: Const.
CtrlSetup.bmRequestType.B = 0xa1;
CtrlSetup.bRequest = Urb->UrbControlVendorClassRequest.Request;
CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value;
CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index;
@ -1579,6 +1629,9 @@ CHubController::HandleDeviceControl(
switch (Urb->UrbHeader.Function)
{
case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE:
Status = HandleGetDescriptorFromInterface(Irp, Urb);
break;
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
Status = HandleGetDescriptor(Irp, Urb);
break;

View file

@ -188,6 +188,14 @@ DECLARE_INTERFACE_(IUSBHardwareDevice, IUnknown)
virtual NTSTATUS GetControlHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) = 0;
//-----------------------------------------------------------------------------------------
//
// GetInterruptEndpointDescriptors
//
// Description: returns interrupt endpoint descriptors
virtual NTSTATUS GetInterruptEndpointDescriptors(struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptorArray) = 0;
//-----------------------------------------------------------------------------------------
//
// HeadEndpointDescriptorModified
@ -451,6 +459,15 @@ DECLARE_INTERFACE_(IUSBRequest, IUnknown)
// Description: frees the associated endpoint descriptor and its general descriptors
virtual VOID FreeEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor) = 0;
//-----------------------------------------------------------------------------------------
//
// GetInterruptInterval
//
// Description: returns interval of the iso / interrupt
virtual UCHAR GetInterval() = 0;
};

View file

@ -44,7 +44,12 @@ public:
// local functions
BOOLEAN IsTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG TransferDescriptorLogicalAddress);
NTSTATUS FindTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor);
NTSTATUS FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor);
VOID CleanupEndpointDescriptor(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, POHCI_ENDPOINT_DESCRIPTOR PreviousEndpointDescriptor);
POHCI_ENDPOINT_DESCRIPTOR FindInterruptEndpointDescriptor(UCHAR InterruptInterval);
VOID PrintEndpointList(POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor);
VOID LinkEndpoint(POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor, POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor);
// constructor / destructor
CUSBQueue(IUnknown *OuterUnknown){}
@ -56,6 +61,7 @@ protected:
PUSBHARDWAREDEVICE m_Hardware; // hardware
POHCI_ENDPOINT_DESCRIPTOR m_BulkHeadEndpointDescriptor; // bulk head descriptor
POHCI_ENDPOINT_DESCRIPTOR m_ControlHeadEndpointDescriptor; // control head descriptor
POHCI_ENDPOINT_DESCRIPTOR * m_InterruptEndpoints;
};
//=================================================================================================
@ -94,6 +100,11 @@ CUSBQueue::Initialize(
//
Hardware->GetControlHeadEndpointDescriptor(&m_ControlHeadEndpointDescriptor);
//
// get interrupt endpoints
//
Hardware->GetInterruptEndpointDescriptors(&m_InterruptEndpoints);
//
// initialize spinlock
//
@ -118,6 +129,32 @@ CUSBQueue::GetPendingRequestCount()
return 0;
}
VOID
CUSBQueue::LinkEndpoint(
POHCI_ENDPOINT_DESCRIPTOR HeadEndpointDescriptor,
POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor)
{
POHCI_ENDPOINT_DESCRIPTOR CurrentEndpointDescriptor = HeadEndpointDescriptor;
//
// get last descriptor in queue
//
while(CurrentEndpointDescriptor->NextDescriptor)
{
//
// move to last descriptor
//
CurrentEndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)CurrentEndpointDescriptor->NextDescriptor;
}
//
// link endpoints
//
CurrentEndpointDescriptor->NextPhysicalEndpoint = EndpointDescriptor->PhysicalAddress.LowPart;
CurrentEndpointDescriptor->NextDescriptor = EndpointDescriptor;
}
NTSTATUS
CUSBQueue::AddUSBRequest(
IUSBRequest * Request)
@ -146,10 +183,10 @@ CUSBQueue::AddUSBRequest(
switch(Type)
{
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
case USB_ENDPOINT_TYPE_INTERRUPT:
/* NOT IMPLEMENTED IN QUEUE */
Status = STATUS_NOT_SUPPORTED;
break;
case USB_ENDPOINT_TYPE_INTERRUPT:
case USB_ENDPOINT_TYPE_CONTROL:
case USB_ENDPOINT_TYPE_BULK:
Status = STATUS_SUCCESS;
@ -213,31 +250,35 @@ CUSBQueue::AddUSBRequest(
//
HeadDescriptor = m_ControlHeadEndpointDescriptor;
}
else if (Type == USB_ENDPOINT_TYPE_INTERRUPT)
{
//
// get head descriptor
//
HeadDescriptor = FindInterruptEndpointDescriptor(Request->GetInterval());
ASSERT(HeadDescriptor);
}
//
// link endpoints
// insert endpoint at end
//
Descriptor->NextPhysicalEndpoint = HeadDescriptor->NextPhysicalEndpoint;
Descriptor->NextDescriptor = HeadDescriptor->NextDescriptor;
HeadDescriptor->NextPhysicalEndpoint = Descriptor->PhysicalAddress.LowPart;
HeadDescriptor->NextDescriptor = Descriptor;
LinkEndpoint(HeadDescriptor, Descriptor);
//
// set descriptor active
//
Descriptor->Flags &= ~OHCI_ENDPOINT_SKIP;
//HeadDescriptor->Flags &= ~OHCI_ENDPOINT_SKIP;
DPRINT("Request %x Logical %x added to queue Queue %p Logical %x\n", Descriptor, Descriptor->PhysicalAddress.LowPart, HeadDescriptor, HeadDescriptor->PhysicalAddress.LowPart);
//
// notify hardware of our request
//
m_Hardware->HeadEndpointDescriptorModified(Type);
if (Type == USB_ENDPOINT_TYPE_CONTROL || Type == USB_ENDPOINT_TYPE_BULK)
{
//
// notify hardware of our request
//
m_Hardware->HeadEndpointDescriptorModified(Type);
}
return STATUS_SUCCESS;
}
@ -318,6 +359,35 @@ CUSBQueue::FindTransferDescriptorInEndpoint(
return STATUS_NOT_FOUND;
}
NTSTATUS
CUSBQueue::FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescriptorLogicalAddress, OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor)
{
ULONG Index;
NTSTATUS Status;
//
// search descriptor in endpoint list
//
for(Index = 0; Index < OHCI_STATIC_ENDPOINT_COUNT; Index++)
{
//
// is it in current endpoint
//
Status = FindTransferDescriptorInEndpoint(m_InterruptEndpoints[Index], TransferDescriptorLogicalAddress, OutEndpointDescriptor, OutPreviousEndpointDescriptor);
if (NT_SUCCESS(Status))
{
//
// found transfer descriptor
//
return STATUS_SUCCESS;
}
}
//
// not found
//
return STATUS_NOT_FOUND;
}
BOOLEAN
CUSBQueue::IsTransferDescriptorInEndpoint(
@ -399,7 +469,27 @@ CUSBQueue::CleanupEndpointDescriptor(
Request->Release();
}
VOID
CUSBQueue::PrintEndpointList(
POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor)
{
DPRINT1("CUSBQueue::PrintEndpointList HeadEndpoint %p Logical %x\n", EndpointDescriptor, EndpointDescriptor->PhysicalAddress.LowPart);
//
// get first general transfer descriptor
//
EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor;
while(EndpointDescriptor)
{
DPRINT1(" CUSBQueue::PrintEndpointList Endpoint %p Logical %x\n", EndpointDescriptor, EndpointDescriptor->PhysicalAddress.LowPart);
//
// move to next
//
EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor;
}
}
VOID
CUSBQueue::TransferDescriptorCompletionCallback(
@ -444,14 +534,73 @@ CUSBQueue::TransferDescriptorCompletionCallback(
return;
}
//
// find transfer descriptor in interrupt list
//
Status = FindTransferDescriptorInInterruptHeadEndpoints(TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor);
if (NT_SUCCESS(Status))
{
//
// cleanup endpoint
//
CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor);
//
// done
//
return;
}
//
// hardware reported dead endpoint completed
//
DPRINT1("CUSBQueue::TransferDescriptorCompletionCallback invalid transfer descriptor %x\n", TransferDescriptorLogicalAddress);
DPRINT("CUSBQueue::TransferDescriptorCompletionCallback invalid transfer descriptor %x\n", TransferDescriptorLogicalAddress);
ASSERT(FALSE);
}
POHCI_ENDPOINT_DESCRIPTOR
CUSBQueue::FindInterruptEndpointDescriptor(
UCHAR InterruptInterval)
{
ULONG Index = 0;
ULONG Power = 1;
//
// sanity check
//
ASSERT(InterruptInterval < OHCI_BIGGEST_INTERVAL);
//
// find interrupt index
//
while (Power <= OHCI_BIGGEST_INTERVAL / 2)
{
//
// is current interval greater
//
if (Power * 2 > InterruptInterval)
break;
//
// increment power
//
Power *= 2;
//
// move to next interrupt
//
Index++;
}
DPRINT("InterruptInterval %lu Selected InterruptIndex %lu Choosen Interval %lu\n", InterruptInterval, Index, Power);
//
// return endpoint
//
return m_InterruptEndpoints[Index];
}
NTSTATUS
CreateUSBQueue(

View file

@ -46,6 +46,8 @@ public:
virtual BOOLEAN IsQueueHeadComplete(struct _QUEUE_HEAD * QueueHead);
virtual VOID CompletionCallback(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor);
virtual VOID FreeEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor);
virtual UCHAR GetInterval();
// local functions
ULONG InternalGetTransferType();
@ -377,6 +379,18 @@ CUSBRequest::GetMaxPacketSize()
return m_EndpointDescriptor->wMaxPacketSize;
}
UCHAR
CUSBRequest::GetInterval()
{
ASSERT(m_EndpointDescriptor);
ASSERT((m_EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT);
//
// return interrupt interval
//
return m_EndpointDescriptor->bInterval;
}
UCHAR
CUSBRequest::GetEndpointAddress()
{
@ -786,7 +800,7 @@ CUSBRequest::BuildBulkInterruptEndpoint(
FirstDescriptor = CurrentDescriptor;
}
DPRINT("PreviousDescriptor %p CurrentDescriptor %p Buffer Logical %p Physical %x Last Physical %x CurrentSize %lu\n", PreviousDescriptor, CurrentDescriptor, CurrentDescriptor->BufferLogical, CurrentDescriptor->BufferPhysical, CurrentDescriptor->LastPhysicalByteAddress, CurrentSize);
DPRINT("PreviousDescriptor %p CurrentDescriptor %p Logical %x Buffer Logical %p Physical %x Last Physical %x CurrentSize %lu\n", PreviousDescriptor, CurrentDescriptor, CurrentDescriptor->PhysicalAddress.LowPart, CurrentDescriptor->BufferLogical, CurrentDescriptor->BufferPhysical, CurrentDescriptor->LastPhysicalByteAddress, CurrentSize);
//
// set previous descriptor
@ -847,7 +861,6 @@ CUSBRequest::BuildBulkInterruptEndpoint(
// done
//
return STATUS_SUCCESS;
}