A stack which works with or without an Mbed os library. Provides IPv4 or IPv6 with a full 1500 byte buffer.

Dependents:   oldheating gps motorhome heating

link/nic.c

Committer:
andrewboyson
Date:
2018-01-11
Revision:
61:aad055f1b0d1
Parent:
link/nic.cpp@ 60:1d8c7a1e7483
Child:
64:2f5db0839b09

File content as of revision 61:aad055f1b0d1:

#include <stdint.h>

#include "peripherals.h"
#include "nicdefs.h"
#include "nicmac.h"
#include "log.h"

#define NUM_RX_FRAMES       6           // Number of Rx Frames (== packets) was 3
#define NUM_TX_FRAMES       4           // Number of Tx Frames (== packets) was 2

#define ETH_FRAME_LEN       1536        // Maximum Ethernet Frame Size

/*
Total length is NUM_RX * ((2 * 4) + (2 * 4) + 0x600) + NUM_TX * ((2 * 4) + (1 * 4) + 0x600)
                    1  *  1552                              1548    
                    
Can fit up to 10 in total
eg 6 * 1552 + 4 * 1548 = 9312 + 6192 = 15504
*/


__attribute__((section("AHBSRAM1"),aligned(4))) static volatile         uint8_t r_buff[NUM_RX_FRAMES][ETH_FRAME_LEN];
__attribute__((section("AHBSRAM1"),aligned(4))) static volatile         uint8_t t_buff[NUM_TX_FRAMES][ETH_FRAME_LEN];
__attribute__((section("AHBSRAM1"),aligned(4))) static volatile RX_DESC_TypeDef r_desc[NUM_RX_FRAMES];
__attribute__((section("AHBSRAM1"),aligned(8))) static volatile RX_STAT_TypeDef r_stat[NUM_RX_FRAMES]; //Must be aligned on an 8 byte boundary
__attribute__((section("AHBSRAM1"),aligned(4))) static volatile TX_DESC_TypeDef t_desc[NUM_TX_FRAMES];
__attribute__((section("AHBSRAM1"),aligned(4))) static volatile TX_STAT_TypeDef t_stat[NUM_TX_FRAMES];

char* NicGetReceivedPacketOrNull(int* pSize)
{    
    if (LPC_EMAC->RxProduceIndex == LPC_EMAC->RxConsumeIndex) return NULL;
        
    uint32_t info = r_stat[LPC_EMAC->RxConsumeIndex].Info;
    *pSize = (info & RINFO_SIZE) + 1 - 4; // exclude checksum
        
    return (char*)r_buff[LPC_EMAC->RxConsumeIndex];
}
void NicReleaseReceivedPacket()
{
    if (LPC_EMAC->RxConsumeIndex == LPC_EMAC->RxDescriptorNumber) LPC_EMAC->RxConsumeIndex = 0;
    else                                                          LPC_EMAC->RxConsumeIndex++;
}
char* NicGetTransmitPacketOrNull(int* pSize)
{
    if (LPC_EMAC->TxConsumeIndex == 0 && LPC_EMAC->TxProduceIndex == LPC_EMAC->TxDescriptorNumber) return NULL;
    if (LPC_EMAC->TxProduceIndex == LPC_EMAC->TxConsumeIndex - 1)                                  return NULL;
    *pSize = ETH_FRAME_LEN - 4;
    return (char*)t_buff[LPC_EMAC->TxProduceIndex];
}
void NicSendTransmitPacket(int size)
{
    if (size == 0) return;
    t_desc[LPC_EMAC->TxProduceIndex].Ctrl = (size - 1) | (TCTRL_INT | TCTRL_LAST);
    if (LPC_EMAC->TxProduceIndex == LPC_EMAC->TxDescriptorNumber) LPC_EMAC->TxProduceIndex = 0;
    else                                                          LPC_EMAC->TxProduceIndex++;
}

static void txdscr_init()
{
    int i;

    for(i = 0; i < NUM_TX_FRAMES; i++)
    {
        t_desc[i].Packet = (uint32_t)&t_buff[i];
        t_desc[i].Ctrl   = 0;
        t_stat[i].Info   = 0;
    }

    LPC_EMAC->TxDescriptor       = (uint32_t)t_desc;         /* Set EMAC Transmit Descriptor Registers. */
    LPC_EMAC->TxStatus           = (uint32_t)t_stat;
    LPC_EMAC->TxDescriptorNumber = NUM_TX_FRAMES - 1;

    LPC_EMAC->TxProduceIndex  = 0;                           /* Tx Descriptors Point to 0 */
}

static void rxdscr_init()
{
    int i;

    for(i = 0; i < NUM_RX_FRAMES; i++)
    {
        r_desc[i].Packet  = (uint32_t)&r_buff[i];
        r_desc[i].Ctrl    = RCTRL_INT | (ETH_FRAME_LEN-1);
        r_stat[i].Info    = 0;
        r_stat[i].HashCRC = 0;
    }

    LPC_EMAC->RxDescriptor       = (uint32_t)r_desc;        /* Set EMAC Receive Descriptor Registers. */
    LPC_EMAC->RxStatus           = (uint32_t)r_stat;        //Must be aligned on an 8 byte boundary
    LPC_EMAC->RxDescriptorNumber = NUM_RX_FRAMES - 1;

    LPC_EMAC->RxConsumeIndex  = 0;                          /* Rx Descriptors Point to 0 */
}
static int phy_write(unsigned int PhyReg, unsigned short Data)
{
    unsigned int timeOut;

    LPC_EMAC->MADR = DP83848C_DEF_ADR | PhyReg;
    LPC_EMAC->MWTD = Data;

    // Wait until operation completed
    for(timeOut = 0; timeOut < MII_WR_TOUT; timeOut++)
    {
        if((LPC_EMAC->MIND & MIND_BUSY) == 0)  return 0;
    }

    //Timed out
    return -1;
}

static int phy_read(unsigned int PhyReg)
{
    unsigned int timeOut;

    LPC_EMAC->MADR = DP83848C_DEF_ADR | PhyReg;
    LPC_EMAC->MCMD = MCMD_READ;

    // Wait until operation completed
    for(timeOut = 0; timeOut < MII_RD_TOUT; timeOut++)
    {
        if((LPC_EMAC->MIND & MIND_BUSY) == 0)
        {
            LPC_EMAC->MCMD = 0;
            return LPC_EMAC->MRDD; // Return a 16-bit value.
        }
    }
    return -1;
}

void NicLinkAddress(char *mac)
{
    mac[5] = LPC_EMAC->SA0 >> 8;
    mac[4] = LPC_EMAC->SA0 & 0xFF;
    mac[3] = LPC_EMAC->SA1 >> 8;
    mac[2] = LPC_EMAC->SA1 & 0xFF;
    mac[1] = LPC_EMAC->SA2 >> 8;
    mac[0] = LPC_EMAC->SA2 & 0xFF;
}

void NicLinkSetSpeedDuplex(int speed, int duplex)
{
    unsigned short phy_data;
    int tout;

    if((speed < 0) || (speed > 1)) phy_data = PHY_AUTO_NEG;
    else                           phy_data = (((unsigned short) speed << 13) | ((unsigned short) duplex << 8));

    phy_write(PHY_REG_BMCR, phy_data);

    for(tout = 100; tout; tout--) __nop();     /* A short delay */
    
    phy_data = phy_read(PHY_REG_STS);

    if(phy_data & PHY_STS_DUPLEX)
    {
        LPC_EMAC->MAC2    |= MAC2_FULL_DUP;
        LPC_EMAC->Command |=   CR_FULL_DUP;
        LPC_EMAC->IPGT     = IPGT_FULL_DUP;
    }
    else
    {
        LPC_EMAC->MAC2    &= ~MAC2_FULL_DUP;
        LPC_EMAC->Command &=   ~CR_FULL_DUP;
        LPC_EMAC->IPGT     =  IPGT_HALF_DUP;
    }

    if(phy_data & PHY_STS_SPEED)
    {
        LPC_EMAC->SUPP &= ~SUPP_SPEED;
    }
    else
    {
        LPC_EMAC->SUPP |= SUPP_SPEED;
    }
}

int NicLinkIsUp(void)
{
    return (phy_read(PHY_REG_STS) & PHY_STS_LINK);
}
static int phy_reset()
{
    int regv, tout;
    
    // perform PHY reset
    phy_write(PHY_REG_BMCR, PHY_BMCR_RESET);           
  
    // Wait for hardware reset to end.
    for(tout = 0x20000; ; tout--)
    {                    
        regv = phy_read(PHY_REG_BMCR);
        if(regv < 0 || tout == 0)     return -1;  // Error
        if(!(regv & PHY_BMCR_RESET))  break;      // Reset complete.
    }
    uint32_t phy_id  = (phy_read(PHY_REG_IDR1) << 16);
    phy_id |= (phy_read(PHY_REG_IDR2) & 0XFFF0);

    //Check is the right PHY
    if (phy_id != DP83848C_ID)
    {
        LogTimeF("Unknown Ethernet PHY (%x)", (unsigned int)phy_id);
        return -1;
    }
    return 0;
}
int NicInit()
{
    int tout;
    char mac[6];
    unsigned int clock = 10; //96,000,000
    
    // Power Up the EMAC controller.
    LPC_SC->PCONP |= 0x40000000;                       
    
    // Enable P1 Ethernet Pins.
    LPC_PINCON->PINSEL2 = 0x50150105;                 
    LPC_PINCON->PINSEL3 = (LPC_PINCON->PINSEL3 & ~0x0000000F) | 0x00000005;
    
    // Reset all EMAC internal modules.
    LPC_EMAC->MAC1    = MAC1_RES_TX | MAC1_RES_MCS_TX | MAC1_RES_RX | MAC1_RES_MCS_RX | MAC1_SIM_RES | MAC1_SOFT_RES;
    LPC_EMAC->Command = CR_REG_RES | CR_TX_RES | CR_RX_RES | CR_PASS_RUNT_FRM;
    
    // A short delay after reset.
    for(tout = 100; tout; tout--) __nop();             
    
    // Initialize MAC control registers.
    LPC_EMAC->MAC1 = MAC1_PASS_ALL;                   
    LPC_EMAC->MAC2 = MAC2_CRC_EN | MAC2_PAD_EN;
    LPC_EMAC->MAXF = ETH_FRAME_LEN;
    LPC_EMAC->CLRT = CLRT_DEF;
    LPC_EMAC->IPGR = IPGR_DEF;
    
    // Enable Reduced MII interface.
    LPC_EMAC->Command = CR_RMII | CR_PASS_RUNT_FRM;    
    
    // Set clock and reset
    LPC_EMAC->MCFG = (clock << 0x2) & MCFG_CLK_SEL;
    LPC_EMAC->MCFG |= MCFG_RES_MII;
    
    // A short delay after reset
    for(tout = 100; tout; tout--) __nop();
    
    // Set clock
    LPC_EMAC->MCFG = (clock << 0x2) & MCFG_CLK_SEL;
    LPC_EMAC->MCMD = 0;
    
    // Reset Reduced MII Logic.
    LPC_EMAC->SUPP = SUPP_RES_RMII;                    
    
    // A short delay
    for (tout = 100; tout; tout--) __nop();
    
    LPC_EMAC->SUPP = 0;
    
    //Reset the PHY
    if (phy_reset()) return -1;
    
    //Set the link to auto negotiate
    NicLinkSetSpeedDuplex(-1, 0);
    
    // Set the Ethernet MAC Address registers
    NicMac(mac);
    LPC_EMAC->SA0 = ((uint32_t)mac[5] << 8) | (uint32_t)mac[4];
    LPC_EMAC->SA1 = ((uint32_t)mac[3] << 8) | (uint32_t)mac[2];
    LPC_EMAC->SA2 = ((uint32_t)mac[1] << 8) | (uint32_t)mac[0];
    
    //Initialise DMA descriptors
    txdscr_init();
    rxdscr_init();
    
    // Set filter
    LPC_EMAC->RxFilterCtrl = RFC_UCAST_EN | RFC_MCAST_EN | RFC_BCAST_EN | RFC_PERFECT_EN;
                                                      
    // Disable and clear EMAC interrupts
    LPC_EMAC->IntEnable = 0;
    LPC_EMAC->IntClear  = 0xFFFF;
    
    //Enable receive and transmit
    LPC_EMAC->Command  |= (CR_RX_EN | CR_TX_EN);
    LPC_EMAC->MAC1     |= MAC1_REC_EN;
    
    //Return success
    return 0;
}