* Implemented transfer finishing in EHCI

* Implemented actual length calculation in EHCI
* Implemented bulk transfers in EHCI
* Implemented queue freeing in EHCI
* Added extended buffer pointers to the EHCI structs, necessary when the controller supports 64bit
* Fixed debug output and removed warnings due to wrong print format

Control and bulk transfers are now working nicely for EHCI. This means that I can now get pictures from my camera with USB 2.0 speed. Note though that many things still are unimplemented (isochronous transfers, split transactions, interrupt transfers, queue chaches per pipe, ...). But it's certainly a nice step forward :-).

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18855 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2006-09-15 19:44:31 +00:00
parent 8c0342c639
commit dff0c634db
4 changed files with 163 additions and 45 deletions

View File

@ -155,7 +155,11 @@ Hub::Explore()
if (result < B_OK)
continue;
TRACE(("USB Hub (%d): port %d: status: 0x%04x; change: 0x%04x\n", DeviceAddress(), i, fPortStatus[i].status, fPortStatus[i].change));
#ifdef TRACE_USB
if (fPortStatus[i].change)
TRACE(("USB Hub: port %d: status: 0x%04x; change: 0x%04x\n", i, fPortStatus[i].status, fPortStatus[i].change));
#endif
if (fPortStatus[i].change & PORT_STATUS_CONNECTION) {
if (fPortStatus[i].status & PORT_STATUS_CONNECTION) {
// new device attached!

View File

@ -60,7 +60,7 @@ void
print_descriptor_chain(ehci_qtd *descriptor)
{
while (descriptor) {
dprintf(" %08x n%08x a%08x t%08x %08x %08x %08x %08x %08x s%d\n",
dprintf(" %08lx n%08lx a%08lx t%08lx %08lx %08lx %08lx %08lx %08lx s%ld\n",
descriptor->this_phy, descriptor->next_phy,
descriptor->alt_next_phy, descriptor->token,
descriptor->buffer_phy[0], descriptor->buffer_phy[1],
@ -77,10 +77,10 @@ print_descriptor_chain(ehci_qtd *descriptor)
void
print_queue(ehci_qh *queueHead)
{
dprintf("queue: t%08x n%08x ch%08x ca%08x cu%08x\n",
dprintf("queue: t%08lx n%08lx ch%08lx ca%08lx cu%08lx\n",
queueHead->this_phy, queueHead->next_phy, queueHead->endpoint_chars,
queueHead->endpoint_caps, queueHead->current_qtd_phy);
dprintf("overlay: n%08x a%08x t%08x %08x %08x %08x %08x %08x\n",
dprintf("overlay: n%08lx a%08lx t%08lx %08lx %08lx %08lx %08lx %08lx\n",
queueHead->overlay.next_phy, queueHead->overlay.alt_next_phy,
queueHead->overlay.token, queueHead->overlay.buffer_phy[0],
queueHead->overlay.buffer_phy[1], queueHead->overlay.buffer_phy[2],
@ -136,7 +136,7 @@ EHCI::EHCI(pci_info *info, Stack *stack)
size_t mapSize = (fPCIInfo->u.h0.base_register_sizes[0] + offset
+ B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
TRACE(("usb_ehci: map physical memory 0x%08x (base: 0x%08x; offset: %x); size: %d -> %d\n", fPCIInfo->u.h0.base_registers[0], physicalAddress, offset, fPCIInfo->u.h0.base_register_sizes[0], mapSize));
TRACE(("usb_ehci: map physical memory 0x%08lx (base: 0x%08lx; offset: %lx); size: %ld\n", fPCIInfo->u.h0.base_registers[0], physicalAddress, offset, fPCIInfo->u.h0.base_register_sizes[0]));
fRegisterArea = map_physical_memory("EHCI memory mapped registers",
(void *)physicalAddress, mapSize, B_ANY_KERNEL_BLOCK_ADDRESS,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA,
@ -148,8 +148,11 @@ EHCI::EHCI(pci_info *info, Stack *stack)
fCapabilityRegisters += offset;
fOperationalRegisters = fCapabilityRegisters + ReadCapReg8(EHCI_CAPLENGTH);
TRACE(("usb_ehci: mapped capability registers: 0x%08x\n", fCapabilityRegisters));
TRACE(("usb_ehci: mapped operational registers: 0x%08x\n", fOperationalRegisters));
TRACE(("usb_ehci: mapped capability registers: 0x%08lx\n", (uint32)fCapabilityRegisters));
TRACE(("usb_ehci: mapped operational registers: 0x%08lx\n", (uint32)fOperationalRegisters));
TRACE(("usb_ehci: structural parameters: 0x%08lx\n", ReadCapReg32(EHCI_HCSPARAMS)));
TRACE(("usb_ehci: capability parameters: 0x%08lx\n", ReadCapReg32(EHCI_HCCPARAMS)));
// read port count from capability register
fPortCount = ReadCapReg32(EHCI_HCSPARAMS) & 0x0f;
@ -157,7 +160,7 @@ EHCI::EHCI(pci_info *info, Stack *stack)
uint32 extendedCapPointer = ReadCapReg32(EHCI_HCCPARAMS) >> EHCI_ECP_SHIFT;
extendedCapPointer &= EHCI_ECP_MASK;
if (extendedCapPointer > 0) {
TRACE(("usb_ehci: extended capabilities register at %d\n", extendedCapPointer));
TRACE(("usb_ehci: extended capabilities register at %ld\n", extendedCapPointer));
uint32 legacySupport = sPCIModule->read_pci_config(fPCIInfo->bus,
fPCIInfo->device, fPCIInfo->function, extendedCapPointer, 4);
@ -215,7 +218,7 @@ EHCI::EHCI(pci_info *info, Stack *stack)
install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line,
InterruptHandler, (void *)this, 0);
WriteOpReg(EHCI_USBINTR, EHCI_USBINTR_HOSTSYSERR
| EHCI_USBINTR_USBERRINT | EHCI_USBINTR_USBINT);
| EHCI_USBINTR_USBERRINT | EHCI_USBINTR_USBINT | EHCI_USBINTR_INTONAA);
// allocate the periodic frame list
fPeriodicFrameListArea = fStack->AllocateArea((void **)&fPeriodicFrameList,
@ -248,6 +251,7 @@ EHCI::EHCI(pci_info *info, Stack *stack)
WriteOpReg(EHCI_ASYNCLISTADDR, (uint32)fAsyncQueueHead->this_phy
| EHCI_QH_TYPE_QH);
TRACE(("usb_ehci: set the async list addr to 0x%08lx\n", ReadOpReg(EHCI_ASYNCLISTADDR)));
fInitOK = true;
TRACE(("usb_ehci: EHCI Host Controller Driver constructed\n"));
@ -277,7 +281,7 @@ status_t
EHCI::Start()
{
TRACE(("usb_ehci: starting EHCI Host Controller\n"));
TRACE(("usb_ehci: usbcmd: 0x%08x; usbsts: 0x%08x\n", ReadOpReg(EHCI_USBCMD), ReadOpReg(EHCI_USBSTS)));
TRACE(("usb_ehci: usbcmd: 0x%08lx; usbsts: 0x%08lx\n", ReadOpReg(EHCI_USBCMD), ReadOpReg(EHCI_USBSTS)));
uint32 frameListSize = (ReadOpReg(EHCI_USBCMD) >> EHCI_USBCMD_FLS_SHIFT)
& EHCI_USBCMD_FLS_MASK;
@ -292,7 +296,7 @@ EHCI::Start()
bool running = false;
for (int32 i = 0; i < 10; i++) {
uint32 status = ReadOpReg(EHCI_USBSTS);
TRACE(("usb_ehci: try %ld: status 0x%08x\n", i, status));
TRACE(("usb_ehci: try %ld: status 0x%08lx\n", i, status));
if (status & EHCI_USBSTS_HCHALTED) {
snooze(10000);
@ -377,14 +381,15 @@ EHCI::SubmitAsyncTransfer(Transfer *transfer)
}
if (pipe->Type() & USB_OBJECT_CONTROL_PIPE) {
queueHead->endpoint_chars |= EHCI_QH_CHARS_TOGGLE
| (pipe->Speed() != USB_SPEED_HIGHSPEED ? EHCI_QH_CHARS_CONTROL : 0);
queueHead->endpoint_chars |=
(pipe->Speed() != USB_SPEED_HIGHSPEED ? EHCI_QH_CHARS_CONTROL : 0);
}
queueHead->endpoint_chars |= (3 << EHCI_QH_CHARS_RL_SHIFT)
| (pipe->MaxPacketSize() << EHCI_QH_CHARS_MPL_SHIFT)
| (pipe->EndpointAddress() << EHCI_QH_CHARS_EPT_SHIFT)
| (pipe->DeviceAddress() << EHCI_QH_CHARS_DEV_SHIFT);
| (pipe->DeviceAddress() << EHCI_QH_CHARS_DEV_SHIFT)
| EHCI_QH_CHARS_TOGGLE;
queueHead->endpoint_caps = (1 << EHCI_QH_CAPS_MULT_SHIFT)
| (0x1c << EHCI_QH_CAPS_SCM_SHIFT);
@ -750,15 +755,12 @@ EHCI::Interrupt()
if (status & EHCI_USBSTS_INTONAA) {
TRACE(("usb_ehci: interrupt on async advance\n"));
acknowledge |= EHCI_USBSTS_INTONAA;
fAsyncAdvance = true;
}
if (status & EHCI_USBSTS_HOSTSYSERR) {
TRACE_ERROR(("usb_ehci: host system error!\n"));
acknowledge |= EHCI_USBSTS_HOSTSYSERR;
print_queue(fAsyncQueueHead);
if (fAsyncQueueHead->next_log) {
print_queue((ehci_qh *)fAsyncQueueHead->next_log);
}
}
if (acknowledge)
@ -884,10 +886,73 @@ EHCI::FinishTransfers()
transfer_data *transfer = fFirstTransfer;
Unlock();
ehci_qh *freeListHead = NULL;
while (transfer) {
bool transferDone = false;
ehci_qtd *descriptor = (ehci_qtd *)transfer->queue_head->element_log;
// ToDo...
#ifdef TRACE_USB
print_queue(transfer->queue_head);
#endif
while (descriptor) {
uint32 status = descriptor->token;
if (status & EHCI_QTD_STATUS_ACTIVE) {
// still in progress
TRACE(("usb_ehci: qtd (0x%08lx) still active\n", descriptor->this_phy));
break;
}
if (status & EHCI_QTD_STATUS_ERRMASK) {
// a transfer error occured
TRACE_ERROR(("usb_ehci: qtd (0x%08lx) error: 0x%08lx\n", descriptor->this_phy, status));
uint32 callbackStatus = 0;
if (status & EHCI_QTD_STATUS_HALTED)
callbackStatus |= B_USB_STATUS_DEVICE_STALLED;
if (status & EHCI_QTD_STATUS_BUFFER)
callbackStatus |= B_USB_STATUS_DEVICE_TIMEOUT;
if (status & EHCI_QTD_STATUS_BABBLE)
callbackStatus |= B_USB_STATUS_ADAPTER_HARDWARE_ERROR;
if (status & EHCI_QTD_STATUS_TERROR)
callbackStatus |= B_USB_STATUS_DRIVER_INTERNAL_ERROR;
// ToDo: define better error values!
UnlinkQueueHead(transfer->queue_head, &freeListHead);
transfer->transfer->Finished(callbackStatus, 0);
transferDone = true;
break;
}
if (descriptor->next_phy & EHCI_QTD_TERMINATE) {
// we arrived at the last (stray) descriptor, we're done
TRACE(("usb_ehci: qtd (0x%08lx) done\n", descriptor->this_phy));
size_t actualLength = 0;
uint8 lastDataToggle = 0;
if (transfer->data_descriptor && transfer->incoming) {
// data to read out
actualLength = ReadDescriptorChain(
transfer->data_descriptor,
transfer->transfer->Vector(),
transfer->transfer->VectorCount(),
&lastDataToggle);
} else {
// calculate transfered length
actualLength = ReadActualLength(
(ehci_qtd *)transfer->queue_head->element_log,
&lastDataToggle);
}
UnlinkQueueHead(transfer->queue_head, &freeListHead);
transfer->transfer->TransferPipe()->SetDataToggle(lastDataToggle);
transfer->transfer->Finished(B_USB_STATUS_SUCCESS, actualLength);
transferDone = true;
break;
}
descriptor = (ehci_qtd *)descriptor->next_log;
}
if (transferDone) {
if (Lock()) {
@ -910,6 +975,20 @@ EHCI::FinishTransfers()
transfer = transfer->link;
}
}
if (freeListHead) {
fAsyncAdvance = false;
// set the doorbell and wait for the host controller to notify us
WriteOpReg(EHCI_USBCMD, ReadOpReg(EHCI_USBCMD) | EHCI_USBCMD_INTONAAD);
while (!fAsyncAdvance)
snooze(10);
while (freeListHead) {
ehci_qh *next = (ehci_qh *)freeListHead->next_log;
FreeQueueHead(freeListHead);
freeListHead = next;
}
}
}
}
@ -944,11 +1023,11 @@ EHCI::CreateQueueHead()
result->overlay.next_phy = descriptor->this_phy;
result->overlay.alt_next_phy = EHCI_QTD_TERMINATE;
result->overlay.token = 0;
result->overlay.buffer_phy[0] = 0;
result->overlay.buffer_phy[1] = 0;
result->overlay.buffer_phy[2] = 0;
result->overlay.buffer_phy[3] = 0;
result->overlay.buffer_phy[4] = 0;
for (int32 i = 0; i < 5; i++) {
result->overlay.buffer_phy[i] = 0;
result->overlay.ext_buffer_phy[i] = 0;
}
return result;
}
@ -959,9 +1038,8 @@ EHCI::FreeQueueHead(ehci_qh *queueHead)
if (!queueHead)
return;
FreeDescriptor((ehci_qtd *)queueHead->stray_log);
FreeDescriptorChain((ehci_qtd *)queueHead->element_log);
fStack->FreeChunk(queueHead, (void *)queueHead->this_phy, sizeof(ehci_qh));
}
@ -985,7 +1063,7 @@ EHCI::LinkQueueHead(ehci_qh *queueHead)
status_t
EHCI::UnlinkQueueHead(ehci_qh *queueHead)
EHCI::UnlinkQueueHead(ehci_qh *queueHead, ehci_qh **freeListHead)
{
if (!Lock())
return B_ERROR;
@ -999,6 +1077,9 @@ EHCI::UnlinkQueueHead(ehci_qh *queueHead)
queueHead->next_log = NULL;
queueHead->prev_log = NULL;
queueHead->next_log = *freeListHead;
*freeListHead = queueHead;
Unlock();
return B_OK;
}
@ -1070,9 +1151,34 @@ EHCI::FillQueueWithRequest(Transfer *transfer, ehci_qh *queueHead,
status_t
EHCI::FillQueueWithData(Transfer *transfer, ehci_qh *queueHead,
ehci_qtd **dataDescriptor, bool *directionIn)
ehci_qtd **_dataDescriptor, bool *_directionIn)
{
return B_ERROR;
Pipe *pipe = transfer->TransferPipe();
bool directionIn = (pipe->Direction() == Pipe::In);
ehci_qtd *firstDescriptor = NULL;
ehci_qtd *lastDescriptor = NULL;
ehci_qtd *strayDescriptor = (ehci_qtd *)queueHead->stray_log;
status_t result = CreateDescriptorChain(pipe, &firstDescriptor,
&lastDescriptor, strayDescriptor, transfer->VectorLength(),
directionIn ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT);
if (result < B_OK)
return result;
lastDescriptor->token |= EHCI_QTD_IOC;
if (!directionIn) {
WriteDescriptorChain(firstDescriptor, transfer->Vector(),
transfer->VectorCount());
}
queueHead->element_log = firstDescriptor;
queueHead->overlay.next_phy = firstDescriptor->this_phy;
queueHead->overlay.alt_next_phy = EHCI_QTD_TERMINATE;
*_dataDescriptor = firstDescriptor;
*_directionIn = directionIn;
return B_OK;
}
@ -1099,11 +1205,11 @@ EHCI::CreateDescriptor(size_t bufferSize, uint8 pid)
result->token |= EHCI_QTD_STATUS_ACTIVE;
if (bufferSize == 0) {
result->buffer_log = NULL;
result->buffer_phy[0] = 0;
result->buffer_phy[1] = 0;
result->buffer_phy[2] = 0;
result->buffer_phy[3] = 0;
result->buffer_phy[4] = 0;
for (int32 i = 0; i < 5; i++) {
result->buffer_phy[i] = 0;
result->ext_buffer_phy[i] = 0;
}
return result;
}
@ -1116,9 +1222,11 @@ EHCI::CreateDescriptor(size_t bufferSize, uint8 pid)
addr_t physicalBase = (addr_t)physicalAddress;
result->buffer_phy[0] = physicalBase;
result->ext_buffer_phy[0] = 0;
for (int32 i = 1; i < 5; i++) {
physicalBase += B_PAGE_SIZE;
result->buffer_phy[i] = physicalBase & EHCI_QTD_PAGE_MASK;
result->ext_buffer_phy[i] = 0;
}
return result;
@ -1236,7 +1344,7 @@ EHCI::WriteDescriptorChain(ehci_qtd *topDescriptor, iovec *vector,
if (vectorOffset >= vector[vectorIndex].iov_len) {
if (++vectorIndex >= vectorCount) {
TRACE(("usb_ehci: wrote descriptor chain (%d bytes, no more vectors)\n", actualLength));
TRACE(("usb_ehci: wrote descriptor chain (%ld bytes, no more vectors)\n", actualLength));
return actualLength;
}
@ -1255,7 +1363,7 @@ EHCI::WriteDescriptorChain(ehci_qtd *topDescriptor, iovec *vector,
current = (ehci_qtd *)current->next_log;
}
TRACE(("usb_ehci: wrote descriptor chain (%d bytes)\n", actualLength));
TRACE(("usb_ehci: wrote descriptor chain (%ld bytes)\n", actualLength));
return actualLength;
}
@ -1271,12 +1379,13 @@ EHCI::ReadDescriptorChain(ehci_qtd *topDescriptor, iovec *vector,
size_t vectorOffset = 0;
size_t bufferOffset = 0;
while (current && (current->token & EHCI_QTD_STATUS_ACTIVE) > 0) {
while (current && (current->token & EHCI_QTD_STATUS_ACTIVE) == 0) {
if (!current->buffer_log)
break;
dataToggle = current->token & EHCI_QTD_DATA_TOGGLE;
size_t bufferSize = 0; // ToDo
size_t bufferSize = current->buffer_size;
bufferSize -= (current->token >> EHCI_QTD_BYTES_SHIFT) & EHCI_QTD_BYTES_MASK;
while (true) {
size_t length = min_c(bufferSize - bufferOffset,
@ -1291,7 +1400,7 @@ EHCI::ReadDescriptorChain(ehci_qtd *topDescriptor, iovec *vector,
if (vectorOffset >= vector[vectorIndex].iov_len) {
if (++vectorIndex >= vectorCount) {
TRACE(("usb_ehci: read descriptor chain (%d bytes, no more vectors)\n", actualLength));
TRACE(("usb_ehci: read descriptor chain (%ld bytes, no more vectors)\n", actualLength));
if (lastDataToggle)
*lastDataToggle = dataToggle > 0 ? 1 : 0;
return actualLength;
@ -1312,7 +1421,7 @@ EHCI::ReadDescriptorChain(ehci_qtd *topDescriptor, iovec *vector,
current = (ehci_qtd *)current->next_log;
}
TRACE(("usb_ehci: read descriptor chain (%d bytes)\n", actualLength));
TRACE(("usb_ehci: read descriptor chain (%ld bytes)\n", actualLength));
if (lastDataToggle)
*lastDataToggle = dataToggle > 0 ? 1 : 0;
return actualLength;
@ -1326,9 +1435,10 @@ EHCI::ReadActualLength(ehci_qtd *topDescriptor, uint8 *lastDataToggle)
ehci_qtd *current = topDescriptor;
uint32 dataToggle = 0;
while (current && (current->token & EHCI_QTD_STATUS_ACTIVE) > 0) {
while (current && (current->token & EHCI_QTD_STATUS_ACTIVE) == 0) {
dataToggle = current->token & EHCI_QTD_DATA_TOGGLE;
size_t length = 0; // ToDo
size_t length = current->buffer_size;
length -= (current->token >> EHCI_QTD_BYTES_SHIFT) & EHCI_QTD_BYTES_MASK;
actualLength += length;
if (current->next_phy & EHCI_QTD_TERMINATE)
@ -1337,7 +1447,7 @@ EHCI::ReadActualLength(ehci_qtd *topDescriptor, uint8 *lastDataToggle)
current = (ehci_qtd *)current->next_log;
}
TRACE(("usb_ehci: read actual length (%d bytes)\n", actualLength));
TRACE(("usb_ehci: read actual length (%ld bytes)\n", actualLength));
if (lastDataToggle)
*lastDataToggle = dataToggle > 0 ? 1 : 0;
return actualLength;

View File

@ -9,7 +9,6 @@
#ifndef EHCI_H
#define EHCI_H
#define TRACE_USB
#include "usb_p.h"
#include "ehci_hardware.h"
@ -74,7 +73,8 @@ static int32 FinishThread(void *data);
void FreeQueueHead(ehci_qh *queueHead);
status_t LinkQueueHead(ehci_qh *queueHead);
status_t UnlinkQueueHead(ehci_qh *queueHead);
status_t UnlinkQueueHead(ehci_qh *queueHead,
ehci_qh **freeList);
// Queue functions
status_t FillQueueWithRequest(Transfer *transfer,
@ -133,6 +133,7 @@ static pci_module_info *sPCIModule;
// Async frame list
ehci_qh *fAsyncQueueHead;
bool fAsyncAdvance;
// Maintain a linked list of transfers
transfer_data *fFirstTransfer;

View File

@ -126,6 +126,7 @@ typedef struct {
addr_t alt_next_phy;
uint32 token;
addr_t buffer_phy[5];
addr_t ext_buffer_phy[5];
// Software Part
addr_t this_phy;
@ -152,6 +153,7 @@ typedef struct {
#define EHCI_QTD_PID_SETUP 0x02
#define EHCI_QTD_STATUS_SHIFT 0
#define EHCI_QTD_STATUS_MASK 0x7f
#define EHCI_QTD_STATUS_ERRMASK 0x78
#define EHCI_QTD_STATUS_ACTIVE (1 << 7) // Active
#define EHCI_QTD_STATUS_HALTED (1 << 6) // Halted
#define EHCI_QTD_STATUS_BUFFER (1 << 5) // Data Buffer Error
@ -176,6 +178,7 @@ typedef struct {
addr_t alt_next_phy;
uint32 token;
addr_t buffer_phy[5];
addr_t ext_buffer_phy[5];
} overlay;
// Software Part