Author: korli Date: 2011-05-10 23:25:32 +0200 (Tue, 10 May 2011) New Revision: 41424 Changeset: https://dev.haiku-os.org/changeset/41424 Modified: haiku/trunk/src/add-ons/kernel/busses/usb/ehci.cpp haiku/trunk/src/add-ons/kernel/busses/usb/ehci.h haiku/trunk/src/add-ons/kernel/busses/usb/ehci_hardware.h Log: * 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. Modified: haiku/trunk/src/add-ons/kernel/busses/usb/ehci.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/busses/usb/ehci.cpp 2011-05-10 21:05:41 UTC (rev 41423) +++ haiku/trunk/src/add-ons/kernel/busses/usb/ehci.cpp 2011-05-10 21:25:32 UTC (rev 41424) @@ -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 <mmlr@xxxxxxxx> + * Jérôme Duval <korli@xxxxxxxxxxxxxxxx> */ + #include <module.h> #include <PCI.h> #include <USB3.h> @@ -69,10 +71,10 @@ 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 @@ 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 @@ 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 @@ 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_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 @@ 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 @@ 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 @@ // 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 @@ 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 @@ 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 @@ | (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 @@ 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) { @@ -497,6 +601,171 @@ 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) { TRACE("pipe change %d for pipe %p\n", change, pipe); @@ -894,8 +1163,57 @@ 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 @@ 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) { @@ -956,6 +1274,28 @@ 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() { if (!Lock()) @@ -1009,7 +1349,7 @@ 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 @@ 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 @@ break; } - descriptor = (ehci_qtd *)descriptor->next_log; + descriptor = descriptor->next_log; } if (!transferDone) { @@ -1190,7 +1530,7 @@ 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 @@ } +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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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_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 @@ 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 @@ } 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,14 +2053,72 @@ 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) { first->next_phy = last->this_phy; @@ -1607,12 +2128,61 @@ 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; [... truncated: 525 lines follow ...]