diff --git a/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_Freescale/k64f_emac.c b/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_Freescale/k64f_emac.c index b4c9d1e822..4b3c003eb9 100644 --- a/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_Freescale/k64f_emac.c +++ b/features/FEATURE_LWIP/lwip-interface/lwip-eth/arch/TARGET_Freescale/k64f_emac.c @@ -52,8 +52,7 @@ extern void k66f_init_eth_hardware(void); /* K64F EMAC driver data structure */ struct k64f_enetdata { struct netif *netif; /**< Reference back to LWIP parent netif */ - sys_sem_t RxReadySem; /**< RX packet ready semaphore */ - sys_sem_t TxCleanSem; /**< TX cleanup thread wakeup semaphore */ + osThreadId_t thread; /**< Processing thread */ sys_mutex_t TXLockMutex; /**< TX critical section mutex */ sys_sem_t xTXDCountSem; /**< TX free buffer counting semaphore */ uint8_t tx_consume_index, tx_produce_index; /**< TX buffers ring */ @@ -61,15 +60,24 @@ struct k64f_enetdata { static struct k64f_enetdata k64f_enetdata; -/** \brief Driver transmit and receive thread priorities - * - * Thread priorities for receive thread and TX cleanup thread. Alter - * to prioritize receive or transmit bandwidth. In a heavily loaded - * system or with LEIP_DEBUG enabled, the priorities might be better - * the same. */ -#define RX_PRIORITY (osPriorityNormal) -#define TX_PRIORITY (osPriorityNormal) -#define PHY_PRIORITY (osPriorityNormal) +/* \brief Flags for worker thread */ +#define FLAG_TX 1 +#define FLAG_RX 2 + +/** \brief Driver thread priority */ +#define THREAD_PRIORITY (osPriorityNormal) + +#ifdef LWIP_DEBUG +#define THREAD_STACKSIZE (DEFAULT_THREAD_STACKSIZE * 5) +#else +#define THREAD_STACKSIZE DEFAULT_THREAD_STACKSIZE +#endif + +static void k64f_phy_task(void *data); +static void packet_rx(struct k64f_enetdata *k64f_enet); +static void packet_tx(struct k64f_enetdata *k64f_enet); + +#define PHY_TASK_PERIOD_MS 200 /******************************************************************************** * Buffer management @@ -132,12 +140,12 @@ static void k64f_tx_reclaim(struct k64f_enetdata *k64f_enet) */ void enet_mac_rx_isr() { - sys_sem_signal(&k64f_enetdata.RxReadySem); + osThreadFlagsSet(k64f_enetdata.thread, FLAG_RX); } void enet_mac_tx_isr() { - sys_sem_signal(&k64f_enetdata.TxCleanSem); + osThreadFlagsSet(k64f_enetdata.thread, FLAG_TX); } void ethernet_callback(ENET_Type *base, enet_handle_t *handle, enet_event_t event, void *param) @@ -461,26 +469,56 @@ void k64f_enetif_input(struct netif *netif, int idx) } } +/** \brief Worker thread. + * + * Woken by thread flags to receive packets or clean up transmit + * + * \param[in] pvParameters pointer to the interface data + */ +static void emac_thread(void* pvParameters) +{ + struct k64f_enetdata *k64f_enet = pvParameters; + + for (;;) { + uint32_t flags = osThreadFlagsWait(FLAG_RX|FLAG_TX, osFlagsWaitAny, PHY_TASK_PERIOD_MS); + if (flags == osFlagsErrorTimeout) { + // Rather than calling strictly every period, we call when idle + // for that period - hopefully good enough. We run this task + // from lwIP's thread rather than our RX/TX thread, as PHY reads can + // be slow, and we don't want them to interfere with data pumping. + // This is analogous to the way the PHY polling works in the Nanostack + // version of the driver + tcpip_callback_with_block(k64f_phy_task, k64f_enet->netif, 0); + continue; + } + + LWIP_ASSERT("osThreadFlagsWait error", !(flags & osFlagsError)); + + if (flags & FLAG_RX) { + packet_rx(k64f_enet); + } + + if (flags & FLAG_TX) { + packet_tx(k64f_enet); + } + } +} + /** \brief Packet reception task * * This task is called when a packet is received. It will * pass the packet to the LWIP core. * - * \param[in] pvParameters pointer to the interface data + * \param[in] k64f_enet pointer to the interface data */ -static void packet_rx(void* pvParameters) { - struct k64f_enetdata *k64f_enet = pvParameters; - int idx = 0; - - while (1) { - /* Wait for receive task to wakeup */ - sys_arch_sem_wait(&k64f_enet->RxReadySem, 0); +static void packet_rx(struct k64f_enetdata *k64f_enet) +{ + static int idx = 0; while ((g_handle.rxBdCurrent->control & ENET_BUFFDESCRIPTOR_RX_EMPTY_MASK) == 0) { - k64f_enetif_input(k64f_enet->netif, idx); - idx = (idx + 1) % ENET_RX_RING_LEN; + k64f_enetif_input(k64f_enet->netif, idx); + idx = (idx + 1) % ENET_RX_RING_LEN; } - } } /** \brief Transmit cleanup task @@ -489,16 +527,11 @@ static void packet_rx(void* pvParameters) { * reclaims the pbuf and descriptor used for the packet once * the packet has been transferred. * - * \param[in] pvParameters pointer to the interface data + * \param[in] k64f_enet pointer to the interface data */ -static void packet_tx(void* pvParameters) { - struct k64f_enetdata *k64f_enet = pvParameters; - - while (1) { - /* Wait for transmit cleanup task to wakeup */ - sys_arch_sem_wait(&k64f_enet->TxCleanSem, 0); +static void packet_tx(struct k64f_enetdata *k64f_enet) +{ k64f_tx_reclaim(k64f_enet); - } } /** \brief Low level output of a packet. Never call this from an @@ -569,7 +602,6 @@ static err_t k64f_low_level_output(struct netif *netif, struct pbuf *p) * PHY task: monitor link *******************************************************************************/ -#define PHY_TASK_PERIOD_MS 200 #define STATE_UNKNOWN (-1) typedef struct { @@ -578,7 +610,7 @@ typedef struct { phy_duplex_t duplex; } PHY_STATE; -int phy_link_status() { +int phy_link_status(void) { bool connection_status; uint32_t phyAddr = 0; @@ -586,40 +618,40 @@ int phy_link_status() { return (int)connection_status; } -static void k64f_phy_task(void *data) { - struct netif *netif = (struct netif*)data; - bool connection_status; - PHY_STATE crt_state = {STATE_UNKNOWN, (phy_speed_t)STATE_UNKNOWN, (phy_duplex_t)STATE_UNKNOWN}; - PHY_STATE prev_state; - uint32_t phyAddr = 0; - uint32_t rcr = 0; +static void k64f_phy_task(void *data) +{ + struct netif *netif = data; + + static PHY_STATE prev_state = {STATE_UNKNOWN, (phy_speed_t)STATE_UNKNOWN, (phy_duplex_t)STATE_UNKNOWN}; + + uint32_t phyAddr = 0; - prev_state = crt_state; - while (true) { // Get current status + PHY_STATE crt_state; + bool connection_status; PHY_GetLinkStatus(ENET, phyAddr, &connection_status); - crt_state.connected = connection_status ? 1 : 0; + crt_state.connected = connection_status; // Get the actual PHY link speed PHY_GetLinkSpeedDuplex(ENET, phyAddr, &crt_state.speed, &crt_state.duplex); // Compare with previous state if (crt_state.connected != prev_state.connected) { - if (crt_state.connected) - tcpip_callback_with_block((tcpip_callback_fn)netif_set_link_up, (void*) netif, 1); - else - tcpip_callback_with_block((tcpip_callback_fn)netif_set_link_down, (void*) netif, 1); + // We're called from lwIP's tcpip thread, so can call link functions directly + if (crt_state.connected) { + netif_set_link_up(netif); + } else { + netif_set_link_down(netif); + } } if (crt_state.speed != prev_state.speed) { - rcr = ENET->RCR; + uint32_t rcr = ENET->RCR; rcr &= ~ENET_RCR_RMII_10T_MASK; rcr |= ENET_RCR_RMII_10T(!crt_state.speed); ENET->RCR = rcr; } prev_state = crt_state; - osDelay(PHY_TASK_PERIOD_MS); - } } /** @@ -707,27 +739,13 @@ err_t eth_arch_enetif_init(struct netif *netif) err = sys_mutex_new(&k64f_enetdata.TXLockMutex); LWIP_ASSERT("TXLockMutex creation error", (err == ERR_OK)); - /* Packet receive task */ - err = sys_sem_new(&k64f_enetdata.RxReadySem, 0); - LWIP_ASSERT("RxReadySem creation error", (err == ERR_OK)); - -#ifdef LWIP_DEBUG - sys_thread_new("k64f_emac_rx_thread", packet_rx, netif->state, DEFAULT_THREAD_STACKSIZE*5, RX_PRIORITY); -#else - sys_thread_new("k64f_emac_thread", packet_rx, netif->state, DEFAULT_THREAD_STACKSIZE, RX_PRIORITY); -#endif - - /* Transmit cleanup task */ - err = sys_sem_new(&k64f_enetdata.TxCleanSem, 0); - LWIP_ASSERT("TxCleanSem creation error", (err == ERR_OK)); - sys_thread_new("k64f_emac_txclean_thread", packet_tx, netif->state, DEFAULT_THREAD_STACKSIZE, TX_PRIORITY); - - /* PHY monitoring task */ - sys_thread_new("k64f_emac_phy_thread", k64f_phy_task, netif, DEFAULT_THREAD_STACKSIZE, PHY_PRIORITY); - /* Allow the PHY task to detect the initial link state and set up the proper flags */ + tcpip_callback_with_block(k64f_phy_task, netif, 1); osDelay(10); + /* Worker thread */ + k64f_enetdata.thread = sys_thread_new("k64f_emac_thread", emac_thread, netif->state, THREAD_STACKSIZE, THREAD_PRIORITY)->id; + return ERR_OK; }