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