From b0e709d9d643b603b965dbce7340ab3e6da467ac Mon Sep 17 00:00:00 2001 From: jzlv Date: Thu, 23 Sep 2021 19:16:38 +0800 Subject: [PATCH] [feat][rt-thread] add rt-thread component and msh demo --- components/rt-thread/.gitignore | 40 + components/rt-thread/LICENSE | 201 + components/rt-thread/README.md | 151 + components/rt-thread/README_zh.md | 178 + .../bsp/bouffalolab/bl602/board/board.c | 95 + .../bsp/bouffalolab/bl602/board/board.h | 13 + .../bsp/bouffalolab/bl602/board/bouffalo.ld | 234 ++ .../bsp/bouffalolab/bl602/board/startup_gcc.S | 103 + .../bsp/bouffalolab/bl602/rtconfig.h | 129 + .../bsp/bouffalolab/bl702/board/board.c | 95 + .../bsp/bouffalolab/bl702/board/board.h | 13 + .../bsp/bouffalolab/bl702/board/bouffalo.ld | 245 ++ .../bsp/bouffalolab/bl702/board/startup_gcc.S | 103 + .../bsp/bouffalolab/bl702/rtconfig.h | 129 + components/rt-thread/components/finsh/cmd.c | 1153 ++++++ components/rt-thread/components/finsh/finsh.h | 249 ++ .../rt-thread/components/finsh/finsh_api.h | 218 ++ .../components/finsh/finsh_compiler.c | 918 +++++ .../rt-thread/components/finsh/finsh_error.c | 55 + .../rt-thread/components/finsh/finsh_error.h | 23 + .../rt-thread/components/finsh/finsh_heap.c | 279 ++ .../rt-thread/components/finsh/finsh_heap.h | 19 + .../rt-thread/components/finsh/finsh_init.c | 57 + .../rt-thread/components/finsh/finsh_node.c | 183 + .../rt-thread/components/finsh/finsh_node.h | 69 + .../rt-thread/components/finsh/finsh_ops.c | 603 +++ .../rt-thread/components/finsh/finsh_ops.h | 116 + .../rt-thread/components/finsh/finsh_parser.c | 986 +++++ .../rt-thread/components/finsh/finsh_parser.h | 18 + .../rt-thread/components/finsh/finsh_token.c | 598 +++ .../rt-thread/components/finsh/finsh_token.h | 63 + .../rt-thread/components/finsh/finsh_var.c | 142 + .../rt-thread/components/finsh/finsh_var.h | 41 + .../rt-thread/components/finsh/finsh_vm.c | 363 ++ .../rt-thread/components/finsh/finsh_vm.h | 35 + components/rt-thread/components/finsh/msh.c | 647 ++++ components/rt-thread/components/finsh/msh.h | 23 + .../rt-thread/components/finsh/msh_file.c | 706 ++++ components/rt-thread/components/finsh/shell.c | 897 +++++ components/rt-thread/components/finsh/shell.h | 109 + .../rt-thread/components/finsh/symbol.c | 66 + .../rt-thread/include/libc/libc_dirent.h | 17 + .../rt-thread/include/libc/libc_fcntl.h | 98 + components/rt-thread/include/libc/libc_stat.h | 110 + components/rt-thread/include/rtdbg.h | 189 + components/rt-thread/include/rtdebug.h | 145 + components/rt-thread/include/rtdef.h | 1170 ++++++ components/rt-thread/include/rthw.h | 196 + components/rt-thread/include/rtlibc.h | 32 + components/rt-thread/include/rtm.h | 46 + components/rt-thread/include/rtservice.h | 315 ++ components/rt-thread/include/rtthread.h | 597 +++ .../libcpu/risc-v/common/context_gcc.S | 288 ++ .../rt-thread/libcpu/risc-v/common/cpuport.c | 164 + .../rt-thread/libcpu/risc-v/common/cpuport.h | 43 + .../libcpu/risc-v/common/riscv-ops.h | 41 + .../libcpu/risc-v/common/riscv-plic.h | 113 + .../libcpu/risc-v/e24/interrupt_gcc.S | 126 + components/rt-thread/src/clock.c | 145 + components/rt-thread/src/components.c | 261 ++ components/rt-thread/src/cpu.c | 188 + components/rt-thread/src/device.c | 437 +++ components/rt-thread/src/idle.c | 382 ++ components/rt-thread/src/ipc.c | 3250 +++++++++++++++++ components/rt-thread/src/irq.c | 133 + components/rt-thread/src/kservice.c | 1388 +++++++ components/rt-thread/src/mem.c | 717 ++++ components/rt-thread/src/memheap.c | 1013 +++++ components/rt-thread/src/mempool.c | 454 +++ components/rt-thread/src/object.c | 577 +++ components/rt-thread/src/scheduler.c | 990 +++++ components/rt-thread/src/signal.c | 589 +++ components/rt-thread/src/slab.c | 937 +++++ components/rt-thread/src/thread.c | 921 +++++ components/rt-thread/src/timer.c | 808 ++++ .../rt-thread/thread_delay/CMakeLists.txt | 9 + examples/rt-thread/thread_delay/main.c | 36 + examples/rt-thread/thread_delay/readme.md | 7 + 78 files changed, 27297 insertions(+) create mode 100644 components/rt-thread/.gitignore create mode 100644 components/rt-thread/LICENSE create mode 100644 components/rt-thread/README.md create mode 100644 components/rt-thread/README_zh.md create mode 100644 components/rt-thread/bsp/bouffalolab/bl602/board/board.c create mode 100644 components/rt-thread/bsp/bouffalolab/bl602/board/board.h create mode 100644 components/rt-thread/bsp/bouffalolab/bl602/board/bouffalo.ld create mode 100644 components/rt-thread/bsp/bouffalolab/bl602/board/startup_gcc.S create mode 100644 components/rt-thread/bsp/bouffalolab/bl602/rtconfig.h create mode 100644 components/rt-thread/bsp/bouffalolab/bl702/board/board.c create mode 100644 components/rt-thread/bsp/bouffalolab/bl702/board/board.h create mode 100644 components/rt-thread/bsp/bouffalolab/bl702/board/bouffalo.ld create mode 100644 components/rt-thread/bsp/bouffalolab/bl702/board/startup_gcc.S create mode 100644 components/rt-thread/bsp/bouffalolab/bl702/rtconfig.h create mode 100644 components/rt-thread/components/finsh/cmd.c create mode 100644 components/rt-thread/components/finsh/finsh.h create mode 100644 components/rt-thread/components/finsh/finsh_api.h create mode 100644 components/rt-thread/components/finsh/finsh_compiler.c create mode 100644 components/rt-thread/components/finsh/finsh_error.c create mode 100644 components/rt-thread/components/finsh/finsh_error.h create mode 100644 components/rt-thread/components/finsh/finsh_heap.c create mode 100644 components/rt-thread/components/finsh/finsh_heap.h create mode 100644 components/rt-thread/components/finsh/finsh_init.c create mode 100644 components/rt-thread/components/finsh/finsh_node.c create mode 100644 components/rt-thread/components/finsh/finsh_node.h create mode 100644 components/rt-thread/components/finsh/finsh_ops.c create mode 100644 components/rt-thread/components/finsh/finsh_ops.h create mode 100644 components/rt-thread/components/finsh/finsh_parser.c create mode 100644 components/rt-thread/components/finsh/finsh_parser.h create mode 100644 components/rt-thread/components/finsh/finsh_token.c create mode 100644 components/rt-thread/components/finsh/finsh_token.h create mode 100644 components/rt-thread/components/finsh/finsh_var.c create mode 100644 components/rt-thread/components/finsh/finsh_var.h create mode 100644 components/rt-thread/components/finsh/finsh_vm.c create mode 100644 components/rt-thread/components/finsh/finsh_vm.h create mode 100644 components/rt-thread/components/finsh/msh.c create mode 100644 components/rt-thread/components/finsh/msh.h create mode 100644 components/rt-thread/components/finsh/msh_file.c create mode 100644 components/rt-thread/components/finsh/shell.c create mode 100644 components/rt-thread/components/finsh/shell.h create mode 100644 components/rt-thread/components/finsh/symbol.c create mode 100644 components/rt-thread/include/libc/libc_dirent.h create mode 100644 components/rt-thread/include/libc/libc_fcntl.h create mode 100644 components/rt-thread/include/libc/libc_stat.h create mode 100644 components/rt-thread/include/rtdbg.h create mode 100644 components/rt-thread/include/rtdebug.h create mode 100644 components/rt-thread/include/rtdef.h create mode 100644 components/rt-thread/include/rthw.h create mode 100644 components/rt-thread/include/rtlibc.h create mode 100644 components/rt-thread/include/rtm.h create mode 100644 components/rt-thread/include/rtservice.h create mode 100644 components/rt-thread/include/rtthread.h create mode 100644 components/rt-thread/libcpu/risc-v/common/context_gcc.S create mode 100644 components/rt-thread/libcpu/risc-v/common/cpuport.c create mode 100644 components/rt-thread/libcpu/risc-v/common/cpuport.h create mode 100644 components/rt-thread/libcpu/risc-v/common/riscv-ops.h create mode 100644 components/rt-thread/libcpu/risc-v/common/riscv-plic.h create mode 100644 components/rt-thread/libcpu/risc-v/e24/interrupt_gcc.S create mode 100644 components/rt-thread/src/clock.c create mode 100644 components/rt-thread/src/components.c create mode 100644 components/rt-thread/src/cpu.c create mode 100644 components/rt-thread/src/device.c create mode 100644 components/rt-thread/src/idle.c create mode 100644 components/rt-thread/src/ipc.c create mode 100644 components/rt-thread/src/irq.c create mode 100644 components/rt-thread/src/kservice.c create mode 100644 components/rt-thread/src/mem.c create mode 100644 components/rt-thread/src/memheap.c create mode 100644 components/rt-thread/src/mempool.c create mode 100644 components/rt-thread/src/object.c create mode 100644 components/rt-thread/src/scheduler.c create mode 100644 components/rt-thread/src/signal.c create mode 100644 components/rt-thread/src/slab.c create mode 100644 components/rt-thread/src/thread.c create mode 100644 components/rt-thread/src/timer.c create mode 100644 examples/rt-thread/thread_delay/CMakeLists.txt create mode 100644 examples/rt-thread/thread_delay/main.c create mode 100644 examples/rt-thread/thread_delay/readme.md diff --git a/components/rt-thread/.gitignore b/components/rt-thread/.gitignore new file mode 100644 index 00000000..4e612654 --- /dev/null +++ b/components/rt-thread/.gitignore @@ -0,0 +1,40 @@ +*.pyc +*.map +*.dblite +*.elf +*.bin +*.hex +*.axf +*.exe +*.pdb +*.idb +*.ilk +*.old +build +Debug +documentation/html +*~ +*.o +*.obj +*.bak +*.dep +*.lib +*.a +*.i +*.d +tools/kconfig-frontends/kconfig-mconf +packages +dist +cconfig.h +GPUCache + +#cscope files +cscope.* +ncscope.* + +#ctag files +tags + +.idea +CMakeLists.txt +cmake-build-debug diff --git a/components/rt-thread/LICENSE b/components/rt-thread/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/components/rt-thread/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/components/rt-thread/README.md b/components/rt-thread/README.md new file mode 100644 index 00000000..d0ca35c9 --- /dev/null +++ b/components/rt-thread/README.md @@ -0,0 +1,151 @@ +# RT-Thread # + +[中文页](README_zh.md) | + +[![GitHub](https://img.shields.io/github/license/RT-Thread/rt-thread.svg)](https://github.com/RT-Thread/rt-thread/blob/master/LICENSE) +[![GitHub release](https://img.shields.io/github/release/RT-Thread/rt-thread.svg)](https://github.com/RT-Thread/rt-thread/releases) +[![Build Status](https://travis-ci.org/RT-Thread/rt-thread.svg)](https://travis-ci.org/RT-Thread/rt-thread) +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/RT-Thread/rt-thread?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![GitHub pull-requests](https://img.shields.io/github/issues-pr/RT-Thread/rt-thread.svg)](https://github.com/RT-Thread/rt-thread/pulls) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](https://github.com/RT-Thread/rt-thread/pulls) + +# Introduction + +RT-Thread was born in 2006, it is an open source, neutral, and community-based real-time operating system (RTOS). + +RT-Thread is mainly written in C language, easy to understand and easy to port(can be quickly port to a wide range of mainstream MCUs and module chips). It applies object-oriented programming methods to real-time system design, making the code elegant, structured, modular, and very tailorable. + +RT-Thread has Standard version and Nano version. For resource-constrained microcontroller (MCU) systems, the NANO kernel version that requires only 3KB Flash and 1.2KB RAM memory resources can be tailored with easy-to-use tools; And for resource-rich IoT devices, RT-Thread can use the on-line software package management tool, together with system configuration tools, to achieve intuitive and rapid modular cutting, seamlessly import rich software packages, thus achieving complex functions like Android's graphical interface and touch sliding effects, smart voice interaction effects, and so on. + +## RT-Thread Architecture + +RT-Thread has not only a real-time kernel, but also rich components. Its architecture is as follows: + + +![architecture](./documentation/figures/architecture.png) + + +It includes: + +- Kernel layer: RT-Thread kernel, the core part of RT-Thread, includes the implementation of objects in the kernel system, such as multi-threading and its scheduling, semaphore, mailbox, message queue, memory management, timer, etc.; libcpu/BSP (Chip Migration Related Files/Board Support Package) is closely related to hardware and consists of peripheral drivers and CPU porting. + +- Components and Service Layer: Components are based on upper-level software on top of the RT-Thread kernel, such as virtual file systems, FinSH command-line interfaces, network frameworks, device frameworks, and more. Its modular design allows for high internal cohesion inside the components and low coupling between components. + +- RT-Thread software package: A general-purpose software component running on the RT-Thread IoT operating system platform for different application areas, consisting of description information, source code or library files. RT-Thread provides an open package platform with officially available or developer-supplied packages that provide developers with a choice of reusable packages that are an important part of the RT-Thread ecosystem. The package ecosystem is critical to the choice of an operating system because these packages are highly reusable and modular, making it easy for application developers to build the system they want in the shortest amount of time. RT-Thread supports more than 180 software packages. + +## RT-Thread Features + +- Designed for resource-constrained devices, the minimum kernel requires only 1.2KB of RAM and 3 KB of Flash. + +- Has rich components and a prosperous and fast growing package ecosystem. + +- Elegant code style, easy to use, read and master. + +- High Scalability. RT-Thread has high-quality scalable software architecture, loose coupling, modularity, is easy to tailor and expand. + +- Supports high-performance applications. + +- Supports cross-platform and a wide range of chips. + +## Code Catalogue + + RT-Thread source code catalog is shown as follow: + +| Name | Description | +| ------------- | ------------------------------------------------------- | +| BSP | Board Support Package based on the porting of various development boards | +| components | Components, such as finsh shell, file system, protocol stack etc. | +| documentation | Related documents, like coding style, doxygen etc. | +| examples | Related sample code | +| include | Head files of RT-Thread kernel | +| libcpu | CPU porting code such as ARM/MIPS/RISC-V etc. | +| src | The source files for the RT-Thread kernel. | +| tools | The script files for the RT-Thread command build tool. | + +RT-Thread has now been ported for nearly 90 development boards, most BSPs support MDK, IAR development environment and GCC compiler, and have provided default MDK and IAR project, which allows users to add their own application code directly based on the project. Each BSP has a similar directory structure, and most BSPs provide a README.md file, which is a markdown-format file that contains the basic introduction of BSP, and introduces how to simply start using BSP. + +Env is a development tool developed by RT-Thread which provides a build environment, text graphical system configuration, and package management capabilities for project based on the RT-Thread operating system. Its built-in `menuconfig` provides an easy-to-use configuration tool. It can tailor the kernels, components and software packages freely, so that the system can be constructed by building blocks. + +- [Download Env Tool](https://www.rt-thread.io/download.html?download=Env) +- [User manual of Env](https://github.com/RT-Thread/rtthread-manual-doc/blob/master/env/env.md) + +# Resources + +## Supported Architectures + +RT-Thread supports many architectures, and has covered the major architectures in current applications. Architecture and chip manufacturer involved: + +- **ARM Cortex-M0/M0+**:manufacturers like ST +- **ARM Cortex-M3**:manufacturers like ST、Winner Micro、MindMotion, ect. +- **ARM Cortex-M4**:manufacturers like ST、Nuvton、NXP、GigaDevice、Realtek、Ambiq Micro, ect. +- **ARM Cortex-M7**:manufacturers like ST、NXP +- **ARM Cortex-M23**:manufacturers like GigaDevice +- **ARM Cortex-R4** +- **ARM Cortex-A8/A9**:manufacturers like NXP +- **ARM7**:manufacturers like Samsung +- **ARM9**:manufacturers like Allwinner、Xilinx 、GOKE +- **ARM11**:manufacturers like Fullhan +- **MIPS32**:manufacturers like loongson、Ingenic +- **RISC-V**:manufacturers like Hifive、Kendryte、[Nuclei](https://nucleisys.com/) +- **ARC**:manufacturers like SYNOPSYS +- **DSP**:manufacturers like TI +- **C-Sky** +- **x86** + +## Supported IDE and Compiler + +The main IDE/compilers supported by RT-Thread are: + +- MDK KEIL +- IAR +- GCC +- RT-Thread Studio + +Use Python-based [scons](http://www.scons.org/) for command-line builds. + +RT-Thread Studio Demonstration: + +![studio](./documentation/figures/studio.gif) + +## Getting Started + +RT-Thread BSP can be compiled directly and downloaded to the corresponding development board for use. In addition, RT-Thread also provides qemu-vexpress-a9 BSP, which can be used without hardware platform. See the getting started guide below for details. + +- [Getting Started of QEMU (Windows)](https://github.com/RT-Thread/rtthread-manual-doc/blob/master/documentation/quick_start_qemu/quick_start_qemu.md) + +- [Getting Started of QEMU (Ubuntu)](https://github.com/RT-Thread/rtthread-manual-doc/blob/master/documentation/quick_start_qemu/quick_start_qemu_linux.md) + +## Documentation + +[RT-Thread Programming Guide](https://github.com/RT-Thread/rtthread-manual-doc) | [RT-Thread Supported Chips & Boards](https://www.rt-thread.io/board.html) | +[RT-Thread Software Package](https://github.com/RT-Thread/packages) | [RT-Thread Studio](https://www.rt-thread.io/studio.html) + +## Sample + +[Kernel Sample](https://github.com/RT-Thread-packages/kernel-sample) | [Device Sample Code](https://github.com/RT-Thread-packages/peripheral-sample) | [File System Sample Code](https://github.com/RT-Thread-packages/filesystem-sample ) | [Network Sample Code](https://github.com/RT-Thread-packages/network-sample ) | + +[Based on the STM32L475 IoT Board SDK](https://github.com/RT-Thread/IoT_Board) | [Based on the W601 IoT Board SDK](https://github.com/RT-Thread/W601_IoT_Board) + +# License + +RT-Thread is an open source software and has been licensed under Apache License Version 2.0 since v3.1.1. License information and copyright information can generally be seen at the beginning of the code: + +```c +/* Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * ... + */ +``` + +To avoid possible future license conflicts, developers need to sign a Contributor License Agreement (CLA) when submitting PR to RT-Thread. + +# Community + +RT-Thread is very grateful for the support from all community developers, and if you have any ideas, suggestions or questions in the process of using RT-Thread, RT-Thread can be reached by the following means, and we are also updating RT-Thread in real time on these channels. At the same time, Any questions can be asked in the [issue section of rtthread-manual-doc](https://github.com/RT-Thread/rtthread-manual-doc/issues). By creating a new issue to describe your questions, community members will answer them. + +[Website](https://www.rt-thread.io) | [Twitter](https://twitter.com/rt_thread) | [Youtube]( https://www.youtube.com/channel/UCdDHtIfSYPq4002r27ffqPw?view_as=subscriber) | [Gitter]( https://gitter.im/RT-Thread) | [Facebook](https://www.facebook.com/RT-Thread-IoT-OS-110395723808463/?modal=admin_todo_tour) | [Medium](https://medium.com/@rt_thread) + +# Contribution + +If you are interested in RT-Thread and want to join in the development of RT-Thread and become a code contributor,please refer to the [Code Contribution Guide](https://github.com/RT-Thread/rtthread-manual-doc/blob/master/documentation/contribution_guide/contribution_guide.md). diff --git a/components/rt-thread/README_zh.md b/components/rt-thread/README_zh.md new file mode 100644 index 00000000..9cdfa307 --- /dev/null +++ b/components/rt-thread/README_zh.md @@ -0,0 +1,178 @@ +## 简介 + +RT-Thread诞生于2006年,是一款以开源、中立、社区化发展起来的物联网操作系统。 +RT-Thread主要采用 C 语言编写,浅显易懂,且具有方便移植的特性(可快速移植到多种主流 MCU 及模组芯片上)。RT-Thread把面向对象的设计方法应用到实时系统设计中,使得代码风格优雅、架构清晰、系统模块化并且可裁剪性非常好。 + +RT-Thread有完整版和Nano版,对于资源受限的微控制器(MCU)系统,可通过简单易用的工具,裁剪出仅需要 3KB Flash、1.2KB RAM 内存资源的 NANO 内核版本;而相对资源丰富的物联网设备,可使用RT-Thread完整版,通过在线的软件包管理工具,配合系统配置工具实现直观快速的模块化裁剪,并且可以无缝地导入丰富的软件功能包,实现类似 Android 的图形界面及触摸滑动效果、智能语音交互效果等复杂功能。 + +## **RT-Thread架构** + +RT-Thread是一个集实时操作系统(RTOS)内核、中间件组件的物联网操作系统,架构如下: + +![architecturezh](./documentation/figures/architecturezh.png) + + + +- 内核层:RT-Thread内核,是 RT-Thread的核心部分,包括了内核系统中对象的实现,例如多线程及其调度、信号量、邮箱、消息队列、内存管理、定时器等;libcpu/BSP(芯片移植相关文件 / 板级支持包)与硬件密切相关,由外设驱动和 CPU 移植构成。 + +- 组件与服务层:组件是基于 RT-Thread内核之上的上层软件,例如虚拟文件系统、FinSH命令行界面、网络框架、设备框架等。采用模块化设计,做到组件内部高内聚,组件之间低耦合。 + + +- RT-Thread软件包:运行于 RT-Thread物联网操作系统平台上,面向不同应用领域的通用软件组件,由描述信息、源代码或库文件组成。RT-Thread提供了开放的软件包平台,这里存放了官方提供或开发者提供的软件包,该平台为开发者提供了众多可重用软件包的选择,这也是 RT-Thread生态的重要组成部分。软件包生态对于一个操作系统的选择至关重要,因为这些软件包具有很强的可重用性,模块化程度很高,极大的方便应用开发者在最短时间内,打造出自己想要的系统。RT-Thread已经支持的软件包数量已经达到 180+。 + + + +## RT-Thread的特点 + +- 资源占用极低,超低功耗设计,最小内核(Nano版本)仅需1.2KB RAM,3KB Flash。 + +- 组件丰富,繁荣发展的软件包生态 。 + +- 简单易用 ,优雅的代码风格,易于阅读、掌握。 + +- 高度可伸缩,优质的可伸缩的软件架构,松耦合,模块化,易于裁剪和扩展。 + +- 强大,支持高性能应用。 + +- 跨平台、芯片支持广泛。 + + +## **代码目录** + +RT-Thread源代码目录结构如下图所示: + +| 名称 | 描述 | +| ------------- | ------------------------------------------------------- | +| BSP | Board Support Package(板级支持包)基于各种开发板的移植 | +| components | RT-Thread 的各个组件代码,例如 finsh,gui 等。 | +| documentation | 相关文档,如编码规范等 | +| examples | 相关示例代码 | +| include | RT-Thread 内核的头文件。 | +| libcpu | 各类芯片的移植代码。 | +| src | RT-Thread 内核的源文件。 | +| tools | RT-Thread 命令构建工具的脚本文件。 | + +目前RT-Thread已经针对将近90种开发板做好了移植,大部分 BSP 都支持 MDK﹑IAR开发环境和GCC编译器,并且已经提供了默认的 MDK 和 IAR 工程,用户可以直接基于这个工程添加自己的应用代码。 每个 BSP 的目录结构高度统一,且都提供一个 README.md 文件,包含了对这个 BSP 的基本介绍,以及相应的说明,方便用户快速上手。 + +Env 是RT-Thread推出的开发辅助工具,针对基于RT-Thread操作系统的项目工程,提供编译构建环境、图形化系统配置及软件包管理功能。其内置的 menuconfig 提供了简单易用的配置剪裁工具,可对内核、组件和软件包进行自由裁剪,使系统以搭积木的方式进行构建。 + +[下载 Env 工具](https://www.rt-thread.org/page/download.html) + +[Env 用户手册](https://github.com/RT-Thread/rtthread-manual-doc/blob/master/env/env.md) + + +# 资源文档 + +## **硬件支持** + +RT-Thread RTOS 支持许多架构,并且已经涵盖了当前应用中的主要架构。涉及的架构和芯片制造商有: + +- ARM Cortex-M0/M0+:如芯片制造商 ST + +- ARM Cortex-M3:如芯片制造商 ST、全志、灵动等. + +- ARM Cortex-M4:如芯片制造商 ST、Nuvton、NXP、GigaDevice、Realtek、Ambiq Micro等 + +- ARM Cortex-M7:如芯片制造商 ST、NXP + +- ARM Cortex-M23:如芯片制造商 GigaDevice + +- ARM Cortex-R4 + +- ARM Cortex-A8/A9:如芯片制造商 NXP + +- ARM7:如芯片制造商Samsung + +- ARM9:如芯片制造商Allwinner、Xilinx 、GOKE + +- ARM11:如芯片制造商Fullhan + +- MIPS32:如芯片制造商loongson、Ingenic + +- RISC-V:如芯片制造商Hifive、Kendryte、[芯来Nuclei](https://nucleisys.com/) + +- ARC:如芯片制造商SYNOPSYS + +- DSP:如芯片制造商 TI + +- C-Sky + +- x86 + + +## **支持的 IDE 和编译器** + +RT-Thread主要支持的IDE/编译器包括: + +- MDK KEIL + +- IAR + +- Gcc + +- RT-Thread Studio + +使用基于 Python 的 [scons](http://www.scons.org/) 进行命令行生成。 + +RT-Thread Studio演示: + + +![studiozh](./documentation/figures/studiozh.gif) + + +## **快速上手** + +RT-Thread BSP可以直接编译并下载到相应的开发板使用。此外,RT-Thread还提供 qemu-vexpress-a9 BSP,无需硬件平台即可使用。有关详细信息,请参阅下面的入门指南。 + +[QEMU 入门指南(Windows)](https://github.com/RT-Thread/rtthread-manual-doc/blob/master/documentation/quick_start_qemu/quick_start_qemu.md) + +[QEMU 入门指南(Ubuntu)](https://github.com/RT-Thread/rtthread-manual-doc/blob/master/documentation/quick_start_qemu/quick_start_qemu_linux.md) + + +## 文档 + +[文档中心](https://www.rt-thread.org/document/site/ ) | [编程指南](https://www.rt-thread.org/document/site/programming-manual/basic/basic/ ) + +[应用 RT-Thread 实现蜂鸣器播放器教程](https://www.rt-thread.org/document/site/tutorial/beep-player/) | [分布式温度监控系统教程](https://www.rt-thread.org/document/site/tutorial/temperature-system/ ) | [智能车连载教程](https://www.rt-thread.org/document/site/tutorial/smart-car/ ) + +## 例程 + +[内核示例](https://github.com/RT-Thread-packages/kernel-sample) | [设备示例代码](https://github.com/RT-Thread-packages/peripheral-sample ) | [文件系统示例代码](https://github.com/RT-Thread-packages/filesystem-sample ) | [网络示例代码](https://github.com/RT-Thread-packages/network-sample ) | [RT-Thread API参考手册](https://www.rt-thread.org/document/api/ ) + +[基于STM32L475 IoT Board 开发板SDK](https://github.com/RT-Thread/IoT_Board) | [基于W601 IoT Board 开发板SDK](https://github.com/RT-Thread/W601_IoT_Board) + +## 视频 + +RT-Thread视频中心提供了一系列RT-Thread相关教程及分享内容。 + +如:内核入门系列 | Env系列 | 网络系列 | Nano移植系列 | RT-Thread Studio系列 | 柿饼UI系列 | 答疑直播系列 | 社区作品系列 + +更多详情,请前往 [视频中心](https://www.rt-thread.org/page/video.html) + +# **许可协议** + +RT-Thread系统完全开源,3.1.0 及以前的版本遵循 GPL V2 + 开源许可协议。从 3.1.0 以后的版本遵循Apache License 2.0开源许可协议,可以免费在商业产品中使用,并且不需要公开私有代码。 + +``` +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ +``` + +# 社区支持 + +RT-Thread非常感谢所有社区小伙伴的支持,在使用RT-Thread的过程中若您有任何的想法,建议或疑问都可通过以下方式联系到 RT-Thread,我们也实时在这些频道更新RT-Thread的最新讯息。同时,任何问题都可以在 [issue section](https://github.com/RT-Thread/rtthread-manual-doc/issues) 中提出。通过创建一个issue来描述您的问题,社区成员将回答这些问题。 + +[官网]( https://www.rt-thread.org) | [论坛]( https://www.rt-thread.org/qa/forum.php) | [哔哩哔哩官方账号](https://space.bilibili.com/423462075?spm_id_from=333.788.b_765f7570696e666f.2) | [微博官方账号](https://weibo.com/rtthread?is_hot=1) | [知乎官方账号](https://www.zhihu.com/topic/19964581/hot) + +RT-Thread微信公众号: + +![qrcode](./documentation/figures/qrcode.png) + + +# 贡献代码 + +如果您对RT-Thread感兴趣,并希望参与RT-Thread的开发并成为代码贡献者,请参阅[代码贡献指南](https://github.com/RT-Thread/rtthread-manual-doc/blob/master/documentation/contribution_guide/contribution_guide.md)。 + diff --git a/components/rt-thread/bsp/bouffalolab/bl602/board/board.c b/components/rt-thread/bsp/bouffalolab/bl602/board/board.c new file mode 100644 index 00000000..780fee1f --- /dev/null +++ b/components/rt-thread/bsp/bouffalolab/bl602/board/board.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include +#include "board.h" +#include "hal_uart.h" +#include "ring_buffer.h" +#include "shell.h" + +#define UART_RX_SIZE 256 +uint8_t uart_rx_mem[UART_RX_SIZE]; +Ring_Buffer_Type uart_rx_rb; +static struct rt_semaphore shell_rx_sem; /* 定义一个静态信号量 */ + +void shell_irq_callback(struct device *dev, void *args, uint32_t size, uint32_t state) +{ + Ring_Buffer_Write_Byte(&uart_rx_rb, *(uint8_t *)args); + rt_sem_release(&shell_rx_sem); +} + +void systick_isr() +{ + rt_interrupt_enter(); + rt_tick_increase(); + rt_interrupt_leave(); +} + +void rt_hw_console_output(const char *str) +{ + rt_size_t i = 0, size = 0; + char a = '\r'; + + struct device *uart = device_find("debug_log"); + + size = rt_strlen(str); + for (i = 0; i < size; i++) { + if (*(str + i) == '\n') { + device_write(uart, 0, (uint8_t *)&a, 1); + } + device_write(uart, 0, (uint8_t *)(str + i), 1); + } +} + +char rt_hw_console_getchar(void) +{ + char ch = 0; + /* 从 ringbuffer 中拿出数据 */ + while (Ring_Buffer_Read_Byte(&uart_rx_rb, (rt_uint8_t *)&ch) != 1) { + rt_sem_take(&shell_rx_sem, RT_WAITING_FOREVER); + } + return ch; +} + +void rt_hw_board_init(void) +{ + bflb_platform_init(0); + + bflb_platform_set_alarm_time(RT_TICK_PER_SECOND, systick_isr); + +#if defined(RT_USING_FINSH) + struct device *uart = device_find("debug_log"); + if (uart) { + Ring_Buffer_Init(&uart_rx_rb, uart_rx_mem, UART_RX_SIZE, NULL, NULL); + device_set_callback(uart, shell_irq_callback); + device_control(uart, DEVICE_CTRL_SET_INT, (void *)(UART_RX_FIFO_IT)); + } +#endif +#if defined(RT_USING_HEAP) + rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END); +#endif + +#ifdef RT_USING_COMPONENTS_INIT + rt_components_board_init(); +#endif + +#ifdef RT_USING_DEVICE + rt_console_set_device(RT_CONSOLE_DEVICE_NAME); +#endif + + return; +} + +int uart_rx_sem_init(void) +{ + rt_sem_init(&(shell_rx_sem), "shell_rx", 0, 0); + return 0; +} +INIT_BOARD_EXPORT(uart_rx_sem_init); +INIT_APP_EXPORT(finsh_system_init); diff --git a/components/rt-thread/bsp/bouffalolab/bl602/board/board.h b/components/rt-thread/bsp/bouffalolab/bl602/board/board.h new file mode 100644 index 00000000..6aac94ca --- /dev/null +++ b/components/rt-thread/bsp/bouffalolab/bl602/board/board.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __BOARD__ +#define __BOARD__ + +#endif diff --git a/components/rt-thread/bsp/bouffalolab/bl602/board/bouffalo.ld b/components/rt-thread/bsp/bouffalolab/bl602/board/bouffalo.ld new file mode 100644 index 00000000..a3826ae1 --- /dev/null +++ b/components/rt-thread/bsp/bouffalolab/bl602/board/bouffalo.ld @@ -0,0 +1,234 @@ +/**************************************************************************************** +* @file map.txt +* +* @brief This file is the map file (gnuarm or armgcc). +* +* Copyright (C) BouffaloLab 2018 +* +**************************************************************************************** +*/ + +/* configure the CPU type */ +OUTPUT_ARCH( "riscv" ) +/* link with the standard c library */ +/*INPUT(-lc)*/ +/* link with the standard GCC library */ +/*INPUT(-lgcc)*/ +/* configure the entry point */ +ENTRY(_enter) + +StackSize = 0x0400; /* 1KB */ +HeapSize = 0x1000; /* 4KB */ + +MEMORY +{ + xip_memory (rx) : ORIGIN = 0x23000000, LENGTH = 1024K + itcm_memory (rx) : ORIGIN = 0x22010000, LENGTH = 16K + dtcm_memory (rx) : ORIGIN = 0x42014000, LENGTH = 48K + ram_memory (!rx) : ORIGIN = 0x42020000, LENGTH = 176K +} + +SECTIONS +{ + PROVIDE(__metal_chicken_bit = 0); + + .text : + { + . = ALIGN(4); + __text_code_start__ = .; + + KEEP (*(.text.metal.init.enter)) + KEEP (*(SORT_NONE(.init))) + + /* section information for shell */ + . = ALIGN(4); + __fsymtab_start = .; + KEEP(*(FSymTab)) + __fsymtab_end = .; + + . = ALIGN(4); + __vsymtab_start = .; + KEEP(*(VSymTab)) + __vsymtab_end = .; + + /* section information for initial. */ + . = ALIGN(4); + __rt_init_start = .; + KEEP(*(SORT(.rti_fn*))) + __rt_init_end = .; + + *(.text) + *(.text.*) + + /*put .rodata**/ + *(EXCLUDE_FILE( *bl602_glb*.o* \ + *bl602_pds*.o* \ + *bl602_common*.o* \ + *bl602_sf_cfg*.o* \ + *bl602_sf_ctrl*.o* \ + *bl602_sflash*.o* \ + *bl602_xip_sflash*.o* \ + *bl602_ef_ctrl*.o*) .rodata*) + *(.rodata) + *(.rodata.*) + + *(.srodata) + *(.srodata.*) + + . = ALIGN(4); + __text_code_end__ = .; + + } > xip_memory + + . = ALIGN(4); + __itcm_load_addr = .; + + .itcm_region : AT (__itcm_load_addr) + { + . = ALIGN(4); + __tcm_code_start__ = .; + + *(.tcm_code.*) + *(.tcm_const.*) + *(.sclock_rlt_code.*) + *(.sclock_rlt_const.*) + + *bl602_glb*.o*(.rodata*) + *bl602_pds*.o*(.rodata*) + *bl602_common*.o*(.rodata*) + *bl602_sf_cfg*.o*(.rodata*) + *bl602_sf_ctrl*.o*(.rodata*) + *bl602_sflash*.o*(.rodata*) + *bl602_xip_sflash*.o*(.rodata*) + *bl602_ef_ctrl*.o*(.rodata*) + + + . = ALIGN(4); + __tcm_code_end__ = .; + } > itcm_memory + + __dtcm_load_addr = __itcm_load_addr + SIZEOF(.itcm_region); + + .dtcm_region : AT (__dtcm_load_addr) + { + . = ALIGN(4); + __tcm_data_start__ = .; + + *(.tcm_data) + + . = ALIGN(4); + __tcm_data_end__ = .; + } > dtcm_memory + + + /* .heap_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of heap sections, and assign + * values to heap symbols later */ + .heap_dummy (NOLOAD): + { + . = ALIGN(0x4); + . = . + HeapSize; + . = ALIGN(0x4); + } > dtcm_memory + + _HeapBase = ORIGIN(dtcm_memory) + LENGTH(dtcm_memory) - StackSize - HeapSize; + _HeapSize = HeapSize; + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(_HeapBase >= __tcm_data_end__, "region RAM overflowed with stack") + + /*************************************************************************/ + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (NOLOAD): + { + . = ALIGN(0x4); + . = . + StackSize; + . = ALIGN(0x4); + } > dtcm_memory + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(dtcm_memory) + LENGTH(dtcm_memory); + PROVIDE( __freertos_irq_stack_top = __StackTop); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __tcm_data_end__, "region RAM overflowed with stack") + /*************************************************************************/ + + __system_ram_load_addr = __dtcm_load_addr + SIZEOF(.dtcm_region); + + .system_ram_data_region : AT (__system_ram_load_addr) + { + . = ALIGN(4); + __system_ram_data_start__ = .; + + *(.system_ram) + + . = ALIGN(4); + __system_ram_data_end__ = .; + } > ram_memory + + __ram_load_addr = __system_ram_load_addr + SIZEOF(.system_ram_data_region); + + /* Data section */ + RAM_DATA : AT (__ram_load_addr) + { + . = ALIGN(4); + __ram_data_start__ = .; + + PROVIDE( __global_pointer$ = . + 0x800 ); + + *(.data) + *(.data.*) + *(.sdata) + *(.sdata.*) + *(.sdata2) + *(.sdata2.*) + + . = ALIGN(4); + __ram_data_end__ = .; + } > ram_memory + + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start__ = .; + + *(.bss*) + *(.sbss*) + *(COMMON) + + . = ALIGN(4); + __bss_end__ = .; + } > ram_memory + + .noinit_data (NOLOAD) : + { + . = ALIGN(4); + __noinit_data_start__ = .; + + *(.noinit_data*) + + . = ALIGN(4); + __noinit_data_end__ = .; + } > ram_memory + + .heap (NOLOAD): + { + . = ALIGN(4); + __HeapBase = .; + + /*__end__ = .;*/ + /*end = __end__;*/ + KEEP(*(.heap*)) + + . = ALIGN(4); + __HeapLimit = .; + } > ram_memory + __HeapLimit = ORIGIN(ram_memory) + LENGTH(ram_memory); + +} + diff --git a/components/rt-thread/bsp/bouffalolab/bl602/board/startup_gcc.S b/components/rt-thread/bsp/bouffalolab/bl602/board/startup_gcc.S new file mode 100644 index 00000000..5a4961cb --- /dev/null +++ b/components/rt-thread/bsp/bouffalolab/bl602/board/startup_gcc.S @@ -0,0 +1,103 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ +#include "riscv_encoding.h" + +/* This code executes before _start, which is contained inside the C library. + * In embedded systems we want to ensure that _enter, which contains the first + * code to be executed, can be loaded at a specific address. To enable this + * feature we provide the '.text.metal.init.enter' section, which is + * defined to have the first address being where execution should start. */ +.section .text.metal.init.enter +.global _enter +_enter: + .cfi_startproc + + /* Inform the debugger that there is nowhere to backtrace past _enter. */ + .cfi_undefined ra + + /* The absolute first thing that must happen is configuring the global + * pointer register, which must be done with relaxation disabled because + * it's not valid to obtain the address of any symbol without GP + * configured. The C environment might go ahead and do this again, but + * that's safe as it's a fixed register. */ +.option push +.option norelax + la gp, __global_pointer$ +.option pop + + /* Disable global interrupt */ + /*clear_csr(mstatus, MSTATUS_MIE);*/ + csrci mstatus,8 + + /* If there is a clint then interrupts can branch directly to the + * trap handler. Otherwise the interrupt controller will need to be configured + * outside of this file. */ + + la t0, trap_entry + ori t0, t0, 2 + csrw mtvec, t0 + + /* enable chicken bit if core is bullet series*/ + la t0, __metal_chicken_bit + beqz t0, 1f + csrwi 0x7C1, 0 +1: + + /* There may be pre-initialization routines inside the MBI code that run in + * C, so here we set up a C environment. First we set up a stack pointer, + * which is left as a weak reference in order to allow initialization + * routines that do not need a stack to be set up to transparently be + * called. */ + .weak __StackTop + la sp, __StackTop +#ifdef __riscv_float_abi_single + /* deal with FP */ + /* Is F extension present? */ + csrr t0, misa + andi t0, t0, (1 << ('F' - 'A')) + beqz t0, 1f + /* If so, enable it */ + li t0, MSTATUS_FS + csrs mstatus, t0 + fssr x0 +1: +#endif + + /* Check for an initialization routine and call it if one exists, otherwise + * just skip over the call entirely. Note that __metal_initialize isn't + * actually a full C function, as it doesn't end up with the .bss or .data + * segments having been initialized. This is done to avoid putting a + * burden on systems that can be initialized without having a C environment + * set up. */ + call SystemInit + + /* start load code to itcm like. */ + call start_load + + jal System_Post_Init + /* At this point we can enter the C runtime's startup file. The arguments + * to this function are designed to match those provided to the SEE, just + * so we don't have to write another ABI. */ + csrr a0, mhartid + li a1, 0 + li a2, 0 + call entry + + csrci mstatus, (1 << 3) + +__exit: + j __exit + + .cfi_endproc +/* The GCC port might not emit a __register_frame_info symbol, which eventually + * results in a weak undefined reference that eventually causes crash when it + * is dereference early in boot. We really shouldn't need to put this here, + * but to deal with what I think is probably a bug in the linker script I'm + * going to leave this in for now. At least it's fairly cheap :) */ +.weak __register_frame_info +.global __register_frame_info +.section .text.metal.init.__register_frame_info +__register_frame_info: + .cfi_startproc + ret + .cfi_endproc diff --git a/components/rt-thread/bsp/bouffalolab/bl602/rtconfig.h b/components/rt-thread/bsp/bouffalolab/bl602/rtconfig.h new file mode 100644 index 00000000..b09f4353 --- /dev/null +++ b/components/rt-thread/bsp/bouffalolab/bl602/rtconfig.h @@ -0,0 +1,129 @@ +#ifndef RT_CONFIG_H__ +#define RT_CONFIG_H__ + +/* Automatically generated file; DO NOT EDIT. */ +/* RT-Thread Configuration */ + +/* RT-Thread Kernel */ + +#define RT_NAME_MAX 32 +#define RT_ALIGN_SIZE 4 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 1000 +#define RT_USING_OVERFLOW_CHECK +// #define RT_USING_HOOK +// #define RT_USING_IDLE_HOOK +#define RT_IDLE_HOOK_LIST_SIZE 1 +#define IDLE_THREAD_STACK_SIZE 512 +// #define RT_DEBUG +// #define RT_DEBUG_COLOR +#define RT_DEBUG_INIT 0 +#define RT_DEBUG_TIMER 0 +/* Inter-Thread communication */ + +#define RT_USING_SEMAPHORE +#define RT_USING_MUTEX +#define RT_USING_EVENT +#define RT_USING_MAILBOX +#define RT_USING_MESSAGEQUEUE + +/* Memory Management */ + +// #define RT_USING_MEMPOOL +// #define RT_USING_SMALL_MEM +// #define RT_USING_MEMTRACE +// #define RT_USING_HEAP + +/* Kernel Device Object */ + +// #define RT_USING_DEVICE +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 128 +// #define RT_CONSOLE_DEVICE_NAME "dusart" + +/* RT-Thread Components */ + +#define RT_USING_COMPONENTS_INIT +#define RT_USING_USER_MAIN +#define RT_MAIN_THREAD_STACK_SIZE 512 +#define RT_MAIN_THREAD_PRIORITY 10 + +/* C++ features */ + +/* Command shell */ + +#define RT_USING_FINSH +#define FINSH_THREAD_NAME "tshell" +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 5 +#define FINSH_USING_SYMTAB +#define FINSH_USING_DESCRIPTION +#define FINSH_THREAD_PRIORITY 20 +#define FINSH_THREAD_STACK_SIZE 2048 +#define FINSH_CMD_SIZE 80 +#define FINSH_USING_MSH +#define FINSH_USING_MSH_DEFAULT +#define FINSH_USING_MSH_ONLY +#define FINSH_ARG_MAX 10 + +/* Device virtual file system */ + +/* Device Drivers */ + +/* Using WiFi */ + +/* Using USB */ + +/* POSIX layer and C standard library */ + +/* Network */ + +/* Socket abstraction layer */ + +/* light weight TCP/IP stack */ + +/* Modbus master and slave stack */ + +/* AT commands */ + +/* VBUS(Virtual Software BUS) */ + +/* Utilities */ + +/* RT-Thread online packages */ + +/* IoT - internet of things */ + +/* Wi-Fi */ + +/* Marvell WiFi */ + +/* Wiced WiFi */ + +/* IoT Cloud */ + +/* security packages */ + +/* language packages */ + +/* multimedia packages */ + +/* tools packages */ + +/* system packages */ + +/* peripheral libraries and drivers */ + +/* miscellaneous packages */ + +/* sample package */ + +/* samples: kernel and components samples */ + +/* example package: hello */ + +/* Privated Packages of RealThread */ + +/* Network Utilities */ + +#endif diff --git a/components/rt-thread/bsp/bouffalolab/bl702/board/board.c b/components/rt-thread/bsp/bouffalolab/bl702/board/board.c new file mode 100644 index 00000000..780fee1f --- /dev/null +++ b/components/rt-thread/bsp/bouffalolab/bl702/board/board.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include +#include "board.h" +#include "hal_uart.h" +#include "ring_buffer.h" +#include "shell.h" + +#define UART_RX_SIZE 256 +uint8_t uart_rx_mem[UART_RX_SIZE]; +Ring_Buffer_Type uart_rx_rb; +static struct rt_semaphore shell_rx_sem; /* 定义一个静态信号量 */ + +void shell_irq_callback(struct device *dev, void *args, uint32_t size, uint32_t state) +{ + Ring_Buffer_Write_Byte(&uart_rx_rb, *(uint8_t *)args); + rt_sem_release(&shell_rx_sem); +} + +void systick_isr() +{ + rt_interrupt_enter(); + rt_tick_increase(); + rt_interrupt_leave(); +} + +void rt_hw_console_output(const char *str) +{ + rt_size_t i = 0, size = 0; + char a = '\r'; + + struct device *uart = device_find("debug_log"); + + size = rt_strlen(str); + for (i = 0; i < size; i++) { + if (*(str + i) == '\n') { + device_write(uart, 0, (uint8_t *)&a, 1); + } + device_write(uart, 0, (uint8_t *)(str + i), 1); + } +} + +char rt_hw_console_getchar(void) +{ + char ch = 0; + /* 从 ringbuffer 中拿出数据 */ + while (Ring_Buffer_Read_Byte(&uart_rx_rb, (rt_uint8_t *)&ch) != 1) { + rt_sem_take(&shell_rx_sem, RT_WAITING_FOREVER); + } + return ch; +} + +void rt_hw_board_init(void) +{ + bflb_platform_init(0); + + bflb_platform_set_alarm_time(RT_TICK_PER_SECOND, systick_isr); + +#if defined(RT_USING_FINSH) + struct device *uart = device_find("debug_log"); + if (uart) { + Ring_Buffer_Init(&uart_rx_rb, uart_rx_mem, UART_RX_SIZE, NULL, NULL); + device_set_callback(uart, shell_irq_callback); + device_control(uart, DEVICE_CTRL_SET_INT, (void *)(UART_RX_FIFO_IT)); + } +#endif +#if defined(RT_USING_HEAP) + rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END); +#endif + +#ifdef RT_USING_COMPONENTS_INIT + rt_components_board_init(); +#endif + +#ifdef RT_USING_DEVICE + rt_console_set_device(RT_CONSOLE_DEVICE_NAME); +#endif + + return; +} + +int uart_rx_sem_init(void) +{ + rt_sem_init(&(shell_rx_sem), "shell_rx", 0, 0); + return 0; +} +INIT_BOARD_EXPORT(uart_rx_sem_init); +INIT_APP_EXPORT(finsh_system_init); diff --git a/components/rt-thread/bsp/bouffalolab/bl702/board/board.h b/components/rt-thread/bsp/bouffalolab/bl702/board/board.h new file mode 100644 index 00000000..6aac94ca --- /dev/null +++ b/components/rt-thread/bsp/bouffalolab/bl702/board/board.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __BOARD__ +#define __BOARD__ + +#endif diff --git a/components/rt-thread/bsp/bouffalolab/bl702/board/bouffalo.ld b/components/rt-thread/bsp/bouffalolab/bl702/board/bouffalo.ld new file mode 100644 index 00000000..b7012bfc --- /dev/null +++ b/components/rt-thread/bsp/bouffalolab/bl702/board/bouffalo.ld @@ -0,0 +1,245 @@ +/**************************************************************************************** +* @file bl702_flash.ld +* +* @brief This file is the map file (gnuarm or armgcc). +* +* Copyright (C) BouffaloLab 2021 +* +**************************************************************************************** +*/ + +/* configure the CPU type */ +OUTPUT_ARCH( "riscv" ) +/* link with the standard c library */ +/* INPUT(-lc) */ +/* link with the standard GCC library */ +/* INPUT(-lgcc) */ +/* configure the entry point */ +ENTRY(_enter) + +StackSize = 0x1000; /* 4KB */ + +MEMORY +{ + xip_memory (rx) : ORIGIN = 0x23000000, LENGTH = 1024K + itcm_memory (rx) : ORIGIN = 0x22014000, LENGTH = 16K + dtcm_memory (rx) : ORIGIN = 0x42018000, LENGTH = 16K + ram_memory (!rx) : ORIGIN = 0x4201C000, LENGTH = 80K + hbn_memory (rx) : ORIGIN = 0x40010000, LENGTH = 0xE00 /* hbn ram 4K used 3.5K*/ +} + +SECTIONS +{ + PROVIDE(__metal_chicken_bit = 0); + + .text : + { + . = ALIGN(4); + __text_code_start__ = .; + + KEEP (*(.text.metal.init.enter)) + KEEP (*(SORT_NONE(.init))) + /* section information for shell */ + . = ALIGN(4); + __fsymtab_start = .; + KEEP(*(FSymTab)) + __fsymtab_end = .; + + . = ALIGN(4); + __vsymtab_start = .; + KEEP(*(VSymTab)) + __vsymtab_end = .; + + /* section information for usb desc */ + . = ALIGN(4); + _usb_desc_start = .; + KEEP(*(usb_desc)) + _usb_desc_end = .; + + /* section information for initial. */ + . = ALIGN(4); + __rt_init_start = .; + KEEP(*(SORT(.rti_fn*))) + __rt_init_end = .; + + . = ALIGN(4); + *(.text) + *(.text.*) + + /*put .rodata**/ + *(EXCLUDE_FILE( *bl702_glb*.o* \ + *bl702_pds*.o* \ + *bl702_common*.o* \ + *bl702_sf_cfg*.o* \ + *bl702_sf_cfg_ext*.o* \ + *bl702_sf_ctrl*.o* \ + *bl702_sflash*.o* \ + *bl702_sflash_ext*.o* \ + *bl702_xip_sflash*.o* \ + *bl702_xip_sflash_ext*.o* \ + *bl702_ef_ctrl*.o*) .rodata*) + + *(.rodata) + *(.rodata.*) + + *(.srodata) + *(.srodata.*) + + . = ALIGN(4); + __text_code_end__ = .; + } > xip_memory + + . = ALIGN(4); + __itcm_load_addr = .; + + .itcm_region : AT (__itcm_load_addr) + { + . = ALIGN(4); + __tcm_code_start__ = .; + + *(.tcm_code.*) + *(.tcm_const.*) + *(.sclock_rlt_code.*) + *(.sclock_rlt_const.*) + + *bl702_glb*.o*(.rodata*) + *bl702_pds*.o*(.rodata*) + *bl702_common*.o*(.rodata*) + *bl702_sf_cfg*.o*(.rodata*) + *bl702_sf_cfg_ext*.o*(.rodata*) + *bl702_sf_ctrl*.o*(.rodata*) + *bl702_sflash*.o*(.rodata*) + *bl702_sflash_ext*.o*(.rodata*) + *bl702_xip_sflash*.o*(.rodata*) + *bl702_xip_sflash_ext*.o*(.rodata*) + *bl702_ef_ctrl*.o*(.rodata*) + + . = ALIGN(4); + __tcm_code_end__ = .; + } > itcm_memory + + __hbn_load_addr = __itcm_load_addr + SIZEOF(.itcm_region); + + .hbn_ram_region : AT (__hbn_load_addr) + { + . = ALIGN(4); + __hbn_ram_start__ = .; + *(EXCLUDE_FILE(*bl702_hbn_wakeup.o).rodata*) + *bl702_hbn_wakeup.o*(.rodata*) + *(.hbn_ram_code) + *(.hbn_ram_data) + . = ALIGN(4); + __hbn_ram_end__ = .; + } > hbn_memory + + __dtcm_load_addr = __hbn_load_addr + SIZEOF(.hbn_ram_region); + + .dtcm_region : AT (__dtcm_load_addr) + { + . = ALIGN(4); + __tcm_data_start__ = .; + + *(.tcm_data) + /* *finger_print.o(.data*) */ + + . = ALIGN(4); + __tcm_data_end__ = .; + } > dtcm_memory + + /*************************************************************************/ + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (NOLOAD): + { + . = ALIGN(0x4); + . = . + StackSize; + . = ALIGN(0x4); + } > dtcm_memory + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(dtcm_memory) + LENGTH(dtcm_memory); + PROVIDE( __freertos_irq_stack_top = __StackTop); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __tcm_data_end__, "region RAM overflowed with stack") + /*************************************************************************/ + + __system_ram_load_addr = __dtcm_load_addr + SIZEOF(.dtcm_region); + + .system_ram_data_region : AT (__system_ram_load_addr) + { + . = ALIGN(4); + __system_ram_data_start__ = .; + + *(.system_ram) + + . = ALIGN(4); + __system_ram_data_end__ = .; + } > ram_memory + + __ram_load_addr = __system_ram_load_addr + SIZEOF(.system_ram_data_region); + + /* Data section */ + RAM_DATA : AT (__ram_load_addr) + { + . = ALIGN(4); + __ram_data_start__ = .; + + PROVIDE( __global_pointer$ = . + 0x800 ); + + *(.data) + *(.data.*) + *(.sdata) + *(.sdata.*) + *(.sdata2) + *(.sdata2.*) + + . = ALIGN(4); + __ram_data_end__ = .; + } > ram_memory + + .bss (NOLOAD) : + { + . = ALIGN(4); + __bss_start__ = .; + + *(.bss*) + *(.sbss*) + *(COMMON) + + . = ALIGN(4); + __bss_end__ = .; + } > ram_memory + + .noinit_data (NOLOAD) : + { + . = ALIGN(4); + __noinit_data_start__ = .; + + *(.noinit_data*) + + . = ALIGN(4); + __noinit_data_end__ = .; + } > ram_memory + + .heap (NOLOAD): + { + . = ALIGN(4); + __HeapBase = .; + + KEEP(*(.heap*)) + + . = ALIGN(4); + __HeapLimit = .; + } > ram_memory + + PROVIDE (__heap_min_size = 0x400); + __HeapLimit = ORIGIN(ram_memory) + LENGTH(ram_memory); + + ASSERT((__HeapLimit - __HeapBase ) >= __heap_min_size, "heap size is too short.") + +} + diff --git a/components/rt-thread/bsp/bouffalolab/bl702/board/startup_gcc.S b/components/rt-thread/bsp/bouffalolab/bl702/board/startup_gcc.S new file mode 100644 index 00000000..5a4961cb --- /dev/null +++ b/components/rt-thread/bsp/bouffalolab/bl702/board/startup_gcc.S @@ -0,0 +1,103 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ +#include "riscv_encoding.h" + +/* This code executes before _start, which is contained inside the C library. + * In embedded systems we want to ensure that _enter, which contains the first + * code to be executed, can be loaded at a specific address. To enable this + * feature we provide the '.text.metal.init.enter' section, which is + * defined to have the first address being where execution should start. */ +.section .text.metal.init.enter +.global _enter +_enter: + .cfi_startproc + + /* Inform the debugger that there is nowhere to backtrace past _enter. */ + .cfi_undefined ra + + /* The absolute first thing that must happen is configuring the global + * pointer register, which must be done with relaxation disabled because + * it's not valid to obtain the address of any symbol without GP + * configured. The C environment might go ahead and do this again, but + * that's safe as it's a fixed register. */ +.option push +.option norelax + la gp, __global_pointer$ +.option pop + + /* Disable global interrupt */ + /*clear_csr(mstatus, MSTATUS_MIE);*/ + csrci mstatus,8 + + /* If there is a clint then interrupts can branch directly to the + * trap handler. Otherwise the interrupt controller will need to be configured + * outside of this file. */ + + la t0, trap_entry + ori t0, t0, 2 + csrw mtvec, t0 + + /* enable chicken bit if core is bullet series*/ + la t0, __metal_chicken_bit + beqz t0, 1f + csrwi 0x7C1, 0 +1: + + /* There may be pre-initialization routines inside the MBI code that run in + * C, so here we set up a C environment. First we set up a stack pointer, + * which is left as a weak reference in order to allow initialization + * routines that do not need a stack to be set up to transparently be + * called. */ + .weak __StackTop + la sp, __StackTop +#ifdef __riscv_float_abi_single + /* deal with FP */ + /* Is F extension present? */ + csrr t0, misa + andi t0, t0, (1 << ('F' - 'A')) + beqz t0, 1f + /* If so, enable it */ + li t0, MSTATUS_FS + csrs mstatus, t0 + fssr x0 +1: +#endif + + /* Check for an initialization routine and call it if one exists, otherwise + * just skip over the call entirely. Note that __metal_initialize isn't + * actually a full C function, as it doesn't end up with the .bss or .data + * segments having been initialized. This is done to avoid putting a + * burden on systems that can be initialized without having a C environment + * set up. */ + call SystemInit + + /* start load code to itcm like. */ + call start_load + + jal System_Post_Init + /* At this point we can enter the C runtime's startup file. The arguments + * to this function are designed to match those provided to the SEE, just + * so we don't have to write another ABI. */ + csrr a0, mhartid + li a1, 0 + li a2, 0 + call entry + + csrci mstatus, (1 << 3) + +__exit: + j __exit + + .cfi_endproc +/* The GCC port might not emit a __register_frame_info symbol, which eventually + * results in a weak undefined reference that eventually causes crash when it + * is dereference early in boot. We really shouldn't need to put this here, + * but to deal with what I think is probably a bug in the linker script I'm + * going to leave this in for now. At least it's fairly cheap :) */ +.weak __register_frame_info +.global __register_frame_info +.section .text.metal.init.__register_frame_info +__register_frame_info: + .cfi_startproc + ret + .cfi_endproc diff --git a/components/rt-thread/bsp/bouffalolab/bl702/rtconfig.h b/components/rt-thread/bsp/bouffalolab/bl702/rtconfig.h new file mode 100644 index 00000000..b09f4353 --- /dev/null +++ b/components/rt-thread/bsp/bouffalolab/bl702/rtconfig.h @@ -0,0 +1,129 @@ +#ifndef RT_CONFIG_H__ +#define RT_CONFIG_H__ + +/* Automatically generated file; DO NOT EDIT. */ +/* RT-Thread Configuration */ + +/* RT-Thread Kernel */ + +#define RT_NAME_MAX 32 +#define RT_ALIGN_SIZE 4 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 1000 +#define RT_USING_OVERFLOW_CHECK +// #define RT_USING_HOOK +// #define RT_USING_IDLE_HOOK +#define RT_IDLE_HOOK_LIST_SIZE 1 +#define IDLE_THREAD_STACK_SIZE 512 +// #define RT_DEBUG +// #define RT_DEBUG_COLOR +#define RT_DEBUG_INIT 0 +#define RT_DEBUG_TIMER 0 +/* Inter-Thread communication */ + +#define RT_USING_SEMAPHORE +#define RT_USING_MUTEX +#define RT_USING_EVENT +#define RT_USING_MAILBOX +#define RT_USING_MESSAGEQUEUE + +/* Memory Management */ + +// #define RT_USING_MEMPOOL +// #define RT_USING_SMALL_MEM +// #define RT_USING_MEMTRACE +// #define RT_USING_HEAP + +/* Kernel Device Object */ + +// #define RT_USING_DEVICE +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 128 +// #define RT_CONSOLE_DEVICE_NAME "dusart" + +/* RT-Thread Components */ + +#define RT_USING_COMPONENTS_INIT +#define RT_USING_USER_MAIN +#define RT_MAIN_THREAD_STACK_SIZE 512 +#define RT_MAIN_THREAD_PRIORITY 10 + +/* C++ features */ + +/* Command shell */ + +#define RT_USING_FINSH +#define FINSH_THREAD_NAME "tshell" +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 5 +#define FINSH_USING_SYMTAB +#define FINSH_USING_DESCRIPTION +#define FINSH_THREAD_PRIORITY 20 +#define FINSH_THREAD_STACK_SIZE 2048 +#define FINSH_CMD_SIZE 80 +#define FINSH_USING_MSH +#define FINSH_USING_MSH_DEFAULT +#define FINSH_USING_MSH_ONLY +#define FINSH_ARG_MAX 10 + +/* Device virtual file system */ + +/* Device Drivers */ + +/* Using WiFi */ + +/* Using USB */ + +/* POSIX layer and C standard library */ + +/* Network */ + +/* Socket abstraction layer */ + +/* light weight TCP/IP stack */ + +/* Modbus master and slave stack */ + +/* AT commands */ + +/* VBUS(Virtual Software BUS) */ + +/* Utilities */ + +/* RT-Thread online packages */ + +/* IoT - internet of things */ + +/* Wi-Fi */ + +/* Marvell WiFi */ + +/* Wiced WiFi */ + +/* IoT Cloud */ + +/* security packages */ + +/* language packages */ + +/* multimedia packages */ + +/* tools packages */ + +/* system packages */ + +/* peripheral libraries and drivers */ + +/* miscellaneous packages */ + +/* sample package */ + +/* samples: kernel and components samples */ + +/* example package: hello */ + +/* Privated Packages of RealThread */ + +/* Network Utilities */ + +#endif diff --git a/components/rt-thread/components/finsh/cmd.c b/components/rt-thread/components/finsh/cmd.c new file mode 100644 index 00000000..fd08246f --- /dev/null +++ b/components/rt-thread/components/finsh/cmd.c @@ -0,0 +1,1153 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-04-30 Bernard first implementation + * 2006-05-04 Bernard add list_thread, + * list_sem, + * list_timer + * 2006-05-20 Bernard add list_mutex, + * list_mailbox, + * list_msgqueue, + * list_event, + * list_fevent, + * list_mempool + * 2006-06-03 Bernard display stack information in list_thread + * 2006-08-10 Bernard change version to invoke rt_show_version + * 2008-09-10 Bernard update the list function for finsh syscall + * list and sysvar list + * 2009-05-30 Bernard add list_device + * 2010-04-21 yi.qiu add list_module + * 2012-04-29 goprife improve the command line auto-complete feature. + * 2012-06-02 lgnq add list_memheap + * 2012-10-22 Bernard add MS VC++ patch. + * 2016-06-02 armink beautify the list_thread command + * 2018-11-22 Jesven list_thread add smp support + * 2018-12-27 Jesven Fix the problem that disable interrupt too long in list_thread + * Provide protection for the "first layer of objects" when list_* + * 2020-04-07 chenhui add clear + */ + +#include +#include + +#ifdef RT_USING_FINSH + +#include "finsh.h" + +#define LIST_FIND_OBJ_NR 8 + +long hello(void) +{ + rt_kprintf("Hello RT-Thread!\n"); + + return 0; +} +FINSH_FUNCTION_EXPORT(hello, say hello world); + +static long clear(void) +{ + rt_kprintf("\x1b[2J\x1b[H"); + + return 0; +} +FINSH_FUNCTION_EXPORT(clear,clear the terminal screen); +MSH_CMD_EXPORT(clear,clear the terminal screen); + +extern void rt_show_version(void); +long version(void) +{ + rt_show_version(); + + return 0; +} +FINSH_FUNCTION_EXPORT(version, show RT-Thread version information); +MSH_CMD_EXPORT(version, show RT-Thread version information); + +rt_inline void object_split(int len) +{ + while (len--) rt_kprintf("-"); +} + +typedef struct +{ + rt_list_t *list; + rt_list_t **array; + rt_uint8_t type; + int nr; /* input: max nr, can't be 0 */ + int nr_out; /* out: got nr */ +} list_get_next_t; + +static void list_find_init(list_get_next_t *p, rt_uint8_t type, rt_list_t **array, int nr) +{ + struct rt_object_information *info; + rt_list_t *list; + + info = rt_object_get_information((enum rt_object_class_type)type); + list = &info->object_list; + + p->list = list; + p->type = type; + p->array = array; + p->nr = nr; + p->nr_out = 0; +} + +static rt_list_t *list_get_next(rt_list_t *current, list_get_next_t *arg) +{ + int first_flag = 0; + rt_ubase_t level; + rt_list_t *node, *list; + rt_list_t **array; + int nr; + + arg->nr_out = 0; + + if (!arg->nr || !arg->type) + { + return (rt_list_t *)RT_NULL; + } + + list = arg->list; + + if (!current) /* find first */ + { + node = list; + first_flag = 1; + } + else + { + node = current; + } + + level = rt_hw_interrupt_disable(); + + if (!first_flag) + { + struct rt_object *obj; + /* The node in the list? */ + obj = rt_list_entry(node, struct rt_object, list); + if ((obj->type & ~RT_Object_Class_Static) != arg->type) + { + rt_hw_interrupt_enable(level); + return (rt_list_t *)RT_NULL; + } + } + + nr = 0; + array = arg->array; + while (1) + { + node = node->next; + + if (node == list) + { + node = (rt_list_t *)RT_NULL; + break; + } + nr++; + *array++ = node; + if (nr == arg->nr) + { + break; + } + } + + rt_hw_interrupt_enable(level); + arg->nr_out = nr; + return node; +} + +long list_thread(void) +{ + rt_ubase_t level; + list_get_next_t find_arg; + rt_list_t *obj_list[LIST_FIND_OBJ_NR]; + rt_list_t *next = (rt_list_t*)RT_NULL; + const char *item_title = "thread"; + int maxlen; + + list_find_init(&find_arg, RT_Object_Class_Thread, obj_list, sizeof(obj_list)/sizeof(obj_list[0])); + + maxlen = RT_NAME_MAX; + +#ifdef RT_USING_SMP + rt_kprintf("%-*.s cpu bind pri status sp stack size max used left tick error\n", maxlen, item_title); object_split(maxlen); + rt_kprintf( " --- ---- --- ------- ---------- ---------- ------ ---------- ---\n"); +#else + rt_kprintf("%-*.s pri status sp stack size max used left tick error\n", maxlen, item_title); object_split(maxlen); + rt_kprintf( " --- ------- ---------- ---------- ------ ---------- ---\n"); +#endif /*RT_USING_SMP*/ + + do + { + next = list_get_next(next, &find_arg); + { + int i; + for (i = 0; i < find_arg.nr_out; i++) + { + struct rt_object *obj; + struct rt_thread thread_info, *thread; + + obj = rt_list_entry(obj_list[i], struct rt_object, list); + level = rt_hw_interrupt_disable(); + + if ((obj->type & ~RT_Object_Class_Static) != find_arg.type) + { + rt_hw_interrupt_enable(level); + continue; + } + /* copy info */ + memcpy(&thread_info, obj, sizeof thread_info); + rt_hw_interrupt_enable(level); + + thread = (struct rt_thread*)obj; + { + rt_uint8_t stat; + rt_uint8_t *ptr; + +#ifdef RT_USING_SMP + if (thread->oncpu != RT_CPU_DETACHED) + rt_kprintf("%-*.*s %3d %3d %4d ", maxlen, RT_NAME_MAX, thread->name, thread->oncpu, thread->bind_cpu, thread->current_priority); + else + rt_kprintf("%-*.*s N/A %3d %4d ", maxlen, RT_NAME_MAX, thread->name, thread->bind_cpu, thread->current_priority); + +#else + rt_kprintf("%-*.*s %3d ", maxlen, RT_NAME_MAX, thread->name, thread->current_priority); +#endif /*RT_USING_SMP*/ + stat = (thread->stat & RT_THREAD_STAT_MASK); + if (stat == RT_THREAD_READY) rt_kprintf(" ready "); + else if (stat == RT_THREAD_SUSPEND) rt_kprintf(" suspend"); + else if (stat == RT_THREAD_INIT) rt_kprintf(" init "); + else if (stat == RT_THREAD_CLOSE) rt_kprintf(" close "); + else if (stat == RT_THREAD_RUNNING) rt_kprintf(" running"); + +#if defined(ARCH_CPU_STACK_GROWS_UPWARD) + ptr = (rt_uint8_t *)thread->stack_addr + thread->stack_size - 1; + while (*ptr == '#')ptr --; + + rt_kprintf(" 0x%08x 0x%08x %02d%% 0x%08x %03d\n", + ((rt_ubase_t)thread->sp - (rt_ubase_t)thread->stack_addr), + thread->stack_size, + ((rt_ubase_t)ptr - (rt_ubase_t)thread->stack_addr) * 100 / thread->stack_size, + thread->remaining_tick, + thread->error); +#else + ptr = (rt_uint8_t *)thread->stack_addr; + while (*ptr == '#')ptr ++; + + rt_kprintf(" 0x%08x 0x%08x %02d%% 0x%08x %03d\n", + thread->stack_size + ((rt_ubase_t)thread->stack_addr - (rt_ubase_t)thread->sp), + thread->stack_size, + (thread->stack_size - ((rt_ubase_t) ptr - (rt_ubase_t) thread->stack_addr)) * 100 + / thread->stack_size, + thread->remaining_tick, + thread->error); +#endif + } + } + } + } + while (next != (rt_list_t*)RT_NULL); + + return 0; +} +FINSH_FUNCTION_EXPORT(list_thread, list thread); +MSH_CMD_EXPORT(list_thread, list thread); + +static void show_wait_queue(struct rt_list_node *list) +{ + struct rt_thread *thread; + struct rt_list_node *node; + + for (node = list->next; node != list; node = node->next) + { + thread = rt_list_entry(node, struct rt_thread, tlist); + rt_kprintf("%s", thread->name); + + if (node->next != list) + rt_kprintf("/"); + } +} + +#ifdef RT_USING_SEMAPHORE +long list_sem(void) +{ + rt_ubase_t level; + list_get_next_t find_arg; + rt_list_t *obj_list[LIST_FIND_OBJ_NR]; + rt_list_t *next = (rt_list_t*)RT_NULL; + + int maxlen; + const char *item_title = "semaphore"; + + list_find_init(&find_arg, RT_Object_Class_Semaphore, obj_list, sizeof(obj_list)/sizeof(obj_list[0])); + + maxlen = RT_NAME_MAX; + + rt_kprintf("%-*.s v suspend thread\n", maxlen, item_title); object_split(maxlen); + rt_kprintf( " --- --------------\n"); + + do + { + next = list_get_next(next, &find_arg); + { + int i; + for (i = 0; i < find_arg.nr_out; i++) + { + struct rt_object *obj; + struct rt_semaphore *sem; + + obj = rt_list_entry(obj_list[i], struct rt_object, list); + level = rt_hw_interrupt_disable(); + if ((obj->type & ~RT_Object_Class_Static) != find_arg.type) + { + rt_hw_interrupt_enable(level); + continue; + } + rt_hw_interrupt_enable(level); + + sem = (struct rt_semaphore*)obj; + if (!rt_list_isempty(&sem->parent.suspend_thread)) + { + rt_kprintf("%-*.*s %03d %d:", + maxlen, RT_NAME_MAX, + sem->parent.parent.name, + sem->value, + rt_list_len(&sem->parent.suspend_thread)); + show_wait_queue(&(sem->parent.suspend_thread)); + rt_kprintf("\n"); + } + else + { + rt_kprintf("%-*.*s %03d %d\n", + maxlen, RT_NAME_MAX, + sem->parent.parent.name, + sem->value, + rt_list_len(&sem->parent.suspend_thread)); + } + } + } + } + while (next != (rt_list_t*)RT_NULL); + + return 0; +} +FINSH_FUNCTION_EXPORT(list_sem, list semaphore in system); +MSH_CMD_EXPORT(list_sem, list semaphore in system); +#endif + +#ifdef RT_USING_EVENT +long list_event(void) +{ + rt_ubase_t level; + list_get_next_t find_arg; + rt_list_t *obj_list[LIST_FIND_OBJ_NR]; + rt_list_t *next = (rt_list_t*)RT_NULL; + + int maxlen; + const char *item_title = "event"; + + list_find_init(&find_arg, RT_Object_Class_Event, obj_list, sizeof(obj_list)/sizeof(obj_list[0])); + + maxlen = RT_NAME_MAX; + + rt_kprintf("%-*.s set suspend thread\n", maxlen, item_title); object_split(maxlen); + rt_kprintf( " ---------- --------------\n"); + + do + { + next = list_get_next(next, &find_arg); + { + int i; + for (i = 0; i < find_arg.nr_out; i++) + { + struct rt_object *obj; + struct rt_event *e; + + obj = rt_list_entry(obj_list[i], struct rt_object, list); + level = rt_hw_interrupt_disable(); + if ((obj->type & ~RT_Object_Class_Static) != find_arg.type) + { + rt_hw_interrupt_enable(level); + continue; + } + + rt_hw_interrupt_enable(level); + + e = (struct rt_event *)obj; + if (!rt_list_isempty(&e->parent.suspend_thread)) + { + rt_kprintf("%-*.*s 0x%08x %03d:", + maxlen, RT_NAME_MAX, + e->parent.parent.name, + e->set, + rt_list_len(&e->parent.suspend_thread)); + show_wait_queue(&(e->parent.suspend_thread)); + rt_kprintf("\n"); + } + else + { + rt_kprintf("%-*.*s 0x%08x 0\n", + maxlen, RT_NAME_MAX, e->parent.parent.name, e->set); + } + } + } + } + while (next != (rt_list_t*)RT_NULL); + + return 0; +} +FINSH_FUNCTION_EXPORT(list_event, list event in system); +MSH_CMD_EXPORT(list_event, list event in system); +#endif + +#ifdef RT_USING_MUTEX +long list_mutex(void) +{ + rt_ubase_t level; + list_get_next_t find_arg; + rt_list_t *obj_list[LIST_FIND_OBJ_NR]; + rt_list_t *next = (rt_list_t*)RT_NULL; + + int maxlen; + const char *item_title = "mutex"; + + list_find_init(&find_arg, RT_Object_Class_Mutex, obj_list, sizeof(obj_list)/sizeof(obj_list[0])); + + maxlen = RT_NAME_MAX; + + rt_kprintf("%-*.s owner hold suspend thread\n", maxlen, item_title); object_split(maxlen); + rt_kprintf( " -------- ---- --------------\n"); + + do + { + next = list_get_next(next, &find_arg); + { + int i; + for (i = 0; i < find_arg.nr_out; i++) + { + struct rt_object *obj; + struct rt_mutex *m; + + obj = rt_list_entry(obj_list[i], struct rt_object, list); + level = rt_hw_interrupt_disable(); + if ((obj->type & ~RT_Object_Class_Static) != find_arg.type) + { + rt_hw_interrupt_enable(level); + continue; + } + + rt_hw_interrupt_enable(level); + + m = (struct rt_mutex *)obj; + rt_kprintf("%-*.*s %-8.*s %04d %d\n", + maxlen, RT_NAME_MAX, + m->parent.parent.name, + RT_NAME_MAX, + m->owner->name, + m->hold, + rt_list_len(&m->parent.suspend_thread)); + + } + } + } + while (next != (rt_list_t*)RT_NULL); + + return 0; +} +FINSH_FUNCTION_EXPORT(list_mutex, list mutex in system); +MSH_CMD_EXPORT(list_mutex, list mutex in system); +#endif + +#ifdef RT_USING_MAILBOX +long list_mailbox(void) +{ + rt_ubase_t level; + list_get_next_t find_arg; + rt_list_t *obj_list[LIST_FIND_OBJ_NR]; + rt_list_t *next = (rt_list_t*)RT_NULL; + + int maxlen; + const char *item_title = "mailbox"; + + list_find_init(&find_arg, RT_Object_Class_MailBox, obj_list, sizeof(obj_list)/sizeof(obj_list[0])); + + maxlen = RT_NAME_MAX; + + rt_kprintf("%-*.s entry size suspend thread\n", maxlen, item_title); object_split(maxlen); + rt_kprintf( " ---- ---- --------------\n"); + + do + { + next = list_get_next(next, &find_arg); + { + int i; + for (i = 0; i < find_arg.nr_out; i++) + { + struct rt_object *obj; + struct rt_mailbox *m; + + obj = rt_list_entry(obj_list[i], struct rt_object, list); + level = rt_hw_interrupt_disable(); + if ((obj->type & ~RT_Object_Class_Static) != find_arg.type) + { + rt_hw_interrupt_enable(level); + continue; + } + + rt_hw_interrupt_enable(level); + + m = (struct rt_mailbox *)obj; + if (!rt_list_isempty(&m->parent.suspend_thread)) + { + rt_kprintf("%-*.*s %04d %04d %d:", + maxlen, RT_NAME_MAX, + m->parent.parent.name, + m->entry, + m->size, + rt_list_len(&m->parent.suspend_thread)); + show_wait_queue(&(m->parent.suspend_thread)); + rt_kprintf("\n"); + } + else + { + rt_kprintf("%-*.*s %04d %04d %d\n", + maxlen, RT_NAME_MAX, + m->parent.parent.name, + m->entry, + m->size, + rt_list_len(&m->parent.suspend_thread)); + } + + } + } + } + while (next != (rt_list_t*)RT_NULL); + + return 0; +} +FINSH_FUNCTION_EXPORT(list_mailbox, list mail box in system); +MSH_CMD_EXPORT(list_mailbox, list mail box in system); +#endif + +#ifdef RT_USING_MESSAGEQUEUE +long list_msgqueue(void) +{ + rt_ubase_t level; + list_get_next_t find_arg; + rt_list_t *obj_list[LIST_FIND_OBJ_NR]; + rt_list_t *next = (rt_list_t*)RT_NULL; + + int maxlen; + const char *item_title = "msgqueue"; + + list_find_init(&find_arg, RT_Object_Class_MessageQueue, obj_list, sizeof(obj_list)/sizeof(obj_list[0])); + + maxlen = RT_NAME_MAX; + + rt_kprintf("%-*.s entry suspend thread\n", maxlen, item_title); object_split(maxlen); + rt_kprintf( " ---- --------------\n"); + do + { + next = list_get_next(next, &find_arg); + { + int i; + for (i = 0; i < find_arg.nr_out; i++) + { + struct rt_object *obj; + struct rt_messagequeue *m; + + obj = rt_list_entry(obj_list[i], struct rt_object, list); + level = rt_hw_interrupt_disable(); + if ((obj->type & ~RT_Object_Class_Static) != find_arg.type) + { + rt_hw_interrupt_enable(level); + continue; + } + + rt_hw_interrupt_enable(level); + + m = (struct rt_messagequeue *)obj; + if (!rt_list_isempty(&m->parent.suspend_thread)) + { + rt_kprintf("%-*.*s %04d %d:", + maxlen, RT_NAME_MAX, + m->parent.parent.name, + m->entry, + rt_list_len(&m->parent.suspend_thread)); + show_wait_queue(&(m->parent.suspend_thread)); + rt_kprintf("\n"); + } + else + { + rt_kprintf("%-*.*s %04d %d\n", + maxlen, RT_NAME_MAX, + m->parent.parent.name, + m->entry, + rt_list_len(&m->parent.suspend_thread)); + } + } + } + } + while (next != (rt_list_t*)RT_NULL); + + return 0; +} +FINSH_FUNCTION_EXPORT(list_msgqueue, list message queue in system); +MSH_CMD_EXPORT(list_msgqueue, list message queue in system); +#endif + +#ifdef RT_USING_MEMHEAP +long list_memheap(void) +{ + rt_ubase_t level; + list_get_next_t find_arg; + rt_list_t *obj_list[LIST_FIND_OBJ_NR]; + rt_list_t *next = (rt_list_t*)RT_NULL; + + int maxlen; + const char *item_title = "memheap"; + + list_find_init(&find_arg, RT_Object_Class_MemHeap, obj_list, sizeof(obj_list)/sizeof(obj_list[0])); + + maxlen = RT_NAME_MAX; + + rt_kprintf("%-*.s pool size max used size available size\n", maxlen, item_title); object_split(maxlen); + rt_kprintf( " ---------- ------------- --------------\n"); + do + { + next = list_get_next(next, &find_arg); + { + int i; + for (i = 0; i < find_arg.nr_out; i++) + { + struct rt_object *obj; + struct rt_memheap *mh; + + obj = rt_list_entry(obj_list[i], struct rt_object, list); + level = rt_hw_interrupt_disable(); + if ((obj->type & ~RT_Object_Class_Static) != find_arg.type) + { + rt_hw_interrupt_enable(level); + continue; + } + + rt_hw_interrupt_enable(level); + + mh = (struct rt_memheap *)obj; + + rt_kprintf("%-*.*s %-010d %-013d %-05d\n", + maxlen, RT_NAME_MAX, + mh->parent.name, + mh->pool_size, + mh->max_used_size, + mh->available_size); + + } + } + } + while (next != (rt_list_t*)RT_NULL); + + return 0; +} +FINSH_FUNCTION_EXPORT(list_memheap, list memory heap in system); +MSH_CMD_EXPORT(list_memheap, list memory heap in system); +#endif + +#ifdef RT_USING_MEMPOOL +long list_mempool(void) +{ + rt_ubase_t level; + list_get_next_t find_arg; + rt_list_t *obj_list[LIST_FIND_OBJ_NR]; + rt_list_t *next = (rt_list_t*)RT_NULL; + + int maxlen; + const char *item_title = "mempool"; + + list_find_init(&find_arg, RT_Object_Class_MemPool, obj_list, sizeof(obj_list)/sizeof(obj_list[0])); + + maxlen = RT_NAME_MAX; + + rt_kprintf("%-*.s block total free suspend thread\n", maxlen, item_title); object_split(maxlen); + rt_kprintf( " ---- ---- ---- --------------\n"); + do + { + next = list_get_next(next, &find_arg); + { + int i; + for (i = 0; i < find_arg.nr_out; i++) + { + struct rt_object *obj; + struct rt_mempool *mp; + int suspend_thread_count; + rt_list_t *node; + + obj = rt_list_entry(obj_list[i], struct rt_object, list); + level = rt_hw_interrupt_disable(); + if ((obj->type & ~RT_Object_Class_Static) != find_arg.type) + { + rt_hw_interrupt_enable(level); + continue; + } + + rt_hw_interrupt_enable(level); + + mp = (struct rt_mempool *)obj; + + suspend_thread_count = 0; + rt_list_for_each(node, &mp->suspend_thread) + { + suspend_thread_count++; + } + + if (suspend_thread_count > 0) + { + rt_kprintf("%-*.*s %04d %04d %04d %d:", + maxlen, RT_NAME_MAX, + mp->parent.name, + mp->block_size, + mp->block_total_count, + mp->block_free_count, + suspend_thread_count); + show_wait_queue(&(mp->suspend_thread)); + rt_kprintf("\n"); + } + else + { + rt_kprintf("%-*.*s %04d %04d %04d %d\n", + maxlen, RT_NAME_MAX, + mp->parent.name, + mp->block_size, + mp->block_total_count, + mp->block_free_count, + suspend_thread_count); + } + } + } + } + while (next != (rt_list_t*)RT_NULL); + + return 0; +} +FINSH_FUNCTION_EXPORT(list_mempool, list memory pool in system) +MSH_CMD_EXPORT(list_mempool, list memory pool in system); +#endif + +long list_timer(void) +{ + rt_ubase_t level; + list_get_next_t find_arg; + rt_list_t *obj_list[LIST_FIND_OBJ_NR]; + rt_list_t *next = (rt_list_t*)RT_NULL; + + int maxlen; + const char *item_title = "timer"; + + list_find_init(&find_arg, RT_Object_Class_Timer, obj_list, sizeof(obj_list)/sizeof(obj_list[0])); + + maxlen = RT_NAME_MAX; + + rt_kprintf("%-*.s periodic timeout flag\n", maxlen, item_title); object_split(maxlen); + rt_kprintf( " ---------- ---------- -----------\n"); + do { + next = list_get_next(next, &find_arg); + { + int i; + for (i = 0; i < find_arg.nr_out; i++) + { + struct rt_object *obj; + struct rt_timer *timer; + + obj = rt_list_entry(obj_list[i], struct rt_object, list); + level = rt_hw_interrupt_disable(); + if ((obj->type & ~RT_Object_Class_Static) != find_arg.type) + { + rt_hw_interrupt_enable(level); + continue; + } + + rt_hw_interrupt_enable(level); + + timer = (struct rt_timer *)obj; + rt_kprintf("%-*.*s 0x%08x 0x%08x ", + maxlen, RT_NAME_MAX, + timer->parent.name, + timer->init_tick, + timer->timeout_tick); + if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED) + rt_kprintf("activated\n"); + else + rt_kprintf("deactivated\n"); + + } + } + } + while (next != (rt_list_t*)RT_NULL); + + rt_kprintf("current tick:0x%08x\n", rt_tick_get()); + + return 0; +} +FINSH_FUNCTION_EXPORT(list_timer, list timer in system); +MSH_CMD_EXPORT(list_timer, list timer in system); + +#ifdef RT_USING_DEVICE +static char *const device_type_str[] = +{ + "Character Device", + "Block Device", + "Network Interface", + "MTD Device", + "CAN Device", + "RTC", + "Sound Device", + "Graphic Device", + "I2C Bus", + "USB Slave Device", + "USB Host Bus", + "USB OTG Bus", + "SPI Bus", + "SPI Device", + "SDIO Bus", + "PM Pseudo Device", + "Pipe", + "Portal Device", + "Timer Device", + "Miscellaneous Device", + "Sensor Device", + "Touch Device", + "Phy Device", + "Security Device", + "Unknown" +}; + +long list_device(void) +{ + rt_ubase_t level; + list_get_next_t find_arg; + rt_list_t *obj_list[LIST_FIND_OBJ_NR]; + rt_list_t *next = (rt_list_t*)RT_NULL; + + int maxlen; + const char *item_title = "device"; + + list_find_init(&find_arg, RT_Object_Class_Device, obj_list, sizeof(obj_list)/sizeof(obj_list[0])); + + maxlen = RT_NAME_MAX; + + rt_kprintf("%-*.s type ref count\n", maxlen, item_title); object_split(maxlen); + rt_kprintf( " -------------------- ----------\n"); + do + { + next = list_get_next(next, &find_arg); + { + int i; + for (i = 0; i < find_arg.nr_out; i++) + { + struct rt_object *obj; + struct rt_device *device; + + obj = rt_list_entry(obj_list[i], struct rt_object, list); + level = rt_hw_interrupt_disable(); + if ((obj->type & ~RT_Object_Class_Static) != find_arg.type) + { + rt_hw_interrupt_enable(level); + continue; + } + + rt_hw_interrupt_enable(level); + + device = (struct rt_device *)obj; + rt_kprintf("%-*.*s %-20s %-8d\n", + maxlen, RT_NAME_MAX, + device->parent.name, + (device->type <= RT_Device_Class_Unknown) ? + device_type_str[device->type] : + device_type_str[RT_Device_Class_Unknown], + device->ref_count); + + } + } + } + while (next != (rt_list_t*)RT_NULL); + + return 0; +} +FINSH_FUNCTION_EXPORT(list_device, list device in system); +MSH_CMD_EXPORT(list_device, list device in system); +#endif + +long list(void) +{ +#ifndef FINSH_USING_MSH_ONLY + struct finsh_syscall_item *syscall_item; + struct finsh_sysvar_item *sysvar_item; +#endif + + rt_kprintf("--Function List:\n"); + { + struct finsh_syscall *index; + for (index = _syscall_table_begin; + index < _syscall_table_end; + FINSH_NEXT_SYSCALL(index)) + { + /* skip the internal command */ + if (strncmp((char *)index->name, "__", 2) == 0) continue; + +#if defined(FINSH_USING_DESCRIPTION) && defined(FINSH_USING_SYMTAB) + rt_kprintf("%-16s -- %s\n", index->name, index->desc); +#else + rt_kprintf("%s\n", index->name); +#endif + } + } + +#ifndef FINSH_USING_MSH_ONLY + /* list syscall list */ + syscall_item = global_syscall_list; + while (syscall_item != NULL) + { + rt_kprintf("[l] %s\n", syscall_item->syscall.name); + syscall_item = syscall_item->next; + } + + rt_kprintf("--Variable List:\n"); + { + struct finsh_sysvar *index; + for (index = _sysvar_table_begin; + index < _sysvar_table_end; + FINSH_NEXT_SYSVAR(index)) + { +#ifdef FINSH_USING_DESCRIPTION + rt_kprintf("%-16s -- %s\n", index->name, index->desc); +#else + rt_kprintf("%s\n", index->name); +#endif + } + } + + sysvar_item = global_sysvar_list; + while (sysvar_item != NULL) + { + rt_kprintf("[l] %s\n", sysvar_item->sysvar.name); + sysvar_item = sysvar_item->next; + } +#endif + + return 0; +} +FINSH_FUNCTION_EXPORT(list, list all symbol in system) + +#ifndef FINSH_USING_MSH_ONLY +static int str_is_prefix(const char *prefix, const char *str) +{ + while ((*prefix) && (*prefix == *str)) + { + prefix ++; + str ++; + } + + if (*prefix == 0) + return 0; + + return -1; +} + +static int str_common(const char *str1, const char *str2) +{ + const char *str = str1; + + while ((*str != 0) && (*str2 != 0) && (*str == *str2)) + { + str ++; + str2 ++; + } + + return (str - str1); +} + +void list_prefix(char *prefix) +{ + struct finsh_syscall_item *syscall_item; + struct finsh_sysvar_item *sysvar_item; + rt_uint16_t func_cnt, var_cnt; + int length, min_length; + const char *name_ptr; + + func_cnt = 0; + var_cnt = 0; + min_length = 0; + name_ptr = RT_NULL; + + /* checks in system function call */ + { + struct finsh_syscall *index; + for (index = _syscall_table_begin; + index < _syscall_table_end; + FINSH_NEXT_SYSCALL(index)) + { + /* skip internal command */ + if (str_is_prefix("__", index->name) == 0) continue; + + if (str_is_prefix(prefix, index->name) == 0) + { + if (func_cnt == 0) + { + rt_kprintf("--function:\n"); + + if (*prefix != 0) + { + /* set name_ptr */ + name_ptr = index->name; + + /* set initial length */ + min_length = strlen(name_ptr); + } + } + + func_cnt ++; + + if (*prefix != 0) + { + length = str_common(name_ptr, index->name); + if (length < min_length) + min_length = length; + } + +#ifdef FINSH_USING_DESCRIPTION + rt_kprintf("%-16s -- %s\n", index->name, index->desc); +#else + rt_kprintf("%s\n", index->name); +#endif + } + } + } + + /* checks in dynamic system function call */ + syscall_item = global_syscall_list; + while (syscall_item != NULL) + { + if (str_is_prefix(prefix, syscall_item->syscall.name) == 0) + { + if (func_cnt == 0) + { + rt_kprintf("--function:\n"); + if (*prefix != 0 && name_ptr == NULL) + { + /* set name_ptr */ + name_ptr = syscall_item->syscall.name; + + /* set initial length */ + min_length = strlen(name_ptr); + } + } + + func_cnt ++; + + if (*prefix != 0) + { + length = str_common(name_ptr, syscall_item->syscall.name); + if (length < min_length) + min_length = length; + } + + rt_kprintf("[l] %s\n", syscall_item->syscall.name); + } + syscall_item = syscall_item->next; + } + + /* checks in system variable */ + { + struct finsh_sysvar *index; + for (index = _sysvar_table_begin; + index < _sysvar_table_end; + FINSH_NEXT_SYSVAR(index)) + { + if (str_is_prefix(prefix, index->name) == 0) + { + if (var_cnt == 0) + { + rt_kprintf("--variable:\n"); + + if (*prefix != 0 && name_ptr == NULL) + { + /* set name_ptr */ + name_ptr = index->name; + + /* set initial length */ + min_length = strlen(name_ptr); + + } + } + + var_cnt ++; + + if (*prefix != 0) + { + length = str_common(name_ptr, index->name); + if (length < min_length) + min_length = length; + } + +#ifdef FINSH_USING_DESCRIPTION + rt_kprintf("%-16s -- %s\n", index->name, index->desc); +#else + rt_kprintf("%s\n", index->name); +#endif + } + } + } + + /* checks in dynamic system variable */ + sysvar_item = global_sysvar_list; + while (sysvar_item != NULL) + { + if (str_is_prefix(prefix, sysvar_item->sysvar.name) == 0) + { + if (var_cnt == 0) + { + rt_kprintf("--variable:\n"); + if (*prefix != 0 && name_ptr == NULL) + { + /* set name_ptr */ + name_ptr = sysvar_item->sysvar.name; + + /* set initial length */ + min_length = strlen(name_ptr); + } + } + + var_cnt ++; + + if (*prefix != 0) + { + length = str_common(name_ptr, sysvar_item->sysvar.name); + if (length < min_length) + min_length = length; + } + + rt_kprintf("[v] %s\n", sysvar_item->sysvar.name); + } + sysvar_item = sysvar_item->next; + } + + /* only one matched */ + if (name_ptr != NULL) + { + rt_strncpy(prefix, name_ptr, min_length); + } +} +#endif + +#if defined(FINSH_USING_SYMTAB) && !defined(FINSH_USING_MSH_ONLY) +static int dummy = 0; +FINSH_VAR_EXPORT(dummy, finsh_type_int, dummy variable for finsh) +#endif + +#endif /* RT_USING_FINSH */ + diff --git a/components/rt-thread/components/finsh/finsh.h b/components/rt-thread/components/finsh/finsh.h new file mode 100644 index 00000000..dd5e27de --- /dev/null +++ b/components/rt-thread/components/finsh/finsh.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#ifndef __FINSH_H__ +#define __FINSH_H__ + +#include +#include "finsh_api.h" + +/* -- the beginning of option -- */ +#define FINSH_NAME_MAX 16 /* max length of identifier */ +#define FINSH_NODE_MAX 16 /* max number of node */ + +#define FINSH_HEAP_MAX 128 /* max length of heap */ +#define FINSH_STRING_MAX 128 /* max length of string */ +#define FINSH_VARIABLE_MAX 8 /* max number of variable */ + +#define FINSH_STACK_MAX 64 /* max stack size */ +#define FINSH_TEXT_MAX 128 /* max text segment size */ + +#define HEAP_ALIGNMENT 4 /* heap alignment */ + +#define FINSH_GET16(x) (*(x)) | (*((x)+1) << 8) +#define FINSH_GET32(x) (rt_ubase_t)(*(x)) | ((rt_ubase_t)*((x)+1) << 8) | \ + ((rt_ubase_t)*((x)+2) << 16) | ((rt_ubase_t)*((x)+3) << 24) + +#define FINSH_SET16(x, v) \ + do \ + { \ + *(x) = (v) & 0x00ff; \ + (*((x)+1)) = (v) >> 8; \ + } while ( 0 ) + +#define FINSH_SET32(x, v) \ + do \ + { \ + *(x) = (rt_uint32_t)(v) & 0x000000ff; \ + (*((x)+1)) = ((rt_uint32_t)(v) >> 8) & 0x000000ff; \ + (*((x)+2)) = ((rt_uint32_t)(v) >> 16) & 0x000000ff; \ + (*((x)+3)) = ((rt_uint32_t)(v) >> 24); \ + } while ( 0 ) + +/* -- the end of option -- */ + +/* std header file */ +#include +#include +#include +#include +#include + +#define FINSH_VERSION_MAJOR 1 +#define FINSH_VERSION_MINOR 0 + +/** + * @addtogroup finsh + */ +/*@{*/ +#define FINSH_ERROR_OK 0 /**< No error */ +#define FINSH_ERROR_INVALID_TOKEN 1 /**< Invalid token */ +#define FINSH_ERROR_EXPECT_TYPE 2 /**< Expect a type */ +#define FINSH_ERROR_UNKNOWN_TYPE 3 /**< Unknown type */ +#define FINSH_ERROR_VARIABLE_EXIST 4 /**< Variable exist */ +#define FINSH_ERROR_EXPECT_OPERATOR 5 /**< Expect a operator */ +#define FINSH_ERROR_MEMORY_FULL 6 /**< Memory full */ +#define FINSH_ERROR_UNKNOWN_OP 7 /**< Unknown operator */ +#define FINSH_ERROR_UNKNOWN_NODE 8 /**< Unknown node */ +#define FINSH_ERROR_EXPECT_CHAR 9 /**< Expect a character */ +#define FINSH_ERROR_UNEXPECT_END 10 /**< Unexpect end */ +#define FINSH_ERROR_UNKNOWN_TOKEN 11 /**< Unknown token */ +#define FINSH_ERROR_NO_FLOAT 12 /**< Float not supported */ +#define FINSH_ERROR_UNKNOWN_SYMBOL 13 /**< Unknown symbol */ +#define FINSH_ERROR_NULL_NODE 14 /**< Null node */ +/*@}*/ + +/* system call item */ +struct finsh_syscall_item +{ + struct finsh_syscall_item* next; /* next item */ + struct finsh_syscall syscall; /* syscall */ +}; +extern struct finsh_syscall_item *global_syscall_list; + +/* system variable table */ +struct finsh_sysvar +{ + const char* name; /* the name of variable */ +#if defined(FINSH_USING_DESCRIPTION) && defined(FINSH_USING_SYMTAB) + const char* desc; /* description of system variable */ +#endif + uint8_t type; /* the type of variable */ + void* var ; /* the address of variable */ +}; + +#if defined(_MSC_VER) || (defined(__GNUC__) && defined(__x86_64__)) +struct finsh_syscall* finsh_syscall_next(struct finsh_syscall* call); +struct finsh_sysvar* finsh_sysvar_next(struct finsh_sysvar* call); +#define FINSH_NEXT_SYSCALL(index) index=finsh_syscall_next(index) +#define FINSH_NEXT_SYSVAR(index) index=finsh_sysvar_next(index) +#else +#define FINSH_NEXT_SYSCALL(index) index++ +#define FINSH_NEXT_SYSVAR(index) index++ +#endif + +/* system variable item */ +struct finsh_sysvar_item +{ + struct finsh_sysvar_item *next; /* next item */ + struct finsh_sysvar sysvar; /* system variable */ +}; +extern struct finsh_sysvar *_sysvar_table_begin, *_sysvar_table_end; +extern struct finsh_sysvar_item* global_sysvar_list; + +/* find out system variable, which should be implemented in user program */ +struct finsh_sysvar* finsh_sysvar_lookup(const char* name); + + +struct finsh_token +{ + char eof; + char replay; + + int position; + uint8_t current_token; + + union { + char char_value; + int int_value; + long long_value; + } value; + uint8_t string[FINSH_STRING_MAX]; + + uint8_t* line; +}; + +#define FINSH_IDTYPE_VAR 0x01 +#define FINSH_IDTYPE_SYSVAR 0x02 +#define FINSH_IDTYPE_SYSCALL 0x04 +#define FINSH_IDTYPE_ADDRESS 0x08 +struct finsh_node +{ + uint8_t node_type; /* node node_type */ + uint8_t data_type; /* node data node_type */ + uint8_t idtype; /* id node information */ + + union { /* value node */ + char char_value; + short short_value; + int int_value; + long long_value; + void* ptr; + } value; + union + { + /* point to variable identifier or function identifier */ + struct finsh_var *var; + struct finsh_sysvar *sysvar; + struct finsh_syscall*syscall; + }id; + + /* sibling and child node */ + struct finsh_node *sibling, *child; +}; + +struct finsh_parser +{ + uint8_t* parser_string; + + struct finsh_token token; + struct finsh_node* root; +}; + +/** + * @ingroup finsh + * + * The basic data type in finsh shell + */ +enum finsh_type { + finsh_type_unknown = 0, /**< unknown data type */ + finsh_type_void, /**< void */ + finsh_type_voidp, /**< void pointer */ + finsh_type_char, /**< char */ + finsh_type_uchar, /**< unsigned char */ + finsh_type_charp, /**< char pointer */ + finsh_type_short, /**< short */ + finsh_type_ushort, /**< unsigned short */ + finsh_type_shortp, /**< short pointer */ + finsh_type_int, /**< int */ + finsh_type_uint, /**< unsigned int */ + finsh_type_intp, /**< int pointer */ + finsh_type_long, /**< long */ + finsh_type_ulong, /**< unsigned long */ + finsh_type_longp /**< long pointer */ +}; + +/* init finsh environment */ +int finsh_init(struct finsh_parser* parser); +/* flush finsh node, text segment */ +int finsh_flush(struct finsh_parser* parser); +/* reset all of finsh */ +int finsh_reset(struct finsh_parser* parser); +#ifdef RT_USING_DEVICE +void finsh_set_device(const char* device_name); +#endif + +/* run finsh parser to generate abstract synatx tree */ +void finsh_parser_run (struct finsh_parser* parser, const unsigned char* string); +/* run compiler to compile abstract syntax tree */ +int finsh_compiler_run(struct finsh_node* node); +/* run finsh virtual machine */ +void finsh_vm_run(void); + +/* get variable value */ +struct finsh_var* finsh_var_lookup(const char* name); +/* get bottom value of stack */ +long finsh_stack_bottom(void); + +/* get error number of finsh */ +uint8_t finsh_errno(void); +/* get error string */ +const char* finsh_error_string(uint8_t type); + +#ifdef RT_USING_HEAP +/** + * @ingroup finsh + * + * This function appends a system call to finsh runtime environment + * @param name the name of system call + * @param func the function pointer of system call + */ +void finsh_syscall_append(const char* name, syscall_func func); + +/** + * @ingroup finsh + * + * This function appends a system variable to finsh runtime environment + * @param name the name of system variable + * @param type the data type of system variable + * @param addr the address of system variable + */ +void finsh_sysvar_append(const char* name, uint8_t type, void* addr); +#endif +#endif diff --git a/components/rt-thread/components/finsh/finsh_api.h b/components/rt-thread/components/finsh/finsh_api.h new file mode 100644 index 00000000..fd9e6a77 --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_api.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#ifndef FINSH_API_H__ +#define FINSH_API_H__ + +#if defined(_MSC_VER) +#pragma section("FSymTab$f",read) +#pragma section("VSymTab",read) +#endif + +typedef long (*syscall_func)(void); + +/* system call table */ +struct finsh_syscall +{ + const char* name; /* the name of system call */ +#if defined(FINSH_USING_DESCRIPTION) && defined(FINSH_USING_SYMTAB) + const char* desc; /* description of system call */ +#endif + syscall_func func; /* the function address of system call */ +}; +extern struct finsh_syscall *_syscall_table_begin, *_syscall_table_end; + +/* find out system call, which should be implemented in user program */ +struct finsh_syscall* finsh_syscall_lookup(const char* name); + +#ifdef FINSH_USING_SYMTAB + +#ifdef __TI_COMPILER_VERSION__ +#define __TI_FINSH_EXPORT_FUNCTION(f) PRAGMA(DATA_SECTION(f,"FSymTab")) +#define __TI_FINSH_EXPORT_VAR(v) PRAGMA(DATA_SECTION(v,"VSymTab")) +#endif + + #ifdef FINSH_USING_DESCRIPTION + #ifdef _MSC_VER + #define FINSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \ + const char __fsym_##cmd##_name[] = #cmd; \ + const char __fsym_##cmd##_desc[] = #desc; \ + __declspec(allocate("FSymTab$f")) \ + const struct finsh_syscall __fsym_##cmd = \ + { \ + __fsym_##cmd##_name, \ + __fsym_##cmd##_desc, \ + (syscall_func)&name \ + }; + #pragma comment(linker, "/merge:FSymTab=mytext") + + #define FINSH_VAR_EXPORT(name, type, desc) \ + const char __vsym_##name##_name[] = #name; \ + const char __vsym_##name##_desc[] = #desc; \ + __declspec(allocate("VSymTab")) \ + const struct finsh_sysvar __vsym_##name = \ + { \ + __vsym_##name##_name, \ + __vsym_##name##_desc, \ + type, \ + (void*)&name \ + }; + + #elif defined(__TI_COMPILER_VERSION__) + #define FINSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \ + __TI_FINSH_EXPORT_FUNCTION(__fsym_##cmd); \ + const char __fsym_##cmd##_name[] = #cmd; \ + const char __fsym_##cmd##_desc[] = #desc; \ + const struct finsh_syscall __fsym_##cmd = \ + { \ + __fsym_##cmd##_name, \ + __fsym_##cmd##_desc, \ + (syscall_func)&name \ + }; + + #define FINSH_VAR_EXPORT(name, type, desc) \ + __TI_FINSH_EXPORT_VAR(__vsym_##name); \ + const char __vsym_##name##_name[] = #name; \ + const char __vsym_##name##_desc[] = #desc; \ + const struct finsh_sysvar __vsym_##name = \ + { \ + __vsym_##name##_name, \ + __vsym_##name##_desc, \ + type, \ + (void*)&name \ + }; + + #else + #define FINSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \ + const char __fsym_##cmd##_name[] RT_SECTION(".rodata.name") = #cmd; \ + const char __fsym_##cmd##_desc[] RT_SECTION(".rodata.name") = #desc; \ + RT_USED const struct finsh_syscall __fsym_##cmd RT_SECTION("FSymTab")= \ + { \ + __fsym_##cmd##_name, \ + __fsym_##cmd##_desc, \ + (syscall_func)&name \ + }; + + #define FINSH_VAR_EXPORT(name, type, desc) \ + const char __vsym_##name##_name[] RT_SECTION(".rodata.name") = #name; \ + const char __vsym_##name##_desc[] RT_SECTION(".rodata.name") = #desc; \ + RT_USED const struct finsh_sysvar __vsym_##name RT_SECTION("VSymTab")= \ + { \ + __vsym_##name##_name, \ + __vsym_##name##_desc, \ + type, \ + (void*)&name \ + }; + + #endif + #else + #ifdef _MSC_VER + #define FINSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \ + const char __fsym_##cmd##_name[] = #cmd; \ + __declspec(allocate("FSymTab$f")) \ + const struct finsh_syscall __fsym_##cmd = \ + { \ + __fsym_##cmd##_name, \ + (syscall_func)&name \ + }; + #pragma comment(linker, "/merge:FSymTab=mytext") + + #define FINSH_VAR_EXPORT(name, type, desc) \ + const char __vsym_##name##_name[] = #name; \ + __declspec(allocate("VSymTab")) const struct finsh_sysvar __vsym_##name = \ + { \ + __vsym_##name##_name, \ + type, \ + (void*)&name \ + }; + + #elif defined(__TI_COMPILER_VERSION__) + #define FINSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \ + __TI_FINSH_EXPORT_FUNCTION(__fsym_##cmd); \ + const char __fsym_##cmd##_name[] = #cmd; \ + const struct finsh_syscall __fsym_##cmd = \ + { \ + __fsym_##cmd##_name, \ + (syscall_func)&name \ + }; + + #define FINSH_VAR_EXPORT(name, type, desc) \ + __TI_FINSH_EXPORT_VAR(__vsym_##name); \ + const char __vsym_##name##_name[] = #name; \ + const struct finsh_sysvar __vsym_##name = \ + { \ + __vsym_##name##_name, \ + type, \ + (void*)&name \ + }; + + #else + #define FINSH_FUNCTION_EXPORT_CMD(name, cmd, desc) \ + const char __fsym_##cmd##_name[] = #cmd; \ + RT_USED const struct finsh_syscall __fsym_##cmd RT_SECTION("FSymTab")= \ + { \ + __fsym_##cmd##_name, \ + (syscall_func)&name \ + }; + + #define FINSH_VAR_EXPORT(name, type, desc) \ + const char __vsym_##name##_name[] = #name; \ + RT_USED const struct finsh_sysvar __vsym_##name RT_SECTION("VSymTab")= \ + { \ + __vsym_##name##_name, \ + type, \ + (void*)&name \ + }; + + #endif + #endif /* end of FINSH_USING_DESCRIPTION */ +#endif /* end of FINSH_USING_SYMTAB */ + +/** + * @ingroup finsh + * + * This macro exports a system function to finsh shell. + * + * @param name the name of function. + * @param desc the description of function, which will show in help. + */ +#define FINSH_FUNCTION_EXPORT(name, desc) \ + FINSH_FUNCTION_EXPORT_CMD(name, name, desc) + +/** + * @ingroup finsh + * + * This macro exports a system function with an alias name to finsh shell. + * + * @param name the name of function. + * @param alias the alias name of function. + * @param desc the description of function, which will show in help. + */ +#define FINSH_FUNCTION_EXPORT_ALIAS(name, alias, desc) \ + FINSH_FUNCTION_EXPORT_CMD(name, alias, desc) + +/** + * @ingroup finsh + * + * This macro exports a command to module shell. + * + * @param command the name of command. + * @param desc the description of command, which will show in help. + */ +#ifdef FINSH_USING_MSH +#define MSH_CMD_EXPORT(command, desc) \ + FINSH_FUNCTION_EXPORT_CMD(command, __cmd_##command, desc) +#define MSH_CMD_EXPORT_ALIAS(command, alias, desc) \ + FINSH_FUNCTION_EXPORT_ALIAS(command, __cmd_##alias, desc) +#else +#define MSH_CMD_EXPORT(command, desc) +#define MSH_CMD_EXPORT_ALIAS(command, alias, desc) +#endif + +#endif diff --git a/components/rt-thread/components/finsh/finsh_compiler.c b/components/rt-thread/components/finsh/finsh_compiler.c new file mode 100644 index 00000000..1cb4ff00 --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_compiler.c @@ -0,0 +1,918 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#include + +#include "finsh_node.h" +#include "finsh_error.h" +#include "finsh_var.h" +#include "finsh_ops.h" + +union finsh_value* finsh_compile_sp; /* stack pointer */ +uint8_t* finsh_compile_pc; /* PC */ + +#define finsh_code_byte(x) do { *finsh_compile_pc = (x); finsh_compile_pc ++; } while(0) +#define finsh_code_word(x) do { FINSH_SET16(finsh_compile_pc, x); finsh_compile_pc +=2; } while(0) +#define finsh_code_dword(x) do { FINSH_SET32(finsh_compile_pc, x); finsh_compile_pc +=4; } while(0) + +static int finsh_compile(struct finsh_node* node) +{ + if (node != NULL) + { + /* compile child node */ + if (finsh_node_child(node) != NULL) + finsh_compile(finsh_node_child(node)); + + /* compile current node */ + switch (node->node_type) + { + case FINSH_NODE_ID: + { + /* identifier::syscall */ + if (node->idtype & FINSH_IDTYPE_SYSCALL) + { + /* load address */ + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword((long)node->id.syscall->func); + } + /* identifier::sysvar */ + else if (node->idtype & FINSH_IDTYPE_SYSVAR) + { + struct finsh_sysvar* sysvar; + + sysvar = node->id.sysvar; + if (sysvar != NULL) + { + switch (sysvar->type) + { + case finsh_type_char: + case finsh_type_uchar: + if (node->idtype & FINSH_IDTYPE_ADDRESS) + { + /* load address */ + finsh_code_byte(FINSH_OP_LD_DWORD); + } + else + { + /* load value */ + finsh_code_byte(FINSH_OP_LD_VALUE_BYTE); + } + + finsh_code_dword((long)(sysvar->var)); + break; + + case finsh_type_short: + case finsh_type_ushort: + if (node->idtype & FINSH_IDTYPE_ADDRESS) + { + /* load address */ + finsh_code_byte(FINSH_OP_LD_DWORD); + } + else + { + /* load value */ + finsh_code_byte(FINSH_OP_LD_VALUE_WORD); + } + + finsh_code_dword((long)(sysvar->var)); + break; + + case finsh_type_int: + case finsh_type_uint: + case finsh_type_long: + case finsh_type_ulong: + case finsh_type_charp: + case finsh_type_shortp: + case finsh_type_intp: + case finsh_type_longp: + if (node->idtype & FINSH_IDTYPE_ADDRESS) + { + /* load address */ + finsh_code_byte(FINSH_OP_LD_DWORD); + } + else + { + /* load value */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD); + } + + finsh_code_dword((long)(sysvar->var)); + break; + } + } + } + /* identifier::var */ + else + { + struct finsh_var* var; + + var = node->id.var; + if (var != NULL) + { + switch (var->type) + { + case finsh_type_char: + case finsh_type_uchar: + if (node->idtype & FINSH_IDTYPE_ADDRESS) + { + /* load address */ + finsh_code_byte(FINSH_OP_LD_DWORD); + } + else + { + /* load value */ + finsh_code_byte(FINSH_OP_LD_VALUE_BYTE); + } + + finsh_code_dword((long)&(var->value.char_value)); + break; + + case finsh_type_short: + case finsh_type_ushort: + if (node->idtype & FINSH_IDTYPE_ADDRESS) + { + /* load address */ + finsh_code_byte(FINSH_OP_LD_DWORD); + } + else + { + /* load value */ + finsh_code_byte(FINSH_OP_LD_VALUE_WORD); + } + + finsh_code_dword((long)&(var->value.short_value)); + break; + + case finsh_type_int: + case finsh_type_uint: + case finsh_type_long: + case finsh_type_ulong: + case finsh_type_charp: + case finsh_type_shortp: + case finsh_type_intp: + case finsh_type_longp: + if (node->idtype & FINSH_IDTYPE_ADDRESS) + { + /* load address */ + finsh_code_byte(FINSH_OP_LD_DWORD); + } + else + { + /* load value */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD); + } + + finsh_code_dword((long)&(var->value.long_value)); + break; + } + } + } + } + break; + + /* load const */ + case FINSH_NODE_VALUE_CHAR: + finsh_code_byte(FINSH_OP_LD_BYTE); + finsh_code_byte(node->value.char_value); + break; + + case FINSH_NODE_VALUE_INT: + case FINSH_NODE_VALUE_LONG: + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword(node->value.long_value); + break; + + case FINSH_NODE_VALUE_NULL: + case FINSH_NODE_VALUE_STRING: + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword((rt_ubase_t)node->value.ptr); + break; + + /* arithmetic operation */ + case FINSH_NODE_SYS_ADD: + if (node->data_type == FINSH_DATA_TYPE_BYTE) finsh_code_byte(FINSH_OP_ADD_BYTE); + else if (node->data_type == FINSH_DATA_TYPE_WORD) finsh_code_byte(FINSH_OP_ADD_WORD); + else if (node->data_type == FINSH_DATA_TYPE_DWORD) finsh_code_byte(FINSH_OP_ADD_DWORD); + break; + + case FINSH_NODE_SYS_SUB: + if (node->data_type == FINSH_DATA_TYPE_BYTE) finsh_code_byte(FINSH_OP_SUB_BYTE); + else if (node->data_type == FINSH_DATA_TYPE_WORD) finsh_code_byte(FINSH_OP_SUB_WORD); + else if (node->data_type == FINSH_DATA_TYPE_DWORD) finsh_code_byte(FINSH_OP_SUB_DWORD); + break; + + case FINSH_NODE_SYS_MUL: + if (node->data_type == FINSH_DATA_TYPE_BYTE) finsh_code_byte(FINSH_OP_MUL_BYTE); + else if (node->data_type == FINSH_DATA_TYPE_WORD) finsh_code_byte(FINSH_OP_MUL_WORD); + else if (node->data_type == FINSH_DATA_TYPE_DWORD) finsh_code_byte(FINSH_OP_MUL_DWORD); + break; + + case FINSH_NODE_SYS_DIV: + if (node->data_type == FINSH_DATA_TYPE_BYTE) finsh_code_byte(FINSH_OP_DIV_BYTE); + else if (node->data_type == FINSH_DATA_TYPE_WORD) finsh_code_byte(FINSH_OP_DIV_WORD); + else if (node->data_type == FINSH_DATA_TYPE_DWORD) finsh_code_byte(FINSH_OP_DIV_DWORD); + break; + + case FINSH_NODE_SYS_MOD: + if (node->data_type == FINSH_DATA_TYPE_BYTE) finsh_code_byte(FINSH_OP_MOD_BYTE); + else if (node->data_type == FINSH_DATA_TYPE_WORD) finsh_code_byte(FINSH_OP_MOD_WORD); + else if (node->data_type == FINSH_DATA_TYPE_DWORD) finsh_code_byte(FINSH_OP_MOD_DWORD); + break; + + /* bit operation */ + case FINSH_NODE_SYS_AND: + if (node->data_type == FINSH_DATA_TYPE_BYTE) finsh_code_byte(FINSH_OP_AND_BYTE); + else if (node->data_type == FINSH_DATA_TYPE_WORD) finsh_code_byte(FINSH_OP_AND_WORD); + else if (node->data_type == FINSH_DATA_TYPE_DWORD) finsh_code_byte(FINSH_OP_AND_DWORD); + break; + + case FINSH_NODE_SYS_OR: + if (node->data_type == FINSH_DATA_TYPE_BYTE) finsh_code_byte(FINSH_OP_OR_BYTE); + else if (node->data_type == FINSH_DATA_TYPE_WORD) finsh_code_byte(FINSH_OP_OR_WORD); + else if (node->data_type == FINSH_DATA_TYPE_DWORD) finsh_code_byte(FINSH_OP_OR_DWORD); + break; + + case FINSH_NODE_SYS_XOR: + if (node->data_type == FINSH_DATA_TYPE_BYTE) finsh_code_byte(FINSH_OP_XOR_BYTE); + else if (node->data_type == FINSH_DATA_TYPE_WORD) finsh_code_byte(FINSH_OP_XOR_WORD); + else if (node->data_type == FINSH_DATA_TYPE_DWORD) finsh_code_byte(FINSH_OP_XOR_DWORD); + break; + + case FINSH_NODE_SYS_BITWISE: + if (node->data_type == FINSH_DATA_TYPE_BYTE) finsh_code_byte(FINSH_OP_BITWISE_BYTE); + else if (node->data_type == FINSH_DATA_TYPE_WORD) finsh_code_byte(FINSH_OP_BITWISE_WORD); + else if (node->data_type == FINSH_DATA_TYPE_DWORD) finsh_code_byte(FINSH_OP_BITWISE_DWORD); + break; + + case FINSH_NODE_SYS_SHL: + if (node->data_type == FINSH_DATA_TYPE_BYTE) finsh_code_byte(FINSH_OP_SHL_BYTE); + else if (node->data_type == FINSH_DATA_TYPE_WORD) finsh_code_byte(FINSH_OP_SHL_WORD); + else if (node->data_type == FINSH_DATA_TYPE_DWORD) finsh_code_byte(FINSH_OP_SHL_DWORD); + break; + + case FINSH_NODE_SYS_SHR: + if (node->data_type == FINSH_DATA_TYPE_BYTE) finsh_code_byte(FINSH_OP_SHR_BYTE); + else if (node->data_type == FINSH_DATA_TYPE_WORD) finsh_code_byte(FINSH_OP_SHR_WORD); + else if (node->data_type == FINSH_DATA_TYPE_DWORD) finsh_code_byte(FINSH_OP_SHR_DWORD); + break; + + /* syscall */ + case FINSH_NODE_SYS_FUNC: + { + int parameters; + struct finsh_node* sibling; + + parameters = 0; + if (finsh_node_child(node) != NULL) + { + sibling = finsh_node_sibling(finsh_node_child(node)); + while (sibling != NULL) + { + parameters ++; + sibling = finsh_node_sibling(sibling); + } + + /* load address of function */ + // finsh_code_dword((long)&(node->var->value.ptr)); + + /* syscall parameters */ + finsh_code_byte(FINSH_OP_SYSCALL); + finsh_code_byte(parameters); + } + } + break; + + /* assign expression */ + case FINSH_NODE_SYS_ASSIGN: + if (finsh_node_child(node) && finsh_node_child(node)->node_type == FINSH_NODE_ID) + { + switch (finsh_node_child(node)->data_type) + { + case FINSH_DATA_TYPE_BYTE: + finsh_code_byte(FINSH_OP_ST_BYTE); + + /* load value again */ + finsh_code_byte(FINSH_OP_LD_VALUE_BYTE_STACK); + break; + + case FINSH_DATA_TYPE_WORD: + finsh_code_byte(FINSH_OP_ST_WORD); + + /* load value again */ + finsh_code_byte(FINSH_OP_LD_VALUE_WORD_STACK); + break; + + case FINSH_DATA_TYPE_DWORD: + finsh_code_byte(FINSH_OP_ST_DWORD); + + /* load value again */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD_STACK); + break; + + default: + finsh_error_set(FINSH_ERROR_UNKNOWN_TYPE); + } + } + else if (finsh_node_child(node)->node_type == FINSH_NODE_SYS_GETVALUE) + { + switch ((finsh_node_child(node)->data_type) & 0x0F) + { + case FINSH_DATA_TYPE_BYTE: + finsh_code_byte(FINSH_OP_ST_BYTE); + + /* load value again */ + finsh_code_byte(FINSH_OP_LD_VALUE_BYTE_STACK); + break; + + case FINSH_DATA_TYPE_WORD: + finsh_code_byte(FINSH_OP_ST_WORD); + + /* load value again */ + finsh_code_byte(FINSH_OP_LD_VALUE_WORD_STACK); + break; + + case FINSH_DATA_TYPE_DWORD: + finsh_code_byte(FINSH_OP_ST_DWORD); + + /* load value again */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD_STACK); + break; + + default: + finsh_error_set(FINSH_ERROR_UNKNOWN_TYPE); + } + } + break; + + /* pre-increase */ + case FINSH_NODE_SYS_PREINC: + if (finsh_node_child(node) && finsh_node_child(node)->node_type == FINSH_NODE_ID) + { + struct finsh_var* var; + var = finsh_node_child(node)->id.var; + + /* ld_dword &id */ + // finsh_code_byte(FINSH_OP_LD_DWORD); + + switch (node->data_type) + { + case FINSH_DATA_TYPE_BYTE: + /* address */ + // finsh_code_dword((long)&(var->value.char_value)); + + /* ld_value_byte &id */ + finsh_code_byte(FINSH_OP_LD_VALUE_BYTE); + finsh_code_dword((long)&(var->value.char_value)); + + /* ld_byte 1 */ + finsh_code_byte(FINSH_OP_LD_BYTE); + finsh_code_byte(1); + + /* add_byte */ + finsh_code_byte(FINSH_OP_ADD_BYTE); + /* st_byte */ + finsh_code_byte(FINSH_OP_ST_BYTE); + + /* load value again */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD_STACK); + + break; + + case FINSH_DATA_TYPE_WORD: + /* address */ + // finsh_code_dword((long)&(var->value.short_value)); + + /* ld_value_word &id */ + finsh_code_byte(FINSH_OP_LD_VALUE_WORD); + finsh_code_dword((long)&(var->value.short_value)); + + /* ld_word 1 */ + finsh_code_byte(FINSH_OP_LD_WORD); + finsh_code_word(1); + + /* add_word */ + finsh_code_byte(FINSH_OP_ADD_WORD); + /* st_word */ + finsh_code_byte(FINSH_OP_ST_WORD); + + /* load value again */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD_STACK); + + break; + + case FINSH_DATA_TYPE_DWORD: + /* address */ + // finsh_code_dword((long)&(var->value.long_value)); + + /* ld_dword &id */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD); + finsh_code_dword((long)&(var->value.long_value)); + + /* ld_dword 1 */ + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword(1); + + /* add_dword */ + finsh_code_byte(FINSH_OP_ADD_DWORD); + /* st_dword */ + finsh_code_byte(FINSH_OP_ST_DWORD); + + /* load value again */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD_STACK); + + break; + } + } + break; + + /* pre-decrease */ + case FINSH_NODE_SYS_PREDEC: + if (finsh_node_child(node) && finsh_node_child(node)->node_type == FINSH_NODE_ID) + { + struct finsh_var* var; + var = finsh_node_child(node)->id.var; + + /* ld_dword &id */ + // finsh_code_byte(FINSH_OP_LD_DWORD); + + switch (node->data_type) + { + case FINSH_DATA_TYPE_BYTE: + /* address */ + // finsh_code_dword((long)&(var->value.char_value)); + + /* ld_value_byte &id */ + finsh_code_byte(FINSH_OP_LD_VALUE_BYTE); + finsh_code_dword((long)&(var->value.char_value)); + + /* ld_byte 1 */ + finsh_code_byte(FINSH_OP_LD_BYTE); + finsh_code_byte(1); + + /* add_byte */ + finsh_code_byte(FINSH_OP_SUB_BYTE); + /* st_byte */ + finsh_code_byte(FINSH_OP_ST_BYTE); + + /* load value again */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD_STACK); + + break; + + case FINSH_DATA_TYPE_WORD: + /* address */ + // finsh_code_dword((long)&(var->value.short_value)); + + /* ld_value_word &id */ + finsh_code_byte(FINSH_OP_LD_VALUE_WORD); + finsh_code_dword((long)&(var->value.short_value)); + + /* ld_word 1 */ + finsh_code_byte(FINSH_OP_LD_WORD); + finsh_code_word(1); + + /* add_word */ + finsh_code_byte(FINSH_OP_SUB_WORD); + /* st_word */ + finsh_code_byte(FINSH_OP_ST_WORD); + + /* load value again */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD_STACK); + + break; + + case FINSH_DATA_TYPE_DWORD: + /* address */ + // finsh_code_dword((long)&(var->value.long_value)); + + /* ld_dword &id */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD); + finsh_code_dword((long)&(var->value.long_value)); + + /* ld_dword 1 */ + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword(1); + + /* add_dword */ + finsh_code_byte(FINSH_OP_SUB_DWORD); + /* st_dword */ + finsh_code_byte(FINSH_OP_ST_DWORD); + + /* load value again */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD_STACK); + + break; + } + } + break; + + /* increase */ + case FINSH_NODE_SYS_INC: + if (finsh_node_child(node) && finsh_node_child(node)->node_type == FINSH_NODE_ID) + { + struct finsh_var* var; + var = finsh_node_child(node)->id.var; + + switch (node->data_type) + { + case FINSH_DATA_TYPE_BYTE: + /* ld_value_byte &id */ + // finsh_code_byte(FINSH_OP_LD_VALUE_BYTE); + // finsh_code_dword((long)&(var->value.char_value)); + + /* ld_dword &id */ + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword((long)&(var->value.char_value)); + + /* ld_value_byte &id */ + finsh_code_byte(FINSH_OP_LD_VALUE_BYTE); + finsh_code_dword((long)&(var->value.char_value)); + + /* ld_byte 1 */ + finsh_code_byte(FINSH_OP_LD_BYTE); + finsh_code_byte(1); + + /* add_byte */ + finsh_code_byte(FINSH_OP_ADD_BYTE); + /* get byte */ + finsh_code_byte(FINSH_OP_ST_BYTE); + + /* pop */ + finsh_code_byte(FINSH_OP_POP); + break; + + case FINSH_DATA_TYPE_WORD: + /* ld_value_word &id */ + // finsh_code_byte(FINSH_OP_LD_VALUE_WORD); + // finsh_code_dword((long)&(var->value.short_value)); + + /* ld_dword &id */ + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword((long)&(var->value.short_value)); + + /* ld_value_word &id */ + finsh_code_byte(FINSH_OP_LD_VALUE_WORD); + finsh_code_dword((long)&(var->value.short_value)); + + /* ld_word 1 */ + finsh_code_byte(FINSH_OP_LD_WORD); + finsh_code_word(1); + + /* add_byte */ + finsh_code_byte(FINSH_OP_ADD_WORD); + /* get byte */ + finsh_code_byte(FINSH_OP_ST_WORD); + + /* pop */ + finsh_code_byte(FINSH_OP_POP); + break; + + case FINSH_DATA_TYPE_DWORD: + /* ld_value_dword &id */ + // finsh_code_byte(FINSH_OP_LD_VALUE_DWORD); + // finsh_code_dword((long)&(var->value.long_value)); + + /* ld_dword &id */ + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword((long)&(var->value.long_value)); + + /* ld_value_dword &id */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD); + finsh_code_dword((long)&(var->value.long_value)); + + /* ld_dword 1 */ + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword(1); + + /* add_byte */ + finsh_code_byte(FINSH_OP_ADD_DWORD); + /* get byte */ + finsh_code_byte(FINSH_OP_ST_DWORD); + + /* pop */ + finsh_code_byte(FINSH_OP_POP); + break; + } + } + break; + + /* decrease */ + case FINSH_NODE_SYS_DEC: + if (finsh_node_child(node) && finsh_node_child(node)->node_type == FINSH_NODE_ID) + { + struct finsh_var* var; + var = finsh_node_child(node)->id.var; + + switch (node->data_type) + { + case FINSH_DATA_TYPE_BYTE: + /* ld_value_byte &id */ + // finsh_code_byte(FINSH_OP_LD_VALUE_BYTE); + // finsh_code_dword((long)&(var->value.char_value)); + + /* ld_dword &id */ + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword((long)&(var->value.char_value)); + + /* ld_value_byte &id */ + finsh_code_byte(FINSH_OP_LD_VALUE_BYTE); + finsh_code_dword((long)&(var->value.char_value)); + + /* ld_byte 1 */ + finsh_code_byte(FINSH_OP_LD_BYTE); + finsh_code_byte(1); + + /* add_byte */ + finsh_code_byte(FINSH_OP_SUB_BYTE); + /* get byte */ + finsh_code_byte(FINSH_OP_ST_BYTE); + + /* pop */ + finsh_code_byte(FINSH_OP_POP); + break; + + case FINSH_DATA_TYPE_WORD: + /* ld_value_word &id */ + // finsh_code_byte(FINSH_OP_LD_VALUE_WORD); + // finsh_code_dword((long)&(var->value.short_value)); + + /* ld_dword &id */ + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword((long)&(var->value.short_value)); + + /* ld_value_word &id */ + finsh_code_byte(FINSH_OP_LD_VALUE_WORD); + finsh_code_dword((long)&(var->value.short_value)); + + /* ld_word 1 */ + finsh_code_byte(FINSH_OP_LD_WORD); + finsh_code_word(1); + + /* add_byte */ + finsh_code_byte(FINSH_OP_SUB_WORD); + /* get byte */ + finsh_code_byte(FINSH_OP_ST_WORD); + + /* pop */ + finsh_code_byte(FINSH_OP_POP); + break; + + case FINSH_DATA_TYPE_DWORD: + /* ld_value_dword &id */ + // finsh_code_byte(FINSH_OP_LD_VALUE_DWORD); + // finsh_code_dword((long)&(var->value.long_value)); + + /* ld_dword &id */ + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword((long)&(var->value.long_value)); + + /* ld_value_dword &id */ + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD); + finsh_code_dword((long)&(var->value.long_value)); + + /* ld_dword 1 */ + finsh_code_byte(FINSH_OP_LD_DWORD); + finsh_code_dword(1); + + /* add_byte */ + finsh_code_byte(FINSH_OP_SUB_DWORD); + /* get byte */ + finsh_code_byte(FINSH_OP_ST_DWORD); + + /* pop */ + finsh_code_byte(FINSH_OP_POP); + break; + } + } + break; + + case FINSH_NODE_SYS_NULL: + finsh_code_dword(0); + break; + + case FINSH_NODE_SYS_GETVALUE: + if (node->idtype & FINSH_IDTYPE_ADDRESS) + { + /* nothing will be generated */ + } + else + { + switch (node->data_type) + { + case FINSH_DATA_TYPE_BYTE: + finsh_code_byte(FINSH_OP_LD_VALUE_BYTE_STACK); + break; + case FINSH_DATA_TYPE_WORD: + finsh_code_byte(FINSH_OP_LD_VALUE_WORD_STACK); + break; + case FINSH_DATA_TYPE_DWORD: + finsh_code_byte(FINSH_OP_LD_VALUE_DWORD_STACK); + break; + default: + break; + } + } + break; + + case FINSH_NODE_SYS_GETADDR: + /* nothing will be generated */ + break; + + default: + finsh_error_set(FINSH_ERROR_UNKNOWN_NODE); + break; + } + + /* compile sibling node */ + if (finsh_node_sibling(node) != NULL) + finsh_compile(finsh_node_sibling(node)); + } + + return 0; +} + +static int finsh_type_check(struct finsh_node* node, uint8_t is_addr) +{ + if (node != NULL) + { + /* address & value */ + if (node->node_type == FINSH_NODE_SYS_ASSIGN || + node->node_type == FINSH_NODE_SYS_PREINC || + node->node_type == FINSH_NODE_SYS_PREDEC || + node->node_type == FINSH_NODE_SYS_GETADDR) + { + /* address */ + finsh_type_check(finsh_node_child(node), FINSH_IDTYPE_ADDRESS); + } + else if (node->node_type == FINSH_NODE_SYS_GETVALUE && is_addr) + { + /* change the attribute of getvalue in left expr */ + finsh_type_check(finsh_node_child(node), 0); + } + else + { + /* transfer 'av' to child node */ + finsh_type_check(finsh_node_child(node), is_addr); + } + + /* always does not load address in sibling */ + finsh_type_check(finsh_node_sibling(node), FINSH_NODE_VALUE); + + /** set attribute of current node */ + + /* make sure the current node is address or value */ + if (node->idtype != FINSH_IDTYPE_SYSCALL) node->idtype |= is_addr; + + if (finsh_node_child(node) != NULL) + { + node->data_type = finsh_node_child(node)->data_type; + return 0; + } + + if (node->node_type == FINSH_NODE_ID) + { + if (node->idtype & FINSH_IDTYPE_VAR) + { + struct finsh_var* var; + + var = node->id.var; + if (var != NULL) + { + switch (var->type) + { + case finsh_type_void: + node->data_type = FINSH_DATA_TYPE_VOID; + break; + + case finsh_type_char: + case finsh_type_uchar: + node->data_type = FINSH_DATA_TYPE_BYTE; + break; + + case finsh_type_short: + case finsh_type_ushort: + node->data_type = FINSH_DATA_TYPE_WORD; + break; + + case finsh_type_int: + case finsh_type_uint: + case finsh_type_long: + case finsh_type_ulong: + node->data_type = FINSH_DATA_TYPE_DWORD; + break; + + case finsh_type_charp: + case finsh_type_voidp: + case finsh_type_shortp: + case finsh_type_intp: + case finsh_type_longp: + node->data_type = FINSH_DATA_TYPE_DWORD; + break; + + default: + finsh_error_set(FINSH_ERROR_UNKNOWN_TYPE); + break; + } + } + } + else if (node->idtype & FINSH_IDTYPE_SYSVAR) + { + struct finsh_sysvar *sysvar; + + sysvar = node->id.sysvar; + if (sysvar != NULL) + { + switch (sysvar->type) + { + case finsh_type_void: + node->data_type = FINSH_DATA_TYPE_VOID; + break; + + case finsh_type_char: + case finsh_type_uchar: + node->data_type = FINSH_DATA_TYPE_BYTE; + break; + + case finsh_type_short: + case finsh_type_ushort: + node->data_type = FINSH_DATA_TYPE_WORD; + break; + + case finsh_type_int: + case finsh_type_uint: + case finsh_type_long: + case finsh_type_ulong: + node->data_type = FINSH_DATA_TYPE_DWORD; + break; + + case finsh_type_charp: + case finsh_type_voidp: + case finsh_type_shortp: + case finsh_type_intp: + case finsh_type_longp: + node->data_type = FINSH_DATA_TYPE_DWORD; + break; + + default: + finsh_error_set(FINSH_ERROR_UNKNOWN_TYPE); + break; + } + } + } + } + else if (node->node_type == FINSH_NODE_VALUE_CHAR) + { + node->data_type = FINSH_DATA_TYPE_BYTE; + } + else if (node->node_type == FINSH_NODE_VALUE_INT || + node->node_type == FINSH_NODE_VALUE_LONG || + node->node_type == FINSH_NODE_VALUE_STRING || + node->node_type == FINSH_NODE_VALUE_NULL) + { + node->data_type = FINSH_DATA_TYPE_DWORD; + } + } + return 0; +} + +int finsh_compiler_run(struct finsh_node* node) +{ + struct finsh_node* sibling; + + /* type check */ + finsh_type_check(node, FINSH_NODE_VALUE); + + /* clean text segment and vm stack */ + memset(&text_segment[0], 0, sizeof(text_segment)); + memset(&finsh_vm_stack[0], 0, sizeof(finsh_vm_stack)); + + /* reset compile stack pointer and pc */ + finsh_compile_sp = &finsh_vm_stack[0]; + finsh_compile_pc = &text_segment[0]; + + /* compile node */ + sibling = node; + while (sibling != NULL) + { + struct finsh_node* current_node; + current_node = sibling; + + /* get sibling node */ + sibling = current_node->sibling; + + /* clean sibling node */ + current_node->sibling = NULL; + finsh_compile(current_node); + + /* pop current value */ + if (sibling != NULL) finsh_code_byte(FINSH_OP_POP); + } + + return 0; +} diff --git a/components/rt-thread/components/finsh/finsh_error.c b/components/rt-thread/components/finsh/finsh_error.c new file mode 100644 index 00000000..a5cf2eb5 --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_error.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#include "finsh_error.h" + +uint8_t global_errno; + +static const char * finsh_error_string_table[] = +{ + "No error", + "Invalid token", + "Expect a type", + "Unknown type", + "Variable exist", + "Expect a operater", + "Memory full", + "Unknown operator", + "Unknown node", + "Expect a character", + "Unexpect end", + "Unknown token", + "Float not supported", + "Unknown symbol", + "Null node" +}; + +int finsh_error_init() +{ + global_errno = FINSH_ERROR_OK; + + return 0; +} + +int finsh_error_set(uint8_t type) +{ + global_errno = type; + + return 0; +} + +uint8_t finsh_errno() +{ + return global_errno; +} + +const char* finsh_error_string(uint8_t type) +{ + return finsh_error_string_table[type]; +} diff --git a/components/rt-thread/components/finsh/finsh_error.h b/components/rt-thread/components/finsh/finsh_error.h new file mode 100644 index 00000000..8dfafd01 --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_error.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#ifndef __FINSH_ERROR_H__ +#define __FINSH_ERROR_H__ + +#include + +int finsh_error_init(void); + +/* get error number */ +uint8_t finsh_errno(void); + +int finsh_error_set(uint8_t type); +const char* finsh_error_string(uint8_t type); + +#endif diff --git a/components/rt-thread/components/finsh/finsh_heap.c b/components/rt-thread/components/finsh/finsh_heap.c new file mode 100644 index 00000000..9f64d108 --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_heap.c @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#include + +#include "finsh_var.h" + +ALIGN(RT_ALIGN_SIZE) +uint8_t finsh_heap[FINSH_HEAP_MAX]; +struct finsh_block_header +{ + uint32_t length; + struct finsh_block_header* next; +}; +#define BLOCK_HEADER(x) (struct finsh_block_header*)(x) +#define finsh_block_get_header(data) (struct finsh_block_header*)((uint8_t*)data - sizeof(struct finsh_block_header)) +#define finsh_block_get_data(header) (uint8_t*)((struct finsh_block_header*)header + 1) +#define HEAP_ALIGN_SIZE(size) (((size) + HEAP_ALIGNMENT - 1) & ~(HEAP_ALIGNMENT-1)) + +static struct finsh_block_header* free_list; +static struct finsh_block_header* allocate_list; + +static void finsh_heap_gc(void); + +static void finsh_block_insert(struct finsh_block_header** list, struct finsh_block_header* header); +static void finsh_block_remove(struct finsh_block_header** list, struct finsh_block_header* header); +static void finsh_block_split(struct finsh_block_header* header, size_t size); +static void finsh_block_merge(struct finsh_block_header** list, struct finsh_block_header* header); + +int finsh_heap_init(void) +{ + /* clear heap to zero */ + memset(&finsh_heap[0], 0, sizeof(finsh_heap)); + + /* init free and alloc list */ + free_list = BLOCK_HEADER(&finsh_heap[0]); + free_list->length = FINSH_HEAP_MAX - sizeof(struct finsh_block_header); + free_list->next = NULL; + + allocate_list = NULL; + + return 0; +} + +/** + * allocate a block from heap + */ +void* finsh_heap_allocate(size_t size) +{ + struct finsh_block_header* header; + + size = HEAP_ALIGN_SIZE(size); + + /* find the first fit block */ + for (header = free_list; + ((header != NULL) && (header->length <= size + sizeof(struct finsh_block_header))); + header = header->next) ; + + if (header == NULL) + { + finsh_heap_gc(); + + /* find the first fit block */ + for (header = free_list; + ((header != NULL) && (header->length < size + sizeof(struct finsh_block_header))); + header = header->next) ; + + /* there is no memory */ + if (header == NULL) return NULL; + } + + /* split block */ + finsh_block_split(header, size); + + /* remove from free list */ + finsh_block_remove(&free_list, header); + header->next = NULL; + + /* insert to allocate list */ + finsh_block_insert(&allocate_list, header); + + memset(finsh_block_get_data(header), 0, size); + + return finsh_block_get_data(header); +} + +/** + * release the allocated block + */ +void finsh_heap_free(void*ptr) +{ + struct finsh_block_header* header; + + /* get block header */ + header = finsh_block_get_header(ptr); + + /* remove from allocate list */ + finsh_block_remove(&allocate_list, header); + + /* insert to free list */ + finsh_block_insert(&free_list, header); + finsh_block_merge(&free_list, header); +} + +/** + * garbage collector + */ +static void finsh_heap_gc(void) +{ + int i; + struct finsh_block_header *header, *temp; + + temp = NULL; + + /* find the first fit block */ + for (header = allocate_list; header != NULL; ) + { + for (i = 0; i < FINSH_VARIABLE_MAX; i ++) + { + if (global_variable[i].type != finsh_type_unknown) + { + if (global_variable[i].value.ptr == finsh_block_get_data(header)) + break; + } + } + + temp = header; + header = header->next; + + /* this block is an unused block, release it */ + if (i == FINSH_VARIABLE_MAX) + { + finsh_heap_free(finsh_block_get_data(temp)); + } + } +} + +/** + * insert a block to list + */ +void finsh_block_insert(struct finsh_block_header** list, struct finsh_block_header* header) +{ + struct finsh_block_header* node; + + if (*list == NULL) + { + *list = header; + return; + } + + /* find out insert point */ + node = *list; + + if (node > header) + { + /* insert node in the header of list */ + header->next = node; + *list = header; + + return; + } + else + { + for (node = *list; node; node = node->next) + { + if (node->next > header) break; + + if (node->next == NULL) break; + } + } + + /* insert node */ + if (node->next != NULL) header->next = node->next; + node->next = header; +} + +/** + * remove block from list + */ +void finsh_block_remove(struct finsh_block_header** list, struct finsh_block_header* header) +{ + struct finsh_block_header* node; + + node = *list; + if (node == header) + { + /* remove list header */ + *list = header->next; + header->next = NULL; + + return; + } + + for (node = *list; node != NULL; node = node->next) + { + if (node->next == header) + { + node->next = header->next; + break; + } + } +} + +/** + * split block + */ +void finsh_block_split(struct finsh_block_header* header, size_t size) +{ + struct finsh_block_header* next; + + /* + * split header into two node: + * header->next->... + */ + next = BLOCK_HEADER((uint8_t*)header + sizeof(struct finsh_block_header) + size); + next->length = header->length - sizeof(struct finsh_block_header) - size; + header->length = size; + next->next = header->next; + + header->next = next; +} + +void finsh_block_merge(struct finsh_block_header** list, struct finsh_block_header* header) +{ + struct finsh_block_header* prev_node; + struct finsh_block_header* next_node; + + next_node = header->next; + + if (*list == header) prev_node = NULL; + else + { + /* find out the previous header */ + for (prev_node = *list; prev_node; prev_node =prev_node->next) + { + if (prev_node->next == header) + break; + } + } + + /* try merge node */ + + /* merge to previous node */ + if (prev_node != NULL && + ((uint8_t*)prev_node + prev_node->length + sizeof(struct finsh_block_header) + == (uint8_t*)header)) + { + /* is it close to next node? */ + if ((next_node != NULL) && + ((uint8_t*)header + header->length + sizeof(struct finsh_block_header) + == (uint8_t*)next_node)) + { + /* merge three node */ + prev_node->length += header->length + next_node->length + + 2 * sizeof(struct finsh_block_header); + + prev_node->next = next_node->next; + } + else + { + prev_node->length += header->length + sizeof(struct finsh_block_header); + prev_node->next = header->next; + } + } + else /* merge to last node */ + if ( (next_node != NULL) && + ((uint8_t*)header + header->length + sizeof(struct finsh_block_header) + == (uint8_t*)next_node)) + { + header->length += next_node->length + sizeof(struct finsh_block_header); + header->next = next_node->next; + } +} diff --git a/components/rt-thread/components/finsh/finsh_heap.h b/components/rt-thread/components/finsh/finsh_heap.h new file mode 100644 index 00000000..f320c277 --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_heap.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#include + +#ifndef __FINSH_HEAP_H__ +#define __FINSH_HEAP_H__ + +int finsh_heap_init(void); +void* finsh_heap_allocate(size_t size); +void finsh_heap_free(void*ptr); + +#endif diff --git a/components/rt-thread/components/finsh/finsh_init.c b/components/rt-thread/components/finsh/finsh_init.c new file mode 100644 index 00000000..76302a05 --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_init.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#include + +#include "finsh_node.h" +#include "finsh_vm.h" +#include "finsh_parser.h" +#include "finsh_var.h" +#include "finsh_error.h" +#include "finsh_heap.h" + +int finsh_init(struct finsh_parser* parser) +{ + finsh_parser_init(parser); + + /* finsh init */ + finsh_node_init(); + finsh_var_init(); + finsh_error_init(); + finsh_heap_init(); + + return 0; +} + +long finsh_stack_bottom() +{ + return finsh_vm_stack[0].long_value; +} + +int finsh_flush(struct finsh_parser* parser) +{ + finsh_parser_init(parser); + + /* finsh init */ + finsh_node_init(); + finsh_error_init(); + + return 0; +} + +int finsh_reset(struct finsh_parser* parser) +{ + /* finsh init */ + finsh_node_init(); + finsh_var_init(); + finsh_error_init(); + finsh_heap_init(); + + return 0; +} diff --git a/components/rt-thread/components/finsh/finsh_node.c b/components/rt-thread/components/finsh/finsh_node.c new file mode 100644 index 00000000..82a4c03f --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_node.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#include + +#include "finsh_node.h" +#include "finsh_error.h" +#include "finsh_var.h" +#include "finsh_heap.h" + +struct finsh_node global_node_table[FINSH_NODE_MAX]; + +int finsh_node_init() +{ + memset(global_node_table, 0, sizeof(global_node_table)); + + return 0; +} + +struct finsh_node* finsh_node_allocate(uint8_t type) +{ + int i; + + /* find an empty entry */ + for (i = 0; i < FINSH_NODE_MAX; i ++) + { + if (global_node_table[i].node_type == FINSH_NODE_UNKNOWN) break; + } + + if (i == FINSH_NODE_MAX) return NULL; + + /* fill type field */ + global_node_table[i].node_type = type; + + /* return this allocated node */ + return &global_node_table[i]; +} + +struct finsh_node* finsh_node_new_id(char* id) +{ + struct finsh_node* node; + void* symbol; + unsigned char type; + + symbol = NULL; + type = 0; + node = NULL; + + /* lookup variable firstly */ + symbol = (void*)finsh_var_lookup(id); + if (symbol == NULL) + { + /* then lookup system variable */ + symbol = (void*)finsh_sysvar_lookup(id); + if (symbol == NULL) + { + /* then lookup system call */ + symbol = (void*)finsh_syscall_lookup(id); + if (symbol != NULL) type = FINSH_IDTYPE_SYSCALL; + } + else type = FINSH_IDTYPE_SYSVAR; + } + else type = FINSH_IDTYPE_VAR; + + if (symbol != NULL) + { + /* allocate a new node */ + node = finsh_node_allocate(FINSH_NODE_ID); + + /* allocate node error */ + if (node == NULL) + { + finsh_error_set(FINSH_ERROR_MEMORY_FULL); + return NULL; + } + + /* fill node value according type */ + switch (type) + { + case FINSH_IDTYPE_VAR: + node->id.var = (struct finsh_var*)symbol; + break; + + case FINSH_IDTYPE_SYSVAR: + node->id.sysvar = (struct finsh_sysvar*)symbol; + break; + + case FINSH_IDTYPE_SYSCALL: + node->id.syscall = (struct finsh_syscall*)symbol; + break; + } + /* fill identifier type */ + node->idtype = type; + } + else finsh_error_set(FINSH_ERROR_UNKNOWN_SYMBOL); + + return node; +} + +struct finsh_node* finsh_node_new_char(char c) +{ + struct finsh_node* node; + + node = finsh_node_allocate(FINSH_NODE_VALUE_CHAR); + if (node == NULL) + { + finsh_error_set(FINSH_ERROR_MEMORY_FULL); + return NULL; + } + + node->value.char_value = c; + return node; +} + +struct finsh_node* finsh_node_new_int(int i) +{ + struct finsh_node* node; + + node = finsh_node_allocate(FINSH_NODE_VALUE_INT); + if (node == NULL) + { + finsh_error_set(FINSH_ERROR_MEMORY_FULL); + return NULL; + } + + node->value.int_value = i; + return node; +} + +struct finsh_node* finsh_node_new_long(long l) +{ + struct finsh_node* node; + + node = finsh_node_allocate(FINSH_NODE_VALUE_LONG); + if (node == NULL) + { + finsh_error_set(FINSH_ERROR_MEMORY_FULL); + return NULL; + } + + node->value.long_value = l; + return node; +} + +struct finsh_node* finsh_node_new_string(char* s) +{ + struct finsh_node* node; + + node = finsh_node_allocate(FINSH_NODE_VALUE_STRING); + if (node == NULL) + { + finsh_error_set(FINSH_ERROR_MEMORY_FULL); + return NULL; + } + + /* make string */ + node->value.ptr = finsh_heap_allocate(strlen(s) + 1); + strncpy(node->value.ptr, s, strlen(s)); + ((uint8_t*)node->value.ptr)[strlen(s)] = '\0'; + + return node; +} + +struct finsh_node* finsh_node_new_ptr(void* ptr) +{ + struct finsh_node* node; + + node = finsh_node_allocate(FINSH_NODE_VALUE_NULL); + if (node == NULL) + { + finsh_error_set(FINSH_ERROR_MEMORY_FULL); + return NULL; + } + + node->value.ptr = ptr; + return node; +} diff --git a/components/rt-thread/components/finsh/finsh_node.h b/components/rt-thread/components/finsh/finsh_node.h new file mode 100644 index 00000000..ff4333df --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_node.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#ifndef __FINSH_NODE_H__ +#define __FINSH_NODE_H__ + +#include + +#define FINSH_NODE_UNKNOWN 0 +#define FINSH_NODE_ID 1 + +#define FINSH_NODE_VALUE_CHAR 2 +#define FINSH_NODE_VALUE_INT 3 +#define FINSH_NODE_VALUE_LONG 4 +#define FINSH_NODE_VALUE_STRING 5 +#define FINSH_NODE_VALUE_NULL 6 + +#define FINSH_NODE_SYS_ADD 7 +#define FINSH_NODE_SYS_SUB 8 +#define FINSH_NODE_SYS_MUL 9 +#define FINSH_NODE_SYS_DIV 10 +#define FINSH_NODE_SYS_MOD 11 +#define FINSH_NODE_SYS_AND 12 +#define FINSH_NODE_SYS_OR 13 +#define FINSH_NODE_SYS_XOR 14 +#define FINSH_NODE_SYS_BITWISE 15 +#define FINSH_NODE_SYS_SHL 16 +#define FINSH_NODE_SYS_SHR 17 +#define FINSH_NODE_SYS_FUNC 18 +#define FINSH_NODE_SYS_ASSIGN 19 +#define FINSH_NODE_SYS_CAST 20 +#define FINSH_NODE_SYS_PREINC 21 +#define FINSH_NODE_SYS_PREDEC 22 +#define FINSH_NODE_SYS_INC 23 +#define FINSH_NODE_SYS_DEC 24 +#define FINSH_NODE_SYS_GETVALUE 25 +#define FINSH_NODE_SYS_GETADDR 26 +#define FINSH_NODE_SYS_NULL 27 + +#define FINSH_DATA_TYPE_VOID 0x00 +#define FINSH_DATA_TYPE_BYTE 0x01 +#define FINSH_DATA_TYPE_WORD 0x02 +#define FINSH_DATA_TYPE_DWORD 0x03 +#define FINSH_DATA_TYPE_PTR 0x10 + +#define FINSH_NODE_VALUE 0 +#define FINSH_NODE_ADDRESS 1 +#define FINSH_NODE_FUNCTION 2 + +int finsh_node_init(void); + +struct finsh_node* finsh_node_allocate(uint8_t type); +struct finsh_node* finsh_node_new_id(char* id); +struct finsh_node* finsh_node_new_char(char c); +struct finsh_node* finsh_node_new_int(int i); +struct finsh_node* finsh_node_new_long(long l); +struct finsh_node* finsh_node_new_string(char* s); +struct finsh_node* finsh_node_new_ptr(void* ptr); + +#define finsh_node_sibling(node) ((node)->sibling) +#define finsh_node_child(node) ((node)->child) + +#endif diff --git a/components/rt-thread/components/finsh/finsh_ops.c b/components/rt-thread/components/finsh/finsh_ops.c new file mode 100644 index 00000000..ec8b8cb5 --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_ops.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#include "finsh_ops.h" +#include "finsh_vm.h" + +#define OP_BIN_BYTE(x) do {\ + (finsh_sp - 2)->char_value = (finsh_sp - 2)->char_value x (finsh_sp - 1)->char_value; \ + finsh_sp--; \ + }while (0) + +#define OP_BIN_WORD(x) do {\ + (finsh_sp - 2)->short_value = (finsh_sp - 2)->short_value x (finsh_sp - 1)->short_value; \ + finsh_sp--; \ + }while (0) + +#define OP_BIN_DWORD(x) do {\ + (finsh_sp - 2)->long_value = (finsh_sp - 2)->long_value x (finsh_sp - 1)->long_value; \ + finsh_sp--; \ + }while (0) + +/* --- noop --- */ +void OP_no_op() +{ + /* none */ + return ; +} + +/* --- add --- */ +void OP_add_byte() +{ + OP_BIN_BYTE(+); + + return ; +} + +void OP_add_word() +{ + OP_BIN_WORD(+); + + return ; +} + +void OP_add_dword() +{ + OP_BIN_DWORD(+); + + return ; +} + +/* --- sub --- */ +void OP_sub_byte() +{ + OP_BIN_BYTE(-); + + return ; +} + +void OP_sub_word() +{ + OP_BIN_WORD(-); + + return ; +} + +void OP_sub_dword() +{ + OP_BIN_DWORD(-); + + return ; +} + +/* --- div --- */ +void OP_div_byte() +{ + OP_BIN_BYTE(/); + + return ; +} + +void OP_div_word() +{ + OP_BIN_WORD(/); + + return ; +} + +void OP_div_dword() +{ + OP_BIN_DWORD(/); + + return ; +} + +/* --- mod --- */ +void OP_mod_byte() +{ + OP_BIN_BYTE(%); + + return ; +} + +void OP_mod_word() +{ + OP_BIN_WORD(%); + + return ; +} + +void OP_mod_dword() +{ + OP_BIN_DWORD(%); + + return ; +} + +/* --- mul --- */ +void OP_mul_byte() +{ + OP_BIN_BYTE(*); + + return ; +} + +void OP_mul_word() +{ + OP_BIN_WORD(*); + + return ; +} + +void OP_mul_dword() +{ + OP_BIN_DWORD(*); + + return ; +} + +/* --- and --- */ +void OP_and_byte() +{ + OP_BIN_BYTE(&); + + return ; +} + +void OP_and_word() +{ + OP_BIN_WORD(&); + + return ; +} + +void OP_and_dword() +{ + OP_BIN_DWORD(&); + + return ; +} + +/* --- or --- */ +void OP_or_byte() +{ + OP_BIN_BYTE(|); + + return ; +} + +void OP_or_word() +{ + OP_BIN_WORD(|); + + return ; +} + +void OP_or_dword() +{ + OP_BIN_DWORD(|); + + return ; +} + +/* --- xor --- */ +void OP_xor_byte() +{ + OP_BIN_BYTE(^); + + return ; +} + +void OP_xor_word() +{ + OP_BIN_WORD(^); + + return ; +} + +void OP_xor_dword() +{ + OP_BIN_DWORD(^); + + return ; +} + +/* --- bw --- */ +void OP_bw_byte() +{ + (finsh_sp - 1)->char_value = ~ ((finsh_sp - 1)->char_value); + + return ; +} + +void OP_bw_word() +{ + (finsh_sp - 1)->short_value = ~ ((finsh_sp - 1)->short_value); + + return ; +} + +void OP_bw_dword() +{ + (finsh_sp - 1)->long_value = ~ ((finsh_sp - 1)->long_value); + + return ; +} + +/* --- shl --- */ +void OP_shl_byte() +{ + OP_BIN_BYTE(<<); + + return ; +} + +void OP_shl_word() +{ + OP_BIN_WORD(<<); + + return ; +} + +void OP_shl_dword() +{ + OP_BIN_DWORD(<<); + + return ; +} + +/* --- shr --- */ +void OP_shr_byte() +{ + OP_BIN_BYTE(>>); + + return ; +} + +void OP_shr_word() +{ + OP_BIN_WORD(>>); + + return ; +} + +void OP_shr_dword() +{ + OP_BIN_DWORD(>>); + + return ; +} + +/* --- ld --- */ +void OP_ld_byte() +{ + finsh_sp->char_value = *finsh_pc; + + finsh_sp++; + finsh_pc++; + + return ; +} + +void OP_ld_word() +{ + finsh_sp->short_value = FINSH_GET16(finsh_pc); + + finsh_sp ++; + finsh_pc += 2; + + return ; +} + +void OP_ld_dword() +{ + finsh_sp->long_value = FINSH_GET32(finsh_pc); + + finsh_sp ++; + finsh_pc += 4; + + return ; +} + +void OP_ld_value_byte() +{ + char* c; + + c = (char*) (FINSH_GET32(finsh_pc)); + + finsh_sp->char_value = *c; + + finsh_sp ++; + finsh_pc += 4; + + return; +} + +void OP_ld_value_byte_stack() +{ + char* c; + + c = (char *)(finsh_sp - 1)->long_value; + (finsh_sp - 1)->char_value = *c; + + return; +} + +void OP_ld_value_word() +{ + short* s; + + s = (short*) (FINSH_GET32(finsh_pc)); + + finsh_sp->short_value = *s; + + finsh_sp ++; + finsh_pc += 4; + + return; +} + +void OP_ld_value_word_stack() +{ + short* s; + + s = (short *)(finsh_sp - 1)->long_value; + (finsh_sp - 1)->short_value = *s; + + return; +} + +void OP_ld_value_dword() +{ + long* l; + + l = (long*) (FINSH_GET32(finsh_pc)); + + finsh_sp->long_value = *l; + + finsh_sp ++; + finsh_pc += 4; + + return; +} + +void OP_ld_value_dword_stack() +{ + long* l; + + l = (long *)(finsh_sp - 1)->long_value; + (finsh_sp - 1)->long_value = *l; + + return; +} + +/* --- st --- */ +/* + * 2006-4-16 bernard + * fixed the sp move bug + */ +void OP_st_byte() +{ + *(char*)((finsh_sp - 2)->long_value) = (finsh_sp - 1)->char_value; + finsh_sp --; + + return ; +} + +/* + * 2006-4-16 bernard + * fixed the sp move bug + */ +void OP_st_word() +{ + *(short*)((finsh_sp - 2)->long_value) = (finsh_sp - 1)->short_value; + finsh_sp --; + + return ; +} + +/* + * 2006-4-16 bernard + * fixed the sp move bug + */ +void OP_st_dword() +{ + *(long*)((finsh_sp - 2)->long_value) = (finsh_sp - 1)->long_value; + finsh_sp --; + + return ; +} + +/* --- pop --- */ +void OP_pop() +{ + finsh_sp --; + return ; +} + +/* --- call --- */ +void OP_call() +{ + /* the max number of arg*/ + unsigned long parameterv[16]; + unsigned int parameters, i; + + typedef unsigned long var_t; + typedef var_t (*op_func)(); + op_func f; + var_t r; + + parameters = *finsh_pc ++; + + i = 0; finsh_sp --; + while (i < parameters) + { + parameterv[parameters - 1 - i] = finsh_sp->long_value; + finsh_sp --; + i++; + } + + f = (op_func)(finsh_sp->long_value); + switch (parameters) + { + case 0: + r = f(0); + break; + + case 1: + r = f(parameterv[0]); + break; + + case 2: + r = f(parameterv[0], parameterv[1]); + break; + + case 3: + r = f(parameterv[0], parameterv[1], parameterv[2]); + break; + + case 4: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3]); + break; + + case 5: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3], + parameterv[4]); + break; + + case 6: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3], + parameterv[4], parameterv[5]); + break; + + case 7: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3], + parameterv[4], parameterv[5], parameterv[6]); + break; + + case 8: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3], + parameterv[4], parameterv[5], parameterv[6], parameterv[7]); + break; + + case 9: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3], + parameterv[4], parameterv[5], parameterv[6], parameterv[7], + parameterv[8]); + break; + + case 10: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3], + parameterv[4], parameterv[5], parameterv[6], parameterv[7], + parameterv[8], parameterv[9]); + break; + + case 11: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3], + parameterv[4], parameterv[5], parameterv[6], parameterv[7], + parameterv[8], parameterv[9], parameterv[10]); + break; + + case 12: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3], + parameterv[4], parameterv[5], parameterv[6], parameterv[7], + parameterv[8], parameterv[9], parameterv[10], parameterv[11]); + break; + + case 13: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3], + parameterv[4], parameterv[5], parameterv[6], parameterv[7], + parameterv[8], parameterv[9], parameterv[10], parameterv[11], + parameterv[12]); + break; + + case 14: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3], + parameterv[4], parameterv[5], parameterv[6], parameterv[7], + parameterv[8], parameterv[9], parameterv[10], parameterv[11], + parameterv[12], parameterv[13]); + break; + + case 15: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3], + parameterv[4], parameterv[5], parameterv[6], parameterv[7], + parameterv[8], parameterv[9], parameterv[10], parameterv[11], + parameterv[12], parameterv[13], parameterv[14]); + break; + + case 16: + r = f(parameterv[0], parameterv[1], parameterv[2], parameterv[3], + parameterv[4], parameterv[5], parameterv[6], parameterv[7], + parameterv[8], parameterv[9], parameterv[10], parameterv[11], + parameterv[12], parameterv[13], parameterv[14], parameterv[15]); + break; + + default: + r = 0; + break; + } + + finsh_sp->long_value = r; + finsh_sp ++; + + return ; +} + +const op_func op_table[] = +{ + /* 00 */ OP_no_op, + /* 01 */ OP_add_byte, + /* 02 */ OP_add_word, + /* 03 */ OP_add_dword, + /* 04 */ OP_sub_byte, + /* 05 */ OP_sub_word, + /* 06 */ OP_sub_dword, + /* 07 */ OP_div_byte, + /* 08 */ OP_div_word, + /* 09 */ OP_div_dword, + /* 10 */ OP_mod_byte, + /* 11 */ OP_mod_word, + /* 12 */ OP_mod_dword, + /* 13 */ OP_mul_byte, + /* 14 */ OP_mul_word, + /* 15 */ OP_mul_dword, + /* 16 */ OP_and_byte, + /* 17 */ OP_and_word, + /* 18 */ OP_and_dword, + /* 19 */ OP_or_byte, + /* 20 */ OP_or_word, + /* 21 */ OP_or_dword, + /* 22 */ OP_xor_byte, + /* 23 */ OP_xor_word, + /* 24 */ OP_xor_dword, + /* 25 */ OP_bw_byte, + /* 26 */ OP_bw_word, + /* 27 */ OP_bw_dword, + /* 28 */ OP_shl_byte, + /* 29 */ OP_shl_word, + /* 30 */ OP_shl_dword, + /* 31 */ OP_shr_byte, + /* 32 */ OP_shr_word, + /* 33 */ OP_shr_dword, + /* 34 */ OP_ld_byte, + /* 35 */ OP_ld_word, + /* 36 */ OP_ld_dword, + /* 37 */ OP_ld_value_byte, + /* 38 */ OP_ld_value_word, + /* 39 */ OP_ld_value_dword, + /* 40 */ OP_st_byte, + /* 41 */ OP_st_word, + /* 42 */ OP_st_dword, + /* 43 */ OP_pop, + /* 44 */ OP_call, + /* 45 */ OP_ld_value_byte_stack, + /* 46 */ OP_ld_value_word_stack, + /* 47 */ OP_ld_value_dword_stack, + NULL +}; diff --git a/components/rt-thread/components/finsh/finsh_ops.h b/components/rt-thread/components/finsh/finsh_ops.h new file mode 100644 index 00000000..0cc945f0 --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_ops.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#ifndef __FINSH_OP_H__ +#define __FINSH_OP_H__ + +#include "finsh_vm.h" + +/* + * FinC VM specification + * Memory + * .VAR + * + * .STACK + * + * .HEAP + * + * .TEXT + * OP [op1] + */ + +#define FINSH_OP_NOOP 0x00 + +/* add @ r1 = r2 + r3 */ +#define FINSH_OP_ADD_BYTE 0x01 +#define FINSH_OP_ADD_WORD 0x02 +#define FINSH_OP_ADD_DWORD 0x03 + +/* sub @ r1 = r2 - r3 */ +#define FINSH_OP_SUB_BYTE 0x04 +#define FINSH_OP_SUB_WORD 0x05 +#define FINSH_OP_SUB_DWORD 0x06 + +/* div @ r1 = r2 / r3 */ +#define FINSH_OP_DIV_BYTE 0x07 +#define FINSH_OP_DIV_WORD 0x08 +#define FINSH_OP_DIV_DWORD 0x09 + +/* mod @ r1 = r2 % r3 */ +#define FINSH_OP_MOD_BYTE 0x0A +#define FINSH_OP_MOD_WORD 0x0B +#define FINSH_OP_MOD_DWORD 0x0C + +/* mul @ r1 = r2 * r3 */ +#define FINSH_OP_MUL_BYTE 0x0D +#define FINSH_OP_MUL_WORD 0x0E +#define FINSH_OP_MUL_DWORD 0x0F + +/* and @ r1 = r2 & r3 */ +#define FINSH_OP_AND_BYTE 0x10 +#define FINSH_OP_AND_WORD 0x11 +#define FINSH_OP_AND_DWORD 0x12 + +/* or @ r1 = r2 | r3 */ +#define FINSH_OP_OR_BYTE 0x13 +#define FINSH_OP_OR_WORD 0x14 +#define FINSH_OP_OR_DWORD 0x15 + +/* xor @ r1 = r2 ^ r3 */ +#define FINSH_OP_XOR_BYTE 0x16 +#define FINSH_OP_XOR_WORD 0x17 +#define FINSH_OP_XOR_DWORD 0x18 + +/* bw @ r1 = ~r2 */ +#define FINSH_OP_BITWISE_BYTE 0x19 +#define FINSH_OP_BITWISE_WORD 0x1A +#define FINSH_OP_BITWISE_DWORD 0x1B + +/* shl @ r1 = r2 << r3 */ +#define FINSH_OP_SHL_BYTE 0x1C +#define FINSH_OP_SHL_WORD 0x1D +#define FINSH_OP_SHL_DWORD 0x1E + +/* shr @ r1 = r2 >> r3 */ +#define FINSH_OP_SHR_BYTE 0x1F +#define FINSH_OP_SHR_WORD 0x20 +#define FINSH_OP_SHR_DWORD 0x21 + +/* ld @ r1 = [r2] */ +#define FINSH_OP_LD_BYTE 0x22 +#define FINSH_OP_LD_WORD 0x23 +#define FINSH_OP_LD_DWORD 0x24 + +#define FINSH_OP_LD_VALUE_BYTE 0x25 +#define FINSH_OP_LD_VALUE_WORD 0x26 +#define FINSH_OP_LD_VALUE_DWORD 0x27 + +/* st @ [r2] = r1 */ +#define FINSH_OP_ST_BYTE 0x28 +#define FINSH_OP_ST_WORD 0x29 +#define FINSH_OP_ST_DWORD 0x2A + +/* pop */ +#define FINSH_OP_POP 0x2B + +/* call r1 @ [r1](stack) */ +#define FINSH_OP_SYSCALL 0x2C + +/* load value from stack */ +#define FINSH_OP_LD_VALUE_BYTE_STACK 0x2D +#define FINSH_OP_LD_VALUE_WORD_STACK 0x2E +#define FINSH_OP_LD_VALUE_DWORD_STACK 0x2F + +/* halt */ +#define FINSH_OP_HALT 0xFF + +typedef void (*op_func)(); +extern const op_func op_table[]; + +#endif diff --git a/components/rt-thread/components/finsh/finsh_parser.c b/components/rt-thread/components/finsh/finsh_parser.c new file mode 100644 index 00000000..4b8b4af7 --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_parser.c @@ -0,0 +1,986 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + * 2013-10-09 Bernard fix the command line too long issue. + */ +#include + +#include "finsh_token.h" +#include "finsh_node.h" +#include "finsh_error.h" +#include "finsh_parser.h" +#include "finsh_var.h" + +/* + * the structure of abstract syntax tree: + * root____________ + * | \ + * child__ sibling__ + * | \ | \ + * child sibling child sibling + * ... + */ +static enum finsh_type proc_type(struct finsh_parser* self); +static int proc_identifier(struct finsh_parser* self, char* id); +static struct finsh_node* proc_variable_decl(struct finsh_parser* self); +static struct finsh_node* proc_expr(struct finsh_parser* self); +static struct finsh_node* proc_assign_expr(struct finsh_parser* self); +static struct finsh_node* proc_inclusive_or_expr(struct finsh_parser* self); +static struct finsh_node* proc_exclusive_or_expr(struct finsh_parser* self); +static struct finsh_node* proc_and_expr(struct finsh_parser* self); +static struct finsh_node* proc_shift_expr(struct finsh_parser* self); +static struct finsh_node* proc_additive_expr(struct finsh_parser* self); +static struct finsh_node* proc_multiplicative_expr(struct finsh_parser* self); +static struct finsh_node* proc_cast_expr(struct finsh_parser* self); +static struct finsh_node* proc_unary_expr(struct finsh_parser* self); +static struct finsh_node* proc_postfix_expr(struct finsh_parser* self); +static struct finsh_node* proc_primary_expr(struct finsh_parser* self); +static struct finsh_node* proc_param_list(struct finsh_parser* self); +static struct finsh_node* proc_expr_statement(struct finsh_parser* self); +static struct finsh_node* make_sys_node(uint8_t type, struct finsh_node* node1, + struct finsh_node* node2); + +/* check token */ +#define check_token(token, lex, type) if ( (token) != (type) ) \ + { \ + finsh_error_set(FINSH_ERROR_INVALID_TOKEN); \ + finsh_token_replay(lex); \ + } + +/* is the token a data type? */ +#define is_base_type(token) ((token) == finsh_token_type_void \ + || (token) == finsh_token_type_char \ + || (token) == finsh_token_type_short \ + || (token) == finsh_token_type_int \ + || (token) == finsh_token_type_long) + +/* get the next token */ +#define next_token(token, lex) (token) = finsh_token_token(lex) + +/* match a specified token */ +#define match_token(token, lex, type) next_token(token, lex); \ + check_token(token, lex, type) + +/* +process for function and variable declaration. +decl_variable -> type declaration_list ';' +declarator_list -> declarator_list ',' declarator + | declarator +declarator -> identifier + | identifier ASSIGN expr_assign +*/ +static struct finsh_node* proc_variable_decl(struct finsh_parser* self) +{ + enum finsh_token_type token; + enum finsh_type type; + char id[FINSH_NAME_MAX + 1]; + + struct finsh_node *node; + struct finsh_node *end; + struct finsh_node *assign; + + node = NULL; + end = NULL; + + /* get type */ + type = proc_type(self); + + /*process id.*/ + if (proc_identifier(self, id) == 0) + { + /* if add variable failed */ + if (finsh_var_insert(id, type) < 0) + { + finsh_error_set(FINSH_ERROR_VARIABLE_EXIST); + } + } + + next_token(token, &(self->token)); + switch ( token ) + { + case finsh_token_type_comma:/*',', it's a variable_list declaration.*/ + if (proc_identifier(self, id) == 0) + { + /* if add variable failed */ + if (finsh_var_insert(id, type) < 0) + { + finsh_error_set(FINSH_ERROR_VARIABLE_EXIST); + } + } + + next_token(token, &(self->token)); + if ( token == finsh_token_type_assign ) + { + /* get the right side of assign expression */ + assign = proc_assign_expr(self); + + if (assign != NULL) + { + struct finsh_node* idnode; + + idnode = finsh_node_new_id(id); + end = make_sys_node(FINSH_NODE_SYS_ASSIGN, idnode, assign); + node = end; + + next_token(token, &(self->token)); + } + } + + while ( token == finsh_token_type_comma ) + { + if (proc_identifier(self, id) == 0) + { + /* if add variable failed */ + if (finsh_var_insert(id, type) < 0) + { + finsh_error_set(FINSH_ERROR_VARIABLE_EXIST); + } + } + + next_token(token, &(self->token)); + if ( token == finsh_token_type_assign ) + { + /* get the right side of assign expression */ + assign = proc_assign_expr(self); + + if (assign != NULL) + { + struct finsh_node* idnode; + + idnode = finsh_node_new_id(id); + + /* make assign expression node */ + if (node != NULL) + { + finsh_node_sibling(end) = make_sys_node(FINSH_NODE_SYS_ASSIGN, idnode, assign); + end = finsh_node_sibling(end); + } + else + { + end = make_sys_node(FINSH_NODE_SYS_ASSIGN, idnode, assign); + node = end; + } + + next_token(token, &(self->token)); + } + } + } + + check_token(token, &(self->token), finsh_token_type_semicolon); + return node; + + case finsh_token_type_assign:/*'=', it's a variable with assign declaration.*/ + { + struct finsh_node *idnode; + + assign = proc_assign_expr(self); + if (assign != NULL) + { + idnode = finsh_node_new_id(id); + + /* make assign expression node */ + end = make_sys_node(FINSH_NODE_SYS_ASSIGN, idnode, assign); + node = end; + + next_token(token, &(self->token)); + } + + while ( token == finsh_token_type_comma ) + { + if (proc_identifier(self, id) == 0) + { + /* if add variable failed */ + if (finsh_var_insert(id, type) < 0) + { + finsh_error_set(FINSH_ERROR_VARIABLE_EXIST); + } + } + + next_token(token, &(self->token)); + if (token == finsh_token_type_assign) + { + /* get the right side of assign expression */ + assign = proc_assign_expr(self); + + if (assign != NULL) + { + idnode = finsh_node_new_id(id); + + /* make assign expression node */ + if (node != NULL) + { + finsh_node_sibling(end) = make_sys_node(FINSH_NODE_SYS_ASSIGN, idnode, assign); + end = finsh_node_sibling(end); + } + else + { + end = make_sys_node(FINSH_NODE_SYS_ASSIGN, idnode, assign); + node = end; + } + + next_token(token, &(self->token)); + } + } + } + + check_token(token, &(self->token), finsh_token_type_semicolon); + return node; + } + + case finsh_token_type_semicolon:/*';', it's a variable declaration.*/ + return node; + + default: + finsh_error_set(FINSH_ERROR_EXPECT_TYPE); + + return NULL; + } +} + +/* +type -> type_prefix type_basic | type_basic +type_prefix -> UNSIGNED +type_basic -> VOID + | CHAR + | SHORT + | INT + | STRING +*/ +static enum finsh_type proc_type(struct finsh_parser* self) +{ + enum finsh_type type; + enum finsh_token_type token; + + /* set init type */ + type = finsh_type_unknown; + + next_token(token, &(self->token)); + if ( is_base_type(token) ) /* base_type */ + { + switch (token) + { + case finsh_token_type_void: + type = finsh_type_void; + break; + + case finsh_token_type_char: + type = finsh_type_char; + break; + + case finsh_token_type_short: + type = finsh_type_short; + break; + + case finsh_token_type_int: + type = finsh_type_int; + break; + + case finsh_token_type_long: + type = finsh_type_long; + break; + + default: + goto __return; + } + } + else if ( token == finsh_token_type_unsigned ) /* unsigned base_type */ + { + next_token(token, &(self->token)); + if ( is_base_type(token) ) + { + switch (token) + { + case finsh_token_type_char: + type = finsh_type_uchar; + break; + + case finsh_token_type_short: + type = finsh_type_ushort; + break; + + case finsh_token_type_int: + type = finsh_type_uint; + break; + + case finsh_token_type_long: + type = finsh_type_ulong; + break; + + default: + goto __return; + } + } + else + { + finsh_token_replay(&(self->token)); + finsh_error_set(FINSH_ERROR_EXPECT_TYPE); + } + } + else + { + goto __return; + } + + /* parse for pointer */ + next_token(token, &(self->token)); + if (token == finsh_token_type_mul) + { + switch (type) + { + case finsh_type_void: + type = finsh_type_voidp; + break; + + case finsh_type_char: + case finsh_type_uchar: + type = finsh_type_charp; + break; + + case finsh_type_short: + case finsh_type_ushort: + type = finsh_type_shortp; + break; + + case finsh_type_int: + case finsh_type_uint: + type = finsh_type_intp; + break; + + case finsh_type_long: + case finsh_type_ulong: + type = finsh_type_longp; + break; + + default: + type = finsh_type_voidp; + break; + } + } + else finsh_token_replay(&(self->token)); + + return type; + +__return: + finsh_token_replay(&(self->token)); + finsh_error_set(FINSH_ERROR_UNKNOWN_TYPE); + + return type; +} + +/* +identifier -> IDENTIFIER +*/ +static int proc_identifier(struct finsh_parser* self, char* id) +{ + enum finsh_token_type token; + + match_token(token, &(self->token), finsh_token_type_identifier); + + strncpy(id, (char*)self->token.string, FINSH_NAME_MAX); + + return 0; +} + +/* +statement_expr -> ';' + | expr ';' +*/ +static struct finsh_node* proc_expr_statement(struct finsh_parser* self) +{ + enum finsh_token_type token; + struct finsh_node* expr; + + expr = NULL; + next_token(token, &(self->token)); + if ( token != finsh_token_type_semicolon ) + { + finsh_token_replay(&(self->token)); + expr = proc_expr(self); + + match_token(token, &(self->token), finsh_token_type_semicolon); + } + + return expr; +} + +/* +expr -> expr_assign +*/ +static struct finsh_node* proc_expr(struct finsh_parser* self) +{ + return proc_assign_expr(self); +} + +/* +expr_assign -> expr_inclusive_or + | expr_unary ASSIGN expr_assign +*/ +static struct finsh_node* proc_assign_expr(struct finsh_parser* self) +{ + enum finsh_token_type token; + struct finsh_node* or; + struct finsh_node* assign; + + or = proc_inclusive_or_expr(self); + + next_token(token, &(self->token)); + + if (token == finsh_token_type_assign) + { + assign = proc_assign_expr(self); + + return make_sys_node(FINSH_NODE_SYS_ASSIGN, or, assign); + } + else finsh_token_replay(&(self->token)); + + return or; +} + +/* +expr_inclusive_or -> expr_exclusive_or + | expr_inclusive_or '|' expr_exclusive_or +*/ +static struct finsh_node* proc_inclusive_or_expr(struct finsh_parser* self) +{ + enum finsh_token_type token; + struct finsh_node* xor; + struct finsh_node* xor_new; + + xor = proc_exclusive_or_expr(self); + + next_token(token, &(self->token)); + while ( token == finsh_token_type_or ) + { + xor_new = proc_exclusive_or_expr(self); + + if (xor_new == NULL) finsh_error_set(FINSH_ERROR_EXPECT_OPERATOR); + else xor = make_sys_node(FINSH_NODE_SYS_OR, xor, xor_new); + + next_token(token, &(self->token)); + } + + finsh_token_replay(&(self->token)); + return xor; +} + +/* +expr_exclusive_or -> expr_and + | expr_exclusive '^' expr_and +*/ +static struct finsh_node* proc_exclusive_or_expr(struct finsh_parser* self) +{ + enum finsh_token_type token; + struct finsh_node* and; + struct finsh_node* and_new; + + and = proc_and_expr(self); + next_token(token, &(self->token)); + while ( token == finsh_token_type_xor ) + { + and_new = proc_and_expr(self); + if (and_new == NULL) finsh_error_set(FINSH_ERROR_EXPECT_OPERATOR); + else and = make_sys_node(FINSH_NODE_SYS_XOR, and, and_new); + + next_token(token, &(self->token)); + } + + finsh_token_replay(&(self->token)); + return and; +} + +/* +expr_and -> expr_shift + | expr_and '&' expr_shift +*/ +static struct finsh_node* proc_and_expr(struct finsh_parser* self) +{ + enum finsh_token_type token; + struct finsh_node* shift; + struct finsh_node* shift_new; + + shift = proc_shift_expr(self); + + next_token(token, &(self->token)); + while ( token == finsh_token_type_and ) + { + shift_new = proc_shift_expr(self); + + if (shift_new == NULL) finsh_error_set(FINSH_ERROR_EXPECT_OPERATOR); + else shift = make_sys_node(FINSH_NODE_SYS_AND, shift, shift_new); + + next_token(token, &(self->token)); + } + + finsh_token_replay(&(self->token)); + return shift; +} + +/* +expr_shift -> expr_additive + | expr_shift '<<' expr_additive + | expr_shift '>>' expr_additive +*/ +static struct finsh_node* proc_shift_expr(struct finsh_parser* self) +{ + enum finsh_token_type token; + struct finsh_node* add; + struct finsh_node* add_new; + + add = proc_additive_expr(self); + + next_token(token, &(self->token)); + while ( token == finsh_token_type_shl || token == finsh_token_type_shr) + { + add_new = proc_additive_expr(self); + if (add_new == NULL) finsh_error_set(FINSH_ERROR_EXPECT_OPERATOR); + else + { + switch (token) + { + case finsh_token_type_shl: + add = make_sys_node(FINSH_NODE_SYS_SHL, add, add_new); + break; + case finsh_token_type_shr: + add = make_sys_node(FINSH_NODE_SYS_SHR, add, add_new); + break; + default: + finsh_error_set(FINSH_ERROR_EXPECT_OPERATOR); + break; + } + } + next_token(token, &(self->token)); + } + + finsh_token_replay(&(self->token)); + return add; +} + +/* +expr_additive -> expr_multiplicative + | expr_additive SUB expr_multiplicative + | expr_additive ADD expr_multiplicative +*/ +static struct finsh_node* proc_additive_expr(struct finsh_parser* self) +{ + enum finsh_token_type token; + struct finsh_node* mul; + struct finsh_node* mul_new; + + mul = proc_multiplicative_expr(self); + + next_token(token, &(self->token)); + while ( token == finsh_token_type_sub || token == finsh_token_type_add ) + { + mul_new = proc_multiplicative_expr(self); + if (mul_new == NULL) finsh_error_set(FINSH_ERROR_EXPECT_OPERATOR); + else + { + switch (token) + { + case finsh_token_type_sub: + mul = make_sys_node(FINSH_NODE_SYS_SUB, mul, mul_new); + break; + case finsh_token_type_add: + mul = make_sys_node(FINSH_NODE_SYS_ADD, mul, mul_new); + break; + default: + finsh_error_set(FINSH_ERROR_EXPECT_OPERATOR); + break; + } + } + next_token(token, &(self->token)); + } + + finsh_token_replay(&(self->token)); + return mul; +} + +/* +expr_multiplicative -> expr_cast + | expr_multiplicative '*' expr_cast + | expr_multiplicative '/' expr_cast + | expr_multiplicative '%' expr_cast +*/ +static struct finsh_node* proc_multiplicative_expr(struct finsh_parser* self) +{ + enum finsh_token_type token; + struct finsh_node* cast; + struct finsh_node* cast_new; + + cast = proc_cast_expr(self); + next_token(token, &(self->token)); + while (token == finsh_token_type_mul || + token == finsh_token_type_div || + token == finsh_token_type_mod ) + { + cast_new = proc_cast_expr(self); + if (cast_new == NULL) finsh_error_set(FINSH_ERROR_EXPECT_OPERATOR); + else + { + switch (token) + { + case finsh_token_type_mul: + cast = make_sys_node(FINSH_NODE_SYS_MUL, cast, cast_new); + break; + + case finsh_token_type_div: + cast = make_sys_node(FINSH_NODE_SYS_DIV, cast, cast_new); + break; + + case finsh_token_type_mod: + cast = make_sys_node(FINSH_NODE_SYS_MOD, cast, cast_new); + break; + + default: + finsh_error_set(FINSH_ERROR_EXPECT_OPERATOR); + break; + } + } + next_token(token, &(self->token)); + } + + finsh_token_replay(&(self->token)); + return cast; +} + +/* +20060313, add recast parse +expr_cast -> expr_unary + | '(' type ')' expr_cast +*/ +static struct finsh_node* proc_cast_expr(struct finsh_parser* self) +{ + enum finsh_token_type token; + enum finsh_type type; + struct finsh_node* cast; + + next_token(token, &(self->token)); + if (token == finsh_token_type_left_paren) + { + type = proc_type(self); + match_token(token, &(self->token), finsh_token_type_right_paren); + + cast = proc_cast_expr(self); + if (cast != NULL) + { + cast->data_type = type; + return cast; + } + } + + finsh_token_replay(&(self->token)); + return proc_unary_expr(self); +} + +/* +20050921, add '*' and '&' +expr_unary -> expr_postfix + | ADD expr_cast + | INC expr_cast + | SUB expr_cast + | DEC expr_cast + | '~' expr_cast + | '*' expr_cast + | '&' expr_cast +*/ +static struct finsh_node* proc_unary_expr(struct finsh_parser* self) +{ + enum finsh_token_type token; + struct finsh_node *cast; + + next_token(token, &(self->token)); + switch (token) + { + case finsh_token_type_add: /* + */ + cast = proc_cast_expr(self); + return cast; + + case finsh_token_type_inc: /* ++ */ + cast = proc_cast_expr(self); + return make_sys_node(FINSH_NODE_SYS_PREINC, cast, NULL); + + case finsh_token_type_sub: /* - */ + cast = proc_cast_expr(self); + return make_sys_node(FINSH_NODE_SYS_SUB, finsh_node_new_long(0), cast); + + case finsh_token_type_dec: /* -- */ + cast = proc_cast_expr(self); + return make_sys_node(FINSH_NODE_SYS_PREDEC, cast, NULL); + + case finsh_token_type_bitwise: /* ~ */ + cast = proc_cast_expr(self); + return make_sys_node(FINSH_NODE_SYS_BITWISE, cast, NULL); + + case finsh_token_type_mul: /* * */ + cast = proc_cast_expr(self); + return make_sys_node(FINSH_NODE_SYS_GETVALUE, cast, NULL); + + case finsh_token_type_and: /* & */ + cast = proc_cast_expr(self); + return make_sys_node(FINSH_NODE_SYS_GETADDR, cast, NULL); + + default: + finsh_token_replay(&(self->token)); + return proc_postfix_expr(self); + } +} + +/* +expr_postfix -> expr_primary + | expr_postfix INC + | expr_postfix DEC + | expr_postfix '(' param_list ')' +*/ +static struct finsh_node* proc_postfix_expr(struct finsh_parser* self) +{ + enum finsh_token_type token; + struct finsh_node* postfix; + + postfix = proc_primary_expr(self); + + next_token(token, &(self->token)); + while ( token == finsh_token_type_inc || + token == finsh_token_type_dec || + token == finsh_token_type_left_paren ) + { + switch (token) + { + case finsh_token_type_inc :/* '++' */ + postfix = make_sys_node(FINSH_NODE_SYS_INC, postfix, NULL); + break; + + case finsh_token_type_dec :/* '--' */ + postfix = make_sys_node(FINSH_NODE_SYS_DEC, postfix, NULL); + break; + + case finsh_token_type_left_paren :/* '(' */ + { + struct finsh_node* param_list; + + param_list = NULL; + next_token(token, &(self->token)); + if (token != finsh_token_type_right_paren) + { + finsh_token_replay(&(self->token)); + param_list = proc_param_list(self); + + match_token(token, &(self->token), finsh_token_type_right_paren); + } + + postfix = make_sys_node(FINSH_NODE_SYS_FUNC, postfix, param_list); + } + break; + + default: + break; + } + + next_token(token, &(self->token)); + } + + finsh_token_replay(&(self->token)); + return postfix; +} + +/* +expr_primary -> literal + | '(' expr ')' + | identifier +*/ +static struct finsh_node* proc_primary_expr(struct finsh_parser* self) +{ + enum finsh_token_type token; + struct finsh_node* expr; + + next_token(token, &(self->token)); + switch ( token ) + { + case finsh_token_type_identifier: + { + char id[FINSH_NAME_MAX + 1]; + + finsh_token_replay(&(self->token)); + proc_identifier(self, id); + return finsh_node_new_id(id); + } + + case finsh_token_type_left_paren: + expr = proc_expr(self); + match_token(token, &(self->token), finsh_token_type_right_paren); + return expr; + + case finsh_token_type_value_int: + return finsh_node_new_int(self->token.value.int_value); + + case finsh_token_type_value_long: + return finsh_node_new_long(self->token.value.long_value); + + case finsh_token_type_value_char: + return finsh_node_new_char(self->token.value.char_value); + + case finsh_token_type_value_string: + return finsh_node_new_string((char*)self->token.string); + + case finsh_token_type_value_null: + return finsh_node_new_ptr(NULL); + + default: + finsh_error_set(FINSH_ERROR_INVALID_TOKEN); + break; + } + + return NULL; +} + +/* +param_list -> empty + | expr_assign + | param_list ',' expr_assign +*/ +static struct finsh_node* proc_param_list(struct finsh_parser* self) +{ + enum finsh_token_type token; + struct finsh_node *node, *assign; + + assign = proc_assign_expr(self); + if (assign == NULL) return NULL; + node = assign; + + next_token(token, &(self->token)); + while (token == finsh_token_type_comma ) + { + finsh_node_sibling(assign) = proc_assign_expr(self); + + if (finsh_node_sibling(assign) != NULL) assign = finsh_node_sibling(assign); + else finsh_error_set(FINSH_ERROR_EXPECT_OPERATOR); + + next_token(token, &(self->token)); + } + + finsh_token_replay(&(self->token)); + + return node; +} + +/* +make a new node as following tree: +new_node +| +node1__ + \ + node2 +*/ +static struct finsh_node* make_sys_node(uint8_t type, struct finsh_node* node1, struct finsh_node* node2) +{ + struct finsh_node* node; + + node = finsh_node_allocate(type); + + if ((node1 != NULL) && (node != NULL)) + { + finsh_node_child(node) = node1; + finsh_node_sibling(node1) = node2; + } + else finsh_error_set(FINSH_ERROR_NULL_NODE); + + return node; +} + +/* +start -> statement_expr | decl_variable +*/ +void finsh_parser_run(struct finsh_parser* self, const uint8_t* string) +{ + enum finsh_token_type token; + struct finsh_node *node; + + node = NULL; + + /* init parser */ + self->parser_string = (uint8_t*)string; + + /* init token */ + finsh_token_init(&(self->token), self->parser_string); + + /* get next token */ + next_token(token, &(self->token)); + while (token != finsh_token_type_eof && token != finsh_token_type_bad) + { + switch (token) + { + case finsh_token_type_identifier: + /* process expr_statement */ + finsh_token_replay(&(self->token)); + + if (self->root != NULL) + { + finsh_node_sibling(node) = proc_expr_statement(self); + if (finsh_node_sibling(node) != NULL) + node = finsh_node_sibling(node); + } + else + { + node = proc_expr_statement(self); + self->root = node; + } + break; + + default: + if (is_base_type(token) || token == finsh_token_type_unsigned) + { + /* variable decl */ + finsh_token_replay(&(self->token)); + + if (self->root != NULL) + { + finsh_node_sibling(node) = proc_variable_decl(self); + if (finsh_node_sibling(node) != NULL) + node = finsh_node_sibling(node); + } + else + { + node = proc_variable_decl(self); + self->root = node; + } + } + else + { + /* process expr_statement */ + finsh_token_replay(&(self->token)); + + if (self->root != NULL) + { + finsh_node_sibling(node) = proc_expr_statement(self); + if (finsh_node_sibling(node) != NULL) + node = finsh_node_sibling(node); + else next_token(token, &(self->token)); + } + else + { + node = proc_expr_statement(self); + self->root = node; + } + } + + break; + } + + /* no root found, break out */ + if (self->root == NULL) break; + + /* get next token */ + next_token(token, &(self->token)); + } +} + +int finsh_parser_init(struct finsh_parser* self) +{ + memset(self, 0, sizeof(struct finsh_parser)); + + return 0; +} diff --git a/components/rt-thread/components/finsh/finsh_parser.h b/components/rt-thread/components/finsh/finsh_parser.h new file mode 100644 index 00000000..2d014809 --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_parser.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#ifndef __FINSH_PARSER_H__ +#define __FINSH_PARSER_H__ + +#include + +int finsh_parser_init(struct finsh_parser* self); +void finsh_parser_run(struct finsh_parser* self, const uint8_t* string); + +#endif diff --git a/components/rt-thread/components/finsh/finsh_token.c b/components/rt-thread/components/finsh/finsh_token.c new file mode 100644 index 00000000..dfd613da --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_token.c @@ -0,0 +1,598 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + * 2013-04-03 Bernard strip more characters. + */ +#include +#include + +#include "finsh_token.h" +#include "finsh_error.h" + +#define is_alpha(ch) ((ch | 0x20) - 'a') < 26u +#define is_digit(ch) ((ch) >= '0' && (ch) <= '9') +#define is_xdigit(ch) (((ch) >= '0' && (ch) <= '9') || (((ch | 0x20) - 'a') < 6u)) +#define is_separator(ch) !(((ch) >= 'a' && (ch) <= 'z') \ + || ((ch) >= 'A' && (ch) <= 'Z') || ((ch) >= '0' && (ch) <= '9') || ((ch) == '_')) +#define is_eof(self) (self)->eof + +struct name_table +{ + char* name; + enum finsh_token_type type; +}; + +/* keyword */ +static const struct name_table finsh_name_table[] = +{ + {"void", finsh_token_type_void}, + {"char", finsh_token_type_char}, + {"short", finsh_token_type_short}, + {"int", finsh_token_type_int}, + {"long", finsh_token_type_long}, + {"unsigned", finsh_token_type_unsigned}, + + {"NULL", finsh_token_type_value_null}, + {"null", finsh_token_type_value_null} +}; + +static char token_next_char(struct finsh_token* self); +static void token_prev_char(struct finsh_token* self); +static long token_spec_number(char* string, int length, int b); +static void token_run(struct finsh_token* self); +static int token_match_name(struct finsh_token* self, const char* str); +static void token_proc_number(struct finsh_token* self); +static uint8_t* token_proc_string(struct finsh_token* self); +static void token_trim_space(struct finsh_token* self); +static char token_proc_char(struct finsh_token* self); +static int token_proc_escape(struct finsh_token* self); + +void finsh_token_init(struct finsh_token* self, uint8_t* line) +{ + memset(self, 0, sizeof(struct finsh_token)); + + self->line = line; +} + +enum finsh_token_type finsh_token_token(struct finsh_token* self) +{ + if ( self->replay ) self->replay = 0; + else token_run(self); + + return (enum finsh_token_type)self->current_token; +} + +void finsh_token_get_token(struct finsh_token* self, uint8_t* token) +{ + strncpy((char*)token, (char*)self->string, FINSH_NAME_MAX); +} + +int token_get_string(struct finsh_token* self, uint8_t* str) +{ + unsigned char *p=str; + char ch; + + ch = token_next_char(self); + if (is_eof(self)) return -1; + + str[0] = '\0'; + + if ( is_digit(ch) )/*the first character of identifier is not a digit.*/ + { + token_prev_char(self); + return -1; + } + + while (!is_separator(ch) && !is_eof(self)) + { + *p++ = ch; + + ch = token_next_char(self); + } + self->eof = 0; + + token_prev_char(self); + *p = '\0'; + + return 0; +} + +/* +get next character. +*/ +static char token_next_char(struct finsh_token* self) +{ + if (self->eof) return '\0'; + + if (self->position == (int)strlen((char*)self->line) || self->line[self->position] =='\n') + { + self->eof = 1; + self->position = 0; + return '\0'; + } + + return self->line[self->position++]; +} + +static void token_prev_char(struct finsh_token* self) +{ + if ( self->eof ) return; + + if ( self->position == 0 ) return; + else self->position--; +} + +static void token_run(struct finsh_token* self) +{ + char ch; + + token_trim_space(self); /* first trim space and tab. */ + token_get_string(self, &(self->string[0])); + + if ( is_eof(self) ) /*if it is eof, break;*/ + { + self->current_token = finsh_token_type_eof; + return ; + } + + if (self->string[0] != '\0') /*It is a key word or a identifier.*/ + { + if ( !token_match_name(self, (char*)self->string) ) + { + self->current_token = finsh_token_type_identifier; + } + } + else/*It is a operator character.*/ + { + ch = token_next_char(self); + + switch ( ch ) + { + case '(': + self->current_token = finsh_token_type_left_paren; + break; + + case ')': + self->current_token = finsh_token_type_right_paren; + break; + + case ',': + self->current_token = finsh_token_type_comma; + break; + + case ';': + self->current_token = finsh_token_type_semicolon; + break; + + case '&': + self->current_token = finsh_token_type_and; + break; + + case '*': + self->current_token = finsh_token_type_mul; + break; + + case '+': + ch = token_next_char(self); + + if ( ch == '+' ) + { + self->current_token = finsh_token_type_inc; + } + else + { + token_prev_char(self); + self->current_token = finsh_token_type_add; + } + break; + + case '-': + ch = token_next_char(self); + + if ( ch == '-' ) + { + self->current_token = finsh_token_type_dec; + } + else + { + token_prev_char(self); + self->current_token = finsh_token_type_sub; + } + break; + + case '/': + ch = token_next_char(self); + if (ch == '/') + { + /* line comments, set to end of file */ + self->current_token = finsh_token_type_eof; + } + else + { + token_prev_char(self); + self->current_token = finsh_token_type_div; + } + break; + + case '<': + ch = token_next_char(self); + + if ( ch == '<' ) + { + self->current_token = finsh_token_type_shl; + } + else + { + token_prev_char(self); + self->current_token = finsh_token_type_bad; + } + break; + + case '>': + ch = token_next_char(self); + + if ( ch == '>' ) + { + self->current_token = finsh_token_type_shr; + } + else + { + token_prev_char(self); + self->current_token = finsh_token_type_bad; + } + break; + + case '|': + self->current_token = finsh_token_type_or; + break; + + case '%': + self->current_token = finsh_token_type_mod; + break; + + case '~': + self->current_token = finsh_token_type_bitwise; + break; + + case '^': + self->current_token = finsh_token_type_xor; + break; + + case '=': + self->current_token = finsh_token_type_assign; + break; + + case '\'': + self->value.char_value = token_proc_char(self); + self->current_token = finsh_token_type_value_char; + break; + + case '"': + token_proc_string(self); + self->current_token = finsh_token_type_value_string; + break; + + default: + if ( is_digit(ch) ) + { + token_prev_char(self); + token_proc_number(self); + break; + } + + finsh_error_set(FINSH_ERROR_UNKNOWN_TOKEN); + self->current_token = finsh_token_type_bad; + + break; + } + } +} + +static int token_match_name(struct finsh_token* self, const char* str) +{ + int i; + + for (i = 0; i < sizeof(finsh_name_table)/sizeof(struct name_table); i++) + { + if ( strcmp(finsh_name_table[i].name, str)==0 ) + { + self->current_token = finsh_name_table[i].type; + return 1; + } + } + + return 0; +} + +static void token_trim_space(struct finsh_token* self) +{ + char ch; + while ( (ch = token_next_char(self)) ==' ' || + ch == '\t' || + ch == '\r'); + + token_prev_char(self); +} + +static char token_proc_char(struct finsh_token* self) +{ + char ch; + char buf[4], *p; + + p = buf; + ch = token_next_char(self); + + if ( ch == '\\' ) + { + ch = token_next_char(self); + switch ( ch ) + { + case 'n': ch = '\n'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + case 'b': ch = '\b'; break; + case 'r': ch = '\r'; break; + case '\\': ch = '\\'; break; + case '\'': ch = '\''; break; + default : + while ( is_digit(ch) )/*for '\113' char*/ + { + ch = token_next_char(self); + *p++ = ch; + } + + token_prev_char(self); + *p = '\0'; + ch = atoi(p); + break; + } + } + + if ( token_next_char(self) != '\'' ) + { + token_prev_char(self); + finsh_error_set(FINSH_ERROR_EXPECT_CHAR); + return ch; + } + + return ch; +} + +static uint8_t* token_proc_string(struct finsh_token* self) +{ + uint8_t* p; + + for ( p = &self->string[0]; p - &(self->string[0]) < FINSH_STRING_MAX; ) + { + char ch = token_next_char(self); + + if ( is_eof(self) ) + { + finsh_error_set(FINSH_ERROR_UNEXPECT_END); + return NULL;; + } + if ( ch == '\\' ) + { + ch = token_proc_escape(self); + } + else if ( ch == '"' )/*end of string.*/ + { + *p = '\0'; + return self->string; + } + + *p++ = ch; + } + + return NULL; +} + +static int token_proc_escape(struct finsh_token* self) +{ + char ch; + int result=0; + + ch = token_next_char(self); + switch (ch) + { + case 'n': + result = '\n'; + break; + case 't': + result = '\t'; + break; + case 'v': + result = '\v'; + break; + case 'b': + result = '\b'; + break; + case 'r': + result = '\r'; + break; + case 'f': + result = '\f'; + break; + case 'a': + result = '\007'; + break; + case '"': + result = '"'; + break; + case 'x': + case 'X': + result = 0; + ch = token_next_char(self); + while (is_xdigit(ch)) + { + result = result * 16 + ((ch < 'A') ? (ch - '0') : (ch | 0x20) - 'a' + 10); + ch = token_next_char(self); + } + token_prev_char(self); + break; + default: + if ( (ch - '0') < 8u) + { + result = 0; + while ( (ch - '0') < 8u ) + { + result = result*8 + ch - '0'; + ch = token_next_char(self); + } + + token_prev_char(self); + } + break; + } + + return result; +} + +/* +(0|0x|0X|0b|0B)number+(l|L) +*/ +static void token_proc_number(struct finsh_token* self) +{ + char ch; + char *p, buf[128]; + long value; + + value = 0; + p = buf; + + ch = token_next_char(self); + if ( ch == '0' ) + { + int b; + ch = token_next_char(self); + if ( ch == 'x' || ch == 'X' )/*it's a hex number*/ + { + b = 16; + ch = token_next_char(self); + while ( is_digit(ch) || is_alpha(ch) ) + { + *p++ = ch; + ch = token_next_char(self); + } + + *p = '\0'; + } + else if ( ch == 'b' || ch == 'B' ) + { + b = 2; + ch = token_next_char(self); + while ( (ch=='0')||(ch=='1') ) + { + *p++ = ch; + ch = token_next_char(self); + } + + *p = '\0'; + } + else if ( '0' <= ch && ch <= '7' ) + { + b = 8; + while ( '0' <= ch && ch <= '7' ) + { + *p++ = ch; + ch = token_next_char(self); + } + + *p = '\0'; + } + else + { + token_prev_char(self); + + /* made as 0 value */ + self->value.int_value = 0; + self->current_token = finsh_token_type_value_int; + return; + } + + self->value.int_value = token_spec_number(buf, strlen(buf), b); + self->current_token = finsh_token_type_value_int; + } + else + { + while ( is_digit(ch) ) + { + value = value*10 + ( ch - '0' ); + ch = token_next_char(self); + } + + self->value.int_value = value; + self->current_token = finsh_token_type_value_int; + } + + switch ( ch ) + { + case 'l': + case 'L': + self->current_token = finsh_token_type_value_long; + break; + + default: + token_prev_char(self); + break; + } +} + +/*use 64 bit number*/ +#define BN_SIZE 2 + +static long token_spec_number(char* string, int length, int b) +{ + char* p; + int t; + int i, j, shift=1; + unsigned int bn[BN_SIZE], v; + long d; + + p = string; + i = 0; + + switch ( b ) + { + case 16: shift = 4; + break; + case 8: shift = 3; + break; + case 2: shift = 1; + break; + default: break; + } + + for ( j=0; j='a' && t <='f' ) + { + t = t - 'a' +10; + } + else if ( t >='A' && t <='F' ) + { + t = t - 'A' +10; + } + else t = t - '0'; + + for ( j=0; j> (32 - shift); + } + i++; + } + + d = (long)bn[0]; + + return d; +} diff --git a/components/rt-thread/components/finsh/finsh_token.h b/components/rt-thread/components/finsh/finsh_token.h new file mode 100644 index 00000000..9845357b --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_token.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#ifndef __FINSH_TOKEN_H__ +#define __FINSH_TOKEN_H__ + +#include + +enum finsh_token_type +{ + finsh_token_type_left_paren = 1, /* ( */ + finsh_token_type_right_paren , /* ) */ + finsh_token_type_comma , /* , */ + finsh_token_type_semicolon , /* ; */ + finsh_token_type_mul , /* * */ + finsh_token_type_add , /* + */ + finsh_token_type_inc , /* ++ */ + finsh_token_type_sub , /* - */ + finsh_token_type_dec , /* -- */ + finsh_token_type_div , /* / */ + finsh_token_type_mod , /* % */ + finsh_token_type_assign , /* = */ + finsh_token_type_and, /* & */ + finsh_token_type_or, /* | */ + finsh_token_type_xor, /* ^ */ + finsh_token_type_bitwise, /* ~ */ + finsh_token_type_shl, /* << */ + finsh_token_type_shr, /* >> */ + finsh_token_type_comments, /* // */ + /*-- data type --*/ + finsh_token_type_void, /* void */ + finsh_token_type_char, /* char */ + finsh_token_type_short, /* short */ + finsh_token_type_int, /* int */ + finsh_token_type_long, /* long */ + finsh_token_type_unsigned, /* unsigned */ + /* data value type */ + finsh_token_type_value_char, /* v:char */ + finsh_token_type_value_int, /* v:int */ + finsh_token_type_value_long, /* v:long */ + finsh_token_type_value_string, /* v:string */ + finsh_token_type_value_null, /* NULL */ + /*-- others --*/ + finsh_token_type_identifier, /* ID */ + finsh_token_type_bad, /* bad token */ + finsh_token_type_eof +}; + +#define finsh_token_position(self) (self)->position +#define finsh_token_replay(self) (self)->replay = 1 + +void finsh_token_init(struct finsh_token* self, uint8_t* script); + +enum finsh_token_type finsh_token_token(struct finsh_token* self); +void finsh_token_get_token(struct finsh_token* self, uint8_t* token); + +#endif diff --git a/components/rt-thread/components/finsh/finsh_var.c b/components/rt-thread/components/finsh/finsh_var.c new file mode 100644 index 00000000..2e3df30c --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_var.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + * 2012-04-27 Bernard fixed finsh_var_delete issue which + * is found by Grissiom. + */ +#include +#include "finsh_var.h" + +struct finsh_var global_variable[FINSH_VARIABLE_MAX]; +struct finsh_sysvar_item* global_sysvar_list; + +int finsh_var_init() +{ + memset(global_variable, 0, sizeof(global_variable)); + + return 0; +} + +int finsh_var_insert(const char* name, int type) +{ + int i, empty; + + empty = -1; + for (i = 0; i < FINSH_VARIABLE_MAX; i ++) + { + /* there is a same name variable exist. */ + if (strncmp(global_variable[i].name, name, FINSH_NAME_MAX) == 0) + return -1; + + if (global_variable[i].type == finsh_type_unknown && empty == -1) + { + empty = i; + } + } + + /* there is no empty entry */ + if (empty == -1) return -1; + + /* insert entry */ + strncpy(global_variable[empty].name, name, FINSH_NAME_MAX); + global_variable[empty].type = type; + + /* return the offset */ + return empty; +} + +int finsh_var_delete(const char* name) +{ + int i; + + for (i = 0; i < FINSH_VARIABLE_MAX; i ++) + { + if (strncmp(global_variable[i].name, name, FINSH_NAME_MAX) == 0) + break; + } + + /* can't find variable */ + if (i == FINSH_VARIABLE_MAX) return -1; + + memset(&global_variable[i], 0, sizeof(struct finsh_var)); + + return 0; +} + +struct finsh_var* finsh_var_lookup(const char* name) +{ + int i; + + for (i = 0; i < FINSH_VARIABLE_MAX; i ++) + { + if (strncmp(global_variable[i].name, name, FINSH_NAME_MAX) == 0) + break; + } + + /* can't find variable */ + if (i == FINSH_VARIABLE_MAX) return NULL; + + return &global_variable[i]; +} + +#ifdef RT_USING_HEAP +void finsh_sysvar_append(const char* name, uint8_t type, void* var_addr) +{ + /* create a sysvar */ + struct finsh_sysvar_item* item; + + item = (struct finsh_sysvar_item*) rt_malloc (sizeof(struct finsh_sysvar_item)); + if (item != NULL) + { + item->next = NULL; + item->sysvar.name = rt_strdup(name); + item->sysvar.type = type; + item->sysvar.var = var_addr; + + if (global_sysvar_list == NULL) + { + global_sysvar_list = item; + } + else + { + item->next = global_sysvar_list; + global_sysvar_list = item; + } + } +} +#endif + +struct finsh_sysvar* finsh_sysvar_lookup(const char* name) +{ + struct finsh_sysvar* index; + struct finsh_sysvar_item* item; + + for (index = _sysvar_table_begin; + index < _sysvar_table_end; + FINSH_NEXT_SYSVAR(index)) + { + if (strcmp(index->name, name) == 0) + return index; + } + + /* find in sysvar list */ + item = global_sysvar_list; + while (item != NULL) + { + if (strncmp(item->sysvar.name, name, strlen(name)) == 0) + { + return &(item->sysvar); + } + + /* move to next item */ + item = item->next; + } + + /* can't find variable */ + return NULL; +} diff --git a/components/rt-thread/components/finsh/finsh_var.h b/components/rt-thread/components/finsh/finsh_var.h new file mode 100644 index 00000000..796f9794 --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_var.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#ifndef __FINSH_VAR_H__ +#define __FINSH_VAR_H__ + +#include + +/* + * The variable in finsh is put in data segment as a global variable. + * The 'finsh_var' structure presents the structure of variable in data segment. + */ +struct finsh_var +{ + char name[FINSH_NAME_MAX + 1]; /* the name of variable */ + + uint8_t type; /* the type of variable */ + + /* variable value */ + union { + char char_value; + short short_value; + int int_value; + long long_value; + void* ptr; + }value; +}; +extern struct finsh_var global_variable[]; + +int finsh_var_init(void); +int finsh_var_insert(const char* name, int type); +int finsh_var_delete(const char* name); +struct finsh_var* finsh_var_lookup(const char* name); + +#endif diff --git a/components/rt-thread/components/finsh/finsh_vm.c b/components/rt-thread/components/finsh/finsh_vm.c new file mode 100644 index 00000000..e56cedbd --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_vm.c @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#include + +#include "finsh_vm.h" +#include "finsh_ops.h" +#include "finsh_var.h" + +/* stack */ +union finsh_value finsh_vm_stack[FINSH_STACK_MAX]; +/* text segment */ +uint8_t text_segment[FINSH_TEXT_MAX]; + +union finsh_value* finsh_sp; /* stack pointer */ +uint8_t* finsh_pc; /* PC */ + +/* syscall list, for dynamic system call register */ +struct finsh_syscall_item* global_syscall_list = NULL; + +// #define FINSH_VM_DISASSEMBLE +void finsh_vm_run() +{ + uint8_t op; + + /* if you want to disassemble the byte code, please define FINSH_VM_DISASSEMBLE */ +#ifdef FINSH_VM_DISASSEMBLE + void finsh_disassemble(); + finsh_disassemble(); +#endif + + /* set sp(stack pointer) to the beginning of stack */ + finsh_sp = &finsh_vm_stack[0]; + + /* set pc to the beginning of text segment */ + finsh_pc = &text_segment[0]; + + while ((finsh_pc - &text_segment[0] >= 0) && + (finsh_pc - &text_segment[0] < FINSH_TEXT_MAX)) + { + /* get op */ + op = *finsh_pc++; + + /* call op function */ + op_table[op](); + } +} + +#ifdef RT_USING_HEAP +void finsh_syscall_append(const char* name, syscall_func func) +{ + /* create the syscall */ + struct finsh_syscall_item* item; + + item = (struct finsh_syscall_item*)rt_malloc(sizeof(struct finsh_syscall_item)); + if (item != RT_NULL) + { + item->next = NULL; + item->syscall.name = rt_strdup(name); + item->syscall.func = func; + + if (global_syscall_list == NULL) + { + global_syscall_list = item; + } + else + { + item->next = global_syscall_list; + global_syscall_list = item; + } + } +} +#endif + +struct finsh_syscall* finsh_syscall_lookup(const char* name) +{ + struct finsh_syscall* index; + struct finsh_syscall_item* item; + + for (index = _syscall_table_begin; index < _syscall_table_end; FINSH_NEXT_SYSCALL(index)) + { + if (strcmp(index->name, name) == 0) + return index; + } + + /* find on syscall list */ + item = global_syscall_list; + while (item != NULL) + { + if (strncmp(item->syscall.name, name, strlen(name)) == 0) + { + return &(item->syscall); + } + + item = item->next; + } + + return NULL; +} + +#ifdef FINSH_VM_DISASSEMBLE +void finsh_disassemble() +{ + uint8_t *pc, op; + + pc = &text_segment[0]; + while (*pc != 0) + { + op = *pc; + switch (op) + { + case FINSH_OP_ADD_BYTE: + pc ++; + rt_kprintf("addb\n"); + break; + + case FINSH_OP_SUB_BYTE: + pc ++; + rt_kprintf("subb\n"); + break; + + case FINSH_OP_DIV_BYTE: + pc ++; + rt_kprintf("divb\n"); + break; + + case FINSH_OP_MOD_BYTE: + pc ++; + rt_kprintf("modb\n"); + break; + + case FINSH_OP_MUL_BYTE: + pc ++; + rt_kprintf("mulb\n"); + break; + + case FINSH_OP_AND_BYTE: + pc ++; + rt_kprintf("andb\n"); + break; + + case FINSH_OP_OR_BYTE: + pc ++; + rt_kprintf("orb\n"); + break; + + case FINSH_OP_XOR_BYTE: + pc ++; + rt_kprintf("xorb\n"); + break; + + case FINSH_OP_BITWISE_BYTE: + pc ++; + rt_kprintf("bwb\n"); + break; + + case FINSH_OP_SHL_BYTE: + pc ++; + rt_kprintf("shlb\n"); + break; + + case FINSH_OP_SHR_BYTE: + pc ++; + rt_kprintf("shrb\n"); + break; + + case FINSH_OP_LD_BYTE: + pc ++; + rt_kprintf("ldb %d\n", *pc++); + break; + + case FINSH_OP_LD_VALUE_BYTE: + pc ++; + rt_kprintf("ldb [0x%x]\n", FINSH_GET32(pc)); + pc += 4; + break; + + case FINSH_OP_ST_BYTE: + pc ++; + rt_kprintf("stb\n"); + break; + + case FINSH_OP_ADD_WORD: + pc ++; + rt_kprintf("addw\n"); + break; + + case FINSH_OP_SUB_WORD: + pc ++; + rt_kprintf("subw\n"); + break; + + case FINSH_OP_DIV_WORD: + pc ++; + rt_kprintf("divw\n"); + break; + + case FINSH_OP_MOD_WORD: + pc ++; + rt_kprintf("modw\n"); + break; + + case FINSH_OP_MUL_WORD: + pc ++; + rt_kprintf("mulw\n"); + break; + + case FINSH_OP_AND_WORD: + pc ++; + rt_kprintf("andw\n"); + break; + + case FINSH_OP_OR_WORD: + pc ++; + rt_kprintf("orw\n"); + break; + + case FINSH_OP_XOR_WORD: + pc ++; + rt_kprintf("xorw\n"); + break; + + case FINSH_OP_BITWISE_WORD: + pc ++; + rt_kprintf("bww\n"); + break; + + case FINSH_OP_SHL_WORD: + pc ++; + rt_kprintf("shlw\n"); + break; + + case FINSH_OP_SHR_WORD: + pc ++; + rt_kprintf("shrw\n"); + break; + + case FINSH_OP_LD_WORD: + pc ++; + rt_kprintf("ldw %d\n", FINSH_GET16(pc)); + pc += 2; + break; + + case FINSH_OP_LD_VALUE_WORD: + pc ++; + rt_kprintf("ldw [0x%x]\n", FINSH_GET32(pc)); + pc += 4; + break; + + case FINSH_OP_ST_WORD: + pc ++; + rt_kprintf("stw\n"); + break; + + case FINSH_OP_ADD_DWORD: + pc ++; + rt_kprintf("addd\n"); + break; + + case FINSH_OP_SUB_DWORD: + pc ++; + rt_kprintf("subd\n"); + break; + + case FINSH_OP_DIV_DWORD: + pc ++; + rt_kprintf("divd\n"); + break; + + case FINSH_OP_MOD_DWORD: + pc ++; + rt_kprintf("modd\n"); + break; + + case FINSH_OP_MUL_DWORD: + pc ++; + rt_kprintf("muld\n"); + break; + + case FINSH_OP_AND_DWORD: + pc ++; + rt_kprintf("andd\n"); + break; + + case FINSH_OP_OR_DWORD: + pc ++; + rt_kprintf("ord\n"); + break; + + case FINSH_OP_XOR_DWORD: + pc ++; + rt_kprintf("xord\n"); + break; + + case FINSH_OP_BITWISE_DWORD: + pc ++; + rt_kprintf("bwd\n"); + break; + + case FINSH_OP_SHL_DWORD: + pc ++; + rt_kprintf("shld\n"); + break; + + case FINSH_OP_SHR_DWORD: + pc ++; + rt_kprintf("shrd\n"); + break; + + case FINSH_OP_LD_DWORD: + pc ++; + rt_kprintf("ldd 0x%x\n", FINSH_GET32(pc)); + pc += 4; + break; + + case FINSH_OP_LD_VALUE_DWORD: + pc ++; + rt_kprintf("ldd [0x%x]\n", FINSH_GET32(pc)); + pc += 4; + break; + + case FINSH_OP_ST_DWORD: + pc ++; + rt_kprintf("std\n"); + break; + + case FINSH_OP_POP: + rt_kprintf("pop\n"); + pc ++; + break; + + case FINSH_OP_SYSCALL: + pc ++; + rt_kprintf("syscall %d\n", *pc++); + break; + + case FINSH_OP_LD_VALUE_BYTE_STACK: + pc ++; + rt_kprintf("ldb [sp]\n"); + break; + + case FINSH_OP_LD_VALUE_WORD_STACK: + pc ++; + rt_kprintf("ldw [sp]\n"); + break; + + case FINSH_OP_LD_VALUE_DWORD_STACK: + pc ++; + rt_kprintf("ldd [sp]\n"); + break; + + default: + return; + } + } +} +#endif diff --git a/components/rt-thread/components/finsh/finsh_vm.h b/components/rt-thread/components/finsh/finsh_vm.h new file mode 100644 index 00000000..15d5da1e --- /dev/null +++ b/components/rt-thread/components/finsh/finsh_vm.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ +#ifndef __FINSH_VM_H__ +#define __FINSH_VM_H__ + +#include + +#include "finsh_var.h" + +union finsh_value { + char char_value; + short short_value; + long long_value; + void* ptr; +}; + +extern union finsh_value* finsh_sp; /* stack pointer */ +extern uint8_t* finsh_pc; /* PC */ + +/* stack */ +extern union finsh_value finsh_vm_stack[FINSH_STACK_MAX]; +/* text segment */ +extern uint8_t text_segment[FINSH_TEXT_MAX]; + +void finsh_vm_run(void); +//void finsh_disassemble(void); + +#endif diff --git a/components/rt-thread/components/finsh/msh.c b/components/rt-thread/components/finsh/msh.c new file mode 100644 index 00000000..ed12219c --- /dev/null +++ b/components/rt-thread/components/finsh/msh.c @@ -0,0 +1,647 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-03-30 Bernard the first verion for finsh + * 2014-01-03 Bernard msh can execute module. + * 2017-07-19 Aubr.Cool limit argc to RT_FINSH_ARG_MAX + */ +#include + +#ifdef FINSH_USING_MSH + +#include "msh.h" +#include +#include + +#ifdef RT_USING_DFS +#include +#endif + +#ifdef RT_USING_MODULE +#include +#endif + +#ifndef FINSH_ARG_MAX +#define FINSH_ARG_MAX 8 +#endif + +typedef int (*cmd_function_t)(int argc, char **argv); + +#ifdef FINSH_USING_MSH +#ifdef FINSH_USING_MSH_ONLY +rt_bool_t msh_is_used(void) +{ + return RT_TRUE; +} +#else +#ifdef FINSH_USING_MSH_DEFAULT +static rt_bool_t __msh_state = RT_TRUE; +#else +static rt_bool_t __msh_state = RT_FALSE; +#endif +rt_bool_t msh_is_used(void) +{ + return __msh_state; +} + +static int msh_exit(int argc, char **argv) +{ + /* return to finsh shell mode */ + __msh_state = RT_FALSE; + return 0; +} +FINSH_FUNCTION_EXPORT_ALIAS(msh_exit, __cmd_exit, return to RT-Thread shell mode.); + +static int msh_enter(void) +{ + /* enter module shell mode */ + __msh_state = RT_TRUE; + return 0; +} +FINSH_FUNCTION_EXPORT_ALIAS(msh_enter, msh, use module shell); +#endif + +int msh_help(int argc, char **argv) +{ + rt_kprintf("RT-Thread shell commands:\n"); + { + struct finsh_syscall *index; + + for (index = _syscall_table_begin; + index < _syscall_table_end; + FINSH_NEXT_SYSCALL(index)) + { + if (strncmp(index->name, "__cmd_", 6) != 0) continue; +#if defined(FINSH_USING_DESCRIPTION) && defined(FINSH_USING_SYMTAB) + rt_kprintf("%-16s - %s\n", &index->name[6], index->desc); +#else + rt_kprintf("%s ", &index->name[6]); +#endif + } + } + rt_kprintf("\n"); + + return 0; +} +FINSH_FUNCTION_EXPORT_ALIAS(msh_help, __cmd_help, RT-Thread shell help.); + +int cmd_ps(int argc, char **argv) +{ + extern long list_thread(void); + extern int list_module(void); + +#ifdef RT_USING_MODULE + if ((argc == 2) && (strcmp(argv[1], "-m") == 0)) + list_module(); + else +#endif + list_thread(); + return 0; +} +FINSH_FUNCTION_EXPORT_ALIAS(cmd_ps, __cmd_ps, List threads in the system.); + +#ifdef RT_USING_HEAP +int cmd_free(int argc, char **argv) +{ + extern void list_mem(void); + extern void list_memheap(void); + +#ifdef RT_USING_MEMHEAP_AS_HEAP + list_memheap(); +#else + list_mem(); +#endif + return 0; +} +FINSH_FUNCTION_EXPORT_ALIAS(cmd_free, __cmd_free, Show the memory usage in the system.); +#endif + +static int msh_split(char *cmd, rt_size_t length, char *argv[FINSH_ARG_MAX]) +{ + char *ptr; + rt_size_t position; + rt_size_t argc; + rt_size_t i; + + ptr = cmd; + position = 0; argc = 0; + + while (position < length) + { + /* strip bank and tab */ + while ((*ptr == ' ' || *ptr == '\t') && position < length) + { + *ptr = '\0'; + ptr ++; position ++; + } + + if(argc >= FINSH_ARG_MAX) + { + rt_kprintf("Too many args ! We only Use:\n"); + for(i = 0; i < argc; i++) + { + rt_kprintf("%s ", argv[i]); + } + rt_kprintf("\n"); + break; + } + + if (position >= length) break; + + /* handle string */ + if (*ptr == '"') + { + ptr ++; position ++; + argv[argc] = ptr; argc ++; + + /* skip this string */ + while (*ptr != '"' && position < length) + { + if (*ptr == '\\') + { + if (*(ptr + 1) == '"') + { + ptr ++; position ++; + } + } + ptr ++; position ++; + } + if (position >= length) break; + + /* skip '"' */ + *ptr = '\0'; ptr ++; position ++; + } + else + { + argv[argc] = ptr; + argc ++; + while ((*ptr != ' ' && *ptr != '\t') && position < length) + { + ptr ++; position ++; + } + if (position >= length) break; + } + } + + return argc; +} + +static cmd_function_t msh_get_cmd(char *cmd, int size) +{ + struct finsh_syscall *index; + cmd_function_t cmd_func = RT_NULL; + + for (index = _syscall_table_begin; + index < _syscall_table_end; + FINSH_NEXT_SYSCALL(index)) + { + if (strncmp(index->name, "__cmd_", 6) != 0) continue; + + if (strncmp(&index->name[6], cmd, size) == 0 && + index->name[6 + size] == '\0') + { + cmd_func = (cmd_function_t)index->func; + break; + } + } + + return cmd_func; +} + +#if defined(RT_USING_MODULE) && defined(RT_USING_DFS) +/* Return 0 on module executed. Other value indicate error. + */ +int msh_exec_module(const char *cmd_line, int size) +{ + int ret; + int fd = -1; + char *pg_name; + int length, cmd_length = 0; + + if (size == 0) + return -RT_ERROR; + /* get the length of command0 */ + while ((cmd_line[cmd_length] != ' ' && cmd_line[cmd_length] != '\t') && cmd_length < size) + cmd_length ++; + + /* get name length */ + length = cmd_length + 32; + + /* allocate program name memory */ + pg_name = (char *) rt_malloc(length); + if (pg_name == RT_NULL) + return -RT_ENOMEM; + + /* copy command0 */ + memcpy(pg_name, cmd_line, cmd_length); + pg_name[cmd_length] = '\0'; + + if (strstr(pg_name, ".mo") != RT_NULL || strstr(pg_name, ".MO") != RT_NULL) + { + /* try to open program */ + fd = open(pg_name, O_RDONLY, 0); + + /* search in /bin path */ + if (fd < 0) + { + rt_snprintf(pg_name, length - 1, "/bin/%.*s", cmd_length, cmd_line); + fd = open(pg_name, O_RDONLY, 0); + } + } + else + { + /* add .mo and open program */ + + /* try to open program */ + strcat(pg_name, ".mo"); + fd = open(pg_name, O_RDONLY, 0); + + /* search in /bin path */ + if (fd < 0) + { + rt_snprintf(pg_name, length - 1, "/bin/%.*s.mo", cmd_length, cmd_line); + fd = open(pg_name, O_RDONLY, 0); + } + } + + if (fd >= 0) + { + /* found program */ + close(fd); + dlmodule_exec(pg_name, cmd_line, size); + ret = 0; + } + else + { + ret = -1; + } + + rt_free(pg_name); + return ret; +} + +int system(const char *command) +{ + int ret = -RT_ENOMEM; + char *cmd = rt_strdup(command); + + if (cmd) + { + ret = msh_exec(cmd, rt_strlen(cmd)); + rt_free(cmd); + } + + return ret; +} +RTM_EXPORT(system); +#endif + +static int _msh_exec_cmd(char *cmd, rt_size_t length, int *retp) +{ + int argc; + rt_size_t cmd0_size = 0; + cmd_function_t cmd_func; + char *argv[FINSH_ARG_MAX]; + + RT_ASSERT(cmd); + RT_ASSERT(retp); + + /* find the size of first command */ + while ((cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t') && cmd0_size < length) + cmd0_size ++; + if (cmd0_size == 0) + return -RT_ERROR; + + cmd_func = msh_get_cmd(cmd, cmd0_size); + if (cmd_func == RT_NULL) + return -RT_ERROR; + + /* split arguments */ + memset(argv, 0x00, sizeof(argv)); + argc = msh_split(cmd, length, argv); + if (argc == 0) + return -RT_ERROR; + + /* exec this command */ + *retp = cmd_func(argc, argv); + return 0; +} + +#if defined(RT_USING_LWP) && defined(RT_USING_DFS) +static int _msh_exec_lwp(char *cmd, rt_size_t length) +{ + int argc; + int cmd0_size = 0; + char *argv[FINSH_ARG_MAX]; + int fd = -1; + char *pg_name; + + extern int exec(char*, int, char**); + + /* find the size of first command */ + while ((cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t') && cmd0_size < length) + cmd0_size ++; + if (cmd0_size == 0) + return -1; + + /* split arguments */ + rt_memset(argv, 0x00, sizeof(argv)); + argc = msh_split(cmd, length, argv); + if (argc == 0) + return -1; + + pg_name = argv[0]; + /* try to open program */ + fd = open(pg_name, O_RDONLY, 0); + + if (fd < 0) + return -1; + + /* found program */ + close(fd); + exec(pg_name, argc, argv); + + return 0; +} +#endif + +int msh_exec(char *cmd, rt_size_t length) +{ + int cmd_ret; + + /* strim the beginning of command */ + while ((length > 0) && (*cmd == ' ' || *cmd == '\t')) + { + cmd++; + length--; + } + + if (length == 0) + return 0; + + /* Exec sequence: + * 1. built-in command + * 2. module(if enabled) + */ + if (_msh_exec_cmd(cmd, length, &cmd_ret) == 0) + { + return cmd_ret; + } +#ifdef RT_USING_DFS +#ifdef DFS_USING_WORKDIR + if (msh_exec_script(cmd, length) == 0) + { + return 0; + } +#endif + +#ifdef RT_USING_MODULE + if (msh_exec_module(cmd, length) == 0) + { + return 0; + } +#endif + +#ifdef RT_USING_LWP + if (_msh_exec_lwp(cmd, length) == 0) + { + return 0; + } +#endif +#endif + + /* truncate the cmd at the first space. */ + { + char *tcmd; + tcmd = cmd; + while (*tcmd != ' ' && *tcmd != '\0') + { + tcmd++; + } + *tcmd = '\0'; + } + rt_kprintf("%s: command not found.\n", cmd); + return -1; +} + +static int str_common(const char *str1, const char *str2) +{ + const char *str = str1; + + while ((*str != 0) && (*str2 != 0) && (*str == *str2)) + { + str ++; + str2 ++; + } + + return (str - str1); +} + +#ifdef RT_USING_DFS +void msh_auto_complete_path(char *path) +{ + DIR *dir = RT_NULL; + struct dirent *dirent = RT_NULL; + char *full_path, *ptr, *index; + + if (!path) + return; + + full_path = (char *)rt_malloc(256); + if (full_path == RT_NULL) return; /* out of memory */ + + if (*path != '/') + { + getcwd(full_path, 256); + if (full_path[rt_strlen(full_path) - 1] != '/') + strcat(full_path, "/"); + } + else *full_path = '\0'; + + index = RT_NULL; + ptr = path; + for (;;) + { + if (*ptr == '/') index = ptr + 1; + if (!*ptr) break; + + ptr ++; + } + if (index == RT_NULL) index = path; + + if (index != RT_NULL) + { + char *dest = index; + + /* fill the parent path */ + ptr = full_path; + while (*ptr) ptr ++; + + for (index = path; index != dest;) + *ptr++ = *index++; + *ptr = '\0'; + + dir = opendir(full_path); + if (dir == RT_NULL) /* open directory failed! */ + { + rt_free(full_path); + return; + } + + /* restore the index position */ + index = dest; + } + + /* auto complete the file or directory name */ + if (*index == '\0') /* display all of files and directories */ + { + for (;;) + { + dirent = readdir(dir); + if (dirent == RT_NULL) break; + + rt_kprintf("%s\n", dirent->d_name); + } + } + else + { + rt_size_t length, min_length; + + min_length = 0; + for (;;) + { + dirent = readdir(dir); + if (dirent == RT_NULL) break; + + /* matched the prefix string */ + if (strncmp(index, dirent->d_name, rt_strlen(index)) == 0) + { + if (min_length == 0) + { + min_length = rt_strlen(dirent->d_name); + /* save dirent name */ + strcpy(full_path, dirent->d_name); + } + + length = str_common(dirent->d_name, full_path); + + if (length < min_length) + { + min_length = length; + } + } + } + + if (min_length) + { + if (min_length < rt_strlen(full_path)) + { + /* list the candidate */ + rewinddir(dir); + + for (;;) + { + dirent = readdir(dir); + if (dirent == RT_NULL) break; + + if (strncmp(index, dirent->d_name, rt_strlen(index)) == 0) + rt_kprintf("%s\n", dirent->d_name); + } + } + + length = index - path; + memcpy(index, full_path, min_length); + path[length + min_length] = '\0'; + } + } + + closedir(dir); + rt_free(full_path); +} +#endif + +void msh_auto_complete(char *prefix) +{ + int length, min_length; + const char *name_ptr, *cmd_name; + struct finsh_syscall *index; + + min_length = 0; + name_ptr = RT_NULL; + + if (*prefix == '\0') + { + msh_help(0, RT_NULL); + return; + } + +#ifdef RT_USING_DFS + /* check whether a spare in the command */ + { + char *ptr; + + ptr = prefix + rt_strlen(prefix); + while (ptr != prefix) + { + if (*ptr == ' ') + { + msh_auto_complete_path(ptr + 1); + break; + } + + ptr --; + } +#ifdef RT_USING_MODULE + /* There is a chance that the user want to run the module directly. So + * try to complete the file names. If the completed path is not a + * module, the system won't crash anyway. */ + if (ptr == prefix) + { + msh_auto_complete_path(ptr); + } +#endif + } +#endif + + /* checks in internal command */ + { + for (index = _syscall_table_begin; index < _syscall_table_end; FINSH_NEXT_SYSCALL(index)) + { + /* skip finsh shell function */ + if (strncmp(index->name, "__cmd_", 6) != 0) continue; + + cmd_name = (const char *) &index->name[6]; + if (strncmp(prefix, cmd_name, strlen(prefix)) == 0) + { + if (min_length == 0) + { + /* set name_ptr */ + name_ptr = cmd_name; + /* set initial length */ + min_length = strlen(name_ptr); + } + + length = str_common(name_ptr, cmd_name); + if (length < min_length) + min_length = length; + + rt_kprintf("%s\n", cmd_name); + } + } + } + + /* auto complete string */ + if (name_ptr != NULL) + { + rt_strncpy(prefix, name_ptr, min_length); + } + + return ; +} +#endif + +#endif /* FINSH_USING_MSH */ diff --git a/components/rt-thread/components/finsh/msh.h b/components/rt-thread/components/finsh/msh.h new file mode 100644 index 00000000..17c7a39e --- /dev/null +++ b/components/rt-thread/components/finsh/msh.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-03-30 Bernard the first verion for FinSH + */ + +#ifndef __M_SHELL__ +#define __M_SHELL__ + +#include + +rt_bool_t msh_is_used(void); +int msh_exec(char *cmd, rt_size_t length); +void msh_auto_complete(char *prefix); + +int msh_exec_module(const char *cmd_line, int size); +int msh_exec_script(const char *cmd_line, int size); + +#endif diff --git a/components/rt-thread/components/finsh/msh_file.c b/components/rt-thread/components/finsh/msh_file.c new file mode 100644 index 00000000..3f21cc6f --- /dev/null +++ b/components/rt-thread/components/finsh/msh_file.c @@ -0,0 +1,706 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2015-09-25 Bernard the first verion for FinSH + * 2021-06-09 Meco Man implement tail command + */ + +#include + +#if defined(FINSH_USING_MSH) && defined(RT_USING_DFS) + +#include +#include "msh.h" +#include + +static int msh_readline(int fd, char *line_buf, int size) +{ + char ch; + int index = 0; + + do + { + if (read(fd, &ch, 1) != 1) + { + /* nothing in this file */ + return 0; + } + } + while (ch == '\n' || ch == '\r'); + + /* set the first character */ + line_buf[index ++] = ch; + + while (index < size) + { + if (read(fd, &ch, 1) == 1) + { + if (ch == '\n' || ch == '\r') + { + line_buf[index] = '\0'; + break; + } + + line_buf[index++] = ch; + } + else + { + line_buf[index] = '\0'; + break; + } + } + + return index; +} + +int msh_exec_script(const char *cmd_line, int size) +{ + int ret; + int fd = -1; + char *pg_name; + int length, cmd_length = 0; + + if (size == 0) return -RT_ERROR; + + /* get the length of command0 */ + while ((cmd_line[cmd_length] != ' ' && cmd_line[cmd_length] != '\t') && cmd_length < size) + cmd_length ++; + + /* get name length */ + length = cmd_length + 32; + + /* allocate program name memory */ + pg_name = (char *) rt_malloc(length); + if (pg_name == RT_NULL) return -RT_ENOMEM; + + /* copy command0 */ + memcpy(pg_name, cmd_line, cmd_length); + pg_name[cmd_length] = '\0'; + + if (strstr(pg_name, ".sh") != RT_NULL || strstr(pg_name, ".SH") != RT_NULL) + { + /* try to open program */ + fd = open(pg_name, O_RDONLY, 0); + + /* search in /bin path */ + if (fd < 0) + { + rt_snprintf(pg_name, length - 1, "/bin/%.*s", cmd_length, cmd_line); + fd = open(pg_name, O_RDONLY, 0); + } + } + + rt_free(pg_name); + if (fd >= 0) + { + /* found script */ + char *line_buf; + int length; + + line_buf = (char *) rt_malloc(RT_CONSOLEBUF_SIZE); + if (line_buf == RT_NULL) + { + close(fd); + return -RT_ENOMEM; + } + + /* read line by line and then exec it */ + do + { + length = msh_readline(fd, line_buf, RT_CONSOLEBUF_SIZE); + if (length > 0) + { + char ch = '\0'; + int index; + + for (index = 0; index < length; index ++) + { + ch = line_buf[index]; + if (ch == ' ' || ch == '\t') continue; + else break; + } + + if (ch != '#') /* not a comment */ + msh_exec(line_buf, length); + } + } + while (length > 0); + + close(fd); + rt_free(line_buf); + + ret = 0; + } + else + { + ret = -1; + } + + return ret; +} + +#ifdef DFS_USING_WORKDIR +extern char working_directory[]; +#endif + +static int cmd_ls(int argc, char **argv) +{ + extern void ls(const char *pathname); + + if (argc == 1) + { +#ifdef DFS_USING_WORKDIR + ls(working_directory); +#else + ls("/"); +#endif + } + else + { + ls(argv[1]); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_ls, ls, List information about the FILEs.); + +static int cmd_cp(int argc, char **argv) +{ + void copy(const char *src, const char *dst); + + if (argc != 3) + { + rt_kprintf("Usage: cp SOURCE DEST\n"); + rt_kprintf("Copy SOURCE to DEST.\n"); + } + else + { + copy(argv[1], argv[2]); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_cp, cp, Copy SOURCE to DEST.); + +static int cmd_mv(int argc, char **argv) +{ + if (argc != 3) + { + rt_kprintf("Usage: mv SOURCE DEST\n"); + rt_kprintf("Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n"); + } + else + { + int fd; + char *dest = RT_NULL; + + rt_kprintf("%s => %s\n", argv[1], argv[2]); + + fd = open(argv[2], O_DIRECTORY, 0); + if (fd >= 0) + { + char *src; + + close(fd); + + /* it's a directory */ + dest = (char *)rt_malloc(DFS_PATH_MAX); + if (dest == RT_NULL) + { + rt_kprintf("out of memory\n"); + return -RT_ENOMEM; + } + + src = argv[1] + rt_strlen(argv[1]); + while (src != argv[1]) + { + if (*src == '/') break; + src --; + } + + rt_snprintf(dest, DFS_PATH_MAX - 1, "%s/%s", argv[2], src); + } + else + { + fd = open(argv[2], O_RDONLY, 0); + if (fd >= 0) + { + close(fd); + + unlink(argv[2]); + } + + dest = argv[2]; + } + + rename(argv[1], dest); + if (dest != RT_NULL && dest != argv[2]) rt_free(dest); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_mv, mv, Rename SOURCE to DEST.); + +static int cmd_cat(int argc, char **argv) +{ + int index; + extern void cat(const char *filename); + + if (argc == 1) + { + rt_kprintf("Usage: cat [FILE]...\n"); + rt_kprintf("Concatenate FILE(s)\n"); + return 0; + } + + for (index = 1; index < argc; index ++) + { + cat(argv[index]); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_cat, cat, Concatenate FILE(s)); + +static void directory_delete_for_msh(const char *pathname, char f, char v) +{ + DIR *dir = NULL; + struct dirent *dirent = NULL; + char *full_path; + + if (pathname == RT_NULL) + return; + + full_path = (char *)rt_malloc(DFS_PATH_MAX); + if (full_path == RT_NULL) + return; + + dir = opendir(pathname); + if (dir == RT_NULL) + { + if (f == 0) + { + rt_kprintf("cannot remove '%s'\n", pathname); + } + rt_free(full_path); + return; + } + + while (1) + { + dirent = readdir(dir); + if (dirent == RT_NULL) + break; + if (rt_strcmp(".", dirent->d_name) != 0 && + rt_strcmp("..", dirent->d_name) != 0) + { + rt_sprintf(full_path, "%s/%s", pathname, dirent->d_name); + if (dirent->d_type == DT_REG) + { + if (unlink(full_path) != 0) + { + if (f == 0) + rt_kprintf("cannot remove '%s'\n", full_path); + } + else if (v) + { + rt_kprintf("removed '%s'\n", full_path); + } + } + else if (dirent->d_type == DT_DIR) + { + directory_delete_for_msh(full_path, f, v); + } + } + } + closedir(dir); + rt_free(full_path); + if (unlink(pathname) != 0) + { + if (f == 0) + rt_kprintf("cannot remove '%s'\n", pathname); + } + else if (v) + { + rt_kprintf("removed directory '%s'\n", pathname); + } +} + +static int cmd_rm(int argc, char **argv) +{ + int index, n; + char f = 0, r = 0, v = 0; + + if (argc == 1) + { + rt_kprintf("Usage: rm option(s) FILE...\n"); + rt_kprintf("Remove (unlink) the FILE(s).\n"); + return 0; + } + + if (argv[1][0] == '-') + { + for (n = 0; argv[1][n]; n++) + { + switch (argv[1][n]) + { + case 'f': + f = 1; + break; + case 'r': + r = 1; + break; + case 'v': + v = 1; + break; + case '-': + break; + default: + rt_kprintf("Error: Bad option: %c\n", argv[1][n]); + return 0; + } + } + argc -= 1; + argv = argv + 1; + } + + for (index = 1; index < argc; index ++) + { + struct stat s; + if (stat(argv[index], &s) == 0) + { + if (s.st_mode & S_IFDIR) + { + if (r == 0) + rt_kprintf("cannot remove '%s': Is a directory\n", argv[index]); + else + directory_delete_for_msh(argv[index], f, v); + } + else if (s.st_mode & S_IFREG) + { + if (unlink(argv[index]) != 0) + { + if (f == 0) + rt_kprintf("cannot remove '%s'\n", argv[index]); + } + else if (v) + { + rt_kprintf("removed '%s'\n", argv[index]); + } + } + } + else if (f == 0) + { + rt_kprintf("cannot remove '%s': No such file or directory\n", argv[index]); + } + } + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_rm, rm, Remove(unlink) the FILE(s).); + +#ifdef DFS_USING_WORKDIR +static int cmd_cd(int argc, char **argv) +{ + if (argc == 1) + { + rt_kprintf("%s\n", working_directory); + } + else if (argc == 2) + { + if (chdir(argv[1]) != 0) + { + rt_kprintf("No such directory: %s\n", argv[1]); + } + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_cd, cd, Change the shell working directory.); + +static int cmd_pwd(int argc, char **argv) +{ + rt_kprintf("%s\n", working_directory); + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_pwd, pwd, Print the name of the current working directory.); +#endif + +static int cmd_mkdir(int argc, char **argv) +{ + if (argc == 1) + { + rt_kprintf("Usage: mkdir [OPTION] DIRECTORY\n"); + rt_kprintf("Create the DIRECTORY, if they do not already exist.\n"); + } + else + { + mkdir(argv[1], 0); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_mkdir, mkdir, Create the DIRECTORY.); + +static int cmd_mkfs(int argc, char **argv) +{ + int result = 0; + char *type = "elm"; /* use the default file system type as 'fatfs' */ + + if (argc == 2) + { + result = dfs_mkfs(type, argv[1]); + } + else if (argc == 4) + { + if (strcmp(argv[1], "-t") == 0) + { + type = argv[2]; + result = dfs_mkfs(type, argv[3]); + } + } + else + { + rt_kprintf("Usage: mkfs [-t type] device\n"); + return 0; + } + + if (result != RT_EOK) + { + rt_kprintf("mkfs failed, result=%d\n", result); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_mkfs, mkfs, format disk with file system); + +extern struct dfs_filesystem filesystem_table[]; +static int cmd_mount(int argc, char **argv) +{ + if (argc == 1) + { + struct dfs_filesystem *iter; + + /* display the mount history */ + rt_kprintf("filesystem device mountpoint\n"); + rt_kprintf("---------- ------ ----------\n"); + for (iter = &filesystem_table[0]; + iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++) + { + if ((iter != NULL) && (iter->path != NULL)) + { + rt_kprintf("%-10s %-6s %-s\n", + iter->ops->name, iter->dev_id->parent.name, iter->path); + } + } + return 0; + } + else if (argc == 4) + { + char *device = argv[1]; + char *path = argv[2]; + char *fstype = argv[3]; + + /* mount a filesystem to the specified directory */ + rt_kprintf("mount device %s(%s) onto %s ... ", device, fstype, path); + if (dfs_mount(device, path, fstype, 0, 0) == 0) + { + rt_kprintf("succeed!\n"); + return 0; + } + else + { + rt_kprintf("failed!\n"); + return -1; + } + } + else + { + rt_kprintf("Usage: mount .\n"); + return -1; + } +} +MSH_CMD_EXPORT_ALIAS(cmd_mount, mount, mount ); + +/* unmount the filesystem from the specified mountpoint */ +static int cmd_umount(int argc, char **argv) +{ + char *path = argv[1]; + + if (argc != 2) + { + rt_kprintf("Usage: unmount .\n"); + return -1; + } + + rt_kprintf("unmount %s ... ", path); + if (dfs_unmount(path) < 0) + { + rt_kprintf("failed!\n"); + return -1; + } + else + { + rt_kprintf("succeed!\n"); + return 0; + } +} +MSH_CMD_EXPORT_ALIAS(cmd_umount, umount, Unmount device from file system); + +extern int df(const char *path); +static int cmd_df(int argc, char **argv) +{ + if (argc != 2) + { + df("/"); + } + else + { + if ((strcmp(argv[1], "--help") == 0) || (strcmp(argv[1], "-h") == 0)) + { + rt_kprintf("df [path]\n"); + } + else + { + df(argv[1]); + } + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_df, df, disk free); + +static int cmd_echo(int argc, char **argv) +{ + if (argc == 2) + { + rt_kprintf("%s\n", argv[1]); + } + else if (argc == 3) + { + int fd; + + fd = open(argv[2], O_RDWR | O_APPEND | O_CREAT, 0); + if (fd >= 0) + { + write(fd, argv[1], strlen(argv[1])); + close(fd); + } + else + { + rt_kprintf("open file:%s failed!\n", argv[2]); + } + } + else + { + rt_kprintf("Usage: echo \"string\" [filename]\n"); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_echo, echo, echo string to file); + +static int cmd_tail(int argc, char **argv) +{ + int fd; + char c = RT_NULL; + char *file_name = RT_NULL; + rt_uint32_t total_lines = 0; + rt_uint32_t target_line = 0; + rt_uint32_t current_line = 0; + rt_uint32_t required_lines = 0; + rt_uint32_t after_xxx_line = 0; + + if(argc < 2) + { + rt_kprintf("Usage: tail [-n numbers] \n"); + return -1; + } + else if(argc == 2) + { + required_lines = 10; /* default: 10 lines from tail */ + file_name = argv[1]; + } + else if(rt_strcmp(argv[1], "-n") == 0) + { + if(argv[2][0] != '+') + { + required_lines = atoi(argv[2]); + } + else + { + after_xxx_line = atoi(&argv[2][1]); /* eg: +100, to get the 100 */ + } + file_name = argv[3]; + } + else + { + rt_kprintf("Usage: tail [-n numbers] \n"); + return -1; + } + + fd = open(file_name, O_RDONLY); + if (fd < 0) + { + rt_kprintf("File doesn't exist\n"); + return -1; + } + + while ((read(fd, &c, sizeof(char))) > 0) + { + if (c == '\n') + { + total_lines++; + } + } + + rt_kprintf("\nTotal Number of lines:%d\n", total_lines); + + if(after_xxx_line != 0) + { + if(total_lines > after_xxx_line) + { + required_lines = total_lines - after_xxx_line; + } + else + { + rt_kprintf("\nError:Required lines are more than total number of lines\n"); + close(fd); + return -1; + } + } + + if (required_lines > total_lines) + { + rt_kprintf("\nError:Required lines are more than total number of lines\n"); + close(fd); + return -1; + } + rt_kprintf("Required Number of lines:%d\n", required_lines); + + target_line = total_lines - required_lines; + lseek(fd, 0, SEEK_SET); /* back to head */ + + while ((read(fd, &c, sizeof(char))) > 0) + { + if (c == '\n') + { + current_line++; + } + if (current_line > target_line) + { + rt_kprintf("%c", c); + } + } + rt_kprintf("\n"); + + close(fd); + return 0; +} +MSH_CMD_EXPORT_ALIAS(cmd_tail, tail, print the last N-lines data of the given file); + +#endif /* defined(FINSH_USING_MSH) && defined(RT_USING_DFS) */ + diff --git a/components/rt-thread/components/finsh/shell.c b/components/rt-thread/components/finsh/shell.c new file mode 100644 index 00000000..23643f61 --- /dev/null +++ b/components/rt-thread/components/finsh/shell.c @@ -0,0 +1,897 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-04-30 Bernard the first version for FinSH + * 2006-05-08 Bernard change finsh thread stack to 2048 + * 2006-06-03 Bernard add support for skyeye + * 2006-09-24 Bernard remove the code related with hardware + * 2010-01-18 Bernard fix down then up key bug. + * 2010-03-19 Bernard fix backspace issue and fix device read in shell. + * 2010-04-01 Bernard add prompt output when start and remove the empty history + * 2011-02-23 Bernard fix variable section end issue of finsh shell + * initialization when use GNU GCC compiler. + * 2016-11-26 armink add password authentication + * 2018-07-02 aozima add custom prompt support. + */ + +#include + +#ifdef RT_USING_FINSH + +#include "finsh.h" +#include "shell.h" + +#ifdef FINSH_USING_MSH +#include "msh.h" +#endif + +#ifdef _WIN32 +#include /* for putchar */ +#endif + +/* finsh thread */ +#ifndef RT_USING_HEAP +static struct rt_thread finsh_thread; +ALIGN(RT_ALIGN_SIZE) +static char finsh_thread_stack[FINSH_THREAD_STACK_SIZE]; +struct finsh_shell _shell; +#endif + +/* finsh symtab */ +#ifdef FINSH_USING_SYMTAB +struct finsh_syscall *_syscall_table_begin = NULL; +struct finsh_syscall *_syscall_table_end = NULL; +struct finsh_sysvar *_sysvar_table_begin = NULL; +struct finsh_sysvar *_sysvar_table_end = NULL; +#endif + +struct finsh_shell *shell; +static char *finsh_prompt_custom = RT_NULL; + +#if defined(_MSC_VER) || (defined(__GNUC__) && defined(__x86_64__)) +struct finsh_syscall* finsh_syscall_next(struct finsh_syscall* call) +{ + unsigned int *ptr; + ptr = (unsigned int*) (call + 1); + while ((*ptr == 0) && ((unsigned int*)ptr < (unsigned int*) _syscall_table_end)) + ptr ++; + + return (struct finsh_syscall*)ptr; +} + +struct finsh_sysvar* finsh_sysvar_next(struct finsh_sysvar* call) +{ + unsigned int *ptr; + ptr = (unsigned int*) (call + 1); + while ((*ptr == 0) && ((unsigned int*)ptr < (unsigned int*) _sysvar_table_end)) + ptr ++; + + return (struct finsh_sysvar*)ptr; +} +#endif /* defined(_MSC_VER) || (defined(__GNUC__) && defined(__x86_64__)) */ + +#ifdef RT_USING_HEAP +int finsh_set_prompt(const char * prompt) +{ + if(finsh_prompt_custom) + { + rt_free(finsh_prompt_custom); + finsh_prompt_custom = RT_NULL; + } + + /* strdup */ + if(prompt) + { + finsh_prompt_custom = (char *)rt_malloc(strlen(prompt)+1); + if(finsh_prompt_custom) + { + strcpy(finsh_prompt_custom, prompt); + } + } + + return 0; +} +#endif /* RT_USING_HEAP */ + +#ifdef RT_USING_DFS +#include +#endif /* RT_USING_DFS */ + +const char *finsh_get_prompt(void) +{ +#define _MSH_PROMPT "msh " +#define _PROMPT "finsh " + static char finsh_prompt[RT_CONSOLEBUF_SIZE + 1] = {0}; + + /* check prompt mode */ + if (!shell->prompt_mode) + { + finsh_prompt[0] = '\0'; + return finsh_prompt; + } + + if(finsh_prompt_custom) + { + strncpy(finsh_prompt, finsh_prompt_custom, sizeof(finsh_prompt)-1); + return finsh_prompt; + } + +#ifdef FINSH_USING_MSH + if (msh_is_used()) strcpy(finsh_prompt, _MSH_PROMPT); + else +#endif + strcpy(finsh_prompt, _PROMPT); + +#if defined(RT_USING_DFS) && defined(DFS_USING_WORKDIR) + /* get current working directory */ + getcwd(&finsh_prompt[rt_strlen(finsh_prompt)], RT_CONSOLEBUF_SIZE - rt_strlen(finsh_prompt)); +#endif + + strcat(finsh_prompt, ">"); + + return finsh_prompt; +} + +/** + * @ingroup finsh + * + * This function get the prompt mode of finsh shell. + * + * @return prompt the prompt mode, 0 disable prompt mode, other values enable prompt mode. + */ +rt_uint32_t finsh_get_prompt_mode(void) +{ + RT_ASSERT(shell != RT_NULL); + return shell->prompt_mode; +} + +/** + * @ingroup finsh + * + * This function set the prompt mode of finsh shell. + * + * The parameter 0 disable prompt mode, other values enable prompt mode. + * + * @param prompt the prompt mode + */ +void finsh_set_prompt_mode(rt_uint32_t prompt_mode) +{ + RT_ASSERT(shell != RT_NULL); + shell->prompt_mode = prompt_mode; +} + +static int finsh_getchar(void) +{ +#ifdef RT_USING_DEVICE +#ifdef RT_USING_POSIX + return getchar(); +#else + char ch = 0; + + RT_ASSERT(shell != RT_NULL); + while (rt_device_read(shell->device, -1, &ch, 1) != 1) + rt_sem_take(&shell->rx_sem, RT_WAITING_FOREVER); + + return (int)ch; +#endif +#else + extern char rt_hw_console_getchar(void); + return rt_hw_console_getchar(); +#endif +} + +#if !defined(RT_USING_POSIX) && defined(RT_USING_DEVICE) +static rt_err_t finsh_rx_ind(rt_device_t dev, rt_size_t size) +{ + RT_ASSERT(shell != RT_NULL); + + /* release semaphore to let finsh thread rx data */ + rt_sem_release(&shell->rx_sem); + + return RT_EOK; +} + +/** + * @ingroup finsh + * + * This function sets the input device of finsh shell. + * + * @param device_name the name of new input device. + */ +void finsh_set_device(const char *device_name) +{ + rt_device_t dev = RT_NULL; + + RT_ASSERT(shell != RT_NULL); + dev = rt_device_find(device_name); + if (dev == RT_NULL) + { + rt_kprintf("finsh: can not find device: %s\n", device_name); + return; + } + + /* check whether it's a same device */ + if (dev == shell->device) return; + /* open this device and set the new device in finsh shell */ + if (rt_device_open(dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | \ + RT_DEVICE_FLAG_STREAM) == RT_EOK) + { + if (shell->device != RT_NULL) + { + /* close old finsh device */ + rt_device_close(shell->device); + rt_device_set_rx_indicate(shell->device, RT_NULL); + } + + /* clear line buffer before switch to new device */ + memset(shell->line, 0, sizeof(shell->line)); + shell->line_curpos = shell->line_position = 0; + + shell->device = dev; + rt_device_set_rx_indicate(dev, finsh_rx_ind); + } +} + +/** + * @ingroup finsh + * + * This function returns current finsh shell input device. + * + * @return the finsh shell input device name is returned. + */ +const char *finsh_get_device() +{ + RT_ASSERT(shell != RT_NULL); + return shell->device->parent.name; +} +#endif + +/** + * @ingroup finsh + * + * This function set the echo mode of finsh shell. + * + * FINSH_OPTION_ECHO=0x01 is echo mode, other values are none-echo mode. + * + * @param echo the echo mode + */ +void finsh_set_echo(rt_uint32_t echo) +{ + RT_ASSERT(shell != RT_NULL); + shell->echo_mode = (rt_uint8_t)echo; +} + +/** + * @ingroup finsh + * + * This function gets the echo mode of finsh shell. + * + * @return the echo mode + */ +rt_uint32_t finsh_get_echo() +{ + RT_ASSERT(shell != RT_NULL); + + return shell->echo_mode; +} + +#ifdef FINSH_USING_AUTH +/** + * set a new password for finsh + * + * @param password new password + * + * @return result, RT_EOK on OK, -RT_ERROR on the new password length is less than + * FINSH_PASSWORD_MIN or greater than FINSH_PASSWORD_MAX + */ +rt_err_t finsh_set_password(const char *password) { + rt_ubase_t level; + rt_size_t pw_len = rt_strlen(password); + + if (pw_len < FINSH_PASSWORD_MIN || pw_len > FINSH_PASSWORD_MAX) + return -RT_ERROR; + + level = rt_hw_interrupt_disable(); + rt_strncpy(shell->password, password, FINSH_PASSWORD_MAX); + rt_hw_interrupt_enable(level); + + return RT_EOK; +} + +/** + * get the finsh password + * + * @return password + */ +const char *finsh_get_password(void) +{ + return shell->password; +} + +static void finsh_wait_auth(void) +{ + int ch; + rt_bool_t input_finish = RT_FALSE; + char password[FINSH_PASSWORD_MAX] = { 0 }; + rt_size_t cur_pos = 0; + /* password not set */ + if (rt_strlen(finsh_get_password()) == 0) return; + + while (1) + { + rt_kprintf("Password for login: "); + while (!input_finish) + { + while (1) + { + /* read one character from device */ + ch = finsh_getchar(); + if (ch < 0) + { + continue; + } + + if (ch >= ' ' && ch <= '~' && cur_pos < FINSH_PASSWORD_MAX) + { + /* change the printable characters to '*' */ + rt_kprintf("*"); + password[cur_pos++] = ch; + } + else if (ch == '\b' && cur_pos > 0) + { + /* backspace */ + cur_pos--; + password[cur_pos] = '\0'; + rt_kprintf("\b \b"); + } + else if (ch == '\r' || ch == '\n') + { + rt_kprintf("\n"); + input_finish = RT_TRUE; + break; + } + } + } + if (!rt_strncmp(shell->password, password, FINSH_PASSWORD_MAX)) return; + else + { + /* authentication failed, delay 2S for retry */ + rt_thread_delay(2 * RT_TICK_PER_SECOND); + rt_kprintf("Sorry, try again.\n"); + cur_pos = 0; + input_finish = RT_FALSE; + rt_memset(password, '\0', FINSH_PASSWORD_MAX); + } + } +} +#endif /* FINSH_USING_AUTH */ + +static void shell_auto_complete(char *prefix) +{ + + rt_kprintf("\n"); +#ifdef FINSH_USING_MSH + if (msh_is_used() == RT_TRUE) + { + msh_auto_complete(prefix); + } + else +#endif + { +#ifndef FINSH_USING_MSH_ONLY + extern void list_prefix(char * prefix); + list_prefix(prefix); +#endif + } + + rt_kprintf("%s%s", FINSH_PROMPT, prefix); +} + +#ifndef FINSH_USING_MSH_ONLY +void finsh_run_line(struct finsh_parser *parser, const char *line) +{ + const char *err_str; + + if(shell->echo_mode) + rt_kprintf("\n"); + finsh_parser_run(parser, (unsigned char *)line); + + /* compile node root */ + if (finsh_errno() == 0) + { + finsh_compiler_run(parser->root); + } + else + { + err_str = finsh_error_string(finsh_errno()); + rt_kprintf("%s\n", err_str); + } + + /* run virtual machine */ + if (finsh_errno() == 0) + { + char ch; + finsh_vm_run(); + + ch = (unsigned char)finsh_stack_bottom(); + if (ch > 0x20 && ch < 0x7e) + { + rt_kprintf("\t'%c', %d, 0x%08x\n", + (unsigned char)finsh_stack_bottom(), + (unsigned int)finsh_stack_bottom(), + (unsigned int)finsh_stack_bottom()); + } + else + { + rt_kprintf("\t%d, 0x%08x\n", + (unsigned int)finsh_stack_bottom(), + (unsigned int)finsh_stack_bottom()); + } + } + + finsh_flush(parser); +} +#endif + +#ifdef FINSH_USING_HISTORY +static rt_bool_t shell_handle_history(struct finsh_shell *shell) +{ +#if defined(_WIN32) + int i; + rt_kprintf("\r"); + + for (i = 0; i <= 60; i++) + putchar(' '); + rt_kprintf("\r"); + +#else + rt_kprintf("\033[2K\r"); +#endif + rt_kprintf("%s%s", FINSH_PROMPT, shell->line); + return RT_FALSE; +} + +static void shell_push_history(struct finsh_shell *shell) +{ + if (shell->line_position != 0) + { + /* push history */ + if (shell->history_count >= FINSH_HISTORY_LINES) + { + /* if current cmd is same as last cmd, don't push */ + if (memcmp(&shell->cmd_history[FINSH_HISTORY_LINES - 1], shell->line, FINSH_CMD_SIZE)) + { + /* move history */ + int index; + for (index = 0; index < FINSH_HISTORY_LINES - 1; index ++) + { + memcpy(&shell->cmd_history[index][0], + &shell->cmd_history[index + 1][0], FINSH_CMD_SIZE); + } + memset(&shell->cmd_history[index][0], 0, FINSH_CMD_SIZE); + memcpy(&shell->cmd_history[index][0], shell->line, shell->line_position); + + /* it's the maximum history */ + shell->history_count = FINSH_HISTORY_LINES; + } + } + else + { + /* if current cmd is same as last cmd, don't push */ + if (shell->history_count == 0 || memcmp(&shell->cmd_history[shell->history_count - 1], shell->line, FINSH_CMD_SIZE)) + { + shell->current_history = shell->history_count; + memset(&shell->cmd_history[shell->history_count][0], 0, FINSH_CMD_SIZE); + memcpy(&shell->cmd_history[shell->history_count][0], shell->line, shell->line_position); + + /* increase count and set current history position */ + shell->history_count ++; + } + } + } + shell->current_history = shell->history_count; +} +#endif + +void finsh_thread_entry(void *parameter) +{ + int ch; + + /* normal is echo mode */ +#ifndef FINSH_ECHO_DISABLE_DEFAULT + shell->echo_mode = 1; +#else + shell->echo_mode = 0; +#endif + +#ifndef FINSH_USING_MSH_ONLY + finsh_init(&shell->parser); +#endif + +#if !defined(RT_USING_POSIX) && defined(RT_USING_DEVICE) + /* set console device as shell device */ + if (shell->device == RT_NULL) + { + rt_device_t console = rt_console_get_device(); + if (console) + { + finsh_set_device(console->parent.name); + } + } +#endif + +#ifdef FINSH_USING_AUTH + /* set the default password when the password isn't setting */ + if (rt_strlen(finsh_get_password()) == 0) + { + if (finsh_set_password(FINSH_DEFAULT_PASSWORD) != RT_EOK) + { + rt_kprintf("Finsh password set failed.\n"); + } + } + /* waiting authenticate success */ + finsh_wait_auth(); +#endif + + rt_kprintf(FINSH_PROMPT); + + while (1) + { + ch = finsh_getchar(); + if (ch < 0) + { + continue; + } + + /* + * handle control key + * up key : 0x1b 0x5b 0x41 + * down key: 0x1b 0x5b 0x42 + * right key:0x1b 0x5b 0x43 + * left key: 0x1b 0x5b 0x44 + */ + if (ch == 0x1b) + { + shell->stat = WAIT_SPEC_KEY; + continue; + } + else if (shell->stat == WAIT_SPEC_KEY) + { + if (ch == 0x5b) + { + shell->stat = WAIT_FUNC_KEY; + continue; + } + + shell->stat = WAIT_NORMAL; + } + else if (shell->stat == WAIT_FUNC_KEY) + { + shell->stat = WAIT_NORMAL; + + if (ch == 0x41) /* up key */ + { +#ifdef FINSH_USING_HISTORY + /* prev history */ + if (shell->current_history > 0) + shell->current_history --; + else + { + shell->current_history = 0; + continue; + } + + /* copy the history command */ + memcpy(shell->line, &shell->cmd_history[shell->current_history][0], + FINSH_CMD_SIZE); + shell->line_curpos = shell->line_position = strlen(shell->line); + shell_handle_history(shell); +#endif + continue; + } + else if (ch == 0x42) /* down key */ + { +#ifdef FINSH_USING_HISTORY + /* next history */ + if (shell->current_history < shell->history_count - 1) + shell->current_history ++; + else + { + /* set to the end of history */ + if (shell->history_count != 0) + shell->current_history = shell->history_count - 1; + else + continue; + } + + memcpy(shell->line, &shell->cmd_history[shell->current_history][0], + FINSH_CMD_SIZE); + shell->line_curpos = shell->line_position = strlen(shell->line); + shell_handle_history(shell); +#endif + continue; + } + else if (ch == 0x44) /* left key */ + { + if (shell->line_curpos) + { + rt_kprintf("\b"); + shell->line_curpos --; + } + + continue; + } + else if (ch == 0x43) /* right key */ + { + if (shell->line_curpos < shell->line_position) + { + rt_kprintf("%c", shell->line[shell->line_curpos]); + shell->line_curpos ++; + } + + continue; + } + } + + /* received null or error */ + if (ch == '\0' || ch == 0xFF) continue; + /* handle tab key */ + else if (ch == '\t') + { + int i; + /* move the cursor to the beginning of line */ + for (i = 0; i < shell->line_curpos; i++) + rt_kprintf("\b"); + + /* auto complete */ + shell_auto_complete(&shell->line[0]); + /* re-calculate position */ + shell->line_curpos = shell->line_position = strlen(shell->line); + + continue; + } + /* handle backspace key */ + else if (ch == 0x7f || ch == 0x08) + { + /* note that shell->line_curpos >= 0 */ + if (shell->line_curpos == 0) + continue; + + shell->line_position--; + shell->line_curpos--; + + if (shell->line_position > shell->line_curpos) + { + int i; + + rt_memmove(&shell->line[shell->line_curpos], + &shell->line[shell->line_curpos + 1], + shell->line_position - shell->line_curpos); + shell->line[shell->line_position] = 0; + + rt_kprintf("\b%s \b", &shell->line[shell->line_curpos]); + + /* move the cursor to the origin position */ + for (i = shell->line_curpos; i <= shell->line_position; i++) + rt_kprintf("\b"); + } + else + { + rt_kprintf("\b \b"); + shell->line[shell->line_position] = 0; + } + + continue; + } + + /* handle end of line, break */ + if (ch == '\r' || ch == '\n') + { +#ifdef FINSH_USING_HISTORY + shell_push_history(shell); +#endif + +#ifdef FINSH_USING_MSH + if (msh_is_used() == RT_TRUE) + { + if (shell->echo_mode) + rt_kprintf("\n"); + msh_exec(shell->line, shell->line_position); + } + else +#endif + { +#ifndef FINSH_USING_MSH_ONLY + /* add ';' and run the command line */ + shell->line[shell->line_position] = ';'; + + if (shell->line_position != 0) finsh_run_line(&shell->parser, shell->line); + else + if (shell->echo_mode) rt_kprintf("\n"); +#endif + } + + rt_kprintf(FINSH_PROMPT); + memset(shell->line, 0, sizeof(shell->line)); + shell->line_curpos = shell->line_position = 0; + continue; + } + + /* it's a large line, discard it */ + if (shell->line_position >= FINSH_CMD_SIZE) + shell->line_position = 0; + + /* normal character */ + if (shell->line_curpos < shell->line_position) + { + int i; + + rt_memmove(&shell->line[shell->line_curpos + 1], + &shell->line[shell->line_curpos], + shell->line_position - shell->line_curpos); + shell->line[shell->line_curpos] = ch; + if (shell->echo_mode) + rt_kprintf("%s", &shell->line[shell->line_curpos]); + + /* move the cursor to new position */ + for (i = shell->line_curpos; i < shell->line_position; i++) + rt_kprintf("\b"); + } + else + { + shell->line[shell->line_position] = ch; + if (shell->echo_mode) + rt_kprintf("%c", ch); + } + + ch = 0; + shell->line_position ++; + shell->line_curpos++; + if (shell->line_position >= FINSH_CMD_SIZE) + { + /* clear command line */ + shell->line_position = 0; + shell->line_curpos = 0; + } + } /* end of device read */ +} + +void finsh_system_function_init(const void *begin, const void *end) +{ + _syscall_table_begin = (struct finsh_syscall *) begin; + _syscall_table_end = (struct finsh_syscall *) end; +} + +void finsh_system_var_init(const void *begin, const void *end) +{ + _sysvar_table_begin = (struct finsh_sysvar *) begin; + _sysvar_table_end = (struct finsh_sysvar *) end; +} + +#if defined(__ICCARM__) || defined(__ICCRX__) /* for IAR compiler */ +#ifdef FINSH_USING_SYMTAB +#pragma section="FSymTab" +#pragma section="VSymTab" +#endif +#elif defined(__ADSPBLACKFIN__) /* for VisaulDSP++ Compiler*/ +#ifdef FINSH_USING_SYMTAB +extern "asm" int __fsymtab_start; +extern "asm" int __fsymtab_end; +extern "asm" int __vsymtab_start; +extern "asm" int __vsymtab_end; +#endif +#elif defined(_MSC_VER) +#pragma section("FSymTab$a", read) +const char __fsym_begin_name[] = "__start"; +const char __fsym_begin_desc[] = "begin of finsh"; +__declspec(allocate("FSymTab$a")) const struct finsh_syscall __fsym_begin = +{ + __fsym_begin_name, + __fsym_begin_desc, + NULL +}; + +#pragma section("FSymTab$z", read) +const char __fsym_end_name[] = "__end"; +const char __fsym_end_desc[] = "end of finsh"; +__declspec(allocate("FSymTab$z")) const struct finsh_syscall __fsym_end = +{ + __fsym_end_name, + __fsym_end_desc, + NULL +}; +#endif + +/* + * @ingroup finsh + * + * This function will initialize finsh shell + */ +int finsh_system_init(void) +{ + rt_err_t result = RT_EOK; + rt_thread_t tid; + +#ifdef FINSH_USING_SYMTAB +#ifdef __ARMCC_VERSION /* ARM C Compiler */ + extern const int FSymTab$$Base; + extern const int FSymTab$$Limit; + extern const int VSymTab$$Base; + extern const int VSymTab$$Limit; + finsh_system_function_init(&FSymTab$$Base, &FSymTab$$Limit); +#ifndef FINSH_USING_MSH_ONLY + finsh_system_var_init(&VSymTab$$Base, &VSymTab$$Limit); +#endif +#elif defined (__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */ + finsh_system_function_init(__section_begin("FSymTab"), + __section_end("FSymTab")); + finsh_system_var_init(__section_begin("VSymTab"), + __section_end("VSymTab")); +#elif defined (__GNUC__) || defined(__TI_COMPILER_VERSION__) || defined(__TASKING__) + /* GNU GCC Compiler and TI CCS */ + extern const int __fsymtab_start; + extern const int __fsymtab_end; + extern const int __vsymtab_start; + extern const int __vsymtab_end; + finsh_system_function_init(&__fsymtab_start, &__fsymtab_end); + finsh_system_var_init(&__vsymtab_start, &__vsymtab_end); +#elif defined(__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */ + finsh_system_function_init(&__fsymtab_start, &__fsymtab_end); + finsh_system_var_init(&__vsymtab_start, &__vsymtab_end); +#elif defined(_MSC_VER) + unsigned int *ptr_begin, *ptr_end; + + if(shell) + { + rt_kprintf("finsh shell already init.\n"); + return RT_EOK; + } + + ptr_begin = (unsigned int *)&__fsym_begin; + ptr_begin += (sizeof(struct finsh_syscall) / sizeof(unsigned int)); + while (*ptr_begin == 0) ptr_begin ++; + + ptr_end = (unsigned int *) &__fsym_end; + ptr_end --; + while (*ptr_end == 0) ptr_end --; + + finsh_system_function_init(ptr_begin, ptr_end); +#endif +#endif + +#ifdef RT_USING_HEAP + /* create or set shell structure */ + shell = (struct finsh_shell *)rt_calloc(1, sizeof(struct finsh_shell)); + if (shell == RT_NULL) + { + rt_kprintf("no memory for shell\n"); + return -1; + } + tid = rt_thread_create(FINSH_THREAD_NAME, + finsh_thread_entry, RT_NULL, + FINSH_THREAD_STACK_SIZE, FINSH_THREAD_PRIORITY, 10); +#else + shell = &_shell; + tid = &finsh_thread; + result = rt_thread_init(&finsh_thread, + FINSH_THREAD_NAME, + finsh_thread_entry, RT_NULL, + &finsh_thread_stack[0], sizeof(finsh_thread_stack), + FINSH_THREAD_PRIORITY, 10); +#endif /* RT_USING_HEAP */ + + rt_sem_init(&(shell->rx_sem), "shrx", 0, 0); + finsh_set_prompt_mode(1); + + if (tid != NULL && result == RT_EOK) + rt_thread_startup(tid); + return 0; +} +// INIT_APP_EXPORT(finsh_system_init); + +#endif /* RT_USING_FINSH */ + diff --git a/components/rt-thread/components/finsh/shell.h b/components/rt-thread/components/finsh/shell.h new file mode 100644 index 00000000..6a4dfff1 --- /dev/null +++ b/components/rt-thread/components/finsh/shell.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-06-02 Bernard Add finsh_get_prompt function declaration + */ + +#ifndef __SHELL_H__ +#define __SHELL_H__ + +#include +#include "finsh.h" + +#ifndef FINSH_THREAD_PRIORITY +#define FINSH_THREAD_PRIORITY 20 +#endif +#ifndef FINSH_THREAD_STACK_SIZE +#define FINSH_THREAD_STACK_SIZE 2048 +#endif +#ifndef FINSH_CMD_SIZE +#define FINSH_CMD_SIZE 80 +#endif + +#define FINSH_OPTION_ECHO 0x01 + +#define FINSH_PROMPT finsh_get_prompt() +const char* finsh_get_prompt(void); +int finsh_set_prompt(const char * prompt); + +#ifdef FINSH_USING_HISTORY + #ifndef FINSH_HISTORY_LINES + #define FINSH_HISTORY_LINES 5 + #endif +#endif + +#ifdef FINSH_USING_AUTH + #ifndef FINSH_PASSWORD_MAX + #define FINSH_PASSWORD_MAX RT_NAME_MAX + #endif + #ifndef FINSH_PASSWORD_MIN + #define FINSH_PASSWORD_MIN 6 + #endif + #ifndef FINSH_DEFAULT_PASSWORD + #define FINSH_DEFAULT_PASSWORD "rtthread" + #endif +#endif /* FINSH_USING_AUTH */ + +#ifndef FINSH_THREAD_NAME +#define FINSH_THREAD_NAME "tshell" +#endif + +enum input_stat +{ + WAIT_NORMAL, + WAIT_SPEC_KEY, + WAIT_FUNC_KEY, +}; +struct finsh_shell +{ + struct rt_semaphore rx_sem; + + enum input_stat stat; + + rt_uint8_t echo_mode:1; + rt_uint8_t prompt_mode: 1; + +#ifdef FINSH_USING_HISTORY + rt_uint16_t current_history; + rt_uint16_t history_count; + + char cmd_history[FINSH_HISTORY_LINES][FINSH_CMD_SIZE]; +#endif + +#ifndef FINSH_USING_MSH_ONLY + struct finsh_parser parser; +#endif + + char line[FINSH_CMD_SIZE + 1]; + rt_uint16_t line_position; + rt_uint16_t line_curpos; + +#if !defined(RT_USING_POSIX) && defined(RT_USING_DEVICE) + rt_device_t device; +#endif + +#ifdef FINSH_USING_AUTH + char password[FINSH_PASSWORD_MAX]; +#endif +}; + +void finsh_set_echo(rt_uint32_t echo); +rt_uint32_t finsh_get_echo(void); + +int finsh_system_init(void); +void finsh_set_device(const char* device_name); +const char* finsh_get_device(void); + +rt_uint32_t finsh_get_prompt_mode(void); +void finsh_set_prompt_mode(rt_uint32_t prompt_mode); + +#ifdef FINSH_USING_AUTH +rt_err_t finsh_set_password(const char *password); +const char *finsh_get_password(void); +#endif + +#endif diff --git a/components/rt-thread/components/finsh/symbol.c b/components/rt-thread/components/finsh/symbol.c new file mode 100644 index 00000000..c6623e2d --- /dev/null +++ b/components/rt-thread/components/finsh/symbol.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2010-03-22 Bernard first version + */ + +#include + +#if defined(RT_USING_FINSH) && !defined(FINSH_USING_SYMTAB) + +#include "finsh.h" + +long hello(void); +long version(void); +long list(void); +long list_thread(void); +long list_sem(void); +long list_mutex(void); +long list_fevent(void); +long list_event(void); +long list_mailbox(void); +long list_msgqueue(void); +long list_mempool(void); +long list_timer(void); + +struct finsh_syscall _syscall_table[] = +{ + {"hello", hello}, + {"version", version}, + {"list", list}, + {"list_thread", list_thread}, +#ifdef RT_USING_SEMAPHORE + {"list_sem", list_sem}, +#endif +#ifdef RT_USING_MUTEX + {"list_mutex", list_mutex}, +#endif +#ifdef RT_USING_FEVENT + {"list_fevent", list_fevent}, +#endif +#ifdef RT_USING_EVENT + {"list_event", list_event}, +#endif +#ifdef RT_USING_MAILBOX + {"list_mb", list_mailbox}, +#endif +#ifdef RT_USING_MESSAGEQUEUE + {"list_mq", list_msgqueue}, +#endif +#ifdef RT_USING_MEMPOOL + {"list_memp", list_mempool}, +#endif + {"list_timer", list_timer}, +}; +struct finsh_syscall *_syscall_table_begin = &_syscall_table[0]; +struct finsh_syscall *_syscall_table_end = &_syscall_table[sizeof(_syscall_table) / sizeof(struct finsh_syscall)]; + +struct finsh_sysvar *_sysvar_table_begin = NULL; +struct finsh_sysvar *_sysvar_table_end = NULL; + +#endif /* RT_USING_FINSH && !FINSH_USING_SYMTAB */ + diff --git a/components/rt-thread/include/libc/libc_dirent.h b/components/rt-thread/include/libc/libc_dirent.h new file mode 100644 index 00000000..a86332e6 --- /dev/null +++ b/components/rt-thread/include/libc/libc_dirent.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef LIBC_DIRENT_H__ +#define LIBC_DIRENT_H__ + +#define DT_UNKNOWN 0x00 +#define DT_REG 0x01 +#define DT_DIR 0x02 + +#endif diff --git a/components/rt-thread/include/libc/libc_fcntl.h b/components/rt-thread/include/libc/libc_fcntl.h new file mode 100644 index 00000000..945e674d --- /dev/null +++ b/components/rt-thread/include/libc/libc_fcntl.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-02-07 Bernard Add O_DIRECTORY definition in NEWLIB mode. + * 2018-02-09 Bernard Add O_BINARY definition + */ + +#ifndef LIBC_FCNTL_H__ +#define LIBC_FCNTL_H__ + +#if defined(RT_USING_NEWLIB) || defined(_WIN32) || (defined( __GNUC__ ) && !defined(__ARMCC_VERSION)) +#include + +#ifndef O_NONBLOCK +#define O_NONBLOCK 0x4000 +#endif + +#if defined(_WIN32) +#define O_ACCMODE (_O_RDONLY | _O_WRONLY | _O_RDWR) +#endif + +#ifndef F_GETFL +#define F_GETFL 3 +#endif +#ifndef F_SETFL +#define F_SETFL 4 +#endif + +#ifndef O_DIRECTORY +#define O_DIRECTORY 0x200000 +#endif + +#ifndef O_BINARY +#ifdef _O_BINARY +#define O_BINARY _O_BINARY +#else +#define O_BINARY 0 +#endif +#endif + +#else +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 + +#define O_CREAT 0100 +#define O_EXCL 0200 +#define O_NOCTTY 0400 +#define O_TRUNC 01000 +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_DSYNC 010000 +#define O_SYNC 04010000 +#define O_RSYNC 04010000 +#define O_BINARY 0100000 +#define O_DIRECTORY 0200000 +#define O_NOFOLLOW 0400000 +#define O_CLOEXEC 02000000 + +#define O_ASYNC 020000 +#define O_DIRECT 040000 +#define O_LARGEFILE 0100000 +#define O_NOATIME 01000000 +#define O_PATH 010000000 +#define O_TMPFILE 020200000 +#define O_NDELAY O_NONBLOCK + +#define O_SEARCH O_PATH +#define O_EXEC O_PATH + +#define O_ACCMODE (03|O_SEARCH) + +#define F_DUPFD 0 +#define F_GETFD 1 +#define F_SETFD 2 +#define F_GETFL 3 +#define F_SETFL 4 + +#define F_SETOWN 8 +#define F_GETOWN 9 +#define F_SETSIG 10 +#define F_GETSIG 11 + +#define F_GETLK 12 +#define F_SETLK 13 +#define F_SETLKW 14 + +#define F_SETOWN_EX 15 +#define F_GETOWN_EX 16 + +#define F_GETOWNER_UIDS 17 +#endif + +#endif diff --git a/components/rt-thread/include/libc/libc_stat.h b/components/rt-thread/include/libc/libc_stat.h new file mode 100644 index 00000000..f680de4e --- /dev/null +++ b/components/rt-thread/include/libc/libc_stat.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef LIBC_STAT_H__ +#define LIBC_STAT_H__ + +#include + +#if defined(RT_USING_NEWLIB) || (defined( __GNUC__ ) && !defined(__ARMCC_VERSION)) +/* use header file of newlib */ +#include + +#elif defined(_WIN32) +#include + +#define S_IRWXU 00700 +#define S_IRUSR 00400 +#define S_IWUSR 00200 +#define S_IXUSR 00100 + +#define S_IRWXG 00070 +#define S_IRGRP 00040 +#define S_IWGRP 00020 +#define S_IXGRP 00010 + +#define S_IRWXO 00007 +#define S_IROTH 00004 +#define S_IWOTH 00002 +#define S_IXOTH 00001 + +#define S_IFSOCK 0140000 +#define S_IFLNK 0120000 +#define S_IFBLK 0060000 +#define S_IFIFO 0010000 +#define S_ISUID 0004000 +#define S_ISGID 0002000 +#define S_ISVTX 0001000 + +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) + +#else +#define S_IFMT 00170000 +#define S_IFSOCK 0140000 +#define S_IFLNK 0120000 +#define S_IFREG 0100000 +#define S_IFBLK 0060000 +#define S_IFDIR 0040000 +#define S_IFCHR 0020000 +#define S_IFIFO 0010000 +#define S_ISUID 0004000 +#define S_ISGID 0002000 +#define S_ISVTX 0001000 + +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) + +#define S_IRWXU 00700 +#define S_IRUSR 00400 +#define S_IWUSR 00200 +#define S_IXUSR 00100 + +#define S_IRWXG 00070 +#define S_IRGRP 00040 +#define S_IWGRP 00020 +#define S_IXGRP 00010 + +#define S_IRWXO 00007 +#define S_IROTH 00004 +#define S_IWOTH 00002 +#define S_IXOTH 00001 + +/* stat structure */ +#include +#include + +struct stat +{ + struct rt_device *st_dev; + uint16_t st_ino; + uint16_t st_mode; + uint16_t st_nlink; + uint16_t st_uid; + uint16_t st_gid; + struct rt_device *st_rdev; + uint32_t st_size; + time_t st_atime; + long st_spare1; + time_t st_mtime; + long st_spare2; + time_t st_ctime; + long st_spare3; + uint32_t st_blksize; + uint32_t st_blocks; + long st_spare4[2]; +}; + +#endif + +#endif diff --git a/components/rt-thread/include/rtdbg.h b/components/rt-thread/include/rtdbg.h new file mode 100644 index 00000000..6a6ea3d6 --- /dev/null +++ b/components/rt-thread/include/rtdbg.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2016-11-12 Bernard The first version + * 2018-05-25 armink Add simple API, such as LOG_D, LOG_E + */ + +/* + * The macro definitions for debug + * + * These macros are defined in static. If you want to use debug macro, you can + * use as following code: + * + * In your C/C++ file, enable/disable DEBUG_ENABLE macro, and then include this + * header file. + * + * #define DBG_TAG "MOD_TAG" + * #define DBG_LVL DBG_INFO + * #include // must after of DBG_LVL, DBG_TAG or other options + * + * Then in your C/C++ file, you can use LOG_X macro to print out logs: + * LOG_D("this is a debug log!"); + * LOG_E("this is a error log!"); + */ + +#ifndef RT_DBG_H__ +#define RT_DBG_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* the debug log will force enable when RT_DEBUG macro is defined */ +#if defined(RT_DEBUG) && !defined(DBG_ENABLE) +#define DBG_ENABLE +#endif + +/* it will force output color log when RT_DEBUG_COLOR macro is defined */ +#if defined(RT_DEBUG_COLOR) && !defined(DBG_COLOR) +#define DBG_COLOR +#endif + +#if defined(RT_USING_ULOG) +/* using ulog compatible with rtdbg */ +#include +#else + +/* DEBUG level */ +#define DBG_ERROR 0 +#define DBG_WARNING 1 +#define DBG_INFO 2 +#define DBG_LOG 3 + +#ifdef DBG_TAG +#ifndef DBG_SECTION_NAME +#define DBG_SECTION_NAME DBG_TAG +#endif +#else +/* compatible with old version */ +#ifndef DBG_SECTION_NAME +#define DBG_SECTION_NAME "DBG" +#endif +#endif /* DBG_TAG */ + +#ifdef DBG_ENABLE + +#ifdef DBG_LVL +#ifndef DBG_LEVEL +#define DBG_LEVEL DBG_LVL +#endif +#else +/* compatible with old version */ +#ifndef DBG_LEVEL +#define DBG_LEVEL DBG_WARNING +#endif +#endif /* DBG_LVL */ + +/* + * The color for terminal (foreground) + * BLACK 30 + * RED 31 + * GREEN 32 + * YELLOW 33 + * BLUE 34 + * PURPLE 35 + * CYAN 36 + * WHITE 37 + */ +#ifdef DBG_COLOR +#define _DBG_COLOR(n) rt_kprintf("\033["#n"m") +#define _DBG_LOG_HDR(lvl_name, color_n) \ + rt_kprintf("\033["#color_n"m[" lvl_name "/" DBG_SECTION_NAME "] ") +#define _DBG_LOG_X_END \ + rt_kprintf("\033[0m\n") +#else +#define _DBG_COLOR(n) +#define _DBG_LOG_HDR(lvl_name, color_n) \ + rt_kprintf("[" lvl_name "/" DBG_SECTION_NAME "] ") +#define _DBG_LOG_X_END \ + rt_kprintf("\n") +#endif /* DBG_COLOR */ + +/* + * static debug routine + * NOTE: This is a NOT RECOMMENDED API. Please using LOG_X API. + * It will be DISCARDED later. Because it will take up more resources. + */ +#define dbg_log(level, fmt, ...) \ + if ((level) <= DBG_LEVEL) \ + { \ + switch(level) \ + { \ + case DBG_ERROR: _DBG_LOG_HDR("E", 31); break; \ + case DBG_WARNING: _DBG_LOG_HDR("W", 33); break; \ + case DBG_INFO: _DBG_LOG_HDR("I", 32); break; \ + case DBG_LOG: _DBG_LOG_HDR("D", 0); break; \ + default: break; \ + } \ + rt_kprintf(fmt, ##__VA_ARGS__); \ + _DBG_COLOR(0); \ + } + +#define dbg_here \ + if ((DBG_LEVEL) <= DBG_LOG){ \ + rt_kprintf(DBG_SECTION_NAME " Here %s:%d\n", \ + __FUNCTION__, __LINE__); \ + } + +#define dbg_log_line(lvl, color_n, fmt, ...) \ + do \ + { \ + _DBG_LOG_HDR(lvl, color_n); \ + rt_kprintf(fmt, ##__VA_ARGS__); \ + _DBG_LOG_X_END; \ + } \ + while (0) + +#define dbg_raw(...) rt_kprintf(__VA_ARGS__); + +#else +#define dbg_log(level, fmt, ...) +#define dbg_here +#define dbg_enter +#define dbg_exit +#define dbg_log_line(lvl, color_n, fmt, ...) +#define dbg_raw(...) +#endif /* DBG_ENABLE */ + +#if (DBG_LEVEL >= DBG_LOG) +#define LOG_D(fmt, ...) dbg_log_line("D", 0, fmt, ##__VA_ARGS__) +#else +#define LOG_D(...) +#endif + +#if (DBG_LEVEL >= DBG_INFO) +#define LOG_I(fmt, ...) dbg_log_line("I", 32, fmt, ##__VA_ARGS__) +#else +#define LOG_I(...) +#endif + +#if (DBG_LEVEL >= DBG_WARNING) +#define LOG_W(fmt, ...) dbg_log_line("W", 33, fmt, ##__VA_ARGS__) +#else +#define LOG_W(...) +#endif + +#if (DBG_LEVEL >= DBG_ERROR) +#define LOG_E(fmt, ...) dbg_log_line("E", 31, fmt, ##__VA_ARGS__) +#else +#define LOG_E(...) +#endif + +#define LOG_RAW(...) dbg_raw(__VA_ARGS__) + +#define LOG_HEX(name, width, buf, size) + +#endif /* defined(RT_USING_ULOG) && define(DBG_ENABLE) */ + +#ifdef __cplusplus +} +#endif + +#endif /* RT_DBG_H__ */ diff --git a/components/rt-thread/include/rtdebug.h b/components/rt-thread/include/rtdebug.h new file mode 100644 index 00000000..8c7d450a --- /dev/null +++ b/components/rt-thread/include/rtdebug.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __RTDEBUG_H__ +#define __RTDEBUG_H__ + +#include + +/* settings depend check */ +#ifdef RT_USING_POSIX +#if !defined(RT_USING_DFS) || !defined(RT_USING_DFS_DEVFS) +#error "POSIX poll/select, stdin need file system(RT_USING_DFS) and device file system(RT_USING_DFS_DEVFS)" +#endif + +#if !defined(RT_USING_LIBC) +#error "POSIX layer need standard C library(RT_USING_LIBC)" +#endif + +#endif + +#ifdef RT_USING_POSIX_TERMIOS +#if !defined(RT_USING_POSIX) +#error "termios need POSIX layer(RT_USING_POSIX)" +#endif +#endif + +/* Using this macro to control all kernel debug features. */ +#ifdef RT_DEBUG + +/* Turn on some of these (set to non-zero) to debug kernel */ +#ifndef RT_DEBUG_MEM +#define RT_DEBUG_MEM 0 +#endif + +#ifndef RT_DEBUG_MEMHEAP +#define RT_DEBUG_MEMHEAP 0 +#endif + +#ifndef RT_DEBUG_MODULE +#define RT_DEBUG_MODULE 0 +#endif + +#ifndef RT_DEBUG_SCHEDULER +#define RT_DEBUG_SCHEDULER 0 +#endif + +#ifndef RT_DEBUG_SLAB +#define RT_DEBUG_SLAB 0 +#endif + +#ifndef RT_DEBUG_THREAD +#define RT_DEBUG_THREAD 0 +#endif + +#ifndef RT_DEBUG_TIMER +#define RT_DEBUG_TIMER 0 +#endif + +#ifndef RT_DEBUG_IRQ +#define RT_DEBUG_IRQ 0 +#endif + +#ifndef RT_DEBUG_IPC +#define RT_DEBUG_IPC 0 +#endif + +#ifndef RT_DEBUG_INIT +#define RT_DEBUG_INIT 0 +#endif + +/* Turn on this to enable context check */ +#ifndef RT_DEBUG_CONTEXT_CHECK +#define RT_DEBUG_CONTEXT_CHECK 1 +#endif + +#define RT_DEBUG_LOG(type, message) \ +do \ +{ \ + if (type) \ + rt_kprintf message; \ +} \ +while (0) + +#define RT_ASSERT(EX) \ +if (!(EX)) \ +{ \ + rt_assert_handler(#EX, __FUNCTION__, __LINE__); \ +} + +/* Macro to check current context */ +#if RT_DEBUG_CONTEXT_CHECK +#define RT_DEBUG_NOT_IN_INTERRUPT \ +do \ +{ \ + rt_base_t level; \ + level = rt_hw_interrupt_disable(); \ + if (rt_interrupt_get_nest() != 0) \ + { \ + rt_kprintf("Function[%s] shall not be used in ISR\n", __FUNCTION__); \ + RT_ASSERT(0) \ + } \ + rt_hw_interrupt_enable(level); \ +} \ +while (0) + +/* "In thread context" means: + * 1) the scheduler has been started + * 2) not in interrupt context. + */ +#define RT_DEBUG_IN_THREAD_CONTEXT \ +do \ +{ \ + rt_base_t level; \ + level = rt_hw_interrupt_disable(); \ + if (rt_thread_self() == RT_NULL) \ + { \ + rt_kprintf("Function[%s] shall not be used before scheduler start\n", \ + __FUNCTION__); \ + RT_ASSERT(0) \ + } \ + RT_DEBUG_NOT_IN_INTERRUPT; \ + rt_hw_interrupt_enable(level); \ +} \ +while (0) +#else +#define RT_DEBUG_NOT_IN_INTERRUPT +#define RT_DEBUG_IN_THREAD_CONTEXT +#endif + +#else /* RT_DEBUG */ + +#define RT_ASSERT(EX) +#define RT_DEBUG_LOG(type, message) +#define RT_DEBUG_NOT_IN_INTERRUPT +#define RT_DEBUG_IN_THREAD_CONTEXT + +#endif /* RT_DEBUG */ + +#endif /* __RTDEBUG_H__ */ diff --git a/components/rt-thread/include/rtdef.h b/components/rt-thread/include/rtdef.h new file mode 100644 index 00000000..278676bc --- /dev/null +++ b/components/rt-thread/include/rtdef.h @@ -0,0 +1,1170 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2007-01-10 Bernard the first version + * 2008-07-12 Bernard remove all rt_int8, rt_uint32_t etc typedef + * 2010-10-26 yi.qiu add module support + * 2010-11-10 Bernard add cleanup callback function in thread exit. + * 2011-05-09 Bernard use builtin va_arg in GCC 4.x + * 2012-11-16 Bernard change RT_NULL from ((void*)0) to 0. + * 2012-12-29 Bernard change the RT_USING_MEMPOOL location and add + * RT_USING_MEMHEAP condition. + * 2012-12-30 Bernard add more control command for graphic. + * 2013-01-09 Bernard change version number. + * 2015-02-01 Bernard change version number to v2.1.0 + * 2017-08-31 Bernard change version number to v3.0.0 + * 2017-11-30 Bernard change version number to v3.0.1 + * 2017-12-27 Bernard change version number to v3.0.2 + * 2018-02-24 Bernard change version number to v3.0.3 + * 2018-04-25 Bernard change version number to v3.0.4 + * 2018-05-31 Bernard change version number to v3.1.0 + * 2018-09-04 Bernard change version number to v3.1.1 + * 2018-09-14 Bernard apply Apache License v2.0 to RT-Thread Kernel + * 2018-10-13 Bernard change version number to v4.0.0 + * 2018-10-02 Bernard add 64bit arch support + * 2018-11-22 Jesven add smp member to struct rt_thread + * add struct rt_cpu + * add smp relevant macros + * 2019-01-27 Bernard change version number to v4.0.1 + * 2019-05-17 Bernard change version number to v4.0.2 + * 2019-12-20 Bernard change version number to v4.0.3 + * 2020-08-10 Meco Man add macro for struct rt_device_ops + * 2020-10-23 Meco Man define maximum value of ipc type + * 2021-03-19 Meco Man add security devices + * 2021-05-10 armink change version number to v4.0.4 + */ + +#ifndef __RT_DEF_H__ +#define __RT_DEF_H__ + +/* include rtconfig header to import configuration */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup BasicDef + */ + +/**@{*/ + +/* RT-Thread version information */ +#define RT_VERSION 4L /**< major version number */ +#define RT_SUBVERSION 0L /**< minor version number */ +#define RT_REVISION 4L /**< revise version number */ + +/* RT-Thread version */ +#define RTTHREAD_VERSION ((RT_VERSION * 10000) + \ + (RT_SUBVERSION * 100) + RT_REVISION) + +/* RT-Thread basic data type definitions */ +#ifndef RT_USING_ARCH_DATA_TYPE +typedef signed char rt_int8_t; /**< 8bit integer type */ +typedef signed short rt_int16_t; /**< 16bit integer type */ +typedef signed int rt_int32_t; /**< 32bit integer type */ +typedef unsigned char rt_uint8_t; /**< 8bit unsigned integer type */ +typedef unsigned short rt_uint16_t; /**< 16bit unsigned integer type */ +typedef unsigned int rt_uint32_t; /**< 32bit unsigned integer type */ + +#ifdef ARCH_CPU_64BIT +typedef signed long rt_int64_t; /**< 64bit integer type */ +typedef unsigned long rt_uint64_t; /**< 64bit unsigned integer type */ +#else +typedef signed long long rt_int64_t; /**< 64bit integer type */ +typedef unsigned long long rt_uint64_t; /**< 64bit unsigned integer type */ +#endif +#endif + +typedef int rt_bool_t; /**< boolean type */ +typedef long rt_base_t; /**< Nbit CPU related date type */ +typedef unsigned long rt_ubase_t; /**< Nbit unsigned CPU related data type */ + +typedef rt_base_t rt_err_t; /**< Type for error number */ +typedef rt_uint32_t rt_time_t; /**< Type for time stamp */ +typedef rt_uint32_t rt_tick_t; /**< Type for tick count */ +typedef rt_base_t rt_flag_t; /**< Type for flags */ +typedef rt_ubase_t rt_size_t; /**< Type for size number */ +typedef rt_ubase_t rt_dev_t; /**< Type for device */ +typedef rt_base_t rt_off_t; /**< Type for offset */ + +/* boolean type definitions */ +#define RT_TRUE 1 /**< boolean true */ +#define RT_FALSE 0 /**< boolean fails */ + +/**@}*/ + +/* maximum value of base type */ +#define RT_UINT8_MAX 0xff /**< Maxium number of UINT8 */ +#define RT_UINT16_MAX 0xffff /**< Maxium number of UINT16 */ +#define RT_UINT32_MAX 0xffffffff /**< Maxium number of UINT32 */ +#define RT_TICK_MAX RT_UINT32_MAX /**< Maxium number of tick */ + +/* maximum value of ipc type */ +#define RT_SEM_VALUE_MAX RT_UINT16_MAX /**< Maxium number of semaphore .value */ +#define RT_MUTEX_VALUE_MAX RT_UINT16_MAX /**< Maxium number of mutex .value */ +#define RT_MUTEX_HOLD_MAX RT_UINT8_MAX /**< Maxium number of mutex .hold */ +#define RT_MB_ENTRY_MAX RT_UINT16_MAX /**< Maxium number of mailbox .entry */ +#define RT_MQ_ENTRY_MAX RT_UINT16_MAX /**< Maxium number of message queue .entry */ + +#if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#define __CLANG_ARM +#endif + +/* Compiler Related Definitions */ +#if defined(__CC_ARM) || defined(__CLANG_ARM) /* ARM Compiler */ + #include + #define RT_SECTION(x) __attribute__((section(x))) + #define RT_UNUSED __attribute__((unused)) + #define RT_USED __attribute__((used)) + #define ALIGN(n) __attribute__((aligned(n))) + + #define RT_WEAK __attribute__((weak)) + #define rt_inline static __inline + /* module compiling */ + #ifdef RT_USING_MODULE + #define RTT_API __declspec(dllimport) + #else + #define RTT_API __declspec(dllexport) + #endif + +#elif defined (__IAR_SYSTEMS_ICC__) /* for IAR Compiler */ + #include + #define RT_SECTION(x) @ x + #define RT_UNUSED + #define RT_USED __root + #define PRAGMA(x) _Pragma(#x) + #define ALIGN(n) PRAGMA(data_alignment=n) + #define RT_WEAK __weak + #define rt_inline static inline + #define RTT_API + +#elif defined (__GNUC__) /* GNU GCC Compiler */ + #ifdef RT_USING_NEWLIB + #include + #else + /* the version of GNU GCC must be greater than 4.x */ + typedef __builtin_va_list __gnuc_va_list; + typedef __gnuc_va_list va_list; + #define va_start(v,l) __builtin_va_start(v,l) + #define va_end(v) __builtin_va_end(v) + #define va_arg(v,l) __builtin_va_arg(v,l) + #endif + + #define RT_SECTION(x) __attribute__((section(x))) + #define RT_UNUSED __attribute__((unused)) + #define RT_USED __attribute__((used)) + #define ALIGN(n) __attribute__((aligned(n))) + #define RT_WEAK __attribute__((weak)) + #define rt_inline static __inline + #define RTT_API +#elif defined (__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */ + #include + #define RT_SECTION(x) __attribute__((section(x))) + #define RT_UNUSED __attribute__((unused)) + #define RT_USED __attribute__((used)) + #define ALIGN(n) __attribute__((aligned(n))) + #define RT_WEAK __attribute__((weak)) + #define rt_inline static inline + #define RTT_API +#elif defined (_MSC_VER) + #include + #define RT_SECTION(x) + #define RT_UNUSED + #define RT_USED + #define ALIGN(n) __declspec(align(n)) + #define RT_WEAK + #define rt_inline static __inline + #define RTT_API +#elif defined (__TI_COMPILER_VERSION__) + #include + /* The way that TI compiler set section is different from other(at least + * GCC and MDK) compilers. See ARM Optimizing C/C++ Compiler 5.9.3 for more + * details. */ + #define RT_SECTION(x) + #define RT_UNUSED + #define RT_USED + #define PRAGMA(x) _Pragma(#x) + #define ALIGN(n) + #define RT_WEAK + #define rt_inline static inline + #define RTT_API +#elif defined (__TASKING__) + #include + #define RT_SECTION(x) __attribute__((section(x))) + #define RT_UNUSED __attribute__((unused)) + #define RT_USED __attribute__((used, protect)) + #define PRAGMA(x) _Pragma(#x) + #define ALIGN(n) __attribute__((__align(n))) + #define RT_WEAK __attribute__((weak)) + #define rt_inline static inline + #define RTT_API +#else + #error not supported tool chain +#endif + +/* initialization export */ +#ifdef RT_USING_COMPONENTS_INIT +typedef int (*init_fn_t)(void); +#ifdef _MSC_VER /* we do not support MS VC++ compiler */ +#pragma section("rti_fn$f",read) + #if RT_DEBUG_INIT + struct rt_init_desc + { + const char* level; + const init_fn_t fn; + const char* fn_name; + }; + #define INIT_EXPORT(fn, level) \ + const char __rti_level_##fn[] = level"__rt_init_"#fn; \ + const char __rti_##fn##_name[] = #fn; \ + __declspec(allocate("rti_fn$f")) \ + RT_USED const struct rt_init_desc __rt_init_msc_##fn = \ + {__rti_level_##fn, fn, __rti_##fn##_name}; + #else + struct rt_init_desc + { + const char* level; + const init_fn_t fn; + }; + #define INIT_EXPORT(fn, level) \ + const char __rti_level_##fn[] = level"__rt_init_"#fn; \ + __declspec(allocate("rti_fn$f")) \ + RT_USED const struct rt_init_desc __rt_init_msc_##fn = \ + {__rti_level_##fn, fn }; + #endif +#else + #if RT_DEBUG_INIT + struct rt_init_desc + { + const char* fn_name; + const init_fn_t fn; + }; + #define INIT_EXPORT(fn, level) \ + const char __rti_##fn##_name[] = #fn; \ + RT_USED const struct rt_init_desc __rt_init_desc_##fn RT_SECTION(".rti_fn." level) = \ + { __rti_##fn##_name, fn}; + #else + #define INIT_EXPORT(fn, level) \ + RT_USED const init_fn_t __rt_init_##fn RT_SECTION(".rti_fn." level) = fn + #endif +#endif +#else +#define INIT_EXPORT(fn, level) +#endif + +/* board init routines will be called in board_init() function */ +#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1") + +/* pre/device/component/env/app init routines will be called in init_thread */ +/* components pre-initialization (pure software initilization) */ +#define INIT_PREV_EXPORT(fn) INIT_EXPORT(fn, "2") +/* device initialization */ +#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3") +/* components initialization (dfs, lwip, ...) */ +#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4") +/* environment initialization (mount disk, ...) */ +#define INIT_ENV_EXPORT(fn) INIT_EXPORT(fn, "5") +/* appliation initialization (rtgui application etc ...) */ +#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6") + +#if !defined(RT_USING_FINSH) +/* define these to empty, even if not include finsh.h file */ +#define FINSH_FUNCTION_EXPORT(name, desc) +#define FINSH_FUNCTION_EXPORT_ALIAS(name, alias, desc) +#define FINSH_VAR_EXPORT(name, type, desc) + +#define MSH_CMD_EXPORT(command, desc) +#define MSH_CMD_EXPORT_ALIAS(command, alias, desc) +#elif !defined(FINSH_USING_SYMTAB) +#define FINSH_FUNCTION_EXPORT_CMD(name, cmd, desc) +#endif + +/* event length */ +#define RT_EVENT_LENGTH 32 + +/* memory management option */ +#define RT_MM_PAGE_SIZE 4096 +#define RT_MM_PAGE_MASK (RT_MM_PAGE_SIZE - 1) +#define RT_MM_PAGE_BITS 12 + +/* kernel malloc definitions */ +#ifndef RT_KERNEL_MALLOC +#define RT_KERNEL_MALLOC(sz) rt_malloc(sz) +#endif + +#ifndef RT_KERNEL_FREE +#define RT_KERNEL_FREE(ptr) rt_free(ptr) +#endif + +#ifndef RT_KERNEL_REALLOC +#define RT_KERNEL_REALLOC(ptr, size) rt_realloc(ptr, size) +#endif + +/** + * @addtogroup Error + */ + +/**@{*/ + +/* RT-Thread error code definitions */ +#define RT_EOK 0 /**< There is no error */ +#define RT_ERROR 1 /**< A generic error happens */ +#define RT_ETIMEOUT 2 /**< Timed out */ +#define RT_EFULL 3 /**< The resource is full */ +#define RT_EEMPTY 4 /**< The resource is empty */ +#define RT_ENOMEM 5 /**< No memory */ +#define RT_ENOSYS 6 /**< No system */ +#define RT_EBUSY 7 /**< Busy */ +#define RT_EIO 8 /**< IO error */ +#define RT_EINTR 9 /**< Interrupted system call */ +#define RT_EINVAL 10 /**< Invalid argument */ + +/**@}*/ + +/** + * @ingroup BasicDef + * + * @def RT_ALIGN(size, align) + * Return the most contiguous size aligned at specified width. RT_ALIGN(13, 4) + * would return 16. + */ +#define RT_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1)) + +/** + * @ingroup BasicDef + * + * @def RT_ALIGN_DOWN(size, align) + * Return the down number of aligned at specified width. RT_ALIGN_DOWN(13, 4) + * would return 12. + */ +#define RT_ALIGN_DOWN(size, align) ((size) & ~((align) - 1)) + +/** + * @ingroup BasicDef + * + * @def RT_NULL + * Similar as the \c NULL in C library. + */ +#define RT_NULL (0) + +/** + * Double List structure + */ +struct rt_list_node +{ + struct rt_list_node *next; /**< point to next node. */ + struct rt_list_node *prev; /**< point to prev node. */ +}; +typedef struct rt_list_node rt_list_t; /**< Type for lists. */ + +/** + * Single List structure + */ +struct rt_slist_node +{ + struct rt_slist_node *next; /**< point to next node. */ +}; +typedef struct rt_slist_node rt_slist_t; /**< Type for single list. */ + +/** + * @addtogroup KernelObject + */ + +/**@{*/ + +/* + * kernel object macros + */ +#define RT_OBJECT_FLAG_MODULE 0x80 /**< is module object. */ + +/** + * Base structure of Kernel object + */ +struct rt_object +{ + char name[RT_NAME_MAX]; /**< name of kernel object */ + rt_uint8_t type; /**< type of kernel object */ + rt_uint8_t flag; /**< flag of kernel object */ + +#ifdef RT_USING_MODULE + void *module_id; /**< id of application module */ +#endif + rt_list_t list; /**< list node of kernel object */ +}; +typedef struct rt_object *rt_object_t; /**< Type for kernel objects. */ + +/** + * The object type can be one of the follows with specific + * macros enabled: + * - Thread + * - Semaphore + * - Mutex + * - Event + * - MailBox + * - MessageQueue + * - MemHeap + * - MemPool + * - Device + * - Timer + * - Module + * - Unknown + * - Static + */ +enum rt_object_class_type +{ + RT_Object_Class_Null = 0x00, /**< The object is not used. */ + RT_Object_Class_Thread = 0x01, /**< The object is a thread. */ + RT_Object_Class_Semaphore = 0x02, /**< The object is a semaphore. */ + RT_Object_Class_Mutex = 0x03, /**< The object is a mutex. */ + RT_Object_Class_Event = 0x04, /**< The object is a event. */ + RT_Object_Class_MailBox = 0x05, /**< The object is a mail box. */ + RT_Object_Class_MessageQueue = 0x06, /**< The object is a message queue. */ + RT_Object_Class_MemHeap = 0x07, /**< The object is a memory heap. */ + RT_Object_Class_MemPool = 0x08, /**< The object is a memory pool. */ + RT_Object_Class_Device = 0x09, /**< The object is a device. */ + RT_Object_Class_Timer = 0x0a, /**< The object is a timer. */ + RT_Object_Class_Module = 0x0b, /**< The object is a module. */ + RT_Object_Class_Unknown = 0x0c, /**< The object is unknown. */ + RT_Object_Class_Static = 0x80 /**< The object is a static object. */ +}; + +/** + * The information of the kernel object + */ +struct rt_object_information +{ + enum rt_object_class_type type; /**< object class type */ + rt_list_t object_list; /**< object list */ + rt_size_t object_size; /**< object size */ +}; + +/** + * The hook function call macro + */ +#ifdef RT_USING_HOOK +#define RT_OBJECT_HOOK_CALL(func, argv) \ + do { if ((func) != RT_NULL) func argv; } while (0) +#else +#define RT_OBJECT_HOOK_CALL(func, argv) +#endif + +/**@}*/ + +/** + * @addtogroup Clock + */ + +/**@{*/ + +/** + * clock & timer macros + */ +#define RT_TIMER_FLAG_DEACTIVATED 0x0 /**< timer is deactive */ +#define RT_TIMER_FLAG_ACTIVATED 0x1 /**< timer is active */ +#define RT_TIMER_FLAG_ONE_SHOT 0x0 /**< one shot timer */ +#define RT_TIMER_FLAG_PERIODIC 0x2 /**< periodic timer */ + +#define RT_TIMER_FLAG_HARD_TIMER 0x0 /**< hard timer,the timer's callback function will be called in tick isr. */ +#define RT_TIMER_FLAG_SOFT_TIMER 0x4 /**< soft timer,the timer's callback function will be called in timer thread. */ + +#define RT_TIMER_CTRL_SET_TIME 0x0 /**< set timer control command */ +#define RT_TIMER_CTRL_GET_TIME 0x1 /**< get timer control command */ +#define RT_TIMER_CTRL_SET_ONESHOT 0x2 /**< change timer to one shot */ +#define RT_TIMER_CTRL_SET_PERIODIC 0x3 /**< change timer to periodic */ +#define RT_TIMER_CTRL_GET_STATE 0x4 /**< get timer run state active or deactive*/ + +#ifndef RT_TIMER_SKIP_LIST_LEVEL +#define RT_TIMER_SKIP_LIST_LEVEL 1 +#endif + +/* 1 or 3 */ +#ifndef RT_TIMER_SKIP_LIST_MASK +#define RT_TIMER_SKIP_LIST_MASK 0x3 +#endif + +/** + * timer structure + */ +struct rt_timer +{ + struct rt_object parent; /**< inherit from rt_object */ + + rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL]; + + void (*timeout_func)(void *parameter); /**< timeout function */ + void *parameter; /**< timeout function's parameter */ + + rt_tick_t init_tick; /**< timer timeout tick */ + rt_tick_t timeout_tick; /**< timeout tick */ +}; +typedef struct rt_timer *rt_timer_t; + +/**@}*/ + +/** + * @addtogroup Signal + */ +#ifdef RT_USING_SIGNALS +#include +typedef unsigned long rt_sigset_t; +typedef void (*rt_sighandler_t)(int signo); +typedef siginfo_t rt_siginfo_t; + +#define RT_SIG_MAX 32 +#endif +/**@}*/ + +/** + * @addtogroup Thread + */ + +/**@{*/ + +/* + * Thread + */ + +/* + * thread state definitions + */ +#define RT_THREAD_INIT 0x00 /**< Initialized status */ +#define RT_THREAD_READY 0x01 /**< Ready status */ +#define RT_THREAD_SUSPEND 0x02 /**< Suspend status */ +#define RT_THREAD_RUNNING 0x03 /**< Running status */ +#define RT_THREAD_BLOCK RT_THREAD_SUSPEND /**< Blocked status */ +#define RT_THREAD_CLOSE 0x04 /**< Closed status */ +#define RT_THREAD_STAT_MASK 0x07 + +#define RT_THREAD_STAT_YIELD 0x08 /**< indicate whether remaining_tick has been reloaded since last schedule */ +#define RT_THREAD_STAT_YIELD_MASK RT_THREAD_STAT_YIELD + +#define RT_THREAD_STAT_SIGNAL 0x10 /**< task hold signals */ +#define RT_THREAD_STAT_SIGNAL_READY (RT_THREAD_STAT_SIGNAL | RT_THREAD_READY) +#define RT_THREAD_STAT_SIGNAL_WAIT 0x20 /**< task is waiting for signals */ +#define RT_THREAD_STAT_SIGNAL_PENDING 0x40 /**< signals is held and it has not been procressed */ +#define RT_THREAD_STAT_SIGNAL_MASK 0xf0 + +/** + * thread control command definitions + */ +#define RT_THREAD_CTRL_STARTUP 0x00 /**< Startup thread. */ +#define RT_THREAD_CTRL_CLOSE 0x01 /**< Close thread. */ +#define RT_THREAD_CTRL_CHANGE_PRIORITY 0x02 /**< Change thread priority. */ +#define RT_THREAD_CTRL_INFO 0x03 /**< Get thread information. */ +#define RT_THREAD_CTRL_BIND_CPU 0x04 /**< Set thread bind cpu. */ + +#ifdef RT_USING_SMP + +#define RT_CPU_DETACHED RT_CPUS_NR /**< The thread not running on cpu. */ +#define RT_CPU_MASK ((1 << RT_CPUS_NR) - 1) /**< All CPUs mask bit. */ + +#ifndef RT_SCHEDULE_IPI +#define RT_SCHEDULE_IPI 0 +#endif + +#ifndef RT_STOP_IPI +#define RT_STOP_IPI 1 +#endif + +/** + * CPUs definitions + * + */ +struct rt_cpu +{ + struct rt_thread *current_thread; + + rt_uint16_t irq_nest; + rt_uint8_t irq_switch_flag; + + rt_uint8_t current_priority; + rt_list_t priority_table[RT_THREAD_PRIORITY_MAX]; +#if RT_THREAD_PRIORITY_MAX > 32 + rt_uint32_t priority_group; + rt_uint8_t ready_table[32]; +#else + rt_uint32_t priority_group; +#endif + + rt_tick_t tick; +}; + +#endif + +/** + * Thread structure + */ +struct rt_thread +{ + /* rt object */ + char name[RT_NAME_MAX]; /**< the name of thread */ + rt_uint8_t type; /**< type of object */ + rt_uint8_t flags; /**< thread's flags */ + +#ifdef RT_USING_MODULE + void *module_id; /**< id of application module */ +#endif + + rt_list_t list; /**< the object list */ + rt_list_t tlist; /**< the thread list */ + + /* stack point and entry */ + void *sp; /**< stack point */ + void *entry; /**< entry */ + void *parameter; /**< parameter */ + void *stack_addr; /**< stack address */ + rt_uint32_t stack_size; /**< stack size */ + + /* error code */ + rt_err_t error; /**< error code */ + + rt_uint8_t stat; /**< thread status */ + +#ifdef RT_USING_SMP + rt_uint8_t bind_cpu; /**< thread is bind to cpu */ + rt_uint8_t oncpu; /**< process on cpu` */ + + rt_uint16_t scheduler_lock_nest; /**< scheduler lock count */ + rt_uint16_t cpus_lock_nest; /**< cpus lock count */ + rt_uint16_t critical_lock_nest; /**< critical lock count */ +#endif /*RT_USING_SMP*/ + + /* priority */ + rt_uint8_t current_priority; /**< current priority */ + rt_uint8_t init_priority; /**< initialized priority */ +#if RT_THREAD_PRIORITY_MAX > 32 + rt_uint8_t number; + rt_uint8_t high_mask; +#endif + rt_uint32_t number_mask; + +#if defined(RT_USING_EVENT) + /* thread event */ + rt_uint32_t event_set; + rt_uint8_t event_info; +#endif + +#if defined(RT_USING_SIGNALS) + rt_sigset_t sig_pending; /**< the pending signals */ + rt_sigset_t sig_mask; /**< the mask bits of signal */ + +#ifndef RT_USING_SMP + void *sig_ret; /**< the return stack pointer from signal */ +#endif + rt_sighandler_t *sig_vectors; /**< vectors of signal handler */ + void *si_list; /**< the signal infor list */ +#endif + + rt_ubase_t init_tick; /**< thread's initialized tick */ + rt_ubase_t remaining_tick; /**< remaining tick */ + +#ifdef RT_USING_CPU_USAGE + rt_uint64_t duration_tick; /**< cpu usage tick */ +#endif + + struct rt_timer thread_timer; /**< built-in thread timer */ + + void (*cleanup)(struct rt_thread *tid); /**< cleanup function when thread exit */ + + /* light weight process if present */ +#ifdef RT_USING_LWP + void *lwp; +#endif + + rt_ubase_t user_data; /**< private user data beyond this thread */ +}; +typedef struct rt_thread *rt_thread_t; + +/**@}*/ + +/** + * @addtogroup IPC + */ + +/**@{*/ + +/** + * IPC flags and control command definitions + */ +#define RT_IPC_FLAG_FIFO 0x00 /**< FIFOed IPC. @ref IPC. */ +#define RT_IPC_FLAG_PRIO 0x01 /**< PRIOed IPC. @ref IPC. */ + +#define RT_IPC_CMD_UNKNOWN 0x00 /**< unknown IPC command */ +#define RT_IPC_CMD_RESET 0x01 /**< reset IPC object */ + +#define RT_WAITING_FOREVER -1 /**< Block forever until get resource. */ +#define RT_WAITING_NO 0 /**< Non-block. */ + +/** + * Base structure of IPC object + */ +struct rt_ipc_object +{ + struct rt_object parent; /**< inherit from rt_object */ + + rt_list_t suspend_thread; /**< threads pended on this resource */ +}; + +#ifdef RT_USING_SEMAPHORE +/** + * Semaphore structure + */ +struct rt_semaphore +{ + struct rt_ipc_object parent; /**< inherit from ipc_object */ + + rt_uint16_t value; /**< value of semaphore. */ + rt_uint16_t reserved; /**< reserved field */ +}; +typedef struct rt_semaphore *rt_sem_t; +#endif + +#ifdef RT_USING_MUTEX +/** + * Mutual exclusion (mutex) structure + */ +struct rt_mutex +{ + struct rt_ipc_object parent; /**< inherit from ipc_object */ + + rt_uint16_t value; /**< value of mutex */ + + rt_uint8_t original_priority; /**< priority of last thread hold the mutex */ + rt_uint8_t hold; /**< numbers of thread hold the mutex */ + + struct rt_thread *owner; /**< current owner of mutex */ +}; +typedef struct rt_mutex *rt_mutex_t; +#endif + +#ifdef RT_USING_EVENT +/** + * flag defintions in event + */ +#define RT_EVENT_FLAG_AND 0x01 /**< logic and */ +#define RT_EVENT_FLAG_OR 0x02 /**< logic or */ +#define RT_EVENT_FLAG_CLEAR 0x04 /**< clear flag */ + +/* + * event structure + */ +struct rt_event +{ + struct rt_ipc_object parent; /**< inherit from ipc_object */ + + rt_uint32_t set; /**< event set */ +}; +typedef struct rt_event *rt_event_t; +#endif + +#ifdef RT_USING_MAILBOX +/** + * mailbox structure + */ +struct rt_mailbox +{ + struct rt_ipc_object parent; /**< inherit from ipc_object */ + + rt_ubase_t *msg_pool; /**< start address of message buffer */ + + rt_uint16_t size; /**< size of message pool */ + + rt_uint16_t entry; /**< index of messages in msg_pool */ + rt_uint16_t in_offset; /**< input offset of the message buffer */ + rt_uint16_t out_offset; /**< output offset of the message buffer */ + + rt_list_t suspend_sender_thread; /**< sender thread suspended on this mailbox */ +}; +typedef struct rt_mailbox *rt_mailbox_t; +#endif + +#ifdef RT_USING_MESSAGEQUEUE +/** + * message queue structure + */ +struct rt_messagequeue +{ + struct rt_ipc_object parent; /**< inherit from ipc_object */ + + void *msg_pool; /**< start address of message queue */ + + rt_uint16_t msg_size; /**< message size of each message */ + rt_uint16_t max_msgs; /**< max number of messages */ + + rt_uint16_t entry; /**< index of messages in the queue */ + + void *msg_queue_head; /**< list head */ + void *msg_queue_tail; /**< list tail */ + void *msg_queue_free; /**< pointer indicated the free node of queue */ + + rt_list_t suspend_sender_thread; /**< sender thread suspended on this message queue */ +}; +typedef struct rt_messagequeue *rt_mq_t; +#endif + +/**@}*/ + +/** + * @addtogroup MM + */ + +/**@{*/ + +/* + * memory management + * heap & partition + */ + +#ifdef RT_USING_MEMHEAP +/** + * memory item on the heap + */ +struct rt_memheap_item +{ + rt_uint32_t magic; /**< magic number for memheap */ + struct rt_memheap *pool_ptr; /**< point of pool */ + + struct rt_memheap_item *next; /**< next memheap item */ + struct rt_memheap_item *prev; /**< prev memheap item */ + + struct rt_memheap_item *next_free; /**< next free memheap item */ + struct rt_memheap_item *prev_free; /**< prev free memheap item */ +#ifdef RT_USING_MEMTRACE + rt_uint8_t owner_thread_name[4]; /**< owner thread name */ +#endif +}; + +/** + * Base structure of memory heap object + */ +struct rt_memheap +{ + struct rt_object parent; /**< inherit from rt_object */ + + void *start_addr; /**< pool start address and size */ + + rt_uint32_t pool_size; /**< pool size */ + rt_uint32_t available_size; /**< available size */ + rt_uint32_t max_used_size; /**< maximum allocated size */ + + struct rt_memheap_item *block_list; /**< used block list */ + + struct rt_memheap_item *free_list; /**< free block list */ + struct rt_memheap_item free_header; /**< free block list header */ + + struct rt_semaphore lock; /**< semaphore lock */ +}; +#endif + +#ifdef RT_USING_MEMPOOL +/** + * Base structure of Memory pool object + */ +struct rt_mempool +{ + struct rt_object parent; /**< inherit from rt_object */ + + void *start_address; /**< memory pool start */ + rt_size_t size; /**< size of memory pool */ + + rt_size_t block_size; /**< size of memory blocks */ + rt_uint8_t *block_list; /**< memory blocks list */ + + rt_size_t block_total_count; /**< numbers of memory block */ + rt_size_t block_free_count; /**< numbers of free memory block */ + + rt_list_t suspend_thread; /**< threads pended on this resource */ +}; +typedef struct rt_mempool *rt_mp_t; +#endif + +/**@}*/ + +#ifdef RT_USING_DEVICE +/** + * @addtogroup Device + */ + +/**@{*/ + +/** + * device (I/O) class type + */ +enum rt_device_class_type +{ + RT_Device_Class_Char = 0, /**< character device */ + RT_Device_Class_Block, /**< block device */ + RT_Device_Class_NetIf, /**< net interface */ + RT_Device_Class_MTD, /**< memory device */ + RT_Device_Class_CAN, /**< CAN device */ + RT_Device_Class_RTC, /**< RTC device */ + RT_Device_Class_Sound, /**< Sound device */ + RT_Device_Class_Graphic, /**< Graphic device */ + RT_Device_Class_I2CBUS, /**< I2C bus device */ + RT_Device_Class_USBDevice, /**< USB slave device */ + RT_Device_Class_USBHost, /**< USB host bus */ + RT_Device_Class_USBOTG, /**< USB OTG bus */ + RT_Device_Class_SPIBUS, /**< SPI bus device */ + RT_Device_Class_SPIDevice, /**< SPI device */ + RT_Device_Class_SDIO, /**< SDIO bus device */ + RT_Device_Class_PM, /**< PM pseudo device */ + RT_Device_Class_Pipe, /**< Pipe device */ + RT_Device_Class_Portal, /**< Portal device */ + RT_Device_Class_Timer, /**< Timer device */ + RT_Device_Class_Miscellaneous, /**< Miscellaneous device */ + RT_Device_Class_Sensor, /**< Sensor device */ + RT_Device_Class_Touch, /**< Touch device */ + RT_Device_Class_PHY, /**< PHY device */ + RT_Device_Class_Security, /**< Security device */ + RT_Device_Class_Unknown /**< unknown device */ +}; + +/** + * device flags defitions + */ +#define RT_DEVICE_FLAG_DEACTIVATE 0x000 /**< device is not not initialized */ + +#define RT_DEVICE_FLAG_RDONLY 0x001 /**< read only */ +#define RT_DEVICE_FLAG_WRONLY 0x002 /**< write only */ +#define RT_DEVICE_FLAG_RDWR 0x003 /**< read and write */ + +#define RT_DEVICE_FLAG_REMOVABLE 0x004 /**< removable device */ +#define RT_DEVICE_FLAG_STANDALONE 0x008 /**< standalone device */ +#define RT_DEVICE_FLAG_ACTIVATED 0x010 /**< device is activated */ +#define RT_DEVICE_FLAG_SUSPENDED 0x020 /**< device is suspended */ +#define RT_DEVICE_FLAG_STREAM 0x040 /**< stream mode */ + +#define RT_DEVICE_FLAG_INT_RX 0x100 /**< INT mode on Rx */ +#define RT_DEVICE_FLAG_DMA_RX 0x200 /**< DMA mode on Rx */ +#define RT_DEVICE_FLAG_INT_TX 0x400 /**< INT mode on Tx */ +#define RT_DEVICE_FLAG_DMA_TX 0x800 /**< DMA mode on Tx */ + +#define RT_DEVICE_OFLAG_CLOSE 0x000 /**< device is closed */ +#define RT_DEVICE_OFLAG_RDONLY 0x001 /**< read only access */ +#define RT_DEVICE_OFLAG_WRONLY 0x002 /**< write only access */ +#define RT_DEVICE_OFLAG_RDWR 0x003 /**< read and write */ +#define RT_DEVICE_OFLAG_OPEN 0x008 /**< device is opened */ +#define RT_DEVICE_OFLAG_MASK 0xf0f /**< mask of open flag */ + +/** + * general device commands + */ +#define RT_DEVICE_CTRL_RESUME 0x01 /**< resume device */ +#define RT_DEVICE_CTRL_SUSPEND 0x02 /**< suspend device */ +#define RT_DEVICE_CTRL_CONFIG 0x03 /**< configure device */ +#define RT_DEVICE_CTRL_CLOSE 0x04 /**< close device */ + +#define RT_DEVICE_CTRL_SET_INT 0x10 /**< set interrupt */ +#define RT_DEVICE_CTRL_CLR_INT 0x11 /**< clear interrupt */ +#define RT_DEVICE_CTRL_GET_INT 0x12 /**< get interrupt status */ + +/** + * special device commands + */ +#define RT_DEVICE_CTRL_CHAR_STREAM 0x10 /**< stream mode on char device */ +#define RT_DEVICE_CTRL_BLK_GETGEOME 0x10 /**< get geometry information */ +#define RT_DEVICE_CTRL_BLK_SYNC 0x11 /**< flush data to block device */ +#define RT_DEVICE_CTRL_BLK_ERASE 0x12 /**< erase block on block device */ +#define RT_DEVICE_CTRL_BLK_AUTOREFRESH 0x13 /**< block device : enter/exit auto refresh mode */ +#define RT_DEVICE_CTRL_NETIF_GETMAC 0x10 /**< get mac address */ +#define RT_DEVICE_CTRL_MTD_FORMAT 0x10 /**< format a MTD device */ + +typedef struct rt_device *rt_device_t; + +#ifdef RT_USING_DEVICE_OPS +/** + * operations set for device object + */ +struct rt_device_ops +{ + /* common device interface */ + rt_err_t (*init) (rt_device_t dev); + rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag); + rt_err_t (*close) (rt_device_t dev); + rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size); + rt_size_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size); + rt_err_t (*control)(rt_device_t dev, int cmd, void *args); +}; +#endif + +/** + * WaitQueue structure + */ +struct rt_wqueue +{ + rt_uint32_t flag; + rt_list_t waiting_list; +}; +typedef struct rt_wqueue rt_wqueue_t; + +/** + * Device structure + */ +struct rt_device +{ + struct rt_object parent; /**< inherit from rt_object */ + + enum rt_device_class_type type; /**< device type */ + rt_uint16_t flag; /**< device flag */ + rt_uint16_t open_flag; /**< device open flag */ + + rt_uint8_t ref_count; /**< reference count */ + rt_uint8_t device_id; /**< 0 - 255 */ + + /* device call back */ + rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size); + rt_err_t (*tx_complete)(rt_device_t dev, void *buffer); + +#ifdef RT_USING_DEVICE_OPS + const struct rt_device_ops *ops; +#else + /* common device interface */ + rt_err_t (*init) (rt_device_t dev); + rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag); + rt_err_t (*close) (rt_device_t dev); + rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size); + rt_size_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size); + rt_err_t (*control)(rt_device_t dev, int cmd, void *args); +#endif + +#if defined(RT_USING_POSIX) + const struct dfs_file_ops *fops; + struct rt_wqueue wait_queue; +#endif + + void *user_data; /**< device private data */ +}; + +/** + * block device geometry structure + */ +struct rt_device_blk_geometry +{ + rt_uint32_t sector_count; /**< count of sectors */ + rt_uint32_t bytes_per_sector; /**< number of bytes per sector */ + rt_uint32_t block_size; /**< number of bytes to erase one block */ +}; + +/** + * sector arrange struct on block device + */ +struct rt_device_blk_sectors +{ + rt_uint32_t sector_begin; /**< begin sector */ + rt_uint32_t sector_end; /**< end sector */ +}; + +/** + * cursor control command + */ +#define RT_DEVICE_CTRL_CURSOR_SET_POSITION 0x10 +#define RT_DEVICE_CTRL_CURSOR_SET_TYPE 0x11 + +/** + * graphic device control command + */ +#define RTGRAPHIC_CTRL_RECT_UPDATE 0 +#define RTGRAPHIC_CTRL_POWERON 1 +#define RTGRAPHIC_CTRL_POWEROFF 2 +#define RTGRAPHIC_CTRL_GET_INFO 3 +#define RTGRAPHIC_CTRL_SET_MODE 4 +#define RTGRAPHIC_CTRL_GET_EXT 5 +#define RTGRAPHIC_CTRL_SET_BRIGHTNESS 6 +#define RTGRAPHIC_CTRL_GET_BRIGHTNESS 7 +#define RTGRAPHIC_CTRL_GET_MODE 8 +#define RTGRAPHIC_CTRL_GET_STATUS 9 + +/* graphic deice */ +enum +{ + RTGRAPHIC_PIXEL_FORMAT_MONO = 0, + RTGRAPHIC_PIXEL_FORMAT_GRAY4, + RTGRAPHIC_PIXEL_FORMAT_GRAY16, + RTGRAPHIC_PIXEL_FORMAT_RGB332, + RTGRAPHIC_PIXEL_FORMAT_RGB444, + RTGRAPHIC_PIXEL_FORMAT_RGB565, + RTGRAPHIC_PIXEL_FORMAT_RGB565P, + RTGRAPHIC_PIXEL_FORMAT_BGR565 = RTGRAPHIC_PIXEL_FORMAT_RGB565P, + RTGRAPHIC_PIXEL_FORMAT_RGB666, + RTGRAPHIC_PIXEL_FORMAT_RGB888, + RTGRAPHIC_PIXEL_FORMAT_ARGB888, + RTGRAPHIC_PIXEL_FORMAT_ABGR888, + RTGRAPHIC_PIXEL_FORMAT_RESERVED, +}; + +/** + * build a pixel position according to (x, y) coordinates. + */ +#define RTGRAPHIC_PIXEL_POSITION(x, y) ((x << 16) | y) + +/** + * graphic device information structure + */ +struct rt_device_graphic_info +{ + rt_uint8_t pixel_format; /**< graphic format */ + rt_uint8_t bits_per_pixel; /**< bits per pixel */ + rt_uint16_t pitch; /**< bytes per line */ + + rt_uint16_t width; /**< width of graphic device */ + rt_uint16_t height; /**< height of graphic device */ + + rt_uint8_t *framebuffer; /**< frame buffer */ +}; + +/** + * rectangle information structure + */ +struct rt_device_rect_info +{ + rt_uint16_t x; /**< x coordinate */ + rt_uint16_t y; /**< y coordinate */ + rt_uint16_t width; /**< width */ + rt_uint16_t height; /**< height */ +}; + +/** + * graphic operations + */ +struct rt_device_graphic_ops +{ + void (*set_pixel) (const char *pixel, int x, int y); + void (*get_pixel) (char *pixel, int x, int y); + + void (*draw_hline)(const char *pixel, int x1, int x2, int y); + void (*draw_vline)(const char *pixel, int x, int y1, int y2); + + void (*blit_line) (const char *pixel, int x, int y, rt_size_t size); +}; +#define rt_graphix_ops(device) ((struct rt_device_graphic_ops *)(device->user_data)) + +/**@}*/ +#endif + +/* definitions for libc */ +#include "rtlibc.h" + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +/* RT-Thread definitions for C++ */ +namespace rtthread { + +enum TICK_WAIT { + WAIT_NONE = 0, + WAIT_FOREVER = -1, +}; + +} + +#endif /* end of __cplusplus */ + +#endif diff --git a/components/rt-thread/include/rthw.h b/components/rt-thread/include/rthw.h new file mode 100644 index 00000000..5672a8f9 --- /dev/null +++ b/components/rt-thread/include/rthw.h @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-03-18 Bernard the first version + * 2006-04-25 Bernard add rt_hw_context_switch_interrupt declaration + * 2006-09-24 Bernard add rt_hw_context_switch_to declaration + * 2012-12-29 Bernard add rt_hw_exception_install declaration + * 2017-10-17 Hichard add some micros + * 2018-11-17 Jesven add rt_hw_spinlock_t + * add smp support + */ + +#ifndef __RT_HW_H__ +#define __RT_HW_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Some macros define + */ +#ifndef HWREG32 +#define HWREG32(x) (*((volatile rt_uint32_t *)(x))) +#endif +#ifndef HWREG16 +#define HWREG16(x) (*((volatile rt_uint16_t *)(x))) +#endif +#ifndef HWREG8 +#define HWREG8(x) (*((volatile rt_uint8_t *)(x))) +#endif + +#ifndef RT_CPU_CACHE_LINE_SZ +#define RT_CPU_CACHE_LINE_SZ 32 +#endif + +enum RT_HW_CACHE_OPS +{ + RT_HW_CACHE_FLUSH = 0x01, + RT_HW_CACHE_INVALIDATE = 0x02, +}; + +/* + * CPU interfaces + */ +void rt_hw_cpu_icache_enable(void); +void rt_hw_cpu_icache_disable(void); +rt_base_t rt_hw_cpu_icache_status(void); +void rt_hw_cpu_icache_ops(int ops, void* addr, int size); + +void rt_hw_cpu_dcache_enable(void); +void rt_hw_cpu_dcache_disable(void); +rt_base_t rt_hw_cpu_dcache_status(void); +void rt_hw_cpu_dcache_ops(int ops, void* addr, int size); + +void rt_hw_cpu_reset(void); +void rt_hw_cpu_shutdown(void); + +rt_uint8_t *rt_hw_stack_init(void *entry, + void *parameter, + rt_uint8_t *stack_addr, + void *exit); + +/* + * Interrupt handler definition + */ +typedef void (*rt_isr_handler_t)(int vector, void *param); + +struct rt_irq_desc +{ + rt_isr_handler_t handler; + void *param; + +#ifdef RT_USING_INTERRUPT_INFO + char name[RT_NAME_MAX]; + rt_uint32_t counter; +#endif +}; + +/* + * Interrupt interfaces + */ +void rt_hw_interrupt_init(void); +void rt_hw_interrupt_mask(int vector); +void rt_hw_interrupt_umask(int vector); +rt_isr_handler_t rt_hw_interrupt_install(int vector, + rt_isr_handler_t handler, + void *param, + const char *name); + +#ifdef RT_USING_SMP +rt_base_t rt_hw_local_irq_disable(); +void rt_hw_local_irq_enable(rt_base_t level); + +#define rt_hw_interrupt_disable rt_cpus_lock +#define rt_hw_interrupt_enable rt_cpus_unlock + +#else +rt_base_t rt_hw_interrupt_disable(void); +void rt_hw_interrupt_enable(rt_base_t level); +#endif /*RT_USING_SMP*/ + +/* + * Context interfaces + */ +#ifdef RT_USING_SMP +void rt_hw_context_switch(rt_ubase_t from, rt_ubase_t to, struct rt_thread *to_thread); +void rt_hw_context_switch_to(rt_ubase_t to, struct rt_thread *to_thread); +void rt_hw_context_switch_interrupt(void *context, rt_ubase_t from, rt_ubase_t to, struct rt_thread *to_thread); +#else +void rt_hw_context_switch(rt_ubase_t from, rt_ubase_t to); +void rt_hw_context_switch_to(rt_ubase_t to); +void rt_hw_context_switch_interrupt(rt_ubase_t from, rt_ubase_t to); +#endif /*RT_USING_SMP*/ + +void rt_hw_console_output(const char *str); + +void rt_hw_backtrace(rt_uint32_t *fp, rt_ubase_t thread_entry); +void rt_hw_show_memory(rt_uint32_t addr, rt_size_t size); + +/* + * Exception interfaces + */ +void rt_hw_exception_install(rt_err_t (*exception_handle)(void *context)); + +/* + * delay interfaces + */ +void rt_hw_us_delay(rt_uint32_t us); + +#ifdef RT_USING_SMP +typedef union { + unsigned long slock; + struct __arch_tickets { + unsigned short owner; + unsigned short next; + } tickets; +} rt_hw_spinlock_t; + +struct rt_spinlock +{ + rt_hw_spinlock_t lock; +}; + +void rt_hw_spin_lock_init(rt_hw_spinlock_t *lock); +void rt_hw_spin_lock(rt_hw_spinlock_t *lock); +void rt_hw_spin_unlock(rt_hw_spinlock_t *lock); + +int rt_hw_cpu_id(void); + +extern rt_hw_spinlock_t _cpus_lock; +extern rt_hw_spinlock_t _rt_critical_lock; + +#define __RT_HW_SPIN_LOCK_INITIALIZER(lockname) {0} + +#define __RT_HW_SPIN_LOCK_UNLOCKED(lockname) \ + (rt_hw_spinlock_t) __RT_HW_SPIN_LOCK_INITIALIZER(lockname) + +#define RT_DEFINE_SPINLOCK(x) rt_hw_spinlock_t x = __RT_HW_SPIN_LOCK_UNLOCKED(x) +#define RT_DECLARE_SPINLOCK(x) + +/** + * ipi function + */ +void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask); + +/** + * boot secondary cpu + */ +void rt_hw_secondary_cpu_up(void); + +/** + * secondary cpu idle function + */ +void rt_hw_secondary_cpu_idle_exec(void); +#else + +#define RT_DEFINE_SPINLOCK(x) +#define RT_DECLARE_SPINLOCK(x) rt_ubase_t x + +#define rt_hw_spin_lock(lock) *(lock) = rt_hw_interrupt_disable() +#define rt_hw_spin_unlock(lock) rt_hw_interrupt_enable(*(lock)) + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/rt-thread/include/rtlibc.h b/components/rt-thread/include/rtlibc.h new file mode 100644 index 00000000..4b145b1b --- /dev/null +++ b/components/rt-thread/include/rtlibc.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017-01-21 Bernard the first version + */ + +#ifndef RTLIBC_H__ +#define RTLIBC_H__ + +/* definitions for libc if toolchain has no these definitions */ +#include "libc/libc_stat.h" +#include "libc/libc_fcntl.h" +#include "libc/libc_dirent.h" + +#ifndef RT_USING_LIBC +#if defined(__ARMCC_VERSION) || defined(__IAR_SYSTEMS_ICC__) +typedef signed long off_t; +typedef int mode_t; +#endif +#endif + +#if defined(__MINGW32__) || defined(_WIN32) +typedef signed long off_t; +typedef int mode_t; +#endif + +#endif + diff --git a/components/rt-thread/include/rtm.h b/components/rt-thread/include/rtm.h new file mode 100644 index 00000000..ec056624 --- /dev/null +++ b/components/rt-thread/include/rtm.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __RTM_H__ +#define __RTM_H__ + +#include +#include + +#ifdef RT_USING_MODULE +struct rt_module_symtab +{ + void *addr; + const char *name; +}; + +#if defined(_MSC_VER) +#pragma section("RTMSymTab$f",read) +#define RTM_EXPORT(symbol) \ +__declspec(allocate("RTMSymTab$f"))const char __rtmsym_##symbol##_name[] = "__vs_rtm_"#symbol; +#pragma comment(linker, "/merge:RTMSymTab=mytext") + +#elif defined(__MINGW32__) +#define RTM_EXPORT(symbol) + +#else +#define RTM_EXPORT(symbol) \ +const char __rtmsym_##symbol##_name[] RT_SECTION(".rodata.name") = #symbol; \ +const struct rt_module_symtab __rtmsym_##symbol RT_SECTION("RTMSymTab")= \ +{ \ + (void *)&symbol, \ + __rtmsym_##symbol##_name \ +}; +#endif + +#else +#define RTM_EXPORT(symbol) +#endif + +#endif diff --git a/components/rt-thread/include/rtservice.h b/components/rt-thread/include/rtservice.h new file mode 100644 index 00000000..d0bad85d --- /dev/null +++ b/components/rt-thread/include/rtservice.h @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-03-16 Bernard the first version + * 2006-09-07 Bernard move the kservice APIs to rtthread.h + * 2007-06-27 Bernard fix the rt_list_remove bug + * 2012-03-22 Bernard rename kservice.h to rtservice.h + * 2017-11-15 JasonJia Modify rt_slist_foreach to rt_slist_for_each_entry. + * Make code cleanup. + */ + +#ifndef __RT_SERVICE_H__ +#define __RT_SERVICE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup KernelService + */ + +/**@{*/ + +/** + * rt_container_of - return the member address of ptr, if the type of ptr is the + * struct type. + */ +#define rt_container_of(ptr, type, member) \ + ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member))) + + +/** + * @brief initialize a list object + */ +#define RT_LIST_OBJECT_INIT(object) { &(object), &(object) } + +/** + * @brief initialize a list + * + * @param l list to be initialized + */ +rt_inline void rt_list_init(rt_list_t *l) +{ + l->next = l->prev = l; +} + +/** + * @brief insert a node after a list + * + * @param l list to insert it + * @param n new node to be inserted + */ +rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n) +{ + l->next->prev = n; + n->next = l->next; + + l->next = n; + n->prev = l; +} + +/** + * @brief insert a node before a list + * + * @param n new node to be inserted + * @param l list to insert it + */ +rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n) +{ + l->prev->next = n; + n->prev = l->prev; + + l->prev = n; + n->next = l; +} + +/** + * @brief remove node from list. + * @param n the node to remove from the list. + */ +rt_inline void rt_list_remove(rt_list_t *n) +{ + n->next->prev = n->prev; + n->prev->next = n->next; + + n->next = n->prev = n; +} + +/** + * @brief tests whether a list is empty + * @param l the list to test. + */ +rt_inline int rt_list_isempty(const rt_list_t *l) +{ + return l->next == l; +} + +/** + * @brief get the list length + * @param l the list to get. + */ +rt_inline unsigned int rt_list_len(const rt_list_t *l) +{ + unsigned int len = 0; + const rt_list_t *p = l; + while (p->next != l) + { + p = p->next; + len ++; + } + + return len; +} + +/** + * @brief get the struct for this entry + * @param node the entry point + * @param type the type of structure + * @param member the name of list in structure + */ +#define rt_list_entry(node, type, member) \ + rt_container_of(node, type, member) + +/** + * rt_list_for_each - iterate over a list + * @pos: the rt_list_t * to use as a loop cursor. + * @head: the head for your list. + */ +#define rt_list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * rt_list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the rt_list_t * to use as a loop cursor. + * @n: another rt_list_t * to use as temporary storage + * @head: the head for your list. + */ +#define rt_list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * rt_list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define rt_list_for_each_entry(pos, head, member) \ + for (pos = rt_list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = rt_list_entry(pos->member.next, typeof(*pos), member)) + +/** + * rt_list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define rt_list_for_each_entry_safe(pos, n, head, member) \ + for (pos = rt_list_entry((head)->next, typeof(*pos), member), \ + n = rt_list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = rt_list_entry(n->member.next, typeof(*n), member)) + +/** + * rt_list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define rt_list_first_entry(ptr, type, member) \ + rt_list_entry((ptr)->next, type, member) + +#define RT_SLIST_OBJECT_INIT(object) { RT_NULL } + +/** + * @brief initialize a single list + * + * @param l the single list to be initialized + */ +rt_inline void rt_slist_init(rt_slist_t *l) +{ + l->next = RT_NULL; +} + +rt_inline void rt_slist_append(rt_slist_t *l, rt_slist_t *n) +{ + struct rt_slist_node *node; + + node = l; + while (node->next) node = node->next; + + /* append the node to the tail */ + node->next = n; + n->next = RT_NULL; +} + +rt_inline void rt_slist_insert(rt_slist_t *l, rt_slist_t *n) +{ + n->next = l->next; + l->next = n; +} + +rt_inline unsigned int rt_slist_len(const rt_slist_t *l) +{ + unsigned int len = 0; + const rt_slist_t *list = l->next; + while (list != RT_NULL) + { + list = list->next; + len ++; + } + + return len; +} + +rt_inline rt_slist_t *rt_slist_remove(rt_slist_t *l, rt_slist_t *n) +{ + /* remove slist head */ + struct rt_slist_node *node = l; + while (node->next && node->next != n) node = node->next; + + /* remove node */ + if (node->next != (rt_slist_t *)0) node->next = node->next->next; + + return l; +} + +rt_inline rt_slist_t *rt_slist_first(rt_slist_t *l) +{ + return l->next; +} + +rt_inline rt_slist_t *rt_slist_tail(rt_slist_t *l) +{ + while (l->next) l = l->next; + + return l; +} + +rt_inline rt_slist_t *rt_slist_next(rt_slist_t *n) +{ + return n->next; +} + +rt_inline int rt_slist_isempty(rt_slist_t *l) +{ + return l->next == RT_NULL; +} + +/** + * @brief get the struct for this single list node + * @param node the entry point + * @param type the type of structure + * @param member the name of list in structure + */ +#define rt_slist_entry(node, type, member) \ + rt_container_of(node, type, member) + +/** + * rt_slist_for_each - iterate over a single list + * @pos: the rt_slist_t * to use as a loop cursor. + * @head: the head for your single list. + */ +#define rt_slist_for_each(pos, head) \ + for (pos = (head)->next; pos != RT_NULL; pos = pos->next) + +/** + * rt_slist_for_each_entry - iterate over single list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your single list. + * @member: the name of the list_struct within the struct. + */ +#define rt_slist_for_each_entry(pos, head, member) \ + for (pos = rt_slist_entry((head)->next, typeof(*pos), member); \ + &pos->member != (RT_NULL); \ + pos = rt_slist_entry(pos->member.next, typeof(*pos), member)) + +/** + * rt_slist_first_entry - get the first element from a slist + * @ptr: the slist head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the slist_struct within the struct. + * + * Note, that slist is expected to be not empty. + */ +#define rt_slist_first_entry(ptr, type, member) \ + rt_slist_entry((ptr)->next, type, member) + +/** + * rt_slist_tail_entry - get the tail element from a slist + * @ptr: the slist head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the slist_struct within the struct. + * + * Note, that slist is expected to be not empty. + */ +#define rt_slist_tail_entry(ptr, type, member) \ + rt_slist_entry(rt_slist_tail(ptr), type, member) + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/rt-thread/include/rtthread.h b/components/rt-thread/include/rtthread.h new file mode 100644 index 00000000..cf47a48e --- /dev/null +++ b/components/rt-thread/include/rtthread.h @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-03-18 Bernard the first version + * 2006-04-26 Bernard add semaphore APIs + * 2006-08-10 Bernard add version information + * 2007-01-28 Bernard rename RT_OBJECT_Class_Static to RT_Object_Class_Static + * 2007-03-03 Bernard clean up the definitions to rtdef.h + * 2010-04-11 yi.qiu add module feature + * 2013-06-24 Bernard add rt_kprintf re-define when not use RT_USING_CONSOLE. + * 2016-08-09 ArdaFu add new thread and interrupt hook. + * 2018-11-22 Jesven add all cpu's lock and ipi handler + * 2021-02-28 Meco Man add RT_KSERVICE_USING_STDLIB + */ + +#ifndef __RT_THREAD_H__ +#define __RT_THREAD_H__ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup KernelObject + */ + +/**@{*/ + +/* + * kernel object interface + */ +struct rt_object_information * +rt_object_get_information(enum rt_object_class_type type); +int rt_object_get_length(enum rt_object_class_type type); +int rt_object_get_pointers(enum rt_object_class_type type, rt_object_t *pointers, int maxlen); + +void rt_object_init(struct rt_object *object, + enum rt_object_class_type type, + const char *name); +void rt_object_detach(rt_object_t object); +rt_object_t rt_object_allocate(enum rt_object_class_type type, + const char *name); +void rt_object_delete(rt_object_t object); +rt_bool_t rt_object_is_systemobject(rt_object_t object); +rt_uint8_t rt_object_get_type(rt_object_t object); +rt_object_t rt_object_find(const char *name, rt_uint8_t type); + +#ifdef RT_USING_HOOK +void rt_object_attach_sethook(void (*hook)(struct rt_object *object)); +void rt_object_detach_sethook(void (*hook)(struct rt_object *object)); +void rt_object_trytake_sethook(void (*hook)(struct rt_object *object)); +void rt_object_take_sethook(void (*hook)(struct rt_object *object)); +void rt_object_put_sethook(void (*hook)(struct rt_object *object)); +#endif + +/**@}*/ + +/** + * @addtogroup Clock + */ + +/**@{*/ + +/* + * clock & timer interface + */ +rt_tick_t rt_tick_get(void); +void rt_tick_set(rt_tick_t tick); +void rt_tick_increase(void); +rt_tick_t rt_tick_from_millisecond(rt_int32_t ms); +rt_tick_t rt_tick_get_millisecond(void); + +void rt_system_timer_init(void); +void rt_system_timer_thread_init(void); + +void rt_timer_init(rt_timer_t timer, + const char *name, + void (*timeout)(void *parameter), + void *parameter, + rt_tick_t time, + rt_uint8_t flag); +rt_err_t rt_timer_detach(rt_timer_t timer); +rt_timer_t rt_timer_create(const char *name, + void (*timeout)(void *parameter), + void *parameter, + rt_tick_t time, + rt_uint8_t flag); +rt_err_t rt_timer_delete(rt_timer_t timer); +rt_err_t rt_timer_start(rt_timer_t timer); +rt_err_t rt_timer_stop(rt_timer_t timer); +rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg); + +rt_tick_t rt_timer_next_timeout_tick(void); +void rt_timer_check(void); + +#ifdef RT_USING_HOOK +void rt_timer_enter_sethook(void (*hook)(struct rt_timer *timer)); +void rt_timer_exit_sethook(void (*hook)(struct rt_timer *timer)); +#endif + +/**@}*/ + +/** + * @addtogroup Thread + */ + +/**@{*/ + +/* + * thread interface + */ +rt_err_t rt_thread_init(struct rt_thread *thread, + const char *name, + void (*entry)(void *parameter), + void *parameter, + void *stack_start, + rt_uint32_t stack_size, + rt_uint8_t priority, + rt_uint32_t tick); +rt_err_t rt_thread_detach(rt_thread_t thread); +rt_thread_t rt_thread_create(const char *name, + void (*entry)(void *parameter), + void *parameter, + rt_uint32_t stack_size, + rt_uint8_t priority, + rt_uint32_t tick); +rt_thread_t rt_thread_self(void); +rt_thread_t rt_thread_find(char *name); +rt_err_t rt_thread_startup(rt_thread_t thread); +rt_err_t rt_thread_delete(rt_thread_t thread); + +rt_err_t rt_thread_yield(void); +rt_err_t rt_thread_delay(rt_tick_t tick); +rt_err_t rt_thread_delay_until(rt_tick_t *tick, rt_tick_t inc_tick); +rt_err_t rt_thread_mdelay(rt_int32_t ms); +rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg); +rt_err_t rt_thread_suspend(rt_thread_t thread); +rt_err_t rt_thread_resume(rt_thread_t thread); +void rt_thread_timeout(void *parameter); + +#ifdef RT_USING_SIGNALS +void rt_thread_alloc_sig(rt_thread_t tid); +void rt_thread_free_sig(rt_thread_t tid); +int rt_thread_kill(rt_thread_t tid, int sig); +#endif + +#ifdef RT_USING_HOOK +void rt_thread_suspend_sethook(void (*hook)(rt_thread_t thread)); +void rt_thread_resume_sethook (void (*hook)(rt_thread_t thread)); +void rt_thread_inited_sethook (void (*hook)(rt_thread_t thread)); +#endif + +/* + * idle thread interface + */ +void rt_thread_idle_init(void); +#if defined(RT_USING_HOOK) || defined(RT_USING_IDLE_HOOK) +rt_err_t rt_thread_idle_sethook(void (*hook)(void)); +rt_err_t rt_thread_idle_delhook(void (*hook)(void)); +#endif +rt_thread_t rt_thread_idle_gethandler(void); + +/* + * schedule service + */ +void rt_system_scheduler_init(void); +void rt_system_scheduler_start(void); + +void rt_schedule(void); +void rt_schedule_insert_thread(struct rt_thread *thread); +void rt_schedule_remove_thread(struct rt_thread *thread); + +void rt_enter_critical(void); +void rt_exit_critical(void); +rt_uint16_t rt_critical_level(void); + +#ifdef RT_USING_HOOK +void rt_scheduler_sethook(void (*hook)(rt_thread_t from, rt_thread_t to)); +void rt_scheduler_switch_sethook(void (*hook)(struct rt_thread *tid)); +#endif + +#ifdef RT_USING_SMP +void rt_scheduler_ipi_handler(int vector, void *param); +#endif + +/**@}*/ + +/** + * @addtogroup Signals + * @{ + */ +#ifdef RT_USING_SIGNALS +void rt_signal_mask(int signo); +void rt_signal_unmask(int signo); +rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler); +int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout); + +int rt_system_signal_init(void); +#endif +/*@}*/ + +/** + * @addtogroup MM + */ + +/**@{*/ + +/* + * memory management interface + */ +#ifdef RT_USING_MEMPOOL +/* + * memory pool interface + */ +rt_err_t rt_mp_init(struct rt_mempool *mp, + const char *name, + void *start, + rt_size_t size, + rt_size_t block_size); +rt_err_t rt_mp_detach(struct rt_mempool *mp); +rt_mp_t rt_mp_create(const char *name, + rt_size_t block_count, + rt_size_t block_size); +rt_err_t rt_mp_delete(rt_mp_t mp); + +void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time); +void rt_mp_free(void *block); + +#ifdef RT_USING_HOOK +void rt_mp_alloc_sethook(void (*hook)(struct rt_mempool *mp, void *block)); +void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block)); +#endif + +#endif + +#ifdef RT_USING_HEAP +/* + * heap memory interface + */ +void rt_system_heap_init(void *begin_addr, void *end_addr); + +void *rt_malloc(rt_size_t nbytes); +void rt_free(void *ptr); +void *rt_realloc(void *ptr, rt_size_t nbytes); +void *rt_calloc(rt_size_t count, rt_size_t size); +void *rt_malloc_align(rt_size_t size, rt_size_t align); +void rt_free_align(void *ptr); + +void rt_memory_info(rt_uint32_t *total, + rt_uint32_t *used, + rt_uint32_t *max_used); + +#ifdef RT_USING_SLAB +void *rt_page_alloc(rt_size_t npages); +void rt_page_free(void *addr, rt_size_t npages); +#endif + +#ifdef RT_USING_HOOK +void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size)); +void rt_free_sethook(void (*hook)(void *ptr)); +#endif + +#endif + +#ifdef RT_USING_MEMHEAP +/** + * memory heap object interface + */ +rt_err_t rt_memheap_init(struct rt_memheap *memheap, + const char *name, + void *start_addr, + rt_size_t size); +rt_err_t rt_memheap_detach(struct rt_memheap *heap); +void *rt_memheap_alloc(struct rt_memheap *heap, rt_size_t size); +void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize); +void rt_memheap_free(void *ptr); +#endif + +/**@}*/ + +/** + * @addtogroup IPC + */ + +/**@{*/ + +#ifdef RT_USING_SEMAPHORE +/* + * semaphore interface + */ +rt_err_t rt_sem_init(rt_sem_t sem, + const char *name, + rt_uint32_t value, + rt_uint8_t flag); +rt_err_t rt_sem_detach(rt_sem_t sem); +rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag); +rt_err_t rt_sem_delete(rt_sem_t sem); + +rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time); +rt_err_t rt_sem_trytake(rt_sem_t sem); +rt_err_t rt_sem_release(rt_sem_t sem); +rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg); +#endif + +#ifdef RT_USING_MUTEX +/* + * mutex interface + */ +rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag); +rt_err_t rt_mutex_detach(rt_mutex_t mutex); +rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag); +rt_err_t rt_mutex_delete(rt_mutex_t mutex); + +rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time); +rt_err_t rt_mutex_trytake(rt_mutex_t mutex); +rt_err_t rt_mutex_release(rt_mutex_t mutex); +rt_err_t rt_mutex_control(rt_mutex_t mutex, int cmd, void *arg); +#endif + +#ifdef RT_USING_EVENT +/* + * event interface + */ +rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag); +rt_err_t rt_event_detach(rt_event_t event); +rt_event_t rt_event_create(const char *name, rt_uint8_t flag); +rt_err_t rt_event_delete(rt_event_t event); + +rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set); +rt_err_t rt_event_recv(rt_event_t event, + rt_uint32_t set, + rt_uint8_t opt, + rt_int32_t timeout, + rt_uint32_t *recved); +rt_err_t rt_event_control(rt_event_t event, int cmd, void *arg); +#endif + +#ifdef RT_USING_MAILBOX +/* + * mailbox interface + */ +rt_err_t rt_mb_init(rt_mailbox_t mb, + const char *name, + void *msgpool, + rt_size_t size, + rt_uint8_t flag); +rt_err_t rt_mb_detach(rt_mailbox_t mb); +rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag); +rt_err_t rt_mb_delete(rt_mailbox_t mb); + +rt_err_t rt_mb_send(rt_mailbox_t mb, rt_ubase_t value); +rt_err_t rt_mb_send_wait(rt_mailbox_t mb, + rt_ubase_t value, + rt_int32_t timeout); +rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout); +rt_err_t rt_mb_control(rt_mailbox_t mb, int cmd, void *arg); +#endif + +#ifdef RT_USING_MESSAGEQUEUE +/* + * message queue interface + */ +rt_err_t rt_mq_init(rt_mq_t mq, + const char *name, + void *msgpool, + rt_size_t msg_size, + rt_size_t pool_size, + rt_uint8_t flag); +rt_err_t rt_mq_detach(rt_mq_t mq); +rt_mq_t rt_mq_create(const char *name, + rt_size_t msg_size, + rt_size_t max_msgs, + rt_uint8_t flag); +rt_err_t rt_mq_delete(rt_mq_t mq); + +rt_err_t rt_mq_send(rt_mq_t mq, const void *buffer, rt_size_t size); +rt_err_t rt_mq_send_wait(rt_mq_t mq, + const void *buffer, + rt_size_t size, + rt_int32_t timeout); +rt_err_t rt_mq_urgent(rt_mq_t mq, const void *buffer, rt_size_t size); +rt_err_t rt_mq_recv(rt_mq_t mq, + void *buffer, + rt_size_t size, + rt_int32_t timeout); +rt_err_t rt_mq_control(rt_mq_t mq, int cmd, void *arg); +#endif + +/* defunct */ +void rt_thread_defunct_enqueue(rt_thread_t thread); +rt_thread_t rt_thread_defunct_dequeue(void); + +/* + * spinlock + */ +#ifdef RT_USING_SMP +struct rt_spinlock; + +void rt_spin_lock_init(struct rt_spinlock *lock); +void rt_spin_lock(struct rt_spinlock *lock); +void rt_spin_unlock(struct rt_spinlock *lock); +rt_base_t rt_spin_lock_irqsave(struct rt_spinlock *lock); +void rt_spin_unlock_irqrestore(struct rt_spinlock *lock, rt_base_t level); + +#else +#define rt_spin_lock_init(lock) /* nothing */ +#define rt_spin_lock(lock) rt_enter_critical() +#define rt_spin_unlock(lock) rt_exit_critical() +#define rt_spin_lock_irqsave(lock) rt_hw_interrupt_disable() +#define rt_spin_unlock_irqrestore(lock, level) rt_hw_interrupt_enable(level) + +#endif + +/**@}*/ + +#ifdef RT_USING_DEVICE +/** + * @addtogroup Device + */ + +/**@{*/ + +/* + * device (I/O) system interface + */ +rt_device_t rt_device_find(const char *name); + +rt_err_t rt_device_register(rt_device_t dev, + const char *name, + rt_uint16_t flags); +rt_err_t rt_device_unregister(rt_device_t dev); + +rt_device_t rt_device_create(int type, int attach_size); +void rt_device_destroy(rt_device_t device); + +rt_err_t +rt_device_set_rx_indicate(rt_device_t dev, + rt_err_t (*rx_ind)(rt_device_t dev, rt_size_t size)); +rt_err_t +rt_device_set_tx_complete(rt_device_t dev, + rt_err_t (*tx_done)(rt_device_t dev, void *buffer)); + +rt_err_t rt_device_init (rt_device_t dev); +rt_err_t rt_device_open (rt_device_t dev, rt_uint16_t oflag); +rt_err_t rt_device_close(rt_device_t dev); +rt_size_t rt_device_read (rt_device_t dev, + rt_off_t pos, + void *buffer, + rt_size_t size); +rt_size_t rt_device_write(rt_device_t dev, + rt_off_t pos, + const void *buffer, + rt_size_t size); +rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg); + +/**@}*/ +#endif + +/* + * interrupt service + */ + +/* + * rt_interrupt_enter and rt_interrupt_leave only can be called by BSP + */ +void rt_interrupt_enter(void); +void rt_interrupt_leave(void); + +#ifdef RT_USING_SMP + +/* + * smp cpus lock service + */ + +rt_base_t rt_cpus_lock(void); +void rt_cpus_unlock(rt_base_t level); + +struct rt_cpu *rt_cpu_self(void); +struct rt_cpu *rt_cpu_index(int index); + +#endif + +/* + * the number of nested interrupts. + */ +rt_uint8_t rt_interrupt_get_nest(void); + +#ifdef RT_USING_HOOK +void rt_interrupt_enter_sethook(void (*hook)(void)); +void rt_interrupt_leave_sethook(void (*hook)(void)); +#endif + +#ifdef RT_USING_COMPONENTS_INIT +void rt_components_init(void); +void rt_components_board_init(void); +#endif + +/** + * @addtogroup KernelService + */ + +/**@{*/ + +/* + * general kernel service + */ +#ifndef RT_USING_CONSOLE +#define rt_kprintf(...) +#define rt_kputs(str) +#else +void rt_kprintf(const char *fmt, ...); +void rt_kputs(const char *str); +#endif + +rt_int32_t rt_vsprintf(char *dest, const char *format, va_list arg_ptr); +rt_int32_t rt_vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args); +rt_int32_t rt_sprintf(char *buf, const char *format, ...); +rt_int32_t rt_snprintf(char *buf, rt_size_t size, const char *format, ...); + +#if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE) +rt_device_t rt_console_set_device(const char *name); +rt_device_t rt_console_get_device(void); +#endif + +rt_err_t rt_get_errno(void); +void rt_set_errno(rt_err_t no); +int *_rt_errno(void); +#if !defined(RT_USING_NEWLIB) && !defined(_WIN32) +#ifndef errno +#define errno *_rt_errno() +#endif +#endif + +int __rt_ffs(int value); + +void *rt_memset(void *src, int c, rt_ubase_t n); +void *rt_memcpy(void *dest, const void *src, rt_ubase_t n); + +#ifndef RT_KSERVICE_USING_STDLIB +void *rt_memmove(void *dest, const void *src, rt_ubase_t n); +rt_int32_t rt_memcmp(const void *cs, const void *ct, rt_ubase_t count); +char *rt_strstr(const char *str1, const char *str2); +rt_int32_t rt_strcasecmp(const char *a, const char *b); +char *rt_strncpy(char *dest, const char *src, rt_ubase_t n); +rt_int32_t rt_strncmp(const char *cs, const char *ct, rt_ubase_t count); +rt_int32_t rt_strcmp(const char *cs, const char *ct); +rt_size_t rt_strnlen(const char *s, rt_ubase_t maxlen); +rt_size_t rt_strlen(const char *src); +#else +#include +#define rt_memmove(dest, src, n) memmove(dest, src, n) +#define rt_memcmp(cs, ct, count) memcmp(cs, ct, count) +#define rt_strstr(str1, str2) strstr(str1, str2) +#define rt_strcasecmp(a, b) strcasecmp(a, b) +#define rt_strncpy(dest, src, n) strncpy(dest, src, n) +#define rt_strncmp(cs, ct, count) strncmp(cs, ct, count) +#define rt_strcmp(cs, ct) strcmp(cs, ct) +#define rt_strnlen(s, maxlen) strnlen(s, maxlen) +#define rt_strlen(src) strlen(src) +#endif /*RT_KSERVICE_USING_STDLIB*/ + +char *rt_strdup(const char *s); +#ifdef __ARMCC_VERSION +/* lack strdup interface */ +char* strdup(const char* str); +#endif + +void rt_show_version(void); + +#ifdef RT_DEBUG +extern void (*rt_assert_hook)(const char *ex, const char *func, rt_size_t line); +void rt_assert_set_hook(void (*hook)(const char *ex, const char *func, rt_size_t line)); + +void rt_assert_handler(const char *ex, const char *func, rt_size_t line); +#endif /* RT_DEBUG */ + +#ifdef RT_USING_FINSH +#include +#endif + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/rt-thread/libcpu/risc-v/common/context_gcc.S b/components/rt-thread/libcpu/risc-v/common/context_gcc.S new file mode 100644 index 00000000..e5fc8536 --- /dev/null +++ b/components/rt-thread/libcpu/risc-v/common/context_gcc.S @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018/10/28 Bernard The unify RISC-V porting implementation + * 2018/12/27 Jesven Add SMP support + * 2020/11/20 BalanceTWK Add FPU support + */ + +#include "cpuport.h" + +#ifdef RT_USING_SMP +#define rt_hw_interrupt_disable rt_hw_local_irq_disable +#define rt_hw_interrupt_enable rt_hw_local_irq_enable +#endif + +/* + * rt_base_t rt_hw_interrupt_disable(void); + */ + .globl rt_hw_interrupt_disable +rt_hw_interrupt_disable: + csrrci a0, mstatus, 8 + ret + +/* + * void rt_hw_interrupt_enable(rt_base_t level); + */ + .globl rt_hw_interrupt_enable +rt_hw_interrupt_enable: + csrw mstatus, a0 + ret + +/* + * #ifdef RT_USING_SMP + * void rt_hw_context_switch_to(rt_ubase_t to, stuct rt_thread *to_thread); + * #else + * void rt_hw_context_switch_to(rt_ubase_t to); + * #endif + * a0 --> to + * a1 --> to_thread + */ + .globl rt_hw_context_switch_to +rt_hw_context_switch_to: + LOAD sp, (a0) + +#ifdef RT_USING_SMP + mv a0, a1 + call rt_cpus_lock_status_restore +#endif + LOAD a0, 2 * REGBYTES(sp) + csrw mstatus, a0 + j rt_hw_context_switch_exit + +/* + * #ifdef RT_USING_SMP + * void rt_hw_context_switch(rt_ubase_t from, rt_ubase_t to, struct rt_thread *to_thread); + * #else + * void rt_hw_context_switch(rt_ubase_t from, rt_ubase_t to); + * #endif + * + * a0 --> from + * a1 --> to + * a2 --> to_thread + */ + .globl rt_hw_context_switch +rt_hw_context_switch: + /* saved from thread context + * x1/ra -> sp(0) + * x1/ra -> sp(1) + * mstatus.mie -> sp(2) + * x(i) -> sp(i-4) + */ +#ifdef ARCH_RISCV_FPU + addi sp, sp, -32 * FREGBYTES + + FSTORE f0, 0 * FREGBYTES(sp) + FSTORE f1, 1 * FREGBYTES(sp) + FSTORE f2, 2 * FREGBYTES(sp) + FSTORE f3, 3 * FREGBYTES(sp) + FSTORE f4, 4 * FREGBYTES(sp) + FSTORE f5, 5 * FREGBYTES(sp) + FSTORE f6, 6 * FREGBYTES(sp) + FSTORE f7, 7 * FREGBYTES(sp) + FSTORE f8, 8 * FREGBYTES(sp) + FSTORE f9, 9 * FREGBYTES(sp) + FSTORE f10, 10 * FREGBYTES(sp) + FSTORE f11, 11 * FREGBYTES(sp) + FSTORE f12, 12 * FREGBYTES(sp) + FSTORE f13, 13 * FREGBYTES(sp) + FSTORE f14, 14 * FREGBYTES(sp) + FSTORE f15, 15 * FREGBYTES(sp) + FSTORE f16, 16 * FREGBYTES(sp) + FSTORE f17, 17 * FREGBYTES(sp) + FSTORE f18, 18 * FREGBYTES(sp) + FSTORE f19, 19 * FREGBYTES(sp) + FSTORE f20, 20 * FREGBYTES(sp) + FSTORE f21, 21 * FREGBYTES(sp) + FSTORE f22, 22 * FREGBYTES(sp) + FSTORE f23, 23 * FREGBYTES(sp) + FSTORE f24, 24 * FREGBYTES(sp) + FSTORE f25, 25 * FREGBYTES(sp) + FSTORE f26, 26 * FREGBYTES(sp) + FSTORE f27, 27 * FREGBYTES(sp) + FSTORE f28, 28 * FREGBYTES(sp) + FSTORE f29, 29 * FREGBYTES(sp) + FSTORE f30, 30 * FREGBYTES(sp) + FSTORE f31, 31 * FREGBYTES(sp) + +#endif + addi sp, sp, -32 * REGBYTES + STORE sp, (a0) + + STORE x1, 0 * REGBYTES(sp) + STORE x1, 1 * REGBYTES(sp) + + csrr a0, mstatus + andi a0, a0, 8 + beqz a0, save_mpie + li a0, 0x80 +save_mpie: + STORE a0, 2 * REGBYTES(sp) + + STORE x4, 4 * REGBYTES(sp) + STORE x5, 5 * REGBYTES(sp) + STORE x6, 6 * REGBYTES(sp) + STORE x7, 7 * REGBYTES(sp) + STORE x8, 8 * REGBYTES(sp) + STORE x9, 9 * REGBYTES(sp) + STORE x10, 10 * REGBYTES(sp) + STORE x11, 11 * REGBYTES(sp) + STORE x12, 12 * REGBYTES(sp) + STORE x13, 13 * REGBYTES(sp) + STORE x14, 14 * REGBYTES(sp) + STORE x15, 15 * REGBYTES(sp) + STORE x16, 16 * REGBYTES(sp) + STORE x17, 17 * REGBYTES(sp) + STORE x18, 18 * REGBYTES(sp) + STORE x19, 19 * REGBYTES(sp) + STORE x20, 20 * REGBYTES(sp) + STORE x21, 21 * REGBYTES(sp) + STORE x22, 22 * REGBYTES(sp) + STORE x23, 23 * REGBYTES(sp) + STORE x24, 24 * REGBYTES(sp) + STORE x25, 25 * REGBYTES(sp) + STORE x26, 26 * REGBYTES(sp) + STORE x27, 27 * REGBYTES(sp) + STORE x28, 28 * REGBYTES(sp) + STORE x29, 29 * REGBYTES(sp) + STORE x30, 30 * REGBYTES(sp) + STORE x31, 31 * REGBYTES(sp) + + /* restore to thread context + * sp(0) -> epc; + * sp(1) -> ra; + * sp(i) -> x(i+2) + */ + LOAD sp, (a1) + +#ifdef RT_USING_SMP + mv a0, a2 + call rt_cpus_lock_status_restore +#endif /*RT_USING_SMP*/ + + j rt_hw_context_switch_exit + +#ifdef RT_USING_SMP +/* + * void rt_hw_context_switch_interrupt(void *context, rt_ubase_t from, rt_ubase_t to, struct rt_thread *to_thread); + * + * a0 --> context + * a1 --> from + * a2 --> to + * a3 --> to_thread + */ + .globl rt_hw_context_switch_interrupt +rt_hw_context_switch_interrupt: + + STORE a0, 0(a1) + + LOAD sp, 0(a2) + move a0, a3 + call rt_cpus_lock_status_restore + + j rt_hw_context_switch_exit + +#endif + +.global rt_hw_context_switch_exit +rt_hw_context_switch_exit: +#ifdef RT_USING_SMP +#ifdef RT_USING_SIGNALS + mv a0, sp + + csrr t0, mhartid + /* switch interrupt stack of current cpu */ + la sp, __stack_start__ + addi t1, t0, 1 + li t2, __STACKSIZE__ + mul t1, t1, t2 + add sp, sp, t1 /* sp = (cpuid + 1) * __STACKSIZE__ + __stack_start__ */ + + call rt_signal_check + mv sp, a0 +#endif +#endif + /* resw ra to mepc */ + LOAD a0, 0 * REGBYTES(sp) + csrw mepc, a0 + + LOAD x1, 1 * REGBYTES(sp) + + li t0, 0x00007800 + csrw mstatus, t0 + LOAD a0, 2 * REGBYTES(sp) + csrs mstatus, a0 + + LOAD x4, 4 * REGBYTES(sp) + LOAD x5, 5 * REGBYTES(sp) + LOAD x6, 6 * REGBYTES(sp) + LOAD x7, 7 * REGBYTES(sp) + LOAD x8, 8 * REGBYTES(sp) + LOAD x9, 9 * REGBYTES(sp) + LOAD x10, 10 * REGBYTES(sp) + LOAD x11, 11 * REGBYTES(sp) + LOAD x12, 12 * REGBYTES(sp) + LOAD x13, 13 * REGBYTES(sp) + LOAD x14, 14 * REGBYTES(sp) + LOAD x15, 15 * REGBYTES(sp) + LOAD x16, 16 * REGBYTES(sp) + LOAD x17, 17 * REGBYTES(sp) + LOAD x18, 18 * REGBYTES(sp) + LOAD x19, 19 * REGBYTES(sp) + LOAD x20, 20 * REGBYTES(sp) + LOAD x21, 21 * REGBYTES(sp) + LOAD x22, 22 * REGBYTES(sp) + LOAD x23, 23 * REGBYTES(sp) + LOAD x24, 24 * REGBYTES(sp) + LOAD x25, 25 * REGBYTES(sp) + LOAD x26, 26 * REGBYTES(sp) + LOAD x27, 27 * REGBYTES(sp) + LOAD x28, 28 * REGBYTES(sp) + LOAD x29, 29 * REGBYTES(sp) + LOAD x30, 30 * REGBYTES(sp) + LOAD x31, 31 * REGBYTES(sp) + + addi sp, sp, 32 * REGBYTES + +#ifdef ARCH_RISCV_FPU + FLOAD f0, 0 * FREGBYTES(sp) + FLOAD f1, 1 * FREGBYTES(sp) + FLOAD f2, 2 * FREGBYTES(sp) + FLOAD f3, 3 * FREGBYTES(sp) + FLOAD f4, 4 * FREGBYTES(sp) + FLOAD f5, 5 * FREGBYTES(sp) + FLOAD f6, 6 * FREGBYTES(sp) + FLOAD f7, 7 * FREGBYTES(sp) + FLOAD f8, 8 * FREGBYTES(sp) + FLOAD f9, 9 * FREGBYTES(sp) + FLOAD f10, 10 * FREGBYTES(sp) + FLOAD f11, 11 * FREGBYTES(sp) + FLOAD f12, 12 * FREGBYTES(sp) + FLOAD f13, 13 * FREGBYTES(sp) + FLOAD f14, 14 * FREGBYTES(sp) + FLOAD f15, 15 * FREGBYTES(sp) + FLOAD f16, 16 * FREGBYTES(sp) + FLOAD f17, 17 * FREGBYTES(sp) + FLOAD f18, 18 * FREGBYTES(sp) + FLOAD f19, 19 * FREGBYTES(sp) + FLOAD f20, 20 * FREGBYTES(sp) + FLOAD f21, 21 * FREGBYTES(sp) + FLOAD f22, 22 * FREGBYTES(sp) + FLOAD f23, 23 * FREGBYTES(sp) + FLOAD f24, 24 * FREGBYTES(sp) + FLOAD f25, 25 * FREGBYTES(sp) + FLOAD f26, 26 * FREGBYTES(sp) + FLOAD f27, 27 * FREGBYTES(sp) + FLOAD f28, 28 * FREGBYTES(sp) + FLOAD f29, 29 * FREGBYTES(sp) + FLOAD f30, 30 * FREGBYTES(sp) + FLOAD f31, 31 * FREGBYTES(sp) + + addi sp, sp, 32 * FREGBYTES +#endif + + mret diff --git a/components/rt-thread/libcpu/risc-v/common/cpuport.c b/components/rt-thread/libcpu/risc-v/common/cpuport.c new file mode 100644 index 00000000..622e1bd3 --- /dev/null +++ b/components/rt-thread/libcpu/risc-v/common/cpuport.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018/10/28 Bernard The unify RISC-V porting code. + * 2020/11/20 BalanceTWK Add FPU support + */ + +#include +#include + +#include "cpuport.h" + +#ifndef RT_USING_SMP +volatile rt_ubase_t rt_interrupt_from_thread = 0; +volatile rt_ubase_t rt_interrupt_to_thread = 0; +volatile rt_uint32_t rt_thread_switch_interrupt_flag = 0; +#endif + +struct rt_hw_stack_frame +{ + rt_ubase_t epc; /* epc - epc - program counter */ + rt_ubase_t ra; /* x1 - ra - return address for jumps */ + rt_ubase_t mstatus; /* - machine status register */ + rt_ubase_t gp; /* x3 - gp - global pointer */ + rt_ubase_t tp; /* x4 - tp - thread pointer */ + rt_ubase_t t0; /* x5 - t0 - temporary register 0 */ + rt_ubase_t t1; /* x6 - t1 - temporary register 1 */ + rt_ubase_t t2; /* x7 - t2 - temporary register 2 */ + rt_ubase_t s0_fp; /* x8 - s0/fp - saved register 0 or frame pointer */ + rt_ubase_t s1; /* x9 - s1 - saved register 1 */ + rt_ubase_t a0; /* x10 - a0 - return value or function argument 0 */ + rt_ubase_t a1; /* x11 - a1 - return value or function argument 1 */ + rt_ubase_t a2; /* x12 - a2 - function argument 2 */ + rt_ubase_t a3; /* x13 - a3 - function argument 3 */ + rt_ubase_t a4; /* x14 - a4 - function argument 4 */ + rt_ubase_t a5; /* x15 - a5 - function argument 5 */ + rt_ubase_t a6; /* x16 - a6 - function argument 6 */ + rt_ubase_t a7; /* x17 - s7 - function argument 7 */ + rt_ubase_t s2; /* x18 - s2 - saved register 2 */ + rt_ubase_t s3; /* x19 - s3 - saved register 3 */ + rt_ubase_t s4; /* x20 - s4 - saved register 4 */ + rt_ubase_t s5; /* x21 - s5 - saved register 5 */ + rt_ubase_t s6; /* x22 - s6 - saved register 6 */ + rt_ubase_t s7; /* x23 - s7 - saved register 7 */ + rt_ubase_t s8; /* x24 - s8 - saved register 8 */ + rt_ubase_t s9; /* x25 - s9 - saved register 9 */ + rt_ubase_t s10; /* x26 - s10 - saved register 10 */ + rt_ubase_t s11; /* x27 - s11 - saved register 11 */ + rt_ubase_t t3; /* x28 - t3 - temporary register 3 */ + rt_ubase_t t4; /* x29 - t4 - temporary register 4 */ + rt_ubase_t t5; /* x30 - t5 - temporary register 5 */ + rt_ubase_t t6; /* x31 - t6 - temporary register 6 */ +#ifdef ARCH_RISCV_FPU + rv_floatreg_t f0; /* f0 */ + rv_floatreg_t f1; /* f1 */ + rv_floatreg_t f2; /* f2 */ + rv_floatreg_t f3; /* f3 */ + rv_floatreg_t f4; /* f4 */ + rv_floatreg_t f5; /* f5 */ + rv_floatreg_t f6; /* f6 */ + rv_floatreg_t f7; /* f7 */ + rv_floatreg_t f8; /* f8 */ + rv_floatreg_t f9; /* f9 */ + rv_floatreg_t f10; /* f10 */ + rv_floatreg_t f11; /* f11 */ + rv_floatreg_t f12; /* f12 */ + rv_floatreg_t f13; /* f13 */ + rv_floatreg_t f14; /* f14 */ + rv_floatreg_t f15; /* f15 */ + rv_floatreg_t f16; /* f16 */ + rv_floatreg_t f17; /* f17 */ + rv_floatreg_t f18; /* f18 */ + rv_floatreg_t f19; /* f19 */ + rv_floatreg_t f20; /* f20 */ + rv_floatreg_t f21; /* f21 */ + rv_floatreg_t f22; /* f22 */ + rv_floatreg_t f23; /* f23 */ + rv_floatreg_t f24; /* f24 */ + rv_floatreg_t f25; /* f25 */ + rv_floatreg_t f26; /* f26 */ + rv_floatreg_t f27; /* f27 */ + rv_floatreg_t f28; /* f28 */ + rv_floatreg_t f29; /* f29 */ + rv_floatreg_t f30; /* f30 */ + rv_floatreg_t f31; /* f31 */ +#endif +}; + +/** + * This function will initialize thread stack + * + * @param tentry the entry of thread + * @param parameter the parameter of entry + * @param stack_addr the beginning stack address + * @param texit the function will be called when thread exit + * + * @return stack address + */ +rt_uint8_t *rt_hw_stack_init(void *tentry, + void *parameter, + rt_uint8_t *stack_addr, + void *texit) +{ + struct rt_hw_stack_frame *frame; + rt_uint8_t *stk; + int i; + + stk = stack_addr + sizeof(rt_ubase_t); + stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_ubase_t)stk, REGBYTES); + stk -= sizeof(struct rt_hw_stack_frame); + + frame = (struct rt_hw_stack_frame *)stk; + + for (i = 0; i < sizeof(struct rt_hw_stack_frame) / sizeof(rt_ubase_t); i++) + { + ((rt_ubase_t *)frame)[i] = 0xdeadbeef; + } + + frame->ra = (rt_ubase_t)texit; + frame->a0 = (rt_ubase_t)parameter; + frame->epc = (rt_ubase_t)tentry; + + /* force to machine mode(MPP=11) and set MPIE to 1 */ + frame->mstatus = 0x00007880; + + return stk; +} + +/* + * #ifdef RT_USING_SMP + * void rt_hw_context_switch_interrupt(void *context, rt_ubase_t from, rt_ubase_t to, struct rt_thread *to_thread); + * #else + * void rt_hw_context_switch_interrupt(rt_ubase_t from, rt_ubase_t to); + * #endif + */ +#ifndef RT_USING_SMP +void rt_hw_context_switch_interrupt(rt_ubase_t from, rt_ubase_t to) +{ + if (rt_thread_switch_interrupt_flag == 0) + rt_interrupt_from_thread = from; + + rt_interrupt_to_thread = to; + rt_thread_switch_interrupt_flag = 1; + + return ; +} +#endif /* end of RT_USING_SMP */ + +/** shutdown CPU */ +RT_WEAK void rt_hw_cpu_shutdown() +{ + rt_uint32_t level; + rt_kprintf("shutdown...\n"); + + level = rt_hw_interrupt_disable(); + while (level) + { + RT_ASSERT(0); + } +} diff --git a/components/rt-thread/libcpu/risc-v/common/cpuport.h b/components/rt-thread/libcpu/risc-v/common/cpuport.h new file mode 100644 index 00000000..e625d9f9 --- /dev/null +++ b/components/rt-thread/libcpu/risc-v/common/cpuport.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-10-03 Bernard The first version + * 2020/11/20 BalanceTWK Add FPU support + */ + +#ifndef CPUPORT_H__ +#define CPUPORT_H__ + +#include + +/* bytes of register width */ +#ifdef ARCH_CPU_64BIT +#define STORE sd +#define LOAD ld +#define REGBYTES 8 +#else +#define STORE sw +#define LOAD lw +#define REGBYTES 4 +#endif + +#ifdef ARCH_RISCV_FPU +#ifdef ARCH_RISCV_FPU_D +#define FSTORE fsd +#define FLOAD fld +#define FREGBYTES 8 +#define rv_floatreg_t rt_int64_t +#endif +#ifdef ARCH_RISCV_FPU_S +#define FSTORE fsw +#define FLOAD flw +#define FREGBYTES 4 +#define rv_floatreg_t rt_int32_t +#endif +#endif + +#endif diff --git a/components/rt-thread/libcpu/risc-v/common/riscv-ops.h b/components/rt-thread/libcpu/risc-v/common/riscv-ops.h new file mode 100644 index 00000000..35d009ad --- /dev/null +++ b/components/rt-thread/libcpu/risc-v/common/riscv-ops.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-10-03 Bernard The first version + */ + +#ifndef RISCV_OPS_H__ +#define RISCV_OPS_H__ + +#if defined(__GNUC__) && !defined(__ASSEMBLER__) + +#define read_csr(reg) ({ unsigned long __tmp; \ + asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \ + __tmp; }) + +#define write_csr(reg, val) ({ \ + if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \ + asm volatile ("csrw " #reg ", %0" :: "i"(val)); \ + else \ + asm volatile ("csrw " #reg ", %0" :: "r"(val)); }) + +#define set_csr(reg, bit) ({ unsigned long __tmp; \ + if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \ + asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "i"(bit)); \ + else \ + asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "r"(bit)); \ + __tmp; }) + +#define clear_csr(reg, bit) ({ unsigned long __tmp; \ + if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \ + asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "i"(bit)); \ + else \ + asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "r"(bit)); \ + __tmp; }) +#endif /* end of __GNUC__ */ + +#endif diff --git a/components/rt-thread/libcpu/risc-v/common/riscv-plic.h b/components/rt-thread/libcpu/risc-v/common/riscv-plic.h new file mode 100644 index 00000000..53e19d29 --- /dev/null +++ b/components/rt-thread/libcpu/risc-v/common/riscv-plic.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-10-03 Bernard The first version + */ + +#ifndef RISCV_PLIC_H__ +#define RISCV_PLIC_H__ + +#ifndef PLIC_BASE_ADDR +#define PLIC_BASE_ADDR 0x0 +#endif + +/* Priority Register - 32 bits per source */ +#define PLIC_PRIORITY_OFFSET (0x00000000UL) +#define PLIC_PRIORITY_SHIFT_PER_SOURCE 2 + +/* Pending Register - 1 bit per soirce */ +#define PLIC_PENDING_OFFSET (0x00001000UL) +#define PLIC_PENDING_SHIFT_PER_SOURCE 0 + +/* Enable Register - 0x80 per target */ +#define PLIC_ENABLE_OFFSET (0x00002000UL) +#define PLIC_ENABLE_SHIFT_PER_TARGET 7 + +/* Priority Threshold Register - 0x1000 per target */ +#define PLIC_THRESHOLD_OFFSET (0x00200000UL) +#define PLIC_THRESHOLD_SHIFT_PER_TARGET 12 + +/* Claim Register - 0x1000 per target */ +#define PLIC_CLAIM_OFFSET (0x00200004UL) +#define PLIC_CLAIM_SHIFT_PER_TARGET 12 + +#if defined(__GNUC__) && !defined(__ASSEMBLER__) +__attribute__((always_inline)) static inline void __plic_set_feature(unsigned int feature) +{ + volatile unsigned int *feature_ptr = (volatile unsigned int *)PLIC_BASE_ADDR; + *feature_ptr = feature; +} + +__attribute__((always_inline)) static inline void __plic_set_threshold(unsigned int threshold) +{ + unsigned int hart_id = read_csr(mhartid); + volatile unsigned int *threshold_ptr = (volatile unsigned int *)(PLIC_BASE_ADDR + + PLIC_THRESHOLD_OFFSET + + (hart_id << PLIC_THRESHOLD_SHIFT_PER_TARGET)); + *threshold_ptr = threshold; +} + +__attribute__((always_inline)) static inline void __plic_set_priority(unsigned int source, unsigned int priority) +{ + volatile unsigned int *priority_ptr = (volatile unsigned int *)(PLIC_BASE_ADDR + + PLIC_PRIORITY_OFFSET + + (source << PLIC_PRIORITY_SHIFT_PER_SOURCE)); + *priority_ptr = priority; +} + +__attribute__((always_inline)) static inline void __plic_set_pending(unsigned int source) +{ + volatile unsigned int *current_ptr = (volatile unsigned int *)(PLIC_BASE_ADDR + + PLIC_PENDING_OFFSET + + ((source >> 5) << 2)); + *current_ptr = (1 << (source & 0x1F)); +} + +__attribute__((always_inline)) static inline void __plic_irq_enable(unsigned int source) +{ + unsigned int hart_id = read_csr(mhartid); + volatile unsigned int *current_ptr = (volatile unsigned int *)(PLIC_BASE_ADDR + + PLIC_ENABLE_OFFSET + + (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET) + + ((source >> 5) << 2)); + unsigned int current = *current_ptr; + current = current | (1 << (source & 0x1F)); + *current_ptr = current; +} + +__attribute__((always_inline)) static inline void __plic_irq_disable(unsigned int source) +{ + unsigned int hart_id = read_csr(mhartid); + volatile unsigned int *current_ptr = (volatile unsigned int *)(PLIC_BASE_ADDR + + PLIC_ENABLE_OFFSET + + (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET) + + ((source >> 5) << 2)); + unsigned int current = *current_ptr; + current = current & ~((1 << (source & 0x1F))); + *current_ptr = current; +} + +__attribute__((always_inline)) static inline unsigned int __plic_irq_claim(void) +{ + unsigned int hart_id = read_csr(mhartid); + volatile unsigned int *claim_addr = (volatile unsigned int *)(PLIC_BASE_ADDR + + PLIC_CLAIM_OFFSET + + (hart_id << PLIC_CLAIM_SHIFT_PER_TARGET)); + return *claim_addr; +} + +__attribute__((always_inline)) static inline void __plic_irq_complete(unsigned int source) +{ + unsigned int hart_id = read_csr(mhartid); + volatile unsigned int *claim_addr = (volatile unsigned int *)(PLIC_BASE_ADDR + + PLIC_CLAIM_OFFSET + + (hart_id << PLIC_CLAIM_SHIFT_PER_TARGET)); + *claim_addr = source; +} +#endif /* end of __GNUC__ */ + +#endif diff --git a/components/rt-thread/libcpu/risc-v/e24/interrupt_gcc.S b/components/rt-thread/libcpu/risc-v/e24/interrupt_gcc.S new file mode 100644 index 00000000..173c2eb9 --- /dev/null +++ b/components/rt-thread/libcpu/risc-v/e24/interrupt_gcc.S @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-05-29 tanek first implementation + */ + + .section .text.metal.init.trapvec + .align 6 + .global trap_entry +trap_entry: + + // save all from thread context + addi sp, sp, -32 * 4 + + sw x1, 1 * 4(sp) + li t0, 0x80 + sw t0, 2 * 4(sp) + + sw x4, 4 * 4(sp) + sw x5, 5 * 4(sp) + sw x6, 6 * 4(sp) + sw x7, 7 * 4(sp) + sw x8, 8 * 4(sp) + sw x9, 9 * 4(sp) + sw x10, 10 * 4(sp) + sw x11, 11 * 4(sp) + sw x12, 12 * 4(sp) + sw x13, 13 * 4(sp) + sw x14, 14 * 4(sp) + sw x15, 15 * 4(sp) + sw x16, 16 * 4(sp) + sw x17, 17 * 4(sp) + sw x18, 18 * 4(sp) + sw x19, 19 * 4(sp) + sw x20, 20 * 4(sp) + sw x21, 21 * 4(sp) + sw x22, 22 * 4(sp) + sw x23, 23 * 4(sp) + sw x24, 24 * 4(sp) + sw x25, 25 * 4(sp) + sw x26, 26 * 4(sp) + sw x27, 27 * 4(sp) + sw x28, 28 * 4(sp) + sw x29, 29 * 4(sp) + sw x30, 30 * 4(sp) + sw x31, 31 * 4(sp) + + // switch to interrupt stack + move s0, sp + la sp, __StackTop + + // interrupt handle + call rt_interrupt_enter + csrr a0, mcause + csrr a1, mepc + mv a2, sp + call handle_trap + call rt_interrupt_leave + + // switch to from thread stack + move sp, s0 + + // need to switch new thread + la s0, rt_thread_switch_interrupt_flag + lw s2, 0(s0) + beqz s2, spurious_interrupt + sw zero, 0(s0) + + csrr a0, mepc + sw a0, 0 * 4(sp) + + la s0, rt_interrupt_from_thread + lw s1, 0(s0) + sw sp, 0(s1) + + la s0, rt_interrupt_to_thread + lw s1, 0(s0) + lw sp, 0(s1) + + lw a0, 0 * 4(sp) + csrw mepc, a0 + +spurious_interrupt: + lw x1, 1 * 4(sp) + + // Remain in M-mode after mret + li t0, 0x00001800 + csrs mstatus, t0 + lw t0, 2 * 4(sp) + csrs mstatus, t0 + + lw x4, 4 * 4(sp) + lw x5, 5 * 4(sp) + lw x6, 6 * 4(sp) + lw x7, 7 * 4(sp) + lw x8, 8 * 4(sp) + lw x9, 9 * 4(sp) + lw x10, 10 * 4(sp) + lw x11, 11 * 4(sp) + lw x12, 12 * 4(sp) + lw x13, 13 * 4(sp) + lw x14, 14 * 4(sp) + lw x15, 15 * 4(sp) + lw x16, 16 * 4(sp) + lw x17, 17 * 4(sp) + lw x18, 18 * 4(sp) + lw x19, 19 * 4(sp) + lw x20, 20 * 4(sp) + lw x21, 21 * 4(sp) + lw x22, 22 * 4(sp) + lw x23, 23 * 4(sp) + lw x24, 24 * 4(sp) + lw x25, 25 * 4(sp) + lw x26, 26 * 4(sp) + lw x27, 27 * 4(sp) + lw x28, 28 * 4(sp) + lw x29, 29 * 4(sp) + lw x30, 30 * 4(sp) + lw x31, 31 * 4(sp) + + addi sp, sp, 32 * 4 + mret diff --git a/components/rt-thread/src/clock.c b/components/rt-thread/src/clock.c new file mode 100644 index 00000000..77516b37 --- /dev/null +++ b/components/rt-thread/src/clock.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-03-12 Bernard first version + * 2006-05-27 Bernard add support for same priority thread schedule + * 2006-08-10 Bernard remove the last rt_schedule in rt_tick_increase + * 2010-03-08 Bernard remove rt_passed_second + * 2010-05-20 Bernard fix the tick exceeds the maximum limits + * 2010-07-13 Bernard fix rt_tick_from_millisecond issue found by kuronca + * 2011-06-26 Bernard add rt_tick_set function. + * 2018-11-22 Jesven add per cpu tick + * 2020-12-29 Meco Man implement rt_tick_get_millisecond() + * 2021-06-01 Meco Man add critical section projection for rt_tick_increase() + */ + +#include +#include + +#ifdef RT_USING_SMP +#define rt_tick rt_cpu_index(0)->tick +#else +static volatile rt_tick_t rt_tick = 0; +#endif /* RT_USING_SMP */ + +/** + * @addtogroup Clock + */ + +/**@{*/ + +/** + * This function will return current tick from operating system startup + * + * @return current tick + */ +rt_tick_t rt_tick_get(void) +{ + /* return the global tick */ + return rt_tick; +} +RTM_EXPORT(rt_tick_get); + +/** + * This function will set current tick + */ +void rt_tick_set(rt_tick_t tick) +{ + rt_base_t level; + + level = rt_hw_interrupt_disable(); + rt_tick = tick; + rt_hw_interrupt_enable(level); +} + +/** + * This function will notify kernel there is one tick passed. Normally, + * this function is invoked by clock ISR. + */ +void rt_tick_increase(void) +{ + struct rt_thread *thread; + rt_base_t level; + + level = rt_hw_interrupt_disable(); + + /* increase the global tick */ +#ifdef RT_USING_SMP + rt_cpu_self()->tick ++; +#else + ++ rt_tick; +#endif /* RT_USING_SMP */ + + /* check time slice */ + thread = rt_thread_self(); + + -- thread->remaining_tick; + if (thread->remaining_tick == 0) + { + /* change to initialized tick */ + thread->remaining_tick = thread->init_tick; + thread->stat |= RT_THREAD_STAT_YIELD; + + rt_hw_interrupt_enable(level); + rt_schedule(); + } + else + { + rt_hw_interrupt_enable(level); + } + + /* check timer */ + rt_timer_check(); +} + +/** + * This function will calculate the tick from millisecond. + * + * @param ms the specified millisecond + * - Negative Number wait forever + * - Zero not wait + * - Max 0x7fffffff + * + * @return the calculated tick + */ +rt_tick_t rt_tick_from_millisecond(rt_int32_t ms) +{ + rt_tick_t tick; + + if (ms < 0) + { + tick = (rt_tick_t)RT_WAITING_FOREVER; + } + else + { + tick = RT_TICK_PER_SECOND * (ms / 1000); + tick += (RT_TICK_PER_SECOND * (ms % 1000) + 999) / 1000; + } + + /* return the calculated tick */ + return tick; +} +RTM_EXPORT(rt_tick_from_millisecond); + +/** + * This function will provide the passed millisecond from boot. + * + * @return passed millisecond from boot + */ +RT_WEAK rt_tick_t rt_tick_get_millisecond(void) +{ +#if 1000 % RT_TICK_PER_SECOND == 0u + return rt_tick_get() * (1000u / RT_TICK_PER_SECOND); +#else + #warning "rt-thread cannot provide a correct 1ms-based tick any longer,\ + please redefine this function in another file by using a high-precision hard-timer." + return 0; +#endif /* 1000 % RT_TICK_PER_SECOND == 0u */ +} + +/**@}*/ + diff --git a/components/rt-thread/src/components.c b/components/rt-thread/src/components.c new file mode 100644 index 00000000..e156b783 --- /dev/null +++ b/components/rt-thread/src/components.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-09-20 Bernard Change the name to components.c + * And all components related header files. + * 2012-12-23 Bernard fix the pthread initialization issue. + * 2013-06-23 Bernard Add the init_call for components initialization. + * 2013-07-05 Bernard Remove initialization feature for MS VC++ compiler + * 2015-02-06 Bernard Remove the MS VC++ support and move to the kernel + * 2015-05-04 Bernard Rename it to components.c because compiling issue + * in some IDEs. + * 2015-07-29 Arda.Fu Add support to use RT_USING_USER_MAIN with IAR + * 2018-11-22 Jesven Add secondary cpu boot up + */ + +#include +#include + +#ifdef RT_USING_USER_MAIN +#ifndef RT_MAIN_THREAD_STACK_SIZE +#define RT_MAIN_THREAD_STACK_SIZE 2048 +#endif /* RT_MAIN_THREAD_STACK_SIZE */ +#ifndef RT_MAIN_THREAD_PRIORITY +#define RT_MAIN_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 3) +#endif /* RT_MAIN_THREAD_PRIORITY */ +#endif /* RT_USING_USER_MAIN */ + +#ifdef RT_USING_COMPONENTS_INIT +/* + * Components Initialization will initialize some driver and components as following + * order: + * rti_start --> 0 + * BOARD_EXPORT --> 1 + * rti_board_end --> 1.end + * + * DEVICE_EXPORT --> 2 + * COMPONENT_EXPORT --> 3 + * FS_EXPORT --> 4 + * ENV_EXPORT --> 5 + * APP_EXPORT --> 6 + * + * rti_end --> 6.end + * + * These automatically initialization, the driver or component initial function must + * be defined with: + * INIT_BOARD_EXPORT(fn); + * INIT_DEVICE_EXPORT(fn); + * ... + * INIT_APP_EXPORT(fn); + * etc. + */ +static int rti_start(void) +{ + return 0; +} +INIT_EXPORT(rti_start, "0"); + +static int rti_board_start(void) +{ + return 0; +} +INIT_EXPORT(rti_board_start, "0.end"); + +static int rti_board_end(void) +{ + return 0; +} +INIT_EXPORT(rti_board_end, "1.end"); + +static int rti_end(void) +{ + return 0; +} +INIT_EXPORT(rti_end, "6.end"); + +/** + * RT-Thread Components Initialization for board + */ +void rt_components_board_init(void) +{ +#if RT_DEBUG_INIT + int result; + const struct rt_init_desc *desc; + for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++) + { + rt_kprintf("initialize %s", desc->fn_name); + result = desc->fn(); + rt_kprintf(":%d done\n", result); + } +#else + volatile const init_fn_t *fn_ptr; + + for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++) + { + (*fn_ptr)(); + } +#endif /* RT_DEBUG_INIT */ +} + +/** + * RT-Thread Components Initialization + */ +void rt_components_init(void) +{ +#if RT_DEBUG_INIT + int result; + const struct rt_init_desc *desc; + + rt_kprintf("do components initialization.\n"); + for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++) + { + rt_kprintf("initialize %s", desc->fn_name); + result = desc->fn(); + rt_kprintf(":%d done\n", result); + } +#else + volatile const init_fn_t *fn_ptr; + + for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++) + { + (*fn_ptr)(); + } +#endif /* RT_DEBUG_INIT */ +} +#endif /* RT_USING_COMPONENTS_INIT */ + +#ifdef RT_USING_USER_MAIN + +void rt_application_init(void); +void rt_hw_board_init(void); +int rtthread_startup(void); + +#ifdef __ARMCC_VERSION +extern int $Super$$main(void); +/* re-define main function */ +int $Sub$$main(void) +{ + rtthread_startup(); + return 0; +} +#elif defined(__ICCARM__) +extern int main(void); +/* __low_level_init will auto called by IAR cstartup */ +extern void __iar_data_init3(void); +int __low_level_init(void) +{ + // call IAR table copy function. + __iar_data_init3(); + rtthread_startup(); + return 0; +} +#elif defined(__GNUC__) +/* Add -eentry to arm-none-eabi-gcc argument */ +int entry(void) +{ + rtthread_startup(); + return 0; +} +#endif + +#ifndef RT_USING_HEAP +/* if there is not enable heap, we should use static thread and stack. */ +ALIGN(8) +static rt_uint8_t main_stack[RT_MAIN_THREAD_STACK_SIZE]; +struct rt_thread main_thread; +#endif /* RT_USING_HEAP */ + +/* the system main thread */ +void main_thread_entry(void *parameter) +{ + extern int main(void); + +#ifdef RT_USING_COMPONENTS_INIT + /* RT-Thread components initialization */ + rt_components_init(); +#endif /* RT_USING_COMPONENTS_INIT */ + +#ifdef RT_USING_SMP + rt_hw_secondary_cpu_up(); +#endif /* RT_USING_SMP */ + /* invoke system main function */ +#ifdef __ARMCC_VERSION + { + extern int $Super$$main(void); + $Super$$main(); /* for ARMCC. */ + } +#elif defined(__ICCARM__) || defined(__GNUC__) || defined(__TASKING__) + main(); +#endif +} + +void rt_application_init(void) +{ + rt_thread_t tid; + +#ifdef RT_USING_HEAP + tid = rt_thread_create("main", main_thread_entry, RT_NULL, + RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20); + RT_ASSERT(tid != RT_NULL); +#else + rt_err_t result; + + tid = &main_thread; + result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL, + main_stack, sizeof(main_stack), RT_MAIN_THREAD_PRIORITY, 20); + RT_ASSERT(result == RT_EOK); + + /* if not define RT_USING_HEAP, using to eliminate the warning */ + (void)result; +#endif /* RT_USING_HEAP */ + + rt_thread_startup(tid); +} + +int rtthread_startup(void) +{ + rt_hw_interrupt_disable(); + + /* board level initialization + * NOTE: please initialize heap inside board initialization. + */ + rt_hw_board_init(); + + /* show RT-Thread version */ + rt_show_version(); + + /* timer system initialization */ + rt_system_timer_init(); + + /* scheduler system initialization */ + rt_system_scheduler_init(); + +#ifdef RT_USING_SIGNALS + /* signal system initialization */ + rt_system_signal_init(); +#endif /* RT_USING_SIGNALS */ + + /* create init_thread */ + rt_application_init(); + + /* timer thread initialization */ + rt_system_timer_thread_init(); + + /* idle thread initialization */ + rt_thread_idle_init(); + +#ifdef RT_USING_SMP + rt_hw_spin_lock(&_cpus_lock); +#endif /* RT_USING_SMP */ + + /* start scheduler */ + rt_system_scheduler_start(); + + /* never reach here */ + return 0; +} +#endif /* RT_USING_USER_MAIN */ diff --git a/components/rt-thread/src/cpu.c b/components/rt-thread/src/cpu.c new file mode 100644 index 00000000..fc1fed5a --- /dev/null +++ b/components/rt-thread/src/cpu.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-10-30 Bernard The first version + */ +#include +#include + +#ifdef RT_USING_SMP +static struct rt_cpu _cpus[RT_CPUS_NR]; +rt_hw_spinlock_t _cpus_lock; + +/* + * disable scheduler + */ +static void _cpu_preempt_disable(void) +{ + register rt_base_t level; + struct rt_thread *current_thread; + + /* disable interrupt */ + level = rt_hw_local_irq_disable(); + + current_thread = rt_thread_self(); + if (!current_thread) + { + rt_hw_local_irq_enable(level); + return; + } + + /* lock scheduler for local cpu */ + current_thread->scheduler_lock_nest ++; + + /* enable interrupt */ + rt_hw_local_irq_enable(level); +} + +/* + * enable scheduler + */ +static void _cpu_preempt_enable(void) +{ + register rt_base_t level; + struct rt_thread *current_thread; + + /* disable interrupt */ + level = rt_hw_local_irq_disable(); + + current_thread = rt_thread_self(); + if (!current_thread) + { + rt_hw_local_irq_enable(level); + return; + } + + /* unlock scheduler for local cpu */ + current_thread->scheduler_lock_nest --; + + rt_schedule(); + /* enable interrupt */ + rt_hw_local_irq_enable(level); +} + +void rt_spin_lock_init(struct rt_spinlock *lock) +{ + rt_hw_spin_lock_init(&lock->lock); +} +RTM_EXPORT(rt_spin_lock_init) + +void rt_spin_lock(struct rt_spinlock *lock) +{ + _cpu_preempt_disable(); + rt_hw_spin_lock(&lock->lock); +} +RTM_EXPORT(rt_spin_lock) + +void rt_spin_unlock(struct rt_spinlock *lock) +{ + rt_hw_spin_unlock(&lock->lock); + _cpu_preempt_enable(); +} +RTM_EXPORT(rt_spin_unlock) + +rt_base_t rt_spin_lock_irqsave(struct rt_spinlock *lock) +{ + unsigned long level; + + _cpu_preempt_disable(); + + level = rt_hw_local_irq_disable(); + rt_hw_spin_lock(&lock->lock); + + return level; +} +RTM_EXPORT(rt_spin_lock_irqsave) + +void rt_spin_unlock_irqrestore(struct rt_spinlock *lock, rt_base_t level) +{ + rt_hw_spin_unlock(&lock->lock); + rt_hw_local_irq_enable(level); + + _cpu_preempt_enable(); +} +RTM_EXPORT(rt_spin_unlock_irqrestore) + +/** + * This fucntion will return current cpu. + */ +struct rt_cpu *rt_cpu_self(void) +{ + return &_cpus[rt_hw_cpu_id()]; +} + +struct rt_cpu *rt_cpu_index(int index) +{ + return &_cpus[index]; +} + +/** + * This function will lock all cpus's scheduler and disable local irq. + */ +rt_base_t rt_cpus_lock(void) +{ + rt_base_t level; + struct rt_cpu* pcpu; + + level = rt_hw_local_irq_disable(); + + pcpu = rt_cpu_self(); + if (pcpu->current_thread != RT_NULL) + { + register rt_ubase_t lock_nest = pcpu->current_thread->cpus_lock_nest; + + pcpu->current_thread->cpus_lock_nest++; + if (lock_nest == 0) + { + pcpu->current_thread->scheduler_lock_nest++; + rt_hw_spin_lock(&_cpus_lock); + } + } + + return level; +} +RTM_EXPORT(rt_cpus_lock); + +/** + * This function will restore all cpus's scheduler and restore local irq. + */ +void rt_cpus_unlock(rt_base_t level) +{ + struct rt_cpu* pcpu = rt_cpu_self(); + + if (pcpu->current_thread != RT_NULL) + { + pcpu->current_thread->cpus_lock_nest--; + + if (pcpu->current_thread->cpus_lock_nest == 0) + { + pcpu->current_thread->scheduler_lock_nest--; + rt_hw_spin_unlock(&_cpus_lock); + } + } + rt_hw_local_irq_enable(level); +} +RTM_EXPORT(rt_cpus_unlock); + +/** + * This function is invoked by scheduler. + * It will restore the lock state to whatever the thread's counter expects. + * If target thread not locked the cpus then unlock the cpus lock. + */ +void rt_cpus_lock_status_restore(struct rt_thread *thread) +{ + struct rt_cpu* pcpu = rt_cpu_self(); + + pcpu->current_thread = thread; + if (!thread->cpus_lock_nest) + { + rt_hw_spin_unlock(&_cpus_lock); + } +} +RTM_EXPORT(rt_cpus_lock_status_restore); + +#endif /* RT_USING_SMP */ diff --git a/components/rt-thread/src/device.c b/components/rt-thread/src/device.c new file mode 100644 index 00000000..a9c244d3 --- /dev/null +++ b/components/rt-thread/src/device.c @@ -0,0 +1,437 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2007-01-21 Bernard the first version + * 2010-05-04 Bernard add rt_device_init implementation + * 2012-10-20 Bernard add device check in register function, + * provided by Rob + * 2012-12-25 Bernard return RT_EOK if the device interface not exist. + * 2013-07-09 Grissiom add ref_count support + * 2016-04-02 Bernard fix the open_flag initialization issue. + * 2021-03-19 Meco Man remove rt_device_init_all() + */ + +#include +#ifdef RT_USING_POSIX +#include /* for wqueue_init */ +#endif /* RT_USING_POSIX */ + +#ifdef RT_USING_DEVICE + +#ifdef RT_USING_DEVICE_OPS +#define device_init (dev->ops->init) +#define device_open (dev->ops->open) +#define device_close (dev->ops->close) +#define device_read (dev->ops->read) +#define device_write (dev->ops->write) +#define device_control (dev->ops->control) +#else +#define device_init (dev->init) +#define device_open (dev->open) +#define device_close (dev->close) +#define device_read (dev->read) +#define device_write (dev->write) +#define device_control (dev->control) +#endif /* RT_USING_DEVICE_OPS */ + +/** + * This function registers a device driver with specified name. + * + * @param dev the pointer of device driver structure + * @param name the device driver's name + * @param flags the capabilities flag of device + * + * @return the error code, RT_EOK on initialization successfully. + */ +rt_err_t rt_device_register(rt_device_t dev, + const char *name, + rt_uint16_t flags) +{ + if (dev == RT_NULL) + return -RT_ERROR; + + if (rt_device_find(name) != RT_NULL) + return -RT_ERROR; + + rt_object_init(&(dev->parent), RT_Object_Class_Device, name); + dev->flag = flags; + dev->ref_count = 0; + dev->open_flag = 0; + +#ifdef RT_USING_POSIX + dev->fops = RT_NULL; + rt_wqueue_init(&(dev->wait_queue)); +#endif /* RT_USING_POSIX */ + + return RT_EOK; +} +RTM_EXPORT(rt_device_register); + +/** + * This function removes a previously registered device driver + * + * @param dev the pointer of device driver structure + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_device_unregister(rt_device_t dev) +{ + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); + RT_ASSERT(rt_object_is_systemobject(&dev->parent)); + + rt_object_detach(&(dev->parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_device_unregister); + +/** + * This function finds a device driver by specified name. + * + * @param name the device driver's name + * + * @return the registered device driver on successful, or RT_NULL on failure. + */ +rt_device_t rt_device_find(const char *name) +{ + return (rt_device_t)rt_object_find(name, RT_Object_Class_Device); +} +RTM_EXPORT(rt_device_find); + +#ifdef RT_USING_HEAP +/** + * This function creates a device object with user data size. + * + * @param type, the kind type of this device object. + * @param attach_size, the size of user data. + * + * @return the allocated device object, or RT_NULL when failed. + */ +rt_device_t rt_device_create(int type, int attach_size) +{ + int size; + rt_device_t device; + + size = RT_ALIGN(sizeof(struct rt_device), RT_ALIGN_SIZE); + attach_size = RT_ALIGN(attach_size, RT_ALIGN_SIZE); + /* use the total size */ + size += attach_size; + + device = (rt_device_t)rt_malloc(size); + if (device) + { + rt_memset(device, 0x0, sizeof(struct rt_device)); + device->type = (enum rt_device_class_type)type; + } + + return device; +} +RTM_EXPORT(rt_device_create); + +/** + * This function destroy the specific device object. + * + * @param dev, the specific device object. + */ +void rt_device_destroy(rt_device_t dev) +{ + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); + RT_ASSERT(rt_object_is_systemobject(&dev->parent) == RT_FALSE); + + rt_object_detach(&(dev->parent)); + + /* release this device object */ + rt_free(dev); +} +RTM_EXPORT(rt_device_destroy); +#endif /* RT_USING_HEAP */ + +/** + * This function will initialize the specified device + * + * @param dev the pointer of device driver structure + * + * @return the result + */ +rt_err_t rt_device_init(rt_device_t dev) +{ + rt_err_t result = RT_EOK; + + RT_ASSERT(dev != RT_NULL); + + /* get device_init handler */ + if (device_init != RT_NULL) + { + if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED)) + { + result = device_init(dev); + if (result != RT_EOK) + { + rt_kprintf("To initialize device:%s failed. The error code is %d\n", + dev->parent.name, result); + } + else + { + dev->flag |= RT_DEVICE_FLAG_ACTIVATED; + } + } + } + + return result; +} + +/** + * This function will open a device + * + * @param dev the pointer of device driver structure + * @param oflag the flags for device open + * + * @return the result + */ +rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag) +{ + rt_err_t result = RT_EOK; + + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); + + /* if device is not initialized, initialize it. */ + if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED)) + { + if (device_init != RT_NULL) + { + result = device_init(dev); + if (result != RT_EOK) + { + rt_kprintf("To initialize device:%s failed. The error code is %d\n", + dev->parent.name, result); + + return result; + } + } + + dev->flag |= RT_DEVICE_FLAG_ACTIVATED; + } + + /* device is a stand alone device and opened */ + if ((dev->flag & RT_DEVICE_FLAG_STANDALONE) && + (dev->open_flag & RT_DEVICE_OFLAG_OPEN)) + { + return -RT_EBUSY; + } + + /* call device_open interface */ + if (device_open != RT_NULL) + { + result = device_open(dev, oflag); + } + else + { + /* set open flag */ + dev->open_flag = (oflag & RT_DEVICE_OFLAG_MASK); + } + + /* set open flag */ + if (result == RT_EOK || result == -RT_ENOSYS) + { + dev->open_flag |= RT_DEVICE_OFLAG_OPEN; + + dev->ref_count++; + /* don't let bad things happen silently. If you are bitten by this assert, + * please set the ref_count to a bigger type. */ + RT_ASSERT(dev->ref_count != 0); + } + + return result; +} +RTM_EXPORT(rt_device_open); + +/** + * This function will close a device + * + * @param dev the pointer of device driver structure + * + * @return the result + */ +rt_err_t rt_device_close(rt_device_t dev) +{ + rt_err_t result = RT_EOK; + + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); + + if (dev->ref_count == 0) + return -RT_ERROR; + + dev->ref_count--; + + if (dev->ref_count != 0) + return RT_EOK; + + /* call device_close interface */ + if (device_close != RT_NULL) + { + result = device_close(dev); + } + + /* set open flag */ + if (result == RT_EOK || result == -RT_ENOSYS) + dev->open_flag = RT_DEVICE_OFLAG_CLOSE; + + return result; +} +RTM_EXPORT(rt_device_close); + +/** + * This function will read some data from a device. + * + * @param dev the pointer of device driver structure + * @param pos the position of reading + * @param buffer the data buffer to save read data + * @param size the size of buffer + * + * @return the actually read size on successful, otherwise negative returned. + * + * @note since 0.4.0, the unit of size/pos is a block for block device. + */ +rt_size_t rt_device_read(rt_device_t dev, + rt_off_t pos, + void *buffer, + rt_size_t size) +{ + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); + + if (dev->ref_count == 0) + { + rt_set_errno(-RT_ERROR); + return 0; + } + + /* call device_read interface */ + if (device_read != RT_NULL) + { + return device_read(dev, pos, buffer, size); + } + + /* set error code */ + rt_set_errno(-RT_ENOSYS); + + return 0; +} +RTM_EXPORT(rt_device_read); + +/** + * This function will write some data to a device. + * + * @param dev the pointer of device driver structure + * @param pos the position of written + * @param buffer the data buffer to be written to device + * @param size the size of buffer + * + * @return the actually written size on successful, otherwise negative returned. + * + * @note since 0.4.0, the unit of size/pos is a block for block device. + */ +rt_size_t rt_device_write(rt_device_t dev, + rt_off_t pos, + const void *buffer, + rt_size_t size) +{ + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); + + if (dev->ref_count == 0) + { + rt_set_errno(-RT_ERROR); + return 0; + } + + /* call device_write interface */ + if (device_write != RT_NULL) + { + return device_write(dev, pos, buffer, size); + } + + /* set error code */ + rt_set_errno(-RT_ENOSYS); + + return 0; +} +RTM_EXPORT(rt_device_write); + +/** + * This function will perform a variety of control functions on devices. + * + * @param dev the pointer of device driver structure + * @param cmd the command sent to device + * @param arg the argument of command + * + * @return the result + */ +rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg) +{ + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); + + /* call device_write interface */ + if (device_control != RT_NULL) + { + return device_control(dev, cmd, arg); + } + + return -RT_ENOSYS; +} +RTM_EXPORT(rt_device_control); + +/** + * This function will set the reception indication callback function. This callback function + * is invoked when this device receives data. + * + * @param dev the pointer of device driver structure + * @param rx_ind the indication callback function + * + * @return RT_EOK + */ +rt_err_t +rt_device_set_rx_indicate(rt_device_t dev, + rt_err_t (*rx_ind)(rt_device_t dev, rt_size_t size)) +{ + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); + + dev->rx_indicate = rx_ind; + + return RT_EOK; +} +RTM_EXPORT(rt_device_set_rx_indicate); + +/** + * This function will set the indication callback function when device has + * written data to physical hardware. + * + * @param dev the pointer of device driver structure + * @param tx_done the indication callback function + * + * @return RT_EOK + */ +rt_err_t +rt_device_set_tx_complete(rt_device_t dev, + rt_err_t (*tx_done)(rt_device_t dev, void *buffer)) +{ + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); + + dev->tx_complete = tx_done; + + return RT_EOK; +} +RTM_EXPORT(rt_device_set_tx_complete); + +#endif /* RT_USING_DEVICE */ diff --git a/components/rt-thread/src/idle.c b/components/rt-thread/src/idle.c new file mode 100644 index 00000000..ec625cb4 --- /dev/null +++ b/components/rt-thread/src/idle.c @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-03-23 Bernard the first version + * 2010-11-10 Bernard add cleanup callback function in thread exit. + * 2012-12-29 Bernard fix compiling warning. + * 2013-12-21 Grissiom let rt_thread_idle_excute loop until there is no + * dead thread. + * 2016-08-09 ArdaFu add method to get the handler of the idle thread. + * 2018-02-07 Bernard lock scheduler to protect tid->cleanup. + * 2018-07-14 armink add idle hook list + * 2018-11-22 Jesven add per cpu idle task + * combine the code of primary and secondary cpu + */ + +#include +#include + +#ifdef RT_USING_MODULE +#include +#endif /* RT_USING_MODULE */ + +#ifdef RT_USING_HOOK +#ifndef RT_USING_IDLE_HOOK +#define RT_USING_IDLE_HOOK +#endif /* RT_USING_IDLE_HOOK */ +#endif /* RT_USING_HOOK */ + +#ifndef IDLE_THREAD_STACK_SIZE +#if defined (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP) +#define IDLE_THREAD_STACK_SIZE 256 +#else +#define IDLE_THREAD_STACK_SIZE 128 +#endif /* (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP) */ +#endif /* IDLE_THREAD_STACK_SIZE */ + +#ifdef RT_USING_SMP +#define _CPUS_NR RT_CPUS_NR +#else +#define _CPUS_NR 1 +#endif /* RT_USING_SMP */ + +static rt_list_t _rt_thread_defunct = RT_LIST_OBJECT_INIT(_rt_thread_defunct);; + +static struct rt_thread idle[_CPUS_NR]; +ALIGN(RT_ALIGN_SIZE) +static rt_uint8_t rt_thread_stack[_CPUS_NR][IDLE_THREAD_STACK_SIZE]; + +#ifdef RT_USING_SMP +#ifndef SYSTEM_THREAD_STACK_SIZE +#define SYSTEM_THREAD_STACK_SIZE IDLE_THREAD_STACK_SIZE +#endif +static struct rt_thread rt_system_thread; +ALIGN(RT_ALIGN_SIZE) +static rt_uint8_t rt_system_stack[SYSTEM_THREAD_STACK_SIZE]; +static struct rt_semaphore system_sem; +#endif + +#ifdef RT_USING_IDLE_HOOK +#ifndef RT_IDLE_HOOK_LIST_SIZE +#define RT_IDLE_HOOK_LIST_SIZE 4 +#endif /* RT_IDLE_HOOK_LIST_SIZE */ + +static void (*idle_hook_list[RT_IDLE_HOOK_LIST_SIZE])(void); + +/** + * @ingroup Hook + * This function sets a hook function to idle thread loop. When the system performs + * idle loop, this hook function should be invoked. + * + * @param hook the specified hook function + * + * @return RT_EOK: set OK + * -RT_EFULL: hook list is full + * + * @note the hook function must be simple and never be blocked or suspend. + */ +rt_err_t rt_thread_idle_sethook(void (*hook)(void)) +{ + rt_size_t i; + rt_base_t level; + rt_err_t ret = -RT_EFULL; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++) + { + if (idle_hook_list[i] == RT_NULL) + { + idle_hook_list[i] = hook; + ret = RT_EOK; + break; + } + } + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return ret; +} + +/** + * delete the idle hook on hook list + * + * @param hook the specified hook function + * + * @return RT_EOK: delete OK + * -RT_ENOSYS: hook was not found + */ +rt_err_t rt_thread_idle_delhook(void (*hook)(void)) +{ + rt_size_t i; + rt_base_t level; + rt_err_t ret = -RT_ENOSYS; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++) + { + if (idle_hook_list[i] == hook) + { + idle_hook_list[i] = RT_NULL; + ret = RT_EOK; + break; + } + } + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return ret; +} + +#endif /* RT_USING_IDLE_HOOK */ + +#ifdef RT_USING_MODULE +/* Return whether there is defunctional thread to be deleted. */ +rt_inline int _idle_has_defunct_thread(void) +{ + /* The rt_list_isempty has prototype of "int rt_list_isempty(const rt_list_t *l)". + * So the compiler has a good reason that the _rt_thread_defunct list does + * not change within rt_thread_defunct_exceute thus optimize the "while" loop + * into a "if". + * + * So add the volatile qualifier here. */ + const volatile rt_list_t *l = (const volatile rt_list_t *)&_rt_thread_defunct; + + return l->next != l; +} +#endif /* RT_USING_MODULE */ + +/* enqueue a thread to defunct queue + * it must be called between rt_hw_interrupt_disable and rt_hw_interrupt_enable + */ +void rt_thread_defunct_enqueue(rt_thread_t thread) +{ + rt_list_insert_after(&_rt_thread_defunct, &thread->tlist); +#ifdef RT_USING_SMP + rt_sem_release(&system_sem); +#endif +} + +/* dequeue a thread from defunct queue + * it must be called between rt_hw_interrupt_disable and rt_hw_interrupt_enable + */ +rt_thread_t rt_thread_defunct_dequeue(void) +{ + rt_thread_t thread = RT_NULL; + rt_list_t *l = &_rt_thread_defunct; + + if (l->next != l) + { + thread = rt_list_entry(l->next, + struct rt_thread, + tlist); + rt_list_remove(&(thread->tlist)); + } + return thread; +} + +/** + * @ingroup Thread + * + * This function will perform system background job when system idle. + */ +static void rt_defunct_execute(void) +{ + /* Loop until there is no dead thread. So one call to rt_defunct_execute + * will do all the cleanups. */ + while (1) + { + rt_base_t lock; + rt_thread_t thread; + void (*cleanup)(struct rt_thread *tid); + +#ifdef RT_USING_MODULE + struct rt_dlmodule *module = RT_NULL; +#endif + RT_DEBUG_NOT_IN_INTERRUPT; + + /* disable interrupt */ + lock = rt_hw_interrupt_disable(); + +#ifdef RT_USING_MODULE + /* check whether list is empty */ + if (!_idle_has_defunct_thread()) + { + rt_hw_interrupt_enable(lock); + break; + } + /* get defunct thread */ + thread = rt_list_entry(_rt_thread_defunct.next, + struct rt_thread, + tlist); + module = (struct rt_dlmodule*)thread->module_id; + if (module) + { + dlmodule_destroy(module); + } + /* remove defunct thread */ + rt_list_remove(&(thread->tlist)); +#else + thread = rt_thread_defunct_dequeue(); + if (!thread) + { + rt_hw_interrupt_enable(lock); + break; + } +#endif + /* invoke thread cleanup */ + cleanup = thread->cleanup; + if (cleanup != RT_NULL) + { + rt_hw_interrupt_enable(lock); + cleanup(thread); + lock = rt_hw_interrupt_disable(); + } + +#ifdef RT_USING_SIGNALS + rt_thread_free_sig(thread); +#endif + + /* if it's a system object, not delete it */ + if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) + { + /* detach this object */ + rt_object_detach((rt_object_t)thread); + /* enable interrupt */ + rt_hw_interrupt_enable(lock); + } + else + { + rt_hw_interrupt_enable(lock); +#ifdef RT_USING_HEAP + /* release thread's stack */ + RT_KERNEL_FREE(thread->stack_addr); + /* delete thread object */ + rt_object_delete((rt_object_t)thread); +#endif + } + } +} + +extern void rt_system_power_manager(void); +static void rt_thread_idle_entry(void *parameter) +{ +#ifdef RT_USING_SMP + if (rt_hw_cpu_id() != 0) + { + while (1) + { + rt_hw_secondary_cpu_idle_exec(); + } + } +#endif /* RT_USING_SMP */ + + while (1) + { +#ifdef RT_USING_IDLE_HOOK + rt_size_t i; + void (*idle_hook)(void); + + for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++) + { + idle_hook = idle_hook_list[i]; + if (idle_hook != RT_NULL) + { + idle_hook(); + } + } +#endif /* RT_USING_IDLE_HOOK */ + +#ifndef RT_USING_SMP + rt_defunct_execute(); +#endif /* RT_USING_SMP */ + +#ifdef RT_USING_PM + rt_system_power_manager(); +#endif /* RT_USING_PM */ + } +} + +#ifdef RT_USING_SMP +static void rt_thread_system_entry(void *parameter) +{ + while (1) + { + rt_sem_take(&system_sem, RT_WAITING_FOREVER); + rt_defunct_execute(); + } +} +#endif + +/** + * @ingroup SystemInit + * + * This function will initialize idle thread, then start it. + * + * @note this function must be invoked when system init. + */ +void rt_thread_idle_init(void) +{ + rt_ubase_t i; + char tidle_name[RT_NAME_MAX]; + + for (i = 0; i < _CPUS_NR; i++) + { + rt_sprintf(tidle_name, "tidle%d", i); + rt_thread_init(&idle[i], + tidle_name, + rt_thread_idle_entry, + RT_NULL, + &rt_thread_stack[i][0], + sizeof(rt_thread_stack[i]), + RT_THREAD_PRIORITY_MAX - 1, + 32); +#ifdef RT_USING_SMP + rt_thread_control(&idle[i], RT_THREAD_CTRL_BIND_CPU, (void*)i); +#endif /* RT_USING_SMP */ + /* startup */ + rt_thread_startup(&idle[i]); + } + +#ifdef RT_USING_SMP + RT_ASSERT(RT_THREAD_PRIORITY_MAX > 2); + + rt_sem_init(&system_sem, "defunct", 1, RT_IPC_FLAG_FIFO); + + /* create defunct thread */ + rt_thread_init(&rt_system_thread, + "tsystem", + rt_thread_system_entry, + RT_NULL, + rt_system_stack, + sizeof(rt_system_stack), + RT_THREAD_PRIORITY_MAX - 2, + 32); + /* startup */ + rt_thread_startup(&rt_system_thread); +#endif +} + +/** + * @ingroup Thread + * + * This function will get the handler of the idle thread. + * + */ +rt_thread_t rt_thread_idle_gethandler(void) +{ +#ifdef RT_USING_SMP + register int id = rt_hw_cpu_id(); +#else + register int id = 0; +#endif /* RT_USING_SMP */ + + return (rt_thread_t)(&idle[id]); +} diff --git a/components/rt-thread/src/ipc.c b/components/rt-thread/src/ipc.c new file mode 100644 index 00000000..57ac453a --- /dev/null +++ b/components/rt-thread/src/ipc.c @@ -0,0 +1,3250 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-03-14 Bernard the first version + * 2006-04-25 Bernard implement semaphore + * 2006-05-03 Bernard add RT_IPC_DEBUG + * modify the type of IPC waiting time to rt_int32_t + * 2006-05-10 Bernard fix the semaphore take bug and add IPC object + * 2006-05-12 Bernard implement mailbox and message queue + * 2006-05-20 Bernard implement mutex + * 2006-05-23 Bernard implement fast event + * 2006-05-24 Bernard implement event + * 2006-06-03 Bernard fix the thread timer init bug + * 2006-06-05 Bernard fix the mutex release bug + * 2006-06-07 Bernard fix the message queue send bug + * 2006-08-04 Bernard add hook support + * 2009-05-21 Yi.qiu fix the sem release bug + * 2009-07-18 Bernard fix the event clear bug + * 2009-09-09 Bernard remove fast event and fix ipc release bug + * 2009-10-10 Bernard change semaphore and mutex value to unsigned value + * 2009-10-25 Bernard change the mb/mq receive timeout to 0 if the + * re-calculated delta tick is a negative number. + * 2009-12-16 Bernard fix the rt_ipc_object_suspend issue when IPC flag + * is RT_IPC_FLAG_PRIO + * 2010-01-20 mbbill remove rt_ipc_object_decrease function. + * 2010-04-20 Bernard move memcpy outside interrupt disable in mq + * 2010-10-26 yi.qiu add module support in rt_mp_delete and rt_mq_delete + * 2010-11-10 Bernard add IPC reset command implementation. + * 2011-12-18 Bernard add more parameter checking in message queue + * 2013-09-14 Grissiom add an option check in rt_event_recv + * 2018-10-02 Bernard add 64bit support for mailbox + * 2019-09-16 tyx add send wait support for message queue + * 2020-07-29 Meco Man fix thread->event_set/event_info when received an + * event without pending + * 2020-10-11 Meco Man add value overflow-check code + * 2021-01-03 Meco Man implement rt_mb_urgent() + * 2021-05-30 Meco Man implement rt_mutex_trytake() + */ + +#include +#include + +#ifdef RT_USING_HOOK +extern void (*rt_object_trytake_hook)(struct rt_object *object); +extern void (*rt_object_take_hook)(struct rt_object *object); +extern void (*rt_object_put_hook)(struct rt_object *object); +#endif /* RT_USING_HOOK */ + +/** + * @addtogroup IPC + */ + +/**@{*/ + +/** + * @brief This function will initialize an IPC object, such as semaphore, mutex, messagequeue and mailbox. + * + * @note Executing this function will complete an initialization of the suspend thread list of the ipc object. + * + * @param ipc is a pointer to the IPC object. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * When the return value is any other values, it means the initialization failed. + * + * @warning This function can be called from all IPC initialization and creation. + */ +rt_inline rt_err_t _ipc_object_init(struct rt_ipc_object *ipc) +{ + /* initialize ipc object */ + rt_list_init(&(ipc->suspend_thread)); + + return RT_EOK; +} + + +/** + * @brief This function will suspend a thread to a IPC object list. + * + * @param list is a pointer to a suspended thread list of the IPC object. + * + * @param thread is a pointer to the thread object to be suspended. + * + * @param flag is a flag for the thread object to be suspended. It determines how the thread is suspended. + * The flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to use + * RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this semaphore will become non-real-time threads. + * + * @return Return the operation status. When the return value is RT_EOK, the function is successfully executed. + * When the return value is any other values, it means the initialization failed. + * + * @warning This function can ONLY be called in the thread context, you can use RT_DEBUG_IN_THREAD_CONTEXT to + * check the context. + * In addition, this function is generally called by the following functions: + * rt_sem_take(), rt_mutex_take(), rt_event_recv(), rt_mb_send_wait(), + * rt_mb_recv(), rt_mq_recv(), rt_mq_send_wait() + */ +rt_inline rt_err_t _ipc_list_suspend(rt_list_t *list, + struct rt_thread *thread, + rt_uint8_t flag) +{ + /* suspend thread */ + rt_thread_suspend(thread); + + switch (flag) + { + case RT_IPC_FLAG_FIFO: + rt_list_insert_before(list, &(thread->tlist)); + break; /* RT_IPC_FLAG_FIFO */ + + case RT_IPC_FLAG_PRIO: + { + struct rt_list_node *n; + struct rt_thread *sthread; + + /* find a suitable position */ + for (n = list->next; n != list; n = n->next) + { + sthread = rt_list_entry(n, struct rt_thread, tlist); + + /* find out */ + if (thread->current_priority < sthread->current_priority) + { + /* insert this thread before the sthread */ + rt_list_insert_before(&(sthread->tlist), &(thread->tlist)); + break; + } + } + + /* + * not found a suitable position, + * append to the end of suspend_thread list + */ + if (n == list) + rt_list_insert_before(list, &(thread->tlist)); + } + break;/* RT_IPC_FLAG_PRIO */ + + default: + RT_ASSERT(0); + break; + } + + return RT_EOK; +} + + +/** + * @brief This function will resume a thread. + * + * @note This function will resume the first thread in the list of a IPC object. + * 1. remove the thread from suspend queue of a IPC object. + * 2. put the thread into system ready queue. + * + * By contrast, the rt_ipc_list_resume_all() function will resume all suspended threads + * in the list of a IPC object. + * + * @param list is a pointer to a suspended thread list of the IPC object. + * + * @return Return the operation status. When the return value is RT_EOK, the function is successfully executed. + * When the return value is any other values, it means this operation failed. + * + * @warning This function is generally called by the following functions: + * rt_sem_release(), rt_mutex_release(), rt_mb_send_wait(), rt_mq_send_wait(), + * rt_mb_urgent(), rt_mb_recv(), rt_mq_urgent(), rt_mq_recv(), + */ +rt_inline rt_err_t _ipc_list_resume(rt_list_t *list) +{ + struct rt_thread *thread; + + /* get thread entry */ + thread = rt_list_entry(list->next, struct rt_thread, tlist); + + RT_DEBUG_LOG(RT_DEBUG_IPC, ("resume thread:%s\n", thread->name)); + + /* resume it */ + rt_thread_resume(thread); + + return RT_EOK; +} + + +/** + * @brief This function will resume all suspended threads in the IPC object list, + * including the suspended list of IPC object, and private list of mailbox etc. + * + * @note This function will resume all threads in the IPC object list. + * By contrast, the rt_ipc_list_resume() function will resume a suspended thread in the list of a IPC object. + * + * @param list is a pointer to a suspended thread list of the IPC object. + * + * @return Return the operation status. When the return value is RT_EOK, the function is successfully executed. + * When the return value is any other values, it means this operation failed. + * + */ +rt_inline rt_err_t _ipc_list_resume_all(rt_list_t *list) +{ + struct rt_thread *thread; + register rt_ubase_t temp; + + /* wakeup all suspended threads */ + while (!rt_list_isempty(list)) + { + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* get next suspended thread */ + thread = rt_list_entry(list->next, struct rt_thread, tlist); + /* set error code to RT_ERROR */ + thread->error = -RT_ERROR; + + /* + * resume thread + * In rt_thread_resume function, it will remove current thread from + * suspended list + */ + rt_thread_resume(thread); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + } + + return RT_EOK; +} + +/**@}*/ + +#ifdef RT_USING_SEMAPHORE +/** + * @addtogroup semaphore + */ + +/**@{*/ +/** + * @brief This function will initialize a static semaphore object. + * + * @note For the static semaphore object, its memory space is allocated by the compiler during compiling, + * and shall placed on the read-write data segment or on the uninitialized data segment. + * By contrast, the rt_sem_create() function will allocate memory space automatically and initialize + * the semaphore. + * + * @see rt_sem_create() + * + * @param sem is a pointer to the semaphore to initialize. It is assumed that storage for the semaphore will be + * allocated in your application. + * + * @param name is a pointer to the name you would like to give the semaphore. + * + * @param value is the initial value for the semaphore. + * If used to share resources, you should initialize the value as the number of available resources. + * If used to signal the occurrence of an event, you should initialize the value as 0. + * + * @param flag is the semaphore flag, which determines the queuing way of how multiple threads wait + * when the semaphore is not available. + * The semaphore flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this semaphore will become non-real-time threads. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it represents the initialization failed. + * + * @warning This function can ONLY be called from threads. + */ +rt_err_t rt_sem_init(rt_sem_t sem, + const char *name, + rt_uint32_t value, + rt_uint8_t flag) +{ + RT_ASSERT(sem != RT_NULL); + RT_ASSERT(value < 0x10000U); + + /* initialize object */ + rt_object_init(&(sem->parent.parent), RT_Object_Class_Semaphore, name); + + /* initialize ipc object */ + _ipc_object_init(&(sem->parent)); + + /* set initial value */ + sem->value = (rt_uint16_t)value; + + /* set parent */ + sem->parent.parent.flag = flag; + + return RT_EOK; +} +RTM_EXPORT(rt_sem_init); + + +/** + * @brief This function will detach a static semaphore object. + * + * @note This function is used to detach a static semaphore object which is initialized by rt_sem_init() function. + * By contrast, the rt_sem_delete() function will delete a semaphore object. + * When the semaphore is successfully detached, it will resume all suspended threads in the semaphore list. + * + * @see rt_sem_delete() + * + * @param sem is a pointer to a semaphore object to be detached. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it means that the semaphore detach failed. + * + * @warning This function can ONLY detach a static semaphore initialized by the rt_sem_init() function. + * If the semaphore is created by the rt_sem_create() function, you MUST NOT USE this function to detach it, + * ONLY USE the rt_sem_delete() function to complete the deletion. + */ +rt_err_t rt_sem_detach(rt_sem_t sem) +{ + /* parameter check */ + RT_ASSERT(sem != RT_NULL); + RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore); + RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent)); + + /* wakeup all suspended threads */ + _ipc_list_resume_all(&(sem->parent.suspend_thread)); + + /* detach semaphore object */ + rt_object_detach(&(sem->parent.parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_sem_detach); + +#ifdef RT_USING_HEAP +/** + * @brief Creating a semaphore object. + * + * @note For the semaphore object, its memory space is allocated automatically. + * By contrast, the rt_sem_init() function will initialize a static semaphore object. + * + * @see rt_sem_init() + * + * @param name is a pointer to the name you would like to give the semaphore. + * + * @param value is the initial value for the semaphore. + * If used to share resources, you should initialize the value as the number of available resources. + * If used to signal the occurrence of an event, you should initialize the value as 0. + * + * @param flag is the semaphore flag, which determines the queuing way of how multiple threads wait + * when the semaphore is not available. + * The semaphore flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this semaphore will become non-real-time threads. + * + * @return Return a pointer to the semaphore object. When the return value is RT_NULL, it means the creation failed. + * + * @warning This function can NOT be called in interrupt context. You can use macor RT_DEBUG_NOT_IN_INTERRUPT to check it. + */ +rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag) +{ + rt_sem_t sem; + + RT_DEBUG_NOT_IN_INTERRUPT; + RT_ASSERT(value < 0x10000U); + + /* allocate object */ + sem = (rt_sem_t)rt_object_allocate(RT_Object_Class_Semaphore, name); + if (sem == RT_NULL) + return sem; + + /* initialize ipc object */ + _ipc_object_init(&(sem->parent)); + + /* set initial value */ + sem->value = value; + + /* set parent */ + sem->parent.parent.flag = flag; + + return sem; +} +RTM_EXPORT(rt_sem_create); + + +/** + * @brief This function will delete a semaphore object and release the memory space. + * + * @note This function is used to delete a semaphore object which is created by the rt_sem_create() function. + * By contrast, the rt_sem_detach() function will detach a static semaphore object. + * When the semaphore is successfully deleted, it will resume all suspended threads in the semaphore list. + * + * @see rt_sem_detach() + * + * @param sem is a pointer to a semaphore object to be deleted. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the semaphore detach failed. + * + * @warning This function can ONLY delete a semaphore initialized by the rt_sem_create() function. + * If the semaphore is initialized by the rt_sem_init() function, you MUST NOT USE this function to delete it, + * ONLY USE the rt_sem_detach() function to complete the detachment. + */ +rt_err_t rt_sem_delete(rt_sem_t sem) +{ + RT_DEBUG_NOT_IN_INTERRUPT; + + /* parameter check */ + RT_ASSERT(sem != RT_NULL); + RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore); + RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent) == RT_FALSE); + + /* wakeup all suspended threads */ + _ipc_list_resume_all(&(sem->parent.suspend_thread)); + + /* delete semaphore object */ + rt_object_delete(&(sem->parent.parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_sem_delete); +#endif /* RT_USING_HEAP */ + + +/** + * @brief This function will take a semaphore, if the semaphore is unavailable, the thread shall wait for + * the semaphore up to a specified time. + * + * @note When this function is called, the count value of the sem->value will decrease 1 until it is equal to 0. + * When the sem->value is 0, it means that the semaphore is unavailable. At this time, it will suspend the + * thread preparing to take the semaphore. + * On the contrary, the rt_sem_release() function will increase the count value of sem->value by 1 each time. + * + * @see rt_sem_trytake() + * + * @param sem is a pointer to a semaphore object. + * + * @param time is a timeout period (unit: an OS tick). If the semaphore is unavailable, the thread will wait for + * the semaphore up to the amount of time specified by the argument. + * NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the + * semaphore is unavailable, the thread will be waitting forever. + * + * @return Return the operation status. ONLY When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the semaphore take failed. + * + * @warning This function can ONLY be called in the thread context. It MUST NOT BE called in interrupt context. + */ +rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time) +{ + register rt_base_t temp; + struct rt_thread *thread; + + /* parameter check */ + RT_ASSERT(sem != RT_NULL); + RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore); + + RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(sem->parent.parent))); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + RT_DEBUG_LOG(RT_DEBUG_IPC, ("thread %s take sem:%s, which value is: %d\n", + rt_thread_self()->name, + ((struct rt_object *)sem)->name, + sem->value)); + + if (sem->value > 0) + { + /* semaphore is available */ + sem->value --; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + } + else + { + /* no waiting, return with timeout */ + if (time == 0) + { + rt_hw_interrupt_enable(temp); + + return -RT_ETIMEOUT; + } + else + { + /* current context checking */ + RT_DEBUG_IN_THREAD_CONTEXT; + + /* semaphore is unavailable, push to suspend list */ + /* get current thread */ + thread = rt_thread_self(); + + /* reset thread error number */ + thread->error = RT_EOK; + + RT_DEBUG_LOG(RT_DEBUG_IPC, ("sem take: suspend thread - %s\n", + thread->name)); + + /* suspend thread */ + _ipc_list_suspend(&(sem->parent.suspend_thread), + thread, + sem->parent.parent.flag); + + /* has waiting time, start thread timer */ + if (time > 0) + { + RT_DEBUG_LOG(RT_DEBUG_IPC, ("set thread:%s to timer list\n", + thread->name)); + + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &time); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* do schedule */ + rt_schedule(); + + if (thread->error != RT_EOK) + { + return thread->error; + } + } + } + + RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(sem->parent.parent))); + + return RT_EOK; +} +RTM_EXPORT(rt_sem_take); + + +/** + * @brief This function will try to take a semaphore, if the semaphore is unavailable, the thread returns immediately. + * + * @note This function is very similar to the rt_sem_take() function, when the semaphore is not available, + * the rt_sem_trytake() function will return immediately without waiting for a timeout. + * In other words, rt_sem_trytake(sem) has the same effect as rt_sem_take(sem, 0). + * + * @see rt_sem_take() + * + * @param sem is a pointer to a semaphore object. + * + * @return Return the operation status. ONLY When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the semaphore take failed. + */ +rt_err_t rt_sem_trytake(rt_sem_t sem) +{ + return rt_sem_take(sem, RT_WAITING_NO); +} +RTM_EXPORT(rt_sem_trytake); + + +/** + * @brief This function will release a semaphore. If there is thread suspended on the semaphore, it will get resumed. + * + * @note If there are threads suspended on this semaphore, the first thread in the list of this semaphore object + * will be resumed, and a thread scheduling (rt_schedule) will be executed. + * If no threads are suspended on this semaphore, the count value sem->value of this semaphore will increase by 1. + * + * @param sem is a pointer to a semaphore object. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the semaphore release failed. + */ +rt_err_t rt_sem_release(rt_sem_t sem) +{ + register rt_base_t temp; + register rt_bool_t need_schedule; + + /* parameter check */ + RT_ASSERT(sem != RT_NULL); + RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore); + + RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(sem->parent.parent))); + + need_schedule = RT_FALSE; + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + RT_DEBUG_LOG(RT_DEBUG_IPC, ("thread %s releases sem:%s, which value is: %d\n", + rt_thread_self()->name, + ((struct rt_object *)sem)->name, + sem->value)); + + if (!rt_list_isempty(&sem->parent.suspend_thread)) + { + /* resume the suspended thread */ + _ipc_list_resume(&(sem->parent.suspend_thread)); + need_schedule = RT_TRUE; + } + else + { + if(sem->value < RT_SEM_VALUE_MAX) + { + sem->value ++; /* increase value */ + } + else + { + rt_hw_interrupt_enable(temp); /* enable interrupt */ + return -RT_EFULL; /* value overflowed */ + } + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* resume a thread, re-schedule */ + if (need_schedule == RT_TRUE) + rt_schedule(); + + return RT_EOK; +} +RTM_EXPORT(rt_sem_release); + + +/** + * @brief This function will set some extra attributions of a semaphore object. + * + * @note Currently this function only supports the RT_IPC_CMD_RESET command to reset the semaphore. + * + * @param sem is a pointer to a semaphore object. + * + * @param cmd is a command word used to configure some attributions of the semaphore. + * + * @param arg is the argument of the function to execute the command. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that this function failed to execute. + */ +rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg) +{ + rt_ubase_t level; + + /* parameter check */ + RT_ASSERT(sem != RT_NULL); + RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore); + + if (cmd == RT_IPC_CMD_RESET) + { + rt_ubase_t value; + + /* get value */ + value = (rt_ubase_t)arg; + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* resume all waiting thread */ + _ipc_list_resume_all(&sem->parent.suspend_thread); + + /* set new value */ + sem->value = (rt_uint16_t)value; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + rt_schedule(); + + return RT_EOK; + } + + return -RT_ERROR; +} +RTM_EXPORT(rt_sem_control); + +/**@}*/ +#endif /* RT_USING_SEMAPHORE */ + +#ifdef RT_USING_MUTEX +/** + * @addtogroup mutex + */ + +/**@{*/ + +/** + * @brief Initialize a static mutex object. + * + * @note For the static mutex object, its memory space is allocated by the compiler during compiling, + * and shall placed on the read-write data segment or on the uninitialized data segment. + * By contrast, the rt_mutex_create() function will automatically allocate memory space + * and initialize the mutex. + * + * @see rt_mutex_create() + * + * @param mutex is a pointer to the mutex to initialize. It is assumed that storage for the mutex will be + * allocated in your application. + * + * @param name is a pointer to the name that given to the mutex. + * + * @param flag is the mutex flag, which determines the queuing way of how multiple threads wait + * when the mutex is not available. + * NOTE: The mutex flag can ONLY be RT_IPC_FLAG_PRIO. Using RT_IPC_FLAG_FIFO will seriously affect + * the real-time performance of the system. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it represents the initialization failed. + * + * @warning This function can ONLY be called from threads. + */ +rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag) +{ + (void)flag; + + /* parameter check */ + RT_ASSERT(mutex != RT_NULL); + + /* initialize object */ + rt_object_init(&(mutex->parent.parent), RT_Object_Class_Mutex, name); + + /* initialize ipc object */ + _ipc_object_init(&(mutex->parent)); + + mutex->value = 1; + mutex->owner = RT_NULL; + mutex->original_priority = 0xFF; + mutex->hold = 0; + + /* flag can only be RT_IPC_FLAG_PRIO. RT_IPC_FLAG_FIFO cannot solve the unbounded priority inversion problem */ + mutex->parent.parent.flag = RT_IPC_FLAG_PRIO; + + return RT_EOK; +} +RTM_EXPORT(rt_mutex_init); + + +/** + * @brief This function will detach a static mutex object. + * + * @note This function is used to detach a static mutex object which is initialized by rt_mutex_init() function. + * By contrast, the rt_mutex_delete() function will delete a mutex object. + * When the mutex is successfully detached, it will resume all suspended threads in the mutex list. + * + * @see rt_mutex_delete() + * + * @param mutex is a pointer to a mutex object to be detached. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it means that the mutex detach failed. + * + * @warning This function can ONLY detach a static mutex initialized by the rt_mutex_init() function. + * If the mutex is created by the rt_mutex_create() function, you MUST NOT USE this function to detach it, + * ONLY USE the rt_mutex_delete() function to complete the deletion. + */ +rt_err_t rt_mutex_detach(rt_mutex_t mutex) +{ + /* parameter check */ + RT_ASSERT(mutex != RT_NULL); + RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); + RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent)); + + /* wakeup all suspended threads */ + _ipc_list_resume_all(&(mutex->parent.suspend_thread)); + + /* detach mutex object */ + rt_object_detach(&(mutex->parent.parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_mutex_detach); + +#ifdef RT_USING_HEAP +/** + * @brief This function will create a mutex object. + * + * @note For the mutex object, its memory space is automatically allocated. + * By contrast, the rt_mutex_init() function will initialize a static mutex object. + * + * @see rt_mutex_init() + * + * @param name is a pointer to the name that given to the mutex. + * + * @param flag is the mutex flag, which determines the queuing way of how multiple threads wait + * when the mutex is not available. + * NOTE: The mutex flag can ONLY be RT_IPC_FLAG_PRIO. Using RT_IPC_FLAG_FIFO will seriously affect + * the real-time performance of the system. + * + * @return Return a pointer to the mutex object. When the return value is RT_NULL, it means the creation failed. + * + * @warning This function can ONLY be called from threads. + */ +rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag) +{ + struct rt_mutex *mutex; + (void)flag; + + RT_DEBUG_NOT_IN_INTERRUPT; + + /* allocate object */ + mutex = (rt_mutex_t)rt_object_allocate(RT_Object_Class_Mutex, name); + if (mutex == RT_NULL) + return mutex; + + /* initialize ipc object */ + _ipc_object_init(&(mutex->parent)); + + mutex->value = 1; + mutex->owner = RT_NULL; + mutex->original_priority = 0xFF; + mutex->hold = 0; + + /* flag can only be RT_IPC_FLAG_PRIO. RT_IPC_FLAG_FIFO cannot solve the unbounded priority inversion problem */ + mutex->parent.parent.flag = RT_IPC_FLAG_PRIO; + + return mutex; +} +RTM_EXPORT(rt_mutex_create); + + +/** + * @brief This function will delete a mutex object and release this memory space. + * + * @note This function is used to delete a mutex object which is created by the rt_mutex_create() function. + * By contrast, the rt_mutex_detach() function will detach a static mutex object. + * When the mutex is successfully deleted, it will resume all suspended threads in the mutex list. + * + * @see rt_mutex_detach() + * + * @param mutex is a pointer to a mutex object to be deleted. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mutex detach failed. + * + * @warning This function can ONLY delete a mutex initialized by the rt_mutex_create() function. + * If the mutex is initialized by the rt_mutex_init() function, you MUST NOT USE this function to delete it, + * ONLY USE the rt_mutex_detach() function to complete the detachment. + */ +rt_err_t rt_mutex_delete(rt_mutex_t mutex) +{ + RT_DEBUG_NOT_IN_INTERRUPT; + + /* parameter check */ + RT_ASSERT(mutex != RT_NULL); + RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); + RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent) == RT_FALSE); + + /* wakeup all suspended threads */ + _ipc_list_resume_all(&(mutex->parent.suspend_thread)); + + /* delete mutex object */ + rt_object_delete(&(mutex->parent.parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_mutex_delete); +#endif /* RT_USING_HEAP */ + + +/** + * @brief This function will take a mutex, if the mutex is unavailable, the thread shall wait for + * the mutex up to a specified time. + * + * @note When this function is called, the count value of the sem->value will decrease 1 until it is equal to 0. + * When the sem->value is 0, it means that the mutex is unavailable. At this time, it will suspend the + * thread preparing to take the mutex. + * On the contrary, the rt_sem_release() function will increase the count value of sem->value by 1 each time. + * + * @see rt_mutex_trytake() + * + * @param mutex is a pointer to a mutex object. + * + * @param time is a timeout period (unit: an OS tick). If the mutex is unavailable, the thread will wait for + * the mutex up to the amount of time specified by the argument. + * NOTE: Generally, we set this parameter to RT_WAITING_FOREVER, which means that when the mutex is unavailable, + * the thread will be waitting forever. + * + * @return Return the operation status. ONLY When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mutex take failed. + * + * @warning This function can ONLY be called in the thread context. It MUST NOT BE called in interrupt context. + */ +rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time) +{ + register rt_base_t temp; + struct rt_thread *thread; + + /* this function must not be used in interrupt even if time = 0 */ + RT_DEBUG_IN_THREAD_CONTEXT; + + /* parameter check */ + RT_ASSERT(mutex != RT_NULL); + RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); + + /* get current thread */ + thread = rt_thread_self(); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mutex->parent.parent))); + + RT_DEBUG_LOG(RT_DEBUG_IPC, + ("mutex_take: current thread %s, mutex value: %d, hold: %d\n", + thread->name, mutex->value, mutex->hold)); + + /* reset thread error */ + thread->error = RT_EOK; + + if (mutex->owner == thread) + { + if(mutex->hold < RT_MUTEX_HOLD_MAX) + { + /* it's the same thread */ + mutex->hold ++; + } + else + { + rt_hw_interrupt_enable(temp); /* enable interrupt */ + return -RT_EFULL; /* value overflowed */ + } + } + else + { +#ifdef RT_USING_SIGNALS +__again: +#endif /* RT_USING_SIGNALS */ + /* The value of mutex is 1 in initial status. Therefore, if the + * value is great than 0, it indicates the mutex is avaible. + */ + if (mutex->value > 0) + { + /* mutex is available */ + mutex->value --; + + /* set mutex owner and original priority */ + mutex->owner = thread; + mutex->original_priority = thread->current_priority; + if(mutex->hold < RT_MUTEX_HOLD_MAX) + { + mutex->hold ++; + } + else + { + rt_hw_interrupt_enable(temp); /* enable interrupt */ + return -RT_EFULL; /* value overflowed */ + } + } + else + { + /* no waiting, return with timeout */ + if (time == 0) + { + /* set error as timeout */ + thread->error = -RT_ETIMEOUT; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return -RT_ETIMEOUT; + } + else + { + /* mutex is unavailable, push to suspend list */ + RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_take: suspend thread: %s\n", + thread->name)); + + /* change the owner thread priority of mutex */ + if (thread->current_priority < mutex->owner->current_priority) + { + /* change the owner thread priority */ + rt_thread_control(mutex->owner, + RT_THREAD_CTRL_CHANGE_PRIORITY, + &thread->current_priority); + } + + /* suspend current thread */ + _ipc_list_suspend(&(mutex->parent.suspend_thread), + thread, + mutex->parent.parent.flag); + + /* has waiting time, start thread timer */ + if (time > 0) + { + RT_DEBUG_LOG(RT_DEBUG_IPC, + ("mutex_take: start the timer of thread:%s\n", + thread->name)); + + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &time); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* do schedule */ + rt_schedule(); + + if (thread->error != RT_EOK) + { +#ifdef RT_USING_SIGNALS + /* interrupt by signal, try it again */ + if (thread->error == -RT_EINTR) goto __again; +#endif /* RT_USING_SIGNALS */ + + /* return error */ + return thread->error; + } + else + { + /* the mutex is taken successfully. */ + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + } + } + } + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mutex->parent.parent))); + + return RT_EOK; +} +RTM_EXPORT(rt_mutex_take); + + +/** + * @brief This function will try to take a mutex, if the mutex is unavailable, the thread returns immediately. + * + * @note This function is very similar to the rt_mutex_take() function, when the mutex is not available, + * except that rt_mutex_trytake() will return immediately without waiting for a timeout + * when the mutex is not available. + * In other words, rt_mutex_trytake(mutex) has the same effect as rt_mutex_take(mutex, 0). + * + * @see rt_mutex_take() + * + * @param mutex is a pointer to a mutex object. + * + * @return Return the operation status. ONLY When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mutex take failed. + */ +rt_err_t rt_mutex_trytake(rt_mutex_t mutex) +{ + return rt_mutex_take(mutex, RT_WAITING_NO); +} +RTM_EXPORT(rt_mutex_trytake); + + +/** + * @brief This function will release a mutex. If there is thread suspended on the mutex, the thread will be resumed. + * + * @note If there are threads suspended on this mutex, the first thread in the list of this mutex object + * will be resumed, and a thread scheduling (rt_schedule) will be executed. + * If no threads are suspended on this mutex, the count value mutex->value of this mutex will increase by 1. + * + * @param mutex is a pointer to a mutex object. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mutex release failed. + */ +rt_err_t rt_mutex_release(rt_mutex_t mutex) +{ + register rt_base_t temp; + struct rt_thread *thread; + rt_bool_t need_schedule; + + /* parameter check */ + RT_ASSERT(mutex != RT_NULL); + RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); + + need_schedule = RT_FALSE; + + /* only thread could release mutex because we need test the ownership */ + RT_DEBUG_IN_THREAD_CONTEXT; + + /* get current thread */ + thread = rt_thread_self(); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + RT_DEBUG_LOG(RT_DEBUG_IPC, + ("mutex_release:current thread %s, mutex value: %d, hold: %d\n", + thread->name, mutex->value, mutex->hold)); + + RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mutex->parent.parent))); + + /* mutex only can be released by owner */ + if (thread != mutex->owner) + { + thread->error = -RT_ERROR; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return -RT_ERROR; + } + + /* decrease hold */ + mutex->hold --; + /* if no hold */ + if (mutex->hold == 0) + { + /* change the owner thread to original priority */ + if (mutex->original_priority != mutex->owner->current_priority) + { + rt_thread_control(mutex->owner, + RT_THREAD_CTRL_CHANGE_PRIORITY, + &(mutex->original_priority)); + } + + /* wakeup suspended thread */ + if (!rt_list_isempty(&mutex->parent.suspend_thread)) + { + /* get suspended thread */ + thread = rt_list_entry(mutex->parent.suspend_thread.next, + struct rt_thread, + tlist); + + RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_release: resume thread: %s\n", + thread->name)); + + /* set new owner and priority */ + mutex->owner = thread; + mutex->original_priority = thread->current_priority; + + if(mutex->hold < RT_MUTEX_HOLD_MAX) + { + mutex->hold ++; + } + else + { + rt_hw_interrupt_enable(temp); /* enable interrupt */ + return -RT_EFULL; /* value overflowed */ + } + + /* resume thread */ + _ipc_list_resume(&(mutex->parent.suspend_thread)); + + need_schedule = RT_TRUE; + } + else + { + if(mutex->value < RT_MUTEX_VALUE_MAX) + { + /* increase value */ + mutex->value ++; + } + else + { + rt_hw_interrupt_enable(temp); /* enable interrupt */ + return -RT_EFULL; /* value overflowed */ + } + + /* clear owner */ + mutex->owner = RT_NULL; + mutex->original_priority = 0xff; + } + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* perform a schedule */ + if (need_schedule == RT_TRUE) + rt_schedule(); + + return RT_EOK; +} +RTM_EXPORT(rt_mutex_release); + + +/** + * @brief This function will set some extra attributions of a mutex object. + * + * @note Currently this function does not implement the control function. + * + * @param mutex is a pointer to a mutex object. + * + * @param cmd is a command word used to configure some attributions of the mutex. + * + * @param arg is the argument of the function to execute the command. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that this function failed to execute. + */ +rt_err_t rt_mutex_control(rt_mutex_t mutex, int cmd, void *arg) +{ + /* parameter check */ + RT_ASSERT(mutex != RT_NULL); + RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); + + return -RT_ERROR; +} +RTM_EXPORT(rt_mutex_control); + +/**@}*/ +#endif /* RT_USING_MUTEX */ + +#ifdef RT_USING_EVENT +/** + * @addtogroup event + */ + +/**@{*/ + +/** + * @brief The function will initialize a static event object. + * + * @note For the static event object, its memory space is allocated by the compiler during compiling, + * and shall placed on the read-write data segment or on the uninitialized data segment. + * By contrast, the rt_event_create() function will allocate memory space automatically + * and initialize the event. + * + * @see rt_event_create() + * + * @param event is a pointer to the event to initialize. It is assumed that storage for the event + * will be allocated in your application. + * + * @param name is a pointer to the name that given to the event. + * + * @param value is the initial value for the event. + * If want to share resources, you should initialize the value as the number of available resources. + * If want to signal the occurrence of an event, you should initialize the value as 0. + * + * @param flag is the event flag, which determines the queuing way of how multiple threads wait + * when the event is not available. + * The event flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this event will become non-real-time threads. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it represents the initialization failed. + * + * @warning This function can ONLY be called from threads. + */ +rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag) +{ + /* parameter check */ + RT_ASSERT(event != RT_NULL); + + /* initialize object */ + rt_object_init(&(event->parent.parent), RT_Object_Class_Event, name); + + /* set parent flag */ + event->parent.parent.flag = flag; + + /* initialize ipc object */ + _ipc_object_init(&(event->parent)); + + /* initialize event */ + event->set = 0; + + return RT_EOK; +} +RTM_EXPORT(rt_event_init); + + +/** + * @brief This function will detach a static event object. + * + * @note This function is used to detach a static event object which is initialized by rt_event_init() function. + * By contrast, the rt_event_delete() function will delete an event object. + * When the event is successfully detached, it will resume all suspended threads in the event list. + * + * @see rt_event_delete() + * + * @param event is a pointer to an event object to be detached. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it means that the event detach failed. + * + * @warning This function can ONLY detach a static event initialized by the rt_event_init() function. + * If the event is created by the rt_event_create() function, you MUST NOT USE this function to detach it, + * ONLY USE the rt_event_delete() function to complete the deletion. + */ +rt_err_t rt_event_detach(rt_event_t event) +{ + /* parameter check */ + RT_ASSERT(event != RT_NULL); + RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event); + RT_ASSERT(rt_object_is_systemobject(&event->parent.parent)); + + /* resume all suspended thread */ + _ipc_list_resume_all(&(event->parent.suspend_thread)); + + /* detach event object */ + rt_object_detach(&(event->parent.parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_event_detach); + +#ifdef RT_USING_HEAP +/** + * @brief Creating an event object. + * + * @note For the event object, its memory space is allocated automatically. + * By contrast, the rt_event_init() function will initialize a static event object. + * + * @see rt_event_init() + * + * @param name is a pointer to the name that given to the event. + * + * @param flag is the event flag, which determines the queuing way of how multiple threads wait when the event + * is not available. + * The event flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this event will become non-real-time threads. + * + * @return Return a pointer to the event object. When the return value is RT_NULL, it means the creation failed. + * + * @warning This function can ONLY be called from threads. + */ +rt_event_t rt_event_create(const char *name, rt_uint8_t flag) +{ + rt_event_t event; + + RT_DEBUG_NOT_IN_INTERRUPT; + + /* allocate object */ + event = (rt_event_t)rt_object_allocate(RT_Object_Class_Event, name); + if (event == RT_NULL) + return event; + + /* set parent */ + event->parent.parent.flag = flag; + + /* initialize ipc object */ + _ipc_object_init(&(event->parent)); + + /* initialize event */ + event->set = 0; + + return event; +} +RTM_EXPORT(rt_event_create); + + +/** + * @brief This function will delete an event object and release the memory space. + * + * @note This function is used to delete an event object which is created by the rt_event_create() function. + * By contrast, the rt_event_detach() function will detach a static event object. + * When the event is successfully deleted, it will resume all suspended threads in the event list. + * + * @see rt_event_detach() + * + * @param event is a pointer to an event object to be deleted. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the event detach failed. + * + * @warning This function can ONLY delete an event initialized by the rt_event_create() function. + * If the event is initialized by the rt_event_init() function, you MUST NOT USE this function to delete it, + * ONLY USE the rt_event_detach() function to complete the detachment. + */ +rt_err_t rt_event_delete(rt_event_t event) +{ + /* parameter check */ + RT_ASSERT(event != RT_NULL); + RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event); + RT_ASSERT(rt_object_is_systemobject(&event->parent.parent) == RT_FALSE); + + RT_DEBUG_NOT_IN_INTERRUPT; + + /* resume all suspended thread */ + _ipc_list_resume_all(&(event->parent.suspend_thread)); + + /* delete event object */ + rt_object_delete(&(event->parent.parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_event_delete); +#endif /* RT_USING_HEAP */ + + +/** + * @brief This function will send an event to the event object. + * If there is a thread suspended on the event, the thread will be resumed. + * + * @note When using this function, you need to use the parameter (set) to specify the event flag of the event object, + * then the function will traverse the list of suspended threads waiting on the event object. + * If there is a thread suspended on the event, and the thread's event_info and the event flag of + * the current event object matches, the thread will be resumed. + * + * @param event is a pointer to the event object to be sent. + * + * @param set is a flag that you will set for this event's flag. + * You can set an event flag, or you can set multiple flags through OR logic operation. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the event detach failed. + */ +rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set) +{ + struct rt_list_node *n; + struct rt_thread *thread; + register rt_ubase_t level; + register rt_base_t status; + rt_bool_t need_schedule; + + /* parameter check */ + RT_ASSERT(event != RT_NULL); + RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event); + + if (set == 0) + return -RT_ERROR; + + need_schedule = RT_FALSE; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* set event */ + event->set |= set; + + RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(event->parent.parent))); + + if (!rt_list_isempty(&event->parent.suspend_thread)) + { + /* search thread list to resume thread */ + n = event->parent.suspend_thread.next; + while (n != &(event->parent.suspend_thread)) + { + /* get thread */ + thread = rt_list_entry(n, struct rt_thread, tlist); + + status = -RT_ERROR; + if (thread->event_info & RT_EVENT_FLAG_AND) + { + if ((thread->event_set & event->set) == thread->event_set) + { + /* received an AND event */ + status = RT_EOK; + } + } + else if (thread->event_info & RT_EVENT_FLAG_OR) + { + if (thread->event_set & event->set) + { + /* save the received event set */ + thread->event_set = thread->event_set & event->set; + + /* received an OR event */ + status = RT_EOK; + } + } + else + { + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return -RT_EINVAL; + } + + /* move node to the next */ + n = n->next; + + /* condition is satisfied, resume thread */ + if (status == RT_EOK) + { + /* clear event */ + if (thread->event_info & RT_EVENT_FLAG_CLEAR) + event->set &= ~thread->event_set; + + /* resume thread, and thread list breaks out */ + rt_thread_resume(thread); + + /* need do a scheduling */ + need_schedule = RT_TRUE; + } + } + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + /* do a schedule */ + if (need_schedule == RT_TRUE) + rt_schedule(); + + return RT_EOK; +} +RTM_EXPORT(rt_event_send); + + +/** + * @brief This function will receive an event from event object. if the event is unavailable, the thread shall wait for + * the event up to a specified time. + * + * @note If there are threads suspended on this semaphore, the first thread in the list of this semaphore object + * will be resumed, and a thread scheduling (rt_schedule) will be executed. + * If no threads are suspended on this semaphore, the count value sem->value of this semaphore will increase by 1. + * + * @param event is a pointer to the event object to be received. + * + * @param set is a flag that you will set for this event's flag. + * You can set an event flag, or you can set multiple flags through OR logic operation. + * + * @param option is the option of this receiving event, it indicates how the receiving event is operated. + * The option can be one or more of the following values, When selecting multiple values,use logical OR to operate. + * (NOTE: RT_EVENT_FLAG_OR and RT_EVENT_FLAG_AND can only select one): + * + * + * RT_EVENT_FLAG_OR The thread select to use logical OR to receive the event. + * + * RT_EVENT_FLAG_AND The thread select to use logical OR to receive the event. + * + * RT_EVENT_FLAG_CLEAR When the thread receives the corresponding event, the function + * determines whether to clear the event flag. + * + * @param timeout is a timeout period (unit: an OS tick). + * + * @param recved is a pointer to the received event. If you don't care about this value, you can use RT_NULL to set. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the semaphore release failed. + */ +rt_err_t rt_event_recv(rt_event_t event, + rt_uint32_t set, + rt_uint8_t option, + rt_int32_t timeout, + rt_uint32_t *recved) +{ + struct rt_thread *thread; + register rt_ubase_t level; + register rt_base_t status; + + RT_DEBUG_IN_THREAD_CONTEXT; + + /* parameter check */ + RT_ASSERT(event != RT_NULL); + RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event); + + if (set == 0) + return -RT_ERROR; + + /* initialize status */ + status = -RT_ERROR; + /* get current thread */ + thread = rt_thread_self(); + /* reset thread error */ + thread->error = RT_EOK; + + RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(event->parent.parent))); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* check event set */ + if (option & RT_EVENT_FLAG_AND) + { + if ((event->set & set) == set) + status = RT_EOK; + } + else if (option & RT_EVENT_FLAG_OR) + { + if (event->set & set) + status = RT_EOK; + } + else + { + /* either RT_EVENT_FLAG_AND or RT_EVENT_FLAG_OR should be set */ + RT_ASSERT(0); + } + + if (status == RT_EOK) + { + /* set received event */ + if (recved) + *recved = (event->set & set); + + /* fill thread event info */ + thread->event_set = (event->set & set); + thread->event_info = option; + + /* received event */ + if (option & RT_EVENT_FLAG_CLEAR) + event->set &= ~set; + } + else if (timeout == 0) + { + /* no waiting */ + thread->error = -RT_ETIMEOUT; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return -RT_ETIMEOUT; + } + else + { + /* fill thread event info */ + thread->event_set = set; + thread->event_info = option; + + /* put thread to suspended thread list */ + _ipc_list_suspend(&(event->parent.suspend_thread), + thread, + event->parent.parent.flag); + + /* if there is a waiting timeout, active thread timer */ + if (timeout > 0) + { + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &timeout); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + /* do a schedule */ + rt_schedule(); + + if (thread->error != RT_EOK) + { + /* return error */ + return thread->error; + } + + /* received an event, disable interrupt to protect */ + level = rt_hw_interrupt_disable(); + + /* set received event */ + if (recved) + *recved = thread->event_set; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(event->parent.parent))); + + return thread->error; +} +RTM_EXPORT(rt_event_recv); + + +/** + * @brief This function will set some extra attributions of an event object. + * + * @note Currently this function only supports the RT_IPC_CMD_RESET command to reset the event. + * + * @param event is a pointer to an event object. + * + * @param cmd is a command word used to configure some attributions of the event. + * + * @param arg is the argument of the function to execute the command. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that this function failed to execute. + */ +rt_err_t rt_event_control(rt_event_t event, int cmd, void *arg) +{ + rt_ubase_t level; + + /* parameter check */ + RT_ASSERT(event != RT_NULL); + RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event); + + if (cmd == RT_IPC_CMD_RESET) + { + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* resume all waiting thread */ + _ipc_list_resume_all(&event->parent.suspend_thread); + + /* initialize event set */ + event->set = 0; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + rt_schedule(); + + return RT_EOK; + } + + return -RT_ERROR; +} +RTM_EXPORT(rt_event_control); + +/**@}*/ +#endif /* RT_USING_EVENT */ + +#ifdef RT_USING_MAILBOX +/** + * @addtogroup mailbox + */ + +/**@{*/ + +/** + * @brief Initialize a static mailbox object. + * + * @note For the static mailbox object, its memory space is allocated by the compiler during compiling, + * and shall placed on the read-write data segment or on the uninitialized data segment. + * By contrast, the rt_mb_create() function will allocate memory space automatically and initialize the mailbox. + * + * @see rt_mb_create() + * + * @param mb is a pointer to the mailbox to initialize. + * It is assumed that storage for the mailbox will be allocated in your application. + * + * @param name is a pointer to the name that given to the mailbox. + * + * @param msgpoll is a pointer to the starting address of the memory space you allocated for the mailbox in advance. + * In other words, msgpoll is a pointer to the mailbox buffer of the starting address. + * + * @param size is the maximum number of mails in the mailbox. + * For example, when the mailbox buffer capacity is N, size is N/4. + * + * @param flag is the mailbox flag, which determines the queuing way of how multiple threads wait + * when the mailbox is not available. + * The mailbox flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this mailbox will become non-real-time threads. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it represents the initialization failed. + * + * @warning This function can ONLY be called from threads. + */ +rt_err_t rt_mb_init(rt_mailbox_t mb, + const char *name, + void *msgpool, + rt_size_t size, + rt_uint8_t flag) +{ + RT_ASSERT(mb != RT_NULL); + + /* initialize object */ + rt_object_init(&(mb->parent.parent), RT_Object_Class_MailBox, name); + + /* set parent flag */ + mb->parent.parent.flag = flag; + + /* initialize ipc object */ + _ipc_object_init(&(mb->parent)); + + /* initialize mailbox */ + mb->msg_pool = (rt_ubase_t *)msgpool; + mb->size = size; + mb->entry = 0; + mb->in_offset = 0; + mb->out_offset = 0; + + /* initialize an additional list of sender suspend thread */ + rt_list_init(&(mb->suspend_sender_thread)); + + return RT_EOK; +} +RTM_EXPORT(rt_mb_init); + + +/** + * @brief This function will detach a static mailbox object. + * + * @note This function is used to detach a static mailbox object which is initialized by rt_mb_init() function. + * By contrast, the rt_mb_delete() function will delete a mailbox object. + * When the mailbox is successfully detached, it will resume all suspended threads in the mailbox list. + * + * @see rt_mb_delete() + * + * @param mb is a pointer to a mailbox object to be detached. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it means that the mailbox detach failed. + * + * @warning This function can ONLY detach a static mailbox initialized by the rt_mb_init() function. + * If the mailbox is created by the rt_mb_create() function, you MUST NOT USE this function to detach it, + * ONLY USE the rt_mb_delete() function to complete the deletion. + */ +rt_err_t rt_mb_detach(rt_mailbox_t mb) +{ + /* parameter check */ + RT_ASSERT(mb != RT_NULL); + RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); + RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent)); + + /* resume all suspended thread */ + _ipc_list_resume_all(&(mb->parent.suspend_thread)); + /* also resume all mailbox private suspended thread */ + _ipc_list_resume_all(&(mb->suspend_sender_thread)); + + /* detach mailbox object */ + rt_object_detach(&(mb->parent.parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_mb_detach); + +#ifdef RT_USING_HEAP +/** + * @brief Creating a mailbox object. + * + * @note For the mailbox object, its memory space is allocated automatically. + * By contrast, the rt_mb_init() function will initialize a static mailbox object. + * + * @see rt_mb_init() + * + * @param name is a pointer that given to the mailbox. + * + * @param size is the maximum number of mails in the mailbox. + * For example, when mailbox buffer capacity is N, size is N/4. + * + * @param flag is the mailbox flag, which determines the queuing way of how multiple threads wait + * when the mailbox is not available. + * The mailbox flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this mailbox will become non-real-time threads. + * + * @return Return a pointer to the mailbox object. When the return value is RT_NULL, it means the creation failed. + * + * @warning This function can ONLY be called from threads. + */ +rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag) +{ + rt_mailbox_t mb; + + RT_DEBUG_NOT_IN_INTERRUPT; + + /* allocate object */ + mb = (rt_mailbox_t)rt_object_allocate(RT_Object_Class_MailBox, name); + if (mb == RT_NULL) + return mb; + + /* set parent */ + mb->parent.parent.flag = flag; + + /* initialize ipc object */ + _ipc_object_init(&(mb->parent)); + + /* initialize mailbox */ + mb->size = size; + mb->msg_pool = (rt_ubase_t *)RT_KERNEL_MALLOC(mb->size * sizeof(rt_ubase_t)); + if (mb->msg_pool == RT_NULL) + { + /* delete mailbox object */ + rt_object_delete(&(mb->parent.parent)); + + return RT_NULL; + } + mb->entry = 0; + mb->in_offset = 0; + mb->out_offset = 0; + + /* initialize an additional list of sender suspend thread */ + rt_list_init(&(mb->suspend_sender_thread)); + + return mb; +} +RTM_EXPORT(rt_mb_create); + + +/** + * @brief This function will delete a mailbox object and release the memory space. + * + * @note This function is used to delete a mailbox object which is created by the rt_mb_create() function. + * By contrast, the rt_mb_detach() function will detach a static mailbox object. + * When the mailbox is successfully deleted, it will resume all suspended threads in the mailbox list. + * + * @see rt_mb_detach() + * + * @param mb is a pointer to a mailbox object to be deleted. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox detach failed. + * + * @warning This function can only delete mailbox created by the rt_mb_create() function. + * If the mailbox is initialized by the rt_mb_init() function, you MUST NOT USE this function to delete it, + * ONLY USE the rt_mb_detach() function to complete the detachment. + */ +rt_err_t rt_mb_delete(rt_mailbox_t mb) +{ + RT_DEBUG_NOT_IN_INTERRUPT; + + /* parameter check */ + RT_ASSERT(mb != RT_NULL); + RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); + RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent) == RT_FALSE); + + /* resume all suspended thread */ + _ipc_list_resume_all(&(mb->parent.suspend_thread)); + + /* also resume all mailbox private suspended thread */ + _ipc_list_resume_all(&(mb->suspend_sender_thread)); + + /* free mailbox pool */ + RT_KERNEL_FREE(mb->msg_pool); + + /* delete mailbox object */ + rt_object_delete(&(mb->parent.parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_mb_delete); +#endif /* RT_USING_HEAP */ + + +/** + * @brief This function will send an mail to the mailbox object. If there is a thread suspended on the mailbox, + * the thread will be resumed. + * + * @note When using this function to send a mail, if the mailbox if fully used, the current thread will + * wait for a timeout. If the set timeout time is reached and there is still no space available, + * the sending thread will be resumed and an error code will be returned. + * By contrast, the rt_mb_send() function will return an error code immediately without waiting time + * when the mailbox if fully used. + * + * @see rt_mb_send() + * + * @param mb is a pointer to the mailbox object to be sent. + * + * @param value is a value to the content of the mail you want to send. + * + * @param timeout is a timeout period (unit: an OS tick). + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox detach failed. + * + * @warning This function can be called in interrupt context and thread context. + */ +rt_err_t rt_mb_send_wait(rt_mailbox_t mb, + rt_ubase_t value, + rt_int32_t timeout) +{ + struct rt_thread *thread; + register rt_ubase_t temp; + rt_uint32_t tick_delta; + + /* parameter check */ + RT_ASSERT(mb != RT_NULL); + RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); + + /* initialize delta tick */ + tick_delta = 0; + /* get current thread */ + thread = rt_thread_self(); + + RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mb->parent.parent))); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* for non-blocking call */ + if (mb->entry == mb->size && timeout == 0) + { + rt_hw_interrupt_enable(temp); + return -RT_EFULL; + } + + /* mailbox is full */ + while (mb->entry == mb->size) + { + /* reset error number in thread */ + thread->error = RT_EOK; + + /* no waiting, return timeout */ + if (timeout == 0) + { + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return -RT_EFULL; + } + + RT_DEBUG_IN_THREAD_CONTEXT; + /* suspend current thread */ + _ipc_list_suspend(&(mb->suspend_sender_thread), + thread, + mb->parent.parent.flag); + + /* has waiting time, start thread timer */ + if (timeout > 0) + { + /* get the start tick of timer */ + tick_delta = rt_tick_get(); + + RT_DEBUG_LOG(RT_DEBUG_IPC, ("mb_send_wait: start timer of thread:%s\n", + thread->name)); + + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &timeout); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* re-schedule */ + rt_schedule(); + + /* resume from suspend state */ + if (thread->error != RT_EOK) + { + /* return error */ + return thread->error; + } + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* if it's not waiting forever and then re-calculate timeout tick */ + if (timeout > 0) + { + tick_delta = rt_tick_get() - tick_delta; + timeout -= tick_delta; + if (timeout < 0) + timeout = 0; + } + } + + /* set ptr */ + mb->msg_pool[mb->in_offset] = value; + /* increase input offset */ + ++ mb->in_offset; + if (mb->in_offset >= mb->size) + mb->in_offset = 0; + + if(mb->entry < RT_MB_ENTRY_MAX) + { + /* increase message entry */ + mb->entry ++; + } + else + { + rt_hw_interrupt_enable(temp); /* enable interrupt */ + return -RT_EFULL; /* value overflowed */ + } + + /* resume suspended thread */ + if (!rt_list_isempty(&mb->parent.suspend_thread)) + { + _ipc_list_resume(&(mb->parent.suspend_thread)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + rt_schedule(); + + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return RT_EOK; +} +RTM_EXPORT(rt_mb_send_wait); + + +/** + * @brief This function will send an mail to the mailbox object. If there is a thread suspended on the mailbox, + * the thread will be resumed. + * + * @note When using this function to send a mail, if the mailbox is fully used, this function will return an error + * code immediately without waiting time. + * By contrast, the rt_mb_send_wait() function is set a timeout to wait for the mail to be sent. + * + * @see rt_mb_send_wait() + * + * @param mb is a pointer to the mailbox object to be sent. + * + * @param value is a value to the content of the mail you want to send. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox detach failed. + */ +rt_err_t rt_mb_send(rt_mailbox_t mb, rt_ubase_t value) +{ + return rt_mb_send_wait(mb, value, 0); +} +RTM_EXPORT(rt_mb_send); + + +/** + * @brief This function will send an urgent mail to the mailbox object. + * + * @note This function is almost the same as the rt_mb_send() function. The only difference is that + * when sending an urgent mail, the mail will be placed at the head of the mail queue so that + * the recipient can receive the urgent mail first. + * + * @see rt_mb_send() + * + * @param mb is a pointer to the mailbox object to be sent. + * + * @param value is the content of the mail you want to send. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox detach failed. + */ +rt_err_t rt_mb_urgent(rt_mailbox_t mb, rt_ubase_t value) +{ + register rt_ubase_t temp; + + /* parameter check */ + RT_ASSERT(mb != RT_NULL); + RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); + + RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mb->parent.parent))); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + if (mb->entry == mb->size) + { + rt_hw_interrupt_enable(temp); + return -RT_EFULL; + } + + /* rewind to the previous position */ + if (mb->out_offset > 0) + { + mb->out_offset --; + } + else + { + mb->out_offset = mb->size - 1; + } + + /* set ptr */ + mb->msg_pool[mb->out_offset] = value; + + /* increase message entry */ + mb->entry ++; + + /* resume suspended thread */ + if (!rt_list_isempty(&mb->parent.suspend_thread)) + { + _ipc_list_resume(&(mb->parent.suspend_thread)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + rt_schedule(); + + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return RT_EOK; +} +RTM_EXPORT(rt_mb_urgent); + + +/** + * @brief This function will receive a mail from mailbox object, if there is no mail in mailbox object, + * the thread shall wait for a specified time. + * + * @note Only when there is mail in the mailbox, the receiving thread can get the mail immediately and + * return RT_EOK, otherwise the receiving thread will be suspended until the set timeout. If the mail + * is still not received within the specified time, it will return-RT_ETIMEOUT. + * + * @param mb is a pointer to the mailbox object to be received. + * + * @param value is a flag that you will set for this mailbox's flag. + * You can set an mailbox flag, or you can set multiple flags through OR logic operations. + * + * @param timeout is a timeout period (unit: an OS tick). + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox release failed. + */ +rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout) +{ + struct rt_thread *thread; + register rt_ubase_t temp; + rt_uint32_t tick_delta; + + /* parameter check */ + RT_ASSERT(mb != RT_NULL); + RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); + + /* initialize delta tick */ + tick_delta = 0; + /* get current thread */ + thread = rt_thread_self(); + + RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mb->parent.parent))); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* for non-blocking call */ + if (mb->entry == 0 && timeout == 0) + { + rt_hw_interrupt_enable(temp); + + return -RT_ETIMEOUT; + } + + /* mailbox is empty */ + while (mb->entry == 0) + { + /* reset error number in thread */ + thread->error = RT_EOK; + + /* no waiting, return timeout */ + if (timeout == 0) + { + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + thread->error = -RT_ETIMEOUT; + + return -RT_ETIMEOUT; + } + + RT_DEBUG_IN_THREAD_CONTEXT; + /* suspend current thread */ + _ipc_list_suspend(&(mb->parent.suspend_thread), + thread, + mb->parent.parent.flag); + + /* has waiting time, start thread timer */ + if (timeout > 0) + { + /* get the start tick of timer */ + tick_delta = rt_tick_get(); + + RT_DEBUG_LOG(RT_DEBUG_IPC, ("mb_recv: start timer of thread:%s\n", + thread->name)); + + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &timeout); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* re-schedule */ + rt_schedule(); + + /* resume from suspend state */ + if (thread->error != RT_EOK) + { + /* return error */ + return thread->error; + } + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* if it's not waiting forever and then re-calculate timeout tick */ + if (timeout > 0) + { + tick_delta = rt_tick_get() - tick_delta; + timeout -= tick_delta; + if (timeout < 0) + timeout = 0; + } + } + + /* fill ptr */ + *value = mb->msg_pool[mb->out_offset]; + + /* increase output offset */ + ++ mb->out_offset; + if (mb->out_offset >= mb->size) + mb->out_offset = 0; + + /* decrease message entry */ + if(mb->entry > 0) + { + mb->entry --; + } + + /* resume suspended thread */ + if (!rt_list_isempty(&(mb->suspend_sender_thread))) + { + _ipc_list_resume(&(mb->suspend_sender_thread)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent))); + + rt_schedule(); + + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent))); + + return RT_EOK; +} +RTM_EXPORT(rt_mb_recv); + + +/** + * @brief This function will set some extra attributions of a mailbox object. + * + * @note Currently this function only supports the RT_IPC_CMD_RESET command to reset the mailbox. + * + * @param mb is a pointer to a mailbox object. + * + * @param cmd is a command used to configure some attributions of the mailbox. + * + * @param arg is the argument of the function to execute the command. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that this function failed to execute. + */ +rt_err_t rt_mb_control(rt_mailbox_t mb, int cmd, void *arg) +{ + rt_ubase_t level; + + /* parameter check */ + RT_ASSERT(mb != RT_NULL); + RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); + + if (cmd == RT_IPC_CMD_RESET) + { + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* resume all waiting thread */ + _ipc_list_resume_all(&(mb->parent.suspend_thread)); + /* also resume all mailbox private suspended thread */ + _ipc_list_resume_all(&(mb->suspend_sender_thread)); + + /* re-init mailbox */ + mb->entry = 0; + mb->in_offset = 0; + mb->out_offset = 0; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + rt_schedule(); + + return RT_EOK; + } + + return -RT_ERROR; +} +RTM_EXPORT(rt_mb_control); + +/**@}*/ +#endif /* RT_USING_MAILBOX */ + +#ifdef RT_USING_MESSAGEQUEUE +/** + * @addtogroup messagequeue + */ + +/**@{*/ + +struct rt_mq_message +{ + struct rt_mq_message *next; +}; + + +/** + * @brief Initialize a static messagequeue object. + * + * @note For the static messagequeue object, its memory space is allocated by the compiler during compiling, + * and shall placed on the read-write data segment or on the uninitialized data segment. + * By contrast, the rt_mq_create() function will allocate memory space automatically + * and initialize the messagequeue. + * + * @see rt_mq_create() + * + * @param mq is a pointer to the messagequeue to initialize. It is assumed that storage for + * the messagequeue will be allocated in your application. + * + * @param name is a pointer to the name that given to the messagequeue. + * + * @param msgpool is a pointer to the starting address of the memory space you allocated for + * the messagequeue in advance. + * In other words, msgpool is a pointer to the messagequeue buffer of the starting address. + * + * @param msg_size is the maximum length of a message in the messagequeue (Unit: Byte). + * + * @param pool_size is the size of the memory space allocated for the messagequeue in advance. + * + * @param flag is the messagequeue flag, which determines the queuing way of how multiple threads wait + * when the messagequeue is not available. + * The messagequeue flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this messagequeue will become non-real-time threads. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it represents the initialization failed. + * + * @warning This function can ONLY be called from threads. + */ +rt_err_t rt_mq_init(rt_mq_t mq, + const char *name, + void *msgpool, + rt_size_t msg_size, + rt_size_t pool_size, + rt_uint8_t flag) +{ + struct rt_mq_message *head; + register rt_base_t temp; + + /* parameter check */ + RT_ASSERT(mq != RT_NULL); + + /* initialize object */ + rt_object_init(&(mq->parent.parent), RT_Object_Class_MessageQueue, name); + + /* set parent flag */ + mq->parent.parent.flag = flag; + + /* initialize ipc object */ + _ipc_object_init(&(mq->parent)); + + /* set message pool */ + mq->msg_pool = msgpool; + + /* get correct message size */ + mq->msg_size = RT_ALIGN(msg_size, RT_ALIGN_SIZE); + mq->max_msgs = pool_size / (mq->msg_size + sizeof(struct rt_mq_message)); + + /* initialize message list */ + mq->msg_queue_head = RT_NULL; + mq->msg_queue_tail = RT_NULL; + + /* initialize message empty list */ + mq->msg_queue_free = RT_NULL; + for (temp = 0; temp < mq->max_msgs; temp ++) + { + head = (struct rt_mq_message *)((rt_uint8_t *)mq->msg_pool + + temp * (mq->msg_size + sizeof(struct rt_mq_message))); + head->next = (struct rt_mq_message *)mq->msg_queue_free; + mq->msg_queue_free = head; + } + + /* the initial entry is zero */ + mq->entry = 0; + + /* initialize an additional list of sender suspend thread */ + rt_list_init(&(mq->suspend_sender_thread)); + + return RT_EOK; +} +RTM_EXPORT(rt_mq_init); + + +/** + * @brief This function will detach a static messagequeue object. + * + * @note This function is used to detach a static messagequeue object which is initialized by rt_mq_init() function. + * By contrast, the rt_mq_delete() function will delete a messagequeue object. + * When the messagequeue is successfully detached, it will resume all suspended threads in the messagequeue list. + * + * @see rt_mq_delete() + * + * @param mq is a pointer to a messagequeue object to be detached. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it means that the messagequeue detach failed. + * + * @warning This function can ONLY detach a static messagequeue initialized by the rt_mq_init() function. + * If the messagequeue is created by the rt_mq_create() function, you MUST NOT USE this function to detach it, + * and ONLY USE the rt_mq_delete() function to complete the deletion. + */ +rt_err_t rt_mq_detach(rt_mq_t mq) +{ + /* parameter check */ + RT_ASSERT(mq != RT_NULL); + RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue); + RT_ASSERT(rt_object_is_systemobject(&mq->parent.parent)); + + /* resume all suspended thread */ + _ipc_list_resume_all(&mq->parent.suspend_thread); + /* also resume all message queue private suspended thread */ + _ipc_list_resume_all(&(mq->suspend_sender_thread)); + + /* detach message queue object */ + rt_object_detach(&(mq->parent.parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_mq_detach); + +#ifdef RT_USING_HEAP +/** + * @brief Creating a messagequeue object. + * + * @note For the messagequeue object, its memory space is allocated automatically. + * By contrast, the rt_mq_init() function will initialize a static messagequeue object. + * + * @see rt_mq_init() + * + * @param name is a pointer that given to the messagequeue. + * + * @param msg_size is the maximum length of a message in the messagequeue (Unit: Byte). + * + * @param max_msgs is the maximum number of messages in the messagequeue. + * + * @param flag is the messagequeue flag, which determines the queuing way of how multiple threads wait + * when the messagequeue is not available. + * The messagequeue flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this messagequeue will become non-real-time threads. + * + * @return Return a pointer to the messagequeue object. When the return value is RT_NULL, it means the creation failed. + * + * @warning This function can NOT be called in interrupt context. You can use macor RT_DEBUG_NOT_IN_INTERRUPT to check it. + */ +rt_mq_t rt_mq_create(const char *name, + rt_size_t msg_size, + rt_size_t max_msgs, + rt_uint8_t flag) +{ + struct rt_messagequeue *mq; + struct rt_mq_message *head; + register rt_base_t temp; + + RT_DEBUG_NOT_IN_INTERRUPT; + + /* allocate object */ + mq = (rt_mq_t)rt_object_allocate(RT_Object_Class_MessageQueue, name); + if (mq == RT_NULL) + return mq; + + /* set parent */ + mq->parent.parent.flag = flag; + + /* initialize ipc object */ + _ipc_object_init(&(mq->parent)); + + /* initialize message queue */ + + /* get correct message size */ + mq->msg_size = RT_ALIGN(msg_size, RT_ALIGN_SIZE); + mq->max_msgs = max_msgs; + + /* allocate message pool */ + mq->msg_pool = RT_KERNEL_MALLOC((mq->msg_size + sizeof(struct rt_mq_message)) * mq->max_msgs); + if (mq->msg_pool == RT_NULL) + { + rt_object_delete(&(mq->parent.parent)); + + return RT_NULL; + } + + /* initialize message list */ + mq->msg_queue_head = RT_NULL; + mq->msg_queue_tail = RT_NULL; + + /* initialize message empty list */ + mq->msg_queue_free = RT_NULL; + for (temp = 0; temp < mq->max_msgs; temp ++) + { + head = (struct rt_mq_message *)((rt_uint8_t *)mq->msg_pool + + temp * (mq->msg_size + sizeof(struct rt_mq_message))); + head->next = (struct rt_mq_message *)mq->msg_queue_free; + mq->msg_queue_free = head; + } + + /* the initial entry is zero */ + mq->entry = 0; + + /* initialize an additional list of sender suspend thread */ + rt_list_init(&(mq->suspend_sender_thread)); + + return mq; +} +RTM_EXPORT(rt_mq_create); + + +/** + * @brief This function will delete a messagequeue object and release the memory. + * + * @note This function is used to delete a messagequeue object which is created by the rt_mq_create() function. + * By contrast, the rt_mq_detach() function will detach a static messagequeue object. + * When the messagequeue is successfully deleted, it will resume all suspended threads in the messagequeue list. + * + * @see rt_mq_detach() + * + * @param mq is a pointer to a messagequeue object to be deleted. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the messagequeue detach failed. + * + * @warning This function can ONLY delete a messagequeue initialized by the rt_mq_create() function. + * If the messagequeue is initialized by the rt_mq_init() function, you MUST NOT USE this function to delete it, + * ONLY USE the rt_mq_detach() function to complete the detachment. + * for example,the rt_mq_create() function, it cannot be called in interrupt context. + */ +rt_err_t rt_mq_delete(rt_mq_t mq) +{ + RT_DEBUG_NOT_IN_INTERRUPT; + + /* parameter check */ + RT_ASSERT(mq != RT_NULL); + RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue); + RT_ASSERT(rt_object_is_systemobject(&mq->parent.parent) == RT_FALSE); + + /* resume all suspended thread */ + _ipc_list_resume_all(&(mq->parent.suspend_thread)); + /* also resume all message queue private suspended thread */ + _ipc_list_resume_all(&(mq->suspend_sender_thread)); + + /* free message queue pool */ + RT_KERNEL_FREE(mq->msg_pool); + + /* delete message queue object */ + rt_object_delete(&(mq->parent.parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_mq_delete); +#endif /* RT_USING_HEAP */ + + +/** + * @brief This function will send a message to the messagequeue object. If + * there is a thread suspended on the messagequeue, the thread will be + * resumed. + * + * @note When using this function to send a message, if the messagequeue is + * fully used, the current thread will wait for a timeout. If reaching + * the timeout and there is still no space available, the sending + * thread will be resumed and an error code will be returned. By + * contrast, the rt_mq_send() function will return an error code + * immediately without waiting when the messagequeue if fully used. + * + * @see rt_mq_send() + * + * @param mq is a pointer to the messagequeue object to be sent. + * + * @param buffer is the content of the message. + * + * @param size is the length of the message(Unit: Byte). + * + * @param timeout is a timeout period (unit: an OS tick). + * + * @return Return the operation status. When the return value is RT_EOK, the + * operation is successful. If the return value is any other values, + * it means that the messagequeue detach failed. + * + * @warning This function can be called in interrupt context and thread + * context. + */ +rt_err_t rt_mq_send_wait(rt_mq_t mq, + const void *buffer, + rt_size_t size, + rt_int32_t timeout) +{ + register rt_ubase_t temp; + struct rt_mq_message *msg; + rt_uint32_t tick_delta; + struct rt_thread *thread; + + /* parameter check */ + RT_ASSERT(mq != RT_NULL); + RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue); + RT_ASSERT(buffer != RT_NULL); + RT_ASSERT(size != 0); + + /* greater than one message size */ + if (size > mq->msg_size) + return -RT_ERROR; + + /* initialize delta tick */ + tick_delta = 0; + /* get current thread */ + thread = rt_thread_self(); + + RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mq->parent.parent))); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* get a free list, there must be an empty item */ + msg = (struct rt_mq_message *)mq->msg_queue_free; + /* for non-blocking call */ + if (msg == RT_NULL && timeout == 0) + { + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return -RT_EFULL; + } + + /* message queue is full */ + while ((msg = (struct rt_mq_message *)mq->msg_queue_free) == RT_NULL) + { + /* reset error number in thread */ + thread->error = RT_EOK; + + /* no waiting, return timeout */ + if (timeout == 0) + { + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return -RT_EFULL; + } + + RT_DEBUG_IN_THREAD_CONTEXT; + /* suspend current thread */ + _ipc_list_suspend(&(mq->suspend_sender_thread), + thread, + mq->parent.parent.flag); + + /* has waiting time, start thread timer */ + if (timeout > 0) + { + /* get the start tick of timer */ + tick_delta = rt_tick_get(); + + RT_DEBUG_LOG(RT_DEBUG_IPC, ("mq_send_wait: start timer of thread:%s\n", + thread->name)); + + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &timeout); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* re-schedule */ + rt_schedule(); + + /* resume from suspend state */ + if (thread->error != RT_EOK) + { + /* return error */ + return thread->error; + } + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* if it's not waiting forever and then re-calculate timeout tick */ + if (timeout > 0) + { + tick_delta = rt_tick_get() - tick_delta; + timeout -= tick_delta; + if (timeout < 0) + timeout = 0; + } + } + + /* move free list pointer */ + mq->msg_queue_free = msg->next; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* the msg is the new tailer of list, the next shall be NULL */ + msg->next = RT_NULL; + /* copy buffer */ + rt_memcpy(msg + 1, buffer, size); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + /* link msg to message queue */ + if (mq->msg_queue_tail != RT_NULL) + { + /* if the tail exists, */ + ((struct rt_mq_message *)mq->msg_queue_tail)->next = msg; + } + + /* set new tail */ + mq->msg_queue_tail = msg; + /* if the head is empty, set head */ + if (mq->msg_queue_head == RT_NULL) + mq->msg_queue_head = msg; + + if(mq->entry < RT_MQ_ENTRY_MAX) + { + /* increase message entry */ + mq->entry ++; + } + else + { + rt_hw_interrupt_enable(temp); /* enable interrupt */ + return -RT_EFULL; /* value overflowed */ + } + + /* resume suspended thread */ + if (!rt_list_isempty(&mq->parent.suspend_thread)) + { + _ipc_list_resume(&(mq->parent.suspend_thread)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + rt_schedule(); + + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return RT_EOK; +} +RTM_EXPORT(rt_mq_send_wait) + + +/** + * @brief This function will send a message to the messagequeue object. + * If there is a thread suspended on the messagequeue, the thread will be resumed. + * + * @note When using this function to send a message, if the messagequeue is fully used, + * the current thread will wait for a timeout. + * By contrast, when the messagequeue is fully used, the rt_mq_send_wait() function will + * return an error code immediately without waiting. + * + * @see rt_mq_send_wait() + * + * @param mq is a pointer to the messagequeue object to be sent. + * + * @param buffer is the content of the message. + * + * @param size is the length of the message(Unit: Byte). + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the messagequeue detach failed. + * + * @warning This function can be called in interrupt context and thread context. + */ +rt_err_t rt_mq_send(rt_mq_t mq, const void *buffer, rt_size_t size) +{ + return rt_mq_send_wait(mq, buffer, size, 0); +} +RTM_EXPORT(rt_mq_send); + + +/** + * @brief This function will send an urgent message to the messagequeue object. + * + * @note This function is almost the same as the rt_mq_send() function. The only difference is that + * when sending an urgent message, the message is placed at the head of the messagequeue so that + * the recipient can receive the urgent message first. + * + * @see rt_mq_send() + * + * @param mq is a pointer to the messagequeue object to be sent. + * + * @param buffer is the content of the message. + * + * @param size is the length of the message(Unit: Byte). + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox detach failed. + */ +rt_err_t rt_mq_urgent(rt_mq_t mq, const void *buffer, rt_size_t size) +{ + register rt_ubase_t temp; + struct rt_mq_message *msg; + + /* parameter check */ + RT_ASSERT(mq != RT_NULL); + RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue); + RT_ASSERT(buffer != RT_NULL); + RT_ASSERT(size != 0); + + /* greater than one message size */ + if (size > mq->msg_size) + return -RT_ERROR; + + RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mq->parent.parent))); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* get a free list, there must be an empty item */ + msg = (struct rt_mq_message *)mq->msg_queue_free; + /* message queue is full */ + if (msg == RT_NULL) + { + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return -RT_EFULL; + } + /* move free list pointer */ + mq->msg_queue_free = msg->next; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* copy buffer */ + rt_memcpy(msg + 1, buffer, size); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* link msg to the beginning of message queue */ + msg->next = (struct rt_mq_message *)mq->msg_queue_head; + mq->msg_queue_head = msg; + + /* if there is no tail */ + if (mq->msg_queue_tail == RT_NULL) + mq->msg_queue_tail = msg; + + if(mq->entry < RT_MQ_ENTRY_MAX) + { + /* increase message entry */ + mq->entry ++; + } + else + { + rt_hw_interrupt_enable(temp); /* enable interrupt */ + return -RT_EFULL; /* value overflowed */ + } + + /* resume suspended thread */ + if (!rt_list_isempty(&mq->parent.suspend_thread)) + { + _ipc_list_resume(&(mq->parent.suspend_thread)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + rt_schedule(); + + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return RT_EOK; +} +RTM_EXPORT(rt_mq_urgent); + + +/** + * @brief This function will receive a message from message queue object, + * if there is no message in messagequeue object, the thread shall wait for a specified time. + * + * @note Only when there is mail in the mailbox, the receiving thread can get the mail immediately and return RT_EOK, + * otherwise the receiving thread will be suspended until timeout. + * If the mail is not received within the specified time, it will return -RT_ETIMEOUT. + * + * @param mq is a pointer to the messagequeue object to be received. + * + * @param buffer is the content of the message. + * + * @param size is the length of the message(Unit: Byte). + * + * @param timeout is a timeout period (unit: an OS tick). + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox release failed. + */ +rt_err_t rt_mq_recv(rt_mq_t mq, + void *buffer, + rt_size_t size, + rt_int32_t timeout) +{ + struct rt_thread *thread; + register rt_ubase_t temp; + struct rt_mq_message *msg; + rt_uint32_t tick_delta; + + /* parameter check */ + RT_ASSERT(mq != RT_NULL); + RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue); + RT_ASSERT(buffer != RT_NULL); + RT_ASSERT(size != 0); + + /* initialize delta tick */ + tick_delta = 0; + /* get current thread */ + thread = rt_thread_self(); + RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mq->parent.parent))); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* for non-blocking call */ + if (mq->entry == 0 && timeout == 0) + { + rt_hw_interrupt_enable(temp); + + return -RT_ETIMEOUT; + } + + /* message queue is empty */ + while (mq->entry == 0) + { + RT_DEBUG_IN_THREAD_CONTEXT; + + /* reset error number in thread */ + thread->error = RT_EOK; + + /* no waiting, return timeout */ + if (timeout == 0) + { + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + thread->error = -RT_ETIMEOUT; + + return -RT_ETIMEOUT; + } + + /* suspend current thread */ + _ipc_list_suspend(&(mq->parent.suspend_thread), + thread, + mq->parent.parent.flag); + + /* has waiting time, start thread timer */ + if (timeout > 0) + { + /* get the start tick of timer */ + tick_delta = rt_tick_get(); + + RT_DEBUG_LOG(RT_DEBUG_IPC, ("set thread:%s to timer list\n", + thread->name)); + + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &timeout); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* re-schedule */ + rt_schedule(); + + /* recv message */ + if (thread->error != RT_EOK) + { + /* return error */ + return thread->error; + } + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* if it's not waiting forever and then re-calculate timeout tick */ + if (timeout > 0) + { + tick_delta = rt_tick_get() - tick_delta; + timeout -= tick_delta; + if (timeout < 0) + timeout = 0; + } + } + + /* get message from queue */ + msg = (struct rt_mq_message *)mq->msg_queue_head; + + /* move message queue head */ + mq->msg_queue_head = msg->next; + /* reach queue tail, set to NULL */ + if (mq->msg_queue_tail == msg) + mq->msg_queue_tail = RT_NULL; + + /* decrease message entry */ + if(mq->entry > 0) + { + mq->entry --; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* copy message */ + rt_memcpy(buffer, msg + 1, size > mq->msg_size ? mq->msg_size : size); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + /* put message to free list */ + msg->next = (struct rt_mq_message *)mq->msg_queue_free; + mq->msg_queue_free = msg; + + /* resume suspended thread */ + if (!rt_list_isempty(&(mq->suspend_sender_thread))) + { + _ipc_list_resume(&(mq->suspend_sender_thread)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mq->parent.parent))); + + rt_schedule(); + + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mq->parent.parent))); + + return RT_EOK; +} +RTM_EXPORT(rt_mq_recv); + + +/** + * @brief This function will set some extra attributions of a messagequeue object. + * + * @note Currently this function only supports the RT_IPC_CMD_RESET command to reset the messagequeue. + * + * @param mq is a pointer to a messagequeue object. + * + * @param cmd is a command used to configure some attributions of the messagequeue. + * + * @param arg is the argument of the function to execute the command. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that this function failed to execute. + */ +rt_err_t rt_mq_control(rt_mq_t mq, int cmd, void *arg) +{ + rt_ubase_t level; + struct rt_mq_message *msg; + + /* parameter check */ + RT_ASSERT(mq != RT_NULL); + RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue); + + if (cmd == RT_IPC_CMD_RESET) + { + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* resume all waiting thread */ + _ipc_list_resume_all(&mq->parent.suspend_thread); + /* also resume all message queue private suspended thread */ + _ipc_list_resume_all(&(mq->suspend_sender_thread)); + + /* release all message in the queue */ + while (mq->msg_queue_head != RT_NULL) + { + /* get message from queue */ + msg = (struct rt_mq_message *)mq->msg_queue_head; + + /* move message queue head */ + mq->msg_queue_head = msg->next; + /* reach queue tail, set to NULL */ + if (mq->msg_queue_tail == msg) + mq->msg_queue_tail = RT_NULL; + + /* put message to free list */ + msg->next = (struct rt_mq_message *)mq->msg_queue_free; + mq->msg_queue_free = msg; + } + + /* clean entry */ + mq->entry = 0; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + rt_schedule(); + + return RT_EOK; + } + + return -RT_ERROR; +} +RTM_EXPORT(rt_mq_control); + +/**@}*/ +#endif /* RT_USING_MESSAGEQUEUE */ + +/**@}*/ diff --git a/components/rt-thread/src/irq.c b/components/rt-thread/src/irq.c new file mode 100644 index 00000000..40c2aa53 --- /dev/null +++ b/components/rt-thread/src/irq.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-02-24 Bernard first version + * 2006-05-03 Bernard add IRQ_DEBUG + * 2016-08-09 ArdaFu add interrupt enter and leave hook. + * 2018-11-22 Jesven rt_interrupt_get_nest function add disable irq + * 2021-08-15 Supperthomas fix the comment + */ + +#include +#include + +#ifdef RT_USING_HOOK + +static void (*rt_interrupt_enter_hook)(void); +static void (*rt_interrupt_leave_hook)(void); + +/** + * @ingroup Hook + * + * @brief This function set a hook function when the system enter a interrupt + * + * @note the hook function must be simple and never be blocked or suspend. + * + * @param hook The function point to be called + */ +void rt_interrupt_enter_sethook(void (*hook)(void)) +{ + rt_interrupt_enter_hook = hook; +} + +/** + * @ingroup Hook + * + * @brief This function set a hook function when the system exit a interrupt. + * + * @note the hook function must be simple and never be blocked or suspend. + * + * @param hook The function point to be called + */ +void rt_interrupt_leave_sethook(void (*hook)(void)) +{ + rt_interrupt_leave_hook = hook; +} +#endif /* RT_USING_HOOK */ + +/** + * @addtogroup Kernel + */ + +/**@{*/ + +#ifdef RT_USING_SMP +#define rt_interrupt_nest rt_cpu_self()->irq_nest +#else +volatile rt_uint8_t rt_interrupt_nest = 0; +#endif /* RT_USING_SMP */ + + +/** + * @brief This function will be invoked by BSP, when enter interrupt service routine + * + * @note please don't invoke this routine in application + * + * @see rt_interrupt_leave + */ +void rt_interrupt_enter(void) +{ + rt_base_t level; + + level = rt_hw_interrupt_disable(); + rt_interrupt_nest ++; + RT_OBJECT_HOOK_CALL(rt_interrupt_enter_hook,()); + rt_hw_interrupt_enable(level); + + RT_DEBUG_LOG(RT_DEBUG_IRQ, ("irq has come..., irq current nest:%d\n", + rt_interrupt_nest)); +} +RTM_EXPORT(rt_interrupt_enter); + + +/** + * @brief This function will be invoked by BSP, when leave interrupt service routine + * + * @note please don't invoke this routine in application + * + * @see rt_interrupt_enter + */ +void rt_interrupt_leave(void) +{ + rt_base_t level; + + RT_DEBUG_LOG(RT_DEBUG_IRQ, ("irq is going to leave, irq current nest:%d\n", + rt_interrupt_nest)); + + level = rt_hw_interrupt_disable(); + RT_OBJECT_HOOK_CALL(rt_interrupt_leave_hook,()); + rt_interrupt_nest --; + rt_hw_interrupt_enable(level); +} +RTM_EXPORT(rt_interrupt_leave); + + +/** + * @brief This function will return the nest of interrupt. + * + * User application can invoke this function to get whether current + * context is interrupt context. + * + * @return rt_uint8_t the number of nested interrupts. + */ +RT_WEAK rt_uint8_t rt_interrupt_get_nest(void) +{ + rt_uint8_t ret; + rt_base_t level; + + level = rt_hw_interrupt_disable(); + ret = rt_interrupt_nest; + rt_hw_interrupt_enable(level); + return ret; +} +RTM_EXPORT(rt_interrupt_get_nest); + +RTM_EXPORT(rt_hw_interrupt_disable); +RTM_EXPORT(rt_hw_interrupt_enable); + +/**@}*/ + diff --git a/components/rt-thread/src/kservice.c b/components/rt-thread/src/kservice.c new file mode 100644 index 00000000..91872492 --- /dev/null +++ b/components/rt-thread/src/kservice.c @@ -0,0 +1,1388 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-03-16 Bernard the first version + * 2006-05-25 Bernard rewrite vsprintf + * 2006-08-10 Bernard add rt_show_version + * 2010-03-17 Bernard remove rt_strlcpy function + * fix gcc compiling issue. + * 2010-04-15 Bernard remove weak definition on ICCM16C compiler + * 2012-07-18 Arda add the alignment display for signed integer + * 2012-11-23 Bernard fix IAR compiler error. + * 2012-12-22 Bernard fix rt_kprintf issue, which found by Grissiom. + * 2013-06-24 Bernard remove rt_kprintf if RT_USING_CONSOLE is not defined. + * 2013-09-24 aozima make sure the device is in STREAM mode when used by rt_kprintf. + * 2015-07-06 Bernard Add rt_assert_handler routine. + * 2021-02-28 Meco Man add RT_KSERVICE_USING_STDLIB + */ + +#include +#include + +#ifdef RT_USING_MODULE +#include +#endif /* RT_USING_MODULE */ + +/* use precision */ +#define RT_PRINTF_PRECISION + +/** + * @addtogroup KernelService + */ + +/**@{*/ + +/* global errno in RT-Thread */ +static volatile int __rt_errno; + +#if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE) +static rt_device_t _console_device = RT_NULL; +#endif + +/* + * This function will get errno + * + * @return errno + */ +rt_err_t rt_get_errno(void) +{ + rt_thread_t tid; + + if (rt_interrupt_get_nest() != 0) + { + /* it's in interrupt context */ + return __rt_errno; + } + + tid = rt_thread_self(); + if (tid == RT_NULL) + return __rt_errno; + + return tid->error; +} +RTM_EXPORT(rt_get_errno); + +/* + * This function will set errno + * + * @param error the errno shall be set + */ +void rt_set_errno(rt_err_t error) +{ + rt_thread_t tid; + + if (rt_interrupt_get_nest() != 0) + { + /* it's in interrupt context */ + __rt_errno = error; + + return; + } + + tid = rt_thread_self(); + if (tid == RT_NULL) + { + __rt_errno = error; + + return; + } + + tid->error = error; +} +RTM_EXPORT(rt_set_errno); + +/** + * This function returns errno. + * + * @return the errno in the system + */ +int *_rt_errno(void) +{ + rt_thread_t tid; + + if (rt_interrupt_get_nest() != 0) + return (int *)&__rt_errno; + + tid = rt_thread_self(); + if (tid != RT_NULL) + return (int *) & (tid->error); + + return (int *)&__rt_errno; +} +RTM_EXPORT(_rt_errno); + +/** + * This function will set the content of memory to specified value + * + * @param s the address of source memory + * @param c the value shall be set in content + * @param count the copied length + * + * @return the address of source memory + */ +RT_WEAK void *rt_memset(void *s, int c, rt_ubase_t count) +{ +#ifdef RT_KSERVICE_USING_TINY_SIZE + char *xs = (char *)s; + + while (count--) + *xs++ = c; + + return s; +#else +#define LBLOCKSIZE (sizeof(long)) +#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1)) +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + + unsigned int i; + char *m = (char *)s; + unsigned long buffer; + unsigned long *aligned_addr; + unsigned int d = c & 0xff; /* To avoid sign extension, copy C to an + unsigned variable. */ + + if (!TOO_SMALL(count) && !UNALIGNED(s)) + { + /* If we get this far, we know that count is large and s is word-aligned. */ + aligned_addr = (unsigned long *)s; + + /* Store d into each char sized location in buffer so that + * we can set large blocks quickly. + */ + if (LBLOCKSIZE == 4) + { + buffer = (d << 8) | d; + buffer |= (buffer << 16); + } + else + { + buffer = 0; + for (i = 0; i < LBLOCKSIZE; i ++) + buffer = (buffer << 8) | d; + } + + while (count >= LBLOCKSIZE * 4) + { + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + count -= 4 * LBLOCKSIZE; + } + + while (count >= LBLOCKSIZE) + { + *aligned_addr++ = buffer; + count -= LBLOCKSIZE; + } + + /* Pick up the remainder with a bytewise loop. */ + m = (char *)aligned_addr; + } + + while (count--) + { + *m++ = (char)d; + } + + return s; + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL +#endif /* RT_KSERVICE_USING_TINY_SIZE */ +} +RTM_EXPORT(rt_memset); + +#ifndef RT_USING_ASM_MEMCPY +/** + * This function will copy memory content from source address to destination + * address. + * + * @param dst the address of destination memory + * @param src the address of source memory + * @param count the copied length + * + * @return the address of destination memory + */ +void *rt_memcpy(void *dst, const void *src, rt_ubase_t count) +{ +#ifdef RT_KSERVICE_USING_TINY_SIZE + char *tmp = (char *)dst, *s = (char *)src; + rt_ubase_t len; + + if (tmp <= s || tmp > (s + count)) + { + while (count--) + *tmp ++ = *s ++; + } + else + { + for (len = count; len > 0; len --) + tmp[len - 1] = s[len - 1]; + } + + return dst; +#else + +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) +#define BIGBLOCKSIZE (sizeof (long) << 2) +#define LITTLEBLOCKSIZE (sizeof (long)) +#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) + + char *dst_ptr = (char *)dst; + char *src_ptr = (char *)src; + long *aligned_dst; + long *aligned_src; + int len = count; + + /* If the size is small, or either SRC or DST is unaligned, + then punt into the byte copy loop. This should be rare. */ + if (!TOO_SMALL(len) && !UNALIGNED(src_ptr, dst_ptr)) + { + aligned_dst = (long *)dst_ptr; + aligned_src = (long *)src_ptr; + + /* Copy 4X long words at a time if possible. */ + while (len >= BIGBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + len -= BIGBLOCKSIZE; + } + + /* Copy one long word at a time if possible. */ + while (len >= LITTLEBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + len -= LITTLEBLOCKSIZE; + } + + /* Pick up any residual with a byte copier. */ + dst_ptr = (char *)aligned_dst; + src_ptr = (char *)aligned_src; + } + + while (len--) + *dst_ptr++ = *src_ptr++; + + return dst; +#undef UNALIGNED +#undef BIGBLOCKSIZE +#undef LITTLEBLOCKSIZE +#undef TOO_SMALL +#endif /* RT_KSERVICE_USING_TINY_SIZE */ +} +RTM_EXPORT(rt_memcpy); +#endif /* RT_USING_ASM_MEMCPY */ + +#ifndef RT_KSERVICE_USING_STDLIB + +/** + * This function will move memory content from source address to destination + * address. + * + * @param dest the address of destination memory + * @param src the address of source memory + * @param n the copied length + * + * @return the address of destination memory + */ +void *rt_memmove(void *dest, const void *src, rt_ubase_t n) +{ + char *tmp = (char *)dest, *s = (char *)src; + + if (s < tmp && tmp < s + n) + { + tmp += n; + s += n; + + while (n--) + *(--tmp) = *(--s); + } + else + { + while (n--) + *tmp++ = *s++; + } + + return dest; +} +RTM_EXPORT(rt_memmove); + +/** + * This function will compare two areas of memory + * + * @param cs one area of memory + * @param ct another area of memory + * @param count the size of the area + * + * @return the result + */ +RT_WEAK rt_int32_t rt_memcmp(const void *cs, const void *ct, rt_ubase_t count) +{ + const unsigned char *su1, *su2; + int res = 0; + + for (su1 = (const unsigned char *)cs, su2 = (const unsigned char *)ct; 0 < count; ++su1, ++su2, count--) + if ((res = *su1 - *su2) != 0) + break; + + return res; +} +RTM_EXPORT(rt_memcmp); + +/** + * This function will return the first occurrence of a string. + * + * @param s1 the source string + * @param s2 the find string + * + * @return the first occurrence of a s2 in s1, or RT_NULL if no found. + */ +char *rt_strstr(const char *s1, const char *s2) +{ + int l1, l2; + + l2 = rt_strlen(s2); + if (!l2) + return (char *)s1; + l1 = rt_strlen(s1); + while (l1 >= l2) + { + l1 --; + if (!rt_memcmp(s1, s2, l2)) + return (char *)s1; + s1 ++; + } + + return RT_NULL; +} +RTM_EXPORT(rt_strstr); + +/** + * This function will compare two strings while ignoring differences in case + * + * @param a the string to be compared + * @param b the string to be compared + * + * @return the result + */ +rt_int32_t rt_strcasecmp(const char *a, const char *b) +{ + int ca, cb; + + do + { + ca = *a++ & 0xff; + cb = *b++ & 0xff; + if (ca >= 'A' && ca <= 'Z') + ca += 'a' - 'A'; + if (cb >= 'A' && cb <= 'Z') + cb += 'a' - 'A'; + } + while (ca == cb && ca != '\0'); + + return ca - cb; +} +RTM_EXPORT(rt_strcasecmp); + +/** + * This function will copy string no more than n bytes. + * + * @param dst the string to copy + * @param src the string to be copied + * @param n the maximum copied length + * + * @return the result + */ +char *rt_strncpy(char *dst, const char *src, rt_ubase_t n) +{ + if (n != 0) + { + char *d = dst; + const char *s = src; + + do + { + if ((*d++ = *s++) == 0) + { + /* NUL pad the remaining n-1 bytes */ + while (--n != 0) + *d++ = 0; + break; + } + } while (--n != 0); + } + + return (dst); +} +RTM_EXPORT(rt_strncpy); + +/** + * This function will compare two strings with specified maximum length + * + * @param cs the string to be compared + * @param ct the string to be compared + * @param count the maximum compare length + * + * @return the result + */ +rt_int32_t rt_strncmp(const char *cs, const char *ct, rt_ubase_t count) +{ + register signed char __res = 0; + + while (count) + { + if ((__res = *cs - *ct++) != 0 || !*cs++) + break; + count --; + } + + return __res; +} +RTM_EXPORT(rt_strncmp); + +/** + * This function will compare two strings without specified length + * + * @param cs the string to be compared + * @param ct the string to be compared + * + * @return the result + */ +rt_int32_t rt_strcmp(const char *cs, const char *ct) +{ + while (*cs && *cs == *ct) + { + cs++; + ct++; + } + + return (*cs - *ct); +} +RTM_EXPORT(rt_strcmp); + +/** + * The strnlen() function returns the number of characters in the + * string pointed to by s, excluding the terminating null byte ('\0'), + * but at most maxlen. In doing this, strnlen() looks only at the + * first maxlen characters in the string pointed to by s and never + * beyond s+maxlen. + * + * @param s the string + * @param maxlen the max size + * @return the length of string + */ +rt_size_t rt_strnlen(const char *s, rt_ubase_t maxlen) +{ + const char *sc; + + for (sc = s; *sc != '\0' && (rt_ubase_t)(sc - s) < maxlen; ++sc) /* nothing */ + ; + + return sc - s; +} +RTM_EXPORT(rt_strnlen); + +/** + * This function will return the length of a string, which terminate will + * null character. + * + * @param s the string + * + * @return the length of string + */ +rt_size_t rt_strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) /* nothing */ + ; + + return sc - s; +} +RTM_EXPORT(rt_strlen); + +#endif /* RT_KSERVICE_USING_STDLIB */ + +#ifdef RT_USING_HEAP +/** + * This function will duplicate a string. + * + * @param s the string to be duplicated + * + * @return the duplicated string pointer + */ +char *rt_strdup(const char *s) +{ + rt_size_t len = rt_strlen(s) + 1; + char *tmp = (char *)rt_malloc(len); + + if (!tmp) + return RT_NULL; + + rt_memcpy(tmp, s, len); + + return tmp; +} +RTM_EXPORT(rt_strdup); +#ifdef __ARMCC_VERSION +char *strdup(const char *s) __attribute__((alias("rt_strdup"))); +#endif +#endif /* RT_USING_HEAP */ + +/** + * This function will show the version of rt-thread rtos + */ +void rt_show_version(void) +{ + rt_kprintf("\n \\ | /\n"); + rt_kprintf("- RT - Thread Operating System\n"); + rt_kprintf(" / | \\ %d.%d.%d build %s\n", + RT_VERSION, RT_SUBVERSION, RT_REVISION, __DATE__); + rt_kprintf(" 2006 - 2021 Copyright by rt-thread team\n"); +} +RTM_EXPORT(rt_show_version); + +/* private function */ +#define _ISDIGIT(c) ((unsigned)((c) - '0') < 10) + +#ifdef RT_PRINTF_LONGLONG +rt_inline int divide(long long *n, int base) +{ + int res; + + /* optimized for processor which does not support divide instructions. */ + if (base == 10) + { + res = (int)(((unsigned long long)*n) % 10U); + *n = (long long)(((unsigned long long)*n) / 10U); + } + else + { + res = (int)(((unsigned long long)*n) % 16U); + *n = (long long)(((unsigned long long)*n) / 16U); + } + + return res; +} +#else +rt_inline int divide(long *n, int base) +{ + int res; + + /* optimized for processor which does not support divide instructions. */ + if (base == 10) + { + res = (int)(((unsigned long)*n) % 10U); + *n = (long)(((unsigned long)*n) / 10U); + } + else + { + res = (int)(((unsigned long)*n) % 16U); + *n = (long)(((unsigned long)*n) / 16U); + } + + return res; +} +#endif /* RT_PRINTF_LONGLONG */ + +rt_inline int skip_atoi(const char **s) +{ + register int i = 0; + while (_ISDIGIT(**s)) + i = i * 10 + *((*s)++) - '0'; + + return i; +} + +#define ZEROPAD (1 << 0) /* pad with zero */ +#define SIGN (1 << 1) /* unsigned/signed long */ +#define PLUS (1 << 2) /* show plus */ +#define SPACE (1 << 3) /* space if plus */ +#define LEFT (1 << 4) /* left justified */ +#define SPECIAL (1 << 5) /* 0x */ +#define LARGE (1 << 6) /* use 'ABCDEF' instead of 'abcdef' */ + +#ifdef RT_PRINTF_PRECISION +static char *print_number(char *buf, + char *end, +#ifdef RT_PRINTF_LONGLONG + long long num, +#else + long num, +#endif /* RT_PRINTF_LONGLONG */ + int base, + int s, + int precision, + int type) +#else +static char *print_number(char *buf, + char *end, +#ifdef RT_PRINTF_LONGLONG + long long num, +#else + long num, +#endif /* RT_PRINTF_LONGLONG */ + int base, + int s, + int type) +#endif /* RT_PRINTF_PRECISION */ +{ + char c, sign; +#ifdef RT_PRINTF_LONGLONG + char tmp[32]; +#else + char tmp[16]; +#endif /* RT_PRINTF_LONGLONG */ + int precision_bak = precision; + const char *digits; + static const char small_digits[] = "0123456789abcdef"; + static const char large_digits[] = "0123456789ABCDEF"; + register int i; + register int size; + + size = s; + + digits = (type & LARGE) ? large_digits : small_digits; + if (type & LEFT) + type &= ~ZEROPAD; + + c = (type & ZEROPAD) ? '0' : ' '; + + /* get sign */ + sign = 0; + if (type & SIGN) + { + if (num < 0) + { + sign = '-'; + num = -num; + } + else if (type & PLUS) + sign = '+'; + else if (type & SPACE) + sign = ' '; + } + +#ifdef RT_PRINTF_SPECIAL + if (type & SPECIAL) + { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } +#endif /* RT_PRINTF_SPECIAL */ + + i = 0; + if (num == 0) + tmp[i++] = '0'; + else + { + while (num != 0) + tmp[i++] = digits[divide(&num, base)]; + } + +#ifdef RT_PRINTF_PRECISION + if (i > precision) + precision = i; + size -= precision; +#else + size -= i; +#endif /* RT_PRINTF_PRECISION */ + + if (!(type & (ZEROPAD | LEFT))) + { + if ((sign) && (size > 0)) + size--; + + while (size-- > 0) + { + if (buf < end) + *buf = ' '; + ++ buf; + } + } + + if (sign) + { + if (buf < end) + { + *buf = sign; + } + -- size; + ++ buf; + } + +#ifdef RT_PRINTF_SPECIAL + if (type & SPECIAL) + { + if (base == 8) + { + if (buf < end) + *buf = '0'; + ++ buf; + } + else if (base == 16) + { + if (buf < end) + *buf = '0'; + ++ buf; + if (buf < end) + { + *buf = type & LARGE ? 'X' : 'x'; + } + ++ buf; + } + } +#endif /* RT_PRINTF_SPECIAL */ + + /* no align to the left */ + if (!(type & LEFT)) + { + while (size-- > 0) + { + if (buf < end) + *buf = c; + ++ buf; + } + } + +#ifdef RT_PRINTF_PRECISION + while (i < precision--) + { + if (buf < end) + *buf = '0'; + ++ buf; + } +#endif /* RT_PRINTF_PRECISION */ + + /* put number in the temporary buffer */ + while (i-- > 0 && (precision_bak != 0)) + { + if (buf < end) + *buf = tmp[i]; + ++ buf; + } + + while (size-- > 0) + { + if (buf < end) + *buf = ' '; + ++ buf; + } + + return buf; +} + +rt_int32_t rt_vsnprintf(char *buf, + rt_size_t size, + const char *fmt, + va_list args) +{ +#ifdef RT_PRINTF_LONGLONG + unsigned long long num; +#else + rt_uint32_t num; +#endif /* RT_PRINTF_LONGLONG */ + int i, len; + char *str, *end, c; + const char *s; + + rt_uint8_t base; /* the base of number */ + rt_uint8_t flags; /* flags to print number */ + rt_uint8_t qualifier; /* 'h', 'l', or 'L' for integer fields */ + rt_int32_t field_width; /* width of output field */ + +#ifdef RT_PRINTF_PRECISION + int precision; /* min. # of digits for integers and max for a string */ +#endif /* RT_PRINTF_PRECISION */ + + str = buf; + end = buf + size; + + /* Make sure end is always >= buf */ + if (end < buf) + { + end = ((char *) - 1); + size = end - buf; + } + + for (; *fmt ; ++fmt) + { + if (*fmt != '%') + { + if (str < end) + *str = *fmt; + ++ str; + continue; + } + + /* process flags */ + flags = 0; + + while (1) + { + /* skips the first '%' also */ + ++ fmt; + if (*fmt == '-') flags |= LEFT; + else if (*fmt == '+') flags |= PLUS; + else if (*fmt == ' ') flags |= SPACE; + else if (*fmt == '#') flags |= SPECIAL; + else if (*fmt == '0') flags |= ZEROPAD; + else break; + } + + /* get field width */ + field_width = -1; + if (_ISDIGIT(*fmt)) field_width = skip_atoi(&fmt); + else if (*fmt == '*') + { + ++ fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) + { + field_width = -field_width; + flags |= LEFT; + } + } + +#ifdef RT_PRINTF_PRECISION + /* get the precision */ + precision = -1; + if (*fmt == '.') + { + ++ fmt; + if (_ISDIGIT(*fmt)) precision = skip_atoi(&fmt); + else if (*fmt == '*') + { + ++ fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) precision = 0; + } +#endif /* RT_PRINTF_PRECISION */ + /* get the conversion qualifier */ + qualifier = 0; +#ifdef RT_PRINTF_LONGLONG + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') +#else + if (*fmt == 'h' || *fmt == 'l') +#endif /* RT_PRINTF_LONGLONG */ + { + qualifier = *fmt; + ++ fmt; +#ifdef RT_PRINTF_LONGLONG + if (qualifier == 'l' && *fmt == 'l') + { + qualifier = 'L'; + ++ fmt; + } +#endif /* RT_PRINTF_LONGLONG */ + } + + /* the default base */ + base = 10; + + switch (*fmt) + { + case 'c': + if (!(flags & LEFT)) + { + while (--field_width > 0) + { + if (str < end) *str = ' '; + ++ str; + } + } + + /* get character */ + c = (rt_uint8_t)va_arg(args, int); + if (str < end) *str = c; + ++ str; + + /* put width */ + while (--field_width > 0) + { + if (str < end) *str = ' '; + ++ str; + } + continue; + + case 's': + s = va_arg(args, char *); + if (!s) s = "(NULL)"; + + for (len = 0; (len != field_width) && (s[len] != '\0'); len++); +#ifdef RT_PRINTF_PRECISION + if (precision > 0 && len > precision) len = precision; +#endif /* RT_PRINTF_PRECISION */ + + if (!(flags & LEFT)) + { + while (len < field_width--) + { + if (str < end) *str = ' '; + ++ str; + } + } + + for (i = 0; i < len; ++i) + { + if (str < end) *str = *s; + ++ str; + ++ s; + } + + while (len < field_width--) + { + if (str < end) *str = ' '; + ++ str; + } + continue; + + case 'p': + if (field_width == -1) + { + field_width = sizeof(void *) << 1; + flags |= ZEROPAD; + } +#ifdef RT_PRINTF_PRECISION + str = print_number(str, end, + (long)va_arg(args, void *), + 16, field_width, precision, flags); +#else + str = print_number(str, end, + (long)va_arg(args, void *), + 16, field_width, flags); +#endif /* RT_PRINTF_PRECISION */ + continue; + + case '%': + if (str < end) *str = '%'; + ++ str; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + if (str < end) *str = '%'; + ++ str; + + if (*fmt) + { + if (str < end) *str = *fmt; + ++ str; + } + else + { + -- fmt; + } + continue; + } + +#ifdef RT_PRINTF_LONGLONG + if (qualifier == 'L') num = va_arg(args, long long); + else if (qualifier == 'l') +#else + if (qualifier == 'l') +#endif /* RT_PRINTF_LONGLONG */ + { + num = va_arg(args, rt_uint32_t); + if (flags & SIGN) num = (rt_int32_t)num; + } + else if (qualifier == 'h') + { + num = (rt_uint16_t)va_arg(args, rt_int32_t); + if (flags & SIGN) num = (rt_int16_t)num; + } + else + { + num = va_arg(args, rt_uint32_t); + if (flags & SIGN) num = (rt_int32_t)num; + } +#ifdef RT_PRINTF_PRECISION + str = print_number(str, end, num, base, field_width, precision, flags); +#else + str = print_number(str, end, num, base, field_width, flags); +#endif /* RT_PRINTF_PRECISION */ + } + + if (size > 0) + { + if (str < end) *str = '\0'; + else + { + end[-1] = '\0'; + } + } + + /* the trailing null byte doesn't count towards the total + * ++str; + */ + return str - buf; +} +RTM_EXPORT(rt_vsnprintf); + +/** + * This function will fill a formatted string to buffer + * + * @param buf the buffer to save formatted string + * @param size the size of buffer + * @param fmt the format + */ +rt_int32_t rt_snprintf(char *buf, rt_size_t size, const char *fmt, ...) +{ + rt_int32_t n; + va_list args; + + va_start(args, fmt); + n = rt_vsnprintf(buf, size, fmt, args); + va_end(args); + + return n; +} +RTM_EXPORT(rt_snprintf); + +/** + * This function will fill a formatted string to buffer + * + * @param buf the buffer to save formatted string + * @param arg_ptr the arg_ptr + * @param format the format + */ +rt_int32_t rt_vsprintf(char *buf, const char *format, va_list arg_ptr) +{ + return rt_vsnprintf(buf, (rt_size_t) - 1, format, arg_ptr); +} +RTM_EXPORT(rt_vsprintf); + +/** + * This function will fill a formatted string to buffer + * + * @param buf the buffer to save formatted string + * @param format the format + */ +rt_int32_t rt_sprintf(char *buf, const char *format, ...) +{ + rt_int32_t n; + va_list arg_ptr; + + va_start(arg_ptr, format); + n = rt_vsprintf(buf, format, arg_ptr); + va_end(arg_ptr); + + return n; +} +RTM_EXPORT(rt_sprintf); + +#ifdef RT_USING_CONSOLE + +#ifdef RT_USING_DEVICE +/** + * This function returns the device using in console. + * + * @return the device using in console or RT_NULL + */ +rt_device_t rt_console_get_device(void) +{ + return _console_device; +} +RTM_EXPORT(rt_console_get_device); + +/** + * This function will set a device as console device. + * After set a device to console, all output of rt_kprintf will be + * redirected to this new device. + * + * @param name the name of new console device + * + * @return the old console device handler on successful, or RT_NULL on failure. + */ +rt_device_t rt_console_set_device(const char *name) +{ + rt_device_t new_device, old_device; + + /* save old device */ + old_device = _console_device; + + /* find new console device */ + new_device = rt_device_find(name); + + /* check whether it's a same device */ + if (new_device == old_device) return RT_NULL; + + if (new_device != RT_NULL) + { + if (_console_device != RT_NULL) + { + /* close old console device */ + rt_device_close(_console_device); + } + + /* set new console device */ + rt_device_open(new_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_STREAM); + _console_device = new_device; + } + + return old_device; +} +RTM_EXPORT(rt_console_set_device); +#endif /* RT_USING_DEVICE */ + +RT_WEAK void rt_hw_console_output(const char *str) +{ + /* empty console output */ +} +RTM_EXPORT(rt_hw_console_output); + +/** + * This function will put string to the console. + * + * @param str the string output to the console. + */ +void rt_kputs(const char *str) +{ + if (!str) return; + +#ifdef RT_USING_DEVICE + if (_console_device == RT_NULL) + { + rt_hw_console_output(str); + } + else + { + rt_device_write(_console_device, 0, str, rt_strlen(str)); + } +#else + rt_hw_console_output(str); +#endif /* RT_USING_DEVICE */ +} + +/** + * This function will print a formatted string on system console + * + * @param fmt the format + */ +RT_WEAK void rt_kprintf(const char *fmt, ...) +{ + va_list args; + rt_size_t length; + static char rt_log_buf[RT_CONSOLEBUF_SIZE]; + + va_start(args, fmt); + /* the return value of vsnprintf is the number of bytes that would be + * written to buffer had if the size of the buffer been sufficiently + * large excluding the terminating null byte. If the output string + * would be larger than the rt_log_buf, we have to adjust the output + * length. */ + length = rt_vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args); + if (length > RT_CONSOLEBUF_SIZE - 1) + length = RT_CONSOLEBUF_SIZE - 1; +#ifdef RT_USING_DEVICE + if (_console_device == RT_NULL) + { + rt_hw_console_output(rt_log_buf); + } + else + { + rt_device_write(_console_device, 0, rt_log_buf, length); + } +#else + rt_hw_console_output(rt_log_buf); +#endif /* RT_USING_DEVICE */ + va_end(args); +} +RTM_EXPORT(rt_kprintf); +#endif /* RT_USING_CONSOLE */ + +#ifdef RT_USING_HEAP +/** + * This function allocates a memory block, which address is aligned to the + * specified alignment size. + * + * @param size the allocated memory block size + * @param align the alignment size + * + * @return the allocated memory block on successful, otherwise returns RT_NULL + */ +RT_WEAK void *rt_malloc_align(rt_size_t size, rt_size_t align) +{ + void *ptr; + void *align_ptr; + int uintptr_size; + rt_size_t align_size; + + /* sizeof pointer */ + uintptr_size = sizeof(void*); + uintptr_size -= 1; + + /* align the alignment size to uintptr size byte */ + align = ((align + uintptr_size) & ~uintptr_size); + + /* get total aligned size */ + align_size = ((size + uintptr_size) & ~uintptr_size) + align; + /* allocate memory block from heap */ + ptr = rt_malloc(align_size); + if (ptr != RT_NULL) + { + /* the allocated memory block is aligned */ + if (((rt_ubase_t)ptr & (align - 1)) == 0) + { + align_ptr = (void *)((rt_ubase_t)ptr + align); + } + else + { + align_ptr = (void *)(((rt_ubase_t)ptr + (align - 1)) & ~(align - 1)); + } + + /* set the pointer before alignment pointer to the real pointer */ + *((rt_ubase_t *)((rt_ubase_t)align_ptr - sizeof(void *))) = (rt_ubase_t)ptr; + + ptr = align_ptr; + } + + return ptr; +} +RTM_EXPORT(rt_malloc_align); + +/** + * This function release the memory block, which is allocated by + * rt_malloc_align function and address is aligned. + * + * @param ptr the memory block pointer + */ +RT_WEAK void rt_free_align(void *ptr) +{ + void *real_ptr; + + real_ptr = (void *) * (rt_ubase_t *)((rt_ubase_t)ptr - sizeof(void *)); + rt_free(real_ptr); +} +RTM_EXPORT(rt_free_align); +#endif /* RT_USING_HEAP */ + +#ifndef RT_USING_CPU_FFS +const rt_uint8_t __lowest_bit_bitmap[] = +{ + /* 00 */ 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 10 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 20 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 30 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 40 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 50 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 60 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 70 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 80 */ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 90 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* A0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* B0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* C0 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* D0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* E0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* F0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +/** + * This function finds the first bit set (beginning with the least significant bit) + * in value and return the index of that bit. + * + * Bits are numbered starting at 1 (the least significant bit). A return value of + * zero from any of these functions means that the argument was zero. + * + * @return return the index of the first bit set. If value is 0, then this function + * shall return 0. + */ +int __rt_ffs(int value) +{ + if (value == 0) return 0; + + if (value & 0xff) + return __lowest_bit_bitmap[value & 0xff] + 1; + + if (value & 0xff00) + return __lowest_bit_bitmap[(value & 0xff00) >> 8] + 9; + + if (value & 0xff0000) + return __lowest_bit_bitmap[(value & 0xff0000) >> 16] + 17; + + return __lowest_bit_bitmap[(value & 0xff000000) >> 24] + 25; +} +#endif /* RT_USING_CPU_FFS */ + +#ifdef RT_DEBUG +/* RT_ASSERT(EX)'s hook */ + +void (*rt_assert_hook)(const char *ex, const char *func, rt_size_t line); + +/** + * This function will set a hook function to RT_ASSERT(EX). It will run when the expression is false. + * + * @param hook the hook function + */ +void rt_assert_set_hook(void (*hook)(const char *ex, const char *func, rt_size_t line)) +{ + rt_assert_hook = hook; +} + +/** + * The RT_ASSERT function. + * + * @param ex the assertion condition string + * @param func the function name when assertion. + * @param line the file line number when assertion. + */ +void rt_assert_handler(const char *ex_string, const char *func, rt_size_t line) +{ + volatile char dummy = 0; + + if (rt_assert_hook == RT_NULL) + { +#ifdef RT_USING_MODULE + if (dlmodule_self()) + { + /* close assertion module */ + dlmodule_exit(-1); + } + else +#endif /*RT_USING_MODULE*/ + { + rt_kprintf("(%s) assertion failed at function:%s, line number:%d \n", ex_string, func, line); + while (dummy == 0); + } + } + else + { + rt_assert_hook(ex_string, func, line); + } +} +RTM_EXPORT(rt_assert_handler); +#endif /* RT_DEBUG */ + +/**@}*/ diff --git a/components/rt-thread/src/mem.c b/components/rt-thread/src/mem.c new file mode 100644 index 00000000..20ac0e58 --- /dev/null +++ b/components/rt-thread/src/mem.c @@ -0,0 +1,717 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2008-7-12 Bernard the first version + * 2010-06-09 Bernard fix the end stub of heap + * fix memory check in rt_realloc function + * 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca + * 2010-10-14 Bernard fix rt_realloc issue when realloc a NULL pointer. + * 2017-07-14 armink fix rt_realloc issue when new size is 0 + * 2018-10-02 Bernard Add 64bit support + */ + +/* + * 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 + * Simon Goldschmidt + * + */ + +#include +#include + +#ifndef RT_USING_MEMHEAP_AS_HEAP + +#define RT_MEM_STATS + +#if defined (RT_USING_HEAP) && defined (RT_USING_SMALL_MEM) +#ifdef RT_USING_HOOK +static void (*rt_malloc_hook)(void *ptr, rt_size_t size); +static void (*rt_free_hook)(void *ptr); + +/** + * @addtogroup Hook + */ + +/**@{*/ + +/** + * This function will set a hook function, which will be invoked when a memory + * block is allocated from heap memory. + * + * @param hook the hook function + */ +void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size)) +{ + rt_malloc_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when a memory + * block is released to heap memory. + * + * @param hook the hook function + */ +void rt_free_sethook(void (*hook)(void *ptr)) +{ + rt_free_hook = hook; +} + +/**@}*/ + +#endif /* RT_USING_HOOK */ + +#define HEAP_MAGIC 0x1ea0 +struct heap_mem +{ + /* magic and used flag */ + rt_uint16_t magic; + rt_uint16_t used; +#ifdef ARCH_CPU_64BIT + rt_uint32_t resv; +#endif /* ARCH_CPU_64BIT */ + + rt_size_t next, prev; + +#ifdef RT_USING_MEMTRACE +#ifdef ARCH_CPU_64BIT + rt_uint8_t thread[8]; +#else + rt_uint8_t thread[4]; /* thread name */ +#endif /* ARCH_CPU_64BIT */ +#endif /* RT_USING_MEMTRACE */ +}; + +/** pointer to the heap: for alignment, heap_ptr is now a pointer instead of an array */ +static rt_uint8_t *heap_ptr; + +/** the last entry, always unused! */ +static struct heap_mem *heap_end; + +#ifdef ARCH_CPU_64BIT +#define MIN_SIZE 24 +#else +#define MIN_SIZE 12 +#endif /* ARCH_CPU_64BIT */ + +#define MIN_SIZE_ALIGNED RT_ALIGN(MIN_SIZE, RT_ALIGN_SIZE) +#define SIZEOF_STRUCT_MEM RT_ALIGN(sizeof(struct heap_mem), RT_ALIGN_SIZE) + +static struct heap_mem *lfree; /* pointer to the lowest free block */ + +static struct rt_semaphore heap_sem; +static rt_size_t mem_size_aligned; + +#ifdef RT_MEM_STATS +static rt_size_t used_mem, max_mem; +#endif /* RT_MEM_STATS */ + +#ifdef RT_USING_MEMTRACE +rt_inline void rt_mem_setname(struct heap_mem *mem, const char *name) +{ + int index; + for (index = 0; index < sizeof(mem->thread); index ++) + { + if (name[index] == '\0') break; + mem->thread[index] = name[index]; + } + + for (; index < sizeof(mem->thread); index ++) + { + mem->thread[index] = ' '; + } +} +#endif /* RT_USING_MEMTRACE */ + +static void plug_holes(struct heap_mem *mem) +{ + struct heap_mem *nmem; + struct heap_mem *pmem; + + RT_ASSERT((rt_uint8_t *)mem >= heap_ptr); + RT_ASSERT((rt_uint8_t *)mem < (rt_uint8_t *)heap_end); + RT_ASSERT(mem->used == 0); + + /* plug hole forward */ + nmem = (struct heap_mem *)&heap_ptr[mem->next]; + if (mem != nmem && + nmem->used == 0 && + (rt_uint8_t *)nmem != (rt_uint8_t *)heap_end) + { + /* if mem->next is unused and not end of heap_ptr, + * combine mem and mem->next + */ + if (lfree == nmem) + { + lfree = mem; + } + mem->next = nmem->next; + ((struct heap_mem *)&heap_ptr[nmem->next])->prev = (rt_uint8_t *)mem - heap_ptr; + } + + /* plug hole backward */ + pmem = (struct heap_mem *)&heap_ptr[mem->prev]; + if (pmem != mem && pmem->used == 0) + { + /* if mem->prev is unused, combine mem and mem->prev */ + if (lfree == mem) + { + lfree = pmem; + } + pmem->next = mem->next; + ((struct heap_mem *)&heap_ptr[mem->next])->prev = (rt_uint8_t *)pmem - heap_ptr; + } +} + +/** + * @ingroup SystemInit + * + * This function will initialize system heap memory. + * + * @param begin_addr the beginning address of system heap memory. + * @param end_addr the end address of system heap memory. + */ +void rt_system_heap_init(void *begin_addr, void *end_addr) +{ + struct heap_mem *mem; + rt_ubase_t begin_align = RT_ALIGN((rt_ubase_t)begin_addr, RT_ALIGN_SIZE); + rt_ubase_t end_align = RT_ALIGN_DOWN((rt_ubase_t)end_addr, RT_ALIGN_SIZE); + + RT_DEBUG_NOT_IN_INTERRUPT; + + /* alignment addr */ + if ((end_align > (2 * SIZEOF_STRUCT_MEM)) && + ((end_align - 2 * SIZEOF_STRUCT_MEM) >= begin_align)) + { + /* calculate the aligned memory size */ + mem_size_aligned = end_align - begin_align - 2 * SIZEOF_STRUCT_MEM; + } + else + { + rt_kprintf("mem init, error begin address 0x%x, and end address 0x%x\n", + (rt_ubase_t)begin_addr, (rt_ubase_t)end_addr); + + return; + } + + /* point to begin address of heap */ + heap_ptr = (rt_uint8_t *)begin_align; + + RT_DEBUG_LOG(RT_DEBUG_MEM, ("mem init, heap begin address 0x%x, size %d\n", + (rt_ubase_t)heap_ptr, mem_size_aligned)); + + /* initialize the start of the heap */ + mem = (struct heap_mem *)heap_ptr; + mem->magic = HEAP_MAGIC; + mem->next = mem_size_aligned + SIZEOF_STRUCT_MEM; + mem->prev = 0; + mem->used = 0; +#ifdef RT_USING_MEMTRACE + rt_mem_setname(mem, "INIT"); +#endif /* RT_USING_MEMTRACE */ + + /* initialize the end of the heap */ + heap_end = (struct heap_mem *)&heap_ptr[mem->next]; + heap_end->magic = HEAP_MAGIC; + heap_end->used = 1; + heap_end->next = mem_size_aligned + SIZEOF_STRUCT_MEM; + heap_end->prev = mem_size_aligned + SIZEOF_STRUCT_MEM; +#ifdef RT_USING_MEMTRACE + rt_mem_setname(heap_end, "INIT"); +#endif /* RT_USING_MEMTRACE */ + + rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_PRIO); + + /* initialize the lowest-free pointer to the start of the heap */ + lfree = (struct heap_mem *)heap_ptr; +} + +/** + * @addtogroup MM + */ + +/**@{*/ + +/** + * Allocate a block of memory with a minimum of 'size' bytes. + * + * @param size is the minimum size of the requested block in bytes. + * + * @return pointer to allocated memory or NULL if no free memory was found. + */ +void *rt_malloc(rt_size_t size) +{ + rt_size_t ptr, ptr2; + struct heap_mem *mem, *mem2; + + if (size == 0) + return RT_NULL; + + RT_DEBUG_NOT_IN_INTERRUPT; + + if (size != RT_ALIGN(size, RT_ALIGN_SIZE)) + RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d, but align to %d\n", + size, RT_ALIGN(size, RT_ALIGN_SIZE))); + else + RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d\n", size)); + + /* alignment size */ + size = RT_ALIGN(size, RT_ALIGN_SIZE); + + if (size > mem_size_aligned) + { + RT_DEBUG_LOG(RT_DEBUG_MEM, ("no memory\n")); + + return RT_NULL; + } + + /* every data block must be at least MIN_SIZE_ALIGNED long */ + if (size < MIN_SIZE_ALIGNED) + size = MIN_SIZE_ALIGNED; + + /* take memory semaphore */ + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + + for (ptr = (rt_uint8_t *)lfree - heap_ptr; + ptr < mem_size_aligned - size; + ptr = ((struct heap_mem *)&heap_ptr[ptr])->next) + { + mem = (struct heap_mem *)&heap_ptr[ptr]; + + if ((!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) + { + /* mem is not used and at least perfect fit is possible: + * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ + + if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= + (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) + { + /* (in addition to the above, we test if another struct heap_mem (SIZEOF_STRUCT_MEM) containing + * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') + * -> split large block, create empty remainder, + * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if + * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, + * struct heap_mem would fit in but no data between mem2 and mem2->next + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory + */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + + /* create mem2 struct */ + mem2 = (struct heap_mem *)&heap_ptr[ptr2]; + mem2->magic = HEAP_MAGIC; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; +#ifdef RT_USING_MEMTRACE + rt_mem_setname(mem2, " "); +#endif /* RT_USING_MEMTRACE */ + + /* and insert it between mem and mem->next */ + mem->next = ptr2; + mem->used = 1; + + if (mem2->next != mem_size_aligned + SIZEOF_STRUCT_MEM) + { + ((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2; + } +#ifdef RT_MEM_STATS + used_mem += (size + SIZEOF_STRUCT_MEM); + if (max_mem < used_mem) + max_mem = used_mem; +#endif /* RT_MEM_STATS */ + } + else + { + /* (a mem2 struct does no fit into the user data space of mem and mem->next will always + * be used at this point: if not we have 2 unused structs in a row, plug_holes should have + * take care of this). + * -> near fit or excact fit: do not split, no mem2 creation + * also can't move mem->next directly behind mem, since mem->next + * will always be used at this point! + */ + mem->used = 1; +#ifdef RT_MEM_STATS + used_mem += mem->next - ((rt_uint8_t *)mem - heap_ptr); + if (max_mem < used_mem) + max_mem = used_mem; +#endif /* RT_MEM_STATS */ + } + /* set memory block magic */ + mem->magic = HEAP_MAGIC; +#ifdef RT_USING_MEMTRACE + if (rt_thread_self()) + rt_mem_setname(mem, rt_thread_self()->name); + else + rt_mem_setname(mem, "NONE"); +#endif /* RT_USING_MEMTRACE */ + + if (mem == lfree) + { + /* Find next free block after mem and update lowest free pointer */ + while (lfree->used && lfree != heap_end) + lfree = (struct heap_mem *)&heap_ptr[lfree->next]; + + RT_ASSERT(((lfree == heap_end) || (!lfree->used))); + } + + rt_sem_release(&heap_sem); + RT_ASSERT((rt_ubase_t)mem + SIZEOF_STRUCT_MEM + size <= (rt_ubase_t)heap_end); + RT_ASSERT((rt_ubase_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM) % RT_ALIGN_SIZE == 0); + RT_ASSERT((((rt_ubase_t)mem) & (RT_ALIGN_SIZE - 1)) == 0); + + RT_DEBUG_LOG(RT_DEBUG_MEM, + ("allocate memory at 0x%x, size: %d\n", + (rt_ubase_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM), + (rt_ubase_t)(mem->next - ((rt_uint8_t *)mem - heap_ptr)))); + + RT_OBJECT_HOOK_CALL(rt_malloc_hook, + (((void *)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM)), size)); + + /* return the memory data except mem struct */ + return (rt_uint8_t *)mem + SIZEOF_STRUCT_MEM; + } + } + + rt_sem_release(&heap_sem); + + return RT_NULL; +} +RTM_EXPORT(rt_malloc); + +/** + * This function will change the previously allocated memory block. + * + * @param rmem pointer to memory allocated by rt_malloc + * @param newsize the required new size + * + * @return the changed memory block address + */ +void *rt_realloc(void *rmem, rt_size_t newsize) +{ + rt_size_t size; + rt_size_t ptr, ptr2; + struct heap_mem *mem, *mem2; + void *nmem; + + RT_DEBUG_NOT_IN_INTERRUPT; + + /* alignment size */ + newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE); + if (newsize > mem_size_aligned) + { + RT_DEBUG_LOG(RT_DEBUG_MEM, ("realloc: out of memory\n")); + + return RT_NULL; + } + else if (newsize == 0) + { + rt_free(rmem); + return RT_NULL; + } + + /* allocate a new memory block */ + if (rmem == RT_NULL) + return rt_malloc(newsize); + + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + + if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr || + (rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end) + { + /* illegal memory */ + rt_sem_release(&heap_sem); + + return rmem; + } + + mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM); + + ptr = (rt_uint8_t *)mem - heap_ptr; + size = mem->next - ptr - SIZEOF_STRUCT_MEM; + if (size == newsize) + { + /* the size is the same as */ + rt_sem_release(&heap_sem); + + return rmem; + } + + if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size) + { + /* split memory block */ +#ifdef RT_MEM_STATS + used_mem -= (size - newsize); +#endif /* RT_MEM_STATS */ + + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + mem2 = (struct heap_mem *)&heap_ptr[ptr2]; + mem2->magic = HEAP_MAGIC; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; +#ifdef RT_USING_MEMTRACE + rt_mem_setname(mem2, " "); +#endif /* RT_USING_MEMTRACE */ + mem->next = ptr2; + if (mem2->next != mem_size_aligned + SIZEOF_STRUCT_MEM) + { + ((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2; + } + + if (mem2 < lfree) + { + /* the splited struct is now the lowest */ + lfree = mem2; + } + + plug_holes(mem2); + + rt_sem_release(&heap_sem); + + return rmem; + } + rt_sem_release(&heap_sem); + + /* expand memory */ + nmem = rt_malloc(newsize); + if (nmem != RT_NULL) /* check memory */ + { + rt_memcpy(nmem, rmem, size < newsize ? size : newsize); + rt_free(rmem); + } + + return nmem; +} +RTM_EXPORT(rt_realloc); + +/** + * This function will contiguously allocate enough space for count objects + * that are size bytes of memory each and returns a pointer to the allocated + * memory. + * + * The allocated memory is filled with bytes of value zero. + * + * @param count number of objects to allocate + * @param size size of the objects to allocate + * + * @return pointer to allocated memory / NULL pointer if there is an error + */ +void *rt_calloc(rt_size_t count, rt_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = rt_malloc(count * size); + + /* zero the memory */ + if (p) + rt_memset(p, 0, count * size); + + return p; +} +RTM_EXPORT(rt_calloc); + +/** + * This function will release the previously allocated memory block by + * rt_malloc. The released memory block is taken back to system heap. + * + * @param rmem the address of memory which will be released + */ +void rt_free(void *rmem) +{ + struct heap_mem *mem; + + if (rmem == RT_NULL) + return; + + RT_DEBUG_NOT_IN_INTERRUPT; + + RT_ASSERT((((rt_ubase_t)rmem) & (RT_ALIGN_SIZE - 1)) == 0); + RT_ASSERT((rt_uint8_t *)rmem >= (rt_uint8_t *)heap_ptr && + (rt_uint8_t *)rmem < (rt_uint8_t *)heap_end); + + RT_OBJECT_HOOK_CALL(rt_free_hook, (rmem)); + + if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr || + (rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end) + { + RT_DEBUG_LOG(RT_DEBUG_MEM, ("illegal memory\n")); + + return; + } + + /* Get the corresponding struct heap_mem ... */ + mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM); + + RT_DEBUG_LOG(RT_DEBUG_MEM, + ("release memory 0x%x, size: %d\n", + (rt_ubase_t)rmem, + (rt_ubase_t)(mem->next - ((rt_uint8_t *)mem - heap_ptr)))); + + + /* protect the heap from concurrent access */ + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + + /* ... which has to be in a used state ... */ + if (!mem->used || mem->magic != HEAP_MAGIC) + { + rt_kprintf("to free a bad data block:\n"); + rt_kprintf("mem: 0x%08x, used flag: %d, magic code: 0x%04x\n", mem, mem->used, mem->magic); + } + RT_ASSERT(mem->used); + RT_ASSERT(mem->magic == HEAP_MAGIC); + /* ... and is now unused. */ + mem->used = 0; + mem->magic = HEAP_MAGIC; +#ifdef RT_USING_MEMTRACE + rt_mem_setname(mem, " "); +#endif /* RT_USING_MEMTRACE */ + + if (mem < lfree) + { + /* the newly freed struct is now the lowest */ + lfree = mem; + } + +#ifdef RT_MEM_STATS + used_mem -= (mem->next - ((rt_uint8_t *)mem - heap_ptr)); +#endif /* RT_MEM_STATS */ + + /* finally, see if prev or next are free also */ + plug_holes(mem); + rt_sem_release(&heap_sem); +} +RTM_EXPORT(rt_free); + +#ifdef RT_MEM_STATS +void rt_memory_info(rt_uint32_t *total, + rt_uint32_t *used, + rt_uint32_t *max_used) +{ + if (total != RT_NULL) + *total = mem_size_aligned; + if (used != RT_NULL) + *used = used_mem; + if (max_used != RT_NULL) + *max_used = max_mem; +} + +#ifdef RT_USING_FINSH +#include + +void list_mem(void) +{ + rt_kprintf("total memory: %d\n", mem_size_aligned); + rt_kprintf("used memory : %d\n", used_mem); + rt_kprintf("maximum allocated memory: %d\n", max_mem); +} +FINSH_FUNCTION_EXPORT(list_mem, list memory usage information) + +#ifdef RT_USING_MEMTRACE +int memcheck(void) +{ + int position; + rt_ubase_t level; + struct heap_mem *mem; + level = rt_hw_interrupt_disable(); + for (mem = (struct heap_mem *)heap_ptr; mem != heap_end; mem = (struct heap_mem *)&heap_ptr[mem->next]) + { + position = (rt_ubase_t)mem - (rt_ubase_t)heap_ptr; + if (position < 0) goto __exit; + if (position > (int)mem_size_aligned) goto __exit; + if (mem->magic != HEAP_MAGIC) goto __exit; + if (mem->used != 0 && mem->used != 1) goto __exit; + } + rt_hw_interrupt_enable(level); + + return 0; +__exit: + rt_kprintf("Memory block wrong:\n"); + rt_kprintf("address: 0x%08x\n", mem); + rt_kprintf(" magic: 0x%04x\n", mem->magic); + rt_kprintf(" used: %d\n", mem->used); + rt_kprintf(" size: %d\n", mem->next - position - SIZEOF_STRUCT_MEM); + rt_hw_interrupt_enable(level); + + return 0; +} +MSH_CMD_EXPORT(memcheck, check memory data); + +int memtrace(int argc, char **argv) +{ + struct heap_mem *mem; + + list_mem(); + + rt_kprintf("\nmemory heap address:\n"); + rt_kprintf("heap_ptr: 0x%08x\n", heap_ptr); + rt_kprintf("lfree : 0x%08x\n", lfree); + rt_kprintf("heap_end: 0x%08x\n", heap_end); + + rt_kprintf("\n--memory item information --\n"); + for (mem = (struct heap_mem *)heap_ptr; mem != heap_end; mem = (struct heap_mem *)&heap_ptr[mem->next]) + { + int position = (rt_ubase_t)mem - (rt_ubase_t)heap_ptr; + int size; + + rt_kprintf("[0x%08x - ", mem); + + size = mem->next - position - SIZEOF_STRUCT_MEM; + if (size < 1024) + rt_kprintf("%5d", size); + else if (size < 1024 * 1024) + rt_kprintf("%4dK", size / 1024); + else + rt_kprintf("%4dM", size / (1024 * 1024)); + + rt_kprintf("] %c%c%c%c", mem->thread[0], mem->thread[1], mem->thread[2], mem->thread[3]); + if (mem->magic != HEAP_MAGIC) + rt_kprintf(": ***\n"); + else + rt_kprintf("\n"); + } + + return 0; +} +MSH_CMD_EXPORT(memtrace, dump memory trace information); +#endif /* RT_USING_MEMTRACE */ +#endif /* RT_USING_FINSH */ + +#endif /* defined (RT_USING_HEAP) && defined (RT_USING_SMALL_MEM) */ + +/**@}*/ + +#endif /* RT_MEM_STATS */ +#endif /* RT_USING_MEMHEAP_AS_HEAP */ diff --git a/components/rt-thread/src/memheap.c b/components/rt-thread/src/memheap.c new file mode 100644 index 00000000..d0801d72 --- /dev/null +++ b/components/rt-thread/src/memheap.c @@ -0,0 +1,1013 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * File : memheap.c + * + * Change Logs: + * Date Author Notes + * 2012-04-10 Bernard first implementation + * 2012-10-16 Bernard add the mutex lock for heap object. + * 2012-12-29 Bernard memheap can be used as system heap. + * change mutex lock to semaphore lock. + * 2013-04-10 Bernard add rt_memheap_realloc function. + * 2013-05-24 Bernard fix the rt_memheap_realloc issue. + * 2013-07-11 Grissiom fix the memory block splitting issue. + * 2013-07-15 Grissiom optimize rt_memheap_realloc + * 2021-06-03 Flybreak Fix the crash problem after opening Oz optimization on ac6. + */ + +#include +#include + +#ifdef RT_USING_MEMHEAP + +/* dynamic pool magic and mask */ +#define RT_MEMHEAP_MAGIC 0x1ea01ea0 +#define RT_MEMHEAP_MASK 0xfffffffe +#define RT_MEMHEAP_USED 0x01 +#define RT_MEMHEAP_FREED 0x00 + +#define RT_MEMHEAP_IS_USED(i) ((i)->magic & RT_MEMHEAP_USED) +#define RT_MEMHEAP_MINIALLOC 12 + +#define RT_MEMHEAP_SIZE RT_ALIGN(sizeof(struct rt_memheap_item), RT_ALIGN_SIZE) +#define MEMITEM_SIZE(item) ((rt_ubase_t)item->next - (rt_ubase_t)item - RT_MEMHEAP_SIZE) +#define MEMITEM(ptr) (struct rt_memheap_item*)((rt_uint8_t*)ptr - RT_MEMHEAP_SIZE) + +#ifdef RT_USING_MEMTRACE +rt_inline void rt_memheap_setname(struct rt_memheap_item *item, const char *name) +{ + int index; + rt_uint8_t *ptr; + + ptr = (rt_uint8_t *) & (item->next_free); + for (index = 0; index < sizeof(void *); index ++) + { + if (name[index] == '\0') break; + ptr[index] = name[index]; + } + if (name[index] == '\0') ptr[index] = '\0'; + else + { + ptr = (rt_uint8_t *) & (item->prev_free); + for (index = 0; index < sizeof(void *) && (index + sizeof(void *)) < RT_NAME_MAX; index ++) + { + if (name[sizeof(void *) + index] == '\0') break; + ptr[index] = name[sizeof(void *) + index]; + } + + if (name[sizeof(void *) + index] == '\0') ptr[index] = '\0'; + } +} + +void rt_mem_set_tag(void *ptr, const char *name) +{ + struct rt_memheap_item *item; + + if (ptr && name) + { + item = MEMITEM(ptr); + rt_memheap_setname(item, name); + } +} +#endif /* RT_USING_MEMTRACE */ + +/* + * The initialized memory pool will be: + * +-----------------------------------+--------------------------+ + * | whole freed memory block | Used Memory Block Tailer | + * +-----------------------------------+--------------------------+ + * + * block_list --> whole freed memory block + * + * The length of Used Memory Block Tailer is 0, + * which is prevents block merging across list + */ +rt_err_t rt_memheap_init(struct rt_memheap *memheap, + const char *name, + void *start_addr, + rt_size_t size) +{ + struct rt_memheap_item *item; + + RT_ASSERT(memheap != RT_NULL); + + /* initialize pool object */ + rt_object_init(&(memheap->parent), RT_Object_Class_MemHeap, name); + + memheap->start_addr = start_addr; + memheap->pool_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE); + memheap->available_size = memheap->pool_size - (2 * RT_MEMHEAP_SIZE); + memheap->max_used_size = memheap->pool_size - memheap->available_size; + + /* initialize the free list header */ + item = &(memheap->free_header); + item->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED); + item->pool_ptr = memheap; + item->next = RT_NULL; + item->prev = RT_NULL; + item->next_free = item; + item->prev_free = item; + + /* set the free list to free list header */ + memheap->free_list = item; + + /* initialize the first big memory block */ + item = (struct rt_memheap_item *)start_addr; + item->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED); + item->pool_ptr = memheap; + item->next = RT_NULL; + item->prev = RT_NULL; + item->next_free = item; + item->prev_free = item; + +#ifdef RT_USING_MEMTRACE + rt_memset(item->owner_thread_name, ' ', sizeof(item->owner_thread_name)); +#endif /* RT_USING_MEMTRACE */ + + item->next = (struct rt_memheap_item *) + ((rt_uint8_t *)item + memheap->available_size + RT_MEMHEAP_SIZE); + item->prev = item->next; + + /* block list header */ + memheap->block_list = item; + + /* place the big memory block to free list */ + item->next_free = memheap->free_list->next_free; + item->prev_free = memheap->free_list; + memheap->free_list->next_free->prev_free = item; + memheap->free_list->next_free = item; + + /* move to the end of memory pool to build a small tailer block, + * which prevents block merging + */ + item = item->next; + /* it's a used memory block */ + item->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED); + item->pool_ptr = memheap; + item->next = (struct rt_memheap_item *)start_addr; + item->prev = (struct rt_memheap_item *)start_addr; + /* not in free list */ + item->next_free = item->prev_free = RT_NULL; + + /* initialize semaphore lock */ + rt_sem_init(&(memheap->lock), name, 1, RT_IPC_FLAG_PRIO); + + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, + ("memory heap: start addr 0x%08x, size %d, free list header 0x%08x\n", + start_addr, size, &(memheap->free_header))); + + return RT_EOK; +} +RTM_EXPORT(rt_memheap_init); + +rt_err_t rt_memheap_detach(struct rt_memheap *heap) +{ + RT_ASSERT(heap); + RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap); + RT_ASSERT(rt_object_is_systemobject(&heap->parent)); + + rt_sem_detach(&heap->lock); + rt_object_detach(&(heap->parent)); + + /* Return a successful completion. */ + return RT_EOK; +} +RTM_EXPORT(rt_memheap_detach); + +void *rt_memheap_alloc(struct rt_memheap *heap, rt_size_t size) +{ + rt_err_t result; + rt_uint32_t free_size; + struct rt_memheap_item *header_ptr; + + RT_ASSERT(heap != RT_NULL); + RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap); + + /* align allocated size */ + size = RT_ALIGN(size, RT_ALIGN_SIZE); + if (size < RT_MEMHEAP_MINIALLOC) + size = RT_MEMHEAP_MINIALLOC; + + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate %d on heap:%8.*s", + size, RT_NAME_MAX, heap->parent.name)); + + if (size < heap->available_size) + { + /* search on free list */ + free_size = 0; + + /* lock memheap */ + result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); + if (result != RT_EOK) + { + rt_set_errno(result); + + return RT_NULL; + } + + /* get the first free memory block */ + header_ptr = heap->free_list->next_free; + while (header_ptr != heap->free_list && free_size < size) + { + /* get current freed memory block size */ + free_size = MEMITEM_SIZE(header_ptr); + if (free_size < size) + { + /* move to next free memory block */ + header_ptr = header_ptr->next_free; + } + } + + /* determine if the memory is available. */ + if (free_size >= size) + { + /* a block that satisfies the request has been found. */ + + /* determine if the block needs to be split. */ + if (free_size >= (size + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC)) + { + struct rt_memheap_item *new_ptr; + + /* split the block. */ + new_ptr = (struct rt_memheap_item *) + (((rt_uint8_t *)header_ptr) + size + RT_MEMHEAP_SIZE); + + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, + ("split: block[0x%08x] nextm[0x%08x] prevm[0x%08x] to new[0x%08x]\n", + header_ptr, + header_ptr->next, + header_ptr->prev, + new_ptr)); + + /* mark the new block as a memory block and freed. */ + new_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED); + + /* put the pool pointer into the new block. */ + new_ptr->pool_ptr = heap; + +#ifdef RT_USING_MEMTRACE + rt_memset(new_ptr->owner_thread_name, ' ', sizeof(new_ptr->owner_thread_name)); +#endif /* RT_USING_MEMTRACE */ + + /* break down the block list */ + new_ptr->prev = header_ptr; + new_ptr->next = header_ptr->next; + header_ptr->next->prev = new_ptr; + header_ptr->next = new_ptr; + + /* remove header ptr from free list */ + header_ptr->next_free->prev_free = header_ptr->prev_free; + header_ptr->prev_free->next_free = header_ptr->next_free; + header_ptr->next_free = RT_NULL; + header_ptr->prev_free = RT_NULL; + + /* insert new_ptr to free list */ + new_ptr->next_free = heap->free_list->next_free; + new_ptr->prev_free = heap->free_list; + heap->free_list->next_free->prev_free = new_ptr; + heap->free_list->next_free = new_ptr; + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new ptr: next_free 0x%08x, prev_free 0x%08x\n", + new_ptr->next_free, + new_ptr->prev_free)); + + /* decrement the available byte count. */ + heap->available_size = heap->available_size - + size - + RT_MEMHEAP_SIZE; + if (heap->pool_size - heap->available_size > heap->max_used_size) + heap->max_used_size = heap->pool_size - heap->available_size; + } + else + { + /* decrement the entire free size from the available bytes count. */ + heap->available_size = heap->available_size - free_size; + if (heap->pool_size - heap->available_size > heap->max_used_size) + heap->max_used_size = heap->pool_size - heap->available_size; + + /* remove header_ptr from free list */ + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, + ("one block: block[0x%08x], next_free 0x%08x, prev_free 0x%08x\n", + header_ptr, + header_ptr->next_free, + header_ptr->prev_free)); + + header_ptr->next_free->prev_free = header_ptr->prev_free; + header_ptr->prev_free->next_free = header_ptr->next_free; + header_ptr->next_free = RT_NULL; + header_ptr->prev_free = RT_NULL; + } + + /* Mark the allocated block as not available. */ + header_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED); + +#ifdef RT_USING_MEMTRACE + if (rt_thread_self()) + rt_memcpy(header_ptr->owner_thread_name, rt_thread_self()->name, sizeof(header_ptr->owner_thread_name)); + else + rt_memcpy(header_ptr->owner_thread_name, "NONE", sizeof(header_ptr->owner_thread_name)); +#endif /* RT_USING_MEMTRACE */ + + /* release lock */ + rt_sem_release(&(heap->lock)); + + /* Return a memory address to the caller. */ + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, + ("alloc mem: memory[0x%08x], heap[0x%08x], size: %d\n", + (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE), + header_ptr, + size)); + + return (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE); + } + + /* release lock */ + rt_sem_release(&(heap->lock)); + } + + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate memory: failed\n")); + + /* Return the completion status. */ + return RT_NULL; +} +RTM_EXPORT(rt_memheap_alloc); + +void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize) +{ + rt_err_t result; + rt_size_t oldsize; + struct rt_memheap_item *header_ptr; + struct rt_memheap_item *new_ptr; + + RT_ASSERT(heap); + RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap); + + if (newsize == 0) + { + rt_memheap_free(ptr); + + return RT_NULL; + } + /* align allocated size */ + newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE); + if (newsize < RT_MEMHEAP_MINIALLOC) + newsize = RT_MEMHEAP_MINIALLOC; + + if (ptr == RT_NULL) + { + return rt_memheap_alloc(heap, newsize); + } + + /* get memory block header and get the size of memory block */ + header_ptr = (struct rt_memheap_item *) + ((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE); + oldsize = MEMITEM_SIZE(header_ptr); + /* re-allocate memory */ + if (newsize > oldsize) + { + void *new_ptr; + /* Fix the crash problem after opening Oz optimization on ac6 */ + volatile struct rt_memheap_item *next_ptr; + + /* lock memheap */ + result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); + if (result != RT_EOK) + { + rt_set_errno(result); + return RT_NULL; + } + + next_ptr = header_ptr->next; + + /* header_ptr should not be the tail */ + RT_ASSERT(next_ptr > header_ptr); + + /* check whether the following free space is enough to expand */ + if (!RT_MEMHEAP_IS_USED(next_ptr)) + { + rt_int32_t nextsize; + + nextsize = MEMITEM_SIZE(next_ptr); + RT_ASSERT(next_ptr > 0); + + /* Here is the ASCII art of the situation that we can make use of + * the next free node without alloc/memcpy, |*| is the control + * block: + * + * oldsize free node + * |*|-----------|*|----------------------|*| + * newsize >= minialloc + * |*|----------------|*|-----------------|*| + */ + if (nextsize + oldsize > newsize + RT_MEMHEAP_MINIALLOC) + { + /* decrement the entire free size from the available bytes count. */ + heap->available_size = heap->available_size - (newsize - oldsize); + if (heap->pool_size - heap->available_size > heap->max_used_size) + heap->max_used_size = heap->pool_size - heap->available_size; + + /* remove next_ptr from free list */ + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, + ("remove block: block[0x%08x], next_free 0x%08x, prev_free 0x%08x", + next_ptr, + next_ptr->next_free, + next_ptr->prev_free)); + + next_ptr->next_free->prev_free = next_ptr->prev_free; + next_ptr->prev_free->next_free = next_ptr->next_free; + next_ptr->next->prev = next_ptr->prev; + next_ptr->prev->next = next_ptr->next; + + /* build a new one on the right place */ + next_ptr = (struct rt_memheap_item *)((char *)ptr + newsize); + + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, + ("new free block: block[0x%08x] nextm[0x%08x] prevm[0x%08x]", + next_ptr, + next_ptr->next, + next_ptr->prev)); + + /* mark the new block as a memory block and freed. */ + next_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED); + + /* put the pool pointer into the new block. */ + next_ptr->pool_ptr = heap; + +#ifdef RT_USING_MEMTRACE + rt_memset((void *)next_ptr->owner_thread_name, ' ', sizeof(next_ptr->owner_thread_name)); +#endif /* RT_USING_MEMTRACE */ + + next_ptr->prev = header_ptr; + next_ptr->next = header_ptr->next; + header_ptr->next->prev = (struct rt_memheap_item *)next_ptr; + header_ptr->next = (struct rt_memheap_item *)next_ptr; + + /* insert next_ptr to free list */ + next_ptr->next_free = heap->free_list->next_free; + next_ptr->prev_free = heap->free_list; + heap->free_list->next_free->prev_free = (struct rt_memheap_item *)next_ptr; + heap->free_list->next_free = (struct rt_memheap_item *)next_ptr; + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new ptr: next_free 0x%08x, prev_free 0x%08x", + next_ptr->next_free, + next_ptr->prev_free)); + + /* release lock */ + rt_sem_release(&(heap->lock)); + + return ptr; + } + } + + /* release lock */ + rt_sem_release(&(heap->lock)); + + /* re-allocate a memory block */ + new_ptr = (void *)rt_memheap_alloc(heap, newsize); + if (new_ptr != RT_NULL) + { + rt_memcpy(new_ptr, ptr, oldsize < newsize ? oldsize : newsize); + rt_memheap_free(ptr); + } + + return new_ptr; + } + + /* don't split when there is less than one node space left */ + if (newsize + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC >= oldsize) + return ptr; + + /* lock memheap */ + result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); + if (result != RT_EOK) + { + rt_set_errno(result); + + return RT_NULL; + } + + /* split the block. */ + new_ptr = (struct rt_memheap_item *) + (((rt_uint8_t *)header_ptr) + newsize + RT_MEMHEAP_SIZE); + + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, + ("split: block[0x%08x] nextm[0x%08x] prevm[0x%08x] to new[0x%08x]\n", + header_ptr, + header_ptr->next, + header_ptr->prev, + new_ptr)); + + /* mark the new block as a memory block and freed. */ + new_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED); + /* put the pool pointer into the new block. */ + new_ptr->pool_ptr = heap; + +#ifdef RT_USING_MEMTRACE + rt_memset(new_ptr->owner_thread_name, ' ', sizeof(new_ptr->owner_thread_name)); +#endif /* RT_USING_MEMTRACE */ + + /* break down the block list */ + new_ptr->prev = header_ptr; + new_ptr->next = header_ptr->next; + header_ptr->next->prev = new_ptr; + header_ptr->next = new_ptr; + + /* determine if the block can be merged with the next neighbor. */ + if (!RT_MEMHEAP_IS_USED(new_ptr->next)) + { + struct rt_memheap_item *free_ptr; + + /* merge block with next neighbor. */ + free_ptr = new_ptr->next; + heap->available_size = heap->available_size - MEMITEM_SIZE(free_ptr); + + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, + ("merge: right node 0x%08x, next_free 0x%08x, prev_free 0x%08x\n", + header_ptr, header_ptr->next_free, header_ptr->prev_free)); + + free_ptr->next->prev = new_ptr; + new_ptr->next = free_ptr->next; + + /* remove free ptr from free list */ + free_ptr->next_free->prev_free = free_ptr->prev_free; + free_ptr->prev_free->next_free = free_ptr->next_free; + } + + /* insert the split block to free list */ + new_ptr->next_free = heap->free_list->next_free; + new_ptr->prev_free = heap->free_list; + heap->free_list->next_free->prev_free = new_ptr; + heap->free_list->next_free = new_ptr; + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new free ptr: next_free 0x%08x, prev_free 0x%08x\n", + new_ptr->next_free, + new_ptr->prev_free)); + + /* increment the available byte count. */ + heap->available_size = heap->available_size + MEMITEM_SIZE(new_ptr); + + /* release lock */ + rt_sem_release(&(heap->lock)); + + /* return the old memory block */ + return ptr; +} +RTM_EXPORT(rt_memheap_realloc); + +void rt_memheap_free(void *ptr) +{ + rt_err_t result; + struct rt_memheap *heap; + struct rt_memheap_item *header_ptr, *new_ptr; + rt_uint32_t insert_header; + + /* NULL check */ + if (ptr == RT_NULL) return; + + /* set initial status as OK */ + insert_header = 1; + new_ptr = RT_NULL; + header_ptr = (struct rt_memheap_item *) + ((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE); + + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("free memory: memory[0x%08x], block[0x%08x]\n", + ptr, header_ptr)); + + /* check magic */ + if (header_ptr->magic != (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED)) + { + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("bad magic:0x%08x @ memheap\n", + header_ptr->magic)); + } + RT_ASSERT(header_ptr->magic == (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED)); + /* check whether this block of memory has been over-written. */ + RT_ASSERT((header_ptr->next->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC); + + /* get pool ptr */ + heap = header_ptr->pool_ptr; + + RT_ASSERT(heap); + RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap); + + /* lock memheap */ + result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); + if (result != RT_EOK) + { + rt_set_errno(result); + + return ; + } + + /* Mark the memory as available. */ + header_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED); + /* Adjust the available number of bytes. */ + heap->available_size += MEMITEM_SIZE(header_ptr); + + /* Determine if the block can be merged with the previous neighbor. */ + if (!RT_MEMHEAP_IS_USED(header_ptr->prev)) + { + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("merge: left node 0x%08x\n", + header_ptr->prev)); + + /* adjust the available number of bytes. */ + heap->available_size += RT_MEMHEAP_SIZE; + + /* yes, merge block with previous neighbor. */ + (header_ptr->prev)->next = header_ptr->next; + (header_ptr->next)->prev = header_ptr->prev; + + /* move header pointer to previous. */ + header_ptr = header_ptr->prev; + /* don't insert header to free list */ + insert_header = 0; + } + + /* determine if the block can be merged with the next neighbor. */ + if (!RT_MEMHEAP_IS_USED(header_ptr->next)) + { + /* adjust the available number of bytes. */ + heap->available_size += RT_MEMHEAP_SIZE; + + /* merge block with next neighbor. */ + new_ptr = header_ptr->next; + + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, + ("merge: right node 0x%08x, next_free 0x%08x, prev_free 0x%08x\n", + new_ptr, new_ptr->next_free, new_ptr->prev_free)); + + new_ptr->next->prev = header_ptr; + header_ptr->next = new_ptr->next; + + /* remove new ptr from free list */ + new_ptr->next_free->prev_free = new_ptr->prev_free; + new_ptr->prev_free->next_free = new_ptr->next_free; + } + + if (insert_header) + { + /* no left merge, insert to free list */ + header_ptr->next_free = heap->free_list->next_free; + header_ptr->prev_free = heap->free_list; + heap->free_list->next_free->prev_free = header_ptr; + heap->free_list->next_free = header_ptr; + + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, + ("insert to free list: next_free 0x%08x, prev_free 0x%08x\n", + header_ptr->next_free, header_ptr->prev_free)); + } + +#ifdef RT_USING_MEMTRACE + rt_memset(header_ptr->owner_thread_name, ' ', sizeof(header_ptr->owner_thread_name)); +#endif /* RT_USING_MEMTRACE */ + + /* release lock */ + rt_sem_release(&(heap->lock)); +} +RTM_EXPORT(rt_memheap_free); + +#ifdef RT_USING_FINSH +static void _memheap_dump_tag(struct rt_memheap_item *item) +{ + rt_uint8_t name[2 * sizeof(void *)]; + rt_uint8_t *ptr; + + ptr = (rt_uint8_t *) & (item->next_free); + rt_memcpy(name, ptr, sizeof(void *)); + ptr = (rt_uint8_t *) & (item->prev_free); + rt_memcpy(&name[sizeof(void *)], ptr, sizeof(void *)); + + rt_kprintf("%.*s", 2 * sizeof(void *), name); +} + +int rt_memheap_dump(struct rt_memheap *heap) +{ + struct rt_memheap_item *item, *end; + + if (heap == RT_NULL) return 0; + RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap); + + rt_kprintf("\n[%.*s] [0x%08x - 0x%08x]->\n", RT_NAME_MAX, heap->parent.name, + (rt_ubase_t)heap->start_addr, (rt_ubase_t)heap->start_addr + heap->pool_size); + rt_kprintf("------------------------------\n"); + + /* lock memheap */ + rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); + item = heap->block_list; + + end = (struct rt_memheap_item *)((rt_uint8_t *)heap->start_addr + heap->pool_size - RT_MEMHEAP_SIZE); + + /* for each memory block */ + while ((rt_ubase_t)item < ((rt_ubase_t)end)) + { + if (RT_MEMHEAP_IS_USED(item) && ((item->magic & RT_MEMHEAP_MASK) != RT_MEMHEAP_MAGIC)) + rt_kprintf("0x%08x", item + 1); + + if (item->magic == (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED)) + { + rt_kprintf("0x%08x: %-8d ", item + 1, MEMITEM_SIZE(item)); + _memheap_dump_tag(item); + rt_kprintf("\n"); + } + else + { + rt_kprintf("0x%08x: %-8d \n", item + 1, MEMITEM_SIZE(item)); + } + + item = item->next; + } + rt_sem_release(&(heap->lock)); + + return 0; +} + +int memheaptrace(void) +{ + int count = rt_object_get_length(RT_Object_Class_MemHeap); + struct rt_memheap **heaps; + + if (count > 0) + { + int index; + extern int list_memheap(void); + + heaps = (struct rt_memheap **)rt_malloc(sizeof(struct rt_memheap *) * count); + if (heaps == RT_NULL) return 0; + + list_memheap(); + + rt_kprintf("memheap header size: %d\n", RT_MEMHEAP_SIZE); + count = rt_object_get_pointers(RT_Object_Class_MemHeap, (rt_object_t *)heaps, count); + for (index = 0; index < count; index++) + { + rt_memheap_dump(heaps[index]); + } + + rt_free(heaps); + } + + return 0; +} +MSH_CMD_EXPORT(memheaptrace, dump memory trace information); +#endif /* RT_USING_FINSH */ + +#ifdef RT_USING_MEMHEAP_AS_HEAP +static struct rt_memheap _heap; + +void rt_system_heap_init(void *begin_addr, void *end_addr) +{ + RT_ASSERT((rt_uint32_t)end_addr > (rt_uint32_t)begin_addr); + + /* initialize a default heap in the system */ + rt_memheap_init(&_heap, + "heap", + begin_addr, + (rt_uint32_t)end_addr - (rt_uint32_t)begin_addr); +} + +void *rt_malloc(rt_size_t size) +{ + void *ptr; + + /* try to allocate in system heap */ + ptr = rt_memheap_alloc(&_heap, size); + if (ptr == RT_NULL) + { + struct rt_object *object; + struct rt_list_node *node; + struct rt_memheap *heap; + struct rt_object_information *information; + + /* try to allocate on other memory heap */ + information = rt_object_get_information(RT_Object_Class_MemHeap); + RT_ASSERT(information != RT_NULL); + for (node = information->object_list.next; + node != &(information->object_list); + node = node->next) + { + object = rt_list_entry(node, struct rt_object, list); + heap = (struct rt_memheap *)object; + + RT_ASSERT(heap); + RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap); + + /* not allocate in the default system heap */ + if (heap == &_heap) + continue; + + ptr = rt_memheap_alloc(heap, size); + if (ptr != RT_NULL) + break; + } + } + + +#ifdef RT_USING_MEMTRACE + if (ptr == RT_NULL) + { + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("malloc[%d] => NULL", size)); + } + else + { + struct rt_memheap_item *item = MEMITEM(ptr); + if (rt_thread_self()) + rt_memheap_setname(item, rt_thread_self()->name); + else + rt_memheap_setname(item, ""); + + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("malloc => 0x%08x : %d", ptr, size)); + } +#endif /* RT_USING_MEMTRACE */ + + return ptr; +} +RTM_EXPORT(rt_malloc); + +void rt_free(void *rmem) +{ + rt_memheap_free(rmem); +} +RTM_EXPORT(rt_free); + +void *rt_realloc(void *rmem, rt_size_t newsize) +{ + void *new_ptr; + struct rt_memheap_item *header_ptr; + + if (rmem == RT_NULL) + return rt_malloc(newsize); + + if (newsize == 0) + { + rt_free(rmem); + return RT_NULL; + } + + /* get old memory item */ + header_ptr = (struct rt_memheap_item *) + ((rt_uint8_t *)rmem - RT_MEMHEAP_SIZE); + + new_ptr = rt_memheap_realloc(header_ptr->pool_ptr, rmem, newsize); + if (new_ptr == RT_NULL && newsize != 0) + { + /* allocate memory block from other memheap */ + new_ptr = rt_malloc(newsize); + if (new_ptr != RT_NULL && rmem != RT_NULL) + { + rt_size_t oldsize; + + /* get the size of old memory block */ + oldsize = MEMITEM_SIZE(header_ptr); + if (newsize > oldsize) + rt_memcpy(new_ptr, rmem, oldsize); + else + rt_memcpy(new_ptr, rmem, newsize); + + rt_free(rmem); + } + } + +#ifdef RT_USING_MEMTRACE + if (new_ptr == RT_NULL) + { + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("realloc[%d] => NULL", newsize)); + } + else + { + struct rt_memheap_item *item = MEMITEM(new_ptr); + if (rt_thread_self()) + rt_memheap_setname(item, rt_thread_self()->name); + else + rt_memheap_setname(item, ""); + + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("realloc => 0x%08x : %d", + new_ptr, newsize)); + } +#endif /* RT_USING_MEMTRACE */ + + return new_ptr; +} +RTM_EXPORT(rt_realloc); + +void *rt_calloc(rt_size_t count, rt_size_t size) +{ + void *ptr; + rt_size_t total_size; + + total_size = count * size; + ptr = rt_malloc(total_size); + if (ptr != RT_NULL) + { + /* clean memory */ + rt_memset(ptr, 0, total_size); + } + +#ifdef RT_USING_MEMTRACE + if (ptr == RT_NULL) + { + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("calloc[%d x %d] => NULL", + count, size)); + } + else + { + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("calloc => 0x%08x : %d", + ptr, count * size)); + } +#endif /* RT_USING_MEMTRACE */ + + return ptr; +} +RTM_EXPORT(rt_calloc); + +void rt_memory_info(rt_uint32_t *total, + rt_uint32_t *used, + rt_uint32_t *max_used) +{ + if (total != RT_NULL) + *total = _heap.pool_size; + + if (used != RT_NULL) + *used = _heap.pool_size - _heap.available_size; + + if (max_used != RT_NULL) + *max_used = _heap.max_used_size; +} + +#endif /* RT_USING_MEMHEAP_AS_HEAP */ + +#ifdef RT_USING_MEMTRACE + +void dump_used_memheap(struct rt_memheap *mh) +{ + struct rt_memheap_item *header_ptr; + rt_uint32_t block_size; + + + rt_kprintf("\nmemory heap address:\n"); + rt_kprintf("heap_ptr: 0x%08x\n", mh->start_addr); + rt_kprintf("free : 0x%08x\n", mh->available_size); + rt_kprintf("max_used: 0x%08x\n", mh->max_used_size); + rt_kprintf("size : 0x%08x\n", mh->pool_size); + + rt_kprintf("\n--memory used information --\n"); + + header_ptr = mh->block_list; + while (header_ptr->next != mh->block_list) + { + if ((header_ptr->magic & RT_MEMHEAP_MASK) != RT_MEMHEAP_MAGIC) + { + rt_kprintf("[0x%08x - incorrect magic: 0x%08x\n", header_ptr, header_ptr->magic); + break; + } + + /* get current memory block size */ + block_size = MEMITEM_SIZE(header_ptr); + if (block_size < 0) + break; + + if (RT_MEMHEAP_IS_USED(header_ptr)) + { + /* dump information */ + rt_kprintf("[0x%08x - %d - %c%c%c%c] used\n", header_ptr, block_size, + header_ptr->owner_thread_name[0], header_ptr->owner_thread_name[1], + header_ptr->owner_thread_name[2], header_ptr->owner_thread_name[3]); + } + else + { + /* dump information */ + rt_kprintf("[0x%08x - %d - %c%c%c%c] free\n", header_ptr, block_size, + header_ptr->owner_thread_name[0], header_ptr->owner_thread_name[1], + header_ptr->owner_thread_name[2], header_ptr->owner_thread_name[3]); + } + + /* move to next used memory block */ + header_ptr = header_ptr->next; + } +} + +void memtrace_heap() +{ + struct rt_object_information *info; + struct rt_list_node *list; + struct rt_memheap *mh; + struct rt_list_node *node; + + info = rt_object_get_information(RT_Object_Class_MemHeap); + list = &info->object_list; + + for (node = list->next; node != list; node = node->next) + { + mh = (struct rt_memheap *)rt_list_entry(node, struct rt_object, list); + dump_used_memheap(mh); + } +} + +#ifdef RT_USING_FINSH +#include +MSH_CMD_EXPORT(memtrace_heap, dump memory trace for heap); +#endif /* RT_USING_FINSH */ + +#endif /* RT_USING_MEMTRACE */ + +#endif /* RT_USING_MEMHEAP */ diff --git a/components/rt-thread/src/mempool.c b/components/rt-thread/src/mempool.c new file mode 100644 index 00000000..5ac87d2b --- /dev/null +++ b/components/rt-thread/src/mempool.c @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-05-27 Bernard implement memory pool + * 2006-06-03 Bernard fix the thread timer init bug + * 2006-06-30 Bernard fix the allocate/free block bug + * 2006-08-04 Bernard add hook support + * 2006-08-10 Bernard fix interrupt bug in rt_mp_alloc + * 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca + * 2010-10-26 yi.qiu add module support in rt_mp_delete + * 2011-01-24 Bernard add object allocation check. + * 2012-03-22 Bernard fix align issue in rt_mp_init and rt_mp_create. + */ + +#include +#include + +#ifdef RT_USING_MEMPOOL + +#ifdef RT_USING_HOOK +static void (*rt_mp_alloc_hook)(struct rt_mempool *mp, void *block); +static void (*rt_mp_free_hook)(struct rt_mempool *mp, void *block); + +/** + * @addtogroup Hook + */ + +/**@{*/ + +/** + * This function will set a hook function, which will be invoked when a memory + * block is allocated from memory pool. + * + * @param hook the hook function + */ +void rt_mp_alloc_sethook(void (*hook)(struct rt_mempool *mp, void *block)) +{ + rt_mp_alloc_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when a memory + * block is released to memory pool. + * + * @param hook the hook function + */ +void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block)) +{ + rt_mp_free_hook = hook; +} + +/**@}*/ +#endif /* RT_USING_HOOK */ + +/** + * @addtogroup MM + */ + +/**@{*/ + +/** + * This function will initialize a memory pool object, normally which is used + * for static object. + * + * @param mp the memory pool object + * @param name the name of memory pool + * @param start the star address of memory pool + * @param size the total size of memory pool + * @param block_size the size for each block + * + * @return RT_EOK + */ +rt_err_t rt_mp_init(struct rt_mempool *mp, + const char *name, + void *start, + rt_size_t size, + rt_size_t block_size) +{ + rt_uint8_t *block_ptr; + register rt_size_t offset; + + /* parameter check */ + RT_ASSERT(mp != RT_NULL); + RT_ASSERT(name != RT_NULL); + RT_ASSERT(start != RT_NULL); + RT_ASSERT(size > 0 && block_size > 0); + + /* initialize object */ + rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name); + + /* initialize memory pool */ + mp->start_address = start; + mp->size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE); + + /* align the block size */ + block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE); + mp->block_size = block_size; + + /* align to align size byte */ + mp->block_total_count = mp->size / (mp->block_size + sizeof(rt_uint8_t *)); + mp->block_free_count = mp->block_total_count; + + /* initialize suspended thread list */ + rt_list_init(&(mp->suspend_thread)); + + /* initialize free block list */ + block_ptr = (rt_uint8_t *)mp->start_address; + for (offset = 0; offset < mp->block_total_count; offset ++) + { + *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) = + (rt_uint8_t *)(block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *))); + } + + *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) = + RT_NULL; + + mp->block_list = block_ptr; + + return RT_EOK; +} +RTM_EXPORT(rt_mp_init); + +/** + * This function will detach a memory pool from system object management. + * + * @param mp the memory pool object + * + * @return RT_EOK + */ +rt_err_t rt_mp_detach(struct rt_mempool *mp) +{ + struct rt_thread *thread; + register rt_ubase_t level; + + /* parameter check */ + RT_ASSERT(mp != RT_NULL); + RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool); + RT_ASSERT(rt_object_is_systemobject(&mp->parent)); + + /* wake up all suspended threads */ + while (!rt_list_isempty(&(mp->suspend_thread))) + { + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* get next suspend thread */ + thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); + /* set error code to RT_ERROR */ + thread->error = -RT_ERROR; + + /* + * resume thread + * In rt_thread_resume function, it will remove current thread from + * suspend list + */ + rt_thread_resume(thread); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + } + + /* detach object */ + rt_object_detach(&(mp->parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_mp_detach); + +#ifdef RT_USING_HEAP +/** + * This function will create a mempool object and allocate the memory pool from + * heap. + * + * @param name the name of memory pool + * @param block_count the count of blocks in memory pool + * @param block_size the size for each block + * + * @return the created mempool object + */ +rt_mp_t rt_mp_create(const char *name, + rt_size_t block_count, + rt_size_t block_size) +{ + rt_uint8_t *block_ptr; + struct rt_mempool *mp; + register rt_size_t offset; + + RT_DEBUG_NOT_IN_INTERRUPT; + + /* parameter check */ + RT_ASSERT(name != RT_NULL); + RT_ASSERT(block_count > 0 && block_size > 0); + + /* allocate object */ + mp = (struct rt_mempool *)rt_object_allocate(RT_Object_Class_MemPool, name); + /* allocate object failed */ + if (mp == RT_NULL) + return RT_NULL; + + /* initialize memory pool */ + block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE); + mp->block_size = block_size; + mp->size = (block_size + sizeof(rt_uint8_t *)) * block_count; + + /* allocate memory */ + mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t *)) * + block_count); + if (mp->start_address == RT_NULL) + { + /* no memory, delete memory pool object */ + rt_object_delete(&(mp->parent)); + + return RT_NULL; + } + + mp->block_total_count = block_count; + mp->block_free_count = mp->block_total_count; + + /* initialize suspended thread list */ + rt_list_init(&(mp->suspend_thread)); + + /* initialize free block list */ + block_ptr = (rt_uint8_t *)mp->start_address; + for (offset = 0; offset < mp->block_total_count; offset ++) + { + *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) + = block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *)); + } + + *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) + = RT_NULL; + + mp->block_list = block_ptr; + + return mp; +} +RTM_EXPORT(rt_mp_create); + +/** + * This function will delete a memory pool and release the object memory. + * + * @param mp the memory pool object + * + * @return RT_EOK + */ +rt_err_t rt_mp_delete(rt_mp_t mp) +{ + struct rt_thread *thread; + register rt_ubase_t level; + + RT_DEBUG_NOT_IN_INTERRUPT; + + /* parameter check */ + RT_ASSERT(mp != RT_NULL); + RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool); + RT_ASSERT(rt_object_is_systemobject(&mp->parent) == RT_FALSE); + + /* wake up all suspended threads */ + while (!rt_list_isempty(&(mp->suspend_thread))) + { + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* get next suspend thread */ + thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); + /* set error code to RT_ERROR */ + thread->error = -RT_ERROR; + + /* + * resume thread + * In rt_thread_resume function, it will remove current thread from + * suspend list + */ + rt_thread_resume(thread); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + } + + /* release allocated room */ + rt_free(mp->start_address); + + /* detach object */ + rt_object_delete(&(mp->parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_mp_delete); +#endif /* RT_USING_HEAP */ + +/** + * This function will allocate a block from memory pool + * + * @param mp the memory pool object + * @param time the waiting time + * + * @return the allocated memory block or RT_NULL on allocated failed + */ +void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time) +{ + rt_uint8_t *block_ptr; + register rt_base_t level; + struct rt_thread *thread; + rt_uint32_t before_sleep = 0; + + /* parameter check */ + RT_ASSERT(mp != RT_NULL); + + /* get current thread */ + thread = rt_thread_self(); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + while (mp->block_free_count == 0) + { + /* memory block is unavailable. */ + if (time == 0) + { + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + rt_set_errno(-RT_ETIMEOUT); + + return RT_NULL; + } + + RT_DEBUG_NOT_IN_INTERRUPT; + + thread->error = RT_EOK; + + /* need suspend thread */ + rt_thread_suspend(thread); + rt_list_insert_after(&(mp->suspend_thread), &(thread->tlist)); + + if (time > 0) + { + /* get the start tick of timer */ + before_sleep = rt_tick_get(); + + /* init thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &time); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + /* do a schedule */ + rt_schedule(); + + if (thread->error != RT_EOK) + return RT_NULL; + + if (time > 0) + { + time -= rt_tick_get() - before_sleep; + if (time < 0) + time = 0; + } + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + } + + /* memory block is available. decrease the free block counter */ + mp->block_free_count--; + + /* get block from block list */ + block_ptr = mp->block_list; + RT_ASSERT(block_ptr != RT_NULL); + + /* Setup the next free node. */ + mp->block_list = *(rt_uint8_t **)block_ptr; + + /* point to memory pool */ + *(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + RT_OBJECT_HOOK_CALL(rt_mp_alloc_hook, + (mp, (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *)))); + + return (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *)); +} +RTM_EXPORT(rt_mp_alloc); + +/** + * This function will release a memory block + * + * @param block the address of memory block to be released + */ +void rt_mp_free(void *block) +{ + rt_uint8_t **block_ptr; + struct rt_mempool *mp; + struct rt_thread *thread; + register rt_base_t level; + + /* parameter check */ + if (block == RT_NULL) return; + + /* get the control block of pool which the block belongs to */ + block_ptr = (rt_uint8_t **)((rt_uint8_t *)block - sizeof(rt_uint8_t *)); + mp = (struct rt_mempool *)*block_ptr; + + RT_OBJECT_HOOK_CALL(rt_mp_free_hook, (mp, block)); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* increase the free block count */ + mp->block_free_count ++; + + /* link the block into the block list */ + *block_ptr = mp->block_list; + mp->block_list = (rt_uint8_t *)block_ptr; + + if (!rt_list_isempty(&(mp->suspend_thread))) + { + /* get the suspended thread */ + thread = rt_list_entry(mp->suspend_thread.next, + struct rt_thread, + tlist); + + /* set error */ + thread->error = RT_EOK; + + /* resume thread */ + rt_thread_resume(thread); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + /* do a schedule */ + rt_schedule(); + + return; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +} +RTM_EXPORT(rt_mp_free); + +/**@}*/ + +#endif /* RT_USING_MEMPOOL */ diff --git a/components/rt-thread/src/object.c b/components/rt-thread/src/object.c new file mode 100644 index 00000000..718b0aa5 --- /dev/null +++ b/components/rt-thread/src/object.c @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-03-14 Bernard the first version + * 2006-04-21 Bernard change the scheduler lock to interrupt lock + * 2006-05-18 Bernard fix the object init bug + * 2006-08-03 Bernard add hook support + * 2007-01-28 Bernard rename RT_OBJECT_Class_Static to RT_Object_Class_Static + * 2010-10-26 yi.qiu add module support in rt_object_allocate and rt_object_free + * 2017-12-10 Bernard Add object_info enum. + * 2018-01-25 Bernard Fix the object find issue when enable MODULE. + */ + +#include +#include + +#ifdef RT_USING_MODULE +#include +#endif /* RT_USING_MODULE */ + +/* + * define object_info for the number of _object_container items. + */ +enum rt_object_info_type +{ + RT_Object_Info_Thread = 0, /**< The object is a thread. */ +#ifdef RT_USING_SEMAPHORE + RT_Object_Info_Semaphore, /**< The object is a semaphore. */ +#endif +#ifdef RT_USING_MUTEX + RT_Object_Info_Mutex, /**< The object is a mutex. */ +#endif +#ifdef RT_USING_EVENT + RT_Object_Info_Event, /**< The object is a event. */ +#endif +#ifdef RT_USING_MAILBOX + RT_Object_Info_MailBox, /**< The object is a mail box. */ +#endif +#ifdef RT_USING_MESSAGEQUEUE + RT_Object_Info_MessageQueue, /**< The object is a message queue. */ +#endif +#ifdef RT_USING_MEMHEAP + RT_Object_Info_MemHeap, /**< The object is a memory heap */ +#endif +#ifdef RT_USING_MEMPOOL + RT_Object_Info_MemPool, /**< The object is a memory pool. */ +#endif +#ifdef RT_USING_DEVICE + RT_Object_Info_Device, /**< The object is a device */ +#endif + RT_Object_Info_Timer, /**< The object is a timer. */ +#ifdef RT_USING_MODULE + RT_Object_Info_Module, /**< The object is a module. */ +#endif + RT_Object_Info_Unknown, /**< The object is unknown. */ +}; + +#define _OBJ_CONTAINER_LIST_INIT(c) \ + {&(_object_container[c].object_list), &(_object_container[c].object_list)} + +static struct rt_object_information _object_container[RT_Object_Info_Unknown] = +{ + /* initialize object container - thread */ + {RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), sizeof(struct rt_thread)}, +#ifdef RT_USING_SEMAPHORE + /* initialize object container - semaphore */ + {RT_Object_Class_Semaphore, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore), sizeof(struct rt_semaphore)}, +#endif +#ifdef RT_USING_MUTEX + /* initialize object container - mutex */ + {RT_Object_Class_Mutex, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Mutex), sizeof(struct rt_mutex)}, +#endif +#ifdef RT_USING_EVENT + /* initialize object container - event */ + {RT_Object_Class_Event, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Event), sizeof(struct rt_event)}, +#endif +#ifdef RT_USING_MAILBOX + /* initialize object container - mailbox */ + {RT_Object_Class_MailBox, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MailBox), sizeof(struct rt_mailbox)}, +#endif +#ifdef RT_USING_MESSAGEQUEUE + /* initialize object container - message queue */ + {RT_Object_Class_MessageQueue, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MessageQueue), sizeof(struct rt_messagequeue)}, +#endif +#ifdef RT_USING_MEMHEAP + /* initialize object container - memory heap */ + {RT_Object_Class_MemHeap, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemHeap), sizeof(struct rt_memheap)}, +#endif +#ifdef RT_USING_MEMPOOL + /* initialize object container - memory pool */ + {RT_Object_Class_MemPool, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemPool), sizeof(struct rt_mempool)}, +#endif +#ifdef RT_USING_DEVICE + /* initialize object container - device */ + {RT_Object_Class_Device, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Device), sizeof(struct rt_device)}, +#endif + /* initialize object container - timer */ + {RT_Object_Class_Timer, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Timer), sizeof(struct rt_timer)}, +#ifdef RT_USING_MODULE + /* initialize object container - module */ + {RT_Object_Class_Module, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Module), sizeof(struct rt_dlmodule)}, +#endif +}; + +#ifdef RT_USING_HOOK +static void (*rt_object_attach_hook)(struct rt_object *object); +static void (*rt_object_detach_hook)(struct rt_object *object); +void (*rt_object_trytake_hook)(struct rt_object *object); +void (*rt_object_take_hook)(struct rt_object *object); +void (*rt_object_put_hook)(struct rt_object *object); + +/** + * @addtogroup Hook + */ + +/**@{*/ + +/** + * This function will set a hook function, which will be invoked when object + * attaches to kernel object system. + * + * @param hook the hook function + */ +void rt_object_attach_sethook(void (*hook)(struct rt_object *object)) +{ + rt_object_attach_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when object + * detaches from kernel object system. + * + * @param hook the hook function + */ +void rt_object_detach_sethook(void (*hook)(struct rt_object *object)) +{ + rt_object_detach_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when object + * is taken from kernel object system. + * + * The object is taken means: + * semaphore - semaphore is taken by thread + * mutex - mutex is taken by thread + * event - event is received by thread + * mailbox - mail is received by thread + * message queue - message is received by thread + * + * @param hook the hook function + */ +void rt_object_trytake_sethook(void (*hook)(struct rt_object *object)) +{ + rt_object_trytake_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when object + * have been taken from kernel object system. + * + * The object have been taken means: + * semaphore - semaphore have been taken by thread + * mutex - mutex have been taken by thread + * event - event have been received by thread + * mailbox - mail have been received by thread + * message queue - message have been received by thread + * timer - timer is started + * + * @param hook the hook function + */ +void rt_object_take_sethook(void (*hook)(struct rt_object *object)) +{ + rt_object_take_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when object + * is put to kernel object system. + * + * @param hook the hook function + */ +void rt_object_put_sethook(void (*hook)(struct rt_object *object)) +{ + rt_object_put_hook = hook; +} + +/**@}*/ +#endif /* RT_USING_HOOK */ + +/** + * @addtogroup KernelObject + */ + +/**@{*/ + +/** + * This function will return the specified type of object information. + * + * @param type the type of object, which can be + * RT_Object_Class_Thread/Semaphore/Mutex... etc + * + * @return the object type information or RT_NULL + */ +struct rt_object_information * +rt_object_get_information(enum rt_object_class_type type) +{ + int index; + + for (index = 0; index < RT_Object_Info_Unknown; index ++) + if (_object_container[index].type == type) return &_object_container[index]; + + return RT_NULL; +} +RTM_EXPORT(rt_object_get_information); + +/** + * This function will return the length of object list in object container. + * + * @param type the type of object, which can be + * RT_Object_Class_Thread/Semaphore/Mutex... etc + * @return the length of object list + */ +int rt_object_get_length(enum rt_object_class_type type) +{ + int count = 0; + rt_ubase_t level; + struct rt_list_node *node = RT_NULL; + struct rt_object_information *information = RT_NULL; + + information = rt_object_get_information((enum rt_object_class_type)type); + if (information == RT_NULL) return 0; + + level = rt_hw_interrupt_disable(); + /* get the count of objects */ + rt_list_for_each(node, &(information->object_list)) + { + count ++; + } + rt_hw_interrupt_enable(level); + + return count; +} +RTM_EXPORT(rt_object_get_length); + +/** + * This function will copy the object pointer of the specified type, + * with the maximum size specified by maxlen. + * + * @param type the type of object, which can be + * RT_Object_Class_Thread/Semaphore/Mutex... etc + * @param pointers the pointers will be saved to + * @param maxlen the maximum number of pointers can be saved + * + * @return the copied number of object pointers + */ +int rt_object_get_pointers(enum rt_object_class_type type, rt_object_t *pointers, int maxlen) +{ + int index = 0; + rt_ubase_t level; + + struct rt_object *object; + struct rt_list_node *node = RT_NULL; + struct rt_object_information *information = RT_NULL; + + if (maxlen <= 0) return 0; + + information = rt_object_get_information((enum rt_object_class_type)type); + if (information == RT_NULL) return 0; + + level = rt_hw_interrupt_disable(); + /* retrieve pointer of object */ + rt_list_for_each(node, &(information->object_list)) + { + object = rt_list_entry(node, struct rt_object, list); + + pointers[index] = object; + index ++; + + if (index >= maxlen) break; + } + rt_hw_interrupt_enable(level); + + return index; +} +RTM_EXPORT(rt_object_get_pointers); + +/** + * This function will initialize an object and add it to object system + * management. + * + * @param object the specified object to be initialized. + * @param type the object type. + * @param name the object name. In system, the object's name must be unique. + */ +void rt_object_init(struct rt_object *object, + enum rt_object_class_type type, + const char *name) +{ + register rt_base_t temp; + struct rt_list_node *node = RT_NULL; + struct rt_object_information *information; +#ifdef RT_USING_MODULE + struct rt_dlmodule *module = dlmodule_self(); +#endif /* RT_USING_MODULE */ + + /* get object information */ + information = rt_object_get_information(type); + RT_ASSERT(information != RT_NULL); + + /* check object type to avoid re-initialization */ + + /* enter critical */ + rt_enter_critical(); + /* try to find object */ + for (node = information->object_list.next; + node != &(information->object_list); + node = node->next) + { + struct rt_object *obj; + + obj = rt_list_entry(node, struct rt_object, list); + if (obj) /* skip warning when disable debug */ + { + RT_ASSERT(obj != object); + } + } + /* leave critical */ + rt_exit_critical(); + + /* initialize object's parameters */ + /* set object type to static */ + object->type = type | RT_Object_Class_Static; + /* copy name */ + rt_strncpy(object->name, name, RT_NAME_MAX); + + RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object)); + + /* lock interrupt */ + temp = rt_hw_interrupt_disable(); + +#ifdef RT_USING_MODULE + if (module) + { + rt_list_insert_after(&(module->object_list), &(object->list)); + object->module_id = (void *)module; + } + else +#endif /* RT_USING_MODULE */ + { + /* insert object into information object list */ + rt_list_insert_after(&(information->object_list), &(object->list)); + } + + /* unlock interrupt */ + rt_hw_interrupt_enable(temp); +} + +/** + * This function will detach a static object from object system, + * and the memory of static object is not freed. + * + * @param object the specified object to be detached. + */ +void rt_object_detach(rt_object_t object) +{ + register rt_base_t temp; + + /* object check */ + RT_ASSERT(object != RT_NULL); + + RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object)); + + /* reset object type */ + object->type = 0; + + /* lock interrupt */ + temp = rt_hw_interrupt_disable(); + + /* remove from old list */ + rt_list_remove(&(object->list)); + + /* unlock interrupt */ + rt_hw_interrupt_enable(temp); +} + +#ifdef RT_USING_HEAP +/** + * This function will allocate an object from object system + * + * @param type the type of object + * @param name the object name. In system, the object's name must be unique. + * + * @return object + */ +rt_object_t rt_object_allocate(enum rt_object_class_type type, const char *name) +{ + struct rt_object *object; + register rt_base_t temp; + struct rt_object_information *information; +#ifdef RT_USING_MODULE + struct rt_dlmodule *module = dlmodule_self(); +#endif /* RT_USING_MODULE */ + + RT_DEBUG_NOT_IN_INTERRUPT; + + /* get object information */ + information = rt_object_get_information(type); + RT_ASSERT(information != RT_NULL); + + object = (struct rt_object *)RT_KERNEL_MALLOC(information->object_size); + if (object == RT_NULL) + { + /* no memory can be allocated */ + return RT_NULL; + } + + /* clean memory data of object */ + rt_memset(object, 0x0, information->object_size); + + /* initialize object's parameters */ + + /* set object type */ + object->type = type; + + /* set object flag */ + object->flag = 0; + + /* copy name */ + rt_strncpy(object->name, name, RT_NAME_MAX); + + RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object)); + + /* lock interrupt */ + temp = rt_hw_interrupt_disable(); + +#ifdef RT_USING_MODULE + if (module) + { + rt_list_insert_after(&(module->object_list), &(object->list)); + object->module_id = (void *)module; + } + else +#endif /* RT_USING_MODULE */ + { + /* insert object into information object list */ + rt_list_insert_after(&(information->object_list), &(object->list)); + } + + /* unlock interrupt */ + rt_hw_interrupt_enable(temp); + + /* return object */ + return object; +} + +/** + * This function will delete an object and release object memory. + * + * @param object the specified object to be deleted. + */ +void rt_object_delete(rt_object_t object) +{ + register rt_base_t temp; + + /* object check */ + RT_ASSERT(object != RT_NULL); + RT_ASSERT(!(object->type & RT_Object_Class_Static)); + + RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object)); + + /* reset object type */ + object->type = RT_Object_Class_Null; + + /* lock interrupt */ + temp = rt_hw_interrupt_disable(); + + /* remove from old list */ + rt_list_remove(&(object->list)); + + /* unlock interrupt */ + rt_hw_interrupt_enable(temp); + + /* free the memory of object */ + RT_KERNEL_FREE(object); +} +#endif /* RT_USING_HEAP */ + +/** + * This function will judge the object is system object or not. + * Normally, the system object is a static object and the type + * of object set to RT_Object_Class_Static. + * + * @param object the specified object to be judged. + * + * @return RT_TRUE if a system object, RT_FALSE for others. + */ +rt_bool_t rt_object_is_systemobject(rt_object_t object) +{ + /* object check */ + RT_ASSERT(object != RT_NULL); + + if (object->type & RT_Object_Class_Static) + return RT_TRUE; + + return RT_FALSE; +} + +/** + * This function will return the type of object without + * RT_Object_Class_Static flag. + * + * @param object the specified object to be get type. + * + * @return the type of object. + */ +rt_uint8_t rt_object_get_type(rt_object_t object) +{ + /* object check */ + RT_ASSERT(object != RT_NULL); + + return object->type & ~RT_Object_Class_Static; +} + +/** + * This function will find specified name object from object + * container. + * + * @param name the specified name of object. + * @param type the type of object + * + * @return the found object or RT_NULL if there is no this object + * in object container. + * + * @note this function shall not be invoked in interrupt status. + */ +rt_object_t rt_object_find(const char *name, rt_uint8_t type) +{ + struct rt_object *object = RT_NULL; + struct rt_list_node *node = RT_NULL; + struct rt_object_information *information = RT_NULL; + + information = rt_object_get_information((enum rt_object_class_type)type); + + /* parameter check */ + if ((name == RT_NULL) || (information == RT_NULL)) return RT_NULL; + + /* which is invoke in interrupt status */ + RT_DEBUG_NOT_IN_INTERRUPT; + + /* enter critical */ + rt_enter_critical(); + + /* try to find object */ + rt_list_for_each(node, &(information->object_list)) + { + object = rt_list_entry(node, struct rt_object, list); + if (rt_strncmp(object->name, name, RT_NAME_MAX) == 0) + { + /* leave critical */ + rt_exit_critical(); + + return object; + } + } + + /* leave critical */ + rt_exit_critical(); + + return RT_NULL; +} + +/**@}*/ diff --git a/components/rt-thread/src/scheduler.c b/components/rt-thread/src/scheduler.c new file mode 100644 index 00000000..bb431e2e --- /dev/null +++ b/components/rt-thread/src/scheduler.c @@ -0,0 +1,990 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-03-17 Bernard the first version + * 2006-04-28 Bernard fix the scheduler algorthm + * 2006-04-30 Bernard add SCHEDULER_DEBUG + * 2006-05-27 Bernard fix the scheduler algorthm for same priority + * thread schedule + * 2006-06-04 Bernard rewrite the scheduler algorithm + * 2006-08-03 Bernard add hook support + * 2006-09-05 Bernard add 32 priority level support + * 2006-09-24 Bernard add rt_system_scheduler_start function + * 2009-09-16 Bernard fix _rt_scheduler_stack_check + * 2010-04-11 yi.qiu add module feature + * 2010-07-13 Bernard fix the maximal number of rt_scheduler_lock_nest + * issue found by kuronca + * 2010-12-13 Bernard add defunct list initialization even if not use heap. + * 2011-05-10 Bernard clean scheduler debug log. + * 2013-12-21 Grissiom add rt_critical_level + * 2018-11-22 Jesven remove the current task from ready queue + * add per cpu ready queue + * add _scheduler_get_highest_priority_thread to find highest priority task + * rt_schedule_insert_thread won't insert current task to ready queue + * in smp version, rt_hw_context_switch_interrupt maybe switch to + * new task directly + * + */ + +#include +#include + +rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX]; +rt_uint32_t rt_thread_ready_priority_group; +#if RT_THREAD_PRIORITY_MAX > 32 +/* Maximum priority level, 256 */ +rt_uint8_t rt_thread_ready_table[32]; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + +#ifndef RT_USING_SMP +extern volatile rt_uint8_t rt_interrupt_nest; +static rt_int16_t rt_scheduler_lock_nest; +struct rt_thread *rt_current_thread = RT_NULL; +rt_uint8_t rt_current_priority; +#endif /* RT_USING_SMP */ + +#ifdef RT_USING_HOOK +static void (*rt_scheduler_hook)(struct rt_thread *from, struct rt_thread *to); +static void (*rt_scheduler_switch_hook)(struct rt_thread *tid); + +/** + * @addtogroup Hook + */ + +/**@{*/ + +/** + * This function will set a hook function, which will be invoked when thread + * switch happens. + * + * @param hook the hook function + */ +void +rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to)) +{ + rt_scheduler_hook = hook; +} + +void +rt_scheduler_switch_sethook(void (*hook)(struct rt_thread *tid)) +{ + rt_scheduler_switch_hook = hook; +} + +/**@}*/ +#endif /* RT_USING_HOOK */ + +#ifdef RT_USING_OVERFLOW_CHECK +static void _rt_scheduler_stack_check(struct rt_thread *thread) +{ + RT_ASSERT(thread != RT_NULL); + +#ifdef ARCH_CPU_STACK_GROWS_UPWARD + if (*((rt_uint8_t *)((rt_ubase_t)thread->stack_addr + thread->stack_size - 1)) != '#' || +#else + if (*((rt_uint8_t *)thread->stack_addr) != '#' || +#endif /* ARCH_CPU_STACK_GROWS_UPWARD */ + (rt_ubase_t)thread->sp <= (rt_ubase_t)thread->stack_addr || + (rt_ubase_t)thread->sp > + (rt_ubase_t)thread->stack_addr + (rt_ubase_t)thread->stack_size) + { + rt_ubase_t level; + + rt_kprintf("thread:%s stack overflow\n", thread->name); + + level = rt_hw_interrupt_disable(); + while (level); + } +#ifdef ARCH_CPU_STACK_GROWS_UPWARD + else if ((rt_ubase_t)thread->sp > ((rt_ubase_t)thread->stack_addr + thread->stack_size)) + { + rt_kprintf("warning: %s stack is close to the top of stack address.\n", + thread->name); + } +#else + else if ((rt_ubase_t)thread->sp <= ((rt_ubase_t)thread->stack_addr + 32)) + { + rt_kprintf("warning: %s stack is close to end of stack address.\n", + thread->name); + } +#endif /* ARCH_CPU_STACK_GROWS_UPWARD */ +} +#endif /* RT_USING_OVERFLOW_CHECK */ + +/* + * get the highest priority thread in ready queue + */ +#ifdef RT_USING_SMP +static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *highest_prio) +{ + register struct rt_thread *highest_priority_thread; + register rt_ubase_t highest_ready_priority, local_highest_ready_priority; + struct rt_cpu* pcpu = rt_cpu_self(); +#if RT_THREAD_PRIORITY_MAX > 32 + register rt_ubase_t number; + + number = __rt_ffs(rt_thread_ready_priority_group) - 1; + highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1; + number = __rt_ffs(pcpu->priority_group) - 1; + local_highest_ready_priority = (number << 3) + __rt_ffs(pcpu->ready_table[number]) - 1; +#else + highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1; + local_highest_ready_priority = __rt_ffs(pcpu->priority_group) - 1; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + + /* get highest ready priority thread */ + if (highest_ready_priority < local_highest_ready_priority) + { + *highest_prio = highest_ready_priority; + highest_priority_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next, + struct rt_thread, + tlist); + } + else + { + *highest_prio = local_highest_ready_priority; + highest_priority_thread = rt_list_entry(pcpu->priority_table[local_highest_ready_priority].next, + struct rt_thread, + tlist); + } + + return highest_priority_thread; +} +#else +static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *highest_prio) +{ + register struct rt_thread *highest_priority_thread; + register rt_ubase_t highest_ready_priority; + +#if RT_THREAD_PRIORITY_MAX > 32 + register rt_ubase_t number; + + number = __rt_ffs(rt_thread_ready_priority_group) - 1; + highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1; +#else + highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + + /* get highest ready priority thread */ + highest_priority_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next, + struct rt_thread, + tlist); + + *highest_prio = highest_ready_priority; + + return highest_priority_thread; +} +#endif /* RT_USING_SMP */ + +/** + * @ingroup SystemInit + * This function will initialize the system scheduler + */ +void rt_system_scheduler_init(void) +{ +#ifdef RT_USING_SMP + int cpu; +#endif /* RT_USING_SMP */ + register rt_base_t offset; + +#ifndef RT_USING_SMP + rt_scheduler_lock_nest = 0; +#endif /* RT_USING_SMP */ + + RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("start scheduler: max priority 0x%02x\n", + RT_THREAD_PRIORITY_MAX)); + + for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++) + { + rt_list_init(&rt_thread_priority_table[offset]); + } +#ifdef RT_USING_SMP + for (cpu = 0; cpu < RT_CPUS_NR; cpu++) + { + struct rt_cpu *pcpu = rt_cpu_index(cpu); + for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++) + { + rt_list_init(&pcpu->priority_table[offset]); + } + + pcpu->irq_switch_flag = 0; + pcpu->current_priority = RT_THREAD_PRIORITY_MAX - 1; + pcpu->current_thread = RT_NULL; + pcpu->priority_group = 0; + +#if RT_THREAD_PRIORITY_MAX > 32 + rt_memset(pcpu->ready_table, 0, sizeof(pcpu->ready_table)); +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + } +#endif /* RT_USING_SMP */ + + /* initialize ready priority group */ + rt_thread_ready_priority_group = 0; + +#if RT_THREAD_PRIORITY_MAX > 32 + /* initialize ready table */ + rt_memset(rt_thread_ready_table, 0, sizeof(rt_thread_ready_table)); +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ +} + +/** + * @ingroup SystemInit + * This function will startup scheduler. It will select one thread + * with the highest priority level, then switch to it. + */ +void rt_system_scheduler_start(void) +{ + register struct rt_thread *to_thread; + rt_ubase_t highest_ready_priority; + + to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); + +#ifdef RT_USING_SMP + to_thread->oncpu = rt_hw_cpu_id(); +#else + rt_current_thread = to_thread; +#endif /* RT_USING_SMP */ + + rt_schedule_remove_thread(to_thread); + to_thread->stat = RT_THREAD_RUNNING; + + /* switch to new thread */ +#ifdef RT_USING_SMP + rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp, to_thread); +#else + rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp); +#endif /* RT_USING_SMP */ + + /* never come back */ +} + +/** + * @addtogroup Thread + */ + +/**@{*/ + + +#ifdef RT_USING_SMP +/** + * This function will handle IPI interrupt and do a scheduling in system; + * + * @param vector, the number of IPI interrupt for system scheduling + * @param param, use RT_NULL + * + * NOTE: this function should be invoke or register as ISR in BSP. + */ +void rt_scheduler_ipi_handler(int vector, void *param) +{ + rt_schedule(); +} + +/** + * This function will perform one scheduling. It will select one thread + * with the highest priority level in global ready queue or local ready queue, + * then switch to it. + */ +void rt_schedule(void) +{ + rt_base_t level; + struct rt_thread *to_thread; + struct rt_thread *current_thread; + struct rt_cpu *pcpu; + int cpu_id; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + cpu_id = rt_hw_cpu_id(); + pcpu = rt_cpu_index(cpu_id); + current_thread = pcpu->current_thread; + + /* whether do switch in interrupt */ + if (pcpu->irq_nest) + { + pcpu->irq_switch_flag = 1; + rt_hw_interrupt_enable(level); + goto __exit; + } + +#ifdef RT_USING_SIGNALS + if ((current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND) + { + /* if current_thread signal is in pending */ + + if ((current_thread->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL_PENDING) + { + rt_thread_resume(current_thread); + } + } +#endif /* RT_USING_SIGNALS */ + + if (current_thread->scheduler_lock_nest == 1) /* whether lock scheduler */ + { + rt_ubase_t highest_ready_priority; + + if (rt_thread_ready_priority_group != 0 || pcpu->priority_group != 0) + { + to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); + current_thread->oncpu = RT_CPU_DETACHED; + if ((current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) + { + if (current_thread->current_priority < highest_ready_priority) + { + to_thread = current_thread; + } + else if (current_thread->current_priority == highest_ready_priority && (current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0) + { + to_thread = current_thread; + } + else + { + rt_schedule_insert_thread(current_thread); + } + current_thread->stat &= ~RT_THREAD_STAT_YIELD_MASK; + } + to_thread->oncpu = cpu_id; + if (to_thread != current_thread) + { + /* if the destination thread is not the same as current thread */ + pcpu->current_priority = (rt_uint8_t)highest_ready_priority; + + RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (current_thread, to_thread)); + + rt_schedule_remove_thread(to_thread); + to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK); + + /* switch to new thread */ + RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, + ("[%d]switch to priority#%d " + "thread:%.*s(sp:0x%08x), " + "from thread:%.*s(sp: 0x%08x)\n", + pcpu->irq_nest, highest_ready_priority, + RT_NAME_MAX, to_thread->name, to_thread->sp, + RT_NAME_MAX, current_thread->name, current_thread->sp)); + +#ifdef RT_USING_OVERFLOW_CHECK + _rt_scheduler_stack_check(to_thread); +#endif /* RT_USING_OVERFLOW_CHECK */ + + RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (current_thread)); + + rt_hw_context_switch((rt_ubase_t)¤t_thread->sp, + (rt_ubase_t)&to_thread->sp, to_thread); + } + } + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + +#ifdef RT_USING_SIGNALS + /* check stat of thread for signal */ + level = rt_hw_interrupt_disable(); + if (current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING) + { + extern void rt_thread_handle_sig(rt_bool_t clean_state); + + current_thread->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; + + rt_hw_interrupt_enable(level); + + /* check signal status */ + rt_thread_handle_sig(RT_TRUE); + } + else + { + rt_hw_interrupt_enable(level); + } +#endif /* RT_USING_SIGNALS */ + +__exit: + return ; +} +#else +/** + * This function will perform one schedule. It will select one thread + * with the highest priority level, and switch to it immediately. + */ +void rt_schedule(void) +{ + rt_base_t level; + struct rt_thread *to_thread; + struct rt_thread *from_thread; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* check the scheduler is enabled or not */ + if (rt_scheduler_lock_nest == 0) + { + rt_ubase_t highest_ready_priority; + + if (rt_thread_ready_priority_group != 0) + { + /* need_insert_from_thread: need to insert from_thread to ready queue */ + int need_insert_from_thread = 0; + + to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); + + if ((rt_current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) + { + if (rt_current_thread->current_priority < highest_ready_priority) + { + to_thread = rt_current_thread; + } + else if (rt_current_thread->current_priority == highest_ready_priority && (rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0) + { + to_thread = rt_current_thread; + } + else + { + need_insert_from_thread = 1; + } + rt_current_thread->stat &= ~RT_THREAD_STAT_YIELD_MASK; + } + + if (to_thread != rt_current_thread) + { + /* if the destination thread is not the same as current thread */ + rt_current_priority = (rt_uint8_t)highest_ready_priority; + from_thread = rt_current_thread; + rt_current_thread = to_thread; + + RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (from_thread, to_thread)); + + if (need_insert_from_thread) + { + rt_schedule_insert_thread(from_thread); + } + + rt_schedule_remove_thread(to_thread); + to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK); + + /* switch to new thread */ + RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, + ("[%d]switch to priority#%d " + "thread:%.*s(sp:0x%08x), " + "from thread:%.*s(sp: 0x%08x)\n", + rt_interrupt_nest, highest_ready_priority, + RT_NAME_MAX, to_thread->name, to_thread->sp, + RT_NAME_MAX, from_thread->name, from_thread->sp)); + +#ifdef RT_USING_OVERFLOW_CHECK + _rt_scheduler_stack_check(to_thread); +#endif /* RT_USING_OVERFLOW_CHECK */ + + if (rt_interrupt_nest == 0) + { + extern void rt_thread_handle_sig(rt_bool_t clean_state); + + RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (from_thread)); + + rt_hw_context_switch((rt_ubase_t)&from_thread->sp, + (rt_ubase_t)&to_thread->sp); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + +#ifdef RT_USING_SIGNALS + /* check stat of thread for signal */ + level = rt_hw_interrupt_disable(); + if (rt_current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING) + { + extern void rt_thread_handle_sig(rt_bool_t clean_state); + + rt_current_thread->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; + + rt_hw_interrupt_enable(level); + + /* check signal status */ + rt_thread_handle_sig(RT_TRUE); + } + else + { + rt_hw_interrupt_enable(level); + } +#endif /* RT_USING_SIGNALS */ + goto __exit; + } + else + { + RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n")); + + rt_hw_context_switch_interrupt((rt_ubase_t)&from_thread->sp, + (rt_ubase_t)&to_thread->sp); + } + } + else + { + rt_schedule_remove_thread(rt_current_thread); + rt_current_thread->stat = RT_THREAD_RUNNING | (rt_current_thread->stat & ~RT_THREAD_STAT_MASK); + } + } + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + +__exit: + return; +} +#endif /* RT_USING_SMP */ + +/** + * This function checks if a scheduling is needed after IRQ context. If yes, + * it will select one thread with the highest priority level, and then switch + * to it. + */ +#ifdef RT_USING_SMP +void rt_scheduler_do_irq_switch(void *context) +{ + int cpu_id; + rt_base_t level; + struct rt_cpu* pcpu; + struct rt_thread *to_thread; + struct rt_thread *current_thread; + + level = rt_hw_interrupt_disable(); + + cpu_id = rt_hw_cpu_id(); + pcpu = rt_cpu_index(cpu_id); + current_thread = pcpu->current_thread; + +#ifdef RT_USING_SIGNALS + if ((current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND) + { + /* if current_thread signal is in pending */ + + if ((current_thread->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL_PENDING) + { + rt_thread_resume(current_thread); + } + } +#endif /* RT_USING_SIGNALS */ + + if (pcpu->irq_switch_flag == 0) + { + rt_hw_interrupt_enable(level); + return; + } + + if (current_thread->scheduler_lock_nest == 1 && pcpu->irq_nest == 0) + { + rt_ubase_t highest_ready_priority; + + /* clear irq switch flag */ + pcpu->irq_switch_flag = 0; + + if (rt_thread_ready_priority_group != 0 || pcpu->priority_group != 0) + { + to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); + current_thread->oncpu = RT_CPU_DETACHED; + if ((current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) + { + if (current_thread->current_priority < highest_ready_priority) + { + to_thread = current_thread; + } + else if (current_thread->current_priority == highest_ready_priority && (current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0) + { + to_thread = current_thread; + } + else + { + rt_schedule_insert_thread(current_thread); + } + current_thread->stat &= ~RT_THREAD_STAT_YIELD_MASK; + } + to_thread->oncpu = cpu_id; + if (to_thread != current_thread) + { + /* if the destination thread is not the same as current thread */ + + pcpu->current_priority = (rt_uint8_t)highest_ready_priority; + + RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (current_thread, to_thread)); + + rt_schedule_remove_thread(to_thread); + to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK); + +#ifdef RT_USING_OVERFLOW_CHECK + _rt_scheduler_stack_check(to_thread); +#endif /* RT_USING_OVERFLOW_CHECK */ + RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n")); + + current_thread->cpus_lock_nest--; + current_thread->scheduler_lock_nest--; + + RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (current_thread)); + + rt_hw_context_switch_interrupt(context, (rt_ubase_t)¤t_thread->sp, + (rt_ubase_t)&to_thread->sp, to_thread); + } + } + } + rt_hw_interrupt_enable(level); +} +#endif /* RT_USING_SMP */ + +/* + * This function will insert a thread to system ready queue. The state of + * thread will be set as READY and remove from suspend queue. + * + * @param thread the thread to be inserted + * @note Please do not invoke this function in user application. + */ +#ifdef RT_USING_SMP +void rt_schedule_insert_thread(struct rt_thread *thread) +{ + int cpu_id; + int bind_cpu; + rt_uint32_t cpu_mask; + register rt_base_t level; + + RT_ASSERT(thread != RT_NULL); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* it should be RUNNING thread */ + if (thread->oncpu != RT_CPU_DETACHED) + { + thread->stat = RT_THREAD_RUNNING | (thread->stat & ~RT_THREAD_STAT_MASK); + goto __exit; + } + + /* READY thread, insert to ready queue */ + thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK); + + cpu_id = rt_hw_cpu_id(); + bind_cpu = thread->bind_cpu ; + + /* insert thread to ready list */ + if (bind_cpu == RT_CPUS_NR) + { +#if RT_THREAD_PRIORITY_MAX > 32 + rt_thread_ready_table[thread->number] |= thread->high_mask; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + rt_thread_ready_priority_group |= thread->number_mask; + + rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]), + &(thread->tlist)); + cpu_mask = RT_CPU_MASK ^ (1 << cpu_id); + rt_hw_ipi_send(RT_SCHEDULE_IPI, cpu_mask); + } + else + { + struct rt_cpu *pcpu = rt_cpu_index(bind_cpu); + +#if RT_THREAD_PRIORITY_MAX > 32 + pcpu->ready_table[thread->number] |= thread->high_mask; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + pcpu->priority_group |= thread->number_mask; + + rt_list_insert_before(&(rt_cpu_index(bind_cpu)->priority_table[thread->current_priority]), + &(thread->tlist)); + + if (cpu_id != bind_cpu) + { + cpu_mask = 1 << bind_cpu; + rt_hw_ipi_send(RT_SCHEDULE_IPI, cpu_mask); + } + } + + RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("insert thread[%.*s], the priority: %d\n", + RT_NAME_MAX, thread->name, thread->current_priority)); + +__exit: + /* enable interrupt */ + rt_hw_interrupt_enable(level); +} +#else +void rt_schedule_insert_thread(struct rt_thread *thread) +{ + register rt_base_t temp; + + RT_ASSERT(thread != RT_NULL); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* it's current thread, it should be RUNNING thread */ + if (thread == rt_current_thread) + { + thread->stat = RT_THREAD_RUNNING | (thread->stat & ~RT_THREAD_STAT_MASK); + goto __exit; + } + + /* READY thread, insert to ready queue */ + thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK); + /* insert thread to ready list */ + rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]), + &(thread->tlist)); + + RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("insert thread[%.*s], the priority: %d\n", + RT_NAME_MAX, thread->name, thread->current_priority)); + + /* set priority mask */ +#if RT_THREAD_PRIORITY_MAX > 32 + rt_thread_ready_table[thread->number] |= thread->high_mask; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + rt_thread_ready_priority_group |= thread->number_mask; + +__exit: + /* enable interrupt */ + rt_hw_interrupt_enable(temp); +} +#endif /* RT_USING_SMP */ + +/* + * This function will remove a thread from system ready queue. + * + * @param thread the thread to be removed + * + * @note Please do not invoke this function in user application. + */ +#ifdef RT_USING_SMP +void rt_schedule_remove_thread(struct rt_thread *thread) +{ + register rt_base_t level; + + RT_ASSERT(thread != RT_NULL); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("remove thread[%.*s], the priority: %d\n", + RT_NAME_MAX, thread->name, + thread->current_priority)); + + /* remove thread from ready list */ + rt_list_remove(&(thread->tlist)); + if (thread->bind_cpu == RT_CPUS_NR) + { + if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority]))) + { +#if RT_THREAD_PRIORITY_MAX > 32 + rt_thread_ready_table[thread->number] &= ~thread->high_mask; + if (rt_thread_ready_table[thread->number] == 0) + { + rt_thread_ready_priority_group &= ~thread->number_mask; + } +#else + rt_thread_ready_priority_group &= ~thread->number_mask; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + } + } + else + { + struct rt_cpu *pcpu = rt_cpu_index(thread->bind_cpu); + + if (rt_list_isempty(&(pcpu->priority_table[thread->current_priority]))) + { +#if RT_THREAD_PRIORITY_MAX > 32 + pcpu->ready_table[thread->number] &= ~thread->high_mask; + if (pcpu->ready_table[thread->number] == 0) + { + pcpu->priority_group &= ~thread->number_mask; + } +#else + pcpu->priority_group &= ~thread->number_mask; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + } + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +} +#else +void rt_schedule_remove_thread(struct rt_thread *thread) +{ + register rt_base_t level; + + RT_ASSERT(thread != RT_NULL); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("remove thread[%.*s], the priority: %d\n", + RT_NAME_MAX, thread->name, + thread->current_priority)); + + /* remove thread from ready list */ + rt_list_remove(&(thread->tlist)); + if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority]))) + { +#if RT_THREAD_PRIORITY_MAX > 32 + rt_thread_ready_table[thread->number] &= ~thread->high_mask; + if (rt_thread_ready_table[thread->number] == 0) + { + rt_thread_ready_priority_group &= ~thread->number_mask; + } +#else + rt_thread_ready_priority_group &= ~thread->number_mask; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +} +#endif /* RT_USING_SMP */ + +/** + * This function will lock the thread scheduler. + */ +#ifdef RT_USING_SMP +void rt_enter_critical(void) +{ + register rt_base_t level; + struct rt_thread *current_thread; + + /* disable interrupt */ + level = rt_hw_local_irq_disable(); + + current_thread = rt_cpu_self()->current_thread; + if (!current_thread) + { + rt_hw_local_irq_enable(level); + return; + } + + /* + * the maximal number of nest is RT_UINT16_MAX, which is big + * enough and does not check here + */ + + { + register rt_uint16_t lock_nest = current_thread->cpus_lock_nest; + current_thread->cpus_lock_nest++; + if (lock_nest == 0) + { + current_thread->scheduler_lock_nest ++; + rt_hw_spin_lock(&_cpus_lock); + } + } + /* critical for local cpu */ + current_thread->critical_lock_nest ++; + + /* lock scheduler for local cpu */ + current_thread->scheduler_lock_nest ++; + + /* enable interrupt */ + rt_hw_local_irq_enable(level); +} +#else +void rt_enter_critical(void) +{ + register rt_base_t level; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* + * the maximal number of nest is RT_UINT16_MAX, which is big + * enough and does not check here + */ + rt_scheduler_lock_nest ++; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +} +#endif /* RT_USING_SMP */ +RTM_EXPORT(rt_enter_critical); + +/** + * This function will unlock the thread scheduler. + */ +#ifdef RT_USING_SMP +void rt_exit_critical(void) +{ + register rt_base_t level; + struct rt_thread *current_thread; + + /* disable interrupt */ + level = rt_hw_local_irq_disable(); + + current_thread = rt_cpu_self()->current_thread; + if (!current_thread) + { + rt_hw_local_irq_enable(level); + return; + } + + current_thread->scheduler_lock_nest --; + + current_thread->critical_lock_nest --; + + current_thread->cpus_lock_nest--; + if (current_thread->cpus_lock_nest == 0) + { + current_thread->scheduler_lock_nest --; + rt_hw_spin_unlock(&_cpus_lock); + } + + if (current_thread->scheduler_lock_nest <= 0) + { + current_thread->scheduler_lock_nest = 0; + /* enable interrupt */ + rt_hw_local_irq_enable(level); + + rt_schedule(); + } + else + { + /* enable interrupt */ + rt_hw_local_irq_enable(level); + } +} +#else +void rt_exit_critical(void) +{ + register rt_base_t level; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + rt_scheduler_lock_nest --; + if (rt_scheduler_lock_nest <= 0) + { + rt_scheduler_lock_nest = 0; + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + if (rt_current_thread) + { + /* if scheduler is started, do a schedule */ + rt_schedule(); + } + } + else + { + /* enable interrupt */ + rt_hw_interrupt_enable(level); + } +} +#endif /* RT_USING_SMP */ +RTM_EXPORT(rt_exit_critical); + +/** + * Get the scheduler lock level + * + * @return the level of the scheduler lock. 0 means unlocked. + */ +rt_uint16_t rt_critical_level(void) +{ +#ifdef RT_USING_SMP + struct rt_thread *current_thread = rt_cpu_self()->current_thread; + + return current_thread->critical_lock_nest; +#else + return rt_scheduler_lock_nest; +#endif /* RT_USING_SMP */ +} +RTM_EXPORT(rt_critical_level); + +/**@}*/ diff --git a/components/rt-thread/src/signal.c b/components/rt-thread/src/signal.c new file mode 100644 index 00000000..c5ae2a70 --- /dev/null +++ b/components/rt-thread/src/signal.c @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2017/10/5 Bernard the first version + * 2018/09/17 Jesven fix: in _signal_deliver RT_THREAD_STAT_MASK to RT_THREAD_STAT_SIGNAL_MASK + * 2018/11/22 Jesven in smp version rt_hw_context_switch_to add a param + */ + +#include +#include + +#include +#include + +#ifdef RT_USING_SIGNALS + +#ifndef RT_SIG_INFO_MAX +#define RT_SIG_INFO_MAX 32 +#endif /* RT_SIG_INFO_MAX */ + +#define DBG_TAG "SIGN" +#define DBG_LVL DBG_WARNING +#include + +#define sig_mask(sig_no) (1u << sig_no) +#define sig_valid(sig_no) (sig_no >= 0 && sig_no < RT_SIG_MAX) + +struct siginfo_node +{ + siginfo_t si; + struct rt_slist_node list; +}; + +static struct rt_mempool *_siginfo_pool; +static void _signal_deliver(rt_thread_t tid); +void rt_thread_handle_sig(rt_bool_t clean_state); + +static void _signal_default_handler(int signo) +{ + LOG_I("handled signo[%d] with default action.", signo); + return ; +} + +static void _signal_entry(void *parameter) +{ + rt_thread_t tid = rt_thread_self(); + + /* handle signal */ + rt_thread_handle_sig(RT_FALSE); + +#ifdef RT_USING_SMP + { + struct rt_cpu* pcpu = rt_cpu_self(); + + pcpu->current_thread->cpus_lock_nest--; + if (pcpu->current_thread->cpus_lock_nest == 0) + { + pcpu->current_thread->scheduler_lock_nest--; + } + + } +#else + /* return to thread */ + tid->sp = tid->sig_ret; + tid->sig_ret = RT_NULL; +#endif /* RT_USING_SMP */ + + LOG_D("switch back to: 0x%08x\n", tid->sp); + tid->stat &= ~RT_THREAD_STAT_SIGNAL; + +#ifdef RT_USING_SMP + rt_hw_context_switch_to((rt_base_t)¶meter, tid); +#else + rt_hw_context_switch_to((rt_ubase_t)&(tid->sp)); +#endif /* RT_USING_SMP */ +} + +/* + * To deliver a signal to thread, there are cases: + * 1. When thread is suspended, function resumes thread and + * set signal stat; + * 2. When thread is ready: + * - If function delivers a signal to self thread, just handle + * it. + * - If function delivers a signal to another ready thread, OS + * should build a slice context to handle it. + */ +static void _signal_deliver(rt_thread_t tid) +{ + rt_ubase_t level; + + level = rt_hw_interrupt_disable(); + + /* thread is not interested in pended signals */ + if (!(tid->sig_pending & tid->sig_mask)) + { + rt_hw_interrupt_enable(level); + return; + } + + if ((tid->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND) + { + /* resume thread to handle signal */ + rt_thread_resume(tid); + /* add signal state */ + tid->stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING); + + rt_hw_interrupt_enable(level); + + /* re-schedule */ + rt_schedule(); + } + else + { + if (tid == rt_thread_self()) + { + /* add signal state */ + tid->stat |= RT_THREAD_STAT_SIGNAL; + + rt_hw_interrupt_enable(level); + + /* do signal action in self thread context */ + if (rt_interrupt_get_nest() == 0) + { + rt_thread_handle_sig(RT_TRUE); + } + } + else if (!((tid->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL)) + { + /* add signal state */ + tid->stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING); + +#ifdef RT_USING_SMP + { + int cpu_id; + + cpu_id = tid->oncpu; + if ((cpu_id != RT_CPU_DETACHED) && (cpu_id != rt_hw_cpu_id())) + { + rt_uint32_t cpu_mask; + + cpu_mask = RT_CPU_MASK ^ (1 << cpu_id); + rt_hw_ipi_send(RT_SCHEDULE_IPI, cpu_mask); + } + } +#else + /* point to the signal handle entry */ + tid->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; + tid->sig_ret = tid->sp; + tid->sp = rt_hw_stack_init((void *)_signal_entry, RT_NULL, + (void *)((char *)tid->sig_ret - 32), RT_NULL); +#endif /* RT_USING_SMP */ + + rt_hw_interrupt_enable(level); + LOG_D("signal stack pointer @ 0x%08x", tid->sp); + + /* re-schedule */ + rt_schedule(); + } + else + { + rt_hw_interrupt_enable(level); + } + } +} + +#ifdef RT_USING_SMP +void *rt_signal_check(void* context) +{ + rt_base_t level; + int cpu_id; + struct rt_cpu* pcpu; + struct rt_thread *current_thread; + + level = rt_hw_interrupt_disable(); + cpu_id = rt_hw_cpu_id(); + pcpu = rt_cpu_index(cpu_id); + current_thread = pcpu->current_thread; + + if (pcpu->irq_nest) + { + rt_hw_interrupt_enable(level); + return context; + } + + if (current_thread->cpus_lock_nest == 1) + { + if (current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING) + { + void *sig_context; + + current_thread->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; + + rt_hw_interrupt_enable(level); + sig_context = rt_hw_stack_init((void *)_signal_entry, context, + (void *)(context - 32), RT_NULL); + return sig_context; + } + } + rt_hw_interrupt_enable(level); + return context; +} +#endif /* RT_USING_SMP */ + +rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler) +{ + rt_base_t level; + rt_sighandler_t old = RT_NULL; + rt_thread_t tid = rt_thread_self(); + + if (!sig_valid(signo)) return SIG_ERR; + + level = rt_hw_interrupt_disable(); + if (tid->sig_vectors == RT_NULL) + { + rt_thread_alloc_sig(tid); + } + + if (tid->sig_vectors) + { + old = tid->sig_vectors[signo]; + + if (handler == SIG_IGN) tid->sig_vectors[signo] = RT_NULL; + else if (handler == SIG_DFL) tid->sig_vectors[signo] = _signal_default_handler; + else tid->sig_vectors[signo] = handler; + } + rt_hw_interrupt_enable(level); + + return old; +} + +void rt_signal_mask(int signo) +{ + rt_base_t level; + rt_thread_t tid = rt_thread_self(); + + level = rt_hw_interrupt_disable(); + + tid->sig_mask &= ~sig_mask(signo); + + rt_hw_interrupt_enable(level); +} + +void rt_signal_unmask(int signo) +{ + rt_base_t level; + rt_thread_t tid = rt_thread_self(); + + level = rt_hw_interrupt_disable(); + + tid->sig_mask |= sig_mask(signo); + + /* let thread handle pended signals */ + if (tid->sig_mask & tid->sig_pending) + { + rt_hw_interrupt_enable(level); + _signal_deliver(tid); + } + else + { + rt_hw_interrupt_enable(level); + } +} + +int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout) +{ + int ret = RT_EOK; + rt_base_t level; + rt_thread_t tid = rt_thread_self(); + struct siginfo_node *si_node = RT_NULL, *si_prev = RT_NULL; + + /* current context checking */ + RT_DEBUG_IN_THREAD_CONTEXT; + + /* parameters check */ + if (set == NULL || *set == 0 || si == NULL ) + { + ret = -RT_EINVAL; + goto __done_return; + } + + /* clear siginfo to avoid unknown value */ + memset(si, 0x0, sizeof(rt_siginfo_t)); + + level = rt_hw_interrupt_disable(); + + /* already pending */ + if (tid->sig_pending & *set) goto __done; + + if (timeout == 0) + { + ret = -RT_ETIMEOUT; + goto __done_int; + } + + /* suspend self thread */ + rt_thread_suspend(tid); + /* set thread stat as waiting for signal */ + tid->stat |= RT_THREAD_STAT_SIGNAL_WAIT; + + /* start timeout timer */ + if (timeout != RT_WAITING_FOREVER) + { + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(tid->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &timeout); + rt_timer_start(&(tid->thread_timer)); + } + rt_hw_interrupt_enable(level); + + /* do thread scheduling */ + rt_schedule(); + + level = rt_hw_interrupt_disable(); + + /* remove signal waiting flag */ + tid->stat &= ~RT_THREAD_STAT_SIGNAL_WAIT; + + /* check errno of thread */ + if (tid->error == -RT_ETIMEOUT) + { + tid->error = RT_EOK; + rt_hw_interrupt_enable(level); + + /* timer timeout */ + ret = -RT_ETIMEOUT; + goto __done_return; + } + +__done: + /* to get the first matched pending signals */ + si_node = (struct siginfo_node *)tid->si_list; + while (si_node) + { + int signo; + + signo = si_node->si.si_signo; + if (sig_mask(signo) & *set) + { + *si = si_node->si; + + LOG_D("sigwait: %d sig raised!", signo); + if (si_prev) si_prev->list.next = si_node->list.next; + else + { + struct siginfo_node *node_next; + + if (si_node->list.next) + { + node_next = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list); + tid->si_list = node_next; + } + else + { + tid->si_list = RT_NULL; + } + } + + /* clear pending */ + tid->sig_pending &= ~sig_mask(signo); + rt_mp_free(si_node); + break; + } + + si_prev = si_node; + if (si_node->list.next) + { + si_node = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list); + } + else + { + si_node = RT_NULL; + } + } + +__done_int: + rt_hw_interrupt_enable(level); + +__done_return: + return ret; +} + +void rt_thread_handle_sig(rt_bool_t clean_state) +{ + rt_base_t level; + + rt_thread_t tid = rt_thread_self(); + struct siginfo_node *si_node; + + level = rt_hw_interrupt_disable(); + if (tid->sig_pending & tid->sig_mask) + { + /* if thread is not waiting for signal */ + if (!(tid->stat & RT_THREAD_STAT_SIGNAL_WAIT)) + { + while (tid->sig_pending & tid->sig_mask) + { + int signo, error; + rt_sighandler_t handler; + + si_node = (struct siginfo_node *)tid->si_list; + if (!si_node) break; + + /* remove this sig info node from list */ + if (si_node->list.next == RT_NULL) + tid->si_list = RT_NULL; + else + tid->si_list = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list); + + signo = si_node->si.si_signo; + handler = tid->sig_vectors[signo]; + tid->sig_pending &= ~sig_mask(signo); + rt_hw_interrupt_enable(level); + + LOG_D("handle signal: %d, handler 0x%08x", signo, handler); + if (handler) handler(signo); + + level = rt_hw_interrupt_disable(); + error = -RT_EINTR; + + rt_mp_free(si_node); /* release this siginfo node */ + /* set errno in thread tcb */ + tid->error = error; + } + + /* whether clean signal status */ + if (clean_state == RT_TRUE) + { + tid->stat &= ~RT_THREAD_STAT_SIGNAL; + } + else + { + return; + } + } + } + rt_hw_interrupt_enable(level); +} + +void rt_thread_alloc_sig(rt_thread_t tid) +{ + int index; + rt_base_t level; + rt_sighandler_t *vectors; + + vectors = (rt_sighandler_t *)RT_KERNEL_MALLOC(sizeof(rt_sighandler_t) * RT_SIG_MAX); + RT_ASSERT(vectors != RT_NULL); + + for (index = 0; index < RT_SIG_MAX; index ++) + { + vectors[index] = _signal_default_handler; + } + + level = rt_hw_interrupt_disable(); + tid->sig_vectors = vectors; + rt_hw_interrupt_enable(level); +} + +void rt_thread_free_sig(rt_thread_t tid) +{ + rt_base_t level; + struct siginfo_node *si_node; + rt_sighandler_t *sig_vectors; + + level = rt_hw_interrupt_disable(); + si_node = (struct siginfo_node *)tid->si_list; + tid->si_list = RT_NULL; + + sig_vectors = tid->sig_vectors; + tid->sig_vectors = RT_NULL; + rt_hw_interrupt_enable(level); + + if (si_node) + { + struct rt_slist_node *node; + struct rt_slist_node *node_to_free; + + LOG_D("free signal info list"); + node = &(si_node->list); + do + { + node_to_free = node; + node = node->next; + si_node = rt_slist_entry(node_to_free, struct siginfo_node, list); + rt_mp_free(si_node); + } while (node); + } + + if (sig_vectors) + { + RT_KERNEL_FREE(sig_vectors); + } +} + +int rt_thread_kill(rt_thread_t tid, int sig) +{ + siginfo_t si; + rt_base_t level; + struct siginfo_node *si_node; + + RT_ASSERT(tid != RT_NULL); + if (!sig_valid(sig)) return -RT_EINVAL; + + LOG_I("send signal: %d", sig); + si.si_signo = sig; + si.si_code = SI_USER; + si.si_value.sival_ptr = RT_NULL; + + level = rt_hw_interrupt_disable(); + if (tid->sig_pending & sig_mask(sig)) + { + /* whether already emits this signal? */ + struct rt_slist_node *node; + struct siginfo_node *entry; + + si_node = (struct siginfo_node *)tid->si_list; + if (si_node) + node = (struct rt_slist_node *)&si_node->list; + else + node = RT_NULL; + + /* update sig info */ + for (; (node) != RT_NULL; node = node->next) + { + entry = rt_slist_entry(node, struct siginfo_node, list); + if (entry->si.si_signo == sig) + { + memcpy(&(entry->si), &si, sizeof(siginfo_t)); + rt_hw_interrupt_enable(level); + return 0; + } + } + } + rt_hw_interrupt_enable(level); + + si_node = (struct siginfo_node *) rt_mp_alloc(_siginfo_pool, 0); + if (si_node) + { + rt_slist_init(&(si_node->list)); + memcpy(&(si_node->si), &si, sizeof(siginfo_t)); + + level = rt_hw_interrupt_disable(); + + if (tid->si_list) + { + struct siginfo_node *si_list; + + si_list = (struct siginfo_node *)tid->si_list; + rt_slist_append(&(si_list->list), &(si_node->list)); + } + else + { + tid->si_list = si_node; + } + + /* a new signal */ + tid->sig_pending |= sig_mask(sig); + + rt_hw_interrupt_enable(level); + } + else + { + LOG_E("The allocation of signal info node failed."); + } + + /* deliver signal to this thread */ + _signal_deliver(tid); + + return RT_EOK; +} + +int rt_system_signal_init(void) +{ + _siginfo_pool = rt_mp_create("signal", RT_SIG_INFO_MAX, sizeof(struct siginfo_node)); + if (_siginfo_pool == RT_NULL) + { + LOG_E("create memory pool for signal info failed."); + RT_ASSERT(0); + } + + return 0; +} + +#endif /* RT_USING_SIGNALS */ diff --git a/components/rt-thread/src/slab.c b/components/rt-thread/src/slab.c new file mode 100644 index 00000000..ad1dc718 --- /dev/null +++ b/components/rt-thread/src/slab.c @@ -0,0 +1,937 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * File : slab.c + * + * Change Logs: + * Date Author Notes + * 2008-07-12 Bernard the first version + * 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca + * 2010-10-23 yi.qiu add module memory allocator + * 2010-12-18 yi.qiu fix zone release bug + */ + +/* + * KERN_SLABALLOC.C - Kernel SLAB memory allocator + * + * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * + * 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. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``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 + * COPYRIGHT HOLDERS OR CONTRIBUTORS 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. + * + */ + +#include +#include + +#define RT_MEM_STATS + +#if defined (RT_USING_HEAP) && defined (RT_USING_SLAB) +/* some statistical variable */ +#ifdef RT_MEM_STATS +static rt_size_t used_mem, max_mem; +#endif /* RT_MEM_STATS */ + +#ifdef RT_USING_HOOK +static void (*rt_malloc_hook)(void *ptr, rt_size_t size); +static void (*rt_free_hook)(void *ptr); + +/** + * @addtogroup Hook + */ + +/**@{*/ + +/** + * This function will set a hook function, which will be invoked when a memory + * block is allocated from heap memory. + * + * @param hook the hook function + */ +void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size)) +{ + rt_malloc_hook = hook; +} +RTM_EXPORT(rt_malloc_sethook); + +/** + * This function will set a hook function, which will be invoked when a memory + * block is released to heap memory. + * + * @param hook the hook function + */ +void rt_free_sethook(void (*hook)(void *ptr)) +{ + rt_free_hook = hook; +} +RTM_EXPORT(rt_free_sethook); + +/**@}*/ + +#endif /* RT_USING_HOOK */ + +/* + * slab allocator implementation + * + * A slab allocator reserves a ZONE for each chunk size, then lays the + * chunks out in an array within the zone. Allocation and deallocation + * is nearly instantanious, and fragmentation/overhead losses are limited + * to a fixed worst-case amount. + * + * The downside of this slab implementation is in the chunk size + * multiplied by the number of zones. ~80 zones * 128K = 10MB of VM per cpu. + * In a kernel implementation all this memory will be physical so + * the zone size is adjusted downward on machines with less physical + * memory. The upside is that overhead is bounded... this is the *worst* + * case overhead. + * + * Slab management is done on a per-cpu basis and no locking or mutexes + * are required, only a critical section. When one cpu frees memory + * belonging to another cpu's slab manager an asynchronous IPI message + * will be queued to execute the operation. In addition, both the + * high level slab allocator and the low level zone allocator optimize + * M_ZERO requests, and the slab allocator does not have to pre initialize + * the linked list of chunks. + * + * XXX Balancing is needed between cpus. Balance will be handled through + * asynchronous IPIs primarily by reassigning the z_Cpu ownership of chunks. + * + * XXX If we have to allocate a new zone and M_USE_RESERVE is set, use of + * the new zone should be restricted to M_USE_RESERVE requests only. + * + * Alloc Size Chunking Number of zones + * 0-127 8 16 + * 128-255 16 8 + * 256-511 32 8 + * 512-1023 64 8 + * 1024-2047 128 8 + * 2048-4095 256 8 + * 4096-8191 512 8 + * 8192-16383 1024 8 + * 16384-32767 2048 8 + * (if RT_MM_PAGE_SIZE is 4K the maximum zone allocation is 16383) + * + * Allocations >= zone_limit go directly to kmem. + * + * API REQUIREMENTS AND SIDE EFFECTS + * + * To operate as a drop-in replacement to the FreeBSD-4.x malloc() we + * have remained compatible with the following API requirements: + * + * + small power-of-2 sized allocations are power-of-2 aligned (kern_tty) + * + all power-of-2 sized allocations are power-of-2 aligned (twe) + * + malloc(0) is allowed and returns non-RT_NULL (ahc driver) + * + ability to allocate arbitrarily large chunks of memory + */ + +/* + * Chunk structure for free elements + */ +typedef struct slab_chunk +{ + struct slab_chunk *c_next; +} slab_chunk; + +/* + * The IN-BAND zone header is placed at the beginning of each zone. + */ +typedef struct slab_zone +{ + rt_int32_t z_magic; /* magic number for sanity check */ + rt_int32_t z_nfree; /* total free chunks / ualloc space in zone */ + rt_int32_t z_nmax; /* maximum free chunks */ + + struct slab_zone *z_next; /* zoneary[] link if z_nfree non-zero */ + rt_uint8_t *z_baseptr; /* pointer to start of chunk array */ + + rt_int32_t z_uindex; /* current initial allocation index */ + rt_int32_t z_chunksize; /* chunk size for validation */ + + rt_int32_t z_zoneindex; /* zone index */ + slab_chunk *z_freechunk; /* free chunk list */ +} slab_zone; + +#define ZALLOC_SLAB_MAGIC 0x51ab51ab +#define ZALLOC_ZONE_LIMIT (16 * 1024) /* max slab-managed alloc */ +#define ZALLOC_MIN_ZONE_SIZE (32 * 1024) /* minimum zone size */ +#define ZALLOC_MAX_ZONE_SIZE (128 * 1024) /* maximum zone size */ +#define NZONES 72 /* number of zones */ +#define ZONE_RELEASE_THRESH 2 /* threshold number of zones */ + +static slab_zone *zone_array[NZONES]; /* linked list of zones NFree > 0 */ +static slab_zone *zone_free; /* whole zones that have become free */ + +static int zone_free_cnt; +static int zone_size; +static int zone_limit; +static int zone_page_cnt; + +/* + * Misc constants. Note that allocations that are exact multiples of + * RT_MM_PAGE_SIZE, or exceed the zone limit, fall through to the kmem module. + */ +#define MIN_CHUNK_SIZE 8 /* in bytes */ +#define MIN_CHUNK_MASK (MIN_CHUNK_SIZE - 1) + +/* + * Array of descriptors that describe the contents of each page + */ +#define PAGE_TYPE_FREE 0x00 +#define PAGE_TYPE_SMALL 0x01 +#define PAGE_TYPE_LARGE 0x02 +struct memusage +{ + rt_uint32_t type: 2 ; /* page type */ + rt_uint32_t size: 30; /* pages allocated or offset from zone */ +}; +static struct memusage *memusage = RT_NULL; +#define btokup(addr) \ + (&memusage[((rt_ubase_t)(addr) - heap_start) >> RT_MM_PAGE_BITS]) + +static rt_ubase_t heap_start, heap_end; + +/* page allocator */ +struct rt_page_head +{ + struct rt_page_head *next; /* next valid page */ + rt_size_t page; /* number of page */ + + /* dummy */ + char dummy[RT_MM_PAGE_SIZE - (sizeof(struct rt_page_head *) + sizeof(rt_size_t))]; +}; +static struct rt_page_head *rt_page_list; +static struct rt_semaphore heap_sem; + +void *rt_page_alloc(rt_size_t npages) +{ + struct rt_page_head *b, *n; + struct rt_page_head **prev; + + if (npages == 0) + return RT_NULL; + + /* lock heap */ + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + for (prev = &rt_page_list; (b = *prev) != RT_NULL; prev = &(b->next)) + { + if (b->page > npages) + { + /* splite pages */ + n = b + npages; + n->next = b->next; + n->page = b->page - npages; + *prev = n; + break; + } + + if (b->page == npages) + { + /* this node fit, remove this node */ + *prev = b->next; + break; + } + } + + /* unlock heap */ + rt_sem_release(&heap_sem); + + return b; +} + +void rt_page_free(void *addr, rt_size_t npages) +{ + struct rt_page_head *b, *n; + struct rt_page_head **prev; + + RT_ASSERT(addr != RT_NULL); + RT_ASSERT((rt_ubase_t)addr % RT_MM_PAGE_SIZE == 0); + RT_ASSERT(npages != 0); + + n = (struct rt_page_head *)addr; + + /* lock heap */ + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + + for (prev = &rt_page_list; (b = *prev) != RT_NULL; prev = &(b->next)) + { + RT_ASSERT(b->page > 0); + RT_ASSERT(b > n || b + b->page <= n); + + if (b + b->page == n) + { + if (b + (b->page += npages) == b->next) + { + b->page += b->next->page; + b->next = b->next->next; + } + + goto _return; + } + + if (b == n + npages) + { + n->page = b->page + npages; + n->next = b->next; + *prev = n; + + goto _return; + } + + if (b > n + npages) + break; + } + + n->page = npages; + n->next = b; + *prev = n; + +_return: + /* unlock heap */ + rt_sem_release(&heap_sem); +} + +/* + * Initialize the page allocator + */ +static void rt_page_init(void *addr, rt_size_t npages) +{ + RT_ASSERT(addr != RT_NULL); + RT_ASSERT(npages != 0); + + rt_page_list = RT_NULL; + rt_page_free(addr, npages); +} + +/** + * @ingroup SystemInit + * + * This function will init system heap + * + * @param begin_addr the beginning address of system page + * @param end_addr the end address of system page + */ +void rt_system_heap_init(void *begin_addr, void *end_addr) +{ + rt_uint32_t limsize, npages; + + RT_DEBUG_NOT_IN_INTERRUPT; + + /* align begin and end addr to page */ + heap_start = RT_ALIGN((rt_ubase_t)begin_addr, RT_MM_PAGE_SIZE); + heap_end = RT_ALIGN_DOWN((rt_ubase_t)end_addr, RT_MM_PAGE_SIZE); + + if (heap_start >= heap_end) + { + rt_kprintf("rt_system_heap_init, wrong address[0x%x - 0x%x]\n", + (rt_ubase_t)begin_addr, (rt_ubase_t)end_addr); + + return; + } + + limsize = heap_end - heap_start; + npages = limsize / RT_MM_PAGE_SIZE; + + /* initialize heap semaphore */ + rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_PRIO); + + RT_DEBUG_LOG(RT_DEBUG_SLAB, ("heap[0x%x - 0x%x], size 0x%x, 0x%x pages\n", + heap_start, heap_end, limsize, npages)); + + /* init pages */ + rt_page_init((void *)heap_start, npages); + + /* calculate zone size */ + zone_size = ZALLOC_MIN_ZONE_SIZE; + while (zone_size < ZALLOC_MAX_ZONE_SIZE && (zone_size << 1) < (limsize / 1024)) + zone_size <<= 1; + + zone_limit = zone_size / 4; + if (zone_limit > ZALLOC_ZONE_LIMIT) + zone_limit = ZALLOC_ZONE_LIMIT; + + zone_page_cnt = zone_size / RT_MM_PAGE_SIZE; + + RT_DEBUG_LOG(RT_DEBUG_SLAB, ("zone size 0x%x, zone page count 0x%x\n", + zone_size, zone_page_cnt)); + + /* allocate memusage array */ + limsize = npages * sizeof(struct memusage); + limsize = RT_ALIGN(limsize, RT_MM_PAGE_SIZE); + memusage = rt_page_alloc(limsize / RT_MM_PAGE_SIZE); + + RT_DEBUG_LOG(RT_DEBUG_SLAB, ("memusage 0x%x, size 0x%x\n", + (rt_ubase_t)memusage, limsize)); +} + +/* + * Calculate the zone index for the allocation request size and set the + * allocation request size to that particular zone's chunk size. + */ +rt_inline int zoneindex(rt_size_t *bytes) +{ + /* unsigned for shift opt */ + rt_ubase_t n = (rt_ubase_t)(*bytes); + + if (n < 128) + { + *bytes = n = (n + 7) & ~7; + + /* 8 byte chunks, 16 zones */ + return (n / 8 - 1); + } + if (n < 256) + { + *bytes = n = (n + 15) & ~15; + + return (n / 16 + 7); + } + if (n < 8192) + { + if (n < 512) + { + *bytes = n = (n + 31) & ~31; + + return (n / 32 + 15); + } + if (n < 1024) + { + *bytes = n = (n + 63) & ~63; + + return (n / 64 + 23); + } + if (n < 2048) + { + *bytes = n = (n + 127) & ~127; + + return (n / 128 + 31); + } + if (n < 4096) + { + *bytes = n = (n + 255) & ~255; + + return (n / 256 + 39); + } + *bytes = n = (n + 511) & ~511; + + return (n / 512 + 47); + } + if (n < 16384) + { + *bytes = n = (n + 1023) & ~1023; + + return (n / 1024 + 55); + } + + rt_kprintf("Unexpected byte count %d", n); + + return 0; +} + +/** + * @addtogroup MM + */ + +/**@{*/ + +/** + * This function will allocate a block from system heap memory. + * - If the nbytes is less than zero, + * or + * - If there is no nbytes sized memory valid in system, + * the RT_NULL is returned. + * + * @param size the size of memory to be allocated + * + * @return the allocated memory + */ +void *rt_malloc(rt_size_t size) +{ + slab_zone *z; + rt_int32_t zi; + slab_chunk *chunk; + struct memusage *kup; + + /* zero size, return RT_NULL */ + if (size == 0) + return RT_NULL; + + /* + * Handle large allocations directly. There should not be very many of + * these so performance is not a big issue. + */ + if (size >= zone_limit) + { + size = RT_ALIGN(size, RT_MM_PAGE_SIZE); + + chunk = rt_page_alloc(size >> RT_MM_PAGE_BITS); + if (chunk == RT_NULL) + return RT_NULL; + + /* set kup */ + kup = btokup(chunk); + kup->type = PAGE_TYPE_LARGE; + kup->size = size >> RT_MM_PAGE_BITS; + + RT_DEBUG_LOG(RT_DEBUG_SLAB, + ("malloc a large memory 0x%x, page cnt %d, kup %d\n", + size, + size >> RT_MM_PAGE_BITS, + ((rt_ubase_t)chunk - heap_start) >> RT_MM_PAGE_BITS)); + + /* lock heap */ + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + +#ifdef RT_MEM_STATS + used_mem += size; + if (used_mem > max_mem) + max_mem = used_mem; +#endif /* RT_MEM_STATS */ + goto done; + } + + /* lock heap */ + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + + /* + * Attempt to allocate out of an existing zone. First try the free list, + * then allocate out of unallocated space. If we find a good zone move + * it to the head of the list so later allocations find it quickly + * (we might have thousands of zones in the list). + * + * Note: zoneindex() will panic of size is too large. + */ + zi = zoneindex(&size); + RT_ASSERT(zi < NZONES); + + RT_DEBUG_LOG(RT_DEBUG_SLAB, ("try to malloc 0x%x on zone: %d\n", size, zi)); + + if ((z = zone_array[zi]) != RT_NULL) + { + RT_ASSERT(z->z_nfree > 0); + + /* Remove us from the zone_array[] when we become full */ + if (--z->z_nfree == 0) + { + zone_array[zi] = z->z_next; + z->z_next = RT_NULL; + } + + /* + * No chunks are available but nfree said we had some memory, so + * it must be available in the never-before-used-memory area + * governed by uindex. The consequences are very serious if our zone + * got corrupted so we use an explicit rt_kprintf rather then a KASSERT. + */ + if (z->z_uindex + 1 != z->z_nmax) + { + z->z_uindex = z->z_uindex + 1; + chunk = (slab_chunk *)(z->z_baseptr + z->z_uindex * size); + } + else + { + /* find on free chunk list */ + chunk = z->z_freechunk; + + /* remove this chunk from list */ + z->z_freechunk = z->z_freechunk->c_next; + } + +#ifdef RT_MEM_STATS + used_mem += z->z_chunksize; + if (used_mem > max_mem) + max_mem = used_mem; +#endif /* RT_MEM_STATS */ + + goto done; + } + + /* + * If all zones are exhausted we need to allocate a new zone for this + * index. + * + * At least one subsystem, the tty code (see CROUND) expects power-of-2 + * allocations to be power-of-2 aligned. We maintain compatibility by + * adjusting the base offset below. + */ + { + rt_int32_t off; + + if ((z = zone_free) != RT_NULL) + { + /* remove zone from free zone list */ + zone_free = z->z_next; + -- zone_free_cnt; + } + else + { + /* unlock heap, since page allocator will think about lock */ + rt_sem_release(&heap_sem); + + /* allocate a zone from page */ + z = rt_page_alloc(zone_size / RT_MM_PAGE_SIZE); + if (z == RT_NULL) + { + chunk = RT_NULL; + goto __exit; + } + + /* lock heap */ + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + + RT_DEBUG_LOG(RT_DEBUG_SLAB, ("alloc a new zone: 0x%x\n", + (rt_ubase_t)z)); + + /* set message usage */ + for (off = 0, kup = btokup(z); off < zone_page_cnt; off ++) + { + kup->type = PAGE_TYPE_SMALL; + kup->size = off; + + kup ++; + } + } + + /* clear to zero */ + rt_memset(z, 0, sizeof(slab_zone)); + + /* offset of slab zone struct in zone */ + off = sizeof(slab_zone); + + /* + * Guarentee power-of-2 alignment for power-of-2-sized chunks. + * Otherwise just 8-byte align the data. + */ + if ((size | (size - 1)) + 1 == (size << 1)) + off = (off + size - 1) & ~(size - 1); + else + off = (off + MIN_CHUNK_MASK) & ~MIN_CHUNK_MASK; + + z->z_magic = ZALLOC_SLAB_MAGIC; + z->z_zoneindex = zi; + z->z_nmax = (zone_size - off) / size; + z->z_nfree = z->z_nmax - 1; + z->z_baseptr = (rt_uint8_t *)z + off; + z->z_uindex = 0; + z->z_chunksize = size; + + chunk = (slab_chunk *)(z->z_baseptr + z->z_uindex * size); + + /* link to zone array */ + z->z_next = zone_array[zi]; + zone_array[zi] = z; + +#ifdef RT_MEM_STATS + used_mem += z->z_chunksize; + if (used_mem > max_mem) + max_mem = used_mem; +#endif /* RT_MEM_STATS */ + } + +done: + rt_sem_release(&heap_sem); + RT_OBJECT_HOOK_CALL(rt_malloc_hook, ((char *)chunk, size)); + +__exit: + return chunk; +} +RTM_EXPORT(rt_malloc); + +/** + * This function will change the size of previously allocated memory block. + * + * @param ptr the previously allocated memory block + * @param size the new size of memory block + * + * @return the allocated memory + */ +void *rt_realloc(void *ptr, rt_size_t size) +{ + void *nptr; + slab_zone *z; + struct memusage *kup; + + if (ptr == RT_NULL) + return rt_malloc(size); + if (size == 0) + { + rt_free(ptr); + + return RT_NULL; + } + + /* + * Get the original allocation's zone. If the new request winds up + * using the same chunk size we do not have to do anything. + */ + kup = btokup((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK); + if (kup->type == PAGE_TYPE_LARGE) + { + rt_size_t osize; + + osize = kup->size << RT_MM_PAGE_BITS; + if ((nptr = rt_malloc(size)) == RT_NULL) + return RT_NULL; + rt_memcpy(nptr, ptr, size > osize ? osize : size); + rt_free(ptr); + + return nptr; + } + else if (kup->type == PAGE_TYPE_SMALL) + { + z = (slab_zone *)(((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK) - + kup->size * RT_MM_PAGE_SIZE); + RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC); + + zoneindex(&size); + if (z->z_chunksize == size) + return (ptr); /* same chunk */ + + /* + * Allocate memory for the new request size. Note that zoneindex has + * already adjusted the request size to the appropriate chunk size, which + * should optimize our bcopy(). Then copy and return the new pointer. + */ + if ((nptr = rt_malloc(size)) == RT_NULL) + return RT_NULL; + + rt_memcpy(nptr, ptr, size > z->z_chunksize ? z->z_chunksize : size); + rt_free(ptr); + + return nptr; + } + + return RT_NULL; +} +RTM_EXPORT(rt_realloc); + +/** + * This function will contiguously allocate enough space for count objects + * that are size bytes of memory each and returns a pointer to the allocated + * memory. + * + * The allocated memory is filled with bytes of value zero. + * + * @param count number of objects to allocate + * @param size size of the objects to allocate + * + * @return pointer to allocated memory / NULL pointer if there is an error + */ +void *rt_calloc(rt_size_t count, rt_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = rt_malloc(count * size); + + /* zero the memory */ + if (p) + rt_memset(p, 0, count * size); + + return p; +} +RTM_EXPORT(rt_calloc); + +/** + * This function will release the previous allocated memory block by rt_malloc. + * The released memory block is taken back to system heap. + * + * @param ptr the address of memory which will be released + */ +void rt_free(void *ptr) +{ + slab_zone *z; + slab_chunk *chunk; + struct memusage *kup; + + /* free a RT_NULL pointer */ + if (ptr == RT_NULL) + return ; + + RT_OBJECT_HOOK_CALL(rt_free_hook, (ptr)); + + /* get memory usage */ +#if RT_DEBUG_SLAB + { + rt_ubase_t addr = ((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK); + RT_DEBUG_LOG(RT_DEBUG_SLAB, + ("free a memory 0x%x and align to 0x%x, kup index %d\n", + (rt_ubase_t)ptr, + (rt_ubase_t)addr, + ((rt_ubase_t)(addr) - heap_start) >> RT_MM_PAGE_BITS)); + } +#endif /* RT_DEBUG_SLAB */ + + kup = btokup((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK); + /* release large allocation */ + if (kup->type == PAGE_TYPE_LARGE) + { + rt_ubase_t size; + + /* lock heap */ + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + /* clear page counter */ + size = kup->size; + kup->size = 0; + +#ifdef RT_MEM_STATS + used_mem -= size * RT_MM_PAGE_SIZE; +#endif /* RT_MEM_STATS */ + rt_sem_release(&heap_sem); + + RT_DEBUG_LOG(RT_DEBUG_SLAB, + ("free large memory block 0x%x, page count %d\n", + (rt_ubase_t)ptr, size)); + + /* free this page */ + rt_page_free(ptr, size); + + return; + } + + /* lock heap */ + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + + /* zone case. get out zone. */ + z = (slab_zone *)(((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK) - + kup->size * RT_MM_PAGE_SIZE); + RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC); + + chunk = (slab_chunk *)ptr; + chunk->c_next = z->z_freechunk; + z->z_freechunk = chunk; + +#ifdef RT_MEM_STATS + used_mem -= z->z_chunksize; +#endif /* RT_MEM_STATS */ + + /* + * Bump the number of free chunks. If it becomes non-zero the zone + * must be added back onto the appropriate list. + */ + if (z->z_nfree++ == 0) + { + z->z_next = zone_array[z->z_zoneindex]; + zone_array[z->z_zoneindex] = z; + } + + /* + * If the zone becomes totally free, and there are other zones we + * can allocate from, move this zone to the FreeZones list. Since + * this code can be called from an IPI callback, do *NOT* try to mess + * with kernel_map here. Hysteresis will be performed at malloc() time. + */ + if (z->z_nfree == z->z_nmax && + (z->z_next || zone_array[z->z_zoneindex] != z)) + { + slab_zone **pz; + + RT_DEBUG_LOG(RT_DEBUG_SLAB, ("free zone 0x%x\n", + (rt_ubase_t)z, z->z_zoneindex)); + + /* remove zone from zone array list */ + for (pz = &zone_array[z->z_zoneindex]; z != *pz; pz = &(*pz)->z_next) + ; + *pz = z->z_next; + + /* reset zone */ + z->z_magic = -1; + + /* insert to free zone list */ + z->z_next = zone_free; + zone_free = z; + + ++ zone_free_cnt; + + /* release zone to page allocator */ + if (zone_free_cnt > ZONE_RELEASE_THRESH) + { + register rt_base_t i; + + z = zone_free; + zone_free = z->z_next; + -- zone_free_cnt; + + /* set message usage */ + for (i = 0, kup = btokup(z); i < zone_page_cnt; i ++) + { + kup->type = PAGE_TYPE_FREE; + kup->size = 0; + kup ++; + } + + /* unlock heap */ + rt_sem_release(&heap_sem); + + /* release pages */ + rt_page_free(z, zone_size / RT_MM_PAGE_SIZE); + + return; + } + } + /* unlock heap */ + rt_sem_release(&heap_sem); +} +RTM_EXPORT(rt_free); + +#ifdef RT_MEM_STATS +void rt_memory_info(rt_uint32_t *total, + rt_uint32_t *used, + rt_uint32_t *max_used) +{ + if (total != RT_NULL) + *total = heap_end - heap_start; + + if (used != RT_NULL) + *used = used_mem; + + if (max_used != RT_NULL) + *max_used = max_mem; +} + +#ifdef RT_USING_FINSH +#include + +void list_mem(void) +{ + rt_kprintf("total memory: %d\n", heap_end - heap_start); + rt_kprintf("used memory : %d\n", used_mem); + rt_kprintf("maximum allocated memory: %d\n", max_mem); +} +FINSH_FUNCTION_EXPORT(list_mem, list memory usage information) +#endif /* RT_USING_FINSH */ +#endif /* RT_MEM_STATS */ + +/**@}*/ + +#endif /* defined (RT_USING_HEAP) && defined (RT_USING_SLAB) */ diff --git a/components/rt-thread/src/thread.c b/components/rt-thread/src/thread.c new file mode 100644 index 00000000..7e0c10bf --- /dev/null +++ b/components/rt-thread/src/thread.c @@ -0,0 +1,921 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-03-28 Bernard first version + * 2006-04-29 Bernard implement thread timer + * 2006-04-30 Bernard added THREAD_DEBUG + * 2006-05-27 Bernard fixed the rt_thread_yield bug + * 2006-06-03 Bernard fixed the thread timer init bug + * 2006-08-10 Bernard fixed the timer bug in thread_sleep + * 2006-09-03 Bernard changed rt_timer_delete to rt_timer_detach + * 2006-09-03 Bernard implement rt_thread_detach + * 2008-02-16 Bernard fixed the rt_thread_timeout bug + * 2010-03-21 Bernard change the errno of rt_thread_delay/sleep to + * RT_EOK. + * 2010-11-10 Bernard add cleanup callback function in thread exit. + * 2011-09-01 Bernard fixed rt_thread_exit issue when the current + * thread preempted, which reported by Jiaxing Lee. + * 2011-09-08 Bernard fixed the scheduling issue in rt_thread_startup. + * 2012-12-29 Bernard fixed compiling warning. + * 2016-08-09 ArdaFu add thread suspend and resume hook. + * 2017-04-10 armink fixed the rt_thread_delete and rt_thread_detach + * bug when thread has not startup. + * 2018-11-22 Jesven yield is same to rt_schedule + * add support for tasks bound to cpu + * 2021-02-24 Meco Man rearrange rt_thread_control() - schedule the thread when close it + */ + +#include +#include + +#ifdef RT_USING_HOOK +static void (*rt_thread_suspend_hook)(rt_thread_t thread); +static void (*rt_thread_resume_hook) (rt_thread_t thread); +static void (*rt_thread_inited_hook) (rt_thread_t thread); + +/** + * @ingroup Hook + * This function sets a hook function when the system suspend a thread. + * + * @param hook the specified hook function + * + * @note the hook function must be simple and never be blocked or suspend. + */ +void rt_thread_suspend_sethook(void (*hook)(rt_thread_t thread)) +{ + rt_thread_suspend_hook = hook; +} + +/** + * @ingroup Hook + * This function sets a hook function when the system resume a thread. + * + * @param hook the specified hook function + * + * @note the hook function must be simple and never be blocked or suspend. + */ +void rt_thread_resume_sethook(void (*hook)(rt_thread_t thread)) +{ + rt_thread_resume_hook = hook; +} + +/** + * @ingroup Hook + * This function sets a hook function when a thread is initialized. + * + * @param hook the specified hook function + */ +void rt_thread_inited_sethook(void (*hook)(rt_thread_t thread)) +{ + rt_thread_inited_hook = hook; +} + +#endif /* RT_USING_HOOK */ + +/* must be invoke witch rt_hw_interrupt_disable */ +static void _thread_cleanup_execute(rt_thread_t thread) +{ + register rt_base_t level; +#ifdef RT_USING_MODULE + struct rt_dlmodule *module = RT_NULL; +#endif /* RT_USING_MODULE */ + level = rt_hw_interrupt_disable(); +#ifdef RT_USING_MODULE + module = (struct rt_dlmodule*)thread->module_id; + if (module) + { + dlmodule_destroy(module); + } +#endif /* RT_USING_MODULE */ + /* invoke thread cleanup */ + if (thread->cleanup != RT_NULL) + thread->cleanup(thread); + +#ifdef RT_USING_SIGNALS + rt_thread_free_sig(thread); +#endif /* RT_USING_SIGNALS */ + rt_hw_interrupt_enable(level); +} + +static void _thread_exit(void) +{ + struct rt_thread *thread; + register rt_base_t level; + + /* get current thread */ + thread = rt_thread_self(); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + _thread_cleanup_execute(thread); + + /* remove from schedule */ + rt_schedule_remove_thread(thread); + /* change stat */ + thread->stat = RT_THREAD_CLOSE; + + /* remove it from timer list */ + rt_timer_detach(&thread->thread_timer); + + if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) + { + rt_object_detach((rt_object_t)thread); + } + else + { + /* insert to defunct thread list */ + rt_thread_defunct_enqueue(thread); + } + + /* switch to next task */ + rt_schedule(); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +} + +static rt_err_t _thread_init(struct rt_thread *thread, + const char *name, + void (*entry)(void *parameter), + void *parameter, + void *stack_start, + rt_uint32_t stack_size, + rt_uint8_t priority, + rt_uint32_t tick) +{ + /* init thread list */ + rt_list_init(&(thread->tlist)); + + thread->entry = (void *)entry; + thread->parameter = parameter; + + /* stack init */ + thread->stack_addr = stack_start; + thread->stack_size = stack_size; + + /* init thread stack */ + rt_memset(thread->stack_addr, '#', thread->stack_size); +#ifdef ARCH_CPU_STACK_GROWS_UPWARD + thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter, + (void *)((char *)thread->stack_addr), + (void *)_thread_exit); +#else + thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter, + (rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)), + (void *)_thread_exit); +#endif /* ARCH_CPU_STACK_GROWS_UPWARD */ + + /* priority init */ + RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX); + thread->init_priority = priority; + thread->current_priority = priority; + + thread->number_mask = 0; +#if RT_THREAD_PRIORITY_MAX > 32 + thread->number = 0; + thread->high_mask = 0; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + + /* tick init */ + thread->init_tick = tick; + thread->remaining_tick = tick; + + /* error and flags */ + thread->error = RT_EOK; + thread->stat = RT_THREAD_INIT; + +#ifdef RT_USING_SMP + /* not bind on any cpu */ + thread->bind_cpu = RT_CPUS_NR; + thread->oncpu = RT_CPU_DETACHED; + + /* lock init */ + thread->scheduler_lock_nest = 0; + thread->cpus_lock_nest = 0; + thread->critical_lock_nest = 0; +#endif /* RT_USING_SMP */ + + /* initialize cleanup function and user data */ + thread->cleanup = 0; + thread->user_data = 0; + + /* initialize thread timer */ + rt_timer_init(&(thread->thread_timer), + thread->name, + rt_thread_timeout, + thread, + 0, + RT_TIMER_FLAG_ONE_SHOT); + + /* initialize signal */ +#ifdef RT_USING_SIGNALS + thread->sig_mask = 0x00; + thread->sig_pending = 0x00; + +#ifndef RT_USING_SMP + thread->sig_ret = RT_NULL; +#endif /* RT_USING_SMP */ + thread->sig_vectors = RT_NULL; + thread->si_list = RT_NULL; +#endif /* RT_USING_SIGNALS */ + +#ifdef RT_USING_LWP + thread->lwp = RT_NULL; +#endif /* RT_USING_LWP */ + +#ifdef RT_USING_CPU_USAGE + thread->duration_tick = 0; +#endif + + RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread)); + + return RT_EOK; +} + +/** + * @addtogroup Thread + */ + +/**@{*/ + +/** + * This function will initialize a thread, normally it's used to initialize a + * static thread object. + * + * @param thread the static thread object + * @param name the name of thread, which shall be unique + * @param entry the entry function of thread + * @param parameter the parameter of thread enter function + * @param stack_start the start address of thread stack + * @param stack_size the size of thread stack + * @param priority the priority of thread + * @param tick the time slice if there are same priority thread + * + * @return the operation status, RT_EOK on OK, -RT_ERROR on error + */ +rt_err_t rt_thread_init(struct rt_thread *thread, + const char *name, + void (*entry)(void *parameter), + void *parameter, + void *stack_start, + rt_uint32_t stack_size, + rt_uint8_t priority, + rt_uint32_t tick) +{ + /* thread check */ + RT_ASSERT(thread != RT_NULL); + RT_ASSERT(stack_start != RT_NULL); + + /* initialize thread object */ + rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name); + + return _thread_init(thread, + name, + entry, + parameter, + stack_start, + stack_size, + priority, + tick); +} +RTM_EXPORT(rt_thread_init); + +/** + * This function will return self thread object + * + * @return the self thread object + */ +rt_thread_t rt_thread_self(void) +{ +#ifdef RT_USING_SMP + rt_base_t lock; + rt_thread_t self; + + lock = rt_hw_local_irq_disable(); + self = rt_cpu_self()->current_thread; + rt_hw_local_irq_enable(lock); + return self; +#else + extern rt_thread_t rt_current_thread; + + return rt_current_thread; +#endif /* RT_USING_SMP */ +} +RTM_EXPORT(rt_thread_self); + +/** + * This function will start a thread and put it to system ready queue + * + * @param thread the thread to be started + * + * @return the operation status, RT_EOK on OK, -RT_ERROR on error + */ +rt_err_t rt_thread_startup(rt_thread_t thread) +{ + /* thread check */ + RT_ASSERT(thread != RT_NULL); + RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT); + RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); + + /* set current priority to initialize priority */ + thread->current_priority = thread->init_priority; + + /* calculate priority attribute */ +#if RT_THREAD_PRIORITY_MAX > 32 + thread->number = thread->current_priority >> 3; /* 5bit */ + thread->number_mask = 1L << thread->number; + thread->high_mask = 1L << (thread->current_priority & 0x07); /* 3bit */ +#else + thread->number_mask = 1L << thread->current_priority; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + + RT_DEBUG_LOG(RT_DEBUG_THREAD, ("startup a thread:%s with priority:%d\n", + thread->name, thread->init_priority)); + /* change thread stat */ + thread->stat = RT_THREAD_SUSPEND; + /* then resume it */ + rt_thread_resume(thread); + if (rt_thread_self() != RT_NULL) + { + /* do a scheduling */ + rt_schedule(); + } + + return RT_EOK; +} +RTM_EXPORT(rt_thread_startup); + +/** + * This function will detach a thread. The thread object will be removed from + * thread queue and detached/deleted from system object management. + * + * @param thread the thread to be deleted + * + * @return the operation status, RT_EOK on OK, -RT_ERROR on error + */ +rt_err_t rt_thread_detach(rt_thread_t thread) +{ + rt_base_t lock; + + /* thread check */ + RT_ASSERT(thread != RT_NULL); + RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); + RT_ASSERT(rt_object_is_systemobject((rt_object_t)thread)); + + if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE) + return RT_EOK; + + if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) + { + /* remove from schedule */ + rt_schedule_remove_thread(thread); + } + + _thread_cleanup_execute(thread); + + /* release thread timer */ + rt_timer_detach(&(thread->thread_timer)); + + /* change stat */ + thread->stat = RT_THREAD_CLOSE; + + if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) + { + rt_object_detach((rt_object_t)thread); + } + else + { + /* disable interrupt */ + lock = rt_hw_interrupt_disable(); + /* insert to defunct thread list */ + rt_thread_defunct_enqueue(thread); + /* enable interrupt */ + rt_hw_interrupt_enable(lock); + } + + return RT_EOK; +} +RTM_EXPORT(rt_thread_detach); + +#ifdef RT_USING_HEAP +/** + * This function will create a thread object and allocate thread object memory + * and stack. + * + * @param name the name of thread, which shall be unique + * @param entry the entry function of thread + * @param parameter the parameter of thread enter function + * @param stack_size the size of thread stack + * @param priority the priority of thread + * @param tick the time slice if there are same priority thread + * + * @return the created thread object + */ +rt_thread_t rt_thread_create(const char *name, + void (*entry)(void *parameter), + void *parameter, + rt_uint32_t stack_size, + rt_uint8_t priority, + rt_uint32_t tick) +{ + struct rt_thread *thread; + void *stack_start; + + thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread, + name); + if (thread == RT_NULL) + return RT_NULL; + + stack_start = (void *)RT_KERNEL_MALLOC(stack_size); + if (stack_start == RT_NULL) + { + /* allocate stack failure */ + rt_object_delete((rt_object_t)thread); + + return RT_NULL; + } + + _thread_init(thread, + name, + entry, + parameter, + stack_start, + stack_size, + priority, + tick); + + return thread; +} +RTM_EXPORT(rt_thread_create); + +/** + * This function will delete a thread. The thread object will be removed from + * thread queue and deleted from system object management in the idle thread. + * + * @param thread the thread to be deleted + * + * @return the operation status, RT_EOK on OK, -RT_ERROR on error + */ +rt_err_t rt_thread_delete(rt_thread_t thread) +{ + rt_base_t lock; + + /* thread check */ + RT_ASSERT(thread != RT_NULL); + RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); + RT_ASSERT(rt_object_is_systemobject((rt_object_t)thread) == RT_FALSE); + + if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE) + return RT_EOK; + + if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) + { + /* remove from schedule */ + rt_schedule_remove_thread(thread); + } + + _thread_cleanup_execute(thread); + + /* release thread timer */ + rt_timer_detach(&(thread->thread_timer)); + + /* disable interrupt */ + lock = rt_hw_interrupt_disable(); + + /* change stat */ + thread->stat = RT_THREAD_CLOSE; + + /* insert to defunct thread list */ + rt_thread_defunct_enqueue(thread); + + /* enable interrupt */ + rt_hw_interrupt_enable(lock); + + return RT_EOK; +} +RTM_EXPORT(rt_thread_delete); +#endif /* RT_USING_HEAP */ + +/** + * This function will let current thread yield processor, and scheduler will + * choose a highest thread to run. After yield processor, the current thread + * is still in READY state. + * + * @return RT_EOK + */ +rt_err_t rt_thread_yield(void) +{ + struct rt_thread *thread; + rt_base_t lock; + + thread = rt_thread_self(); + lock = rt_hw_interrupt_disable(); + thread->remaining_tick = thread->init_tick; + thread->stat |= RT_THREAD_STAT_YIELD; + rt_schedule(); + rt_hw_interrupt_enable(lock); + + return RT_EOK; +} +RTM_EXPORT(rt_thread_yield); + +/** + * This function will let current thread sleep for some ticks. + * + * @param tick the sleep ticks + * + * @return RT_EOK + */ +rt_err_t rt_thread_sleep(rt_tick_t tick) +{ + register rt_base_t temp; + struct rt_thread *thread; + + /* set to current thread */ + thread = rt_thread_self(); + RT_ASSERT(thread != RT_NULL); + RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* suspend thread */ + rt_thread_suspend(thread); + + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick); + rt_timer_start(&(thread->thread_timer)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + rt_schedule(); + + /* clear error number of this thread to RT_EOK */ + if (thread->error == -RT_ETIMEOUT) + thread->error = RT_EOK; + + return RT_EOK; +} + +/** + * This function will let current thread delay for some ticks. + * + * @param tick the delay ticks + * + * @return RT_EOK + */ +rt_err_t rt_thread_delay(rt_tick_t tick) +{ + return rt_thread_sleep(tick); +} +RTM_EXPORT(rt_thread_delay); + +/** + * This function will let current thread delay until (*tick + inc_tick). + * + * @param tick the tick of last wakeup. + * @param inc_tick the increment tick + * + * @return RT_EOK + */ +rt_err_t rt_thread_delay_until(rt_tick_t *tick, rt_tick_t inc_tick) +{ + register rt_base_t level; + struct rt_thread *thread; + rt_tick_t cur_tick; + + RT_ASSERT(tick != RT_NULL); + + /* set to current thread */ + thread = rt_thread_self(); + RT_ASSERT(thread != RT_NULL); + RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + cur_tick = rt_tick_get(); + if (cur_tick - *tick < inc_tick) + { + rt_tick_t left_tick; + + *tick += inc_tick; + left_tick = *tick - cur_tick; + + /* suspend thread */ + rt_thread_suspend(thread); + + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &left_tick); + rt_timer_start(&(thread->thread_timer)); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + rt_schedule(); + + /* clear error number of this thread to RT_EOK */ + if (thread->error == -RT_ETIMEOUT) + { + thread->error = RT_EOK; + } + } + else + { + *tick = cur_tick; + rt_hw_interrupt_enable(level); + } + + return RT_EOK; +} +RTM_EXPORT(rt_thread_delay_until); + +/** + * This function will let current thread delay for some milliseconds. + * + * @param ms the delay ms time + * + * @return RT_EOK + */ +rt_err_t rt_thread_mdelay(rt_int32_t ms) +{ + rt_tick_t tick; + + tick = rt_tick_from_millisecond(ms); + + return rt_thread_sleep(tick); +} +RTM_EXPORT(rt_thread_mdelay); + +/** + * This function will control thread behaviors according to control command. + * + * @param thread the specified thread to be controlled + * @param cmd the control command, which includes + * RT_THREAD_CTRL_CHANGE_PRIORITY for changing priority level of thread; + * RT_THREAD_CTRL_STARTUP for starting a thread; + * RT_THREAD_CTRL_CLOSE for delete a thread; + * RT_THREAD_CTRL_BIND_CPU for bind the thread to a CPU. + * @param arg the argument of control command + * + * @return RT_EOK + */ +rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg) +{ + register rt_base_t temp; + + /* thread check */ + RT_ASSERT(thread != RT_NULL); + RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); + + switch (cmd) + { + case RT_THREAD_CTRL_CHANGE_PRIORITY: + { + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* for ready thread, change queue */ + if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY) + { + /* remove thread from schedule queue first */ + rt_schedule_remove_thread(thread); + + /* change thread priority */ + thread->current_priority = *(rt_uint8_t *)arg; + + /* recalculate priority attribute */ + #if RT_THREAD_PRIORITY_MAX > 32 + thread->number = thread->current_priority >> 3; /* 5bit */ + thread->number_mask = 1 << thread->number; + thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ + #else + thread->number_mask = 1 << thread->current_priority; + #endif /* RT_THREAD_PRIORITY_MAX > 32 */ + + /* insert thread to schedule queue again */ + rt_schedule_insert_thread(thread); + } + else + { + thread->current_priority = *(rt_uint8_t *)arg; + + /* recalculate priority attribute */ + #if RT_THREAD_PRIORITY_MAX > 32 + thread->number = thread->current_priority >> 3; /* 5bit */ + thread->number_mask = 1 << thread->number; + thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ + #else + thread->number_mask = 1 << thread->current_priority; + #endif /* RT_THREAD_PRIORITY_MAX > 32 */ + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + break; + } + + case RT_THREAD_CTRL_STARTUP: + { + return rt_thread_startup(thread); + } + + case RT_THREAD_CTRL_CLOSE: + { + rt_err_t rt_err; + + if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) + { + rt_err = rt_thread_detach(thread); + } + #ifdef RT_USING_HEAP + else + { + rt_err = rt_thread_delete(thread); + } + #endif /* RT_USING_HEAP */ + rt_schedule(); + return rt_err; + } + + #ifdef RT_USING_SMP + case RT_THREAD_CTRL_BIND_CPU: + { + rt_uint8_t cpu; + + if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) + { + /* we only support bind cpu before started phase. */ + return RT_ERROR; + } + + cpu = (rt_uint8_t)(size_t)arg; + thread->bind_cpu = cpu > RT_CPUS_NR? RT_CPUS_NR : cpu; + break; + } + #endif /* RT_USING_SMP */ + + default: + break; + } + + return RT_EOK; +} +RTM_EXPORT(rt_thread_control); + +/** + * This function will suspend the specified thread. + * + * @param thread the thread to be suspended + * + * @return the operation status, RT_EOK on OK, -RT_ERROR on error + * + * @note if suspend self thread, after this function call, the + * rt_schedule() must be invoked. + */ +rt_err_t rt_thread_suspend(rt_thread_t thread) +{ + register rt_base_t stat; + register rt_base_t temp; + + /* thread check */ + RT_ASSERT(thread != RT_NULL); + RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); + + RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: %s\n", thread->name)); + + stat = thread->stat & RT_THREAD_STAT_MASK; + if ((stat != RT_THREAD_READY) && (stat != RT_THREAD_RUNNING)) + { + RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, 0x%2x\n", + thread->stat)); + return -RT_ERROR; + } + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + if (stat == RT_THREAD_RUNNING) + { + /* not suspend running status thread on other core */ + RT_ASSERT(thread == rt_thread_self()); + } + + /* change thread stat */ + rt_schedule_remove_thread(thread); + thread->stat = RT_THREAD_SUSPEND | (thread->stat & ~RT_THREAD_STAT_MASK); + + /* stop thread timer anyway */ + rt_timer_stop(&(thread->thread_timer)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + RT_OBJECT_HOOK_CALL(rt_thread_suspend_hook, (thread)); + return RT_EOK; +} +RTM_EXPORT(rt_thread_suspend); + +/** + * This function will resume a thread and put it to system ready queue. + * + * @param thread the thread to be resumed + * + * @return the operation status, RT_EOK on OK, -RT_ERROR on error + */ +rt_err_t rt_thread_resume(rt_thread_t thread) +{ + register rt_base_t temp; + + /* thread check */ + RT_ASSERT(thread != RT_NULL); + RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); + + RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: %s\n", thread->name)); + + if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_SUSPEND) + { + RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: thread disorder, %d\n", + thread->stat)); + + return -RT_ERROR; + } + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* remove from suspend list */ + rt_list_remove(&(thread->tlist)); + + rt_timer_stop(&thread->thread_timer); + + /* insert to schedule ready list */ + rt_schedule_insert_thread(thread); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + RT_OBJECT_HOOK_CALL(rt_thread_resume_hook, (thread)); + return RT_EOK; +} +RTM_EXPORT(rt_thread_resume); + +/** + * This function is the timeout function for thread, normally which is invoked + * when thread is timeout to wait some resource. + * + * @param parameter the parameter of thread timeout function + */ +void rt_thread_timeout(void *parameter) +{ + struct rt_thread *thread; + register rt_base_t temp; + + thread = (struct rt_thread *)parameter; + + /* thread check */ + RT_ASSERT(thread != RT_NULL); + RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND); + RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* set error number */ + thread->error = -RT_ETIMEOUT; + + /* remove from suspend list */ + rt_list_remove(&(thread->tlist)); + + /* insert to schedule ready list */ + rt_schedule_insert_thread(thread); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* do schedule */ + rt_schedule(); +} +RTM_EXPORT(rt_thread_timeout); + +/** + * This function will find the specified thread. + * + * @param name the name of thread finding + * + * @return the found thread + * + * @note please don't invoke this function in interrupt status. + */ +rt_thread_t rt_thread_find(char *name) +{ + return (rt_thread_t)rt_object_find(name, RT_Object_Class_Thread); +} +RTM_EXPORT(rt_thread_find); + +/**@}*/ diff --git a/components/rt-thread/src/timer.c b/components/rt-thread/src/timer.c new file mode 100644 index 00000000..44d0d4a7 --- /dev/null +++ b/components/rt-thread/src/timer.c @@ -0,0 +1,808 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-03-12 Bernard first version + * 2006-04-29 Bernard implement thread timer + * 2006-06-04 Bernard implement rt_timer_control + * 2006-08-10 Bernard fix the periodic timer bug + * 2006-09-03 Bernard implement rt_timer_detach + * 2009-11-11 LiJin add soft timer + * 2010-05-12 Bernard fix the timer check bug. + * 2010-11-02 Charlie re-implement tick overflow issue + * 2012-12-15 Bernard fix the next timeout issue in soft timer + * 2014-07-12 Bernard does not lock scheduler when invoking soft-timer + * timeout function. + * 2021-08-15 supperthomas add the comment + */ + +#include +#include + +/* hard timer list */ +static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; + +#ifdef RT_USING_TIMER_SOFT + +#define RT_SOFT_TIMER_IDLE 1 +#define RT_SOFT_TIMER_BUSY 0 + +#ifndef RT_TIMER_THREAD_STACK_SIZE +#define RT_TIMER_THREAD_STACK_SIZE 512 +#endif /* RT_TIMER_THREAD_STACK_SIZE */ + +#ifndef RT_TIMER_THREAD_PRIO +#define RT_TIMER_THREAD_PRIO 0 +#endif /* RT_TIMER_THREAD_PRIO */ + +/* soft timer status */ +static rt_uint8_t soft_timer_status = RT_SOFT_TIMER_IDLE; +/* soft timer list */ +static rt_list_t rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; +static struct rt_thread timer_thread; +ALIGN(RT_ALIGN_SIZE) +static rt_uint8_t timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE]; +#endif /* RT_USING_TIMER_SOFT */ + +#ifdef RT_USING_HOOK +extern void (*rt_object_take_hook)(struct rt_object *object); +extern void (*rt_object_put_hook)(struct rt_object *object); +static void (*rt_timer_enter_hook)(struct rt_timer *timer); +static void (*rt_timer_exit_hook)(struct rt_timer *timer); + +/** + * @addtogroup Hook + */ + +/**@{*/ + +/** + * @brief This function will set a hook function on timer, + * which will be invoked when enter timer timeout callback function. + * + * @param hook the function point of timer + */ +void rt_timer_enter_sethook(void (*hook)(struct rt_timer *timer)) +{ + rt_timer_enter_hook = hook; +} + +/** + * @brief This function will set a hook function, which will be + * invoked when exit * timer timeout callback function. + * + * @param hook the function point of timer + */ +void rt_timer_exit_sethook(void (*hook)(struct rt_timer *timer)) +{ + rt_timer_exit_hook = hook; +} + +/**@}*/ +#endif /* RT_USING_HOOK */ + + +/** + * @brief [internal] the init funtion of timer + * + * the internal called function of rt_timer_init + * + * @see rt_timer_init + * + * @param timer the static timer object + * @param timeout the timeout function + * @param parameter the parameter of timeout function + * @param time the tick of timer + * @param flag the flag of timer + */ +static void _rt_timer_init(rt_timer_t timer, + void (*timeout)(void *parameter), + void *parameter, + rt_tick_t time, + rt_uint8_t flag) +{ + int i; + + /* set flag */ + timer->parent.flag = flag; + + /* set deactivated */ + timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + + timer->timeout_func = timeout; + timer->parameter = parameter; + + timer->timeout_tick = 0; + timer->init_tick = time; + + /* initialize timer list */ + for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++) + { + rt_list_init(&(timer->row[i])); + } +} + +/** + * @brief find the next emtpy timer + * + * @param timer_list the timer of the next timeout + * + * @return rt_tick_t the point of timer + */ +static rt_tick_t rt_timer_list_next_timeout(rt_list_t timer_list[]) +{ + struct rt_timer *timer; + register rt_base_t level; + rt_tick_t timeout_tick = RT_TICK_MAX; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + if (!rt_list_isempty(&timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) + { + timer = rt_list_entry(timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, + struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]); + timeout_tick = timer->timeout_tick; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return timeout_tick; +} + +/** + * @brief remove the timer + * + * @param timer the point of timer + */ +rt_inline void _rt_timer_remove(rt_timer_t timer) +{ + int i; + + for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++) + { + rt_list_remove(&timer->row[i]); + } +} + +#if RT_DEBUG_TIMER +/** + * @brief the number of timer + * + * @param timer + * @return int the count + */ +static int rt_timer_count_height(struct rt_timer *timer) +{ + int i, cnt = 0; + + for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++) + { + if (!rt_list_isempty(&timer->row[i])) + cnt++; + } + return cnt; +} +/** + * @brief dump the all timer information + * + * @param timer_heads the head of timer + */ +void rt_timer_dump(rt_list_t timer_heads[]) +{ + rt_list_t *list; + + for (list = timer_heads[RT_TIMER_SKIP_LIST_LEVEL - 1].next; + list != &timer_heads[RT_TIMER_SKIP_LIST_LEVEL - 1]; + list = list->next) + { + struct rt_timer *timer = rt_list_entry(list, + struct rt_timer, + row[RT_TIMER_SKIP_LIST_LEVEL - 1]); + rt_kprintf("%d", rt_timer_count_height(timer)); + } + rt_kprintf("\n"); +} +#endif /* RT_DEBUG_TIMER */ + +/** + * @addtogroup Clock + */ + +/**@{*/ + +/** + * @brief This function will initialize a timer + * normally this function is used to initialize a static timer object. + * @param timer the static timer object + * @param name the name of timer + * @param timeout the timeout function + * @param parameter the parameter of timeout function + * @param time the tick of timer + * @param flag the flag of timer + */ +void rt_timer_init(rt_timer_t timer, + const char *name, + void (*timeout)(void *parameter), + void *parameter, + rt_tick_t time, + rt_uint8_t flag) +{ + /* timer check */ + RT_ASSERT(timer != RT_NULL); + + /* timer object initialization */ + rt_object_init(&(timer->parent), RT_Object_Class_Timer, name); + + _rt_timer_init(timer, timeout, parameter, time, flag); +} +RTM_EXPORT(rt_timer_init); + +/** + * @brief This function will detach a timer from timer management. + * + * @param timer the timer to be detached + * @return rt_err_t RT_EOK + */ +rt_err_t rt_timer_detach(rt_timer_t timer) +{ + register rt_base_t level; + + /* timer check */ + RT_ASSERT(timer != RT_NULL); + RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); + RT_ASSERT(rt_object_is_systemobject(&timer->parent)); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + _rt_timer_remove(timer); + /* stop timer */ + timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + rt_object_detach(&(timer->parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_timer_detach); + +#ifdef RT_USING_HEAP +/** + * @brief This function will create a timer + * + * @param name the name of timer + * @param timeout the timeout function + * @param parameter the parameter of timeout function + * @param time the tick of timer + * @param flag the flag of timer + * + * @return the created timer object + */ +rt_timer_t rt_timer_create(const char *name, + void (*timeout)(void *parameter), + void *parameter, + rt_tick_t time, + rt_uint8_t flag) +{ + struct rt_timer *timer; + + /* allocate a object */ + timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name); + if (timer == RT_NULL) + { + return RT_NULL; + } + + _rt_timer_init(timer, timeout, parameter, time, flag); + + return timer; +} +RTM_EXPORT(rt_timer_create); + +/** + * @brief This function will delete a timer and release timer memory + * + * @param timer the timer to be deleted + * + * @return the operation status, RT_EOK on OK; RT_ERROR on error + */ +rt_err_t rt_timer_delete(rt_timer_t timer) +{ + register rt_base_t level; + + /* timer check */ + RT_ASSERT(timer != RT_NULL); + RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); + RT_ASSERT(rt_object_is_systemobject(&timer->parent) == RT_FALSE); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + _rt_timer_remove(timer); + /* stop timer */ + timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + rt_object_delete(&(timer->parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_timer_delete); +#endif /* RT_USING_HEAP */ + +/** + * @brief This function will start the timer + * + * @param timer the timer to be started + * + * @return the operation status, RT_EOK on OK, -RT_ERROR on error + */ +rt_err_t rt_timer_start(rt_timer_t timer) +{ + unsigned int row_lvl; + rt_list_t *timer_list; + register rt_base_t level; + rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL]; + unsigned int tst_nr; + static unsigned int random_nr; + + /* timer check */ + RT_ASSERT(timer != RT_NULL); + RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); + + /* stop timer firstly */ + level = rt_hw_interrupt_disable(); + /* remove timer from list */ + _rt_timer_remove(timer); + /* change status of timer */ + timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + + RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent))); + + /* + * get timeout tick, + * the max timeout tick shall not great than RT_TICK_MAX/2 + */ + RT_ASSERT(timer->init_tick < RT_TICK_MAX / 2); + timer->timeout_tick = rt_tick_get() + timer->init_tick; + +#ifdef RT_USING_TIMER_SOFT + if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) + { + /* insert timer to soft timer list */ + timer_list = rt_soft_timer_list; + } + else +#endif /* RT_USING_TIMER_SOFT */ + { + /* insert timer to system timer list */ + timer_list = rt_timer_list; + } + + row_head[0] = &timer_list[0]; + for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++) + { + for (; row_head[row_lvl] != timer_list[row_lvl].prev; + row_head[row_lvl] = row_head[row_lvl]->next) + { + struct rt_timer *t; + rt_list_t *p = row_head[row_lvl]->next; + + /* fix up the entry pointer */ + t = rt_list_entry(p, struct rt_timer, row[row_lvl]); + + /* If we have two timers that timeout at the same time, it's + * preferred that the timer inserted early get called early. + * So insert the new timer to the end the the some-timeout timer + * list. + */ + if ((t->timeout_tick - timer->timeout_tick) == 0) + { + continue; + } + else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2) + { + break; + } + } + if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1) + row_head[row_lvl + 1] = row_head[row_lvl] + 1; + } + + /* Interestingly, this super simple timer insert counter works very very + * well on distributing the list height uniformly. By means of "very very + * well", I mean it beats the randomness of timer->timeout_tick very easily + * (actually, the timeout_tick is not random and easy to be attacked). */ + random_nr++; + tst_nr = random_nr; + + rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - 1], + &(timer->row[RT_TIMER_SKIP_LIST_LEVEL - 1])); + for (row_lvl = 2; row_lvl <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++) + { + if (!(tst_nr & RT_TIMER_SKIP_LIST_MASK)) + rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - row_lvl], + &(timer->row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl])); + else + break; + /* Shift over the bits we have tested. Works well with 1 bit and 2 + * bits. */ + tst_nr >>= (RT_TIMER_SKIP_LIST_MASK + 1) >> 1; + } + + timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + +#ifdef RT_USING_TIMER_SOFT + if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) + { + /* check whether timer thread is ready */ + if ((soft_timer_status == RT_SOFT_TIMER_IDLE) && + ((timer_thread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND)) + { + /* resume timer thread to check soft timer */ + rt_thread_resume(&timer_thread); + rt_schedule(); + } + } +#endif /* RT_USING_TIMER_SOFT */ + + return RT_EOK; +} +RTM_EXPORT(rt_timer_start); + +/** + * @brief This function will stop the timer + * + * @param timer the timer to be stopped + * + * @return the operation status, RT_EOK on OK, -RT_ERROR on error + */ +rt_err_t rt_timer_stop(rt_timer_t timer) +{ + register rt_base_t level; + + /* timer check */ + RT_ASSERT(timer != RT_NULL); + RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); + + if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)) + return -RT_ERROR; + + RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent))); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + _rt_timer_remove(timer); + /* change status */ + timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return RT_EOK; +} +RTM_EXPORT(rt_timer_stop); + +/** + * @brief This function will get or set some options of the timer + * + * @param timer the timer to be get or set + * @param cmd the control command + * @param arg the argument + * + * @return RT_EOK + */ +rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg) +{ + register rt_base_t level; + + /* timer check */ + RT_ASSERT(timer != RT_NULL); + RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); + + level = rt_hw_interrupt_disable(); + switch (cmd) + { + case RT_TIMER_CTRL_GET_TIME: + *(rt_tick_t *)arg = timer->init_tick; + break; + + case RT_TIMER_CTRL_SET_TIME: + timer->init_tick = *(rt_tick_t *)arg; + break; + + case RT_TIMER_CTRL_SET_ONESHOT: + timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC; + break; + + case RT_TIMER_CTRL_SET_PERIODIC: + timer->parent.flag |= RT_TIMER_FLAG_PERIODIC; + break; + + case RT_TIMER_CTRL_GET_STATE: + if(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED) + { + /*timer is start and run*/ + *(rt_tick_t *)arg = RT_TIMER_FLAG_ACTIVATED; + } + else + { + /*timer is stop*/ + *(rt_tick_t *)arg = RT_TIMER_FLAG_DEACTIVATED; + } + break; + + default: + break; + } + rt_hw_interrupt_enable(level); + + return RT_EOK; +} +RTM_EXPORT(rt_timer_control); + +/** + * @brief This function will check timer list, if a timeout event happens, + * the corresponding timeout function will be invoked. + * + * @note this function shall be invoked in operating system timer interrupt. + */ +void rt_timer_check(void) +{ + struct rt_timer *t; + rt_tick_t current_tick; + register rt_base_t level; + rt_list_t list; + + rt_list_init(&list); + + RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter\n")); + + current_tick = rt_tick_get(); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + while (!rt_list_isempty(&rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) + { + t = rt_list_entry(rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, + struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]); + + /* + * It supposes that the new tick shall less than the half duration of + * tick max. + */ + if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2) + { + RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t)); + + /* remove timer from timer list firstly */ + _rt_timer_remove(t); + if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC)) + { + t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + } + /* add timer to temporary list */ + rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1])); + /* call timeout function */ + t->timeout_func(t->parameter); + + /* re-get tick */ + current_tick = rt_tick_get(); + + RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t)); + RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick)); + + /* Check whether the timer object is detached or started again */ + if (rt_list_isempty(&list)) + { + continue; + } + rt_list_remove(&(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1])); + if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) && + (t->parent.flag & RT_TIMER_FLAG_ACTIVATED)) + { + /* start it */ + t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + rt_timer_start(t); + } + } + else break; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check leave\n")); +} + +/** + * @brief This function will return the next timeout tick in the system. + * + * @return the next timeout tick in the system + */ +rt_tick_t rt_timer_next_timeout_tick(void) +{ + return rt_timer_list_next_timeout(rt_timer_list); +} + +#ifdef RT_USING_TIMER_SOFT +/** + * @brief This function will check software-timer list, if a timeout event happens, the + * corresponding timeout function will be invoked. + */ +void rt_soft_timer_check(void) +{ + rt_tick_t current_tick; + struct rt_timer *t; + register rt_base_t level; + rt_list_t list; + + rt_list_init(&list); + + RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check enter\n")); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + while (!rt_list_isempty(&rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) + { + t = rt_list_entry(rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, + struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]); + + current_tick = rt_tick_get(); + + /* + * It supposes that the new tick shall less than the half duration of + * tick max. + */ + if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2) + { + RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t)); + + /* remove timer from timer list firstly */ + _rt_timer_remove(t); + if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC)) + { + t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + } + /* add timer to temporary list */ + rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1])); + + soft_timer_status = RT_SOFT_TIMER_BUSY; + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + /* call timeout function */ + t->timeout_func(t->parameter); + + RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t)); + RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick)); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + soft_timer_status = RT_SOFT_TIMER_IDLE; + /* Check whether the timer object is detached or started again */ + if (rt_list_isempty(&list)) + { + continue; + } + rt_list_remove(&(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1])); + if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) && + (t->parent.flag & RT_TIMER_FLAG_ACTIVATED)) + { + /* start it */ + t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + rt_timer_start(t); + } + } + else break; /* not check anymore */ + } + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check leave\n")); +} + +/** + * @brief system timer thread entry + * + * @param parameter + */ +static void rt_thread_timer_entry(void *parameter) +{ + rt_tick_t next_timeout; + + while (1) + { + /* get the next timeout tick */ + next_timeout = rt_timer_list_next_timeout(rt_soft_timer_list); + if (next_timeout == RT_TICK_MAX) + { + /* no software timer exist, suspend self. */ + rt_thread_suspend(rt_thread_self()); + rt_schedule(); + } + else + { + rt_tick_t current_tick; + + /* get current tick */ + current_tick = rt_tick_get(); + + if ((next_timeout - current_tick) < RT_TICK_MAX / 2) + { + /* get the delta timeout tick */ + next_timeout = next_timeout - current_tick; + rt_thread_delay(next_timeout); + } + } + + /* check software timer */ + rt_soft_timer_check(); + } +} +#endif /* RT_USING_TIMER_SOFT */ + +/** + * @ingroup SystemInit + * + * @brief This function will initialize system timer + */ +void rt_system_timer_init(void) +{ + int i; + + for (i = 0; i < sizeof(rt_timer_list) / sizeof(rt_timer_list[0]); i++) + { + rt_list_init(rt_timer_list + i); + } +} + +/** + * @ingroup SystemInit + * + * @brief This function will initialize system timer thread + */ +void rt_system_timer_thread_init(void) +{ +#ifdef RT_USING_TIMER_SOFT + int i; + + for (i = 0; + i < sizeof(rt_soft_timer_list) / sizeof(rt_soft_timer_list[0]); + i++) + { + rt_list_init(rt_soft_timer_list + i); + } + + /* start software timer thread */ + rt_thread_init(&timer_thread, + "timer", + rt_thread_timer_entry, + RT_NULL, + &timer_thread_stack[0], + sizeof(timer_thread_stack), + RT_TIMER_THREAD_PRIO, + 10); + + /* startup */ + rt_thread_startup(&timer_thread); +#endif /* RT_USING_TIMER_SOFT */ +} + +/**@}*/ diff --git a/examples/rt-thread/thread_delay/CMakeLists.txt b/examples/rt-thread/thread_delay/CMakeLists.txt new file mode 100644 index 00000000..41482c78 --- /dev/null +++ b/examples/rt-thread/thread_delay/CMakeLists.txt @@ -0,0 +1,9 @@ +set(TARGET_REQUIRED_LIBS rt-thread) +set(TARGET_REQUIRED_SRCS ${CMAKE_SOURCE_DIR}/components/rt-thread/bsp/bouffalolab/${CHIP}/board/board.c +${CMAKE_SOURCE_DIR}/components/rt-thread/bsp/bouffalolab/${CHIP}/board/startup_gcc.S) +set(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/components/rt-thread/bsp/bouffalolab/${CHIP}/board/bouffalo.ld) +set(mains main.c) +generate_bin() + + + diff --git a/examples/rt-thread/thread_delay/main.c b/examples/rt-thread/thread_delay/main.c new file mode 100644 index 00000000..e6a18846 --- /dev/null +++ b/examples/rt-thread/thread_delay/main.c @@ -0,0 +1,36 @@ +/** + * @file main.c + * @brief + * + * Copyright (c) 2021 Bouffalolab team + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +#include +#include "hal_gpio.h" + +int main(void) +{ + gpio_set_mode(GPIO_PIN_22, GPIO_OUTPUT_PP_MODE); + + while (1) { + gpio_write(GPIO_PIN_22, 0); + rt_thread_mdelay(1000); + gpio_write(GPIO_PIN_22, 1); + rt_thread_mdelay(1000); + } +} diff --git a/examples/rt-thread/thread_delay/readme.md b/examples/rt-thread/thread_delay/readme.md new file mode 100644 index 00000000..a769da88 --- /dev/null +++ b/examples/rt-thread/thread_delay/readme.md @@ -0,0 +1,7 @@ +**注意 rt-thread使用start_gcc.S替代entry.S,所以编译的时候需要将entry.S在cmakelist中屏蔽** + +```bash + +$ make APP=thread_delay BOARD=bl706_iot + +``` \ No newline at end of file