Work in progress on xHCI bus driver

* added a thread to handle events, locking wasn't easy in an interrupt
  handler
* the td struct can now track several buffers instead of just one.
* use Transfer::Data*() instead of Vector*() for the time being
  until support for fragmented transfers is done
* added CreateDescriptorChain, WriteDescriptorChain and ReadDescriptorChain,
  chained tds not working yet though.
* added a mutex lock per enabled endpoint, lock when touching the endpoint
  transfer ring.
* correctly configure interval and average trb length for endpoint contexts.
* interrupt transfers seem to work on real hardware
* xhci qemu driver doesn't advance ring dequeue pointers on link trbs, thus
  accessing freed trbs that could already be reused, leading to crash.
This commit is contained in:
Jérôme Duval 2012-06-03 22:19:51 +02:00
parent dc09611aad
commit 411272adfd
3 changed files with 383 additions and 109 deletions

View File

@ -124,6 +124,8 @@ XHCI::XHCI(pci_info *info, Stack *stack)
fPortCount(0),
fSlotCount(0),
fScratchpadCount(0),
fEventSem(-1),
fEventThread(-1),
fEventIdx(0),
fCmdIdx(0),
fEventCcs(1),
@ -233,7 +235,8 @@ XHCI::XHCI(pci_info *info, Stack *stack)
fCmdCompSem = create_sem(0, "XHCI Command Complete");
fFinishTransfersSem = create_sem(0, "XHCI Finish Transfers");
if (fFinishTransfersSem < B_OK || fCmdCompSem < B_OK) {
fEventSem = create_sem(0, "XHCI Event");
if (fFinishTransfersSem < B_OK || fCmdCompSem < B_OK || fEventSem < B_OK) {
TRACE_ERROR("failed to create semaphores\n");
return;
}
@ -243,6 +246,11 @@ XHCI::XHCI(pci_info *info, Stack *stack)
B_NORMAL_PRIORITY, (void *)this);
resume_thread(fFinishThread);
// create finisher service thread
fEventThread = spawn_kernel_thread(EventThread, "xhci event thread",
B_NORMAL_PRIORITY, (void *)this);
resume_thread(fEventThread);
// Install the interrupt handler
TRACE("installing interrupt handler\n");
install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line,
@ -267,12 +275,14 @@ XHCI::~XHCI()
fStopThreads = true;
delete_sem(fCmdCompSem);
delete_sem(fFinishTransfersSem);
delete_sem(fEventSem);
delete_area(fRegisterArea);
delete_area(fErstArea);
for (uint32 i = 0; i < fScratchpadCount; i++)
delete_area(fScratchpadArea[i]);
delete_area(fDcbaArea);
wait_for_thread(fFinishThread, &result);
wait_for_thread(fEventThread, &result);
put_module(B_PCI_MODULE_NAME);
}
@ -485,7 +495,7 @@ XHCI::SubmitControlRequest(Transfer *transfer)
usb_request_data *requestData = transfer->RequestData();
bool directionIn = (requestData->RequestType & USB_REQTYPE_DEVICE_IN) != 0;
TRACE("SubmitControlRequest()\n");
TRACE("SubmitControlRequest() length %d\n", requestData->Length);
xhci_td *setupDescriptor = CreateDescriptor(requestData->Length);
@ -505,7 +515,7 @@ XHCI::SubmitControlRequest(Transfer *transfer)
if (requestData->Length > 0) {
// set DataStage if any
setupDescriptor->trbs[index].qwtrb0 = setupDescriptor->buffer_phy;
setupDescriptor->trbs[index].qwtrb0 = setupDescriptor->buffer_phy[0];
setupDescriptor->trbs[index].dwtrb2 = TRB_2_IRQ(0)
| TRB_2_BYTES(requestData->Length)
| TRB_2_TD_SIZE(transfer->VectorCount());
@ -522,7 +532,7 @@ XHCI::SubmitControlRequest(Transfer *transfer)
| ((directionIn && requestData->Length > 0) ? 0 : TRB_3_DIR_IN)
| TRB_3_IOC_BIT | TRB_3_CYCLE_BIT;
setupDescriptor->last_used = index;
setupDescriptor->trb_count = index + 1;
xhci_endpoint *endpoint = (xhci_endpoint *)pipe->ControllerCookie();
uint8 id = XHCI_ENDPOINT_ID(pipe);
@ -542,33 +552,41 @@ XHCI::SubmitControlRequest(Transfer *transfer)
status_t
XHCI::SubmitNormalRequest(Transfer *transfer)
{
TRACE("SubmitNormalRequest()\n");
TRACE("SubmitNormalRequest() length %ld\n", transfer->DataLength());
Pipe *pipe = transfer->TransferPipe();
bool directionIn = (pipe->Direction() == Pipe::In);
xhci_td *normalDescriptor = CreateDescriptor(transfer->VectorLength());
// set NormalStage
uint8 index = 0;
normalDescriptor->trbs[index].qwtrb0 = normalDescriptor->buffer_phy;
normalDescriptor->trbs[index].dwtrb2 = TRB_2_IRQ(0)
| TRB_2_BYTES(transfer->VectorLength())
| TRB_2_TD_SIZE(transfer->VectorCount());
normalDescriptor->trbs[index].dwtrb3 = TRB_3_TYPE(TRB_TYPE_NORMAL)
| TRB_3_CYCLE_BIT | TRB_3_IOC_BIT;
if (!directionIn) {
memcpy(normalDescriptor->buffer_log,
(uint8 *)transfer->Vector()[0].iov_base, transfer->VectorLength());
}
normalDescriptor->last_used = index;
xhci_endpoint *endpoint = (xhci_endpoint *)pipe->ControllerCookie();
uint8 id = XHCI_ENDPOINT_ID(pipe);
if (id >= XHCI_MAX_ENDPOINTS)
return B_BAD_VALUE;
normalDescriptor->transfer = transfer;
_LinkDescriptorForPipe(normalDescriptor, endpoint);
bool directionIn = (pipe->Direction() == Pipe::In);
xhci_td *descriptor = CreateDescriptorChain(transfer->DataLength());
descriptor->trb_count = descriptor->buffer_count;
// set NormalStage
uint8 index;
for (index = 0; index < descriptor->buffer_count; index++) {
descriptor->trbs[index].qwtrb0 = descriptor->buffer_phy[index];
descriptor->trbs[index].dwtrb2 = TRB_2_IRQ(0)
| TRB_2_BYTES(descriptor->buffer_size[index])
| TRB_2_TD_SIZE(descriptor->trb_count);
descriptor->trbs[index].dwtrb3 = TRB_3_TYPE(TRB_TYPE_NORMAL)
| TRB_3_CYCLE_BIT;
}
if (descriptor->trb_count > 0)
descriptor->trbs[index - 1].dwtrb3 |= TRB_3_IOC_BIT;
if (!directionIn) {
TRACE("copying out iov count %ld\n", transfer->VectorCount());
WriteDescriptorChain(descriptor, transfer->Vector(),
transfer->VectorCount());
}
/* memcpy(descriptor->buffer_log[index],
(uint8 *)transfer->Vector()[index].iov_base, transfer->VectorLength());
}*/
xhci_endpoint *endpoint = (xhci_endpoint *)pipe->ControllerCookie();
descriptor->transfer = transfer;
_LinkDescriptorForPipe(descriptor, endpoint);
TRACE("SubmitNormalRequest() request linked\n");
@ -682,6 +700,52 @@ XHCI::AddTo(Stack *stack)
}
xhci_td *
XHCI::CreateDescriptorChain(size_t bufferSize)
{
size_t packetSize = B_PAGE_SIZE * 16;
int32 trbCount = (bufferSize + packetSize - 1) / packetSize;
// keep one trb for linking
int32 tdCount = (trbCount + XHCI_MAX_TRBS_PER_TD - 2) / (XHCI_MAX_TRBS_PER_TD - 1);
xhci_td *first = NULL;
xhci_td *last = NULL;
for (int32 i = 0; i < tdCount; i++) {
xhci_td *descriptor = CreateDescriptor(0);
if (!descriptor) {
//FreeDescriptorChain(firstDescriptor);
return NULL;
} else if (first == NULL)
first = descriptor;
uint8 trbs = min_c(trbCount, XHCI_MAX_TRBS_PER_TD);
TRACE("CreateDescriptorChain trbs %d for td %ld\n", trbs, i);
for (int j = 0; j < trbs; j++) {
if (fStack->AllocateChunk(&descriptor->buffer_log[j],
(void **)&descriptor->buffer_phy[j],
min_c(packetSize, bufferSize)) < B_OK) {
TRACE_ERROR("unable to allocate space for the buffer (size %ld)\n",
bufferSize);
return NULL;
}
descriptor->buffer_size[j] = min_c(packetSize, bufferSize);
bufferSize -= descriptor->buffer_size[j];
TRACE("CreateDescriptorChain allocated %ld for trb %d\n",
descriptor->buffer_size[j], j);
}
descriptor->buffer_count = trbs;
trbCount -= trbs;
if (last != NULL)
last->next = descriptor;
last = descriptor;
}
return first;
}
xhci_td *
XHCI::CreateDescriptor(size_t bufferSize)
{
@ -695,15 +759,17 @@ XHCI::CreateDescriptor(size_t bufferSize)
}
result->this_phy = (addr_t)physicalAddress;
result->buffer_size = bufferSize;
result->buffer_size[0] = bufferSize;
result->trb_count = 0;
result->buffer_count = 1;
if (bufferSize <= 0) {
result->buffer_log = NULL;
result->buffer_phy = 0;
result->buffer_log[0] = NULL;
result->buffer_phy[0] = 0;
return result;
}
if (fStack->AllocateChunk(&result->buffer_log,
(void **)&result->buffer_phy, bufferSize) < B_OK) {
if (fStack->AllocateChunk(&result->buffer_log[0],
(void **)&result->buffer_phy[0], bufferSize) < B_OK) {
TRACE_ERROR("unable to allocate space for the buffer (size %ld)\n",
bufferSize);
fStack->FreeChunk(result, (void *)result->this_phy, sizeof(xhci_td));
@ -720,9 +786,13 @@ XHCI::FreeDescriptor(xhci_td *descriptor)
if (!descriptor)
return;
if (descriptor->buffer_log) {
fStack->FreeChunk(descriptor->buffer_log,
(void *)descriptor->buffer_phy, descriptor->buffer_size);
for (int i = 0; i < descriptor->buffer_count; i++) {
if (descriptor->buffer_size[i] == 0)
continue;
TRACE("FreeDescriptor buffer %d buffer_size %ld\n", i,
descriptor->buffer_size[i]);
fStack->FreeChunk(descriptor->buffer_log[i],
(void *)descriptor->buffer_phy[i], descriptor->buffer_size[i]);
}
fStack->FreeChunk(descriptor, (void *)descriptor->this_phy,
@ -730,6 +800,116 @@ XHCI::FreeDescriptor(xhci_td *descriptor)
}
size_t
XHCI::WriteDescriptorChain(xhci_td *descriptor, iovec *vector,
size_t vectorCount)
{
xhci_td *current = descriptor;
uint8 trbIndex = 0;
size_t actualLength = 0;
uint8 vectorIndex = 0;
size_t vectorOffset = 0;
size_t bufferOffset = 0;
while (current != NULL) {
if (current->buffer_log == NULL)
break;
while (true) {
size_t length = min_c(current->buffer_size[trbIndex] - bufferOffset,
vector[vectorIndex].iov_len - vectorOffset);
TRACE("copying %ld bytes to bufferOffset %ld from"
" vectorOffset %ld at index %d of %ld\n", length, bufferOffset,
vectorOffset, vectorIndex, vectorCount);
memcpy((uint8 *)current->buffer_log[trbIndex] + bufferOffset,
(uint8 *)vector[vectorIndex].iov_base + vectorOffset, length);
actualLength += length;
vectorOffset += length;
bufferOffset += length;
if (vectorOffset >= vector[vectorIndex].iov_len) {
if (++vectorIndex >= vectorCount) {
TRACE("wrote descriptor chain (%ld bytes, no more vectors)\n",
actualLength);
return actualLength;
}
vectorOffset = 0;
}
if (bufferOffset >= current->buffer_size[trbIndex]) {
if (++trbIndex >= current->buffer_count)
break;
bufferOffset = 0;
}
}
current = current->next;
trbIndex = 0;
}
TRACE("wrote descriptor chain (%ld bytes)\n", actualLength);
return actualLength;
}
size_t
XHCI::ReadDescriptorChain(xhci_td *descriptor, iovec *vector,
size_t vectorCount)
{
xhci_td *current = descriptor;
uint8 trbIndex = 0;
size_t actualLength = 0;
uint8 vectorIndex = 0;
size_t vectorOffset = 0;
size_t bufferOffset = 0;
while (current != NULL) {
if (current->buffer_log == NULL)
break;
while (true) {
size_t length = min_c(current->buffer_size[trbIndex] - bufferOffset,
vector[vectorIndex].iov_len - vectorOffset);
TRACE("copying %ld bytes to vectorOffset %ld from"
" bufferOffset %ld at index %d of %ld\n", length, vectorOffset,
bufferOffset, vectorIndex, vectorCount);
memcpy((uint8 *)vector[vectorIndex].iov_base + vectorOffset,
(uint8 *)current->buffer_log[trbIndex] + bufferOffset, length);
actualLength += length;
vectorOffset += length;
bufferOffset += length;
if (vectorOffset >= vector[vectorIndex].iov_len) {
if (++vectorIndex >= vectorCount) {
TRACE("read descriptor chain (%ld bytes, no more vectors)\n",
actualLength);
return actualLength;
}
vectorOffset = 0;
}
if (bufferOffset >= current->buffer_size[trbIndex]) {
if (++trbIndex >= current->buffer_count)
break;
bufferOffset = 0;
}
}
current = current->next;
trbIndex = 0;
}
TRACE("read descriptor chain (%ld bytes)\n", actualLength);
return actualLength;
}
Device *
XHCI::AllocateDevice(Hub *parent, int8 hubAddress, uint8 hubPort,
usb_speed speed)
@ -768,6 +948,7 @@ XHCI::AllocateDevice(Hub *parent, int8 hubAddress, uint8 hubPort,
return NULL;
}
memset(device->input_ctx, 0, sizeof(*device->input_ctx));
device->input_ctx->input.dropFlags = 0;
device->input_ctx->input.addFlags = 3;
@ -825,6 +1006,7 @@ XHCI::AllocateDevice(Hub *parent, int8 hubAddress, uint8 hubPort,
delete_area(device->input_ctx_area);
return NULL;
}
memset(device->device_ctx, 0, sizeof(*device->device_ctx));
device->trb_area = fStack->AllocateArea((void **)&device->trbs,
(void**)&device->trb_addr, sizeof(*device->trbs), "XHCI endpoint trbs");
@ -862,7 +1044,7 @@ XHCI::AllocateDevice(Hub *parent, int8 hubAddress, uint8 hubPort,
// configure the Control endpoint 0 (type 4)
if (ConfigureEndpoint(slot, 0, 4, device->trb_addr, 0, 1, 1, 0,
maxPacketSize, maxPacketSize) != B_OK) {
maxPacketSize, maxPacketSize, speed) != B_OK) {
TRACE_ERROR("unable to configure default control endpoint\n");
return NULL;
}
@ -873,6 +1055,7 @@ XHCI::AllocateDevice(Hub *parent, int8 hubAddress, uint8 hubPort,
device->endpoints[0].used = 0;
device->endpoints[0].current = 0;
device->endpoints[0].trb_addr = device->trb_addr;
mutex_init(&device->endpoints[0].lock, "xhci endpoint lock");
// device should get to addressed state (bsr = 0)
if (SetAddress(device->input_ctx_addr, false, slot) != B_OK) {
@ -974,6 +1157,13 @@ XHCI::_InsertEndpointForPipe(Pipe *pipe)
return B_BAD_VALUE;
if (id > 0) {
if (SLOT_0_NUM_ENTRIES_GET(device->device_ctx->slot.dwslot0) == 1) {
device->input_ctx->slot.dwslot0 &= ~(SLOT_0_NUM_ENTRIES(0x1f));
device->input_ctx->slot.dwslot0 |=
SLOT_0_NUM_ENTRIES(XHCI_MAX_ENDPOINTS - 1);
EvaluateContext(device->input_ctx_addr, device->slot);
}
device->endpoints[id].device = device;
device->endpoints[id].trbs = device->trbs
+ id * XHCI_MAX_TRANSFERS;
@ -981,6 +1171,7 @@ XHCI::_InsertEndpointForPipe(Pipe *pipe)
device->endpoints[id].used = 0;
device->endpoints[id].trb_addr = device->trb_addr
+ id * XHCI_MAX_TRANSFERS * sizeof(xhci_trb);
mutex_init(&device->endpoints[id].lock, "xhci endpoint lock");
TRACE("_InsertEndpointForPipe trbs device %p endpoint %p\n",
device->trbs, device->endpoints[id].trbs);
@ -1013,7 +1204,8 @@ XHCI::_InsertEndpointForPipe(Pipe *pipe)
if (ConfigureEndpoint(device->slot, id, type,
device->endpoints[id].trb_addr, pipe->Interval(),
1, 1, 0, pipe->MaxPacketSize(), pipe->MaxPacketSize()) != B_OK) {
1, 1, 0, pipe->MaxPacketSize(), pipe->MaxPacketSize(),
usbDevice->Speed()) != B_OK) {
TRACE_ERROR("unable to configure endpoint\n");
return B_ERROR;
}
@ -1021,10 +1213,16 @@ XHCI::_InsertEndpointForPipe(Pipe *pipe)
EvaluateContext(device->input_ctx_addr, device->slot);
ConfigureEndpoint(device->input_ctx_addr, false, device->slot);
device->state = XHCI_STATE_CONFIGURED;
TRACE("device: address 0x%x state 0x%lx\n", device->address,
SLOT_3_SLOT_STATE_GET(device->device_ctx->slot.dwslot3));
TRACE("endpoint[0] state 0x%lx\n",
ENDPOINT_0_STATE_GET(device->device_ctx->endpoints[0].dwendpoint0));
TRACE("endpoint[%d] state 0x%lx\n", id,
ENDPOINT_0_STATE_GET(device->device_ctx->endpoints[id].dwendpoint0));
device->state = XHCI_STATE_CONFIGURED;
}
pipe->SetControllerCookie(&device->endpoints[id]);
TRACE("_InsertEndpointForPipe for pipe %p at id %d\n", pipe, id);
return B_OK;
@ -1046,6 +1244,7 @@ status_t
XHCI::_LinkDescriptorForPipe(xhci_td *descriptor, xhci_endpoint *endpoint)
{
TRACE("_LinkDescriptorForPipe\n");
MutexLocker endpointLocker(endpoint->lock);
if (endpoint->used >= XHCI_MAX_TRANSFERS)
return B_BAD_VALUE;
@ -1062,11 +1261,10 @@ XHCI::_LinkDescriptorForPipe(xhci_td *descriptor, xhci_endpoint *endpoint)
TRACE("_LinkDescriptorForPipe current %d, next %d\n", current, next);
// compute next link
uint8 lastUsed = descriptor->last_used;
addr_t addr = endpoint->trb_addr + next * sizeof(struct xhci_trb);
descriptor->trbs[lastUsed + 1].qwtrb0 = addr;
descriptor->trbs[lastUsed + 1].dwtrb2 = TRB_2_IRQ(0);
descriptor->trbs[lastUsed + 1].dwtrb3 = TRB_3_TYPE(TRB_TYPE_LINK)
descriptor->trbs[descriptor->trb_count].qwtrb0 = addr;
descriptor->trbs[descriptor->trb_count].dwtrb2 = TRB_2_IRQ(0);
descriptor->trbs[descriptor->trb_count].dwtrb3 = TRB_3_TYPE(TRB_TYPE_LINK)
| TRB_3_IOC_BIT | TRB_3_CYCLE_BIT;
endpoint->trbs[next].qwtrb0 = 0;
@ -1092,6 +1290,7 @@ status_t
XHCI::_UnlinkDescriptorForPipe(xhci_td *descriptor, xhci_endpoint *endpoint)
{
TRACE("_UnlinkDescriptorForPipe\n");
MutexLocker endpointLocker(endpoint->lock);
endpoint->used--;
if (descriptor == endpoint->td_head) {
endpoint->td_head = descriptor->next;
@ -1115,7 +1314,7 @@ XHCI::_UnlinkDescriptorForPipe(xhci_td *descriptor, xhci_endpoint *endpoint)
status_t
XHCI::ConfigureEndpoint(uint8 slot, uint8 number, uint8 type, uint64 ringAddr, uint16 interval,
uint8 maxPacketCount, uint8 mult, uint8 fpsShift, uint16 maxPacketSize,
uint16 maxFrameSize)
uint16 maxFrameSize, usb_speed speed)
{
struct xhci_device *device = &fDevices[slot];
struct xhci_endpoint_ctx *endpoint = &device->input_ctx->endpoints[number];
@ -1127,6 +1326,26 @@ XHCI::ConfigureEndpoint(uint8 slot, uint8 number, uint8 type, uint64 ringAddr, u
endpoint->dwendpoint0 = ENDPOINT_0_STATE(0) | ENDPOINT_0_MAXPSTREAMS(0);
// add mult for isochronous and interrupt types
switch (speed) {
case USB_SPEED_LOWSPEED:
case USB_SPEED_FULLSPEED:
fpsShift += 3;
break;
default:
break;
}
switch (type) {
case 1:
case 5:
if (fpsShift > 3)
fpsShift--;
case 3:
case 7:
endpoint->dwendpoint0 |= ENDPOINT_0_INTERVAL(fpsShift);
break;
default:
break;
}
// add interval
endpoint->dwendpoint1 = ENDPOINT_1_EPTYPE(type)
| ENDPOINT_1_MAXBURST(maxPacketCount)
@ -1142,9 +1361,11 @@ XHCI::ConfigureEndpoint(uint8 slot, uint8 number, uint8 type, uint64 ringAddr, u
case 3:
case 5:
case 7:
endpoint->dwendpoint4 = ENDPOINT_4_AVGTRBLENGTH(maxFrameSize)
| ENDPOINT_4_MAXESITPAYLOAD(maxFrameSize);
endpoint->dwendpoint4 = ENDPOINT_4_AVGTRBLENGTH(min_c(maxFrameSize,
B_PAGE_SIZE)) | ENDPOINT_4_MAXESITPAYLOAD(maxFrameSize);
break;
default:
endpoint->dwendpoint4 = ENDPOINT_4_AVGTRBLENGTH(B_PAGE_SIZE);
}
TRACE("endpoint 0x%lx 0x%lx 0x%llx 0x%lx\n", endpoint->dwendpoint0,
@ -1406,55 +1627,8 @@ XHCI::Interrupt()
}
TRACE("Event Interrupt\n");
uint16 i = fEventIdx;
uint8 j = fEventCcs;
uint8 t = 2;
while (1) {
temp = fEventRing[i].dwtrb3;
uint8 k = (temp & TRB_3_CYCLE_BIT) ? 1 : 0;
if (j != k)
break;
uint8 event = TRB_3_TYPE_GET(temp);
TRACE("event[%u] = %u (0x%016llx 0x%08lx 0x%08lx)\n", i, event,
fEventRing[i].qwtrb0, fEventRing[i].dwtrb2, fEventRing[i].dwtrb3);
switch (event) {
case TRB_TYPE_COMMAND_COMPLETION:
HandleCmdComplete(&fEventRing[i]);
result = B_INVOKE_SCHEDULER;
break;
case TRB_TYPE_TRANSFER:
HandleTransferComplete(&fEventRing[i]);
result = B_INVOKE_SCHEDULER;
break;
case TRB_TYPE_PORT_STATUS_CHANGE:
TRACE("port change detected\n");
break;
default:
TRACE_ERROR("Unhandled event = %u\n", event);
break;
}
i++;
if (i == XHCI_MAX_EVENTS) {
i = 0;
j ^= 1;
if (!--t)
break;
}
}
fEventIdx = i;
fEventCcs = j;
uint64 addr = fErst->rs_addr + i * sizeof(xhci_trb);
addr |= ERST_EHB;
WriteRunReg32(XHCI_ERDP_LO(0), (uint32)addr);
WriteRunReg32(XHCI_ERDP_HI(0), (uint32)(addr >> 32));
return result;
release_sem_etc(fEventSem, 1, B_DO_NOT_RESCHEDULE);
return B_INVOKE_SCHEDULER;
}
@ -1552,8 +1726,10 @@ XHCI::HandleTransferComplete(xhci_trb *trb)
_UnlinkDescriptorForPipe(td, endpoint);
// add descriptor to finished list (to be processed and freed)
Lock();
td->next = fFinishedHead;
fFinishedHead = td;
Unlock();
release_sem(fFinishTransfersSem);
break;
}
@ -1735,6 +1911,76 @@ XHCI::ResetDevice(uint8 slot)
}
int32
XHCI::EventThread(void* data)
{
((XHCI *)data)->CompleteEvents();
return B_OK;
}
void
XHCI::CompleteEvents()
{
while (!fStopThreads) {
if (acquire_sem(fEventSem) < B_OK)
continue;
// eat up sems that have been released by multiple interrupts
int32 semCount = 0;
get_sem_count(fEventSem, &semCount);
if (semCount > 0)
acquire_sem_etc(fEventSem, semCount, B_RELATIVE_TIMEOUT, 0);
uint16 i = fEventIdx;
uint8 j = fEventCcs;
uint8 t = 2;
while (1) {
uint32 temp = fEventRing[i].dwtrb3;
uint8 k = (temp & TRB_3_CYCLE_BIT) ? 1 : 0;
if (j != k)
break;
uint8 event = TRB_3_TYPE_GET(temp);
TRACE("event[%u] = %u (0x%016llx 0x%08lx 0x%08lx)\n", i, event,
fEventRing[i].qwtrb0, fEventRing[i].dwtrb2, fEventRing[i].dwtrb3);
switch (event) {
case TRB_TYPE_COMMAND_COMPLETION:
HandleCmdComplete(&fEventRing[i]);
break;
case TRB_TYPE_TRANSFER:
HandleTransferComplete(&fEventRing[i]);
break;
case TRB_TYPE_PORT_STATUS_CHANGE:
TRACE("port change detected\n");
break;
default:
TRACE_ERROR("Unhandled event = %u\n", event);
break;
}
i++;
if (i == XHCI_MAX_EVENTS) {
i = 0;
j ^= 1;
if (!--t)
break;
}
}
fEventIdx = i;
fEventCcs = j;
uint64 addr = fErst->rs_addr + i * sizeof(xhci_trb);
addr |= ERST_EHB;
WriteRunReg32(XHCI_ERDP_LO(0), (uint32)addr);
WriteRunReg32(XHCI_ERDP_HI(0), (uint32)(addr >> 32));
}
}
int32
XHCI::FinishThread(void* data)
{
@ -1756,28 +2002,40 @@ XHCI::FinishTransfers()
if (semCount > 0)
acquire_sem_etc(fFinishTransfersSem, semCount, B_RELATIVE_TIMEOUT, 0);
Lock();
TRACE("finishing transfers\n");
while (fFinishedHead != NULL) {
xhci_td* td = fFinishedHead;
fFinishedHead = td->next;
td->next = NULL;
Unlock();
Transfer* transfer = td->transfer;
bool directionIn = (transfer->TransferPipe()->Direction() != Pipe::Out);
usb_request_data *requestData = transfer->RequestData();
// TODO check event
status_t callbackStatus = B_OK;
size_t actualLength = requestData ? requestData->Length
: transfer->VectorLength();
if (directionIn) {
memcpy((uint8 *)transfer->Vector()[0].iov_base,
td->buffer_log, actualLength);
: transfer->DataLength();
TRACE("finishing transfer td %p\n", td);
if (directionIn && actualLength > 0) {
if (requestData) {
TRACE("copying in data %d bytes\n", requestData->Length);
memcpy((uint8 *)transfer->Vector()[0].iov_base,
td->buffer_log[0], requestData->Length);
} else {
TRACE("copying in iov count %ld\n", transfer->VectorCount());
ReadDescriptorChain(td, transfer->Vector(),
transfer->VectorCount());
}
}
transfer->Finished(callbackStatus, actualLength);
FreeDescriptor(td);
Lock();
}
Unlock();
}
}

View File

@ -33,16 +33,17 @@ enum xhci_state {
typedef struct xhci_td {
struct xhci_trb trbs[18];
struct xhci_trb trbs[XHCI_MAX_TRBS_PER_TD];
addr_t buffer_phy;
addr_t this_phy; // A physical pointer to this address
void *buffer_log; // Pointer to the logical buffer
size_t buffer_size; // Size of the buffer
addr_t this_phy; // A physical pointer to this address
addr_t buffer_phy[XHCI_MAX_TRBS_PER_TD];
void *buffer_log[XHCI_MAX_TRBS_PER_TD]; // Pointer to the logical buffer
size_t buffer_size[XHCI_MAX_TRBS_PER_TD]; // Size of the buffer
uint8 buffer_count;
struct xhci_td *next;
uint8 last_used;
Transfer *transfer;
uint8 trb_count;
} xhci_td __attribute__((__aligned__(16)));
@ -53,6 +54,7 @@ typedef struct xhci_endpoint {
addr_t trb_addr;
uint8 used;
uint8 current;
mutex lock;
} xhci_endpoint;
@ -99,7 +101,8 @@ public:
uint8 type, uint64 ringAddr,
uint16 interval, uint8 maxPacketCount,
uint8 mult, uint8 fpsShift,
uint16 maxPacketSize, uint16 maxFrameSize);
uint16 maxPacketSize, uint16 maxFrameSize,
usb_speed speed);
virtual void FreeDevice(Device *device);
status_t _InsertEndpointForPipe(Pipe *pipe);
@ -125,14 +128,24 @@ private:
static int32 InterruptHandler(void *data);
int32 Interrupt();
// Event management
static int32 EventThread(void *data);
void CompleteEvents();
// Transfer management
static int32 FinishThread(void *data);
void FinishTransfers();
// Descriptor
xhci_td * CreateDescriptor(size_t bufferSize);
xhci_td * CreateDescriptorChain(size_t bufferSize);
void FreeDescriptor(xhci_td *descriptor);
size_t WriteDescriptorChain(xhci_td *descriptor,
iovec *vector, size_t vectorCount);
size_t ReadDescriptorChain(xhci_td *descriptor,
iovec *vector, size_t vectorCount);
status_t _LinkDescriptorForPipe(xhci_td *descriptor,
xhci_endpoint *endpoint);
status_t _UnlinkDescriptorForPipe(xhci_td *descriptor,
@ -230,6 +243,8 @@ private:
// Devices
struct xhci_device fDevices[XHCI_MAX_DEVICES];
sem_id fEventSem;
thread_id fEventThread;
uint16 fEventIdx;
uint16 fCmdIdx;
uint8 fEventCcs;

View File

@ -266,13 +266,14 @@
#define XHCI_MAX_SCRATCHPADS 32
#define XHCI_MAX_DEVICES 128
#define XHCI_MAX_TRANSFERS 4
#define XHCI_MAX_TRBS_PER_TD 18
struct xhci_trb {
uint64 qwtrb0;
uint32 dwtrb2;
uint32 dwtrb3;
} __attribute__((__aligned__(4)));;
} __attribute__((__aligned__(4)));
struct xhci_segment {