X-Git-Url: https://git.sur5r.net/?p=freertos;a=blobdiff_plain;f=FreeRTOS-Plus%2FSource%2FFreeRTOS-Plus-TCP%2Fportable%2FNetworkInterface%2Fksz8851snl%2FNetworkInterface.c;fp=FreeRTOS-Plus%2FSource%2FFreeRTOS-Plus-TCP%2Fportable%2FNetworkInterface%2Fksz8851snl%2FNetworkInterface.c;h=e4ae1168f6ef0a1e52e8f47f03e44ac3938d7f62;hp=544e5ba095497386218b939f7dbc2d1ba1ee632c;hb=b15dfacb6026af3b0ba697e5753844923b468d2b;hpb=4334233a064299a09d167a497889d3860932a587 diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/ksz8851snl/NetworkInterface.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/ksz8851snl/NetworkInterface.c index 544e5ba09..e4ae1168f 100644 --- a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/ksz8851snl/NetworkInterface.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/ksz8851snl/NetworkInterface.c @@ -1,1272 +1,1272 @@ -/* -FreeRTOS+TCP V2.0.11 -Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - http://aws.amazon.com/freertos - http://www.FreeRTOS.org -*/ - -/* Standard includes. */ -#include -#include -#include -#include - -/* FreeRTOS includes. */ -#include "FreeRTOS.h" -#include "task.h" -#include "queue.h" -#include "semphr.h" - -/* FreeRTOS+TCP includes. */ -#include "FreeRTOS_IP.h" -#include "FreeRTOS_Sockets.h" -#include "FreeRTOS_IP_Private.h" -#include "NetworkBufferManagement.h" -#include "NetworkInterface.h" - -#include "sam4e_xplained_pro.h" -#include "hr_gettime.h" -#include "conf_eth.h" -#include "ksz8851snl.h" -#include "ksz8851snl_reg.h" - -/* Some files from the Atmel Software Framework */ -#include -#include -#include - -/* - Sending a packet: - - 1) Called by UP-task, add buffer to the TX-list: - xNetworkInterfaceOutput() - tx_buffers[ us_tx_head ] = pxNetworkBuffer; - tx_busy[ us_tx_head ] = pdTRUE; - us_tx_head++; - - 2) Called by EMAC-Task: start SPI transfer - ksz8851snl_update() - if( ul_spi_pdc_status == SPI_PDC_IDLE ) - { - if( ( tx_busy[ us_tx_tail ] != pdFALSE ) && - ( us_pending_frame == 0 ) && - ( ul_had_intn_interrupt == 0 ) ) - { - // disable all interrupts. - ksz8851_reg_write( REG_INT_MASK, 0 ); - Bring KSZ8851SNL_CSN_GPIO low - ksz8851_fifo_write( pxNetworkBuffer->pucEthernetBuffer, xLength, xLength ); - ul_spi_pdc_status = SPI_PDC_TX_START; - tx_cur_buffer = pxNetworkBuffer; - } - } - 3) Wait for SPI RXBUFF interrupt - SPI_Handler() - if( ul_spi_pdc_status == SPI_PDC_TX_START ) - { - if( SPI_Status & SPI_SR_RXBUFF ) - { - ul_spi_pdc_status = SPI_PDC_TX_COMPLETE; - } - } - - 4) Called by EMAC-Task: finish SPI transfer - ksz8851snl_update() - if( ul_spi_pdc_status == SPI_PDC_TX_COMPLETE ) - { - ul_spi_pdc_status = SPI_PDC_IDLE; - Bring KSZ8851SNL_CSN_GPIO high - // TX step12: disable TXQ write access. - ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); - // TX step12.1: enqueue frame in TXQ. - ksz8851_reg_setbits( REG_TXQ_CMD, TXQ_ENQUEUE ); - - // RX step13: enable INT_RX flag. - ksz8851_reg_write( REG_INT_MASK, INT_RX ); - - // Buffer sent, free the corresponding buffer and mark descriptor as owned by software. - vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); - - tx_buffers[ us_tx_tail ] = NULL; - tx_busy[ us_tx_tail ] = pdFALSE; - us_tx_tail++ - } - - Receiving a packet: - - 1) Wait for a INTN interrupt - INTN_Handler() - ul_had_intn_interrupt = 1 - vTaskNotifyGiveFromISR(); // Wake up the EMAC task - - 2) Called by EMAC-Task: check for new fragments and start SPI transfer - ksz8851snl_update() - if( ul_spi_pdc_status == SPI_PDC_IDLE ) - { - if( ( ul_had_intn_interrupt != 0 ) || ( us_pending_frame > 0 ) ) - { - if( us_pending_frame == 0 ) - { - us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8; - if( us_pending_frame == 0 ) - { - break; - } - } - // RX step2: disable all interrupts. - ksz8851_reg_write( REG_INT_MASK, 0 ); - Check if there is a valid packet: REG_RX_FHR_STATUS - Read the length of the next fragment: REG_RX_FHR_BYTE_CNT - ul_spi_pdc_status = SPI_PDC_RX_START; - gpio_set_pin_low(KSZ8851SNL_CSN_GPIO); - // Start SPI data transfer - ksz8851_fifo_read( pxNetworkBuffer->pucEthernetBuffer, xReadLength ); - } - } - - 3) Wait for SPI RXBUFF interrupt - SPI_Handler() - if( ul_spi_pdc_status == SPI_PDC_RX_START: - { - if( ( ulCurrentSPIStatus & SPI_SR_RXBUFF ) != 0 ) - { - // Transfer complete, disable SPI RXBUFF interrupt. - spi_disable_interrupt( KSZ8851SNL_SPI, SPI_IDR_RXBUFF ); - - ul_spi_pdc_status = SPI_PDC_RX_COMPLETE; - } - } - } - - 4) Finish SPI transfer - ksz8851snl_update() - if( ul_spi_pdc_status == SPI_PDC_RX_COMPLETE ) - { - ul_spi_pdc_status = SPI_PDC_IDLE; - Bring KSZ8851SNL_CSN_GPIO high - // RX step21: end RXQ read access. - ksz8851_reg_clrbits(REG_RXQ_CMD, RXQ_START); - // RX step22-23: update frame count to be read. - us_pending_frame-- - // RX step24: enable INT_RX flag if transfer complete. - if( us_pending_frame == 0 ) - { - // Allow more RX interrupts. - ksz8851_reg_write( REG_INT_MASK, INT_RX ); - } - - // Mark descriptor ready to be read. - rx_ready[ rxHead ] = pdTRUE; - rxHead++ - } -*/ - -#define PHY_REG_00_BMCR 0x00 // Basic mode control register -#define PHY_REG_01_BMSR 0x01 // Basic mode status register -#define PHY_REG_02_PHYSID1 0x02 // PHYS ID 1 -#define PHY_REG_03_PHYSID2 0x03 // PHYS ID 2 -#define PHY_REG_04_ADVERTISE 0x04 // Advertisement control reg -#define PHY_REG_05_LPA 0x05 // Link partner ability reg -#define PHY_REG_06_ANER 0x06 // 6 RW Auto-Negotiation Expansion Register -#define PHY_REG_07_ANNPTR 0x07 // 7 RW Auto-Negotiation Next Page TX -#define PHY_REG_08_RESERVED0 0x08 // 0x08..0x0Fh 8-15 RW RESERVED - -#define BMSR_LINK_STATUS 0x0004 //!< Link status - -#ifndef PHY_LS_HIGH_CHECK_TIME_MS - /* Check if the LinkSStatus in the PHY is still high after 15 seconds of not - receiving packets. */ - #define PHY_LS_HIGH_CHECK_TIME_MS 15000 -#endif - -#ifndef PHY_LS_LOW_CHECK_TIME_MS - /* Check if the LinkSStatus in the PHY is still low every second. */ - #define PHY_LS_LOW_CHECK_TIME_MS 1000 -#endif - -/* Interrupt events to process. Currently only the Rx event is processed -although code for other events is included to allow for possible future -expansion. */ -#define EMAC_IF_RX_EVENT 1UL -#define EMAC_IF_TX_EVENT 2UL -#define EMAC_IF_ERR_EVENT 4UL -#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT ) - -#define ETHERNET_CONF_PHY_ADDR BOARD_GMAC_PHY_ADDR - -#ifdef ipconfigHAS_TX_CRC_OFFLOADING - #undef ipconfigHAS_TX_CRC_OFFLOADING -#endif -/* Override this define because the KSZ8851 is programmed to set all outgoing CRC's */ -#define ipconfigHAS_TX_CRC_OFFLOADING 1 - -#ifndef EMAC_MAX_BLOCK_TIME_MS - #define EMAC_MAX_BLOCK_TIME_MS 100ul -#endif - -/* Default the size of the stack used by the EMAC deferred handler task to 4x -the size of the stack used by the idle task - but allow this to be overridden in -FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ -#ifndef configEMAC_TASK_STACK_SIZE - #define configEMAC_TASK_STACK_SIZE ( 6 * configMINIMAL_STACK_SIZE ) -#endif - -#define SPI_PDC_IDLE 0 -#define SPI_PDC_RX_START 1 -#define SPI_PDC_TX_ERROR 2 -#define SPI_PDC_RX_COMPLETE 3 -#define SPI_PDC_TX_START 4 -#define SPI_PDC_RX_ERROR 5 -#define SPI_PDC_TX_COMPLETE 6 - -/** - * ksz8851snl driver structure. - */ -typedef struct { - /** Set to 1 when owner is software (ready to read), 0 for Micrel. */ - uint32_t rx_ready[MICREL_RX_BUFFERS]; - /** Set to 1 when owner is Micrel, 0 for software. */ - uint32_t tx_busy[MICREL_TX_BUFFERS]; - /** RX NetworkBufferDescriptor_t pointer list */ - NetworkBufferDescriptor_t *rx_buffers[MICREL_RX_BUFFERS]; - /** TX NetworkBufferDescriptor_t pointer list */ - NetworkBufferDescriptor_t *tx_buffers[MICREL_TX_BUFFERS]; - NetworkBufferDescriptor_t *tx_cur_buffer; - - /** Circular buffer head pointer for packet received. */ - uint32_t us_rx_head; - /** Circular buffer tail pointer for packet to be read. */ - uint32_t us_rx_tail; - /** Circular buffer head pointer by upper layer (buffer to be sent). */ - uint32_t us_tx_head; - /** Circular buffer tail pointer incremented by handlers (buffer sent). */ - uint32_t us_tx_tail; - - uint32_t ul_total_tx; - uint32_t ul_total_rx; - uint32_t tx_space; - - /** Still experimental: hash table to allow certain multicast addresses. */ - uint16_t pusHashTable[ 4 ]; - - /* ul_spi_pdc_status has "SPI_PDC_xxx" values. */ - volatile uint32_t ul_spi_pdc_status; - - /* ul_had_intn_interrupt becomes true within the INTN interrupt. */ - volatile uint32_t ul_had_intn_interrupt; - - uint16_t us_pending_frame; -} xKSZ8851_Device_t; - -/* SPI PDC register base. -Declared in ASF\sam\components\ksz8851snl\ksz8851snl.c */ -extern Pdc *g_p_spi_pdc; - -/* Temporary buffer for PDC reception. -declared in ASF\sam\components\ksz8851snl\ksz8851snl.c */ -extern uint8_t tmpbuf[1536]; - -COMPILER_ALIGNED(8) -static xKSZ8851_Device_t xMicrelDevice; - -static TaskHandle_t xTransmitHandle; - -/*-----------------------------------------------------------*/ - -/* - * Wait a fixed time for the link status to indicate the network is up. - */ -static BaseType_t xGMACWaitLS( TickType_t xMaxTime ); - -/* - * A deferred interrupt handler task that processes GMAC interrupts. - */ -static void prvEMACHandlerTask( void *pvParameters ); - -/* - * Try to obtain an Rx packet from the hardware. - */ -static uint32_t prvEMACRxPoll( void ); - -static inline unsigned long ulReadMDIO( unsigned uAddress ); - -static void ksz8851snl_low_level_init( void ); - -static NetworkBufferDescriptor_t *ksz8851snl_low_level_input( void ); - -/*-----------------------------------------------------------*/ - -/* Bit map of outstanding ETH interrupt events for processing. Currently only -the Rx interrupt is handled, although code is included for other events to -enable future expansion. */ -static volatile uint32_t ulISREvents; - -/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ -static uint32_t ulPHYLinkStatus = 0; -static volatile BaseType_t xGMACSwitchRequired; - -static void ksz8851snl_update( void ); - -static void ksz8851snl_rx_init( void ); - -static void ksz8851snl_tx_init( void ); - -/* Holds the handle of the task used as a deferred interrupt processor. The -handle is used so direct notifications can be sent to the task for all EMAC/DMA -related interrupts. */ -TaskHandle_t xEMACTaskHandle = NULL; - - -/*-----------------------------------------------------------*/ - -BaseType_t xNetworkInterfaceInitialise( void ) -{ -const TickType_t x5_Seconds = 5000UL; - - if( xEMACTaskHandle == NULL ) - { - ksz8851snl_low_level_init(); - - /* Wait at most 5 seconds for a Link Status in the PHY. */ - xGMACWaitLS( pdMS_TO_TICKS( x5_Seconds ) ); - - /* The handler task is created at the highest possible priority to - ensure the interrupt handler can return directly to it. */ - xTaskCreate( prvEMACHandlerTask, "KSZ8851", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle ); - configASSERT( xEMACTaskHandle ); - } - - /* When returning non-zero, the stack will become active and - start DHCP (in configured) */ - ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); - - return ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0; -} -/*-----------------------------------------------------------*/ - -BaseType_t xGetPhyLinkStatus( void ) -{ -BaseType_t xResult; - - /* This function returns true if the Link Status in the PHY is high. */ - if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) - { - xResult = pdTRUE; - } - else - { - xResult = pdFALSE; - } - - return xResult; -} -/*-----------------------------------------------------------*/ - -BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, BaseType_t bReleaseAfterSend ) -{ -BaseType_t xResult = pdFALSE; -int txHead = xMicrelDevice.us_tx_head; - - /* Make sure the next descriptor is free. */ - if( xMicrelDevice.tx_busy[ txHead ] != pdFALSE ) - { - /* All TX buffers busy. */ - } - else if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) == 0 ) - { - /* Output: LS low. */ - } - else - { - /* Pass the packet. */ - xMicrelDevice.tx_buffers[ txHead ] = pxNetworkBuffer; - /* The descriptor is now owned by Micrel. */ - xMicrelDevice.tx_busy[ txHead ] = pdTRUE; - - /* Move the head pointer. */ - if( ++txHead == MICREL_TX_BUFFERS ) - { - txHead = 0; - } - xMicrelDevice.us_tx_head = txHead; - if( xEMACTaskHandle != NULL ) - { - xTaskNotifyGive( xEMACTaskHandle ); - } - - #if( ipconfigZERO_COPY_TX_DRIVER != 1 ) - #warning Please ipconfigZERO_COPY_TX_DRIVER as 1 - #endif - configASSERT( bReleaseAfterSend != pdFALSE ); - xResult = pdTRUE; - } - if( ( xResult == pdFALSE ) && ( bReleaseAfterSend != pdFALSE ) ) - { - vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); - } - return xResult; -} -/*-----------------------------------------------------------*/ - -/* This Micrel has numbered it's PHY registers in a different way. -Translate the register index. */ -static int ks8851_phy_reg( int reg ) -{ - switch (reg) { - case PHY_REG_00_BMCR: - return REG_PHY_CNTL; // P1MBCR; - case PHY_REG_01_BMSR: - return REG_PHY_STATUS; - case PHY_REG_02_PHYSID1: - return REG_PHY_ID_LOW; - case PHY_REG_03_PHYSID2: - return REG_PHY_ID_HIGH; - case PHY_REG_04_ADVERTISE: - return REG_PHY_AUTO_NEGOTIATION; - case PHY_REG_05_LPA: - return REG_PHY_REMOTE_CAPABILITY; - } - - return 0x0; -} -/*-----------------------------------------------------------*/ - -static inline unsigned long ulReadMDIO( unsigned uAddress ) -{ -uint16_t usPHYStatus; -int ks8851_reg = ks8851_phy_reg( uAddress ); - - if( ks8851_reg != 0 ) - { - usPHYStatus = ksz8851_reg_read( ks8851_reg ); - } - else - { - /* Other addresses not yet implemented. */ - usPHYStatus = 0; - } - return usPHYStatus; -} -/*-----------------------------------------------------------*/ - -static BaseType_t xGMACWaitLS( TickType_t xMaxTime ) -{ -TickType_t xStartTime = xTaskGetTickCount(); -TickType_t xEndTime; -BaseType_t xReturn; -const TickType_t xShortTime = pdMS_TO_TICKS( 100UL ); -const uint32_t ulHz_Per_MHz = 1000000UL; - - for( ;; ) - { - xEndTime = xTaskGetTickCount(); - - if( ( xEndTime - xStartTime ) > xMaxTime ) - { - /* Wated more than xMaxTime, return. */ - xReturn = pdFALSE; - break; - } - - /* Check the link status again. */ - ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); - - if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) - { - /* Link is up - return. */ - xReturn = pdTRUE; - break; - } - - /* Link is down - wait in the Blocked state for a short while (to allow - other tasks to execute) before checking again. */ - vTaskDelay( xShortTime ); - } - - FreeRTOS_printf( ( "xGMACWaitLS: %ld freq %lu Mz\n", - xReturn, - sysclk_get_cpu_hz() / ulHz_Per_MHz ) ); - - return xReturn; -} -/*-----------------------------------------------------------*/ - -static void vPioSetPinHigh(uint32_t ul_pin) -{ - Pio *p_pio = (Pio *)((uint32_t)PIOA + (PIO_DELTA * (ul_pin >> 5))); - // Value to be driven on the I/O line: 1. - p_pio->PIO_SODR = 1 << (ul_pin & 0x1F); -} - -/** - * \brief Handler for SPI interrupt. - */ -void SPI_Handler(void) -{ -BaseType_t xDoWakeup = pdFALSE; -BaseType_t xKSZTaskWoken = pdFALSE; -uint32_t ulCurrentSPIStatus; -uint32_t ulEnabledSPIStatus; - - ulCurrentSPIStatus = spi_read_status( KSZ8851SNL_SPI ); - ulEnabledSPIStatus = spi_read_interrupt_mask( KSZ8851SNL_SPI ); - ulCurrentSPIStatus &= ulEnabledSPIStatus; - spi_disable_interrupt( KSZ8851SNL_SPI, ulCurrentSPIStatus ); - - - switch( xMicrelDevice.ul_spi_pdc_status ) - { - case SPI_PDC_RX_START: - { - if( ( ulCurrentSPIStatus & SPI_SR_OVRES ) != 0 ) - { - pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS); - xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_ERROR; - xDoWakeup = pdTRUE; - } - else - { - if( ( ulCurrentSPIStatus & SPI_SR_RXBUFF ) != 0 ) - { - xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_COMPLETE; - xDoWakeup = pdTRUE; - } - } - } - break; - - case SPI_PDC_TX_START: - { - /* Middle of TX. */ - if( ( ulCurrentSPIStatus & SPI_SR_OVRES ) != 0 ) - { - pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS); - xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_ERROR; - xDoWakeup = pdTRUE; - } - else - { - if( ( ulCurrentSPIStatus & SPI_SR_ENDRX ) != 0 ) - { - /* Enable RX complete interrupt. */ - spi_enable_interrupt( KSZ8851SNL_SPI, SPI_IER_RXBUFF ); - } - /* End of TX. */ - if( ( ulCurrentSPIStatus & SPI_END_OF_TX ) != 0 ) - { - xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_COMPLETE; - xDoWakeup = pdTRUE; - } - } - } - break; - } /* switch( xMicrelDevice.ul_spi_pdc_status ) */ - - if( xDoWakeup != pdFALSE ) - { - if( xEMACTaskHandle != NULL ) - { - vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xKSZTaskWoken ); - } - } - else - { - } - portEND_SWITCHING_ISR( xKSZTaskWoken ); -} -/*-----------------------------------------------------------*/ - -static void INTN_Handler(uint32_t id, uint32_t mask) -{ -BaseType_t xKSZTaskWoken = pdFALSE; - - if( ( id == INTN_ID ) && - ( mask == INTN_PIN_MSK ) ) - { - /* Clear the PIO interrupt flags. */ - pio_get_interrupt_status( INTN_PIO ); - - /* Set the INTN flag. */ - xMicrelDevice.ul_had_intn_interrupt++; - if( xEMACTaskHandle != NULL ) - { - vTaskNotifyGiveFromISR( xEMACTaskHandle, &( xKSZTaskWoken ) ); - } - } - portEND_SWITCHING_ISR( xKSZTaskWoken ); -} -/*-----------------------------------------------------------*/ - -/** - * \brief Populate the RX descriptor ring buffers with pbufs. - * - * \param p_ksz8851snl_dev Pointer to driver data structure. - */ -static void ksz8851snl_rx_populate_queue( void ) -{ - uint32_t ul_index = 0; - NetworkBufferDescriptor_t *pxNetworkBuffer; - - /* Set up the RX descriptors */ - for (ul_index = 0; ul_index < MICREL_RX_BUFFERS; ul_index++) { - if( xMicrelDevice.rx_buffers[ ul_index ] == NULL ) - { - /* Allocate a new NetworkBufferDescriptor_t with the maximum size. */ - pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ipconfigNETWORK_MTU + 36, 100 ); - if( pxNetworkBuffer == NULL ) - { - FreeRTOS_printf( ( "ksz8851snl_rx_populate_queue: NetworkBufferDescriptor_t allocation failure\n" ) ); - configASSERT( 1 == 2 ); - } - - /* Make sure lwIP is well configured so one NetworkBufferDescriptor_t can contain the maximum packet size. */ - //LWIP_ASSERT("ksz8851snl_rx_populate_queue: NetworkBufferDescriptor_t size too small!", pbuf_clen(pxNetworkBuffer) <= 1); - - /* Save NetworkBufferDescriptor_t pointer to be sent to lwIP upper layer. */ - xMicrelDevice.rx_buffers[ ul_index ] = pxNetworkBuffer; - /* Pass it to Micrel for reception. */ - xMicrelDevice.rx_ready[ ul_index ] = pdFALSE; - } - } -} - -unsigned tx_space, wait_tx_space, tx_status, fhr_status; -unsigned rx_debug = 0; -/** - * \brief Update Micrel state machine and perform required actions. - * - * \param netif the lwIP network interface structure for this ethernetif. - */ -static void ksz8851snl_update() -{ - uint16_t txmir = 0; - -/* Check for free PDC. */ - switch( xMicrelDevice.ul_spi_pdc_status ) - { - case SPI_PDC_TX_ERROR: - { - uint32_t ulValue; - // /* TX step11: end TX transfer. */ - gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); - - vTaskDelay( 2 ); gpio_set_pin_low( KSZ8851SNL_CSN_GPIO ); - vTaskDelay( 1 ); gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); - vTaskDelay( 1 ); - - /* Disable asynchronous transfer mode. */ - xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE; - - /* TX step12: disable TXQ write access. */ - ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); - - ulValue = ksz8851snl_reset_tx(); - - xMicrelDevice.tx_space = ksz8851_reg_read( REG_TX_MEM_INFO ) & TX_MEM_AVAILABLE_MASK; - - FreeRTOS_printf( ("SPI_PDC_TX_ERROR %02X\n", ulValue ) ); - } - break; - - case SPI_PDC_RX_ERROR: - { - uint32_t ulValue; - /* TX step11: end TX transfer. */ - gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); - - vTaskDelay( 2 ); gpio_set_pin_low( KSZ8851SNL_CSN_GPIO ); - vTaskDelay( 1 ); gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); - vTaskDelay( 1 ); - - /* Disable asynchronous transfer mode. */ - xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE; - - /* TX step12: disable TXQ write access. */ - ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); - - //ulValue = ksz8851snl_reset_rx(); - ulValue = ksz8851snl_reinit(); - - xGMACWaitLS( pdMS_TO_TICKS( 5000UL ) ); - - FreeRTOS_printf( ("SPI_PDC_RX_ERROR %02X\n", ulValue ) ); - } - break; - } - switch( xMicrelDevice.ul_spi_pdc_status ) - { - case SPI_PDC_IDLE: - { - int txTail = xMicrelDevice.us_tx_tail; - - /* - * ========================== Handle RX ========================== - */ - if( ( xMicrelDevice.ul_had_intn_interrupt != 0 ) || ( xMicrelDevice.us_pending_frame > 0 ) ) - { - int rxHead = xMicrelDevice.us_rx_head; - NetworkBufferDescriptor_t *pxNetworkBuffer; -#warning try - xMicrelDevice.ul_had_intn_interrupt = 0; - - if( xMicrelDevice.us_pending_frame == 0 ) - { - uint16_t int_status; - /* RX step1: read interrupt status for INT_RX flag. */ - int_status = ksz8851_reg_read( REG_INT_STATUS ); - - - /* RX step2: disable all interrupts. */ - ksz8851_reg_write( REG_INT_MASK, 0 ); - - /* RX step3: clear INT_RX flag. */ - ksz8851_reg_setbits( REG_INT_STATUS, INT_RX ); - - /* RX step4-5: check for received frames. */ - xMicrelDevice.us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8; - if( xMicrelDevice.us_pending_frame == 0 ) - { - /* RX step24: enable INT_RX flag. */ - ksz8851_reg_write(REG_INT_MASK, INT_RX); - return; - } - } -#warning try - xMicrelDevice.ul_had_intn_interrupt = 0; - - /* Now xMicrelDevice.us_pending_frame != 0 */ - - /* Don't break Micrel state machine, wait for a free descriptor first! */ - if( xMicrelDevice.rx_ready[ rxHead ] != pdFALSE ) - { - FreeRTOS_printf( ( "ksz8851snl_update: out of free descriptor! [tail=%u head=%u]\n", - xMicrelDevice.us_rx_tail, rxHead ) ); - return; - } - pxNetworkBuffer = xMicrelDevice.rx_buffers[ rxHead ]; - - if( pxNetworkBuffer == NULL ) - { - ksz8851snl_rx_populate_queue(); - FreeRTOS_printf( ( "ksz8851snl_update: no buffer set [head=%u]\n", rxHead ) ); - return; - } - - /* RX step6: get RX packet status. */ - fhr_status = ksz8851_reg_read( REG_RX_FHR_STATUS ); - if( ( ( fhr_status & RX_VALID ) == 0 ) || ( ( fhr_status & RX_ERRORS ) != 0 ) ) - { - ksz8851_reg_setbits(REG_RXQ_CMD, RXQ_CMD_FREE_PACKET); - FreeRTOS_printf( ( "ksz8851snl_update: RX packet error!\n" ) ); - - /* RX step4-5: check for received frames. */ - xMicrelDevice.us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8; - if( xMicrelDevice.us_pending_frame == 0 ) - { - /* RX step24: enable INT_RX flag. */ - ksz8851_reg_write(REG_INT_MASK, INT_RX); - } - ulISREvents |= EMAC_IF_ERR_EVENT; - } - else - { - size_t xLength; - /* RX step7: read frame length. */ - xLength = ksz8851_reg_read(REG_RX_FHR_BYTE_CNT) & RX_BYTE_CNT_MASK; - - /* RX step8: Drop packet if len is invalid or no descriptor available. */ - if( xLength == 0 ) - { - ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_CMD_FREE_PACKET ); - FreeRTOS_printf( ( "ksz8851snl_update: RX bad len!\n" ) ); - ulISREvents |= EMAC_IF_ERR_EVENT; - } - else - { - size_t xReadLength = xLength; - - xMicrelDevice.ul_total_rx++; - /* RX step9: reset RX frame pointer. */ - ksz8851_reg_clrbits(REG_RX_ADDR_PTR, ADDR_PTR_MASK); - - /* RX step10: start RXQ read access. */ - ksz8851_reg_setbits(REG_RXQ_CMD, RXQ_START); - /* RX step11-17: start asynchronous FIFO read operation. */ - xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_START; - gpio_set_pin_low( KSZ8851SNL_CSN_GPIO ); - if( ( xReadLength & ( sizeof( size_t ) - 1 ) ) != 0 ) - { - xReadLength = ( xReadLength | ( sizeof( size_t ) - 1 ) ) + 1; - } - - /* Pass the buffer minus 2 bytes, see ksz8851snl.c: RXQ_TWOBYTE_OFFSET. */ - ksz8851_fifo_read( pxNetworkBuffer->pucEthernetBuffer - 2, xReadLength ); - /* Remove CRC and update buffer length. */ - xLength -= 4; - pxNetworkBuffer->xDataLength = xLength; - /* Wait for SPI interrupt to set status 'SPI_PDC_RX_COMPLETE'. */ - } - } - break; - } /* ul_had_intn_interrupt || us_pending_frame */ - /* - * ========================== Handle TX ========================== - */ - - /* Fetch next packet to be sent. */ - if( ( xMicrelDevice.tx_busy[ txTail ] != pdFALSE ) && - ( xMicrelDevice.us_pending_frame == 0 ) && - ( xMicrelDevice.ul_had_intn_interrupt == 0 ) ) - { - NetworkBufferDescriptor_t *pxNetworkBuffer = xMicrelDevice.tx_buffers[ txTail ]; - size_t xLength = pxNetworkBuffer->xDataLength; - int iIndex = xLength; - - xLength = 4 * ( ( xLength + 3 ) / 4 ); - while( iIndex < ( int ) xLength ) - { - pxNetworkBuffer->pucEthernetBuffer[ iIndex ] = '\0'; - iIndex++; - } - pxNetworkBuffer->xDataLength = xLength; - - /* TX step1: check if TXQ memory size is available for transmit. */ - txmir = ksz8851_reg_read( REG_TX_MEM_INFO ); - txmir = txmir & TX_MEM_AVAILABLE_MASK; - - if( txmir < ( xLength + 8 ) ) - { - if( wait_tx_space == pdFALSE ) - { - tx_status = ksz8851_reg_read( REG_TX_STATUS ); - fhr_status = ksz8851_reg_read( REG_RX_FHR_STATUS ); - wait_tx_space = pdTRUE; - } - //return; - rx_debug = 1; - tx_space = txmir; - } - else - { - tx_space = txmir; - - /* TX step2: disable all interrupts. */ - ksz8851_reg_write( REG_INT_MASK, 0 ); - - xMicrelDevice.tx_space -= xLength; - - /* TX step3: enable TXQ write access. */ - ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_START ); - /* TX step4-8: perform FIFO write operation. */ - xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_START; - xMicrelDevice.tx_cur_buffer = pxNetworkBuffer; - /* Bring SPI SS low. */ - gpio_set_pin_low( KSZ8851SNL_CSN_GPIO ); - xMicrelDevice.ul_total_tx++; - - ksz8851_fifo_write( pxNetworkBuffer->pucEthernetBuffer, xLength, xLength ); - } - } - } - break; /* SPI_PDC_IDLE */ - - case SPI_PDC_RX_COMPLETE: - { - int rxHead = xMicrelDevice.us_rx_head; - /* RX step18-19: pad with dummy data to keep dword alignment. */ - /* Packet lengths will be rounded up to a multiple of "sizeof size_t". */ -// xLength = xMicrelDevice.rx_buffers[ rxHead ]->xDataLength & 3; -// if( xLength != 0 ) -// { -// ksz8851_fifo_dummy( 4 - xLength ); -// } - - /* RX step20: end RX transfer. */ - gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); - - /* Disable asynchronous transfer mode. */ - xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE; - - /* RX step21: end RXQ read access. */ - ksz8851_reg_clrbits(REG_RXQ_CMD, RXQ_START); - - /* RX step22-23: update frame count to be read. */ - xMicrelDevice.us_pending_frame -= 1; - - /* RX step24: enable INT_RX flag if transfer complete. */ - if( xMicrelDevice.us_pending_frame == 0 ) - { - ksz8851_reg_write(REG_INT_MASK, INT_RX); - } - - /* Mark descriptor ready to be read. */ - xMicrelDevice.rx_ready[ rxHead ] = pdTRUE; - if( ++rxHead == MICREL_RX_BUFFERS ) - { - rxHead = 0; - } - xMicrelDevice.us_rx_head = rxHead; - if( rx_debug != 0 ) - { - uint32_t txmir; - rx_debug = 0; - txmir = ksz8851_reg_read( REG_TX_MEM_INFO ); - txmir = txmir & TX_MEM_AVAILABLE_MASK; - } - /* Tell prvEMACHandlerTask that RX packets are available. */ - ulISREvents |= EMAC_IF_RX_EVENT; - } /* case SPI_PDC_RX_COMPLETE */ - break; - - case SPI_PDC_TX_COMPLETE: - { - int txTail = xMicrelDevice.us_tx_tail; - NetworkBufferDescriptor_t *pxNetworkBuffer = xMicrelDevice.tx_buffers[ txTail ]; - - size_t xLength; - /* TX step9-10: pad with dummy data to keep dword alignment. */ - /* Not necessary: length is already a multiple of 4. */ - xLength = pxNetworkBuffer->xDataLength & 3; - if( xLength != 0 ) - { -// ksz8851_fifo_dummy( 4 - xLength ); - } - -// /* TX step11: end TX transfer. */ - gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); - - /* Disable asynchronous transfer mode. */ - xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE; - - /* TX step12: disable TXQ write access. */ - ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); - - xMicrelDevice.tx_space = ksz8851_reg_read( REG_TX_MEM_INFO ) & TX_MEM_AVAILABLE_MASK; - - /* TX step12.1: enqueue frame in TXQ. */ - ksz8851_reg_setbits( REG_TXQ_CMD, TXQ_ENQUEUE ); - - /* RX step13: enable INT_RX flag. */ -// ksz8851_reg_write( REG_INT_MASK, INT_RX ); - /* Buffer sent, free the corresponding buffer and mark descriptor as owned by software. */ - vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); - - xMicrelDevice.tx_buffers[ txTail ] = NULL; - xMicrelDevice.tx_busy[ txTail ] = pdFALSE; - if( ++txTail == MICREL_TX_BUFFERS ) - { - txTail = 0; - } - - xMicrelDevice.us_tx_tail = txTail; - /* Experiment. */ - //xMicrelDevice.ul_had_intn_interrupt = 1; - if( xTransmitHandle != NULL ) - { - xTaskNotifyGive( xTransmitHandle ); - } -#warning moved downward - /* RX step13: enable INT_RX flag. */ - ksz8851_reg_write( REG_INT_MASK, INT_RX ); - /* Prevent the EMAC task from sleeping a single time. */ - ulISREvents |= EMAC_IF_TX_EVENT; - } /* case SPI_PDC_TX_COMPLETE */ - break; - } /* switch( xMicrelDevice.ul_spi_pdc_status ) */ -} - -/** - * \brief Set up the RX descriptor ring buffers. - * - * This function sets up the descriptor list used for RX packets. - * - */ -static void ksz8851snl_rx_init() -{ - uint32_t ul_index = 0; - - /* Init pointer index. */ - xMicrelDevice.us_rx_head = 0; - xMicrelDevice.us_rx_tail = 0; - - /* Set up the RX descriptors. */ - for (ul_index = 0; ul_index < MICREL_RX_BUFFERS; ul_index++) { - xMicrelDevice.rx_buffers[ul_index] = NULL; - xMicrelDevice.rx_ready[ul_index] = pdFALSE; - } - - /* Build RX buffer and descriptors. */ - ksz8851snl_rx_populate_queue(); -} - -/** - * \brief Set up the TX descriptor ring buffers. - * - * This function sets up the descriptor list used for TX packets. - * - */ -static void ksz8851snl_tx_init() -{ - uint32_t ul_index = 0; - - /* Init TX index pointer. */ - xMicrelDevice.us_tx_head = 0; - xMicrelDevice.us_tx_tail = 0; - - /* Set up the TX descriptors */ - for( ul_index = 0; ul_index < MICREL_TX_BUFFERS; ul_index++ ) - { - xMicrelDevice.tx_busy[ul_index] = pdFALSE; - } - xMicrelDevice.tx_space = 6144; -} - -/** - * \brief Initialize ksz8851snl ethernet controller. - * - * \note Called from ethernetif_init(). - * - * \param netif the lwIP network interface structure for this ethernetif. - */ -static void ksz8851snl_low_level_init( void ) -{ - ksz8851snl_rx_init(); - ksz8851snl_tx_init(); - - /* Enable NVIC interrupts. */ - NVIC_SetPriority(SPI_IRQn, INT_PRIORITY_SPI); - NVIC_EnableIRQ(SPI_IRQn); - - /* Initialize SPI link. */ - if( ksz8851snl_init() < 0 ) - { - FreeRTOS_printf( ( "ksz8851snl_low_level_init: failed to initialize the Micrel driver!\n" ) ); - configASSERT(0 == 1); - } - memset( xMicrelDevice.pusHashTable, 255, sizeof( xMicrelDevice.pusHashTable ) ); - ksz8851_reg_write( REG_MAC_HASH_0, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 0 ] ) ); - ksz8851_reg_write( REG_MAC_HASH_2, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 1 ] ) ); - ksz8851_reg_write( REG_MAC_HASH_4, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 2 ] ) ); - ksz8851_reg_write( REG_MAC_HASH_6, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 3 ] ) ); - - /* Initialize interrupt line INTN. */ - configure_intn( INTN_Handler ); -} - -/** - * \brief Use pre-allocated pbuf as DMA source and return the incoming packet. - * - * \param netif the lwIP network interface structure for this ethernetif. - * - * \return a pbuf filled with the received packet (including MAC header). - * 0 on memory error. - */ -static NetworkBufferDescriptor_t *ksz8851snl_low_level_input( void ) -{ -NetworkBufferDescriptor_t *pxNetworkBuffer = NULL; -int rxTail = xMicrelDevice.us_rx_tail; - - /* Check that descriptor is owned by software (ie packet received). */ - if( xMicrelDevice.rx_ready[ rxTail ] != pdFALSE ) - { - - /* Fetch pre-allocated buffer */ - pxNetworkBuffer = xMicrelDevice.rx_buffers[ rxTail ]; - - /* Remove this pbuf from its descriptor. */ - xMicrelDevice.rx_buffers[ rxTail ] = NULL; - - /* Clears rx_ready and sets rx_buffers. */ - ksz8851snl_rx_populate_queue(); - - if( ++rxTail == MICREL_RX_BUFFERS ) - { - rxTail = 0; - } - xMicrelDevice.us_rx_tail = rxTail; - } - - return pxNetworkBuffer; -} -/*-----------------------------------------------------------*/ - -static uint32_t prvEMACRxPoll( void ) -{ -NetworkBufferDescriptor_t *pxNetworkBuffer; -IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; -uint32_t ulReturnValue = 0; - - for( ;; ) - { - /* Only for logging. */ - int rxTail = xMicrelDevice.us_rx_tail; - EthernetHeader_t *pxEthernetHeader; - - pxNetworkBuffer = ksz8851snl_low_level_input(); - - if( pxNetworkBuffer == NULL ) - { - break; - } - pxEthernetHeader = ( EthernetHeader_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); - - if( ( pxEthernetHeader->usFrameType != ipIPv4_FRAME_TYPE ) && - ( pxEthernetHeader->usFrameType != ipARP_FRAME_TYPE ) ) - { - FreeRTOS_printf( ( "Frame type %02X received\n", pxEthernetHeader->usFrameType ) ); - } - ulReturnValue++; - - xRxEvent.pvData = ( void * )pxNetworkBuffer; - /* Send the descriptor to the IP task for processing. */ - if( xSendEventStructToIPTask( &xRxEvent, 100UL ) != pdTRUE ) - { - vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); - iptraceETHERNET_RX_EVENT_LOST(); - FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) ); - } - } - - return ulReturnValue; -} -/*-----------------------------------------------------------*/ - -static void prvEMACHandlerTask( void *pvParameters ) -{ -TimeOut_t xPhyTime; -TickType_t xPhyRemTime; -TickType_t xLoggingTime; -UBaseType_t uxLastMinBufferCount = 0; -UBaseType_t uxCurrentCount; -BaseType_t xResult = 0; -uint32_t xStatus; -const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( EMAC_MAX_BLOCK_TIME_MS ); -#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) - UBaseType_t uxLastMinQueueSpace = 0; -#endif - - /* Remove compiler warnings about unused parameters. */ - ( void ) pvParameters; - - configASSERT( xEMACTaskHandle ); - - vTaskSetTimeOutState( &xPhyTime ); - xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); - xLoggingTime = xTaskGetTickCount(); - - for( ;; ) - { - uxCurrentCount = uxGetMinimumFreeNetworkBuffers(); - if( uxLastMinBufferCount != uxCurrentCount ) - { - /* The logging produced below may be helpful - while tuning +TCP: see how many buffers are in use. */ - uxLastMinBufferCount = uxCurrentCount; - FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", - uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) ); - } - - #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) - { - uxCurrentCount = uxGetMinimumIPQueueSpace(); - if( uxLastMinQueueSpace != uxCurrentCount ) - { - /* The logging produced below may be helpful - while tuning +TCP: see how many buffers are in use. */ - uxLastMinQueueSpace = uxCurrentCount; - FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) ); - } - } - #endif /* ipconfigCHECK_IP_QUEUE_SPACE */ - - /* Run the state-machine of the ksz8851 driver. */ - ksz8851snl_update(); - - if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 ) - { - /* No events to process now, wait for the next. */ - ulTaskNotifyTake( pdTRUE, ulMaxBlockTime ); - } - - if( ( xTaskGetTickCount() - xLoggingTime ) > 10000 ) - { - xLoggingTime += 10000; - FreeRTOS_printf( ( "Now Tx/Rx %7d /%7d\n", - xMicrelDevice.ul_total_tx, xMicrelDevice.ul_total_rx ) ); - } - - if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 ) - { - ulISREvents &= ~EMAC_IF_RX_EVENT; - - /* Wait for the EMAC interrupt to indicate that another packet has been - received. */ - xResult = prvEMACRxPoll(); - } - - if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 ) - { - /* Future extension: code to release TX buffers if zero-copy is used. */ - ulISREvents &= ~EMAC_IF_TX_EVENT; - } - - if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 ) - { - /* Future extension: logging about errors that occurred. */ - ulISREvents &= ~EMAC_IF_ERR_EVENT; - } - - if( xResult > 0 ) - { - /* As long as packets are being received, assume that - the Link Status is high. */ - ulPHYLinkStatus |= BMSR_LINK_STATUS; - /* A packet was received. No need to check for the PHY status now, - but set a timer to check it later on. */ - vTaskSetTimeOutState( &xPhyTime ); - xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); - xResult = 0; - } - else if( ( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) && - ( xMicrelDevice.ul_spi_pdc_status == SPI_PDC_IDLE ) ) - { - /* Check the link status again. */ - xStatus = ulReadMDIO( PHY_REG_01_BMSR ); - - if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) ) - { - ulPHYLinkStatus = xStatus; - FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) ); - } - - vTaskSetTimeOutState( &xPhyTime ); - if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) - { - xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); - } - else - { - xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); - } - } - } -} -/*-----------------------------------------------------------*/ +/* +FreeRTOS+TCP V2.0.11 +Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + http://aws.amazon.com/freertos + http://www.FreeRTOS.org +*/ + +/* Standard includes. */ +#include +#include +#include +#include + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_IP_Private.h" +#include "NetworkBufferManagement.h" +#include "NetworkInterface.h" + +#include "sam4e_xplained_pro.h" +#include "hr_gettime.h" +#include "conf_eth.h" +#include "ksz8851snl.h" +#include "ksz8851snl_reg.h" + +/* Some files from the Atmel Software Framework */ +#include +#include +#include + +/* + Sending a packet: + + 1) Called by UP-task, add buffer to the TX-list: + xNetworkInterfaceOutput() + tx_buffers[ us_tx_head ] = pxNetworkBuffer; + tx_busy[ us_tx_head ] = pdTRUE; + us_tx_head++; + + 2) Called by EMAC-Task: start SPI transfer + ksz8851snl_update() + if( ul_spi_pdc_status == SPI_PDC_IDLE ) + { + if( ( tx_busy[ us_tx_tail ] != pdFALSE ) && + ( us_pending_frame == 0 ) && + ( ul_had_intn_interrupt == 0 ) ) + { + // disable all interrupts. + ksz8851_reg_write( REG_INT_MASK, 0 ); + Bring KSZ8851SNL_CSN_GPIO low + ksz8851_fifo_write( pxNetworkBuffer->pucEthernetBuffer, xLength, xLength ); + ul_spi_pdc_status = SPI_PDC_TX_START; + tx_cur_buffer = pxNetworkBuffer; + } + } + 3) Wait for SPI RXBUFF interrupt + SPI_Handler() + if( ul_spi_pdc_status == SPI_PDC_TX_START ) + { + if( SPI_Status & SPI_SR_RXBUFF ) + { + ul_spi_pdc_status = SPI_PDC_TX_COMPLETE; + } + } + + 4) Called by EMAC-Task: finish SPI transfer + ksz8851snl_update() + if( ul_spi_pdc_status == SPI_PDC_TX_COMPLETE ) + { + ul_spi_pdc_status = SPI_PDC_IDLE; + Bring KSZ8851SNL_CSN_GPIO high + // TX step12: disable TXQ write access. + ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); + // TX step12.1: enqueue frame in TXQ. + ksz8851_reg_setbits( REG_TXQ_CMD, TXQ_ENQUEUE ); + + // RX step13: enable INT_RX flag. + ksz8851_reg_write( REG_INT_MASK, INT_RX ); + + // Buffer sent, free the corresponding buffer and mark descriptor as owned by software. + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + + tx_buffers[ us_tx_tail ] = NULL; + tx_busy[ us_tx_tail ] = pdFALSE; + us_tx_tail++ + } + + Receiving a packet: + + 1) Wait for a INTN interrupt + INTN_Handler() + ul_had_intn_interrupt = 1 + vTaskNotifyGiveFromISR(); // Wake up the EMAC task + + 2) Called by EMAC-Task: check for new fragments and start SPI transfer + ksz8851snl_update() + if( ul_spi_pdc_status == SPI_PDC_IDLE ) + { + if( ( ul_had_intn_interrupt != 0 ) || ( us_pending_frame > 0 ) ) + { + if( us_pending_frame == 0 ) + { + us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8; + if( us_pending_frame == 0 ) + { + break; + } + } + // RX step2: disable all interrupts. + ksz8851_reg_write( REG_INT_MASK, 0 ); + Check if there is a valid packet: REG_RX_FHR_STATUS + Read the length of the next fragment: REG_RX_FHR_BYTE_CNT + ul_spi_pdc_status = SPI_PDC_RX_START; + gpio_set_pin_low(KSZ8851SNL_CSN_GPIO); + // Start SPI data transfer + ksz8851_fifo_read( pxNetworkBuffer->pucEthernetBuffer, xReadLength ); + } + } + + 3) Wait for SPI RXBUFF interrupt + SPI_Handler() + if( ul_spi_pdc_status == SPI_PDC_RX_START: + { + if( ( ulCurrentSPIStatus & SPI_SR_RXBUFF ) != 0 ) + { + // Transfer complete, disable SPI RXBUFF interrupt. + spi_disable_interrupt( KSZ8851SNL_SPI, SPI_IDR_RXBUFF ); + + ul_spi_pdc_status = SPI_PDC_RX_COMPLETE; + } + } + } + + 4) Finish SPI transfer + ksz8851snl_update() + if( ul_spi_pdc_status == SPI_PDC_RX_COMPLETE ) + { + ul_spi_pdc_status = SPI_PDC_IDLE; + Bring KSZ8851SNL_CSN_GPIO high + // RX step21: end RXQ read access. + ksz8851_reg_clrbits(REG_RXQ_CMD, RXQ_START); + // RX step22-23: update frame count to be read. + us_pending_frame-- + // RX step24: enable INT_RX flag if transfer complete. + if( us_pending_frame == 0 ) + { + // Allow more RX interrupts. + ksz8851_reg_write( REG_INT_MASK, INT_RX ); + } + + // Mark descriptor ready to be read. + rx_ready[ rxHead ] = pdTRUE; + rxHead++ + } +*/ + +#define PHY_REG_00_BMCR 0x00 // Basic mode control register +#define PHY_REG_01_BMSR 0x01 // Basic mode status register +#define PHY_REG_02_PHYSID1 0x02 // PHYS ID 1 +#define PHY_REG_03_PHYSID2 0x03 // PHYS ID 2 +#define PHY_REG_04_ADVERTISE 0x04 // Advertisement control reg +#define PHY_REG_05_LPA 0x05 // Link partner ability reg +#define PHY_REG_06_ANER 0x06 // 6 RW Auto-Negotiation Expansion Register +#define PHY_REG_07_ANNPTR 0x07 // 7 RW Auto-Negotiation Next Page TX +#define PHY_REG_08_RESERVED0 0x08 // 0x08..0x0Fh 8-15 RW RESERVED + +#define BMSR_LINK_STATUS 0x0004 //!< Link status + +#ifndef PHY_LS_HIGH_CHECK_TIME_MS + /* Check if the LinkSStatus in the PHY is still high after 15 seconds of not + receiving packets. */ + #define PHY_LS_HIGH_CHECK_TIME_MS 15000 +#endif + +#ifndef PHY_LS_LOW_CHECK_TIME_MS + /* Check if the LinkSStatus in the PHY is still low every second. */ + #define PHY_LS_LOW_CHECK_TIME_MS 1000 +#endif + +/* Interrupt events to process. Currently only the Rx event is processed +although code for other events is included to allow for possible future +expansion. */ +#define EMAC_IF_RX_EVENT 1UL +#define EMAC_IF_TX_EVENT 2UL +#define EMAC_IF_ERR_EVENT 4UL +#define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT ) + +#define ETHERNET_CONF_PHY_ADDR BOARD_GMAC_PHY_ADDR + +#ifdef ipconfigHAS_TX_CRC_OFFLOADING + #undef ipconfigHAS_TX_CRC_OFFLOADING +#endif +/* Override this define because the KSZ8851 is programmed to set all outgoing CRC's */ +#define ipconfigHAS_TX_CRC_OFFLOADING 1 + +#ifndef EMAC_MAX_BLOCK_TIME_MS + #define EMAC_MAX_BLOCK_TIME_MS 100ul +#endif + +/* Default the size of the stack used by the EMAC deferred handler task to 4x +the size of the stack used by the idle task - but allow this to be overridden in +FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ +#ifndef configEMAC_TASK_STACK_SIZE + #define configEMAC_TASK_STACK_SIZE ( 6 * configMINIMAL_STACK_SIZE ) +#endif + +#define SPI_PDC_IDLE 0 +#define SPI_PDC_RX_START 1 +#define SPI_PDC_TX_ERROR 2 +#define SPI_PDC_RX_COMPLETE 3 +#define SPI_PDC_TX_START 4 +#define SPI_PDC_RX_ERROR 5 +#define SPI_PDC_TX_COMPLETE 6 + +/** + * ksz8851snl driver structure. + */ +typedef struct { + /** Set to 1 when owner is software (ready to read), 0 for Micrel. */ + uint32_t rx_ready[MICREL_RX_BUFFERS]; + /** Set to 1 when owner is Micrel, 0 for software. */ + uint32_t tx_busy[MICREL_TX_BUFFERS]; + /** RX NetworkBufferDescriptor_t pointer list */ + NetworkBufferDescriptor_t *rx_buffers[MICREL_RX_BUFFERS]; + /** TX NetworkBufferDescriptor_t pointer list */ + NetworkBufferDescriptor_t *tx_buffers[MICREL_TX_BUFFERS]; + NetworkBufferDescriptor_t *tx_cur_buffer; + + /** Circular buffer head pointer for packet received. */ + uint32_t us_rx_head; + /** Circular buffer tail pointer for packet to be read. */ + uint32_t us_rx_tail; + /** Circular buffer head pointer by upper layer (buffer to be sent). */ + uint32_t us_tx_head; + /** Circular buffer tail pointer incremented by handlers (buffer sent). */ + uint32_t us_tx_tail; + + uint32_t ul_total_tx; + uint32_t ul_total_rx; + uint32_t tx_space; + + /** Still experimental: hash table to allow certain multicast addresses. */ + uint16_t pusHashTable[ 4 ]; + + /* ul_spi_pdc_status has "SPI_PDC_xxx" values. */ + volatile uint32_t ul_spi_pdc_status; + + /* ul_had_intn_interrupt becomes true within the INTN interrupt. */ + volatile uint32_t ul_had_intn_interrupt; + + uint16_t us_pending_frame; +} xKSZ8851_Device_t; + +/* SPI PDC register base. +Declared in ASF\sam\components\ksz8851snl\ksz8851snl.c */ +extern Pdc *g_p_spi_pdc; + +/* Temporary buffer for PDC reception. +declared in ASF\sam\components\ksz8851snl\ksz8851snl.c */ +extern uint8_t tmpbuf[1536]; + +COMPILER_ALIGNED(8) +static xKSZ8851_Device_t xMicrelDevice; + +static TaskHandle_t xTransmitHandle; + +/*-----------------------------------------------------------*/ + +/* + * Wait a fixed time for the link status to indicate the network is up. + */ +static BaseType_t xGMACWaitLS( TickType_t xMaxTime ); + +/* + * A deferred interrupt handler task that processes GMAC interrupts. + */ +static void prvEMACHandlerTask( void *pvParameters ); + +/* + * Try to obtain an Rx packet from the hardware. + */ +static uint32_t prvEMACRxPoll( void ); + +static inline unsigned long ulReadMDIO( unsigned uAddress ); + +static void ksz8851snl_low_level_init( void ); + +static NetworkBufferDescriptor_t *ksz8851snl_low_level_input( void ); + +/*-----------------------------------------------------------*/ + +/* Bit map of outstanding ETH interrupt events for processing. Currently only +the Rx interrupt is handled, although code is included for other events to +enable future expansion. */ +static volatile uint32_t ulISREvents; + +/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ +static uint32_t ulPHYLinkStatus = 0; +static volatile BaseType_t xGMACSwitchRequired; + +static void ksz8851snl_update( void ); + +static void ksz8851snl_rx_init( void ); + +static void ksz8851snl_tx_init( void ); + +/* Holds the handle of the task used as a deferred interrupt processor. The +handle is used so direct notifications can be sent to the task for all EMAC/DMA +related interrupts. */ +TaskHandle_t xEMACTaskHandle = NULL; + + +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceInitialise( void ) +{ +const TickType_t x5_Seconds = 5000UL; + + if( xEMACTaskHandle == NULL ) + { + ksz8851snl_low_level_init(); + + /* Wait at most 5 seconds for a Link Status in the PHY. */ + xGMACWaitLS( pdMS_TO_TICKS( x5_Seconds ) ); + + /* The handler task is created at the highest possible priority to + ensure the interrupt handler can return directly to it. */ + xTaskCreate( prvEMACHandlerTask, "KSZ8851", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle ); + configASSERT( xEMACTaskHandle ); + } + + /* When returning non-zero, the stack will become active and + start DHCP (in configured) */ + ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + return ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0; +} +/*-----------------------------------------------------------*/ + +BaseType_t xGetPhyLinkStatus( void ) +{ +BaseType_t xResult; + + /* This function returns true if the Link Status in the PHY is high. */ + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) + { + xResult = pdTRUE; + } + else + { + xResult = pdFALSE; + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer, BaseType_t bReleaseAfterSend ) +{ +BaseType_t xResult = pdFALSE; +int txHead = xMicrelDevice.us_tx_head; + + /* Make sure the next descriptor is free. */ + if( xMicrelDevice.tx_busy[ txHead ] != pdFALSE ) + { + /* All TX buffers busy. */ + } + else if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) == 0 ) + { + /* Output: LS low. */ + } + else + { + /* Pass the packet. */ + xMicrelDevice.tx_buffers[ txHead ] = pxNetworkBuffer; + /* The descriptor is now owned by Micrel. */ + xMicrelDevice.tx_busy[ txHead ] = pdTRUE; + + /* Move the head pointer. */ + if( ++txHead == MICREL_TX_BUFFERS ) + { + txHead = 0; + } + xMicrelDevice.us_tx_head = txHead; + if( xEMACTaskHandle != NULL ) + { + xTaskNotifyGive( xEMACTaskHandle ); + } + + #if( ipconfigZERO_COPY_TX_DRIVER != 1 ) + #warning Please ipconfigZERO_COPY_TX_DRIVER as 1 + #endif + configASSERT( bReleaseAfterSend != pdFALSE ); + xResult = pdTRUE; + } + if( ( xResult == pdFALSE ) && ( bReleaseAfterSend != pdFALSE ) ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + return xResult; +} +/*-----------------------------------------------------------*/ + +/* This Micrel has numbered it's PHY registers in a different way. +Translate the register index. */ +static int ks8851_phy_reg( int reg ) +{ + switch (reg) { + case PHY_REG_00_BMCR: + return REG_PHY_CNTL; // P1MBCR; + case PHY_REG_01_BMSR: + return REG_PHY_STATUS; + case PHY_REG_02_PHYSID1: + return REG_PHY_ID_LOW; + case PHY_REG_03_PHYSID2: + return REG_PHY_ID_HIGH; + case PHY_REG_04_ADVERTISE: + return REG_PHY_AUTO_NEGOTIATION; + case PHY_REG_05_LPA: + return REG_PHY_REMOTE_CAPABILITY; + } + + return 0x0; +} +/*-----------------------------------------------------------*/ + +static inline unsigned long ulReadMDIO( unsigned uAddress ) +{ +uint16_t usPHYStatus; +int ks8851_reg = ks8851_phy_reg( uAddress ); + + if( ks8851_reg != 0 ) + { + usPHYStatus = ksz8851_reg_read( ks8851_reg ); + } + else + { + /* Other addresses not yet implemented. */ + usPHYStatus = 0; + } + return usPHYStatus; +} +/*-----------------------------------------------------------*/ + +static BaseType_t xGMACWaitLS( TickType_t xMaxTime ) +{ +TickType_t xStartTime = xTaskGetTickCount(); +TickType_t xEndTime; +BaseType_t xReturn; +const TickType_t xShortTime = pdMS_TO_TICKS( 100UL ); +const uint32_t ulHz_Per_MHz = 1000000UL; + + for( ;; ) + { + xEndTime = xTaskGetTickCount(); + + if( ( xEndTime - xStartTime ) > xMaxTime ) + { + /* Wated more than xMaxTime, return. */ + xReturn = pdFALSE; + break; + } + + /* Check the link status again. */ + ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) + { + /* Link is up - return. */ + xReturn = pdTRUE; + break; + } + + /* Link is down - wait in the Blocked state for a short while (to allow + other tasks to execute) before checking again. */ + vTaskDelay( xShortTime ); + } + + FreeRTOS_printf( ( "xGMACWaitLS: %ld freq %lu Mz\n", + xReturn, + sysclk_get_cpu_hz() / ulHz_Per_MHz ) ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static void vPioSetPinHigh(uint32_t ul_pin) +{ + Pio *p_pio = (Pio *)((uint32_t)PIOA + (PIO_DELTA * (ul_pin >> 5))); + // Value to be driven on the I/O line: 1. + p_pio->PIO_SODR = 1 << (ul_pin & 0x1F); +} + +/** + * \brief Handler for SPI interrupt. + */ +void SPI_Handler(void) +{ +BaseType_t xDoWakeup = pdFALSE; +BaseType_t xKSZTaskWoken = pdFALSE; +uint32_t ulCurrentSPIStatus; +uint32_t ulEnabledSPIStatus; + + ulCurrentSPIStatus = spi_read_status( KSZ8851SNL_SPI ); + ulEnabledSPIStatus = spi_read_interrupt_mask( KSZ8851SNL_SPI ); + ulCurrentSPIStatus &= ulEnabledSPIStatus; + spi_disable_interrupt( KSZ8851SNL_SPI, ulCurrentSPIStatus ); + + + switch( xMicrelDevice.ul_spi_pdc_status ) + { + case SPI_PDC_RX_START: + { + if( ( ulCurrentSPIStatus & SPI_SR_OVRES ) != 0 ) + { + pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS); + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_ERROR; + xDoWakeup = pdTRUE; + } + else + { + if( ( ulCurrentSPIStatus & SPI_SR_RXBUFF ) != 0 ) + { + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_COMPLETE; + xDoWakeup = pdTRUE; + } + } + } + break; + + case SPI_PDC_TX_START: + { + /* Middle of TX. */ + if( ( ulCurrentSPIStatus & SPI_SR_OVRES ) != 0 ) + { + pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS); + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_ERROR; + xDoWakeup = pdTRUE; + } + else + { + if( ( ulCurrentSPIStatus & SPI_SR_ENDRX ) != 0 ) + { + /* Enable RX complete interrupt. */ + spi_enable_interrupt( KSZ8851SNL_SPI, SPI_IER_RXBUFF ); + } + /* End of TX. */ + if( ( ulCurrentSPIStatus & SPI_END_OF_TX ) != 0 ) + { + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_COMPLETE; + xDoWakeup = pdTRUE; + } + } + } + break; + } /* switch( xMicrelDevice.ul_spi_pdc_status ) */ + + if( xDoWakeup != pdFALSE ) + { + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xKSZTaskWoken ); + } + } + else + { + } + portEND_SWITCHING_ISR( xKSZTaskWoken ); +} +/*-----------------------------------------------------------*/ + +static void INTN_Handler(uint32_t id, uint32_t mask) +{ +BaseType_t xKSZTaskWoken = pdFALSE; + + if( ( id == INTN_ID ) && + ( mask == INTN_PIN_MSK ) ) + { + /* Clear the PIO interrupt flags. */ + pio_get_interrupt_status( INTN_PIO ); + + /* Set the INTN flag. */ + xMicrelDevice.ul_had_intn_interrupt++; + if( xEMACTaskHandle != NULL ) + { + vTaskNotifyGiveFromISR( xEMACTaskHandle, &( xKSZTaskWoken ) ); + } + } + portEND_SWITCHING_ISR( xKSZTaskWoken ); +} +/*-----------------------------------------------------------*/ + +/** + * \brief Populate the RX descriptor ring buffers with pbufs. + * + * \param p_ksz8851snl_dev Pointer to driver data structure. + */ +static void ksz8851snl_rx_populate_queue( void ) +{ + uint32_t ul_index = 0; + NetworkBufferDescriptor_t *pxNetworkBuffer; + + /* Set up the RX descriptors */ + for (ul_index = 0; ul_index < MICREL_RX_BUFFERS; ul_index++) { + if( xMicrelDevice.rx_buffers[ ul_index ] == NULL ) + { + /* Allocate a new NetworkBufferDescriptor_t with the maximum size. */ + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ipconfigNETWORK_MTU + 36, 100 ); + if( pxNetworkBuffer == NULL ) + { + FreeRTOS_printf( ( "ksz8851snl_rx_populate_queue: NetworkBufferDescriptor_t allocation failure\n" ) ); + configASSERT( 1 == 2 ); + } + + /* Make sure lwIP is well configured so one NetworkBufferDescriptor_t can contain the maximum packet size. */ + //LWIP_ASSERT("ksz8851snl_rx_populate_queue: NetworkBufferDescriptor_t size too small!", pbuf_clen(pxNetworkBuffer) <= 1); + + /* Save NetworkBufferDescriptor_t pointer to be sent to lwIP upper layer. */ + xMicrelDevice.rx_buffers[ ul_index ] = pxNetworkBuffer; + /* Pass it to Micrel for reception. */ + xMicrelDevice.rx_ready[ ul_index ] = pdFALSE; + } + } +} + +unsigned tx_space, wait_tx_space, tx_status, fhr_status; +unsigned rx_debug = 0; +/** + * \brief Update Micrel state machine and perform required actions. + * + * \param netif the lwIP network interface structure for this ethernetif. + */ +static void ksz8851snl_update() +{ + uint16_t txmir = 0; + +/* Check for free PDC. */ + switch( xMicrelDevice.ul_spi_pdc_status ) + { + case SPI_PDC_TX_ERROR: + { + uint32_t ulValue; + // /* TX step11: end TX transfer. */ + gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); + + vTaskDelay( 2 ); gpio_set_pin_low( KSZ8851SNL_CSN_GPIO ); + vTaskDelay( 1 ); gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); + vTaskDelay( 1 ); + + /* Disable asynchronous transfer mode. */ + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE; + + /* TX step12: disable TXQ write access. */ + ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); + + ulValue = ksz8851snl_reset_tx(); + + xMicrelDevice.tx_space = ksz8851_reg_read( REG_TX_MEM_INFO ) & TX_MEM_AVAILABLE_MASK; + + FreeRTOS_printf( ("SPI_PDC_TX_ERROR %02X\n", ulValue ) ); + } + break; + + case SPI_PDC_RX_ERROR: + { + uint32_t ulValue; + /* TX step11: end TX transfer. */ + gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); + + vTaskDelay( 2 ); gpio_set_pin_low( KSZ8851SNL_CSN_GPIO ); + vTaskDelay( 1 ); gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); + vTaskDelay( 1 ); + + /* Disable asynchronous transfer mode. */ + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE; + + /* TX step12: disable TXQ write access. */ + ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); + + //ulValue = ksz8851snl_reset_rx(); + ulValue = ksz8851snl_reinit(); + + xGMACWaitLS( pdMS_TO_TICKS( 5000UL ) ); + + FreeRTOS_printf( ("SPI_PDC_RX_ERROR %02X\n", ulValue ) ); + } + break; + } + switch( xMicrelDevice.ul_spi_pdc_status ) + { + case SPI_PDC_IDLE: + { + int txTail = xMicrelDevice.us_tx_tail; + + /* + * ========================== Handle RX ========================== + */ + if( ( xMicrelDevice.ul_had_intn_interrupt != 0 ) || ( xMicrelDevice.us_pending_frame > 0 ) ) + { + int rxHead = xMicrelDevice.us_rx_head; + NetworkBufferDescriptor_t *pxNetworkBuffer; +#warning try + xMicrelDevice.ul_had_intn_interrupt = 0; + + if( xMicrelDevice.us_pending_frame == 0 ) + { + uint16_t int_status; + /* RX step1: read interrupt status for INT_RX flag. */ + int_status = ksz8851_reg_read( REG_INT_STATUS ); + + + /* RX step2: disable all interrupts. */ + ksz8851_reg_write( REG_INT_MASK, 0 ); + + /* RX step3: clear INT_RX flag. */ + ksz8851_reg_setbits( REG_INT_STATUS, INT_RX ); + + /* RX step4-5: check for received frames. */ + xMicrelDevice.us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8; + if( xMicrelDevice.us_pending_frame == 0 ) + { + /* RX step24: enable INT_RX flag. */ + ksz8851_reg_write(REG_INT_MASK, INT_RX); + return; + } + } +#warning try + xMicrelDevice.ul_had_intn_interrupt = 0; + + /* Now xMicrelDevice.us_pending_frame != 0 */ + + /* Don't break Micrel state machine, wait for a free descriptor first! */ + if( xMicrelDevice.rx_ready[ rxHead ] != pdFALSE ) + { + FreeRTOS_printf( ( "ksz8851snl_update: out of free descriptor! [tail=%u head=%u]\n", + xMicrelDevice.us_rx_tail, rxHead ) ); + return; + } + pxNetworkBuffer = xMicrelDevice.rx_buffers[ rxHead ]; + + if( pxNetworkBuffer == NULL ) + { + ksz8851snl_rx_populate_queue(); + FreeRTOS_printf( ( "ksz8851snl_update: no buffer set [head=%u]\n", rxHead ) ); + return; + } + + /* RX step6: get RX packet status. */ + fhr_status = ksz8851_reg_read( REG_RX_FHR_STATUS ); + if( ( ( fhr_status & RX_VALID ) == 0 ) || ( ( fhr_status & RX_ERRORS ) != 0 ) ) + { + ksz8851_reg_setbits(REG_RXQ_CMD, RXQ_CMD_FREE_PACKET); + FreeRTOS_printf( ( "ksz8851snl_update: RX packet error!\n" ) ); + + /* RX step4-5: check for received frames. */ + xMicrelDevice.us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8; + if( xMicrelDevice.us_pending_frame == 0 ) + { + /* RX step24: enable INT_RX flag. */ + ksz8851_reg_write(REG_INT_MASK, INT_RX); + } + ulISREvents |= EMAC_IF_ERR_EVENT; + } + else + { + size_t xLength; + /* RX step7: read frame length. */ + xLength = ksz8851_reg_read(REG_RX_FHR_BYTE_CNT) & RX_BYTE_CNT_MASK; + + /* RX step8: Drop packet if len is invalid or no descriptor available. */ + if( xLength == 0 ) + { + ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_CMD_FREE_PACKET ); + FreeRTOS_printf( ( "ksz8851snl_update: RX bad len!\n" ) ); + ulISREvents |= EMAC_IF_ERR_EVENT; + } + else + { + size_t xReadLength = xLength; + + xMicrelDevice.ul_total_rx++; + /* RX step9: reset RX frame pointer. */ + ksz8851_reg_clrbits(REG_RX_ADDR_PTR, ADDR_PTR_MASK); + + /* RX step10: start RXQ read access. */ + ksz8851_reg_setbits(REG_RXQ_CMD, RXQ_START); + /* RX step11-17: start asynchronous FIFO read operation. */ + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_START; + gpio_set_pin_low( KSZ8851SNL_CSN_GPIO ); + if( ( xReadLength & ( sizeof( size_t ) - 1 ) ) != 0 ) + { + xReadLength = ( xReadLength | ( sizeof( size_t ) - 1 ) ) + 1; + } + + /* Pass the buffer minus 2 bytes, see ksz8851snl.c: RXQ_TWOBYTE_OFFSET. */ + ksz8851_fifo_read( pxNetworkBuffer->pucEthernetBuffer - 2, xReadLength ); + /* Remove CRC and update buffer length. */ + xLength -= 4; + pxNetworkBuffer->xDataLength = xLength; + /* Wait for SPI interrupt to set status 'SPI_PDC_RX_COMPLETE'. */ + } + } + break; + } /* ul_had_intn_interrupt || us_pending_frame */ + /* + * ========================== Handle TX ========================== + */ + + /* Fetch next packet to be sent. */ + if( ( xMicrelDevice.tx_busy[ txTail ] != pdFALSE ) && + ( xMicrelDevice.us_pending_frame == 0 ) && + ( xMicrelDevice.ul_had_intn_interrupt == 0 ) ) + { + NetworkBufferDescriptor_t *pxNetworkBuffer = xMicrelDevice.tx_buffers[ txTail ]; + size_t xLength = pxNetworkBuffer->xDataLength; + int iIndex = xLength; + + xLength = 4 * ( ( xLength + 3 ) / 4 ); + while( iIndex < ( int ) xLength ) + { + pxNetworkBuffer->pucEthernetBuffer[ iIndex ] = '\0'; + iIndex++; + } + pxNetworkBuffer->xDataLength = xLength; + + /* TX step1: check if TXQ memory size is available for transmit. */ + txmir = ksz8851_reg_read( REG_TX_MEM_INFO ); + txmir = txmir & TX_MEM_AVAILABLE_MASK; + + if( txmir < ( xLength + 8 ) ) + { + if( wait_tx_space == pdFALSE ) + { + tx_status = ksz8851_reg_read( REG_TX_STATUS ); + fhr_status = ksz8851_reg_read( REG_RX_FHR_STATUS ); + wait_tx_space = pdTRUE; + } + //return; + rx_debug = 1; + tx_space = txmir; + } + else + { + tx_space = txmir; + + /* TX step2: disable all interrupts. */ + ksz8851_reg_write( REG_INT_MASK, 0 ); + + xMicrelDevice.tx_space -= xLength; + + /* TX step3: enable TXQ write access. */ + ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_START ); + /* TX step4-8: perform FIFO write operation. */ + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_START; + xMicrelDevice.tx_cur_buffer = pxNetworkBuffer; + /* Bring SPI SS low. */ + gpio_set_pin_low( KSZ8851SNL_CSN_GPIO ); + xMicrelDevice.ul_total_tx++; + + ksz8851_fifo_write( pxNetworkBuffer->pucEthernetBuffer, xLength, xLength ); + } + } + } + break; /* SPI_PDC_IDLE */ + + case SPI_PDC_RX_COMPLETE: + { + int rxHead = xMicrelDevice.us_rx_head; + /* RX step18-19: pad with dummy data to keep dword alignment. */ + /* Packet lengths will be rounded up to a multiple of "sizeof size_t". */ +// xLength = xMicrelDevice.rx_buffers[ rxHead ]->xDataLength & 3; +// if( xLength != 0 ) +// { +// ksz8851_fifo_dummy( 4 - xLength ); +// } + + /* RX step20: end RX transfer. */ + gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); + + /* Disable asynchronous transfer mode. */ + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE; + + /* RX step21: end RXQ read access. */ + ksz8851_reg_clrbits(REG_RXQ_CMD, RXQ_START); + + /* RX step22-23: update frame count to be read. */ + xMicrelDevice.us_pending_frame -= 1; + + /* RX step24: enable INT_RX flag if transfer complete. */ + if( xMicrelDevice.us_pending_frame == 0 ) + { + ksz8851_reg_write(REG_INT_MASK, INT_RX); + } + + /* Mark descriptor ready to be read. */ + xMicrelDevice.rx_ready[ rxHead ] = pdTRUE; + if( ++rxHead == MICREL_RX_BUFFERS ) + { + rxHead = 0; + } + xMicrelDevice.us_rx_head = rxHead; + if( rx_debug != 0 ) + { + uint32_t txmir; + rx_debug = 0; + txmir = ksz8851_reg_read( REG_TX_MEM_INFO ); + txmir = txmir & TX_MEM_AVAILABLE_MASK; + } + /* Tell prvEMACHandlerTask that RX packets are available. */ + ulISREvents |= EMAC_IF_RX_EVENT; + } /* case SPI_PDC_RX_COMPLETE */ + break; + + case SPI_PDC_TX_COMPLETE: + { + int txTail = xMicrelDevice.us_tx_tail; + NetworkBufferDescriptor_t *pxNetworkBuffer = xMicrelDevice.tx_buffers[ txTail ]; + + size_t xLength; + /* TX step9-10: pad with dummy data to keep dword alignment. */ + /* Not necessary: length is already a multiple of 4. */ + xLength = pxNetworkBuffer->xDataLength & 3; + if( xLength != 0 ) + { +// ksz8851_fifo_dummy( 4 - xLength ); + } + +// /* TX step11: end TX transfer. */ + gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); + + /* Disable asynchronous transfer mode. */ + xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE; + + /* TX step12: disable TXQ write access. */ + ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START ); + + xMicrelDevice.tx_space = ksz8851_reg_read( REG_TX_MEM_INFO ) & TX_MEM_AVAILABLE_MASK; + + /* TX step12.1: enqueue frame in TXQ. */ + ksz8851_reg_setbits( REG_TXQ_CMD, TXQ_ENQUEUE ); + + /* RX step13: enable INT_RX flag. */ +// ksz8851_reg_write( REG_INT_MASK, INT_RX ); + /* Buffer sent, free the corresponding buffer and mark descriptor as owned by software. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + + xMicrelDevice.tx_buffers[ txTail ] = NULL; + xMicrelDevice.tx_busy[ txTail ] = pdFALSE; + if( ++txTail == MICREL_TX_BUFFERS ) + { + txTail = 0; + } + + xMicrelDevice.us_tx_tail = txTail; + /* Experiment. */ + //xMicrelDevice.ul_had_intn_interrupt = 1; + if( xTransmitHandle != NULL ) + { + xTaskNotifyGive( xTransmitHandle ); + } +#warning moved downward + /* RX step13: enable INT_RX flag. */ + ksz8851_reg_write( REG_INT_MASK, INT_RX ); + /* Prevent the EMAC task from sleeping a single time. */ + ulISREvents |= EMAC_IF_TX_EVENT; + } /* case SPI_PDC_TX_COMPLETE */ + break; + } /* switch( xMicrelDevice.ul_spi_pdc_status ) */ +} + +/** + * \brief Set up the RX descriptor ring buffers. + * + * This function sets up the descriptor list used for RX packets. + * + */ +static void ksz8851snl_rx_init() +{ + uint32_t ul_index = 0; + + /* Init pointer index. */ + xMicrelDevice.us_rx_head = 0; + xMicrelDevice.us_rx_tail = 0; + + /* Set up the RX descriptors. */ + for (ul_index = 0; ul_index < MICREL_RX_BUFFERS; ul_index++) { + xMicrelDevice.rx_buffers[ul_index] = NULL; + xMicrelDevice.rx_ready[ul_index] = pdFALSE; + } + + /* Build RX buffer and descriptors. */ + ksz8851snl_rx_populate_queue(); +} + +/** + * \brief Set up the TX descriptor ring buffers. + * + * This function sets up the descriptor list used for TX packets. + * + */ +static void ksz8851snl_tx_init() +{ + uint32_t ul_index = 0; + + /* Init TX index pointer. */ + xMicrelDevice.us_tx_head = 0; + xMicrelDevice.us_tx_tail = 0; + + /* Set up the TX descriptors */ + for( ul_index = 0; ul_index < MICREL_TX_BUFFERS; ul_index++ ) + { + xMicrelDevice.tx_busy[ul_index] = pdFALSE; + } + xMicrelDevice.tx_space = 6144; +} + +/** + * \brief Initialize ksz8851snl ethernet controller. + * + * \note Called from ethernetif_init(). + * + * \param netif the lwIP network interface structure for this ethernetif. + */ +static void ksz8851snl_low_level_init( void ) +{ + ksz8851snl_rx_init(); + ksz8851snl_tx_init(); + + /* Enable NVIC interrupts. */ + NVIC_SetPriority(SPI_IRQn, INT_PRIORITY_SPI); + NVIC_EnableIRQ(SPI_IRQn); + + /* Initialize SPI link. */ + if( ksz8851snl_init() < 0 ) + { + FreeRTOS_printf( ( "ksz8851snl_low_level_init: failed to initialize the Micrel driver!\n" ) ); + configASSERT(0 == 1); + } + memset( xMicrelDevice.pusHashTable, 255, sizeof( xMicrelDevice.pusHashTable ) ); + ksz8851_reg_write( REG_MAC_HASH_0, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 0 ] ) ); + ksz8851_reg_write( REG_MAC_HASH_2, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 1 ] ) ); + ksz8851_reg_write( REG_MAC_HASH_4, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 2 ] ) ); + ksz8851_reg_write( REG_MAC_HASH_6, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 3 ] ) ); + + /* Initialize interrupt line INTN. */ + configure_intn( INTN_Handler ); +} + +/** + * \brief Use pre-allocated pbuf as DMA source and return the incoming packet. + * + * \param netif the lwIP network interface structure for this ethernetif. + * + * \return a pbuf filled with the received packet (including MAC header). + * 0 on memory error. + */ +static NetworkBufferDescriptor_t *ksz8851snl_low_level_input( void ) +{ +NetworkBufferDescriptor_t *pxNetworkBuffer = NULL; +int rxTail = xMicrelDevice.us_rx_tail; + + /* Check that descriptor is owned by software (ie packet received). */ + if( xMicrelDevice.rx_ready[ rxTail ] != pdFALSE ) + { + + /* Fetch pre-allocated buffer */ + pxNetworkBuffer = xMicrelDevice.rx_buffers[ rxTail ]; + + /* Remove this pbuf from its descriptor. */ + xMicrelDevice.rx_buffers[ rxTail ] = NULL; + + /* Clears rx_ready and sets rx_buffers. */ + ksz8851snl_rx_populate_queue(); + + if( ++rxTail == MICREL_RX_BUFFERS ) + { + rxTail = 0; + } + xMicrelDevice.us_rx_tail = rxTail; + } + + return pxNetworkBuffer; +} +/*-----------------------------------------------------------*/ + +static uint32_t prvEMACRxPoll( void ) +{ +NetworkBufferDescriptor_t *pxNetworkBuffer; +IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; +uint32_t ulReturnValue = 0; + + for( ;; ) + { + /* Only for logging. */ + int rxTail = xMicrelDevice.us_rx_tail; + EthernetHeader_t *pxEthernetHeader; + + pxNetworkBuffer = ksz8851snl_low_level_input(); + + if( pxNetworkBuffer == NULL ) + { + break; + } + pxEthernetHeader = ( EthernetHeader_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); + + if( ( pxEthernetHeader->usFrameType != ipIPv4_FRAME_TYPE ) && + ( pxEthernetHeader->usFrameType != ipARP_FRAME_TYPE ) ) + { + FreeRTOS_printf( ( "Frame type %02X received\n", pxEthernetHeader->usFrameType ) ); + } + ulReturnValue++; + + xRxEvent.pvData = ( void * )pxNetworkBuffer; + /* Send the descriptor to the IP task for processing. */ + if( xSendEventStructToIPTask( &xRxEvent, 100UL ) != pdTRUE ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + iptraceETHERNET_RX_EVENT_LOST(); + FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) ); + } + } + + return ulReturnValue; +} +/*-----------------------------------------------------------*/ + +static void prvEMACHandlerTask( void *pvParameters ) +{ +TimeOut_t xPhyTime; +TickType_t xPhyRemTime; +TickType_t xLoggingTime; +UBaseType_t uxLastMinBufferCount = 0; +UBaseType_t uxCurrentCount; +BaseType_t xResult = 0; +uint32_t xStatus; +const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( EMAC_MAX_BLOCK_TIME_MS ); +#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + UBaseType_t uxLastMinQueueSpace = 0; +#endif + + /* Remove compiler warnings about unused parameters. */ + ( void ) pvParameters; + + configASSERT( xEMACTaskHandle ); + + vTaskSetTimeOutState( &xPhyTime ); + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + xLoggingTime = xTaskGetTickCount(); + + for( ;; ) + { + uxCurrentCount = uxGetMinimumFreeNetworkBuffers(); + if( uxLastMinBufferCount != uxCurrentCount ) + { + /* The logging produced below may be helpful + while tuning +TCP: see how many buffers are in use. */ + uxLastMinBufferCount = uxCurrentCount; + FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", + uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) ); + } + + #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) + { + uxCurrentCount = uxGetMinimumIPQueueSpace(); + if( uxLastMinQueueSpace != uxCurrentCount ) + { + /* The logging produced below may be helpful + while tuning +TCP: see how many buffers are in use. */ + uxLastMinQueueSpace = uxCurrentCount; + FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) ); + } + } + #endif /* ipconfigCHECK_IP_QUEUE_SPACE */ + + /* Run the state-machine of the ksz8851 driver. */ + ksz8851snl_update(); + + if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 ) + { + /* No events to process now, wait for the next. */ + ulTaskNotifyTake( pdTRUE, ulMaxBlockTime ); + } + + if( ( xTaskGetTickCount() - xLoggingTime ) > 10000 ) + { + xLoggingTime += 10000; + FreeRTOS_printf( ( "Now Tx/Rx %7d /%7d\n", + xMicrelDevice.ul_total_tx, xMicrelDevice.ul_total_rx ) ); + } + + if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 ) + { + ulISREvents &= ~EMAC_IF_RX_EVENT; + + /* Wait for the EMAC interrupt to indicate that another packet has been + received. */ + xResult = prvEMACRxPoll(); + } + + if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 ) + { + /* Future extension: code to release TX buffers if zero-copy is used. */ + ulISREvents &= ~EMAC_IF_TX_EVENT; + } + + if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 ) + { + /* Future extension: logging about errors that occurred. */ + ulISREvents &= ~EMAC_IF_ERR_EVENT; + } + + if( xResult > 0 ) + { + /* As long as packets are being received, assume that + the Link Status is high. */ + ulPHYLinkStatus |= BMSR_LINK_STATUS; + /* A packet was received. No need to check for the PHY status now, + but set a timer to check it later on. */ + vTaskSetTimeOutState( &xPhyTime ); + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); + xResult = 0; + } + else if( ( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) && + ( xMicrelDevice.ul_spi_pdc_status == SPI_PDC_IDLE ) ) + { + /* Check the link status again. */ + xStatus = ulReadMDIO( PHY_REG_01_BMSR ); + + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) ) + { + ulPHYLinkStatus = xStatus; + FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) ); + } + + vTaskSetTimeOutState( &xPhyTime ); + if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) + { + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); + } + else + { + xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); + } + } + } +} +/*-----------------------------------------------------------*/