From 3d5ca66e7a4ea50a248fb203745f08c577fe5fbf Mon Sep 17 00:00:00 2001 From: xxq250 Date: Sat, 20 May 2023 12:46:01 +0800 Subject: [PATCH 01/33] Update .gitmodules subdomain --- .gitmodules | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitmodules b/.gitmodules index 389e23ce1..8d30f8a02 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,24 +1,24 @@ [submodule "Ubiquitous/RT-Thread_Fusion_XiUOS/rt-thread"] path = Ubiquitous/RT-Thread_Fusion_XiUOS/rt-thread - url = https://code.gitlink.org.cn/chunyexixiaoyu/rt-thread.git + url = https://www.gitlink.org.cn/chunyexixiaoyu/rt-thread.git [submodule "Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/k210/kendryte-sdk/kendryte-sdk-source"] path = Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/k210/kendryte-sdk/kendryte-sdk-source - url = https://code.gitlink.org.cn/chunyexixiaoyu/kendryte-sdk-source.git + url = https://www.gitlink.org.cn/chunyexixiaoyu/kendryte-sdk-source.git [submodule "Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/aiit-riscv64-board/kendryte-sdk/kendryte-sdk-source"] path = Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/aiit-riscv64-board/kendryte-sdk/kendryte-sdk-source - url = https://code.gitlink.org.cn/chunyexixiaoyu/kendryte-sdk-source.git + url = https://www.gitlink.org.cn/chunyexixiaoyu/kendryte-sdk-source.git [submodule "Ubiquitous/XiZi/fs/lwext4/lwext4_submodule"] path = Ubiquitous/XiZi/fs/lwext4/lwext4_submodule url = https://gitlink.org.cn/xuos/lwext4_filesystem_support_XiUOS.git [submodule "Ubiquitous/Nuttx_Fusion_XiUOS/nuttx"] path = Ubiquitous/Nuttx_Fusion_XiUOS/nuttx - url = https://code.gitlink.org.cn/wgzAIIT/incubator-nuttx.git + url = https://www.gitlink.org.cn/wgzAIIT/incubator-nuttx.git [submodule "Ubiquitous/Nuttx_Fusion_XiUOS/apps"] path = Ubiquitous/Nuttx_Fusion_XiUOS/apps - url = https://code.gitlink.org.cn/wgzAIIT/incubator-nuttx-apps.git + url = https://www.gitlink.org.cn/wgzAIIT/incubator-nuttx-apps.git [submodule "APP_Framework/Applications/webnet/WebNet_XiUOS"] path = APP_Framework/Applications/webnet/WebNet_XiUOS url = https://gitlink.org.cn/xuos/WebNet_XiUOS.git [submodule "Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/kendryte-sdk/kendryte-sdk-source"] path = Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/kendryte-sdk/kendryte-sdk-source - url = https://code.gitlink.org.cn/chunyexixiaoyu/kendryte-sdk-source.git + url = https://www.gitlink.org.cn/chunyexixiaoyu/kendryte-sdk-source.git From 3463c8515f921b09e6523627980e77b17ec31097 Mon Sep 17 00:00:00 2001 From: Liu_Weichao Date: Mon, 29 May 2023 16:58:22 +0800 Subject: [PATCH 02/33] feat add abstruction_mmu for XiZi_AIoT(support MmuInit\MmuSectionMap\MmuSectionUnmap\MmuTtbSwitch\MmuTransform) --- .../XiZi_AIoT/hardkernel/abstraction/Makefile | 2 +- .../hardkernel/abstraction/abstraction_mmu.c | 206 ++++++++++++++++++ .../hardkernel/abstraction/abstraction_mmu.h | 114 ++++++++++ .../XiZi_AIoT/hardkernel/abstraction/cache.c | 19 ++ .../XiZi_AIoT/hardkernel/abstraction/mmu.c | 0 .../memory/multiple-address-spaces/memory.c | 20 ++ 6 files changed, 360 insertions(+), 1 deletion(-) create mode 100755 Ubiquitous/XiZi_AIoT/hardkernel/abstraction/abstraction_mmu.c create mode 100644 Ubiquitous/XiZi_AIoT/hardkernel/abstraction/abstraction_mmu.h delete mode 100755 Ubiquitous/XiZi_AIoT/hardkernel/abstraction/mmu.c diff --git a/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/Makefile b/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/Makefile index 262587f99..c794367b8 100644 --- a/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/Makefile +++ b/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/Makefile @@ -1,3 +1,3 @@ -SRC_FILES := cache.c isr.c mmu.c +SRC_FILES := cache.c isr.c abstraction_mmu.c include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/abstraction_mmu.c b/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/abstraction_mmu.c new file mode 100755 index 000000000..6f37b7d6f --- /dev/null +++ b/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/abstraction_mmu.c @@ -0,0 +1,206 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file: abstraction_mmu.c +* @brief: the general management of system mmu +* @version: 3.0 +* @author: AIIT XUOS Lab +* @date: 2023/4/27 +* +*/ + +#include + +AbstractionMmu abstraction_mmu; + +volatile uint32_t global_L1_pte_table[4096]; + +/** + * @description: write cmd to CP15 register + * @param reg_type - CP15 register type + * @param val - ops val pointer + * @return + */ +static void MmuCp15Write(uint8_t reg_type, uint32_t *val) +{ + uint32_t write_val = *val; + switch (reg_type) { + case AM_MMU_CP15_TTBCR: + TTBCR_W(write_val); + AM_ISB; + case AM_MMU_CP15_TTBR0: + TTBR0_W(write_val); + AM_ISB; + default: + break; + } +} + +/** + * @description: read CP15 register from mmu + * @param reg_type - CP15 register type + * @param val - ops val pointer + * @return + */ +static void MmuCp15Read(uint8_t reg_type, uint32_t *val) +{ + uint32_t read_val = 0; + switch (reg_type) { + case AM_MMU_CP15_TTBCR: + TTBCR_R(read_val); + case AM_MMU_CP15_TTBR0: + TTBR0_R(read_val); + default: + break; + } + + *val = read_val; +} + +/** + * @description: write or read CP15 register to set mmu + * @param ops_type - CP15 write or read + * @param reg_type - CP15 register type + * @param val - ops val pointer + * @return + */ +static void MmuRegOps(uint8_t ops_type, uint8_t reg_type, uint32_t *val) +{ + switch (ops_type) { + case AM_MMU_CP15_WRITE: + MmuCp15Write(reg_type, val); + case AM_MMU_CP15_READ: + MmuCp15Read(reg_type, val); + default: + break; + } +} + +/** + * @description: Init abstraction_mmu function + * @param mmu - abstraction mmu pointer + * @param ttb_base - ttb base pointer + * @return success : 0 error : -1 + */ +static int _AbstractionMmuInit(AbstractionMmu *mmu, uint32_t *ttb_base) +{ + mmu_init(); + + return 0; +} + +/** + * @description: map L1 or L2 page table section + * @param mmu - abstraction mmu pointer + * @param section_size - section size + * @return success : 0 error : -1 + */ +static int _AbstractionMmuSectionMap(AbstractionMmu *mmu, uint32_t section_size) +{ + uint32_t vaddr_length = mmu->vaddr_end - mmu->vaddr_start + 1; + + mmu_map_l1_range(mmu->paddr_start, mmu->vaddr_start, vaddr_length, + mmu->mmu_memory_type, mmu->mmu_shareability, mmu->mmu_access); + + mmu->mmu_status = 1; + + return 0; +} + +/** + * @description: unmap L1 or L2 page table section + * @param mmu - abstraction mmu pointer + * @param vaddr_start - virtual address start + * @param vaddr_size - virtual address size + * @return success : 0 error : -1 + */ +static int _AbstractionMmuSectionUnmap(AbstractionMmu *mmu, uint32_t vaddr_start, uint32_t vaddr_size) +{ + uint32_t *l1_umap_ventry = mmu->ttb_vbase + (vaddr_start >> AM_MMU_L1_SECTION_SHIFT); + uint32_t vaddr_end = vaddr_start + vaddr_size - 1; + uint32_t umap_count = (vaddr_end >> AM_MMU_L1_SECTION_SHIFT) - (vaddr_start >> AM_MMU_L1_SECTION_SHIFT) + 1; + + while (umap_count) { + AM_DMB; + *l1_umap_ventry = 0; + AM_DSB; + + umap_count--; + l1_umap_ventry += (1 << AM_MMU_L1_SECTION_SHIFT);//1MB section + } + + AM_DSB; + CLEARTLB(0);//clear TLB data and configure + AM_DSB; + AM_ISB; + mmu->mmu_status = 0; + + return 0; +} + +/** + * @description: switch ttb base by re-write ttbr register + * @param mmu - abstraction mmu pointer + * @return success : 0 error : -1 + */ +static int _AbstractionMmuTtbSwitch(AbstractionMmu *mmu) +{ + uint32_t ttbr, ttbcr; + MmuRegOps(AM_MMU_CP15_READ, AM_MMU_CP15_TTBCR, &ttbcr); + + /* Set TTBR0 with inner/outer write back write allocate and not shareable, [4:3]=01, [1]=0, [6,0]=01 */ + ttbr = ((mmu->ttb_pbase & 0xFFFFC000UL) | 0x9UL); + /* enable TTBR0 */ + ttbcr = 0; + + AM_DSB; + MmuRegOps(AM_MMU_CP15_WRITE, AM_MMU_CP15_TTBR0, &ttbr); + MmuRegOps(AM_MMU_CP15_WRITE, AM_MMU_CP15_TTBCR, &ttbcr); + + return 0; +} + +/** + * @description: get physical address transformed from virtual address + * @param mmu - abstraction mmu pointer + * @param vaddr - virtual address pointer + * @param paddr - physical address pointer + * @return success : 0 error : -1 + */ +static int _AbstracktonMmuTransform(AbstractionMmu *mmu, uint32_t *vaddr, uint32_t *paddr) +{ + uint32_t virtualAddress = *vaddr; + + if (mmu->mmu_status) { + mmu_virtual_to_physical(virtualAddress, paddr); + } + + return 0; +} + +static struct AbstractionMmuDone mmu_done = { + .AbstractionMmuInit = _AbstractionMmuInit, + .AbstractionMmuSectionMap = _AbstractionMmuSectionMap, + .AbstractionMmuSectionUnmap = _AbstractionMmuSectionUnmap, + .AbstractionMmuTtbSwitch = _AbstractionMmuTtbSwitch, + .AbstracktonMmuTransform = _AbstracktonMmuTransform, +}; + +/** + * @description: init abstraciton mmu info when system start + * @return success : 0 error : -1 + */ +int SysInitAbstractionMmu(void) +{ + abstraction_mmu.mmu_done = &mmu_done; +} diff --git a/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/abstraction_mmu.h b/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/abstraction_mmu.h new file mode 100644 index 000000000..ce3c35d06 --- /dev/null +++ b/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/abstraction_mmu.h @@ -0,0 +1,114 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file: mmu.h +* @brief: the general management of system mmu +* @version: 3.0 +* @author: AIIT XUOS Lab +* @date: 2023/5/24 +* +*/ +#include +#include + +#define ARCH_ARM +#ifdef ARCH_ARM +/* ARM System Registers */ +#define AM_DSB __asm__ volatile("dsb" ::: "memory") +#define AM_DMB __asm__ volatile("dmb" ::: "memory") +#define AM_ISB __asm__ volatile("isb" ::: "memory") +#define AM_WFI __asm__ volatile("wfi" ::: "memory") +#define AM_BARRIER __asm__ volatile("":::"memory") +#define AM_WFE __asm__ volatile("wfe" ::: "memory") +#define AM_SEV __asm__ volatile("sev" ::: "memory") + +#define TTBR0_R(val) __asm__ volatile("mrc p15, 0, %0, c2, c0, 0" : "=r"(val)) +#define TTBR0_W(val) __asm__ volatile("mcr p15, 0, %0, c2, c0, 0" ::"r"(val)) + +#define TTBCR_R(val) __asm__ volatile("mrc p15, 0, %0, c2, c0, 2" : "=r"(val)) +#define TTBCR_W(val) __asm__ volatile("mcr p15, 0, %0, c2, c0, 2" ::"r"(val)) + +#define CLEARTLB(val) __asm__ volatile("mcr p15, 0, %0, c8, c7, 0" ::"r"(val)) +#endif + +#define AM_MMU_L1_PAGE_TABLE_SIZE (4 * 4096) +#define AM_MMU_L1_SECTION_SHIFT 20 + +typedef enum +{ + AM_MMU_CP15_WRITE = 0, + AM_MMU_CP15_READ, +}MmuCP15OpsType; + +typedef enum +{ + AM_MMU_CP15_TTBCR = 0, + AM_MMU_CP15_TTBR0, + AM_MMU_CP15_CLEARTLB, +}MmuCP15RegType; + +typedef enum +{ + AM_StronglyOrdered = 0, + AM_Device, + AM_OuterInner_WB_WA, + AM_OuterInner_WT, + AM_Noncacheable, +}MmuMemoryType; + +typedef enum +{ + AM_Noaccess = 0, + AM_Read_Write, + AM_Read, +}MmuAccess; + +typedef enum +{ + AM_Shareable = 1, + AM_Nonshareable = 0 +}MmuShareability; + +struct AbstractionMmuDone +{ + int (*AbstractionMmuInit)(AbstractionMmu *mmu, uint32_t *ttb_base); + int (*AbstractionMmuSectionMap)(AbstractionMmu *mmu, uint32_t section_size); + int (*AbstractionMmuSectionUnmap)(AbstractionMmu *mmu, uint32_t vaddr_start, uint32_t vaddr_size); + int (*AbstractionMmuTtbSwitch)(AbstractionMmu *mmu); + int (*AbstracktonMmuTransform)(AbstractionMmu *mmu, uint32_t *vaddr, uint32_t *paddr); +}; + +typedef struct +{ + uint32_t ttb_vbase; + uint32_t ttb_pbase; + + uint32_t vaddr_start; + uint32_t vaddr_end; + uint32_t paddr_start; + uint32_t paddr_end; + + uint32_t vpaddr_offset; + + uint32_t pte_attr; + uint32_t mmu_status; + + MmuMemoryType mmu_memory_type; + MmuAccess mmu_access; + MmuShareability mmu_shareability; + + struct AbstractionMmuDone *mmu_done; + + int lock; + int link_list; +}AbstractionMmu; \ No newline at end of file diff --git a/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/cache.c b/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/cache.c index 5af33edcf..4a71e6c30 100755 --- a/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/cache.c +++ b/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/cache.c @@ -1,4 +1,23 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ +/** +* @file: cache.c +* @brief: the general management of system cache +* @version: 3.0 +* @author: AIIT XUOS Lab +* @date: 2023/4/27 +* +*/ void InvalidInsCache() { diff --git a/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/mmu.c b/Ubiquitous/XiZi_AIoT/hardkernel/abstraction/mmu.c deleted file mode 100755 index e69de29bb..000000000 diff --git a/Ubiquitous/XiZi_AIoT/softkernel/memory/multiple-address-spaces/memory.c b/Ubiquitous/XiZi_AIoT/softkernel/memory/multiple-address-spaces/memory.c index e69de29bb..eb735195b 100644 --- a/Ubiquitous/XiZi_AIoT/softkernel/memory/multiple-address-spaces/memory.c +++ b/Ubiquitous/XiZi_AIoT/softkernel/memory/multiple-address-spaces/memory.c @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file: memory.c +* @brief: the general management of system memory +* @version: 3.0 +* @author: AIIT XUOS Lab +* @date: 2023/4/27 +* +*/ \ No newline at end of file From b7797340610bcc5fdc6416c3412ebc2c919d5b67 Mon Sep 17 00:00:00 2001 From: pgh_dd <1041315949@qq.com> Date: Tue, 25 Jul 2023 20:02:06 +0800 Subject: [PATCH 03/33] "modbustcp_20230725" --- APP_Framework/Applications/app_test/Kconfig | 5 +- APP_Framework/Applications/app_test/Makefile | 4 + .../app_test/test_modbus_tcp/Makefile | 11 + .../app_test/test_modbus_tcp/modbus_tcp.c | 729 ++++++++++++++++++ .../app_test/test_modbus_tcp/modbus_tcp.h | 141 ++++ .../test_modbus_tcp/test_modbus_tcp.c | 248 ++++++ 6 files changed, 1137 insertions(+), 1 deletion(-) create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/Makefile create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/modbus_tcp.c create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/modbus_tcp.h create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/test_modbus_tcp.c diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index 3a103a6b3..55f0ad243 100644 --- a/APP_Framework/Applications/app_test/Kconfig +++ b/APP_Framework/Applications/app_test/Kconfig @@ -239,6 +239,9 @@ menu "test app" menuconfig USER_TEST_TIMER bool "Config test soft timer" default n - + + menuconfig USER_TEST_MODBUS_TCP + bool "Config test modbus_tcp" + default n endif endmenu diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 200e9e02a..b4408d781 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -101,5 +101,9 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) SRC_FILES += test_timer.c endif + ifeq ($(CONFIG_USER_TEST_MODBUS_TCP),y) + SRC_DIR += test_modbus_tcp + endif + include $(KERNEL_ROOT)/compiler.mk endif diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/Makefile b/APP_Framework/Applications/app_test/test_modbus_tcp/Makefile new file mode 100644 index 000000000..34093f67d --- /dev/null +++ b/APP_Framework/Applications/app_test/test_modbus_tcp/Makefile @@ -0,0 +1,11 @@ +ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) +SRC_FILES := modbus_tcp.c test_modbus_tcp.c +include $(KERNEL_ROOT)/compiler.mk +endif + +include $(KERNEL_ROOT)/.config +ifeq ($(CONFIG_ADD_NUTTX_FEATURES),y) + include $(APPDIR)/Make.defs + CSRCS += modbus_tcp.c test_modbus_tcp.c + include $(APPDIR)/Application.mk +endif diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/modbus_tcp.c b/APP_Framework/Applications/app_test/test_modbus_tcp/modbus_tcp.c new file mode 100644 index 000000000..7e7f57ee4 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_modbus_tcp/modbus_tcp.c @@ -0,0 +1,729 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/* + * @Description:包含modbusTCP请求报文的包装解析,以及报文收发,和每种功能码对应操作的实现。 + * @Version: V1.0.0 + * @Author: pgh_dd 1041315949@qq.com + * @Date: 2023-05-24 04:00:02 + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 16:36:53 + */ + + + +#include"modbus_tcp.h" + +u16_t Func0x_response_length[20]= +{ + 0,9,9,9,9,12,12,0,0,0,0,0,0,0,0,12,12 +}; + + +/** + * @description: 初始化功能码解析器 + * @param {MbParserType*}mbp {u8_t} type + * @return {void} + * @Date: 2023-07-25 16:59:23 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +void MbparserInit(MbParserType*mbp,u8_t type) +{ + mbp->func_set[R_RW_COIL]=FuncReadRwCoilX01; + mbp->func_set[R_R_COIL]=FuncReadRCoilX02; + mbp->func_set[R_RW_REG]=FuncReadRwRegX03; + mbp->func_set[R_R_REG]=FuncReadRRegX04; + mbp->func_set[W_RW_COIL]=FuncWriteRwCoilX05; + mbp->func_set[W_RW_REG]=FuncWriteRwRegX06; + mbp->func_set[W_MRW_REG]=FuncWriteRwMregsX10; + mbp->func_set[W_MRW_COIL]=FuncWriteRwMcoilsX0f; + mbp->func_set[REPORT_SlAVE_ID]=FuncReportSlaveIDX11; +}; + + +/** + * @description:初始化存储区 + * @return {int} + * @Date: 2023-07-25 17:01:47 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +int MbMemoryInit(MBmemoryType*mbm) +{ + mbm->rcoil_mem=(coils8_t*)malloc(sizeof(coils8_t)*RCOILMEM); + mbm->rreg_mem=(reg_t*)malloc(sizeof(reg_t)*RREGMEM); + mbm->rwcoil_mem=(coils8_t*)malloc(sizeof(coils8_t)*RWCOILMEM); + mbm->rwreg_mem=(reg_t*)malloc(sizeof(reg_t)*RWREGMEM); + + memset(mbm->rwreg_mem,0,RWREGMEM); + memset(mbm->rreg_mem,0,RREGMEM); + memset(mbm->rcoil_mem,0,RCOILMEM); + memset(mbm->rwcoil_mem,0,RWCOILMEM); + + // mbm->rwreg_mem[0]=3; + // mbm->rwreg_mem[1]=0x30ff; + mbm->rwcoil_mem[0]=1;mbm->rwcoil_mem[2]=1;mbm->rwcoil_mem[4]=1; + + if(mbm->rcoil_mem==NULL||mbm->rreg_mem==NULL||mbm->rwcoil_mem==NULL||mbm->rwreg_mem==NULL) + { + lw_error("memory is not full\n"); + return -1; + } + return 0; +} + +/** + * @description: 释放存储区 + * @return {void} + * @Date: 2023-07-25 17:02:22 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +void MbMemoryFree(MBmemoryType*mbm) +{ + free(mbm->rcoil_mem); + free(mbm->rreg_mem); + free(mbm->rwcoil_mem); + free(mbm->rwreg_mem); +} + +/** + * @description: 创建tcp通信套接字 + * @param {int} port + * @return {int} + * @Date: 2023-07-25 17:02:39 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +int CreateSocket(int port) +{ + int fd = -1, clientfd; + fd = socket(AF_INET, SOCK_STREAM, 0); + if(fd==-1)return -1; + int recv_len; + char *recv_buf; + struct sockaddr_in tcp_addr; + socklen_t addr_len; + + tcp_addr.sin_family = AF_INET; + tcp_addr.sin_addr.s_addr = INADDR_ANY; + tcp_addr.sin_port = htons(port); + memset(&(tcp_addr.sin_zero), 0, sizeof(tcp_addr.sin_zero)); + + if (bind(fd, (struct sockaddr *)&tcp_addr, sizeof(struct sockaddr)) == -1) { + lw_error("Unable to bind\n"); + close(fd); + return -1; + } + + lw_print("tcp bind success, start to receive.\n"); + lw_notice("\nLocal Port:%d\n", port); + + // setup socket fd as listening mode + if (listen(fd,128) != 0 ) { + lw_error("Unable to listen\n"); + close(fd); + return -1; + } + lw_print("Tcp start to listen.\n"); + return fd; +} + +/** + * @description: 读取请求报文的MBAP头部分 + * @param {int} fd {MbapType*}mbap + * @return {} + * @Date: 2023-07-25 17:03:04 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +int ReadMbtcpMBAP(int fd,MbapType*mbap) +{ + char buf[MODBUS_MBAP]; + read(fd,buf,MODBUS_MBAP); + mbap->tid=(((u16_t)buf[0])<<8)+(u16_t)buf[1];//高位左移8位再加低位 + mbap->pid=(((u16_t)buf[2])<<8)+(u16_t)buf[3]; + mbap->len=(((u16_t)buf[4])<<8)+(u16_t)buf[5]; + mbap->uid=((u8_t)buf[6]); + +}; + +/** + * @description: 读取请求报文的PDU部分 + * @param {int} fd {PduType*}pdu + * @return {} + * @Date: 2023-07-25 17:03:04 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +int ReadMbtcpPDU(int fd,PduType*pdu) +{ + char buf[MODBUS_PDUHEAD]; + + int n=read(fd,buf,MODBUS_PDUHEAD); + + + pdu->func=(u8_t)buf[0];//高位左移8位再加低位 + + if(n>3) + { + pdu->addr=(((u16_t)buf[1])<<8)+(u16_t)buf[2]; + pdu->operand1=(u8_t)buf[3]; + pdu->operand2=(u8_t)buf[4]; + } +}; + +/** + * @description: 制作响应报文 + * @param {MbapType*mbap,PduType*pdu,u8_t**resp,u16_t buf_len} + * @return {} + * @Date: 2023-07-25 17:11:23 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +void MakeResponse(MbapType*mbap,PduType*pdu,u8_t**resp,u16_t buf_len) +{ + buf_len-=6; + //这个长度实际是从数据长度位置到结束的长度,因此要将响应报文的总长减去事务头、协议、长度三个两字节数共6个 + (*resp)[0]=(u8_t)(mbap->tid>>8);(*resp)[1]=(u8_t)(0xff&mbap->tid); + (*resp)[2]=(u8_t)(mbap->pid>>8);(*resp)[3]=(u8_t)(0xff&mbap->pid); + (*resp)[4]=(u8_t)(buf_len>>8);(*resp)[5]=(u8_t)(0xff&buf_len); + (*resp)[6]=mbap->uid; + (*resp)[7]=pdu->func; +} + + +/** + * @description: 功能码0x1,负责读取从设备线圈的状态 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {} + * @Date: 2023-07-25 17:12:00 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +int FuncReadRwCoilX01(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x01\n"); + printf("coils num:%d\n"); + u16_t coils_num=((u16_t)pdu->operand1<<8)+(u16_t)pdu->operand2;//线圈个数 + u16_t bytes_num=(coils_num/8+((coils_num%8)!=0));//输出的字节长度 + u16_t buf_len=Func0x_response_length[R_RW_COIL]+bytes_num;//response报文所占长度 + + *resp=(u8_t*)malloc(buf_len); memset(*resp,0,buf_len); + MakeResponse(mbap,pdu,resp,buf_len); + (*resp)[8]=(u8_t)bytes_num; + u8_t*sub_mem=mem->rwcoil_mem; + for(int i=0;iaddr]==1?((u8_t)1<<(i%8)):0); + } + + return buf_len; + // u8_t +}; + +/** + * @Description: 0x2功能码,读离散输入状态 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {} + * @Date: 2023-07-25 17:20:13 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncReadRCoilX02(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x02\n"); + + u16_t coils_num=((u16_t)pdu->operand1<<8)+(u16_t)pdu->operand2;//线圈个数 + u16_t bytes_num=(coils_num/8+((coils_num%8)!=0));//输出的字节长度 + u16_t buf_len=Func0x_response_length[R_RW_COIL]+bytes_num;//response报文所占长度 + + *resp=(u8_t*)malloc(buf_len); memset(*resp,0,buf_len); + MakeResponse(mbap,pdu,resp,buf_len); + (*resp)[8]=(u8_t)bytes_num; + u8_t*sub_mem=mem->rcoil_mem; + for(int i=0;iaddr]==1?((u8_t)1<<(i%8)):0); + } + + return buf_len; +}; + + +/** + * @Description: 读保持寄存器 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {} + * @Date: 2023-07-25 17:21:01 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncReadRwRegX03(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x03\n"); + u16_t data_len=((u16_t)pdu->operand1<<8)+(u16_t)pdu->operand2; + + u16_t buf_len=Func0x_response_length[R_RW_REG]+data_len*2; + *resp=(u8_t*)malloc(buf_len); + + MakeResponse(mbap,pdu,resp,buf_len); + + (*resp)[8]=(u8_t)data_len*2; + + u16_t*sub_mem=mem->rwreg_mem; + + u16_t addr_mem=pdu->addr,addr_resp=9; + int c=0; + while(c>8); + (*resp)[addr_resp+1]=(u8_t)(sub_mem[addr_mem]&0xff); + addr_resp+=2;addr_mem++; + c++; + } + + return buf_len; +}; + +/** + * @Description: 0x4功能码,读输入寄存器 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {} + * @Date: 2023-07-25 17:21:22 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncReadRRegX04(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x04\n"); + u16_t data_len=((u16_t)pdu->operand1<<8)+(u16_t)pdu->operand2; + + u16_t buf_len=Func0x_response_length[R_R_REG]+data_len*2; + *resp=(u8_t*)malloc(buf_len); + + MakeResponse(mbap,pdu,resp,buf_len); + + (*resp)[8]=(u8_t)data_len*2; + + u16_t*sub_mem=mem->rreg_mem; + + u16_t addr_mem=pdu->addr,addr_resp=9; + + int c=0; + while(c>8); + (*resp)[addr_resp+1]=(u8_t)(sub_mem[addr_mem]&0xff); + addr_resp+=2;addr_mem++; + c++; + } + + return buf_len; +}; + +/** + * @Description: 0x5功能码,写单个线圈 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {int} + * @Date: 2023-07-25 17:21:51 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncWriteRwCoilX05(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x05\n"); + u16_t addr_coil=pdu->addr; + u16_t operand=(((u16_t)pdu->operand1)<<8)+(u16_t)pdu->operand2; + if(operand==0xff00||operand==0x0000) + { + mem->rwcoil_mem[addr_coil]=(operand==0xff00?1:0); + } + + u16_t buf_len=Func0x_response_length[W_RW_COIL]; + *resp=(u8_t*)malloc(buf_len); + + MakeResponse(mbap,pdu,resp,buf_len); + + (*resp)[8]=(u8_t)((pdu->addr)>>8); + (*resp)[9]=(u8_t)(pdu->addr&0xff); + (*resp)[10]=(u8_t)(pdu->operand1); + (*resp)[11]=(u8_t)(pdu->operand2); + + printf("write data:%x\n",operand); + + return buf_len; + +}; + +/** + * @Description: 0x6功能码,写单个保持寄存器 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {int} + * @Date: 2023-07-25 17:22:38 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncWriteRwRegX06(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x06\n"); + u16_t addr_reg=pdu->addr; + u16_t reg_data=(u16_t)((pdu->operand1)<<8)+(u16_t)(pdu->operand2); + mem->rwreg_mem[addr_reg]=reg_data; + u16_t buf_len=Func0x_response_length[W_RW_REG]; + *resp=(u8_t*)malloc(buf_len); + + MakeResponse(mbap,pdu,resp,buf_len); + + (*resp)[8]=(u8_t)((pdu->addr)>>8); + (*resp)[9]=(u8_t)(pdu->addr&0xff); + (*resp)[10]=(u8_t)(pdu->operand1); + (*resp)[11]=(u8_t)(pdu->operand2); + + printf("write data:%x\n",reg_data); + return buf_len; +}; + +/** + * @Description: 0xf功能码,写多个线圈 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {int} + * @Date: 2023-07-25 17:23:16 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncWriteRwMcoilsX0f(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x0f\n"); + u16_t coils_num=(((u16_t)(pdu->operand1))<<8)+(u16_t)(pdu->operand2); + + u16_t res_num=coils_num/8+(coils_num%8!=0)+1; + + u8_t*recv_buf=(u8_t*)malloc(sizeof(u8_t)*res_num); + + int n=read(fd,(char*)recv_buf,res_num); + + + u8_t*sub_mem=mem->rwcoil_mem; + + for(int i=0;iaddr+i]=((recv_buf[1+i/8]&(1<<(i%8)))==0?0:1); + } + + + u16_t buf_len=Func0x_response_length[W_MRW_COIL]; + *resp=(u8_t*)malloc(buf_len); + + MakeResponse(mbap,pdu,resp,buf_len); + + (*resp)[8]=(u8_t)((pdu->addr)>>8); + (*resp)[9]=(u8_t)(pdu->addr&0xff); + (*resp)[10]=(u8_t)(pdu->operand1); + (*resp)[11]=(u8_t)(pdu->operand2); + + free(recv_buf); + + + + return buf_len; +}; + +/** + * @Description: 0x10功能码,写多个保持寄存器 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {int} + * @Date: 2023-07-25 17:23:56 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncWriteRwMregsX10(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x10\n"); + u16_t data_num=(((u16_t)(pdu->operand1))<<8)+(u16_t)(pdu->operand2); + u16_t res_num=data_num*2+1; + u8_t*recv_buf=(u8_t*)malloc(sizeof(u8_t)*res_num); + + + int n=read(fd,(char*)recv_buf,res_num); + + // printf("%x %x %x\n",recv_buf[0],recv_buf[1],recv_buf[2]); + + u16_t*sub_mem=mem->rwreg_mem; + + for(int i=0;iaddr+i]=(((u16_t)(recv_buf[1+i*2]))<<8)+(u16_t)(recv_buf[1+i*2+1]); + } + + + u16_t buf_len=Func0x_response_length[W_MRW_REG]; + *resp=(u8_t*)malloc(buf_len); + + MakeResponse(mbap,pdu,resp,buf_len); + + (*resp)[8]=(u8_t)((pdu->addr)>>8); + (*resp)[9]=(u8_t)(pdu->addr&0xff); + (*resp)[10]=(u8_t)(pdu->operand1); + (*resp)[11]=(u8_t)(pdu->operand2); + + + free(recv_buf); + + return buf_len; +}; + +/** + * @Description: 报告从设备id + * @param {int} fd + * @return {} + * @Date: 2023-07-25 17:24:43 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncReportSlaveIDX11(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x11\n"); + +}; + +/** + * @Description: 发送响应报文 + * @param {int} fd + * @param {u16_t} n + * @return {} + * @Date: 2023-07-25 17:24:55 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int SendResponse(int fd,u8_t**buf,u16_t n) +{ + printf("Response:"); + for(int i=0;iuid);printf("%d",mbap->uid);printf("\n"); + + printf("Please input function code:"); + scanf("%x",&pdu->func);printf("%x",pdu->func);printf("\n"); + + printf("The address:"); + scanf("%d",&pdu->addr);printf("%d",pdu->addr);printf("\n"); + + switch (pdu->func) + { + case R_RW_COIL: + case R_R_COIL:printf("The number of coils you want read:");break; + case R_RW_REG: + case R_R_REG:printf("The number of registers you want read:");break; + + case W_RW_COIL:printf("The value of coil you want write(1 or 0):");break; + case W_RW_REG:printf("The value of register you want write:");break; + + case W_MRW_COIL:printf("The number of coils you want write:");break; + case W_MRW_REG:printf("The number of registers you want write:");break; + + default: + break; + } + u16_t num; + scanf("%d",&num);printf("%d\n",num); + if(pdu->func==W_RW_COIL) + { + pdu->operand2=0; + pdu->operand1=(num==1?0xff:0); + } + else { + pdu->operand1=(u8_t)(num>>8); + pdu->operand2=(u8_t)(num&0x00ff); + } + + + if(pdu->func==W_MRW_REG) + { + send_buf_length=num*2+1+12; + *request=(u8_t*)malloc(send_buf_length); + memset(*request,0,send_buf_length); + printf("input register data(decimal integer,in total %d):",num); + (*request)[12]=(u8_t)(num*2); + for(int i=0;i>8); + (*request)[i*2+13+1]=(u8_t)(tem&0x00ff); + } + printf("\n"); + } + else if(pdu->func==W_MRW_COIL) + { + send_buf_length=num/8+(num%8!=0)+1+12; + *request=(u8_t*)malloc(send_buf_length); + memset(*request,0,send_buf_length); + printf("input coil data(0 or 1,in total %d):",num); + (*request)[12]=(u8_t)(num/8+(num%8!=0)); + for(int i=0;iaddr]==1?((u8_t)1<<(i%8)):0); + } + } + printf("\n"); + } + else + { + send_buf_length=12; + *request=(u8_t*)malloc(send_buf_length); + if((*request)==NULL)printf("erro\n"); + + } + + (*request)[0]=(u8_t)(mbap->tid>>8);(*request)[1]=(u8_t)(0xff&mbap->tid); + (*request)[2]=(u8_t)(mbap->pid>>8);(*request)[3]=(u8_t)(0xff&mbap->pid); + (*request)[4]=(u8_t)((send_buf_length-6)>>8);(*request)[5]=(u8_t)(0xff&(send_buf_length-6)); + (*request)[6]=mbap->uid; + (*request)[7]=pdu->func; + (*request)[8]=(u8_t)(pdu->addr>>8);(*request)[9]=(u8_t)(0xff&pdu->addr); + (*request)[10]=pdu->operand1;(*request)[11]=pdu->operand2; + + printf("messege is:"); + for(int i=0;ifunc,1);//读取功能码 + + u8_t byte_num=0; + u8_t *recv_buf; + switch (pdu->func) + { + case R_R_COIL: + case R_R_REG: + case R_RW_COIL: + case R_RW_REG: + data_num=(((u16_t)(pdu->operand1))<<8)+(u16_t)(pdu->operand2); + read(fd,&byte_num,1); + recv_buf=(u8_t*)malloc(byte_num); + read(fd,recv_buf,byte_num); + /* code */ + break; + case W_RW_COIL: + case W_RW_REG: + case W_MRW_REG: + case W_MRW_COIL: + recv_buf=(u8_t*)malloc(4); + read(fd, recv_buf,4); + break; + default: + break; + } + + printf("Response: TID:%x func code:%x byte num:%x\n",mbap->tid,pdu->func,byte_num); + + if(pdu->func==R_R_COIL||pdu->func==R_RW_COIL) + { + printf("coils:"); + for(int i=0;ifunc==R_R_REG||pdu->func==R_RW_REG) + { + printf("registers:"); + // for(int i=0;i +#include +#include +#include +#include +#include "lwip/sys.h" + +#define PORT 8888 +#define RCOILMEM 1000 +#define RREGMEM 1000 +#define RWCOILMEM 1000 +#define RWREGMEM 1000 + +#define MBTCP 1 +#define MBRTU 2 +//功能码 +#define R_RW_COIL 0x01 +#define R_R_COIL 0x02 +#define R_RW_REG 0x03 +#define R_R_REG 0x04 +#define W_RW_COIL 0x05 +#define W_RW_REG 0x06 +#define DIAGNOSTIC 0x08 +#define GET_COUNTER 0x0B +#define W_MRW_COIL 0x0F +#define W_MRW_REG 0x10 +#define REPORT_SlAVE_ID 0x11 + + +typedef u8_t coils8_t; +typedef u16_t reg_t; + +#define MODBUS_MBAP 7 +#define MODBUS_PDUHEAD 5 + + +//定义存储区结构体 +typedef struct MbMemory +{ + coils8_t*rcoil_mem; + reg_t*rreg_mem; + coils8_t*rwcoil_mem; + reg_t*rwreg_mem; +}MBmemoryType; +//初始化存储区的函数 +int MbMemoryInit(MBmemoryType*mb); +//释放存储区 +void MbMemoryFree(MBmemoryType*mb); + + +//协议的固定部分为12个字节,当功能码为写多个数据时,后续还有不定长的数据部分 +typedef struct mbap +{ + //MbapType + u16_t tid; + u16_t pid; + u16_t len; + u8_t uid; + + /* data */ +}MbapType; + +typedef struct pdu +{ + u8_t func; + + u16_t addr; + + u8_t operand1; + u8_t operand2; + /* data */ +}PduType; + +int CreateSocket(int port); + +int ReadMbtcpMBAP(int fd,MbapType*mb_s); +int ReadMbtcpPDU(int fd,PduType*mb_s); + + +//操作函数 + +int FuncReadRwCoilX01(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReadRCoilX02(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReadRwRegX03(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReadRRegX04(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwCoilX05(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwRegX06(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwMcoilsX0f(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwMregsX10(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); + +int FuncReportSlaveIDX11(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +//位地址转换字节地址 + +//定义解析器结构体 +typedef struct mbparser +{ + int (*func_set[20])(MBmemoryType*,int,MbapType*,PduType*,u8_t**resp); +}MbParserType; +//解析器初始化,实际上就是每个功能码对应的操作函数的注册 +void MbparserInit(MbParserType*mbp,u8_t flag); + +void MakeResponse(MbapType*,PduType*,u8_t**,u16_t); + +int SendResponse(int fd,u8_t**buf,u16_t n); + + +// void func(PDU*pdu); + +// void delete_modbus_request(); +//主机程序 +int GenerateModbusRequest(MbapType*,PduType*,u8_t flag,u8_t**request); + +void SendModbus(int fd,u8_t**request,int n); + +void GetRequest(int fd,MbapType*,PduType*); +#endif diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/test_modbus_tcp.c b/APP_Framework/Applications/app_test/test_modbus_tcp/test_modbus_tcp.c new file mode 100644 index 000000000..e2376e9e0 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_modbus_tcp/test_modbus_tcp.c @@ -0,0 +1,248 @@ +/* +* Copyright (c) 2022 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file lwip_tcp_socket_demo.c +* @brief TCP socket demo based on LwIP +* @version 1.0 +* @author AIIT XUOS Lab +* @date 2022-03-21 +*/ + +#include +#include"modbus_tcp.h" + +#ifdef ADD_XIZI_FEATURES +#include +#include +#include "lwip/sys.h" +#endif + +#ifdef ADD_NUTTX_FEATURES +#include +#include +#include +#include "stdio.h" +#endif + +#define TCP_DEMO_BUF_SIZE 65535 +#define TCP_DEMO_SEND_TIMES 20 +#define LWIP_TCP_DEMO_TASK_STACK_SIZE 4096 +#define LWIP_TCP_DEMO_TASK_PRIO 20 + +static pthread_t tcp_client_task; +static pthread_t tcp_server_task; + +static char tcp_demo_ipaddr[] = {192, 168, 31, 77}; +static char tcp_demo_netmask[] = {255, 255, 255, 0}; +static char tcp_demo_gwaddr[] = {192, 168, 31, 1}; + +#ifdef ADD_NUTTX_FEATURES +#define lw_print printf +#define lw_notice printf +#define lw_error printf + +#define LWIP_DEMO_TIMES 3 +#define LWIP_TARGET_PORT 4840 +#endif + +static uint16_t tcp_socket_port = 8888; +static char tcp_ip_str[128] = {0}; + +/******************************************************************************/ +static void TcpSocketConfigParam(char *ip_str) +{ + int ip1, ip2, ip3, ip4, port = 0; + + if(ip_str == NULL) + return; + + if(sscanf(ip_str, "%d.%d.%d.%d:%d", &ip1, &ip2, &ip3, &ip4, &port)) { + printf("config ip %s port %d\n", ip_str, port); + strcpy(tcp_ip_str, ip_str); + if(port) + tcp_socket_port = port; + return; + } + + if(sscanf(ip_str, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4)) { + printf("config ip %s\n", ip_str); + strcpy(tcp_ip_str, ip_str); + } +} + +static void *ModbusTcpServer(void *arg) +{ + + u8_t uid=1;//定义从设备id和存储区 + + MBmemoryType mbm;//定义存储区 + if(MbMemoryInit(&mbm)==-1)//初始化存储区,包括对四个存储区进行内存分配 + { + return 0; + }; + + MbParserType mb_parser;//初始化功能码解析器 + MbparserInit(&mb_parser,MBTCP);//初始化解析器,将功能码对应函数注册 + + int fd=CreateSocket(PORT);//创建监听套接字 + if(fd==-1)return 0; + + int recv_len; + char *recv_buf; + struct sockaddr_in tcp_addr; + socklen_t addr_len; + + while(1) + { + struct sockaddr_in tcp_addr; + socklen_t addr_len; + + printf("wait accept\n"); + int clientfd = accept(fd, (struct sockaddr *)&tcp_addr, (socklen_t*)&addr_len); + + if(clientfd==-1) + { + lw_error("Unable to listen\n"); + return 0; + } + + while(1) + { + MbapType mbap; + PduType pdu; + ReadMbtcpMBAP(clientfd,&mbap);//读取数据前7字节为mbap初始化 + + if(mbap.uid!=uid){//检验是否为此从机 + close(clientfd); + break; + } + + + ReadMbtcpPDU(clientfd,&pdu);//读取pdu和一些定长部分 + + printf("OP:%x\n",pdu.func); + printf("ADDR:%x\n",pdu.addr); + + u8_t* response_buf;//定义操作返回的指针 + u8_t buf_len=mb_parser.func_set[pdu.func](&mbm,clientfd,&mbap,&pdu,&response_buf); + + SendResponse(clientfd,&response_buf,buf_len); + // return NULL; + //执行操作 + + } + close(clientfd); + } + close(fd); + MbMemoryFree(&mbm);//释放存储区 +} + +void Test_ModbusTcpServer(int argc, char *argv[]) +{ + if(argc >= 2) { + lw_print("lw: [%s] target ip %s\n", __func__, argv[1]); + TcpSocketConfigParam(argv[1]); + } + +#ifdef ADD_XIZI_FEATURES + lwip_config_tcp(0, tcp_demo_ipaddr, tcp_demo_netmask, tcp_demo_gwaddr); + +#endif + +#ifdef ADD_NUTTX_FEATURES + pthread_attr_t attr = PTHREAD_ATTR_INITIALIZER; + attr.priority = LWIP_TCP_DEMO_TASK_PRIO; + attr.stacksize = LWIP_TCP_DEMO_TASK_STACK_SIZE; +#endif + + ModbusTcpServer(NULL); +} + +PRIV_SHELL_CMD_FUNCTION(Test_ModbusTcpServer, a modbusS test sample, PRIV_SHELL_CMD_MAIN_ATTR); + +static void *ModbusTcpClient(void *arg) +{ + u16_t counter=0; + int fd = -1; + int ret; + + // lw_print("2023-05-27 Peng Guanhua\n"); + lw_print("%s start\n", __func__); + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + lw_print("Socket error\n"); + return NULL; + } + + char tcp_ip_str[128]="192.168.31.148"; + u16_t tcp_socket_port=6000; + + + printf("%s\n",tcp_ip_str); + struct sockaddr_in tcp_sock; + tcp_sock.sin_family = AF_INET; + tcp_sock.sin_port = htons(tcp_socket_port); + tcp_sock.sin_addr.s_addr = inet_addr(tcp_ip_str); + printf("%s\n",tcp_ip_str); + + memset(&(tcp_sock.sin_zero), 0, sizeof(tcp_sock.sin_zero)); + + ret = connect(fd, (struct sockaddr *)&tcp_sock, sizeof(struct sockaddr)); + + if (ret < 0) { + lw_print("Unable to connect %s:%d = %d\n", tcp_ip_str, tcp_socket_port, ret); + close(fd); + return NULL; + } + + lw_print("TCP connect %s:%d success, start.\n", tcp_ip_str, tcp_socket_port); + + + while (1) { + + MbapType mbap={counter,0,0,0}; + PduType pdu; + u8_t*request; + + int mesg_len=GenerateModbusRequest(&mbap,&pdu,MBTCP,&request); + SendModbus(fd,&request,mesg_len); + GetRequest(fd,&mbap,&pdu); + counter++; + } + + close(fd); + return NULL; +} + +void Test_ModbusTcpClient(int argc, char *argv[]) +{ + if(argc >= 2) { + lw_print("lw: [%s] target ip %s\n", __func__, argv[1]); + TcpSocketConfigParam(argv[1]); + } + +#ifdef ADD_XIZI_FEATURES + lwip_config_tcp(0, tcp_demo_ipaddr, tcp_demo_netmask, tcp_demo_gwaddr); + +#endif +#ifdef ADD_NUTTX_FEATURES + pthread_attr_t attr = PTHREAD_ATTR_INITIALIZER; + attr.priority = LWIP_TCP_DEMO_TASK_PRIO; + attr.stacksize = LWIP_TCP_DEMO_TASK_STACK_SIZE; +#endif + + ModbusTcpClient(NULL); +} +PRIV_SHELL_CMD_FUNCTION(Test_ModbusTcpClient, a modbustcpC test sample, PRIV_SHELL_CMD_MAIN_ATTR); + From 10482724a996925330008ea026fae30aef883601 Mon Sep 17 00:00:00 2001 From: pgh_dd <1041315949@qq.com> Date: Tue, 25 Jul 2023 22:10:16 +0800 Subject: [PATCH 04/33] =?UTF-8?q?"=E6=B7=BB=E5=8A=A0=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E6=96=87=E4=BB=B6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app_test/test_modbus_tcp/README.md | 340 ++++++++++++++++++ .../test_modbus_tcp/img/modbusTCP_C1.png | Bin 0 -> 28917 bytes .../test_modbus_tcp/img/modbusTCP_C2.png | Bin 0 -> 10857 bytes .../test_modbus_tcp/img/modbusTCP_C3.png | Bin 0 -> 10649 bytes .../test_modbus_tcp/img/modbusTCP_C4.png | Bin 0 -> 10950 bytes .../test_modbus_tcp/img/modbusTCP_C5.png | Bin 0 -> 10632 bytes .../test_modbus_tcp/img/modbusTCP_C6.png | Bin 0 -> 11141 bytes .../test_modbus_tcp/img/modbusTCP_S1.png | Bin 0 -> 27843 bytes .../test_modbus_tcp/img/modbusTCP_S2.png | Bin 0 -> 23644 bytes .../test_modbus_tcp/img/modbusTCP_S3.png | Bin 0 -> 16429 bytes .../test_modbus_tcp/img/modbusTCP_S4.png | Bin 0 -> 5522 bytes .../test_modbus_tcp/img/modbusTCP_S5.png | Bin 0 -> 2813 bytes .../test_modbus_tcp/img/modbusTCP_S6.png | Bin 0 -> 3461 bytes .../test_modbus_tcp/img/modbusTCP_S7.png | Bin 0 -> 10904 bytes 14 files changed, 340 insertions(+) create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/README.md create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C1.png create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C2.png create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C3.png create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C4.png create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C5.png create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C6.png create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S1.png create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S2.png create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S3.png create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S4.png create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S5.png create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S6.png create mode 100644 APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S7.png diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/README.md b/APP_Framework/Applications/app_test/test_modbus_tcp/README.md new file mode 100644 index 000000000..153cd6d86 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_modbus_tcp/README.md @@ -0,0 +1,340 @@ +# ##modbus-tcp## + +## 1. 简介 + +在xiuos平台实现modbusTCP协议,包括协议报文组装、解析和数据传输,支持主从通信。 + +## 2. 数据结构设计说明 + +### 2.1 数据结构定义 + +首先,需要定义设备存储区的结构体,包括两种存储类型,16位的寄存器和线圈。 + +```c +//定义存储区结构体 +typedef struct MbMemory +{ + coils8_t*rcoil_mem; + reg_t*rreg_mem; + coils8_t*rwcoil_mem; + reg_t*rwreg_mem; +}MBmemoryType; +``` + +​ 然后便是关于ModbusTCP协议相关的结构体定义,包括MBAP和PDU,后续数据区视情况而定,长短不固定。 + +```c +//协议的固定部分为12个字节,当功能码为写多个数据时,后续还有不定长的数据部分 +typedef struct mbap +{ + //MbapType + u16_t tid; + u16_t pid; + u16_t len; + u8_t uid; + + /* data */ +}MbapType; + +typedef struct pdu +{ + u8_t func; + + u16_t addr; + + u8_t operand1; + u8_t operand2; + /* data */ +}PduType; +``` + +### 2.1 从设备请求解析和响应部分 + +​ 主要定义请求的解析器结构,以及每种功能码对应的解析函数,和发送响应的函数。 + +```c +//定义解析器结构体 +typedef struct mbparser +{ + int (*func_set[20])(MBmemoryType*,int,MbapType*,PduType*,u8_t**resp); +}MbParserType; + +//功能码解析函数 +int FuncReadRwCoilX01(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReadRCoilX02(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReadRwRegX03(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReadRRegX04(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwCoilX05(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwRegX06(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwMcoilsX0f(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwMregsX10(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReportSlaveIDX11(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); + + +/** + * @description: 制作响应报文 + * @param {MbapType*mbap,PduType*pdu,u8_t**resp,u16_t buf_len} + * @return {} + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +void MakeResponse(MbapType*,PduType*,u8_t**,u16_t); + +/** + * @Description: 发送响应报文 + * @param {int} fd 套接字对应文件描述符 + * @param {u16_t} n 报文大小 + * @return {int} + * @Date: 2023-07-25 17:24:55 + * @Author: pgh_dd 1041315949@qq.com + */ +int SendResponse(int fd,u8_t**buf,u16_t n); + + +``` + +### 2.2 主设备的请求包装和发送部分 + +```c +/** + * @Description: 读取键盘输入,并生成请求报文 + * @param {u8_t} flag + * @return {int} + * @Date: 2023-07-25 17:25:26 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int GenerateModbusRequest(MbapType*,PduType*,u8_t flag,u8_t**request); + +/** + * @Description: 发送请求报文 + * @param {int fd,u8_t**request,int n} + * @return {} + * @Date: 2023-07-25 17:26:10 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +void SendModbus(int fd,u8_t**request,int n); + +/** + * @Description: 读取请求报文 + * @param {int fd,MbapType*mbap,PduType*pdu} + * @return {void} + * @Date: 2023-07-25 17:26:49 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +void GetRequest(int fd,MbapType*,PduType*); +``` + + + +## 3. 测试程序说明 + +​ modbusTCP协议基于TCP协议,因此其主从通信实际上是基于TCP的S/C通信,因此分为服务端和客户端。 + +​ 服务端部分(从设备)程序实际上是一个被动接受请求报文的TCP服务器程序,除了一些基础参数的定义外,主要包括一个无限循环的服务程序,包括报文的接收,以及对存储区的操作,和生成发送响应。 + +```c +static void *ModbusTcpServer(void *arg) +{ + //设置IP和子网掩码网关 + u8_t uid=1;//定义从设备id和存储区 + + MBmemory mbm;//定义存储区 + if(mb_memory_init(&mbm)==-1)//初始化存储区,包括对四个存储区进行内存分配 + { + return 0; + }; + + MBparser mb_parser;//初始化功能码解析器 + MBparser_init(&mb_parser,MBTCP);//初始化解析器,将功能码对应函数注册 + + int fd=create_socket(PORT);//创建监听套接字 + if(fd==-1)return 0; + + if (listen(fd, 10) != 0 ) { + lw_error("Unable to listen\n"); + close(fd); + return 0; + } + + while(1) + { + //建立连接,因为每次接受的连接可能不是同一个设备发来的,因此需要把建立连接部分放在循环体内。 + struct sockaddr_in tcp_addr; + socklen_t addr_len; + + printf("wait accept\n"); + int clientfd = accept(fd, (struct sockaddr *)&tcp_addr, (socklen_t*)&addr_len); + + if(clientfd==-1) + { + lw_error("Unable to listen\n"); + return 0; + } + + while(1) + { + MBAP mbap; + PDU pdu; + read_mbtcp_MBAP(clientfd,&mbap);//读取数据前7字节为mbap初始化 + + if(mbap.uid!=uid){//检验是否为此从机 + close(clientfd); + break; + } + + + read_mbtcp_PDU(clientfd,&pdu);//读取pdu和一些定长部分 + + printf("OP:%x\n",pdu.func); + printf("ADDR:%x\n",pdu.addr); + + u8_t** response_buf;//定义操作返回的指针 + u8_t buf_len=mb_parser.func_set[pdu.func](&mbm,clientfd,&mbap,&pdu,response_buf);//请求的解析和对存储区的操作 + + send_response(clientfd,response_buf,buf_len);//发送响应 + // return NULL; + //执行操作 + + } + close(clientfd); + } + close(fd); + mb_memory_free(&mbm);//释放存储区 +} + +``` + +​ 客户端部分(主设备)是一个主动发送请求的TCP客户端程序,主要包括一个接受键盘输入的循环体,可以接受用户输入的指令,然后包装成Modbus请求报文,并发送给ModbusTCP服务器,然后接受响应报文。 + +```c +static void *ModbusTcpClient(void *arg) +{ + u16_t counter=0; + int fd = -1; + int ret; + + // lw_print("2023-05-27 Peng Guanhua\n"); + lw_print("%s start\n", __func__); + + fd = socket(AF_INET, SOCK_STREAM, 0);//定义服务器套接字 + if (fd < 0) { + lw_print("Socket error\n"); + return NULL; + } + + char tcp_ip_str[128]="192.168.31.148";//服务器ip和端口号 + u16_t tcp_socket_port=6000; + + /*建立套接字连接*/ + printf("%s\n",tcp_ip_str); + struct sockaddr_in tcp_sock; + tcp_sock.sin_family = AF_INET; + tcp_sock.sin_port = htons(tcp_socket_port); + tcp_sock.sin_addr.s_addr = inet_addr(tcp_ip_str); + printf("%s\n",tcp_ip_str); + + memset(&(tcp_sock.sin_zero), 0, sizeof(tcp_sock.sin_zero)); + + ret = connect(fd, (struct sockaddr *)&tcp_sock, sizeof(struct sockaddr)); + + if (ret < 0) { + lw_print("Unable to connect %s:%d = %d\n", tcp_ip_str, tcp_socket_port, ret); + close(fd); + return NULL; + } + + lw_print("TCP connect %s:%d success, start.\n", tcp_ip_str, tcp_socket_port); + + + + while (1) { + + MBAP mbap={counter,0,0,0}; + PDU pdu; + u8_t*request; + + int mesg_len=generate_modbus_request(&mbap,&pdu,MBTCP,&request);//此函数中接收键盘输入,并生成请求报文。 + send_modbus(fd,&request,mesg_len);//发送请求报文。 + get_response(fd,&mbap,&pdu);//接收响应报文,并显示 + counter++; + } + + close(fd); + return NULL; +} +``` + + + +## 4. 运行结果 + +### 4.1 从设备通信测试 + +从设备测试,在终端上将TCP服务端程序打开,如图1,等待主设备的连接。 + +![alt 图1从设备等到主设备连接](./img/modbusTCP_S1.png) + +​ 主设备采用Modbus Poll应用程序,建立TCP连接,如图2所示。 + +![alt 图2主设备与从设备建立TCP连接](./img/modbusTCP_S2.png) + +​ 此时modbus poll程序便会不断的向从设备发送请求,如图3。 + +![alt 图3从设备接收从设备请求报文并响应](./img/modbusTCP_S3.png) + +​ 可以看到解析出的功能码、地址以及对应的响应报文,然后我们在modbus poll上修改一下存储区数据。如图4所示。 + +![alt 图4](./img/modbusTCP_S4.png) + +因为修改的是寄存器存储区的值,因此对应0x10功能码,然后看看从设备的反映。如图5所示。 + +![alt 图5](./img/modbusTCP_S6.png) + +可见成功收到功能码,并返回了响应的报文。 + +![alt 图6](./img/modbusTCP_S5.png) + +modbus poll显示响应成功,存储区已成功修改期望的值。 + +![alt 图7](./img/modbusTCP_S7.png) + +可见存储区已成功修改。 + +线圈部分的查询修改同理,不再赘述。 + +### 4.2主设备通信测试 + +​ 首先打开modbus slave应用程序,用以作为从设备,然后将存储区数据修改,用以测试,并打开TCP端口,等待主设备的连接,如图8所示。 + +![alt 图8](./img/modbusTCP_C2.png) + +​ 同样在终端打开从设备程序,从设备的ip,port在源码中已定义好,所以打开时已经连接上,如图9所示。 + + + +![alt 图9](./img/modbusTCP_C1.png) + +​ 开始输入从设备id,功能码,以及其他信息用以生成请求报文。 + +![alt 图10](./img/modbusTCP_C3.png) + +​ 如图10所示,输入功能码3,对应读取寄存器功能,地址从0开始,数量4,然后便会生成请求报文,然后发送,结果如图11所示。 + +![alt](./img/modbusTCP_C4.png) + +​ 可见,已成功查询到寄存器的值。 + +​ 然后测试写入功能,输入功能码15(0xf),对应写入多个线圈功能,如图12,modbus slave对应的响应结果如图13所示。 + +​ ![alt 图12](./img/modbusTCP_C5.png) + +​ 写入线圈的值为5个,分别为1 0 1 0 1。发送成功后,modbus salve中显示如图13所示。 + +![alt 图13](./img/modbusTCP_C6.png) + +​ 可见,已成功修改。 + +​ 其他功能码测试过程类似,不再赘述。 \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C1.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C1.png new file mode 100644 index 0000000000000000000000000000000000000000..5561b12c4df250ee4c36c30a1491b2940a2f9b80 GIT binary patch literal 28917 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%0D2LqLTud)7+Wu6;RVQ|ug(llB|r`qN%^yKkpU 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 literal 0 HcmV?d00001 diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C2.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C2.png new file mode 100644 index 0000000000000000000000000000000000000000..86ae5e38adabbc5f7886089e7f9404e45b8b3636 GIT binary patch literal 10857 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 JJT!a#e*kaDvQPj3 literal 0 HcmV?d00001 diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C3.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C3.png new file mode 100644 index 0000000000000000000000000000000000000000..89a1b8d9326fff0b4282b211b0f908a2a340e206 GIT binary patch literal 10649 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 b4w;@qIiF)5Q4z0PUeZ+6RjE+2`S?Eo4BRwJ literal 0 HcmV?d00001 diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C4.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C4.png new file mode 100644 index 0000000000000000000000000000000000000000..7f9af533d4e7576d8f4fa68ebd6d9fbc5758c1f7 GIT binary patch literal 10950 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 v=*M8Aex*8>FHn0B+s^s{{ literal 0 HcmV?d00001 diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C5.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C5.png new file mode 100644 index 0000000000000000000000000000000000000000..da53192850683f04010eb0fe258cc0a538fdad42 GIT binary patch literal 10632 zcmcI~cT^MYyKO9>BA_U}ML>#DMWjem=_2qcCG-FS(t9t7iu4jXQllUpgY=#V(mMi1 znn($uV?qst#lX@k38=rEA!60Gtaa4e)dHFrK>?l!%hPL0O+(d zRbK!A=UFM^vdh$z&mA^t4FKR4Kuh(pK|sz{Zd<~-!p$CRpfAFP>9db17!XrG6?2!1 zijK;+Ohw~y?pwFg68+In9}1q<(LFH-Y&|U(YG`-Hj$hGo(L9qlouc z!~5qP4gBc^-wGdrXjfY_L7RufkJC__+rPSjx$i@L2&`X%2XNP;m3I)Q0Iyp-Jg3Wh zk$2@rUpEtTi5y$ce@!51JpyHk=QPE*UYm-_Y-51e841M%j7so_*=t#b=X zwO*|c-w;4ZsR;W~E12?@@P4uA$E3L5)c_Rk zV6(L61lF(-<{+ze0@ZepN2e$`1VCz!DpT%!vjp&7vubAMS^F=)8dg!Td-1wfBQX4i_pjF?&T%IF0RO=`1 zB1!0XOhnM$Ke!E9OaS%nNM(uPnY=U^HT}8Mxv4_JTUl?{#WMX=n6nra0=L%P<};K($|72g$#O*X=<^y znnEEl#89~-R09J}P;>82u;(bfWl5?)jg^CmT-%Ge^i_O~^Xgl+#B%}KJ0J^?g7VvG zQq8a4pPz+T$d9ro6C%c$EzSb~?3y&x(6xh+sdQc05dG_jT{wsCmtK?ij3LAU7^8^2dvL9p<{?b^YJP~l zoK&u+x=%RKX&pZhdXo+EBCXvv`v3$3kM$ZDl1h`XiAK>bLAxq;%Tu!urXlsbE<&y^ zV85=yM{j`TwB}%?c2MATNALa5?kCK3IvbdoAq2gOy302bEg`1Z zGs*-24D-E^*cQDQASQxfq;*6P@Rum-%26s;MDvItkmZ(AHw}hz;~pvgY(Fb|kFd9x zul!r}4-4B|EzvagST5jF++!Ci@KvJbb;3&g)t90EbHd;2wt@yJ6k71H@5Jh`af>o1 zKyvx=d#_Y=xhJ0*H4=UWR2r~+-X*-?blu$yN#9kOhP|U~d*kEqZ(h${KD3h)3q5v9 z*|_EfLchEtVcO2*xgO;hsP0jcT#gCVwB6jh;9(*y_wDgEP2kp;3!Ao}A5-!WEZ}e{ zICU%HujbfMBgiDZc3SyLjFJbDYF~`*;n_JTux8ft>{#_`ZE_wVy=#qwz@CEbemQ(8 zyAsGJ{(GQR-0-`!wDzN?bCt#m=pCysd@uL6WcvjD&U2RECuC!8wlD?GUolZ5=hnlx zxaMIeO$ho;D@r}(;ZfB3?#F~A_l5<#)}NyTr(7Krs6iLJ*~#!uyewT>b3<7~k!JRa zD0Rx}jsy+0pZaxy1KonPx1Wp+7#aKFyLgSx^cn|H2RD0dcJac@%)orqzIwMCKb5-t zsC`|*LT$*>8~anp%rxaHUPpci6-oXkL$zQ^#>PFBmf|QD59!_yXAmutcCGEN{U_lH zOz_Ms5#iM7*gQj6?iQPPgo-0|Zq5JCI}%26H_g}p4Jq`yE@y|3q$`iwx3v2IN1Apj0+0N4D3dG!CAwp0^i*kt$&Y&)Q?G~)>=fy zef?nRW~T+*-4o1QHpXfpoY=zO6!kDg^u5l1Szp~|=L27OPF`+x#Oh>{KS>?k6;z&DB6XWr;?i)4wmTsTL`^t68FI=`Xdy_41E!jj67+hl!yhI5BRhh~@pM3b^L=ssv4rr%?` z9#2vy8|r(6bG2;bjid(}4G6rF?X|G0RLl~^6msy*WPbmU(R@N59cRJ)`k5gzCW7nz z#85!v{LTF!&$#OgWg(m`8wvjYPfRN>NM0iB?}soq@#yVpxJ}43ne9|?+o!a!b`nA$ zqNOrzU_Iij;Sq2AaaP3_!~OEF zkVqaihvu|(qy>Kr$R!Bc^ogV*ehn;9Qb;-T|3FZ-zY#R?9|To=Xw+mbEF5MsJeFaU zxQ^d{kxN|k23L%l+y7Vo8amf&C1M#f{YM6ci@4$P-KH$RE8nTj&-q!O;-r4~Mh zv>&@v$p_sc3XSkH?_8JAAsA@!L%mNI$$@h=E^2|;5#{cUJs1)^u1B{Nc~&lN{tGgn zFqJp{7i4-TA_gT7o*RtH{zS{i7hO%ZiGF@-?hbD*_4d0;+mz`7t5<8=;pzmgFNfcM zJMjXX9(@B5oHSb|C297uG@rq>jt+-Xh!K|aO0mDO4|uQL z;{DmYH;h+u-6jh#;>*{`CzM20<8XA5Dd(aqP9~b}?tI5p4m_9(>wIO~~pda?Z&2-Q5XDjo*BNxD_A;cg;7nbLwTWn6B(w0{=`oXSe?o zp`vc}mv}>d0gUi$y53C;m1$2v-Jat8ruOiGF(b|OKyJ2gouic^aF>fhh^?rGN{~c= zZMw>Mv~8!c1NeS%PZXbZDcsaVdP2Flv^vy|Pjd|A?5?Y=^)-mFzY^KsTVtZcbeAA5 zfzRpHF!WskPhTsRf76e<0C)}5{A4w27NtR9-YeDL&*=VZfe%(fLKRA$wb##T$oc=I z{8gHiT}n;)OX7cyP+JWRHJeB?_)eEY?xCQkgF&#Zf$!-(hF|xJ?O~RdPiWBII+ye* z2l@K)#+KD(SoA3bu5?f(qlP2wdsJ?nHwhgjEpA+k`jf0B%nnnVhdB}z z?>4QSDUrNbgH>j>YrOgwUA|DY|8uXK|dIBhf_5D7na*1+Yas8BPS_$ttKJiC7eMfkBhB5 zB`gE8;u{;Rc8YX%w$w|;{E_x(;u1#0nwaF?C8(WP%=B+t=Zk>IL3cBjPCS*wf&+%vB$!(yIC-q*-a=Zo6sp!s^Qf zS0j0L-3OK0jIJEXj||!IKnfS$e#ac9#S6cDkhY^+f++SHIu6bKb$0XexLv6V($BE6 zdC&W+obj^AGa4n*n8?_TcMEGJbyispeW;I9vhn8qjY?AaT@yt*=^DNkU02zl;UeEP zOd5kI$M{WH{r!GDqW+B*BJ`snZ!kX}?dq;DowM6`Z>nycOZ^#BG){k0LCKJ2w0t61 zRp0kJ4ZXQ8Y5N*K)gxcUv;kZwJ~Xobfvb^oKDy#u60x26faUDuGAKxiNffWRb>B-x zmB8xkV~~aq4j;26rd)uQ%IjxoqCDTd(b%0*9~jj~sIVFb5yO?){_=SyEr#rf^5*4I zb8RJY0nJ1wO%(iQL{M7~Z7)*qdkOz_{2r^!R*LAk-SbK~PGL#OBLJ9sLLq~I0Nv|4 zXAzKlK`-wtu5t>UY3+YM<|tREl=E6IudeSZA}xKM?JIO98GM^{L|=F*=eE5m?8PdL zbcDwu*{PXykrj8bPQRy0xaOU|xp=W$=xcGStC0hIy~dYtkzGSQt>FE%8~1|#tUi<7 zzDe?IdZfi z2O(*2DTYxV;fm=-x2QmKRV_ONm%#MwKOQK-^?luby~<*FAzXrLb25peWq##wr?h7S zOHhi6AVy?V$3b1imTsk2ZT6e-pv;Rk_}J<1_Pe|B|1EW?tQ!T~TLZLr&iL%MYfFl38B+x__9x{Qd>-9|o?{BQi*4L-sOxyX90j&S=pMHHTY2k*t*sIOGXE`nE2VOl zdaYl6gh7pN(xPv(Eer2^*&p}R;+?|0dx4knqrLR`M;io58XtDgpQBDPpBPv?b_>O! zjd|MT%59I_o|7+f$78au@xCqB-g%b1W+M`G-_NAvq?Tx+Ob3K+9S3@9!_ND6fbh6u znK-{FAAhjsyOhFQd0yvCM5Bi7woz?sI5Coci#Dgkq{Qov5c73h)lh{FUGU{u|#y##ZH<3DET zp{jWnq>Bujr;wry^N(!BG;~WlOaIXClf6gg8a&A6BDxWqYtgN^Ez?~tR&$f8-rwRo zu&^g>OF%)3PBBmL6z z0+9RIa*Lj3wbKsWuNYr`rYO;HcVyOz_}7~2Evze1F7m(HdVkbs;Ttl6Ha+dEZAn!p zaU}OF^N&A^XvT%vtRKO_krl)w6W{fx;g2iVh&5bZp^?Vk)QW_M1-eReSe2hvN6Wh* z82K(@;;xx_*k-OgMY8K@a3U-u&&T%B)h)lQNLpYqWQ$9Sr&S@n#W^VHyN^4iiF0H| zZyj=t*^Vxu(Tudz6S4R8p(o53Cc=tTTEcup?MmigwC$(69D&mP?p+a|_?t%L>t|eS zGuiQJcRwKgsJigsL<-~&%*NDxB&{D{ydU{m^zQs&s(aivOY>y{m8H6ycsK4MlX_X* zuFAO814$n#M;5ls7#2+G`8DGj1G1wG)#kh@F?o*IfTPC_m-aAoNRJ=m>`4=E#* zax2$`JRI>i)k{lyHFeo|{xT=vPirTI8o&V1zNSjyqbUkb0v`o)&;m?(f#XbO`v=%1 zc{Pfp8m0C77h#A}I}<1Wy9l`>#Jbq{!Fl7Dg%~(d50-r$)5_>Krfjr{8?7@-_m=CZ zv1wvUI#dVMj)gYL-BL&~jSg|OJ*nDl*&;F~v?dcFX)?N=xg%)pU_>m=`Mxsr$tgfV z*dNv?@V#qH!2P|!nEJjxC*7XdDPO)@dVzO(0Su2S&tLzgn0u3dzq@qn$mbrCWn?5P z>a$l_Y;8yiZo>(p48!Pc4R4xxb?N*=gT8c_T8zsmPd2Iya(Zz9|UKXe|7FoO*9=biw&|oKy=KEK_E*0lm)n-S^vD@ z{0;btO?cNLO0+lt2$DRa=~a?UU$G8n%IpP?9tlekq=jX9U@x^W_cm6Kw#V!prwHVl z4Y7=d{9GO3xB;W6d_?Px>ip%v(++y-lw(9t-OpL&bz-In$x-e}o(=gA(-xb&i0P@Y zpYGB7s5GzJ!ZkFK&V}j1U$wCfmcR)ZOqWbq+|PazA#j*pGR>}Go_wKSs;y5xk#4Ke zgE&1r@~RSeWz8g|zTBf~T_+3}9hLCyNi8PP`Dj2W(SGoal0WsFdLN9>d3d?^c_iJ? zF@gRyTYy0nDfBu6uSL+S>Ou4kJ=Jz|tv3bqArBgt${(+B|EsC+f-y%w(hb7EmP+3B zwYB95~e9aXUwOr$?;W`iNWS|EVDlmx~&iWrfu+7umGouhRS7JVIuKH{x8S}(cIuGA8iY-SVdIzMK> z-s$A=NWbr)nX#)pLN7rg99v$Kdru@U%L<3+!HTAj{v$w2-6{0S#K*6y{Q8*!irBAr zv`v56aMkHpLteQ{oSvjdXn8?I<}nR3;Hk`pJ{Jccx<3s{?x>nLv<%O}jcvo}yAPjq zmhz&9Pll=^C*%X)nA*0!W1Mh=zdNH;4;?)=hSe&&@uTb3b3L)LQz4y8K41*4J+uL@oCGn^=Rmtj=SlJS=7azy^}L%TnT&!FjMWQZWU!c-6`sUnN**YPk=}>tk{GR1 zodb%gYZnXaSX+Ji9D8r5?*0wxDO7G7rL+_coHIhG6K}zOykM*qN+_qN4G&kvw)v5x8P!I8 zbb0?MsgUc2#ixITd)L>Gsczu?`GGfXyeM9SMFB^z9-3o!p6`S^lYpu!3)RMr!rX%8 zuiut$+y!;mfxb}~UV-Nd4an2N1Q{ht=@*!1g5MN+UHj|=4uLI+ms#+m&NP%AgvxeS zV8TUke_kHi>F@b$@uzfyX9a&tkmVmN{~|4Dd8$ z;$HTkC5*z~L|+2cbQqm);m%+FyNB7>C}gZpqM8k`Fhn-$%Ci?I!RarV1_euw?`Y5w zrt_V^UMxdLv6sxxbzhp{*p*8pI?C@2=a&N9+O4%H#A-f#=v(h%3_5J{wL)_UfcRoU zJ&(6j2HX_S0dnEUvYRKx{XB_c*3XtU$&1)9#WbW#laS$>(UxUB#Kz z@TRu8^)lDf!(lShb?D<2!c_U=H-6hE-a91cZ15nIK&am2*>@|uC9}JVKljIs07Egt z#x{b;k59MEY&V5AIaZ_!yMBj5XN#wb0-hyyB%-Vovqq%H@_YTbB+rKhHKp-~4!%ky zm3?fsREjVbL-qH1YYO+V>ZhTuSk{jWC=nE7ei#ghXXuBj>bh)*Q|m;0qPy`_J%a5O zD_i|%GKnytUXSI{cb$QHcK-ezxfm#iE!l*vlXv4vW)4sfq4ER(0KIUhpwzT}fGKV_ z^sGe+_${pvUmzOD#8&y&2lKNpVEy{rR%e6XRgXn(oDKfltp3kzA13pnP8}0J2w%E- zgWlWR%eUiLU4t;h4#!I*Dufp3E>{>PZ-mhM39&UBve;WePL42QB` zEf={L)eC$yJd*(1I@UmSwK}GdHAX5EZa_`uTq&*k2-9OJ0%BC<>{oV)!M=hVCzlE_W;Q>N4}2Z2Oz1^mwKkRlUuWYP=(s3j9v`=G+J= z61J*RsbTG0m~66_(^5*X_<+T;nA3R^+Hm+d%?dkE7o|K5T=?>!SKR1w2Y>M2&DpA) z4km)JmkeSZJ|q$=b(hyv6yz+afmt=8lZt~qMgBZzs=49&Xyq0PnKzPOrMFvrn%f*Y zygtSbzh4sf_#-ROjzkUlMtl*2UL?3oDfy0}fxtlBV5$6%0rbmCKaNPPji;_~;2g2i zra1V+J*XH5x%*IK>*)3ZTcbkoI_|j19H{zDYnp5p!@i!uBpZ$2mLbB0icVjCz>hns>OHATNY!f~fdv6%mTB z4y#KP0{KhJz=97PSL@A^)4JG&#zo}aGScQoROfSfP`x|ap|_^thbhg_GK?Iw&^V*g z1Q~$Y!5>IysW+kYj2_wa6dLHU2tO;g$ErV2DkHuJ$J9G3!F6`abxyVwmDX|&2>y9L zwc_$F*xQYS4IkCIlI#2>LQo3?cR8i9Lye`QsJVyJD_j7;6_vAh0S=Ze&xEgMCObMs zZsffP419!KUm;j)FdPb(kp~InL9MO3InIp&=viv>7SU>0egusuKHk~vz8oHHtx|(5 zBDuGD+PU&m(f{MH7&MU5qwp7C-PO$$eq~DGSK8mBFGGxj7#nXQ*1C_OtW+x6AYc%F zr%fSX+MZ)nB3}CQI{by%>>V_*O)g$$uK7h}nmF&Lxu@IZ7ecpb*>TivZY)yi(lroF zhyy1up7{AlYquN`DheMFoA$rQ6{q*KF@1^8wVI1Mjp*-+5;%cn5ynlGx=vIcC@t=I zuCBu;C#O)Uy04vN=N>(U1gSeQ(K6hSr$y#l9NaP& zZg76L_QNngj1yV6juI!n(S#QKE|Ulbj@dYktM`Uz6UgkUErB>|stH*oz4oxd%DWFs z$!})R%%TK%<0)64f8Al^jo2J2aB3*Uww37`;VsDS|Fb!-w{HN{w_}bUeBG+Kb#ppj z3t{m}T?BEBH#lCT}kDBL4M3gK#PU>d$8wil*Yh*Rl^Fno{J7dg4&!!LV za4fuQy`YdalI;35IVq+QNpV}_Xf$a(b<5J@EXv|+1v+G3>6~iD>Q7ci;KX|=r0@Hk zy2|*ZjQ{Mt32u=JI-`Wv8LW-fn5Z-QNerJ(2Tw>~C(c5Z-J=l4UP12bcm>Ib=s3*5 z06x}PUX1hsBq8L=4fiYQDOOYtg>*&4`FfYH5e$^cuG9WxEUKB(=Kd}~XmNnfV2j0+ z$1{~b_Y|+4dIp_#_TFbsIG$*VdSsU;j3#c-j zJ@D7!$(rS@th)|6pR5K)o;!W@Zb$u|qsnZFxoHIe{B=Pg@U2m2j{(@>%WLmU;MHKk zT{}3@meS)7vbuC;7Xos_xc+CR_g|O*luNenCyf1ph*$Gv@iyt_OHJFHTR2h+SWGHl zvo{&>uH{vjKmFSs7H%r2JU`&+{YpORTPDtq;W;o%>TSWMYO!KR@M0=#en8te#_?lf z(bzd=&8AxDIlz{Xnw;(D{HLV)+lRTF;XAj_|JG90F6A6D3IMg;erThX`aQc)b)|;) zFD2KQ0y#e;%fQ*A`S8`67=s7sVI2$An$VvU)lf<+QB0eueOOhh@d2F%^v+dfiW6sA z_w6}wM9HtS6J)&cdXUn!^BV%KYSV$**61If)%M$<`j=C4PNAnk_#Qi1QvUZe=bMq3 z_uWiz@g76C6U)=xTn++uUc}qKDWjL=m0>No9J-$5??CAxcG-r*6Ts^a<=SL?Ys%%3 zYM@T%UsnaFrhlmz5||MA<>u#DV%%j=6*6Id!fV@=uj9N`#z=Kdv|;+7ttomFmBkbIr z&y-ZLFr({e*MCGa+z0X~3pg6%aIiop|( z%l!;VD+ECje1Li9PgCx&_;brD(lN5)=_26<-gbmV+ZK__vT56s?|7+Ti#QqxamGfj z#@bfA-|9T=`F^iWhs?{vW079X6_?TemiXofZv1hma57d$031mBJ3fiv zBG{ZX>83fw8(Ne9cT2Hi^D{$ctBxu2ZLT3$5f-G?80(+aS+ow}xU@hM^@~PE@p$*@ z*Bw56iE2@221EB=RGf5Q#@IskTaR4w>|cFtOH(ovmHgzTc1t>%u*w6f(rNPTQ@*!s z4fh6|vr*}~$2;M)Ybs{5#CO|&XDtP$U*j2hB!Y==DK$a3XkiT|m z>&sTa;bN^1-_6H4jUG)on-vFjOL#((*AR&cVBP^8b3a|XkjvE;`|bWUA##hu?SEdP znG!H%ELoh=SZGy*#VssPTo!%ex_l>!wp~csD0{oPWF6E8OyN{+?To@Nm$+BOxh0hT z7yy|-7FY|*P9L7?>$&urjU!6}`ZWKlb=8n!+snsgj~mD#F$A7vvtV!2^}Hp+71Y;W zZ(g9Gt1_xnJM^AVgPD%svo!xQIksi>RKuQ}g21Pf`?0wLT9pE1%&rQS@EHztrlG#D z-j!L33XR40?+VDMx9L)Dg&0;~%z%$dWo8iw@L+1Zv4BMU@uC9EzAG1I4WD%F8IA@3 zoMPn7J^HBtjdU_o?PrDb>(2Rq*I@kL49Nf2-^rJpS1p*`>s+6oosgEAt}5b*)!Y97 D1_P%} literal 0 HcmV?d00001 diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C6.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C6.png new file mode 100644 index 0000000000000000000000000000000000000000..9f9c230ecd51aaa42c9acc5b40383d84cc62ab3f GIT binary patch literal 11141 zcmch7cT`i^8t-8kWrU%qG-*N60YpGXdR0MCkq!bPQlj)C2BaoI5u_t3MFb)sy&35> z79dJBw9qpUdNq_lNJ!p6XYSm0XXdW=)+?<1!Ad!2XYcR(wJ%Y|hI;I*0;~W4uwS{X zV+sI2WCH+G=T8U0XE^xUbpSvVxT15xJRp5#oXywPA1=O2P$`i2x%j#UfL8~3%5H78C5E%j8eQu` z#TEu$?&wzsU?!v;VIz&L{uV!QSDvIq9ma81k0sah0)X2mxtJu8!0AMDKL9X>8d@nJ zKe8(cRqW*>ck?Zz4g#W8oFTt47wQ)r6aNwCV+}}iv-*5&@Q$s)Y~-bCF{0fD4*Zi1S)B}ca9Ejh*`X6LPg zcbH)6A(@Ly*G9Ao=cb04?V|Y*uAYtLw-qXDR8oQ+;unDla$W(-;Q(-VFuGOZxyZok zIaue~gk(Q@ZSpbkoh3vCy3sMQn`}ku_BX`DuajT5SRoYaWn*&eR=O64kd>I4+j~tp z=7f}~cVc==9gDSyfug8+lg>1WQ@BL*lN&;7v$y@8=%_^A@|lUcBD1D6*Fr|cK!oPw z))<=>J~QQwVr9ycyOXA&v{CPtF9+}~ODPJMD9o4Ha#qr93v(Ei9dljNg36`18+NoA z3!g4`na)x^z$s<@C;NpZsUjhr7&$ui?cM8sw~udDpcYi2RE*DPTL6_^=~_cPZiPf> zC)!o~bmNR3?#v^^YQ?reV(Djw~i5;)3sb z!5k^)6>*fC>kCkOKB{Jgi5?=y1%d$5)d<9{~dmo25kVm+#n@mopyTyU9YM%-h4 zhutEV+K@^J;}eszZW;6O{KdC-JMmF_JSDeJdm89;eeT<6{V@M&`A|hcEABx~F9R{N zTa&Ck%Zwbf)qD!y+uE*4#?U)3H2pxk>Wn5Y-{=0mLCt4z3jx0zi>nEr`ivAYw^NBk zOtRTUgB^Xp^TCukA^n-{b|9_!S>x>O73bh?Ovr`Cuj65Iv=O_g81ZSZs|%iw7iG@f+t_)#0?5Y&yL4(Z?U}9BG>sbV-mbe_te}^=M5x;ln&Uk6H|R&1k_FE4x(Gwk9%h~QOZESo{3AU(}= z+MJfG7LtKs7}FM`mx!KgL3pc}R&Edf~}7(Fq-i9u+Yq^w1sf+{>84XQT( zxZdi6C`@~gQ5l(|l&kI5=McJv-XOcR^Z=*C^|f$I9THEu@=6DtT+UE!uJ$Gzdl|eD z%|jylW2mv^QoPYnxKXA-`bbrg7M+wJp2@%xs7+CGjIC`XBNb}qT1-oOHX!Gk7INET z^Ik}zN#Kyn?UNHAhIU|VD!vSF;Rq-f7}1+?YhliqHET_zN#0&PAk*U!c|LO4$M4Ct zOPbb&H8tBMYMKN6A>^&66gI~d!g&!+O(p_Kh;kfF)ueBz=Y z-QZ@1r#f|OCy1crI3Lj)s0BZh88Bs$3tdZ-f2%rS!J2xBp<@p&~^Bl@9zFwNvyVN$?=rUO)u6BdNdnU2H!w$mMOvRHn{p}zr zH+QVmZEF>iZ<&lRKJrtSTs?FuH+S_XzRuR(ZtR!R?NOuU^fq@?02CKVd6B3C-c85L z>h13tL04KXq3stgkv<%?i!JAjPe_;H*SO#Mu=e#Djz5ULl?%|W3DFlG))w9TXy&7g zIPw@mK@u0P44@b^SZ-guFi+e=QPy|pyj}N65*AW!pHGDI#SbebnIgy)s3Z)X1ljAB zOfXnGd>Fe?0mNby#tn(h>d+Agq)gKE*&OiH`?k2 zvn!sm7}|QjV8#diHrZn@8#R*5)wfjhu78?VZR3e)#WkGfiHDPR1}g1F8)WIfF=$_P zr2QEXhIBH<^Nvc06ZPCM-h-i*_EIlJMY!qA<`4?!vn_Y3wCGw_0?U_9HT@n8HhnJi&&$vRaFC1en3W;+O zqN+)bTB?6kc$!Y(7J zqwNGsm9NfXMPfNPq~m$tGp~}VKVx!w4p_WM!|YV)ci5FvTd;TzzlBSb4{SSw(x7SL{U|A@5)KM?d_Z_Vh?6~eKg-SSCh9agdi6@YdbE4H<~bE zeTKISe2Ta8U+0x=T7~=U9l~C9*vbpXR-*5&PeN}YpAuhqaBtcS_Va)E{+2j8E3J$f!vt{aCZCX$SH z)q~s%(CPQys$PcbUhus)q)tyK!Bd)7+0RLE0<-bB(cL>tZ|YGc<`TR{{88#Di^}9@ zN%4lS6XKu78=_PAMB@xrj6x!5(`;TKf~i!)X|>+>5p@~FMbQjb7F_e1a6E(^;qFL% z+qU$}2WPSnpghRF+Vg4L9oXohO|b^ezHshj6ZM7Nap>Y0EopXS^&Px+I!0=1uq!hz%OJ#HX`i;H_^P zp{neh{%ho}7!wex-sR#Z0swhSk3>}|yw}7xKJn2{)Hnh_(E@qYJ3$Yndas@6+OA?5SH+68B7X~O6X9XMLYPnMr+iK zqaCv23snu8a`kfcCz>2gsZup6iqaVt=eIvbUFOmqK^tn8#R@eOsjAEE($60!*dst&!km) zcS)l)R}-QjSXT@u5L&m?4?a+R#AgKtH4 zPC-7-|4Y$H;8nH79i2*#)~8m!vz8VX)mEE?5dF4Z?dH)VW$paFgq0Ir4x&{>^7 z@=yYFcHgYl@&9X2`t@n}1O(JR{Z_$?^u-4aQ#la&TAykl7Nl?)7C_Vv^vw-SRWO}# zC9MrTQfVrrBX9VfSc1AK?jZoI-^1Jb=gK^|vWW24Yx!{CocrtIVt3s+@3C5+HqR%h z<-N(Vq&=;Ha5la?8D{(I0qdW|%~IZ->}%HD3`pXiQoM%n8f^@OEgn&h64Q%+EyD!l zwH5^ez|#V1u`Md9964D*GF`k1o`+=Xc1e@;>fW-YDV_;9A19~4`5&9oEvbQ<_;PF4 z`?I}&iIfrev8w~@xGxjW>YoTC9iaF2U}(yk%a&Arp!3$lc@Oy= zV|D%xD5W~Xr(I%XNFX?Ztm6(Fs> zYdrP+(|z-*L*nLNCswSJcQ(DWuVoHEmTzgGFM$#X= zUxYA+S{rg@cm}OT#n&o$wIBSH<{h8B$JwUs+<7t#2RCYI8t(TB465O_#|zu``|4qz z4rwAh3Sg%5lL2AOdvl#~K-KfT#2MvNxr_H(&HIBsKMg6vA#zdB2o~76*sD_Bvhv| zNhp7cX;hbwGwdN0INLFhD-&ASkvEfbhw6jB>(sY#c2pu5`n1F2~!Ga5%L!s(9z=#SYIK)gb;j%3Y04TJ zkM%%Uv=H*k4gjZ%SugmWt+y>XfZ!Y zz}SJpf_wjX>iwQHy0FT}w}H|<)EELb4!AJ48}tjCB&;K{qYI~w&XG$?*4gAtMX>+{ zcVe1Y^$=QO7+Ij!! z;FT14h1pSY0GO+s!md(2?sOp;&9tCTyyBPi#?Cb>7tUq&4$jtAP%5240LKpev8BW? z>6)4TOVG-R29@iw?vWAe_w_v>ZtTRYyZ2Wa{W(6!#S_4*hl@qM`cU^}$#5e8;J4gf zFhZ#B?uOiE1AqYwnm>kOL;7q4m+=z@fd16e@IiF|5Yp!YBeSS_WYu(9EoofpJBAff zGYkZY4^}wa-YPZujKq!k;cb1UKP4XH=^Y(a%h~vh|F}lEuy_OrRmoc=b5AbApsA4@oSc?Ll3*2twu0z+^1o8X zT;n)8%Q*5P1^sI^$Llf{;B?N_3zgy{f&z|y+xC9-VJcA3>av@H@QlZfw{M@+y)PX{ zz9e`R#|=1P(!=KMeapQfBO)>mOH;@{!g}9z2iOyKYa2>keS^F5=5P8Sm!Ie`r83{+ z$d1F+J4-}Fs1j#G)E?F?7$cW$koqMrhH%`VZrBu-*W!G)JSSkH{Fq&6S>N^tU|N?e z9A;&vNIL|;o{O^1)i;Ov_?|$BzP-?!8}srA?~g#z@yHK#XKd2BSI~Dte0o=dKI7(A*&7rMbYACaNN`a+4My!|Gv8YE&&&Sx zUg6db{vHzCjx&P~`on?qtguk>*qEk(O?`davO)8x9f2_rurQ+z zvPic^*E2tOsbR*ETy;W`6T8{NZpG)g133S-hY`yHIAmGK5_qjUHu@fX7nuNMLlTU$B*88%#cU}-igqzm!oug=a%yU7j^sW$y2c3@yMnRG*47qo zU_fDIL&NR*&vq2mNFMEfky^nHJ;x4L8xIqiX0K)BB3>+Z}YC79kdSaIprJ1v9Yl& zpUQG^%`V!KX+OMMwxsP726Y>GE`OFKCwQy7HV}l{s|hR8qmH%>60By5Z_bGf)o8AI zxeJWns*qH*9D}HxGy!Hqryj4;E{8B!dS?4Wpp~l&v9svX*_Spac3G~HxKp2M@1eax zOm=eAGdpuj0Myzvb)dq|qWtj6aGi8AA;?TJ^9M7zyKiLu4gpVI-s8&fr&B0fXNQD~ zv1NzH12>G9U3s(}iqhUr8Ud*}?H4ws?DljK8uHa`nP_(%?+I(x*ToG6T_1<%)%f+q zr&vx1p0us6m2|iO7$37}N&s7r>@v)-S3eo`q5pQwvuus(SXsl7rrgSbV=o8O$!CC6 z9`K?~m}*tjV1+B?O7S#HLrqKdOii1C)0{)}w+6@6++Wtrb{D(9;>kwL0y+=Oi0-DK z2+xd`;!1ti6l~g7kB@M5s3y0NHA;D<-VeKgy+5p>(~&^^U@rl{F4;~9%U7>PWeqk4 zGXeJ1Bd)w zWo51?3<%R!Rc-^ZZlYS&KW)*|8)%7OgIV~KTDN#yB?Ng60HK9NHg^Y#iFf;!zSDxF zh^9I>Gqcg_GG-uD06GBj$G-{x{QH5)(9wfszWZeu^e9>}W8gmsluoQpc>*QfZ``tOt8ZLZz$qfGkkYrKiuJdh6 z-TV&1bj>PPgrgD@sZ+`Qxxo2w3-L&f`&ePl34xb3MZJOmP;s8;A&B_|++lsiDqS;r z2FRfL(;^^1rL>0Ch(vbJHU|Fy*ee!xRBdIFt?(EX4qwID5q9vCM)CV%p~fR-2!bxj zg7(ty;J*Gg%RkbWW`_rW^o@Hzf2W9Fuw9wth@pF34~F{O9&rP&24~b1IL$lLsJe~@ z-x#<^Kqu9x1h0P*|+@5LsqIT_?h;H-@WSX2LNeGWE%cVGilJBurP9SSA@mu)!# zbKTVozHfs4 zOGn4eyd=KM9DJfZc}Bjfbt194-vZ>k86h1#KB=nN(L-S6d>QJeg}WAmB|jT`5Et%! z^(Hu>uF8{1W8inC9lPTq&Ta z4qOg|x_h_{LpU`-NPqs<3W^O0n;*cptlv}%Rgw8!yS<=&*KW77jsURp%@-<3d9@0v zQ}Lv_o%gFlZ#@IYf}Z8-@5>o-RQJ4%+ITm1aq0#3#C zoa2;4t6F|@lXOeQ$0Am?)E2%+4+h7a9`k$T{WZ)t#%B8hr46 z8#8qfJMQ^Mc~PN{PqMxfGNy&s+ZEh!*4XxKdAgEu{P72F;l%{_l6RECQMEKjQg38) z*z6or>R$B5oE>jh*V-pc{&yZ&eSXoQ{U8T97mRuCnW={aNCX8nKZhxW;hOH)RC>6@ zwWS9?ta~jfNdGZZrAW1SmIV^cvfZG_otL|2FO(4piS&5lyD*ja=m69CJLab%o$wKD zvud`2tj<#Zc~2)Vc?0b;nrlbFDv~0EN3WNU-qM+b$8Hn4j1lZsc9WS(fa4UbN1gW z2J>mn?7tTaiJ~{`>>#$bwtbpNVp|;AZJ-E*BaXLRi+d{m6fvgI;UY&=$^TI?K)_;v z`e|#+$7+lO-}~J&{5`yY-b@46Tq2h20g2Aa>V*dDd)VMb-b!4<{UZULRUjNuQcBS9 zY_6*ufG~$qwpn2xvgkuK^kl1Z=p0F2*|>Q%7lcUYFc>nA2>E-hCP4u&r65n4?4@$E|A1c>nAmz=S-VycxI z$A96`Y7o z8=v^fgz3m)tbw~>FfY)_T6YlF_R=eEX0%#oTW57SDr59SvV7fT?z}w75l!4NxI1=L ztX|O(KT&;Fg!SD`;}myLOQM~rP~4l6XP?w`(o_8 zIt~D58+IbWh~VEGIxaK@bV%=#Kr&&|;ax3H=}pUZ@X*j&XBpZMUc5 zjZsERmUc%wKqk{5x@YajsCyLO12B{C*@8J~4}-873nrkzdxOEw@WheP!8qybBliqE zysSm=H#3a<&x-#Ik9m1)+$>V z=x_F?6Nk{x($jv(moPRFXt^dcZfDy3AGj-KboZKLBRx zXaB<~o6t7JwM3LAcH+N>gwf;MUNQPIWk%m3#k`%FCc7AAF%BHNAs#a z8pW8$65X|~*nSh+JEX_95O5N;#bd??0Ovi6Ub-^-kNa7wQ>LFK6@b+z@N14}5Ot-- zvS>ek?u>mn5323l#eJW8O)AoBfr>?}6LH;}^r*B(qC}#Kp)Ip@t}s z8xyEEC&4vb7;oI@NqCRN2$oydzG?i(z69PgVroOSkt@t7MD}|^JC&skT5WK2EMPuX zv$pj64?-Eu40J!3Y#E1*eE|voKcm2Z1M+V@_urEBe~k(lTDgf&V#f=A`vlqp;L0UK KowAGe5B~?oK1_oE literal 0 HcmV?d00001 diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S1.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S1.png new file mode 100644 index 0000000000000000000000000000000000000000..174c7d1f86678ec2653c99ecc20d0eb447f24938 GIT binary patch literal 27843 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 REVTlFv7z~eYJKN>{{e?2j=2B; literal 0 HcmV?d00001 diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S5.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S5.png new file mode 100644 index 0000000000000000000000000000000000000000..31ad122b8562b39c710e6c3f9ee014bd1ca2939e GIT binary patch literal 2813 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 Gru`GG;$WZv literal 0 HcmV?d00001 diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S6.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S6.png new file mode 100644 index 0000000000000000000000000000000000000000..f06a4bf299f79c0b13b98f318d629a261d80488d GIT binary patch literal 3461 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 literal 0 HcmV?d00001 diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S7.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S7.png new file mode 100644 index 0000000000000000000000000000000000000000..fdbec3295c031812a1e24f51f9e2bea5917e9cc3 GIT binary patch literal 10904 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 Date: Mon, 31 Jul 2023 12:51:36 +0800 Subject: [PATCH 05/33] =?UTF-8?q?=E4=BB=93=E5=BA=93=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app_test/test_modbus_tcp/test_modbus_tcp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/test_modbus_tcp.c b/APP_Framework/Applications/app_test/test_modbus_tcp/test_modbus_tcp.c index e2376e9e0..b0565511d 100644 --- a/APP_Framework/Applications/app_test/test_modbus_tcp/test_modbus_tcp.c +++ b/APP_Framework/Applications/app_test/test_modbus_tcp/test_modbus_tcp.c @@ -147,7 +147,7 @@ static void *ModbusTcpServer(void *arg) MbMemoryFree(&mbm);//释放存储区 } -void Test_ModbusTcpServer(int argc, char *argv[]) +void TestModbusTcpServer(int argc, char *argv[]) { if(argc >= 2) { lw_print("lw: [%s] target ip %s\n", __func__, argv[1]); @@ -168,7 +168,7 @@ void Test_ModbusTcpServer(int argc, char *argv[]) ModbusTcpServer(NULL); } -PRIV_SHELL_CMD_FUNCTION(Test_ModbusTcpServer, a modbusS test sample, PRIV_SHELL_CMD_MAIN_ATTR); +PRIV_SHELL_CMD_FUNCTION(TestModbusTcpServer, a modbusS test sample, PRIV_SHELL_CMD_MAIN_ATTR); static void *ModbusTcpClient(void *arg) { @@ -225,7 +225,7 @@ static void *ModbusTcpClient(void *arg) return NULL; } -void Test_ModbusTcpClient(int argc, char *argv[]) +void TestModbusTcpClient(int argc, char *argv[]) { if(argc >= 2) { lw_print("lw: [%s] target ip %s\n", __func__, argv[1]); @@ -244,5 +244,5 @@ void Test_ModbusTcpClient(int argc, char *argv[]) ModbusTcpClient(NULL); } -PRIV_SHELL_CMD_FUNCTION(Test_ModbusTcpClient, a modbustcpC test sample, PRIV_SHELL_CMD_MAIN_ATTR); +PRIV_SHELL_CMD_FUNCTION(TestModbusTcpClient, a modbustcpC test sample, PRIV_SHELL_CMD_MAIN_ATTR); From c56298fa99414f74749042e14b126fd7ac34bcb3 Mon Sep 17 00:00:00 2001 From: JasenChao Date: Wed, 2 Aug 2023 19:09:46 +0800 Subject: [PATCH 06/33] Further completion --- APP_Framework/Applications/app_test/Kconfig | 4 + APP_Framework/Applications/app_test/Makefile | 6 +- .../app_test/test_radix_tree/README.md | 21 +- .../test_radix_tree/test_radix_tree.c | 196 ++++++++++-------- .../test_radix_tree/test_radix_tree.h | 39 ++-- 5 files changed, 152 insertions(+), 114 deletions(-) diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index 14c28b1d5..b487d6222 100644 --- a/APP_Framework/Applications/app_test/Kconfig +++ b/APP_Framework/Applications/app_test/Kconfig @@ -244,5 +244,9 @@ menu "test app" bool "Config test red black tree" default n + menuconfig USER_TEST_RADIX_TREE + bool "Config test radix tree" + default n + endif endmenu diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 37cb3d64b..624807cf0 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -103,7 +103,11 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) ifeq ($(CONFIG_USER_TEST_RBTREE),y) SRC_FILES += test_rbtree/test_rbtree.c - endif + endif + + ifeq ($(CONFIG_USER_TEST_RADIX_TREE),y) + SRC_FILES += test_radix_tree/test_radix_tree.c + endif include $(KERNEL_ROOT)/compiler.mk endif diff --git a/APP_Framework/Applications/app_test/test_radix_tree/README.md b/APP_Framework/Applications/app_test/test_radix_tree/README.md index b2bfe9cb9..fb4efcb6f 100644 --- a/APP_Framework/Applications/app_test/test_radix_tree/README.md +++ b/APP_Framework/Applications/app_test/test_radix_tree/README.md @@ -9,13 +9,15 @@ 基数树节点设计为: ```c -typedef struct _node { -​ void* value; -​ struct _node* next[NODE_SIZE]; -} node; +typedef struct radix_node +{ + void *value; + struct radix_node *child[NODE_SIZE]; + struct radix_node *parent; +} radix_node; ``` -其中,节点在树中的路径即为键,`value` 存储值,`NODE_SIZE` 定义为 128,足以容纳所有 ASCII 值。 +其中,节点在树中的路径即为键,为`unsigned int`类型,`value` 存储值,`NODE_SIZE` 定义为 4,即每个树节点包含2个bit位,可以根据实际需求调整。 一共实现了 5 个函数,分别为: @@ -32,20 +34,19 @@ typedef struct _node { 测试程序定义了以下键值对: ```c -char keys[][MAX_WORD_LEN] = { +char values[][16] = { "what", "where", "why", "how", "hello!", "apple", - "12345" -}; -int values[] = {1, 2, 3, 4, 5, 6, 7}; + "12345"}; +unsigned int keys[] = {1, 2, 3, 4, 5, 6, 7}; ``` 1. 程序的第一部分创建了基数树,并且将定义的 7 个键值对的前 6 个插入了基数树,然后分别查找 7 个键,前 6 个均可以找到对应的值,最后一个未插入,因此无法找到 -2. 程序的第二部分从基数树中删除了 `where` 和 `how` 两个键,再次分别查找 7 个键,删除的键值对和未插入的键值对均无法找到 +2. 程序的第二部分从基数树中删除了 `where` 和 `how` 两个值的键,再次分别查找 7 个键,删除的键值对和未插入的键值对均无法找到 3. 程序的第三部分重新插入了已删除的 `where` 和未插入过的 `12345` ,再次分别查找 7 个键,新插入的值可以检索到 4. 程序的第四部分将基数树销毁,再次分别查找 7 个键,所有的键值对均无法找到 diff --git a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c index c54a49805..8fc455da7 100644 --- a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c +++ b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c @@ -1,23 +1,27 @@ /** -* @file: test_radix_tree.c -* @brief: Implement a simple radix tree -* @version: 1.0 -* @date: 2023/5/24 -*/ + * @file: test_radix_tree.c + * @brief: Implement a simple radix tree + * @version: 1.0 + * @date: 2023/5/24 + */ -#include #include "test_radix_tree.h" /** * @description: Create a radix tree node * @return node pointer */ -node* CreateNode() +radix_node *CreateNode() { - node* n = (node*)malloc(sizeof(node)); - n->value = NULL; - for (int i = 0; i < NODE_SIZE; i++) { - n->next[i] = NULL; + radix_node *n = (radix_node *)malloc(sizeof(radix_node)); + if (n != NULL) + { + n->parent = NULL; + n->value = NULL; + for (int i = 0; i < NODE_SIZE; ++i) + { + n->child[i] = NULL; + } } return n; } @@ -29,21 +33,32 @@ node* CreateNode() * @param value - new node value * @return void */ -void InsertNode(node* root, const char* key, void* value) +int InsertNode(radix_node *root, unsigned int key, void *value) { - if (root == NULL) { - return; + if (root == NULL) + { + return -1; // The root node is empty } - node* cur = root; - size_t len = strlen(key); - for (size_t i = 0; i < len; i++) { - uint8_t b = (uint8_t)key[i]; - if (cur->next[b] == NULL) { - cur->next[b] = CreateNode(); + radix_node *cur = root; + int temp; + for (int i = radix_tree_height - 1; i >= 0; --i) + { + temp = CHECK_BITS(key, i); + if (!cur->child[temp]) + { + cur->child[temp] = CreateNode(); + if (!cur->child[temp]) + return -2; // Failed to apply for a node + cur->child[temp]->parent = cur; } - cur = cur->next[b]; + cur = cur->child[temp]; } - cur->value = value; + if (cur->value == value) + return -3; // Repeat insertion + if (cur->value != NULL) + return -4; // Already occupied + cur->value == value; + return 0; } /** @@ -52,38 +67,27 @@ void InsertNode(node* root, const char* key, void* value) * @param key - key which is needed to delete * @return void */ -void DeleteNode(node* root, const char* key) +void DeleteNode(radix_node *root, unsigned int key) { - if (root == NULL) { + if (root == NULL) + { return; } - node** cur = &root; - size_t len = strlen(key); - for (size_t i = 0; i < len; i++) { - uint8_t b = (uint8_t)key[i]; - if ((*cur)->next[b] == NULL) { - return; - } - cur = &((*cur)->next[b]); - } - - if ((*cur)->value == NULL) { - return; - } - - (*cur)->value = NULL; - - int has_children = 0; - for (int i = 0; i < NODE_SIZE; i++) { - if ((*cur)->next[i] != NULL) { - has_children = 1; + radix_node *cur = root; + int temp; + for (int i = radix_tree_height - 1; i >= 0; --i) + { + temp = CHECK_BITS(key, i); + cur = cur->child[temp]; + if (!cur) break; - } - } - if (!has_children) { - free(*cur); - (*cur) = NULL; } + + if (!cur) + return; + + cur->parent->child[temp] = NULL; + free(cur); } /** @@ -92,20 +96,23 @@ void DeleteNode(node* root, const char* key) * @param key - key which is needed to find * @return value pointer corresponding to key */ -void* FindNode(node* root, const char* key) +void *FindNode(radix_node *root, unsigned int key) { - if (root == NULL) { + if (root == NULL) + { return NULL; } - node* cur = root; - size_t len = strlen(key); - for (size_t i = 0; i < len; i++) { - uint8_t b = (uint8_t)key[i]; - if (cur->next[b] == NULL) { - return NULL; - } - cur = cur->next[b]; + radix_node *cur = root; + int temp; + for (int i = radix_tree_height - 1; i >= 0; --i) + { + temp = CHECK_BITS(key, i); + cur = cur->child[temp]; + if (!cur) + break; } + if (!cur) + return NULL; return cur->value; } @@ -114,73 +121,88 @@ void* FindNode(node* root, const char* key) * @param root - radix tree root * @return void */ -void DestroyTree(node* root) +void DestroyTree(radix_node *root) { - if (root == NULL) { + if (root == NULL) + { return; } - for (int i = 0; i < NODE_SIZE; i++) { - DestroyTree(root->next[i]); + for (int i = 0; i < NODE_SIZE; i++) + { + DestroyTree(root->child[i]); } free(root); } void TestRadix() { - char keys[][MAX_WORD_LEN] = { + char values[][16] = { "what", "where", "why", "how", "hello!", "apple", - "12345" - }; - int values[] = {1, 2, 3, 4, 5, 6, 7}; + "12345"}; + unsigned int keys[] = {1, 2, 3, 4, 5, 6, 7}; printf("\nCreate tree and add key & value:\n"); - node* root = CreateNode(); - if (!root) printf("Create node failed.\n"); + radix_node *root = CreateNode(); + if (!root) + printf("Create node failed.\n"); int num = sizeof(keys) / sizeof(keys[0]); - for (int i = 0; i < num - 1; ++i) { + for (int i = 0; i < num - 1; ++i) + { InsertNode(root, keys[i], &values[i]); } - for (int i = 0; i < num; ++i) { - int* v = (int*)FindNode(root, keys[i]); - if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); - else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + for (int i = 0; i < num; ++i) + { + int *v = (int *)FindNode(root, keys[i]); + if (v) + printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, *v); + else + printf("keys[%d] %x not found\n", i, keys[i]); } printf("\nDelete \"where\" and \"how\":\n"); DeleteNode(root, keys[1]); DeleteNode(root, keys[3]); - - for (int i = 0; i < num; ++i) { - int* v = (int*)FindNode(root, keys[i]); - if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); - else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + + for (int i = 0; i < num; ++i) + { + int *v = (int *)FindNode(root, keys[i]); + if (v) + printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, *v); + else + printf("keys[%d] %x not found\n", i, keys[i]); } printf("\nInsert \"where\" and \"12345\":\n"); InsertNode(root, keys[1], &values[1]); InsertNode(root, keys[6], &values[6]); - for (int i = 0; i < num; ++i) { - int* v = (int*)FindNode(root, keys[i]); - if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); - else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + for (int i = 0; i < num; ++i) + { + int *v = (int *)FindNode(root, keys[i]); + if (v) + printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, *v); + else + printf("keys[%d] %x not found\n", i, keys[i]); } printf("\nDestroy tree:\n"); DestroyTree(root); root = NULL; - for (int i = 0; i < num; ++i) { - int* v = (int*)FindNode(root, keys[i]); - if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); - else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + for (int i = 0; i < num; ++i) + { + int *v = (int *)FindNode(root, keys[i]); + if (v) + printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, *v); + else + printf("keys[%d] %x not found\n", i, keys[i]); } } diff --git a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h index ea9e7b7fa..cef53561f 100644 --- a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h +++ b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h @@ -1,20 +1,27 @@ /** -* @file: test_radix_tree.h -* @brief: Implement a simple radix tree -* @version: 1.0 -* @date: 2023/5/24 -*/ + * @file: test_radix_tree.h + * @brief: Implement a simple radix tree + * @version: 1.0 + * @date: 2023/5/24 + */ -#define NODE_SIZE 128 -#define MAX_WORD_LEN 128 +#include -typedef struct _node { - void* value; - struct _node* next[NODE_SIZE]; -} node; +#define NODE_SIZE 4 +#define BITS 2 +#define CHECK_BITS(key, pos) ((((unsigned int)(key)) << (sizeof(int) * 8 - (pos + 1) * BITS)) >> (sizeof(int) * 8 - BITS)) -node* CreateNode(); -void InsertNode(node* root, const char* key, void* value); -void DeleteNode(node* root, const char* key); -void* FindNode(node* root, const char* key); -void DestroyTree(node* root); \ No newline at end of file +const int radix_tree_height = sizeof(void *) * 8 / BITS; // Height of tree + +typedef struct radix_node +{ + void *value; + struct radix_node *child[NODE_SIZE]; + struct radix_node *parent; +} radix_node; + +radix_node *CreateNode(); +int InsertNode(radix_node *root, unsigned int key, void *value); +void DeleteNode(radix_node *root, unsigned int key); +void *FindNode(radix_node *root, unsigned int key); +void DestroyTree(radix_node *root); \ No newline at end of file From 8effd595283d6b545b44db174a23316b51e27e69 Mon Sep 17 00:00:00 2001 From: JasenChao Date: Wed, 2 Aug 2023 21:13:41 +0800 Subject: [PATCH 07/33] Fix test --- .../app_test/test_radix_tree/fig6.png | Bin 62122 -> 93299 bytes .../app_test/test_radix_tree/fig7.png | Bin 243325 -> 97952 bytes .../test_radix_tree/test_radix_tree.c | 18 +++++++++--------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig6.png b/APP_Framework/Applications/app_test/test_radix_tree/fig6.png index 80bf281369bcb059cfbc825056ef99527eae3b20..a8f0ab9c029b80d2561a2cbe5c5053afedcc562b 100644 GIT binary patch literal 93299 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 literal 62122 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 Fe*tKLp-lh) diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig7.png b/APP_Framework/Applications/app_test/test_radix_tree/fig7.png index 7fad83c9e61cb3587045e57d224ac7039b756593..4b1073ba47f8dc7bc77ad2968c4a8a4f4e7ab649 100644 GIT binary patch literal 97952 zcmce;1yEewwyxbkf;+)A5F|LkX$ZjsL4&)yySp`>1PKm-;2s+HV1eKcjk`7OdYgRb zJ7@2I-+k`+>z7qUK~u9;~nql2t|1*3{(`sPU4jX8>KE&5Q zl`vHTX~+vVMf|OyE$7pZuQY#<@7=?9Pt8Ff+TJYPSv42tEaZGn>%WGns9{&mkE6%Y zR$Ls|r9^Ci6aa!!$42ZxNtGz)|Z78F;Aeo{&Z4am=nXNWMpg5771zIGch&x0QH|BoaB+N=)Kh~y_!3AqgtJjI z=y%POhhR`lsU)BsKLp>XuVG~N@ex$F;!LC3&%5URz`$3S`Qb;1Dv_( zs2bsiJ!F~iiphv>ba}OG677$zAv$ke)1{Gu`+|B7DppFztxbW(lamn&CTYSDB!$lq z!KeP%t&s(gXWN&ECBh-I#Td#n#ghz8b}$F6d00>ce--Y|J`|R)#6G~IPmrH3T9k0@ z^s-TCOY^dYC!o9|o3Qh|O<2Vrc~M;aRz^9k~&@w9+o zlJPse>Lo)0rpGT-c7%HucvUG`SlLsyH)x^B&aL-(yaHX*>#rQIlGeP-_Xa0dc~Zxr z2UWD6U+2zW*zSsZ@ilGmGmv?l6KN%;GJJ$ zN7?Z;CQ;_yy<@D1%k3{_5;-{8jFzx7Qb6oCO>pwsk?=K<&~W#u6x)cAR!__L>J0(Y zX_p^b`~qb=6%de?Zf`d&J2`R=DC0iNyTG5eHnk(;?fIIWUb}{4rXBkl!jq(CuayRh zz-+f1-H~oR6QIx3tyx*HFiRWPCSA6$mWY{s7VPH!k^+OKo;}PuZn{?1`xfDNE;izH zuo3^WqfVKxb%pfHx(a2&*l)y2_5$Mf3Jy0t&UfiEK`N&|?3AJd6xq``JnNr~rFIA3vyF2-1N%f0a+Uh1=0)QWygL z>8evPl}SH4`cWH&SaNruroO@KR)FL{^bR?PDKmOmUn{1s%9FfU%Pwc-6FGi(H-2Qu zvGC*|(CpMYNz=Xzfpfc1;rZhic^$UBKF+JVcb}~_DwNB)A8>kw6X5$|_jrMd_R_Eh zLk2;4RiwGPs>EAN!RH5(o~@c4k398W4*`$&@fLJb&K^h7yP^}i!t_D@qr@n8fgQq6 zUDA};18i#}Io!0kO8`g%ypN3DOqz_4zY`B}+-$`3d4(XZZvJL)Et98+YI|zzv1p4RWIegw(c@dY znXZkfUOj7V_psLU4uWaf65ddR8Umd6y$H#Tca}~|zk}_rIxSmBrxVv}<@PjYf3)W2 zl!pyzBu;w#eBfs;>W=ha4r;%{o0kTLcU*t^$VO+4=2w(a-U@{ynIK~tz|xaH@IJ0P zMJ{Ukt|@+4)YY&MJT*T>nATI)>#tTGw=tj#dW(DJD$i$fMtN*l@2&;;{Iq@!Uuz*{ zf0Ja*APRIpBGJiIEqifPUv9X|=2Q=cpQN-3rKKJgsT<|LlJH;{{wB#J3kqK$e;ap7 zZK0hs%gP~4@u+&Q+2RL9<%+r-UNgpdwVCjsJQ|C|atQM9gu9&HW9|~%1~LUre}xOF z>B|@R&EM?76ZBA>Dv{T83Pc~O6xe9!c+#u(;3q8{XKaAiAst|WGGIr zy*b+!UliKmZ$}*(*;#~LiJESHjY-=RvN!O1SBwO$Q5FhZbI5KY@eboNOZdO)6yAjr9xTVZ3TWM=^6*K~vBx{MvhXD-Rk_{8%NG5#e%7Y^A<}l^nAYdA zE-RZ;+mNFRC!G|L)BkDIDwsYZEmOH5)!4fa>hXnh-SB`lFsLJSdjw{MtyPk-m~tnX z6oyu(h1hTsf)A4r&v;E-H6j@wtqD%gR;u+c4X*OAIvmJ)WSDl@=6m)Bu*s638J0%) zR1NT-^v{B2Yo?EX8IIOk?eiWFex)}o;fmZ#4E7+?TxiM>NN)BMAYx(!A4b5Q=kv*aam88v8C|ZnXbnK%&Kho_E8*S=)K6Skh>M3 z)2f2xEY|eJIBR6yBKWQn+%;BIbn3EZPh4<~_=u19T!7}s+6iMges_JT>ktapL!CRe zo<~q9y8)Qr?T!--_OPH6RWLumIq(y5sa)Y9Wk7DZ4Aa7VT5>A0Z8=oR`g}9%>SnVI z_jYl?LEjV8>BR2s)+e?G^g>yol-aXf+md6=m>*|Yf(pDc$aQO*zmU^o1yoeG4S7gR z9CGvp;dX_5SMR-Fv+t^z+ys!7;RRm4*HuIPMENj%QYu^6;`qED36-)~ zvc&ntJ&V&~=Y4m@56Qj`Pmjh4O*!e5=8E^NC? z1tF7j<-WP+Wa)A=?{?%(a%c z{V=u{O0i#YrMQ^=-0ID5JN)HO>dm6+*!M3?Cj-4&*$(f`Y>(JBlzV5@D2#NjT}<$- z$jf9#i$6SDIZfFr)~hh|=+qJkx>{%>zgRt-L;YZ+5aMIJ-M|+bM5vued2ybH{t$R{ z=PNiC;d@hT(E;@@oB{Qn*8 z{=Wpx|G(dc-(mgN=d7B;hzlYliWZBGs${H7`Ew&BrUgwHzSp*s4Q`N~%(|0bXeaMK z0K&3KH7X6vs=Dnt3uofpXWkRR$VMMFV zTn7Z4#N7oe$qo$|xvBXM$i^CFFO)+o>omv_lCif0)<;m?0w<5(0mBirP)7ZfWA+~B zVtT}n*mk1XdG&gO)q>TYVifPeByxc70*36J-rZ;a;b&qe70p*bd-jg88MrNqeFU=N z!T=7aj(g`f|G9f-D#oHzL=CxB>xsCkO#R%VB9u=52c;L=21P<;`#|}ZBVh}re_)e502S$=_7PR`*j8{q-23RqMQ=3<;h|uM4|`C3BRx)HTCf1w3;i4> zHV$aqYYFE}Dia;@IH`m!*u#M@Z3K|AZ#JK$_r~yHU#t!#zGb!*Fv{PsZgW}_AI5## zuo$bRO`9MdV1e8!EGT|>if=mn%=;EFG^u4U_T%~LXR6ICpU~bY`w94%#W{eKHj~vr zQ4{vD1yM&RhtIKx(jd2+;7^<19Hx+-AO~JoYM~~9Y3E-N#>i}112cj<^!H@objOra zmU2yNoQf2ciRt{lBGy-qEN0r6o$iO!n`L#5-xf<3a3W}M9oGB~`M}8>(n9*xd`qR; z^pIcUi~BwmFe)~)_oo_K(U9L#gl#qknC}wnlkRhLcUBFV(e*og6?S6^GY`EHC!K%%)Oc+BRf4Hce=pcP zEweKgXL^4#Y+BZvb=|pD8>&4s5aP4 z+W57?*jp>2pM#5LNCeSAJ4?)R$h}PEi1O#%{Sb`veX;W3MP-Zkvp+@hF4c#lWxJMLXmTE z6FDwrYR*&FbI08=^LzCwPQ_8^Zr}+uMrV4LO|AK>!_XEQ*n~!{l16GrjUTx0ULooix|9 z>vAqakud03<5}P}eZBMb_(bcezxr65?39^fe$bwb6GVumK|OYnwtqs(>P&x0AYlBk zrD=zs%i?x(U@`)4&hg;>S~xK$`&Rs9W|!s^GsLLHnm|P_quxx7R%3X}$9b0b{$`V= zpz*yi`!BBx<9#OA$BIp{{#Hbsvx&kchTKLlL_-YsVEpA`gc(u;G?f!)N5bSWN^RqK=f+ap?N$gR(WbMMln@ArOG+iR5 zYMZ8(Q)D4kqv?aeW^Bd!c%`qEtdG;C@T`iClM$y~V^~witG)$4VB!=%qjwHZD?;)x zV32zEJ-FcPbg(A&18r90d(EypCk_5ySODYhKVv2aJ7W69>`DL|G!$X(SAs*A^2VBr z*Rj{fR~04wbS)2`PHI-fa3(;)F^e764xSUXUwU0Jl>LtBM->Biv1&w*!+#v!Y3g`i zk$tH6ST`nw<&Uk4CsGmaHF3u^@#}LJ#?T5|PAB#7OPp^>sZN7_@t%WsB{Bi=&1PvC z_N%FuTWRCgAM}=d*7XUGkKhs6E)29h4ckw|uo7Bhl7v}lCBNO5fACm;V*K(g8CjH1 zsCA>)tQGt)Toc$Jn>%LTBYXJfn^ie`ZFZ<#$T-^P(7!B9(ykJ>&1Gg5l-Cm8`m1=2 zZ3-MdQ;)=yqfpAJJ({on`+MY+6%M!;N5do9w2YbZNAO8g2@la4j<~4=@jzkOsAQ8V z>fk;z9hwF$6%SN>4pd%DP^OjgUHJ3^!#)1eIQUezYhl>AWbI>L0qYpcqHtWCgw)EK zeEuUg&gc-M2$BIl+LEapU_x?B;k;iKLUI?iLw-C7TB}q39Z5$)SbCCQkCP8J1p3O6 zrqkdh_s!Hvpdg|u2Q$Xscuxt1UrwHW5Dx$HW!i=nznKBtgmRL4-#mG3rc*uETI;iC zYQOBg5!0&)COkb`9rDp|0Yz83`UIn?o{ZP#ykW|xa#r@cyQ;D#BHXP7b zaWEmN+ZAEh3!oFX>zk3U4#X z={AeVV!qppF8r11uDem0V?+vNy*ypmC-ZyV*uGQCrw_e_Ms!SGbIh;an}hY*L#pMO z6yAGd>Go$DfARWt@u_rUvR4BYbm^RjF$j_O3~4le^^fK+r3jO5D_`7bN^9T9`UCLGcvL7ffSL^rqhb1>if-HZ*FBvn z4M5ew81@o2UFCPWFkplJkW&umS@8Q+RNB}td3UT$uVo!uOd68QJ-AIm(lH4C7TLy5 zB32?*aY+~;wXj*@l6OIbF#XAT&CF1m{4 zWVfXV;(biy3vVLL6nhJMHy>MSMR$#?{jT(XOGD@;7Wad6CHKRNM|a6o>Dv3(BSkm- zS}nI6)=>9s@+m>$Zv)eO2E2);j8HVYe%y@!};Pn?|C%X`|?_=$GFC{ouTmmq@7T z@d_}d`IDS5(!1iO2NnUVI2-I4@s%cbR2eJ(RcC;a^>nY^@7OfF1-{JtuUsPKx#i@! zKaY{ctb;1K_R1Rm3dFjz)@}YXMxzbKnpWP{IzZvi2)^J4@ZXpF4Dp8h)|KYgoD`#N zI0FXKDsnSt)OAYiGivz0kUx`vg-;13zt^37r~3DNc#A8}hrWq#H63gLRJd~=f1_1K zUdnU>vTH2gMB!Nr@s90txh&R0^SxCA$k7`kylnP zG60VA>)wT*8b=n9`WHfAaieco>xSrF5D3H|tSt|JG5<)`)^LgYptxD^(9=z6n&qv5 z_HbGQ3)qB+P9uf)caU^@i$C0nWDd`>kzgA5c@QmDs(rG(etRH%Oby=YAauO^2%A5j z(uZDBUZ>kz>S>F(enxXFD;IjuNA3%Bm3Z-TiO7ppUZk&_K~ZmuX=tUcQ}52V#d$>L z3lC7g?7M|pX6{z3wkCeBM>E2bp29rJuX~|{AJL9gZAQQye$?yR!(~${WSWAUx8_J>6ew9S9m@p9w7Vti@je*ynqGlo^hYS%T^daXgo`E_}lEf;+;Vz;CkzCKTMLOD*Huy6b|w zzBJymvJtl7PqNPE!9&1Ih6fKYYW5`(CfP_o$jK<&`6HNo)(MH3Pm82LsK!QlXR$Vkb$HQg${L9b#>m?QOC|x6iYf! zW78!#Cx?s=El$~?H7Ycd+nQ5W3s4vsNxHFL`N570#{3_d2qQk1#EqmZ?8IPr5%bcn zW3+qfZ4I_hOh3fj_->@wuWupsiuPejOpBBRquz`-XN$pX#rTJKO^a^*?ce;11C$Dc zU>kW(&T9x}Kx+eK!IHna4`HDHJ`=a%{TWmAHBF8zl5;RZnV0GGm!gCE00L`TbP&zH z_B(^D=Y6-6dKN&~h>3?X79uEa5dDmn^-d=4K5ra&Z*HsAg*^ui2bYPS%L)3vtd+aS z6=9MouqRcF72+JpPb?&9q%fgycY1bj%gw;WzbxB=e;C;gJUhX(lVvLjwNK($-~>IM zuMJbja^{+~;4$ZH@C%C?!lu#Cs!s*sXvPrw>ulDuDMS~HXd@@O7FJ1)5j(beRuk51 zG)J)A5A4idruRJyPusi8^1c2q<6T8Vmg9uU zjoW&cR>JXz3GR8NzDB;tIal~8wUWZE=*G&mn?cM8ZCUp3d1l+_bQ&a0f1;Y#WmAED zWsnrJ>RM$D>;_Lr6{Dw4o;@&DYF5PYLJaX_ZiwIOT=GH>o!w3&MvchSUzkEg?hnc_ol5mILcBP)^i|j1?D@(E3BUj&L~)4KL(bxx&jK8 zya_?imMNMGH~e@Fp8|<|i3!(b8Ws)8r^7=@O*M^MMq@PAszJ@jgIIZqR+I_JiiO$; zgv9Q8J-+V9hSj)&MM%Pzmi<-a{W`bWx^fFXhOU?l>jQt9Y@ZS_Pi)8qMhz=^4(ksu zV=p9|lSZjKOV^jhF9}2p9#9UxPj)ZgEp>Brsn+mBVON1ofIwp$?gTNQF{Cd}I7bh! zMeo8PB$72ca;VG1KgZoe!v;@ZoK!;t@Z!vk zE&cbJ41yYHy+AzVDWEZx-VCwX7)AN|BNVolQ9nB)W$#wQ@tGb`;&-UTT_Kc;3%|zT zHfyF4Wj|czt`{siYF}#%D{2Edv_C=rn43T6s((#%_?n^~RX<%f{r!vZIV5(D(YRQzYTV{-Q<~{dXyA@C#_jaCaSI>%) zw{fBL9vt{}ossB~Tt)t5C}`YjZka4aaVy{zD&uusb%s6AN${4X=8RGXiOTUZ zXGNb88!Hr>L*<*^1)x;&*MpZz^P)>f=-cn6@Ti7eQw``%5I5L3GrWZLYLC_NtR$&%$Y0da2BDy71s+xIot{6h1x|Bou7LH1S@0hNj}Yxbh!d7JE1I?^YwX4G7>; zb8^V1WN>B>laBa=raS!*mV7*MYh`k0ym{A`hInj&&+{@4V``Nn`2p_$IG(qn_96I# zl_^+M+n%wLh1>5Y)z;cywv54fw4@%EJvW?&V8RYZ+Vy1k7oBzLZ^N`*;G?I|k{zZj zH&CK9d|BGUIQ$)3T%Mhe?#PgS(DRMX3jlp*wC#_i#-J}_k7NyW*U?s`)gFjz=xx~+ z^hcGW&Q?y+c|raqY*={I?N=`t8F6E}9@*hwZB)msay zxJ*)NBrW}8tTnJOmDDCDwkr{3(3qViuBoUmCcE)>EfJP^`GE4|JaTm-c65G#8y##? zk?lw;G|c5dXBIbWb9MD|aipL;WWMJ3OwP_ntv4u=&O&~#y|xiYS*f3^xE1~B;s$Gl zbbEQG;g$Hl30PNm93>eodv?%Sl}a3nIlY@wa46jTXE>uT$bR66Y~Mszf0 zc1Z)G6rS`?>g>}9kgD|I)a&2n>(FZ_Y~enoLqvMJ8mreYooK&4piEHm103HtD3o-2 z%Pqtd>@pm7$(fY>)T%tr1FQ<5*_aoPSaJ7hb0qgS;v0W7Ym1~hc*1>(%fE0R@?j0s zCGm@SF0tl$mc}HZ%Jc*B5SrSZsAm#2Bqbe`rVNBsLQ9j%p})RglUo33QxK@0ZLk0q zUe4^67$84ij*OVz(-oOliK%7O>yvQr z27(uX3RFmniSq4(D)a{D$CS(WNbu;iF;$mNCkLdHzJR7$e0(!z7% z$MEL0U#*0km($s}g`}nAeW$lKR%kXw=akB0Q_f#22I<^6W=1lYKWP8V_!|NdQRF2#bgp{6=!E>_hVINN8|FpFnfAfEISXY_MLh@6Us$402u zl@D=8Jsd%l1<^6GS~ZMbf0aNj?oJ<(`UeSeEePxUv+nr)o?I=X7wgk-pZ&q{5g;+&G7-#S{~mLD(}H;I&82wc)ruaQl~!}8VI9`D=-?h4jNcJo+e8XJ++M@i z+s$h^xAr?8>WJHKmi7pB#jTjXHGQW2M)ifEXo+Y>a#=}f$ZLxBv8y=-`$^a5<<~XS z)-Zn8qy5OFXhTv*F&rf@m5Da`=`H{qrQwm0I7Bn2xztStnN6}S2zak{>QfJclmQ}mg{+UMV2Tdut>Q;J}Y8s}9 z`&KRd4|J!qhHmEA*QXsdKd9s5*|lT4mz&&4TB3lyD0|E4&Bf8hQqArTwH30PjQh`e zX-3^*Ut14XqPn;pIc_=OqDkB+^|@q_cKE_AW9 z6bEhimg^Uv$e{?i$+C$3up_J?Lajs(66hv2;%jtRNB)#ihfg>p1K@Z7{|V2PF7}A- zb$Llx=!n47?~sf)9MhdII+d<@mfW743gRQ)SgnO>&6NA0wPG+1Q_21T1*o{|ysNJH z_-xTJ#1{>DUoxO^ztf62X!#D$=e&r7S)VLjih;FuXP}V({)4OVobrYEpSQ}WC_)~< z*o$puGS@l4P(E#%UpxarVoy!F72@Owal3tKTcYTTr=SnAjlZ`Ti5!C|{Rzgy=wM6Q z;;?e0l->+75r1tARB{^q%u|{d7lx%<%tM!6IOF~9DzJU0+I;l-h(BqmP@*I=d|Ug< zeZyN>BIcyV1ho*Yr3Oy-jj84T4A#AKpb76z7lb4CXp~cU%BBAusN?NJHC?OhTKc5N zjhbw*y_mEzXo_C&E4dGCGTOE!_{<@|W=q4r5BJ%qx>^> zd@F~#`S84v;ecDW(-so}4+&BeQ+KFGu~J z$h?KLRf0wY+Jq}T)vYv9K8|2f6X`Wnhz2ZQ+JDy~t2t&V5%iZ|2R5MKFJo5Z$@7gF zmOR$L=fk=S2)4b)+Y)bPbRVl`(?iZ%yzp=yNet#INy^y!AN&M^vh`U$V@~@8KRENK z&{b5y(d|h`>s1UDqiNb}x_(x(wOI<2@8gSiIti+iW-+*G z;9RZpL?XSO3HOMIFPIZr;$UJdgWZ#YzBN4xmrlw>HsgvEEq_bgN6BNW9q*d#kcmci zP~)%Nm>u%UzCDWLg2~Xru)h)s@oI#@r~ZV!0mwJ05+Dt&9~l`{p!aOyZfdc%=D<6g z^VIEgtr4BkP^Hn7B6TqPXb5(lsx2eFD4N%3x(%og?6NB60wCJESD8FD%RsQB^#3eX>N zsgrkiLaZeq*_MSIYvjlD(?AalRs}}Ezx+JZWb7*0H)1u+JP$%@IRQg9653jbN)ja) ziPQ<`%;;<3CIbzT$pckWs_j$R52Ii8JaxADmk`ufqN)$2VTKAABLskkLrIS`*v$61L5H(|Nd69iw+EMk&^Ov997e@N*WOM8|_ zcyM@BA&qNMKwyc0pdUyMis8XB5ruVFc;^@0b6%%WhzxryKqjWAlm#!}60j`)SNEOA zzJ91ptXIq27|*@GdF~p_Dw^0ZF8~PNbJ+X-9FhNI$s1!$vqM-BCbAEpaI9WIYj%(c z7yYGX&15jKh9iWBs3%4=6~~k6CG=x>(vW#`n2w@*6NFES`R5%yk7>}^dEM4Rf5@5% zH>Qp|0WKm^%X_ygiE*$R9c!oK-7sVrcrfGUNX6u|u(IVE@bQA|wJ_>&&$w+JVr2`R z-+hVO$7*0qyn|>qfHQJ5Zm?ip&Jm{ol7~F_V?|^fG5lRqtahuA{R$}>I8$#?hi_Px zDFLEH9Q=w|D#897MXt_w7jqMNXlga`%IEnJ6-I`vlcOs20*<0C4)PI;gTvZ2;W4ov zU^8UXX%En>7bHbY$CG<8FA#CdhpcN<$R_oBO%F#8K2Z(oF^N&hkn&8K+UEC*f9nqF zsy~2~b?^tW?LDS}^7V4+%*}iAxAO0(kH%q!PHzbbU}@qTXET&I<>2Cgd&Ji3lUFv0 zl2w;>zuGcv=iH`-Xnm~{j4%P%@5C25MoA4NIBhl&ki9aExful%YbN;|-dDH)^eQ&J ztzSNR{gx|wdUSBx#{Uhr-&RqXOTd>Pk=HVOp$mU_^`llSDVfqa&U@7JA>j*}VE()a zd+y4q0+@X9qZiX$xLLyIM+*Z~O82)tvBNC72f^xRJ-Dwmtr(Ni?||jdY%9CU2mxI3 z;1NN@ai`t1)Om&;7t^(s0iBrQP08e?$xH@R3-d8a+uqNaXX1T()?BNF3t_p3rADT9 zn2YMd9Tw%iY!X-tRC5v!&f|nn*#iNbb2rPiiM*la+m?n}BvW(Pi8;2;jWRRIiJk?@ zh^{TM7JzdO#P$nAIlo76nAqR}3mjW8KYZ^_+4_77A0R?@x)BzP?x7R)xcg&w^Un?luk;&9-kz& zje{qSDXfH2 zq_<_&(iwMwPW1bLm@xH97Ki7{SyRUi*$2y&e68!+*UKcIiS1W`E1^iiK&+;&fVb5YgN9T`ao~D zBU$~~Ct^GRZ*{a|iZ`{yokPW?y4Nv5*uvY8H<%<+Tx9LPz1wgCrPN>$dd+b+P!MhK zAwSYc&O(*mY)|~i9#>>>%;Tbr$lwp%58Nc4vkMEN+iNf}i2oDcXO~53D1MY0W63!* ziiO(zuC2b{PHLLjCEc)#mMzhgE-&Vcp*6jhdwD|gd>(*(Qdrmg;^Yw)#4CA1?l9Xi z|B}n=9xQCWIW-_u$~6HawUM^gRez-+;T)2uLw{=|e6FSW3IssFUtv-?EWCO6`bZu? zJ*qyDUE?qM?C}i@-6M0W_?61jPSHzc^ga+h@FPCLW}J9JQ$`!YGSSZ{0_PL4lx&~k zqd6=BNYeSq@c0(T3-R9{rZS7xn?o~5yK9!b!Va)-Q??vK2*Vs7{ERRW@k8B^s5;;N zB?qH4xgotnto;0m(6~jY8{^@!MFkTW>Sg?%c7v_-`cdE zba{^~Rs!j`gIjz7xt0w}oOogub(CYZsFS?MXQ4AZvvG)J3>mGq@AxkU-oZQ&R@+{mJm>px z&gv$!mpM+7k?P(8qlUtVtL`LOw9W;^mqdx)h;*tb1Vx#X)`-CW;5Z2 zS}P6t?3NFGq5p^AIZ+CH*HT^3`>YujFNuB0gi6cgqhD1VzmmKiUu@ff+GO;JleTQZ z5U-*S8tD zinn&=QEf2)!*qoCKf=MYjM6RHYmdaku#-h}$bz(uAR}foV99K~>)%ilNeR)IA z!;Q*buM91?FsgJhc@ZwH`2fdXOBZAXlE6;(4+2`ca_~L6JEDWKD>Oqud9;vO;wTW8 zvxRxnsN4EUCUDj*vX4@!B0{f1&ZuzvXQ4%W{C&jYieZ(U500&YDvKgfv}9gJv~Ql? z8qC;pC7qB~igBe_5;@`hz*$4}@?J=|fZ%C4SZ>V272enYPBHa@f5)R@36@(Y-$WrJq2m`mD6&Mw_{ZVn%U*d0mx|ta`(G-$Z%m`Mf)bliJxGDfn=lwh*OaB)Zy|28xT}OEW9CdB=f!B# z-%b@{)u}S9+n(`VBOKqY;nlg$Ee)@{jaKB$aN$C@Zy?NRg`ck}W`|t(YpYoxu@}um zyXknqIX4~kF}2rPmyW0{QFB8LC*j?R_>&fQ<{L$=D-0%IdmO-$A3EN2GDuz1RD71v z!lTwh*W2#^Ct=n6gOvUS;_au&L<2jd#`?<;9O^Oa)}($$G)}_ z>iG{O18&itgVFczW;3yMw#BsQvauy)7jgTuTetkD7Ei6rnNNja76EY}&A%$`v@oR| z{4YwoXy`vF?HyJBptPIb=B{JZWv>3dk-_O}7PlXEdwcA9tH&ajdwEB6_a>kioWQIg zv@oBbFPDYW(E^j&)g1X(18D{2htDgQ+IjUeL(0Y&E*3FhIRiO}d}8f5k4ard#>f8< z+k3_TTd_UyB$M!vWkZNs0$u3V$Q|bH4cYMiF+M`(ePsrG(#s`jU0>fFN@yOdf2|#2s zE}w^pS~0jYH89p71I^=T`-`^?CqxfI9N5HSK7+_sC@hy9OkTzjXXxB4#?bErn}}(J zG@~5IPl|g%D@<`WlJ(}M7}60cC~m;nbg!Y0gr7?RD?uE;(+h39E`j>YH>l4Ixmiq4 zTXZQkVMr}$cK6SdHCOlrjq1edM_N3QdWUm*xA%iWU{=EA9bubb<+*nz>HZX^x+h>2e-wb@Rn7>Q?DY@0lsb+X4N59`OihfXqHtol z+_%YSc)g*4s7+HE#-OJ6cADq=c!$N0?1!IUuWl}j)_i?ee2K(oh|c^ApkFC(CwcC6 zK_nw2w&XB>z0i8&8SMs<)+w;HUHMyt?5jM?zsz} z?IK$5dina+Jg=R&iF-|4QKr2!0*aJ$|6#+BxX4xaU7mzn!?FBvDFLcZ8W(XBQ~3I| zWr7>soU9$xCb!mja!%ebg8oUA`}L8JTHqwHG6EH?qFE6wi;aeex7`Q_gNCAG*A4!n zVTWw-6t<*QWUgHS-xIgC8`jj+VmT-d+qcif=?!q)+0G3HNWUO7@@w_f4+wpx_N$*J ztoHxWDQNlwW zbTG8~+~j{Kr?VL=NzP&Zk<%k;osFP6LDaJgwbi#WCz&vVN>2LUnz{uv3p+3%+Q>KA zi0TvGMkD`ld$Gwv$V!&+k%B!^>(gd(5%#~Ma{?&lJD5YCzTCztgz+>5*xDUn0w6{4 zsFz225R53VOK6}ppS3xk?*$q~C0Xw{57C5edUoHGYm>%wM z-1MC#k^`|KYWJ|fv^IL@6+83h;z`;>FzMkEm$Q6 z-LO4$K9B{XnHn=Ezrte&ybY&lvvDe_)&Vd7uK6X@h1*9sQEV>ZWi*d9R5FXx^#(O=ep`{7jv(?>q5Gu z1poY4visB3Q;0@Suwg@F*Xkg^M3+ngDj*b~R>D{D5B8RAFtv#JQ{_jDeG{{mv}FyZ ztlh#87tGN!y2Oww4#8=uC%1&zTiCw9UEahEA{>;FmER-YZ`l;e>hU7TPQ??o=#>c( zp1C^sjmJ6nG8qn7U?o;&?DfJ-q(xEcpF!IbBnB@Ajqlw{@Fn?UvE!(=)jvGF2bYsw z!1tg>s(GQWM-H#?%Xj~-oPF`fInQnMr*ii5zd-s6rvHNUF24GwI#%7TKXt4p^79GSxac*cGvbw&e=l=*=J^>zzbVRoMFza zyT)CO?~1C3{YQ;!NmR#mTa@v;XP`mq1SXc*p&<=%l~gex1cqdu1#|M2#4rv&=xzO2 z0Z~7FdH1ScZVK_$LB>o)6c4qV@y>#il2T^z;Wt&WMa{R5A((-ohIgN$`PFgarH>m?XGlu>aJtkf2)%|b z+f}bt_xHwBr&f0rg;PtwLAb@vdhM{%;*N%Jk91cdcNv?T3gk!&Id#=2wKCbUtSZL6 zsc_#lOV=t;>#4aTbj6vu#Va>4Lcs4Tn4~^}!S4Q5`s!z>>{nwTuu;eNiK{*NeDtKr!~eRyghoDHgyv zyY0;B5kF<8P<0#SO*X_$H00CD&2vjJObCx`mSil7gv}lq+W(6!<|@^o22qtR1;L=} z7*eD(+O_uc_K*Q0+qbg9FWX1_m%IE!zDn+(8T=nHee7g;>+i}~m@B5=De8XPO8$ey z45GR@qeY{SIeKd)C@e-zXWMG}f&s%f0}nF|=eV76BxNCOPP)p`V`7oWYRn5@+h3N9 z?K56N?EJsc*Ju4~fWVjaBjJafX1U=w@trr)blaw?A>FsG8Jo9vlhzw0i&w+#W7gv0 zG;f{=UlspK>K_O{7D1c_Zo~X(xq$ywNzV{qPOjc?tUzW9lqqp*-bK_%C=Gs%Q)?RO zd2=*d;mv+G7M_j#BRWxSLccy{HsvKBbA;mp)Vqc&X7SYc6GC4h_yxC=pth@RBJu}Q z%55DpDu``olM|9Nn%*yz#_Cn>q$>nCn1jaZCK~wwA0Jw&)<2N)1oeIY8>q*T+QqZ& zWeZ-%ts^g*fE8sD_D)&e!ik%hi`sXwjiRS*vmgo$SCQb8S8I(D1HY>a04mh$oPWby zx*(@SV^l@-CB17RyHON&EjEVu+czvms>DZV#t%kTDqOe!;G#)_xoG++|8~*%XhL3o z_(vBFsqz6;^61euOd9IO6%#c6UX9ND7rQ4gbK`%Ie;}WBQ{?8I5hsuM#P;N!PrrHc z&hN59j0Dpg-YBR#(+tSiGkp6m!%VlLye)lBtC}+Y-1PN)@Mdm!Vnv0zKlbBO?J$9> z5!dSWW93Sr9X`{NFVZ2?@nb{-h1%#TFX)2cwInR^q|Ra4_&@6G{ugrp;h};57Z1%p zGdy3c*51h5^>!rc?Pw%|$V{3{yDIf(xqHtlA3)YkR-FCxan@VNcY`qNQzbOAl(ACYnx6uVa2L-`+u2k5GTt ze&jCeuPfnoa;b}!O^NLqDqM~6reZ~0DGju) zK}8*r&i9Y`Hf?hrvJ>{=a^%vvV9rsqr*Nl+ykyGlNBp4E*;H-DDvA5xYrJ0@&Ed}a zez|&4OG`ccY0^5L{0$4C1xhuijb~bFTC_GfFGJM3ZgmKRFC()4P(Le0hn3q_*3)K3 z-G&+P>cQg@Ca?Qn#qj8GLNgS-s3g1DL2j`N^g!YCrP%(zJ zY)A17F&?Vt7nhDMbh~ob>Ux~&Wx`#cgKU|@Viq+7;_v730+J7({3)3A8Vo^{MA-n`0~0-iR;f`wQ8icS?p?p5fK~X8Xvu|Mc27K5MbY!V_{AP`$C=f zZXq?fyXo=uR^uRm9}w1##y-NEmvN~{{^ zaky+lNvOa~@;CmAf-}Y@_6};Flw3yl>m@h_Twe(cty_9nC1b7zWqu?dNmJJA=|NA# ze%yWJPT}6=r?(1nsyl`8`GZRo|MfRn#kZ;+P^2i^D45zFoUY>C9r{z4cOdW^_uhh( zyUUB||Ha-}hDEige;Y(XM38P!kd_vSA(c?1yGsdyp}V^!q@+tag`rzYI;6WnVCV+n zT?6iY_C9Ct^N;I&uj~DAzWHUdXJ*Z;^{n5zfA{0INm#RjrY~|k_wNiQ%gY+wj{Pud zB&y^uM}6Q{gd*JqdQJid`~#TQxody%@&R^4e{TYsNQv|~`N5V7L8t;~ec*<`I$;jJ z1hi*rhgbFzo#YkMv~<*cKsb{Q{dOFABm05A-?!!w)(ot4*(l=)qDVO|X>MraeLOK3 z*+E*5p05PEX=FOKMU?@Hpjp(=!Rj-Vq6VqWt#AGzZle}y?3PdWEUpU)C@Zbrsazy& z^7@f%*+OH0rlA(tkWJPcG-{VatA}}7ZVoF7Ag;l^I#=QXnZ9yqXToivaFV9-nswe4 zDPR04Y63!n(t)3p@m-z?lrP$@l$m>aVQ!64zXqPC;@ZL8?U?yqvc8D&^wf@q7Nw?K zhvs1L$KtC!t;$WXKPvVZOsN!|=EVc282Y&9n~i^*tVZ#7lT3P`+$;Kh-`g{W(d%m)^gA^{bX;}QE0e-Il3BTejBh?Fw{d2w`(ghI+$;$ z^~xJ+od$b9H~8a#`k<5bU2H0c_Rc}a|=v36KV$$jcVKOHlI<=ZJmSLX! zV}Sn?pZ>Q4e9}?rk#)?7o!pLB(t^Vw1>siF^(P-|V)i18--(xmU&PC3a{kR599tND zCD2D&?8BNNsr1#Zh#b3*r_cxZv0(jqT0j2fx$%4jx7{4v`7B1r9=LlIjo;+ijle4i zyQcG~qRr3iC6X-wDxw+;taTx|J6O%IVSO)_kF05UMLM#ho_F0(pf0JTJCvAnQx}Ug zF4oolOHTAQs`VRsi-fr5)&T5KQX(P6&99F(DDpY8?3<{Iom*3s@Y9L(Fz>FW%uA_h zqV-*CcIL3CCcJ4#2ht`$oio{!mJ}^AMThA- zT>^n8Z~GKm-&vIx-Kjt_u@c^k?(j`M;IqK3;&M(pxZ`#d%;jZ`!HPw@ML?DIy;*bN(11O#@f!?c8Tx4X zyO9P)qg(mZk!$wG<)WMojLmqp7zp$3BHIKd_iX!K$92E#>R16umpuIVHrDEHIe2FJ zGCc@Z_iUGm97QmaH*52mPtfYE;=h&H40Au~6g&3Uh!($snqF5V4WTj}E;thk(D-6@ zf8t62-mTX?Cr4D>J@@wb>qw8a=CLm2`n{Zej+P^j{e)BiLcGHJc} zWP9n6O`vWw31gVEh{HVvwelrJ&ZhKtq!NK8jPgC-{rlZ!ycc5m@3vSHFcjuAS`e49 zzqC-`7Gpx-{Ixq|@kW-oSfLJyUk8o_OEfFrgxe9-Pf%oK`inW)<+(h9?LE;G?Go~ztP%}`R z>zBGG(N*=_dib_Q*3RUOu;6DtSs^!v+z{yFV)Qn~9;9JAJ+v!U@>@KA}#94OaNWHPM);;4wM$g=uf z7R5Bzq-3kIvy%OzqYbC)>>T?!DKLXHa6`8ajT(ERqukHpLSe@?Syc4nZV%#%Q$p(D z6O1O=*r|byDnk*UIBJE;ij3nNc-eo0qVN!YTs7Pq{bB1Jo#pJV6dI=%+gLWwyZ2(` znZ52eRpz3g9zV4vh+5*`PRBuWOhD2Nz$tWRcN>Xb5TpQ z_RL6JJeQ)D6Dw_PxlhnwMV=hOYuXvKIX5c;#pF1mtAqJsf+OGtJ+3;@6+~VHC@4`} z?Nc!6AGOnygs}`;nJyiYt9E1ylXmZv^Ng!J+!hWmFmLnDUfWHUAntz%6gGV@-*@0b zw^oHKM-4_JW?|)-q;%JJM_g?tiMYkA>--UO>$d4k_Ic2#pqxtl^jWRIlTe55>|V?8 zl+J+<80a`E_>;CXDr(N(vY`sxyX$86Lkr)7J{ByI$%dKGpg;n7xEv)K{D(qekRtVd z4MM7lV`wtSOn-%7X%9UF*1!BUJqN+bp?9sk`<+8KZ?*boHbs^AvTF zQEQhOz*L?d<(6dyeWigI$lYQpKu7agvMU3Uimj%n34fWJK3@+8Mho#MDk>&Sf_9J| zTE6z;*>a0YzsS?NKJ;+>L3`E?y9)g&)e9mza>rqj+=42Lm3C|gk)_uHz6zwp>>!m- zT2b9|L8A{3*@4z&dTH4isRyad4Sa6)b8!3xt`qVjjNdSoakPD=9spBOAL@EPg<6jg z?$76X)6G1tzubYt32aD5D*TB}aT+UIfFV!vp*PTKCebkUse9<3`EtQ-Rw<_0T$enH zwfGxG#VP>CsI=Y;!rhMdL7cZ}Ir)>Awm`}{mXL!JY-^Sr+R<*sIpo`fS9c7^H{qUR z?dug(W>+;|H^g@78t%fLMGBuy{@>2Kt5W_c?@li?9k4rfhvB)hrf$fBc5pRd2m2|? zRFFh1!b2O*wC|bGg$qp?_>5c?LNZ^@`nxr zj619$XcMfh->rEF{o%{i7mlF%FA$26Yr=>nQk*d-$lFti&zNoFEfV%iXwK{FVxcT0 zx(lL;*-E%G2r_VGvWm3_fTio7j0WqAk9+2JT3w~-b-*{;M_@d*vbX);jK^1a{ZbX9 z3A(~0@b=6@OiO)#iH5h`eW_OTOXc{_Xn1<#-sr~z6s+d$^RU-k_v0q~o;R6T<@m$*n&^-|J^gTM zwa$}Q{!uPa@)}nNbK*yyaRfh7zQA9xC4U}`{ToY@eps0T7$hmwQFhA$$f&s^(Co(C z6JMAMbu%(9Zm(^Q?6EEPrXOu^5qlCzz9zrBKa@*JV)*Q@slEHCZ!a6m^@ zyH}vD=kiwT7E+g%p#iC#Uhx&JCE6U5rqq~^3!2!;UeXt&wA>UlQ^#QiO*TV^&%j7L z_27eM)khA`gZ(5L6_idL;ZTy~sV@;N4o^H{y&ENyI_7z!zOyJUedY@6VI8Ij{+GE} zGXFm2^$4Vyn=P#m{xyH8z;d1fFic+5L!-Wm+SU!0W)~~Bm+#2&=IcUhC5n~ha$WBw z51e0^frF2A6e}}f{SM66)wXb2%*`2S*a@=EW=XBCBtF}z$KcSIj(Uo^yT3_Lx!s7Y1F8e6UMEADtkpi}T9cDiN9{qk4~fhDl1v zB>^L2`z9)P%i<_AeuRbSGb)q6rF+DK8~t3P^3F}HyxR$CD1e4jt#(W=Y1t$T_)=pI ze4w*`*kg2hi4FFr$Q{V_l;&mfS{y?zR6Bh(M1SNSZ0eeye60gl;{* z37X$v$9L$cxjD#35)0YJf?3J>dPZ`L&3w)@p@HeYf@WW7Ud=J|iX^%BW{?H^#Z4#M z%gTc8nz&W>hy(S-fEAXiIgRLxhBzxQMpqwjz4_c}f>i>jNcb@)2yRp)t9Kq>KJ}V7 zzk=u5up(9?Jt+ua`R=nZYBx;A@n|EZo5V6^i<5Fj>i5RCS`#@OrD$3MKD=1%1Bmrb{JVN-54kgaEP8KzMR>eCH zwS4vv*VY0}B6Y8^lQV*xS^HguR{dT}`Grytjn&+mxTO~AnY%x5ZJoYRb+<1$+0i81 zPVE+5Hf1m{(H?;F;|H%-5LNVRLhT^yQsTY7#QhZS)NF$}L?P&#=?L5{2Jo()QutfQRcFb#@W&dMmwD0A0A1iE_hu?A7W0-k+wU6>Kh=hcTk%NqjiN3O+w!Q;VfT<^Hj?g=kRm|%0}BPTDm`f?aeZ{M zhiB-f&M2=jS3s7nQLI(ho_gUkULjP!9Z?~Vikm)57U-ldk9&`M%<^tSbMU&P1`4#M zh}n6LcT$S!dgsYa&-bUrE|&i#tFnX_--Qk35Jsv`%j;V;J;0e&t7o7DlnV-xWE6z5E64;ea`9iYuqwFWMh`0D=Rh-Vi1;=NVsoyH?PfoH(hYwujsQ*neS)T*LsJ{Z^>2L2x4J#W_MI*FGp$`Z${{ z3WI=%g7cOnSuVdNNno>W_0$=<2cQr%Fx)era|yJzqVT*l^#r+6bq;b=er62jS{B?# z(7V6bB(tcpI9NIu@(rtW&M8ZaGvOXjE2h>d!nS#d5f(VCnhG4(>pE~lK494a6bjpQ zWu;+nHtKf+Iqt2s9U5vKSWOdi#b9jhRxdx;H_`ShO@@5`d-wdetHiqnu7a{RR7=&a zb#t*2)5^G4Plo8+6^1n|pH43=y(PedO3`^_xAWr7_1@`Gm!mhjNbu;6RqSX^I$fK$ zRRbZdZ1vrp=@v=AvhYQ}jS*W{0`rhv92q#?G~rIuIm! zzF9L|#r;Uw`Rcg9MgQS8UJMAzty_azHyPky^S%moQjtJnS?=itJSu{@iwEqsN(9T< zB+e=G``PbFtK{pc&LBpx*J~v|O6oDHuASN-CtN!C&}Ccb@_4a065cTT%~b9?n3sjI zsn=oJo0;A&5P2a5q07+Ell0o=)MLM9ZM&fJWro2re&wAFXTi^YR}&_g;@Z&7M~qK|Of9vDUT)JUXwL znj)wB!LwnT(CkxHG}=RtjXx7Knn3yfB6nwu9{C*|W z)_D*22)-uRI$LpTckaEKsCcgU`c0#F|41x$N3@9n&X}purU`Wpn9sH7#qkqM*hf@< z6Hs4W{2`!L7??hvzj50H&T|MEKFj!WVNZdQFXMf*!vEA)*upU91-PvAjUW5-`!)f3 zZ3hYrm>r~_gv5S2$8~The0ADK<7c+8D=nWzTk_<(v#sUAEH-t5>dHRJ&r9Jo54TEo zDP36d5J|z-g>qv_MXe?jU9|i4be);BZQ}2g2>X!J@=^H>B~k>jeh3t4;?PoS(+(NY zp4)Uw>6P+=r9@x%D{GZuTJ{Q0sjS6kTJxS~jE;Vnf;}!?XADHGp&b5ZzQSF?C?Jly6w@d;f@|HBJLuC4FDt4brU( zwO?okz^D@Lz(G&D;R5!>TI@WKnoe}=;0ZGC2Q%ig4xNX5Spnp4wR6Xy`DYK^KCY~) zx#Wm+9y9a%_tG{*T+9q^K)-hr;9Rted>OO@P~f({BB{iIqM!8w*V+4JLMMUmTyw&t zqIj&k+-4%C&hU(&92d1#8jiij!tr~hSd=L;614Qvj-1BK1uB2~C5 z1nn4|g}&OZ$srn4c1e zYxiEPz6%31>D62dj=We+-OY2<2)*6j2PmB${R$rop`I?}lM5Keca;0KacIkDHyzC1@*Q1cGnH*;4U<=AMNE8xYDx*=uDx41Tu&Pt zL`w>&jeyhnpj!m+CXTRptq2qhMWNNUSSOZ%rEu2>HfM|12xlsSv}_TcipIIJQoXqP zBd3klp<4h+zxoR9=|gMQrQ@AJSVUW8)@oUT80|-|JwC^~(sVKOXz)RpGg06gpch-x z-@aY~@USE-f9;EHXbEy3Cz=2HSKyqES#SGKRJDG(ewIi9y?3!Lri@{h*=GSB&G$>0 zzXi#}Me3e@kxB$2g4?9pqjul04uYF*1nOz8ZIJ!|Rl9j%a@1#*N;Mh`tuI`Qp|kH4 ztcLP@gY55m-FKfh*mmEhU<<{KO+$rl+StYScFAyR6>r=4zoS%7+!;LTA9GOY=KHR| z!TB{Y`6xP;GoTZ6mxq$Rv@fkow@vDp5C~HMZP0Jw@%HoI!(-LE!#71n#rK&B#3u!h z=-skv%1O(`9t51P?Q%I!y!nHrBp-Od;n7cwCBU%Af*FAOwd@e}`W|V4;khLpqhTFj zR#$dEy1%E1;<=@cnJG8oix)RVcGLn@n`nl7e1EAJha(f%e&q%kH5yxx;ZRG;;G*fL z1EO731OB0eYq^vJlb!T(9&q5;#{@s^*Wc#mUFMW`QlIp5Vky%gsjCm<6)vfc@!wrT zoS1kT1z6XO*S;JZ;BV~je zRi77|tiscp6d7Wa_e;`su#FSxatMFf->_o+Gk-XcnYws**LpR;3D%j;H3PaT}rS2?bTJrXXl)o{%oZ1n?OwQ%!o6SNcG|9rZtD6M zV@Ms$7^3Gc(u#4Ceh&6)Qzx*yc-X~dL^5ar2RPJ`l8V0KYuU_Hm9M%iQ3NEAZ)0|7 z8pgb*QC| z{ALBoMwtzIw#+0jQVw`Q;#BxcPf5bGQh(zZs`6vQfj1gJYxpNl!5of)Ujml_X4eD$ z>I3K{DO|@xEyyQ%h8@}z`nqH9whED2gz(4f?STwTI9JuWFroOPgB;mgAA5RbSJ4c7 zxZ~yT%|O_v8DR9Xmj71J7hu=N^h@2Ip49%|+BpRK(*0jpUG)FcpQ#;&2MUec!4ne@ zSf6nl!*1>KdAz~-pvyemJi(CG+LJ`zhF6v$_=^ND(FIBN3d1c)-wCzbI;d;;BD~yB ztQ_QrRwnCf`is3E6PggojPB-{mE=xghMhd4pZ|a>!2Zl2)O=U;ymmlm#N-0Xr_=#Q zZD7Y7elB)NzQMW2MR7`qBK%Mhl>&vCVJ^zIXY{*_P{;K0!e z0N;%PLfQ9)Vq({v>*hs6sDP|;R$kJ`_+yy!ZtkGSAk5GX7zQU0-xNoexRYKt3Fu8$ z>I-W5A4N~to`zL!^3;LXfEK&(*+dP(!=r^{tepI?Ids4W*U^SVS+K@4pCe@6u25AG|#IDz~_HatPOouk;Ei}mYYz>AJzvK}= zyTPwB(R+X4*J@nO7v8;L2|e=3B9*#r%HHWQEtt02$j4-QLVleb*V1*Up;zJOMl=lJztRJ6E!2XLXaRvupPiD#LU{ zudZg2n_!xJbsm_|{{aOnH*zZR^HYl*g<>A8Pg&_EICH}Z9s_TlX*v}|l>@F5>z(?1 zmlV3t8`oz3rjKjFHCe8$4%XUJW0{oKWG6s+@R(!VggYzR{0WM^JdMU=Sz%wLe4=@- zN^w|vBv()HC-fzB6nx}@9ddIxzZT^vOfu%&aH7P%4_K;8iW#| zfWhy$(8y5C%G^+(*wx>Rv0jQ|*Uc8sHIV`MbRdSfb*KY*=8B|L&*EEC+%dx-=<^UX zE=u?+WM*o4KcpFWOssXCqRl2TE0ILrAL``uA!uAqMF)Zgbu2X%?OT|H7bL}3z(nk0VeV&kOG3JIqk?>it+?^KK$nBb! zxC5})m`(`PCJ4Z^jG7-TPI%dYdHi7^U%Kt8al5%Y)#{zX$hF)A)mjbhm#Ql28|}Jk zJyI9KO{{N?LZ1o*f6_*0Xyhc*(1AVUf^^_c!+#sS=~|*4-gGUA zxKD0Y8VLmEWZmpEax^*}c>3av<1}+==mjERu~-i8kQBCb{Px4;K9^bFrS9I8h>iho z9`6W@ezyFyVD#Y1UrrBDlH`bLElGD-OST3#zv26&<(9##>9+2Pl}!$l2FmGZwzlk| zUn8wKXQz?ytaiL@JTG&qkDry#tNGSXao(5GRu0sX55?<|Zk89Zxs7Z_B!%E$Fzf6t zu8wVm^ipW4vWm_02E9eowG$YGtzBtq=%`lbPDUOq^{r!b&H!e2;`_XdE~u8(N&%b55n%&) zmzWgqFG_@4^AR$~DSqU6S%-Ny?@Gav+Q=MTSOUj-t*=~?Ia$nbQ;!aWR+>1EemCsa zOLFb+z@bF>F39N_mf(4lDPSgAz!7w*uejKUM!S)9{?}lI%^+o)Itfh*Z>xX$jQeO1Yw(jgKJ~cKLRClMpYf|jo!G&@&smBHp`*Ks72Hjnl$b_~$XSQN#mC_>sZTU!41z?<2 z@46+gseB;PDf+q7^qB9RLHnj6q08D^0d4kXH|$&G;9Dnh2p87oY$M0wY5ws{yZA^S zaIIbkE2;oB7f);PJ60s{bL6dKusS30*H4W(t5U@$>ozN()~bN(3$?2duI)_hO)`1@ zccVgXVtq$0(MfNFLCCij z0|JQzgn{AZ_Bqt6HuHy(l-sa*I;OnP>JrP@JaZ~wD<3Y4UL#M%c`9SVMWy=wfs-Qj zQt|h`)zzSZ+m{-xj4Y@v>&qi1AbO#m`ZNY>ubHoCl^$R=C_Grw8r4$QrQWRk&{eFg%a8 z?bt_E)NXvGdkIx=3(;#E54Wtzy#xq#zI+mXh~I2%TmM)1bzqkSM=y^@R!U{Ph&uR3TnU6sx#5vcqv+s z7if*AOv`n`2x&N*w4=sNOUfJvHs;rpOx zJFSMc6tGik9KmJ4fM+Jy)&%NW1$8spr6#^3torZ`E$gr~T(_y$X03f4jXU$N*f zF$RJfp{&%Fg;Y=l<&^Xy>kphvi21kBiX7#=IoK1kdXYbpwpS=cHzEnojh#m$%1T{!B8j+sTOV)2$5+XyNCfL@^w{H^}3 z?2%*RlrhAL<_K}X4CS^|KKyi%ADwY(_;eduhCZO>S zTRb;S_F5(QI3`oKQ>fdO9o3hh{H*oN(=cE{-I`w*J75AIi336#9;wN>jC*8vx z54oTKqn=~o2oU1Cb8~urOk}|B8L8=$RtTfuruWehMGvsBG6`jyet6!j8sp7l%pXK~ zGE)Ux9~VM;?~npJXn04&TP=|d24t_bRxKYymn8Fg_ur3CnnttsJ;lQKdfkD6(ic!Am)5wD$M&7*O{UcNf#_B7#bj%xnkJ2Lj}m3^?KB!EJe`8?`9X%! zlA7-5BfzpuThMpYQ9IQ86rAQDC91IeL%@1R3!?3F`zEM0#VuAm`)xKD zzWqV|AZo_eP6s8*`3Tkz3n=HhfZct+WP?erDm zwNRBa)53xry~Ri+`uw5u&YcLjX>a`U=^bjAL*pAH;GY#5_x{k%6E<3bDKyajggCrF zFvLD&LkX;F4|2(?Oiwd;)!8Os7)i%X-_jm2(2zFjD>N*kWG=DeA*po|AOHvwI9e%C zdIXx2ATzI^Ki{WVhLvR<29k?iS0Y^=JJ}7Ihp<5!U&4 zrn?d1LW$wM7V=>a8DalTngyJ&f4J_n!U1@whyms$U&3+1FHf>w!3btx$QwNE?~-?I zksX}D{M~lfATxNTDwk|>>(DqXourN-cX}Ab=K<{LQV{?AVX|*Ov>ev2kXspr=hE<3 zr(tCI7I8gmLeP(T{-4~u482ZhZd|n|~e(L2wAsC3nDlqLG z)(^z6BDU}3%=rpJ){zP;gpEmj;4fn>(d~2Z+Bb9K`@{oY%IVJKV zDWvW91&S*7pEm#oPeV_r1g=^kwIc(PylSPMMAw!wW^28wCtx(> zza37U-;+dCp~Q*QmR8JT=bw9ROp-S{HV0|b_RGBkMPgf88{haahRR9fo;4zXv!7VQ z`J~n%rh@LOyW*my!D+DWIn^@+B;*Y$QEgKDeT&CwD#RVKo4t&USAJJiA3SQ@_Yk55a?I{2VK=xw{u`Qf+9s+4Elq!X8RD zfv}sBX42`lxj`{fhMOu*P3ssG-jx}PyOQuKjR;^sA{{>4c9+eLBV;i#x*;vj4=b){W}?9>-y zFb!zpDd&Ho$uPE~;X`&TC;i}lbuCyup@#!qJl=^E=(sVXO(tLRzzzM`QkRZh5>N{~ z!CHOL2|q)^_*P>z`4y(X^rnd9&34YmQI-Q->AKu|`<&}G?*+8s#y+T<^eJ<1Ck3zf zCXQ73pZ;#FqmVFC%)u_RjgDq%Lr@{oCSED5DQe&Wj7| z1h~zqe*#nHpdAcy`Ez}4T1hZQD3rhQQyZ&3f0dHbJ1Z=M?k@x`CsG;qmf6g@FytXx z_9);4z3(`>Q^PlD-r>n)AFDp}jKlg~(7W4HVRY>;URbm2MOv1yum z673^^bRcLyyC8#d|G)Rz!EP8Jn*Y+BQ&3av^FM*_gw&L&#w$mVS{YEiTF zk-Raxap!_itDDYXmT-=h-J!VN5evJS=Q+PzMi_8)4Z1CJR3ENo?k%T z5O<6swB+O-;2|6J1dRlXfXQKx3FtK@?BMc1OsNxUxkw2OhyW$P}dB;9HywHuBB4ZiNm4V;R@ zwAu&M8*9EF`)pnfcTiGk(!^;!DcX$RqNv9$gbf9bX_eq@*D+kyVZ}$s1fhHs>#aP4=P62A_|plw$V`=eW z;sZb*@+WNu?Pd02=8!N<5K>DaA>3%;ii#sUj`n%;hXh9zb42iDyPN7eqCft9J;ffmE|mRjwegLo%AXy zH%uzia1ds>cmQcWyArwDJ?>MkHo*U0JaK~3MJBi(pKG5xi%I&daTANo5tg=Q7PanT zK%HT~$NIn9_#*!}gv$-YZu|1LlAXk0GOT3h(Uw<@;O5ZGjvL7?};baz~ujRF}^rg ze%^7nd|%NVu(&koOEnrAA_^r1Y`;u?6ojGl&1SdZPaK4oL1{H#H4VRl9FTVnac{EU;l{U!Dp z7tOBzPj>__=D{iX4dHlG%hU3mS#;-lmnF*prl+v~ojOeiPAWk|{XXk-I<^i(ONLs_ zKn>`-of5(NZo?lA#j+W|bv8$=F?Z7CW#6~h=A(E>|Am+tCtlc4N21zWN3>-v1*mak z{!#z7?Glod5K9ta`BW$G#RZQld#{0h4l&?;kZIU%VC*|tKVLK9Em%o@4;yKv5@eE7 z05suzkWenm0IWCz7PYUvYjEX-n=9)f>--x6!JYHK zh0@YiucVynth37&`qDxxJgN5uELZLOkW)W_Sj!uJd=Y(bzl$GsC3RNTB8AhRIft!3 znft5tCq;3{sRh-^Nrmj3nIzphCO#t%+q$~fCj9`SUlMOv`xHz2n%}mrn7OXlB|!NW zKiHDueUq%sY1$P~0$;H`yz`2QPnF_0AIw$MlN3HyMp@nGcK$N?C0%4PUFc*rDdN1ma>+{95;TlC_DSiPhbHF5V>B)O*Kk`<|tgzo2c70t*cXZrc;Jp zALz|pxyv8dPa5HslL$R|YbNZrniaFRv#7jIzd6VkaeP53GZ zs7l!WMXpv1ldFwuWA3%o=eQ*RAk&{k#zNV__nFPQgYGBsNzXqJi5lGZ89vff1r6HX zEE6;s=|t~bb9wuHoiw>)?)%51jK^mS=J;Py`|z1U9Da_GT^B=i1cJiN(fIJQOOje( zSPs+&K`fL+6?+c)nP=@u(WDJZ4M=1+Ch1!K(OGU0qk&~l>wZKVaFMvf*rB&O1Nt!A zrdjU;J7mJ#-Ph=X5$g=b1Cs)ez1+{5-~2UX;0W!?MlJrS^94M(4fhTv5DB{P>u&bx zS1=lNYZG2CEQMJP$lRYSx2c#*Ti$wyDJBR07vkYh-5zpj|2Ad)kAC<+`r-e2!QX%M z!~f9_|3^RkAN}xu^uzzr5C2C${2%>rcg277!~gH~!#8kog$Yot0~(gSeR_7^JP|rY z>H@{{^UkP6sSQ{drnCSs!bfwe!M;;!amuO)xEWC>`Xd)%cQ`jW;OjZq2@$jg9sqI< z8jqMbf42V3MRuBTbzYNTme)XZ+fsTQ8BnDM_$PFZEIj5P6Uj3v1q z?lu2JE-V?&tFh43>JPw|HggQGiJ#IJRP5kP=MT-y++5dzB)0Z$9xHUb)<$0A{K;is z$3|Hwrlmbq?FN|tD+W8eJo&)0EvB4N4Zh8OW)rU^V!9t`&D2BI7>XwXsti_bi&c3# zG6aml^}^LMumeW*@lORZ3&lAekMBFb7sv_?{s#rJpg*qLHW$#aL(BB)`Lz^8$B=pG zoP+9JM=x{R4o*^Tto^yn)`;)}q`|jAr>ww!?3o7Ger#2+z69;_5ll z!dd?M5TP&T8aTvY!(Cs+t*wpLd(g?bivxFh0b-k!SEK&t>ppKLBep5y#Tn)3% zu4`X!&d1FQ++lP@hzqa#4NTSlNoCE>jiZ8_1rjtI}f`t_<%UHNr)7ko~qZV zr0eks%j!ghKkJiMgVh(HRp%?O1~)TogCRag0k~MO0cdzI_wZ@_ zpiy+C=X0*P#&6`avuE^UTYBdl7i2(q`$~)bh-m~A@O7ugM=V)cE!QZQf%Wp`1#GDT zkTCy~$*h4|r4Z0IWW9iC8w@u5Tr?c6N%a%x;?nv2>A0tq422}sDOB=B6MCO`HNw_v zzbBl(?TiuVm~FM+*U#;9yS$#bxDJ0_c4>a;ejN6CArkG`g_yj_O|3aZvEbMHAx+^pIEp7UB_c;f(GnS(}|xj4}}z1X^Fta@ld zA(O9>y(siuxFd9%tL`x-l#tN~YI@@_F2qL9$7H>>r>J5SI%F;==54}w&k*!7piAd@ z^WA#WitwC+r`fv{yP{`#oLBk#wmV?!hGKiShD`$ZYn043ZQ|J!SZTwSotAP&~?c*0mM<9&bVABGECqS>@-uI@6pvF6ZLrgz^NPAPAq^}E}R4Y=(I)A@S@ zeu#9eSR3JZds?obdJ!IJyJbOTp2`wVkz(l{mB-T$BvYAWbrwk2F7=*=b&b_m_MRp8 zxHYsdscpw)rRT!0Mj@`8IR@v{y+~zGf4W3MPlAV&>;ijj5YWEb-K`BmPvzDb(ajGJ zygeM<9||=4)OZSU8C$K z^;qUL(dkdR#{vZlRHW352l=;TwuP||m}6{&Hsy*5zP%%S!ZCXSd``p`OCcYT=VT9U z(OF@ms2R^UJV92wrukbCIDzX^%8R2b&79 z4y$m<@rLiQFg|5@_E}N*6 zA%qm8BG9~18NZr{vFdPy(^&KJ3Y|$+GJSRBjy`^}$Pn_Q2H$Jn@JrFF6i}mdZVN08 z#-r9a0Mg(~@)1}X?CGw2TtBFT_meurVsbLzvPZgjQJ^hsAbKp#nQdjw9|2{1z6y5C zY9BYkdq9rw@Y&CZ>*({k;=a|~6KcZR*8I_YY9%8D6w+|9fkN6i^2x_@aC|0zNL4*b z7U?s;SKq>vPuzXcmG{i*2<{^0<6qeSc%FLE6n~$TPx=c98Ux_&?mX9=sv598 zf+yqE>@$kg{MdgcYB&HY^=Qm|vMJ`J6>L?BolrQ8^2E?oN|ZICS@1(}iCuj2V=18C)L2!r@D--t6?x1KKHgvp-_aBL?H6N#sS%&-F5o_Cv_ zaP6INQKr25?H3E1?@0-gO+?RSDeLaQnli#WQGB&`9vz&vH0y_)9OH2QDzZsJ&xn4x zZ~2@Z5CNnT_L_;2;b8xbus_Pb{No$2Q?h63CL0!+zXr=3IEq+Kk;l4N(1zSx=Es*^Z67A4UBaoj#yZ-|tVN1OS`y&FM4Z&sbZ2djdJ7$dIujhC) z66t1lj_Y=0boC~cf!FymCly}M_bovg+l;Oqnr1o7M_@wUsv>^1y|v6T3GRRx`sPhJ z9Lw=?a(Flu(ndGv%WxlW=>#jK=k2(Ba+}VTMT02pa7n8+#7Ik#ij0H28uSZyOK4LW z4`ZBc&_i{NpO-VXvy7WD)G+wC zUOf6)e-$L(LiVf7^ym*ed zhOVSRUVMgx5L-YiJq!qNyH!JM72k&`odL0D{oMU3^ z<8lzP2kAp|KAwo973!T|IIwIBN@kvRlO4Q0pIi%E(jvBM!Jm9RuHRBPWy=!i*^__e ztTH5i;#18{SOwfxSqLh;P0)-;1pa~QGm71l(4nV9ww5`FXXks?Xr7@ z?vCr!8p*bWIJ;G5dguLs05>Xa<8ApL|i*|?b?t3AW1MlLUMY}PfN zbhc_WWh9e&D%OnC$)y+lD`Zuub==N{Ww?!}s%V~PVgyI{b|D`Je&{@{OjnH)V*DB6 zWV6bQ67cG^vMfTa67>&em8gqR|2k|s@E4z}ZhXsw!TSYuZu!xH&)p6)jHrZ)#d7@_ zOH+PWZ5d7-lR_;Uj$h7MYD!nXksXDSIN7tY-D7^cE{_O#g>?+dMBE@e#QRa5Tfpy1dS^(io+1H%0DPMNquW$QJU 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 literal 243325 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 YGpZ{WUwnAO>?a3DJ6GEV>kG;M1s9dtZvX%Q diff --git a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c index 8fc455da7..a2c96e15e 100644 --- a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c +++ b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c @@ -57,7 +57,7 @@ int InsertNode(radix_node *root, unsigned int key, void *value) return -3; // Repeat insertion if (cur->value != NULL) return -4; // Already occupied - cur->value == value; + cur->value = value; return 0; } @@ -159,9 +159,9 @@ void TestRadix() for (int i = 0; i < num; ++i) { - int *v = (int *)FindNode(root, keys[i]); + char *v = (char *)FindNode(root, keys[i]); if (v) - printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, *v); + printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, v); else printf("keys[%d] %x not found\n", i, keys[i]); } @@ -172,9 +172,9 @@ void TestRadix() for (int i = 0; i < num; ++i) { - int *v = (int *)FindNode(root, keys[i]); + char *v = (char *)FindNode(root, keys[i]); if (v) - printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, *v); + printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, v); else printf("keys[%d] %x not found\n", i, keys[i]); } @@ -185,9 +185,9 @@ void TestRadix() for (int i = 0; i < num; ++i) { - int *v = (int *)FindNode(root, keys[i]); + char *v = (char *)FindNode(root, keys[i]); if (v) - printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, *v); + printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, v); else printf("keys[%d] %x not found\n", i, keys[i]); } @@ -198,9 +198,9 @@ void TestRadix() for (int i = 0; i < num; ++i) { - int *v = (int *)FindNode(root, keys[i]); + char *v = (char *)FindNode(root, keys[i]); if (v) - printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, *v); + printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, v); else printf("keys[%d] %x not found\n", i, keys[i]); } From 3efe663ee0368742b45f67125f7699650b8bc155 Mon Sep 17 00:00:00 2001 From: Liu_Weichao Date: Thu, 3 Aug 2023 15:40:11 +0800 Subject: [PATCH 08/33] update lorawan submodule commit --- APP_Framework/lib/lorawan/lora_radio_driver | 2 +- APP_Framework/lib/lorawan/lorawan_devicenode | 2 +- Ubiquitous/XiZi_IIoT/board/cortex-m3-emulator/README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/APP_Framework/lib/lorawan/lora_radio_driver b/APP_Framework/lib/lorawan/lora_radio_driver index d21965b1c..a94c007cb 160000 --- a/APP_Framework/lib/lorawan/lora_radio_driver +++ b/APP_Framework/lib/lorawan/lora_radio_driver @@ -1 +1 @@ -Subproject commit d21965b1cbcfa99b2d36acd029a37f3f2eba612e +Subproject commit a94c007cb4ee726cc29b10626f8bbfc19c989b89 diff --git a/APP_Framework/lib/lorawan/lorawan_devicenode b/APP_Framework/lib/lorawan/lorawan_devicenode index 2896d7234..254754bc7 160000 --- a/APP_Framework/lib/lorawan/lorawan_devicenode +++ b/APP_Framework/lib/lorawan/lorawan_devicenode @@ -1 +1 @@ -Subproject commit 2896d7234688de77992e7e1872a7e67a9456b420 +Subproject commit 254754bc7d06011cbec4655cd229c8ccfb95240b diff --git a/Ubiquitous/XiZi_IIoT/board/cortex-m3-emulator/README.md b/Ubiquitous/XiZi_IIoT/board/cortex-m3-emulator/README.md index 3045cf738..8837deb62 100644 --- a/Ubiquitous/XiZi_IIoT/board/cortex-m3-emulator/README.md +++ b/Ubiquitous/XiZi_IIoT/board/cortex-m3-emulator/README.md @@ -188,7 +188,7 @@ sudo apt install gdb-multiarch qemu-system-arm -machine lm3s6965evb -nographic -kernel build/XiZi-cortex-m3-emulator.elf -s -S ``` -然后要重新开启另一个linux系统终端一个终端,执行`riscv-none-embed-gdb`命令 +然后要重新开启另一个linux系统终端一个终端,执行命令 ``` gdb-multiarch build/XiZi-cortex-m3-emulator.elf -ex "target remote localhost:1234" From 0307fb2671b95925672960a7ee84c32604d8dc6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Thu, 27 Jul 2023 15:05:50 +0800 Subject: [PATCH 09/33] 23/07/27 1.Add netdev module and for edu-arm32, fit Lwip to it; 2.Fit Webnet to edu-arm32; --- APP_Framework/Applications/Makefile | 4 + APP_Framework/Applications/benchmark/Makefile | 16 + APP_Framework/Applications/webnet/Makefile | 3 + .../user_api/posix_support/include/mqueue.h | 3 +- .../xizi/user_api/posix_support/mqueue.c | 9 +- .../applications/benchmark/Makefile | 16 + .../third_party_driver/ethernet/Makefile | 2 +- .../third_party_driver/ethernet/eth_driver.c | 82 +-- .../third_party_driver/ethernet/eth_netdev.c | 162 +++++ .../third_party_driver/ethernet/ethernetif.c | 99 +-- .../include/connect_ethernet.h | 64 +- .../include/connect_ethernet.h | 36 +- Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h | 196 +++--- Ubiquitous/XiZi_IIoT/kernel/thread/delay.c | 2 +- Ubiquitous/XiZi_IIoT/kernel/thread/msgqueue.c | 85 +-- Ubiquitous/XiZi_IIoT/path_app.mk | 4 + Ubiquitous/XiZi_IIoT/path_kernel.mk | 9 + .../resources/ethernet/LwIP/arch/sys_arch.c | 630 +++++++++--------- .../ethernet/LwIP/core/ipv4/ip4_addr.c | 2 +- .../XiZi_IIoT/resources/ethernet/Makefile | 2 +- .../ethernet/cmd_lwip/lwip_config_demo.c | 81 ++- .../resources/ethernet/netdev/Makefile | 4 + .../ethernet/netdev/netdev_ipaddr_util.c | 0 .../ethernet/netdev/netdev_lowlevel.c | 220 ++++++ .../ethernet/netdev/netdev_manipulate.c | 288 ++++++++ .../ethernet/netdev/netdev_register.c | 184 +++++ .../resources/include/netdev/netdev.h | 182 +++++ .../resources/include/netdev/netdev_ipaddr.h | 124 ++++ .../XiZi_IIoT/tool/shell/letter-shell/cmd.c | 2 +- 29 files changed, 1884 insertions(+), 627 deletions(-) create mode 100644 APP_Framework/Applications/benchmark/Makefile create mode 100644 APP_Framework/Applications/webnet/Makefile create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/benchmark/Makefile create mode 100644 Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_netdev.c create mode 100644 Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/Makefile create mode 100644 Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_ipaddr_util.c create mode 100644 Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_lowlevel.c create mode 100644 Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_manipulate.c create mode 100644 Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_register.c create mode 100644 Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h create mode 100644 Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev_ipaddr.h diff --git a/APP_Framework/Applications/Makefile b/APP_Framework/Applications/Makefile index 0f0d2a744..334845b8a 100644 --- a/APP_Framework/Applications/Makefile +++ b/APP_Framework/Applications/Makefile @@ -36,5 +36,9 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) SRC_DIR += control_app endif + ifeq ($(CONFIG_APP_USING_WEBNET),y) + SRC_DIR += webnet + endif + include $(KERNEL_ROOT)/compiler.mk endif \ No newline at end of file diff --git a/APP_Framework/Applications/benchmark/Makefile b/APP_Framework/Applications/benchmark/Makefile new file mode 100644 index 000000000..d9d5e47dc --- /dev/null +++ b/APP_Framework/Applications/benchmark/Makefile @@ -0,0 +1,16 @@ + +# include $(APPDIR)/Application.mk +# ifeq ($(CONFIG_ADD_NUTTX_FETURES),y) + # include $(APPDIR)/Make.defs + # CSRCS += $(wildcard src/*/*.c) $(wildcard support/*.c) + # include $(APPDIR)/Application.mk +# endif + +# ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) + SRC_DIR := + ifeq ($(CONFIG_APP_BENCHMARK), y) + SRC_DIR += src + SRC_DIR += support + endif + include $(KERNEL_ROOT)/compiler.mk +# endif diff --git a/APP_Framework/Applications/webnet/Makefile b/APP_Framework/Applications/webnet/Makefile new file mode 100644 index 000000000..8eca9e823 --- /dev/null +++ b/APP_Framework/Applications/webnet/Makefile @@ -0,0 +1,3 @@ +SRC_DIR += WebNet_XiUOS + +include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/APP_Framework/Framework/transform_layer/xizi/user_api/posix_support/include/mqueue.h b/APP_Framework/Framework/transform_layer/xizi/user_api/posix_support/include/mqueue.h index ae13f6667..6ac021b36 100644 --- a/APP_Framework/Framework/transform_layer/xizi/user_api/posix_support/include/mqueue.h +++ b/APP_Framework/Framework/transform_layer/xizi/user_api/posix_support/include/mqueue.h @@ -40,7 +40,8 @@ struct mq_attr { long mq_curmsgs; /* number of messages currently queued */ }; -mqd_t mq_open(const char *name, int oflag, ...); +// mqd_t mq_open(const char *name, int oflag, ...); +mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr); int mq_close(mqd_t mqdes); ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio); int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio); diff --git a/APP_Framework/Framework/transform_layer/xizi/user_api/posix_support/mqueue.c b/APP_Framework/Framework/transform_layer/xizi/user_api/posix_support/mqueue.c index e0f5e9507..a92dc95f6 100644 --- a/APP_Framework/Framework/transform_layer/xizi/user_api/posix_support/mqueue.c +++ b/APP_Framework/Framework/transform_layer/xizi/user_api/posix_support/mqueue.c @@ -21,12 +21,13 @@ #include "include/mqueue.h" -mqd_t mq_open(const char *name, int oflag, ...) +mqd_t mq_open(const char* name, int oflag, mode_t mode, struct mq_attr* attr) { mqd_t mq; - mq = UserMsgQueueCreate( DEFAULT_MQUEUE_SIZE, DEFAULT_MAX_MSG_SIZE); + // Todo: config mq by mode + mq = UserMsgQueueCreate(attr->mq_msgsize, attr->mq_maxmsg); if (mq < 0) { return -1; } @@ -39,12 +40,12 @@ int mq_close(mqd_t mqdes) return UserMsgQueueDelete(mqdes); } -ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio) +ssize_t mq_receive(mqd_t mqdes, char* msg_ptr, size_t msg_len, unsigned* msg_prio) { ssize_t ret; *msg_prio = 0; - ret = UserMsgQueueRecv(mqdes, msg_ptr, (unsigned long)&msg_len, 0); + ret = UserMsgQueueRecv(mqdes, (void*)msg_ptr, msg_len, 100000); return ret; } diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/benchmark/Makefile b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/benchmark/Makefile new file mode 100644 index 000000000..0c8c2bfcf --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/benchmark/Makefile @@ -0,0 +1,16 @@ + +# include $(APPDIR)/Application.mk +ifeq ($(CONFIG_ADD_NUTTX_FETURES),y) + include $(APPDIR)/Make.defs + CSRCS += $(wildcard src/*/*.c) $(wildcard support/*.c) + include $(APPDIR)/Application.mk +endif + +ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) + SRC_DIR := + ifeq ($(CONFIG_APP_BENCHMARK), y) + SRC_DIR += src + SRC_DIR += support + endif + include $(KERNEL_ROOT)/compiler.mk +endif diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/Makefile b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/Makefile index b63aff8c2..385b69d4f 100755 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/Makefile +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/Makefile @@ -1,3 +1,3 @@ -SRC_FILES := ethernetif.c eth_driver.c +SRC_FILES := ethernetif.c eth_driver.c eth_netdev.c include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_driver.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_driver.c index 69a76fa0a..800c08697 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_driver.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_driver.c @@ -1,21 +1,22 @@ /** -* @file ethernetif.c -* @brief support edu-arm32-board ethernetif function and register to Lwip -* @version 3.0 -* @author AIIT XUOS Lab -* @date 2022-12-05 -*/ + * @file ethernetif.c + * @brief support hc32f4a0-board ethernetif function and register to Lwip + * @version 3.0 + * @author AIIT XUOS Lab + * @date 2022-12-05 + */ #include +#include #include #include -#include #include #include #include -void eth_irq_handler(void) { +void eth_irq_handler(void) +{ static x_base eth_irq_lock; eth_irq_lock = DISABLE_INTERRUPT(); @@ -24,7 +25,7 @@ void eth_irq_handler(void) { sys_sem_signal(get_eth_recv_sem()); ETH_DMA_ClearStatus(ETH_DMA_FLAG_RIS | ETH_DMA_FLAG_NIS); } - + ENABLE_INTERRUPT(eth_irq_lock); } @@ -35,7 +36,7 @@ void eth_irq_handler(void) { * - LL_OK: Initialize success * - LL_ERR: Initialize failed */ -int32_t low_level_init(struct netif *netif) +int32_t low_level_init(struct netif* netif) { int32_t i32Ret = LL_ERR; stc_eth_init_t stcEthInit; @@ -52,9 +53,9 @@ int32_t low_level_init(struct netif *netif) (void)ETH_StructInit(&stcEthInit); #ifdef ETH_INTERFACE_RMII - EthHandle.stcCommInit.u32Interface = ETH_MAC_IF_RMII; + EthHandle.stcCommInit.u32Interface = ETH_MAC_IF_RMII; #else - EthHandle.stcCommInit.u32Interface = ETH_MAC_IF_MII; + EthHandle.stcCommInit.u32Interface = ETH_MAC_IF_MII; #endif // stcEthInit.stcMacInit.u32ReceiveAll = ETH_MAC_RX_ALL_ENABLE; EthHandle.stcCommInit.u32ReceiveMode = ETH_RX_MD_INT; @@ -125,7 +126,7 @@ int32_t low_level_init(struct netif *netif) u16RegVal = PHY_PAGE_ADDR_0; (void)ETH_PHY_WriteReg(&EthHandle, PHY_PSR, u16RegVal); #endif - + return i32Ret; } @@ -137,19 +138,19 @@ int32_t low_level_init(struct netif *netif) * - LL_OK: The packet could be sent * - LL_ERR: The packet couldn't be sent */ -err_t low_level_output(struct netif *netif, struct pbuf *p) +err_t low_level_output(struct netif* netif, struct pbuf* p) { err_t i32Ret; - struct pbuf *q; - uint8_t *txBuffer; - __IO stc_eth_dma_desc_t *DmaTxDesc; + struct pbuf* q; + uint8_t* txBuffer; + __IO stc_eth_dma_desc_t* DmaTxDesc; uint32_t byteCnt; uint32_t frameLength = 0UL; uint32_t bufferOffset; uint32_t payloadOffset; DmaTxDesc = EthHandle.stcTxDesc; - txBuffer = (uint8_t *)((EthHandle.stcTxDesc)->u32Buf1Addr); + txBuffer = (uint8_t*)((EthHandle.stcTxDesc)->u32Buf1Addr); bufferOffset = 0UL; /* Copy frame from pbufs to driver buffers */ for (q = p; q != NULL; q = q->next) { @@ -165,28 +166,28 @@ err_t low_level_output(struct netif *netif, struct pbuf *p) /* Check if the length of data to copy is bigger than Tx buffer size */ while ((byteCnt + bufferOffset) > ETH_TX_BUF_SIZE) { /* Copy data to Tx buffer*/ - (void)memcpy((uint8_t *) & (txBuffer[bufferOffset]), (uint8_t *) & (((uint8_t *)q->payload)[payloadOffset]), (ETH_TX_BUF_SIZE - bufferOffset)); + (void)memcpy((uint8_t*)&(txBuffer[bufferOffset]), (uint8_t*)&(((uint8_t*)q->payload)[payloadOffset]), (ETH_TX_BUF_SIZE - bufferOffset)); /* Point to next descriptor */ - DmaTxDesc = (stc_eth_dma_desc_t *)(DmaTxDesc->u32Buf2NextDescAddr); + DmaTxDesc = (stc_eth_dma_desc_t*)(DmaTxDesc->u32Buf2NextDescAddr); /* Check if the buffer is available */ if (0UL != (DmaTxDesc->u32ControlStatus & ETH_DMA_TXDESC_OWN)) { i32Ret = (err_t)ERR_USE; goto error; } - txBuffer = (uint8_t *)(DmaTxDesc->u32Buf1Addr); + txBuffer = (uint8_t*)(DmaTxDesc->u32Buf1Addr); byteCnt = byteCnt - (ETH_TX_BUF_SIZE - bufferOffset); payloadOffset = payloadOffset + (ETH_TX_BUF_SIZE - bufferOffset); frameLength = frameLength + (ETH_TX_BUF_SIZE - bufferOffset); bufferOffset = 0UL; } /* Copy the remaining bytes */ - (void)memcpy((uint8_t *) & (txBuffer[bufferOffset]), (uint8_t *) & (((uint8_t *)q->payload)[payloadOffset]), byteCnt); + (void)memcpy((uint8_t*)&(txBuffer[bufferOffset]), (uint8_t*)&(((uint8_t*)q->payload)[payloadOffset]), byteCnt); bufferOffset = bufferOffset + byteCnt; frameLength = frameLength + byteCnt; } /* Prepare transmit descriptors to give to DMA */ - if(LL_OK != ETH_DMA_SetTransFrame(&EthHandle, frameLength)) { + if (LL_OK != ETH_DMA_SetTransFrame(&EthHandle, frameLength)) { KPrintf("[%s] Error sending eth DMA frame\n", __func__); } i32Ret = (err_t)ERR_OK; @@ -208,13 +209,13 @@ error: * @param netif The network interface structure for this ethernetif. * @retval A pbuf filled with the received packet (including MAC header) or NULL on memory error. */ -struct pbuf *low_level_input(struct netif *netif) +struct pbuf* low_level_input(struct netif* netif) { - struct pbuf *p = NULL; - struct pbuf *q; + struct pbuf* p = NULL; + struct pbuf* q; uint32_t len; - uint8_t *rxBuffer; - __IO stc_eth_dma_desc_t *DmaRxDesc; + uint8_t* rxBuffer; + __IO stc_eth_dma_desc_t* DmaRxDesc; uint32_t byteCnt; uint32_t bufferOffset; uint32_t payloadOffset; @@ -227,7 +228,7 @@ struct pbuf *low_level_input(struct netif *netif) /* Obtain the size of the packet */ len = (EthHandle.stcRxFrame).u32Len; - rxBuffer = (uint8_t *)(EthHandle.stcRxFrame).u32Buf; + rxBuffer = (uint8_t*)(EthHandle.stcRxFrame).u32Buf; if (len > 0UL) { /* Allocate a pbuf chain of pbufs from the buffer */ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); @@ -243,17 +244,17 @@ struct pbuf *low_level_input(struct netif *netif) /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size */ while ((byteCnt + bufferOffset) > ETH_RX_BUF_SIZE) { /* Copy data to pbuf */ - (void)memcpy((uint8_t *) & (((uint8_t *)q->payload)[payloadOffset]), (uint8_t *) & (rxBuffer[bufferOffset]), (ETH_RX_BUF_SIZE - bufferOffset)); + (void)memcpy((uint8_t*)&(((uint8_t*)q->payload)[payloadOffset]), (uint8_t*)&(rxBuffer[bufferOffset]), (ETH_RX_BUF_SIZE - bufferOffset)); /* Point to next descriptor */ - DmaRxDesc = (stc_eth_dma_desc_t *)(DmaRxDesc->u32Buf2NextDescAddr); - rxBuffer = (uint8_t *)(DmaRxDesc->u32Buf1Addr); + DmaRxDesc = (stc_eth_dma_desc_t*)(DmaRxDesc->u32Buf2NextDescAddr); + rxBuffer = (uint8_t*)(DmaRxDesc->u32Buf1Addr); byteCnt = byteCnt - (ETH_RX_BUF_SIZE - bufferOffset); payloadOffset = payloadOffset + (ETH_RX_BUF_SIZE - bufferOffset); bufferOffset = 0UL; } /* Copy remaining data in pbuf */ - (void)memcpy((uint8_t *) & (((uint8_t *)q->payload)[payloadOffset]), (uint8_t *) & (rxBuffer[bufferOffset]), byteCnt); + (void)memcpy((uint8_t*)&(((uint8_t*)q->payload)[payloadOffset]), (uint8_t*)&(rxBuffer[bufferOffset]), byteCnt); bufferOffset = bufferOffset + byteCnt; } } @@ -261,7 +262,7 @@ struct pbuf *low_level_input(struct netif *netif) DmaRxDesc = (EthHandle.stcRxFrame).pstcFSDesc; for (i = 0UL; i < (EthHandle.stcRxFrame).u32SegCount; i++) { DmaRxDesc->u32ControlStatus |= ETH_DMA_RXDESC_OWN; - DmaRxDesc = (stc_eth_dma_desc_t *)(DmaRxDesc->u32Buf2NextDescAddr); + DmaRxDesc = (stc_eth_dma_desc_t*)(DmaRxDesc->u32Buf2NextDescAddr); } /* Clear Segment_Count */ (EthHandle.stcRxFrame).u32SegCount = 0UL; @@ -277,11 +278,10 @@ struct pbuf *low_level_input(struct netif *netif) return p; } -extern void LwipSetIPTest(int argc, char *argv[]); -int HwEthInit(void) { -// lwip_config_tcp(0, lwip_ipaddr, lwip_netmask, lwip_gwaddr); - LwipSetIPTest(1, NULL); - return EOK; +extern void LwipSetIPTest(int argc, char* argv[]); +int HwEthInit(void) +{ + // lwip_config_tcp(0, lwip_ipaddr, lwip_netmask, lwip_gwaddr); + LwipSetIPTest(1, NULL); + return EOK; } - - diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_netdev.c new file mode 100644 index 000000000..145ebc620 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_netdev.c @@ -0,0 +1,162 @@ + +#include +#include +#include +#include +#include +#include + +static const uint32_t NETIF_NAME_LEN = 2; + +static int lwip_netdev_set_up(struct netdev* netdev) +{ + netif_set_up((struct netif*)netdev->user_data); + return ERR_OK; +} + +static int lwip_netdev_set_down(struct netdev* netif) +{ + netif_set_down((struct netif*)netif->user_data); + return ERR_OK; +} + +#ifndef ip_2_ip4 +#define ip_2_ip4(ipaddr) (ipaddr) +#endif +static int lwip_netdev_set_addr_info(struct netdev* netdev, ip_addr_t* ip_addr, ip_addr_t* netmask, ip_addr_t* gw) +{ + if (ip_addr && netmask && gw) { + netif_set_addr((struct netif*)netdev->user_data, ip_2_ip4(ip_addr), ip_2_ip4(netmask), ip_2_ip4(gw)); + } else { + if (ip_addr) { + netif_set_ipaddr((struct netif*)netdev->user_data, ip_2_ip4(ip_addr)); + } + if (netmask) { + netif_set_netmask((struct netif*)netdev->user_data, ip_2_ip4(netmask)); + } + if (gw) { + netif_set_gw((struct netif*)netdev->user_data, ip_2_ip4(gw)); + } + } +} + +#ifdef LWIP_DNS +static int lwip_netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, ip_addr_t* dns_server) +{ +#if LWIP_VERSION_MAJOR == 1U /* v1.x */ + extern void dns_setserver(u8_t numdns, ip_addr_t * dnsserver); +#else /* >=2.x */ + extern void dns_setserver(uint8_t dns_num, const ip_addr_t* dns_server); +#endif /* LWIP_VERSION_MAJOR == 1U */ + + dns_setserver(dns_num, dns_server); + return ERR_OK; +} +#endif + +#ifdef LWIP_DHCP +static int lwip_netdev_set_dhcp(struct netdev* netdev, bool is_enabled) +{ + netdev_low_level_set_dhcp_status(netdev, is_enabled); + + if (true == is_enabled) { + dhcp_start((struct netif*)netdev->user_data); + } else { + dhcp_stop((struct netif*)netdev->user_data); + } + + return ERR_OK; +} +#endif + +static int lwip_netdev_set_default(struct netdev* netdev) +{ + netif_set_default((struct netif*)netdev->user_data); + return ERR_OK; +} + +static const struct netdev_ops lwip_netdev_ops = { + .set_up = lwip_netdev_set_up, + .set_down = lwip_netdev_set_down, + .set_addr_info = lwip_netdev_set_addr_info, +#ifdef LWIP_DNS + .set_dns_server = lwip_netdev_set_dns_server, +#endif +#ifdef LWIP_DHCP + .set_dhcp = lwip_netdev_set_dhcp, +#endif + .set_default = lwip_netdev_set_default, +}; + +static inline int netdev_set_flags(struct netif* lwip_netif) +{ + CHECK(lwip_netif); + struct netdev* netdev = netdev_get_by_name(lwip_netif->name); + if (netdev == NULL) { + return -ERR_IF; + } + + netdev->mtu = lwip_netif->mtu; + // set flags + if (lwip_netif->flags | NETIF_FLAG_BROADCAST) { + netdev->flags |= NETDEV_FLAG_BROADCAST; + } + if (lwip_netif->flags | NETIF_FLAG_ETHARP) { + netdev->flags |= NETDEV_FLAG_ETHARP; + } + if (lwip_netif->flags | NETIF_FLAG_IGMP) { + netdev->flags |= NETDEV_FLAG_IGMP; + } +#if LWIP_VERSION_MAJOR >= 2U /* >= v2.x */ + if (lwip_netif->flags & NETIF_FLAG_MLD6) { + netdev->flags |= NETDEV_FLAG_MLD6; + } +#endif /* LWIP_VERSION_MAJOR >= 2U */ + +#if LWIP_DHCP + netdev_low_level_set_dhcp_status(netdev, true); +#else + netdev_low_level_set_dhcp_status(netdev, false); +#endif + + return ERR_OK; +} + +int lwip_netdev_add(struct netif* lwip_netif) +{ + CHECK(lwip_netif); + + struct netdev* netdev = calloc(1, sizeof(struct netdev)); + if (netdev == NULL) { + return -ERR_IF; + } + + // init netdev + char netif_name[NETIF_NAME_LEN + 1]; + strncpy(netif_name, lwip_netif->name, NETIF_NAME_LEN); + // register netdev + int result = netdev_register(netdev, netif_name, (void*)lwip_netif); + // set values of netdev + netdev_set_flags(lwip_netif); + netdev->ops = &lwip_netdev_ops; + netdev->hwaddr_len = lwip_netif->hwaddr_len; + memcpy(netdev->hwaddr, lwip_netif->hwaddr, lwip_netif->hwaddr_len); + netdev->ip_addr = lwip_netif->ip_addr; + netdev->gw = lwip_netif->gw; + netdev->netmask = lwip_netif->netmask; + + return result; +} + +void lwip_netdev_del(struct netif* lwip_netif) +{ + char name[NETIF_NAME_LEN + 1]; + struct netdev* netdev; + + CHECK(lwip_netif); + + strncpy(name, lwip_netif->name, NETIF_NAME_LEN); + netdev = netdev_get_by_name(name); + netdev_unregister(netdev); + free(netdev); +} \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/ethernetif.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/ethernetif.c index 1e50cdfab..7a50d2315 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/ethernetif.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/ethernetif.c @@ -19,21 +19,21 @@ */ /** -* @file ethernetif.c -* @brief support edu-arm32-board ethernetif function and register to Lwip -* @version 3.0 -* @author AIIT XUOS Lab -* @date 2022-12-05 -*/ + * @file ethernetif.c + * @brief support hc32f4a0-board ethernetif function and register to Lwip + * @version 3.0 + * @author AIIT XUOS Lab + * @date 2022-12-05 + */ /************************************************* File name: ethernetif.c Description: support edu-arm32-board ethernetif configure and register to Lwip Others: take projects\ev_hc32f4a0_lqfp176\examples\eth\eth_loopback\source\ethernetif.c for references -History: +History: 1. Date: 2022-12-05 Author: AIIT XUOS Lab -Modification: +Modification: 1、include harware_ethernetif.h、hc32_ll_eth.h、hc32_ll_gpio.h、hc32_ll_utility.h、hc32_ll_fcg.h and lwip H files; 2、modify ethernetif_init as err_t; 3、add ETH_RST_PORT and ETH_RST_PIN; @@ -47,13 +47,15 @@ Modification: * Include files ******************************************************************************/ #include +#include #include #include -#include #include #include +#include #include +#include /** * @addtogroup HC32F4A0_DDL_Examples @@ -73,7 +75,6 @@ Modification: * Local pre-processor symbols/macros ('#define') ******************************************************************************/ - /******************************************************************************* * Global variable definitions (declared in header file with 'extern') ******************************************************************************/ @@ -86,7 +87,6 @@ Modification: * Local variable definitions ('static') ******************************************************************************/ - /******************************************************************************* * Function implementation - global ('extern') and local ('static') ******************************************************************************/ @@ -180,12 +180,14 @@ void Ethernet_GpioInit(void) #endif } -void *ethernetif_config_enet_set(uint8_t enet_port) { +void* ethernetif_config_enet_set(uint8_t enet_port) +{ return NONE; } -void Time_Update_LwIP(void) { - //no need to do +void Time_Update_LwIP(void) +{ + // no need to do } /** @@ -195,7 +197,7 @@ void Time_Update_LwIP(void) { * - LL_OK: The IF is initialized * - LL_ERR: The IF is uninitialized */ -err_t ethernetif_init(struct netif *netif) +err_t ethernetif_init(struct netif* netif) { #if LWIP_NETIF_HOSTNAME /* Initialize interface hostname */ @@ -206,15 +208,24 @@ err_t ethernetif_init(struct netif *netif) #ifndef ETHERNET_LOOPBACK_TEST /* We directly use etharp_output() here to save a function call. - * You can instead declare your own function an call etharp_output() - * from it if you have to do some checks before sending (e.g. if link - * is available...) */ + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ netif->output = etharp_output; netif->linkoutput = low_level_output; #endif /* initialize the hardware */ - return low_level_init(netif); + if (LL_OK != low_level_init(netif)) { + return LL_ERR; + } + + if (EOK != lwip_netdev_add(netif)) { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] LWIP add netdev failed.\n", __func__)); + } else { + printf("[%s] Add Netdev successful\n", __func__); + } + return LL_OK; } /** @@ -222,16 +233,16 @@ err_t ethernetif_init(struct netif *netif) * @param netif The network interface structure for this ethernetif. * @retval None */ -void ethernetif_input(void *netif_arg) +void ethernetif_input(void* netif_arg) { - struct pbuf *p; - struct netif *netif = (struct netif *)netif_arg; + struct pbuf* p; + struct netif* netif = (struct netif*)netif_arg; x_base critical_lock; /* Move received packet into a new pbuf */ while (1) { sys_arch_sem_wait(get_eth_recv_sem(), WAITING_FOREVER); - while(1) { + while (1) { p = low_level_input(netif); #ifndef ETHERNET_LOOPBACK_TEST /* Entry point to the LwIP stack */ @@ -261,7 +272,7 @@ void ethernetif_input(void *netif_arg) * @param netif the network interface * @retval None */ -void EthernetIF_CheckLink(struct netif *netif) +void EthernetIF_CheckLink(struct netif* netif) { uint16_t u16RegVal = 0U; static uint8_t u8PreStatus = 0U; @@ -296,7 +307,7 @@ void EthernetIF_CheckLink(struct netif *netif) * @param netif The network interface. * @retval None */ -void EthernetIF_UpdateLink(struct netif *netif) +void EthernetIF_UpdateLink(struct netif* netif) { uint16_t u16RegVal; @@ -337,7 +348,7 @@ void EthernetIF_UpdateLink(struct netif *netif) * @param netif The network interface * @retval None */ -void EthernetIF_PeriodicHandle(struct netif *netif) +void EthernetIF_PeriodicHandle(struct netif* netif) { #ifndef ETH_INTERFACE_RMII uint32_t curTick; @@ -358,7 +369,7 @@ void EthernetIF_PeriodicHandle(struct netif *netif) * @param netif The network interface * @retval None */ -void EthernetIF_LinkCallback(struct netif *netif) +void EthernetIF_LinkCallback(struct netif* netif) { __IO uint32_t tickStart = 0UL; uint16_t u16RegVal = 0U; @@ -405,8 +416,7 @@ void EthernetIF_LinkCallback(struct netif *netif) CLR_REG16_BIT(u16RegVal, PHY_FULLDUPLEX_100M); /* Set MAC Speed and Duplex Mode to PHY */ (void)ETH_PHY_WriteReg(&EthHandle, PHY_BCR, - ((uint16_t)((EthHandle.stcCommInit).u32DuplexMode >> 3U) | - (uint16_t)((EthHandle.stcCommInit).u32Speed >> 1U) | u16RegVal)); + ((uint16_t)((EthHandle.stcCommInit).u32DuplexMode >> 3U) | (uint16_t)((EthHandle.stcCommInit).u32Speed >> 1U) | u16RegVal)); } /* ETH MAC Re-Configuration */ ETH_MAC_SetDuplexSpeed((EthHandle.stcCommInit).u32DuplexMode, (EthHandle.stcCommInit).u32Speed); @@ -427,7 +437,7 @@ void EthernetIF_LinkCallback(struct netif *netif) * - LL_OK: The IF is link up * - LL_ERR: The IF is link down */ -int32_t EthernetIF_IsLinkUp(struct netif *netif) +int32_t EthernetIF_IsLinkUp(struct netif* netif) { return (0U != u8PhyLinkStatus) ? LL_OK : LL_ERR; } @@ -437,14 +447,14 @@ int32_t EthernetIF_IsLinkUp(struct netif *netif) * @param netif The network interface * @retval None */ -__WEAKDEF void EthernetIF_NotifyLinkChange(struct netif *netif) +__WEAKDEF void EthernetIF_NotifyLinkChange(struct netif* netif) { /* This is function could be implemented in user file when the callback is needed */ if (LL_OK == EthernetIF_IsLinkUp(netif)) { GPIO_SetPins(ETH_LINK_LED_PORT, ETH_LINK_LED_PIN); } else { GPIO_ResetPins(ETH_LINK_LED_PORT, ETH_LINK_LED_PIN); - } + } } /** @@ -453,7 +463,8 @@ __WEAKDEF void EthernetIF_NotifyLinkChange(struct netif *netif) * @param p The MAC packet to receive * @retval None */ -__WEAKDEF void EthernetIF_InputCallback(struct netif *netif, struct pbuf *p) { +__WEAKDEF void EthernetIF_InputCallback(struct netif* netif, struct pbuf* p) +{ /* This is function could be implemented in user file when the callback is needed */ #ifdef ETHERNET_LOOPBACK_TEST if ((0 == (memcmp(p->payload, txPbuf.payload, p->len))) && (p->len == txPbuf.len)) { @@ -479,7 +490,7 @@ __WEAKDEF void EthernetIF_InputCallback(struct netif *netif, struct pbuf *p) { #ifdef ETHERNET_LOOPBACK_TEST -static void EthLoopBackTask(void *parameter) +static void EthLoopBackTask(void* parameter) { while (1) { if (RESET == GPIO_ReadInputPins(USER_KEY_PORT, USER_KEY_PIN)) { @@ -489,7 +500,7 @@ static void EthLoopBackTask(void *parameter) } } - //KPrintf("ready to receive eth loop back data\n"); + // KPrintf("ready to receive eth loop back data\n"); /* Read a received packet */ ethernetif_input(&testnetif); /* Handle periodic timers */ @@ -514,24 +525,24 @@ static void EthLoopBackTest(void) (void)ethernetif_init(&testnetif); /* fill data to txPbuf */ - txPbuf.next = NULL; + txPbuf.next = NULL; txPbuf.payload = txBuf; - txPbuf.len = strlen(txBuf); + txPbuf.len = strlen(txBuf); int eth_loopback_task = 0; eth_loopback_task = KTaskCreate("eth_loopback", EthLoopBackTask, NONE, - 2048, 8); - if(eth_loopback_task < 0) { - KPrintf("eth_loopback_task create failed ...%s %d.\n", __FUNCTION__,__LINE__); - return; - } + 2048, 8); + if (eth_loopback_task < 0) { + KPrintf("eth_loopback_task create failed ...%s %d.\n", __FUNCTION__, __LINE__); + return; + } StartupKTask(eth_loopback_task); return; } -SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), -EthLoopBackTest, EthLoopBackTest, EthLoopBackTest); +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), + EthLoopBackTest, EthLoopBackTest, EthLoopBackTest); #endif diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_ethernet.h b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_ethernet.h index 7fae45a9f..4f0529a0b 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_ethernet.h +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_ethernet.h @@ -1,40 +1,39 @@ /* -* Copyright (c) 2021 AIIT XUOS Lab -* XiUOS is licensed under Mulan PSL v2. -* You can use this software according to the terms and conditions of the Mulan PSL v2. -* You may obtain a copy of Mulan PSL v2 at: -* http://license.coscl.org.cn/MulanPSL2 -* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -* See the Mulan PSL v2 for more details. -*/ + * Copyright (c) 2021 AIIT XUOS Lab + * XiUOS is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ /** -* @file connect_ethernet.h -* @brief Adapted network software protocol stack and hardware operation functions -* @version 2.0 -* @author AIIT XUOS Lab -* @date 2022-12-05 -*/ + * @file connect_ethernet.h + * @brief Adapted network software protocol stack and hardware operation functions + * @version 2.0 + * @author AIIT XUOS Lab + * @date 2022-12-05 + */ #ifndef CONNECT_ETHERNET_H #define CONNECT_ETHERNET_H #include "hardware_ethernetif.h" -#include -#include #include +#include +#include #ifdef __cplusplus - extern "C" { +extern "C" { #endif -struct hc32_irq_config -{ - IRQn_Type irq_num; - uint32_t irq_prio; - en_int_src_t int_src; +struct hc32_irq_config { + IRQn_Type irq_num; + uint32_t irq_prio; + en_int_src_t int_src; }; /* Global Ethernet handle*/ @@ -52,22 +51,23 @@ __ALIGN_BEGIN static uint8_t EthRxBuff[ETH_RX_BUF_NUM][ETH_RX_BUF_SIZE]; static uint8_t u8PhyLinkStatus = 0U, u8EthInitStatus = 0U; static struct Hc32IrqConfig eth_irq_config = { - .irq_num = BSP_ETH_IRQ_NUM, - .irq_prio = BSP_ETH_IRQ_PRIO, - .int_src = INT_SRC_ETH_GLB_INT, + .irq_num = BSP_ETH_IRQ_NUM, + .irq_prio = BSP_ETH_IRQ_PRIO, + .int_src = INT_SRC_ETH_GLB_INT, }; void Ethernet_GpioInit(void); -int32_t low_level_init(struct netif *netif); -err_t low_level_output(struct netif *netif, struct pbuf *p); -struct pbuf *low_level_input(struct netif *netif); +int32_t low_level_init(struct netif* netif); +err_t low_level_output(struct netif* netif, struct pbuf* p); +struct pbuf* low_level_input(struct netif* netif); + +int lwip_netdev_add(struct netif* lwip_netif); +void lwip_netdev_del(struct netif* lwip_netif); int HwEthInit(void); - #ifdef __cplusplus } #endif #endif - diff --git a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/include/connect_ethernet.h b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/include/connect_ethernet.h index 49f92d4c8..ad4062e9a 100755 --- a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/include/connect_ethernet.h +++ b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/include/connect_ethernet.h @@ -1,22 +1,22 @@ /* -* Copyright (c) 2021 AIIT XUOS Lab -* XiUOS is licensed under Mulan PSL v2. -* You can use this software according to the terms and conditions of the Mulan PSL v2. -* You may obtain a copy of Mulan PSL v2 at: -* http://license.coscl.org.cn/MulanPSL2 -* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -* See the Mulan PSL v2 for more details. -*/ + * Copyright (c) 2021 AIIT XUOS Lab + * XiUOS is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ /** -* @file connect_ethernet.h -* @brief Adapted network software protocol stack and hardware operation functions -* @version 1.0 -* @author AIIT XUOS Lab -* @date 2021-12-7 -*/ + * @file connect_ethernet.h + * @brief Adapted network software protocol stack and hardware operation functions + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2021-12-7 + */ #ifndef __CONNECT_ETHERNET_H_ #define __CONNECT_ETHERNET_H_ @@ -25,17 +25,15 @@ #include "enet_ethernetif_priv.h" #ifdef __cplusplus - extern "C" { +extern "C" { #endif #ifndef sourceClock #define sourceClock CLOCK_GetFreq(kCLOCK_CoreSysClk) #endif - #ifdef __cplusplus } #endif #endif - diff --git a/Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h b/Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h index dcb75cee8..7612a959e 100644 --- a/Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h +++ b/Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h @@ -1,23 +1,23 @@ /* -* Copyright (c) 2020 AIIT XUOS Lab -* XiUOS is licensed under Mulan PSL v2. -* You can use this software according to the terms and conditions of the Mulan PSL v2. -* You may obtain a copy of Mulan PSL v2 at: -* http://license.coscl.org.cn/MulanPSL2 -* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -* See the Mulan PSL v2 for more details. -*/ + * Copyright (c) 2020 AIIT XUOS Lab + * XiUOS is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ /** -* @file: xs_kdbg.h -* @brief: function declaration and structure defintion of kernel debug -* @version: 1.0 -* @author: AIIT XUOS Lab -* @date: 2020/4/20 -* -*/ + * @file: xs_kdbg.h + * @brief: function declaration and structure defintion of kernel debug + * @version: 1.0 + * @author: AIIT XUOS Lab + * @date: 2020/4/20 + * + */ #ifndef XS_KDBG_H #define XS_KDBG_H @@ -28,6 +28,8 @@ extern "C" { #endif +#define KERNEL_DEBUG + #ifdef KERNEL_DEBUG /*Kernel Section Debug Define*/ @@ -40,18 +42,19 @@ extern "C" { #define KDBG_IPC 0 #define KDBG_HOOK 0 -#define SYS_KDEBUG_LOG(section, information) \ - do \ - { \ - if(section) { \ - KPrintf information; \ - } \ - }while (0) +#define MSGQUEUE_DEBUG 0 -#define KDYN_NONE 0 -#define KDYN_DBG 1 -#define KDYN_ERROR 2 -#define KDYN_WARNING 3 +#define SYS_KDEBUG_LOG(section, information) \ + do { \ + if (section) { \ + KPrintf information; \ + } \ + } while (0) + +#define KDYN_NONE 0 +#define KDYN_DBG 1 +#define KDYN_ERROR 2 +#define KDYN_WARNING 3 #ifdef KDYN_LOG_DBG #define DBG(args, ...) KDYNAMIC_LOG(KDYN_DBG, args, ##__VA_ARGS__) @@ -63,83 +66,76 @@ extern "C" { #define SYS_WARN(args, ...) KDYNAMIC_LOG(KDYN_WARNING, args, ##__VA_ARGS__) #endif -#define KDYNAMIC_LOG(level, args, ...) \ - do \ - { \ - switch(level) \ - { \ - case KDYN_NONE: \ - break; \ - case KDYN_DBG: \ - KPrintf("[DBG]"); \ - KPrintf(args, ##__VA_ARGS__); \ - break; \ - case KDYN_ERROR: \ - KPrintf("[ERR]"); \ - KPrintf(args, ##__VA_ARGS__); \ - break; \ - case KDYN_WARNING: \ - KPrintf("[WARN]"); \ - KPrintf(args, ##__VA_ARGS__); \ - break; \ - default: \ - break; \ - } \ - }while (0) +#define KDYNAMIC_LOG(level, args, ...) \ + do { \ + switch (level) { \ + case KDYN_NONE: \ + break; \ + case KDYN_DBG: \ + KPrintf("[DBG]"); \ + KPrintf(args, ##__VA_ARGS__); \ + break; \ + case KDYN_ERROR: \ + KPrintf("[ERR]"); \ + KPrintf(args, ##__VA_ARGS__); \ + break; \ + case KDYN_WARNING: \ + KPrintf("[WARN]"); \ + KPrintf(args, ##__VA_ARGS__); \ + break; \ + default: \ + break; \ + } \ + } while (0) -#define NULL_PARAM_CHECK(param) \ - do \ - { \ - if(param == NONE) { \ - KPrintf("PARAM CHECK FAILED ...%s %d %s is NULL.\n",__FUNCTION__,__LINE__,#param); \ - while(RET_TRUE); \ - } \ - }while (0) +#define NULL_PARAM_CHECK(param) \ + do { \ + if (param == NONE) { \ + KPrintf("PARAM CHECK FAILED ...%s %d %s is NULL.\n", __FUNCTION__, __LINE__, #param); \ + while (RET_TRUE) \ + ; \ + } \ + } while (0) -#define CHECK(TRUE_CONDITION) \ - do \ - { \ - if(!(TRUE_CONDITION)) { \ - KPrintf("%s CHECK condition is false at line[%d] of [%s] func.\n",#TRUE_CONDITION,__LINE__,__FUNCTION__);\ - while(RET_TRUE); \ - } \ - } while(0) +#define CHECK(TRUE_CONDITION) \ + do { \ + if (!(TRUE_CONDITION)) { \ + KPrintf("%s CHECK condition is false at line[%d] of [%s] func.\n", #TRUE_CONDITION, __LINE__, __FUNCTION__); \ + while (RET_TRUE) \ + ; \ + } \ + } while (0) +#define KDEBUG_NOT_IN_INTERRUPT \ + do { \ + x_base level; \ + level = DISABLE_INTERRUPT(); \ + if (isrManager.done->getCounter() != 0) { \ + KPrintf("Function[%s] is not supported in ISR\n", __FUNCTION__); \ + CHECK(0); \ + } \ + ENABLE_INTERRUPT(level); \ + } while (0) -#define KDEBUG_NOT_IN_INTERRUPT \ - do \ - { \ - x_base level; \ - level = DISABLE_INTERRUPT(); \ - if (isrManager.done->getCounter() != 0) \ - { \ - KPrintf("Function[%s] is not supported in ISR\n", __FUNCTION__); \ - CHECK(0); \ - } \ - ENABLE_INTERRUPT(level); \ - } while (0) +#define KDEBUG_IN_KTASK_CONTEXT \ + do { \ + x_base level; \ + level = DISABLE_INTERRUPT(); \ + if (GetKTaskDescriptor() == NONE) { \ + KPrintf("Function[%s] is not supported before task assign\n", __FUNCTION__); \ + CHECK(0); \ + } \ + KDEBUG_NOT_IN_INTERRUPT; \ + ENABLE_INTERRUPT(level); \ + } while (0) -#define KDEBUG_IN_KTASK_CONTEXT \ - do \ - { \ - x_base level; \ - level = DISABLE_INTERRUPT(); \ - if (GetKTaskDescriptor() == NONE) \ - { \ - KPrintf("Function[%s] is not supported before task assign\n", __FUNCTION__); \ - CHECK(0); \ - } \ - KDEBUG_NOT_IN_INTERRUPT; \ - ENABLE_INTERRUPT(level); \ - } while (0) - -#define NOT_IN_CRITICAL_AREA \ - do { \ - if(GetOsAssignLockLevel() != 0){ \ +#define NOT_IN_CRITICAL_AREA \ + do { \ + if (GetOsAssignLockLevel() != 0) { \ KPrintf("Function[%s] is not supported switch in critical area.\n", __FUNCTION__); \ - CHECK(0); \ - } \ - } while (0) + CHECK(0); \ + } \ + } while (0) #else @@ -159,4 +155,4 @@ extern "C" { } #endif -#endif +#endif diff --git a/Ubiquitous/XiZi_IIoT/kernel/thread/delay.c b/Ubiquitous/XiZi_IIoT/kernel/thread/delay.c index 1cbdabaa4..6842ac209 100644 --- a/Ubiquitous/XiZi_IIoT/kernel/thread/delay.c +++ b/Ubiquitous/XiZi_IIoT/kernel/thread/delay.c @@ -30,7 +30,7 @@ DoubleLinklistType xiaoshan_delay_head = {&xiaoshan_delay_head, &xiaoshan_delay_head}; /** - * This function will delay a task with sone ticks. + * This function will delay a task with some ticks. * * @param task the task needed to be delayed * @param ticks delay timeout diff --git a/Ubiquitous/XiZi_IIoT/kernel/thread/msgqueue.c b/Ubiquitous/XiZi_IIoT/kernel/thread/msgqueue.c index ee58bc0d7..526331aeb 100644 --- a/Ubiquitous/XiZi_IIoT/kernel/thread/msgqueue.c +++ b/Ubiquitous/XiZi_IIoT/kernel/thread/msgqueue.c @@ -21,6 +21,7 @@ #include #include +#include DECLARE_ID_MANAGER(k_mq_id_manager, ID_NUM_MAX); DoubleLinklistType k_mq_list = {&k_mq_list, &k_mq_list}; @@ -41,17 +42,17 @@ static struct MsgQueue *GetMsgQueueById(int32 id) lock = CriticalAreaLock(); idnode = IdGetObj(&k_mq_id_manager, id); - if (idnode == NONE){ - CriticalAreaUnLock(lock); - return NONE; + if (idnode == NONE) { + CriticalAreaUnLock(lock); + return NONE; } mq = CONTAINER_OF(idnode, struct MsgQueue, id); CriticalAreaUnLock(lock); + return mq; } -static x_err_t _InitMsgQueue( struct MsgQueue *mq ,x_size_t msg_size, - x_size_t max_msgs ) +static x_err_t _InitMsgQueue(struct MsgQueue* mq, x_size_t msg_size, x_size_t max_msgs) { x_base lock = 0; @@ -62,10 +63,12 @@ static x_err_t _InitMsgQueue( struct MsgQueue *mq ,x_size_t msg_size, mq->each_len = ALIGN_MEN_UP(msg_size, MEM_ALIGN_SIZE); mq->index = 0; + SYS_KDEBUG_LOG(MSGQUEUE_DEBUG, ("[%s] Msg attr, max_msg_num: %d, block size: %d\n", __func__, mq->max_msgs, mq->each_len)); + InitDoubleLinkList(&mq->send_pend_list); InitDoubleLinkList(&(mq->recv_pend_list)); - mq->msg_buf = x_malloc( mq->each_len * mq->max_msgs); + mq->msg_buf = x_malloc(mq->each_len * mq->max_msgs); if (mq->msg_buf == NONE) { lock = CriticalAreaLock(); DoubleLinkListRmNode(&(mq->link)); @@ -77,26 +80,28 @@ static x_err_t _InitMsgQueue( struct MsgQueue *mq ,x_size_t msg_size, return EOK; } -static x_err_t _MsgQueueSend(struct MsgQueue *mq, - const void *buffer, - x_size_t size, - int32 msec) +static x_err_t _MsgQueueSend(struct MsgQueue* mq, + const void* buffer, + x_size_t size, + int32 msec) { x_ubase lock = 0; uint32 tick_delta = 0; int32 timeout = 0; - uint8 *msg = NONE; + uint8* msg = NONE; struct TaskDescriptor *task = NONE; NULL_PARAM_CHECK(mq); NULL_PARAM_CHECK(buffer); + SYS_KDEBUG_LOG(MSGQUEUE_DEBUG, ("[%s] mq_num_msgs: %d, block size: %d, needed size: %d\n", __func__, mq->num_msgs, mq->each_len, size)); + if (size > mq->each_len) return -ERROR; tick_delta = 0; task = GetKTaskDescriptor(); - if(WAITING_FOREVER == msec) + if (WAITING_FOREVER == msec) timeout = WAITING_FOREVER; else timeout = CalculateTickFromTimeMs(msec); @@ -107,8 +112,7 @@ static x_err_t _MsgQueueSend(struct MsgQueue *mq, return -EFULL; } - while(mq->num_msgs >= mq->max_msgs ) { - + while (mq->num_msgs >= mq->max_msgs) { task->exstatus = EOK; if (timeout == 0) { CriticalAreaUnLock(lock); @@ -121,7 +125,7 @@ static x_err_t _MsgQueueSend(struct MsgQueue *mq, tick_delta = CurrentTicksGain(); SYS_KDEBUG_LOG(KDBG_IPC, ("mq_send_wait: start timer of task:%s\n", task->task_base_info.name)); - KTaskSetDelay(task,timeout); + KTaskSetDelay(task, timeout); } CriticalAreaUnLock(lock); @@ -139,19 +143,19 @@ static x_err_t _MsgQueueSend(struct MsgQueue *mq, if (timeout < 0) timeout = 0; } - } + } // end with lock here - msg = mq->msg_buf + ( ( mq->index + mq->num_msgs ) % mq->max_msgs ) * mq->each_len ; + msg = mq->msg_buf + ((mq->index + mq->num_msgs) % mq->max_msgs) * mq->each_len; memcpy(msg, buffer, size); - mq->num_msgs ++; + mq->num_msgs++; if (!IsDoubleLinkListEmpty(&mq->recv_pend_list)) { LinklistResume(&(mq->recv_pend_list)); CriticalAreaUnLock(lock); DO_KTASK_ASSIGN; return EOK; } - CriticalAreaUnLock(lock); + return EOK; } @@ -191,15 +195,15 @@ static x_err_t _MsgQueueUrgentSend(struct MsgQueue *mq, const void *buffer, x_si return EOK; } -static x_err_t _MsgQueueRecv(struct MsgQueue *mq, - void *buffer, - x_size_t size, - int32 msec) +static x_err_t _MsgQueueRecv(struct MsgQueue* mq, + void* buffer, + x_size_t size, + int32 msec) { x_ubase lock = 0; uint32 tick_delta = 0; int32 timeout = 0; - struct MqMessage *msg = NONE; + uint8* msg = NONE; struct TaskDescriptor *task = NONE; NULL_PARAM_CHECK(mq); @@ -208,14 +212,14 @@ static x_err_t _MsgQueueRecv(struct MsgQueue *mq, tick_delta = 0; task = GetKTaskDescriptor(); timeout = CalculateTickFromTimeMs(msec); - lock = CriticalAreaLock(); + lock = CriticalAreaLock(); if (mq->index == 0 && timeout == 0) { CriticalAreaUnLock(lock); return -ETIMEOUT; } - for( ; mq->num_msgs <= 0 ; ) { + while (mq->num_msgs <= 0) { KDEBUG_IN_KTASK_CONTEXT; task->exstatus = EOK; @@ -225,19 +229,16 @@ static x_err_t _MsgQueueRecv(struct MsgQueue *mq, return -ETIMEOUT; } - LinklistSuspend(&(mq->recv_pend_list), - task, - LINKLIST_FLAG_FIFO); + LinklistSuspend(&(mq->recv_pend_list), task, LINKLIST_FLAG_FIFO); if (timeout > 0) { tick_delta = CurrentTicksGain(); SYS_KDEBUG_LOG(KDBG_IPC, ("set task:%s to timer list\n", task->task_base_info.name)); - KTaskSetDelay(task,timeout); + KTaskSetDelay(task, timeout); } CriticalAreaUnLock(lock); - DO_KTASK_ASSIGN; if (task->exstatus != EOK) { @@ -256,8 +257,8 @@ static x_err_t _MsgQueueRecv(struct MsgQueue *mq, msg = mq->msg_buf + mq->index * mq->each_len; mq->index = (mq->index + 1) % mq->max_msgs; - memcpy(buffer, msg , size > mq->each_len ? mq->each_len : size); - mq->num_msgs --; + memcpy(buffer, msg, size > mq->each_len ? mq->each_len : size); + mq->num_msgs--; if (!IsDoubleLinkListEmpty(&(mq->send_pend_list))) { LinklistResume(&(mq->send_pend_list)); @@ -326,8 +327,7 @@ static struct MsgQueueDone Done = { * * @return id on success;ENOMEMORY/ERROR on failure */ -int32 KCreateMsgQueue(x_size_t msg_size, - x_size_t max_msgs) +int32 KCreateMsgQueue(x_size_t msg_size, x_size_t max_msgs) { int32 id = 0; x_base temp = 0; @@ -337,11 +337,12 @@ int32 KCreateMsgQueue(x_size_t msg_size, mq = (struct MsgQueue *)x_malloc(sizeof(struct MsgQueue)); if (mq == NONE) return -ENOMEMORY; - memset(mq,0x0,sizeof(struct MsgQueue)); + memset(mq, 0x0, sizeof(struct MsgQueue)); lock = CriticalAreaLock(); id = IdInsertObj(&k_mq_id_manager, &mq->id); CriticalAreaUnLock(lock); + if (id < 0) { x_free(mq); return -ENOMEMORY; @@ -350,11 +351,12 @@ int32 KCreateMsgQueue(x_size_t msg_size, lock = CriticalAreaLock(); DoubleLinkListInsertNodeAfter(&k_mq_list, &mq->link); CriticalAreaUnLock(lock); + mq->Done = &Done; - if( mq->Done->init(mq, msg_size,max_msgs) == EOK ) - return mq->id.id; + if (mq->Done->init(mq, msg_size, max_msgs) == EOK) + return mq->id.id; else - return -ERROR; + return -ERROR; } /** @@ -401,7 +403,7 @@ x_err_t KMsgQueueRecv(int32 id, mq = GetMsgQueueById(id); if (mq != NONE) - return mq->Done->recv(mq,buffer,size,timeout); + return mq->Done->recv(mq, buffer, size, timeout); else return -EINVALED; @@ -471,10 +473,11 @@ x_err_t KMsgQueueSend(int32 id, const void *buffer, x_size_t size) mq = GetMsgQueueById(id); if (mq != NONE) - return mq->Done->send(mq,buffer,size,0); + return mq->Done->send(mq, buffer, size, WAITING_FOREVER); else return -EINVALED; } + /** * send message with waiting time,current suspend task will be resumed * diff --git a/Ubiquitous/XiZi_IIoT/path_app.mk b/Ubiquitous/XiZi_IIoT/path_app.mk index 78427f764..a027e759a 100644 --- a/Ubiquitous/XiZi_IIoT/path_app.mk +++ b/Ubiquitous/XiZi_IIoT/path_app.mk @@ -8,6 +8,9 @@ ifeq ($(CONFIG_APP_SELECT_NEWLIB), y) APPPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/lib/app_newlib/include # endif +ifeq ($(CONFIG_APP_USING_WEBNET),y) + APPPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Applications/webnet/WebNet_XiUOS/inc # +endif ifeq ($(CONFIG_ADD_XIZI_FEATURES), y) APPPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/transform_layer/xizi \ @@ -36,6 +39,7 @@ ifeq ($(CONFIG_CRYPTO), y) APPPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Framework/security/crypto/include # endif + # COMPILE_APP: # @$(eval CPPPATHS=$(APPPATHS)) # @echo $(SRC_APP_DIR) diff --git a/Ubiquitous/XiZi_IIoT/path_kernel.mk b/Ubiquitous/XiZi_IIoT/path_kernel.mk index df84837ca..5ee36f062 100755 --- a/Ubiquitous/XiZi_IIoT/path_kernel.mk +++ b/Ubiquitous/XiZi_IIoT/path_kernel.mk @@ -52,6 +52,7 @@ KERNELPATHS += \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/priv \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/prot \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/arch + endif endif @@ -419,6 +420,8 @@ KERNELPATHS += \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/priv \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/prot \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/arch + +KERNELPATHS += -I$(KERNEL_ROOT)/resources/include/netdev endif endif @@ -554,6 +557,12 @@ endif endif +ifeq ($(CONFIG_APP_USING_WEBNET),y) + KERNELPATHS += -I$(KERNEL_ROOT)/../../APP_Framework/Applications/webnet/WebNet_XiUOS/inc \ + -I$(KERNEL_ROOT)/../../APP_Framework/Applications/webnet/WebNet_XiUOS/samples # +endif + + ifeq ($(CONFIG_TOOL_SHELL), y) KERNELPATHS +=-I$(KERNEL_ROOT)/tool/shell/letter-shell \ -I$(KERNEL_ROOT)/tool/shell/letter-shell/file_ext # diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c index e491c5cb1..29175d23f 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c @@ -31,55 +31,54 @@ */ /** -* @file sys_arch.c -* @brief In order to adapt to XiZi, some changes have been made to implement the LwIP interface. -* @version 1.0 -* @author AIIT XUOS Lab -* @date 2021-05-29 -*/ + * @file sys_arch.c + * @brief In order to adapt to XiZi, some changes have been made to implement the LwIP interface. + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2021-05-29 + */ #include "debug.h" -#include #include +#include -#include "tcpip.h" +#include "lwip/dhcp.h" #include "lwip/init.h" #include "lwip/netif.h" #include "lwip/sio.h" -#include -#include -#include -#include "lwip/dhcp.h" +#include "tcpip.h" #include "tcpip_priv.h" +#include +#include +#include #if !NO_SYS #include "sys_arch.h" #endif #include +#include #include #include -#include -#include -#include -#include #include +#include +#include #include "board.h" -#include "ethernet.h" #include "connect_ethernet.h" +#include "ethernet.h" -char lwip_ipaddr[20] = {192, 168, 130, 77}; -char lwip_netmask[20] = {255, 255, 254, 0}; -char lwip_gwaddr[20] = {192, 168, 130, 1}; +char lwip_ipaddr[20] = { 192, 168, 130, 77 }; +char lwip_netmask[20] = { 255, 255, 254, 0 }; +char lwip_gwaddr[20] = { 192, 168, 130, 1 }; -char lwip_eth0_ipaddr[20] = {192, 168, 130, 77}; -char lwip_eth0_netmask[20] = {255, 255, 254, 0}; -char lwip_eth0_gwaddr[20] = {192, 168, 130, 1}; +char lwip_eth0_ipaddr[20] = { 192, 168, 130, 77 }; +char lwip_eth0_netmask[20] = { 255, 255, 254, 0 }; +char lwip_eth0_gwaddr[20] = { 192, 168, 130, 1 }; -char lwip_eth1_ipaddr[20] = {192, 168, 130, 99}; -char lwip_eth1_netmask[20] = {255, 255, 254, 0}; -char lwip_eth1_gwaddr[20] = {192, 168, 130, 23}; +char lwip_eth1_ipaddr[20] = { 192, 168, 130, 99 }; +char lwip_eth1_netmask[20] = { 255, 255, 254, 0 }; +char lwip_eth1_gwaddr[20] = { 192, 168, 130, 23 }; char lwip_flag = 0; @@ -88,220 +87,238 @@ x_ticks_t lwip_sys_now; #define SYS_THREAD_MAX 4 struct netif gnetif; -sys_sem_t* get_eth_recv_sem() { +sys_sem_t* get_eth_recv_sem() +{ static sys_sem_t g_recv_sem = 0; return &g_recv_sem; } -void sys_init(void) { - // do nothing +void sys_init(void) +{ + // do nothing } -u32_t -sys_jiffies(void) { - lwip_sys_now = CurrentTicksGain(); - return lwip_sys_now; +u32_t sys_jiffies(void) +{ + lwip_sys_now = CurrentTicksGain(); + return lwip_sys_now; } -u32_t -sys_now(void) { - lwip_sys_now = CurrentTicksGain(); - return CalculateTimeMsFromTick(lwip_sys_now); +u32_t sys_now(void) +{ + lwip_sys_now = CurrentTicksGain(); + return CalculateTimeMsFromTick(lwip_sys_now); } -sys_prot_t sys_arch_protect(void) { - return CriticalAreaLock(); +sys_prot_t sys_arch_protect(void) +{ + return CriticalAreaLock(); } -void sys_arch_unprotect(sys_prot_t pval) { - CriticalAreaUnLock(pval); +void sys_arch_unprotect(sys_prot_t pval) +{ + CriticalAreaUnLock(pval); } #if !NO_SYS -err_t -sys_sem_new(sys_sem_t *sem, u8_t count) { - *sem = KSemaphoreCreate((uint16)count); +err_t sys_sem_new(sys_sem_t* sem, u8_t count) +{ + *sem = KSemaphoreCreate((uint16)count); #if SYS_STATS - ++lwip_stats.sys.sem.used; - if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) { - lwip_stats.sys.sem.max = lwip_stats.sys.sem.used; - } -#endif /* SYS_STATS */ - - if(*sem >= 0) - return ERR_OK; - else { -#if SYS_STATS - ++lwip_stats.sys.sem.err; -#endif /* SYS_STATS */ - KPrintf("[sys_arch]:new sem fail!\n"); - return ERR_MEM; - } -} - -void -sys_sem_free(sys_sem_t *sem) { -#if SYS_STATS - --lwip_stats.sys.sem.used; -#endif /* SYS_STATS */ - KSemaphoreDelete(*sem); - *sem = SYS_SEM_NULL; -} - -int sys_sem_valid(sys_sem_t *sem) { - return (*sem > SYS_SEM_NULL); -} - -void -sys_sem_set_invalid(sys_sem_t *sem) { - *sem = SYS_SEM_NULL; -} - -u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) { - x_ticks_t start_tick = 0 ; - int32 wait_time = 0; - - - if(*sem == SYS_SEM_NULL) - return SYS_ARCH_TIMEOUT; - - - start_tick = CurrentTicksGain(); - - if (0 == timeout) - wait_time = WAITING_FOREVER; - else - wait_time = timeout; - - - if(KSemaphoreObtain(*sem, wait_time) == EOK) - return CalculateTimeMsFromTick(CurrentTicksGain()-start_tick); - else - return SYS_ARCH_TIMEOUT; -} - -void sys_sem_signal(sys_sem_t *sem) { - if(KSemaphoreAbandon(*sem) != EOK) - KPrintf("[sys_arch]:sem signal fail!\n"); -} - -err_t sys_mutex_new(sys_mutex_t *mutex) { - *mutex = KMutexCreate(); - if (*mutex > SYS_MRTEX_NULL) - return ERR_OK; - else { - KPrintf("[sys_arch]:new mutex fail!\n"); - return ERR_MEM; - } -} - -void sys_mutex_free(sys_mutex_t *mutex) { - KMutexDelete(*mutex); -} - -void sys_mutex_set_invalid(sys_mutex_t *mutex) { - *mutex = SYS_MRTEX_NULL; -} - -void sys_mutex_lock(sys_mutex_t *mutex) { - KMutexObtain(*mutex, WAITING_FOREVER); -} - -void sys_mutex_unlock(sys_mutex_t *mutex) { - KMutexAbandon(*mutex); -} - - -sys_thread_t sys_thread_new(const char *name, lwip_thread_fn function, void *arg, int stacksize, int prio) { - sys_thread_t handle = -1; - handle = KTaskCreate(name, - function, - arg, - (uint32)stacksize, - (uint8)prio); - if (handle >= 0) { - StartupKTask(handle); - lw_print("lw: [%s] create %s handle %x\n", __func__, name, handle); - return handle; - } - lw_print("lw: [%s] create %s failed\n", __func__, name); - return -ERROR; -} - -err_t sys_mbox_new(sys_mbox_t *mbox, int size) { - *mbox = KCreateMsgQueue(sizeof(void *), size); - -#if SYS_STATS - ++lwip_stats.sys.mbox.used; - if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) { - lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; + ++lwip_stats.sys.sem.used; + if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) { + lwip_stats.sys.sem.max = lwip_stats.sys.sem.used; } #endif /* SYS_STATS */ - if(*mbox < 0) { - lw_print("lw: [%s] alloc %d mbox %p failed\n", __func__, size, mbox); - return ERR_MEM; - } - lw_print("lw: [%s] alloc %d mbox %p ok!\n", __func__, size, mbox); - return ERR_OK; + if (*sem >= 0) + return ERR_OK; + else { +#if SYS_STATS + ++lwip_stats.sys.sem.err; +#endif /* SYS_STATS */ + KPrintf("[sys_arch]:new sem fail!\n"); + return ERR_MEM; + } } -void sys_mbox_free(sys_mbox_t *mbox) { - KDeleteMsgQueue(*mbox); +void sys_sem_free(sys_sem_t* sem) +{ +#if SYS_STATS + --lwip_stats.sys.sem.used; +#endif /* SYS_STATS */ + KSemaphoreDelete(*sem); + *sem = SYS_SEM_NULL; } -int sys_mbox_valid(sys_mbox_t *mbox) { - if (*mbox <= SYS_MBOX_NULL) - return 0; - else - return 1; +int sys_sem_valid(sys_sem_t* sem) +{ + return (*sem > SYS_SEM_NULL); } -void sys_mbox_set_invalid(sys_mbox_t *mbox) { - *mbox = SYS_MBOX_NULL; +void sys_sem_set_invalid(sys_sem_t* sem) +{ + *sem = SYS_SEM_NULL; } -void sys_mbox_post(sys_mbox_t *q, void *msg) { - KMsgQueueSendwait(*q, &msg, sizeof(void *), WAITING_FOREVER); +u32_t sys_arch_sem_wait(sys_sem_t* sem, u32_t timeout) +{ + x_ticks_t start_tick = 0; + int32 wait_time = 0; + + if (*sem == SYS_SEM_NULL) + return SYS_ARCH_TIMEOUT; + + start_tick = CurrentTicksGain(); + + if (0 == timeout) + wait_time = WAITING_FOREVER; + else + wait_time = timeout; + + if (KSemaphoreObtain(*sem, wait_time) == EOK) + return CalculateTimeMsFromTick(CurrentTicksGain() - start_tick); + else + return SYS_ARCH_TIMEOUT; } -err_t sys_mbox_trypost(sys_mbox_t *q, void *msg) { - // if(KMsgQueueSend(*q, &msg, sizeof(void *)) == EOK) - if(KMsgQueueSend(*q, &msg, sizeof(void *)) == EOK) +void sys_sem_signal(sys_sem_t* sem) +{ + if (KSemaphoreAbandon(*sem) != EOK) + KPrintf("[sys_arch]:sem signal fail!\n"); +} + +err_t sys_mutex_new(sys_mutex_t* mutex) +{ + *mutex = KMutexCreate(); + if (*mutex > SYS_MRTEX_NULL) + return ERR_OK; + else { + KPrintf("[sys_arch]:new mutex fail!\n"); + return ERR_MEM; + } +} + +void sys_mutex_free(sys_mutex_t* mutex) +{ + KMutexDelete(*mutex); +} + +void sys_mutex_set_invalid(sys_mutex_t* mutex) +{ + *mutex = SYS_MRTEX_NULL; +} + +void sys_mutex_lock(sys_mutex_t* mutex) +{ + KMutexObtain(*mutex, WAITING_FOREVER); +} + +void sys_mutex_unlock(sys_mutex_t* mutex) +{ + KMutexAbandon(*mutex); +} + +sys_thread_t sys_thread_new(const char* name, lwip_thread_fn function, void* arg, int stacksize, int prio) +{ + sys_thread_t handle = -1; + handle = KTaskCreate(name, + function, + arg, + (uint32)stacksize, + (uint8)prio); + if (handle >= 0) { + StartupKTask(handle); + lw_print("lw: [%s] create %s handle %x\n", __func__, name, handle); + return handle; + } + lw_print("lw: [%s] create %s failed\n", __func__, name); + return -ERROR; +} + +err_t sys_mbox_new(sys_mbox_t* mbox, int size) +{ + *mbox = KCreateMsgQueue(sizeof(void*), size); + +#if SYS_STATS + ++lwip_stats.sys.mbox.used; + if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) { + lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; + } +#endif /* SYS_STATS */ + if (*mbox < 0) { + lw_print("lw: [%s] alloc %d mbox %p failed\n", __func__, size, mbox); + return ERR_MEM; + } + + lw_print("lw: [%s] alloc %d mbox %p ok!\n", __func__, size, mbox); return ERR_OK; - else - return ERR_MEM; } -err_t sys_mbox_trypost_fromisr(sys_mbox_t *q, void *msg) { - return sys_mbox_trypost(q, msg); +void sys_mbox_free(sys_mbox_t* mbox) +{ + KDeleteMsgQueue(*mbox); } -u32_t sys_arch_mbox_fetch(sys_mbox_t *q, void **msg, u32_t timeout) { - x_ticks_t start_tick = 0 ; - int32 wait_time = 0; - - start_tick = CurrentTicksGain(); - - if (0 == timeout) - wait_time = WAITING_FOREVER; - else - wait_time = timeout; - - if(KMsgQueueRecv(*q, &(*msg), sizeof(void *), wait_time) == EOK) { - return CalculateTimeMsFromTick(CurrentTicksGain() - start_tick); - } else { - return SYS_ARCH_TIMEOUT; - } +int sys_mbox_valid(sys_mbox_t* mbox) +{ + if (*mbox <= SYS_MBOX_NULL) + return 0; + else + return 1; } -u32_t sys_arch_mbox_tryfetch(sys_mbox_t *q, void **msg) { - if (KMsgQueueRecv(*q, &(*msg), sizeof(void *), 0) == EOK) - return ERR_OK; - else - return SYS_MBOX_EMPTY; +void sys_mbox_set_invalid(sys_mbox_t* mbox) +{ + *mbox = SYS_MBOX_NULL; +} + +void sys_mbox_post(sys_mbox_t* q, void* msg) +{ + KMsgQueueSendwait(*q, &msg, sizeof(void*), WAITING_FOREVER); +} + +err_t sys_mbox_trypost(sys_mbox_t* q, void* msg) +{ + // if(KMsgQueueSend(*q, &msg, sizeof(void *)) == EOK) + if (KMsgQueueSend(*q, &msg, sizeof(void*)) == EOK) + return ERR_OK; + else + return ERR_MEM; +} + +err_t sys_mbox_trypost_fromisr(sys_mbox_t* q, void* msg) +{ + return sys_mbox_trypost(q, msg); +} + +u32_t sys_arch_mbox_fetch(sys_mbox_t* q, void** msg, u32_t timeout) +{ + x_ticks_t start_tick = 0; + int32 wait_time = 0; + + start_tick = CurrentTicksGain(); + + if (0 == timeout) + wait_time = WAITING_FOREVER; + else + wait_time = timeout; + + if (KMsgQueueRecv(*q, &(*msg), sizeof(void*), wait_time) == EOK) { + return CalculateTimeMsFromTick(CurrentTicksGain() - start_tick); + } else { + return SYS_ARCH_TIMEOUT; + } +} + +u32_t sys_arch_mbox_tryfetch(sys_mbox_t* q, void** msg) +{ + if (KMsgQueueRecv(*q, &(*msg), sizeof(void*), 0) == EOK) + return ERR_OK; + else + return SYS_MBOX_EMPTY; } #if LWIP_NETCONN_SEM_PER_THREAD @@ -315,128 +332,129 @@ ip4_addr_t ipaddr; ip4_addr_t netmask; ip4_addr_t gw; -void lwip_config_input(struct netif *net) { - sys_thread_t th_id = 0; +void lwip_config_input(struct netif* net) +{ + sys_thread_t th_id = 0; - th_id = sys_thread_new("eth_input", ethernetif_input, net, LWIP_TASK_STACK_SIZE, 20); + th_id = sys_thread_new("eth_input", ethernetif_input, net, LWIP_TASK_STACK_SIZE, 20); - if (th_id >= 0) { - lw_print("%s %d successfully!\n", __func__, th_id); - } else { - lw_print("%s failed!\n", __func__); - } + if (th_id >= 0) { + lw_print("%s %d successfully!\n", __func__, th_id); + } else { + lw_print("%s failed!\n", __func__); + } } -void lwip_config_tcp(uint8_t enet_port, char *ip, char *mask, char *gw) { - ip4_addr_t net_ipaddr, net_netmask, net_gw; - char* eth_cfg; +void lwip_config_tcp(uint8_t enet_port, char* ip, char* mask, char* gw) +{ + ip4_addr_t net_ipaddr, net_netmask, net_gw; + char* eth_cfg; - eth_cfg = ethernetif_config_enet_set(enet_port); + eth_cfg = ethernetif_config_enet_set(enet_port); - if(chk_lwip_bit(LWIP_INIT_FLAG)) { - lw_print("lw: [%s] already ...\n", __func__); - return; - } + if (chk_lwip_bit(LWIP_INIT_FLAG)) { + lw_print("lw: [%s] already ...\n", __func__); + return; + } - set_lwip_bit(LWIP_INIT_FLAG); + set_lwip_bit(LWIP_INIT_FLAG); - tcpip_init(NULL, NULL); + tcpip_init(NULL, NULL); - lw_print("lw: [%s] start ...\n", __func__); - - IP4_ADDR(&net_ipaddr, ip[0], ip[1], ip[2], ip[3]); - IP4_ADDR(&net_netmask, mask[0], mask[1], mask[2], mask[3]); - IP4_ADDR(&net_gw, gw[0], gw[1], gw[2], gw[3]); - - if (0 == enet_port) { -#ifdef NETIF_ENET0_INIT_FUNC - printf("[%s:%d] call netif_add\n", __func__, __LINE__); - netif_add(&gnetif, &net_ipaddr, &net_netmask, &net_gw, eth_cfg, NETIF_ENET0_INIT_FUNC, - tcpip_input); -#endif - } else if (1 == enet_port) { -#ifdef NETIF_ENET1_INIT_FUNC - netif_add(&gnetif, &net_ipaddr, &net_netmask, &net_gw, eth_cfg, NETIF_ENET1_INIT_FUNC, - tcpip_input); -#endif - } - - netif_set_default(&gnetif); - netif_set_up(&gnetif); - - lw_print("\r\n************************************************\r\n"); - lw_print(" Network Configuration\r\n"); - lw_print("************************************************\r\n"); - lw_print(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&net_ipaddr)[0], ((u8_t *)&net_ipaddr)[1], - ((u8_t *)&net_ipaddr)[2], ((u8_t *)&net_ipaddr)[3]); - lw_print(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&net_netmask)[0], ((u8_t *)&net_netmask)[1], - ((u8_t *)&net_netmask)[2], ((u8_t *)&net_netmask)[3]); - lw_print(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&net_gw)[0], ((u8_t *)&net_gw)[1], - ((u8_t *)&net_gw)[2], ((u8_t *)&net_gw)[3]); - lw_print("************************************************\r\n"); - - lwip_config_input(&gnetif); -} - - -void lwip_config_net(uint8_t enet_port, char *ip, char *mask, char *gw) { - ip4_addr_t net_ipaddr, net_netmask, net_gw; - char* eth_cfg; - - eth_cfg = ethernetif_config_enet_set(enet_port); - - if(chk_lwip_bit(LWIP_INIT_FLAG)) { - lw_print("lw: [%s] already ...\n", __func__); + lw_print("lw: [%s] start ...\n", __func__); IP4_ADDR(&net_ipaddr, ip[0], ip[1], ip[2], ip[3]); IP4_ADDR(&net_netmask, mask[0], mask[1], mask[2], mask[3]); IP4_ADDR(&net_gw, gw[0], gw[1], gw[2], gw[3]); - // update ip addr - netif_set_down(&gnetif); - netif_set_gw(&gnetif, &net_gw); - netif_set_netmask(&gnetif, &net_netmask); - netif_set_ipaddr(&gnetif, &net_ipaddr); - netif_set_up(&gnetif); - return; - } - set_lwip_bit(LWIP_INIT_FLAG); - - lw_print("lw: [%s] start ...\n", __func__); - - IP4_ADDR(&net_ipaddr, ip[0], ip[1], ip[2], ip[3]); - IP4_ADDR(&net_netmask, mask[0], mask[1], mask[2], mask[3]); - IP4_ADDR(&net_gw, gw[0], gw[1], gw[2], gw[3]); - - lwip_init(); - - if(0 == enet_port) { + if (0 == enet_port) { #ifdef NETIF_ENET0_INIT_FUNC - netif_add(&gnetif, &net_ipaddr, &net_netmask, &net_gw, eth_cfg, NETIF_ENET0_INIT_FUNC, - ethernet_input); + printf("[%s:%d] call netif_add\n", __func__, __LINE__); + netif_add(&gnetif, &net_ipaddr, &net_netmask, &net_gw, eth_cfg, NETIF_ENET0_INIT_FUNC, + tcpip_input); #endif - } else if (1 == enet_port) { + } else if (1 == enet_port) { #ifdef NETIF_ENET1_INIT_FUNC - netif_add(&gnetif, &net_ipaddr, &net_netmask, &net_gw, eth_cfg, NETIF_ENET1_INIT_FUNC, - ethernet_input); + netif_add(&gnetif, &net_ipaddr, &net_netmask, &net_gw, eth_cfg, NETIF_ENET1_INIT_FUNC, + tcpip_input); #endif - } - - netif_set_default(&gnetif); - netif_set_up(&gnetif); + } - if(chk_lwip_bit(LWIP_PRINT_FLAG)) { - lw_notice("\r\n************************************************\r\n"); - lw_notice(" Network Configuration\r\n"); - lw_notice("************************************************\r\n"); - lw_notice(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&net_ipaddr)[0], ((u8_t *)&net_ipaddr)[1], - ((u8_t *)&net_ipaddr)[2], ((u8_t *)&net_ipaddr)[3]); - lw_notice(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&net_netmask)[0], ((u8_t *)&net_netmask)[1], - ((u8_t *)&net_netmask)[2], ((u8_t *)&net_netmask)[3]); - lw_notice(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&net_gw)[0], ((u8_t *)&net_gw)[1], - ((u8_t *)&net_gw)[2], ((u8_t *)&net_gw)[3]); - lw_notice("************************************************\r\n"); - } - lwip_config_input(&gnetif); + netif_set_default(&gnetif); + netif_set_up(&gnetif); + + lw_print("\r\n************************************************\r\n"); + lw_print(" Network Configuration\r\n"); + lw_print("************************************************\r\n"); + lw_print(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t*)&net_ipaddr)[0], ((u8_t*)&net_ipaddr)[1], + ((u8_t*)&net_ipaddr)[2], ((u8_t*)&net_ipaddr)[3]); + lw_print(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t*)&net_netmask)[0], ((u8_t*)&net_netmask)[1], + ((u8_t*)&net_netmask)[2], ((u8_t*)&net_netmask)[3]); + lw_print(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t*)&net_gw)[0], ((u8_t*)&net_gw)[1], + ((u8_t*)&net_gw)[2], ((u8_t*)&net_gw)[3]); + lw_print("************************************************\r\n"); + + lwip_config_input(&gnetif); } +void lwip_config_net(uint8_t enet_port, char* ip, char* mask, char* gw) +{ + ip4_addr_t net_ipaddr, net_netmask, net_gw; + char* eth_cfg; + + eth_cfg = ethernetif_config_enet_set(enet_port); + + if (chk_lwip_bit(LWIP_INIT_FLAG)) { + lw_print("lw: [%s] already ...\n", __func__); + + IP4_ADDR(&net_ipaddr, ip[0], ip[1], ip[2], ip[3]); + IP4_ADDR(&net_netmask, mask[0], mask[1], mask[2], mask[3]); + IP4_ADDR(&net_gw, gw[0], gw[1], gw[2], gw[3]); + + // update ip addr + netif_set_down(&gnetif); + netif_set_gw(&gnetif, &net_gw); + netif_set_netmask(&gnetif, &net_netmask); + netif_set_ipaddr(&gnetif, &net_ipaddr); + netif_set_up(&gnetif); + return; + } + set_lwip_bit(LWIP_INIT_FLAG); + + lw_print("lw: [%s] start ...\n", __func__); + + IP4_ADDR(&net_ipaddr, ip[0], ip[1], ip[2], ip[3]); + IP4_ADDR(&net_netmask, mask[0], mask[1], mask[2], mask[3]); + IP4_ADDR(&net_gw, gw[0], gw[1], gw[2], gw[3]); + + lwip_init(); + + if (0 == enet_port) { +#ifdef NETIF_ENET0_INIT_FUNC + netif_add(&gnetif, &net_ipaddr, &net_netmask, &net_gw, eth_cfg, NETIF_ENET0_INIT_FUNC, + ethernet_input); +#endif + } else if (1 == enet_port) { +#ifdef NETIF_ENET1_INIT_FUNC + netif_add(&gnetif, &net_ipaddr, &net_netmask, &net_gw, eth_cfg, NETIF_ENET1_INIT_FUNC, + ethernet_input); +#endif + } + + netif_set_default(&gnetif); + netif_set_up(&gnetif); + + if (chk_lwip_bit(LWIP_PRINT_FLAG)) { + lw_notice("\r\n************************************************\r\n"); + lw_notice(" Network Configuration\r\n"); + lw_notice("************************************************\r\n"); + lw_notice(" IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t*)&net_ipaddr)[0], ((u8_t*)&net_ipaddr)[1], + ((u8_t*)&net_ipaddr)[2], ((u8_t*)&net_ipaddr)[3]); + lw_notice(" IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t*)&net_netmask)[0], ((u8_t*)&net_netmask)[1], + ((u8_t*)&net_netmask)[2], ((u8_t*)&net_netmask)[3]); + lw_notice(" IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t*)&net_gw)[0], ((u8_t*)&net_gw)[1], + ((u8_t*)&net_gw)[2], ((u8_t*)&net_gw)[3]); + lw_notice("************************************************\r\n"); + } + lwip_config_input(&gnetif); +} diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/core/ipv4/ip4_addr.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/core/ipv4/ip4_addr.c index 33204d114..432b06361 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/core/ipv4/ip4_addr.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/core/ipv4/ip4_addr.c @@ -314,7 +314,7 @@ ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen) *rp++ = '.'; ap++; } - *--rp = 0; + *--rp = '\0'; return buf; } diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/Makefile b/Ubiquitous/XiZi_IIoT/resources/ethernet/Makefile index 7de558d34..97452c54f 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/Makefile +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/Makefile @@ -1,4 +1,4 @@ -SRC_DIR += cmd_lwip +SRC_DIR += cmd_lwip netdev LWIP_DIR := LwIP include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_config_demo.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_config_demo.c index 8355989e2..8856be9ff 100755 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_config_demo.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_config_demo.c @@ -19,48 +19,45 @@ */ #include -#include #include -#include #include +#include +#include +#include + +#include /******************************************************************************/ uint8_t enet_id = 0; -static void LwipSetIPTask(void *param) +static void LwipSetIPTask(void* param) { - uint8_t enet_port = *(uint8_t *)param; ///< test enet port + uint8_t enet_port = *(uint8_t*)param; ///< test enet port printf("lw: [%s] config netport id[%d]\n", __func__, enet_port); // lwip_config_net(enet_port, lwip_ipaddr, lwip_netmask, lwip_gwaddr); lwip_config_tcp(enet_port, lwip_ipaddr, lwip_netmask, lwip_gwaddr); } -void LwipSetIPTest(int argc, char *argv[]) +void LwipSetIPTest(int argc, char* argv[]) { - if(argc >= 4) - { + if (argc >= 4) { printf("lw: [%s] ip %s mask %s gw %s netport %s\n", __func__, argv[1], argv[2], argv[3], argv[4]); sscanf(argv[1], "%d.%d.%d.%d", &lwip_ipaddr[0], &lwip_ipaddr[1], &lwip_ipaddr[2], &lwip_ipaddr[3]); sscanf(argv[2], "%d.%d.%d.%d", &lwip_netmask[0], &lwip_netmask[1], &lwip_netmask[2], &lwip_netmask[3]); sscanf(argv[3], "%d.%d.%d.%d", &lwip_gwaddr[0], &lwip_gwaddr[1], &lwip_gwaddr[2], &lwip_gwaddr[3]); sscanf(argv[4], "%d", &enet_id); - if(0 == enet_id) - { + if (0 == enet_id) { printf("save eth0 info\n"); memcpy(lwip_eth0_ipaddr, lwip_ipaddr, 20); memcpy(lwip_eth0_netmask, lwip_netmask, 20); memcpy(lwip_eth0_gwaddr, lwip_gwaddr, 20); - } - else if(1 == enet_id) - { + } else if (1 == enet_id) { printf("save eth1 info\n"); memcpy(lwip_eth1_ipaddr, lwip_ipaddr, 20); memcpy(lwip_eth1_netmask, lwip_netmask, 20); memcpy(lwip_eth1_gwaddr, lwip_gwaddr, 20); } - } - else if(argc == 2) - { + } else if (argc == 2) { printf("lw: [%s] set eth0 ipaddr %s \n", __func__, argv[1]); sscanf(argv[1], "%d.%d.%d.%d", &lwip_ipaddr[0], &lwip_ipaddr[1], &lwip_ipaddr[2], &lwip_ipaddr[3]); memcpy(lwip_eth0_ipaddr, lwip_ipaddr, strlen(lwip_ipaddr)); @@ -70,39 +67,56 @@ void LwipSetIPTest(int argc, char *argv[]) } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(5), - setip, LwipSetIPTest, setip [IP] [Netmask] [Gateway] [port]); + setip, LwipSetIPTest, setip[IP][Netmask][Gateway][port]); - -void LwipShowIPTask(int argc, char *argv[]) +void LwipShowIPTask(int argc, char* argv[]) { #ifdef configMAC_ADDR char mac_addr0[] = configMAC_ADDR; #endif + // find default netdev + struct netdev* netdev = netdev_get_by_name("en\0"); + if (netdev == NULL) { + lw_notice("[%s] Netdev not found by name en\n"); + struct netdev* default_netdev = NETDEV_DEFAULT; + } + lw_notice("\r\n************************************************\r\n"); lw_notice(" Network Configuration\r\n"); lw_notice("************************************************\r\n"); - lw_notice(" ETH0 IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_eth0_ipaddr)[0], ((u8_t *)&lwip_eth0_ipaddr)[1], - ((u8_t *)&lwip_eth0_ipaddr)[2], ((u8_t *)&lwip_eth0_ipaddr)[3]); - lw_notice(" ETH0 IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_eth0_netmask)[0], ((u8_t *)&lwip_eth0_netmask)[1], - ((u8_t *)&lwip_eth0_netmask)[2], ((u8_t *)&lwip_eth0_netmask)[3]); - lw_notice(" ETH0 IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_gwaddr)[0], ((u8_t *)&lwip_eth0_gwaddr)[1], - ((u8_t *)&lwip_eth0_gwaddr)[2], ((u8_t *)&lwip_eth0_gwaddr)[3]); + // lw_notice(" ETH0 IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth0_ipaddr)[0], ((u8_t*)&lwip_eth0_ipaddr)[1], + // ((u8_t*)&lwip_eth0_ipaddr)[2], ((u8_t*)&lwip_eth0_ipaddr)[3]); + // lw_notice(" ETH0 IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth0_netmask)[0], ((u8_t*)&lwip_eth0_netmask)[1], + // ((u8_t*)&lwip_eth0_netmask)[2], ((u8_t*)&lwip_eth0_netmask)[3]); + // lw_notice(" ETH0 IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_gwaddr)[0], ((u8_t*)&lwip_eth0_gwaddr)[1], + // ((u8_t*)&lwip_eth0_gwaddr)[2], ((u8_t*)&lwip_eth0_gwaddr)[3]); + // #ifdef configMAC_ADDR + // lw_notice(" ETH0 MAC Address : %x:%x:%x:%x:%x:%x\r\n", mac_addr0[0], mac_addr0[1], mac_addr0[2], + // mac_addr0[3], mac_addr0[4], mac_addr0[5]); + // #endif + + lw_notice(" ETH0 IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth0_ipaddr)[0], ((u8_t*)&lwip_eth0_ipaddr)[1], + ((u8_t*)&lwip_eth0_ipaddr)[2], ((u8_t*)&lwip_eth0_ipaddr)[3]); + lw_notice(" ETH0 IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth0_netmask)[0], ((u8_t*)&lwip_eth0_netmask)[1], + ((u8_t*)&lwip_eth0_netmask)[2], ((u8_t*)&lwip_eth0_netmask)[3]); + lw_notice(" ETH0 IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_gwaddr)[0], ((u8_t*)&lwip_eth0_gwaddr)[1], + ((u8_t*)&lwip_eth0_gwaddr)[2], ((u8_t*)&lwip_eth0_gwaddr)[3]); #ifdef configMAC_ADDR lw_notice(" ETH0 MAC Address : %x:%x:%x:%x:%x:%x\r\n", mac_addr0[0], mac_addr0[1], mac_addr0[2], mac_addr0[3], mac_addr0[4], mac_addr0[5]); #endif + #ifdef BOARD_NET_COUNT - if(BOARD_NET_COUNT > 1) - { + if (BOARD_NET_COUNT > 1) { char mac_addr1[] = configMAC_ADDR_ETH1; lw_notice("\r\n"); - lw_notice(" ETH1 IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_eth1_ipaddr)[0], ((u8_t *)&lwip_eth1_ipaddr)[1], - ((u8_t *)&lwip_eth1_ipaddr)[2], ((u8_t *)&lwip_eth1_ipaddr)[3]); - lw_notice(" ETH1 IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_eth1_netmask)[0], ((u8_t *)&lwip_eth1_netmask)[1], - ((u8_t *)&lwip_eth1_netmask)[2], ((u8_t *)&lwip_eth1_netmask)[3]); - lw_notice(" ETH1 IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t *)&lwip_eth1_gwaddr)[0], ((u8_t *)&lwip_eth1_gwaddr)[1], - ((u8_t *)&lwip_eth1_gwaddr)[2], ((u8_t *)&lwip_eth1_gwaddr)[3]); + lw_notice(" ETH1 IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth1_ipaddr)[0], ((u8_t*)&lwip_eth1_ipaddr)[1], + ((u8_t*)&lwip_eth1_ipaddr)[2], ((u8_t*)&lwip_eth1_ipaddr)[3]); + lw_notice(" ETH1 IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth1_netmask)[0], ((u8_t*)&lwip_eth1_netmask)[1], + ((u8_t*)&lwip_eth1_netmask)[2], ((u8_t*)&lwip_eth1_netmask)[3]); + lw_notice(" ETH1 IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth1_gwaddr)[0], ((u8_t*)&lwip_eth1_gwaddr)[1], + ((u8_t*)&lwip_eth1_gwaddr)[2], ((u8_t*)&lwip_eth1_gwaddr)[3]); lw_notice(" ETH1 MAC Address : %x:%x:%x:%x:%x:%x\r\n", mac_addr1[0], mac_addr1[1], mac_addr1[2], mac_addr1[3], mac_addr1[4], mac_addr1[5]); } @@ -111,5 +125,4 @@ void LwipShowIPTask(int argc, char *argv[]) } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), - showip, LwipShowIPTask, GetIp [IP] [Netmask] [Gateway]); - + showip, LwipShowIPTask, GetIp[IP][Netmask][Gateway]); diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/Makefile b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/Makefile new file mode 100644 index 000000000..6974c011c --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/Makefile @@ -0,0 +1,4 @@ +SRC_FILES += netdev_register.c netdev_manipulate.c netdev_lowlevel.c + + +include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_ipaddr_util.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_ipaddr_util.c new file mode 100644 index 000000000..e69de29bb diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_lowlevel.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_lowlevel.c new file mode 100644 index 000000000..694fdcdbc --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_lowlevel.c @@ -0,0 +1,220 @@ +#include +#include +#include + +/** + * This function will set network interface device IP address. + * @NOTE it can only be called in the network interface device driver. + * + * @param netdev the network interface device to change + * @param ip_addr the new IP address + */ +void netdev_low_level_set_ipaddr(struct netdev* netdev, const ip_addr_t* ip_addr) +{ + CHECK(ip_addr); + + if (netdev && ip_addr_cmp(&(netdev->ip_addr), ip_addr) == 0) { + ip_addr_copy(netdev->ip_addr, *ip_addr); + + /* execute IP address change callback function */ + if (netdev->addr_callback) { + netdev->addr_callback(netdev, NETDEV_CB_ADDR_IP); + } + } +} + +/** + * This function will set network interface device netmask address. + * @NOTE it can only be called in the network interface device driver. + * + * @param netdev the network interface device to change + * @param netmask the new netmask address + */ +void netdev_low_level_set_netmask(struct netdev* netdev, const ip_addr_t* netmask) +{ + CHECK(netmask); + + if (netdev && ip_addr_cmp(&(netdev->netmask), netmask) == 0) { + ip_addr_copy(netdev->netmask, *netmask); + + /* execute netmask address change callback function */ + if (netdev->addr_callback) { + netdev->addr_callback(netdev, NETDEV_CB_ADDR_NETMASK); + } + } +} + +/** + * This function will set network interface device gateway address. + * @NOTE it can only be called in the network interface device driver. + * + * @param netdev the network interface device to change + * @param gw the new gateway address + */ +void netdev_low_level_set_gw(struct netdev* netdev, const ip_addr_t* gw) +{ + CHECK(gw); + + if (netdev && ip_addr_cmp(&(netdev->gw), gw) == 0) { + ip_addr_copy(netdev->gw, *gw); + + /* execute gateway address change callback function */ + if (netdev->addr_callback) { + netdev->addr_callback(netdev, NETDEV_CB_ADDR_GATEWAY); + } + } +} + +/** + * This function will set network interface device DNS server address. + * @NOTE it can only be called in the network interface device driver. + * + * @param netdev the network interface device to change + * @param dns_num the number of the DNS server + * @param dns_server the new DNS server address + * + */ +void netdev_low_level_set_dns_server(struct netdev* netdev, uint8_t dns_num, const ip_addr_t* dns_server) +{ + unsigned int index; + + CHECK(dns_server); + + if (netdev == NULL) { + return; + } + /* check DNS servers is exist */ + for (index = 0; index < NETDEV_DNS_SERVERS_NUM; index++) { + if (ip_addr_cmp(&(netdev->dns_servers[index]), dns_server)) { + return; + } + } + + if (dns_num < NETDEV_DNS_SERVERS_NUM) { + ip_addr_copy(netdev->dns_servers[dns_num], *dns_server); + + /* execute DNS servers address change callback function */ + if (netdev->addr_callback) { + netdev->addr_callback(netdev, NETDEV_CB_ADDR_DNS_SERVER); + } + } +} + +/* Change to the first link_up network interface device automatically */ +static void netdev_auto_change_default(struct netdev* netdev) +{ + struct netdev* new_netdev = NULL; + + if (netdev->flags & NETDEV_FLAG_LINK_UP) { + if (!(NETDEV_DEFAULT->flags & NETDEV_FLAG_LINK_UP)) { + netdev_set_default(netdev); + } + return; + } + if (memcmp(netdev, NETDEV_DEFAULT, sizeof(struct netdev)) == 0) { + new_netdev = netdev_get_first_by_flags(NETDEV_FLAG_LINK_UP); + if (new_netdev) { + netdev_set_default(new_netdev); + } + } +} + +/** + * This function will set network interface device status. + * @NOTE it can only be called in the network interface device driver. + * + * @param netdev the network interface device to change + * @param is_up the new status + */ +void netdev_low_level_set_status(struct netdev* netdev, bool is_up) +{ + if (netdev && netdev_is_up(netdev) != is_up) { + if (is_up) { + netdev->flags |= NETDEV_FLAG_UP; + } else { + netdev->flags &= ~NETDEV_FLAG_UP; + + /* change to the first link_up network interface device automatically */ + netdev_auto_change_default(netdev); + } + + /* execute network interface device status change callback function */ + if (netdev->status_callback) { + netdev->status_callback(netdev, is_up ? NETDEV_CB_STATUS_UP : NETDEV_CB_STATUS_DOWN); + } + } +} + +/** + * This function will set network interface device active link status. + * @NOTE it can only be called in the network interface device driver. + * + * @param netdev the network interface device to change + * @param is_up the new link status + */ +void netdev_low_level_set_link_status(struct netdev* netdev, bool is_up) +{ + if (netdev && netdev_is_link_up(netdev) != is_up) { + if (is_up) { + netdev->flags |= NETDEV_FLAG_LINK_UP; + } else { + netdev->flags &= ~NETDEV_FLAG_LINK_UP; + + /* set network interface device flags to internet down */ + netdev->flags &= ~NETDEV_FLAG_INTERNET_UP; + + netdev_auto_change_default(netdev); + } + + /* execute link status change callback function */ + if (netdev->status_callback) { + netdev->status_callback(netdev, is_up ? NETDEV_CB_STATUS_LINK_UP : NETDEV_CB_STATUS_LINK_DOWN); + } + } +} + +/** + * This function will set network interface device active internet status. + * @NOTE it can only be called in the network interface device driver. + * + * @param netdev the network interface device to change + * @param is_up the new internet status + */ +void netdev_low_level_set_internet_status(struct netdev* netdev, bool is_up) +{ + if (netdev && netdev_is_internet_up(netdev) != is_up) { + if (is_up) { + netdev->flags |= NETDEV_FLAG_INTERNET_UP; + } else { + netdev->flags &= ~NETDEV_FLAG_INTERNET_UP; + } + + /* execute network interface device status change callback function */ + if (netdev->status_callback) { + netdev->status_callback(netdev, is_up ? NETDEV_CB_STATUS_INTERNET_UP : NETDEV_CB_STATUS_INTERNET_DOWN); + } + } +} + +/** + * This function will set network interface device DHCP status. + * @NOTE it can only be called in the network interface device driver. + * + * @param netdev the network interface device to change + * @param is_up the new DHCP status + */ +void netdev_low_level_set_dhcp_status(struct netdev* netdev, bool is_enable) +{ + if (netdev && netdev_is_dhcp_enabled(netdev) != is_enable) { + if (is_enable) { + netdev->flags |= NETDEV_FLAG_DHCP; + } else { + netdev->flags &= ~NETDEV_FLAG_DHCP; + } + + /* execute DHCP status change callback function */ + if (netdev->status_callback) { + netdev->status_callback(netdev, is_enable ? NETDEV_CB_STATUS_DHCP_ENABLE : NETDEV_CB_STATUS_DHCP_DISABLE); + } + } +} \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_manipulate.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_manipulate.c new file mode 100644 index 000000000..450155d54 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_manipulate.c @@ -0,0 +1,288 @@ +#include +#include +#include +#include + +inline int netdev_check_set_addr_condition(struct netdev* netdev) +{ + if (NULL == netdev->ops || NULL == netdev->ops->set_addr_info) { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("The network interface device(%s) not support to set IP address.\n", netdev->name)); + return -ERROR; + } + + // check whether dhcp enabled + if (netdev_is_dhcp_enabled(netdev)) { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("The network interface device(%s) DHCP capability is enable, not support set IP address.\n", netdev->name)); + return -ERROR; + } + + return EOK; +} + +/** + * This function will set network interface device IP address. + * + * @param netdev the network interface device to change + * @param ip_addr the new IP address + * + * @return 0: set IP address successfully + * -1: set IP address failed + */ +int netdev_set_ipaddr(struct netdev* netdev, const ip_addr_t* ipaddr) +{ + CHECK(netdev); + CHECK(ipaddr); + + if (EOK != netdev_check_set_addr_condition(netdev)) { + return -ERROR; + } + + return netdev->ops->set_addr_info(netdev, (ip_addr_t*)ipaddr, NULL, NULL); +} + +/** + * This function will set network interface device netmask address. + * + * @param netdev the network interface device to change + * @param netmask the new netmask address + * + * @return 0: set netmask address successfully + * -1: set netmask address failed + */ +int netdev_set_netmask(struct netdev* netdev, const ip_addr_t* netmask) +{ + CHECK(netdev); + CHECK(netmask); + + if (EOK != netdev_check_set_addr_condition(netdev)) { + return -ERROR; + } + + return netdev->ops->set_addr_info(netdev, NULL, (ip_addr_t*)netmask, NULL); +} + +/** + * This function will set network interface device gateway address. + * + * @param netdev the network interface device to change + * @param gw the new gateway address + * + * @return 0: set gateway address successfully + * -1: set gateway address failed + */ +int netdev_set_gw(struct netdev* netdev, const ip_addr_t* gw) +{ + CHECK(netdev); + CHECK(gw); + + if (EOK != netdev_check_set_addr_condition(netdev)) { + return -ERROR; + } + + /* execute network interface device set gateway address operations */ + return netdev->ops->set_addr_info(netdev, NULL, NULL, (ip_addr_t*)gw); +} + +/** + * This function will set network interface device DNS server address. + * + * @param netdev the network interface device to change + * @param dns_num the number of the DNS server + * @param dns_server the new DNS server address + * + * @return 0: set netmask address successfully + * -1: set netmask address failed + */ +int netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, const ip_addr_t* dns_server) +{ + CHECK(netdev); + CHECK(dns_server); + + // check dns server number + if (dns_num >= NETDEV_DNS_SERVERS_NUM) { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("The number of DNS servers(%d) set exceeds the maximum number(%d).\n", dns_num + 1, NETDEV_DNS_SERVERS_NUM)); + return -ERROR; + } + + // check dns set function existence + if (NULL == netdev->ops || NULL == netdev->ops->set_dns_server) { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("The network interface device(%s) not support to set DNS server address.\n", netdev->name)); + return -ERROR; + } + + return netdev->ops->set_dns_server(netdev, dns_num, (ip_addr_t*)dns_server); +} + +/** + * This function will set callback to be called when the network interface device status has been changed. + * + * @param netdev the network interface device to change + * @param status_callback the callback be called when the status has been changed. + */ +void netdev_set_status_callback(struct netdev* netdev, netdev_callback_fn status_callback) +{ + CHECK(netdev); + CHECK(status_callback); + + netdev->status_callback = status_callback; +} + +/** + * This function will set callback to be called when the network interface device address has been changed. + * + * @param netdev the network interface device to change + * @param addr_callback the callback be called when the address has been changed. + */ +void netdev_set_addr_callback(struct netdev* netdev, netdev_callback_fn addr_callback) +{ + CHECK(netdev); + CHECK(addr_callback); + + netdev->addr_callback = addr_callback; +} + +/** + * This function will enable network interface device . + * + * @param netdev the network interface device to change + * + * @return 0: set status successfully + * -1: set status failed + */ +int netdev_set_up(struct netdev* netdev) +{ + CHECK(netdev); + + if (!netdev->ops || !netdev->ops->set_up) { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("The network interface device(%s) not support to set status.\n", netdev->name)); + return -ERROR; + } + + /* network interface device status flags check */ + if (netdev_is_up(netdev)) { + return EOK; + } + + /* execute enable network interface device operations by network interface device driver */ + return netdev->ops->set_up(netdev); +} + +/** + * This function will disable network interface device. + * + * @param netdev the network interface device to change + * + * @return 0: set status successfully + * -1: set sttaus failed + */ +int netdev_set_down(struct netdev* netdev) +{ + CHECK(netdev); + + if (!netdev->ops || !netdev->ops->set_down) { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("The network interface device(%s) not support to set status.\n", netdev->name)); + return -ERROR; + } + + /* network interface device status flags check */ + if (!netdev_is_up(netdev)) { + return EOK; + } + + /* execute disable network interface device operations by network interface driver */ + return netdev->ops->set_down(netdev); +} + +/** + * This function will get the first network interface device + * with the flags in network interface device list. + * + * @param flags the network interface device flags + * + * @return != NULL: network interface device object + * NULL: get failed + */ +struct netdev* netdev_get_first_by_flags(uint16_t flags) +{ + if (NETDEV_LISTHEAD == NULL) { + return NULL; + } + + // get netdev from list + x_base lock = DISABLE_INTERRUPT(); + struct netdev* current_dev = NETDEV_LISTHEAD; + SINGLE_LINKLIST_FOR_EACH_ENTRY(current_dev, &(NETDEV_LISTHEAD->list), list) + { + if (NULL != current_dev && 0 != (current_dev->flags & flags)) { + ENABLE_INTERRUPT(lock); + return current_dev; + } + } + ENABLE_INTERRUPT(lock); + + return NULL; +} + +/** + * This function will get the first network interface device + * in network interface device list by IP address. + * + * @param ip_addr the network interface device IP address + * + * @return != NULL: network interface device object + * NULL: get failed + */ +struct netdev* netdev_get_by_ipaddr(ip_addr_t* ip_addr) +{ + if (NETDEV_LISTHEAD == NULL) { + return NULL; + } + + // get netdev from list + x_base lock = DISABLE_INTERRUPT(); + struct netdev* current_dev = NETDEV_LISTHEAD; + SINGLE_LINKLIST_FOR_EACH_ENTRY(current_dev, &(NETDEV_LISTHEAD->list), list) + { + if (NULL != current_dev && ip_addr_cmp(&(current_dev->ip_addr), ip_addr)) { + ENABLE_INTERRUPT(lock); + return current_dev; + } + } + ENABLE_INTERRUPT(lock); + + return NULL; +} + +/** + * This function will get network interface device + * in network interface device list by netdev name. + * + * @param name the network interface device name + * + * @return != NULL: network interface device object + * NULL: get failed + */ +struct netdev* netdev_get_by_name(const char* name) +{ + if (NETDEV_LISTHEAD == NULL) { + return NULL; + } + + // get netdev from list + x_base lock = DISABLE_INTERRUPT(); + struct netdev* current_dev = NETDEV_LISTHEAD; + SINGLE_LINKLIST_FOR_EACH_ENTRY(current_dev, &(NETDEV_LISTHEAD->list), list) + { + if (NULL == current_dev) { + continue; + } + uint32_t name_len = strlen(current_dev->name); + if (NULL != current_dev && (strncmp(current_dev->name, name, strlen(current_dev->name) < NAME_NUM_MAX ? strlen(current_dev->name) : NAME_NUM_MAX) == 0)) { + ENABLE_INTERRUPT(lock); + return current_dev; + } + } + ENABLE_INTERRUPT(lock); + + return NULL; +} diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_register.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_register.c new file mode 100644 index 000000000..d4e855e09 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_register.c @@ -0,0 +1,184 @@ + +#include +#include +#include +#include +#include +#include +#include + +struct netdev** get_netdev_listhead() +{ + static struct netdev* netdev_listhead = NULL; + return &netdev_listhead; +} +struct netdev** get_default_netdev() +{ + static struct netdev* netdev_default = NULL; + return &netdev_default; +} +static netdev_callback_fn g_netdev_register_callback = NULL; +static netdev_callback_fn g_netdev_default_change_callback = NULL; + +int netdev_register(struct netdev* netdev, const char* name, void* user_data) +{ + CHECK(netdev != NULL); + CHECK(name != NULL); + + // set flag mask, assert network is down + uint16_t flag_mask = 0; + flag_mask = NETDEV_FLAG_UP | NETDEV_FLAG_LINK_UP | NETDEV_FLAG_INTERNET_UP | NETDEV_FLAG_DHCP; + netdev->flags &= ~flag_mask; + + // clear dev setting + ip_addr_set_zero(&(netdev->ip_addr)); + ip_addr_set_zero(&(netdev->netmask)); + ip_addr_set_zero(&(netdev->gw)); + + IP_SET_TYPE_VAL(netdev->ip_addr, IPADDR_TYPE_V4); + IP_SET_TYPE_VAL(netdev->netmask, IPADDR_TYPE_V4); + IP_SET_TYPE_VAL(netdev->gw, IPADDR_TYPE_V4); + +#if NETDEV_IPV6 + for (index = 0; index < NETDEV_IPV6_NUM_ADDRESSES; index++) { + ip_addr_set_zero(&(netdev->ip6_addr[index])); + IP_SET_TYPE_VAL(netdev->ip_addr, IPADDR_TYPE_V6); + } +#endif /* NETDEV_IPV6 */ + + // clear DNS servers + for (uint16_t idx = 0; idx < NETDEV_DNS_SERVERS_NUM; idx++) { + ip_addr_set_zero(&(netdev->dns_servers[idx])); + IP_SET_TYPE_VAL(netdev->ip_addr, IPADDR_TYPE_V4); + } + // clear callback fn + netdev->addr_callback = NULL; + netdev->status_callback = NULL; + + // validate name + uint32_t name_len = strlen(name); + if (name_len < NAME_NUM_MAX) { + strncpy(netdev->name, name, name_len); + netdev->name[name_len] = '\0'; + } else { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] name too long.\n", __func__)); + strncpy(netdev->name, name, NAME_NUM_MAX - 1); + netdev->name[NAME_NUM_MAX - 1] = '\0'; + } + + netdev->user_data = user_data; + + InitSingleLinkList(&(netdev->list)); + + // insert netdev to global list + x_base lock = DISABLE_INTERRUPT(); + if (NETDEV_LISTHEAD == NULL) { + NETDEV_LISTHEAD = netdev; + } else { + SingleLinkListNodeInsert(&(NETDEV_LISTHEAD->list), &(netdev->list)); + } + ENABLE_INTERRUPT(lock); + + if (NETDEV_DEFAULT == NULL) { + // set first met netdev to default netdev + netdev_set_default(NETDEV_LISTHEAD); + } + + if (g_netdev_register_callback) { + g_netdev_register_callback(netdev, NETDEV_CB_REGISTER); + } + + return EOK; +} + +/** + * This function will unregister network interface device and + * delete it from network interface device list. + * + * @param netdev the network interface device object + * + * @return 0: unregistered successfully + * -1: unregistered failed + */ +int netdev_unregister(struct netdev* netdev) +{ + CHECK(netdev); + + if (NETDEV_LISTHEAD == NULL) { + return -ERROR; + } + + // remove netdev from netdev list + x_base lock = DISABLE_INTERRUPT(); + struct netdev* current_dev = NETDEV_LISTHEAD; + SINGLE_LINKLIST_FOR_EACH_ENTRY(current_dev, &(NETDEV_LISTHEAD->list), list) + { + // found netdev in list + if (current_dev == netdev) { + if (NETDEV_LISTHEAD == current_dev && NULL == SingleLinkListGetNextNode(&(current_dev->list))) { + // netdev is the only one in list + NETDEV_LISTHEAD = NULL; + } else { + SingleLinkListRmNode(&(NETDEV_LISTHEAD->list), &(current_dev->list)); + } + + // deal default netdev + if (current_dev == NETDEV_DEFAULT) { + NETDEV_DEFAULT = NULL; + } + break; + } + } + ENABLE_INTERRUPT(lock); + + if (NETDEV_DEFAULT == NULL) { + netdev_set_default(NETDEV_LISTHEAD); + } + + // clean netdev if found one + if (current_dev == netdev) { + memset(netdev, 0, sizeof(*netdev)); + } + + return EOK; +} + +/** + * This function will set default network interface device. + * + * @param netdev the network interface device to change + */ +void netdev_set_default(struct netdev* netdev) +{ + if (netdev && (netdev != NETDEV_DEFAULT)) { + NETDEV_DEFAULT = netdev; + + // set default function + if (netdev->ops && netdev->ops->set_default) { + netdev->ops->set_default(netdev); + } + + // default change callback + if (g_netdev_default_change_callback) { + g_netdev_default_change_callback(netdev, NETDEV_CB_DEFAULT_CHANGE); + } + + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("Setting default network interface device name(%s) successfully.\n", netdev->name)); + } +} + +/** + * This function will set register callback + * + * @param register_callback the network register callback + * + */ +void netdev_set_register_callback(netdev_callback_fn register_callback) +{ + g_netdev_register_callback = register_callback; +} + +void netdev_set_default_change_callback(netdev_callback_fn default_change_cb) +{ + g_netdev_default_change_callback = default_change_cb; +} \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h b/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h new file mode 100644 index 000000000..56cb1c02f --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h @@ -0,0 +1,182 @@ + +#ifndef __NETDEV_H__ +#define __NETDEV_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NETDEV_DEBUG true + +#define NETDEV_DEBUG_PRINT_IP_INFO(ip, netmask, gw) \ + ("[%s %d]ip: %u.%u.%u.%u, netmask: %u.%u.%u.%u, gw: %u.%u.%u.%u\n", \ + __func__, __LINE__, \ + ((u8_t*)&ip)[0], ((u8_t*)&ip)[1], ((u8_t*)&ip)[2], ((u8_t*)&ip)[3], \ + ((u8_t*)&netmask)[0], ((u8_t*)&netmask)[1], ((u8_t*)&netmask)[2], ((u8_t*)&netmask)[3], \ + ((u8_t*)&gw)[0], ((u8_t*)&gw)[1], ((u8_t*)&gw)[2], ((u8_t*)&gw)[3]) + +/* the maximum of all used hardware address lengths */ +#ifndef NETDEV_HWADDR_MAX_LEN +#define NETDEV_HWADDR_MAX_LEN 8U +#endif + +/* the maximum of dns server number supported */ +#ifndef NETDEV_DNS_SERVERS_NUM +#define NETDEV_DNS_SERVERS_NUM 2U +#endif + +/* whether the network interface device is 'up' (set by the network interface driver or application) */ +#define NETDEV_FLAG_UP 0x01U +/* if set, the network interface device has broadcast capability, only supported in the 'lwIP' stack */ +#define NETDEV_FLAG_BROADCAST 0x02U +/* if set, the network interface device has an active link (set by the network interface driver) */ +#define NETDEV_FLAG_LINK_UP 0x04U +/* if set, the network interface device is an ethernet device using ARP, only supported in the 'lwIP' stack */ +#define NETDEV_FLAG_ETHARP 0x08U +/* if set, the network interface device is an ethernet device, only supported in the 'lwIP' stack */ +#define NETDEV_FLAG_ETHERNET 0x10U +/* if set, the network interface device has IGMP capability, only supported in the 'lwIP' stack */ +#define NETDEV_FLAG_IGMP 0x20U +/* if set, the network interface device has MLD6 capability, only supported in the 'lwIP' stack */ +#define NETDEV_FLAG_MLD6 0x40U +/* if set, the network interface device connected to internet successfully (set by the network interface driver) */ +#define NETDEV_FLAG_INTERNET_UP 0x80U +/* if set, the network interface device has DHCP capability (set by the network interface device driver or application) */ +#define NETDEV_FLAG_DHCP 0x100U + +enum netdev_cb_type { + NETDEV_CB_ADDR_IP, /* IP address */ + NETDEV_CB_ADDR_NETMASK, /* subnet mask */ + NETDEV_CB_ADDR_GATEWAY, /* netmask */ + NETDEV_CB_ADDR_DNS_SERVER, /* dns server */ + NETDEV_CB_STATUS_UP, /* changed to 'up' */ + NETDEV_CB_STATUS_DOWN, /* changed to 'down' */ + NETDEV_CB_STATUS_LINK_UP, /* changed to 'link up' */ + NETDEV_CB_STATUS_LINK_DOWN, /* changed to 'link down' */ + NETDEV_CB_STATUS_INTERNET_UP, /* changed to 'internet up' */ + NETDEV_CB_STATUS_INTERNET_DOWN, /* changed to 'internet down' */ + NETDEV_CB_STATUS_DHCP_ENABLE, /* enable DHCP capability */ + NETDEV_CB_STATUS_DHCP_DISABLE, /* disable DHCP capability */ + NETDEV_CB_REGISTER, /* netdev register */ + NETDEV_CB_DEFAULT_CHANGE, /* netdev default change */ +}; + +struct netdev; +typedef void (*netdev_callback_fn)(struct netdev* netdev, enum netdev_cb_type type); + +struct netdev_ops { + /* set network interface device hardware status operations */ + int (*set_up)(struct netdev* netdev); + int (*set_down)(struct netdev* netdev); + + /* set network interface device address information operations */ + int (*set_addr_info)(struct netdev* netdev, ip_addr_t* ip_addr, ip_addr_t* netmask, ip_addr_t* gw); + int (*set_dns_server)(struct netdev* netdev, uint8_t dns_num, ip_addr_t* dns_server); + int (*set_dhcp)(struct netdev* netdev, bool is_enabled); + +#ifdef RT_USING_FINSH + /* set network interface device common network interface device operations */ + int (*ping)(struct netdev* netdev, const char* host, size_t data_len, uint32_t timeout, struct netdev_ping_resp* ping_resp); + void (*netstat)(struct netdev* netdev); +#endif + + /* set default network interface device in current network stack*/ + int (*set_default)(struct netdev* netdev); +}; + +/* network interface device object */ +struct netdev { + SysSingleLinklistType list; + + char name[NAME_NUM_MAX]; /* network interface device name */ + ip_addr_t ip_addr; /* IP address */ + ip_addr_t netmask; /* subnet mask */ + ip_addr_t gw; /* gateway */ +#if NETDEV_IPV6 + ip_addr_t ip6_addr[NETDEV_IPV6_NUM_ADDRESSES]; /* array of IPv6 addresses */ +#endif /* NETDEV_IPV6 */ + ip_addr_t dns_servers[NETDEV_DNS_SERVERS_NUM]; /* DNS server */ + uint8_t hwaddr_len; /* hardware address length */ + uint8_t hwaddr[NETDEV_HWADDR_MAX_LEN]; /* hardware address */ + + uint16_t flags; /* network interface device status flag */ + uint16_t mtu; /* maximum transfer unit (in bytes) */ + const struct netdev_ops* ops; /* network interface device operations */ + + netdev_callback_fn status_callback; /* network interface device flags change callback */ + netdev_callback_fn addr_callback; /* network interface device address information change callback */ + +#ifdef RT_USING_SAL + void* sal_user_data; /* user-specific data for SAL */ +#endif /* RT_USING_SAL */ + void* user_data; /* user-specific data */ +}; + +// netdev global list +#define NETDEV_LISTHEAD (*get_netdev_listhead()) +#define NETDEV_DEFAULT (*get_default_netdev()) +extern struct netdev** get_netdev_listhead(); +extern struct netdev** get_default_netdev(); + +// netdev register functions: netdev_register.c +int netdev_register(struct netdev* netdev, const char* name, void* user_data); +int netdev_unregister(struct netdev* netdev); +void netdev_set_default(struct netdev* netdev); +void netdev_set_register_callback(netdev_callback_fn status_callback); +void netdev_set_default_change_callback(netdev_callback_fn default_change_cb); + +// netdev manipulate functions: netdev_manipulate.c +struct netdev* netdev_get_first_by_flags(uint16_t flags); +struct netdev* netdev_get_by_ipaddr(ip_addr_t* ip_addr); +struct netdev* netdev_get_by_name(const char* name); + +int netdev_set_ipaddr(struct netdev* netdev, const ip_addr_t* ipaddr); +int netdev_set_netmask(struct netdev* netdev, const ip_addr_t* netmask); +int netdev_set_gw(struct netdev* netdev, const ip_addr_t* gw); +int netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, const ip_addr_t* dns_server); + +void netdev_set_status_callback(struct netdev* netdev, netdev_callback_fn status_callback); +void netdev_set_addr_callback(struct netdev* netdev, netdev_callback_fn addr_callback); + +/* Set network interface device status */ +int netdev_set_up(struct netdev* netdev); +int netdev_set_down(struct netdev* netdev); +int netdev_dhcp_enabled(struct netdev* netdev, bool is_enabled); + +// low level functions which should only be called by net drivers +void netdev_low_level_set_ipaddr(struct netdev* netdev, const ip_addr_t* ipaddr); +void netdev_low_level_set_netmask(struct netdev* netdev, const ip_addr_t* netmask); +void netdev_low_level_set_gw(struct netdev* netdev, const ip_addr_t* gw); +void netdev_low_level_set_dns_server(struct netdev* netdev, uint8_t dns_num, const ip_addr_t* dns_server); +void netdev_low_level_set_status(struct netdev* netdev, bool is_up); +void netdev_low_level_set_link_status(struct netdev* netdev, bool is_up); +void netdev_low_level_set_internet_status(struct netdev* netdev, bool is_up); +void netdev_low_level_set_dhcp_status(struct netdev* netdev, bool is_enable); + +static inline bool netdev_is_dhcp_enabled(struct netdev* netdev) +{ + return (netdev->flags & NETDEV_FLAG_DHCP) ? true : false; +} +static inline bool netdev_is_up(struct netdev* netdev) +{ + return (netdev->flags & NETDEV_FLAG_UP) ? true : false; +} +static inline bool netdev_is_internet_up(struct netdev* netdev) +{ + return (netdev->flags & NETDEV_FLAG_INTERNET_UP) ? true : false; +} +static inline bool netdev_is_link_up(struct netdev* netdev) +{ + return (netdev->flags & NETDEV_FLAG_LINK_UP) ? true : false; +} + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev_ipaddr.h b/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev_ipaddr.h new file mode 100644 index 000000000..cdc129300 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev_ipaddr.h @@ -0,0 +1,124 @@ + +#ifndef LWIP_HDR_IP4_ADDR_H +#ifndef __NETDEV_IPADDR_H__ +#define __NETDEV_IPADDR_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Currently using netdev ipv4 +#define NETDEV_IPV4 1 + +/* IP address types for use in ip_addr_t.type member */ +enum netdev_ip_addr_type { + /** IPv4 */ + IPADDR_TYPE_V4 = 0U, + /** IPv6 */ + IPADDR_TYPE_V6 = 6U, + /** IPv4+IPv6 ("dual-stack") */ + IPADDR_TYPE_ANY = 46U +}; + +#if (NETDEV_IPV4 && NETDEV_IPV6) /* Both IPV4 and IPV6 */ +// Todo +#elif NETDEV_IPV4 /* NETDEV_IPV4 */ + +#define IP_SET_TYPE_VAL(ipaddr, iptype) +#define IP_SET_TYPE(ipaddr, iptype) +#define IP_GET_TYPE(ipaddr) IPADDR_TYPE_V4 + +#define ip_addr_copy(dest, src) ip4_addr_copy(dest, src) +#define ip_addr_cmp(addr1, addr2) ip4_addr_cmp(addr1, addr2) +#define ip_addr_set(dest, src) ip4_addr_set(dest, src) +#define ip_addr_set_zero(ipaddr) ip4_addr_set_zero(ipaddr) +#define ip_addr_set_any(is_ipv6, ipaddr) ip4_addr_set_any(ipaddr) +#define ip_addr_isany_val(ipaddr) ip4_addr_isany_val(ipaddr) +#define ip_addr_isany(ipaddr) ip4_addr_isany(ipaddr) + +/* directly map this to the lwip internal functions */ +#define inet_addr(cp) netdev_ipaddr_addr(cp) +#define inet_aton(cp, addr) netdev_ip4addr_aton(cp, (ip4_addr_t*)addr) +#define inet_ntoa(addr) netdev_ip4addr_ntoa((const ip4_addr_t*)&(addr)) +#define inet_ntoa_r(addr, buf, buflen) netdev_ip4addr_ntoa_r((const ip4_addr_t*)&(addr), buf, buflen) + +#else /* NETDEV_IPV6 */ +// Todo +#endif /* NTDEV_IPV4 && NTDEV_IPV6 */ + +#ifdef NETDEV_IPV4 +/** IPv4 only: set the IP address given as an u32_t */ +#define ip4_addr_set_u32(dest_ipaddr, src_u32) ((dest_ipaddr)->addr = (src_u32)) +/** IPv4 only: get the IP address as an u32_t */ +#define ip4_addr_get_u32(src_ipaddr) ((src_ipaddr)->addr) + +/** 255.255.255.255 */ +#define IPADDR_NONE ((uint32_t)0xffffffffUL) +/** 127.0.0.1 */ +#define IPADDR_LOOPBACK ((uint32_t)0x7f000001UL) +/** 0.0.0.0 */ +#define IPADDR_ANY ((uint32_t)0x00000000UL) +/** 255.255.255.255 */ +#define IPADDR_BROADCAST ((uint32_t)0xffffffffUL) + +#define IP4ADDR_STRLEN_MAX 16 + +#if !defined(in_addr_t) && !defined(IN_ADDR_T_DEFINED) +typedef uint32_t in_addr_t; +#endif + +struct in_addr { + in_addr_t s_addr; +}; + +typedef struct ip4_addr { + uint32_t addr; +} ip4_addr_t; + +typedef ip4_addr_t ip_addr_t; + +/** Copy IP address - faster than ip4_addr_set: no NULL check */ +static inline void ip4_addr_copy(ip4_addr_t dest_addr, const ip4_addr_t src_addr) +{ + dest_addr.addr = src_addr.addr; +} +static inline bool ip4_addr_cmp(const ip4_addr_t* left_addr, const ip4_addr_t* right_addr) +{ + return left_addr->addr == right_addr->addr; +} +/** Safely copy one IP address to another (src may be NULL) */ +static inline void ip4_addr_set(ip4_addr_t* dest_addr, const ip4_addr_t* src_addr) +{ + dest_addr->addr = (src_addr == NULL) ? 0 : src_addr->addr; +} +/** Set complete address to zero */ +static inline void ip4_addr_set_zero(ip4_addr_t* ip4_addr) +{ + ip4_addr->addr = 0; +} +/** Set address to IPADDR_ANY (no need for htonl()) */ +static inline void ip4_addr_set_any(ip4_addr_t* ipaddr) +{ + ipaddr->addr = IPADDR_ANY; +} +static inline bool ip4_addr_isany_val(const ip4_addr_t ipaddr) +{ + return ipaddr.addr == IPADDR_ANY; +} +static inline bool ip4_addr_isany(const ip4_addr_t* ipaddr) +{ + return ipaddr == NULL || ip4_addr_isany_val(*ipaddr); +} + +#endif + +#ifdef __cplusplus +} +#endif + +#endif +#endif \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/tool/shell/letter-shell/cmd.c b/Ubiquitous/XiZi_IIoT/tool/shell/letter-shell/cmd.c index 82fd4eb6f..ff5e8f98d 100644 --- a/Ubiquitous/XiZi_IIoT/tool/shell/letter-shell/cmd.c +++ b/Ubiquitous/XiZi_IIoT/tool/shell/letter-shell/cmd.c @@ -98,7 +98,7 @@ long ShowTask(void) KPrintf("*************************************************************************************************\n"); #ifndef SCHED_POLICY_FIFO #ifdef ARCH_SMP - KPrintf(" STAT ID %-*.s PRI CORE STACK_DEPTH USED LEFT_TICKS ERROR_STAT\n", NAME_NUM_MAX, item_title); + KPrintf(" STAT ID %-*.s PRI CORE STACK_DEPTH USED LEFT_TICKS ERROR_STAT\n", _NUM_MAX, item_title); #else KPrintf(" STAT ID %-*.s PRI STACK_DEPTH USED LEFT_TICKS ERROR_STAT\n", NAME_NUM_MAX, item_title); #endif From a25eccfac88e45e38b05a6e09186fa80f0918487 Mon Sep 17 00:00:00 2001 From: Gitlinkiacu2016 Date: Thu, 27 Jul 2023 15:20:20 +0800 Subject: [PATCH 10/33] feat add app_test DIR for 2023_open_source_contest From fc4411dad444fa67a4ab61d01854a32e1011eacb Mon Sep 17 00:00:00 2001 From: IACU Date: Thu, 27 Jul 2023 15:23:16 +0800 Subject: [PATCH 11/33] feat add app_test DIR for 2023_open_source_contest from Liu_Weichao it is OK From fce13fe874318954882e093f8201e28a8a58e599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Wed, 2 Aug 2023 17:35:16 +0800 Subject: [PATCH 12/33] 23/08/02 1.Fix ByteManager Bug; 2.Move net delay to webnet(wn_session.h) so it wont affect other net functions. --- APP_Framework/Applications/main.c | 101 +++++++++++++ .../Applications/webnet/WebNet_XiUOS | 2 +- .../XiZi_IIoT/board/edu-arm32/config.mk | 6 +- .../XiZi_IIoT/kernel/memory/byte_manage.c | 143 ++++++++++-------- 4 files changed, 183 insertions(+), 69 deletions(-) diff --git a/APP_Framework/Applications/main.c b/APP_Framework/Applications/main.c index e1de024b5..54991e27b 100644 --- a/APP_Framework/Applications/main.c +++ b/APP_Framework/Applications/main.c @@ -15,6 +15,107 @@ // #include #include +pthread_mutex_t* get_memtest_mtx() { + static pthread_mutex_t mem_test_mtx; + return &mem_test_mtx; +} + + +extern void ShowMemory(); +void malloc_thread(void* size) +{ + int tid = GetKTaskDescriptor()->id.id; + int* tmp = (int*)size; + int malloc_size = *tmp; + + PrivMutexObtain(get_memtest_mtx()); + printf("TID: %d Working. size: %d\n", tid, malloc_size); + PrivMutexAbandon(get_memtest_mtx()); + + void* ptr_allocated = NULL; + while ((ptr_allocated = malloc(malloc_size)) != NULL) { + PrivMutexObtain(get_memtest_mtx()); + printf("\n[TID: %d]==================================================\n", tid); + ShowMemory(); + PrivMutexAbandon(get_memtest_mtx()); + } + + PrivMutexObtain(get_memtest_mtx()); + printf("TID: %d Exiting.\n", tid); + PrivMutexAbandon(get_memtest_mtx()); +} + +#include +int testMemMain() +{ + PrivMutexCreate(get_memtest_mtx(), NULL); + + int malloc_size = 10 * 1024; + int tid = KTaskCreate("mem1", malloc_thread, (void*)&malloc_size, 2048, 20); + StartupKTask(tid); + // malloc_thread(&malloc_size); + + + // void* ptr_allocated[5] = { NULL }; + // ptr_allocated[0] = malloc(malloc_size); + // printf("0x%x ==================================================\n", ptr_allocated[0]); + // ShowMemory(); + // ptr_allocated[1] = malloc(malloc_size); + // printf("0x%x ==================================================\n", ptr_allocated[1]); + // ShowMemory(); + + // malloc_size = 996; + // ptr_allocated[2] = malloc(malloc_size); + // printf("0x%x ==================================================\n", ptr_allocated[2]); + // ShowMemory(); + + // free(ptr_allocated[0]); + // printf("==================================================\n"); + // ShowMemory(); + // ptr_allocated[0] = malloc(malloc_size); + // printf("0x%x ==================================================\n", ptr_allocated[0]); + // ShowMemory(); + + // ptr_allocated[3] = malloc(malloc_size); + // free(ptr_allocated[2]); + // printf("0x%x ==================================================\n", ptr_allocated[3]); + // ShowMemory(); + + // malloc_size = 333; + // ptr_allocated[2] = malloc(malloc_size); + // ptr_allocated[4] = malloc(malloc_size); + // printf("0x%x ==================================================\n", ptr_allocated[2]); + // printf("0x%x ==================================================\n", ptr_allocated[4]); + // ShowMemory(); + + // free(ptr_allocated[0]); + // free(ptr_allocated[1]); + // free(ptr_allocated[2]); + // free(ptr_allocated[3]); + // free(ptr_allocated[4]); + + + int malloc_size2 = 1024; + int tid2 = KTaskCreate("mem2", malloc_thread, &malloc_size2, 2048, 20); + StartupKTask(tid2); + // malloc_thread(&malloc_size); + int malloc_size3 = 127; + int tid3 = KTaskCreate("mem3", malloc_thread, &malloc_size3, 2048, 20); + StartupKTask(tid3); + // malloc_thread(&malloc_size); + int malloc_size4 = 45; + int tid4 = KTaskCreate("mem4", malloc_thread, &malloc_size4, 2048, 20); + StartupKTask(tid4); + // malloc_thread(&malloc_size); + int malloc_size5 = 16; + int tid5 = KTaskCreate("mem5", malloc_thread, &malloc_size5, 2048, 20); + StartupKTask(tid5); + // malloc_thread(&malloc_size); + MdelayKTask(5000); + return 0; +} +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC) | SHELL_CMD_PARAM_NUM(0), + testMemMain, testMemMain, list memory usage information); extern int FrameworkInit(); extern void ApplicationOtaTaskInit(void); diff --git a/APP_Framework/Applications/webnet/WebNet_XiUOS b/APP_Framework/Applications/webnet/WebNet_XiUOS index 956eafa24..83d987597 160000 --- a/APP_Framework/Applications/webnet/WebNet_XiUOS +++ b/APP_Framework/Applications/webnet/WebNet_XiUOS @@ -1 +1 @@ -Subproject commit 956eafa24bb65f5bb84d293ab35bf27084473edf +Subproject commit 83d987597cf027177fb3798c6da547a3d3e3d5e1 diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/config.mk b/Ubiquitous/XiZi_IIoT/board/edu-arm32/config.mk index cf681ac77..0276e1286 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/config.mk +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/config.mk @@ -1,9 +1,11 @@ export CROSS_COMPILE ?=/usr/bin/arm-none-eabi- -export CFLAGS := -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -ffunction-sections -fdata-sections -Dgcc -O0 -gdwarf-2 -g -fgnu89-inline -Wa,-mimplicit-it=thumb -Werror +export CFLAGS := -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -ffunction-sections -fdata-sections -Dgcc -O0 -fgnu89-inline -Wa,-mimplicit-it=thumb -Werror +# export CFLAGS := -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -ffunction-sections -fdata-sections -Dgcc -O0 -gdwarf-2 -g -fgnu89-inline -Wa,-mimplicit-it=thumb -Werror export AFLAGS := -c -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -ffunction-sections -fdata-sections -x assembler-with-cpp -Wa,-mimplicit-it=thumb -gdwarf-2 export LFLAGS := -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -ffunction-sections -fdata-sections -Wl,--gc-sections,-Map=XiZi-edu-arm32.map,-cref,-u,Reset_Handler -T $(BSP_ROOT)/link.lds -export CXXFLAGS := -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -ffunction-sections -fdata-sections -Dgcc -O0 -gdwarf-2 -g -Werror +export CXXFLAGS := -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -ffunction-sections -fdata-sections -Dgcc -O0 -Werror +# export CXXFLAGS := -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -ffunction-sections -fdata-sections -Dgcc -O0 -gdwarf-2 -g -Werror export APPLFLAGS := diff --git a/Ubiquitous/XiZi_IIoT/kernel/memory/byte_manage.c b/Ubiquitous/XiZi_IIoT/kernel/memory/byte_manage.c index 1536fedc2..c2aef788e 100644 --- a/Ubiquitous/XiZi_IIoT/kernel/memory/byte_manage.c +++ b/Ubiquitous/XiZi_IIoT/kernel/memory/byte_manage.c @@ -53,8 +53,10 @@ #define ALLOC_BLOCK_MASK 0xc0000000 #define DYNAMIC_REMAINING_MASK 0x3fffffff -#define SIZEOF_32B (32) -#define SIZEOF_64B (64) +enum SmallSizeAllocSize { + SIZEOF_32B = 32, + SIZEOF_64B = 64, +}; #define SMALL_SIZE_32B(ITEMSIZE) ((ITEMSIZE + SIZEOF_DYNAMICALLOCNODE_MEM) * SMALL_NUMBER_32B) /* Calculate the total size for SIZEOF_32B blocks*/ #define SMALL_SIZE_64B(ITEMSIZE) ((ITEMSIZE + SIZEOF_DYNAMICALLOCNODE_MEM) * SMALL_NUMBER_64B) /* Calculate the total size for SIZEOF_64B blocks*/ @@ -143,7 +145,7 @@ struct ByteMemory struct StaticMemoryDone { void (*init)(struct ByteMemory *byte_memory); - void* (*malloc)(struct ByteMemory *byte_memory, x_size_t size); + void* (*malloc)(struct ByteMemory *byte_memory, enum SmallSizeAllocSize size); void (*release)(void *pointer); }; @@ -182,7 +184,7 @@ static int JudgeValidAddressRange(struct DynamicBuddyMemory *dynamic_buddy, void NULL_PARAM_CHECK(pointer); /* the given address is between the physical start address and physical end address */ - if(((struct DynamicAllocNode *)pointer > dynamic_buddy->mm_dynamic_start[0]) && ((struct DynamicAllocNode *)pointer < dynamic_buddy->mm_dynamic_end[0])) { + if (((struct DynamicAllocNode *)pointer > dynamic_buddy->mm_dynamic_start[0]) && ((struct DynamicAllocNode *)pointer < dynamic_buddy->mm_dynamic_end[0])) { return RET_TRUE; } /* invalid address */ @@ -349,11 +351,18 @@ static void* BigMemMalloc(struct DynamicBuddyMemory *dynamic_buddy, x_size_t siz } /* best-fit method */ - for (node = dynamic_buddy->mm_freenode_list[ndx].next; - (ndx < MEM_LINKNRS ) && (node->size < allocsize); - node = node->next) { - ndx++; - }; + node = dynamic_buddy->mm_freenode_list[ndx].next; + while(ndx < MEM_LINKNRS && (NONE == node || node->size < allocsize)) { + if (NONE == node) { + ndx++; + if (ndx == MEM_LINKNRS) { // no space to allocate + return NONE; + } + node = dynamic_buddy->mm_freenode_list[ndx].next; + } else { + node = node->next; + } + } /* get the best-fit freeNode */ if (node && (node->size >= allocsize)) { struct DynamicFreeNode *remainder; @@ -371,7 +380,7 @@ static void* BigMemMalloc(struct DynamicBuddyMemory *dynamic_buddy, x_size_t siz /* create the remainder node */ remainder = PTR2FREENODE(((char *)node) + allocsize); - remainder->size = remaining; + remainder->size = remaining; remainder->prev_adj_size = allocsize; remainder->flag = 0; @@ -385,13 +394,12 @@ static void* BigMemMalloc(struct DynamicBuddyMemory *dynamic_buddy, x_size_t siz } /* handle the case of an exact size match */ - node->flag = extsram_mask; result = (void *)((char *)node + SIZEOF_DYNAMICALLOCNODE_MEM); } /* failure allocation */ - if(result == NONE) { + if (result == NONE) { #ifndef MEM_EXTERN_SRAM KPrintf("%s: allocation failed, size %d.\n", __func__, size); #endif @@ -429,9 +437,9 @@ static void BigMemFree( struct ByteMemory *byte_memory, void *pointer) byte_memory->dynamic_buddy_manager.active_memory -= node->size; #endif /* get the next sibling freeNode */ - next = PTR2FREENODE((char*)node+node->size); + next = PTR2FREENODE((char*)node + node->size); - if(((next->flag & DYNAMIC_BLOCK_MASK) == 0)) { + if (((next->flag & DYNAMIC_BLOCK_MASK) == 0)) { struct DynamicAllocNode *andbeyond; andbeyond = PTR2ALLOCNODE((char*)next + next->size); @@ -446,7 +454,7 @@ static void BigMemFree( struct ByteMemory *byte_memory, void *pointer) } /* get the prev sibling freeNode */ prev = (struct DynamicFreeNode*)((char*)node - node->prev_adj_size ); - if((prev->flag & DYNAMIC_BLOCK_MASK)==0) { + if ((prev->flag & DYNAMIC_BLOCK_MASK) == 0) { prev->prev->next=prev->next; if(prev->next){ @@ -459,7 +467,7 @@ static void BigMemFree( struct ByteMemory *byte_memory, void *pointer) node->flag = 0; /* insert freeNode into dynamic buddy memory */ - AddNewNodeIntoBuddy(&byte_memory->dynamic_buddy_manager,node); + AddNewNodeIntoBuddy(&byte_memory->dynamic_buddy_manager, node); } static struct DynamicBuddyMemoryDone DynamicDone = { @@ -545,12 +553,14 @@ static void SmallMemFree(void *pointer) NULL_PARAM_CHECK(pointer); /* get the allocNode */ - node = PTR2ALLOCNODE((char*)pointer-SIZEOF_DYNAMICALLOCNODE_MEM); + node = PTR2ALLOCNODE((char*)pointer - SIZEOF_DYNAMICALLOCNODE_MEM); static_segment = (struct segment*)(x_size_t)node->size; /* update the statistic information of static_segment */ node->size = (x_size_t)static_segment->freelist; static_segment->freelist = (uint8 *)node; + node->flag = 0; // it's unnecessary, actually + static_segment->block_free_count++; /* parameter detection */ @@ -565,7 +575,7 @@ static void SmallMemFree(void *pointer) * * @return pointer address on success; NULL on failure */ -static void *SmallMemMalloc(struct ByteMemory *byte_memory, x_size_t size) +static void *SmallMemMalloc(struct ByteMemory *byte_memory, enum SmallSizeAllocSize size) { uint8 i = 0; void *result = NONE; @@ -580,7 +590,7 @@ static void *SmallMemMalloc(struct ByteMemory *byte_memory, x_size_t size) static_segment = &byte_memory->static_manager[1]; /* current static segment has free static memory block */ - if(static_segment->block_free_count>0) { + if(static_segment->block_free_count > 0) { /* get the head static memory block */ result = static_segment->freelist; node = PTR2ALLOCNODE(static_segment->freelist); @@ -592,27 +602,14 @@ static void *SmallMemMalloc(struct ByteMemory *byte_memory, x_size_t size) node->size = (long)static_segment; } - if(result) { + if (NONE != result) { /* return static memory block */ return (char*)result + SIZEOF_DYNAMICALLOCNODE_MEM; } /* the static memory block is exhausted, now turn to dynamic buddy memory for allocation. */ - result = byte_memory->dynamic_buddy_manager.done->malloc(&byte_memory->dynamic_buddy_manager, size, DYNAMIC_BLOCK_NO_EXTMEM_MASK); -#ifdef MEM_EXTERN_SRAM - if(NONE == result) { - for(i = 0; i < EXTSRAM_MAX_NUM; i++) { - if(NONE != ExtByteManager[i].done) { - result = ExtByteManager[i].dynamic_buddy_manager.done->malloc(&ExtByteManager[i].dynamic_buddy_manager, size, DYNAMIC_BLOCK_EXTMEMn_MASK(i + 1)); - if (result){ - CHECK(ExtByteManager[i].dynamic_buddy_manager.done->JudgeLegal(&ExtByteManager[i].dynamic_buddy_manager, ret - SIZEOF_DYNAMICALLOCNODE_MEM)); - break; - } - } - } - } -#endif - return result; + // fall to dynamic allocation + return NONE; } static struct StaticMemoryDone StaticDone = { @@ -634,41 +631,46 @@ void *x_malloc(x_size_t size) void *ret = NONE; register x_base lock = 0; - /* parameter detection */ + /* hold lock before allocation */ + lock = CriticalAreaLock(); + /* alignment */ + size = ALIGN_MEN_UP(size, MEM_ALIGN_SIZE); + + /* parameter detection */ #ifdef MEM_EXTERN_SRAM /* parameter detection */ if(size == 0 ){ - return NONE; + CriticalAreaUnLock(lock); + return NONE; } if((size > ByteManager.dynamic_buddy_manager.dynamic_buddy_end - ByteManager.dynamic_buddy_manager.dynamic_buddy_start - ByteManager.dynamic_buddy_manager.active_memory)){ - lock = CriticalAreaLock(); /* alignment */ size = ALIGN_MEN_UP(size, MEM_ALIGN_SIZE); goto try_extmem; } - #else - /* parameter detection */ - if((size == 0) || (size > ByteManager.dynamic_buddy_manager.dynamic_buddy_end - ByteManager.dynamic_buddy_manager.dynamic_buddy_start - ByteManager.dynamic_buddy_manager.active_memory)) + /* parameter detection */ + if((size == 0) || (size > ByteManager.dynamic_buddy_manager.dynamic_buddy_end - ByteManager.dynamic_buddy_manager.dynamic_buddy_start - ByteManager.dynamic_buddy_manager.active_memory)) { + CriticalAreaUnLock(lock); return NONE; + } #endif - /* hold lock before allocation */ - lock = CriticalAreaLock(); - /* alignment */ - size = ALIGN_MEN_UP(size, MEM_ALIGN_SIZE); + /* determine allocation operation from static segments or dynamic buddy memory */ #ifdef KERNEL_SMALL_MEM_ALLOC if(size <= SIZEOF_32B) { - ret = ByteManager.static_manager[0].done->malloc(&ByteManager,SIZEOF_32B); - } else if(size <= SIZEOF_64B) { - ret = ByteManager.static_manager[1].done->malloc(&ByteManager,SIZEOF_64B); - } else + ret = ByteManager.static_manager[0].done->malloc(&ByteManager, SIZEOF_32B); + } else if (size <= SIZEOF_64B) { + ret = ByteManager.static_manager[1].done->malloc(&ByteManager, SIZEOF_64B); + } #endif - { + + if (ret == NONE) { ret = ByteManager.dynamic_buddy_manager.done->malloc(&ByteManager.dynamic_buddy_manager, size, DYNAMIC_BLOCK_NO_EXTMEM_MASK); - if(ret != NONE) + if (ret != NONE) { CHECK(ByteManager.dynamic_buddy_manager.done->JudgeLegal(&ByteManager.dynamic_buddy_manager, ret - SIZEOF_DYNAMICALLOCNODE_MEM)); + } #ifdef MEM_EXTERN_SRAM try_extmem: @@ -686,6 +688,7 @@ try_extmem: } #endif } + /* release lock */ CriticalAreaUnLock(lock); return ret; @@ -781,14 +784,20 @@ void x_free(void *pointer) struct DynamicAllocNode *node = NONE; /* parameter detection */ - if (pointer == NONE) - return ; - - CHECK(ByteManager.dynamic_buddy_manager.done->JudgeLegal(&ByteManager.dynamic_buddy_manager,pointer)); + if (pointer == NONE) { + return; + } /* hold lock before release */ lock = CriticalAreaLock(); - node = PTR2ALLOCNODE((char*)pointer-SIZEOF_DYNAMICALLOCNODE_MEM); + + if (!ByteManager.dynamic_buddy_manager.done->JudgeLegal(&ByteManager.dynamic_buddy_manager, pointer)) { + CriticalAreaUnLock(lock); + SYS_ERR("[%s] Freeing a no allocated address.\n", __func__); + return; + } + + node = PTR2ALLOCNODE((char*)pointer - SIZEOF_DYNAMICALLOCNODE_MEM); CHECK(ByteManager.done->JudgeAllocated(node)); /* judge release the memory block ro static_segment or dynamic buddy memory */ @@ -799,19 +808,20 @@ void x_free(void *pointer) #endif { #ifdef MEM_EXTERN_SRAM - /* judge the pointer is not malloced from extern memory*/ - if(0 == (node->flag & 0xFF0000)) { - ByteManager.dynamic_buddy_manager.done->release(&ByteManager,pointer); - } + /* judge the pointer is not malloced from extern memory*/ + if(0 == (node->flag & 0xFF0000)) { + ByteManager.dynamic_buddy_manager.done->release(&ByteManager,pointer); + } - /* judge the pointer is malloced from extern memory*/ - if(0 != (node->flag & 0xFF0000)) { - ExtByteManager[((node->flag & 0xFF0000) >> 16) - 1].dynamic_buddy_manager.done->release(&ExtByteManager[((node->flag & 0xFF0000) >> 16) - 1],pointer); - } + /* judge the pointer is malloced from extern memory*/ + if(0 != (node->flag & 0xFF0000)) { + ExtByteManager[((node->flag & 0xFF0000) >> 16) - 1].dynamic_buddy_manager.done->release(&ExtByteManager[((node->flag & 0xFF0000) >> 16) - 1],pointer); + } #else - ByteManager.dynamic_buddy_manager.done->release(&ByteManager,pointer); + ByteManager.dynamic_buddy_manager.done->release(&ByteManager, pointer); #endif } + /* release the lock */ CriticalAreaUnLock(lock); } @@ -888,7 +898,8 @@ void InitBoardMemory(void *start_phy_address, void *end_phy_address) /* parameter detection */ if (ByteManager.dynamic_buddy_manager.dynamic_buddy_start >= ByteManager.dynamic_buddy_manager.dynamic_buddy_end) { //KPrintf("InitBoardMemory, wrong address[0x%x - 0x%x]\n", (x_ubase)start_phy_address, (x_ubase)end_phy_address); - return; + SYS_KDEBUG_LOG(KDBG_MEM, ("InitBoardMemory, wrong address[0x%x - 0x%x]\n", (x_ubase)start_phy_address, (x_ubase)end_phy_address)); + return; } mheap->mm_total_size = 0; From ee10eccb2669b552967d5516dc8143198d6ef731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Wed, 2 Aug 2023 17:41:02 +0800 Subject: [PATCH 13/33] 23/08/02 1.Fix ByteManager Bug; 2.Move net delay to webnet(wn_session.h) so it wont affect other net functions. --- APP_Framework/Applications/main.c | 102 ------------------ .../XiZi_IIoT/kernel/memory/byte_manage.c | 2 +- 2 files changed, 1 insertion(+), 103 deletions(-) diff --git a/APP_Framework/Applications/main.c b/APP_Framework/Applications/main.c index 54991e27b..b10557a97 100644 --- a/APP_Framework/Applications/main.c +++ b/APP_Framework/Applications/main.c @@ -15,108 +15,6 @@ // #include #include -pthread_mutex_t* get_memtest_mtx() { - static pthread_mutex_t mem_test_mtx; - return &mem_test_mtx; -} - - -extern void ShowMemory(); -void malloc_thread(void* size) -{ - int tid = GetKTaskDescriptor()->id.id; - int* tmp = (int*)size; - int malloc_size = *tmp; - - PrivMutexObtain(get_memtest_mtx()); - printf("TID: %d Working. size: %d\n", tid, malloc_size); - PrivMutexAbandon(get_memtest_mtx()); - - void* ptr_allocated = NULL; - while ((ptr_allocated = malloc(malloc_size)) != NULL) { - PrivMutexObtain(get_memtest_mtx()); - printf("\n[TID: %d]==================================================\n", tid); - ShowMemory(); - PrivMutexAbandon(get_memtest_mtx()); - } - - PrivMutexObtain(get_memtest_mtx()); - printf("TID: %d Exiting.\n", tid); - PrivMutexAbandon(get_memtest_mtx()); -} - -#include -int testMemMain() -{ - PrivMutexCreate(get_memtest_mtx(), NULL); - - int malloc_size = 10 * 1024; - int tid = KTaskCreate("mem1", malloc_thread, (void*)&malloc_size, 2048, 20); - StartupKTask(tid); - // malloc_thread(&malloc_size); - - - // void* ptr_allocated[5] = { NULL }; - // ptr_allocated[0] = malloc(malloc_size); - // printf("0x%x ==================================================\n", ptr_allocated[0]); - // ShowMemory(); - // ptr_allocated[1] = malloc(malloc_size); - // printf("0x%x ==================================================\n", ptr_allocated[1]); - // ShowMemory(); - - // malloc_size = 996; - // ptr_allocated[2] = malloc(malloc_size); - // printf("0x%x ==================================================\n", ptr_allocated[2]); - // ShowMemory(); - - // free(ptr_allocated[0]); - // printf("==================================================\n"); - // ShowMemory(); - // ptr_allocated[0] = malloc(malloc_size); - // printf("0x%x ==================================================\n", ptr_allocated[0]); - // ShowMemory(); - - // ptr_allocated[3] = malloc(malloc_size); - // free(ptr_allocated[2]); - // printf("0x%x ==================================================\n", ptr_allocated[3]); - // ShowMemory(); - - // malloc_size = 333; - // ptr_allocated[2] = malloc(malloc_size); - // ptr_allocated[4] = malloc(malloc_size); - // printf("0x%x ==================================================\n", ptr_allocated[2]); - // printf("0x%x ==================================================\n", ptr_allocated[4]); - // ShowMemory(); - - // free(ptr_allocated[0]); - // free(ptr_allocated[1]); - // free(ptr_allocated[2]); - // free(ptr_allocated[3]); - // free(ptr_allocated[4]); - - - int malloc_size2 = 1024; - int tid2 = KTaskCreate("mem2", malloc_thread, &malloc_size2, 2048, 20); - StartupKTask(tid2); - // malloc_thread(&malloc_size); - int malloc_size3 = 127; - int tid3 = KTaskCreate("mem3", malloc_thread, &malloc_size3, 2048, 20); - StartupKTask(tid3); - // malloc_thread(&malloc_size); - int malloc_size4 = 45; - int tid4 = KTaskCreate("mem4", malloc_thread, &malloc_size4, 2048, 20); - StartupKTask(tid4); - // malloc_thread(&malloc_size); - int malloc_size5 = 16; - int tid5 = KTaskCreate("mem5", malloc_thread, &malloc_size5, 2048, 20); - StartupKTask(tid5); - // malloc_thread(&malloc_size); - MdelayKTask(5000); - return 0; -} -SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC) | SHELL_CMD_PARAM_NUM(0), - testMemMain, testMemMain, list memory usage information); - extern int FrameworkInit(); extern void ApplicationOtaTaskInit(void); int main(void) diff --git a/Ubiquitous/XiZi_IIoT/kernel/memory/byte_manage.c b/Ubiquitous/XiZi_IIoT/kernel/memory/byte_manage.c index c2aef788e..3bf3144cb 100644 --- a/Ubiquitous/XiZi_IIoT/kernel/memory/byte_manage.c +++ b/Ubiquitous/XiZi_IIoT/kernel/memory/byte_manage.c @@ -793,7 +793,7 @@ void x_free(void *pointer) if (!ByteManager.dynamic_buddy_manager.done->JudgeLegal(&ByteManager.dynamic_buddy_manager, pointer)) { CriticalAreaUnLock(lock); - SYS_ERR("[%s] Freeing a no allocated address.\n", __func__); + SYS_ERR("[%s] Freeing a unallocated address.\n", __func__); return; } From 06b9d67f2dcc1c70fc274ff3a0babc618650a462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Wed, 2 Aug 2023 17:44:55 +0800 Subject: [PATCH 14/33] Merge branch 'prepare_for_master' of https://gitlink.org.cn/xuos/xiuos into prepare_for_master From 219575898acf9655d852aa3f88d6879a76c7264b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Thu, 3 Aug 2023 14:15:26 +0800 Subject: [PATCH 15/33] 23/08/03 Add Netdev to xidatong-arm32 --- APP_Framework/Applications/benchmark/Makefile | 16 -- .../Applications/webnet/WebNet_XiUOS | 2 +- .../transform_layer/xizi/transform.c | 2 +- .../transform_layer/xizi/transform.h | 2 +- .../xizi/user_api/posix_support/mqueue.c | 2 +- .../applications/benchmark/Makefile | 16 -- .../third_party_driver/ethernet/ethernetif.c | 77 +++++---- .../third_party_driver/ethernet/Makefile | 2 +- .../ethernet/enet_ethernetif.c | 7 + .../third_party_driver/ethernet/eth_netdev.c | 163 ++++++++++++++++++ .../include/connect_ethernet.h | 2 + Ubiquitous/XiZi_IIoT/fs/shared/src/iot-vfs.c | 4 +- Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h | 3 + Ubiquitous/XiZi_IIoT/path_kernel.mk | 1 + .../resources/ethernet/LwIP/arch/sys_arch.c | 2 + .../resources/include/netdev/netdev.h | 2 - .../XiZi_IIoT/tool/shell/letter-shell/cmd.c | 2 +- 17 files changed, 225 insertions(+), 80 deletions(-) delete mode 100644 APP_Framework/Applications/benchmark/Makefile delete mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/benchmark/Makefile create mode 100644 Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/eth_netdev.c diff --git a/APP_Framework/Applications/benchmark/Makefile b/APP_Framework/Applications/benchmark/Makefile deleted file mode 100644 index d9d5e47dc..000000000 --- a/APP_Framework/Applications/benchmark/Makefile +++ /dev/null @@ -1,16 +0,0 @@ - -# include $(APPDIR)/Application.mk -# ifeq ($(CONFIG_ADD_NUTTX_FETURES),y) - # include $(APPDIR)/Make.defs - # CSRCS += $(wildcard src/*/*.c) $(wildcard support/*.c) - # include $(APPDIR)/Application.mk -# endif - -# ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) - SRC_DIR := - ifeq ($(CONFIG_APP_BENCHMARK), y) - SRC_DIR += src - SRC_DIR += support - endif - include $(KERNEL_ROOT)/compiler.mk -# endif diff --git a/APP_Framework/Applications/webnet/WebNet_XiUOS b/APP_Framework/Applications/webnet/WebNet_XiUOS index 83d987597..60a8a500b 160000 --- a/APP_Framework/Applications/webnet/WebNet_XiUOS +++ b/APP_Framework/Applications/webnet/WebNet_XiUOS @@ -1 +1 @@ -Subproject commit 83d987597cf027177fb3798c6da547a3d3e3d5e1 +Subproject commit 60a8a500b93b47876c6eaff600e4cf2b8bf7b283 diff --git a/APP_Framework/Framework/transform_layer/xizi/transform.c b/APP_Framework/Framework/transform_layer/xizi/transform.c index 002c8f1be..ba0b8f614 100644 --- a/APP_Framework/Framework/transform_layer/xizi/transform.c +++ b/APP_Framework/Framework/transform_layer/xizi/transform.c @@ -164,7 +164,7 @@ int PrivTimerModify(timer_t timerid, int flags, const struct itimerspec *restric /*********************fs**************************/ #ifdef FS_VFS /************************Driver Posix Transform***********************/ -int PrivOpen(const char *path, int flags) +int PrivOpen(const char *path, int flags, ...) { return open(path, flags); } diff --git a/APP_Framework/Framework/transform_layer/xizi/transform.h b/APP_Framework/Framework/transform_layer/xizi/transform.h index fe817f18a..ef1894c32 100644 --- a/APP_Framework/Framework/transform_layer/xizi/transform.h +++ b/APP_Framework/Framework/transform_layer/xizi/transform.h @@ -427,7 +427,7 @@ uint32_t PrivGetTickTime(); /*********************driver*************************/ -int PrivOpen(const char *path, int flags); +int PrivOpen(const char *path, int flags, ...); int PrivRead(int fd, void *buf, size_t len); int PrivWrite(int fd, const void *buf, size_t len); int PrivClose(int fd); diff --git a/APP_Framework/Framework/transform_layer/xizi/user_api/posix_support/mqueue.c b/APP_Framework/Framework/transform_layer/xizi/user_api/posix_support/mqueue.c index a92dc95f6..9f13a1445 100644 --- a/APP_Framework/Framework/transform_layer/xizi/user_api/posix_support/mqueue.c +++ b/APP_Framework/Framework/transform_layer/xizi/user_api/posix_support/mqueue.c @@ -45,7 +45,7 @@ ssize_t mq_receive(mqd_t mqdes, char* msg_ptr, size_t msg_len, unsigned* msg_pri ssize_t ret; *msg_prio = 0; - ret = UserMsgQueueRecv(mqdes, (void*)msg_ptr, msg_len, 100000); + ret = UserMsgQueueRecv(mqdes, (void*)msg_ptr, msg_len, WAITING_FOREVER); return ret; } diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/benchmark/Makefile b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/benchmark/Makefile deleted file mode 100644 index 0c8c2bfcf..000000000 --- a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/benchmark/Makefile +++ /dev/null @@ -1,16 +0,0 @@ - -# include $(APPDIR)/Application.mk -ifeq ($(CONFIG_ADD_NUTTX_FETURES),y) - include $(APPDIR)/Make.defs - CSRCS += $(wildcard src/*/*.c) $(wildcard support/*.c) - include $(APPDIR)/Application.mk -endif - -ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) - SRC_DIR := - ifeq ($(CONFIG_APP_BENCHMARK), y) - SRC_DIR += src - SRC_DIR += support - endif - include $(KERNEL_ROOT)/compiler.mk -endif diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/ethernetif.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/ethernetif.c index 7a50d2315..738359b59 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/ethernetif.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/ethernetif.c @@ -190,44 +190,6 @@ void Time_Update_LwIP(void) // no need to do } -/** - * @brief Should be called at the beginning of the program to set up the network interface. - * @param netif The network interface structure for this ethernetif. - * @retval err_t: - * - LL_OK: The IF is initialized - * - LL_ERR: The IF is uninitialized - */ -err_t ethernetif_init(struct netif* netif) -{ -#if LWIP_NETIF_HOSTNAME - /* Initialize interface hostname */ - netif->hostname = "lwip"; -#endif /* LWIP_NETIF_HOSTNAME */ - netif->name[0] = IFNAME0; - netif->name[1] = IFNAME1; - -#ifndef ETHERNET_LOOPBACK_TEST - /* We directly use etharp_output() here to save a function call. - * You can instead declare your own function an call etharp_output() - * from it if you have to do some checks before sending (e.g. if link - * is available...) */ - netif->output = etharp_output; - netif->linkoutput = low_level_output; -#endif - - /* initialize the hardware */ - if (LL_OK != low_level_init(netif)) { - return LL_ERR; - } - - if (EOK != lwip_netdev_add(netif)) { - SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] LWIP add netdev failed.\n", __func__)); - } else { - printf("[%s] Add Netdev successful\n", __func__); - } - return LL_OK; -} - /** * @brief This function should be called when a packet is ready to be read from the interface. * @param netif The network interface structure for this ethernetif. @@ -267,6 +229,45 @@ void ethernetif_input(void* netif_arg) } } +/** + * @brief Should be called at the beginning of the program to set up the network interface. + * @param netif The network interface structure for this ethernetif. + * @retval err_t: + * - LL_OK: The IF is initialized + * - LL_ERR: The IF is uninitialized + */ +err_t ethernetif_init(struct netif* netif) +{ +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + +#ifndef ETHERNET_LOOPBACK_TEST + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; + netif->linkoutput = low_level_output; + // netif->linkoutput = ethernetif_linkoutput; +#endif + + /* initialize the hardware */ + if (LL_OK != low_level_init(netif)) { + return LL_ERR; + } + + if (EOK != lwip_netdev_add(netif)) { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] LWIP add netdev failed.\n", __func__)); + } else { + printf("[%s] Add Netdev successful\n", __func__); + } + return LL_OK; +} + /** * @brief Check the netif link status. * @param netif the network interface diff --git a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/Makefile b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/Makefile index 65f5adc78..6cd8a10fa 100755 --- a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/Makefile +++ b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/Makefile @@ -1,3 +1,3 @@ -SRC_FILES := enet_ethernetif.c enet_ethernetif_kinetis.c fsl_enet.c +SRC_FILES := enet_ethernetif.c enet_ethernetif_kinetis.c fsl_enet.c eth_netdev.c SRC_DIR := lan8720 include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/enet_ethernetif.c b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/enet_ethernetif.c index 9b8ca475a..114f1ef61 100755 --- a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/enet_ethernetif.c +++ b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/enet_ethernetif.c @@ -68,6 +68,7 @@ #include "fsl_gpio.h" #include "fsl_iomuxc.h" +#include "netdev.h" #include "sys_arch.h" /******************************************************************************* @@ -319,6 +320,12 @@ err_t ethernetif_init(struct netif *netif, struct ethernetif *ethernetif, } #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] Adding netdev.\n", __func__)); + if (EOK != lwip_netdev_add(netif)) { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] LWIP add netdev failed.\n", __func__)); + } else { + printf("[%s] Add Netdev successful\n", __func__); + } return ERR_OK; } diff --git a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/eth_netdev.c new file mode 100644 index 000000000..2dfe75795 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/eth_netdev.c @@ -0,0 +1,163 @@ + +#include +#include +#include +#include +#include +#include + +static const uint32_t NETIF_NAME_LEN = 2; + +static int lwip_netdev_set_up(struct netdev* netdev) +{ + netif_set_up((struct netif*)netdev->user_data); + return ERR_OK; +} + +static int lwip_netdev_set_down(struct netdev* netif) +{ + netif_set_down((struct netif*)netif->user_data); + return ERR_OK; +} + +#ifndef ip_2_ip4 +#define ip_2_ip4(ipaddr) (ipaddr) +#endif +static int lwip_netdev_set_addr_info(struct netdev* netdev, ip_addr_t* ip_addr, ip_addr_t* netmask, ip_addr_t* gw) +{ + if (ip_addr && netmask && gw) { + netif_set_addr((struct netif*)netdev->user_data, ip_2_ip4(ip_addr), ip_2_ip4(netmask), ip_2_ip4(gw)); + } else { + if (ip_addr) { + netif_set_ipaddr((struct netif*)netdev->user_data, ip_2_ip4(ip_addr)); + } + if (netmask) { + netif_set_netmask((struct netif*)netdev->user_data, ip_2_ip4(netmask)); + } + if (gw) { + netif_set_gw((struct netif*)netdev->user_data, ip_2_ip4(gw)); + } + } +} + +#ifdef LWIP_DNS +static int lwip_netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, ip_addr_t* dns_server) +{ +#if LWIP_VERSION_MAJOR == 1U /* v1.x */ + extern void dns_setserver(u8_t numdns, ip_addr_t * dnsserver); +#else /* >=2.x */ + extern void dns_setserver(uint8_t dns_num, const ip_addr_t* dns_server); +#endif /* LWIP_VERSION_MAJOR == 1U */ + + dns_setserver(dns_num, dns_server); + return ERR_OK; +} +#endif + +#ifdef LWIP_DHCP +static int lwip_netdev_set_dhcp(struct netdev* netdev, bool is_enabled) +{ + netdev_low_level_set_dhcp_status(netdev, is_enabled); + + if (true == is_enabled) { + dhcp_start((struct netif*)netdev->user_data); + } else { + dhcp_stop((struct netif*)netdev->user_data); + } + + return ERR_OK; +} +#endif + +static int lwip_netdev_set_default(struct netdev* netdev) +{ + netif_set_default((struct netif*)netdev->user_data); + return ERR_OK; +} + +static const struct netdev_ops lwip_netdev_ops = { + .set_up = lwip_netdev_set_up, + .set_down = lwip_netdev_set_down, + .set_addr_info = lwip_netdev_set_addr_info, +#ifdef LWIP_DNS + .set_dns_server = lwip_netdev_set_dns_server, +#endif +#ifdef LWIP_DHCP + .set_dhcp = lwip_netdev_set_dhcp, +#endif + .set_default = lwip_netdev_set_default, +}; + +static inline int netdev_set_flags(struct netif* lwip_netif) +{ + CHECK(lwip_netif); + struct netdev* netdev = netdev_get_by_name(lwip_netif->name); + if (netdev == NULL) { + return -ERR_IF; + } + + netdev->mtu = lwip_netif->mtu; + // set flags + if (lwip_netif->flags | NETIF_FLAG_BROADCAST) { + netdev->flags |= NETDEV_FLAG_BROADCAST; + } + if (lwip_netif->flags | NETIF_FLAG_ETHARP) { + netdev->flags |= NETDEV_FLAG_ETHARP; + } + if (lwip_netif->flags | NETIF_FLAG_IGMP) { + netdev->flags |= NETDEV_FLAG_IGMP; + } +#if LWIP_VERSION_MAJOR >= 2U /* >= v2.x */ + if (lwip_netif->flags & NETIF_FLAG_MLD6) { + netdev->flags |= NETDEV_FLAG_MLD6; + } +#endif /* LWIP_VERSION_MAJOR >= 2U */ + +#if LWIP_DHCP + netdev_low_level_set_dhcp_status(netdev, true); +#else + netdev_low_level_set_dhcp_status(netdev, false); +#endif + + return ERR_OK; +} + +int lwip_netdev_add(struct netif* lwip_netif) +{ + CHECK(lwip_netif); + + struct netdev* netdev = calloc(1, sizeof(struct netdev)); + if (netdev == NULL) { + return -ERR_IF; + } + + // init netdev + char netif_name[NETIF_NAME_LEN + 1]; + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] Lwip netif name: %s\n", __func__, lwip_netif->name)); + strncpy(netif_name, lwip_netif->name, NETIF_NAME_LEN); + // register netdev + int result = netdev_register(netdev, netif_name, (void*)lwip_netif); + // set values of netdev + netdev_set_flags(lwip_netif); + netdev->ops = &lwip_netdev_ops; + netdev->hwaddr_len = lwip_netif->hwaddr_len; + memcpy(netdev->hwaddr, lwip_netif->hwaddr, lwip_netif->hwaddr_len); + netdev->ip_addr = lwip_netif->ip_addr; + netdev->gw = lwip_netif->gw; + netdev->netmask = lwip_netif->netmask; + + return result; +} + +void lwip_netdev_del(struct netif* lwip_netif) +{ + char name[NETIF_NAME_LEN + 1]; + struct netdev* netdev; + + CHECK(lwip_netif); + + strncpy(name, lwip_netif->name, NETIF_NAME_LEN); + netdev = netdev_get_by_name(name); + netdev_unregister(netdev); + free(netdev); +} \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/include/connect_ethernet.h b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/include/connect_ethernet.h index 12f65358b..0933eecd5 100755 --- a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/include/connect_ethernet.h +++ b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/include/connect_ethernet.h @@ -33,6 +33,8 @@ #define sourceClock CLOCK_GetFreq(kCLOCK_CoreSysClk) #endif +int lwip_netdev_add(struct netif* lwip_netif); +void lwip_netdev_del(struct netif* lwip_netif); #ifdef __cplusplus } diff --git a/Ubiquitous/XiZi_IIoT/fs/shared/src/iot-vfs.c b/Ubiquitous/XiZi_IIoT/fs/shared/src/iot-vfs.c index e340d08fe..d81b9113a 100644 --- a/Ubiquitous/XiZi_IIoT/fs/shared/src/iot-vfs.c +++ b/Ubiquitous/XiZi_IIoT/fs/shared/src/iot-vfs.c @@ -772,7 +772,8 @@ int stat(const char *path, struct stat *buf) ret = mp->fs->stat(mp, relpath, &vfs_statbuf); if (ret < 0) { - SYS_ERR("%s: stat file failed\n", __func__); + // stat() is absolutely fine to check if a file exists + // SYS_ERR("%s: stat file failed\n", __func__); goto err; } @@ -790,7 +791,6 @@ err: free(abspath); if (ret < 0) { - KUpdateExstatus(ret); return -1; } return 0; diff --git a/Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h b/Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h index 7612a959e..7dc4bc7fa 100644 --- a/Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h +++ b/Ubiquitous/XiZi_IIoT/kernel/include/xs_kdbg.h @@ -43,6 +43,9 @@ extern "C" { #define KDBG_HOOK 0 #define MSGQUEUE_DEBUG 0 +#define FILESYS_DEBUG 0 +#define NETDEV_DEBUG 0 +#define WEBNET_DEBUG 0 #define SYS_KDEBUG_LOG(section, information) \ do { \ diff --git a/Ubiquitous/XiZi_IIoT/path_kernel.mk b/Ubiquitous/XiZi_IIoT/path_kernel.mk index 5ee36f062..7b44f073e 100755 --- a/Ubiquitous/XiZi_IIoT/path_kernel.mk +++ b/Ubiquitous/XiZi_IIoT/path_kernel.mk @@ -53,6 +53,7 @@ KERNELPATHS += \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/prot \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/arch +KERNELPATHS += -I$(KERNEL_ROOT)/resources/include/netdev endif endif diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c index 29175d23f..7ec40271d 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c @@ -347,6 +347,8 @@ void lwip_config_input(struct netif* net) void lwip_config_tcp(uint8_t enet_port, char* ip, char* mask, char* gw) { + sys_sem_new(get_eth_recv_sem(), 0); + ip4_addr_t net_ipaddr, net_netmask, net_gw; char* eth_cfg; diff --git a/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h b/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h index 56cb1c02f..8ebe09c76 100644 --- a/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h +++ b/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h @@ -11,8 +11,6 @@ extern "C" { #endif -#define NETDEV_DEBUG true - #define NETDEV_DEBUG_PRINT_IP_INFO(ip, netmask, gw) \ ("[%s %d]ip: %u.%u.%u.%u, netmask: %u.%u.%u.%u, gw: %u.%u.%u.%u\n", \ __func__, __LINE__, \ diff --git a/Ubiquitous/XiZi_IIoT/tool/shell/letter-shell/cmd.c b/Ubiquitous/XiZi_IIoT/tool/shell/letter-shell/cmd.c index ff5e8f98d..82fd4eb6f 100644 --- a/Ubiquitous/XiZi_IIoT/tool/shell/letter-shell/cmd.c +++ b/Ubiquitous/XiZi_IIoT/tool/shell/letter-shell/cmd.c @@ -98,7 +98,7 @@ long ShowTask(void) KPrintf("*************************************************************************************************\n"); #ifndef SCHED_POLICY_FIFO #ifdef ARCH_SMP - KPrintf(" STAT ID %-*.s PRI CORE STACK_DEPTH USED LEFT_TICKS ERROR_STAT\n", _NUM_MAX, item_title); + KPrintf(" STAT ID %-*.s PRI CORE STACK_DEPTH USED LEFT_TICKS ERROR_STAT\n", NAME_NUM_MAX, item_title); #else KPrintf(" STAT ID %-*.s PRI STACK_DEPTH USED LEFT_TICKS ERROR_STAT\n", NAME_NUM_MAX, item_title); #endif From e9b4a0de6cb606b37f980640d50abf94f1f528d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Thu, 3 Aug 2023 20:04:16 +0800 Subject: [PATCH 16/33] 23/08/03 Fix former commit errors --- .../edu-arm32/third_party_driver/include/connect_ethernet.h | 3 --- .../third_party_driver/include/enet_ethernetif.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_ethernet.h b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_ethernet.h index 4f0529a0b..888efff7b 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_ethernet.h +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_ethernet.h @@ -61,9 +61,6 @@ int32_t low_level_init(struct netif* netif); err_t low_level_output(struct netif* netif, struct pbuf* p); struct pbuf* low_level_input(struct netif* netif); -int lwip_netdev_add(struct netif* lwip_netif); -void lwip_netdev_del(struct netif* lwip_netif); - int HwEthInit(void); #ifdef __cplusplus diff --git a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/include/enet_ethernetif.h b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/include/enet_ethernetif.h index 7978b36b6..e6f59df3e 100755 --- a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/include/enet_ethernetif.h +++ b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/include/enet_ethernetif.h @@ -185,6 +185,9 @@ int ETH_BSP_Config(void); void *ethernetif_config_enet_set(uint8_t enet_port); int32 lwip_obtain_semaphore(struct netif *netif); +int lwip_netdev_add(struct netif* lwip_netif); +void lwip_netdev_del(struct netif* lwip_netif); + #define NETIF_ENET0_INIT_FUNC ethernetif0_init #if defined(__cplusplus) From 3c1129d88ab7f8412a43a34ced51a9155e6eaef1 Mon Sep 17 00:00:00 2001 From: Liu_Weichao Date: Fri, 4 Aug 2023 10:14:23 +0800 Subject: [PATCH 17/33] fix edu-arm32 board USB IRQ configure error --- .../XiZi_IIoT/arch/arm/cortex-m4/hc32f4a0/interrupt_vector.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ubiquitous/XiZi_IIoT/arch/arm/cortex-m4/hc32f4a0/interrupt_vector.S b/Ubiquitous/XiZi_IIoT/arch/arm/cortex-m4/hc32f4a0/interrupt_vector.S index 91e70665f..e38143b1d 100644 --- a/Ubiquitous/XiZi_IIoT/arch/arm/cortex-m4/hc32f4a0/interrupt_vector.S +++ b/Ubiquitous/XiZi_IIoT/arch/arm/cortex-m4/hc32f4a0/interrupt_vector.S @@ -91,7 +91,7 @@ InterruptVectors: .long IsrEntry .long IsrEntry .long IsrEntry - .long IsrEntry + .long IRQ030_Handler .long IsrEntry .long IsrEntry .long IsrEntry From aa6fb2a8b02b0d95ac5e8d4772eecb639a64d5e4 Mon Sep 17 00:00:00 2001 From: Liu_Weichao Date: Fri, 4 Aug 2023 10:39:11 +0800 Subject: [PATCH 18/33] fix test_i2c WriteReg coredump error --- Ubiquitous/XiZi_IIoT/kernel/kernel_test/test_i2c.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Ubiquitous/XiZi_IIoT/kernel/kernel_test/test_i2c.c b/Ubiquitous/XiZi_IIoT/kernel/kernel_test/test_i2c.c index cd41074bf..37a3c39ff 100644 --- a/Ubiquitous/XiZi_IIoT/kernel/kernel_test/test_i2c.c +++ b/Ubiquitous/XiZi_IIoT/kernel/kernel_test/test_i2c.c @@ -35,6 +35,7 @@ #define ADDR 0x44 /* slave address */ static struct Bus *i2c_bus = NONE; /* I2C bus handle */ +static char addr = 0x44; typedef struct Hs300xData { @@ -50,6 +51,9 @@ static x_err_t WriteReg(struct HardwareDev *dev) { struct BusBlockWriteParam write_param; + write_param.buffer = &addr; + write_param.size = 1; + /* use I2C device API transfer data */ if(1 == BusDevWriteData(dev, &write_param)) { return EOK; From ec7d9f1fcd52d2fdf2d76dc579081d290fa3140f Mon Sep 17 00:00:00 2001 From: Liu_Weichao Date: Mon, 7 Aug 2023 14:48:37 +0800 Subject: [PATCH 19/33] =?UTF-8?q?1=E3=80=81fix=20lwip=5Ftcp=5Fdemo=20and?= =?UTF-8?q?=20lwip=5Fudp=5Fdemo=20error=EF=BC=9B2=E3=80=81add=20license=20?= =?UTF-8?q?for=20ethernet/netdev=EF=BC=9B3=E3=80=81add=20netdev=20for=20xi?= =?UTF-8?q?wangtong=E3=80=81ok1052=E3=80=81imxrt1176=20board=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../third_party_driver/ethernet/eth_netdev.c | 38 ++++ .../include/connect_ethernet.h | 3 + .../third_party_driver/ethernet/Makefile | 2 +- .../ethernet/enet_ethernetif.c | 6 + .../third_party_driver/ethernet/eth_netdev.c | 201 ++++++++++++++++++ .../include/connect_ethernet.h | 2 + .../include/enet_ethernetif.h | 3 + .../third_party_driver/ethernet/Makefile | 2 +- .../ethernet/enet_ethernetif.c | 7 + .../third_party_driver/ethernet/eth_netdev.c | 201 ++++++++++++++++++ .../include/connect_ethernet.h | 2 + .../include/enet_ethernetif.h | 5 +- .../third_party_driver/ethernet/eth_netdev.c | 38 ++++ .../third_party_driver/ethernet/Makefile | 2 +- .../ethernet/enet_ethernetif.c | 7 + .../third_party_driver/ethernet/eth_netdev.c | 201 ++++++++++++++++++ .../include/connect_ethernet.h | 3 + .../include/enet_ethernetif.h | 3 + Ubiquitous/XiZi_IIoT/path_kernel.mk | 6 + .../resources/ethernet/cmd_lwip/Makefile | 2 +- .../resources/ethernet/cmd_lwip/iperf.c | 141 +++++------- .../ethernet/cmd_lwip/lwip_tcp_demo.c | 147 ++++++++++--- .../ethernet/cmd_lwip/lwip_udp_demo.c | 167 ++++++++------- .../ethernet/netdev/netdev_ipaddr_util.c | 0 .../ethernet/netdev/netdev_lowlevel.c | 30 +++ .../ethernet/netdev/netdev_manipulate.c | 30 +++ .../ethernet/netdev/netdev_register.c | 29 +++ .../resources/include/netdev/netdev.h | 1 + 28 files changed, 1081 insertions(+), 198 deletions(-) create mode 100644 Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/eth_netdev.c create mode 100644 Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/eth_netdev.c create mode 100644 Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/eth_netdev.c delete mode 100644 Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_ipaddr_util.c diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_netdev.c index 145ebc620..1c5c9d3c5 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_netdev.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_netdev.c @@ -1,3 +1,41 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + */ + +/** +* @file eth_netdev.c +* @brief register net dev function for lwip +* @version 3.0 +* @author AIIT XUOS Lab +* @date 2023-08-07 +*/ #include #include diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_ethernet.h b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_ethernet.h index 888efff7b..4f0529a0b 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_ethernet.h +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_ethernet.h @@ -61,6 +61,9 @@ int32_t low_level_init(struct netif* netif); err_t low_level_output(struct netif* netif, struct pbuf* p); struct pbuf* low_level_input(struct netif* netif); +int lwip_netdev_add(struct netif* lwip_netif); +void lwip_netdev_del(struct netif* lwip_netif); + int HwEthInit(void); #ifdef __cplusplus diff --git a/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/Makefile b/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/Makefile index 1df7f37e7..968713ab4 100755 --- a/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/Makefile +++ b/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/Makefile @@ -1,3 +1,3 @@ -SRC_FILES := enet_ethernetif.c enet_ethernetif_kinetis.c fsl_enet.c fsl_enet_qos.c +SRC_FILES := enet_ethernetif.c enet_ethernetif_kinetis.c fsl_enet.c fsl_enet_qos.c eth_netdev.c SRC_DIR := phy mdio include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/enet_ethernetif.c b/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/enet_ethernetif.c index b7bf34f03..ac69b5cc0 100644 --- a/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/enet_ethernetif.c +++ b/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/enet_ethernetif.c @@ -353,5 +353,11 @@ err_t ethernetif_init(struct netif *netif, } #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] Adding netdev.\n", __func__)); + if (EOK != lwip_netdev_add(netif)) { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] LWIP add netdev failed.\n", __func__)); + } else { + printf("[%s] Add Netdev successful\n", __func__); + } return ERR_OK; } diff --git a/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/eth_netdev.c new file mode 100644 index 000000000..03ca27594 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/ethernet/eth_netdev.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + */ + +/** +* @file eth_netdev.c +* @brief register net dev function for lwip +* @version 3.0 +* @author AIIT XUOS Lab +* @date 2023-08-07 +*/ + +#include +#include +#include +#include +#include +#include + +static const uint32_t NETIF_NAME_LEN = 2; + +static int lwip_netdev_set_up(struct netdev* netdev) +{ + netif_set_up((struct netif*)netdev->user_data); + return ERR_OK; +} + +static int lwip_netdev_set_down(struct netdev* netif) +{ + netif_set_down((struct netif*)netif->user_data); + return ERR_OK; +} + +#ifndef ip_2_ip4 +#define ip_2_ip4(ipaddr) (ipaddr) +#endif +static int lwip_netdev_set_addr_info(struct netdev* netdev, ip_addr_t* ip_addr, ip_addr_t* netmask, ip_addr_t* gw) +{ + if (ip_addr && netmask && gw) { + netif_set_addr((struct netif*)netdev->user_data, ip_2_ip4(ip_addr), ip_2_ip4(netmask), ip_2_ip4(gw)); + } else { + if (ip_addr) { + netif_set_ipaddr((struct netif*)netdev->user_data, ip_2_ip4(ip_addr)); + } + if (netmask) { + netif_set_netmask((struct netif*)netdev->user_data, ip_2_ip4(netmask)); + } + if (gw) { + netif_set_gw((struct netif*)netdev->user_data, ip_2_ip4(gw)); + } + } +} + +#ifdef LWIP_DNS +static int lwip_netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, ip_addr_t* dns_server) +{ +#if LWIP_VERSION_MAJOR == 1U /* v1.x */ + extern void dns_setserver(u8_t numdns, ip_addr_t * dnsserver); +#else /* >=2.x */ + extern void dns_setserver(uint8_t dns_num, const ip_addr_t* dns_server); +#endif /* LWIP_VERSION_MAJOR == 1U */ + + dns_setserver(dns_num, dns_server); + return ERR_OK; +} +#endif + +#ifdef LWIP_DHCP +static int lwip_netdev_set_dhcp(struct netdev* netdev, bool is_enabled) +{ + netdev_low_level_set_dhcp_status(netdev, is_enabled); + + if (true == is_enabled) { + dhcp_start((struct netif*)netdev->user_data); + } else { + dhcp_stop((struct netif*)netdev->user_data); + } + + return ERR_OK; +} +#endif + +static int lwip_netdev_set_default(struct netdev* netdev) +{ + netif_set_default((struct netif*)netdev->user_data); + return ERR_OK; +} + +static const struct netdev_ops lwip_netdev_ops = { + .set_up = lwip_netdev_set_up, + .set_down = lwip_netdev_set_down, + .set_addr_info = lwip_netdev_set_addr_info, +#ifdef LWIP_DNS + .set_dns_server = lwip_netdev_set_dns_server, +#endif +#ifdef LWIP_DHCP + .set_dhcp = lwip_netdev_set_dhcp, +#endif + .set_default = lwip_netdev_set_default, +}; + +static inline int netdev_set_flags(struct netif* lwip_netif) +{ + CHECK(lwip_netif); + struct netdev* netdev = netdev_get_by_name(lwip_netif->name); + if (netdev == NULL) { + return -ERR_IF; + } + + netdev->mtu = lwip_netif->mtu; + // set flags + if (lwip_netif->flags | NETIF_FLAG_BROADCAST) { + netdev->flags |= NETDEV_FLAG_BROADCAST; + } + if (lwip_netif->flags | NETIF_FLAG_ETHARP) { + netdev->flags |= NETDEV_FLAG_ETHARP; + } + if (lwip_netif->flags | NETIF_FLAG_IGMP) { + netdev->flags |= NETDEV_FLAG_IGMP; + } +#if LWIP_VERSION_MAJOR >= 2U /* >= v2.x */ + if (lwip_netif->flags & NETIF_FLAG_MLD6) { + netdev->flags |= NETDEV_FLAG_MLD6; + } +#endif /* LWIP_VERSION_MAJOR >= 2U */ + +#if LWIP_DHCP + netdev_low_level_set_dhcp_status(netdev, true); +#else + netdev_low_level_set_dhcp_status(netdev, false); +#endif + + return ERR_OK; +} + +int lwip_netdev_add(struct netif* lwip_netif) +{ + CHECK(lwip_netif); + + struct netdev* netdev = calloc(1, sizeof(struct netdev)); + if (netdev == NULL) { + return -ERR_IF; + } + + // init netdev + char netif_name[NETIF_NAME_LEN + 1]; + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] Lwip netif name: %s\n", __func__, lwip_netif->name)); + strncpy(netif_name, lwip_netif->name, NETIF_NAME_LEN); + // register netdev + int result = netdev_register(netdev, netif_name, (void*)lwip_netif); + // set values of netdev + netdev_set_flags(lwip_netif); + netdev->ops = &lwip_netdev_ops; + netdev->hwaddr_len = lwip_netif->hwaddr_len; + memcpy(netdev->hwaddr, lwip_netif->hwaddr, lwip_netif->hwaddr_len); + netdev->ip_addr = lwip_netif->ip_addr; + netdev->gw = lwip_netif->gw; + netdev->netmask = lwip_netif->netmask; + + return result; +} + +void lwip_netdev_del(struct netif* lwip_netif) +{ + char name[NETIF_NAME_LEN + 1]; + struct netdev* netdev; + + CHECK(lwip_netif); + + strncpy(name, lwip_netif->name, NETIF_NAME_LEN); + netdev = netdev_get_by_name(name); + netdev_unregister(netdev); + free(netdev); +} \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/include/connect_ethernet.h b/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/include/connect_ethernet.h index 12f65358b..0933eecd5 100755 --- a/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/include/connect_ethernet.h +++ b/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/include/connect_ethernet.h @@ -33,6 +33,8 @@ #define sourceClock CLOCK_GetFreq(kCLOCK_CoreSysClk) #endif +int lwip_netdev_add(struct netif* lwip_netif); +void lwip_netdev_del(struct netif* lwip_netif); #ifdef __cplusplus } diff --git a/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/include/enet_ethernetif.h b/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/include/enet_ethernetif.h index 6c552e28c..e8fb06718 100644 --- a/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/include/enet_ethernetif.h +++ b/Ubiquitous/XiZi_IIoT/board/imxrt1176-sbc/third_party_driver/include/enet_ethernetif.h @@ -164,6 +164,9 @@ void ethernetif_input( void *netif_arg); int ETH_BSP_Config(void); void *ethernetif_config_enet_set(uint8_t enet_port); +int lwip_netdev_add(struct netif* lwip_netif); +void lwip_netdev_del(struct netif* lwip_netif); + #if defined(__cplusplus) } #endif /* __cplusplus */ diff --git a/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/Makefile b/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/Makefile index 14be72829..57c50e98b 100755 --- a/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/Makefile +++ b/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/Makefile @@ -1,3 +1,3 @@ -SRC_FILES := enet_ethernetif.c enet_ethernetif_kinetis.c fsl_enet.c +SRC_FILES := enet_ethernetif.c enet_ethernetif_kinetis.c fsl_enet.c eth_netdev.c SRC_DIR := ksz8081 include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/enet_ethernetif.c b/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/enet_ethernetif.c index 9b8ca475a..114f1ef61 100755 --- a/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/enet_ethernetif.c +++ b/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/enet_ethernetif.c @@ -68,6 +68,7 @@ #include "fsl_gpio.h" #include "fsl_iomuxc.h" +#include "netdev.h" #include "sys_arch.h" /******************************************************************************* @@ -319,6 +320,12 @@ err_t ethernetif_init(struct netif *netif, struct ethernetif *ethernetif, } #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] Adding netdev.\n", __func__)); + if (EOK != lwip_netdev_add(netif)) { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] LWIP add netdev failed.\n", __func__)); + } else { + printf("[%s] Add Netdev successful\n", __func__); + } return ERR_OK; } diff --git a/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/eth_netdev.c new file mode 100644 index 000000000..03ca27594 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/ethernet/eth_netdev.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + */ + +/** +* @file eth_netdev.c +* @brief register net dev function for lwip +* @version 3.0 +* @author AIIT XUOS Lab +* @date 2023-08-07 +*/ + +#include +#include +#include +#include +#include +#include + +static const uint32_t NETIF_NAME_LEN = 2; + +static int lwip_netdev_set_up(struct netdev* netdev) +{ + netif_set_up((struct netif*)netdev->user_data); + return ERR_OK; +} + +static int lwip_netdev_set_down(struct netdev* netif) +{ + netif_set_down((struct netif*)netif->user_data); + return ERR_OK; +} + +#ifndef ip_2_ip4 +#define ip_2_ip4(ipaddr) (ipaddr) +#endif +static int lwip_netdev_set_addr_info(struct netdev* netdev, ip_addr_t* ip_addr, ip_addr_t* netmask, ip_addr_t* gw) +{ + if (ip_addr && netmask && gw) { + netif_set_addr((struct netif*)netdev->user_data, ip_2_ip4(ip_addr), ip_2_ip4(netmask), ip_2_ip4(gw)); + } else { + if (ip_addr) { + netif_set_ipaddr((struct netif*)netdev->user_data, ip_2_ip4(ip_addr)); + } + if (netmask) { + netif_set_netmask((struct netif*)netdev->user_data, ip_2_ip4(netmask)); + } + if (gw) { + netif_set_gw((struct netif*)netdev->user_data, ip_2_ip4(gw)); + } + } +} + +#ifdef LWIP_DNS +static int lwip_netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, ip_addr_t* dns_server) +{ +#if LWIP_VERSION_MAJOR == 1U /* v1.x */ + extern void dns_setserver(u8_t numdns, ip_addr_t * dnsserver); +#else /* >=2.x */ + extern void dns_setserver(uint8_t dns_num, const ip_addr_t* dns_server); +#endif /* LWIP_VERSION_MAJOR == 1U */ + + dns_setserver(dns_num, dns_server); + return ERR_OK; +} +#endif + +#ifdef LWIP_DHCP +static int lwip_netdev_set_dhcp(struct netdev* netdev, bool is_enabled) +{ + netdev_low_level_set_dhcp_status(netdev, is_enabled); + + if (true == is_enabled) { + dhcp_start((struct netif*)netdev->user_data); + } else { + dhcp_stop((struct netif*)netdev->user_data); + } + + return ERR_OK; +} +#endif + +static int lwip_netdev_set_default(struct netdev* netdev) +{ + netif_set_default((struct netif*)netdev->user_data); + return ERR_OK; +} + +static const struct netdev_ops lwip_netdev_ops = { + .set_up = lwip_netdev_set_up, + .set_down = lwip_netdev_set_down, + .set_addr_info = lwip_netdev_set_addr_info, +#ifdef LWIP_DNS + .set_dns_server = lwip_netdev_set_dns_server, +#endif +#ifdef LWIP_DHCP + .set_dhcp = lwip_netdev_set_dhcp, +#endif + .set_default = lwip_netdev_set_default, +}; + +static inline int netdev_set_flags(struct netif* lwip_netif) +{ + CHECK(lwip_netif); + struct netdev* netdev = netdev_get_by_name(lwip_netif->name); + if (netdev == NULL) { + return -ERR_IF; + } + + netdev->mtu = lwip_netif->mtu; + // set flags + if (lwip_netif->flags | NETIF_FLAG_BROADCAST) { + netdev->flags |= NETDEV_FLAG_BROADCAST; + } + if (lwip_netif->flags | NETIF_FLAG_ETHARP) { + netdev->flags |= NETDEV_FLAG_ETHARP; + } + if (lwip_netif->flags | NETIF_FLAG_IGMP) { + netdev->flags |= NETDEV_FLAG_IGMP; + } +#if LWIP_VERSION_MAJOR >= 2U /* >= v2.x */ + if (lwip_netif->flags & NETIF_FLAG_MLD6) { + netdev->flags |= NETDEV_FLAG_MLD6; + } +#endif /* LWIP_VERSION_MAJOR >= 2U */ + +#if LWIP_DHCP + netdev_low_level_set_dhcp_status(netdev, true); +#else + netdev_low_level_set_dhcp_status(netdev, false); +#endif + + return ERR_OK; +} + +int lwip_netdev_add(struct netif* lwip_netif) +{ + CHECK(lwip_netif); + + struct netdev* netdev = calloc(1, sizeof(struct netdev)); + if (netdev == NULL) { + return -ERR_IF; + } + + // init netdev + char netif_name[NETIF_NAME_LEN + 1]; + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] Lwip netif name: %s\n", __func__, lwip_netif->name)); + strncpy(netif_name, lwip_netif->name, NETIF_NAME_LEN); + // register netdev + int result = netdev_register(netdev, netif_name, (void*)lwip_netif); + // set values of netdev + netdev_set_flags(lwip_netif); + netdev->ops = &lwip_netdev_ops; + netdev->hwaddr_len = lwip_netif->hwaddr_len; + memcpy(netdev->hwaddr, lwip_netif->hwaddr, lwip_netif->hwaddr_len); + netdev->ip_addr = lwip_netif->ip_addr; + netdev->gw = lwip_netif->gw; + netdev->netmask = lwip_netif->netmask; + + return result; +} + +void lwip_netdev_del(struct netif* lwip_netif) +{ + char name[NETIF_NAME_LEN + 1]; + struct netdev* netdev; + + CHECK(lwip_netif); + + strncpy(name, lwip_netif->name, NETIF_NAME_LEN); + netdev = netdev_get_by_name(name); + netdev_unregister(netdev); + free(netdev); +} \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/include/connect_ethernet.h b/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/include/connect_ethernet.h index 12f65358b..0933eecd5 100755 --- a/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/include/connect_ethernet.h +++ b/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/include/connect_ethernet.h @@ -33,6 +33,8 @@ #define sourceClock CLOCK_GetFreq(kCLOCK_CoreSysClk) #endif +int lwip_netdev_add(struct netif* lwip_netif); +void lwip_netdev_del(struct netif* lwip_netif); #ifdef __cplusplus } diff --git a/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/include/enet_ethernetif.h b/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/include/enet_ethernetif.h index 7b8c20c95..889b50acb 100755 --- a/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/include/enet_ethernetif.h +++ b/Ubiquitous/XiZi_IIoT/board/ok1052-c/third_party_driver/include/enet_ethernetif.h @@ -181,11 +181,14 @@ err_t ethernetif1_init(struct netif *netif); */ void ethernetif_input( void *netif_arg); -void ETH_BSP_Config(void); +int ETH_BSP_Config(void); void *ethernetif_config_enet_set(uint8_t enet_port); int32 lwip_obtain_semaphore(struct netif *netif); +int lwip_netdev_add(struct netif* lwip_netif); +void lwip_netdev_del(struct netif* lwip_netif); + #define NETIF_ENET0_INIT_FUNC ethernetif0_init #if defined(__cplusplus) diff --git a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/eth_netdev.c index 2dfe75795..03ca27594 100644 --- a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/eth_netdev.c +++ b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/ethernet/eth_netdev.c @@ -1,3 +1,41 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + */ + +/** +* @file eth_netdev.c +* @brief register net dev function for lwip +* @version 3.0 +* @author AIIT XUOS Lab +* @date 2023-08-07 +*/ #include #include diff --git a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/Makefile b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/Makefile index 65f5adc78..6cd8a10fa 100755 --- a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/Makefile +++ b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/Makefile @@ -1,3 +1,3 @@ -SRC_FILES := enet_ethernetif.c enet_ethernetif_kinetis.c fsl_enet.c +SRC_FILES := enet_ethernetif.c enet_ethernetif_kinetis.c fsl_enet.c eth_netdev.c SRC_DIR := lan8720 include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/enet_ethernetif.c b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/enet_ethernetif.c index 9b8ca475a..114f1ef61 100755 --- a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/enet_ethernetif.c +++ b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/enet_ethernetif.c @@ -68,6 +68,7 @@ #include "fsl_gpio.h" #include "fsl_iomuxc.h" +#include "netdev.h" #include "sys_arch.h" /******************************************************************************* @@ -319,6 +320,12 @@ err_t ethernetif_init(struct netif *netif, struct ethernetif *ethernetif, } #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] Adding netdev.\n", __func__)); + if (EOK != lwip_netdev_add(netif)) { + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] LWIP add netdev failed.\n", __func__)); + } else { + printf("[%s] Add Netdev successful\n", __func__); + } return ERR_OK; } diff --git a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/eth_netdev.c b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/eth_netdev.c new file mode 100644 index 000000000..03ca27594 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/ethernet/eth_netdev.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + */ + +/** +* @file eth_netdev.c +* @brief register net dev function for lwip +* @version 3.0 +* @author AIIT XUOS Lab +* @date 2023-08-07 +*/ + +#include +#include +#include +#include +#include +#include + +static const uint32_t NETIF_NAME_LEN = 2; + +static int lwip_netdev_set_up(struct netdev* netdev) +{ + netif_set_up((struct netif*)netdev->user_data); + return ERR_OK; +} + +static int lwip_netdev_set_down(struct netdev* netif) +{ + netif_set_down((struct netif*)netif->user_data); + return ERR_OK; +} + +#ifndef ip_2_ip4 +#define ip_2_ip4(ipaddr) (ipaddr) +#endif +static int lwip_netdev_set_addr_info(struct netdev* netdev, ip_addr_t* ip_addr, ip_addr_t* netmask, ip_addr_t* gw) +{ + if (ip_addr && netmask && gw) { + netif_set_addr((struct netif*)netdev->user_data, ip_2_ip4(ip_addr), ip_2_ip4(netmask), ip_2_ip4(gw)); + } else { + if (ip_addr) { + netif_set_ipaddr((struct netif*)netdev->user_data, ip_2_ip4(ip_addr)); + } + if (netmask) { + netif_set_netmask((struct netif*)netdev->user_data, ip_2_ip4(netmask)); + } + if (gw) { + netif_set_gw((struct netif*)netdev->user_data, ip_2_ip4(gw)); + } + } +} + +#ifdef LWIP_DNS +static int lwip_netdev_set_dns_server(struct netdev* netdev, uint8_t dns_num, ip_addr_t* dns_server) +{ +#if LWIP_VERSION_MAJOR == 1U /* v1.x */ + extern void dns_setserver(u8_t numdns, ip_addr_t * dnsserver); +#else /* >=2.x */ + extern void dns_setserver(uint8_t dns_num, const ip_addr_t* dns_server); +#endif /* LWIP_VERSION_MAJOR == 1U */ + + dns_setserver(dns_num, dns_server); + return ERR_OK; +} +#endif + +#ifdef LWIP_DHCP +static int lwip_netdev_set_dhcp(struct netdev* netdev, bool is_enabled) +{ + netdev_low_level_set_dhcp_status(netdev, is_enabled); + + if (true == is_enabled) { + dhcp_start((struct netif*)netdev->user_data); + } else { + dhcp_stop((struct netif*)netdev->user_data); + } + + return ERR_OK; +} +#endif + +static int lwip_netdev_set_default(struct netdev* netdev) +{ + netif_set_default((struct netif*)netdev->user_data); + return ERR_OK; +} + +static const struct netdev_ops lwip_netdev_ops = { + .set_up = lwip_netdev_set_up, + .set_down = lwip_netdev_set_down, + .set_addr_info = lwip_netdev_set_addr_info, +#ifdef LWIP_DNS + .set_dns_server = lwip_netdev_set_dns_server, +#endif +#ifdef LWIP_DHCP + .set_dhcp = lwip_netdev_set_dhcp, +#endif + .set_default = lwip_netdev_set_default, +}; + +static inline int netdev_set_flags(struct netif* lwip_netif) +{ + CHECK(lwip_netif); + struct netdev* netdev = netdev_get_by_name(lwip_netif->name); + if (netdev == NULL) { + return -ERR_IF; + } + + netdev->mtu = lwip_netif->mtu; + // set flags + if (lwip_netif->flags | NETIF_FLAG_BROADCAST) { + netdev->flags |= NETDEV_FLAG_BROADCAST; + } + if (lwip_netif->flags | NETIF_FLAG_ETHARP) { + netdev->flags |= NETDEV_FLAG_ETHARP; + } + if (lwip_netif->flags | NETIF_FLAG_IGMP) { + netdev->flags |= NETDEV_FLAG_IGMP; + } +#if LWIP_VERSION_MAJOR >= 2U /* >= v2.x */ + if (lwip_netif->flags & NETIF_FLAG_MLD6) { + netdev->flags |= NETDEV_FLAG_MLD6; + } +#endif /* LWIP_VERSION_MAJOR >= 2U */ + +#if LWIP_DHCP + netdev_low_level_set_dhcp_status(netdev, true); +#else + netdev_low_level_set_dhcp_status(netdev, false); +#endif + + return ERR_OK; +} + +int lwip_netdev_add(struct netif* lwip_netif) +{ + CHECK(lwip_netif); + + struct netdev* netdev = calloc(1, sizeof(struct netdev)); + if (netdev == NULL) { + return -ERR_IF; + } + + // init netdev + char netif_name[NETIF_NAME_LEN + 1]; + SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] Lwip netif name: %s\n", __func__, lwip_netif->name)); + strncpy(netif_name, lwip_netif->name, NETIF_NAME_LEN); + // register netdev + int result = netdev_register(netdev, netif_name, (void*)lwip_netif); + // set values of netdev + netdev_set_flags(lwip_netif); + netdev->ops = &lwip_netdev_ops; + netdev->hwaddr_len = lwip_netif->hwaddr_len; + memcpy(netdev->hwaddr, lwip_netif->hwaddr, lwip_netif->hwaddr_len); + netdev->ip_addr = lwip_netif->ip_addr; + netdev->gw = lwip_netif->gw; + netdev->netmask = lwip_netif->netmask; + + return result; +} + +void lwip_netdev_del(struct netif* lwip_netif) +{ + char name[NETIF_NAME_LEN + 1]; + struct netdev* netdev; + + CHECK(lwip_netif); + + strncpy(name, lwip_netif->name, NETIF_NAME_LEN); + netdev = netdev_get_by_name(name); + netdev_unregister(netdev); + free(netdev); +} \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/include/connect_ethernet.h b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/include/connect_ethernet.h index ad4062e9a..360b28125 100755 --- a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/include/connect_ethernet.h +++ b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/include/connect_ethernet.h @@ -32,6 +32,9 @@ extern "C" { #define sourceClock CLOCK_GetFreq(kCLOCK_CoreSysClk) #endif +int lwip_netdev_add(struct netif* lwip_netif); +void lwip_netdev_del(struct netif* lwip_netif); + #ifdef __cplusplus } #endif diff --git a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/include/enet_ethernetif.h b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/include/enet_ethernetif.h index 7978b36b6..e6f59df3e 100755 --- a/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/include/enet_ethernetif.h +++ b/Ubiquitous/XiZi_IIoT/board/xiwangtong-arm32/third_party_driver/include/enet_ethernetif.h @@ -185,6 +185,9 @@ int ETH_BSP_Config(void); void *ethernetif_config_enet_set(uint8_t enet_port); int32 lwip_obtain_semaphore(struct netif *netif); +int lwip_netdev_add(struct netif* lwip_netif); +void lwip_netdev_del(struct netif* lwip_netif); + #define NETIF_ENET0_INIT_FUNC ethernetif0_init #if defined(__cplusplus) diff --git a/Ubiquitous/XiZi_IIoT/path_kernel.mk b/Ubiquitous/XiZi_IIoT/path_kernel.mk index 7b44f073e..1603864c8 100755 --- a/Ubiquitous/XiZi_IIoT/path_kernel.mk +++ b/Ubiquitous/XiZi_IIoT/path_kernel.mk @@ -88,6 +88,8 @@ KERNELPATHS += \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/priv \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/prot \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/arch + +KERNELPATHS += -I$(KERNEL_ROOT)/resources/include/netdev endif endif @@ -336,6 +338,8 @@ KERNELPATHS += \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/priv \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/prot \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/arch + +KERNELPATHS += -I$(KERNEL_ROOT)/resources/include/netdev endif endif @@ -367,6 +371,8 @@ KERNELPATHS += \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/priv \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/include/lwip/prot \ -I$(KERNEL_ROOT)/resources/ethernet/LwIP/arch + +KERNELPATHS += -I$(KERNEL_ROOT)/resources/include/netdev endif endif diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/Makefile b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/Makefile index 697c6c141..cd4019ebb 100755 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/Makefile +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/Makefile @@ -1,4 +1,4 @@ -SRC_FILES := ping.c lwip_ping_demo.c lwip_tcp_demo.c tcpecho_raw.c lwip_config_demo.c lwip_dhcp_demo.c iperf.c http_test.c +SRC_FILES := ping.c lwip_ping_demo.c lwip_tcp_demo.c lwip_udp_demo.c tcpecho_raw.c lwip_config_demo.c lwip_dhcp_demo.c iperf.c http_test.c # SRC_FILES := ping.c lwip_ping_demo.c lwip_tcp_demo.c lwip_udp_demo.c tcpecho_raw.c lwip_config_demo.c lwip_dhcp_demo.c iperf.c include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/iperf.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/iperf.c index 29c947a18..e2389af3c 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/iperf.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/iperf.c @@ -50,7 +50,7 @@ static void iperf_udp_client(void *thread_param) send_size = IPERF_BUFSZ > 1470 ? 1470 : IPERF_BUFSZ; sock = socket(PF_INET, SOCK_DGRAM, 0); - if(sock < 0){ + if(sock < 0) { KPrintf("[%s:%d] can't create socket! exit!\n", __FILE__, __LINE__); return; } @@ -60,21 +60,21 @@ static void iperf_udp_client(void *thread_param) server.sin_addr.s_addr = inet_addr(param.host); memset(&(server.sin_zero), 0, sizeof(server.sin_zero)); - if (connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr))){ + if (connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr))) { lw_error("Unable to connect\n"); closesocket(sock); return; } buffer = malloc(IPERF_BUFSZ); - if (buffer == NULL){ + if (buffer == NULL) { printf("[%s:%d] malloc failed\n", __FILE__, __LINE__); return; } memset(buffer, 0x00, IPERF_BUFSZ); KPrintf("iperf udp mode run...\n"); - while (param.mode != IPERF_MODE_STOP){ + while (param.mode != IPERF_MODE_STOP) { packet_count++; tick = CurrentTicksGain(); buffer[0] = htonl(packet_count); @@ -101,12 +101,12 @@ static void iperf_udp_server(void *thread_param) struct timeval timeout; buffer = malloc(IPERF_BUFSZ); - if (buffer == NULL){ + if (buffer == NULL) { return; } sock = socket(PF_INET, SOCK_DGRAM, 0); - if(sock < 0){ + if(sock < 0) { KPrintf("can't create socket! exit!"); return; } @@ -117,35 +117,34 @@ static void iperf_udp_server(void *thread_param) timeout.tv_sec = 2; timeout.tv_usec = 0; - if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1){ + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) { KPrintf("setsockopt failed!"); closesocket(sock); free(buffer); return; } - if (bind(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0){ + if (bind(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) { KPrintf("iperf server bind failed! exit!"); closesocket(sock); free(buffer); return; } - while (param.mode != IPERF_MODE_STOP){ + while (param.mode != IPERF_MODE_STOP) { tick1 = CurrentTicksGain(); tick2 = tick1; lost = 0; total = 0; sentlen = 0; - while ((tick2 - tick1) < (TICK_PER_SECOND * 5)){ + while ((tick2 - tick1) < (TICK_PER_SECOND * 5)) { r_size = recvfrom(sock, buffer, IPERF_BUFSZ, 0, (struct sockaddr *)&sender, (socklen_t*)&sender_len); - if (r_size > 12){ + if (r_size > 12) { pcount = ntohl(buffer[0]); - if (last_pcount < pcount){ + if (last_pcount < pcount) { lost += pcount - last_pcount - 1; total += pcount - last_pcount; - } - else{ + } else { last_pcount = pcount; } last_pcount = pcount; @@ -153,7 +152,7 @@ static void iperf_udp_server(void *thread_param) } tick2 = CurrentTicksGain(); } - if (sentlen > 0){ + if (sentlen > 0) { long data; int integer, decimal; KTaskDescriptorType tid; @@ -186,10 +185,9 @@ static void iperf_client(void *thread_param) for (i = 0; i < IPERF_BUFSZ; i ++) send_buf[i] = i & 0xff; - while (param.mode != IPERF_MODE_STOP) - { + while (param.mode != IPERF_MODE_STOP) { sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0){ + if (sock < 0) { KPrintf("create socket failed!"); DelayKTask(TICK_PER_SECOND); continue; @@ -200,8 +198,8 @@ static void iperf_client(void *thread_param) addr.sin_addr.s_addr = inet_addr((char *)param.host); ret = connect(sock, (const struct sockaddr *)&addr, sizeof(addr)); - if (ret == -1){ - if (tips){ + if (ret == -1) { + if (tips) { KPrintf("Connect to iperf server faile, Waiting for the server to open!"); tips = 0; } @@ -224,9 +222,9 @@ static void iperf_client(void *thread_param) sentlen = 0; tick1 = CurrentTicksGain(); - while (param.mode != IPERF_MODE_STOP){ + while (param.mode != IPERF_MODE_STOP) { tick2 = CurrentTicksGain(); - if (tick2 - tick1 >= TICK_PER_SECOND * 5){ + if (tick2 - tick1 >= TICK_PER_SECOND * 5) { double speed; // int integer, decimal; KTaskDescriptorType tid; @@ -240,7 +238,7 @@ static void iperf_client(void *thread_param) } ret = send(sock, send_buf, IPERF_BUFSZ, 0); - if (ret > 0){ + if (ret > 0) { sentlen += ret; } @@ -264,7 +262,7 @@ struct sock_conn_cb { int parent_id; }; -void iperf_sever_worker(void* arg) { +void iperf_server_worker(void* arg) { struct sock_conn_cb *sccb = (struct sock_conn_cb *)arg; x_ticks_t tick1, tick2; @@ -287,7 +285,7 @@ void iperf_sever_worker(void* arg) { int cur_tid = GetKTaskDescriptor()->id.id; tick1 = CurrentTicksGain(); - while (param.mode != IPERF_MODE_STOP){ + while (param.mode != IPERF_MODE_STOP) { bytes_received = recv(sccb->connected, recv_data, IPERF_BUFSZ, 0); if (bytes_received == 0) { KPrintf("client disconnected (%s, %d)\n", @@ -336,7 +334,7 @@ void iperf_server_multithread(void *thread_param) struct timeval timeout; sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0){ + if (sock < 0) { KPrintf("[%s:%d] Socket error!\n", __FILE__, __LINE__); goto __exit; } @@ -346,12 +344,12 @@ void iperf_server_multithread(void *thread_param) server_addr.sin_addr.s_addr = INADDR_ANY; memset(&(server_addr.sin_zero), 0x0, sizeof(server_addr.sin_zero)); - if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1){ + if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { KPrintf("Unable to bind!\n"); goto __exit; } - if (listen(sock, 5) == -1){ + if (listen(sock, 5) == -1) { KPrintf("Listen error!\n"); goto __exit; } @@ -360,7 +358,7 @@ void iperf_server_multithread(void *thread_param) timeout.tv_sec = 5; timeout.tv_usec = 0; - while (param.mode != IPERF_MODE_STOP){ + while (param.mode != IPERF_MODE_STOP) { FD_ZERO(&readset); FD_SET(sock, &readset); @@ -379,8 +377,8 @@ void iperf_server_multithread(void *thread_param) sccb->client_addr = client_addr; sccb->server_addr = server_addr; sccb->parent_id = cur_tid; - int tid = KTaskCreate("iperf server", iperf_sever_worker, sccb, LWIP_TASK_STACK_SIZE, 20); - // iperf_sever_worker(sccb); + int tid = KTaskCreate("iperf server", iperf_server_worker, sccb, LWIP_TASK_STACK_SIZE, 20); + // iperf_server_worker(sccb); if (tid) { StartupKTask(tid); } else { @@ -405,13 +403,13 @@ void iperf_server(void *thread_param) struct timeval timeout; sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0){ + if (sock < 0) { KPrintf("[%s:%d] Socket error!\n", __FILE__, __LINE__); goto __exit; } recv_data = (uint8_t *)malloc(IPERF_BUFSZ); - if (recv_data == NULL){ + if (recv_data == NULL) { KPrintf("No memory!\n"); goto __exit; } @@ -421,12 +419,12 @@ void iperf_server(void *thread_param) server_addr.sin_addr.s_addr = INADDR_ANY; memset(&(server_addr.sin_zero), 0x0, sizeof(server_addr.sin_zero)); - if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1){ + if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { KPrintf("Unable to bind!\n"); goto __exit; } - if (listen(sock, 5) == -1){ + if (listen(sock, 5) == -1) { KPrintf("Listen error!\n"); goto __exit; } @@ -434,7 +432,7 @@ void iperf_server(void *thread_param) timeout.tv_sec = 3; timeout.tv_usec = 0; - while (param.mode != IPERF_MODE_STOP){ + while (param.mode != IPERF_MODE_STOP) { FD_ZERO(&readset); FD_SET(sock, &readset); @@ -458,7 +456,7 @@ void iperf_server(void *thread_param) recvlen = 0; tick1 = CurrentTicksGain(); - while (param.mode != IPERF_MODE_STOP){ + while (param.mode != IPERF_MODE_STOP) { bytes_received = recv(connected, recv_data, IPERF_BUFSZ, 0); if (bytes_received == 0) { KPrintf("client disconnected (%s, %d)\n", @@ -526,48 +524,37 @@ int iperf(int argc, char **argv) int use_udp = 0; int index = 1; - if (argc == 1) - { + if (argc == 1) { goto __usage; } - if (strcmp(argv[1], "-u") == 0) - { + if (strcmp(argv[1], "-u") == 0) { index = 2; use_udp = 1; } if (strcmp(argv[index], "-h") == 0) goto __usage; - else if (strcmp(argv[index], "--stop") == 0) - { + else if (strcmp(argv[index], "--stop") == 0) { /* stop iperf */ param.mode = IPERF_MODE_STOP; printf("iperf stop.\n"); return 0; - } - else if (strcmp(argv[index], "-s") == 0) - { + } else if (strcmp(argv[index], "-s") == 0) { mode = IPERF_MODE_SERVER; /* server mode */ /* iperf -s -p 5000 */ - if (argc >= 4) - { - if (strcmp(argv[index + 1], "-p") == 0) - { + if (argc >= 4) { + if (strcmp(argv[index + 1], "-p") == 0) { port = atoi(argv[index + 2]); } else goto __usage; } - } - else if (strcmp(argv[index], "-c") == 0) - { + } else if (strcmp(argv[index], "-c") == 0) { mode = IPERF_MODE_CLIENT; /* client mode */ if (argc < 3) goto __usage; host = argv[index + 1]; - if (argc >= 5) - { + if (argc >= 5) { /* iperf -c host -p port */ - if (strcmp(argv[index + 2], "-p") == 0) - { + if (strcmp(argv[index + 2], "-p") == 0) { port = atoi(argv[index + 3]); } else goto __usage; @@ -575,57 +562,43 @@ int iperf(int argc, char **argv) } else goto __usage; - if (argc >= 7) - { - if(strcmp(argv[argc - 2], "-m") == 0) - { + if (argc >= 7) { + if(strcmp(argv[argc - 2], "-m") == 0) { numtid = atoi(argv[argc - 1]); } else goto __usage; } /* start iperf */ - if (param.mode == IPERF_MODE_STOP) - { + if (param.mode == IPERF_MODE_STOP) { int i = 0; char tid_name[NAME_NUM_MAX + 1] = {0}; param.mode = mode; param.port = port; - if (param.host) - { + if (param.host) { free(param.host); param.host = NULL; } if (host) param.host = strdup(host); - for (i = 0; i < numtid; i++) - { + for (i = 0; i < numtid; i++) { int32 tid = 0; void (*function)(void *parameter); - if (use_udp) - { - if (mode == IPERF_MODE_CLIENT) - { + if (use_udp) { + if (mode == IPERF_MODE_CLIENT) { snprintf(tid_name, sizeof(tid_name), "iperfc%02d", i + 1); function = iperf_udp_client; - } - else if (mode == IPERF_MODE_SERVER) - { + } else if (mode == IPERF_MODE_SERVER) { snprintf(tid_name, sizeof(tid_name), "iperfd%02d", i + 1); function = iperf_udp_server; } - } - else - { - if (mode == IPERF_MODE_CLIENT) - { + } else { + if (mode == IPERF_MODE_CLIENT) { snprintf(tid_name, sizeof(tid_name), "iperfc%02d", i + 1); function = iperf_client; - } - else if (mode == IPERF_MODE_SERVER) - { + } else if (mode == IPERF_MODE_SERVER) { snprintf(tid_name, sizeof(tid_name), "iperfd%02d", i + 1); function = iperf_server_multithread; } @@ -634,9 +607,7 @@ int iperf(int argc, char **argv) tid = KTaskCreate(tid_name, function, NULL, LWIP_TASK_STACK_SIZE, 20); if (tid) StartupKTask(tid); } - } - else - { + } else { KPrintf("Please stop iperf firstly, by:\n"); KPrintf("iperf --stop\n"); } diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_tcp_demo.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_tcp_demo.c index e1ad56550..1a5fd37bc 100755 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_tcp_demo.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_tcp_demo.c @@ -27,8 +27,8 @@ #include "lwip/sockets.h" #include "tcpecho_raw.h" char tcp_demo_msg[LWIP_TEST_MSG_SIZE] = { 0 }; -char tcp_demo_ip[] = {192, 168, 250, 252}; -u16_t tcp_demo_port = 80; +char tcp_server_ip[] = {192, 168, 130, 2}; +u16_t tcp_server_port = 80; int tcp_send_num = 0; int tcp_send_task_on = 0; uint32 tcp_interval = 50; @@ -39,14 +39,14 @@ static void LwipTcpSendTask(void *arg) { int fd = -1; fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd < 0) - { - lw_error("Socket error\n"); + if (fd < 0) { + KPrintf("Socket error\n"); return; } LwipTcpSocketParamType *param = (LwipTcpSocketParamType *)arg; + //configure tcp client param struct sockaddr_in tcp_sock; tcp_sock.sin_family = AF_INET; tcp_sock.sin_port = htons(param->port); @@ -55,24 +55,22 @@ static void LwipTcpSendTask(void *arg) if (connect(fd, (struct sockaddr *)&tcp_sock, sizeof(struct sockaddr))) { - lw_error("Unable to connect\n"); + KPrintf("Unable to connect\n"); closesocket(fd); return; } - lw_notice("tcp connect success, start to send.\n"); - lw_notice("\n\nTarget Port:%d\n\n", tcp_sock.sin_port); + KPrintf("tcp connect success, start to send.\n"); + KPrintf("\n\nTarget Port:%d\n\n", tcp_sock.sin_port); tcp_send_task_on = 1; - while(tcp_send_num > 0 || tcp_send_num == -1) - { + while(tcp_send_num > 0 || tcp_send_num == -1) { sendto(fd, tcp_demo_msg, strlen(tcp_demo_msg), 0, (struct sockaddr*)&tcp_sock, sizeof(struct sockaddr)); - lw_notice("Send tcp msg: %s \n", tcp_demo_msg); - if(tcp_send_num > 0) - { + KPrintf("Send tcp msg: %s \n", tcp_demo_msg); + if(tcp_send_num > 0) { tcp_send_num--; } - DelayKTask(tcp_interval); + MdelayKTask(tcp_interval); } closesocket(fd); @@ -82,41 +80,39 @@ static void LwipTcpSendTask(void *arg) void LwipTcpSendTest(int argc, char *argv[]) { - if(tcp_send_task_on){ + if(tcp_send_task_on) { tcp_send_num = 0; printf("waitting send task exit...\n"); - while(tcp_send_task_on){ - DelayKTask(1000); + while(tcp_send_task_on) { + MdelayKTask(1000); } tcp_send_num = 1; } LwipTcpSocketParamType param; uint8_t enet_port = 0; memset(tcp_demo_msg, 0, LWIP_TEST_MSG_SIZE); - if(argc >= 2) - { + if(argc >= 2) { strncpy(tcp_demo_msg, argv[1], strlen(argv[1])); - } - else - { + } else { strncpy(tcp_demo_msg, "hello world", strlen("hello world")); + tcp_send_num = 10;//send 10 msg to server + tcp_interval = 100;//100ms } strcat(tcp_demo_msg, "\r\n"); - if(argc >= 3) - { - if(sscanf(argv[2], "%d.%d.%d.%d:%d", &tcp_demo_ip[0], &tcp_demo_ip[1], &tcp_demo_ip[2], &tcp_demo_ip[3], &tcp_demo_port) == EOK) + if(argc >= 3) { + if(sscanf(argv[2], "%d.%d.%d.%d:%d", &tcp_server_ip[0], &tcp_server_ip[1], &tcp_server_ip[2], &tcp_server_ip[3], &tcp_server_port) == EOK) { - sscanf(argv[2], "%d.%d.%d.%d", &tcp_demo_ip[0], &tcp_demo_ip[1], &tcp_demo_ip[2], &tcp_demo_ip[3]); + sscanf(argv[2], "%d.%d.%d.%d", &tcp_server_ip[0], &tcp_server_ip[1], &tcp_server_ip[2], &tcp_server_ip[3]); } sscanf(argv[3], "%d", &tcp_send_num); sscanf(argv[4], "%d", &tcp_interval); } - lw_notice("get ipaddr %d.%d.%d.%d:%d send msg %d times\n", tcp_demo_ip[0], tcp_demo_ip[1], tcp_demo_ip[2], tcp_demo_ip[3], tcp_demo_port, tcp_send_num); - lwip_config_tcp(enet_port, lwip_ipaddr, lwip_netmask, tcp_demo_ip); + KPrintf("connect ipaddr %d.%d.%d.%d:%d send msg %d times\n", tcp_server_ip[0], tcp_server_ip[1], tcp_server_ip[2], tcp_server_ip[3], tcp_server_port, tcp_send_num); + lwip_config_tcp(enet_port, lwip_ipaddr, lwip_netmask, lwip_gwaddr); - memcpy(param.ip, tcp_demo_ip, 4); - param.port = tcp_demo_port; + memcpy(param.ip, tcp_server_ip, 4); + param.port = tcp_server_port; param.buf = malloc(LWIP_TEST_MSG_SIZE); memcpy(param.buf, tcp_demo_msg, LWIP_TEST_MSG_SIZE); @@ -131,7 +127,96 @@ void LwipTcpRecvTest(void) uint8_t enet_port = 0; ///< test enet port 0 lwip_config_net(enet_port, lwip_ipaddr, lwip_netmask, lwip_gwaddr); - tcpecho_raw_init(); + + uint8_t *recv_data; + socklen_t sin_size; + int sock = -1, connected, bytes_received, i; + struct sockaddr_in server_addr, client_addr; + fd_set readset; + struct timeval timeout; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + KPrintf("[%s:%d] Socket error!\n", __FILE__, __LINE__); + goto __exit; + } + + recv_data = (uint8_t *)malloc(128); + if (recv_data == NULL) { + KPrintf("No memory!\n"); + goto __exit; + } + + //configure tcp server param + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(tcp_server_port); + server_addr.sin_addr.s_addr = INADDR_ANY; + memset(&(server_addr.sin_zero), 0x0, sizeof(server_addr.sin_zero)); + + if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { + KPrintf("Unable to bind!\n"); + goto __exit; + } + + if (listen(sock, 5) == -1){ + KPrintf("Listen error!\n"); + goto __exit; + } + + timeout.tv_sec = 30; + timeout.tv_usec = 0; + + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) { + KPrintf("setsockopt failed!"); + goto __exit; + } + + while (1) { + FD_ZERO(&readset); + FD_SET(sock, &readset); + + if (select(sock + 1, &readset, NULL, NULL, &timeout) == 0) { + continue; + } + + sin_size = sizeof(struct sockaddr_in); + + connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size); + + while (1) { + bytes_received = recv(connected, recv_data, 128, 0); + if (bytes_received == 0) { + KPrintf("client disconnected (%s, %d)\n", + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + break; + } else if (bytes_received < 0) { + KPrintf("recv error, client: (%s, %d)\n", + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + break; + } else { + KPrintf("new client connected from (%s, %d)\n", + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + KPrintf("recv data length %d Bytes\n", bytes_received); + for (i = 0; i < bytes_received; i ++) { + KPrintf("data 0x%x\n", recv_data[i]); + } + if (i = bytes_received) { + KPrintf("\r\n"); + memset(recv_data, 0, sizeof(recv_data)); + } + } + } + + if (connected >= 0) { + closesocket(connected); + connected = -1; + break; + } + } + +__exit: + if (sock >= 0) closesocket(sock); + if (recv_data) free(recv_data); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c index d7b51b026..0456aa419 100755 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c @@ -30,8 +30,8 @@ static struct udp_pcb *udpecho_raw_pcb; -char udp_demo_ip[] = {192, 168, 131, 1}; -u16_t udp_demo_port = LWIP_TARGET_PORT; +char udp_server_ip[] = {192, 168, 130, 2}; +u16_t udp_server_port = LWIP_TARGET_PORT; int32 udp_send_num = 0; int8 udp_send_task_on = 0; uint32 udp_interval = 50; @@ -45,38 +45,35 @@ static void LwipUDPSendTask(void *arg) { int cnt = LWIP_DEMO_TIMES; - lw_print("udp_send_demo start.\n"); + KPrintf("udp_send_demo start.\n"); int socket_fd = -1; socket_fd = socket(AF_INET, SOCK_DGRAM, 0); - if (socket_fd < 0) - { - lw_error("Socket error\n"); + if (socket_fd < 0) { + KPrintf("Socket error\n"); return; } struct sockaddr_in udp_sock; udp_sock.sin_family = AF_INET; - udp_sock.sin_port = htons(udp_demo_port); - udp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(udp_demo_ip[0], udp_demo_ip[1], udp_demo_ip[2], udp_demo_ip[3])); + udp_sock.sin_port = htons(udp_server_port); + udp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(udp_server_ip[0], udp_server_ip[1], udp_server_ip[2], udp_server_ip[3])); memset(&(udp_sock.sin_zero), 0, sizeof(udp_sock.sin_zero)); - if (connect(socket_fd, (struct sockaddr *)&udp_sock, sizeof(struct sockaddr))) - { - lw_error("Unable to connect\n"); + if (connect(socket_fd, (struct sockaddr *)&udp_sock, sizeof(struct sockaddr))) { + KPrintf("Unable to connect\n"); closesocket(socket_fd); return; } - lw_notice("UDP connect success, start to send.\n"); - lw_notice("\n\nTarget Port:%d\n\n", udp_sock.sin_port); + KPrintf("UDP connect success, start to send.\n"); + KPrintf("\n\nTarget Port:%d\n\n", udp_sock.sin_port); udp_send_task_on = 1; - while(udp_send_num > 0 || udp_send_num == -1) - { + while(udp_send_num > 0 || udp_send_num == -1) { sendto(socket_fd, udp_demo_msg, strlen(udp_demo_msg), 0, (struct sockaddr*)&udp_sock, sizeof(struct sockaddr)); - lw_notice("Send UDP msg: %s \n", udp_demo_msg); - DelayKTask(udp_interval); + KPrintf("Send UDP msg: %s \n", udp_demo_msg); + MdelayKTask(udp_interval); udp_send_num--; } closesocket(socket_fd); @@ -86,11 +83,11 @@ static void LwipUDPSendTask(void *arg) void *LwipUdpSendTest(int argc, char *argv[]) { - if(udp_send_task_on){ + if(udp_send_task_on) { udp_send_num = 0; printf("waitting send task exit...\n"); while(udp_send_task_on){ - DelayKTask(1000); + MdelayKTask(1000); } udp_send_num = 1; } @@ -98,89 +95,105 @@ void *LwipUdpSendTest(int argc, char *argv[]) uint8_t enet_port = 0; ///< test enet port 0 memset(udp_demo_msg, 0, sizeof(udp_demo_msg)); - if(argc == 1) - { - lw_print("lw: [%s] gw %d.%d.%d.%d:%d\n", __func__, udp_demo_ip[0], udp_demo_ip[1], udp_demo_ip[2], udp_demo_ip[3], udp_demo_port); + if(argc == 1) { + KPrintf("lw: [%s] gw %d.%d.%d.%d:%d\n", __func__, udp_server_ip[0], udp_server_ip[1], udp_server_ip[2], udp_server_ip[3], udp_server_port); strncpy(udp_demo_msg, hello_str, strlen(hello_str)); - } - else - { + udp_send_num = 10; + udp_interval = 100; + } else { strncpy(udp_demo_msg, argv[1], strlen(argv[1])); strncat(udp_demo_msg, "\r\n", 2); - if(argc == 3) - { - sscanf(argv[2], "%d.%d.%d.%d:%d", &udp_demo_ip[0], &udp_demo_ip[1], &udp_demo_ip[2], &udp_demo_ip[3], &udp_demo_port); + if(argc == 3) { + sscanf(argv[2], "%d.%d.%d.%d:%d", &udp_server_ip[0], &udp_server_ip[1], &udp_server_ip[2], &udp_server_ip[3], &udp_server_port); } - if(argc > 3) - { + if(argc > 3) { sscanf(argv[3], "%d", &udp_send_num); sscanf(argv[4], "%d", &udp_interval); } } - lw_print("lw: [%s] gw %d.%d.%d.%d:%d send time %d udp_interval %d\n", __func__, udp_demo_ip[0], udp_demo_ip[1], udp_demo_ip[2], udp_demo_ip[3], udp_demo_port, udp_send_num, udp_interval); + KPrintf("lw: [%s] gw %d.%d.%d.%d:%d send time %d udp_interval %d\n", __func__, udp_server_ip[0], udp_server_ip[1], udp_server_ip[2], udp_server_ip[3], udp_server_port, udp_send_num, udp_interval); - lwip_config_net(enet_port, lwip_ipaddr, lwip_netmask, udp_demo_ip); + //init lwip and net dirver + lwip_config_net(enet_port, lwip_ipaddr, lwip_netmask, lwip_gwaddr); sys_thread_new("udp send", LwipUDPSendTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(3), UDPSend, LwipUdpSendTest, UDPSend msg [ip:port [num [interval]]]); -static void LwipUdpRecvTask(void *arg, struct udp_pcb *upcb, struct pbuf *p, - const ip_addr_t *addr, u16_t port) -{ - int udp_len; - err_t err; - struct pbuf* udp_buf; - - LWIP_UNUSED_ARG(arg); - - if (p == NULL) - { - return; - } - - udp_len = p->tot_len; - lw_notice("Receive data :%dB\r\n", udp_len); - - if(udp_len <= 80) - { - lw_notice("%.*s\r\n", udp_len, (char *)(p->payload)); - } - - udp_buf = pbuf_alloc(PBUF_TRANSPORT, PBUF_SIZE, PBUF_RAM); - - memset(udp_buf->payload, 0, PBUF_SIZE); - - err = pbuf_take(udp_buf, "Client receive success!\r\n", 27); - - /* send received packet back to sender */ - udp_sendto(upcb, udp_buf, addr, port); - - /* free the pbuf */ - pbuf_free(p); - pbuf_free(udp_buf); -} - void LwipUdpRecvTest(void) { - err_t err; uint8_t enet_port = 0; ///< test enet port 0 + //init lwip and net dirver lwip_config_net(enet_port, lwip_ipaddr, lwip_netmask, lwip_gwaddr); - udpecho_raw_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); - if (udpecho_raw_pcb == NULL) - { - return; + uint8_t *recv_data; + socklen_t sin_size; + int sock = -1, connected, bytes_received, i; + struct sockaddr_in server_addr, client_addr; + fd_set readset; + struct timeval timeout; + + sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + KPrintf("[%s:%d] Socket error!\n", __FILE__, __LINE__); + goto __exit; } - err = udp_bind(udpecho_raw_pcb, IP_ANY_TYPE, LWIP_LOCAL_PORT); - if (err == ERR_OK) - { - udp_recv(udpecho_raw_pcb, LwipUdpRecvTask, NULL); + recv_data = (uint8_t *)malloc(128); + if (recv_data == NULL) { + KPrintf("No memory!\n"); + goto __exit; } + + //configure udp server param + server_addr.sin_family = PF_INET; + server_addr.sin_port = htons(udp_server_port); + server_addr.sin_addr.s_addr = INADDR_ANY; + memset(&(server_addr.sin_zero), 0x0, sizeof(server_addr.sin_zero)); + + if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { + KPrintf("Unable to bind!\n"); + goto __exit; + } + + timeout.tv_sec = 30; + timeout.tv_usec = 0; + + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) { + KPrintf("setsockopt failed!"); + goto __exit; + } + + while (1) { + bytes_received = recvfrom(sock, recv_data, 128, 0, (struct sockaddr *)&client_addr, (socklen_t*)&sin_size); + if (bytes_received == 0) { + KPrintf("client disconnected (%s, %d)\n", + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + break; + } else if (bytes_received < 0) { + KPrintf("recv error, client: (%s, %d)\n", + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + break; + } else { + KPrintf("new client connected from (%s, %d)\n", + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + KPrintf("recv data length %d Bytes\n", bytes_received); + for (i = 0; i < bytes_received; i ++) { + KPrintf("data 0x%x\n", recv_data[i]); + } + if (i = bytes_received) { + KPrintf("\r\n"); + memset(recv_data, 0, sizeof(recv_data)); + } + } + } + +__exit: + if (sock >= 0) closesocket(sock); + if (recv_data) free(recv_data); } SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(0), diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_ipaddr_util.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_ipaddr_util.c deleted file mode 100644 index e69de29bb..000000000 diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_lowlevel.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_lowlevel.c index 694fdcdbc..c1d4e0fa4 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_lowlevel.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_lowlevel.c @@ -1,3 +1,33 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-03-18 ChenYong First version + */ + +/** +* @file netdev_lowlevel.c +* @brief register low-level net dev function +* @version 3.0 +* @author AIIT XUOS Lab +* @date 2023-08-07 +*/ + +/************************************************* +File name: netdev_lowlevel.c +Description: register low-level net dev function +Others: take RT-Thread v4.0.2/components/driver/serial/serial.c for references + https://github.com/RT-Thread/rt-thread/tree/v4.0.2 +History: +1. Date: 2023-08-07 +Author: AIIT XUOS Lab +Modification: +1. support low-level net dev set-ip, set-netmask, set-gw, set-status, set-link-status, set-dns-server and set-dhcp-status +*************************************************/ + #include #include #include diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_manipulate.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_manipulate.c index 450155d54..97e15d3a1 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_manipulate.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_manipulate.c @@ -1,3 +1,33 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-03-18 ChenYong First version + */ + +/** +* @file netdev_manipulate.c +* @brief register net dev function +* @version 3.0 +* @author AIIT XUOS Lab +* @date 2023-08-07 +*/ + +/************************************************* +File name: netdev_manipulate.c +Description: register net dev function +Others: take RT-Thread v4.0.2/components/driver/serial/serial.c for references + https://github.com/RT-Thread/rt-thread/tree/v4.0.2 +History: +1. Date: 2023-08-07 +Author: AIIT XUOS Lab +Modification: +1. support net dev set-ip, set-netmask, set-gw, set-addr-callback, set-status-callback, set-up and set-down +*************************************************/ + #include #include #include diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_register.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_register.c index d4e855e09..73ef0ed88 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_register.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/netdev/netdev_register.c @@ -1,3 +1,32 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-03-18 ChenYong First version + */ + +/** +* @file netdev_register.c +* @brief register net dev function for net driver +* @version 3.0 +* @author AIIT XUOS Lab +* @date 2023-08-07 +*/ + +/************************************************* +File name: netdev_register.c +Description: register net dev function for net driver +Others: take RT-Thread v4.0.2/components/driver/serial/serial.c for references + https://github.com/RT-Thread/rt-thread/tree/v4.0.2 +History: +1. Date: 2023-08-07 +Author: AIIT XUOS Lab +Modification: +1. support net dev register, unregister function +*************************************************/ #include #include diff --git a/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h b/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h index 8ebe09c76..e3007fd9e 100644 --- a/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h +++ b/Ubiquitous/XiZi_IIoT/resources/include/netdev/netdev.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #ifdef __cplusplus From a51ef1c41fdf5593095d4af82af82fefdce8af4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Tue, 8 Aug 2023 11:06:34 +0800 Subject: [PATCH 20/33] Fix edu-arm32 UDPSend bug. --- .../XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c index 0456aa419..71c6bc703 100755 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c @@ -20,10 +20,10 @@ #include "board.h" #include "sys_arch.h" #include "lwip/udp.h" -#include "lwip/sockets.h" #include #include #include +#include "lwip/sockets.h" #define PBUF_SIZE 27 @@ -102,8 +102,8 @@ void *LwipUdpSendTest(int argc, char *argv[]) udp_interval = 100; } else { strncpy(udp_demo_msg, argv[1], strlen(argv[1])); - strncat(udp_demo_msg, "\r\n", 2); - if(argc == 3) { + strncat(udp_demo_msg, "\r\n", 3); + if(argc >= 3) { sscanf(argv[2], "%d.%d.%d.%d:%d", &udp_server_ip[0], &udp_server_ip[1], &udp_server_ip[2], &udp_server_ip[3], &udp_server_port); } if(argc > 3) { @@ -119,7 +119,7 @@ void *LwipUdpSendTest(int argc, char *argv[]) sys_thread_new("udp send", LwipUDPSendTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); } -SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(3), +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(5), UDPSend, LwipUdpSendTest, UDPSend msg [ip:port [num [interval]]]); void LwipUdpRecvTest(void) From 87a57d0db0a6b54cea5b6d1ae0cca3d817ec5b77 Mon Sep 17 00:00:00 2001 From: chenyh Date: Wed, 9 Aug 2023 10:59:04 +0800 Subject: [PATCH 21/33] Delete 'Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md' --- .../board/cortex-m4-emulator/README.md | 208 ------------------ 1 file changed, 208 deletions(-) delete mode 100644 Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md diff --git a/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md b/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md deleted file mode 100644 index f8d004f9a..000000000 --- a/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md +++ /dev/null @@ -1,208 +0,0 @@ -# 从零开始构建矽璓工业物联操作系统:使用ARM架构的cortex-m4 emulator - -[XiUOS](http://xuos.io/) (X Industrial Ubiquitous Operating System) 矽璓XiUOS是一款面向智慧车间的工业物联网操作系统,主要由一个极简的微型实时操作系统内核和其上的工业物联框架构成,通过高效管理工业物联网设备、支撑工业物联应用,在生产车间内实现智能化的“感知环境、联网传输、知悉识别、控制调整”,促进以工业设备和工业控制系统为核心的人、机、物深度互联,帮助提升生产线的数字化和智能化水平。 - - - -## 1. 简介 - -Q‎EMU 是一个通用的开源模拟器和虚拟化工具。目前Q‎EMU已经可以较完整的支持ARM cortex-m4架构。XiUOS同样支持运行在Q‎EMU上 - -| 硬件 | 描述 | -| -------- | ------------- | -| 芯片型号 | netduinoplus2 | -| 架构 | cortex-m4 | -| 主频 | 168MHz | -| 片内SRAM | 100+KB | -| 外设支持 | UART、GPIO | - - - -## 2. 开发环境搭建 - -### 推荐使用: - -**操作系统:** ubuntu18.04 [https://ubuntu.com/download/desktop](https://ubuntu.com/download/desktop) - -更新`ubuntu 18.04`源的方法:(根据自身情况而定,可以不更改) - -第一步:打开sources.list文件 - -```c -sudo vim /etc/apt/sources.list -``` - -第二步:将以下内容复制到sources.list文件 - -```c -deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse -deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse -deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse -``` - -第三步:更新源和系统软件 - -```c -sudo apt-get update -sudo apt-get upgrade -``` - -**开发工具推荐使用 VSCode ,VScode下载地址为:** VSCode [https://code.visualstudio.com/](https://code.visualstudio.com/),推荐下载地址为 [http://vscode.cdn.azure.cn/stable/3c4e3df9e89829dce27b7b5c24508306b151f30d/code_1.55.2-1618307277_amd64.deb](http://vscode.cdn.azure.cn/stable/3c4e3df9e89829dce27b7b5c24508306b151f30d/code_1.55.2-1618307277_amd64.deb) - -### 依赖包安装: - -``` -$ sudo apt install build-essential pkg-config git -$ sudo apt install gcc make libncurses5-dev openssl libssl-dev bison flex libelf-dev autoconf libtool gperf libc6-dev -``` - -**XiUOS操作系统源码下载:** XiUOS [https://www.gitlink.org.cn/xuos/xiuos](https://www.gitlink.org.cn/xuos/xiuos) - -新建一个空文件夹并进入文件夹中,并下载源码,具体命令如下: - -```c -mkdir test && cd test -git clone https://gitlink.org.cn/xuos/xiuos.git -``` - -1、打开XiUOS源码文件包可以看到以下目录: -| 名称 | 说明 | -| -- | -- | -| APP_Framework | 应用代码 | -| Ubiquitous | 板级支持包,支持NuttX、RT-Thread和XiZi内核 | - -2、打开XiZi内核源码文件包可以看到以下目录: -| 名称 | 说明 | -| -- | -- | -| arch | 架构代码 | -| board | 板级支持包 | -| fs | 文件系统 | -| kernel | 内核源码 | -| lib | 第三方库源码 | -| resources | 驱动文件 | -| tool | 系统工具 | - -使用VScode打开代码,具体操作步骤为:在源码文件夹下打开系统终端,输入`code .`即可打开VScode开发环境,如下图所示: - -

- -
- - -### 裁减配置工具的下载 - -裁减配置工具: - -**工具地址:** kconfig-frontends [https://www.gitlink.org.cn/xuos/kconfig-frontends](https://www.gitlink.org.cn/xuos/kconfig-frontends),下载与安装的具体命令如下: - -```c -mkdir kfrontends && cd kfrontends -git clone https://gitlink.org.cn/xuos/kconfig-frontends.git -``` - -下载源码后按以下步骤执行软件安装: - -```c -cd kconfig-frontends -./xs_build.sh -``` - -### 编译工具链: - -ARM: arm-none-eabi(`gcc version 6.3.1`),默认安装到Ubuntu的/usr/bin/arm-none-eabi-,使用如下命令行下载和安装。 - -```shell -$ sudo apt install gcc-arm-none-eabi -``` - - - -## 3. 编译说明 - -### 编辑环境:`Ubuntu18.04` - -### 编译工具链:`arm-none-eabi-gcc` - -使用`VScode`打开工程的方法有多种,本文介绍一种快捷键,在项目目录下将`code .`输入linux系统命令终端即可打开目标项目 - - -编译步骤: - -1.在VScode命令终端中执行以下命令,生成配置文件 - -```c -cd ./Ubiquitous/XiZi -make BOARD=cortex-m4-emulator distclean -make BOARD=cortex-m4-emulator menuconfig -``` - -2.在menuconfig界面配置需要关闭和开启的功能,按回车键进入下级菜单,按Y键选中需要开启的功能,按N键选中需要关闭的功能,配置结束后保存并退出(本例旨在演示简单的输出例程,所以没有需要配置的选项,双击快捷键ESC退出配置) - -
- -
- -退出时选择`yes`保存上面所配置的内容,如下图所示: - -
- -
- -3.继续执行以下命令,进行编译 - -``` -make BOARD=cortex-m4-emulator -``` - -4.如果编译正确无误,会产生XiZi-cortex-m4-emulator.elf、XiZi-cortex-m4-emulator.bin文件。 - - - -## 4. 运行 - -### 4.1 安装Q‎EMU - -``` -sudo apt install qemu-system-arm -``` - -### 4.2 运行结果 - -通过以下命令启动Q‎EMU并加载XiUOS ELF文件 - -``` -qemu-system-arm -machine netduinoplus2 -nographic -kernel build/XiZi-cortex-m4-emulator.elf -``` - -QEMU运行起来后将会在终端上看到信息打印输出 - -
- -
- -### 4.3 调试 - -通过Q‎EMU可以方便的对XiUOS进行调试,首先安装gdb调试工具 - -``` -sudo apt install gdb-multiarch -``` - -并通过以下命令启动Q‎EMU - -``` -qemu-system-arm -machine netduinoplus2 -nographic -kernel build/XiZi-cortex-m4-emulator.elf -s -S -``` - -然后要重新开启另一个linux系统终端一个终端,执行`riscv-none-embed-gdb`命令 - -``` -gdb-multiarch build/XiZi-cortex-m4-emulator.elf -ex "target remote localhost:1234" -``` \ No newline at end of file From 6a2a006a3fd6973282a7f039386aa66b24a03160 Mon Sep 17 00:00:00 2001 From: xxq250 Date: Wed, 9 Aug 2023 11:03:52 +0800 Subject: [PATCH 22/33] revert 87a57d0db0a6b54cea5b6d1ae0cca3d817ec5b77 revert Delete 'Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md' --- .../board/cortex-m4-emulator/README.md | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md diff --git a/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md b/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md new file mode 100644 index 000000000..17f1ca1f7 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md @@ -0,0 +1,208 @@ +# 从零开始构建矽璓工业物联操作系统:使用ARM架构的cortex-m4 emulator + +[XiUOS](http://xuos.io/) (X Industrial Ubiquitous Operating System) 矽璓XiUOS是一款面向智慧车间的工业物联网操作系统,主要由一个极简的微型实时操作系统内核和其上的工业物联框架构成,通过高效管理工业物联网设备、支撑工业物联应用,在生产车间内实现智能化的“感知环境、联网传输、知悉识别、控制调整”,促进以工业设备和工业控制系统为核心的人、机、物深度互联,帮助提升生产线的数字化和智能化水平。 + + + +## 1. 简介 + +Q‎EMU 是一个通用的开源模拟器和虚拟化工具。目前Q‎EMU已经可以较完整的支持ARM cortex-m4架构。XiUOS同样支持运行在Q‎EMU上 + +| 硬件 | 描述 | +| -------- | ------------- | +| 芯片型号 | netduinoplus2 | +| 架构 | cortex-m4 | +| 主频 | 168MHz | +| 片内SRAM | 100+KB | +| 外设支持 | UART、GPIO | + + + +## 2. 开发环境搭建 + +### 推荐使用: + +**操作系统:** ubuntu18.04 [https://ubuntu.com/download/desktop](https://ubuntu.com/download/desktop) + +更新`ubuntu 18.04`源的方法:(根据自身情况而定,可以不更改) + +第一步:打开sources.list文件 + +```c +sudo vim /etc/apt/sources.list +``` + +第二步:将以下内容复制到sources.list文件 + +```c +deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse +deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse +deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse +deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse +deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse +deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse +``` + +第三步:更新源和系统软件 + +```c +sudo apt-get update +sudo apt-get upgrade +``` + +**开发工具推荐使用 VSCode ,VScode下载地址为:** VSCode [https://code.visualstudio.com/](https://code.visualstudio.com/),推荐下载地址为 [http://vscode.cdn.azure.cn/stable/3c4e3df9e89829dce27b7b5c24508306b151f30d/code_1.55.2-1618307277_amd64.deb](http://vscode.cdn.azure.cn/stable/3c4e3df9e89829dce27b7b5c24508306b151f30d/code_1.55.2-1618307277_amd64.deb) + +### 依赖包安装: + +``` +$ sudo apt install build-essential pkg-config git +$ sudo apt install gcc make libncurses5-dev openssl libssl-dev bison flex libelf-dev autoconf libtool gperf libc6-dev +``` + +**XiUOS操作系统源码下载:** XiUOS [https://www.gitlink.org.cn/xuos/xiuos](https://www.gitlink.org.cn/xuos/xiuos) + +新建一个空文件夹并进入文件夹中,并下载源码,具体命令如下: + +```c +mkdir test && cd test +git clone https://gitlink.org.cn/xuos/xiuos.git +``` + +1、打开XiUOS源码文件包可以看到以下目录: +| 名称 | 说明 | +| -- | -- | +| APP_Framework | 应用代码 | +| Ubiquitous | 板级支持包,支持NuttX、RT-Thread和XiZi内核 | + +2、打开XiZi内核源码文件包可以看到以下目录: +| 名称 | 说明 | +| -- | -- | +| arch | 架构代码 | +| board | 板级支持包 | +| fs | 文件系统 | +| kernel | 内核源码 | +| lib | 第三方库源码 | +| resources | 驱动文件 | +| tool | 系统工具 | + +使用VScode打开代码,具体操作步骤为:在源码文件夹下打开系统终端,输入`code .`即可打开VScode开发环境,如下图所示: + +
+ +
+ + +### 裁减配置工具的下载 + +裁减配置工具: + +**工具地址:** kconfig-frontends [https://www.gitlink.org.cn/xuos/kconfig-frontends](https://www.gitlink.org.cn/xuos/kconfig-frontends),下载与安装的具体命令如下: + +```c +mkdir kfrontends && cd kfrontends +git clone https://gitlink.org.cn/xuos/kconfig-frontends.git +``` + +下载源码后按以下步骤执行软件安装: + +```c +cd kconfig-frontends +./xs_build.sh +``` + +### 编译工具链: + +ARM: arm-none-eabi(`gcc version 6.3.1`),默认安装到Ubuntu的/usr/bin/arm-none-eabi-,使用如下命令行下载和安装。 + +```shell +$ sudo apt install gcc-arm-none-eabi +``` + + + +## 3. 编译说明 + +### 编辑环境:`Ubuntu18.04` + +### 编译工具链:`arm-none-eabi-gcc` + +使用`VScode`打开工程的方法有多种,本文介绍一种快捷键,在项目目录下将`code .`输入linux系统命令终端即可打开目标项目 + + +编译步骤: + +1.在VScode命令终端中执行以下命令,生成配置文件 + +```c +cd ./Ubiquitous/XiZi +make BOARD=cortex-m4-emulator distclean +make BOARD=cortex-m4-emulator menuconfig +``` + +2.在menuconfig界面配置需要关闭和开启的功能,按回车键进入下级菜单,按Y键选中需要开启的功能,按N键选中需要关闭的功能,配置结束后保存并退出(本例旨在演示简单的输出例程,所以没有需要配置的选项,双击快捷键ESC退出配置) + +
+ +
+ +退出时选择`yes`保存上面所配置的内容,如下图所示: + +
+ +
+ +3.继续执行以下命令,进行编译 + +``` +make BOARD=cortex-m4-emulator +``` + +4.如果编译正确无误,会产生XiZi-cortex-m4-emulator.elf、XiZi-cortex-m4-emulator.bin文件。 + + + +## 4. 运行 + +### 4.1 安装Q‎EMU + +``` +sudo apt install qemu-system-arm +``` + +### 4.2 运行结果 + +通过以下命令启动Q‎EMU并加载XiUOS ELF文件 + +``` +qemu-system-arm -machine netduinoplus2 -nographic -kernel build/XiZi-cortex-m4-emulator.elf +``` + +QEMU运行起来后将会在终端上看到信息打印输出 + +
+ +
+ +### 4.3 调试 + +通过Q‎EMU可以方便的对XiUOS进行调试,首先安装gdb调试工具 + +``` +sudo apt install gdb-multiarch +``` + +并通过以下命令启动Q‎EMU + +``` +qemu-system-arm -machine netduinoplus2 -nographic -kernel build/XiZi-cortex-m4-emulator.elf -s -S +``` + +然后要重新开启另一个linux系统终端一个终端,执行`riscv-none-embed-gdb`命令 + +``` +gdb-multiarch build/XiZi-cortex-m4-emulator.elf -ex "target remote localhost:1234" +``` \ No newline at end of file From d6e7637128a5fbb10a25ebe7a110a1b6c733401e Mon Sep 17 00:00:00 2001 From: xxq250 Date: Wed, 9 Aug 2023 11:09:00 +0800 Subject: [PATCH 23/33] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20'Ubiquitous/XiZi=5FI?= =?UTF-8?q?IoT/board/cortex-m4-emulator/README.md'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md b/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md index 17f1ca1f7..c97db99d1 100644 --- a/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md +++ b/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md @@ -92,7 +92,7 @@ git clone https://gitlink.org.cn/xuos/xiuos.git 使用VScode打开代码,具体操作步骤为:在源码文件夹下打开系统终端,输入`code .`即可打开VScode开发环境,如下图所示:
- +
From 55101230984a23dc46ff5b5b4ad20d0af41d551d Mon Sep 17 00:00:00 2001 From: xxq250 Date: Wed, 9 Aug 2023 11:09:53 +0800 Subject: [PATCH 24/33] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20'Ubiquitous/XiZi=5FI?= =?UTF-8?q?IoT/board/cortex-m4-emulator/README.md'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md b/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md index c97db99d1..b6c7d0d51 100644 --- a/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md +++ b/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md @@ -92,7 +92,7 @@ git clone https://gitlink.org.cn/xuos/xiuos.git 使用VScode打开代码,具体操作步骤为:在源码文件夹下打开系统终端,输入`code .`即可打开VScode开发环境,如下图所示:
- +
From 8c1ad40f8197e426a6b7d2f1e71d380476ce502d Mon Sep 17 00:00:00 2001 From: xxq250 Date: Wed, 9 Aug 2023 11:13:42 +0800 Subject: [PATCH 25/33] Update README.md --- Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md b/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md index b6c7d0d51..b5b89263b 100644 --- a/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md +++ b/Ubiquitous/XiZi_IIoT/board/cortex-m4-emulator/README.md @@ -146,13 +146,13 @@ make BOARD=cortex-m4-emulator menuconfig 2.在menuconfig界面配置需要关闭和开启的功能,按回车键进入下级菜单,按Y键选中需要开启的功能,按N键选中需要关闭的功能,配置结束后保存并退出(本例旨在演示简单的输出例程,所以没有需要配置的选项,双击快捷键ESC退出配置)
- +
退出时选择`yes`保存上面所配置的内容,如下图所示:
- +
3.继续执行以下命令,进行编译 @@ -184,7 +184,7 @@ qemu-system-arm -machine netduinoplus2 -nographic -kernel build/XiZi-cortex-m4- QEMU运行起来后将会在终端上看到信息打印输出
- +
### 4.3 调试 From 5f642507b4dc8e76a6d38b5a572d25ece1bc1ed1 Mon Sep 17 00:00:00 2001 From: Liu_Weichao Date: Wed, 9 Aug 2023 15:10:11 +0800 Subject: [PATCH 26/33] fix adc/dac funciton error on edu-arm32 baord --- .../Applications/app_test/test_adc.c | 17 +- .../Applications/app_test/test_dac.c | 31 +- .../Applications/app_test/test_hwtimer.c | 23 +- .../edu-arm32/third_party_driver/adc/Kconfig | 83 +--- .../third_party_driver/adc/connect_adc.c | 377 +++++++++++------- .../common/hc32_ll_driver/src/Makefile | 2 +- .../third_party_driver/dac/connect_dac.c | 299 ++++++++++++-- .../third_party_driver/include/connect_dac.h | 7 - 8 files changed, 546 insertions(+), 293 deletions(-) diff --git a/APP_Framework/Applications/app_test/test_adc.c b/APP_Framework/Applications/app_test/test_adc.c index ceceec161..c1b8f66d3 100644 --- a/APP_Framework/Applications/app_test/test_adc.c +++ b/APP_Framework/Applications/app_test/test_adc.c @@ -26,9 +26,8 @@ void TestAdc(void) { int adc_fd; - uint8 adc_channel = 0x0; - uint16 adc_sample, adc_value_decimal = 0; - float adc_value; + uint8 adc_channel = 0x1; + uint16 adc_sample = 0; adc_fd = PrivOpen(ADC_DEV_DRIVER, O_RDWR); if (adc_fd < 0) { @@ -45,13 +44,11 @@ void TestAdc(void) return; } - PrivRead(adc_fd, &adc_sample, 2); - - adc_value = (float)adc_sample * (3.3 / 4096); - - adc_value_decimal = (adc_value - (uint16)adc_value) * 1000; - - printf("adc sample %u value integer %u decimal %u\n", adc_sample, (uint16)adc_value, adc_value_decimal); + for (int i = 0; i < 10; i ++) { + PrivRead(adc_fd, &adc_sample, 2); + printf("adc sample %u mv\n", adc_sample); + PrivTaskDelay(500); + } PrivClose(adc_fd); diff --git a/APP_Framework/Applications/app_test/test_dac.c b/APP_Framework/Applications/app_test/test_dac.c index 37ac2bf1c..8628d1a00 100644 --- a/APP_Framework/Applications/app_test/test_dac.c +++ b/APP_Framework/Applications/app_test/test_dac.c @@ -22,17 +22,16 @@ #include #ifdef ADD_XIZI_FEATURES -void TestDac(void) +static pthread_t test_dac_task; + +static void *TestDacTask(void *parameter) { int dac_fd; - uint16 dac_set_value = 800; - uint16 dac_sample, dac_value_decimal = 0; - float dac_value; + uint16 dac_set_value = 4096 * 10;//sin length dac_fd = PrivOpen(DAC_DEV_DRIVER, O_RDWR); if (dac_fd < 0) { KPrintf("open dac fd error %d\n", dac_fd); - return; } struct PrivIoctlCfg ioctl_cfg; @@ -41,20 +40,24 @@ void TestDac(void) if (0 != PrivIoctl(dac_fd, OPE_CFG, &ioctl_cfg)) { KPrintf("ioctl dac fd error %d\n", dac_fd); PrivClose(dac_fd); - return; } - PrivRead(dac_fd, &dac_sample, 2); - - dac_value = (float)dac_sample * (3.3 / 4096);//Vref+ need to be 3.3V - - dac_value_decimal = (dac_value - (uint16)dac_value) * 1000; - - printf("dac sample %u value integer %u decimal %u\n", dac_sample, (uint16)dac_value, dac_value_decimal); + while (1) { + //start dac output sin + PrivWrite(dac_fd, NULL, 0); + } PrivClose(dac_fd); +} - return; +void TestDac(void) +{ + pthread_attr_t tid; + tid.schedparam.sched_priority = 20; + tid.stacksize = 4096; + + PrivTaskCreate(&test_dac_task, &tid, &TestDacTask, NULL); + PrivTaskStartup(&test_dac_task); } PRIV_SHELL_CMD_FUNCTION(TestDac, a dac test sample, PRIV_SHELL_CMD_MAIN_ATTR); #endif \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_hwtimer.c b/APP_Framework/Applications/app_test/test_hwtimer.c index 9e6f7f981..9438683f8 100644 --- a/APP_Framework/Applications/app_test/test_hwtimer.c +++ b/APP_Framework/Applications/app_test/test_hwtimer.c @@ -28,11 +28,11 @@ static uint16_t pin_fd=0; static struct PinStat pin_led; -void ledflip(void *parameter) +void LedFlip(void *parameter) { pin_led.pin = BSP_LED_PIN; pin_led.val = !pin_led.val; - PrivWrite(pin_fd,&pin_led,NULL_PARAMETER); + PrivWrite(pin_fd, &pin_led, NULL_PARAMETER); } void TestHwTimer(void) @@ -40,22 +40,22 @@ void TestHwTimer(void) x_ticks_t period = 100000; pin_fd = PrivOpen(HWTIMER_PIN_DEV_DRIVER, O_RDWR); - if(pin_fd<0){ + if(pin_fd<0) { printf("open pin fd error:%d\n",pin_fd); return; } int timer_fd = PrivOpen(HWTIMER_TIMER_DEV_DRIVER, O_RDWR); - if(timer_fd<0){ + if(timer_fd<0) { printf("open timer fd error:%d\n",timer_fd); return; } //config led pin in board - struct PinParam parameter; - parameter.cmd = GPIO_CONFIG_MODE; - parameter.pin = BSP_LED_PIN; - parameter.mode = GPIO_CFG_OUTPUT; + struct PinParam parameter; + parameter.cmd = GPIO_CONFIG_MODE; + parameter.pin = BSP_LED_PIN; + parameter.mode = GPIO_CFG_OUTPUT; struct PrivIoctlCfg ioctl_cfg; ioctl_cfg.ioctl_driver_type = PIN_TYPE; @@ -68,7 +68,7 @@ void TestHwTimer(void) } ioctl_cfg.ioctl_driver_type = TIME_TYPE; - ioctl_cfg.args = (void *)&ledflip; + ioctl_cfg.args = (void *)&LedFlip; if (0 != PrivIoctl(timer_fd, OPE_INT, &ioctl_cfg)) { printf("timer pin fd error %d\n", pin_fd); PrivClose(pin_fd); @@ -82,13 +82,10 @@ void TestHwTimer(void) return; } - while(1){ + while(1) { } - // int32 timer_handle = KCreateTimer("LED on and off by 1s",&ledflip,&pin_fd,period,TIMER_TRIGGER_PERIODIC); - - // KTimerStartRun(timer_handle); PrivClose(pin_fd); PrivClose(timer_fd); } diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/adc/Kconfig b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/adc/Kconfig index f721032c6..fdc0b1c91 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/adc/Kconfig +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/adc/Kconfig @@ -1,74 +1,13 @@ -menuconfig BSP_USING_ADC1 - bool "Enable ADC1" - default y - if BSP_USING_ADC1 - config ADC1_BUS_NAME - string "adc 1 bus name" - default "adc1" +if BSP_USING_ADC + config ADC1_BUS_NAME + string "adc 1 bus name" + default "adc1" - config ADC1_DRIVER_NAME - string "adc 1 driver name" - default "adc1_drv" + config ADC1_DRIVER_NAME + string "adc 1 driver name" + default "adc1_drv" - config ADC1_DEVICE_NAME - string "adc 1 bus device name" - default "adc1_dev" - - config ADC1_GPIO_NUM - int "adc 1 gpio pin num" - default "0" - - config ADC1_GPIO_DEF - string "adc 1 gpio define type" - default "A" - endif - -menuconfig BSP_USING_ADC2 - bool "Enable ADC2" - default y - if BSP_USING_ADC2 - config ADC2_BUS_NAME - string "adc 2 bus name" - default "adc2" - - config ADC2_DRIVER_NAME - string "adc 2 driver name" - default "adc2_drv" - - config ADC2_DEVICE_NAME - string "adc 2 bus device name" - default "adc2_dev" - - config ADC2_GPIO_NUM - int "adc 2 gpio pin num" - default "6" - - config ADC2_GPIO_DEF - string "adc 2 gpio define type" - default "A" - endif - -menuconfig BSP_USING_ADC3 - bool "Enable ADC3" - default y - if BSP_USING_ADC3 - config ADC3_BUS_NAME - string "adc 3 bus name" - default "adc3" - - config ADC3_DRIVER_NAME - string "adc 3 driver name" - default "adc3_drv" - - config ADC3_DEVICE_NAME - string "adc 3 bus device name" - default "adc3_dev" - - config ADC3_GPIO_NUM - int "adc 3 gpio pin num" - default "0" - - config ADC3_GPIO_DEF - string "adc 3 gpio define type" - default "A" - endif + config ADC1_DEVICE_NAME + string "adc 1 bus device name" + default "adc1_dev" +endif diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/adc/connect_adc.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/adc/connect_adc.c index 620b21d8f..4e8b09475 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/adc/connect_adc.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/adc/connect_adc.c @@ -20,79 +20,251 @@ #include -#define _ADC_CONS(string1, string2) string1##string2 -#define ADC_CONS(string1, string2) _ADC_CONS(string1, string2) +/******************************************************************************* + * Local pre-processor symbols/macros ('#define') + ******************************************************************************/ -#ifdef BSP_USING_ADC1 -#define ADC1_GPIO ADC_CONS(GPIO_Pin_, ADC1_GPIO_NUM) +/* The clock source of ADC. */ +#define ADC_CLK_SYS_CLK (1U) +#define ADC_CLK_PLLH (2U) +#define ADC_CLK_PLLA (3U) + +/* + * Selects a clock source according to the application requirements. + * PCLK4 is the clock for digital interface. + * PCLK2 is the clock for analog circuit. + * PCLK4 and PCLK2 are synchronous when the clock source is PLL. + * PCLK4 : PCLK2 = 1:1, 2:1, 4:1, 8:1, 1:2, 1:4. + * PCLK2 is in range [1MHz, 60MHz]. + * If the system clock is selected as the ADC clock, macro 'ADC_ADC_CLK' can only be defined as 'CLK_PERIPHCLK_PCLK'. + * If PLLH is selected as the ADC clock, macro 'ADC_ADC_CLK' can be defined as 'CLK_PERIPHCLK_PLLx'(x=Q, R). + * If PLLA is selected as the ADC clock, macro 'ADC_ADC_CLK' can be defined as 'CLK_PERIPHCLK_PLLXx'(x=P, Q, R). + */ +#define ADC_CLK_SEL (ADC_CLK_SYS_CLK) + +#if (ADC_CLK_SEL == ADC_CLK_SYS_CLK) +#define ADC_CLK (CLK_PERIPHCLK_PCLK) + +#elif (ADC_CLK_SEL == ADC_CLK_PLLH) +#define ADC_CLK (CLK_PERIPHCLK_PLLQ) + +#elif (ADC_CLK_SEL == ADC_CLK_PLLA) +#define ADC_CLK (CLK_PERIPHCLK_PLLXP) + +#else +#error "The clock source your selected does not exist!!!" #endif -#ifdef BSP_USING_ADC2 -#define ADC2_GPIO ADC_CONS(GPIO_Pin_, ADC2_GPIO_NUM) -#endif +/* ADC unit instance for this example. */ +#define ADC_UNIT (CM_ADC1) +#define ADC_PERIPH_CLK (FCG3_PERIPH_ADC1) -#ifdef BSP_USING_ADC3 -#define ADC3_GPIO ADC_CONS(GPIO_Pin_, ADC3_GPIO_NUM) -#endif +/* Selects ADC channels that needed. */ +#define ADC_CH_POTENTIOMETER (ADC_CH3) +#define ADC_CH (ADC_CH_POTENTIOMETER) +#define ADC_CH_PORT (GPIO_PORT_A) +#define ADC_CH_PIN (GPIO_PIN_03) -static int AdcUdelay(uint32 us) +/* ADC sequence to be used. */ +#define ADC_SEQ (ADC_SEQ_A) +/* Flag of conversion end. */ +#define ADC_EOC_FLAG (ADC_FLAG_EOCA) + +/* ADC reference voltage. The voltage of pin VREFH. */ +#define ADC_VREF (3.3F) + +/* ADC accuracy(according to the resolution of ADC). */ +#define ADC_ACCURACY (1UL << 12U) + +/* Calculate the voltage(mV). */ +#define ADC_CAL_VOL(adcVal) (uint16_t)((((float32_t)(adcVal) * ADC_VREF) / ((float32_t)ADC_ACCURACY)) * 1000.F) + +/* Timeout value. */ +#define ADC_TIMEOUT_VAL (1000U) + +/** + * @brief Set specified ADC pin to analog mode. + * @param None + * @retval None + */ +static void AdcSetPinAnalogMode(void) { - uint32 ticks; - uint32 told, tnow, tcnt = 0; - uint32 reload = SysTick->LOAD; + stc_gpio_init_t stcGpioInit; - ticks = us * reload / (1000000 / TICK_PER_SECOND); - told = SysTick->VAL; - while (1) { - tnow = SysTick->VAL; - if (tnow != told) { - if (tnow < told) { - tcnt += told - tnow; - } else { - tcnt += reload - tnow + told; - } - told = tnow; - if (tcnt >= ticks) { - return 0; - break; - } - } - } + (void)GPIO_StructInit(&stcGpioInit); + stcGpioInit.u16PinAttr = PIN_ATTR_ANALOG; + (void)GPIO_Init(ADC_CH_PORT, ADC_CH_PIN, &stcGpioInit); } -static uint16 GetAdcAverageValue(CM_ADC_TypeDef *ADCx, uint8 channel, uint8 times) +/** + * @brief Configures ADC clock. + * @param None + * @retval None + */ +static void AdcClockConfig(void) { - uint32 temp_val = 0; - int i; +#if (ADC_CLK_SEL == ADC_CLK_SYS_CLK) + /* + * 1. Configures the clock divider of PCLK2 and PCLK4 here or in the function of configuring the system clock. + * In this example, the system clock is MRC@8MHz. + * PCLK4 is the digital interface clock, and PCLK2 is the analog circuit clock. + * Make sure that PCLK2 and PCLK4 meet the following conditions: + * PCLK4 : PCLK2 = 1:1, 2:1, 4:1, 8:1, 1:2, 1:4. + * PCLK2 is in range [1MHz, 60MHz]. + */ + CLK_SetClockDiv((CLK_BUS_PCLK2 | CLK_BUS_PCLK4), (CLK_PCLK2_DIV8 | CLK_PCLK4_DIV2)); - for(i = 0;i < times;i ++) { - temp_val += ADC_GetValue(ADCx, channel) & 0x0FFF; - KPrintf("GetAdcAverageValue val %u\n", ADC_GetValue(ADCx, channel)); - AdcUdelay(5000); - } - return temp_val / times; -} +#elif (ADC_CLK_SEL == ADC_CLK_PLLH) + /* + * 1. Configures PLLH and the divider of PLLHx(x=Q, R). + * PLLHx(x=Q, R) is used as both the digital interface clock and the analog circuit clock. + * PLLHx(x=Q, R) must be in range [1MHz, 60MHz] for ADC use. + * The input source of PLLH is XTAL(8MHz). + */ + stc_clock_pll_init_t stcPLLHInit; + stc_clock_xtal_init_t stcXtalInit; + /* Configures XTAL. PLLH input source is XTAL. */ + (void)CLK_XtalStructInit(&stcXtalInit); + stcXtalInit.u8State = CLK_XTAL_ON; + stcXtalInit.u8Drv = CLK_XTAL_DRV_ULOW; + stcXtalInit.u8Mode = CLK_XTAL_MD_OSC; + stcXtalInit.u8StableTime = CLK_XTAL_STB_499US; + (void)CLK_XtalInit(&stcXtalInit); + + (void)CLK_PLLStructInit(&stcPLLHInit); + /* + * PLLHx(x=Q, R) = ((PLL_source / PLLM) * PLLN) / PLLx + * PLLHQ = (8 / 1) * 80 /16 = 40MHz + * PLLHR = (8 / 1) * 80 /16 = 40MHz + */ + stcPLLHInit.u8PLLState = CLK_PLL_ON; + stcPLLHInit.PLLCFGR = 0UL; + stcPLLHInit.PLLCFGR_f.PLLM = (1UL - 1UL); + stcPLLHInit.PLLCFGR_f.PLLN = (80UL - 1UL); + stcPLLHInit.PLLCFGR_f.PLLP = (4UL - 1UL); + stcPLLHInit.PLLCFGR_f.PLLQ = (16UL - 1UL); + stcPLLHInit.PLLCFGR_f.PLLR = (16UL - 1UL); + /* stcPLLHInit.PLLCFGR_f.PLLSRC = CLK_PLL_SRC_XTAL; */ + (void)CLK_PLLInit(&stcPLLHInit); + +#elif (ADC_CLK_SEL == ADC_CLK_PLLA) + /* + * 1. Configures PLLA and the divider of PLLAx(x=P, Q, R). + * PLLAx(x=P, Q, R) is used as both the digital interface clock and the analog circuit clock. + * PLLAx(x=P, Q, R) must be in range [1MHz, 60MHz] for ADC use. + * The input source of PLLA is HRC(16MHz). + */ + stc_clock_pllx_init_t stcPLLAInit; + + /* Enable HRC(16MHz) for PLLA. */ + CLK_HrcCmd(ENABLE); + + /* Specify the input source of PLLA. NOTE!!! PLLA and PLLH use the same input source. */ + CLK_SetPLLSrc(CLK_PLL_SRC_HRC); + /* PLLA configuration */ + (void)CLK_PLLxStructInit(&stcPLLAInit); + /* + * PLLAx(x=P, Q, R) = ((PLL_source / PLLM) * PLLN) / PLLx + * PLLAP = (16 / 2) * 40 / 8 = 40MHz + * PLLAQ = (16 / 2) * 40 / 10 = 32MHz + * PLLAR = (16 / 2) * 40 / 16 = 20MHz + */ + stcPLLAInit.u8PLLState = CLK_PLLX_ON; + stcPLLAInit.PLLCFGR = 0UL; + stcPLLAInit.PLLCFGR_f.PLLM = (2UL - 1UL); + stcPLLAInit.PLLCFGR_f.PLLN = (40UL - 1UL); + stcPLLAInit.PLLCFGR_f.PLLR = (8UL - 1UL); + stcPLLAInit.PLLCFGR_f.PLLQ = (10UL - 1UL); + stcPLLAInit.PLLCFGR_f.PLLP = (16UL - 1UL); + (void)CLK_PLLxInit(&stcPLLAInit); +#endif + /* 2. Specifies the clock source of ADC. */ + CLK_SetPeriClockSrc(ADC_CLK); +} + +/** + * @brief Initializes ADC. + * @param None + * @retval None + */ +static void AdcInitConfig(void) +{ + stc_adc_init_t stcAdcInit; + + /* 1. Enable ADC peripheral clock. */ + FCG_Fcg3PeriphClockCmd(ADC_PERIPH_CLK, ENABLE); + + /* 2. Modify the default value depends on the application. Not needed here. */ + (void)ADC_StructInit(&stcAdcInit); + + /* 3. Initializes ADC. */ + (void)ADC_Init(ADC_UNIT, &stcAdcInit); + + /* 4. ADC channel configuration. */ + /* 4.1 Set the ADC pin to analog input mode. */ + AdcSetPinAnalogMode(); + /* 4.2 Enable ADC channels. Call ADC_ChCmd() again to enable more channels if needed. */ + ADC_ChCmd(ADC_UNIT, ADC_SEQ, ADC_CH, ENABLE); + + /* 5. Conversion data average calculation function, if needed. + Call ADC_ConvDataAverageChCmd() again to enable more average channels if needed. */ + ADC_ConvDataAverageConfig(ADC_UNIT, ADC_AVG_CNT8); + ADC_ConvDataAverageChCmd(ADC_UNIT, ADC_CH, ENABLE); +} + +/** + * @brief Use ADC in polling mode. + * @param None + * @retval uint16_t u16AdcValue + */ +static uint16_t AdcPolling(void) +{ + uint16_t u16AdcValue = 0; + int32_t iRet = LL_ERR; + __IO uint32_t u32TimeCount = 0UL; + + /* Can ONLY start sequence A conversion. + Sequence B needs hardware trigger to start conversion. */ + ADC_Start(ADC_UNIT); + do { + if (ADC_GetStatus(ADC_UNIT, ADC_EOC_FLAG) == SET) { + ADC_ClearStatus(ADC_UNIT, ADC_EOC_FLAG); + iRet = LL_OK; + break; + } + } while (u32TimeCount++ < ADC_TIMEOUT_VAL); + + if (iRet == LL_OK) { + /* Get any ADC value of sequence A channel that needed. */ + u16AdcValue = ADC_GetValue(ADC_UNIT, ADC_CH); + KPrintf("The ADC value of potentiometer is %u, voltage is %u mV\r\n", + u16AdcValue, ADC_CAL_VOL(u16AdcValue)); + } else { + ADC_Stop(ADC_UNIT); + KPrintf("ADC exception.\r\n"); + } + + return ADC_CAL_VOL(u16AdcValue); +} static uint32 AdcOpen(void *dev) { x_err_t ret = EOK; - stc_adc_init_t stcAdcInit; - ADC_StructInit(&stcAdcInit); struct AdcHardwareDevice* adc_dev = (struct AdcHardwareDevice*)dev; - CM_ADC_TypeDef *ADCx= (CM_ADC_TypeDef *)adc_dev->private_data; - ADC_Init((ADCx),&stcAdcInit); + + AdcClockConfig(); + AdcInitConfig(); + return ret; } - static uint32 AdcClose(void *dev) { - // CM_ADC_TypeDef *adc_dev = (CM_ADC_TypeDef*)dev; struct AdcHardwareDevice* adc_dev = (struct AdcHardwareDevice*)dev; - CM_ADC_TypeDef *ADCx= (CM_ADC_TypeDef *)adc_dev->private_data; - + + ADC_Stop(ADC_UNIT); ADC_DeInit(ADCx); return EOK; @@ -100,19 +272,10 @@ static uint32 AdcClose(void *dev) static uint32 AdcRead(void *dev, struct BusBlockReadParam *read_param) { - struct AdcHardwareDevice *adc_dev = (struct AdcHardwareDevice *)dev; + *(uint16 *)read_param->buffer = AdcPolling(); + read_param->read_length = 2; - struct HwAdc *adc_cfg = (struct HwAdc *)adc_dev->haldev.private_data; - - uint16 adc_average_value = 0; - uint8 times = 20; - - adc_average_value = GetAdcAverageValue(adc_cfg->ADCx, adc_cfg->adc_channel, times); - - *(uint16 *)read_param->buffer = adc_average_value; - read_param->read_length = 2; - - return read_param->read_length; + return EOK; } static uint32 AdcDrvConfigure(void *drv, struct BusConfigureInfo *configure_info) @@ -131,9 +294,9 @@ static uint32 AdcDrvConfigure(void *drv, struct BusConfigureInfo *configure_info { case OPE_CFG: adc_cfg->adc_channel = *(uint8 *)configure_info->private_data; - if (adc_cfg->adc_channel > 18) { - KPrintf("AdcDrvConfigure set adc channel(0-18) %u error!", adc_cfg->adc_channel); - adc_cfg->adc_channel = 0; + if (adc_cfg->adc_channel != 1) { + KPrintf("AdcDrvConfigure set adc channel(1) %u error!", adc_cfg->adc_channel); + adc_cfg->adc_channel = 1; ret = ERROR; } break; @@ -156,7 +319,7 @@ int HwAdcInit(void) { x_err_t ret = EOK; -#ifdef BSP_USING_ADC1 +#ifdef BSP_USING_ADC static struct AdcBus adc1_bus; static struct AdcDriver adc1_drv; static struct AdcHardwareDevice adc1_dev; @@ -183,7 +346,7 @@ int HwAdcInit(void) adc1_dev.adc_dev_done = &dev_done; adc1_cfg.ADCx = CM_ADC1; - adc1_cfg.adc_channel = 0; + adc1_cfg.adc_channel = 1; ret = AdcDeviceRegister(&adc1_dev, (void *)&adc1_cfg, ADC1_DEVICE_NAME); if (ret != EOK) { @@ -197,88 +360,6 @@ int HwAdcInit(void) } #endif -#ifdef BSP_USING_ADC2 - static struct AdcBus adc2_bus; - static struct AdcDriver adc2_drv; - static struct AdcHardwareDevice adc2_dev; - static struct HwAdc adc2_cfg; - - adc2_drv.configure = AdcDrvConfigure; - - ret = AdcBusInit(&adc2_bus, ADC2_BUS_NAME); - if (ret != EOK) { - KPrintf("ADC2 bus init error %d\n", ret); - return ERROR; - } - - ret = AdcDriverInit(&adc2_drv, ADC2_DRIVER_NAME); - if (ret != EOK) { - KPrintf("ADC2 driver init error %d\n", ret); - return ERROR; - } - ret = AdcDriverAttachToBus(ADC2_DRIVER_NAME, ADC2_BUS_NAME); - if (ret != EOK) { - KPrintf("ADC2 driver attach error %d\n", ret); - return ERROR; - } - - adc2_dev.adc_dev_done = &dev_done; - adc2_cfg.ADCx = CM_ADC2; - adc2_cfg.adc_channel = 0; - - ret = AdcDeviceRegister(&adc2_dev, (void *)&adc2_cfg, ADC2_DEVICE_NAME); - if (ret != EOK) { - KPrintf("ADC2 device register error %d\n", ret); - return ERROR; - } - ret = AdcDeviceAttachToBus(ADC2_DEVICE_NAME, ADC2_BUS_NAME); - if (ret != EOK) { - KPrintf("ADC2 device register error %d\n", ret); - return ERROR; - } -#endif - -#ifdef BSP_USING_ADC3 - static struct AdcBus adc3_bus; - static struct AdcDriver adc3_drv; - static struct AdcHardwareDevice adc3_dev; - static struct HwAdc adc3_cfg; - - adc3_drv.configure = AdcDrvConfigure; - - ret = AdcBusInit(&adc3_bus, ADC3_BUS_NAME); - if (ret != EOK) { - KPrintf("ADC3 bus init error %d\n", ret); - return ERROR; - } - - ret = AdcDriverInit(&adc3_drv, ADC3_DRIVER_NAME); - if (ret != EOK) { - KPrintf("ADC3 driver init error %d\n", ret); - return ERROR; - } - ret = AdcDriverAttachToBus(ADC3_DRIVER_NAME, ADC3_BUS_NAME); - if (ret != EOK) { - KPrintf("ADC3 driver attach error %d\n", ret); - return ERROR; - } - - adc3_dev.adc_dev_done = &dev_done; - adc3_cfg.ADCx = CM_ADC3; - adc3_cfg.adc_channel = 0; - - ret = AdcDeviceRegister(&adc3_dev, (void *)&adc3_cfg, ADC3_DEVICE_NAME); - if (ret != EOK) { - KPrintf("ADC3 device register error %d\n", ret); - return ERROR; - } - ret = AdcDeviceAttachToBus(ADC3_DEVICE_NAME, ADC3_BUS_NAME); - if (ret != EOK) { - KPrintf("ADC3 device register error %d\n", ret); - return ERROR; - } -#endif - return ret; } diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/common/hc32_ll_driver/src/Makefile b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/common/hc32_ll_driver/src/Makefile index 501d182d5..7254a2cb6 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/common/hc32_ll_driver/src/Makefile +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/common/hc32_ll_driver/src/Makefile @@ -9,7 +9,7 @@ ifeq ($(CONFIG_BSP_USING_ADC),y) endif ifeq ($(CONFIG_BSP_USING_DAC),y) - SRC_FILES += hc32_ll_dac.c + SRC_FILES += hc32_ll_dac.c hc32_ll_mau.c endif ifeq ($(CONFIG_BSP_USING_SDIO),y) diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/dac/connect_dac.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/dac/connect_dac.c index 3cbd220c3..efcc80743 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/dac/connect_dac.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/dac/connect_dac.c @@ -20,56 +20,301 @@ #include -#define _DAC_CONS(string1, string2) string1##string2 -#define DAC_CONS(string1, string2) _DAC_CONS(string1, string2) +/******************************************************************************* + * Local pre-processor symbols/macros ('#define') + ******************************************************************************/ +#define DAC_UNIT1_PORT (GPIO_PORT_A) +#define DAC_UNIT1_CHN1_PIN (GPIO_PIN_04) -#ifdef BSP_USING_DAC -#define DAC_GPIO DAC_CONS(GPIO_Pin_, DAC_GPIO_NUM) +#define VREFH (3.3F) +#define DAC_CHN1 (0U) +#define DAC_CHN2 (1U) +#define DAC_DATA_ALIGN_12b_R (0U) +#define DAC_DATA_ALIGN_12b_L (1U) + +#define SUPPORT_AMP +#define SUPPORT_ADP +#define SINGLE_WAVE_DAC_CHN (DAC_CHN1) +#define DAC_DATA_ALIGN (DAC_DATA_ALIGN_12b_L) + +#define SINE_DOT_NUMBER (4096U) +#define SINE_NEGATIVE_TO_POSITVE (1.0F) + +/******************************************************************************* + * Local type definitions ('typedef') + ******************************************************************************/ +typedef enum { + DAC_Unit1, + DAC_Unit2, + DAC_Unit_Max, +}en_dac_unit_t; + +typedef enum { + E_Dac_Single, + E_Dac_Dual, +}en_dac_cvt_t; + +typedef struct { + CM_DAC_TypeDef *pUnit; + en_dac_cvt_t enCvtType; + uint16_t u16Ch; +} stc_dac_handle_t; + +/******************************************************************************* + * Local variable definitions ('static') + ******************************************************************************/ +static stc_dac_handle_t m_stcDACHandle[DAC_Unit_Max] = {0}; +static uint32_t gu32SinTable[SINE_DOT_NUMBER]; +static stc_dac_handle_t *pSingleDac; + +/******************************************************************************* + * Function implementation - global ('extern') and local ('static') + ******************************************************************************/ +/** + * @brief MAU Initialization + * @param None + * @retval None + */ +static void MauInit(void) +{ + /* Enable MAU peripheral clock. */ + FCG_Fcg0PeriphClockCmd(PWC_FCG0_MAU, ENABLE); +} + +/** + * @brief MAU De-Initialization + * @param None + * @retval None + */ +static void MauDeinit(void) +{ + /* Enable MAU peripheral clock. */ + FCG_Fcg0PeriphClockCmd(PWC_FCG0_MAU, DISABLE); +} + +/** + * @brief Sin table Initialization + * @param [in] pSinTable sin table + * @param [in] u32count number of pSinTable items + * @retval None + */ +static void SinTableInit(uint32_t pSinTable[], uint32_t u32count) +{ + uint32_t i; + uint32_t u32AngAvg = (uint32_t)(float32_t)((float32_t)((float32_t)MAU_SIN_ANGIDX_TOTAL / (float32_t)u32count) + 0.5); + float32_t fSin; + for (i = 0U; i < u32count; i++) { + fSin = (((float32_t)MAU_Sin(CM_MAU, (uint16_t)(u32AngAvg * i)) + / (float32_t)MAU_SIN_Q15_SCALAR + SINE_NEGATIVE_TO_POSITVE) / VREFH) * + (float32_t)DAC_DATAREG_VALUE_MAX + 0.5F; + +#if (DAC_DATA_ALIGN == DAC_DATA_ALIGN_12b_L) + { + pSinTable[i] = (uint32_t)fSin << 4; + } +#else + { + pSinTable[i] = (uint32_t)fSin; + } #endif - + } +} + +/** + * @brief Enable DAC peripheral clock + * @param [in] enUnit The selected DAC unit + * @retval None + */ +static void DacPClkEnable(en_dac_unit_t enUnit) +{ + uint32_t u32PClk; + switch (enUnit) { + case DAC_Unit1: + u32PClk = PWC_FCG3_DAC1; + break; + case DAC_Unit2: + u32PClk = PWC_FCG3_DAC2; + break; + default: + u32PClk = PWC_FCG3_DAC1 | PWC_FCG3_DAC2; + break; + } + /* Enable DAC peripheral clock. */ + FCG_Fcg3PeriphClockCmd(u32PClk, ENABLE); +} + +/** + * @brief Init DAC single channel + * @param [in] enUnit The selected DAC unit + * @retval A pointer of DAC handler + */ +static stc_dac_handle_t *DacSingleConversionInit(en_dac_unit_t enUnit) +{ + uint8_t u8Port; + uint16_t u16Pin; + stc_dac_handle_t *pDac; + + if (enUnit == DAC_Unit1) { + pDac = &m_stcDACHandle[DAC_Unit1]; + pDac->pUnit = CM_DAC1; + } else { + pDac = &m_stcDACHandle[DAC_Unit2]; + pDac->pUnit = CM_DAC2; + } + DacPClkEnable(enUnit); + + pDac->enCvtType = E_Dac_Single; +#if (SINGLE_WAVE_DAC_CHN == DAC_CHN1) + pDac->u16Ch = DAC_CH1; +#else + pDac->u16Ch = DAC_CH2; +#endif + + /* Init DAC by default value: source from data register and output enabled*/ + DAC_DeInit(pDac->pUnit); + stc_dac_init_t stInit; + (void)DAC_StructInit(&stInit); + (void)DAC_Init(pDac->pUnit, pDac->u16Ch, &stInit); +#if (DAC_DATA_ALIGN == DAC_DATA_ALIGN_12b_L) + DAC_DataRegAlignConfig(pDac->pUnit, DAC_DATA_ALIGN_L); +#else + DAC_DataRegAlignConfig(pDac->pUnit, DAC_DATA_ALIGN_R); +#endif + + /* Set DAC pin attribute to analog */ + if (enUnit == DAC_Unit1) { + u8Port = DAC_UNIT1_PORT; +#if (SINGLE_WAVE_DAC_CHN == DAC_CHN1) + u16Pin = DAC_UNIT1_CHN1_PIN; +#endif + } + stc_gpio_init_t stcGpioInit; + (void)GPIO_StructInit(&stcGpioInit); + stcGpioInit.u16PinAttr = PIN_ATTR_ANALOG; + (void)GPIO_Init(u8Port, u16Pin, &stcGpioInit); + +#ifdef SUPPORT_ADP + /* Set ADC first */ + /* Enable ADC peripheral clock. */ + FCG_Fcg3PeriphClockCmd(PWC_FCG3_ADC1 | PWC_FCG3_ADC2 | PWC_FCG3_ADC3, ENABLE); + if (CM_ADC1->STR == 0U) { + if (CM_ADC2->STR == 0U) { + if (CM_ADC3->STR == 0U) { + DAC_ADCPrioConfig(pDac->pUnit, DAC_ADP_SELECT_ALL, ENABLE); + DAC_ADCPrioCmd(pDac->pUnit, ENABLE); + } + } + } +#endif + return pDac; +} + +/** + * @brief Start single DAC conversions + * @param [in] pDac A pointer of DAC handler + * @retval None + */ +static void DacStartSingleConversion(const stc_dac_handle_t *pDac) +{ + /* Enalbe AMP */ +#ifdef SUPPORT_AMP + (void)DAC_AMPCmd(pDac->pUnit, pDac->u16Ch, ENABLE); +#endif + + (void)DAC_Start(pDac->pUnit, pDac->u16Ch); + +#ifdef SUPPORT_AMP + /* delay 3us before setting data*/ + DDL_DelayMS(1U); +#endif +} + +/** + * @brief Convert data by single DAC channel + * @param [in] pDac A pointer of DAC handler + * @param [in] pDataTable The data table to be converted + * @param [in] u32count Number of data table items + * @retval None + */ +__STATIC_INLINE void DacSetSingleConversionData(const stc_dac_handle_t *pDac, uint32_t const pDataTable[], uint32_t u32count) +{ + uint32_t i = 0U; + + for (i = 0U; i < u32count; i++) { +#ifdef SUPPORT_ADP + uint32_t u32TryCount = 100U; + while (u32TryCount != 0U) { + u32TryCount--; + if (SET != DAC_GetChConvertState(pDac->pUnit, pDac->u16Ch)) { + break; + } + } +#endif + DAC_SetChData(pDac->pUnit, pDac->u16Ch, (uint16_t)pDataTable[i]); + } +} + +/** + * @brief stop DAC conversion + * @param [in] pDac A pointer of DAC handler + * @retval None + */ +static void DAC_StopConversion(const stc_dac_handle_t *pDac) +{ + if (NULL == pDac) { + DAC_DeInit(CM_DAC1); + DAC_DeInit(CM_DAC2); + } else if (pDac->enCvtType != E_Dac_Dual) { + (void)DAC_Stop(pDac->pUnit, pDac->u16Ch); + } else { + DAC_StopDualCh(pDac->pUnit); + } +} static uint32 DacOpen(void *dev) { struct DacHardwareDevice *dac_dev = (struct DacHardwareDevice *)dev; - CM_DAC_TypeDef *DACx = (CM_DAC_TypeDef *)dac_dev->private_data; + /* Init MAU for generating sine data*/ + MauInit(); + /* Init sine data table */ + SinTableInit(gu32SinTable, SINE_DOT_NUMBER); - stc_dac_init_t pstcDacInit; - - DAC_StructInit(&pstcDacInit); - - DAC_Init(DACx,DAC_CH1,&pstcDacInit); + /* Init single DAC */ + pSingleDac = DacSingleConversionInit(DAC_Unit1); return EOK; } static uint32 DacClose(void *dev) { - struct DacHardwareDevice *dac_dev = (struct DacHardwareDevice *)dev; - CM_DAC_TypeDef *DACx = (CM_DAC_TypeDef *)dac_dev->private_data; - + + DAC_StopConversion(pSingleDac); + DAC_DeInit(DACx); + MauDeinit(); + + memset(gu32SinTable, 0 , sizeof(gu32SinTable)); + return EOK; } - -static uint32 DacRead(void *dev, struct BusBlockReadParam *read_param) +static uint32 DacWrite(void *dev, struct BusBlockWriteParam *write_param) { struct DacHardwareDevice *dac_dev = (struct DacHardwareDevice *)dev; + struct HwDac *dac_cfg = (struct HwDac *)dac_dev->haldev.private_data; - CM_DAC_TypeDef *DACx = (CM_DAC_TypeDef *)dac_dev->private_data; + for (int i = 0; i < dac_cfg->digital_data; i ++) { + DacStartSingleConversion(pSingleDac); + DacSetSingleConversionData(pSingleDac, &gu32SinTable[i], 1U); + if (i > SINE_DOT_NUMBER) { + i = 0; + } + } - uint16 dac_set_value = 0; - - dac_set_value = DAC_GetChConvertState(DACx,DAC_CH1); - - *(uint16 *)read_param->buffer = dac_set_value; - read_param->read_length = 2; - - return read_param->read_length; return EOK; } @@ -88,8 +333,6 @@ static uint32 DacDrvConfigure(void *drv, struct BusConfigureInfo *configure_info { case OPE_CFG: dac_cfg->digital_data = *(uint16 *)configure_info->private_data; - // DAC_SetChannel1Data(DAC_Align_12b_R, dac_cfg->digital_data);//12 bits、R-Align data format, digital data - DAC_SetChData(dac_cfg->DACx,DAC_CH1,dac_cfg->digital_data); break; default: break; @@ -102,8 +345,8 @@ static const struct DacDevDone dev_done = { DacOpen, DacClose, + DacWrite, NONE, - DacRead, }; int HwDacInit(void) diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_dac.h b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_dac.h index 1108fcfdb..c99f28010 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_dac.h +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/include/connect_dac.h @@ -34,13 +34,6 @@ struct HwDac uint16 digital_data; }; -typedef struct { - CM_DAC_TypeDef *pUnit; - // en_dac_cvt_t enCvtType; - uint16_t u16Ch; -} stc_dac_handle_t; - - int HwDacInit(void); #ifdef __cplusplus From 379cd567a21115ed633e805a8de27396586c1ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Wed, 9 Aug 2023 16:33:17 +0800 Subject: [PATCH 27/33] Fix edu-arm32 i2c. --- .../Applications/app_test/test_i2c.c | 67 +++++++++++++------ .../third_party_driver/i2c/connect_i2c.c | 10 ++- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/APP_Framework/Applications/app_test/test_i2c.c b/APP_Framework/Applications/app_test/test_i2c.c index 76881b92e..e2be2ce47 100644 --- a/APP_Framework/Applications/app_test/test_i2c.c +++ b/APP_Framework/Applications/app_test/test_i2c.c @@ -24,18 +24,16 @@ #define I2C_SLAVE_ADDRESS 0x0012U -void TestI2C(void) +int open_iic(void) { - // config IIC pin(SCL:34.SDA:35) in menuconfig int iic_fd = PrivOpen(I2C_DEV_DRIVER, O_RDWR); if (iic_fd < 0) { - printf("open iic_fd fd error:%d\n", iic_fd); - return; + printf("[TestI2C] Open iic_fd fd error: %d\n", iic_fd); + return -ERROR; } - printf("IIC open successful!\n"); + printf("[TestI2C] IIC open successful!\n"); - // init iic uint16 iic_address = I2C_SLAVE_ADDRESS; struct PrivIoctlCfg ioctl_cfg; @@ -44,28 +42,55 @@ void TestI2C(void) if (0 != PrivIoctl(iic_fd, OPE_INT, &ioctl_cfg)) { - printf("ioctl iic fd error %d\n", iic_fd); + printf("[TestI2C] Ioctl iic fd error %d\n", iic_fd); PrivClose(iic_fd); - return; + return -ERROR; } printf("IIC configure successful!\n"); - // I2C read and write - char tmp_buff[100]; - while (1) - { - PrivTaskDelay(1000); - PrivWrite(iic_fd, "Hello World!\n", sizeof("Hello World!\n")); - printf("msg send:%s\n", "Hello World!\n"); - PrivTaskDelay(1000); - memset(tmp_buff, 0, sizeof(tmp_buff)); - PrivRead(iic_fd, tmp_buff, sizeof(tmp_buff)); - printf("msg recv:%s\n", tmp_buff); + return iic_fd; +} + +static const int nr_transmit = 15; + +void TestMasterI2c(void) +{ + char recv_buff[13] = { 0 }; + + int iic_fd = open_iic(); + if (iic_fd < 0) { + printf("[%s] Error open iic\n", __func__); + return; + } + + for (int transmit_cnt = 0; transmit_cnt < nr_transmit; transmit_cnt++) { + // wait if you like. + PrivTaskDelay(500); + memset(recv_buff, 0, sizeof(recv_buff)); + PrivRead(iic_fd, recv_buff, sizeof(recv_buff)); + printf("[%s] Msg recv: %s\n", __func__, recv_buff); } PrivClose(iic_fd); - return; } -PRIV_SHELL_CMD_FUNCTION(TestI2C, a iic test sample, PRIV_SHELL_CMD_MAIN_ATTR); +void TestSlaveI2c(void) +{ + char send_buff[] = "Hello, World"; + + int iic_fd = open_iic(); + + for (int transmit_cnt = 0; transmit_cnt < nr_transmit; transmit_cnt++) { + // wait if you like. + PrivTaskDelay(500); + PrivWrite(iic_fd, send_buff, sizeof(send_buff)); + printf("[%s] Msg send: %s\n", __func__, send_buff); + } + + PrivClose(iic_fd); +} + +PRIV_SHELL_CMD_FUNCTION(TestMasterI2c, a iic test sample, PRIV_SHELL_CMD_MAIN_ATTR); +PRIV_SHELL_CMD_FUNCTION(TestSlaveI2c, a iic test sample, PRIV_SHELL_CMD_MAIN_ATTR); + #endif \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/i2c/connect_i2c.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/i2c/connect_i2c.c index d59f7b98b..20149f915 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/i2c/connect_i2c.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/i2c/connect_i2c.c @@ -117,6 +117,9 @@ static uint32 I2cDrvConfigure(void *drv, struct BusConfigureInfo *configure_info static uint32 I2cMasterWriteData(struct I2cHardwareDevice *i2c_dev, struct I2cDataStandard *msg) { + if (msg->len == 0) { + return EOK; + } uint32 i32Ret; I2C_Cmd(I2C_UNIT, ENABLE); @@ -171,6 +174,9 @@ static uint32 I2cMasterReadData(struct I2cHardwareDevice *i2c_dev, struct I2cDat } static uint32 I2cSlaveWriteData(struct I2cHardwareDevice *i2c_dev, struct I2cDataStandard *msg) { + if (msg->len == 0) { + return EOK; + } uint32 i32Ret; I2C_Cmd(I2C_UNIT, ENABLE); @@ -222,7 +228,7 @@ static uint32 I2cSlaveReadData(struct I2cHardwareDevice *i2c_dev, struct I2cData if (RESET == I2C_GetStatus(I2C_UNIT, I2C_FLAG_TRA)) { /* Slave receive data*/ i32Ret = I2C_ReceiveData(I2C_UNIT, msg->buf, msg->len, I2C_TIMEOUT); - KPrintf("Slave receive success!\r\n"); + KPrintf("Slave receive success!\r\n"); if ((LL_OK == i32Ret) || (LL_ERR_TIMEOUT == i32Ret)) { /* Wait stop condition */ @@ -336,7 +342,7 @@ int HwI2cInit(void) return ret; } -//#define I2C_TEST +// #define I2C_TEST #ifdef I2C_TEST #define USER_KEY_PORT (GPIO_PORT_I) From 0e82fac640c1b31ef62ba5a85df041ef28daa7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Wed, 9 Aug 2023 17:31:06 +0800 Subject: [PATCH 28/33] Fix edu-arm32 i2c. --- APP_Framework/Applications/app_test/test_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/APP_Framework/Applications/app_test/test_i2c.c b/APP_Framework/Applications/app_test/test_i2c.c index e2be2ce47..a5a9475bd 100644 --- a/APP_Framework/Applications/app_test/test_i2c.c +++ b/APP_Framework/Applications/app_test/test_i2c.c @@ -24,7 +24,7 @@ #define I2C_SLAVE_ADDRESS 0x0012U -int open_iic(void) +int OpenIic(void) { int iic_fd = PrivOpen(I2C_DEV_DRIVER, O_RDWR); if (iic_fd < 0) From a9ebe1e96d741c9428198356029cdfbd21afb336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Fri, 11 Aug 2023 10:01:08 +0800 Subject: [PATCH 29/33] 23/08/11 Add app test socket; Fix app test i2c; Add command line argument parse tool argparse; Increase lwip related threads' priority. --- APP_Framework/Applications/app_test/Kconfig | 5 + APP_Framework/Applications/app_test/Makefile | 4 + .../Applications/app_test/test_i2c.c | 4 +- .../Applications/app_test/test_socket.c | 347 ++++++++++++++++ .../third_party_driver/ethernet/eth_driver.c | 2 +- .../third_party_driver/ethernet/ethernetif.c | 2 +- Ubiquitous/XiZi_IIoT/path_kernel.mk | 3 +- .../resources/ethernet/LwIP/arch/lwipopts.h | 2 +- .../resources/ethernet/LwIP/arch/sys_arch.c | 9 +- .../ethernet/cmd_lwip/lwip_udp_demo.c | 158 ++++--- Ubiquitous/XiZi_IIoT/tool/shell/Makefile | 1 + Ubiquitous/XiZi_IIoT/tool/shell/argparse.c | 389 ++++++++++++++++++ Ubiquitous/XiZi_IIoT/tool/shell/argparse.h | 157 +++++++ 13 files changed, 1027 insertions(+), 56 deletions(-) create mode 100644 APP_Framework/Applications/app_test/test_socket.c create mode 100644 Ubiquitous/XiZi_IIoT/tool/shell/argparse.c create mode 100644 Ubiquitous/XiZi_IIoT/tool/shell/argparse.h diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index c5e5894cd..136492b05 100644 --- a/APP_Framework/Applications/app_test/Kconfig +++ b/APP_Framework/Applications/app_test/Kconfig @@ -72,6 +72,11 @@ menu "test app" endif endif + menuconfig USER_TEST_SOCKET + select BSP_USING_LWIP + bool "Config test socket(lwip)" + default n + menuconfig USER_TEST_RS485 select BSP_USING_UART select BSP_USING_GPIO diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 6016dcb7a..21e3dd8f5 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -113,6 +113,10 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) SRC_FILES += test_rbtree/test_rbtree.c endif + ifeq ($(CONFIG_USER_TEST_SOCKET),y) + SRC_FILES += test_socket.c + endif + ifeq ($(CONFIG_USER_TEST_WEBSERVER),y) SRC_FILES += endif diff --git a/APP_Framework/Applications/app_test/test_i2c.c b/APP_Framework/Applications/app_test/test_i2c.c index a5a9475bd..a7fb3a060 100644 --- a/APP_Framework/Applications/app_test/test_i2c.c +++ b/APP_Framework/Applications/app_test/test_i2c.c @@ -57,7 +57,7 @@ void TestMasterI2c(void) { char recv_buff[13] = { 0 }; - int iic_fd = open_iic(); + int iic_fd = OpenIic(); if (iic_fd < 0) { printf("[%s] Error open iic\n", __func__); return; @@ -78,7 +78,7 @@ void TestSlaveI2c(void) { char send_buff[] = "Hello, World"; - int iic_fd = open_iic(); + int iic_fd = OpenIic(); for (int transmit_cnt = 0; transmit_cnt < nr_transmit; transmit_cnt++) { // wait if you like. diff --git a/APP_Framework/Applications/app_test/test_socket.c b/APP_Framework/Applications/app_test/test_socket.c new file mode 100644 index 000000000..1df10349e --- /dev/null +++ b/APP_Framework/Applications/app_test/test_socket.c @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2020 AIIT XUOS Lab + * XiUOS is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +#include +#include +#include + +#include "lwip/sockets.h" +#include "sys_arch.h" + +#define IPERF_PORT 5001 +#define IPERF_BUFSZ (4 * 1024) + +enum IperfMode { + IPERF_MODE_STOP = (1 << 0), + IPERF_MODE_SERVER = (1 << 1), + IPERF_MODE_CLIENT = (1 << 2), +}; + +struct AtomicIperfMode { + /* pthread_mutex_t here is a int */ + pthread_mutex_t mtx; + enum IperfMode mode; +}; + +static struct AtomicIperfMode* GetGlobalIperfMode() +{ + /* init when used */ + static struct AtomicIperfMode g_iperf_mode = { + -1, + IPERF_MODE_STOP, + }; + if (g_iperf_mode.mtx < 0) { + /* mtx is a static obj, so there is only creation but not destruction */ + PrivMutexCreate(&g_iperf_mode.mtx, NULL); + /* init lwip if necessary */ + lwip_config_tcp(0, lwip_ipaddr, lwip_netmask, lwip_gwaddr); + } + return &g_iperf_mode; +} + +static enum IperfMode GetGlobalMode() +{ + enum IperfMode mode = IPERF_MODE_STOP; + struct AtomicIperfMode* g_mode = GetGlobalIperfMode(); + + PrivMutexObtain(&g_mode->mtx); + mode = g_mode->mode; + PrivMutexAbandon(&g_mode->mtx); + + return mode; +} + +static void SetGlobalMode(enum IperfMode mode) +{ + struct AtomicIperfMode* g_mode = GetGlobalIperfMode(); + PrivMutexObtain(&g_mode->mtx); + g_mode->mode = mode; + PrivMutexAbandon(&g_mode->mtx); +} + +struct IperfParam { + char host[16]; + int port; +}; + +static void* TestIperfServer(void* param) +{ + struct IperfParam* iperf_param = (struct IperfParam*)param; + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + printf("[%s] Err: Can't create socker.\n", __func__); + return NULL; + } + + uint8_t* recv_data = (uint8_t*)malloc(IPERF_BUFSZ); + if (recv_data == NULL) { + KPrintf("[%s] No memory to alloc buffer!\n", __func__); + goto __exit; + } + + struct sockaddr_in server_addr, client_addr; + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(iperf_param->port); + server_addr.sin_addr.s_addr = INADDR_ANY; + memset(&(server_addr.sin_zero), 0x0, sizeof(server_addr.sin_zero)); + + if (bind(sock, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)) == -1) { + KPrintf("[%s] Err: Unable to bind socket: %d!\n", __func__, sock); + goto __exit; + } + + if (listen(sock, 5) == -1) { + KPrintf("[%s] Err: Listen error!\n", __func__); + goto __exit; + } + + struct timeval timeout = { + .tv_sec = 3, + .tv_usec = 0, + }; + + fd_set readset; + while (GetGlobalMode() != IPERF_MODE_STOP) { + FD_ZERO(&readset); + FD_SET(sock, &readset); + + if (select(sock + 1, &readset, NULL, NULL, &timeout) == 0) { + continue; + } + + socklen_t sin_size = sizeof(struct sockaddr_in); + struct sockaddr_in client_addr; + int connection = accept(sock, (struct sockaddr*)&client_addr, &sin_size); + printf("[%s] Info: New client connected from (%s, %d)\n", __func__, + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + + int flag = 1; + setsockopt(connection, + IPPROTO_TCP, /* set option at TCP level */ + TCP_NODELAY, /* name of option */ + (void*)&flag, /* the cast is historical cruft */ + sizeof(int)); /* length of option value */ + + int recvlen = 0; + int tick_beg = PrivGetTickTime(); + int tick_end = tick_beg; + while (GetGlobalMode() != IPERF_MODE_STOP) { + int bytes_received = recv(connection, recv_data, IPERF_BUFSZ, 0); + if (bytes_received == 0) { + KPrintf("client disconnected (%s, %d)\n", + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + break; + } else if (bytes_received < 0) { + KPrintf("recv error, client: (%s, %d)\n", + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + break; + } + + recvlen += bytes_received; + + tick_end = PrivGetTickTime(); + if (tick_end - tick_beg >= 5000) { + double speed; + // int integer, decimal; + + speed = (double)(recvlen / (tick_end - tick_beg)); + speed = speed / 1000.0f; + printf("[%s]: %2.4f MBps!\n", __func__, speed); + tick_beg = tick_end; + recvlen = 0; + } + } + if (connection >= 0) + closesocket(connection); + connection = -1; + } + +__exit: + if (sock >= 0) + closesocket(sock); + if (recv_data) + free(recv_data); + return NULL; +} + +static void* TestIperfClient(void* param) +{ + struct IperfParam* iperf_param = (struct IperfParam*)param; + + uint8_t* send_buf + = (uint8_t*)malloc(IPERF_BUFSZ); + if (NONE == send_buf) { + printf("[%s] Err: Unable to alloc buffer\n", __func__); + return NULL; + } + for (int i = 0; i < IPERF_BUFSZ; i++) { + send_buf[i] = i & 0xff; + } + + struct sockaddr_in addr; + while (GetGlobalMode() != IPERF_MODE_STOP) { + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + printf("[%s] Warning: Can't create socker.\n", __func__); + PrivTaskDelay(1000); + continue; + } + + addr.sin_family = PF_INET; + addr.sin_port = htons(iperf_param->port); + addr.sin_addr.s_addr = inet_addr((char*)iperf_param->host); + + int ret = connect(sock, (const struct sockaddr*)&addr, sizeof(addr)); + if (ret == -1) { + printf("[%s] Warning: Connect to iperf server faile, Waiting for the server to open!\n", __func__); + closesocket(sock); + DelayKTask(TICK_PER_SECOND); + continue; + } + printf("[%s] Connect to iperf server successful!\n", __func__); + + int flag = 1; + setsockopt(sock, + IPPROTO_TCP, /* set option at TCP level */ + TCP_NODELAY, /* name of option */ + (void*)&flag, /* the cast is historical cruft */ + sizeof(int)); /* length of option value */ + + int tick_beg = PrivGetTickTime(); + int tick_end = tick_beg; + int sentlen = 0; + while (GetGlobalMode() != IPERF_MODE_STOP) { + tick_end = PrivGetTickTime(); + /* Print every 5 second */ + if (tick_end - tick_beg >= 5000) { + double speed; + + speed = (double)(sentlen / (tick_end - tick_beg)); + speed = speed / 1000.0f; + printf("[%s]: %2.4f MBps!\n", __func__, speed); + tick_beg = tick_end; + sentlen = 0; + } + + ret = send(sock, send_buf, IPERF_BUFSZ, 0); + if (ret > 0) { + sentlen += ret; + } + + if (ret < 0) + break; + } + + closesocket(sock); + printf("[%s] Info: Disconnected, iperf server shut down!\n", __func__); + } + free(send_buf); + return NULL; +} + +enum IperfParamEnum { + IPERF_PARAM_SERVER = 's', + IPERF_PARAM_CLIENT = 'c', + IPERF_PARAM_STOP = 0, + IPERF_PARAM_IPADDR = 0, + IPERF_PARAM_PORT = 'p', +}; + +void TestIperf(int argc, char* argv[]) +{ + lwip_config_tcp(0, lwip_ipaddr, lwip_netmask, lwip_gwaddr); + + static char usage_info[] = "Run either a iperf server or iperf client."; + static char program_info[] = "Lwip socket test task, a simple iperf."; + static const char* const usages[] = { + "TestIperf -c [--ip arg] [-p arg]", + "TestIperf -s [-p arg]", + NULL, + }; + + static struct IperfParam iperf_param = { + .host = "255.255.255.255", + .port = 5001, + }; + + enum IperfMode mode = 0; + char* ip_ptr = NULL; + bool is_help = false; + struct argparse_option options[] = { + OPT_HELP(&is_help), + OPT_GROUP("Bit Options"), + OPT_BIT(IPERF_PARAM_SERVER, "server", &mode, "start a iperf server", NULL, IPERF_MODE_SERVER, 0), + OPT_BIT(IPERF_PARAM_CLIENT, "client", &mode, "start a iperf client", NULL, IPERF_MODE_CLIENT, 0), + OPT_BIT(IPERF_PARAM_STOP, "stop", &mode, "stop iperf", NULL, IPERF_MODE_STOP, OPT_NONEG), + OPT_GROUP("Param Options"), + OPT_STRING(IPERF_PARAM_IPADDR, "ip", &ip_ptr, "server IP if iperf is a client", NULL, 0, 0), + OPT_INTEGER(IPERF_PARAM_PORT, "port", &iperf_param.port, "server PORT needed for iperf", NULL, 0, 0), + OPT_END(), + }; + + struct argparse argparse; + argparse_init(&argparse, options, usages, 0); + argparse_describe(&argparse, usage_info, program_info); + argc = argparse_parse(&argparse, argc, (const char**)argv); + /* help task */ + if (is_help) { + return; + } + + /* stop iperf task */ + if (mode & IPERF_MODE_STOP) { + SetGlobalMode(IPERF_MODE_STOP); + return; + } + if (mode & IPERF_MODE_SERVER && mode & IPERF_MODE_CLIENT) { + printf("[%s] Err: Can't run iperf server and client at one time.\n", __func__); + } + + /* iperf server or iperf client*/ + struct AtomicIperfMode* iperf_mode = GetGlobalIperfMode(); + PrivMutexObtain(&iperf_mode->mtx); + if (iperf_mode->mode != IPERF_MODE_STOP) { + PrivMutexAbandon(&iperf_mode->mtx); + printf("[%s] Err: There is already a iperf running, please stop it before running a new one\n", __func__); + return; + } + + if (mode & IPERF_MODE_SERVER) { + iperf_mode->mode = IPERF_MODE_SERVER; + } else if (mode & IPERF_MODE_CLIENT) { + if (ip_ptr == NONE) { + PrivMutexAbandon(&iperf_mode->mtx); + printf("[%s] Err: Iperf client must assign a server ip.\n", __func__); + return; + } else { + memset(iperf_param.host, 0, sizeof(iperf_param.host)); + strncpy(iperf_param.host, ip_ptr, strlen(ip_ptr)); + } + iperf_mode->mode = IPERF_MODE_CLIENT; + } + PrivMutexAbandon(&iperf_mode->mtx); + + pthread_t thd; + mode = GetGlobalMode(); + if (mode == IPERF_MODE_SERVER) { + printf("[%s] Running iperf server at port %d.\n", __func__, iperf_param.port); + + PrivTaskCreate(&thd, NULL, TestIperfServer, (void*)&iperf_param); + } else if (mode == IPERF_MODE_CLIENT) { + printf("[%s] Running iperf client to server at %s:%d.\n", __func__, iperf_param.host, iperf_param.port); + PrivTaskCreate(&thd, NULL, TestIperfClient, (void*)&iperf_param); + } + + PrivTaskStartup(&thd); +} + +PRIV_SHELL_CMD_FUNCTION(TestSocket, Test socket using iperf, PRIV_SHELL_CMD_MAIN_ATTR | SHELL_CMD_PARAM_NUM(8)); \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_driver.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_driver.c index 800c08697..25d106209 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_driver.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/eth_driver.c @@ -281,7 +281,7 @@ struct pbuf* low_level_input(struct netif* netif) extern void LwipSetIPTest(int argc, char* argv[]); int HwEthInit(void) { - // lwip_config_tcp(0, lwip_ipaddr, lwip_netmask, lwip_gwaddr); + // lwip_config_tcp(0, lwip_ipaddr, lwip_netmask, lwip_gwaddr); LwipSetIPTest(1, NULL); return EOK; } diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/ethernetif.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/ethernetif.c index 738359b59..ec463faab 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/ethernetif.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/ethernet/ethernetif.c @@ -263,7 +263,7 @@ err_t ethernetif_init(struct netif* netif) if (EOK != lwip_netdev_add(netif)) { SYS_KDEBUG_LOG(NETDEV_DEBUG, ("[%s] LWIP add netdev failed.\n", __func__)); } else { - printf("[%s] Add Netdev successful\n", __func__); + // printf("[%s] Add Netdev successful\n", __func__); } return LL_OK; } diff --git a/Ubiquitous/XiZi_IIoT/path_kernel.mk b/Ubiquitous/XiZi_IIoT/path_kernel.mk index 1603864c8..c32ee1572 100755 --- a/Ubiquitous/XiZi_IIoT/path_kernel.mk +++ b/Ubiquitous/XiZi_IIoT/path_kernel.mk @@ -572,7 +572,8 @@ endif ifeq ($(CONFIG_TOOL_SHELL), y) KERNELPATHS +=-I$(KERNEL_ROOT)/tool/shell/letter-shell \ - -I$(KERNEL_ROOT)/tool/shell/letter-shell/file_ext # + -I$(KERNEL_ROOT)/tool/shell/letter-shell/file_ext \ + -I$(KERNEL_ROOT)/tool/shell/ endif ifeq ($(CONFIG_TOOL_USING_OTA), y) diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/lwipopts.h b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/lwipopts.h index cdf5a61d9..8d76990a2 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/lwipopts.h +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/lwipopts.h @@ -540,7 +540,7 @@ The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums #define TCPIP_THREAD_NAME "tcp" #define TCPIP_THREAD_STACKSIZE 2048 #define TCPIP_MBOX_SIZE 16 -#define TCPIP_THREAD_PRIO 20 +#define TCPIP_THREAD_PRIO 30 /* ---------------------------------------- diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c index 7ec40271d..e6f405af0 100644 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/LwIP/arch/sys_arch.c @@ -336,7 +336,7 @@ void lwip_config_input(struct netif* net) { sys_thread_t th_id = 0; - th_id = sys_thread_new("eth_input", ethernetif_input, net, LWIP_TASK_STACK_SIZE, 20); + th_id = sys_thread_new("eth_input", ethernetif_input, net, LWIP_TASK_STACK_SIZE, 30); if (th_id >= 0) { lw_print("%s %d successfully!\n", __func__, th_id); @@ -347,6 +347,12 @@ void lwip_config_input(struct netif* net) void lwip_config_tcp(uint8_t enet_port, char* ip, char* mask, char* gw) { + static char is_init = 0; + if (is_init != 0) { + return; + } + is_init = 1; + sys_sem_new(get_eth_recv_sem(), 0); ip4_addr_t net_ipaddr, net_netmask, net_gw; @@ -371,7 +377,6 @@ void lwip_config_tcp(uint8_t enet_port, char* ip, char* mask, char* gw) if (0 == enet_port) { #ifdef NETIF_ENET0_INIT_FUNC - printf("[%s:%d] call netif_add\n", __func__, __LINE__); netif_add(&gnetif, &net_ipaddr, &net_netmask, &net_gw, eth_cfg, NETIF_ENET0_INIT_FUNC, tcpip_input); #endif diff --git a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c index 71c6bc703..a978e773d 100755 --- a/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c +++ b/Ubiquitous/XiZi_IIoT/resources/ethernet/cmd_lwip/lwip_udp_demo.c @@ -19,27 +19,67 @@ */ #include "board.h" #include "sys_arch.h" -#include "lwip/udp.h" -#include #include #include -#include "lwip/sockets.h" +#include +#include + +#include "lwip/sockets.h" +#include "lwip/udp.h" + +#include +#include #define PBUF_SIZE 27 static struct udp_pcb *udpecho_raw_pcb; -char udp_server_ip[] = {192, 168, 130, 2}; u16_t udp_server_port = LWIP_TARGET_PORT; -int32 udp_send_num = 0; -int8 udp_send_task_on = 0; -uint32 udp_interval = 50; - +#define UDP_BUFFER_SIZE 50 char hello_str[] = {"hello world\r\n"}; -char udp_demo_msg[] = "\nThis one is UDP package!!!\n"; +char udp_demo_buffer[UDP_BUFFER_SIZE] = { '\0' }; /******************************************************************************/ +enum LwipUdpSendParamEnum { + TARGET_IP = 0, + TARGET_PORT = 'p', + SEND_MESSAGE = 'm', + SEND_NUM = 'n', + SEND_INTERVAL = 'i', +}; + +struct LwipUdpSendParam { + uint32_t num; + uint32_t interval; + uint16_t port; + uint8_t ip[4]; + bool task_on; + bool given_ip; + bool given_port; + bool given_msg; +}; + +struct LwipUdpSendParam* get_udp_test_info() +{ + /* init once and init when used. */ + static struct LwipUdpSendParam g_udp_send_param = { + .interval = 100, + .num = 10, + .port = LWIP_TARGET_PORT, + .ip = { 127, 0, 0, 1 }, + .task_on = false, + .given_ip = false, + .given_port = false, + .given_msg = false, + }; + return &g_udp_send_param; +} + +static const char* const usages[] = { + "UDPSend [--options arg] [-option arg]", + NULL, +}; static void LwipUDPSendTask(void *arg) { @@ -56,8 +96,8 @@ static void LwipUDPSendTask(void *arg) struct sockaddr_in udp_sock; udp_sock.sin_family = AF_INET; - udp_sock.sin_port = htons(udp_server_port); - udp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(udp_server_ip[0], udp_server_ip[1], udp_server_ip[2], udp_server_ip[3])); + udp_sock.sin_port = htons(get_udp_test_info()->port); + udp_sock.sin_addr.s_addr = PP_HTONL(LWIP_MAKEU32(get_udp_test_info()->ip[0], get_udp_test_info()->ip[1], get_udp_test_info()->ip[2], get_udp_test_info()->ip[3])); memset(&(udp_sock.sin_zero), 0, sizeof(udp_sock.sin_zero)); if (connect(socket_fd, (struct sockaddr *)&udp_sock, sizeof(struct sockaddr))) { @@ -68,59 +108,81 @@ static void LwipUDPSendTask(void *arg) KPrintf("UDP connect success, start to send.\n"); KPrintf("\n\nTarget Port:%d\n\n", udp_sock.sin_port); - udp_send_task_on = 1; + get_udp_test_info()->task_on = true; - while(udp_send_num > 0 || udp_send_num == -1) { - sendto(socket_fd, udp_demo_msg, strlen(udp_demo_msg), 0, (struct sockaddr*)&udp_sock, sizeof(struct sockaddr)); - KPrintf("Send UDP msg: %s \n", udp_demo_msg); - MdelayKTask(udp_interval); - udp_send_num--; + while (get_udp_test_info()->num > 0 || get_udp_test_info()->num == -1) { + sendto(socket_fd, udp_demo_buffer, strlen(udp_demo_buffer), 0, (struct sockaddr*)&udp_sock, sizeof(struct sockaddr)); + KPrintf("Send UDP msg: %s \n", udp_demo_buffer); + MdelayKTask(get_udp_test_info()->interval); + get_udp_test_info()->num--; } closesocket(socket_fd); - udp_send_task_on = 0; + get_udp_test_info()->task_on = false; return; } -void *LwipUdpSendTest(int argc, char *argv[]) +static int LwipUdpSend(int argc, char* argv[]) { - if(udp_send_task_on) { - udp_send_num = 0; - printf("waitting send task exit...\n"); - while(udp_send_task_on){ - MdelayKTask(1000); - } - udp_send_num = 1; + static char usage_info[] = "Send udp NUM message to IP:PORT with time INTERVAL between each message send."; + static char program_info[] = "UDP SEND TEST DEMO."; + + /* Wait if there are former udp task */ + if (get_udp_test_info()->task_on) { + KPrintf("[%s] Waiting former udp send task to exit.\n"); + } + while (get_udp_test_info()->task_on) { + MdelayKTask(1000); } - uint8_t enet_port = 0; ///< test enet port 0 - memset(udp_demo_msg, 0, sizeof(udp_demo_msg)); + get_udp_test_info()->given_ip = false; + get_udp_test_info()->given_port = false; + get_udp_test_info()->given_msg = false; - if(argc == 1) { - KPrintf("lw: [%s] gw %d.%d.%d.%d:%d\n", __func__, udp_server_ip[0], udp_server_ip[1], udp_server_ip[2], udp_server_ip[3], udp_server_port); - strncpy(udp_demo_msg, hello_str, strlen(hello_str)); - udp_send_num = 10; - udp_interval = 100; - } else { - strncpy(udp_demo_msg, argv[1], strlen(argv[1])); - strncat(udp_demo_msg, "\r\n", 3); - if(argc >= 3) { - sscanf(argv[2], "%d.%d.%d.%d:%d", &udp_server_ip[0], &udp_server_ip[1], &udp_server_ip[2], &udp_server_ip[3], &udp_server_port); - } - if(argc > 3) { - sscanf(argv[3], "%d", &udp_send_num); - sscanf(argv[4], "%d", &udp_interval); - } + /* Parse options */ + char* msg_ptr = NULL; + char* ip_ptr = NULL; + bool is_help = false; + struct argparse_option options[] = { + OPT_HELP(&is_help), + OPT_STRING(SEND_MESSAGE, "message", &msg_ptr, "MESSAGE to send", NULL, 0, 0), + OPT_STRING(TARGET_IP, "ip", &ip_ptr, "target IP to send upd messages", NULL, 0, 0), + OPT_INTEGER(TARGET_PORT, "port", &get_udp_test_info()->port, "target PORT to send udp messages", NULL, 0, 0), + OPT_INTEGER(SEND_NUM, "num", &get_udp_test_info()->num, "send NUM udp messages", NULL, 0, 0), + OPT_INTEGER(SEND_INTERVAL, "interval", &get_udp_test_info()->interval, "time INTERVAL between messages", NULL, 0, 0), + OPT_END(), + }; + + struct argparse argparse; + argparse_init(&argparse, options, usages, 0); + argparse_describe(&argparse, usage_info, program_info); + argc = argparse_parse(&argparse, argc, (const char**)argv); + if (argc < 0) { + KPrintf("Error options.\n"); + return -ERROR; + } + if (is_help) { + return EOK; } - KPrintf("lw: [%s] gw %d.%d.%d.%d:%d send time %d udp_interval %d\n", __func__, udp_server_ip[0], udp_server_ip[1], udp_server_ip[2], udp_server_ip[3], udp_server_port, udp_send_num, udp_interval); + // translate string to array + sscanf(ip_ptr, "%d.%d.%d.%d", &get_udp_test_info()->ip[0], &get_udp_test_info()->ip[1], &get_udp_test_info()->ip[2], &get_udp_test_info()->ip[3]); + int msg_len = strlen(msg_ptr); + strncpy(udp_demo_buffer, msg_ptr, msg_len < UDP_BUFFER_SIZE ? msg_len : UDP_BUFFER_SIZE); - //init lwip and net dirver - lwip_config_net(enet_port, lwip_ipaddr, lwip_netmask, lwip_gwaddr); + /* start task */ + KPrintf("[%s] gw %d.%d.%d.%d:%d send time %d udp_interval %d\n", __func__, + get_udp_test_info()->ip[0], get_udp_test_info()->ip[1], get_udp_test_info()->ip[2], get_udp_test_info()->ip[3], + get_udp_test_info()->port, + get_udp_test_info()->num, + get_udp_test_info()->interval); + + lwip_config_net(0, lwip_ipaddr, lwip_netmask, lwip_gwaddr); sys_thread_new("udp send", LwipUDPSendTask, NULL, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); + return EOK; } -SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(5), - UDPSend, LwipUdpSendTest, UDPSend msg [ip:port [num [interval]]]); +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(16), + UDPSend, LwipUdpSend, UDPSend Demo); void LwipUdpRecvTest(void) { diff --git a/Ubiquitous/XiZi_IIoT/tool/shell/Makefile b/Ubiquitous/XiZi_IIoT/tool/shell/Makefile index f45467e4a..d2a8f1357 100644 --- a/Ubiquitous/XiZi_IIoT/tool/shell/Makefile +++ b/Ubiquitous/XiZi_IIoT/tool/shell/Makefile @@ -1,3 +1,4 @@ SRC_DIR := letter-shell +SRC_FILES += argparse.c include $(KERNEL_ROOT)/compiler.mk diff --git a/Ubiquitous/XiZi_IIoT/tool/shell/argparse.c b/Ubiquitous/XiZi_IIoT/tool/shell/argparse.c new file mode 100644 index 000000000..ed453547a --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/tool/shell/argparse.c @@ -0,0 +1,389 @@ +/** + * Copyright (C) 2012-2015 Yecheng Fu + * All rights reserved. + * + * Use of this source code is governed by a MIT-style license that can be found + * in the LICENSE file. + */ +#include "argparse.h" +#include +#include +#include +#include +#include +#include + +#define OPT_UNSET 1 +#define OPT_LONG (1 << 1) + +static const char* +prefix_skip(const char* str, const char* prefix) +{ + size_t len = strlen(prefix); + return strncmp(str, prefix, len) ? NULL : str + len; +} + +static int +prefix_cmp(const char* str, const char* prefix) +{ + for (;; str++, prefix++) + if (!*prefix) { + return 0; + } else if (*str != *prefix) { + return (unsigned char)*prefix - (unsigned char)*str; + } +} + +static void +argparse_error(struct argparse* self, const struct argparse_option* opt, + const char* reason, int flags) +{ + (void)self; + if (flags & OPT_LONG) { + fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason); + } else { + fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason); + } +} + +#include + +static int +argparse_getvalue(struct argparse* self, const struct argparse_option* opt, + int flags) +{ + const char* s = NULL; + if (!opt->value) + goto skipped; + switch (opt->type) { + case ARGPARSE_OPT_BOOLEAN: + if (flags & OPT_UNSET) { + *(int*)opt->value = *(int*)opt->value - 1; + } else { + *(int*)opt->value = *(int*)opt->value + 1; + } + if (*(int*)opt->value < 0) { + *(int*)opt->value = 0; + } + break; + case ARGPARSE_OPT_BIT: + if (flags & OPT_UNSET) { + *(int*)opt->value &= ~opt->data; + } else { + *(int*)opt->value |= opt->data; + } + break; + case ARGPARSE_OPT_STRING: + if (self->optvalue) { + *(const char**)opt->value = self->optvalue; + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(const char**)opt->value = *++self->argv; + } else { + argparse_error(self, opt, "requires a value", flags); + } + break; + case ARGPARSE_OPT_INTEGER: + errno = 0; + if (self->optvalue) { + *(int*)opt->value = strtol(self->optvalue, (char**)&s, 0); + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(int*)opt->value = strtol(*++self->argv, (char**)&s, 0); + } else { + argparse_error(self, opt, "requires a value", flags); + } + if (errno == ERANGE) + argparse_error(self, opt, "numerical result out of range", flags); + if (s[0] != '\0') // no digits or contains invalid characters + argparse_error(self, opt, "expects an integer value", flags); + break; + case ARGPARSE_OPT_FLOAT: + errno = 0; + if (self->optvalue) { + *(float*)opt->value = strtof(self->optvalue, (char**)&s); + self->optvalue = NULL; + } else if (self->argc > 1) { + self->argc--; + *(float*)opt->value = strtof(*++self->argv, (char**)&s); + } else { + argparse_error(self, opt, "requires a value", flags); + } + if (errno == ERANGE) + argparse_error(self, opt, "numerical result out of range", flags); + if (s[0] != '\0') // no digits or contains invalid characters + argparse_error(self, opt, "expects a numerical value", flags); + break; + default: + assert(0); + } + +skipped: + if (opt->callback) { + return opt->callback(self, opt); + } + return 0; +} + +static void +argparse_options_check(const struct argparse_option* options) +{ + for (; options->type != ARGPARSE_OPT_END; options++) { + switch (options->type) { + case ARGPARSE_OPT_END: + case ARGPARSE_OPT_BOOLEAN: + case ARGPARSE_OPT_BIT: + case ARGPARSE_OPT_INTEGER: + case ARGPARSE_OPT_FLOAT: + case ARGPARSE_OPT_STRING: + case ARGPARSE_OPT_GROUP: + continue; + default: + fprintf(stderr, "wrong option type: %d", options->type); + break; + } + } +} + +static int +argparse_short_opt(struct argparse* self, const struct argparse_option* options) +{ + for (; options->type != ARGPARSE_OPT_END; options++) { + if (options->short_name == *self->optvalue) { + self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL; + return argparse_getvalue(self, options, 0); + } + } + return -2; +} + +static int +argparse_long_opt(struct argparse* self, const struct argparse_option* options) +{ + for (; options->type != ARGPARSE_OPT_END; options++) { + const char* rest; + int opt_flags = 0; + if (!options->long_name) + continue; + + rest = prefix_skip(self->argv[0] + 2, options->long_name); + if (!rest) { + // negation disabled? + if (options->flags & OPT_NONEG) { + continue; + } + // only OPT_BOOLEAN/OPT_BIT supports negation + if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != ARGPARSE_OPT_BIT) { + continue; + } + + if (prefix_cmp(self->argv[0] + 2, "no-")) { + continue; + } + rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name); + if (!rest) + continue; + opt_flags |= OPT_UNSET; + } + if (*rest) { + if (*rest != '=') + continue; + self->optvalue = rest + 1; + } + return argparse_getvalue(self, options, opt_flags | OPT_LONG); + } + return -2; +} + +int argparse_init(struct argparse* self, struct argparse_option* options, + const char* const* usages, int flags) +{ + memset(self, 0, sizeof(*self)); + self->options = options; + self->usages = usages; + self->flags = flags; + self->description = NULL; + self->epilog = NULL; + return 0; +} + +void argparse_describe(struct argparse* self, const char* description, + const char* epilog) +{ + self->description = description; + self->epilog = epilog; +} + +int argparse_parse(struct argparse* self, int argc, const char** argv) +{ + self->argc = argc - 1; + self->argv = argv + 1; + self->out = argv; + + argparse_options_check(self->options); + + for (; self->argc; self->argc--, self->argv++) { + const char* arg = self->argv[0]; + if (arg[0] != '-' || !arg[1]) { + if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) { + goto end; + } + // if it's not option or is a single char '-', copy verbatim + self->out[self->cpidx++] = self->argv[0]; + continue; + } + // short option + if (arg[1] != '-') { + self->optvalue = arg + 1; + switch (argparse_short_opt(self, self->options)) { + case -1: + break; + case -2: + goto unknown; + } + while (self->optvalue) { + switch (argparse_short_opt(self, self->options)) { + case -1: + break; + case -2: + goto unknown; + } + } + continue; + } + // if '--' presents + if (!arg[2]) { + self->argc--; + self->argv++; + break; + } + // long option + switch (argparse_long_opt(self, self->options)) { + case -1: + break; + case -2: + goto unknown; + } + continue; + + unknown: + fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]); + argparse_usage(self); + if (!(self->flags & ARGPARSE_IGNORE_UNKNOWN_ARGS)) { + return ARGPARSE_ERROR; + } + } + +end: + memmove(self->out + self->cpidx, self->argv, + self->argc * sizeof(*self->out)); + self->out[self->cpidx + self->argc] = NULL; + + return self->cpidx + self->argc; +} + +void argparse_usage(struct argparse* self) +{ + if (self->usages) { + fprintf(stdout, "Usage: %s\n", *self->usages++); + while (*self->usages && **self->usages) + fprintf(stdout, " or: %s\n", *self->usages++); + } else { + fprintf(stdout, "Usage:\n"); + } + + // print description + if (self->description) + fprintf(stdout, "%s\n", self->description); + + fputc('\n', stdout); + + const struct argparse_option* options; + + // figure out best width + size_t usage_opts_width = 0; + size_t len; + options = self->options; + for (; options->type != ARGPARSE_OPT_END; options++) { + len = 0; + if ((options)->short_name) { + len += 2; + } + if ((options)->short_name && (options)->long_name) { + len += 2; // separator ", " + } + if ((options)->long_name) { + len += strlen((options)->long_name) + 2; + } + if (options->type == ARGPARSE_OPT_INTEGER) { + len += strlen("="); + } + if (options->type == ARGPARSE_OPT_FLOAT) { + len += strlen("="); + } else if (options->type == ARGPARSE_OPT_STRING) { + len += strlen("="); + } + len = (len + 3) - ((len + 3) & 3); + if (usage_opts_width < len) { + usage_opts_width = len; + } + } + usage_opts_width += 4; // 4 spaces prefix + + options = self->options; + for (; options->type != ARGPARSE_OPT_END; options++) { + size_t pos = 0; + size_t pad = 0; + if (options->type == ARGPARSE_OPT_GROUP) { + fputc('\n', stdout); + fprintf(stdout, "%s", options->help); + fputc('\n', stdout); + continue; + } + pos = fprintf(stdout, " "); + if (options->short_name) { + pos += fprintf(stdout, "-%c", options->short_name); + } + if (options->long_name && options->short_name) { + pos += fprintf(stdout, ", "); + } + if (options->long_name) { + pos += fprintf(stdout, "--%s", options->long_name); + } + if (options->type == ARGPARSE_OPT_INTEGER) { + pos += fprintf(stdout, "="); + } else if (options->type == ARGPARSE_OPT_FLOAT) { + pos += fprintf(stdout, "="); + } else if (options->type == ARGPARSE_OPT_STRING) { + pos += fprintf(stdout, "="); + } + if (pos <= usage_opts_width) { + pad = usage_opts_width - pos; + } else { + fputc('\n', stdout); + pad = usage_opts_width; + } + fprintf(stdout, "%*s%s\n", (int)pad + 2, "", options->help); + } + + // print epilog + if (self->epilog) + fprintf(stdout, "%s\n", self->epilog); +} + +int argparse_help_cb_no_exit(struct argparse* self, + const struct argparse_option* option) +{ + (void)option; + argparse_usage(self); + return 0; +} + +int argparse_help_cb(struct argparse* self, const struct argparse_option* option) +{ + argparse_help_cb_no_exit(self, option); + *(bool*)option->value = true; + return 0; +} diff --git a/Ubiquitous/XiZi_IIoT/tool/shell/argparse.h b/Ubiquitous/XiZi_IIoT/tool/shell/argparse.h new file mode 100644 index 000000000..2ccf51b04 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/tool/shell/argparse.h @@ -0,0 +1,157 @@ +/** + * Copyright (C) 2012-2015 Yecheng Fu + * All rights reserved. + * + * Use of this source code is governed by a MIT-style license that can be found + * in the LICENSE file. + */ +#ifndef ARGPARSE_H +#define ARGPARSE_H + +/* For c++ compatibility */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define ARGPARSE_HELP_DONE -1 +#define ARGPARSE_ERROR -2 + +struct argparse; +struct argparse_option; + +typedef int argparse_callback(struct argparse* self, + const struct argparse_option* option); + +enum argparse_flag { + ARGPARSE_STOP_AT_NON_OPTION = 1 << 0, + ARGPARSE_IGNORE_UNKNOWN_ARGS = 1 << 1, +}; + +enum argparse_option_type { + /* special */ + ARGPARSE_OPT_END, + ARGPARSE_OPT_GROUP, + /* options with no arguments */ + ARGPARSE_OPT_BOOLEAN, + ARGPARSE_OPT_BIT, + /* options with arguments (optional or required) */ + ARGPARSE_OPT_INTEGER, + ARGPARSE_OPT_FLOAT, + ARGPARSE_OPT_STRING, +}; + +enum argparse_option_flags { + OPT_NONEG = 1, /* disable negation */ +}; + +/** + * argparse option + * + * `type`: + * holds the type of the option, you must have an ARGPARSE_OPT_END last in your + * array. + * + * `short_name`: + * the character to use as a short option name, '\0' if none. + * + * `long_name`: + * the long option name, without the leading dash, NULL if none. + * + * `value`: + * stores pointer to the value to be filled. + * + * `help`: + * the short help message associated to what the option does. + * Must never be NULL (except for ARGPARSE_OPT_END). + * + * `callback`: + * function is called when corresponding argument is parsed. + * + * `data`: + * associated data. Callbacks can use it like they want. + * + * `flags`: + * option flags. + */ +struct argparse_option { + enum argparse_option_type type; + const char short_name; + const char* long_name; + void* value; + const char* help; + argparse_callback* callback; + intptr_t data; + int flags; +}; + +/** + * argpparse + */ +struct argparse { + // user supplied + const struct argparse_option* options; + const char* const* usages; + int flags; + const char* description; // a description after usage + const char* epilog; // a description at the end + // internal context + int argc; + const char** argv; + const char** out; + int cpidx; + const char* optvalue; // current option value +}; + +// built-in callbacks +int argparse_help_cb(struct argparse* self, + const struct argparse_option* option); +int argparse_help_cb_no_exit(struct argparse* self, + const struct argparse_option* option); + +// built-in option macros +#define OPT_END() \ + { \ + ARGPARSE_OPT_END, 0, NULL, NULL, 0, NULL, 0, 0 \ + } +#define OPT_BOOLEAN(...) \ + { \ + ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ \ + } +#define OPT_BIT(...) \ + { \ + ARGPARSE_OPT_BIT, __VA_ARGS__ \ + } +#define OPT_INTEGER(...) \ + { \ + ARGPARSE_OPT_INTEGER, __VA_ARGS__ \ + } +#define OPT_FLOAT(...) \ + { \ + ARGPARSE_OPT_FLOAT, __VA_ARGS__ \ + } +#define OPT_STRING(...) \ + { \ + ARGPARSE_OPT_STRING, __VA_ARGS__ \ + } +#define OPT_GROUP(h) \ + { \ + ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL, 0, 0 \ + } +#define OPT_HELP(flag) OPT_BOOLEAN('h', "help", flag, \ + "show this help message and exit", \ + argparse_help_cb, 0, OPT_NONEG) + +int argparse_init(struct argparse* self, struct argparse_option* options, + const char* const* usages, int flags); +void argparse_describe(struct argparse* self, const char* description, + const char* epilog); +int argparse_parse(struct argparse* self, int argc, const char** argv); +void argparse_usage(struct argparse* self); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file From 586ceba08e67cc34df4bf74428791423664e9994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Fri, 11 Aug 2023 10:02:51 +0800 Subject: [PATCH 30/33] 23/08/11 Fix app test socket --- APP_Framework/Applications/app_test/test_socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/APP_Framework/Applications/app_test/test_socket.c b/APP_Framework/Applications/app_test/test_socket.c index 1df10349e..ac9b87b34 100644 --- a/APP_Framework/Applications/app_test/test_socket.c +++ b/APP_Framework/Applications/app_test/test_socket.c @@ -256,7 +256,7 @@ enum IperfParamEnum { IPERF_PARAM_PORT = 'p', }; -void TestIperf(int argc, char* argv[]) +void TestSocket(int argc, char* argv[]) { lwip_config_tcp(0, lwip_ipaddr, lwip_netmask, lwip_gwaddr); From dd35692866f7826c3e248678d58959e9acdaf51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Fri, 11 Aug 2023 10:07:31 +0800 Subject: [PATCH 31/33] 23/08/11 Fix app test socket --- APP_Framework/Applications/app_test/test_socket.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/APP_Framework/Applications/app_test/test_socket.c b/APP_Framework/Applications/app_test/test_socket.c index ac9b87b34..81c4543cc 100644 --- a/APP_Framework/Applications/app_test/test_socket.c +++ b/APP_Framework/Applications/app_test/test_socket.c @@ -110,7 +110,7 @@ static void* TestIperfServer(void* param) }; fd_set readset; - while (GetGlobalMode() != IPERF_MODE_STOP) { + while (GetGlobalMode() == IPERF_MODE_SERVER) { FD_ZERO(&readset); FD_SET(sock, &readset); @@ -134,7 +134,7 @@ static void* TestIperfServer(void* param) int recvlen = 0; int tick_beg = PrivGetTickTime(); int tick_end = tick_beg; - while (GetGlobalMode() != IPERF_MODE_STOP) { + while (GetGlobalMode() == IPERF_MODE_SERVER) { int bytes_received = recv(connection, recv_data, IPERF_BUFSZ, 0); if (bytes_received == 0) { KPrintf("client disconnected (%s, %d)\n", @@ -188,7 +188,7 @@ static void* TestIperfClient(void* param) } struct sockaddr_in addr; - while (GetGlobalMode() != IPERF_MODE_STOP) { + while (GetGlobalMode() == IPERF_MODE_CLIENT) { int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { printf("[%s] Warning: Can't create socker.\n", __func__); @@ -219,7 +219,7 @@ static void* TestIperfClient(void* param) int tick_beg = PrivGetTickTime(); int tick_end = tick_beg; int sentlen = 0; - while (GetGlobalMode() != IPERF_MODE_STOP) { + while (GetGlobalMode() == IPERF_MODE_CLIENT) { tick_end = PrivGetTickTime(); /* Print every 5 second */ if (tick_end - tick_beg >= 5000) { From 38962426229def714279d535bfa4762d1c7288de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=82=E7=85=9C=E6=B4=8B?= <1163589503@qq.com> Date: Fri, 11 Aug 2023 11:03:10 +0800 Subject: [PATCH 32/33] 23/08/11 Add app test_uart. --- APP_Framework/Applications/app_test/Kconfig | 13 +++ APP_Framework/Applications/app_test/Makefile | 4 + .../Applications/app_test/test_uart.c | 95 +++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 APP_Framework/Applications/app_test/test_uart.c diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index 136492b05..cfc834c73 100644 --- a/APP_Framework/Applications/app_test/Kconfig +++ b/APP_Framework/Applications/app_test/Kconfig @@ -77,6 +77,19 @@ menu "test app" bool "Config test socket(lwip)" default n + menuconfig USER_TEST_UART + select BSP_USING_UART + select BSP_USING_UART6 + bool "Config test uart" + default n + if USER_TEST_UART + if ADD_XIZI_FEATURES + config UART_DEV_DRIVER + string "Set uart dev path" + default "/dev/usart6_dev6" + endif + endif + menuconfig USER_TEST_RS485 select BSP_USING_UART select BSP_USING_GPIO diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 21e3dd8f5..36a913ca6 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -49,6 +49,10 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) SRC_FILES += test_i2c.c endif + ifeq ($(CONFIG_USER_TEST_UART),y) + SRC_FILES += test_uart.c + endif + ifeq ($(CONFIG_USER_TEST_GPIO),y) SRC_FILES += test_gpio.c endif diff --git a/APP_Framework/Applications/app_test/test_uart.c b/APP_Framework/Applications/app_test/test_uart.c new file mode 100644 index 000000000..c714269ab --- /dev/null +++ b/APP_Framework/Applications/app_test/test_uart.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2020 AIIT XUOS Lab + * XiUOS is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +/** + * @file: test_uart.c + * @brief: a application of uart function, uart6 for edu-arm32 + * @version: 3.0 + * @author: AIIT XUOS Lab + * @date: 2023/8/11 + */ +#include +#include +#include + +#include + +#include +#ifdef ADD_XIZI_FEATURES + +void TestUart(int argc, char* argv[]) +{ + static char program_info[] = "App Test uart, sending a message through uart and receive messages from uart."; + static const char* const usages[] = { + "TestUart -m arg", + NULL, + }; + + bool is_help = false; + char* msg = NULL; + struct argparse_option options[] = { + OPT_HELP(&is_help), + OPT_STRING('m', "message", &msg, "MESSAGE to send through uart.", NULL, 0, 0), + OPT_END(), + }; + struct argparse argparse; + argparse_init(&argparse, options, usages, 0); + argparse_describe(&argparse, NULL, program_info); + argc = argparse_parse(&argparse, argc, (const char**)argv); + if (is_help) { + return; + } + + int uart_fd = PrivOpen(UART_DEV_DRIVER, O_RDWR); + if (uart_fd < 0) { + printf("open pin fd error:%d\n", uart_fd); + return; + } + printf("[%s] Info: Uart and pin fopen success\n", __func__); + + struct SerialDataCfg uart_cfg; + memset(&uart_cfg, 0, sizeof(struct SerialDataCfg)); + + uart_cfg.serial_baud_rate = BAUD_RATE_115200; + uart_cfg.serial_data_bits = DATA_BITS_8; + uart_cfg.serial_stop_bits = STOP_BITS_1; + uart_cfg.serial_parity_mode = PARITY_NONE; + uart_cfg.serial_bit_order = BIT_ORDER_LSB; + uart_cfg.serial_invert_mode = NRZ_NORMAL; + uart_cfg.serial_buffer_size = SERIAL_RB_BUFSZ; + uart_cfg.serial_timeout = -1; + uart_cfg.is_ext_uart = 0; + + struct PrivIoctlCfg ioctl_cfg; + ioctl_cfg.ioctl_driver_type = SERIAL_TYPE; + ioctl_cfg.args = (void*)&uart_cfg; + + if (0 != PrivIoctl(uart_fd, OPE_INT, &ioctl_cfg)) { + printf("[%s] Err: ioctl uart fd error %d\n", __func__, uart_fd); + PrivClose(uart_fd); + return; + } + PrivWrite(uart_fd, msg, strlen(msg)); + + char recv_buf[100]; + while (1) { + memset(recv_buf, 0, sizeof(recv_buf)); + PrivRead(uart_fd, recv_buf, sizeof(recv_buf)); + printf("[%s] Info: Recv from uart: %s\n", __func__, recv_buf); + } + + PrivClose(uart_fd); + return; +} + +PRIV_SHELL_CMD_FUNCTION(TestUart, a uart test sample, PRIV_SHELL_CMD_MAIN_ATTR); +#endif \ No newline at end of file From 7077cf74c23900521f6867068b88d2aa326d69d7 Mon Sep 17 00:00:00 2001 From: Liu_Weichao Date: Thu, 17 Aug 2023 15:04:48 +0800 Subject: [PATCH 33/33] feat add RS485 using usart4 for edu_arm32 board and fix test_rs485 error --- .../Applications/app_test/test_rs485.c | 118 ++++++++++++++---- .../Framework/control/shared/control_io.c | 2 +- .../third_party_driver/usart/Kconfig | 15 +++ .../third_party_driver/usart/connect_usart.c | 91 ++++++++++++++ .../XiZi_IIoT/resources/serial/dev_serial.c | 14 ++- 5 files changed, 211 insertions(+), 29 deletions(-) diff --git a/APP_Framework/Applications/app_test/test_rs485.c b/APP_Framework/Applications/app_test/test_rs485.c index bbe6f1c4d..0b562359c 100644 --- a/APP_Framework/Applications/app_test/test_rs485.c +++ b/APP_Framework/Applications/app_test/test_rs485.c @@ -22,26 +22,94 @@ #include #ifdef ADD_XIZI_FEATURES -#define BSP_485_DIR_PIN 24 +//edu-arm board dir pin PG01----no.67 in XiZi_IIoT/board/edu_arm32/third_party_driver/gpio/connect_gpio.c +#define BSP_485_DIR_PIN 67 + +static int pin_fd; +static int uart_fd; +static char write_485_data[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; +static char read_485_data[8] = {0}; + +/** + * @description: Set Uart 485 Input + * @return + */ +static void Set485Input(void) +{ + struct PinStat pin_stat; + pin_stat.pin = BSP_485_DIR_PIN; + pin_stat.val = GPIO_LOW; + PrivWrite(pin_fd, &pin_stat, 1); +} + +/** + * @description: Set Uart 485 Output + * @return + */ +static void Set485Output(void) +{ + struct PinStat pin_stat; + pin_stat.pin = BSP_485_DIR_PIN; + pin_stat.val = GPIO_HIGH; + PrivWrite(pin_fd, &pin_stat, 1); +} + +/** + * @description: Control Framework Serial Write + * @param write_data - write data + * @param length - length + * @return + */ +void Rs485Write(uint8_t *write_data, int length) +{ + Set485Output(); + PrivTaskDelay(20); + + PrivWrite(uart_fd, write_data, length); + + PrivTaskDelay(15); + Set485Input(); +} + +/** + * @description: Control Framework Serial Read + * @param read_data - read data + * @param length - length + * @return read data size + */ +int Rs485Read(uint8_t *read_data, int length) +{ + int data_size = 0; + int data_recv_size = 0; + + while (data_size < length) { + data_recv_size = PrivRead(uart_fd, read_data + data_size, length - data_size); + data_size += data_recv_size; + } + + //need to wait 30ms , make sure write cmd again and receive data successfully + PrivTaskDelay(30); + + return data_size; +} void Test485(void) { - int pin_fd = PrivOpen(RS485_PIN_DEV_DRIVER, O_RDWR); - if (pin_fd < 0) - { + int read_data_length = 0; + pin_fd = PrivOpen(RS485_PIN_DEV_DRIVER, O_RDWR); + if (pin_fd < 0) { printf("open pin fd error:%d\n", pin_fd); return; } - int uart_fd = PrivOpen(RS485_UART_DEV_DRIVER, O_RDWR); - if (uart_fd < 0) - { + uart_fd = PrivOpen(RS485_UART_DEV_DRIVER, O_RDWR); + if (uart_fd < 0) { printf("open pin fd error:%d\n", uart_fd); return; } printf("uart and pin fopen success\n"); - //config led pin in board + //config dir pin in board struct PinParam pin_parameter; memset(&pin_parameter, 0, sizeof(struct PinParam)); pin_parameter.cmd = GPIO_CONFIG_MODE; @@ -68,36 +136,34 @@ void Test485(void) uart_cfg.serial_bit_order = BIT_ORDER_LSB; uart_cfg.serial_invert_mode = NRZ_NORMAL; uart_cfg.serial_buffer_size = SERIAL_RB_BUFSZ; - uart_cfg.serial_timeout = 1000; + uart_cfg.serial_timeout = -1; uart_cfg.is_ext_uart = 0; ioctl_cfg.ioctl_driver_type = SERIAL_TYPE; ioctl_cfg.args = (void *)&uart_cfg; - if (0 != PrivIoctl(uart_fd, OPE_INT, &ioctl_cfg)) - { + if (0 != PrivIoctl(uart_fd, OPE_INT, &ioctl_cfg)) { printf("ioctl uart fd error %d\n", uart_fd); PrivClose(uart_fd); return; } - struct PinStat pin_dir; - pin_dir.pin = BSP_485_DIR_PIN; - while (1) - { - pin_dir.val = GPIO_HIGH; - PrivWrite(pin_fd,&pin_dir,0); - PrivWrite(uart_fd,"Hello world!\n",sizeof("Hello world!\n")); - PrivTaskDelay(100); + Rs485Write(write_485_data, sizeof(write_485_data)); - pin_dir.val = GPIO_LOW; - PrivWrite(pin_fd,&pin_dir,0); - char recv_buff[100]; - memset(recv_buff,0,sizeof(recv_buff)); - PrivRead(uart_fd,recv_buff,20); - printf("%s",recv_buff); - PrivTaskDelay(100); + while(1) { + printf("ready to read data\n"); + + read_data_length = Rs485Read(read_485_data, sizeof(read_485_data)); + printf("%s read data length %d\n", __func__, read_data_length); + for (int i = 0; i < read_data_length; i ++) { + printf("i %d read data 0x%x\n", i, read_485_data[i]); + } + Rs485Write(read_485_data, read_data_length); + memset(read_485_data, 0, sizeof(read_485_data)); + + printf("read data done\n"); } + PrivClose(pin_fd); PrivClose(uart_fd); return; diff --git a/APP_Framework/Framework/control/shared/control_io.c b/APP_Framework/Framework/control/shared/control_io.c index fbb01b7ec..5d9e00060 100644 --- a/APP_Framework/Framework/control/shared/control_io.c +++ b/APP_Framework/Framework/control/shared/control_io.c @@ -180,7 +180,7 @@ int SerialRead(uint8_t *read_data, int length) int data_recv_size = 0; while (data_size < length) { - data_recv_size = PrivRead(uart_fd, read_data + data_recv_size, length); + data_recv_size = PrivRead(uart_fd, read_data + data_size, length - data_size); data_size += data_recv_size; } diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/usart/Kconfig b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/usart/Kconfig index 7f51f8e7b..626811be0 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/usart/Kconfig +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/usart/Kconfig @@ -13,6 +13,21 @@ menuconfig BSP_USING_UART3 default "usart3_dev3" endif +menuconfig BSP_USING_UART4 + bool "Enable USART4 for RS485" + default y + if BSP_USING_UART4 + config SERIAL_BUS_NAME_4 + string "serial bus 4 name" + default "usart4" + config SERIAL_DRV_NAME_4 + string "serial bus 4 driver name" + default "usart4_drv" + config SERIAL_4_DEVICE_NAME_0 + string "serial bus 4 device 0 name" + default "usart4_dev4" + endif + menuconfig BSP_USING_UART6 bool "Enable USART6" default n diff --git a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/usart/connect_usart.c b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/usart/connect_usart.c index 47a9ca68a..b16c709bd 100644 --- a/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/usart/connect_usart.c +++ b/Ubiquitous/XiZi_IIoT/board/edu-arm32/third_party_driver/usart/connect_usart.c @@ -50,6 +50,14 @@ Modification: #define USART3_TX_PIN (GPIO_PIN_10) #endif +#if defined(BSP_USING_UART4) +#define USART4_RX_PORT (GPIO_PORT_E) +#define USART4_RX_PIN (GPIO_PIN_07) + +#define USART4_TX_PORT (GPIO_PORT_G) +#define USART4_TX_PIN (GPIO_PIN_00) +#endif + #if defined(BSP_USING_UART6) #define USART6_RX_PORT (GPIO_PORT_H) #define USART6_RX_PIN (GPIO_PIN_06) @@ -72,6 +80,12 @@ static x_err_t UartGpioInit(CM_USART_TypeDef *USARTx) GPIO_SetFunc(USART3_TX_PORT, USART3_TX_PIN, GPIO_FUNC_32); break; #endif +#ifdef BSP_USING_UART4 + case (uint32)CM_USART4: + GPIO_SetFunc(USART4_RX_PORT, USART4_RX_PIN, GPIO_FUNC_33); + GPIO_SetFunc(USART4_TX_PORT, USART4_TX_PIN, GPIO_FUNC_32); + break; +#endif #ifdef BSP_USING_UART6 case (uint32)CM_USART6: GPIO_SetFunc(USART6_RX_PORT, USART6_RX_PIN, GPIO_FUNC_37); @@ -123,6 +137,32 @@ void Uart3RxErrIrqHandler(void) } #endif +#ifdef BSP_USING_UART4 +struct SerialBus serial_bus_4; +struct SerialDriver serial_driver_4; +struct SerialHardwareDevice serial_device_4; + +void Uart4RxIrqHandler(void) +{ + x_base lock = 0; + lock = DISABLE_INTERRUPT(); + + SerialSetIsr(&serial_device_4, SERIAL_EVENT_RX_IND); + + ENABLE_INTERRUPT(lock); +} + +void Uart4RxErrIrqHandler(void) +{ + x_base lock = 0; + lock = DISABLE_INTERRUPT(); + + UartRxErrIsr(&serial_bus_4, &serial_driver_4, &serial_device_4); + + ENABLE_INTERRUPT(lock); +} +#endif + #ifdef BSP_USING_UART6 struct SerialBus serial_bus_6; struct SerialDriver serial_driver_6; @@ -499,6 +539,57 @@ int HwUsartInit(void) } #endif +#ifdef BSP_USING_UART4 + static struct SerialCfgParam serial_cfg_4; + memset(&serial_cfg_4, 0, sizeof(struct SerialCfgParam)); + + static struct SerialDevParam serial_dev_param_4; + memset(&serial_dev_param_4, 0, sizeof(struct SerialDevParam)); + + static struct UsartHwCfg serial_hw_cfg_4; + memset(&serial_hw_cfg_4, 0, sizeof(struct UsartHwCfg)); + + serial_driver_4.drv_done = &drv_done; + serial_driver_4.configure = SerialDrvConfigure; + serial_device_4.hwdev_done = &hwdev_done; + + serial_cfg_4.data_cfg = data_cfg_init; + + //default irq configure + serial_hw_cfg_4.uart_device = CM_USART4; + serial_hw_cfg_4.usart_clock = FCG3_PERIPH_USART4; + serial_hw_cfg_4.rx_err_irq.irq_config.irq_num = BSP_UART4_RXERR_IRQ_NUM; + serial_hw_cfg_4.rx_err_irq.irq_config.irq_prio = BSP_UART4_RXERR_IRQ_PRIO; + serial_hw_cfg_4.rx_err_irq.irq_config.int_src = INT_SRC_USART4_EI; + + serial_hw_cfg_4.rx_irq.irq_config.irq_num = BSP_UART4_RX_IRQ_NUM; + serial_hw_cfg_4.rx_irq.irq_config.irq_prio = BSP_UART4_RX_IRQ_PRIO; + serial_hw_cfg_4.rx_irq.irq_config.int_src = INT_SRC_USART4_RI; + + serial_hw_cfg_4.rx_err_irq.irq_callback = Uart4RxErrIrqHandler; + serial_hw_cfg_4.rx_irq.irq_callback = Uart4RxIrqHandler; + + hc32_install_irq_handler(&serial_hw_cfg_4.rx_err_irq.irq_config, serial_hw_cfg_4.rx_err_irq.irq_callback, 0); + + serial_cfg_4.hw_cfg.private_data = (void *)&serial_hw_cfg_4; + serial_driver_4.private_data = (void *)&serial_cfg_4; + + serial_dev_param_4.serial_work_mode = SIGN_OPER_INT_RX; + serial_device_4.haldev.private_data = (void *)&serial_dev_param_4; + + ret = BoardSerialBusInit(&serial_bus_4, &serial_driver_4, SERIAL_BUS_NAME_4, SERIAL_DRV_NAME_4); + if (EOK != ret) { + KPrintf("HwUartInit uart4 error ret %u\n", ret); + return ERROR; + } + + ret = BoardSerialDevBend(&serial_device_4, (void *)&serial_cfg_4, SERIAL_BUS_NAME_4, SERIAL_4_DEVICE_NAME_0); + if (EOK != ret) { + KPrintf("HwUartInit uart4 error ret %u\n", ret); + return ERROR; + } +#endif + #ifdef BSP_USING_UART6 static struct SerialCfgParam serial_cfg_6; memset(&serial_cfg_6, 0, sizeof(struct SerialCfgParam)); diff --git a/Ubiquitous/XiZi_IIoT/resources/serial/dev_serial.c b/Ubiquitous/XiZi_IIoT/resources/serial/dev_serial.c index 37396e6ea..156db4ebb 100644 --- a/Ubiquitous/XiZi_IIoT/resources/serial/dev_serial.c +++ b/Ubiquitous/XiZi_IIoT/resources/serial/dev_serial.c @@ -50,6 +50,7 @@ Modification: #include #include +static int serial_isr_cnt = 0; static DoubleLinklistType serialdev_linklist; static int SerialWorkModeCheck(struct SerialDevParam *serial_dev_param) @@ -138,6 +139,9 @@ static inline int SerialDevIntRead(struct SerialHardwareDevice *serial_dev, stru if (serial_dev->serial_fifo.serial_rx->serial_recv_num == serial_dev->serial_fifo.serial_rx->serial_send_num) { if (RET_FALSE == serial_dev->serial_fifo.serial_rx->serial_rx_full) { CriticalAreaUnLock(lock); + if (0 == serial_isr_cnt) { + KSemaphoreSetValue(serial_dev->haldev.dev_sem, 0); + } break; } } @@ -151,11 +155,13 @@ static inline int SerialDevIntRead(struct SerialHardwareDevice *serial_dev, stru if (RET_TRUE == serial_dev->serial_fifo.serial_rx->serial_rx_full) { serial_dev->serial_fifo.serial_rx->serial_rx_full = RET_FALSE; } + + if (serial_isr_cnt > 0) { + serial_isr_cnt--; + } CriticalAreaUnLock(lock); - //MdelayKTask(20); - *read_data = get_char; read_data++; read_length--; @@ -713,6 +719,10 @@ void SerialSetIsr(struct SerialHardwareDevice *serial_dev, int event) if (serial_dev->haldev.dev_recv_callback) { serial_dev->haldev.dev_recv_callback((void *)serial_dev, serial_rx_length); } + + lock = CriticalAreaLock(); + serial_isr_cnt += 1; + CriticalAreaUnLock(lock); KSemaphoreAbandon(serial_dev->haldev.dev_sem); }