📄 ncvirtep.c
字号:
ActiveTransfers++;
// See if host is actively requesting service on the endpoint
// - Test the endpoint's "NAK returned to host" bit (in one
// of VIRTOUT0, VIRTOUT1, VIRTIN0, VIRTIN1)
if (NETCHIP_READ(Endpoint->Priv.NcVirtReg) & Endpoint->Priv.NcVirtBitMask)
{ // The host has recently requested service from the virtualized endpoint. NET2272
// virtualization logic is currently NAK'ing any host requests. Return
// the endpoint now; firmware will try to assign it to a physical endpoint so
// its data can be transferred.
HISTO(DEFAULT_HISTORY, "VNak", NCHISTO_TRANSFER(Endpoint), Endpoint->Priv.NcVirtReg, Endpoint->Priv.NcVirtBitMask);
ASSERT(Endpoint->Priv.Virtualized);
// Token Notification:
// - If client wants token notification, notify client immediately, without
// waiting to assign the endpoint
ASSERT(Endpoint->Transfer != NULL)
if (Endpoint->Transfer->TransferFlags & (1<<NC_TRANSFER_FLAG_TOKEN_NOTIFICATION))
{ // Client requesting token notification
// - This virtualized endpoint has NAK'd an IN or OUT token
// - Complete client's token notification transfer now
Endpoint->BytesTransferred = 0;
Endpoint->CompletionStatus = NCSTATUS_SUCCESS;
HISTO(DEFAULT_HISTORY, "VTok", NCHISTO_TRANSFER(Endpoint), 0, 0);
// Call client completion routine
// - Client should return from this call as quickly as possible
// - Client may start another transfer on this or another endpoint
Endpoint->Transfer->ClientCompletionHandler(Endpoint);
return NULL;
}
// This endpoint's transfer is active (Pending), and now requires service
return Endpoint;
}
}
// All virtualized endpoints in the configuration have been checked
// - None of the endpoints requesting service have active Transfer Objects (There, may
// be some active Transfer Objects, but the host is not requesting service on those!)
HISTO(DEFAULT_HISTORY, "!VEn", ActiveTransfers, 0, 0);
NC_STATISTIC(NoTransferForVirtualInterrupt++;)
if (ActiveTransfers == 0)
{ // *None* of the virtualized endpoints in the configuration have active Transfer Objects!
// - Disable virtualized interrupt enable
// - Later, when a transfer is started on a virtualized endpoint, Virtualized Interrupt
// should get re-enabled
NETCHIP_WRITE(IRQENB0, NETCHIP_READ(IRQENB0) & ~(1<<VIRTUALIZED_ENDPOINT_INTERRUPT_ENABLE));
}
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
STATIC void
SaveTransfer(
PNC_ENDPOINT_OBJECT Endpoint
)
{ // Save this transfer by saving its entire transfer state
// - Data in physical endpoint buffer must be saved along with registers
WORDBYTE BytesToRewind;
BYTE EpBuffStates;
NCDEBUG(PNC_TRANSFER_OBJECT Transfer = Endpoint->Transfer;)
ASSERT(Transfer != NULL);
ASSERT(Endpoint->Priv.Virtualized);
NETCHIP_WRITE(PAGESEL, Endpoint->Priv.PhysEp);
// The following physical endpoint registers hold persistent values for
// the USB endpoint (i.e. they remain constant until the USB configuration
// changes) and therefore do not need to be saved on every endpoint swap
ASSERT(NETCHIP_READ(EP_MAXPKT0) == Endpoint->Priv.EpMaxPkt.Byte[MYEND_LO]);
ASSERT(NETCHIP_READ(EP_MAXPKT1) == Endpoint->Priv.EpMaxPkt.Byte[MYEND_HI]);
// Endpoint should already be virtualized
ASSERT(NETCHIP_READ(EP_CFG) == (Endpoint->Priv.EpCfg & ~(1<<ENDPOINT_ENABLE)));
// Physical endpoint is now quiescent. It will NAK any request tokens
// - It is now safe to save endpoint register and buffer content
EpBuffStates = NETCHIP_READ(EP_BUFF_STATES);
HISTO(DEFAULT_HISTORY, "Sav(", NCHISTO_TRANSFER(Endpoint), EpBuffStates, NETCHIP_READ(EP_RSPSET));
ASSERT(Endpoint->CompletionStatus == NCSTATUS_PENDING);
if (Endpoint->DirectionIn)
{ // Rewind USB IN (Tx)
// - Rewinding is faster than actually reading and saving bytes out of the IN endpoint
// - Rewinding works as long as endpoint buffer content corresponds perfectly with
// the client buffer in the Transfer Object (which indicates concatenated
// transfers cannot be rewound; there may not be a corresponding buffer!)
// Determine how many bytes to rewind. Endpoint buffer states determines how many packets:
// - Endpoint Direction can be either TRUE (as an IN endpoint) or it could be reprogrammed
// to FALSE (as an OUT endpoint). Either way, care must be taken when firmware
// interprets the Buff States bits.
// - Buffer A and B states key: (BUFF_FREE, BUFF_VALID, BUFF_LCL, BUFF_USB)
// As an IN endpoint: As an OUT endpoint: State means rewind size is:
// USB and VALID VALID and LCL Two packets: MaxPkt + Last Packet Size
// USB and LCL USB and LCL One packet: Last Packet Size
// FREE and LCL USB and FREE Zero packets: No rewind (host has taken all packets)
// - Tip: An IN endpoint can only be switched to OUT after waiting for the Endpoint Enable
// to go FALSE. If the switch happens while the endpoint is transferring a packet to the host,
// the packet may get corrupted. For this reason, it's better to simply keep the direction as IN!
ASSERT((NETCHIP_READ(EP_CFG) & (1<<ENDPOINT_DIRECTION)));
BytesToRewind.Word = 0;
switch (EpBuffStates)
{ // As an OUT:
case (BUFF_USB<<BUFFER_A_STATE) | (BUFF_VALID<<BUFFER_B_STATE):
case (BUFF_USB<<BUFFER_B_STATE) | (BUFF_VALID<<BUFFER_A_STATE):
// Rewind two packets: MaxPkt + Last Packet Size
// - Account for MaxPkt now:
BytesToRewind.Word = Endpoint->Priv.EpMaxPkt.Word;
// DROP THROUGH...
case (BUFF_USB<<BUFFER_A_STATE) | (BUFF_LCL<<BUFFER_B_STATE):
case (BUFF_USB<<BUFFER_B_STATE) | (BUFF_LCL<<BUFFER_A_STATE):
// Rewind one packet: Last Packet Size
BytesToRewind.Word = (USHORT)(BytesToRewind.Word + Endpoint->Priv.LastPacketSize);
// Accounting: Rewind transfer by one or two packets
Endpoint->Priv.BytesRemaining += BytesToRewind.Word;
Endpoint->Priv.pPkt -= BytesToRewind.Word;
// Tip: Put normal Tx packet handler back! If the final
// packets of the transfer were in the endpoint when this
// save routine got called, the Packet Handler routine was
// replaced with another "Final Packets" handler. Since the
// final packets will eventually need to be restarted as a
// regular transfer, the regular packet handler will need
// to be in place
Endpoint->Priv.PioPacketHandler = TxPacketsPio;
ASSERT(Endpoint->Priv.pPkt >= (PBYTE)Transfer->TransferBuffer);
ASSERT((INT)Endpoint->Priv.BytesRemaining >= 0);
NC_STATISTIC(Endpoint->Priv.Reassign_BytesRewound += BytesToRewind.Word;)
break;
case (BUFF_FREE<<BUFFER_A_STATE) | (BUFF_LCL<<BUFFER_B_STATE):
case (BUFF_FREE<<BUFFER_B_STATE) | (BUFF_LCL<<BUFFER_A_STATE):
// Zero packets: Nothing to rewind (host has taken all packets)
// - Tip: The caller may have already checked for this case, however its possible
// for the host to have taken packets since it was checked!
break;
default:
//
HISTO(VOLUME_MINIMUM, "!!!!", NCHISTO_TRANSFER(Endpoint), EpBuffStates, BytesToRewind.Word);
NCFAULT();
}
HISTO(DEFAULT_HISTORY, "Rwnd", NCHISTO_TRANSFER(Endpoint), EpBuffStates, BytesToRewind.Word);
}
else
{ // USB OUT (Rx)
// Tip: The Packet Handler could call its completion handler, which in turn
// could start another transfer on the same (or different!) endpoint, which,
// if currently assigned, could change Page Select!
Endpoint->Priv.PioPacketHandler(Endpoint);
}
NC_STATISTIC(Endpoint->Priv.Reassign_SavedEp++;)
HISTO(DEFAULT_HISTORY, ")Sav", NCHISTO_TRANSFER(Endpoint), NETCHIP_READ(EP_BUFF_STATES), Endpoint->Priv.EpRsp);
}
///////////////////////////////////////////////////////////////////////////////
STATIC void
EpReconfigureAndWait(
PNC_ENDPOINT_OBJECT Endpoint,
NCBYTE EndpointEnable // (1<<ENDPOINT_ENABLE) or (0<<ENDPOINT_ENABLE)
)
{ // Reconfigure (enable or virtualize) an endpoint and wait for completion
NCBYTE ExpectedEpCfg;
NETCHIP_WRITE(PAGESEL, Endpoint->Priv.PhysEp);
ExpectedEpCfg = Endpoint->Priv.EpCfg & ~(1<<ENDPOINT_ENABLE);
ExpectedEpCfg |= EndpointEnable;
NETCHIP_WRITE(EP_CFG, ExpectedEpCfg);
for (;;)
{ // Forever...
// - The endpoint will quickly change to the expected state, however
// there is nothing to prevent getting stuck here forever!
NCBYTE GotEpCfg = NETCHIP_READ(EP_CFG);
HISTO(DEFAULT_HISTORY, "EpWa", NCHISTO_TRANSFER(Endpoint), GotEpCfg, 0);
if (ExpectedEpCfg == GotEpCfg)
{ // Endpoint now matches callers expectation
Endpoint->Priv.Virtualized = !EndpointEnable;
break;
}
}
}
///////////////////////////////////////////////////////////////////////////////
void
RestartTransfer(
PNC_ENDPOINT_OBJECT Endpoint
)
{ // Start (or restart) a transfer to a physical endpoint
// - The physical endpoint must not be active (Endpoint Enable must be FALSE)!
ASSERT(Endpoint != NULL);
ASSERT(Endpoint->Transfer != NULL);
ASSERT(Endpoint->CompletionStatus == NCSTATUS_PENDING);
// Assert: Once an OUT endpoint accidently gets NAK OUT Packets set, the
// endpoint could NAK FOREVER!
ASSERT(Endpoint->DirectionIn || !(Endpoint->Priv.EpRsp & (1<<ALT_NAK_OUT_PACKETS)));
NETCHIP_WRITE(PAGESEL, Endpoint->Priv.PhysEp);
ASSERT(!(NETCHIP_READ(EP_CFG) & (1<<ENDPOINT_ENABLE))); // Endpoint Enable is expected to be FALSE
NETCHIP_WRITE(EP_STAT0,
(1<<NAK_OUT_PACKETS) | // Required!
(1<<SHORT_PACKET_TRANSFERRED_INTERRUPT) | // Info only
(1<<DATA_PACKET_RECEIVED_INTERRUPT) | // Required!
(1<<DATA_PACKET_TRANSMITTED_INTERRUPT) | // Required!
(1<<DATA_OUT_TOKEN_INTERRUPT) | // Info only
(1<<DATA_IN_TOKEN_INTERRUPT) | // Required (for token notification)
0);
// Get rid of junk data in the buffers
NETCHIP_WRITE(EP_STAT1,
(1<<BUFFER_FLUSH) | // Required!
(1<<LOCAL_OUT_ZLP) | // Info only
(1<<USB_STALL_SENT) | // Info only
(1<<USB_IN_NAK_SENT) | // Info only
(1<<USB_IN_ACK_RCVD) | // Info only
(1<<USB_OUT_NAK_SENT) | // Info only
(1<<USB_OUT_ACK_SENT) | // Info only
(1<<TIMEOUT) | // Info only
0);
// Restore virtual endpoint registers
NETCHIP_WRITE(EP_MAXPKT0, Endpoint->Priv.EpMaxPkt.Byte[MYEND_LO]);
NETCHIP_WRITE(EP_MAXPKT1, Endpoint->Priv.EpMaxPkt.Byte[MYEND_HI]);
NETCHIP_WRITE(EP_RSPCLR, ~(Endpoint->Priv.EpRsp));
NETCHIP_WRITE(EP_RSPSET, Endpoint->Priv.EpRsp);
// Assign transfer for the USB endpoint to this physical endpoint
// - When the USB endpoint is assigned to the NET2272 physical endpoint,
// the USB endpoint is no longer 'virtualized'.
// - The associated virtualization bit (in VIRTOUT0, VIRTOUT1, VIRTIN0,
// or VIRTIN1) will no longer be set by the host's request for the USB endpoint
ASSERT(Endpoint->Priv.EpCfg & (1<<ENDPOINT_ENABLE));
ASSERT((Endpoint->Priv.EpCfg & (0x0f<<ENDPOINT_NUMBER)) != 0);
EpReconfigureAndWait(Endpoint, 1<<ENDPOINT_ENABLE);
// Now that the endpoint is enabled, clear virtual endpoint interrupt bit
// left over in VIRTOUT0, VIRTOUT1, VIRTIN0, or VIRTIN1
NETCHIP_WRITE(Endpoint->Priv.NcVirtReg, Endpoint->Priv.NcVirtBitMask);
NETCHIP_WRITE(EP_IRQENB, Endpoint->Priv.EpIrqEnb);
// This restart was requested due to a virtualized endpoint interrupt. Chances are
// good that the bus can productively transfer data immediately:
// - USB IN (Tx) endpoint: Bus is likely in an IN/NAK cycle on this endpoint right
// now. The host will be able to get packets soon after the Tx packet handler runs.
// - USB OUT (Rx) endpoint: Soon after firmware sets Endpoint Enable (in EP_CFG) the
// bus should be able to break out of a PING/NAK cycle and start filling the endpoint
// with packets, however at least one packet must complete before it can be handled
HISTO(DEFAULT_HISTORY, "Rsr(", NCHISTO_TRANSFER(Endpoint), NETCHIP_READ(EP_BUFF_STATES), Endpoint->Priv.EpRsp);
if (Endpoint->DirectionIn)
{ // USB IN (Tx): The host will be able to take packets as soon as the endpoint can be filled.
Endpoint->Priv.PioPacketHandler(Endpoint);
NC_STATISTIC(PhysEpStatistics[Endpoint->Priv.PhysEp].Reassign_In++;)
}
else
{ // USB OUT (Rx): A little time is needed for endpoint to complete a packet
// - Nothing to do until an OUT packet completes
NC_STATISTIC(PhysEpStatistics[Endpoint->Priv.PhysEp].Reassign_Out++;)
}
// Reset lock-time frame counter
// - Endpoint assignment will persist (i.e. not get re-assigned) until at least a
// certain number of USB frames has gone by
Endpoint->Priv.FrameCount = 0;
Endpoint->Priv.SwapCandidate = SwapCandidate_Locked;
HISTO(DEFAULT_HISTORY, ")Rsr", NCHISTO_TRANSFER(Endpoint), NETCHIP_READ(EP_BUFF_STATES), NETCHIP_READ(EP_STAT0));
}
///////////////////////////////////////////////////////////////////////////////
STATIC BOOL
TxActivePacketsTest(
PNC_ENDPOINT_OBJECT Endpoint
)
{ // A USB IN (Tx) endpoint is busy if it has unsent packets in the endpoint or
// it is marked 'locked'
NCBYTE EpBuffStates;
DBG_UNREFERENCED_PARAMETER(Endpoint);
ASSERT(NETCHIP_READ(PAGESEL) == Endpoint->Priv.PhysEp);
EpBuffStates = NETCHIP_READ(EP_BUFF_STATES);
HISTO(DEFAULT_HISTORY, "TxBz", NCHISTO_TRANSFER(Endpoint), EpBuffStates, 0);
switch (EpBuffStates)
{
case (BUFF_FREE<<BUFFER_A_STATE) | (BUFF_LCL<<BUFFER_B_STATE):
case (BUFF_FREE<<BUFFER_B_STATE) | (BUFF_LCL<<BUFFER_A_STATE):
// IN endpoint has no active packets
return FALSE;
default:
return TRUE;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -