From 7df33d912b05c8d986287bec208df68c595c24a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Tue, 10 May 2011 21:25:32 +0000 Subject: [PATCH] * Implement inbound isochronous transfer for EHCI, based on UHCI implementation. * changes next_log to their actual type instead of void* * the field this_phy now includes the item type, it simplifies things. * isochronous transfer descriptors are linked in the periodic frame list first, and points to existing interrupt transfer descriptors. This is still work in progress. Yet it's worth committing as it doesn't seem to have impacts, and is required for the UVC SoC project. Tested basically with usb_webcam to receive UVC stream headers. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41424 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/busses/usb/ehci.cpp | 816 ++++++++++++++++-- src/add-ons/kernel/busses/usb/ehci.h | 76 +- src/add-ons/kernel/busses/usb/ehci_hardware.h | 120 ++- 3 files changed, 910 insertions(+), 102 deletions(-) diff --git a/src/add-ons/kernel/busses/usb/ehci.cpp b/src/add-ons/kernel/busses/usb/ehci.cpp index ecff0e9227..e31e6ef461 100644 --- a/src/add-ons/kernel/busses/usb/ehci.cpp +++ b/src/add-ons/kernel/busses/usb/ehci.cpp @@ -1,11 +1,13 @@ /* - * Copyright 2006-2008, Haiku Inc. All rights reserved. + * Copyright 2006-2011, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Michael Lotz + * Jérôme Duval */ + #include #include #include @@ -69,10 +71,10 @@ print_descriptor_chain(ehci_qtd *descriptor) descriptor->buffer_phy[2], descriptor->buffer_phy[3], descriptor->buffer_phy[4], descriptor->buffer_size); - if (descriptor->next_phy & EHCI_QTD_TERMINATE) + if (descriptor->next_phy & EHCI_ITEM_TERMINATE) break; - descriptor = (ehci_qtd *)descriptor->next_log; + descriptor = descriptor->next_log; } } @@ -87,7 +89,7 @@ print_queue(ehci_qh *queueHead) queueHead->overlay.token, queueHead->overlay.buffer_phy[0], queueHead->overlay.buffer_phy[1], queueHead->overlay.buffer_phy[2], queueHead->overlay.buffer_phy[3], queueHead->overlay.buffer_phy[4]); - print_descriptor_chain((ehci_qtd *)queueHead->element_log); + print_descriptor_chain(queueHead->element_log); } #endif // TRACE_USB @@ -115,11 +117,16 @@ EHCI::EHCI(pci_info *info, Stack *stack) fLastTransfer(NULL), fFinishTransfersSem(-1), fFinishThread(-1), + fProcessingPipe(NULL), + fFreeListHead(NULL), fCleanupSem(-1), fCleanupThread(-1), fStopThreads(false), - fFreeListHead(NULL), - fProcessingPipe(NULL), + fFrameBandwidth(NULL), + fFirstIsochronousTransfer(NULL), + fLastIsochronousTransfer(NULL), + fFinishIsochronousTransfersSem(-1), + fFinishIsochronousThread(-1), fRootHub(NULL), fRootHubAddress(0), fPortCount(0), @@ -248,6 +255,23 @@ EHCI::EHCI(pci_info *info, Stack *stack) B_NORMAL_PRIORITY, (void *)this); resume_thread(fFinishThread); + // Create a lock for the isochronous transfer list + mutex_init(&fIsochronousLock, "EHCI isochronous lock"); + + // Create semaphore the isochronous finisher thread will wait for + fFinishIsochronousTransfersSem = create_sem(0, + "EHCI Isochronous Finish Transfers"); + if (fFinishIsochronousTransfersSem < B_OK) { + TRACE_ERROR("failed to create isochronous finisher semaphore\n"); + return; + } + + // Create the isochronous finisher service thread + fFinishIsochronousThread = spawn_kernel_thread(FinishIsochronousThread, + "ehci isochronous finish thread", B_URGENT_DISPLAY_PRIORITY, + (void *)this); + resume_thread(fFinishIsochronousThread); + // create cleanup service thread fCleanupThread = spawn_kernel_thread(CleanupThread, "ehci cleanup thread", B_NORMAL_PRIORITY, (void *)this); @@ -260,14 +284,24 @@ EHCI::EHCI(pci_info *info, Stack *stack) | EHCI_USBINTR_USBINT | EHCI_USBINTR_INTONAA; WriteOpReg(EHCI_USBINTR, fEnabledInterrupts); + // structures don't span page boundaries + size_t itdListSize = EHCI_VFRAMELIST_ENTRIES_COUNT + / (B_PAGE_SIZE / sizeof(itd_entry)) * B_PAGE_SIZE; + size_t sitdListSize = EHCI_VFRAMELIST_ENTRIES_COUNT + / (B_PAGE_SIZE / sizeof(sitd_entry)) * B_PAGE_SIZE; + size_t frameListSize = B_PAGE_SIZE + B_PAGE_SIZE + itdListSize + + sitdListSize; + // allocate the periodic frame list fPeriodicFrameListArea = fStack->AllocateArea((void **)&fPeriodicFrameList, - (void **)&physicalAddress, B_PAGE_SIZE * 2, "USB EHCI Periodic Framelist"); + (void **)&physicalAddress, frameListSize, "USB EHCI Periodic Framelist"); if (fPeriodicFrameListArea < B_OK) { TRACE_ERROR("unable to allocate periodic framelist\n"); return; } + if ((physicalAddress & 0xfff) != 0) + panic("EHCI_PERIODICLISTBASE not aligned on 4k: 0x%lx\n", physicalAddress); // set the periodic frame list base on the controller WriteOpReg(EHCI_PERIODICLISTBASE, (uint32)physicalAddress); @@ -278,12 +312,12 @@ EHCI::EHCI(pci_info *info, Stack *stack) memset(logicalBase, 0, B_PAGE_SIZE); fInterruptEntries = (interrupt_entry *)logicalBase; - for (int32 i = 0; i < 11; i++) { + for (int32 i = 0; i < EHCI_INTERRUPT_ENTRIES_COUNT; i++) { ehci_qh *queueHead = &fInterruptEntries[i].queue_head; - queueHead->this_phy = physicalBase; - queueHead->current_qtd_phy = EHCI_QTD_TERMINATE; - queueHead->overlay.next_phy = EHCI_QTD_TERMINATE; - queueHead->overlay.alt_next_phy = EHCI_QTD_TERMINATE; + queueHead->this_phy = physicalBase | EHCI_ITEM_TYPE_QH; + queueHead->current_qtd_phy = EHCI_ITEM_TERMINATE; + queueHead->overlay.next_phy = EHCI_ITEM_TERMINATE; + queueHead->overlay.alt_next_phy = EHCI_ITEM_TERMINATE; queueHead->overlay.token = EHCI_QTD_STATUS_HALTED; // set dummy endpoint information @@ -296,16 +330,50 @@ EHCI::EHCI(pci_info *info, Stack *stack) physicalBase += sizeof(interrupt_entry); } + // create the itd and sitd entries + TRACE("build up iso entries\n"); + addr_t itdPhysicalBase = physicalAddress + B_PAGE_SIZE + B_PAGE_SIZE; + itd_entry* itds = (itd_entry *)((uint8 *)fPeriodicFrameList + B_PAGE_SIZE + + B_PAGE_SIZE); + memset(itds, 0, itdListSize); + + addr_t sitdPhysicalBase = itdPhysicalBase + itdListSize; + sitd_entry* sitds = (sitd_entry *)((uint8 *)fPeriodicFrameList + B_PAGE_SIZE + + B_PAGE_SIZE + itdListSize); + memset(sitds, 0, sitdListSize); + + fItdEntries = new(std::nothrow) ehci_itd *[EHCI_VFRAMELIST_ENTRIES_COUNT]; + fSitdEntries = new(std::nothrow) ehci_sitd *[EHCI_VFRAMELIST_ENTRIES_COUNT]; + + for (int32 i = 0; i < EHCI_VFRAMELIST_ENTRIES_COUNT; i++) { + ehci_sitd *sitd = &sitds[i].sitd; + sitd->this_phy = sitdPhysicalBase | EHCI_ITEM_TYPE_SITD; + sitd->back_phy = EHCI_ITEM_TERMINATE; + fSitdEntries[i] = sitd; + TRACE("sitd entry %ld %p 0x%lx\n", i, sitd, sitd->this_phy); + + ehci_itd *itd = &itds[i].itd; + itd->this_phy = itdPhysicalBase | EHCI_ITEM_TYPE_ITD; + itd->next_phy = sitd->this_phy; + fItdEntries[i] = itd; + TRACE("itd entry %ld %p 0x%lx\n", i, itd, itd->this_phy); + + sitdPhysicalBase += sizeof(sitd_entry); + itdPhysicalBase += sizeof(itd_entry); + if ((sitdPhysicalBase & 0x10) != 0 || (itdPhysicalBase & 0x10) != 0) + panic("physical base for entry %ld not aligned on 32\n", i); + } + // build flat interrupt tree TRACE("build up interrupt links\n"); - uint32 interval = 1024; - uint32 intervalIndex = 10; + uint32 interval = EHCI_VFRAMELIST_ENTRIES_COUNT; + uint32 intervalIndex = EHCI_INTERRUPT_ENTRIES_COUNT - 1; while (interval > 1) { - uint32 insertIndex = interval / 2; - while (insertIndex < 1024) { - uint32 entry = fInterruptEntries[intervalIndex].queue_head.this_phy; - fPeriodicFrameList[insertIndex] = entry | EHCI_PFRAMELIST_QH; - insertIndex += interval; + for (uint32 insertIndex = interval / 2; + insertIndex < EHCI_VFRAMELIST_ENTRIES_COUNT; + insertIndex += interval) { + fSitdEntries[insertIndex]->next_phy = + fInterruptEntries[intervalIndex].queue_head.this_phy; } intervalIndex--; @@ -314,19 +382,31 @@ EHCI::EHCI(pci_info *info, Stack *stack) // setup the empty slot in the list and linking of all -> first ehci_qh *firstLogical = &fInterruptEntries[0].queue_head; - uint32 firstPhysical = firstLogical->this_phy | EHCI_QH_TYPE_QH; - fPeriodicFrameList[0] = firstPhysical; - for (int32 i = 1; i < 11; i++) { - fInterruptEntries[i].queue_head.next_phy = firstPhysical; + fSitdEntries[0]->next_phy = firstLogical->this_phy; + for (int32 i = 1; i < EHCI_INTERRUPT_ENTRIES_COUNT; i++) { + fInterruptEntries[i].queue_head.next_phy = firstLogical->this_phy; fInterruptEntries[i].queue_head.next_log = firstLogical; fInterruptEntries[i].queue_head.prev_log = NULL; } // terminate the first entry - firstLogical->next_phy = EHCI_QH_TERMINATE; + firstLogical->next_phy = EHCI_ITEM_TERMINATE; firstLogical->next_log = NULL; firstLogical->prev_log = NULL; + for (int32 i = 0; i < EHCI_FRAMELIST_ENTRIES_COUNT; i++) { + fPeriodicFrameList[i] = + fItdEntries[i & (EHCI_VFRAMELIST_ENTRIES_COUNT - 1)]->this_phy; + TRACE("periodic entry %ld linked to 0x%lx\n", i, fPeriodicFrameList[i]); + } + + // Create the array that will keep bandwidth information + fFrameBandwidth = new(std::nothrow) uint16[EHCI_VFRAMELIST_ENTRIES_COUNT]; + for (int32 i = 0; i < EHCI_VFRAMELIST_ENTRIES_COUNT; i++) { + fFrameBandwidth[i] = MAX_AVAILABLE_BANDWIDTH; + } + + // allocate a queue head that will always stay in the async frame list fAsyncQueueHead = CreateQueueHead(); if (!fAsyncQueueHead) { @@ -334,16 +414,15 @@ EHCI::EHCI(pci_info *info, Stack *stack) return; } - fAsyncQueueHead->next_phy = fAsyncQueueHead->this_phy | EHCI_QH_TYPE_QH; + fAsyncQueueHead->next_phy = fAsyncQueueHead->this_phy; fAsyncQueueHead->next_log = fAsyncQueueHead; fAsyncQueueHead->prev_log = fAsyncQueueHead; fAsyncQueueHead->endpoint_chars = EHCI_QH_CHARS_EPS_HIGH | EHCI_QH_CHARS_RECHEAD; fAsyncQueueHead->endpoint_caps = 1 << EHCI_QH_CAPS_MULT_SHIFT; - fAsyncQueueHead->current_qtd_phy = EHCI_QTD_TERMINATE; - fAsyncQueueHead->overlay.next_phy = EHCI_QTD_TERMINATE; + fAsyncQueueHead->current_qtd_phy = EHCI_ITEM_TERMINATE; + fAsyncQueueHead->overlay.next_phy = EHCI_ITEM_TERMINATE; - WriteOpReg(EHCI_ASYNCLISTADDR, (uint32)fAsyncQueueHead->this_phy - | EHCI_QH_TYPE_QH); + WriteOpReg(EHCI_ASYNCLISTADDR, (uint32)fAsyncQueueHead->this_phy); TRACE("set the async list addr to 0x%08lx\n", ReadOpReg(EHCI_ASYNCLISTADDR)); fInitOK = true; @@ -363,10 +442,24 @@ EHCI::~EHCI() fStopThreads = true; delete_sem(fAsyncAdvanceSem); delete_sem(fFinishTransfersSem); + delete_sem(fFinishIsochronousTransfersSem); wait_for_thread(fFinishThread, &result); wait_for_thread(fCleanupThread, &result); + wait_for_thread(fFinishIsochronousThread, &result); + + LockIsochronous(); + isochronous_transfer_data *isoTransfer = fFirstIsochronousTransfer; + while (isoTransfer) { + isochronous_transfer_data *next = isoTransfer->link; + delete isoTransfer; + isoTransfer = next; + } + mutex_destroy(&fIsochronousLock); delete fRootHub; + delete [] fFrameBandwidth; + delete [] fItdEntries; + delete [] fSitdEntries; delete_area(fPeriodicFrameListArea); delete_area(fRegisterArea); put_module(B_PCI_MODULE_NAME); @@ -386,6 +479,20 @@ EHCI::Start() | (frameListSize << EHCI_USBCMD_FLS_SHIFT) | (1 << EHCI_USBCMD_ITC_SHIFT)); + switch (frameListSize) { + case 0: + TRACE("frame list size 1024\n"); + break; + case 1: + TRACE("frame list size 512\n"); + break; + case 2: + TRACE("frame list size 256\n"); + break; + default: + TRACE("unknown frame list size\n"); + } + bool running = false; for (int32 i = 0; i < 10; i++) { uint32 status = ReadOpReg(EHCI_USBSTS); @@ -434,11 +541,8 @@ EHCI::SubmitTransfer(Transfer *transfer) return fRootHub->ProcessTransfer(this, transfer); Pipe *pipe = transfer->TransferPipe(); - if (pipe->Type() & USB_OBJECT_ISO_PIPE) { - TRACE_ERROR("isochronous transfers not implemented\n"); - // ToDo: implement isochronous transfers... - return B_ERROR; - } + if (pipe->Type() & USB_OBJECT_ISO_PIPE) + return SubmitIsochronous(transfer); ehci_qh *queueHead = CreateQueueHead(); if (!queueHead) { @@ -496,6 +600,171 @@ EHCI::SubmitTransfer(Transfer *transfer) } +status_t +EHCI::SubmitIsochronous(Transfer *transfer) +{ + Pipe *pipe = transfer->TransferPipe(); + bool directionIn = (pipe->Direction() == Pipe::In); + usb_isochronous_data *isochronousData = transfer->IsochronousData(); + size_t packetSize = transfer->DataLength(); +#ifdef TRACE_USB + size_t restSize = packetSize % isochronousData->packet_count; +#endif + packetSize /= isochronousData->packet_count; + uint16 currentFrame; + + if (packetSize > pipe->MaxPacketSize()) { + TRACE_ERROR("isochronous packetSize is bigger than pipe MaxPacketSize\n"); + return B_BAD_VALUE; + } + + // Ignore the fact that the last descriptor might need less bandwidth. + // The overhead is not worthy. + uint16 bandwidth = transfer->Bandwidth() / isochronousData->packet_count; + + TRACE("isochronous transfer descriptor bandwidth %d\n", bandwidth); + + // The following holds the list of transfer descriptor of the + // isochronous request. It is used to quickly remove all the isochronous + // descriptors from the frame list, as descriptors are not link to each + // other in a queue like for every other transfer. + ehci_itd **isoRequest + = new(std::nothrow) ehci_itd *[isochronousData->packet_count]; + if (isoRequest == NULL) { + TRACE("failed to create isoRequest array!\n"); + return B_NO_MEMORY; + } + + TRACE("isochronous submitted size=%ld bytes, TDs=%ld, " + "maxPacketSize=%ld, packetSize=%ld, restSize=%ld\n", transfer->DataLength(), + isochronousData->packet_count, pipe->MaxPacketSize(), packetSize, restSize); + + // Find the entry where to start inserting the first Isochronous descriptor + if (isochronousData->flags & USB_ISO_ASAP || + isochronousData->starting_frame_number == NULL) { + + uint32 threshold = (ReadCapReg32(EHCI_HCCPARAMS) + >> EHCI_HCCPARAMS_IPT_SHIFT) & EHCI_HCCPARAMS_IPT_MASK; + TRACE("threshold: %ld\n", threshold); + + // find the first available frame with enough bandwidth. + // This should always be the case, as defining the starting frame + // number in the driver makes no sense for many reason, one of which + // is that frame numbers value are host controller specific, and the + // driver does not know which host controller is running. + currentFrame = (ReadOpReg(EHCI_FRINDEX) / 8) + & (EHCI_FRAMELIST_ENTRIES_COUNT - 1); + + // Make sure that: + // 1. We are at least 5ms ahead the controller + // 2. We stay in the range 0-127 + // 3. There is enough bandwidth in the first entry + currentFrame = (currentFrame + threshold) + & (EHCI_VFRAMELIST_ENTRIES_COUNT - 1); + } else { + // Find out if the frame number specified has enough bandwidth, + // otherwise find the first next available frame with enough bandwidth + currentFrame = *isochronousData->starting_frame_number; + } + + TRACE("isochronous starting frame=%d\n", currentFrame); + + uint16 itdIndex = 0; + size_t dataLength = transfer->DataLength(); + void* bufferLog; + addr_t bufferPhy; + if (fStack->AllocateChunk(&bufferLog, (void**)&bufferPhy, dataLength) < B_OK) { + TRACE_ERROR("unable to allocate itd buffer\n"); + return B_NO_MEMORY; + } + + memset(bufferLog, 0, dataLength); + + addr_t currentPhy = bufferPhy; + uint32 frameCount = 0; + while (dataLength > 0) { + ehci_itd* itd = CreateItdDescriptor(); + isoRequest[itdIndex++] = itd; + uint16 pg = 0; + itd->buffer_phy[pg] = currentPhy & 0xfffff000; + uint32 offset = currentPhy & 0xfff; + TRACE("isochronous created itd, filling it with phy %lx\n", currentPhy); + for (int32 i = 0; i < 8 && dataLength > 0; i++) { + size_t length = min_c(dataLength, packetSize); + itd->token[i] = (EHCI_ITD_STATUS_ACTIVE << EHCI_ITD_STATUS_SHIFT) + | (length << EHCI_ITD_TLENGTH_SHIFT) | (pg << EHCI_ITD_PG_SHIFT) + | (offset << EHCI_ITD_TOFFSET_SHIFT); + itd->last_token = i; + TRACE("isochronous filled slot %ld 0x%lx\n", i, itd->token[i]); + dataLength -= length; + offset += length; + if (dataLength > 0 && offset > 0xfff) { + offset -= B_PAGE_SIZE; + currentPhy += B_PAGE_SIZE; + itd->buffer_phy[pg + 1] = currentPhy & 0xfffff000; + pg++; + } + } + + currentPhy += (offset & 0xfff) - (currentPhy & 0xfff); + + itd->buffer_phy[0] |= (pipe->EndpointAddress() << EHCI_ITD_ENDPOINT_SHIFT) + | (pipe->DeviceAddress() << EHCI_ITD_ADDRESS_SHIFT); + itd->buffer_phy[1] |= (pipe->MaxPacketSize() & EHCI_ITD_MAXPACKETSIZE_MASK) + | (directionIn << EHCI_ITD_DIR_SHIFT); + itd->buffer_phy[2] |= (1 << EHCI_ITD_MUL_SHIFT); + + TRACE("isochronous filled itd buffer_phy[0,1,2] 0x%lx, 0x%lx 0x%lx\n", + itd->buffer_phy[0], itd->buffer_phy[1], itd->buffer_phy[2]); + + LinkITDescriptors(itd, &fItdEntries[currentFrame]); + fFrameBandwidth[currentFrame] -= bandwidth; + currentFrame = (currentFrame + 1) & (EHCI_VFRAMELIST_ENTRIES_COUNT - 1); + frameCount++; + } + + TRACE("isochronous filled itds count %d\n", itdIndex); + + // Add transfer to the list + status_t result = AddPendingIsochronousTransfer(transfer, isoRequest, + itdIndex - 1, directionIn, bufferPhy, bufferLog, + transfer->DataLength()); + if (result < B_OK) { + TRACE_ERROR("failed to add pending isochronous transfer\n"); + return result; + } + + TRACE("appended isochronous transfer by starting at frame number %d\n", + currentFrame); + + // Wake up the isochronous finisher thread + release_sem_etc(fFinishIsochronousTransfersSem, 1 /*frameCount*/, B_DO_NOT_RESCHEDULE); + + return B_OK; +} + + +isochronous_transfer_data * +EHCI::FindIsochronousTransfer(ehci_itd *itd) +{ + // Simply check every last descriptor of the isochronous transfer list + if (LockIsochronous()) { + isochronous_transfer_data *transfer = fFirstIsochronousTransfer; + if (transfer) { + while (transfer->descriptors[transfer->last_to_process] + != itd) { + transfer = transfer->link; + if (!transfer) + break; + } + } + UnlockIsochronous(); + return transfer; + } + return NULL; +} + + status_t EHCI::NotifyPipeChange(Pipe *pipe, usb_change change) { @@ -893,9 +1162,58 @@ EHCI::AddPendingTransfer(Transfer *transfer, ehci_qh *queueHead, } +status_t +EHCI::AddPendingIsochronousTransfer(Transfer *transfer, ehci_itd **isoRequest, + uint32 lastIndex, bool directionIn, addr_t bufferPhy, void* bufferLog, + size_t bufferSize) +{ + if (!transfer || !isoRequest) + return B_BAD_VALUE; + + isochronous_transfer_data *data + = new(std::nothrow) isochronous_transfer_data; + if (!data) + return B_NO_MEMORY; + + status_t result = transfer->InitKernelAccess(); + if (result < B_OK) { + delete data; + return result; + } + + data->transfer = transfer; + data->descriptors = isoRequest; + data->last_to_process = lastIndex; + data->incoming = directionIn; + data->is_active = true; + data->link = NULL; + data->buffer_phy = bufferPhy; + data->buffer_log = bufferLog; + data->buffer_size = bufferSize; + + // Put in the isochronous transfer list + if (!LockIsochronous()) { + delete data; + return B_ERROR; + } + + if (fLastIsochronousTransfer) + fLastIsochronousTransfer->link = data; + else if (!fFirstIsochronousTransfer) + fFirstIsochronousTransfer = data; + + fLastIsochronousTransfer = data; + UnlockIsochronous(); + return B_OK; +} + + status_t EHCI::CancelQueuedTransfers(Pipe *pipe, bool force) { + if (pipe->Type() & USB_OBJECT_ISO_PIPE) + return CancelQueuedIsochronousTransfers(pipe, force); + if (!Lock()) return B_ERROR; @@ -909,10 +1227,10 @@ EHCI::CancelQueuedTransfers(Pipe *pipe, bool force) while (current) { if (current->transfer && current->transfer->TransferPipe() == pipe) { // clear the active bit so the descriptors are canceled - ehci_qtd *descriptor = (ehci_qtd *)current->queue_head->element_log; + ehci_qtd *descriptor = current->queue_head->element_log; while (descriptor) { descriptor->token &= ~EHCI_QTD_STATUS_ACTIVE; - descriptor = (ehci_qtd *)descriptor->next_log; + descriptor = descriptor->next_log; } if (!force) { @@ -955,6 +1273,28 @@ EHCI::CancelQueuedTransfers(Pipe *pipe, bool force) } +status_t +EHCI::CancelQueuedIsochronousTransfers(Pipe *pipe, bool force) +{ + isochronous_transfer_data *current = fFirstIsochronousTransfer; + + while (current) { + if (current->transfer->TransferPipe() == pipe) { + // TODO implement + + // TODO: Use the force paramater in order to avoid calling + // invalid callbacks + current->is_active = false; + } + + current = current->link; + } + + TRACE_ERROR("no isochronous transfer found!\n"); + return B_ERROR; +} + + status_t EHCI::CancelAllPendingTransfers() { @@ -1009,7 +1349,7 @@ EHCI::FinishTransfers() while (transfer) { bool transferDone = false; - ehci_qtd *descriptor = (ehci_qtd *)transfer->queue_head->element_log; + ehci_qtd *descriptor = transfer->queue_head->element_log; status_t callbackStatus = B_OK; while (descriptor) { @@ -1054,7 +1394,7 @@ EHCI::FinishTransfers() break; } - if (descriptor->next_phy & EHCI_QTD_TERMINATE) { + if (descriptor->next_phy & EHCI_ITEM_TERMINATE) { // we arrived at the last (stray) descriptor, we're done TRACE("qtd (0x%08lx) done\n", descriptor->this_phy); callbackStatus = B_OK; @@ -1062,7 +1402,7 @@ EHCI::FinishTransfers() break; } - descriptor = (ehci_qtd *)descriptor->next_log; + descriptor = descriptor->next_log; } if (!transferDone) { @@ -1190,7 +1530,7 @@ EHCI::Cleanup() ehci_qh *current = freeListHead; while (current != lastFreeListHead) { - ehci_qh *next = (ehci_qh *)current->next_log; + ehci_qh *next = current->next_log; FreeQueueHead(current); current = next; } @@ -1200,6 +1540,129 @@ EHCI::Cleanup() } +int32 +EHCI::FinishIsochronousThread(void *data) +{ + ((EHCI *)data)->FinishIsochronousTransfers(); + return B_OK; +} + + +void +EHCI::FinishIsochronousTransfers() +{ + /* This thread stays one position behind the controller and processes every + * isochronous descriptor. Once it finds the last isochronous descriptor + * of a transfer, it processes the entire transfer. + */ + while (!fStopThreads) { + // Go to sleep if there are not isochronous transfer to process + if (acquire_sem(fFinishIsochronousTransfersSem) < B_OK) + return; + + bool transferDone = false; + + uint32 frame = (ReadOpReg(EHCI_FRINDEX) / 8 ) + & (EHCI_FRAMELIST_ENTRIES_COUNT - 1); + uint32 currentFrame = (frame + EHCI_VFRAMELIST_ENTRIES_COUNT - 5) + & (EHCI_VFRAMELIST_ENTRIES_COUNT - 1); + uint32 loop = 0; + + // Process the frame list until one transfer is processed + while (!transferDone && loop++ < EHCI_VFRAMELIST_ENTRIES_COUNT) { + // wait 1ms in order to be sure to be one position behind + // the controller + while (currentFrame == (((ReadOpReg(EHCI_FRINDEX) / 8) + & (EHCI_VFRAMELIST_ENTRIES_COUNT - 1)))) { + snooze(1000); + } + + ehci_itd *itd = fItdEntries[currentFrame]; + + TRACE("FinishIsochronousTransfers itd %p phy 0x%lx prev (%p/0x%lx)" + " at frame %ld\n", itd, itd->this_phy, itd->prev, + itd->prev != NULL ? itd->prev->this_phy : 0, currentFrame); + + // Process the frame till it has isochronous descriptors in it. + while (!(itd->next_phy & EHCI_ITEM_TERMINATE) && itd->prev != NULL) { + TRACE("FinishIsochronousTransfers checking itd %p last_token" + " %ld\n", itd, itd->last_token); + TRACE("FinishIsochronousTransfers tokens 0x%lx 0x%lx 0x%lx " + "0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", + itd->token[0], itd->token[1], itd->token[2], itd->token[3], + itd->token[4], itd->token[5], itd->token[6], itd->token[7]); + if (((itd->token[itd->last_token] >> EHCI_ITD_STATUS_SHIFT) + & EHCI_ITD_STATUS_ACTIVE) == EHCI_ITD_STATUS_ACTIVE) { + TRACE("FinishIsochronousTransfers unprocessed active itd\n"); + } + UnlinkITDescriptors(itd, &fItdEntries[currentFrame]); + + // Process the transfer if we found the last descriptor + isochronous_transfer_data *transfer + = FindIsochronousTransfer(itd); + // Process the descriptors only if it is still active and + // belongs to an inbound transfer. If the transfer is not + // active, it means the request has been removed, so simply + // remove the descriptors. + if (transfer && transfer->is_active) { + TRACE("FinishIsochronousTransfers active transfer\n"); + size_t actualLength = 0; + if (((itd->buffer_phy[1] >> EHCI_ITD_DIR_SHIFT) & 1) != 0) { + transfer->transfer->PrepareKernelAccess(); + actualLength = ReadIsochronousDescriptorChain(transfer); + } + + // Remove the transfer + if (LockIsochronous()) { + if (transfer == fFirstIsochronousTransfer) { + fFirstIsochronousTransfer = transfer->link; + if (transfer == fLastIsochronousTransfer) + fLastIsochronousTransfer = NULL; + } else { + isochronous_transfer_data *temp + = fFirstIsochronousTransfer; + while (temp != NULL && transfer != temp->link) + temp = temp->link; + + if (transfer == fLastIsochronousTransfer) + fLastIsochronousTransfer = temp; + if (temp != NULL && temp->link != NULL) + temp->link = temp->link->link; + } + transfer->link = NULL; + UnlockIsochronous(); + } + + transfer->transfer->Finished(B_OK, actualLength); + + for (uint32 i = 0; i <= transfer->last_to_process; i++) + FreeDescriptor(transfer->descriptors[i]); + + TRACE("FinishIsochronousTransfers descriptors freed\n"); + + delete [] transfer->descriptors; + delete transfer->transfer; + fStack->FreeChunk(transfer->buffer_log, + (void *)transfer->buffer_phy, transfer->buffer_size); + delete transfer; + transferDone = true; + } else { + TRACE("FinishIsochronousTransfers not end of transfer\n"); + } + itd = itd->prev; + } + + TRACE("FinishIsochronousTransfers next frame\n"); + + // Make sure to reset the frame bandwidth + fFrameBandwidth[currentFrame] = MAX_AVAILABLE_BANDWIDTH; + currentFrame = (currentFrame + 1) % EHCI_VFRAMELIST_ENTRIES_COUNT; + + } + } +} + + ehci_qh * EHCI::CreateQueueHead() { @@ -1211,24 +1674,24 @@ EHCI::CreateQueueHead() return NULL; } - result->this_phy = (addr_t)physicalAddress; - result->next_phy = EHCI_QH_TERMINATE; + result->this_phy = (addr_t)physicalAddress | EHCI_ITEM_TYPE_QH; + result->next_phy = EHCI_ITEM_TERMINATE; result->next_log = NULL; result->prev_log = NULL; ehci_qtd *descriptor = CreateDescriptor(0, 0); if (!descriptor) { TRACE_ERROR("failed to allocate initial qtd for queue head\n"); - fStack->FreeChunk(result, (void *)result->this_phy, sizeof(ehci_qh)); + fStack->FreeChunk(result, physicalAddress, sizeof(ehci_qh)); return NULL; } descriptor->token &= ~EHCI_QTD_STATUS_ACTIVE; result->stray_log = descriptor; result->element_log = descriptor; - result->current_qtd_phy = EHCI_QTD_TERMINATE; + result->current_qtd_phy = EHCI_ITEM_TERMINATE; result->overlay.next_phy = descriptor->this_phy; - result->overlay.alt_next_phy = EHCI_QTD_TERMINATE; + result->overlay.alt_next_phy = EHCI_ITEM_TERMINATE; result->overlay.token = 0; for (int32 i = 0; i < 5; i++) { result->overlay.buffer_phy[i] = 0; @@ -1282,8 +1745,8 @@ EHCI::FreeQueueHead(ehci_qh *queueHead) if (!queueHead) return; - FreeDescriptorChain((ehci_qtd *)queueHead->element_log); - FreeDescriptor((ehci_qtd *)queueHead->stray_log); + FreeDescriptorChain(queueHead->element_log); + FreeDescriptor(queueHead->stray_log); fStack->FreeChunk(queueHead, (void *)queueHead->this_phy, sizeof(ehci_qh)); } @@ -1294,13 +1757,13 @@ EHCI::LinkQueueHead(ehci_qh *queueHead) if (!Lock()) return B_ERROR; - ehci_qh *prevHead = (ehci_qh *)fAsyncQueueHead->prev_log; - queueHead->next_phy = fAsyncQueueHead->this_phy | EHCI_QH_TYPE_QH; + ehci_qh *prevHead = fAsyncQueueHead->prev_log; + queueHead->next_phy = fAsyncQueueHead->this_phy; queueHead->next_log = fAsyncQueueHead; queueHead->prev_log = prevHead; fAsyncQueueHead->prev_log = queueHead; prevHead->next_log = queueHead; - prevHead->next_phy = queueHead->this_phy | EHCI_QH_TYPE_QH; + prevHead->next_phy = queueHead->this_phy; Unlock(); return B_OK; @@ -1336,19 +1799,19 @@ EHCI::LinkInterruptQueueHead(ehci_qh *queueHead, Pipe *pipe) interval = 1; // this may happen as intervals can go up to 16; we limit the value to - // 11 as you cannot support intervals above that with a frame list of - // just 1024 entries... - if (interval > 11) - interval = 11; + // EHCI_INTERRUPT_ENTRIES_COUNT as you cannot support intervals above + // that with a frame list of just EHCI_VFRAMELIST_ENTRIES_COUNT entries... + if (interval > EHCI_INTERRUPT_ENTRIES_COUNT) + interval = EHCI_INTERRUPT_ENTRIES_COUNT; ehci_qh *interruptQueue = &fInterruptEntries[interval - 1].queue_head; queueHead->next_phy = interruptQueue->next_phy; queueHead->next_log = interruptQueue->next_log; queueHead->prev_log = interruptQueue; if (interruptQueue->next_log) - ((ehci_qh *)interruptQueue->next_log)->prev_log = queueHead; + interruptQueue->next_log->prev_log = queueHead; interruptQueue->next_log = queueHead; - interruptQueue->next_phy = queueHead->this_phy | EHCI_QH_TYPE_QH; + interruptQueue->next_phy = queueHead->this_phy; Unlock(); return B_OK; @@ -1361,17 +1824,17 @@ EHCI::UnlinkQueueHead(ehci_qh *queueHead, ehci_qh **freeListHead) if (!Lock()) return B_ERROR; - ehci_qh *prevHead = (ehci_qh *)queueHead->prev_log; - ehci_qh *nextHead = (ehci_qh *)queueHead->next_log; + ehci_qh *prevHead = queueHead->prev_log; + ehci_qh *nextHead = queueHead->next_log; if (prevHead) { - prevHead->next_phy = queueHead->next_phy | EHCI_QH_TYPE_QH; + prevHead->next_phy = queueHead->next_phy; prevHead->next_log = queueHead->next_log; } if (nextHead) nextHead->prev_log = queueHead->prev_log; - queueHead->next_phy = fAsyncQueueHead->this_phy | EHCI_QH_TYPE_QH; + queueHead->next_phy = fAsyncQueueHead->this_phy; queueHead->prev_log = NULL; queueHead->next_log = *freeListHead; @@ -1407,7 +1870,7 @@ EHCI::FillQueueWithRequest(Transfer *transfer, ehci_qh *queueHead, vector.iov_len = sizeof(usb_request_data); WriteDescriptorChain(setupDescriptor, &vector, 1); - ehci_qtd *strayDescriptor = (ehci_qtd *)queueHead->stray_log; + ehci_qtd *strayDescriptor = queueHead->stray_log; statusDescriptor->token |= EHCI_QTD_IOC | EHCI_QTD_DATA_TOGGLE; ehci_qtd *dataDescriptor = NULL; @@ -1437,7 +1900,7 @@ EHCI::FillQueueWithRequest(Transfer *transfer, ehci_qh *queueHead, queueHead->element_log = setupDescriptor; queueHead->overlay.next_phy = setupDescriptor->this_phy; - queueHead->overlay.alt_next_phy = EHCI_QTD_TERMINATE; + queueHead->overlay.alt_next_phy = EHCI_ITEM_TERMINATE; *_dataDescriptor = dataDescriptor; *_directionIn = directionIn; @@ -1454,7 +1917,7 @@ EHCI::FillQueueWithData(Transfer *transfer, ehci_qh *queueHead, ehci_qtd *firstDescriptor = NULL; ehci_qtd *lastDescriptor = NULL; - ehci_qtd *strayDescriptor = (ehci_qtd *)queueHead->stray_log; + ehci_qtd *strayDescriptor = queueHead->stray_log; status_t result = CreateDescriptorChain(pipe, &firstDescriptor, &lastDescriptor, strayDescriptor, transfer->VectorLength(), directionIn ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT); @@ -1470,7 +1933,7 @@ EHCI::FillQueueWithData(Transfer *transfer, ehci_qh *queueHead, queueHead->element_log = firstDescriptor; queueHead->overlay.next_phy = firstDescriptor->this_phy; - queueHead->overlay.alt_next_phy = EHCI_QTD_TERMINATE; + queueHead->overlay.alt_next_phy = EHCI_ITEM_TERMINATE; *_dataDescriptor = firstDescriptor; if (_directionIn) @@ -1491,9 +1954,9 @@ EHCI::CreateDescriptor(size_t bufferSize, uint8 pid) } result->this_phy = (addr_t)physicalAddress; - result->next_phy = EHCI_QTD_TERMINATE; + result->next_phy = EHCI_ITEM_TERMINATE; result->next_log = NULL; - result->alt_next_phy = EHCI_QTD_TERMINATE; + result->alt_next_phy = EHCI_ITEM_TERMINATE; result->alt_next_log = NULL; result->buffer_size = bufferSize; result->token = bufferSize << EHCI_QTD_BYTES_SHIFT; @@ -1590,13 +2053,71 @@ EHCI::FreeDescriptorChain(ehci_qtd *topDescriptor) ehci_qtd *next = NULL; while (current) { - next = (ehci_qtd *)current->next_log; + next = current->next_log; FreeDescriptor(current); current = next; } } +ehci_itd * +EHCI::CreateItdDescriptor() +{ + ehci_itd *result; + void *physicalAddress; + if (fStack->AllocateChunk((void **)&result, &physicalAddress, + sizeof(ehci_itd)) < B_OK) { + TRACE_ERROR("failed to allocate a itd\n"); + return NULL; + } + + memset(result, 0, sizeof(ehci_itd)); + result->this_phy = (addr_t)physicalAddress; + result->next_phy = EHCI_ITEM_TERMINATE; + + return result; +} + + +ehci_sitd * +EHCI::CreateSitdDescriptor() +{ + ehci_sitd *result; + void *physicalAddress; + if (fStack->AllocateChunk((void **)&result, &physicalAddress, + sizeof(ehci_sitd)) < B_OK) { + TRACE_ERROR("failed to allocate a sitd\n"); + return NULL; + } + + memset(result, 0, sizeof(ehci_sitd)); + result->this_phy = (addr_t)physicalAddress | EHCI_ITEM_TYPE_SITD; + result->next_phy = EHCI_ITEM_TERMINATE; + + return result; +} + + +void +EHCI::FreeDescriptor(ehci_itd *descriptor) +{ + if (!descriptor) + return; + + fStack->FreeChunk(descriptor, (void *)descriptor->this_phy, sizeof(ehci_itd)); +} + + +void +EHCI::FreeDescriptor(ehci_sitd *descriptor) +{ + if (!descriptor) + return; + + fStack->FreeChunk(descriptor, (void *)descriptor->this_phy, sizeof(ehci_sitd)); +} + + void EHCI::LinkDescriptors(ehci_qtd *first, ehci_qtd *last, ehci_qtd *alt) { @@ -1607,12 +2128,61 @@ EHCI::LinkDescriptors(ehci_qtd *first, ehci_qtd *last, ehci_qtd *alt) first->alt_next_phy = alt->this_phy; first->alt_next_log = alt; } else { - first->alt_next_phy = EHCI_QTD_TERMINATE; + first->alt_next_phy = EHCI_ITEM_TERMINATE; first->alt_next_log = NULL; } } +void +EHCI::LinkITDescriptors(ehci_itd *itd, ehci_itd **_last) +{ + ehci_itd *last = *_last; + itd->next_phy = last->next_phy; + itd->next = NULL; + itd->prev = last; + last->next = itd; + last->next_phy = itd->this_phy; + *_last = itd; +} + + +void +EHCI::LinkSITDescriptors(ehci_sitd *sitd, ehci_sitd **_last) +{ + ehci_sitd *last = *_last; + sitd->next_phy = last->next_phy; + sitd->next = NULL; + sitd->prev = last; + last->next = sitd; + last->next_phy = sitd->this_phy; + *_last = sitd; +} + +void +EHCI::UnlinkITDescriptors(ehci_itd *itd, ehci_itd **last) +{ + itd->prev->next_phy = itd->next_phy; + itd->prev->next = itd->next; + if (itd->next != NULL) + itd->next->prev = itd->prev; + if (itd == *last) + *last = itd->prev; +} + + +void +EHCI::UnlinkSITDescriptors(ehci_sitd *sitd, ehci_sitd **last) +{ + sitd->prev->next_phy = sitd->next_phy; + sitd->prev->next = sitd->next; + if (sitd->next != NULL) + sitd->next->prev = sitd->prev; + if (sitd == *last) + *last = sitd->prev; +} + + size_t EHCI::WriteDescriptorChain(ehci_qtd *topDescriptor, iovec *vector, size_t vectorCount) @@ -1640,7 +2210,8 @@ EHCI::WriteDescriptorChain(ehci_qtd *topDescriptor, iovec *vector, if (vectorOffset >= vector[vectorIndex].iov_len) { if (++vectorIndex >= vectorCount) { - TRACE("wrote descriptor chain (%ld bytes, no more vectors)\n", actualLength); + TRACE("wrote descriptor chain (%ld bytes, no more vectors)" + "\n", actualLength); return actualLength; } @@ -1653,10 +2224,10 @@ EHCI::WriteDescriptorChain(ehci_qtd *topDescriptor, iovec *vector, } } - if (current->next_phy & EHCI_QTD_TERMINATE) + if (current->next_phy & EHCI_ITEM_TERMINATE) break; - current = (ehci_qtd *)current->next_log; + current = current->next_log; } TRACE("wrote descriptor chain (%ld bytes)\n", actualLength); @@ -1681,7 +2252,8 @@ EHCI::ReadDescriptorChain(ehci_qtd *topDescriptor, iovec *vector, dataToggle = current->token & EHCI_QTD_DATA_TOGGLE; size_t bufferSize = current->buffer_size; - bufferSize -= (current->token >> EHCI_QTD_BYTES_SHIFT) & EHCI_QTD_BYTES_MASK; + bufferSize -= (current->token >> EHCI_QTD_BYTES_SHIFT) + & EHCI_QTD_BYTES_MASK; while (true) { size_t length = min_c(bufferSize - bufferOffset, @@ -1710,10 +2282,10 @@ EHCI::ReadDescriptorChain(ehci_qtd *topDescriptor, iovec *vector, } } - if (current->next_phy & EHCI_QTD_TERMINATE) + if (current->next_phy & EHCI_ITEM_TERMINATE) break; - current = (ehci_qtd *)current->next_log; + current = current->next_log; } TRACE("read descriptor chain (%ld bytes)\n", actualLength); @@ -1735,10 +2307,10 @@ EHCI::ReadActualLength(ehci_qtd *topDescriptor, bool *nextDataToggle) length -= (current->token >> EHCI_QTD_BYTES_SHIFT) & EHCI_QTD_BYTES_MASK; actualLength += length; - if (current->next_phy & EHCI_QTD_TERMINATE) + if (current->next_phy & EHCI_ITEM_TERMINATE) break; - current = (ehci_qtd *)current->next_log; + current = current->next_log; } TRACE("read actual length (%ld bytes)\n", actualLength); @@ -1747,6 +2319,98 @@ EHCI::ReadActualLength(ehci_qtd *topDescriptor, bool *nextDataToggle) } +size_t +EHCI::WriteIsochronousDescriptorChain(isochronous_transfer_data *transfer, + uint32 packetCount, iovec *vector) +{ + // TODO implement + return 0; +} + + +size_t +EHCI::ReadIsochronousDescriptorChain(isochronous_transfer_data *transfer) +{ + iovec *vector = transfer->transfer->Vector(); + size_t vectorCount = transfer->transfer->VectorCount(); + + size_t vectorOffset = 0; + size_t vectorIndex = 0; + usb_isochronous_data *isochronousData + = transfer->transfer->IsochronousData(); + uint32 packet = 0; + size_t totalLength = 0; + size_t bufferOffset = 0; + + size_t packetSize = transfer->transfer->DataLength(); + packetSize /= isochronousData->packet_count; + + for (uint32 i = 0; i <= transfer->last_to_process; i++) { + ehci_itd *itd = transfer->descriptors[i]; + for (uint32 j = 0; j <= itd->last_token + && packet < isochronousData->packet_count; j++) { + + size_t bufferSize = (itd->token[j] >> EHCI_ITD_TLENGTH_SHIFT) + & EHCI_ITD_TLENGTH_MASK; + isochronousData->packet_descriptors[packet].actual_length = + bufferSize; + + if (bufferSize > 0) + isochronousData->packet_descriptors[packet].status = B_OK; + else + isochronousData->packet_descriptors[packet].status = B_ERROR; + + totalLength += bufferSize; + + size_t offset = bufferOffset; + while (bufferSize > 0) { + size_t length = min_c(bufferSize, + vector[vectorIndex].iov_len - vectorOffset); + memcpy((uint8 *)vector[vectorIndex].iov_base + vectorOffset, + (uint8 *)transfer->buffer_log + bufferOffset, length); + offset += length; + vectorOffset += length; + bufferSize -= length; + + if (vectorOffset >= vector[vectorIndex].iov_len) { + if (++vectorIndex >= vectorCount) { + TRACE("read isodescriptor chain (%ld bytes, no more " + "vectors)\n", totalLength); + return totalLength; + } + + vectorOffset = 0; + } + } + + bufferOffset += packetSize; + if (bufferOffset >= transfer->buffer_size) + return totalLength; + + packet++; + } + } + + TRACE("ReadIsochronousDescriptorChain packet count %ld\n", packet); + + return totalLength; +} + + +bool +EHCI::LockIsochronous() +{ + return (mutex_lock(&fIsochronousLock) == B_OK); +} + + +void +EHCI::UnlockIsochronous() +{ + mutex_unlock(&fIsochronousLock); +} + + inline void EHCI::WriteOpReg(uint32 reg, uint32 value) { diff --git a/src/add-ons/kernel/busses/usb/ehci.h b/src/add-ons/kernel/busses/usb/ehci.h index 200cebc472..7f10975fde 100644 --- a/src/add-ons/kernel/busses/usb/ehci.h +++ b/src/add-ons/kernel/busses/usb/ehci.h @@ -27,6 +27,26 @@ typedef struct transfer_data { } transfer_data; +// This structure is used to create a list of +// descriptors per isochronous transfer +typedef struct isochronous_transfer_data { + Transfer * transfer; + // The next field is used to keep track + // of every isochronous descriptor as they are NOT + // linked to each other in a queue like in every other + // transfer type + ehci_itd ** descriptors; + uint16 last_to_process; + bool incoming; + bool is_active; + isochronous_transfer_data * link; + + size_t buffer_size; + void * buffer_log; + addr_t buffer_phy; +} isochronous_transfer_data; + + class EHCI : public BusManager { public: EHCI(pci_info *info, Stack *stack); @@ -35,7 +55,9 @@ public: status_t Start(); virtual status_t SubmitTransfer(Transfer *transfer); virtual status_t CancelQueuedTransfers(Pipe *pipe, bool force); - + status_t CancelQueuedIsochronousTransfers(Pipe *pipe, bool force); + status_t SubmitIsochronous(Transfer *transfer); + virtual status_t NotifyPipeChange(Pipe *pipe, usb_change change); @@ -66,13 +88,32 @@ static int32 InterruptHandler(void *data); ehci_qh *queueHead, ehci_qtd *dataDescriptor, bool directionIn); + status_t AddPendingIsochronousTransfer( + Transfer *transfer, + ehci_itd **isoRequest, uint32 lastIndex, + bool directionIn, addr_t bufferPhy, + void *bufferLog, size_t bufferSize); status_t CancelAllPendingTransfers(); + static int32 FinishThread(void *data); void FinishTransfers(); static int32 CleanupThread(void *data); void Cleanup(); + // Isochronous transfer functions +static int32 FinishIsochronousThread(void *data); + void FinishIsochronousTransfers(); + isochronous_transfer_data * FindIsochronousTransfer(ehci_itd *itd); + void LinkITDescriptors(ehci_itd *itd, + ehci_itd **last); + void LinkSITDescriptors(ehci_sitd *sitd, + ehci_sitd **last); + void UnlinkITDescriptors(ehci_itd *itd, + ehci_itd **last); + void UnlinkSITDescriptors(ehci_sitd *sitd, + ehci_sitd **last); + // Queue Head functions ehci_qh * CreateQueueHead(); status_t InitQueueHead(ehci_qh *queueHead, @@ -95,6 +136,9 @@ static int32 CleanupThread(void *data); ehci_qtd **dataDescriptor, bool *directionIn); + bool LockIsochronous(); + void UnlockIsochronous(); + // Descriptor functions ehci_qtd * CreateDescriptor( size_t bufferSizeToAllocate, @@ -105,9 +149,15 @@ static int32 CleanupThread(void *data); ehci_qtd *strayDescriptor, size_t bufferSizeToAllocate, uint8 pid); + ehci_itd* CreateItdDescriptor(); + ehci_sitd* CreateSitdDescriptor(); void FreeDescriptor(ehci_qtd *descriptor); void FreeDescriptorChain(ehci_qtd *topDescriptor); + void FreeDescriptor(ehci_itd *descriptor); + void FreeDescriptor(ehci_sitd *descriptor); + void FreeIsochronousData( + isochronous_transfer_data *data); void LinkDescriptors(ehci_qtd *first, ehci_qtd *last, ehci_qtd *alt); @@ -120,6 +170,12 @@ static int32 CleanupThread(void *data); bool *nextDataToggle); size_t ReadActualLength(ehci_qtd *topDescriptor, bool *nextDataToggle); + size_t WriteIsochronousDescriptorChain( + isochronous_transfer_data *transfer, + uint32 packetCount, + iovec *vector); + size_t ReadIsochronousDescriptorChain( + isochronous_transfer_data *transfer); // Operational register functions inline void WriteOpReg(uint32 reg, uint32 value); @@ -143,6 +199,8 @@ static pci_module_info * sPCIModule; area_id fPeriodicFrameListArea; addr_t * fPeriodicFrameList; interrupt_entry * fInterruptEntries; + ehci_itd ** fItdEntries; + ehci_sitd ** fSitdEntries; // Async transfer queue management ehci_qh * fAsyncQueueHead; @@ -153,12 +211,24 @@ static pci_module_info * sPCIModule; transfer_data * fLastTransfer; sem_id fFinishTransfersSem; thread_id fFinishThread; + Pipe * fProcessingPipe; + + ehci_qh * fFreeListHead; sem_id fCleanupSem; thread_id fCleanupThread; bool fStopThreads; - ehci_qh * fFreeListHead; - Pipe * fProcessingPipe; + // fFrameBandwidth[n] holds the available bandwidth + // of the nth frame in microseconds + uint16 * fFrameBandwidth; + + // Maintain a linked list of isochronous transfers + isochronous_transfer_data * fFirstIsochronousTransfer; + isochronous_transfer_data * fLastIsochronousTransfer; + sem_id fFinishIsochronousTransfersSem; + thread_id fFinishIsochronousThread; + mutex fIsochronousLock; + // Root Hub EHCIRootHub * fRootHub; uint8 fRootHubAddress; diff --git a/src/add-ons/kernel/busses/usb/ehci_hardware.h b/src/add-ons/kernel/busses/usb/ehci_hardware.h index 112bea8f32..c5f252880d 100644 --- a/src/add-ons/kernel/busses/usb/ehci_hardware.h +++ b/src/add-ons/kernel/busses/usb/ehci_hardware.h @@ -16,7 +16,6 @@ #define EHCI_HCCPARAMS 0x08 // Capability Parameters #define EHCI_HCSP_PORTROUTE 0x0c // Companion Port Route Description - // Host Controller Operational Registers (EHCI Spec 2.3) #define EHCI_USBCMD 0x00 // USB Command #define EHCI_USBSTS 0x04 // USB Status @@ -105,22 +104,89 @@ #define EHCI_LEGSUP_OSOWNED (1 << 24) // OS Owned Semaphore #define EHCI_LEGSUP_BIOSOWNED (1 << 16) // BIOS Owned Semaphore +#define EHCI_HCCPARAMS_IPT_SHIFT 4 // Isochronous Periodic Threshold +#define EHCI_HCCPARAMS_IPT_MASK 0xf + // Data Structures (EHCI Spec 3) -// Periodic Frame List Element Flags (EHCI Spec 3.1) -#define EHCI_PFRAMELIST_TERM (1 << 0) // Terminate -#define EHCI_PFRAMELIST_ITD (0 << 1) // Isochronous Transfer Descriptor -#define EHCI_PFRAMELIST_QH (1 << 1) // Queue Head -#define EHCI_PFRAMELIST_SITD (2 << 1) // Split Transaction Isochronous TD -#define EHCI_PFRAMELIST_FSTN (3 << 1) // Frame Span Traversal Node + +// Applies to ehci_qh.next_phy, ehci_sitd.next_phy, ehci_itd.next_phy +#define EHCI_ITEM_TYPE_ITD (0 << 1) // Isochronous Transfer Descriptor +#define EHCI_ITEM_TYPE_QH (1 << 1) // Queue Head +#define EHCI_ITEM_TYPE_SITD (2 << 1) // Split Transaction Isochronous TD +#define EHCI_ITEM_TYPE_FSTN (3 << 1) // Frame Span Traversal Node +#define EHCI_ITEM_TERMINATE (1 << 0) // Terminate -// ToDo: Isochronous (High-Speed) Transfer Descriptors (iTD, EHCI Spec 3.2) -// ToDo: Split Transaction Isochronous Transfer Descriptors (siTD, EHCI Spec 3.3) +// Isochronous (High-Speed) Transfer Descriptors (iTD, EHCI Spec 3.2) +typedef struct ehci_itd { + // Hardware Part + addr_t next_phy; + uint32 token[8]; + addr_t buffer_phy[7]; + addr_t ext_buffer_phy[7]; + + // Software Part + addr_t this_phy; + struct ehci_itd *next; + struct ehci_itd *prev; + uint32 last_token; +} ehci_itd; + +#define EHCI_ITD_TOFFSET_SHIFT 0 +#define EHCI_ITD_TOFFSET_MASK 0x0fff +#define EHCI_ITD_IOC (1 << 15) +#define EHCI_ITD_PG_SHIFT 12 +#define EHCI_ITD_PG_MASK 0x07 +#define EHCI_ITD_TLENGTH_SHIFT 16 +#define EHCI_ITD_TLENGTH_MASK 0x0fff +#define EHCI_ITD_STATUS_SHIFT 28 +#define EHCI_ITD_STATUS_MASK 0xf +#define EHCI_ITD_STATUS_ACTIVE (1 << 3) // Active +#define EHCI_ITD_STATUS_BUFFER (1 << 2) // Data Buffer Error +#define EHCI_ITD_STATUS_BABBLE (1 << 1) // Babble Detected +#define EHCI_ITD_STATUS_TERROR (1 << 0) // Transaction Error +#define EHCI_ITD_ADDRESS_SHIFT 0 +#define EHCI_ITD_ADDRESS_MASK 0x7f +#define EHCI_ITD_ENDPOINT_SHIFT 8 +#define EHCI_ITD_ENDPOINT_MASK 0xf +#define EHCI_ITD_DIR_SHIFT 11 +#define EHCI_ITD_MUL_SHIFT 0 +#define EHCI_ITD_BUFFERPOINTER_SHIFT 12 +#define EHCI_ITD_BUFFERPOINTER_MASK 0xfffff +#define EHCI_ITD_MAXPACKETSIZE_SHIFT 0 +#define EHCI_ITD_MAXPACKETSIZE_MASK 0x7ff + + +// Split Transaction Isochronous Transfer Descriptors (siTD, EHCI Spec 3.3) +typedef struct ehci_sitd { + // Hardware Part + addr_t next_phy; + uint8 port_number; + uint8 hub_address; + uint8 endpoint; + uint8 device_address; + uint16 reserved1; + uint8 cmask; + uint8 smask; + uint16 transfer_length; + uint8 cprogmask; + uint8 status; + addr_t buffer_phy[2]; + addr_t back_phy; + addr_t ext_buffer_phy[2]; + + // Software Part + addr_t this_phy; + struct ehci_sitd *next; + struct ehci_sitd *prev; + size_t buffer_size; + void *buffer_log; +} ehci_sitd; // Queue Element Transfer Descriptors (qTD, EHCI Spec 3.5) -typedef struct { +typedef struct ehci_qtd { // Hardware Part addr_t next_phy; addr_t alt_next_phy; @@ -130,14 +196,13 @@ typedef struct { // Software Part addr_t this_phy; - void *next_log; + struct ehci_qtd *next_log; void *alt_next_log; size_t buffer_size; void *buffer_log; } ehci_qtd; -#define EHCI_QTD_TERMINATE (1 << 0) #define EHCI_QTD_DATA_TOGGLE (1 << 31) #define EHCI_QTD_BYTES_SHIFT 16 #define EHCI_QTD_BYTES_MASK 0x7fff @@ -166,7 +231,7 @@ typedef struct { // Queue Head (QH, EHCI Spec 3.6) -typedef struct { +typedef struct ehci_qh { // Hardware Part addr_t next_phy; uint32 endpoint_chars; @@ -183,10 +248,10 @@ typedef struct { // Software Part addr_t this_phy; - void *next_log; - void *prev_log; - void *stray_log; - void *element_log; + struct ehci_qh *next_log; + struct ehci_qh *prev_log; + ehci_qtd *stray_log; + ehci_qtd *element_log; } ehci_qh; @@ -195,13 +260,22 @@ typedef struct { uint32 padding[2]; } interrupt_entry; +typedef struct { + ehci_itd itd; + uint32 padding[5]; // align on 128 +} itd_entry; + +typedef struct { + ehci_sitd sitd; + uint32 padding[2]; // align on 64 +} sitd_entry; + +#define EHCI_INTERRUPT_ENTRIES_COUNT (7 + 1) // (log 128 / log 2) + 1 +#define EHCI_VFRAMELIST_ENTRIES_COUNT 128 +#define EHCI_FRAMELIST_ENTRIES_COUNT 1024 + +#define MAX_AVAILABLE_BANDWIDTH 125 // Microseconds -// Applies to ehci_qh.link_phy -#define EHCI_QH_TYPE_ITD (0 << 1) -#define EHCI_QH_TYPE_QH (1 << 1) -#define EHCI_QH_TYPE_SITD (2 << 1) -#define EHCI_QH_TYPE_FSTN (3 << 1) -#define EHCI_QH_TERMINATE (1 << 0) // Applies to ehci_qh.endpoint_chars #define EHCI_QH_CHARS_RL_SHIFT 28 // NAK Count Reload