From d2accc6c3ee3cb138081491dff58b292dbc2d080 Mon Sep 17 00:00:00 2001 From: Johannes Anderwald Date: Sun, 29 May 2011 19:54:55 +0000 Subject: [PATCH] [USBOHCI] - Fix OHCI_ISO_TD structure. Fixes host system crashes in Windows XP - Fix multiple bugs in isochronous transfer implementation - Still not yet working, as the interrupt completion is not fired yet svn path=/branches/usb-bringup/; revision=51998 --- drivers/usb/usbohci/hardware.h | 11 ++- drivers/usb/usbohci/usb_queue.cpp | 140 +++++++++++++++++++++++--- drivers/usb/usbohci/usb_request.cpp | 148 +++++++++++++++++++--------- 3 files changed, 240 insertions(+), 59 deletions(-) diff --git a/drivers/usb/usbohci/hardware.h b/drivers/usb/usbohci/hardware.h index cf4fb544f9d..69bcd1978cc 100644 --- a/drivers/usb/usbohci/hardware.h +++ b/drivers/usb/usbohci/hardware.h @@ -310,13 +310,22 @@ typedef struct _OHCI_ISO_TD_ ULONG BufferPhysical; // Physical page number of byte 0 ULONG NextPhysicalDescriptor; // Next isochronous transfer descriptor ULONG LastPhysicalByteAddress; // Physical buffer end - ULONG Offset[OHCI_ITD_NOFFSET]; // Buffer offsets + USHORT Offset[OHCI_ITD_NOFFSET]; // Buffer offsets // Software part PHYSICAL_ADDRESS PhysicalAddress; // Physical address of this descriptor struct _OHCI_ISO_TD_ * NextLogicalDescriptor; // Logical pointer next descriptor }OHCI_ISO_TD, *POHCI_ISO_TD; +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, Flags) == 0); +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, BufferPhysical) == 4); +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, NextPhysicalDescriptor) == 8); +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, LastPhysicalByteAddress) == 12); +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, Offset) == 16); +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, PhysicalAddress) == 32); +C_ASSERT(FIELD_OFFSET(OHCI_ISO_TD, NextLogicalDescriptor) == 40); +C_ASSERT(sizeof(OHCI_ISO_TD) == 48); + #define OHCI_ITD_GET_STARTING_FRAME(x) ((x) & 0x0000ffff) #define OHCI_ITD_SET_STARTING_FRAME(x) ((x) & 0xffff) #define OHCI_ITD_GET_DELAY_INTERRUPT(x) (((x) >> 21) & 7) diff --git a/drivers/usb/usbohci/usb_queue.cpp b/drivers/usb/usbohci/usb_queue.cpp index a9773506922..af0d8f8c887 100644 --- a/drivers/usb/usbohci/usb_queue.cpp +++ b/drivers/usb/usbohci/usb_queue.cpp @@ -43,8 +43,10 @@ public: // local functions BOOLEAN IsTransferDescriptorInEndpoint(IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN ULONG TransferDescriptorLogicalAddress); + BOOLEAN IsTransferDescriptorInIsoEndpoint(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); + NTSTATUS FindTransferDescriptorInIsochronousHeadEndpoints(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); @@ -101,6 +103,8 @@ CUSBQueue::Initialize( // Hardware->GetControlHeadEndpointDescriptor(&m_ControlHeadEndpointDescriptor); + // + // get isochronous endpoint // Hardware->GetIsochronousHeadEndpointDescriptor(&m_IsoHeadEndpointDescriptor); @@ -170,6 +174,7 @@ CUSBQueue::AddUSBRequest( POHCI_ENDPOINT_DESCRIPTOR Descriptor; POHCI_ISO_TD CurrentDescriptor; ULONG FrameNumber; + USHORT Frame; DPRINT("CUSBQueue::AddUSBRequest\n"); @@ -244,9 +249,9 @@ CUSBQueue::AddUSBRequest( m_Hardware->GetCurrentFrameNumber(&FrameNumber); // - // increment frame number + // FIXME: increment frame number // - FrameNumber++; + FrameNumber += 300; // // apply frame number to iso transfer descriptors @@ -254,18 +259,19 @@ CUSBQueue::AddUSBRequest( CurrentDescriptor = (POHCI_ISO_TD)Descriptor->HeadLogicalDescriptor; DPRINT1("ISO: NextFrameNumber %x\n", FrameNumber); + Frame = (FrameNumber & 0xFFFF); while(CurrentDescriptor) { // // set current frame number // - CurrentDescriptor->Flags |= OHCI_ITD_SET_STARTING_FRAME(FrameNumber); + CurrentDescriptor->Flags |= OHCI_ITD_SET_STARTING_FRAME(Frame); // // move to next frame number // - FrameNumber++; + Frame += OHCI_ITD_GET_FRAME_COUNT(CurrentDescriptor->Flags); // // move to next descriptor @@ -274,16 +280,16 @@ CUSBQueue::AddUSBRequest( } } - // - // insert endpoint at end - // - LinkEndpoint(HeadDescriptor, Descriptor); - // // set descriptor active // Descriptor->Flags &= ~OHCI_ENDPOINT_SKIP; + // + // insert endpoint at end + // + LinkEndpoint(HeadDescriptor, Descriptor); + if (Type == USB_ENDPOINT_TYPE_CONTROL || Type == USB_ENDPOINT_TYPE_BULK) { // @@ -402,6 +408,97 @@ CUSBQueue::FindTransferDescriptorInInterruptHeadEndpoints(IN ULONG TransferDescr return STATUS_NOT_FOUND; } +NTSTATUS +CUSBQueue::FindTransferDescriptorInIsochronousHeadEndpoints( + IN ULONG TransferDescriptorLogicalAddress, + OUT POHCI_ENDPOINT_DESCRIPTOR *OutEndpointDescriptor, + OUT POHCI_ENDPOINT_DESCRIPTOR *OutPreviousEndpointDescriptor) +{ + POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor; + POHCI_ENDPOINT_DESCRIPTOR LastDescriptor = m_IsoHeadEndpointDescriptor; + + + // + // skip first endpoint head + // + EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)m_IsoHeadEndpointDescriptor->NextDescriptor; + + while(EndpointDescriptor) + { + // + // check if the transfer descriptor is inside the list + // + if (IsTransferDescriptorInIsoEndpoint(EndpointDescriptor, TransferDescriptorLogicalAddress)) + { + // + // found endpoint + // + *OutEndpointDescriptor = EndpointDescriptor; + *OutPreviousEndpointDescriptor = LastDescriptor; + + // + // done + // + return STATUS_SUCCESS; + } + + // + // store last endpoint + // + LastDescriptor = EndpointDescriptor; + + // + // move to next + // + EndpointDescriptor = (POHCI_ENDPOINT_DESCRIPTOR)EndpointDescriptor->NextDescriptor; + } + + // + // failed to endpoint + // + return STATUS_NOT_FOUND; +} + +BOOLEAN +CUSBQueue::IsTransferDescriptorInIsoEndpoint( + IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, + IN ULONG TransferDescriptorLogicalAddress) +{ + POHCI_ISO_TD Descriptor; + + // + // get first general transfer descriptor + // + Descriptor = (POHCI_ISO_TD)EndpointDescriptor->HeadLogicalDescriptor; + + // + // sanity check + // + ASSERT(Descriptor); + + do + { + if (Descriptor->PhysicalAddress.LowPart == TransferDescriptorLogicalAddress) + { + // + // found descriptor + // + return TRUE; + } + + // + // move to next + // + Descriptor = (POHCI_ISO_TD)Descriptor->NextLogicalDescriptor; + }while(Descriptor); + + // + // no descriptor found + // + return FALSE; +} + + BOOLEAN CUSBQueue::IsTransferDescriptorInEndpoint( IN POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, @@ -476,6 +573,8 @@ CUSBQueue::CleanupEndpointDescriptor( // //ASSERT(Request->IsRequestComplete()); + Request->FreeEndpointDescriptor(EndpointDescriptor); + // // release request // @@ -511,7 +610,7 @@ CUSBQueue::TransferDescriptorCompletionCallback( POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor, PreviousEndpointDescriptor; NTSTATUS Status; - DPRINT("CUSBQueue::TransferDescriptorCompletionCallback transfer descriptor %x\n", TransferDescriptorLogicalAddress); + DPRINT1("CUSBQueue::TransferDescriptorCompletionCallback transfer descriptor %x\n", TransferDescriptorLogicalAddress); // // find transfer descriptor in control list @@ -564,13 +663,30 @@ CUSBQueue::TransferDescriptorCompletionCallback( return; } + // + // last try: find the descriptor in isochronous list + // + Status = FindTransferDescriptorInIsochronousHeadEndpoints(TransferDescriptorLogicalAddress, &EndpointDescriptor, &PreviousEndpointDescriptor); + if (NT_SUCCESS(Status)) + { + // + // cleanup endpoint + // + DPRINT1("ISO endpoint complete\n"); +ASSERT(FALSE); + CleanupEndpointDescriptor(EndpointDescriptor, PreviousEndpointDescriptor); + + // + // done + // + return; + } // // hardware reported dead endpoint completed // - DPRINT("CUSBQueue::TransferDescriptorCompletionCallback invalid transfer descriptor %x\n", TransferDescriptorLogicalAddress); + DPRINT1("CUSBQueue::TransferDescriptorCompletionCallback invalid transfer descriptor %x\n", TransferDescriptorLogicalAddress); ASSERT(FALSE); - } POHCI_ENDPOINT_DESCRIPTOR diff --git a/drivers/usb/usbohci/usb_request.cpp b/drivers/usb/usbohci/usb_request.cpp index dcb9c19388e..77429f65679 100644 --- a/drivers/usb/usbohci/usb_request.cpp +++ b/drivers/usb/usbohci/usb_request.cpp @@ -299,7 +299,7 @@ CUSBRequest::InitializeWithIrp( m_TransferBufferMDL = Urb->UrbIsochronousTransfer.TransferBufferMDL; } } - + // // save buffer length // @@ -656,13 +656,12 @@ CUSBRequest::BuildIsochronousEndpoint( { POHCI_ISO_TD FirstDescriptor, PreviousDescriptor = NULL, CurrentDescriptor; POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor; - ULONG Index = 0, SubIndex, NumberOfPackets, PageOffset; + ULONG Index = 0, SubIndex, NumberOfPackets, PageOffset, Page; NTSTATUS Status; PVOID Buffer; PIO_STACK_LOCATION IoStack; PURB Urb; - DPRINT1("cp\n"); // // get current irp stack location // @@ -690,23 +689,27 @@ CUSBRequest::BuildIsochronousEndpoint( // // failed to create setup descriptor // + ASSERT(FALSE); return Status; } - DPRINT1("cp\n"); + // // get buffer // Buffer = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority); ASSERT(Buffer); - DPRINT1("cp\n"); + // + // FIXME: support requests which spans serveral pages + // + ASSERT(ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(m_TransferBufferMDL), MmGetMdlByteCount(m_TransferBufferMDL)) <= 2); + while(Index < Urb->UrbIsochronousTransfer.NumberOfPackets) { // // get number of packets remaining // NumberOfPackets = min(Urb->UrbIsochronousTransfer.NumberOfPackets - Index, OHCI_ITD_NOFFSET); - DPRINT1("cp Number Packets %lu\n", NumberOfPackets); // // allocate iso descriptor // @@ -720,23 +723,29 @@ CUSBRequest::BuildIsochronousEndpoint( ASSERT(FALSE); return Status; } - DPRINT1("cp\n"); + // - // initialize descriptor + // get physical page // - CurrentDescriptor->BufferPhysical = (MmGetPhysicalAddress(Buffer).LowPart & ~ (PAGE_SIZE - 1)); + Page = MmGetPhysicalAddress(Buffer).LowPart; // // get page offset // - PageOffset = BYTE_OFFSET(MmGetPhysicalAddress(Buffer).LowPart); - DPRINT1("cp\n"); + PageOffset = MmGetMdlByteOffset(m_TransferBufferMDL); + + // + // initialize descriptor + // + CurrentDescriptor->BufferPhysical = Page - PageOffset; + for(SubIndex = 0; SubIndex < NumberOfPackets; SubIndex++) { // // store buffer offset // - CurrentDescriptor->Offset[SubIndex] = Urb->UrbIsochronousTransfer.IsoPacket[Index].Offset + PageOffset; + CurrentDescriptor->Offset[SubIndex] = Urb->UrbIsochronousTransfer.IsoPacket[Index+SubIndex].Offset + PageOffset; + DPRINT1("Index %lu PacketOffset %lu FinalOffset %lu\n", SubIndex+Index, Urb->UrbIsochronousTransfer.IsoPacket[Index+SubIndex].Offset, CurrentDescriptor->Offset[SubIndex]); } // @@ -752,19 +761,14 @@ CUSBRequest::BuildIsochronousEndpoint( // // end of transfer // - CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + m_TransferBufferLength - 1; + CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + PageOffset + m_TransferBufferLength - 1; } else { // // use start address of next packet - 1 // - CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + PageOffset + Urb->UrbIsochronousTransfer.IsoPacket[Index + 1].Offset - 1; - - // - // move buffer to next address - // - Buffer = (PVOID)((ULONG_PTR)Buffer + Urb->UrbIsochronousTransfer.IsoPacket[Index + 1].Offset); + CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + PageOffset + Urb->UrbIsochronousTransfer.IsoPacket[Index].Offset - 1; } // @@ -790,8 +794,13 @@ CUSBRequest::BuildIsochronousEndpoint( // store as previous descriptor // PreviousDescriptor = CurrentDescriptor; + DPRINT1("Current Descriptor %p Logical %lx StartAddress %x EndAddress %x\n", CurrentDescriptor, CurrentDescriptor->PhysicalAddress.LowPart, CurrentDescriptor->BufferPhysical, CurrentDescriptor->LastPhysicalByteAddress); + + // + // fire interrupt as soon transfer is finished + // + CurrentDescriptor->Flags |= OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE); } - DPRINT1("cp\n"); // // clear interrupt mask for last transfer descriptor @@ -814,7 +823,7 @@ CUSBRequest::BuildIsochronousEndpoint( EndpointDescriptor->HeadPhysicalDescriptor = FirstDescriptor->PhysicalAddress.LowPart; EndpointDescriptor->TailPhysicalDescriptor = CurrentDescriptor->PhysicalAddress.LowPart; EndpointDescriptor->HeadLogicalDescriptor = FirstDescriptor; - DPRINT1("cp\n"); + // // store result // @@ -1451,48 +1460,95 @@ CUSBRequest::FreeEndpointDescriptor( struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor) { POHCI_GENERAL_TD TransferDescriptor, NextTransferDescriptor; + POHCI_ISO_TD IsoTransferDescriptor, IsoNextTransferDescriptor; + ULONG Index, PacketCount; DPRINT("CUSBRequest::FreeEndpointDescriptor EndpointDescriptor %p Logical %x\n", OutDescriptor, OutDescriptor->PhysicalAddress.LowPart); - // - // get first general transfer descriptor - // - TransferDescriptor = (POHCI_GENERAL_TD)OutDescriptor->HeadLogicalDescriptor; - - // - // release endpoint descriptor - // - m_DmaManager->Release(OutDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR)); - - while(TransferDescriptor) + if (OutDescriptor->Flags & OHCI_ENDPOINT_ISOCHRONOUS_FORMAT) { // - // get next + // get first iso transfer descriptor // - NextTransferDescriptor = (POHCI_GENERAL_TD)TransferDescriptor->NextLogicalDescriptor; + IsoTransferDescriptor = (POHCI_ISO_TD)OutDescriptor->HeadLogicalDescriptor; // - // is there a buffer associated + // release endpoint descriptor // - if (TransferDescriptor->BufferSize) + m_DmaManager->Release(OutDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR)); + + while(IsoTransferDescriptor) { // - // release buffer + // get next // - m_DmaManager->Release(TransferDescriptor->BufferLogical, TransferDescriptor->BufferSize); + IsoNextTransferDescriptor = IsoTransferDescriptor->NextLogicalDescriptor; + + // + // get packet count + // + PacketCount = OHCI_ITD_GET_FRAME_COUNT(IsoTransferDescriptor->Flags); + + DPRINT1("CUSBRequest::FreeEndpointDescriptor Descriptor %p Logical %x Buffer Physical %x EndAddress %x PacketCount %lu\n", IsoTransferDescriptor, IsoTransferDescriptor->PhysicalAddress.LowPart, IsoTransferDescriptor->BufferPhysical, IsoTransferDescriptor->LastPhysicalByteAddress, PacketCount); + + for(Index = 0; Index < PacketCount; Index++) + { + DPRINT1("PSW Index %lu Value %x\n", Index, IsoTransferDescriptor->Offset[Index]); + } + + // + // release descriptor + // + m_DmaManager->Release(IsoTransferDescriptor, sizeof(OHCI_ISO_TD)); + + // + // move to next + // + IsoTransferDescriptor = IsoNextTransferDescriptor; } - - DPRINT("CUSBRequest::FreeEndpointDescriptor Descriptor %p Logical %x Buffer Physical %x EndAddress %x\n", TransferDescriptor, TransferDescriptor->PhysicalAddress.LowPart, TransferDescriptor->BufferPhysical, TransferDescriptor->LastPhysicalByteAddress); + } + else + { + // + // get first general transfer descriptor + // + TransferDescriptor = (POHCI_GENERAL_TD)OutDescriptor->HeadLogicalDescriptor; // - // release descriptor + // release endpoint descriptor // - m_DmaManager->Release(TransferDescriptor, sizeof(OHCI_GENERAL_TD)); + m_DmaManager->Release(OutDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR)); - // - // move to next - // - TransferDescriptor = NextTransferDescriptor; + while(TransferDescriptor) + { + // + // get next + // + NextTransferDescriptor = (POHCI_GENERAL_TD)TransferDescriptor->NextLogicalDescriptor; + + // + // is there a buffer associated + // + if (TransferDescriptor->BufferSize) + { + // + // release buffer + // + m_DmaManager->Release(TransferDescriptor->BufferLogical, TransferDescriptor->BufferSize); + } + + DPRINT("CUSBRequest::FreeEndpointDescriptor Descriptor %p Logical %x Buffer Physical %x EndAddress %x\n", TransferDescriptor, TransferDescriptor->PhysicalAddress.LowPart, TransferDescriptor->BufferPhysical, TransferDescriptor->LastPhysicalByteAddress); + + // + // release descriptor + // + m_DmaManager->Release(TransferDescriptor, sizeof(OHCI_GENERAL_TD)); + + // + // move to next + // + TransferDescriptor = NextTransferDescriptor; + } } }