* Build the interrupt tree the same (imperfect) way as in EHCI to conserve

on resources but still satisfy the scheduling policy. This works well, but
  effectifly limits the bandwidth available as the interrupts aren't spread
  across different frames.
* Implement finding the interrupt endpoint head for a certain interval.
* Deactivate endpoints that are going away until removing them is implemented.
* Allow short packets for all data phases.
* Disable tracing by default.

There you go, interrupt transfers should work as well. Should enable mice,
keyboards, hubs and other devices that use interrupt pipes. Note that it's
still easily possible that a single failed request will hang an endpoint and
prevent a device from working.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25554 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2008-05-18 22:57:17 +00:00
parent 11a1e0e580
commit a5a728361c
2 changed files with 72 additions and 38 deletions

View File

@ -7,7 +7,7 @@
* Salvatore Benedetto <salvatore.benedetto@gmail.com>
* Michael Lotz <mmlr@mlotz.ch>
*/
#define TRACE_USB
#include <module.h>
#include <PCI.h>
#include <USB3.h>
@ -51,15 +51,6 @@ module_info *modules[] = {
};
// Reverse the bits in a value between 0 and 31 (Section 3.3.2)
static uint8 revbits[OHCI_NUMBER_OF_INTERRUPTS] = {
0x00, 0x10, 0x08, 0x18, 0x04, 0x14, 0x0c, 0x1c,
0x02, 0x12, 0x0a, 0x1a, 0x06, 0x16, 0x0e, 0x1e,
0x01, 0x11, 0x09, 0x19, 0x05, 0x15, 0x0d, 0x1d,
0x03, 0x13, 0x0b, 0x1b, 0x07, 0x17, 0x0f, 0x1f
};
OHCI::OHCI(pci_info *info, Stack *stack)
: BusManager(stack),
fPCIInfo(info),
@ -162,21 +153,19 @@ OHCI::OHCI(pci_info *info, Stack *stack)
// Static endpoints that get linked in the HCCA
fInterruptEndpoints = new(std::nothrow)
ohci_endpoint_descriptor *[OHCI_NUMBER_OF_INTERRUPTS];
ohci_endpoint_descriptor *[OHCI_STATIC_ENDPOINT_COUNT];
if (!fInterruptEndpoints) {
TRACE_ERROR(("ohci_usb: cannot allocate memory for"
" fInterruptEndpoints array\n"));
TRACE_ERROR(("ohci_usb: failed to allocate memory for interrupt endpoints\n"));
_FreeEndpoint(fDummyControl);
_FreeEndpoint(fDummyBulk);
_FreeEndpoint(fDummyIsochronous);
return;
}
for (int32 i = 0; i < OHCI_NUMBER_OF_INTERRUPTS; i++) {
for (int32 i = 0; i < OHCI_STATIC_ENDPOINT_COUNT; i++) {
fInterruptEndpoints[i] = _AllocateEndpoint();
if (!fInterruptEndpoints[i]) {
TRACE_ERROR(("ohci_usb: cannot allocate memory for"
" fInterruptEndpoints[%ld] endpoint\n", i));
TRACE_ERROR(("ohci_usb: failed to allocate interrupt endpoint %ld", i));
while (--i >= 0)
_FreeEndpoint(fInterruptEndpoints[i]);
_FreeEndpoint(fDummyBulk);
@ -184,15 +173,36 @@ OHCI::OHCI(pci_info *info, Stack *stack)
_FreeEndpoint(fDummyIsochronous);
return;
}
// Make them point all to the dummy isochronous endpoint
fInterruptEndpoints[i]->next_physical_endpoint
= fDummyIsochronous->physical_address;
}
// Fill HCCA interrupt table.
for (uint32 i = 0; i < OHCI_NUMBER_OF_INTERRUPTS; i++)
fHcca->interrupt_table[i] = fInterruptEndpoints[i]->physical_address;
// build flat tree so that at each of the static interrupt endpoints
// fInterruptEndpoints[i] == interrupt endpoint for interval 2^i
uint32 interval = OHCI_BIGGEST_INTERVAL;
uint32 intervalIndex = OHCI_STATIC_ENDPOINT_COUNT - 1;
while (interval > 1) {
uint32 insertIndex = interval / 2;
while (insertIndex < OHCI_BIGGEST_INTERVAL) {
fHcca->interrupt_table[insertIndex]
= fInterruptEndpoints[intervalIndex]->physical_address;
insertIndex += interval;
}
intervalIndex--;
interval /= 2;
}
// setup the empty slot in the list and linking of all -> first
fHcca->interrupt_table[0] = fInterruptEndpoints[0]->physical_address;
for (int32 i = 1; i < OHCI_STATIC_ENDPOINT_COUNT; i++) {
fInterruptEndpoints[i]->next_physical_endpoint
= fInterruptEndpoints[0]->physical_address;
fInterruptEndpoints[i]->next_logical_endpoint
= fInterruptEndpoints[0];
}
// Now link the first endpoint to the isochronous endpoint
fInterruptEndpoints[0]->next_physical_endpoint
= fDummyIsochronous->physical_address;
// Determine in what context we are running (Kindly copied from FreeBSD)
uint32 control = _ReadReg(OHCI_CONTROL);
@ -288,7 +298,10 @@ OHCI::OHCI(pci_info *info, Stack *stack)
uint32 descriptor = _ReadReg(OHCI_RH_DESCRIPTOR_A);
numberOfPorts = OHCI_RH_GET_PORT_COUNT(descriptor);
}
if (numberOfPorts > OHCI_MAX_PORT_COUNT)
numberOfPorts = OHCI_MAX_PORT_COUNT;
fPortCount = numberOfPorts;
TRACE(("usb_ohci: port count is %d\n", fPortCount));
// Create semaphore the finisher thread will wait for
fFinishTransfersSem = create_sem(0, "OHCI Finish Transfers");
@ -332,7 +345,7 @@ OHCI::~OHCI()
_FreeEndpoint(fDummyIsochronous);
if (fInterruptEndpoints != NULL) {
for (int i = 0; i < OHCI_NUMBER_OF_INTERRUPTS; i++)
for (int i = 0; i < OHCI_STATIC_ENDPOINT_COUNT; i++)
_FreeEndpoint(fInterruptEndpoints[i]);
}
@ -389,7 +402,8 @@ OHCI::SubmitTransfer(Transfer *transfer)
}
if ((type & USB_OBJECT_BULK_PIPE) || (type & USB_OBJECT_INTERRUPT_PIPE)) {
TRACE(("usb_ohci: submitting bulk/interrupt transfer\n"));
TRACE(("usb_ohci: submitting %s transfer\n",
(type & USB_OBJECT_BULK_PIPE) ? "bulk" : "interrupt"));
return _SubmitTransfer(transfer);
}
@ -457,25 +471,19 @@ OHCI::NotifyPipeChange(Pipe *pipe, usb_change change)
}
switch (change) {
case USB_CHANGE_CREATED: {
TRACE(("usb_ohci: inserting endpoint\n"));
case USB_CHANGE_CREATED:
return _InsertEndpointForPipe(pipe);
}
case USB_CHANGE_DESTROYED: {
TRACE(("usb_ohci: removing endpoint\n"));
case USB_CHANGE_DESTROYED:
return _RemoveEndpointForPipe(pipe);
}
case USB_CHANGE_PIPE_POLICY_CHANGED: {
case USB_CHANGE_PIPE_POLICY_CHANGED:
TRACE(("usb_ohci: pipe policy changing unhandled!\n"));
break;
}
default: {
default:
TRACE_ERROR(("usb_ohci: unknown pipe change!\n"));
return B_ERROR;
}
}
return B_OK;
@ -1369,14 +1377,34 @@ OHCI::_InsertEndpointForPipe(Pipe *pipe)
status_t
OHCI::_RemoveEndpointForPipe(Pipe *pipe)
{
return B_ERROR;
TRACE(("usb_ohci: removing endpoint for device %u endpoint %u\n",
pipe->DeviceAddress(), pipe->EndpointAddress()));
ohci_endpoint_descriptor *endpoint
= (ohci_endpoint_descriptor *)pipe->ControllerCookie();
if (endpoint == NULL)
return B_OK;
// TODO implement properly, but at least disable it for now
endpoint->flags |= OHCI_ENDPOINT_SKIP;
return B_OK;
}
ohci_endpoint_descriptor *
OHCI::_FindInterruptEndpoint(uint8 interval)
{
return NULL;
uint32 index = 0;
uint32 power = 1;
while (power <= OHCI_BIGGEST_INTERVAL / 2) {
if (power * 2 > interval)
break;
power *= 2;
index++;
}
return fInterruptEndpoints[index];
}
@ -1454,6 +1482,7 @@ OHCI::_CreateDescriptorChain(ohci_general_td **_firstDescriptor,
}
descriptor->flags = direction
| OHCI_TD_BUFFER_ROUNDING
| OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED)
| OHCI_TD_TOGGLE_CARRY
| OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE);

View File

@ -271,6 +271,8 @@
// --------------------------------
#define OHCI_NUMBER_OF_INTERRUPTS 32
#define OHCI_STATIC_ENDPOINT_COUNT 6
#define OHCI_BIGGEST_INTERVAL 32
typedef struct {
uint32 interrupt_table[OHCI_NUMBER_OF_INTERRUPTS];
@ -343,7 +345,7 @@ typedef struct {
void *next_logical_descriptor; // Logical pointer next descriptor
} ohci_general_td;
#define OHCI_BUFFER_ROUNDING 0x00040000
#define OHCI_TD_BUFFER_ROUNDING 0x00040000
#define OHCI_TD_DIRECTION_PID_MASK 0x00180000
#define OHCI_TD_DIRECTION_PID_SETUP 0x00000000
#define OHCI_TD_DIRECTION_PID_OUT 0x00080000
@ -442,4 +444,7 @@ typedef struct {
#define OHCI_ENABLE_POWER_DELAY 5000
#define OHCI_READ_DESC_DELAY 5000
// Maximum port count set by OHCI
#define OHCI_MAX_PORT_COUNT 15
#endif // OHCI_HARDWARE_H