From 77660b03e844ff20dc766a3033f4ee0d776983fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Tue, 23 Aug 2011 18:36:57 +0000 Subject: [PATCH] * threshold higher bit means a full frame. Also use the threshold for micro frames instead of frames. * introduces fNextStartingFrame to keep track of the next frame to use on next submit. * set the IOC bit for the last ITD * computes multiply field of the ITD based on the packed size (1, 2 or 3). * use locking around linking of ITD * free descriptors on errors * on finishing, whenever a non success status is found, set actual length to zero * on finishing, when copying data to the Transfer object, copy starting on packet boundaries (skipping unused bytes). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42679 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/busses/usb/ehci.cpp | 81 ++++++++++++++----- src/add-ons/kernel/busses/usb/ehci.h | 2 + src/add-ons/kernel/busses/usb/ehci_hardware.h | 6 +- 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/src/add-ons/kernel/busses/usb/ehci.cpp b/src/add-ons/kernel/busses/usb/ehci.cpp index 82a6f9a737..b8ad5190e7 100644 --- a/src/add-ons/kernel/busses/usb/ehci.cpp +++ b/src/add-ons/kernel/busses/usb/ehci.cpp @@ -123,6 +123,7 @@ EHCI::EHCI(pci_info *info, Stack *stack) fCleanupSem(-1), fCleanupThread(-1), fStopThreads(false), + fNextStartingFrame(-1), fFrameBandwidth(NULL), fFirstIsochronousTransfer(NULL), fLastIsochronousTransfer(NULL), @@ -221,6 +222,11 @@ EHCI::EHCI(pci_info *info, Stack *stack) TRACE("structural parameters: 0x%08lx\n", ReadCapReg32(EHCI_HCSPARAMS)); TRACE("capability parameters: 0x%08lx\n", ReadCapReg32(EHCI_HCCPARAMS)); + if (EHCI_HCCPARAMS_FRAME_CACHE(ReadCapReg32(EHCI_HCCPARAMS))) + fThreshold = 2 + 8; + else + fThreshold = 2 + EHCI_HCCPARAMS_IPT(ReadCapReg32(EHCI_HCCPARAMS)); + // read port count from capability register fPortCount = ReadCapReg32(EHCI_HCSPARAMS) & 0x0f; @@ -730,24 +736,26 @@ EHCI::SubmitIsochronous(Transfer *transfer) 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); + if (fFirstIsochronousTransfer != NULL && fNextStartingFrame != -1) + currentFrame = fNextStartingFrame; + else { + uint32 threshold = fThreshold; + 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); + // 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) + threshold) / 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); + currentFrame &= 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 @@ -762,6 +770,7 @@ EHCI::SubmitIsochronous(Transfer *transfer) addr_t bufferPhy; if (fStack->AllocateChunk(&bufferLog, (void**)&bufferPhy, dataLength) < B_OK) { TRACE_ERROR("unable to allocate itd buffer\n"); + delete isoRequest; return B_NO_MEMORY; } @@ -791,6 +800,8 @@ EHCI::SubmitIsochronous(Transfer *transfer) itd->buffer_phy[pg + 1] = currentPhy & 0xfffff000; pg++; } + if (dataLength <= 0) + itd->token[i] |= EHCI_ITD_IOC; } currentPhy += (offset & 0xfff) - (currentPhy & 0xfff); @@ -799,12 +810,17 @@ EHCI::SubmitIsochronous(Transfer *transfer) | (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); + itd->buffer_phy[2] |= + ((((pipe->MaxPacketSize() >> EHCI_ITD_MAXPACKETSIZE_LENGTH) + 1) + & EHCI_ITD_MUL_MASK) << 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]); + if (!LockIsochronous()) + continue; LinkITDescriptors(itd, &fItdEntries[currentFrame]); + UnlockIsochronous(); fFrameBandwidth[currentFrame] -= bandwidth; currentFrame = (currentFrame + 1) & (EHCI_VFRAMELIST_ENTRIES_COUNT - 1); frameCount++; @@ -818,11 +834,15 @@ EHCI::SubmitIsochronous(Transfer *transfer) transfer->DataLength()); if (result < B_OK) { TRACE_ERROR("failed to add pending isochronous transfer\n"); + for (uint32 i = 0; i < itdIndex; i++) + FreeDescriptor(isoRequest[i]); + delete isoRequest; return result; } TRACE("appended isochronous transfer by starting at frame number %d\n", currentFrame); + fNextStartingFrame = currentFrame + 1; // Wake up the isochronous finisher thread release_sem_etc(fFinishIsochronousTransfersSem, 1 /*frameCount*/, B_DO_NOT_RESCHEDULE); @@ -2146,7 +2166,8 @@ EHCI::FreeDescriptor(ehci_qtd *descriptor) (void *)descriptor->buffer_phy[0], descriptor->buffer_size); } - fStack->FreeChunk(descriptor, (void *)descriptor->this_phy, sizeof(ehci_qtd)); + fStack->FreeChunk(descriptor, (void *)descriptor->this_phy, + sizeof(ehci_qtd)); } @@ -2208,7 +2229,8 @@ EHCI::FreeDescriptor(ehci_itd *descriptor) if (!descriptor) return; - fStack->FreeChunk(descriptor, (void *)descriptor->this_phy, sizeof(ehci_itd)); + fStack->FreeChunk(descriptor, (void *)descriptor->this_phy, + sizeof(ehci_itd)); } @@ -2218,7 +2240,8 @@ EHCI::FreeDescriptor(ehci_sitd *descriptor) if (!descriptor) return; - fStack->FreeChunk(descriptor, (void *)descriptor->this_phy, sizeof(ehci_sitd)); + fStack->FreeChunk(descriptor, (void *)descriptor->this_phy, + sizeof(ehci_sitd)); } @@ -2372,7 +2395,8 @@ EHCI::ReadDescriptorChain(ehci_qtd *topDescriptor, iovec *vector, if (vectorOffset >= vector[vectorIndex].iov_len) { if (++vectorIndex >= vectorCount) { - TRACE("read descriptor chain (%ld bytes, no more vectors)\n", actualLength); + TRACE("read descriptor chain (%ld bytes, no more vectors)" + "\n", actualLength); *nextDataToggle = dataToggle > 0 ? true : false; return actualLength; } @@ -2437,7 +2461,6 @@ 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 @@ -2456,6 +2479,10 @@ EHCI::ReadIsochronousDescriptorChain(isochronous_transfer_data *transfer) size_t bufferSize = (itd->token[j] >> EHCI_ITD_TLENGTH_SHIFT) & EHCI_ITD_TLENGTH_MASK; + if (((itd->token[j] >> EHCI_ITD_STATUS_SHIFT) + & EHCI_ITD_STATUS_MASK) != 0) { + bufferSize = 0; + } isochronousData->packet_descriptors[packet].actual_length = bufferSize; @@ -2467,6 +2494,7 @@ EHCI::ReadIsochronousDescriptorChain(isochronous_transfer_data *transfer) totalLength += bufferSize; size_t offset = bufferOffset; + size_t skipSize = packetSize - bufferSize; while (bufferSize > 0) { size_t length = min_c(bufferSize, vector[vectorIndex].iov_len - vectorOffset); @@ -2487,6 +2515,23 @@ EHCI::ReadIsochronousDescriptorChain(isochronous_transfer_data *transfer) } } + // skip to next packet offset + while (skipSize > 0) { + size_t length = min_c(skipSize, + vector[vectorIndex].iov_len - vectorOffset); + vectorOffset += length; + skipSize -= 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; diff --git a/src/add-ons/kernel/busses/usb/ehci.h b/src/add-ons/kernel/busses/usb/ehci.h index ce6184bc39..8692939b74 100644 --- a/src/add-ons/kernel/busses/usb/ehci.h +++ b/src/add-ons/kernel/busses/usb/ehci.h @@ -195,6 +195,7 @@ static pci_module_info * sPCIModule; pci_info * fPCIInfo; Stack * fStack; uint32 fEnabledInterrupts; + uint32 fThreshold; // Periodic transfer framelist and interrupt entries area_id fPeriodicFrameListArea; @@ -218,6 +219,7 @@ static pci_module_info * sPCIModule; sem_id fCleanupSem; thread_id fCleanupThread; bool fStopThreads; + int32 fNextStartingFrame; // fFrameBandwidth[n] holds the available bandwidth // of the nth frame in microseconds diff --git a/src/add-ons/kernel/busses/usb/ehci_hardware.h b/src/add-ons/kernel/busses/usb/ehci_hardware.h index d01b513f94..5344332bf4 100644 --- a/src/add-ons/kernel/busses/usb/ehci_hardware.h +++ b/src/add-ons/kernel/busses/usb/ehci_hardware.h @@ -112,8 +112,8 @@ #define EHCI_HCCPARAMS_PPCEC (1 << 18) // Per-Port Change Event #define EHCI_HCCPARAMS_LPM (1 << 17) // Link Power Management #define EHCI_HCCPARAMS_HP (1 << 16) // Hardware Prefetch -#define EHCI_HCCPARAMS_IPT_SHIFT 4 // Isochronous Periodic Threshold -#define EHCI_HCCPARAMS_IPT_MASK 0xf +#define EHCI_HCCPARAMS_FRAME_CACHE(x) ((x >> 7) & 0x1) // Isochronous Periodic Threshold +#define EHCI_HCCPARAMS_IPT(x) ((x >> 4) & 0x7) // Isochronous Periodic Threshold // Data Structures (EHCI Spec 3) @@ -161,10 +161,12 @@ typedef struct ehci_itd { #define EHCI_ITD_ENDPOINT_MASK 0xf #define EHCI_ITD_DIR_SHIFT 11 #define EHCI_ITD_MUL_SHIFT 0 +#define EHCI_ITD_MUL_MASK 0x3 #define EHCI_ITD_BUFFERPOINTER_SHIFT 12 #define EHCI_ITD_BUFFERPOINTER_MASK 0xfffff #define EHCI_ITD_MAXPACKETSIZE_SHIFT 0 #define EHCI_ITD_MAXPACKETSIZE_MASK 0x7ff +#define EHCI_ITD_MAXPACKETSIZE_LENGTH 11 // Split Transaction Isochronous Transfer Descriptors (siTD, EHCI Spec 3.3)