Newer
Older
mbed-os / connectivity / drivers / emac / TARGET_RDA_EMAC / rda5981x_emac.cpp
@George Psimenos George Psimenos on 28 Jul 2020 8 KB Restructure events directory & move tests
/* Copyright (c) 2019 Unisoc Communications Inc.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include "cmsis_os.h"
#include "mbed_interface.h"
#include "mbed_assert.h"
#include "events/mbed_shared_queues.h"
#include "netsocket/nsapi_types.h"
#include "lwip/arch.h"
#include "lwip/pbuf.h"
#include "rda5991h_wland.h"
#include "rda5981x_emac_config.h"
#include "rda5981x_emac.h"
#include "rda_sys_wrapper.h"
#include "maclib_task.h"

#define RDA_HWADDR_SIZE         (6)
#define RDA_ETH_MTU_SIZE        1500
#define RDA_ETH_IF_NAME         "st"

#define RX_PRIORITY   (osPriorityNormal)
#define TX_PRIORITY   (osPriorityNormal)
#define PHY_PRIORITY  (osPriorityNormal)

extern void *packet_rx_queue;

RDA5981x_EMAC::RDA5981x_EMAC()
{
}

/**
 * This function should do the actual transmission of the packet. The packet is
 * contained in the memory buffer chain that is passed to the function.
 *
 * @param buf the MAC packet to send (e.g. IP packet including MAC addresses and type)
 * @return true if the packet could be sent
 *         false value if the packet couldn't be sent
 *
 * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
 *       strange results. You might consider waiting for space in the DMA queue
 *       to become availale since the stack doesn't retry to send a packet
 *       dropped because of memory failure (except for the TCP timers).
 */
bool RDA5981x_EMAC::link_out(emac_mem_buf_t *buf)
{
    emac_mem_buf_t *q, *p = buf;

    u32_t actual_txlen = 0;
    u8_t **data = NULL;
    u16_t retry = 400;

    LWIP_DEBUGF(NETIF_DEBUG, ("low_level_output enter, p:%08x\n", p));

    while ((data == NULL) && (retry-- > 0)) {
        data = (u8_t**)wland_get_databuf();
        osThreadYield();
    }
    if (data == NULL) {
        LWIP_DEBUGF(NETIF_DEBUG, ("rda91h_low_level_output, no PKT buf\r\n"));
        memory_manager->free(buf);
        return false;
    }

    for (q = p; q != NULL; q = memory_manager->get_next(q)) {
        /* Send the data from the pbuf to the interface, one pbuf at a
           time. The size of the data in each pbuf is kept in the ->len
           variable. */
        memcpy(&((*data)[actual_txlen+2]), memory_manager->get_ptr(q), memory_manager->get_len(q));//reserve wid header length
        actual_txlen += memory_manager->get_len(q);
        if (actual_txlen > 1514 || actual_txlen > memory_manager->get_total_len(p)) {
            LWIP_DEBUGF(NETIF_DEBUG, ("low_level_output err, actual_txlen:%d, tot_len%d\n", actual_txlen, memory_manager->get_total_len(p)));
            memory_manager->free(buf);
            return false;
        }
    }

    /* Signal rda5996 that packet should be sent */
    if (actual_txlen == memory_manager->get_total_len(p)) {
        wland_txip_data((void*)data, actual_txlen, 0);
        memory_manager->free(buf);
        return true;
    }

    LWIP_DEBUGF(NETIF_DEBUG, ("low_level_output pkt len mismatch, actual_txlen:%d, tot_len%d\n",
            actual_txlen, memory_manager->get_total_len(p)));

    memory_manager->free(buf);
    return false;
}

/**
 * Should allocate a contiguous memory buffer and transfer the bytes of the incoming
 * packet to the buffer.
 *
 * @param buf If a frame was received and the memory buffer allocation was successful, a memory
 *            buffer filled with the received packet (including MAC header)
 * @return negative value when no more frames,
 *         zero when frame is received
 */
emac_mem_buf_t * RDA5981x_EMAC::low_level_input(u8_t *data, int len)
{
    emac_mem_buf_t *p, *q;
    u16_t index = 0;

    LWIP_DEBUGF(NETIF_DEBUG, ("low_level_input enter, rxlen:%d\n", len));

    /* Obtain the size of the packet and put it into the "len"
     variable. */
    if (!len) {
        return NULL;
    }

    /* We allocate a pbuf chain of pbufs from the pool. */
    p = memory_manager->alloc_pool(len, 0);
    if (p != NULL) {
        /* We iterate over the pbuf chain until we have read the entire
         * packet into the pbuf. */
        for (q = p; q != NULL; q = memory_manager->get_next(q)) {
            /* Read enough bytes to fill this pbuf in the chain. The
            * available data in the pbuf is given by the q->len
            * variable.
            * This does not necessarily have to be a memcpy, you can also preallocate
            * pbufs for a DMA-enabled MAC and after receiving truncate it to the
            * actually received size. In this case, ensure the tot_len member of the
            * pbuf is the sum of the chained pbuf len members.
            */
            /* load rx data from 96 to local mem_pool */
            memcpy(memory_manager->get_ptr(q), &data[index], memory_manager->get_len(q));
            index += memory_manager->get_len(q);

            if (index >= len) {
                break;
            }
        }

    } else {
        /* Drop this packet */
        LWIP_DEBUGF(NETIF_DEBUG, ("low_level_input pbuf_alloc fail, rxlen:%d\n", len));

        return NULL;
    }
    return p;
}


/** \brief  Attempt to read a packet from the EMAC interface.
 *
 */
void RDA5981x_EMAC::packet_rx()
{
    rda_msg msg;
    packet_rx_queue = rda_mail_create(10, sizeof(unsigned int)*4);
    /* move received packet into a new buf */
    while (1) {
        emac_mem_buf_t *p = NULL;
        rda_mail_get(packet_rx_queue, (void*)&msg, osWaitForever);
        switch(msg.type) {
            case 0:
                p = low_level_input((unsigned char*)msg.arg1, msg.arg2);
                if (p == NULL) {
                    break;
                }
                if (p) {
                    emac_link_input_cb(p);
                }
                break;
            case 1:
                emac_link_state_cb(msg.arg1);
                break;
            default:
                break;
        }
    }
}

void RDA5981x_EMAC::thread_function(void *pvParameters)
{
    static struct RDA5981x_EMAC *rda5981x_enet = static_cast<RDA5981x_EMAC *>(pvParameters);
    rda5981x_enet->packet_rx();
}

bool RDA5981x_EMAC::power_up()
{
    /* Initialize the hardware */
    static int init_flag = 0;
    if (init_flag == 0) {
        wland_reg_func();
        rda_thread_new("maclib_thread", maclib_task, NULL, DEFAULT_THREAD_STACKSIZE*8, PHY_PRIORITY);
        rda_thread_new("wland_thread", wland_task, NULL, DEFAULT_THREAD_STACKSIZE*5, PHY_PRIORITY);
        rda_thread_new("packet_rx", RDA5981x_EMAC::thread_function, this, DEFAULT_THREAD_STACKSIZE*5, PHY_PRIORITY);
        /* Allow the PHY task to detect the initial link state and set up the proper flags */
        osDelay(100);
        wland_sta_init();
        init_flag = 1;
    }

    return true;
}

uint32_t RDA5981x_EMAC::get_mtu_size() const
{
    return RDA_ETH_MTU_SIZE;
}

uint32_t RDA5981x_EMAC::get_align_preference() const
{
    return 0;
}

void RDA5981x_EMAC::get_ifname(char *name, uint8_t size) const
{
    memcpy(name, RDA_ETH_IF_NAME, (size < sizeof(RDA_ETH_IF_NAME)) ? size : sizeof(RDA_ETH_IF_NAME));
}

uint8_t RDA5981x_EMAC::get_hwaddr_size() const
{
    return RDA_HWADDR_SIZE;
}

bool RDA5981x_EMAC::get_hwaddr(uint8_t *addr) const
{
    mbed_mac_address((char *)addr);
    return true;
}

void RDA5981x_EMAC::set_hwaddr(const uint8_t *addr)
{
    /* No-op at this stage */
}

void RDA5981x_EMAC::set_link_input_cb(emac_link_input_cb_t input_cb)
{
    emac_link_input_cb = input_cb;
}

void RDA5981x_EMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb)
{
    emac_link_state_cb = state_cb;
}

void RDA5981x_EMAC::add_multicast_group(const uint8_t *addr)
{
    /* No-op at this stage */
}

void RDA5981x_EMAC::remove_multicast_group(const uint8_t *addr)
{
    /* No-op at this stage */
}

void RDA5981x_EMAC::set_all_multicast(bool all)
{
    /* No-op at this stage */
}

void RDA5981x_EMAC::power_down()
{
    /* No-op at this stage */
}

void RDA5981x_EMAC::set_memory_manager(EMACMemoryManager &mem_mngr)
{
    memory_manager = &mem_mngr;
}

RDA5981x_EMAC &RDA5981x_EMAC::get_instance()
{
    static RDA5981x_EMAC emac;
    return emac;
}

// Weak so a module can override
MBED_WEAK EMAC &EMAC::get_default_instance()
{
    return RDA5981x_EMAC::get_instance();
}