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
Diff: link/nic.c
- Revision:
- 61:aad055f1b0d1
- Parent:
- 60:1d8c7a1e7483
- Child:
- 64:2f5db0839b09
diff -r 1d8c7a1e7483 -r aad055f1b0d1 link/nic.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/link/nic.c Thu Jan 11 17:38:21 2018 +0000 @@ -0,0 +1,284 @@ +#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; +}