[feat][rt-thread] add rt-thread component and msh demo

This commit is contained in:
jzlv 2021-09-23 19:16:38 +08:00
parent e45eecfab7
commit b0e709d9d6
78 changed files with 27297 additions and 0 deletions

40
components/rt-thread/.gitignore vendored Normal file
View File

@ -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

View File

@ -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.

View File

@ -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).

View File

@ -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 RAM3KB Flash。
- 组件丰富,繁荣发展的软件包生态 。
- 简单易用 ,优雅的代码风格,易于阅读、掌握。
- 高度可伸缩,优质的可伸缩的软件架构,松耦合,模块化,易于裁剪和扩展。
- 强大,支持高性能应用。
- 跨平台、芯片支持广泛。
## **代码目录**
RT-Thread源代码目录结构如下图所示
| 名称 | 描述 |
| ------------- | ------------------------------------------------------- |
| BSP | Board Support Package板级支持包基于各种开发板的移植 |
| components | RT-Thread 的各个组件代码,例如 finshgui 等。 |
| 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)。

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
*/
#include <rthw.h>
#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);

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
*/
#include <rthw.h>
#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);

View File

@ -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

View File

@ -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.")
}

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 <rtthread.h>
#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 <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#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

View File

@ -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

View File

@ -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 <finsh.h>
#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;
}

View File

@ -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];
}

View File

@ -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 <finsh.h>
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

View File

@ -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 <finsh.h>
#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;
}
}

View File

@ -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 <finsh.h>
#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

View File

@ -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 <finsh.h>
#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;
}

View File

@ -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 <finsh.h>
#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;
}

View File

@ -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 <finsh.h>
#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

View File

@ -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
};

View File

@ -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

View File

@ -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 <finsh.h>
#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;
}

View File

@ -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 <finsh.h>
int finsh_parser_init(struct finsh_parser* self);
void finsh_parser_run(struct finsh_parser* self, const uint8_t* string);
#endif

View File

@ -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 <finsh.h>
#include <stdlib.h>
#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<BN_SIZE ; j++) bn[j] = 0;
while ( i<length )
{
t = *p++;
if ( t>='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<BN_SIZE ; j++)
{
v = bn[j];
bn[j] = (v<<shift) | t;
t = v >> (32 - shift);
}
i++;
}
d = (long)bn[0];
return d;
}

View File

@ -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 <finsh.h>
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

View File

@ -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 <finsh.h>
#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;
}

View File

@ -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 <finsh.h>
/*
* 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

View File

@ -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 <finsh.h>
#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

View File

@ -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 <finsh.h>
#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

View File

@ -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 <rtthread.h>
#ifdef FINSH_USING_MSH
#include "msh.h"
#include <finsh.h>
#include <shell.h>
#ifdef RT_USING_DFS
#include <dfs_posix.h>
#endif
#ifdef RT_USING_MODULE
#include <dlmodule.h>
#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 */

View File

@ -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 <rtthread.h>
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

View File

@ -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 <rtthread.h>
#if defined(FINSH_USING_MSH) && defined(RT_USING_DFS)
#include <finsh.h>
#include "msh.h"
#include <dfs_posix.h>
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 <device> <mountpoint> <fstype>.\n");
return -1;
}
}
MSH_CMD_EXPORT_ALIAS(cmd_mount, mount, mount <device> <mountpoint> <fstype>);
/* 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 <mountpoint>.\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] <filename>\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] <filename>\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) */

View File

@ -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 <rthw.h>
#ifdef RT_USING_FINSH
#include "finsh.h"
#include "shell.h"
#ifdef FINSH_USING_MSH
#include "msh.h"
#endif
#ifdef _WIN32
#include <stdio.h> /* 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 <dfs_posix.h>
#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 */

View File

@ -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 <rtthread.h>
#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

View File

@ -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 <rtthread.h>
#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 */

View File

@ -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

View File

@ -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 <fcntl.h>
#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

View File

@ -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 <rtconfig.h>
#if defined(RT_USING_NEWLIB) || (defined( __GNUC__ ) && !defined(__ARMCC_VERSION))
/* use header file of newlib */
#include <sys/stat.h>
#elif defined(_WIN32)
#include <sys/stat.h>
#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 <stdint.h>
#include <time.h>
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

View File

@ -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 <rtdbg.h> // 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 <rtconfig.h>
#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 <ulog.h>
#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__ */

View File

@ -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 <rtconfig.h>
/* 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__ */

File diff suppressed because it is too large Load Diff

View File

@ -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 <rtthread.h>
#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

View File

@ -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

View File

@ -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 <rtdef.h>
#include <rtthread.h>
#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

View File

@ -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

View File

@ -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 <rtconfig.h>
#include <rtdebug.h>
#include <rtdef.h>
#include <rtservice.h>
#include <rtm.h>
#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 <string.h>
#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 <finsh_api.h>
#endif
/**@}*/
#ifdef __cplusplus
}
#endif
#endif

View File

@ -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

View File

@ -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 <rthw.h>
#include <rtthread.h>
#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);
}
}

View File

@ -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 <rtconfig.h>
/* 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <rthw.h>
#include <rtthread.h>
#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 */
}
/**@}*/

View File

@ -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 <rthw.h>
#include <rtthread.h>
#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 */

View File

@ -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 <rthw.h>
#include <rtthread.h>
#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 */

View File

@ -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 <rdent@iinet.net.au>
* 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 <rtthread.h>
#ifdef RT_USING_POSIX
#include <rtdevice.h> /* 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 */

View File

@ -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 <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_MODULE
#include <dlmodule.h>
#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]);
}

File diff suppressed because it is too large Load Diff

View File

@ -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 <rthw.h>
#include <rtthread.h>
#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);
/**@}*/

File diff suppressed because it is too large Load Diff

View File

@ -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 <adam@sics.se>
* Simon Goldschmidt
*
*/
#include <rthw.h>
#include <rtthread.h>
#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 <finsh.h>
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 */

File diff suppressed because it is too large Load Diff

View File

@ -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 <rthw.h>
#include <rtthread.h>
#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 */

View File

@ -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 <rtthread.h>
#include <rthw.h>
#ifdef RT_USING_MODULE
#include <dlmodule.h>
#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;
}
/**@}*/

View File

@ -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 <rtthread.h>
#include <rthw.h>
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)&current_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)&current_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);
/**@}*/

View File

@ -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 <stdint.h>
#include <string.h>
#include <rthw.h>
#include <rtthread.h>
#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 <rtdbg.h>
#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)&parameter, 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 */

View File

@ -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 <dillon@backplane.com>
*
* 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 <rthw.h>
#include <rtthread.h>
#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 <finsh.h>
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) */

View File

@ -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 <rthw.h>
#include <rtthread.h>
#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);
/**@}*/

View File

@ -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 <rtthread.h>
#include <rthw.h>
/* 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 */
}
/**@}*/

View File

@ -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()

View File

@ -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 <rtthread.h>
#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);
}
}

View File

@ -0,0 +1,7 @@
**注意 rt-thread使用start_gcc.S替代entry.S,所以编译的时候需要将entry.S在cmakelist中屏蔽**
```bash
$ make APP=thread_delay BOARD=bl706_iot
```