/* 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 #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(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(); }