Newer
Older
mbed-os / features / netsocket / emac-drivers / TARGET_RENESAS_EMAC / TARGET_RZ_A2XX / rza2_emac.cpp
@RyoheiHagimoto RyoheiHagimoto on 17 Aug 2020 7 KB Added GR-MANGO to be a target.
/* Copyright (c) 2018-2020 Renesas Electronics Corporation.
 * 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 <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "cmsis_os.h"

#include "mbed_interface.h"
#include "mbed_assert.h"
#include "netsocket/nsapi_types.h"
#include "mbed_shared_queues.h"

#include "r_ether_rza2_if.h"
#include "rza2_emac.h"
#include "r_ether_rza2_config.h"

#define RZ_A2_ETH_IF_NAME    "en"
#define PHY_TASK_PERIOD      200ms

using namespace std::chrono;

// Weak so a module can override
MBED_WEAK EMAC &EMAC::get_default_instance()
{
#if (ETHER_CH0_EN == 1)
    return RZ_A2_EMAC::get_instance(ETHER_CHANNEL_0);
#elif (ETHER_CH1_EN == 1)
    return RZ_A2_EMAC::get_instance(ETHER_CHANNEL_1);
#else
#error "Set ETHER_CH0_EN or ETHER_CH1_EN to 1."
#endif
}

RZ_A2_EMAC &RZ_A2_EMAC::get_instance(uint32_t channel)
{
#if (ETHER_CH0_EN == 1) && (ETHER_CH1_EN == 1)
    static RZ_A2_EMAC emac_0(ETHER_CHANNEL_0);
    static RZ_A2_EMAC emac_1(ETHER_CHANNEL_1);

    if (channel == 0) {
        return emac_0;
    } else {
        return emac_1;
    }
#else
    static RZ_A2_EMAC emac(channel);
    return emac;
#endif
}

RZ_A2_EMAC::RZ_A2_EMAC(uint32_t channel) : _channel(channel), hwaddr(), hwaddr_set(false), power_on(false),
    recvThread(osPriorityNormal, 896), sem_recv(0)
{
}

uint32_t RZ_A2_EMAC::get_mtu_size() const
{
    return 1500;
}

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

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

uint8_t RZ_A2_EMAC::get_hwaddr_size() const
{
    return 6;
}

bool RZ_A2_EMAC::get_hwaddr(uint8_t *addr) const
{
    return false;
}

void RZ_A2_EMAC::set_hwaddr(const uint8_t *addr)
{
    memcpy(hwaddr, addr, sizeof(hwaddr));
    hwaddr_set = true;

    /* Reconnect */
    if (power_on != false) {
        R_ETHER_Open_ZC2(_channel, hwaddr, ETHER_FLAG_OFF);
    }
}

bool RZ_A2_EMAC::link_out(emac_mem_buf_t *buf)
{
    emac_mem_buf_t *copy_buf = buf;
    uint32_t       retry_cnt = 0;
    uint16_t       write_buf_size;
    int            total_write_size = 0;
    uint8_t *      pwrite_buffer_address;

    while (1) {
        if (R_ETHER_Write_ZC2_GetBuf(_channel, (void **) &pwrite_buffer_address, &write_buf_size) == ETHER_SUCCESS) {
            break;
        }
        retry_cnt++;
        if (retry_cnt > 200) {
            memory_manager->free(buf);
            return false;
        }
        osDelay(1);
    }

    while ((copy_buf != NULL) && (memory_manager->get_ptr(copy_buf) != NULL) && (memory_manager->get_len(copy_buf) != 0)) {
        memcpy(&pwrite_buffer_address[total_write_size], memory_manager->get_ptr(copy_buf), memory_manager->get_len(copy_buf));
        total_write_size +=  memory_manager->get_len(copy_buf);
        copy_buf = memory_manager->get_next(copy_buf);
    }
    memory_manager->free(buf);

    if (total_write_size > 0) {
        if (total_write_size < 60) {
            memset(&pwrite_buffer_address[total_write_size], 0, 60 - total_write_size);
            total_write_size = 60;
        }
        if (R_ETHER_Write_ZC2_SetBuf(_channel, total_write_size) == ETHER_SUCCESS) {
            return true;
        }
    }

    return false;
}

bool RZ_A2_EMAC::power_up()
{
    ether_param_t param;

    if (power_on != false) {
        return true;
    }

    /* Initialize memory which ETHERC/EDMAC is used */
    R_ETHER_Initial();

    /* Set the callback function */
    param.ether_callback.pcb_func    = &_callback_pcb;
    R_ETHER_Control(CONTROL_SET_CALLBACK, param);

    /* Set the callback function */
    param.ether_callback.pcb_int_hnd    = &_callback_hnd;
    R_ETHER_Control(CONTROL_SET_INT_HANDLER, param);

    param.channel = _channel;
    R_ETHER_Control(CONTROL_POWER_ON, param);

    if (hwaddr_set != false) {
        R_ETHER_Open_ZC2(_channel, hwaddr, ETHER_FLAG_OFF);
    }

    /* task */
    recvThread.start(mbed::callback(this, &RZ_A2_EMAC::recv_task));
    phy_task_handle = mbed::mbed_event_queue()->call_every(PHY_TASK_PERIOD, mbed::callback(this, &RZ_A2_EMAC::phy_task));

    power_on = true;
    return true;
}

void RZ_A2_EMAC::power_down()
{
    power_on = false;
}

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

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

void RZ_A2_EMAC::add_multicast_group(const uint8_t *addr)
{
    // Not supported
}

void RZ_A2_EMAC::remove_multicast_group(const uint8_t *addr)
{
    // Not supported
}

void RZ_A2_EMAC::set_all_multicast(bool all)
{
    // Not supported
}

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

void RZ_A2_EMAC::_callback_pcb(void* arg)
{
    ether_cb_arg_t * p_cb_arg = (ether_cb_arg_t *)arg;
    get_instance(p_cb_arg->channel).callback_pcb(arg);
}

void RZ_A2_EMAC::_callback_hnd(void* arg)
{
    ether_cb_arg_t * p_cb_arg = (ether_cb_arg_t *)arg;
    get_instance(p_cb_arg->channel).callback_hnd(arg);
}

void RZ_A2_EMAC::callback_pcb(void* arg)
{
    ether_cb_arg_t * p_cb_arg = (ether_cb_arg_t *)arg;

    if (p_cb_arg->event_id == ETHER_CB_EVENT_ID_LINK_ON) {
        emac_link_state_cb(true);
    } else if (p_cb_arg->event_id == ETHER_CB_EVENT_ID_LINK_OFF) {
        emac_link_state_cb(false);
    } else {
        // do nothing
    }
}

void RZ_A2_EMAC::callback_hnd(void* arg)
{
    ether_cb_arg_t * p_cb_arg = (ether_cb_arg_t *)arg;

    if (p_cb_arg->status_eesr & 0x00040000) {
        sem_recv.release();
    }
}

void RZ_A2_EMAC::recv_task(void)
{
    int32_t ret;
    emac_mem_buf_t *buf;
    uint8_t * pread_buffer_address;

    while (1) {
        sem_recv.acquire();
        while (1) {
            /* (1) Retrieve the receive buffer location controlled by the  descriptor. */
            ret = R_ETHER_Read_ZC2(_channel, (void **)&pread_buffer_address);
            if (ret <= ETHER_NO_DATA) {
                break;
            }

            /* When there is data to receive */
            while (1) {
                buf = memory_manager->alloc_heap(ret, 0);
                if (buf != NULL) {
                    /* (2) Copy the data read from the receive buffer which is controlled by the descriptor to
                     the buffer which is specified by the user (up to 1024 bytes). */
                    memcpy(memory_manager->get_ptr(buf), pread_buffer_address, (uint32_t)memory_manager->get_len(buf));

                    /* (3) Read the receive data from the receive buffer controlled by the descriptor,
                     and then release the receive buffer. */
                    R_ETHER_Read_ZC2_BufRelease(_channel);

                    emac_link_input_cb(buf);
                    break;
                }
                osDelay(5);
            }
        }
    }
}

void RZ_A2_EMAC::phy_task(void)
{
    R_ETHER_LinkProcess(_channel);
}