Skip to content

Implementation of unified EMAC driver for Renesas mbed boards #6834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 207 additions & 0 deletions features/netsocket/emac-drivers/TARGET_RZ_A1_EMAC/rza1_emac.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
#include "cmsis_os.h"
#include "netsocket/nsapi_types.h"
#include "mbed_shared_queues.h"
#include "ethernet_api.h"
#include "ethernetext_api.h"
#include "rza1_emac.h"

#define RZ_A1_ETH_IF_NAME "en"

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

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

RZ_A1_EMAC::RZ_A1_EMAC() : hwaddr(), hwaddr_set(false), power_on(false), connect_sts(false),
link_mode_last(NEGO_FAIL), recvThread(osPriorityNormal, 896)
{
}

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

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

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

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

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

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

/* Reconnect */
if (power_on != false) {
ethernet_cfg_t ethcfg;
ethcfg.int_priority = 6;
ethcfg.recv_cb = &_recv_callback;
ethcfg.ether_mac = NULL;
ethcfg.ether_mac = (char *)hwaddr;
ethernetext_init(&ethcfg);
}
}

bool RZ_A1_EMAC::link_out(emac_mem_buf_t *buf)
{
emac_mem_buf_t *copy_buf = buf;
uint32_t retry_cnt;
bool result = false;
int write_size;
int total_write_size = 0;

while ((copy_buf != NULL) && (memory_manager->get_ptr(copy_buf) != NULL) && (memory_manager->get_len(copy_buf) != 0)) {
for (retry_cnt = 0; retry_cnt < 100; retry_cnt++) {
write_size = ethernet_write((char *)memory_manager->get_ptr(copy_buf), memory_manager->get_len(copy_buf));
if (write_size != 0) {
total_write_size += write_size;
break;
}
osDelay(1);
}
copy_buf = memory_manager->get_next(copy_buf);
}
memory_manager->free(buf);

if (total_write_size > 0) {
if (ethernet_send() == 1) {
result = true;
}
}

return result;
}

bool RZ_A1_EMAC::power_up()
{
if (power_on != false) {
return true;
}

ethernet_cfg_t ethcfg;
ethcfg.int_priority = 6;
ethcfg.recv_cb = &_recv_callback;
ethcfg.ether_mac = NULL;
if (hwaddr_set) {
ethcfg.ether_mac = (char *)hwaddr;
}
ethernetext_init(&ethcfg);

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

power_on = true;
return true;
}

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

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

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

void RZ_A1_EMAC::add_multicast_group(const uint8_t *addr)
{
ethernetext_add_multicast_group(addr);
}

void RZ_A1_EMAC::remove_multicast_group(const uint8_t *addr)
{
ethernetext_remove_multicast_group(addr);
}

void RZ_A1_EMAC::set_all_multicast(bool all)
{
ethernetext_set_all_multicast(all);
}

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


void RZ_A1_EMAC::_recv_callback(void) {
get_instance().recv_callback();
}

void RZ_A1_EMAC::recv_callback(void) {
recvThread.signal_set(1);
}

void RZ_A1_EMAC::recv_task(void) {
uint16_t recv_size;
emac_mem_buf_t *buf;
int cnt;

while (1) {
rtos::Thread::signal_wait(1);
for (cnt = 0; cnt < 16; cnt++) {
recv_size = ethernet_receive();
if (recv_size == 0) {
break;
}
buf = memory_manager->alloc_heap(recv_size, 0);
if (buf != NULL) {
(void)ethernet_read((char *)memory_manager->get_ptr(buf), memory_manager->get_len(buf));
emac_link_input_cb(buf);
}
}
}
}

void RZ_A1_EMAC::phy_task(void)
{
if (ethernet_link() == 1) {
int link_mode = ethernetext_chk_link_mode();
if (link_mode != link_mode_last) {
if (connect_sts != false) {
emac_link_state_cb(false);
}
if (link_mode != NEGO_FAIL) {
ethernetext_set_link_mode(link_mode);
emac_link_state_cb(true);
connect_sts = true;
}
link_mode_last = link_mode;
}
} else {
if (connect_sts != false) {
emac_link_state_cb(false);
link_mode_last = NEGO_FAIL;
connect_sts = false;
}
}
}

169 changes: 169 additions & 0 deletions features/netsocket/emac-drivers/TARGET_RZ_A1_EMAC/rza1_emac.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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.
*/

#ifndef RZ_A1_EMAC_H
#define RZ_A1_EMAC_H

#include "EMAC.h"
#include "rtos/Thread.h"

class RZ_A1_EMAC : public EMAC {
public:
RZ_A1_EMAC();

static RZ_A1_EMAC &get_instance();

/**
* Return maximum transmission unit
*
* @return MTU in bytes
*/
virtual uint32_t get_mtu_size() const;

/**
* Gets memory buffer alignment preference
*
* Gets preferred memory buffer alignment of the Emac device. IP stack may or may not
* align link out memory buffer chains using the alignment.
*
* @return Memory alignment requirement in bytes
*/
virtual uint32_t get_align_preference() const;

/**
* Return interface name
*
* @param name Pointer to where the name should be written
* @param size Maximum number of character to copy
*/
virtual void get_ifname(char *name, uint8_t size) const;

/**
* Returns size of the underlying interface HW address size.
*
* @return HW address size in bytes
*/
virtual uint8_t get_hwaddr_size() const;

/**
* Return interface-supplied HW address
*
* Copies HW address to provided memory, @param addr has to be of correct size see @a get_hwaddr_size
*
* HW address need not be provided if this interface does not have its own HW
* address configuration; stack will choose address from central system
* configuration if the function returns false and does not write to addr.
*
* @param addr HW address for underlying interface
* @return true if HW address is available
*/
virtual bool get_hwaddr(uint8_t *addr) const;

/**
* Set HW address for interface
*
* Provided address has to be of correct size, see @a get_hwaddr_size
*
* Called to set the MAC address to actually use - if @a get_hwaddr is provided
* the stack would normally use that, but it could be overridden, eg for test
* purposes.
*
* @param addr Address to be set
*/
virtual void set_hwaddr(const uint8_t *addr);

/**
* Sends the packet over the link
*
* That can not be called from an interrupt context.
*
* @param buf Packet to be send
* @return True if the packet was send successfully, False otherwise
*/
virtual bool link_out(emac_mem_buf_t *buf);

/**
* Initializes the HW
*
* @return True on success, False in case of an error.
*/
virtual bool power_up();

/**
* Deinitializes the HW
*
*/
virtual void power_down();

/**
* Sets a callback that needs to be called for packets received for that interface
*
* @param input_cb Function to be register as a callback
*/
virtual void set_link_input_cb(emac_link_input_cb_t input_cb);

/**
* Sets a callback that needs to be called on link status changes for given interface
*
* @param state_cb Function to be register as a callback
*/
virtual void set_link_state_cb(emac_link_state_change_cb_t state_cb);

/** Add device to a multicast group
*
* @param address A multicast group hardware address
*/
virtual void add_multicast_group(const uint8_t *address);

/** Remove device from a multicast group
*
* @param address A multicast group hardware address
*/
virtual void remove_multicast_group(const uint8_t *address);

/** Request reception of all multicast packets
*
* @param all True to receive all multicasts
* False to receive only multicasts addressed to specified groups
*/
virtual void set_all_multicast(bool all);

/** Sets memory manager that is used to handle memory buffers
*
* @param mem_mngr Pointer to memory manager
*/
virtual void set_memory_manager(EMACMemoryManager &mem_mngr);

private:
EMACMemoryManager *memory_manager; /**< Memory manager */
uint8_t hwaddr[6];
bool hwaddr_set;
bool power_on;
emac_link_input_cb_t emac_link_input_cb; /**< Callback for incoming data */
emac_link_state_change_cb_t emac_link_state_cb; /**< Link state change callback */
bool connect_sts;
int link_mode_last;
rtos::Thread recvThread;
int phy_task_handle; /**< Handle for phy task event */

static void _recv_callback(void);
void recv_callback(void);
void recv_task(void);
void phy_task(void);

};

#endif /* RZ_A1_EMAC_H */
Loading