📄 ncvirtep.c
字号:
///////////////////////////////////////////////////////////////////////////////
STATIC BOOL
RxActivePacketsTest(
PNC_ENDPOINT_OBJECT Endpoint
)
{ // A USB OUT (Rx) endpoint is busy if it has packets in the endpoint
NCBYTE EpStat0;
DBG_UNREFERENCED_PARAMETER(Endpoint);
ASSERT(NETCHIP_READ(PAGESEL) == Endpoint->Priv.PhysEp);
EpStat0 = NETCHIP_READ(EP_STAT0);
HISTO(DEFAULT_HISTORY, "RxBz", NCHISTO_TRANSFER(Endpoint), EpStat0, NETCHIP_READ(EP_BUFF_STATES));
return (EpStat0 & (1<<DATA_PACKET_RECEIVED_INTERRUPT))? TRUE: FALSE;
}
///////////////////////////////////////////////////////////////////////////////
typedef enum _NC_REASSIGNMENT_CANDIDATE
{
ReassignmentCandidate_Invalid = 0, // Error: No level assigned
ReassignmentCandidate_Locked, // Endpoint cannot be reassigned
ReassignmentCandidate_ContainsPackets, // Endpoint content must be saved before re-assigning
ReassignmentCandidate_OutEpNowContainsPackets, // OUT endpoint now contains packets (it didn't at first!)
ReassignmentCandidate_ZeroPackets, // Endpoint contains no packets
ReassignmentCandidate_BestChoice, // Best endpoint choice
} NC_REASSIGNMENT_CANDIDATE, *PNC_REASSIGNMENT_CANDIDATE;
///////////////////////////////////////////////////////////////////////////////
NCSTATUS
ReassignBestEndpoint(
PNC_ENDPOINT_OBJECT NextEndpoint
)
{ // Choose the best physical endpoint to unassign, then reassign it to the Next Endpoint
// - Return Unsuccessful if reassignment NOT made (e.g. all endpoints locked)
PNC_ENDPOINT_OBJECT Endpoint = NULL;
PNC_ENDPOINT_OBJECT E;
NCBYTE BestEp, PhysEp, StartEp, StopEp, EpIncrement;
HISTO(DEFAULT_HISTORY, "Rea(", NCHISTO_TRANSFER(NextEndpoint), 0, 0);
HISTO(DEFAULT_HISTORY, "Cndi", NCHISTO_TRANSFER(PhysicalEndpoints[EPA]), NCHISTO_TRANSFER(PhysicalEndpoints[EPB]), NCHISTO_TRANSFER(PhysicalEndpoints[EPC]));
BestEp = (PhysicalEndpoints[EPC]->Priv.SwapCandidate > PhysicalEndpoints[EPB]->Priv.SwapCandidate)? EPC: EPB;
BestEp = (PhysicalEndpoints[BestEp]->Priv.SwapCandidate > PhysicalEndpoints[EPA]->Priv.SwapCandidate)? BestEp: EPA;
///////////////////////////////////////////////////////////////////////////
// First test: If all endpoints are locked, none of the endpoints can be reassigned
if (PhysicalEndpoints[BestEp]->Priv.SwapCandidate == SwapCandidate_Locked)
{ // All candidate levels are the same as this one: Locked!
// - Unable to reassign endpoint
// - Clear virtual endpoint interrupt bit in VIRTOUT0, VIRTOUT1, VIRTIN0, or VIRTIN1
NETCHIP_WRITE(NextEndpoint->Priv.NcVirtReg, NextEndpoint->Priv.NcVirtBitMask);
NC_STATISTIC(Reassign_AllLocked++;)
return NCSTATUS_UNSUCCESSFUL;
}
if (PhysicalEndpoints[BestEp]->Priv.SwapCandidate == SwapCandidate_Instant)
{ // The Best Endpoint can be reassigned instantly:
Endpoint = PhysicalEndpoints[BestEp];
// Endpoint reconfiguration will complete quickly, however it will be delayed if
// there is traffic on any endpoint
HISTO(DEFAULT_HISTORY, "Inst", NCHISTO_TRANSFER(Endpoint), NCHISTO_TRANSFER(NextEndpoint), 0);
NC_STATISTIC(Endpoint->Priv.Reassign_Instantly++;)
EpReconfigureAndWait(Endpoint, 0<<ENDPOINT_ENABLE);
goto ReassignInstantly;
}
if (NextEndpoint->DirectionIn)
{ // Next transfer is a USB IN transfer
// - Coax this IN transfer to re-assign starting at EPA and work towards EPC
// - Apply same order as found in array of Physical Transfers
StartEp = EPA;
StopEp = EPC + 1;
EpIncrement = 1;
}
else
{ // Next transfer is a USB OUT transfer
// - Coax this OUT transfer to re-assign starting at EPC and work towards EPA
// - Reverse search order
StartEp = EPC;
StopEp = EPA - 1;
EpIncrement = (NCBYTE)-1;
}
///////////////////////////////////////////////////////////////////////////
// Choosing an endpoint to reassign: Strategy notes...
// - We are now committed to choosing an endpoint...
// - The best endpoint to reassign is one that currently doesn't contain any
// packets. Next, if all endpoints have packets, then a "second best" endpoint is
// chosen. The second best choice will require packets to be saved, which takes
// time and possibly requires a call to a client completion handler. The second best
// choice is made with help from a "candidacy score" which was assigned elsewhere.
// - Try to migrate IN endpoints toward EPA, and OUT endpoints toward EPC. As long as
// the top-level interrupt handler handles transfers on EPA, then EPB, then EPC, IN
// packets can be taken by the host concurrent with OUT packets being taken by firmware.
for (PhysEp = StartEp; PhysEp != StopEp; PhysEp += EpIncrement)
{ // For every endpoint
// - Virtualize any endpoint that does not currently contain packets
E = PhysicalEndpoints[PhysEp];
ASSERT(E->Priv.PhysEp == PhysEp);
if (E->Priv.SwapCandidate == SwapCandidate_Locked)
{ // Locked endpoints cannot be re-assigned
// - Leave endpoint enabled
E->Priv.ReassignmentCandidate = ReassignmentCandidate_Locked;
continue;
}
// Quickly test if endpoint has active packets in it
NETCHIP_WRITE(PAGESEL, PhysEp);
if (E->Priv.TestActivePackets(E))
{ // Endpoint contains packets
// - It is NOT the best choice for unassignment
// - Leave endpoint enabled
E->Priv.ReassignmentCandidate = ReassignmentCandidate_ContainsPackets;
continue;
}
// This endpoint does NOT contain packets (at the moment)
// - This endpoint is *possibly* the best candidate for virtualization
// - Virtualize the endpoint so that it can participate in the
// endpoint re-assignment strategy
// - An OUT endpoint *may* accept a packet if it is in the middle of
// receiving a packet when its Endpoint Enable gets cleared
// - Virtualizing takes time. The endpoint is not virtualized until
// Endpoint Enable reads back FALSE. The amount of time it takes
// for an endpoint to virtualize depends on wire-level signalling
// on the NET2272 from this moment forward. It could take as long as a full
// packet, as little as IN/NAK or PING/NAK cycle time, or even zero time!
// - NET2272 uses a single logic path to virtualize. Specifically, traffic
// on any NET2272 endpoint will delay this endpoint's virtualization.
// - Once virtualized, traffic on the USB endpoint will cause a corresponding bit
// in one of VIRTOUT0, VIRTOUT1, VIRTIN0, VIRTIN1 to become set
NETCHIP_WRITE(EP_CFG, E->Priv.EpCfg & ~(1<<ENDPOINT_ENABLE));
E->Priv.ReassignmentCandidate = ReassignmentCandidate_ZeroPackets;
Endpoint = E;
NC_STATISTIC(E->Priv.Reassign_Possible++;)
}
if (Endpoint != NULL)
{ // At least one endpoint does not contain packets (at the moment!)
// - Choose the *best* endpoint that does not contain packets
// - Also re-test OUT endpoints since a packet may have successfully arrived
// after it was tested and commanded to virtualize
// Wait for the NET2272 to complete virtualization
// - NET2272 uses a single logic path to virtualize. Specifically, traffic
// on *any* NET2272 endpoint delays virtualization on this endpoint. Since
// this was the final endpoint that was commanded to virtualize, it's guaranteed
// that *all* virtualized endpoints will complete virtualization when this one completes:
EpReconfigureAndWait(Endpoint, 0<<ENDPOINT_ENABLE);
Endpoint = NULL;
for (PhysEp = StartEp; PhysEp != StopEp; PhysEp += EpIncrement)
{ // For every endpoint
// - Choose the best endpoint that does not contain packets (if any!)
E = PhysicalEndpoints[PhysEp];
if (E->Priv.ReassignmentCandidate != ReassignmentCandidate_ZeroPackets)
{ // Skip endpoints that are locked or contain packets
continue;
}
// When checked *before* virtualizing (above), this endpoint did not contain any
// packets, and was therefore an excellent choice. However, if it's an OUT
// endpoint, a packet may have successfully arrived afterwards
// - See if a packet arrived after endpoint was commanded to virtualize
// - IN endpoints that didn't contain data before virtualizing will
// continue to be empty
NETCHIP_WRITE(PAGESEL, PhysEp);
if (
(!(E->DirectionIn)) && // OUT endpoint? (IN endpoints do not need retesting)
(E->Priv.TestActivePackets(E)) // Endpoint now has a packet?
)
{ // A virtualized OUT endpoint, which did not have a packet earlier, now has a packet
// - It is not as good a candidate as it was before!
// - Note that this OUT endpoint is still virtualized and, like all
// other virtualized endpoints, must be evenutally re-enabled, and
// any Virtual Interrupt that could have occurred while virtualized,
// cleared.
HISTO(DEFAULT_HISTORY, "OutP", NCHISTO_TRANSFER(E), 0, 0);
E->Priv.ReassignmentCandidate = ReassignmentCandidate_OutEpNowContainsPackets;
NC_STATISTIC(E->Priv.Reassign_OutNowContainsPackets++;)
continue;
}
// This endpoint is the very best choice:
// - Endpoint is virtualized and does not contain packets
// - Tip: The search order in this loop should migrate re-assignment of IN
// endpoints towards EPA, and OUT endpoints towards EPC
// - No need to search further
E->Priv.ReassignmentCandidate = ReassignmentCandidate_BestChoice;
E->Priv.Virtualized = TRUE;
HISTO(DEFAULT_HISTORY, "Best", NCHISTO_TRANSFER(E), E->Priv.ReassignmentCandidate, 0);
Endpoint = E;
NC_STATISTIC(E->Priv.Reassign_EmptyEp++;)
break;
}
}
for (PhysEp = StartEp; PhysEp != StopEp; PhysEp += EpIncrement)
{ // For every endpoint:
// - Renable virtualized endpoints, except the one that will be re-assigned
E = PhysicalEndpoints[PhysEp];
switch (E->Priv.ReassignmentCandidate)
{
case ReassignmentCandidate_OutEpNowContainsPackets:
case ReassignmentCandidate_ZeroPackets:
// Re-enable endpoints that were virtualized, except the one that will be re-assigned
NETCHIP_WRITE(PAGESEL, E->Priv.PhysEp);
NETCHIP_WRITE(EP_CFG, E->Priv.EpCfg);
E->Priv.Virtualized = FALSE;
// Now that the endpoint is enabled, clear possible virtual endpoint
// interrupt bit left over in VIRTOUT0, VIRTOUT1, VIRTIN0, or VIRTIN1
NETCHIP_WRITE(E->Priv.NcVirtReg, E->Priv.NcVirtBitMask);
HISTO(DEFAULT_HISTORY, "Enbl", NCHISTO_TRANSFER(E), E->Priv.ReassignmentCandidate, 0);
break;
default:
break;
}
}
if (Endpoint == NULL)
{ // Unfortunately, all unlocked endpoints currently contain packets
// - Make a "second-best" choice
// - Endpoints containing packets were not virtualized. (Exception: it's
// possible that an OUT endpoint received a packet after being
// commanded to virtualize. Such an endpoint contains a packet,
// and is also virtualized. This case was detected above.)
// - Taking the second best choice takes time: The endpoint must be virtualized,
// then the contents of the endpoint must be saved
Endpoint = PhysicalEndpoints[BestEp];
EpReconfigureAndWait(Endpoint, 0<<ENDPOINT_ENABLE);
// Tip: Saving a transfer *could* result in the client's completion handler
// getting called (if transfer manages to complete).
SaveTransfer(Endpoint);
}
// Before reassigning IN endpoints: The host may have taken the final packets of
// the IN transfer. If so, the transfer must be completed before virtualizing the
// endpoint because the host won't issue any more tokens for its transfer.
if (Endpoint->Priv.PioPacketHandler == TxFinalPio)
{ // The final packets of an IN transfer may (or may not) have been transmitted to the host
// - Before reassigning, call the Tx Final routine
// - Tip: If the final packets have been taken, the client's completion handler is
// called. (That handler is allowed to start a new transfer on this or other endpoints!)
ASSERT(Endpoint->Priv.Virtualized);
TxFinalPio(Endpoint);
}
ReassignInstantly:
// Save the transfer's endpoint settings and re-assign the physical endpoint to the Next Endpoint
// - These endpoint settings will be reapplied when the virtualized endpoint eventually generates
// an interrupt and gets re-assigned
ASSERT(Endpoint->Priv.Virtualized);
ASSERT(NextEndpoint->Priv.Virtualized);
PhysEp = Endpoint->Priv.PhysEp;
NETCHIP_WRITE(PAGESEL, PhysEp);
Endpoint->Priv.EpRsp = NETCHIP_READ(EP_RSPSET);
Endpoint->Priv.EpIrqEnb = NETCHIP_READ(EP_IRQENB);
// Setup to re-assign Endpoint to the Next Endpoint
ASSERT(NextEndpoint != Endpoint);
NextEndpoint->Priv.PhysEp = (BYTE)PhysEp;
PhysicalEndpoints[PhysEp] = NextEndpoint;
HISTO(DEFAULT_HISTORY, ")Rea", NCHISTO_TRANSFER(Endpoint), NCHISTO_TRANSFER(NextEndpoint), Endpoint->Priv.EpRsp);
return NCSTATUS_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
PNC_ENDPOINT_OBJECT
NcApi_EpCreate(
NCBYTE LogicalEp, // Endpoint's ordinal position in configuration (One-based)
NCBYTE MapToNcEp // (Optional) Map the logical endpoint to a specific NetChip endpoint
)
{ // Initialize a virtual endpoint's Transfer Object
// - Most values are extracted from the Endpoint Descriptor
// - When this transfer is assigned to a physical endpoint, endpoint values
// will be applied to the physical endpoint registers...
// - Does not apply to Endpoint Zero
PNC_ENDPOINT_OBJECT Endpoint;
PUSB_ENDPOINT_DESCRIPTOR EpDescriptor;
BYTE UsbEp;
UINT VirtReg;
// API for the NET2272 does not support endpoint mapping:
// - All NET2272 endpoints are uniform so mapping NetChip endpoints to USB endpoints
// is not advantageous. (The API for NET2280, with its non-uniform and dedicated
// endpoints, supports endpoint mapping.)
DBG_UNREFERENCED_PARAMETER(MapToNcEp);
ASSERT(MapToNcEp == NC_DEFAULT_ENDPOINT_MAPPING);
ASSERT(LogicalEp > EP0); // Endpoint Zero is created by default, in One Time Init, not here!
ASSERT(LogicalEp <= PrivDeviceObject->ConfigurationEndpointCount);
Endpoint = &LogicalEndpoints[LogicalEp];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -