smc.c

来自「PXA255 WINCE 4.2 BSP ,该BSP是商用的。」· C语言 代码 · 共 1,031 行 · 第 1/3 页

C
1,031
字号
    {
        // Setup pointer address register to point to first byte of Frame
        WriteWord( BANKSEL_REG, BANK2 );
        //WriteWord( POINTER_REG, 0xE000 );          // Auto-increment
        wPointerRegVal = 0xA000;

        // Check for an error.  If no error, then get the Frame out
        WriteWord( POINTER_REG, wPointerRegVal );           // No auto-increment  1
        wStatusWord = ReadWord( DATA_REG );
        wPointerRegVal +=2;                                 // manually increment

        if(wStatusWord & FRAME_FILTER)
        {
            // Don't deliver this frame to the user
            fDropFrame = TRUE;
        }
        else
        {
            fDropFrame = FALSE;
            pwData = (UINT16 *)pbData;

            // The top bits of the byte count are reserved, so mask those (pg 36 of SMC91C94 spec).
            // The byte count includes the status word, byte count and control byte so subtract 6 
            //
            WriteWord( POINTER_REG, wPointerRegVal );       // manual increment
            *pwLength = (0x07FF & ReadWord( DATA_REG )) - 6;
            wPointerRegVal +=2;

            EDBG_DEBUGLED(LED_PKTLEN,*pwLength);

            if(*pwLength > wBufLen)
            {
                EdbgOutputDebugString("SMC9000: received packet larger (%u) than buffer (%u)\n",*pwLength,wBufLen);
                fDropFrame = TRUE;
                goto ReleaseFrame;
            }
            // For broadcast frames, filter out all except for ARPs
            i=0;
            if((dwConfigOptions & OPT_BROADCAST_FILTERING) && (wStatusWord & BROADCAST_FILTER_BIT))
            {
                for(; i< sizeof(EthernetFrameHeader); i++)
                {
                    WriteWord( POINTER_REG, wPointerRegVal );       // manual increment
                    *pwData++ = ReadWord( DATA_REG );
                    wPointerRegVal +=2;
                }

                if(ntohs(((EthernetFrameHeader *)pbData)->wFrameType) != 0x0806)
                {
                    fDropFrame = TRUE;
                    goto ReleaseFrame;
                }
            }

            // Read all but the last word, which will contain the control word
            EDBG_DEBUGLED(LED_COPY,0);
            for(; i < (*pwLength >> 1); i++)
            {
                WriteWord( POINTER_REG, wPointerRegVal );     // 3
                *pwData++ = ReadWord( DATA_REG );
                wPointerRegVal +=2;
            }

            EDBG_DEBUGLED(LED_COPY,*pwLength);

            // Now check to see if there were an odd number of bytes sent.  If so, I have to extract the
            //  last one (pg 37 of the SCM91C94 spec).
            //
            WriteWord( POINTER_REG, wPointerRegVal );     // 4
            wControlWord = ReadWord( DATA_REG );
            wPointerRegVal +=2;
            
            if(wControlWord & 0x2000)
            {
                *pwData = (BYTE)wControlWord;
                (*pwLength)++;
            }
        }

        // Release the memory for the received Frame
        ReleaseFrame:
        WriteWord( MMUCOMMAND_REG, 0x0080 );

        // Wait until release finishes executing
        dwStartTime = OEMEthGetSecs();
        while(ReadWord( MMUCOMMAND_REG ) & 1)
        {
            if((OEMEthGetSecs() - dwStartTime) > 2)
            {
                EdbgOutputDebugString("!GetFrame: Timed out releasing memory\n");
                break;
            }
        }
        // If the buffer is now empty, re-enable the RCV_INT and clear the fFrameReceived flag
        if(!(ReadWord( INTERRUPT_REG ) & RCV_INT))
        {
            fFrameReceived = 0;
            WriteWord( INTERRUPT_REG, wIntMask );
        }

        // Check the status word for errors (pg 28 of the SMC91C94 spec).
        if(wStatusWord & 0x8000)
            EdbgOutputDebugString( "GetFrame() - Receive Alignment Error\n" );
        if(wStatusWord & 0x2000)
            EdbgOutputDebugString( "GetFrame() - CRC Error\n" );
        if(wStatusWord & 0x0800)
            EdbgOutputDebugString( "GetFrame() - Frame Was Too Long\n" );
        if(wStatusWord & 0x0400)
            EdbgOutputDebugString( "GetFrame() - Frame Was Too Short\n" );

//      if (wStatusWord & 0x4000) 
//          EdbgOutputDebugString( "GetFrame() - Broadcast\n" );

#ifdef SMC_DUMP_FRAMES        
        DumpEtherFrame(pbData, *pwLength);
#endif

        // If frame isn't filtered, return data to user. Otherwise, get next frame.
        if(!fDropFrame)
        {
            EDBG_ADAPTERMSG(ZONE_RECV,("-GetFrame, returning %u bytes\n", *pwLength));
            EDBG_DEBUGLED(LED_GETFRAME_EXIT,1);       

            // Take away CRC value.
            //
            if (pwLength)
                *pwLength -= 4;

            return 1;
        }
    }
    EDBG_ADAPTERMSG(ZONE_RECV,("-GetFrame\n"));
    EDBG_DEBUGLED(LED_GETFRAME_EXIT,0);
    return 0;

} // GetFrame()



// This routine should be called with a pointer to the ethernet frame data.  It is the caller's
//  responsibility to fill in all information including the destination and source addresses and
//  the frame type.  The length parameter gives the number of bytes in the ethernet frame.
// The routine will not return until the frame has been transmitted or an error has occured.  If
//  the frame transmits successfully, 0 is returned.  If an error occured, a message is sent to
//  the serial port and the routine returns non-zero.
UINT16 SMCSendFrame( BYTE *pbData, DWORD dwLength )
{

    UINT16 wFrameHandle;
    UINT16 cwBufferSize;
    UINT16 wCompletionCode;
    UINT16 *pwData;
    DWORD  dwStartTime;
    BOOL bErr = FALSE;
    UINT16 wInt;
    DWORD i;

    EDBG_DEBUGLED(LED_SENDFRAME_ENTRY,0);
    EDBG_ADAPTERMSG(ZONE_SEND,("+SendFrame, dwLength: %u\n",dwLength));

#ifdef SMC_DUMP_FRAMES    
    DumpEtherFrame(pbData, (USHORT)dwLength);
#endif        


    // Calculate the memory needed = status word + byte count + data area + control byte
    // This needs to be rounded up to an even number
    cwBufferSize = 2 + 2 + (UINT16)dwLength + 1;
    if(cwBufferSize & 1)
        cwBufferSize++;
    // Allocate memory in the buffer for the Frame
    WriteWord( BANKSEL_REG, BANK2 );
    WriteWord( MMUCOMMAND_REG, (USHORT)(0x0020 | (UINT16)(cwBufferSize >> 8)) );

    // Loop here until the request is satisfied, or we timeout
    //
    dwStartTime = OEMEthGetSecs();
    while(!(ReadWord( INTERRUPT_REG ) & ALLOC_INT))
    {
        if(OEMEthGetSecs() - dwStartTime > 2)
        {
            EdbgOutputDebugString("Timed out waiting for ALLOC_INT\n");

            // Get the status word so that it can be returned.
            WriteWord( POINTER_REG, 0x2000 );           // not setting auto increment; should be safe, that is, no workaround should ne needed here.
            wCompletionCode = ReadWord( DATA_REG );
            EdbgOutputDebugString( "SendFrame return with wCompoletionCode=%x\r\n", wCompletionCode);
            return wCompletionCode;
        }
    }
    wFrameHandle = ReadWord( PNR_ARR_REG ) >> 8;

    // Now write the Frame into the buffer (Pg 39 of the SMC91C94 spec)
    WriteWord( PNR_ARR_REG, wFrameHandle );
    WriteWord( POINTER_REG, 0x4000 );
    // Write the Status Word
    WriteWord( DATA_REG, 0 );
    // Write the byte count = status word + byte count + data area + control byte
    WriteWord( DATA_REG, cwBufferSize );
    // Now write all except possibly the last data byte
    pwData = (UINT16 *)pbData;
    for(i = 0; i < (dwLength >> 1); i++)
        WriteWord( DATA_REG, *pwData++ );
    // If the number of data bytes was odd we can put that just before the Control Byte
    if(dwLength & 1)
    {
        WriteWord( DATA_REG, (USHORT)((0x3000) | *(pbData + dwLength - 1)));
        // Otherwise, the number of bytes is even, and I'll just pad the last byte with 0
    }
    else
        WriteWord( DATA_REG, 0x1000 );

    // Enqueue Frame number into TX FIFO
    WriteWord( MMUCOMMAND_REG, 0x00C0 );

    bErr = TRUE;
    dwStartTime = OEMEthGetSecs();
    while(OEMEthGetSecs() - dwStartTime < 2)
    {
        wInt = ReadWord (INTERRUPT_REG);
        if(wInt & TXEMPTY_INT)
        {
            WriteWord (INTERRUPT_REG, TXEMPTY_INT); // ACK
            wInt = ReadWord (INTERRUPT_REG);        // read status
            WriteWord (INTERRUPT_REG, wIntMask);    // re-enable interrupt
            bErr = FALSE;
            break;
        }
    }
    if(bErr)
        EdbgOutputDebugString( "SendFrame timeout waiting for TXEMPTY! INTERRUPT_REG = 0x%x\r\n", wInt);


    //  If an error occured during transmission, TX_INT will be generated.
    //  If this happens, I need to delete the untransmitted Frame
    //  from the buffer.  If no error occurred, this will be done by the chip because it's in
    //  AUTO RELEASE mode.  (Page 72, SMC spec)
    if(bErr || (TX_INT & wInt))
    {
        // Get the status word so that it can be returned.
        WriteWord( POINTER_REG, 0x2000 );
        wCompletionCode = ReadWord( DATA_REG );     // not setting auto increment; should be safe, that is, no workaround should ne needed here.

        SendFrameError("!EDBG error ",wCompletionCode);

        // Clear the statistics registers
        WriteWord( BANKSEL_REG, BANK0 );
        ReadWord( COUNTER_REG );
        // Re-enable TXENA
        WriteWord( TCR_REG, TCR_REG_INIT );

        WriteWord( BANKSEL_REG, BANK2 );

        // Release the buffer memory for the Frame that failed to transmit
        WriteWord( MMUCOMMAND_REG, 0x00A0 );
        // Wait for MMU Release to complete
        dwStartTime = OEMEthGetSecs();
        while(ReadWord( MMUCOMMAND_REG ) & 1)
        {
            if(OEMEthGetSecs() - dwStartTime > 2)
            {
                EdbgOutputDebugString("Timed out waiting for MMUCOMMAND_REG bit 1\n");
                break;
            }
        }

        if(TX_INT & wInt)
        {
            WriteWord (INTERRUPT_REG, TX_INT);      // Ack TX_INT
            WriteWord (INTERRUPT_REG, wIntMask);    // enable interrupt
        }
        EDBG_DEBUGLED(LED_SENDFRAME_EXIT, wCompletionCode);
        return wCompletionCode;
    }
    else
    {
        // Successful transmission, clear collision count
        WriteWord( BANKSEL_REG, BANK0 );        
        wInt=ReadWord(COUNTER_REG);
        wCompletionCode = ReadWord( EPHSTATUS_REG );
        EDBG_DEBUGLED(LED_SENDFRAME_EXIT,0);
        return 0;
    }

    // The TXEMPTY_INT will be disabled by the ISR.

} // SendFrame()



// This routine is called by a higher level routine if an error was detected by SendFrame()
//  returning non-zero.  The completion code that is returned is decoded here and error
//  message(s) are printed, prefixed by the supplied string.
static void SendFrameError( char *pszPrefix, UINT16 wCompletionCode )
{

    // Output any transmit error messages.  These correspond to the 
    //  EPH Status Register described on page 37 of the SMC91C94 spec.
    if(wCompletionCode & 0x8000)
        EdbgOutputDebugString( "%s::SendFrame()::Transmission Underrun\n", pszPrefix );
    if(wCompletionCode & 0x4000)
        EdbgOutputDebugString( "%s::SendFrame()::Link Test Failed\n", pszPrefix );
    if(wCompletionCode & 0x2000)
        EdbgOutputDebugString( "%s::SendFrame()::Receive Overrun\n", pszPrefix );
    if(wCompletionCode & 0x1000)
        EdbgOutputDebugString( "%s::SendFrame()::Statistics Counter Roll Over\n", pszPrefix );
    if(wCompletionCode & 0x0800)
        EdbgOutputDebugString( "%s::SendFrame()::Excessive Deferral\n", pszPrefix );
    if(wCompletionCode & 0x0400)
        EdbgOutputDebugString( "%s::SendFrame()::Lost Carrier\n", pszPrefix );
    if(wCompletionCode & 0x0200)
        EdbgOutputDebugString( "%s::SendFrame()::Late Collision\n", pszPrefix );
    if(wCompletionCode & 0x0080)
        EdbgOutputDebugString( "%s::SendFrame()::Transmission Deferred\n", pszPrefix );
    if(wCompletionCode & 0x0040)
        EdbgOutputDebugString( "%s::SendFrame()::Last Frame Was Broadcast\n", pszPrefix );
    if(wCompletionCode & 0x0020)
        EdbgOutputDebugString( "%s::SendFrame()::Signal Quality Error\n", pszPrefix );
    if(wCompletionCode & 0x0010)
        EdbgOutputDebugString( "%s::SendFrame()::16 Collisions Reached\n", pszPrefix );
    if(wCompletionCode & 0x0008)
        EdbgOutputDebugString( "%s::SendFrame()::Last Frame Was Multicast\n", pszPrefix );
    if(wCompletionCode & 0x0004)
        EdbgOutputDebugString( "%s::SendFrame()::Multiple Collisions Detected\n", pszPrefix );
    if(wCompletionCode & 0x0002)
        EdbgOutputDebugString( "%s::SendFrame()::Single Collion Detected\n", pszPrefix );
    if(wCompletionCode & 0x0001)
        EdbgOutputDebugString( "%s::SendFrame()::Transmit Successful\n", pszPrefix );
    EdbgOutputDebugString( "%s::SendFrame()::EPH Reg %Hh\n", pszPrefix, wCompletionCode );
}



//  ISR routine. Three global flags are used to communicate with GetFrame() and SendFrame().
//  They are fFrameReceived, fTXEMPTY_INT and fTX_INT and are described at the top of the file.  
static void SMC_ISR( void )
{

    UINT16 wInterruptReg;
    UINT16 wBankSave;

    // I need to save/restore the affected registers in the 91C94
    wBankSave = ReadWord( BANKSEL_REG );
    WriteWord( BANKSEL_REG, BANK2 );

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?