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);
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
};
//=================================================================================================
NTSTATUS
CUSBHardwareDevice::StartController(void)
{
- ULONG Control, NumberOfPorts, Index, Descriptor, FrameInterval, Periodic, IntervalValue;
+ ULONG Control, NumberOfPorts, Index, Descriptor, FrameInterval, Periodic;
//
// first write address of HCCA
//
// 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);
//
return STATUS_SUCCESS;
}
+NTSTATUS
+CUSBHardwareDevice::GetInterruptEndpointDescriptors(
+ struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptor)
+{
+ *OutDescriptor = m_InterruptEndpoints;
+ return STATUS_SUCCESS;
+}
+
VOID
CUSBHardwareDevice::HeadEndpointDescriptorModified(
ULONG Type)
CUSBHardwareDevice::StopController(void)
{
ULONG Control, Reset;
- ULONG Index;
+ ULONG Index, FrameInterval;
//
// first turn off all interrupts
//
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
//
#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)
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);
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(
//
// 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);
//
//
// 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;
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;
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
// 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;
+
};
// 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){}
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;
};
//=================================================================================================
//
Hardware->GetControlHeadEndpointDescriptor(&m_ControlHeadEndpointDescriptor);
+ //
+ // get interrupt endpoints
+ //
+ Hardware->GetInterruptEndpointDescriptors(&m_InterruptEndpoints);
+
//
// initialize spinlock
//
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)
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;
//
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;
}
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(
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(
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(
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();
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()
{
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
// done
//
return STATUS_SUCCESS;
-
}