Webserver+3d print
Diff: cyclone_tcp/drivers/lm3s_eth.c
- Revision:
- 0:8918a71cdbe9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cyclone_tcp/drivers/lm3s_eth.c Sat Feb 04 18:15:49 2017 +0000 @@ -0,0 +1,624 @@ +/** + * @file lm3s_eth.c + * @brief Luminary Stellaris LM3S Ethernet controller + * + * @section License + * + * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. + * + * This file is part of CycloneTCP Open. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * @author Oryx Embedded SARL (www.oryx-embedded.com) + * @version 1.7.6 + **/ + +//Switch to the appropriate trace level +#define TRACE_LEVEL NIC_TRACE_LEVEL + +//LM3S6965 device? +#if defined(LM3S6965) + #include "lm3s6965.h" +//LM3S9B92 device? +#elif defined(LM3S9B92) + #include "lm3s9b92.h" +#endif + +//Dependencies +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/gpio.h" +#include "driverlib/interrupt.h" +#include "driverlib/sysctl.h" +#include "core/net.h" +#include "drivers/lm3s_eth.h" +#include "debug.h" + +//Underlying network interface +static NetInterface *nicDriverInterface; + +//IAR EWARM compiler? +#if defined(__ICCARM__) + +//Transmit buffer +#pragma data_alignment = 4 +static uint8_t txBuffer[ETH_MAX_FRAME_SIZE + 2]; +//Receive buffer +#pragma data_alignment = 4 +static uint8_t rxBuffer[ETH_MAX_FRAME_SIZE]; + +//Keil MDK-ARM or GCC compiler? +#else + +//Transmit buffer +static uint8_t txBuffer[ETH_MAX_FRAME_SIZE + 2] __attribute__((aligned(4))); +//Receive buffer +static uint8_t rxBuffer[ETH_MAX_FRAME_SIZE] __attribute__((aligned(4))); + +#endif + + +/** + * @brief Stellaris LM3S Ethernet driver + **/ + +const NicDriver lm3sEthDriver = +{ + NIC_TYPE_ETHERNET, + ETH_MTU, + lm3sEthInit, + lm3sEthTick, + lm3sEthEnableIrq, + lm3sEthDisableIrq, + lm3sEthEventHandler, + lm3sEthSendPacket, + lm3sEthSetMulticastFilter, + NULL, + NULL, + NULL, + TRUE, + TRUE, + TRUE, + FALSE +}; + + +/** + * @brief Stellaris LM3S Ethernet controller initialization + * @param[in] interface Underlying network interface + * @return Error code + **/ + +error_t lm3sEthInit(NetInterface *interface) +{ + uint_t div; + + //Debug message + TRACE_INFO("Initializing Stellaris LM3S Ethernet controller...\r\n"); + + //Save underlying network interface + nicDriverInterface = interface; + + //Enable Ethernet controller clock + SysCtlPeripheralEnable(SYSCTL_PERIPH_ETH); + //Reset Ethernet controller + SysCtlPeripheralReset(SYSCTL_PERIPH_ETH); + + //GPIO configuration + lm3sEthInitGpio(interface); + + //The MDC clock frequency cannot exceed 2.5MHz + div = SysCtlClockGet() / (2 * 2500000) - 1; + //Adjust MDC clock frequency + MAC_MDV_R = div & MAC_MDV_DIV_M; + + //Reset PHY transceiver + lm3sEthWritePhyReg(PHY_MR0, PHY_MR0_RESET); + //Wait for the reset to complete + while(lm3sEthReadPhyReg(PHY_MR0) & PHY_MR0_RESET); + + //Dump PHY registers for debugging purpose + lm3sEthDumpPhyReg(); + + //Configure LED0 and LED1 + lm3sEthWritePhyReg(PHY_MR23, PHY_MR23_LED0_RXTX | PHY_MR23_LED1_LINK); + + //Set the MAC address + MAC_IA0_R = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16); + MAC_IA1_R = interface->macAddr.w[2]; + + //Enable automatic CRC generation and packet padding + MAC_TCTL_R = MAC_TCTL_DUPLEX | MAC_TCTL_CRC | MAC_TCTL_PADEN; + //Flush the receive FIFO and enable CRC verification + MAC_RCTL_R = MAC_RCTL_RSTFIFO | MAC_RCTL_BADCRC; + + //Configure Ethernet interrupts + MAC_IM_R = MAC_IM_PHYINTM | MAC_IM_TXEMPM | MAC_IM_RXINTM; + //Configure PHY interrupts + lm3sEthWritePhyReg(PHY_MR17, PHY_MR17_LSCHG_IE); + + //Set priority grouping (3 bits for pre-emption priority, no bits for subpriority) + IntPriorityGroupingSet(LM3S_ETH_IRQ_PRIORITY_GROUPING); + //Configure Ethernet interrupt priority + IntPrioritySet(INT_ETH, LM3S_ETH_IRQ_PRIORITY); + + //Enable transmitter + MAC_TCTL_R |= MAC_TCTL_TXEN; + //Enable receiver + MAC_RCTL_R |= MAC_RCTL_RXEN; + + //Accept any packets from the upper layer + osSetEvent(&interface->nicTxEvent); + + //Successful initialization + return NO_ERROR; +} + + +//EK-LM3S6965 evaluation board? +#if defined(USE_EK_LM3S6965) + +/** + * @brief GPIO configuration + * @param[in] interface Underlying network interface + **/ + +void lm3sEthInitGpio(NetInterface *interface) +{ + //Enable GPIO clock + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); + + //Configure status LEDs + GPIOPinTypeEthernetLED(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3); +} + +#endif + + +/** + * @brief Stellaris LM3S Ethernet timer handler + * + * This routine is periodically called by the TCP/IP stack to + * handle periodic operations such as polling the link state + * + * @param[in] interface Underlying network interface + **/ + +void lm3sEthTick(NetInterface *interface) +{ +} + + +/** + * @brief Enable interrupts + * @param[in] interface Underlying network interface + **/ + +void lm3sEthEnableIrq(NetInterface *interface) +{ + //Enable Ethernet interrupts + IntEnable(INT_ETH); +} + + +/** + * @brief Disable interrupts + * @param[in] interface Underlying network interface + **/ + +void lm3sEthDisableIrq(NetInterface *interface) +{ + //Disable Ethernet interrupts + IntDisable(INT_ETH); +} + + +/** + * @brief Stellaris LM3S Ethernet interrupt service routine + **/ + +void ETH_IRQHandler(void) +{ + bool_t flag; + uint32_t status; + + //Enter interrupt service routine + osEnterIsr(); + + //This flag will be set if a higher priority task must be woken + flag = FALSE; + + //Read interrupt status register + status = MAC_RIS_R; + + //PHY interrupt? + if(status & MAC_RIS_PHYINT) + { + //Disable PHYINT interrupt + MAC_IM_R &= ~MAC_IM_PHYINTM; + + //Set event flag + nicDriverInterface->nicEvent = TRUE; + //Notify the TCP/IP stack of the event + flag |= osSetEventFromIsr(&netEvent); + } + + //Transmit FIFO empty? + if(status & MAC_RIS_TXEMP) + { + //Acknowledge TXEMP interrupt + MAC_IACK_R = MAC_IACK_TXEMP; + + //Check whether the transmit FIFO is available for writing + if(!(MAC_TR_R & MAC_TR_NEWTX)) + { + //Notify the TCP/IP stack that the transmitter is ready to send + flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent); + } + } + + //Packet received? + if(status & MAC_RIS_RXINT) + { + //Disable RXINT interrupt + MAC_IM_R &= ~MAC_IM_RXINTM; + + //Set event flag + nicDriverInterface->nicEvent = TRUE; + //Notify the TCP/IP stack of the event + flag |= osSetEventFromIsr(&netEvent); + } + + //Leave interrupt service routine + osExitIsr(flag); +} + + +/** + * @brief Stellaris LM3S Ethernet event handler + * @param[in] interface Underlying network interface + **/ + +void lm3sEthEventHandler(NetInterface *interface) +{ + uint32_t status; + uint16_t value; + + //Read interrupt status register + status = MAC_RIS_R; + + //PHY interrupt? + if(status & MAC_RIS_PHYINT) + { + //Acknowledge PHYINT interrupt + MAC_IACK_R = MAC_IACK_PHYINT; + //Read PHY interrupt status register + value = lm3sEthReadPhyReg(PHY_MR17); + + //Check whether the link state has changed + if(value & PHY_MR17_LSCHG_IE) + { + //Read PHY status register + value = lm3sEthReadPhyReg(PHY_MR1); + + //Check link state + if(value & PHY_MR1_LINK) + { + //Read PHY diagnostic register + value = lm3sEthReadPhyReg(PHY_MR18); + + //Get current speed + if(value & PHY_MR18_RATE) + { + //100BASE-TX operation + interface->linkSpeed = NIC_LINK_SPEED_100MBPS; + } + else + { + //10BASE-T operation + interface->linkSpeed = NIC_LINK_SPEED_10MBPS; + } + + //Get current duplex mode + if(value & PHY_MR18_DPLX) + { + //Full-Duplex mode + interface->duplexMode = NIC_FULL_DUPLEX_MODE; + //Update MAC configuration + MAC_TCTL_R |= MAC_TCTL_DUPLEX; + } + else + { + //Half-Duplex mode + interface->duplexMode = NIC_HALF_DUPLEX_MODE; + //Update MAC configuration + MAC_TCTL_R &= ~MAC_TCTL_DUPLEX; + } + + //Update link state + interface->linkState = TRUE; + } + else + { + //Update link state + interface->linkState = FALSE; + } + + //Process link state change event + nicNotifyLinkChange(interface); + } + } + + //Packet received? + if(status & MAC_RIS_RXINT) + { + //Acknowledge RXINT interrupt + MAC_IACK_R = MAC_IACK_RXINT; + + //Process all the pending packets + while(MAC_NP_R & MAC_NP_NPR_M) + { + //Read incoming packet + lm3sEthReceivePacket(interface); + } + } + + //Re-enable Ethernet interrupts + MAC_IM_R = MAC_IM_PHYINTM | MAC_IM_TXEMPM | MAC_IM_RXINTM; +} + + +/** + * @brief Send a packet + * @param[in] interface Underlying network interface + * @param[in] buffer Multi-part buffer containing the data to send + * @param[in] offset Offset to the first data byte + * @return Error code + **/ + +error_t lm3sEthSendPacket(NetInterface *interface, + const NetBuffer *buffer, size_t offset) +{ + size_t i; + size_t length; + uint32_t *p; + + //Retrieve the length of the packet + length = netBufferGetLength(buffer) - offset; + + //Check the frame length + if(length < sizeof(EthHeader) || length > ETH_MAX_FRAME_SIZE) + { + //The transmitter can accept another packet + osSetEvent(&interface->nicTxEvent); + //Report an error + return ERROR_INVALID_LENGTH; + } + + //Make sure the transmit FIFO is available for writing + if(MAC_TR_R & MAC_TR_NEWTX) + return ERROR_FAILURE; + + //Copy user data + netBufferRead(txBuffer + 2, buffer, offset, length); + + //The packet is preceded by a 16-bit length field + txBuffer[0] = LSB(length - sizeof(EthHeader)); + txBuffer[1] = MSB(length - sizeof(EthHeader)); + + //Point to the beginning of the packet + p = (uint32_t *) txBuffer; + //Compute the length of the packet in 32-bit words + length = (length + 5) / 4; + + //Copy packet to transmit FIFO + for(i = 0; i < length; i++) + MAC_DATA_R = p[i]; + + //Start transmitting + MAC_TR_R = MAC_TR_NEWTX; + + //Data successfully written + return NO_ERROR; +} + + +/** + * @brief Receive a packet + * @return Error code + **/ + +error_t lm3sEthReceivePacket(NetInterface *interface) +{ + error_t error; + size_t i; + size_t n; + size_t length; + uint32_t data; + uint16_t *p; + + //Make sure the FIFO is not empty + if(MAC_NP_R & MAC_NP_NPR_M) + { + //Read the first word + data = MAC_DATA_R; + //Retrieve the total length of the packet + length = data & 0xFFFF; + + //Make sure the length field is valid + if(length > 2) + { + //Point to the beginning of the buffer + p = (uint16_t *) rxBuffer; + + //Retrieve the length of the frame + length -= 2; + //Limit the number of data to be read + n = MIN(length, ETH_MAX_FRAME_SIZE); + + //Copy the first half word + if(n > 0) + *(p++) = (uint16_t) (data >> 16); + + //Copy data from receive FIFO + for(i = 2; i < n; i += 4) + { + //Read a 32-bit word from the FIFO + data = MAC_DATA_R; + //Write the 32-bit to the receive buffer + *(p++) = (uint16_t) data; + *(p++) = (uint16_t) (data >> 16); + } + + //Skip the remaining bytes + while(i < length) + { + //Read a 32-bit word from the FIFO + data = MAC_DATA_R; + //Increment byte counter + i += 4; + } + + //Valid packet received + error = NO_ERROR; + } + else + { + //Disable receiver + MAC_RCTL_R &= ~MAC_RCTL_RXEN; + //Flush the receive FIFO + MAC_RCTL_R |= MAC_RCTL_RSTFIFO; + //Re-enable receiver + MAC_RCTL_R |= MAC_RCTL_RXEN; + + //The packet is not valid + error = ERROR_INVALID_PACKET; + } + } + else + { + //No more data in the receive buffer + error = ERROR_BUFFER_EMPTY; + } + + //Check whether a valid packet has been received + if(!error) + { + //Pass the packet to the upper layer + nicProcessPacket(interface, rxBuffer, n); + } + + //Return status code + return error; +} + + +/** + * @brief Configure multicast MAC address filtering + * @param[in] interface Underlying network interface + * @return Error code + **/ + +error_t lm3sEthSetMulticastFilter(NetInterface *interface) +{ + uint_t i; + bool_t acceptMulticast; + + //This flag will be set if multicast addresses should be accepted + acceptMulticast = FALSE; + + //The MAC filter table contains the multicast MAC addresses + //to accept when receiving an Ethernet frame + for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++) + { + //Valid entry? + if(interface->macMulticastFilter[i].refCount > 0) + { + //Accept multicast addresses + acceptMulticast = TRUE; + //We are done + break; + } + } + + //Enable the reception of multicast frames if necessary + if(acceptMulticast) + MAC_RCTL_R |= MAC_RCTL_AMUL; + else + MAC_RCTL_R &= ~MAC_RCTL_AMUL; + + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Write PHY register + * @param[in] address PHY register address + * @param[in] data Register value + **/ + +void lm3sEthWritePhyReg(uint8_t address, uint16_t data) +{ + //Data to be written in the PHY register + MAC_MTXD_R = data & MAC_MTXD_MDTX_M; + //Start a write operation + MAC_MCTL_R = (address << 3) | MAC_MCTL_WRITE | MAC_MCTL_START; + + //Wait for the write to complete + while(MAC_MCTL_R & MAC_MCTL_START); +} + + +/** + * @brief Read PHY register + * @param[in] address PHY register address + * @return Register value + **/ + +uint16_t lm3sEthReadPhyReg(uint8_t address) +{ + //Start a read operation + MAC_MCTL_R = (address << 3) | MAC_MCTL_START; + + //Wait for the read to complete + while(MAC_MCTL_R & MAC_MCTL_START); + + //Return PHY register contents + return MAC_MRXD_R & MAC_MRXD_MDRX_M; +} + + +/** + * @brief Dump PHY registers for debugging purpose + **/ + +void lm3sEthDumpPhyReg(void) +{ + uint8_t i; + + //Loop through PHY registers + for(i = 0; i < 32; i++) + { + //Display current PHY register + TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, lm3sEthReadPhyReg(i)); + } + + //Terminate with a line feed + TRACE_DEBUG("\r\n"); +} +