Merge pull request #4996 from kjbracey-arm/k64f_emac_1thread

Reduce K64F Ethernet driver to 1 thread
pull/4968/head
Jimmy Brisson 2017-09-05 11:18:44 -05:00 committed by GitHub
commit 857819f5d2
1 changed files with 87 additions and 69 deletions

View File

@ -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;
}