From 411272adfd60dcab0a42879cc617b59d9c333ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Sun, 3 Jun 2012 22:19:51 +0200 Subject: [PATCH] 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. --- src/add-ons/kernel/busses/usb/xhci.cpp | 460 ++++++++++++++---- src/add-ons/kernel/busses/usb/xhci.h | 29 +- src/add-ons/kernel/busses/usb/xhci_hardware.h | 3 +- 3 files changed, 383 insertions(+), 109 deletions(-) diff --git a/src/add-ons/kernel/busses/usb/xhci.cpp b/src/add-ons/kernel/busses/usb/xhci.cpp index 2348264e3f..303e165d48 100644 --- a/src/add-ons/kernel/busses/usb/xhci.cpp +++ b/src/add-ons/kernel/busses/usb/xhci.cpp @@ -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(); } } diff --git a/src/add-ons/kernel/busses/usb/xhci.h b/src/add-ons/kernel/busses/usb/xhci.h index 0fa92eba87..0983aa84b2 100644 --- a/src/add-ons/kernel/busses/usb/xhci.h +++ b/src/add-ons/kernel/busses/usb/xhci.h @@ -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; diff --git a/src/add-ons/kernel/busses/usb/xhci_hardware.h b/src/add-ons/kernel/busses/usb/xhci_hardware.h index 82fffafe8c..ebd3d43ac3 100644 --- a/src/add-ons/kernel/busses/usb/xhci_hardware.h +++ b/src/add-ons/kernel/busses/usb/xhci_hardware.h @@ -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 {