⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ncvirtep.c

📁 CE下 NET2778 NDIS Drivers, 在每个平台上都可以使用
💻 C
📖 第 1 页 / 共 4 页
字号:
        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 + -