From 5ec723a2a6fdfcd8a0921c22c1979be6a2906e80 Mon Sep 17 00:00:00 2001 From: supowang Date: Wed, 17 Jan 2024 12:51:35 +0800 Subject: [PATCH] add cosit api --- osal/cosit/.gitignore | 54 + osal/cosit/IoT OS软件架构.png | Bin 0 -> 111867 bytes osal/cosit/LICENSE | 201 +++ osal/cosit/README.md | 18 + osal/cosit/components/.keep | 0 osal/cosit/documentation/design_guideline.md | 105 ++ osal/cosit/documentation/ipc.md | 704 ++++++++ osal/cosit/documentation/mail_box.md | 191 +++ osal/cosit/documentation/message_queue.md | 198 +++ osal/cosit/documentation/task_mm_timer.md | 1119 ++++++++++++ osal/cosit/documentation/work_queue.md | 200 +++ osal/cosit/include/cosit.h | 1612 ++++++++++++++++++ osal/cosit/include/cosit_port_types.h | 63 + osal/cosit/meeting/2022-05-07.md | 15 + osal/cosit/meeting/2022-05-31.md | 24 + osal/cosit/meeting/2022-06-14.md | 14 + osal/cosit/meeting/2022-06-28.md | 12 + osal/cosit/meeting/2022-07-12.md | 13 + osal/cosit/rtos/TobudOS/cosit.c | 779 +++++++++ osal/cosit/test/greatest/LICENSE | 13 + osal/cosit/test/greatest/greatest.h | 1266 ++++++++++++++ 21 files changed, 6601 insertions(+) create mode 100644 osal/cosit/.gitignore create mode 100644 osal/cosit/IoT OS软件架构.png create mode 100644 osal/cosit/LICENSE create mode 100644 osal/cosit/README.md create mode 100644 osal/cosit/components/.keep create mode 100644 osal/cosit/documentation/design_guideline.md create mode 100644 osal/cosit/documentation/ipc.md create mode 100644 osal/cosit/documentation/mail_box.md create mode 100644 osal/cosit/documentation/message_queue.md create mode 100644 osal/cosit/documentation/task_mm_timer.md create mode 100644 osal/cosit/documentation/work_queue.md create mode 100644 osal/cosit/include/cosit.h create mode 100644 osal/cosit/include/cosit_port_types.h create mode 100644 osal/cosit/meeting/2022-05-07.md create mode 100644 osal/cosit/meeting/2022-05-31.md create mode 100644 osal/cosit/meeting/2022-06-14.md create mode 100644 osal/cosit/meeting/2022-06-28.md create mode 100644 osal/cosit/meeting/2022-07-12.md create mode 100644 osal/cosit/rtos/TobudOS/cosit.c create mode 100644 osal/cosit/test/greatest/LICENSE create mode 100644 osal/cosit/test/greatest/greatest.h diff --git a/osal/cosit/.gitignore b/osal/cosit/.gitignore new file mode 100644 index 0000000..297fd37 --- /dev/null +++ b/osal/cosit/.gitignore @@ -0,0 +1,54 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +.DS_Store diff --git a/osal/cosit/IoT OS软件架构.png b/osal/cosit/IoT OS软件架构.png new file mode 100644 index 0000000000000000000000000000000000000000..ed184a48c52d1439ec660299d3d05d79b462f1e7 GIT binary patch literal 111867 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%0DK2l`DH%0+xGmDm-CKNo_~I}4YJRgD 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Ξ!$w` zqZsygu&VO=U0Utwx|?L6_`^4yP<(}m%6z})&S&$BcP}arZ5nbb3a?lqr-FAVF4m!x zpE`Tr95q))k`T=eH+yX8<8Gfp&IXt8;e!&SvI@`E@fcb;^4fjz8d458kQ4uS^nM3n zIg_ku@a?w|J#7)*=fSanqBEoy$SiB#+)tD3_T?VMP_*0_et3tgH&X(@ULUf4KFPoD zdupsjLR;$D<(Un3#mF7P0Ve1but~|+tTHunSNa5<1L<5V(>t3&oh^U+0!A%p1sFe7 z(H(j8%>OYF{b480f3Phv@_ml~p@S^#oY5cS!jv|8mkbLHr2{J%^7opL?^ z+FjJ{@MXx_FT+eBA}1MhuaO5Q0E0<*HSPaod}Yn`U%BNYBZC+HgFGUNJ^as%RnL~t zh!pu3QPxq*yz$>DFemRPLTmm>J>jWc#Bm4 zEnDU5bt~&enj7$cO#Oc~<^mI~W|gMOw`zj9H)l}FQjSb&08X1{U?;vknuFxI9Lpvm z9B6nyTs-=#{F9A{zycPV>i-DX9t|bkY+2~b#*%*W{wYf|V~<#shzy{HAFlWX&fUB~ z_)>LCM>x@EbzzF($m-hDXDOX)yv%OA9wVIOLGLnC?tlJJs_gQQBU!2hL-rSkpKZE5 z&@r~l^W<6PBrjnrz$3fHDSCdDKLp#u=RTJO{kQOO?T?|PrrM95@8zW0m*6+T|0nV9 z`t}iYfpKMbX(W&e*l8ZDbCRR5jqLxa^N1 z$M4M8h&*N_@qhF9+yO-7YIf~3g#r{Do9mCM&2* zOmkZ||8&%Lly_p5DGH6z``6pCw*s8>GC!rwLAiKu-)92SP@hYNR}3nVPWb-=QL+S* literal 0 HcmV?d00001 diff --git a/osal/cosit/LICENSE b/osal/cosit/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/osal/cosit/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/osal/cosit/README.md b/osal/cosit/README.md new file mode 100644 index 0000000..78a9933 --- /dev/null +++ b/osal/cosit/README.md @@ -0,0 +1,18 @@ +# Cross-Operating System Interface on ioT + +#### 介绍 +围绕物联网操作系统的内核抽象层接口、应用接口和硬件抽象层接口,制定接口实现 + + +#### 软件架构 +![输入图片说明](IoT%20OS%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84.png) + + + +#### 参与贡献 + +1. Fork 本仓库 +2. 新建分支 +3. 提交代码 +4. 新建 Pull Request + diff --git a/osal/cosit/components/.keep b/osal/cosit/components/.keep new file mode 100644 index 0000000..e69de29 diff --git a/osal/cosit/documentation/design_guideline.md b/osal/cosit/documentation/design_guideline.md new file mode 100644 index 0000000..a6e5407 --- /dev/null +++ b/osal/cosit/documentation/design_guideline.md @@ -0,0 +1,105 @@ + +定义的API/宏/枚举常量等统一采用前缀**cos**,以保证命名空间的独立性,不会与其他SDK/BSP内的名称重复。注意API采用小写cos_xxx, 宏/枚举采用大写COS_XXX. + +# 设计目标 +标准的设计目标要达到,开发者只需要查看一个模块的标准文档,就能了解模块的功能是什么,如何使用,使用时的注意事项。开发者不需要查看代码具体实现,甚至不需要查看其他资料,就可以进行应用的开发,并最终可以无缝迁移到支持该标准的平台。 + +# 命名规则 +### 命名风格 +统一采用linux命名风格,即API字母全小写,字段之间加下划线分割。如`cos_task_create/cos_mutex_create/cos_queue_send` 。 +宏和枚举变量采用全大写和下划线分割的形式,如`COS_WAIT_FOREVER` 。 + +### API命名 +遵循名词在前,动词在后。 +`cos__[feature]_` + +cos:标准的统一前缀 +component: 必选的,组件名称。如task/mutex/queue +feature: 可选的,组件内部子特性或子名称,如task组件内的name/priority +action: 必选的, API的动作,如get/set/send/recv/ + +命名举例 +- cos_task_create +- cos_task_name_get +- cos_mutex_lock + +### API术语 +一些API命名时术语,可以参考行业各家OS的术语,及其他标准CMSIS/POSIX的术语。可以使用行业内标准缩写。 +**create/delete** +每次调用都会创建一个新的实例的情形使用create,且句柄作为出参。删除实例使用delete。 +```C +cos_status_t cos_task_create(cos_task_t *task, cos_task_entry_t fn, void *arg, cos_task_attr_t *attr) + +cos_status_t cos_task_delete(cos_task_t task) +``` + +**init/deinit** +只能进行一次的初始化,如接口初始化,硬件初始化等,使用init,句柄作为入参,且句柄可省略。 反初始化使用deinit + +```C +cos_status_t cos_init(void) +void cos_deinit(void) +``` + +# 数据类型 +关于API使用的基本数据类型统一采用标准数据类型。 +对于基本正数采用stdint.h中定义的数据类型 +- int8_t +- uint8_t +- int16_t +- uint16_t +- int32_t +- uint32_t +- int64_t +- uint64_t +- uintptr_t +- intprt_t + +和长度相关的数据类型 +- size_t +- ssize_t + +基本指针类型,不做限制如 +- char * +- void * + +内核对象的句柄 +- cos_xxx_t * +如cos_task_t/cos_mutex_t/cos_sem_t/cos_queue_t等。 + +统一的错误码类型 +- cos_status_t + +其他 +- bool + +# 错误码 +标准应采用统一的错误码,支持可扩展。采用`cos_status_t`数据类型,所有的错误码均为负值。返回0代表成功。 +一些例外:如类似read/write API返回ssize_t类型,> 0 代表长度,< 0代表失败。 + +# 模块描述 +标准中对一个功能模块的描述,应包含以下这些内容。 +## 模块功能描述 +包括解释模块功能是什么,阐述工作原理,使用功能的场景及其注意事项。 + +## 模块常量定义 +包括模块定义的宏和枚举常量,命名全大写和下划线,统一**cos_**前缀。 + +## 模块数据类型定义 +包括模块定义的复合数据类型。全小写,统一**cos_**前缀。 + +## 模块API描述内容 +对一个API的描述需要完整,清晰,表达没有歧义。包括如下内容章节。 + +1. API名称和简短说明 +2. API原型 +API的原型申明和应该使用该API时需要包含的头文件。 +3. API的详细描述 +4. API的参数 +包括参数名称,参数类型,参数方向,参数详细描述这几个要素。 +5. API的返回值 +包括API的返回值及其代表的含义。 +6. API的使用注意 +包括API的使用注意事项,例如能否在中断上下文中使用等。 +7. 使用举例 +给出使用该API的例子。 diff --git a/osal/cosit/documentation/ipc.md b/osal/cosit/documentation/ipc.md new file mode 100644 index 0000000..2f7860f --- /dev/null +++ b/osal/cosit/documentation/ipc.md @@ -0,0 +1,704 @@ +## 5.4. 互斥量 + +互斥量又称互斥锁,一般用于共享资源的互斥排他性访问保护。 + +互斥量在任意时刻处于且仅会处于解锁或锁定状态,当一个任务获取到一把锁后(互斥量锁定),其他任务再尝试获得这把锁时会失败或进入阻塞状态,当该任务释放持有的锁时(互斥量解锁),会唤醒一个正阻塞等待此互斥量的任务,被唤醒的任务将会获取这把锁。 + +在多任务运行环境中,有些共享资源不具有多线程可重入性,对于这类不希望被多任务同时访问的资源(临界资源),可以采用互斥量来进行保护。 + +### 5.4.1. 常量 + +#### 5.4.1.1. COS_MUTEX_NEST +```c +#define COS_MUTEX_NEST (0x1u << 0) +``` +互斥锁支持嵌套锁。 + +### 5.4.2. 类型 + +#### 5.4.2.1. cos_mutex_t + +互斥锁的句柄,代表一个互斥锁。可以通过句柄对互斥锁进行操作,如创建、申请、释放、销毁。 + +```c +typedef void * cos_mutex_t; +``` + +### 5.4.3. 函数 + +#### 5.4.3.1. cos_mutex_create + +互斥锁创建 + +**函数原型** +```c +cos_status_t cos_mutex_create(cos_mutex_t *mutex, char *name, uint32_t options); +``` + +**描述** + +创建一个新的互斥锁并初始化,新的互斥锁的句柄由参数mutex返回,后续对互斥锁的操作可以通过参数mutex进行。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|mutex|出参|互斥锁句柄| +|name|入参|互斥锁名称| +|options|入参|互斥锁选项,如是否支持嵌套锁| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_ISR|在中断上下文中调用| +|COS_ERR_NOMEM|内存不足| +|COS_ERR_PARAM|非法参数| + +**注意** + +此函数不能在中断上下文中使用。 + +#### 5.4.3.2. cos_mutex_init + +初始化一个互斥锁 + +**函数原型** +```c +cos_status_t cos_mutex_init(cos_mutex_t mutex, char *name, uint32_t options); +``` + +**描述** + +初始化一个新的互斥锁。 + +与创建一个互斥锁类型,区别是互斥锁的控制块内存由调用者传入,一般用于静态分配内存的场景。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|mutex|入参|互斥锁的句柄| +|name|入参|互斥锁名称| +|options|入参|互斥锁选项,如是否支持嵌套锁| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_ISR|在中断上下文中调用| +|COS_ERR_NOMEM|内存不足| +|COS_ERR_PARAM|非法参数| + +**注意** + +此函数不能在中断上下文中使用。 + +#### 5.4.3.3. cos_mutex_delete + +删除动态创建的互斥锁 + +**函数原型** +```c +cos_status_t cos_mutex_delete(cos_mutex_t mutex); +``` +**描述** + +销毁互斥锁。系统会唤醒所有等待该互斥锁的任务,返回互斥锁已经被销毁,然后销毁回收其资源。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|mutex|cos_mutex_t|入参|互斥锁的句柄| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| + +#### 5.4.3.4. cos_mutex_deinit + +销毁静态创建的互斥锁 + +**函数原型** +```c +cos_status_t cos_mutex_deinit(cos_mutex_t mutex); +``` + +**描述** + +销毁静态创建的互斥锁。系统会唤醒所有等待该互斥锁的任务,返回互斥锁已经被销毁。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|mutex|cos_mutex_t|入参|互斥锁的句柄| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| + +#### 5.4.3.5. cos_mutex_lock + +互斥锁上锁 + +**函数原型** +```c +cos_status_t cos_mutex_lock(cos_mutex_t mutex, cos_tick_t timeout); +``` +**描述** + +申请互斥锁。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|mutex|入参|互斥锁的句柄| +|timeout|入参|申请超时时间,单位为tick| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_ISR|在中断上下文中调用| +|COS_ERR_TIMEOUT|申请超时| +|COS_ERR_DESTROY|申请的互斥锁已被销毁| +|COS_ERR_MUTEX_NESTING|已经拥有互斥锁,嵌套申请| + +**注意** + +此函数不能在中断上下文中使用。 + +#### 5.4.3.6. cos_mutex_unlock + +互斥锁解锁 + +**函数原型** +```c +cos_status_t cos_mutex_unlock(cos_mutex_t mutex); +``` + +**描述** + +互斥锁解锁。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|mutex|入参|互斥锁的句柄| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_DESTROY|释放的互斥锁已被销毁| +|COS_ERR_MUTEX_NOT_OWNER|未持有该互斥锁| +|COS_ERR_MUTEX_NESTING|已经释放互斥锁,但还在嵌套中| + +### 5.4.4. 示例 +```c + +``` +## 5.5. 信号量 + +信号量是一种实现任务间同步的机制,一般用于多个任务间有限资源竞争访问。 + +通常来说,一个信号量中持有一个整形数值,用以表示可用资源的数量。当一个信号量的可用资源数量大于0时,任务尝试获取该信号量成功,信号量的可用资源数减一;当一个信号量的可用资源数等于0时,任务尝试获取该信号量失败或进入阻塞状态。信号量的这一模式,当可用资源数为1时,可将其用于资源的互斥访问;或者解决生产者-消费者问题中的资源生产-消费问题。编程实例章节会演示生产者-消费者问题的解决范式。 + +### 5.5.1. 常量 + +#### 5.5.1.1. COS_SEM_NO_MAX +```c +#define COS_SEM_NO_MAX (uint32_t)-1 +``` +信号量无最大值,可以用作计数信号量。 + +### 5.5.2. 类型 + +#### 5.5.2.1. cos_sem_t +```c +typedef void * cos_sem_t; +``` + +信号量的句柄,代表一个信号量。可以通过句柄对信号量进行操作,如创建、等待、释放、销毁。 + +### 5.5.3. 函数 + +#### 5.5.3.1. cos_sem_create + +创建一个信号量 + +**函数原型** +```c +cos_status_t cos_sem_create(cos_sem_t *sem, char *name, uint32_t init_count, uint32_t max_count); +``` +**描述** + +创建一个新的信号量并初始化,新的信号量的句柄由参数sem返回,后续对信号量的操作可以通过参数sem进行。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|sem|出参|信号量句柄| +|name|入参|信号量的名称| +|init_count|入参|信号量初始值| +|max_count|入参|信号量最大值| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_ISR|在中断上下文中调用| +|COS_ERR_NOMEM|内存不足| +|COS_ERR_PARAM|非法参数| + +**注意** + +此函数不能在中断上下文中使用。 + +#### 5.5.3.2. cos_sem_init + +初始化一个信号量 + +**函数原型** +```c +cos_status_t cos_sem_init(cos_sem_t sem, char *name, uint32_t init_count, uint32_t max_count); +``` +**描述** + +初始化一个信号量 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|sem|入参|信号量句柄| +|name|入参|信号量的名称| +|init_count|入参|信号量初始值| +|max_count|入参|信号量最大值| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_ISR|在中断上下文中调用| +|COS_ERR_NOMEM|内存不足| +|COS_ERR_PARAM|非法参数| + +**注意** + +此函数不能在中断上下文中使用。 + +#### 5.5.3.3. cos_sem_delete + +删除动态创建的信号量 + +**函数原型** +```c +cos_status_t cos_sem_delete(cos_sem_t sem); +``` +**描述** + +删除动态创建的信号量。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|sem|入参|信号量的句柄| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| + +#### 5.5.3.4. cos_sem_deinit + +销毁静态创建的信号量 + +**函数原型** +```c +cos_status_t cos_sem_deinit(cos_sem_t sem); +``` +**描述** + +销毁静态创建的信号量。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|sem|入参|信号量的句柄| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| + +#### 5.5.3.5. cos_sem_wait + +等待信号量 + +**函数原型** +```c +cos_status_t cos_sem_wait(cos_sem_t *sem, cos_tick_t timeout); +``` +**描述** + +等待信号量。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|sem|入参|信号量的句柄| +|timeout|入参|申请超时时间,单位为tick| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_ISR|在中断上下文中调用| +|COS_ERR_TIMEOUT|申请超时| +|COS_ERR_DESTROY|申请的信号量已被销毁| + +**注意** + +此函数不能在中断上下文中使用。 + +#### 5.5.3.6. cos_sem_release + +释放信号量 + +**函数原型** +```c +cos_status_t cos_sem_release(cos_sem_t sem); +``` +**描述** + +释放信号量。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|sem|入参|信号量的句柄| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_DESTROY|释放的信号量已被销毁| + +#### 5.5.3.7. cos_sem_release_all + +释放信号量 + +**函数原型** +```c +cos_status_t cos_sem_release_all(cos_sem_t sem); +``` +**描述** + +释放信号量,并唤醒所有等待该信号量的任务。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|sem|入参|信号量的句柄| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_DESTROY|释放的信号量已被销毁| + +### 5.5.4. 示例 +```c + +``` + +## 5.6. 事件 + +事件提供了一种任务间实现同步和信息传递的机制。一般来说,一个事件中包含了一个旗标,这个旗标的每一位表示一个“事件”。 + +一个任务可以等待一个或者多个“事件”的发生,其他任务在一定的业务条件下可以通过写入特定“事件”唤醒等待此“事件”的任务,实现一种类似信号的编程范式。 + +### 5.6.1. 常量 + +#### 5.6.1.1. COS_EVENT_WAIT_ANY +```c +#define COS_EVENT_WAIT_ANY (0x1u << 0) +``` +等待事件时,用于指定等待其中任意一个。 +#### 5.6.1.2. COS_EVENT_WAIT_ALL +```c +#define COS_EVENT_WAIT_ALL (0x1u << 1) +``` +等待事件时,用于指定等待所有事件。 +#### 5.6.1.3. COS_EVENT_WAIT_CLR +```c +#define COS_EVENT_WAIT_CLR (0x1u << 2) +``` +等待事件时,用于指定等待后清除该事件。 + +#### 5.6.1.4. COS_EVENT_RELEASE_KEEP +```c +#define COS_EVENT_RELEASE_KEEP (0x1u << 3) +``` +释放事件时,用于指定释放后是否保留该事件。 + +### 5.6.2. 类型 + +#### 5.6.2.1. cos_event_t +```c +typedef void * cos_event_t; +``` + +事件的句柄,代表一个事件。可以通过句柄对事件进行操作,如创建、等待、释放、销毁。 + +#### 5.6.2.2. cos_event_flag_t +```c +typedef uint32_t cos_event_flag_t; +``` +用于存储事件标志。 + +### 5.6.3. 函数 + +#### 5.6.3.1. cos_event_create + +事件创建 + +**函数原型** +```c +cos_status_t cos_event_create(cos_event_t *event, char *name, cos_event_flag_t init_flag); +``` +**描述** + +创建一个新的事件并初始化,新的事件的句柄由参数event返回,后续对事件的操作可以通过参数event进行。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|event|出参|事件的句柄| +|name|入参|事件的名称| +|init_flag|入参|事件初始化标志| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_ISR|在中断上下文中调用| +|COS_ERR_NOMEM|内存不足| + +**注意** + +此函数不能在中断上下文中使用。 + +#### 5.6.3.2. cos_event_init + +初始化一个事件 + +**函数原型** +```c +cos_status_t cos_event_init(cos_event_t event, char *name, cos_event_flag_t init_flag); +``` +**描述** + +初始化一个事件 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|event|入参|事件的句柄| +|name|入参|事件的名称| +|init_flag|入参|事件初始化标志| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_ISR|在中断上下文中调用| +|COS_ERR_NOMEM|内存不足| + +**注意** + +此函数不能在中断上下文中使用。 + +#### 5.6.3.3. cos_event_delete + +删除动态创建的事件 + +**函数原型** +```c +cos_status_t cos_event_delete(cos_event_t event); +``` +**描述** + +删除动态创建的事件。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|event|入参|事件的句柄| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| + +#### 5.6.3.4. cos_event_deinit + +销毁事件 + +**函数原型** +```c +cos_status_t cos_event_deinit(cos_event_t event); +``` +**描述** + +销毁静态创建的事件。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|event|入参|事件的句柄| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| + +#### 5.6.3.5. cos_event_wait + +等待一个或多个事件。 + +**函数原型** +```c +cos_status_t cos_event_wait(cos_event_t event, cos_event_flag_t expect_flag, cos_tick_t timeout, cos_event_flag_t *match_flag, uint32_t options); +``` +**描述** + +等待一个或多个事件。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|event|入参|事件的句柄| +|expect_flag|入参|期望等到的事件| +|timeout|入参|申请超时时间,单位为tick| +|match_flag|出参|事件的匹配结果| +|options|入参|事件匹配选项(任意一个匹配或者全部匹配)| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_ISR|在中断上下文中调用| +|COS_ERR_TIMEOUT|申请超时| +|COS_ERR_DESTROY|事件已被销毁| + +**注意** + +此函数不能在中断上下文中使用。 + +#### 5.6.3.6. cos_event_release + +释放事件 + +**函数原型** +```c +cos_status_t cos_event_release(cos_event_t event, cos_event_flag_t flag, uint32_t options); +``` + +**描述** + +释放事件。 + +**参数** + +|参数名称|参数方向|描述| +|:----:|:----:|:----:| +|event|入参|事件的句柄| +|expect_flag|入参|释放的事件| +|options|入参|事件释放选项,释放后是否保留| + +**返回值** + +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 + +|错误码|描述| +|:---:|:---:| +|COS_ERR|未知错误| +|COS_ERR_DESTROY|事件已被销毁| + +### 5.6.4. 示例 +```c + +``` diff --git a/osal/cosit/documentation/mail_box.md b/osal/cosit/documentation/mail_box.md new file mode 100644 index 0000000..c148348 --- /dev/null +++ b/osal/cosit/documentation/mail_box.md @@ -0,0 +1,191 @@ +# 邮箱 + +消息邮箱(Mail Box)是实时操作系统中一种典型的线程间通信方法,邮箱用于线程间通信,特点是开销比较低,效率较高。 + +## 类型 + +```C +typedef void * cos_mb_t; +``` + +邮箱控制块是操作系统用于管理邮箱的一个数据结构,cos_mb_t是邮箱的句柄。 + +## 函数 + +### cos_mb_create + +创建邮箱 + +原型 + +```C + +#include +cos_status_t cos_mb_create(const char* name, size_t msg_size, + uint8_t flag,cos_mb_t* mb); + +``` + +**描述** +创建一个邮箱,由系统动态为邮箱申请内存空间,并返回邮箱的句柄。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| name | const char* | 入参 | 邮箱名称 | +| msg_size | sizebt | 入参 | 邮箱容量 | +| flag | uint8_t | 入参 | 邮箱标志,如: COS_IPC_FLAG_FIFO 或 COS_IPC_FLAG_PRIO | +| mb | cos_mb_t | 出参 | 邮箱句柄 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| COS_ERROR_PARAM | 非法参数 | +| COS_ERROR_MEM | 内存不足 | + +### cos_mb_delete + +删除邮箱 + +原型 + +```C + +#include +cos_status_t cos_mb_delete(cos_mb_t mb); + +``` + +**描述** +删除一个邮箱。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| mb | cos_mb_t | 入参 | 邮箱的句柄 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| COS_ERROR_PARAM | 非法参数 | + +### cos_mb_init + +初始化邮箱 + +原型 + +```C + +#include +cos_status_t cos_mb_init(const char* name, void *msgpool, size_t msg_size, + size_t max_msgs, cos_mb_t mb); + +``` + +**描述** +创建一个静态邮箱,由编译器提前为邮箱在编译时分配内存空间。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | --------------------------- | +| name | const char* | 入参 | 邮箱的名称 | +| msgpool | void* | 入参 | 缓冲区指针 | +| msg_size | size_t | 入参 | 邮箱容量 | +| flag | uint8_t | 入参 | 邮箱工作方式 cos_IPC_FLAG_FIFO 或 cos_IPC_FLAG_PRIO | +| mb | cos_mb_t | 出参 | 邮箱句柄 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| COS_ERROR_PARAM | 非法参数 | + +### cos_mb_deinit + +删除邮箱 + +原型 + +```C + +#include +cos_status_t cos_mb_deinit(cos_mb_t mb); + +``` + +**描述** +去初始化一个邮箱。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| mb | cos_mb_t | 入参 | 邮箱的句柄 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| COS_ERROR_PARAM | 非法参数 | + +### cos_mb_send + +发送邮件 + +原型 + +```C + +#include +cos_status_t cos_mb_send(cos_mb_t mb,uint32_t value); + +``` + +**描述** +发送邮件,当邮箱中的邮件已经满时,发送邮件的线程或者中断程序会收到 cos_ERROR_mb_FULL 的返回值。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| mb | cos_mb_t | 入参 | 邮箱的句柄 | +| value | uint32_t | 入参 | 邮件内容 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| COS_ERROR_PARAM | 非法参数 | +| COS_ERROR_mb_FULL | 邮箱中的邮已满 | + +### cos_mb_recive + +接收邮件 + +原型 + +```C + +#include +cos_status_t cos_mb_recv(cos_mb_t mb,uint32_t* value, + int32_t timeout); + +``` + +**描述** +当邮箱中有邮件时,接收者才能接收邮件,否则接收者会根据超时时间设置,或挂起在邮箱的等待线程队列上,或直接返回。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| mb | cos_mb_t | 入参 | 邮箱的句柄 | +| value | void* | 入参 | 邮件内容 | +| timeout | int32_t | 入参 | 指定的超时时间 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| COS_ERROR_PARAM | 非法参数 | +| COS_ERROR_mb_TIMEOUT | 超时 | diff --git a/osal/cosit/documentation/message_queue.md b/osal/cosit/documentation/message_queue.md new file mode 100644 index 0000000..d05e1fc --- /dev/null +++ b/osal/cosit/documentation/message_queue.md @@ -0,0 +1,198 @@ +# 消息队列 + +消息队列(Message Queue)是一种常见的异步任务间通讯方式,用于在任务与任务间,任务与中断间传递不定长的数据。任务能够从队列里面读取消息,当队列中的消息为空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。任务也能够往队列里写入消息,当队列已经写满消息时,挂起写入任务;当队列中有空闲消息节点时,挂起的写入任务被唤醒并写入消息。 + +## 类型 + +```C +typedef struct cos_messagequeue* cos_mq_t; +``` + +消息队列控制块,消息队列控制块是操作系统用于管理消息队列的一个数据机构,由结构体cos_messagequeue表示,cos_mq_t是消息队列的句柄。 + +## 函数 + +### cos_mq_create + +创建消息队列 + +原型 + +```C + +#include +cos_status_t cos_mq_create(const char* name, size_t msg_size, + size_t max_msgs, uint8_t flag,cos_mq_t* mq); + +``` + +**描述** +创建一个消息队列,由系统动态为消息队列申请内存空间,并返回消息队列的句柄。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| name | const char* | 入参 | 消息队列的名称 | +| msg_size | size_t | 入参 | 单条消息的最大长度 | +| max_msgs | size_t | 入参 | 消息队列的最大个数 | +| flag | uint8_t | 入参 | 消息队列工作方式 COS_IPC_FLAG_FIFO 或 COS_IPC_FLAG_PRIO | +| mq | cos_mq_t | 出参 | 消息队列句柄 | + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| COS_ERROR_PARAM | 非法参数 | +| COS_ERR_NOMEM | 内存不足 | +| COS_ERR_ISR | 在中断上下文中调用 | +| COS_ERR | 未知错误 | + +### cos_mq_init + +创建消息队列 + +init + +```C + +#include +cos_status_t cos_mq_init(const char* name, void *msgpool, size_t msg_size, + size_t max_msgs, cos_mq_t mq); + +``` + +**描述** +创建一个静态消息队列,由编译器提前为消息队列在编译时分配内存空间。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | --------------------------- | +| name | const char* | 入参 | 消息队列的名称 | +| msgpool | void* | 入参 | 指向存放消息的缓冲区的指针 | +| msg_size | size_t | 入参 | 单条消息的最大长度,单位字节 | +| flag | uint8_t | 入参 | 消息队列工作方式 cos_IPC_FLAG_FIFO 或 cos_IPC_FLAG_PRIO | +| mq | cos_mq_t | 出参 | 消息队列句柄 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| COS_ERROR_PARAM | 非法参数 | + +### cos_mq_delete + +删除消息队列 + +原型 + +```C + +#include +cos_status_t cos_mq_delete(cos_mq_t mq); + +``` + +**描述** +删除一个消息队列。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| mq | cos_mq_t | 入参 | 消息队列的句柄 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| COS_ERROR_PARAM | 非法参数 | + +### cos_mq_deinit + +去初始化消息队列 + +原型 + +```C + +#include +cos_status_t cos_mq_deinit(cos_mq_t mq); + +``` + +**描述** +去初始化一个消息队列。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| mq | cos_mq_t | 入参 | 消息队列的句柄 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| COS_ERROR_PARAM | 非法参数 | +### cos_mq_send + +发送消息 + +原型 + +```C + +#include +cos_status_t cos_mq_send(cos_mq_t mq,void* buffer, size_t size); + +``` + +**描述** +发送消息,线程或者中断服务程序都可以给消息队列发送消息。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| mq | cos_mq_t | 入参 | 消息队列的句柄 | +| buffer | void* | 入参 | 消息内容 | +| size | size_t | 入参 | 消息大小 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| COS_ERROR_PARAM | 非法参数 | +| COS_ERROR_MQ_DELETED | 线程挂起时消息队列已被删除 | +| COS_ERROR_MQ_FULL | 消息队列已满 | + +### cos_mq_recive + +接收消息 + +原型 + +```C + +#include +cos_status_t cos_mq_recv(cos_mq_t mq,void* buffer, + size_t size,int32_t timeout); + +``` + +**描述** +当消息队列中有消息时,接收者才能接收消息,否则接收者会根据超时时间设置,或挂起在消息队列的等待线程队列上,或直接返回。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| mq | cos_mq_t | 入参 | 消息队列的句柄 | +| buffer | void* | 入参 | 消息内容 | +| size | size_t | 入参 | 消息大小 | +| timeout | int32_t | 入参 | 指定的超时时间 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| COS_ERROR_PARAM | 非法参数 | +| COS_ERROR_MQ_TIMEOUT | 超时 | +| COS_ERROR_PARAM | 消息队列已满 | +| COS_ERROR_MQ_DELETED | 线程挂起时消息队列已被删除 | diff --git a/osal/cosit/documentation/task_mm_timer.md b/osal/cosit/documentation/task_mm_timer.md new file mode 100644 index 0000000..74843a6 --- /dev/null +++ b/osal/cosit/documentation/task_mm_timer.md @@ -0,0 +1,1119 @@ +## 5.0 通用 +物联网操作系统各模块都会用到的通用的常量、类型、函数定义。 + +### 5.0.1 常量 +#### 5.0.1.1 COS_WAIT_FOREVER +```C +#define COS_WAIT_FOREVER ((cos_tick_t)-1) +``` +表示永久阻塞等待,直到获得系统资源(如互斥量,信号量,事件,队列消息等)才返回。 + +#### 5.0.1.2 COS_NO_WAIT +```C +#define COS_NO_WAIT (0x0U) +``` +表示非阻塞等待,当不能获得系统资源(如互斥量,信号量,事件,队列消息等)时不会等待,而是立即返回。 + +### 5.0.2 类型 +#### 5.0.2.1 cos_tick_t +```C +typedef uint64_t cos_tick_t +``` +系统tick数 + +### 5.0.3 函数 +#### 5.0.3.1 cos_ms_to_tick +把毫秒数转为系统tick数 + +**原型** +```C +cos_tick_t cos_ms_to_tick(uint64_t ms); +``` + +**描述** +把毫秒数转成系统tick数。在物联网操作系统中系统tick一般用于内核调度的基本单位,其频率在系统中是可以配置的。根据系统tick一般只能达到毫秒精度。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| ms | 入 | 毫秒数| + +**返回值** +转换得到的系统tick数 + +**注意** +可以在中断上下文中使用 + +**例子** + +#### 5.0.3.2 cos_tick_to_ms +把系统tick数转为毫秒数 + +**原型** +```C +uint64_t cos_tick_to_ms(cos_tick_t tick); +``` + +**描述** +把系统tick数转成毫秒数。在物联网操作系统中系统tick一般用于内核调度的基本单位,其频率在系统中是可以配置的。根据系统tick一般只能达到毫秒精度。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| tick | 入 | 系统tick数| + +**返回值** +转换得到的毫秒数 + +**注意** +可以在中断上下中使用 + +**例子** + +#### 5.0.3.3 cos_tick_get +把系统tick数转为毫秒数 + +**原型** +```C +cos_tick_t cos_tick_get(void); +``` + +**描述** +获取当前系统tick数。 + +**参数** + +**返回值** +获取到的当前系统tick数。 + +**注意** +可以在中断上下中使用 + +**例子** + + +## 5.1 任务管理 +任务可以认为是一段独享CPU的运行程序,而应用是完成特定功能的多个任务的集合。任务管理就是为多任务环境中的每个任务分配一个上下文(context)(上下文(context)是指当任务被调度执行的所必不可少的一组数据,包括前任务的CPU指令地址(PC指针),当前任务的栈空间,当前任务的CPU寄存器状态等),在任务相继执行过程中,将切出任务的信息保存在任务上下文中,将切入任务的上下文信息恢复,使其得以执行。为维护任务上下文、状态、栈等相关信息,操作系统内核为每个任务定义了一组数据结构,即任务控制块(Task Control Block),来存放这些信息。 任务调度负责将处理器资源分配给关键任务,让其优先运行。所以系统中的每个任务需要根据关键程度分配不同的优先级,那些执行关键操作的任务被赋予高优先级,而一些非关键性任务赋予低优先级。当系统发生调度时,高优先级任务可以抢占低优先级任务的处理器资源得到调度执行。系统在无任务可调度时,就运行空闲任务,其优先级最低。 任务被创建时,需要为任务指定执行体入口地址、栈大小、优先级等信息,创建过程中内核为任务分配任务控制块(TCB)。任务栈空间可以在任务创建时由用户程序指定,也可以由内核根据用户指定大小来动态分配。操作系统内核提供基于软件的栈溢出检测方法,用户可根据需要配置或关闭该功能。 + +### 5.1.1 常量 +#### 5.1.1.1 COS_TASK_OPTION_NORUN +```C +#define COS_TASK_OPTION_NORUN (0x1U << 0) +``` +用来指定创建一个新任务时任务属性的options参数,表示任务创建后处于挂起状态,不会被调度执行,直到被恢复。 +如果没有指定此选项(默认),任务创建后即处于就绪状态,等待调度运行。 + +#### 5.1.1.2 COS_TASK_PRIORITY_MAX +```C +#define COS_TASK_PRIORITY_MAX 100 +``` +最大任务优先级。任务优先级从0 ~ COS_TASK_PRIORITY_MAX优先级别递减,即COS_TASK_PRIORITY_MAX优先级最低。如内核在实现时,若其优先级级别小于`COS_TASK_PRIORITY_MAX`, 当应用传入的优先级超过内核实现的优先级时,内核应调整为应用可设置的最低优先级。 + +如果应用不关心任务的优先级,则可以使用`cos_task_attr_init`初始化为默认优先级即可,不同内核在实现时,其默认优先级可以不同。 + +#### 5.1.1.4 COS_TASK_SCHED_CFS +```C +#define COS_TASK_SCHED_CFS 0x0 +``` +完全公平调度策略(非实时)。采用完全公平调度算法,对任务进行调度。适用于任务比较多的场景,开发者不需要设计每个任务的合理优先级,由调度算法以完全公平的策略调度任务,但开发者可以微调任务的权重,以相对增加或减少任务的运行时间片。 + +**注意:不是所有内核在实现时都支持CFS调度策略** + +#### 5.1.1.5 COS_TASK_SCHED_FIFO +```C +#define COS_TASK_SCHED_FIFO 0x01 +``` +实时,FIFO调度策略。当同优先级的多个任务同时就绪时,按先进先出的顺序依次执行。 + +#### 5.1.1.6 COS_TASK_SCHED_RR +```C +#define COS_TASK_SCHED_RR 0x02 +``` +实时,Round-Robin调度策略. 当同优先级的多个任务同时就绪时,多个任务按时间片轮流执行。 + +### 5.1.2 类型 +#### 5.1.2.1 cos_task_t +```C +typedef void * cos_task_t +``` +任务的句柄,代表一个任务,调用任务创建函数后返回。可以通过句柄对任务进行操作,如挂起,删除等。 + +#### 5.1.2.2 cos_task_entry_t +```C +typedef void (*cos_task_entry_t) (void *); +``` +任务入口函数的原型。 + +#### 5.1.2.3 cos_task_attr_t +```C +typedef struct cos_task_attr { +       const char *name; +       size_t stack_size; +       int32_t priority; + cos_tick_t tick; + uint8_t sched_policy; + uint8_t cpu_bind; + uint8_t inited; +      uint32_t options; + void *reserved; +} cos_task_attr_t; +``` +任务的属性类型,在创建任务时传递给内核,内核根据这些属性创建任务。 + +| 元素 | 描述 | +| -------- | -------- | +| name | 任务名称(可以为NULL) | +| stack_size | 任务栈的大小(单位:Byte) | +| priority | 任务的优先级 | +| tick | 任务使用RR调度策略时,占用的系统tick数 | +| sched_policy | 调度策略,COS_TASK_SCHED_RR/COS_TASK_SCHED_FIFO/COS_TASK_SCHED_CFS | +| cpu_bind | 绑定的CPU号,适用于多核系统 | +| inited | 标识该结构是否被初始化 | +| options | 任务创建时的选项 | +| reserved | 预留字段(可扩展) | + +### 5.1.3 函数 +#### 5.1.3.1 cos_task_attr_init +初始化一个任务属性 + +**原型** +```C +void cos_task_attr_init(cos_task_attr_t *attr); +``` + +**描述** +初始化一个任务属性,为任务属性设置默认值。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| attr | 入 | 任务属性的句柄| + +**返回值** +无 + +**注意** +此函数与创建任务同时使用。 +不同内核实现的默认值可能不同。 + +**例子** + +#### 5.1.3.2 cos_task_create +创建一个新的任务 + +**函数原型** +```C +cos_status_t cos_task_create(cos_task_t *task, cos_task_entry_t fn, void *arg, cos_task_attr_t *attr); +``` + +**描述** +创建一个新任务并初始化,新任务的句柄由参数task返回,通过任务句柄task对新任务进行后续的操作。 +新任务成功创建后,新任务会进入就绪队列等待调度执行。如果选项 COS_TASK_OPTION_NORUN被指定,则任务创建后处于挂起状态,不会被调度执行,需要应用恢复该任务后,才能进入就绪队列等待调度执行。 +当被调度到时从入口函数`fn`开始运行。从任务入口函数返回,代表此任务退出,系统会清理该任务的资源。 +任务的属性`attr`应使用`cos_task_attr_init`初始化。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| task | 出 | 新任务的句柄,后续对此任务的操作通过该句柄进行。| +| fn | 入 | 新任务的入口函数,新任务从此函数开始运行。| +| arg | 入 | 新任务的入口函数的参数。| +| attr | 入 | 新任务的属性| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | +| COS_ERR_TASK_NOMEM | 内存不足 | +| COS_ERR_ISR | 在中断上下文中调用 | + +**注意** +此函数不能在中断上下文中使用。 + +**例子** +```C +#include +#include +#include +  +static void cos_task_test_entry(void *arg) +{ +    printf("%s:%d:new task is running.\n", __func__, __LINE__); +    return; +} +  +int cos_task_create_example() +{ +    cos_status_t ret; +    cos_task_t task; +    cos_task_attr_t attr; + +  cos_task_attr_init(&attr);  +    ret = cos_task_create(&task, cos_task_test_entry, NULL, &attr) +    if (ret != COS_OK) { +        printf("%s:%d: create task fail ret:%d\n", __func__, __LINE__, ret); +        return -1; +    } +  +    return 0; +} +``` + +#### 5.1.3.3 cos_task_delete +删除一个任务 + +**原型** +```C +cos_status_t cos_task_delete(cos_task_t task); +``` + +**描述** +主动删除一个任务回收其资源。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| task | 入 | 任务的句柄| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | +| COS_ERR_ISR | 在中断上下文中调用 | +| COS_ERR_PERM | 没有权限删除任务,如删除idle任务 | + +**注意** +此函数不能在中断上下文中使用。 +不能删除系统idle任务。 + +**例子** + +#### 5.1.3.4 cos_task_init +初始化一个任务 + +**函数原型** +```C +cos_status_t cos_task_init(cos_task_t task, cos_task_entry_t fn, void *arg, cos_task_attr_t *attr, void *stack_buf); +``` + +**描述** +初始化一个任务。与创建任务类似,区别是调用者为新任务的控制块分配内存,其大小为任务控制块的数据结构大小,不同内核其任务控制块大小不同。 +通过参数`task`指定任务的控制块地址。 调用者还需要为新任务分配栈内存空间,并通过参数`stack_buf`指定栈内存空间地址,栈空间大小通过`attr`中的stack_size传递。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| task | 入 | 新任务控制块句柄,函数对该任务进行初始化| +| fn | 入 | 新任务的入口函数,新任务从此函数开始运行。| +| arg | 入 | 新任务的入口函数的参数。| +| attr | 入 | 新任务的属性| +| stack_buf | 入 | 调用者指定的任务栈空间| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | +| COS_ERR_ISR | 在中断上下文中调用 | + +**注意** +此函数不能在中断上下文中使用。 + +#### 5.1.3.5 cos_task_deinit +反初始化一个任务 + +**原型** +```C +cos_status_t cos_task_deinit(cos_task_t task); +``` + +**描述** +反初始化一个任务。与`cos_task_init`配套使用,销毁其初始化过的任务,把此任务置为销毁状态。如果该任务销毁前处于就绪状态,会把该任务从系统就绪队列中移除。 +根据互斥锁的类型(鲁棒性互斥锁),还会释放其拥有的互斥锁,导致唤醒等待该锁的任务。 但不会释放任务控制块task和任务栈的内存空间,这部分内存应由调用者负责释放。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| task | 入 | 任务的句柄| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | +| COS_ERR_ISR | 在中断上下文中调用 | +| COS_ERR_PERM | 没有权限反初始化任务,如idle任务 | + +**注意** +此函数不能在中断上下文中使用。 +不能反初始化系统idle任务。 + +#### 5.1.3.6 cos_task_exit +退出当前任务 + +**原型** +```C +void cos_task_exit(int32_t status); +``` + +**描述** +主动退出当前任务,使系统回收该任务的资源。其作用与从该任务的入口函数返回相同。参数`status`作为后续扩展使用,目前并不支持获取一个任务退出时的状态码。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| status | 入 | 任务退出的状态码(未使用)| + +**返回值** +此函数导致当前任务退出,不会返回。 + +**注意** +此函数不能在中断上下文中使用。 +目前并不支持获取一个任务退出时的状态码。 + +**例子** + +#### 5.1.3.7 cos_task_suspend +挂起一个任务 + +**原型** +```C +cos_status_t cos_task_suspend(cos_task_t task); +``` + +**描述** +挂起一个任务,暂停该任务的执行。可以挂起自身或其他任务,但不允许挂起系统idle任务。当挂起系统其他内置任务时请谨慎,可能会引起系统错误。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| task | 入 | 任务的句柄| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | +| COS_ERR_PERM | 没有权限挂起任务,如挂起idle任务 | + +**注意** +可以在中断上下文中使用 + +**例子** + +#### 5.1.3.8 cos_task_resume +恢复一个任务 + +**原型** +```C +cos_status_t cos_task_resume(cos_task_t task); +``` + +**描述** +恢复一个任务,使其处于就绪状态,等待调度执行。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| task | 入 | 任务的句柄| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +可以在中断上下文中使用。 + +**例子** + +#### 5.1.3.9 cos_task_yield +当前任务让出CPU + +**原型** +```C +cos_status_t cos_task_yield(void); +``` + +**描述** +使当前正在运行的任务让出CPU,并把当前任务置于其静态优先级的就绪队列的末尾。重新调度,其他任务得到CPU运行。注意根据调度策略,如果该任务是其静态优先级的就绪队列中唯一任务,它会被再次调度到,继续运行。 + +**参数** +无 + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | + +**注意** +可以在中断上下文中使用。 + +**例子** + +#### 5.1.3.10 cos_task_self +获取当前任务自身的句柄 + +**原型** +```C +cos_task_t cos_task_self(void); +``` + +**描述** +返回当前任务自身的句柄,用于对自身任务的操作,如挂起和恢复等。 + +**参数** +无 + +**返回值** +函数总是执行成功并返回当前任务自身的句柄。 + +**注意** +可以在中断上下文中使用。 + +**例子** + +#### 5.1.3.11 cos_task_get_name +获取一个任务的名称 + +**原型** +```C +cos_status_t cos_task_get_name(cos_task_t task, char *buf, size_t buf_size); +``` +**描述** +返回一个任务的名称,注意调用者应该为存放任务名称分配足够的内存,并把内存首地址和大小通过参数buf和buf_size传入。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| task | 入 | 任务的句柄| +| buf | 出 | 存放任务名称的内存| +| buf_size | 入 | 存放任务名称的内存大小| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +可以在中断上下文中使用。 + +**例子** + +#### 5.1.3.12 cos_task_find +根据名称查找任务 + +**原型** +```C +cos_task_t cos_task_find(const char *name); +``` + +**描述** +根据任务名称`name`查找任务,若能找到则返回任务的句柄,不能找到则返回`NULL`。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| name | 入 | 任务的名称| + +**返回值** +函数执行成功返回所找到的任务的句柄,失败则返回`NULL`。 + +**注意** +可以在中断上下文中使用。 +当系统中存在多个具有相同名称的任务时,函数会执行成功,但返回哪一个任务的句柄是不确定的,不同的内核实现会有所不同。 + +**例子** + +#### 5.1.3.13 cos_task_sleep +使当前任务睡眠一段时间 + +**原型** +```C +cos_status_t cos_task_sleep(cos_tick_t tick); +``` + +**描述** +使当前任务睡眠`tick`个系统tick的时间,等时间超期或被唤醒后再次进入就绪状态,等待调度执行。 +如果`tick`为0,则等同于`cos_task_yield`。如果`tick`为`COS_WAIT_FOREVER`, 则等同于`cos_task_suspend`。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| tick | 入 | 系统tick个数| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。被提前唤醒时则返回剩余的tick数。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +不可以在中断上下文中使用。 + +**例子** + +#### 5.1.3.14 cos_task_wakeup +唤醒一个任务 + +**原型** +```C +cos_status_t cos_task_wakeup(cos_task_t task); +``` + +**描述** +唤醒一个任务。可以用来提前唤醒一个睡眠的任务。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| task | 入 | 任务的句柄| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +可以在中断上下文中使用。 + +**例子** + +#### 5.1.3.15 cos_task_get_priority +获取一个任务的优先级 + +**原型** +```C +cos_status_t cos_task_get_priority(cos_task_t task, uint8_t *priority); +``` + +**描述** +获取一个任务的优先级,可以是自身或其他任务。获取的是任务此时的动态优先级,不一定与其创建时指定的静态优先级相同,如任务此时有优先级继承发生。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| task | 入 | 任务的句柄| +| priority | 出 | 获取到的任务动态优先级| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +可以在中断上下文中使用。 + +**例子** + +#### 5.1.3.16 cos_task_set_priority +设置一个任务的优先级 + +**原型** +```C +cos_status_t cos_task_set_priority(cos_task_t task, uint8_t priority); +``` + +**描述** +设置一个任务的优先级,可以是自身或其他任务。改变的是任务的静态优先级。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| task | 入 | 任务的句柄| +| priority | 入 | 任务静态优先级| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +可以在中断上下文中使用。 + +**例子** + +#### 5.1.3.17 cos_task_get_time_slice +获取一个RR调度策略的任务的剩余时间片 + +**原型** +```C +cos_status_t cos_task_get_time_slice(cos_task_t task, cos_tick_t *time_slice); +``` + +**描述** +获取一个使用RR调度策略的任务在本次轮转中剩余时间片。此函数仅适用于采用RR调度策略的任务, 对于其他任务,剩余时间片返回0. + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| task | 入 | 任务的句柄| +| time_slice | 出 | 获取到的任务的剩余时间片| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +可以在中断上下文中使用。 + +**例子** + +#### 5.1.3.18 cos_task_set_time_slice +设置一个RR调度任务的时间片 + +**原型** +```C +cos_status_t cos_task_set_time_slice(cos_task_t task, cos_tick_t time_slice); +``` + +**描述** +设置一个使用RR调度策略的任务在一次轮转中所分配的时间片。此函数仅适用于采用RR调度策略的任务, 对于其他任务,返回错误。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| task | 入 | 任务的句柄| +| time_slice | 入 | RR调度任务的时间片| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +可以在中断上下文中使用。 + +**例子** + +## 5.2 内存管理 +内存管理实现从系统堆内存中动态分配和释放内存,向应用提供动态分配和释放内存的API。 + +### 5.2.1 cos_malloc +申请一块连续的内存 + +**原型** +```C +void *cos_malloc(size_t size) +``` + +**描述** +从系统堆中申请一块`size`大小连续的内存,成功返回申请到内存的首地址,失败返回NULL。 + +**参数** +| 参数名称 | 参数方向 | 描述 | +| -------- | -------- | -------- | +| size | 入 | 想申请内存的大小(单位:Byte) | + +**返回值** +函数执行成功返回申请到内存的首地址,失败则返回NULL。 + +**注意** +此函数不能在中断上下文中使用。 + +**例子** + +### 5.2.2 cos_realloc +改变一块内存的大小 + +**原型** +```C +void *cos_realloc(void *ptr, size_t new_size) +``` + +**描述** +改变一块内存`ptr`的大小,`ptr`应该是之前动态申请内存的返回值。新的内存大小可能扩大或缩小,扩大时原有内存大小的内容保持不变且新增加的内存没有初始化。缩小时,被保留的内存的内容保持不变。如果参数`ptr`为NULL, 不论new_size为多少,则等于cos_malloc(new_size),如果参数`new_size`为0,则等于cos_free(prt)。如果该函数返回的新的内存首地址与原有地址不同,则原有内存被释放。 + +**参数** +| 参数名称 | 参数方向 | 描述 | +| -------- | -------- | -------- | +| ptr | 入 | 之前动态申请到的内存首地址 | +| new_size | 入 | 内存新大小 | + +**返回值** +函数执行成功返回新申请到内存的首地址,失败则返回NULL。 + +**注意** +此函数不能在中断上下文中使用。 + +**例子** + +### 5.2.3 cos_calloc +申请多块连续的内存 + +**原型** +```C +void *cos_calloc(size_t count, size_t size) +``` + +**描述** +从系统堆中申请`count`个长度为`size`的连续的内存,最终申请到内存大小为`count * size`个字节。 + +**参数** +| 参数名称 | 参数方向 | 描述 | +| -------- | -------- | -------- | +| count | 入 | 申请内存块的个数 | +| size | 入 | 每个内存块的大小 | + +**返回值** +函数执行成功返回申请到内存的首地址,失败则返回NULL。 + +**注意** +此函数不能在中断上下文中使用。 + +**例子** + +### 5.2.4 cos_free +释放内存 + +**原型** +```C +void cos_free(void *ptr) +``` + +**描述** +释放由参数`ptr`指向的一块内存,ptr应该是之前动态申请函数的返回值。 + +**参数** +| 参数名称 | 参数方向 | 描述 | +| -------- | -------- | -------- | +| ptr | 入 | 动态申请到的内存首地址 | + +**返回值** +无 + +**注意** +此函数不能在中断上下文中使用。 + +**例子** + +### 5.2.5 cos_malloc_align +申请内存并按要求地址对齐 + +**原型** +```C +int cos_malloc_align(void **memptr, size_t size, size_t alignment) +``` + +**描述** +从系统堆内存中申请`size`字节的连续内存,并按`alignment`字节对齐,申请到的内存首地址由\*memptr返回。如果参数size为0,则函数立即返回错误码,\*memptr为NULL。 参数alignment应该是`sizeof(void*)`的倍数且是2的幂,否则立即返回错误,\*memptr为NULL。 释放内存通过cos_free函数释放。 + +**参数** +| 参数名称 | 参数方向 | 描述 | +| -------- | -------- | -------- | +| memptr | 出 | 返回申请到的内存首地址 | +| size | 入 | 想申请内存的大小(单位:Byte) | +| alignment | 入 | 内存地址对齐大小(单位:Byte) | + +**返回值** +函数执行成功返回COS_OK, 所申请到内存的首地址由参数\*memptr返回。失败则返回错误码,同时参数\*memptr为NULL。 + +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR_PARAM | 非法参数 | +| COS_ERR_NOMEM | 内存不足 | + +**注意** +此函数不能在中断上下文中使用。 + +**例子** + +## 5.3 软件定时器 +软件定时器是由内核提供的隔一段时间单次或周期性触发执行应用函数的功能。应用可以通过设置回调函数,和定时器的周期等参数,让内核周期性地执行应用回调函数。 软件定时器一般由内核的定时器任务,基于系统tick,检查定时器是否超期,超期则在定时器任务的上下文中执行应用回调函数,这是其与硬件定时器不同之处。 + +### 5.3.1 常量 +#### 5.3.1.1 COS_TIMER_OPTION_ACTIVATE +创建时激活选项 + +```C +#define COS_TIMER_OPTION_ACTIVATE (0x1u << 0) +``` +定时器被创建后即开始启动。 + +#### 5.3.1.2 COS_TIMER_OPTION_DEACTIVATE +创建时不激活选项(**默认**) + +```C +#define COS_TIMER_OPTION_DEACTIVATE (0x1u << 1) +``` +定时器被创建后不会立即启动,需要被再次主动调用启动函数来启动。 + +### 5.3.2 类型 +#### 5.3.2.1 cos_timer_t +软件定时器句柄 + +```C +typedef void * cos_timer_t +``` +#### 5.3.2.2 cos_timer_cb_t +软件定时器的回调函数类型 + +```C +typedef void (*cos_timer_cb_t)(void *arg) +``` + +### 5.3.3 函数 +#### 5.3.3.1 cos_timer_create +创建一个定时器 + +**原型** +```C +cos_status_t cos_timer_create(cos_timer_t *timer, const char *name, cos_timer_cb_t cb, void *arg, cos_tick_t initial, cos_tick_t period, uint32_t options); +``` + +**描述** +创建一个定时器,当定时器超期时内核会执行回调函数`cb`, 并传入参数`arg`。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| timer | 出 | 定时器句柄| +| name | 入 | 定时器名称(可以为NULL)| +| cb | 入 | 定时器的回调函数| +| arg | 入 | 定时器的回调函数的参数| +| initial | 入 | 定时器的初始定时时间,即从启动到第一次触发的时间间隔,单位:tick个数| +| period | 入 | 定时器的周期定时时间,即周期性触发的时间间隔,单位:tick个数。0:代表单次触发| +| options | 入 | 定时器的选项,如创建时是否激活 `COS_TIMER_OPTION_ACTIVATE/COS_TIMER_OPTION_DEACTIVATE`| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | +| COS_ERR_NOMEM | 内存不足 | + +**注意** +不可以在中断上下文中使用。 + +**例子** + +#### 5.3.3.2 cos_timer_delete +删除一个定时器 + +**原型** +```C +cos_status_t cos_timer_delete(cos_timer_t timer); +``` + +**描述** +删除一个定时器。系统会先停止该定时器,然后删除回收其资源。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| timer | 入 | 定时器句柄| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +不可以在中断上下文中使用。 + +**例子** + +#### 5.3.3.3 cos_timer_init +初始化一个定时器 + +**原型** +```C +cos_status_t cos_timer_init(cos_timer_t timer, const char *name, cos_timer_cb_t cb, void *arg, cos_tick_t initial, cos_tick_t period, uint32_t options); +``` + +**描述** +初始化一个定时器,当定时器超期时内核会执行回调函数`cb`, 并传入参数`arg`。与创建一个定时器类似,区别是定时器的控制块内存时由调用者传入的,一般用于应用希望静态分配内存的场景。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| timer | 入 | 定时器句柄| +| name | 入 | 定时器名称| +| cb | 入 | 定时器的回调函数| +| arg | 入 | 定时器的回调函数的参数| +| initial | 入 | 定时器的初始定时时间,即从启动到第一次触发的时间间隔,单位:tick个数| +| period | 入 | 定时器的周期定时时间,即周期性触发的时间间隔,单位:tick个数。0:代表单次触发| +| options | 入 | 定时器的选项,如创建时是否激活 `COS_TIMER_OPTION_ACTIVATE/COS_TIMER_OPTION_DEACTIVATE`| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +不可以在中断上下文中使用。 + +**例子** + +#### 5.3.3.4 cos_timer_deinit +反初始化一个定时器 + +**原型** +```C +cos_status_t cos_timer_deinit(cos_timer_t timer); +``` + +**描述** +反初始化一个定时器。系统会先停止该定时器,从系统定时器列表中移除。但不会释放`timer`的内存空间,应由调用者释放。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| timer | 入 | 定时器句柄| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +不可以在中断上下文中使用。 + +**例子** + +#### 5.3.3.5 cos_timer_start +启动一个定时器 + +**原型** +```C +cos_status_t cos_timer_start(cos_timer_t timer); +``` + +**描述** +启动一个定时器,内核开始计时,直到定时器超期时执行应用设置的回调函数。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| timer | 入 | 定时器句柄| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +不可以在中断上下文中使用。 + +**例子** + +#### 5.3.3.6 cos_timer_stop +停止一个定时器 + +**原型** +```C +cos_status_t cos_timer_stop(cos_timer_t timer); +``` + +**描述** +停止一个定时器 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| timer | 入 | 定时器句柄| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +不可以在中断上下文中使用。 + +**例子** + +#### 5.3.3.7 cos_timer_change +修改一个定时器 + +**原型** +```C +cos_status_t cos_timer_change(cos_timer_t timer, cos_tick_t initial, cos_tick_t period); +``` + +**描述** +修改一个定时器。一般用于修改其周期性定时时间,如果该定时器创建后还还有启动,也可以修改其初始定时时间。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| timer | 入 | 定时器句柄| +| initial | 入 | 定时器的初始定时时间,即从启动到第一次触发的时间间隔,单位:tick个数| +| period | 入 | 定时器的周期定时时间,即周期性触发的时间间隔,单位:tick个数。0:代表单次触发| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +不可以在中断上下文中使用。 + +**例子** + +#### 5.3.3.8 cos_timer_get_time +获取一个定时器的时间 + +**原型** +```C +cos_status_t cos_timer_get_time(cos_timer_t timer, cos_tick_t *remaining, cos_tick_t *period); +``` + +**描述** +返回一个定时器的信息,包括离下次触发还剩余的时间(系统tick),和周期触发的时间间隔(系统tick)。 + +**参数** +| 参数名称 | 参数方向 |描述 | +| -------- | -------- | -------- | +| timer | 入 | 定时器句柄| +| remaining | 出 | 离下次触发还剩余的时间,单位:tick个数| +| period | 出 | 周期触发的时间间隔,单位:tick个数。0:代表单次触发| + +**返回值** +函数执行成功返回COS_OK,失败则返回错误码(均为负值)。 +| 错误码 | 描述 | +| -------- | -------- | +| COS_ERR | 未知错误 | +| COS_ERR_PARAM | 非法参数 | + +**注意** +不可以在中断上下文中使用。 + +**例子** diff --git a/osal/cosit/documentation/work_queue.md b/osal/cosit/documentation/work_queue.md new file mode 100644 index 0000000..097a0ea --- /dev/null +++ b/osal/cosit/documentation/work_queue.md @@ -0,0 +1,200 @@ +# 工作队列 + +工作队列(workqueue)是一种转移任务执行环境的工具,例如当系统产生一个中断时,我们可以在中断处理函数里做一些紧急地操作,然后将另外一些不那么紧急,而且需要一定时间的任务封装成函数交给工作队列执行,此时该函数的执行环境就从 中断环境 变成了 线程环境,即中断处理 “下半部”。 + +## 类型 + +```C +typedef struct cos_workqueue* cos_wq_t; +``` + +工作队列控制块,工作队列控制块是操作系统用于管理工作队列的一个数据结构,由结构体cos_workqueue表示,cos_wq_t是工作队列的句柄,工作队列结构体主要保存了当前系统工作队列的相关信息,如:当前工作项,工作队列的链表,工作队列所使用的线程等。 + +```C +typedef struct cos_work* cos_work_t; +``` + +工作项控制块,工作项控制块用于管理该任务,保存了该任务相关的信息,如:任务函数,用户数据等,当该工作项被执行时就会调用该任务函数。 + +```C +typedef void cos_work_handle_t(void *arg); +``` + +工作项控制块,工作项控制块用于管理该任务,保存了该任务相关的信息,如:任务函数,用户数据等,当该工作项被执行时就会调用该任务函数。 + +## 函数 + +### cos_work_init + +初始化工作项 + +原型 + +```C + +#include +cos_status_t cos_work_init(cos_work_t work, cos_work_handle_t work_func, void *work_data); + +``` + +**描述** +该接口初始化 work 指针指向的工作项,并绑定回调函数 work_func 以及用户自定义数据 work_data。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| work | cos_work_t | 入参 | 工作项 | +| work_func | cos_work_handle_t | 入参 | 工作项的回调函数 | +| work_data | size_t | 入参 | 用户自定义数据,回调函数参数 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| cos_ERROR_PARAM | 非法参数 | + +### cos_work_dowork + +触发work + +原型 + +```C + +#include +cos_status_t cos_work_dowork(cos_work_t work, cos_tick_t time); + +``` + +**描述** +work触发函数(由系统默认工作队列来执行)。若 time 大于 0,则提交过程会延时 time 个 ticks 。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| work | cos_work_t | 入参 | 工作项 | +| time | cos_tick_t | 入参 | 提交延时,以 tick 为单位 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| cos_ERROR_PARAM | 非法参数 | + +### cos_work_submit + +向 queue 提交work + +原型 + +```C + +#include +cos_status_t cos_work_submit(cos_wq_t wq, cos_work_t work, cos_tick_t time); + +``` + +**描述** +将 work 指向的工作项提交到 queue 指向的工作队列中,若 time 大于 0,则该提交延时 time 个 tick 之后执行。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| wq | cos_wq_t | 入参 | 工作队列 | +| work | cos_work_t | 入参 | 工作项 | +| time | cos_tick_t | 入参 | 提交延时,以 tick 为单位 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| cos_ERROR_PARAM | 非法参数 | + +### cos_work_cancel + +取消指定工作项 + +原型 + +```C + +#include +cos_status_t cos_work_cancel(cos_wq_t queue, cos_work_t work); + +``` + +**描述** +从 queue 指向的工作队列中将 work 指向的工作项移除。 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| queue | cos_wq_t | 入参 | 工作队列 | +| work | cos_work_t | 入参 | 工作项 | + + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| cos_ERROR_PARAM | 非法参数 | +| cos_ERROR_BUSY | 该工作项正在执行 | + +### cos_workqueue_create + +工作队列创建函数 + +原型 + +```C + +#include +cos_status_t cos_workqueue_create(cos_wq_t* wq, const char *name, uint16_t stack_size, uint8_t priority); + +``` + +**描述** +该函数创建并初始化一个工作队列,利用参数 name,stack_size 和 priority 创建工作队列内部线程,最终返回创建的工作队列。下表描述了该函数的输入参数与返回值 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| wq | cos_wq_t | 出参 | 工作队列控制块 | +| name | const char * | 入参 | 线程名字 | +| stack_size | uint16_t | 入参 | 线程栈大小 | +| priority | uint8_t | 入参 | 线程优先级 | + + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| cos_ERROR_PARAM | 非法参数 | + +### cos_workqueue_destroy + +工作队列销毁函数 + +原型 + +```C + +#include +cos_status_t cos_workqueue_destroy(cos_wq_t wq); + +``` + +**描述** +销毁指定工作队列 + +**参数** +| 参数名称 | 参数类型 | 参数方向 | 描述 | +| ----------- | ----------- | ------------------ | ------------------ | +| wq | cos_wq_t | 入参 | 工作队列控制块 | + +**返回值** +函数执行成功返回cos_OK,失败则返回错误码(均为负值)。 +| 参数名称 | 描述 | +| ----------- | ------------------ | +| cos_ERROR_PARAM | 非法参数 | + diff --git a/osal/cosit/include/cosit.h b/osal/cosit/include/cosit.h new file mode 100644 index 0000000..b29f53b --- /dev/null +++ b/osal/cosit/include/cosit.h @@ -0,0 +1,1612 @@ +/* + * Copyright (C) XXXX + * + * SPDX-License-Identifier: XXXX + * + * Version: 1.1.1 + * + * Change log: + * + * Date Author Note + * 2022-06-15 all first version + * 2022-07-12 Mculover666 Add cos_sem_release_all and cos_tick_get API. + * 2022-07-26 Mculover666 Fix the type error of sem. + */ + +#ifndef _COSIT_H +#define _COSIT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @addtogroup cos_status + * COSIT标准状态码 + * @{ + */ +/** + * 状态码格式说明: + * 0:表示成功,错误码均为负值,错误码用16位(不算符号位)的16进制表示。 + * 其中高1~4位:标识模块, 其余为错误码(后续也可扩展为其他标识)。 + * 高1~4 模块名称 + * 0 通用错误 + * 1 任务管理 + * 2 内存管理 + * 3 软件定时器 + * 4 互斥量 + * 5 信号量 + * 7 事件 + * 8 消息队列 + * 9 邮箱 + * A 工作队列 + */ +#define COS_OK 0 /**< 成功 */ + +/* 通用错误码 */ +#define COS_ERR (-0x0001) /**< 未知错误 */ +#define COS_ERR_PARAM (-0x0002) /**< 非法参数 */ +#define COS_ERR_NOMEM (-0x0003) /**< 内存不足 */ +#define COS_ERR_TIMEOUT (-0x0003) /**< 超时 */ +#define COS_ERR_ISR (-0x0004) /**< 不允许在中断上下文中使用 */ +#define COS_ERR_PERM (-0x0005) /**< 权限不允许 */ + +/* 互斥量特有错误码 */ +#define COS_ERR_MUTEX_NOT_OWNER (-0x4001) /**< 不是互斥锁的所有者 */ +#define COS_ERR_MUTEX_NESTING (-0x4002) /**< 已经拥有互斥锁,导致嵌套但并未指定嵌套选项 */ + +/* 邮箱特有错误码 */ +#define COS_ERR_MB_FULL (-0x9001) + +/** + * @} + */ + + +/** + * 表示永久阻塞等待,直到获得系统资源(如互斥量,信号量,事件,队列消息等)才返回。 + */ +#define COS_WAIT_FOREVER ((cos_tick_t)-1) + +/** + * 表示非阻塞等待,当不能获得系统资源(如互斥量,信号量,事件,队列消息等)时不会等待,而是立即返回。 + */ +#define COS_NO_WAIT (0x0U) + +/** + * @addtogroup cos_timer + * 提供软件定时器相关的标准定义 + * @{ + */ +#define COS_TIMER_OPTION_ACTIVATE (0x1u << 0) /**< 创建时激活选项 */ +#define COS_TIMER_OPTION_DEACTIVATE (0x1u << 1) /**< 创建时不激活选项 */ +/** + * @} + */ + +/** + * @addtogroup cos_mutex + * 提供互斥锁相关的标准定义 + * @{ + */ +#define COS_MUTEX_NEST (0x1u << 0) /**< 开启嵌套锁支持 */ +/** + * @} + */ + +/** + * @addtogroup cos_sem + * 提供信号量相关的标准定义 + * @{ + */ + +/** + * 用来指定创建一个新的信号量时的最大值,表示信号量无最大值。 + */ +#define COS_SEM_NO_MAX ((uint32_t)-1) + +/** + * @} + */ + +/** + * @addtogroup cos_event + * 提供事件相关的标准定义 + * @{ + */ + +/** + * 用来指定等待事件和释放事件时的选项。 + */ +#define COS_EVENT_WAIT_ANY (0x1u << 0) /**< 等待任意一个事件 */ +#define COS_EVENT_WAIT_ALL (0x1u << 1) /**< 等待所有事件 */ +#define COS_EVENT_WAIT_CLR (0x1u << 2) /**< 等待到事件后清除该事件 */ +#define COS_EVENT_RELEASE_KEEP (0x1u << 3) /**< 释放事件后是否保留该事件 */ + +/** + * @} + */ + +typedef int32_t cos_status_t; /**< 状态码类型 */ + +typedef uint64_t cos_tick_t; /**< 系统tick类型 */ + + +/** + * @addtogroup cos_mb + * 提供邮箱相关的标准定义 + * @{ + */ +typedef void * cos_mb_t; /**< 邮箱句柄 */ + +#define COS_IPC_FLAG_FIFO 0 +#define COS_IPC_FLAG_PRIO 1 +/** + * @} + */ + +/** + * @addtogroup cos_mq + * 提供消息队列相关的标准定义 + * @{ + */ +typedef void * cos_mq_t; /**< 邮箱句柄 */ +/** + * @} + */ + +/** + * @addtogroup cos_wq + * 提供工作队列相关的标准定义 + * @{ + */ +typedef void * cos_wq_t; /**< 工作队列句柄 */ +typedef void * cos_work_t; /**< 工作项句柄 */ +typedef void (*cos_work_handle_t) (void *); /**< 任务入口函数的原型。*/ +/** + * @} + */ + +/** + * 用来指定创建一个新任务时任务属性的options参数,表示任务创建后处于挂起状态,不会被调度执行, + * 直到被恢复。如果没有指定此选项(默认),任务创建后即处于就绪状态,等待调度运行。 + */ +#define COS_TASK_OPTION_NORUN (0x1U << 0) + +/** + * 最大任务优先级。任务优先级从0 ~ COS_TASK_PRIORITY_MAX优先级别递减,即COS_TASK_PRIORITY_MAX优先级最低。 + * 如内核在实现时,若其优先级级别小于`COS_TASK_PRIORITY_MAX`, 当应用传入的优先级超过内核实现的优先级时, + * 内核应调整为应用可设置的最低优先级。如果应用不关心任务的优先级,则可以使用`cos_task_attr_init`初始化为 + * 默认优先级即可,不同内核在实现时,其默认优先级可以不同。 + */ +#define COS_TASK_PRIORITY_MAX 100 + +/** + * 完全公平调度策略(非实时)。采用完全公平调度算法,对任务进行调度。适用于任务比较多的场景, + * 开发者不需要设计每个任务的合理优先级,由调度算法以完全公平的策略调度任务,但开发者可以微调任务的权重, + * 以相对增加或减少任务的运行时间片。 + */ +#define COS_TASK_SCHED_CFS 0x0 + +/** + * 实时,FIFO调度策略。当同优先级的多个任务同时就绪时,按先进先出的顺序依次执行。 + */ +#define COS_TASK_SCHED_FIFO 0x01 + +/** + * 实时,Round-Robin调度策略. 当同优先级的多个任务同时就绪时,多个任务按时间片轮流执行。 + */ +#define COS_TASK_SCHED_RR 0x02 + +/** + * @} + */ + +/** + * @addtogroup cos_task + * 提供任务管理相关的标准定义 + * @{ + */ + +/** + * 任务的句柄,代表一个任务,调用任务创建函数后返回。可以通过句柄对任务进行操作,如挂起,删除等。 + */ +typedef void * cos_task_t; + +typedef void (*cos_task_entry_t) (void *); /**< 任务入口函数的原型。*/ + +typedef struct cos_task_attr { + const char *name; /**< 任务名称(可以为NULL) */ + size_t stack_size; /**< 任务栈的大小(单位:Byte) */ + uint8_t priority; /**< 任务的优先级 */ + cos_tick_t tick; /**< 任务使用RR调度策略时,占用的系统tick数 */ + uint8_t sched_policy; /**< 调度策略 @ref COS_TASK_SCHED_RR / @ref COS_TASK_SCHED_FIFO / @ref COS_TASK_SCHED_CFS */ + uint8_t cpu_bind; /**< 绑定的CPU号,适用于多核系统 */ + uint8_t inited; /**< 标识该结构是否被初始化 */ + uint32_t options; /**< 任务创建时的选项 */ + void *reserved; /**< 预留字段(可扩展)*/ +} cos_task_attr_t; +/** + * @} + */ + + +/** + * @addtogroup cos_timer + * 提供软件定时器相关的标准定义 + * @{ + */ +typedef void * cos_timer_t; /**< 软件定时器句柄 */ + +typedef void (*cos_timer_cb_t)(void *arg); /**< 软件定时器的回调函数类型 */ +/** + * @} + */ + +/** + * @addtogroup cos_mutex + * 提供互斥锁相关的标准定义 + * @{ + */ +typedef void * cos_mutex_t; /**< 互斥锁句柄 */ + +/** + * @} + */ + +/** + * @addtogroup cos_sem + * 提供信号量相关的标准定义 + * @{ + */ +typedef void * cos_sem_t; /**< 信号量句柄 */ + +/** + * @} + */ + +/** + * @addtogroup cos_event + * 提供事件相关的标准定义 + * @{ + */ +typedef void * cos_event_t; /**< 事件句柄 */ + +typedef uint32_t cos_event_flag_t; /**< 事件标志 */ + +/** + * @} + */ + +/** + * @brief 毫秒数转为系统tick数 + * + * 把毫秒数转成系统tick数。在物联网操作系统中系统tick一般用于内核调度的基本单位, + * 其频率在系统中是可以配置的。根据系统tick一般只能达到毫秒精度。 + * + * @param[in] ms 毫秒数 + * + * @note 可以在中断上下文中使用。 + * + * @return 总是成功,返回转换得到的系统tick数 + */ +cos_tick_t cos_ms_to_tick(uint64_t ms); + +/** + * @brief 系统tick数转为毫秒数 + * + * 把系统tick数转成毫秒数。在物联网操作系统中系统tick一般用于内核调度的基本单位, + * 其频率在系统中是可以配置的。根据系统tick一般只能达到毫秒精度。 + * + * @param[in] tick 系统tick数 + * + * @note 可以在中断上下文中使用。 + * + * @return 总是成功,返回转换得到的毫秒数 + */ +uint64_t cos_tick_to_ms(cos_tick_t tick); + +/** + * @brief 获取当前系统tick数 + * + * @note 可以在中断上下文中使用。 + * + * @return 总是成功,返回得到的当前系统tick数 + */ +cos_tick_t cos_tick_get(void); + +/** + * @addtogroup cos_task + * 提供任务管理相关的标准定义 + * @{ + */ + +/** + * @brief 初始化一个任务属性 + * + * 初始化一个任务属性,为任务属性设置默认值。 + * + * @param[in] attr 任务属性的句柄 + * + * @note 可以在中断上下文中使用。 + * @note 此函数与创建任务同时使用。 + * @note 每个内核实现的默认值可能不同。 + * + * @return 无 + */ +void cos_task_attr_init(cos_task_attr_t *attr); + +/** + * @brief 创建新任务 + * + * 创建一个新任务并初始化,新任务的句柄由参数 @c task 返回,通过任务句柄 @c task 对新任务进行后续的操作。 + * 新任务成功创建后,会进入就绪队列等待调度执行。如果选项 @ref COS_TASK_OPTION_NORUN 被指定, + * 则任务创建后处于挂起状态,不会被调度执行,需要应用恢复该任务后,才能进入就绪队列等待调度执行。 + * 当被调度到时从入口函数fn开始运行。从任务入口函数返回,代表此任务退出,系统会清理该任务的资源。 + * 该函数在创建时内部会动态分配需要的内存,如果不想使用动态内存,请使用 @ref cos_task_init 。 + * + * @param[out] task 新任务的句柄,后续对此任务的操作通过该句柄进行 + * @param[in] fn 新任务的入口函数,新任务从此函数开始运行 + * @param[in] arg 新任务的入口函数的参数 + * @param[in] attr 新任务的属性 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + * + * @see cos_task_init + */ +cos_status_t cos_task_create(cos_task_t *task, cos_task_entry_t fn, void *arg, cos_task_attr_t *attr); + +/** + * @brief 删除任务 + * + * 主动删除一个任务回收其资源。 + * + * @param[in] task 任务的句柄 + * + * @note 不可以在中断上下文中使用。 + * @note 不能删除idle任务。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_task_delete(cos_task_t task); + +/** + * @brief 初始化一个任务 + * + * 初始化一个任务。与创建任务类似,区别是调用者为新任务的控制块分配内存,其大小为任务控制块的数据结构大小, + * 不同内核其任务控制块大小不同。通过参数 @c task 指定任务的控制块地址。 调用者还需要为新任务分配栈内存空间, + * 并通过参数 @c stack_buf 指定栈内存空间地址,栈空间大小通过 @c attr 中的stack_size传递。 + * + * @param[in] task 新任务的句柄,后续对此任务的操作通过该句柄进行 + * @param[in] fn 新任务的入口函数,新任务从此函数开始运行 + * @param[in] arg 新任务的入口函数的参数 + * @param[in] attr 新任务的属性 + * @param[in] stack_buf 调用者指定的任务栈空间 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + * + * @see cos_task_init + */ +cos_status_t cos_task_init(cos_task_t task, cos_task_entry_t fn, void *arg, + cos_task_attr_t *attr, void *stack_buf); + +/** + * @brief 反初始化一个任务 + * + * 反初始化一个任务。与 @ref cos_task_init 配套使用,销毁其初始化过的任务,把此任务置为销毁状态。 + * 如果该任务销毁前处于就绪状态,会把该任务从系统就绪队列中移除。根据互斥锁的类型(鲁棒性互斥锁), + * 还会释放其拥有的互斥锁,导致唤醒等待该锁的任务。 + * 但不会释放任务控制块task和任务栈的内存空间,这部分内存应由调用者负责释放。 + * + * @param[in] task 新任务的句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + * + * @see cos_task_init + */ +cos_status_t cos_task_deinit(cos_task_t task); + +/** + * @brief 退出任务 + * + * 主动退出当前任务,使系统回收该任务的资源。其作用与从该任务的入口函数返回相同。 + * 参数 @c status 作为后续扩展使用,目前并不支持获取一个任务退出时的状态码。 + * + * @param[in] status 任务退出状态码 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +void cos_task_exit(int32_t status); + +/** + * @brief 挂起任务 + * + * 挂起一个任务,暂停该任务的执行。可以挂起自身或其他任务,但不允许挂起系统idle任务。 + * 当挂起系统其他内置任务时请谨慎,可能会引起系统错误。 + * + * @param[in] task 任务的句柄 + * + * @note 可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_task_suspend(cos_task_t task); + +/** + * @brief 恢复任务 + * + * 恢复一个任务,使其处于就绪状态,等待调度执行。 + * + * @param[in] task 任务的句柄 + * + * @note 可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_task_resume(cos_task_t task); + +/** + * @brief 当前任务让出CPU + * + * 使当前正在运行的任务让出CPU,并把当前任务置于其静态优先级的就绪队列的末尾。 + * 重新调度,其他任务得到CPU运行。 + * + * @param 无 + * + * @note 可以在中断上下文中使用。 + * @note 根据调度策略,如果该任务是其静态优先级的就绪队列中唯一任务,它会被再次调度到,继续运行。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_task_yield(void); + +/** + * @brief 获取当前任务自身的句柄 + * + * 返回当前任务自身的句柄,用于对自身任务的操作,如挂起和恢复等。 + * + * @param 无 + * + * @note 可以在中断上下文中使用。 + * + * @return 函数总是执行成功并返回当前任务自身的句柄。 + */ +cos_task_t cos_task_self(void); + +/** + * @brief 获取一个任务的名称 + * + * 返回一个任务的名称,调用者应该为存放任务名称分配足够的内存, + * 并把内存首地址和大小通过参数 @c buf 和 @c buf_size 传入。 + * + * @param[in] task 任务的句柄 + * @param[out] buf 存放任务名称的内存 + * @param[in] buf_size 存放任务名称的内存大小 + * + * @note 可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_task_get_name(cos_task_t task, char *buf, size_t buf_size); + +/** + * @brief 根据名称查找任务 + * + * 根据任务名称 @c name 查找任务的句柄。 + * + * @param[in] name 任务的名称 + * + * @note 可以在中断上下文中使用。 + * @note 当系统中存在多个具有相同名称的任务时,函数会执行成功,但返回哪一个任务 + * 的句柄是不确定的,不同的内核实现会有所不同。 + * + * @return 成功则返回任务的句柄, 失败则返回 NULL + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_task_t cos_task_find(const char *name); + +/** + * @brief 使当前任务睡眠一段时间 + * + * 使当前任务睡眠 @c tick 个系统tick的时间,等时间超期或被唤醒后再次进入就绪状态,等待调度执行。 + * 如果 @c tick 为0,则等同于 @ref cos_task_yield 。如果 @c tick 为 @ref COS_WAIT_FOREVER , 则等同于 @ref cos_task_suspend。 + * + * @param[in] tick 系统tick个数 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败,>0: 提前被唤醒时剩余的tick数 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_task_sleep(cos_tick_t tick); + +/** + * @brief 唤醒任务 + * + * 唤醒一个任务。可以用来提前唤醒一个睡眠的任务。 + * + * @param[in] task 任务的句柄 + * + * @note 可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_task_wakeup(cos_task_t task); + +/** + * @brief 获取一个任务的优先级 + * + * 获取一个任务的优先级,可以是自身或其他任务。获取的是任务此时的动态优先级, + * 不一定与其创建时指定的静态优先级相同,如任务此时有优先级继承发生。 + * + * @param[in] task 任务的句柄 + * @param[out] priority 获取到的任务动态优先级 + * + * @note 可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_task_get_priority(cos_task_t task, uint8_t *priority); + +/** + * @brief 设置一个任务的优先级 + * + * 设置一个任务的优先级,可以是自身或其他任务。改变的是任务的静态优先级。 + * + * @param[in] task 任务的句柄 + * @param[in] priority 获取到的任务动态优先级 + * + * @note 可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_task_set_priority(cos_task_t task, uint8_t priority); + +/** + * @brief 获取任务的剩余时间片 + * + * 获取一个使用RR调度策略的任务在本次轮转中剩余时间片。 + * + * @param[in] task 任务的句柄 + * @param[out] time_slice 获取到的任务的剩余时间片 + * + * @note 可以在中断上下文中使用。 + * @note 此函数仅适用于采用RR调度策略的任务, 对于其他任务,剩余时间片返回0. + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_task_get_time_slice(cos_task_t task, cos_tick_t *time_slice); + +/** + * @brief 获取任务的剩余时间片 + * + * 设置一个使用RR调度策略的任务在一次轮转中所分配的时间片。 + * + * @param[in] task 任务的句柄 + * @param[in] time_slice 获取到的任务的剩余时间片 + * + * @note 可以在中断上下文中使用。 + * @note 此函数仅适用于采用RR调度策略的任务,对于其他任务,返回错误。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_task_set_time_slice(cos_task_t task, cos_tick_t time_slice); + +/** + * @} + */ + +/** + * @addtogroup cos_timer + * 提供软件定时器相关的标准定义 + * @{ + */ + +/** + * @brief 创建软件定时器 + * + * 创建一个软件定时器,当定时器超期时系统会自动执行回调函数 @c cb 并传入参数 @c arg 。 + * 该函数在创建时内部会动态分配需要的内存,如果不想使用动态内存,请使用 @ref cos_timer_init 。 + * + * @param[out] timer 定时器句柄 + * @param[in] name 定时器名称 + * @param[in] cb 定时器的回调函数 + * @param[in] arg 定时器的回调函数的参数 + * @param[in] initial 定时器的初始定时时间,即从定时器启动到第一次触发的时间间隔,单位:tick个数 + * @param[in] period 定时器的周期定时时间,即周期性触发的时间间隔,单位:tick个数。0:代表单次触发 + * @param[in] options 定时器的选项,如创建时激活 @ref COS_TIMER_OPTION_ACTIVATE, + * 或者不激活 @ref COS_TIMER_OPTION_DEACTIVATE + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR 定时器未知错误 + * + * @see cos_timer_init + */ +cos_status_t cos_timer_create(cos_timer_t *timer, const char *name, cos_timer_cb_t cb, + void *arg, cos_tick_t initial, cos_tick_t period, uint32_t options); + +/** + * @brief 删除软件定时器 + * + * 删除一个软件定时器。系统会先停止该定时器,然后删除回收其资源。 + * + * @param[in] timer 定时器句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 定时器未知错误 + */ +cos_status_t cos_timer_delete(cos_timer_t timer); + +/** + * @brief 初始化软件定时器 + * + * 初始化一个软件定时器,当定时器超期时系统会自动执行回调函数 @c cb , 并传入参数 @c arg 。 + * 与创建一个软件定时器类似,区别是定时器的控制块内存时由调用者传入的,一般用于需要静态内存的场景。 + * + * @param[in] timer 定时器句柄 + * @param[in] name 定时器名称 + * @param[in] cb 定时器的回调函数 + * @param[in] arg 定时器的回调函数的参数 + * @param[in] initial 定时器的初始定时时间,即从定时器启动到第一次触发的时间间隔,单位:tick个数 + * @param[in] period 定时器的周期定时时间,即周期性触发的时间间隔,单位:tick个数。0:代表单次触发 + * @param[in] options 定时器的选项,如创建时激活 @ref COS_TIMER_OPTION_ACTIVATE, + * 或者不激活 @ref COS_TIMER_OPTION_DEACTIVATE + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR 定时器未知错误 + * + * @see cos_timer_create + */ +cos_status_t cos_timer_init(cos_timer_t timer, const char *name, cos_timer_cb_t cb, + void *arg, cos_tick_t initial, cos_tick_t period, uint32_t options); + +/** + * @brief 反初始化软件定时器 + * + * 反初始化一个定时器。系统会先停止该定时器,从系统定时器列表中移除。但不会释放 @c timer 的内存空间,应由调用者释放。 + * + * @param[in] timer 定时器句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 定时器未知错误 + */ +cos_status_t cos_timer_deinit(cos_timer_t timer); + +/** + * @brief 启动软件定时器 + * + * 启动一个定时器,系统开始计时,直到定时器超期时执行应用设置的回调函数。 + * + * @param[in] timer 定时器句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 定时器未知错误 + */ +cos_status_t cos_timer_start(cos_timer_t timer); + +/** + * @brief 停止软件定时器 + * + * 停止一个定时器,系统停止为该定时器计时,但不会删除它。 + * + * @param[in] timer 定时器句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 定时器未知错误 + */ +cos_status_t cos_timer_stop(cos_timer_t timer); + +/** + * @brief 修改软件定时器 + * + * 修改一个定时器。一般用于修改其周期性定时时间,如果该定时器创建后还没有启动,也可以修改其初始定时时间。 + * + * @param[in] timer 定时器句柄 + * @param[in] initial 定时器的初始定时时间,即从启动到第一次触发的时间间隔,单位:tick个数 + * @param[in] period 定时器的周期定时时间,即周期性触发的时间间隔,单位:tick个数。0:代表单次触发 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 定时器未知错误 + */ +cos_status_t cos_timer_change(cos_timer_t timer, cos_tick_t initial, cos_tick_t period); + +/** + * @brief 获取定时器的时间 + * + * 返回一个定时器的时间信息,包括离下次触发还剩余的时间(系统tick),和周期触发的时间间隔(系统tick)。 + * + * @param[in] timer 定时器句柄 + * @param[out] remaining 离下次触发还剩余的时间,单位:tick个数 + * @param[out] period 周期触发的时间间隔,单位:tick个数。0:代表单次触发 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 定时器未知错误 + */ +cos_status_t cos_timer_get_time(cos_timer_t timer, cos_tick_t *remaining, cos_tick_t *period); + +/** + * @} + */ + +/** + * @addtogroup cos_memory + * 提供内存管理相关的标准定义 + * @{ + */ + +/** + * @brief 申请一块连续的内存 + * + * 从系统堆中申请一块 @c size 大小连续的内存,成功返回申请到内存的首地址,失败返回NULL。 + * + * @param[in] size 想申请内存的大小(单位:Byte) + * + * @note 不可以在中断上下文中使用。 + * + * @return 成功返回申请到内存的首地址,失败则返回NULL。 + */ +void *cos_malloc(size_t size); + +/** + * @brief 改变一块内存的大小 + * + * 改变一块内存 @c ptr 的大小, @c ptr 应该是之前动态申请内存的返回值。新的内存大小可能扩大或缩小, + * 扩大时原有内存大小的内容保持不变且新增加的内存没有初始化。缩小时,被保留的内存的内容保持不变。 + * 如果参数 @c ptr 为NULL,不论new_size为多少,则等于 @c cos_malloc(new_size) , + * 如果参数 @c new_size 为0,则等于 @c cos_free(prt) 。如果该函数返回的新的内存首地址与原有地址不同, + * 则原有内存被自动释放。 + * + * @param[in] ptr 之前动态申请到的内存首地址 + * @param[in] new_size 内存新大小 + * + * @note 不可以在中断上下文中使用。 + * + * @return 成功返回新申请到内存的首地址,失败则返回NULL。 + */ +void *cos_realloc(void *ptr, size_t new_size); + +/** + * @brief 申请多块连续的内存 + * + * 从系统堆中申请 @c count 个长度为 @c size 的连续的内存,最终申请到内存大小为count * size个字节。 + * + * @param[in] count 申请内存块的个数 + * @param[in] size 每个内存块的大小 + * + * @note 不可以在中断上下文中使用。 + * + * @return 成功返回申请到内存的首地址,失败则返回NULL。 + */ +void *cos_calloc(size_t count, size_t size); + +/** + * @brief 申请内存并按要求地址对齐 + * + * 从系统堆内存中申请 @c size 字节的连续内存,并按 @c alignment 字节对齐, + * 申请到的内存首地址由 @c *memptr 返回。如果参数 @c size 为0,则函数立即返回错误码, + * @c *memptr 为NULL。 参数 @c alignment 应该是sizeof(void*)的倍数且是2的幂, + * 否则立即返回错误, @c *memptr 为NULL。 释放内存通过 @ref cos_free 函数释放。 + * + * @param[out] memptr 返回申请到的内存首地址 + * @param[in] size 想申请内存的大小(单位:Byte) + * @param[in] alignment 内存地址对齐大小(单位:Byte) + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 内存管理未知错误 + */ +cos_status_t cos_malloc_align(void **memptr, size_t size, size_t alignment); + +/** + * @brief 释放内存 + * + * 释放由参数 @c ptr 指向的一块内存, @c ptr 应该是之前动态申请函数的返回值。 + * + * @param[in] ptr 动态申请到的内存首地址 + * + * @note 不可以在中断上下文中使用。 + * + * @return 无 + */ +void cos_free(void *ptr); + +/** + * @} + */ + +/** + * @addtogroup cos_mb + * 提供邮箱相关的标准定义 + * @{ + */ + +/** + * @brief 创建邮箱 + * + * 从系统堆内存中申请空间创建邮箱控制块 + * + * @param[in] name 邮箱名称 + * + * @param[in] size 邮箱容量 + * + * @param[in] flag 邮箱标志,如: COS_IPC_FLAG_FIFO 或 COS_IPC_FLAG_PRIO + * + * @param[out] mb 邮箱句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_mb_create(const char* name, size_t msg_size, + uint8_t flag, cos_mb_t *mb); + +/** + * @brief 创建邮箱 + * + * 删除邮箱对象并释放系统堆内存空间 + * + * + * @param[in] mb 邮箱句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_mb_delete(cos_mb_t mb); + +/** + * @brief 创建邮箱 + * + * 从系统堆内存中申请空间创建邮箱控制块 + * + * @param[in] name 邮箱名称 + * + * @param[in] msgpool 缓冲区指针 + * + * @param[in] msg_size 邮箱容量 + * + * @param[in] flag 邮箱工作方式 + * + * @param[out] mb 邮箱句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_mb_init(const char* name, size_t msg_size, + uint8_t flag, cos_mb_t *mb); + +/** + * @brief 去初始化邮箱 + * + * 去初始化一个邮箱 + * + * @param[in] mb 邮箱句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_mb_deinit(cos_mb_t mb); + +/** + * @brief 发送邮件 + * + * 发送邮件,当邮箱中的邮件已经满时,发送邮件的线程或者中断程序会收到 cos_ERROR_mb_FULL 的返回值。 + * + * @param[in] mb 邮箱句柄 + * + * @param[in] value 邮件内容 + * + * @note 无 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_mb_send(cos_mb_t mb, uint32_t value); + +/** + * @brief 接收邮件 + * + * 当邮箱中有邮件时,接收者才能接收邮件,否则接收者会根据超时时间设置,或挂起在邮箱的等待线程队列上,或直接返回。 + * + * @param[in] mb 邮箱句柄 + * + * @param[out] value 邮件内容 + * + * @param[in] timeout 指定的超时时间 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_mb_recv(cos_mb_t mb, uint32_t* value, + int32_t timeout); +/** + * @} + */ + +/** + * @addtogroup cos_mq + * 提供消息队列相关的标准定义 + * @{ + */ +/** + * @brief 创建消息队列 + * + * 从系统堆内存中申请空间创建消息队列控制块 + * + * @param[in] name 消息队列名称 + * + * @param[in] msg_size 单条消息的最大长 + * + * @param[in] max_msgs 消息队列的最大个数 + * + * @param[in] flag 消息队列工作方式,如: COS_IPC_FLAG_FIFO 或 COS_IPC_FLAG_PRIO + * + * @param[out] mq 消息队列句柄 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_mq_create(const char* name, size_t msg_size, + size_t max_msgs, uint8_t flag, cos_mq_t* mq); + +/** + * @brief 删除邮箱 + * + * 从系统堆内存中删除邮箱控制块 + * + * @param[out] mq 邮箱句柄 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_mq_delete(cos_mq_t mq); + +/** + * @brief 初始化消息队列 + * + * 初始化消息队列控制块 + * + * @param[in] name 消息队列名称 + * + * @param[in] msgpool 指向存放消息的缓冲区的指针 + * + * @param[in] msg_size 单条消息的最大长 + * + * @param[in] flag 消息队列工作方式,如: COS_IPC_FLAG_FIFO 或 COS_IPC_FLAG_PRIO + * + * @param[out] mq 消息队列句柄 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_mq_init(const char* name, void *msgpool, size_t msg_size, + size_t max_msgs, cos_mq_t mq); + +/** + * @brief 去初始化消息队列 + * + * 去初始化消息队列控制块 + * + * @param[out] mq 邮箱句柄 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_mq_delete(cos_mq_t mq); + +/** + * @brief 发送消息 + * + * 发送消息,线程或者中断服务程序都可以给消息队列发送消息。 + * + * @param[in] mq 消息队列的句柄 + * + * @param[in] value 消息内容 + * + * @param[in] size 消息大小 + * + * @note 无 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERROR_MQ_DELETED 线程挂起时消息队列已被删除 + * @retval COS_ERROR_MQ_FULL 消息队列已满 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_mq_send(cos_mq_t mq, void* buffer, size_t size); + +/** + * @brief 接收邮件 + * + * 当邮箱中有邮件时,接收者才能接收邮件,否则接收者会根据超时时间设置,或挂起在邮箱的等待线程队列上,或直接返回。 + * + * @param[in] mq 消息队列的句柄 + * + * @param[out] buffer 消息内容 + * + * @param[in] size 消息大小 + * + * @param[in] timeout 指定的超时时间 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERROR_MQ_DELETED 线程挂起时消息队列已被删除 + * @retval COS_ERROR_MQ_FULL 消息队列已满 + * @retval COS_ERR_ISR 在中断上下文中调用 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_mq_recv(cos_mq_t mq, void* buffer, + size_t size, int32_t timeout); + +/** + * @} + */ + +/** + * @addtogroup cos_wq + * 提供工作队列相关的标准定义 + * @{ + */ +/** + * @brief 初始化工作项 + * + * 初始化工作项,并绑定工作项的回调函数,以及自定义用户数据 + * + * @param[in] work 工作项 + * + * @param[in] work_func 工作项的回调函数 + * + * @param[in] work_data 用户自定义数据,回调函数参数 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_work_init(cos_work_t work, cos_work_handle_t work_func, void *work_data); + +/** + * @brief 删除邮箱 + * + * 从系统堆内存中删除邮箱控制块 + * + * @param[in] work 工作项 + * + * @param[in] time 提交延时,以 tick 为单位 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_work_dowork(cos_work_t work, cos_tick_t time); + +/** + * @brief 向工作队列提交工作项 + * + * 将 work 指向的工作项提交到 queue 指向的工作队列中,若 time 大于 0,则该提交延时 time 个 tick 之后执行。 + * + * @param[in] wq 工作队列 + * + * @param[in] work 工作项 + * + * @param[in] time 提交延时,以 tick 为单位 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_work_submit(cos_wq_t wq, cos_work_t work, cos_tick_t time); + +/** + * @brief 取消指定工作项 + * + * 从 queue 指向的工作队列中将 work 指向的工作项移除。 + * + * @param[in] queue 工作队列 + * + * @param[in] work 工作项 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_work_cancel(cos_wq_t wq, cos_work_t work); + +/** + * @brief 创建工作队列 + * + * 该函数创建并初始化一个工作队列,利用参数 name,stack_size 和 priority 创建工作队列内部线程,最终返回创建的工作队列。下表描述了该函数的输入参数与返回值 + * + * @param[out] wq 工作队列句柄 + * + * @param[in] name 线程名字 + * + * @param[in] stack_size 线程栈大小 + * + * @param[in] priority 线程优先级 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_workqueue_create(cos_wq_t* wq, const char *name, uint16_t stack_size, uint8_t priority); + +/** + * @brief 工作队列销毁函数 + * + * 销毁指定工作队列 + * + * @param[in] wq 工作队列句柄 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 未知错误 + */ +cos_status_t cos_workqueue_destroy(cos_wq_t wq); +/** + * @} + */ + +/** + * @addtogroup cos_mutex + * 提供互斥锁相关的标准定义 + * @{ + */ + +/** + * @brief 创建互斥锁 + * + * 创建一个互斥锁。 + * 该函数在创建时内部会动态分配需要的内存,如果不想使用动态内存,请使用 @ref cos_mutex_init 。 + * + * @param[out] timer 互斥锁句柄 + * @param[in] name 互斥锁名称 + * @param[in] options 互斥锁的选项,如开启嵌套锁支持 @ref COS_MUTEX_NEST + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR_ISR 在中断上下文中使用 + * @retval COS_ERR 互斥锁未知错误 + * + * @see cos_mutex_init + */ +cos_status_t cos_mutex_create(cos_mutex_t *mutex, char *name, uint32_t options); + +/** + * @brief 删除互斥锁 + * + * 删除一个互斥锁。系统会先唤醒所有等待该锁的任务,唤醒原因为互斥锁已被销毁,然后删除回收其资源。 + * + * @param[in] mutex 互斥锁句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 互斥锁未知错误 + */ +cos_status_t cos_mutex_delete(cos_mutex_t mutex); + +/** + * @brief 初始化互斥锁 + * + * 初始化一个互斥锁。 + * 与创建一个互斥锁类似,区别是互斥锁的控制块内存是由调用者传入的,一般用于需要静态内存的场景。 + * + * @param[in] mutex 互斥锁句柄 + * @param[in] name 互斥锁名称 + * @param[in] options 互斥锁的选项,如开启嵌套锁支持 @ref COS_MUTEX_NEST + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR_ISR 在中断上下文中使用 + * @retval COS_ERR 互斥锁未知错误 + * + * @see cos_mutex_create + */ +cos_status_t cos_mutex_init(cos_mutex_t mutex, char *name, uint32_t options); + +/** + * @brief 反初始化互斥锁 + * + * 反初始化一个互斥锁。系统会先唤醒所有等待该锁的任务,唤醒原因为互斥锁已被销毁。但不会释放 @c mutex 的内存空间,应由调用者释放。 + * + * @param[in] mutex 互斥锁句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR 互斥锁未知错误 + */ +cos_status_t cos_mutex_deinit(cos_mutex_t mutex); + +/** + * @brief 互斥锁上锁 + * + * @param[in] mutex 互斥锁句柄 + * @param[in] timeout 等待互斥锁的超时时间,单位:tick个数 + * + * @note 不可以在中断上下文中使用。 + * + * @retval COS_ERR 互斥锁未知错误 + * @retval COS_ERR_TIMEOUT 申请上锁超时 + * @retval COS_ERR_DESTROY 互斥锁已被销毁 + * @retval COS_ERR_MUTEX_NESTING 未开启嵌套锁的情况下,进行嵌套申请 + */ +cos_status_t cos_mutex_lock(cos_mutex_t mutex, cos_tick_t timeout); + +/** + * @brief 互斥锁解锁 + * + * @param[in] mutex 互斥锁句柄 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR 互斥锁未知错误 + * @retval COS_ERR_DESTROY 互斥锁已被销毁 + * @retval COS_ERR_MUTEX_NOT_OWNER 未持有该互斥锁 + */ +cos_status_t cos_mutex_unlock(cos_mutex_t mutex); + +/** + * @} + */ + +/** + * @addtogroup cos_sem + * 提供信号量相关的标准定义 + * @{ + */ + +/** + * @brief 创建信号量 + * + * 创建一个信号量。 + * 该函数在创建时内部会动态分配需要的内存,如果不想使用动态内存,请使用 @ref cos_sem_init 。 + * + * @param[out] sem 信号量句柄 + * @param[in] name 信号量名称 + * @param[in] init_count 信号量初始值 + * @param[in] max_count 信号量最大值。如果无最大值限制可使用 @ref COS_SEM_NO_MAX。 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR 信号量未知错误 + * + * @see cos_sem_init + */ +cos_status_t cos_sem_create(cos_sem_t *sem, char *name, uint32_t init_count, uint32_t max_count); + +/** + * @brief 删除信号量 + * + * 删除一个信号量。系统会先唤醒所有等待该信号量的任务,唤醒原因为信号量已被销毁,然后删除回收其资源。 + * + * @param[in] sem 信号量句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 信号量未知错误 + */ +cos_status_t cos_sem_delete(cos_sem_t sem); + +/** + * @brief 初始化信号量 + * + * 初始化一个信号量。 + * 与创建一个信号量类似,区别是信号量的控制块内存是由调用者传入的,一般用于需要静态内存的场景。 + * + * @param[in] sem 信号量句柄 + * @param[in] name 信号量名称 + * @param[in] init_count 信号量初始值 + * @param[in] max_count 信号量最大值。如果无最大值限制可使用 @ref COS_SEM_NO_MAX。 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR 信号量未知错误 + * + * @see cos_sem_create + */ +cos_status_t cos_sem_init(cos_sem_t sem, char *name, uint32_t init_count, uint32_t max_count); + +/** + * @brief 反初始化信号量 + * + * 反初始化一个信号量。系统会先唤醒所有等待该信号量的任务,唤醒原因为信号量已被销毁。但不会释放 @c sem 的内存空间,应由调用者释放。 + * + * @param[in] sem 信号量句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 信号量未知错误 + */ +cos_status_t cos_sem_deinit(cos_sem_t sem); + +/** + * @brief 等待信号量 + * + * @param[in] sem 信号量句柄 + * @param[in] timeout 等待信号量的超时时间,单位:tick个数 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 信号量未知错误 + * @retval COS_ERR_TIMEOUT 申请超时 + * @retval COS_ERR_DESTROY 信号量已被销毁 + */ +cos_status_t cos_sem_wait(cos_sem_t sem, cos_tick_t timeout); + +/** + * @brief 释放信号量 + * + * 释放一个信号量。系统唤醒等待该信号量的最高优先级任务。 + * + * @param[in] sem 信号量句柄 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR 信号量未知错误 + * @retval COS_ERR_DESTROY 信号量已被销毁 + */ +cos_status_t cos_sem_release(cos_sem_t sem); + +/** + * @brief 释放信号量,唤醒所有等待任务 + * + * 释放一个信号量。系统唤醒等待该信号量的所有任务。 + * + * @param[in] sem 信号量句柄 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR 信号量未知错误 + * @retval COS_ERR_DESTROY 信号量已被销毁 + */ +cos_status_t cos_sem_release_all(cos_sem_t sem); + +/** + * @} + */ + +/** + * @addtogroup cos_event + * 提供事件相关的标准定义 + * @{ + */ +/** + * @brief 创建事件 + * + * 创建一个事件。 + * 该函数在创建时内部会动态分配需要的内存,如果不想使用动态内存,请使用 @ref cos_sem_init 。 + * + * @param[out] event 事件句柄 + * @param[in] name 事件名称 + * @param[in] init_flag 事件初始标志 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR 事件未知错误 + * + * @see cos_event_init + */ +cos_status_t cos_event_create(cos_event_t *event, char *name, cos_event_flag_t init_flag); + +/** + * @brief 删除事件 + * + * 删除一个事件。系统会先唤醒所有等待该事件的任务,唤醒原因为事件已被销毁,然后删除回收其资源。 + * + * @param[in] event 事件句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 事件未知错误 + */ +cos_status_t cos_event_delete(cos_event_t event); + +/** + * @brief 初始化事件 + * + * 初始化一个事件。 + * 与创建一个事件类似,区别是事件的控制块内存是由调用者传入的,一般用于需要静态内存的场景。 + * + * @param[out] event 事件句柄 + * @param[in] name 事件名称 + * @param[in] init_flag 事件初始标志 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR_NOMEM 内存不足 + * @retval COS_ERR 事件未知错误 + * + * @see cos_event_create + */ +cos_status_t cos_event_init(cos_event_t event, char *name, cos_event_flag_t init_flag); + +/** + * @brief 反初始化事件 + * + * 反初始化一个事件。系统会先唤醒所有等待该事件的任务,唤醒原因为事件已被销毁。但不会释放 @c event 的内存空间,应由调用者释放。 + * + * @param[in] event 事件句柄 + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 事件未知错误 + */ +cos_status_t cos_event_deinit(cos_event_t event); + +/** + * @brief 等待事件 + * + * @param[in] event 事件句柄 + * @param[in] expect_flag 期望期待的事件标志 + * @param[in] timeout 等待事件的超时时间,单位:tick个数 + * @param[out] match_flag 等待到的事件结果 + * @param[in] options 等待事件的选项,如等待一个事件即可 @ref COS_EVENT_WAIT_ANY, + * 等待全部事件 @ref COS_EVENT_WAIT_ALL, 等待到事件后清除该事件 @ref COS_EVENT_WAIT_CLR + * + * @note 不可以在中断上下文中使用。 + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR_PARAM 传入的参数非法 + * @retval COS_ERR 事件未知错误 + * @retval COS_ERR_TIMEOUT 等待超时 + * @retval COS_ERR_DESTROY 事件已被销毁 + */ +cos_status_t cos_event_wait(cos_event_t event, cos_event_flag_t expect_flag, cos_tick_t timeout, cos_event_flag_t *match_flag, uint32_t options); + +/** + * @brief 释放事件 + * + * @param[in] event 事件句柄 + * @param[in] flag 要释放的事件标志 + * @param[in] options 释放事件的选项,如释放后是否保留该事件 @ref COS_EVENT_RELEASE_KEEP + * + * @return COS_OK: 成功, <0: 失败 + * @retval COS_ERR 事件未知错误 + * @retval COS_ERR_DESTROY 事件已被销毁 + */ +cos_status_t cos_event_release(cos_event_t event, cos_event_flag_t flag, uint32_t options); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /*_COSIT_H*/ \ No newline at end of file diff --git a/osal/cosit/include/cosit_port_types.h b/osal/cosit/include/cosit_port_types.h new file mode 100644 index 0000000..3fe1485 --- /dev/null +++ b/osal/cosit/include/cosit_port_types.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) XXXX + * + * SPDX-License-Identifier: XXXX + * + * Version: 1.0.0 + * + * Change log: + * + * Date Author Note + * 2022-07-12 all first version + */ + +#ifndef _COSIT_PORT_TYPES_H +#define _COSIT_PORT_TYPES_H + +//#define COS_USE_AOS /* AliOS Things */ +//#define COS_USE_TOS /* TencentOS-tiny */ +//#define COS_USE_RTTHREAD /* RT-Thread */ + +/* 提供统一的内核对象类型名称,为调用静态内存API前先开辟合适大小的内存,同时屏蔽底层OS的差异, + * 静态内存API:cos_task_init, cos_timer_init, cos_mb_init, cos_mq_init, cos_work_init, + * cos_mutex_init, cos_sem_init, cos_event_init。 + * 只有当用户使用静态内存类型的API时需要此头文件, 否则不需要。 + * + * 使用举例: + * cos_sem_real_t test_sem; + * ret = cos_sem_init((cos_sem_t)&test_sem, "test_sem", 1, 0xffff); + * cos_sem_wait((cos_sem_t)&test_sem, COS_WAIT_FOREVER); + */ + +#ifdef COS_USE_AOS +#include +/* 提供底层OS内核对象的真实数据类型, 用于 */ +typedef ktask_t cos_task_real_t; +typedef ktimer_t cos_timer_real_t; +typedef kmutex_t cos_mutex_real_t; +typedef ksem_t cos_sem_real_t; +typedef kevent_t cos_event_real_t; +typedef kbuf_queue_t cos_mq_real_t; +typedef kqueue_t cos_mb_real_t; +typedef kwork_t cos_work_real_t; +typedef kworkqueue_t cos_wq_real_t; + +#elif defined (COS_USE_TOS) +#include "tos_k.h" +typedef k_task_t cos_task_real_t; +typedef k_timer_t cos_timer_real_t; +typedef k_mutex_t cos_mutex_real_t; +typedef k_sem_t cos_sem_real_t; +typedef k_event_t cos_event_real_t; +typedef k_mail_q_t cos_mq_real_t; +typedef k_msg_q_t cos_mb_real_t; + +#elif defined (COS_USE_RTTHREAD) + +#else + +#warning "The underlying OS is not defined, so you should take care of the real type of kernel object." + +#endif + +#endif /*_COSIT_PORT_TYPES_H*/ diff --git a/osal/cosit/meeting/2022-05-07.md b/osal/cosit/meeting/2022-05-07.md new file mode 100644 index 0000000..d371456 --- /dev/null +++ b/osal/cosit/meeting/2022-05-07.md @@ -0,0 +1,15 @@ +# 5月7日会议结论 + +1、API前缀统一为COS + - 对应更新API前缀 @RTT + +2、消息邮箱API,5月11日前提交 @RTT + +3、头文件,5月11日在群里讨论确定 @阿里、腾讯、RTT、展锐 + +4、create/delete, init/deinit:分开两对写, @阿里、腾讯、RTT + +5、通用错误码,不带上模块名,各个Owner分别更新API @阿里、腾讯、RTT + + + **下次会议:5月31日,对焦实现进展** \ No newline at end of file diff --git a/osal/cosit/meeting/2022-05-31.md b/osal/cosit/meeting/2022-05-31.md new file mode 100644 index 0000000..f260015 --- /dev/null +++ b/osal/cosit/meeting/2022-05-31.md @@ -0,0 +1,24 @@ +# 5月31日会议结论 + +进展 +1. 腾讯实现了任务管理;下次会议可以完成 +2. RTT实现了消息队列、邮箱;下次会议可以完成 + + +Issue: +1. 在头文件里补充错误码 @阿里 +2. COS_EVENT_RELEASE_KEEP,腾讯check下是否说明了该API的功能@腾讯 +3. 所有静态的API,如何隐藏底层具体OS内核的数据类型(例如cos_mutex_init/cos_sem_init等),调研其他标准如何实现@阿里 + + +会上讨论的技术结论: +1. 内核对象的参数均为第一个参数,大家保持一致。 比如cos_mq_create/cos_mq_init的 mq参数,cos_mb_create/cos_mb_init的mb参数。 +2. timeout参数统一使用cos_tick_t类型,代表tick。 +3. 不支持的一些API的子特性, 统一返回错误码COS_ERR_NOTSUPPORT。 + + +其他 +1. 测试套件—等API适配完成后,确定API没问题再进行 + + + **下次会议:6月14日** \ No newline at end of file diff --git a/osal/cosit/meeting/2022-06-14.md b/osal/cosit/meeting/2022-06-14.md new file mode 100644 index 0000000..fd4ec6d --- /dev/null +++ b/osal/cosit/meeting/2022-06-14.md @@ -0,0 +1,14 @@ +# 6月14日会议结论 + +进展 +1. 腾讯:基本已经完成 +2. RTT:部分完成 + +结论: +1. 后续迁移到基金会的代码库,初期先fork一份,Gitee的仍保留 +2. 目标:7月底基金会峰会,发布标准及相关demo。 +3. 腾讯计划在3-4款开发板实现,RTT计划在1款开发板实现,阿里1款开发板实现。 +4. 测试套采用greatest.h开源框架 + + + **下次会议:6月28日** \ No newline at end of file diff --git a/osal/cosit/meeting/2022-06-28.md b/osal/cosit/meeting/2022-06-28.md new file mode 100644 index 0000000..94cef52 --- /dev/null +++ b/osal/cosit/meeting/2022-06-28.md @@ -0,0 +1,12 @@ +# 6月28日会议结论 + +进展 +1. 腾讯:消息邮箱、消息队列,需要等API确认完后再实现;正在写测试例 +2. RTT:还缺工作队列 +3. 阿里:都实现了 + +结论: +1. 静态初始化,隔离底层OS。建议方式:定义统一的宏,用户来开buffer。新开一个头文件,feature.h @李进良 +2. 新增cos_sem_t、获取tick值、信号量唤醒release-all的issue,跟踪讨论@腾讯 + + **下次会议:7月12日** \ No newline at end of file diff --git a/osal/cosit/meeting/2022-07-12.md b/osal/cosit/meeting/2022-07-12.md new file mode 100644 index 0000000..f169a62 --- /dev/null +++ b/osal/cosit/meeting/2022-07-12.md @@ -0,0 +1,13 @@ +# 7月12日会议结论 + +7月26日发布方案: +1. 各家确定届时参会的同事名单@腾讯、RTT、展锐 +2. 发布视频画面,有发布特效;最终显示“《物联网操作系统内核接口规范 1.0》正式发布” +3. 补充各家的标准实现情况@腾讯、RTT + + +结论: +1. 数量类型抽象(https://gitee.com/IoTOSSWG-atom/cosit/issues/I5EYUC?from=project-issue)@尽良 发起PR进行更新 +2. 补充消息队列工作方式 COS_IPC_FLAG_FIFO、 COS_IPC_FLAG_PRIO这两个常量的定义 @RTT + + **下次会议:峰会之后确定** \ No newline at end of file diff --git a/osal/cosit/rtos/TobudOS/cosit.c b/osal/cosit/rtos/TobudOS/cosit.c new file mode 100644 index 0000000..e6a6630 --- /dev/null +++ b/osal/cosit/rtos/TobudOS/cosit.c @@ -0,0 +1,779 @@ +#include "cosit.h" +#include + +static cos_status_t errno_knl2cosit(k_err_t err) +{ + switch (err) { + case K_ERR_NONE: return COS_OK; + case K_ERR_IN_IRQ: return COS_ERR_ISR; + default: return COS_ERR; + } +} + +cos_tick_t cos_ms_to_tick(uint64_t ms) +{ + return tos_millisec2tick((k_time_t)ms); +} + +uint64_t cos_tick_to_ms(cos_tick_t tick) +{ + return tos_tick2millisec((k_tick_t)tick); +} + +void cos_task_attr_init(cos_task_attr_t *attr) +{ + if (!attr) { + return; + } + + attr->name = "default_task"; + attr->stack_size = 512; + attr->priority = 5; + attr->tick = 10; +} + +cos_status_t cos_task_create(cos_task_t *task, cos_task_entry_t fn, void *arg, cos_task_attr_t *attr) +{ + k_err_t err; + k_stack_t *task_stack; + + if (!attr) { + return COS_ERR_PARAM; + } + +#if TOS_CFG_TASK_DYNAMIC_CREATE_EN > 0u + err = tos_task_create_dyn((k_task_t **)task, attr->name, (k_task_entry_t)fn, arg, + (k_prio_t)attr->priority, attr->stack_size, + (k_timeslice_t)attr->tick); + + return errno_knl2cosit(err); +#endif + + *task = (cos_task_t)tos_mmheap_alloc(sizeof(k_task_t)); + if (!task) { + return COS_ERR_NOMEM; + } + + task_stack = (k_stack_t *)tos_mmheap_alloc(attr->stack_size); + if (!task_stack) { + tos_mmheap_free(task); + return COS_ERR_NOMEM; + } + + err = tos_task_create((k_task_t *)*task, attr->name, (k_task_entry_t)fn, arg, + (k_prio_t)attr->priority, task_stack, attr->stack_size, + (k_timeslice_t)attr->tick); + + if (err != K_ERR_NONE) { + tos_mmheap_free(task); + tos_mmheap_free(task_stack); + } + + return errno_knl2cosit(err); +} + +cos_status_t cos_task_delete(cos_task_t task) +{ + k_err_t err; + k_task_t *the_task = (k_task_t *)task; + +#if TOS_CFG_TASK_DYNAMIC_CREATE_EN > 0u + err = tos_task_destroy_dyn(the_task); + return errno_knl2cosit(err); +#endif + + err = tos_task_destroy(the_task); + tos_mmheap_free(the_task->stk_base); + tos_mmheap_free(task); + + return errno_knl2cosit(err); +} + +cos_status_t cos_task_init(cos_task_t task, cos_task_entry_t fn, void *arg, + cos_task_attr_t *attr, void *stack_buf) +{ + k_err_t err; + + err = tos_task_create((k_task_t *)task, attr->name, (k_task_entry_t)fn, + arg, attr->priority, (k_stack_t *)stack_buf, + attr->stack_size, attr->tick); + + return errno_knl2cosit(err); +} + +cos_status_t cos_task_deinit(cos_task_t task) +{ + return errno_knl2cosit(tos_task_destroy((k_task_t *)task)); +} + +void cos_task_exit(int32_t status) +{ + tos_task_destroy(K_NULL); +} + +cos_status_t cos_task_suspend(cos_task_t task) +{ + return errno_knl2cosit(tos_task_suspend((k_task_t *)task)); +} + +cos_status_t cos_task_resume(cos_task_t task) +{ + return errno_knl2cosit(tos_task_resume((k_task_t *)task)); +} + +cos_status_t cos_task_yield(void) +{ + tos_task_yield(); + + return COS_OK; +} + +cos_task_t cos_task_self(void) +{ + return (cos_task_t)tos_task_curr_task_get(); +} + +cos_status_t cos_task_get_name(cos_task_t task, char *buf, size_t buf_size) +{ + k_task_t *the_task = task; + + if (!the_task || !buf) { + return COS_ERR_PARAM; + } + + strncpy(the_task->name, buf, buf_size); + + return COS_OK; +} + +cos_task_t cos_task_find(const char *name) +{ + return (cos_task_t)tos_task_find(name); +} + +cos_status_t cos_task_sleep(cos_tick_t tick) +{ + return errno_knl2cosit(tos_task_delay((k_tick_t)tick)); +} + +cos_status_t cos_task_wakeup(cos_task_t task) +{ + //todo + return COS_ERR; +} + +cos_status_t cos_task_get_priority(cos_task_t task, uint8_t *priority) +{ + k_task_t *the_task = task; + + if (!the_task || !priority) { + return COS_ERR_PARAM; + } + + *priority = the_task->prio; + + return COS_OK; +} + +cos_status_t cos_task_set_priority(cos_task_t task, uint8_t priority) +{ + return tos_task_prio_change((k_task_t *)task, (k_prio_t)priority); +} + +#if TOS_CFG_ROUND_ROBIN_EN > 0u + +cos_status_t cos_task_get_time_slice(cos_task_t task, cos_tick_t *time_slice) +{ + + k_task_t *the_task = task; + + if (!the_task || !time_slice) { + return COS_ERR_PARAM; + } + + *time_slice = the_task->timeslice; + + return COS_OK; +} + +cos_status_t cos_task_set_time_slice(cos_task_t task, cos_tick_t time_slice) +{ + tos_robin_timeslice_set((k_task_t *)task, (k_timeslice_t)time_slice); + return COS_OK; +} + +#else + +cos_status_t cos_task_get_time_slice(cos_task_t task, cos_tick_t *time_slice) +{ + return COS_ERR; +} + +cos_status_t cos_task_set_time_slice(cos_task_t task, cos_tick_t time_slice) +{ + return COS_ERR; +} + +#endif /* TOS_CFG_ROUND_ROBIN_EN */ + +#if TOS_CFG_TIMER_EN > 0u + +cos_status_t cos_timer_create(cos_timer_t *timer, const char *name, cos_timer_cb_t cb, + void *arg, cos_tick_t initial, cos_tick_t period, uint32_t options) +{ + k_err_t err; + k_timer_t **the_timer = (k_timer_t **)timer; + + err = tos_timer_create_dyn(the_timer, (k_tick_t)initial, (k_tick_t)period, + (k_timer_callback_t)cb, arg, TOS_OPT_TIMER_PERIODIC); + + if (err != K_ERR_NONE) { + return errno_knl2cosit(err); + } + + if (options == COS_TIMER_OPTION_ACTIVATE) { + err = tos_timer_start(*the_timer); + return errno_knl2cosit(err); + } + + return COS_OK; +} + +cos_status_t cos_timer_delete(cos_timer_t timer) +{ + return errno_knl2cosit(tos_timer_destroy_dyn((k_timer_t *)timer)); +} + +cos_status_t cos_timer_init(cos_timer_t timer, const char *name, cos_timer_cb_t cb, + void *arg, cos_tick_t initial, cos_tick_t period, uint32_t options) +{ + k_err_t err; + k_timer_t *the_timer = (k_timer_t *)timer; + + err = tos_timer_create(the_timer, (k_tick_t)initial, (k_tick_t)period, + (k_timer_callback_t)cb, arg, TOS_OPT_TIMER_PERIODIC); + + if (err != K_ERR_NONE) { + return errno_knl2cosit(err); + } + + if (options == COS_TIMER_OPTION_ACTIVATE) { + err = tos_timer_start(the_timer); + return errno_knl2cosit(err); + } + + return COS_OK; +} + +cos_status_t cos_timer_deinit(cos_timer_t timer) +{ + return errno_knl2cosit(tos_timer_destroy((k_timer_t *)timer)); +} + +cos_status_t cos_timer_start(cos_timer_t timer) +{ + return errno_knl2cosit(tos_timer_start((k_timer_t *)timer)); +} + +cos_status_t cos_timer_stop(cos_timer_t timer) +{ + return errno_knl2cosit(tos_timer_stop((k_timer_t *)timer)); +} + +cos_status_t cos_timer_change(cos_timer_t timer, cos_tick_t initial, cos_tick_t period) +{ + k_err_t err; + k_timer_t *the_timer = (k_timer_t *)timer; + + err = tos_timer_delay_change(the_timer, (k_tick_t)initial); + if (err != K_ERR_NONE) { + return errno_knl2cosit(err); + } + + return errno_knl2cosit(tos_timer_period_change(the_timer, (k_tick_t)period)); +} + +cos_status_t cos_timer_get_time(cos_timer_t timer, cos_tick_t *remaining, cos_tick_t *period) +{ + //todo + return COS_ERR; +} + +#else + +cos_status_t cos_timer_create(cos_timer_t *timer, const char *name, cos_timer_cb_t cb, + void *arg, cos_tick_t initial, cos_tick_t period, uint32_t options) +{ + return COS_ERR; +} + +cos_status_t cos_timer_delete(cos_timer_t timer) +{ + return COS_ERR; +} + +cos_status_t cos_timer_init(cos_timer_t timer, const char *name, cos_timer_cb_t cb, + void *arg, cos_tick_t initial, cos_tick_t period, uint32_t options) +{ + return COS_ERR; +} + +cos_status_t cos_timer_deinit(cos_timer_t timer) +{ + return COS_ERR; +} + +cos_status_t cos_timer_start(cos_timer_t timer) +{ + return COS_ERR; +} + +cos_status_t cos_timer_stop(cos_timer_t timer) +{ + return COS_ERR; +} + +cos_status_t cos_timer_change(cos_timer_t timer, cos_tick_t initial, cos_tick_t period) +{ + return COS_ERR; +} + +cos_status_t cos_timer_get_time(cos_timer_t timer, cos_tick_t *remaining, cos_tick_t *period) +{ + return COS_ERR; +} + +#endif /* TOS_CFG_TIMER_EN */ + +void *cos_malloc(size_t size) +{ + return tos_mmheap_alloc(size); +} + +void *cos_realloc(void *ptr, size_t new_size) +{ + return tos_mmheap_realloc(ptr, new_size); +} + +void *cos_calloc(size_t count, size_t size) +{ + return tos_mmheap_calloc(count, size); +} + +cos_status_t cos_malloc_align(void **memptr, size_t size, size_t alignment) +{ +#if TOS_CFG_MMHEAP_EN > 0u + + if (!memptr) { + return COS_ERR_PARAM; + } + + *memptr = tos_mmheap_aligned_alloc(size, alignment); + + return *memptr == NULL ? COS_ERR : COS_OK; + +#else + return COS_ERR; +#endif +} + +void cos_free(void *ptr) +{ + tos_mmheap_free(ptr); +} + +#if TOS_CFG_MESSAGE_QUEUE_EN > 0u + +cos_status_t cos_mb_create(const char* name, size_t msg_size, + uint8_t flag, cos_mb_t *mb) +{ + +} + +cos_status_t cos_mb_delete(cos_mb_t mb) +{ + +} + +cos_status_t cos_mb_init(const char* name, size_t msg_size, + uint8_t flag, cos_mb_t *mb) +{ + //todo + //tosҪûpool +} + +cos_status_t cos_mb_deinit(cos_mb_t mb) +{ + +} + +cos_status_t cos_mb_send(cos_mb_t mb, uint32_t value) +{ + return errno_knl2cosit(tos_msg_q_post((k_msg_q_t *)mb, (void *)value)); +} + +cos_status_t cos_mb_recv(cos_mb_t mb, uint32_t* value, + int32_t timeout) +{ + //note: valueҪûָ룬ǷӦʹöָʾ + return errno_knl2cosit(tos_msg_q_pend((k_msg_q_t *)mb, (void**)value, (k_tick_t)timeout)); +} + +#else + +cos_status_t cos_mb_create(const char* name, size_t msg_size, + uint8_t flag, cos_mb_t *mb) +{ + +} + +cos_status_t cos_mb_delete(cos_mb_t mb) +{ + +} + +cos_status_t cos_mb_init(const char* name, size_t msg_size, + uint8_t flag, cos_mb_t *mb) +{ + +} + +cos_status_t cos_mb_deinit(cos_mb_t mb) +{ + +} + +cos_status_t cos_mb_send(cos_mb_t mb, uint32_t value) +{ + +} + +cos_status_t cos_mb_recv(cos_mb_t mb, uint32_t* value, + int32_t timeout) +{ + +} + +#endif + +#if TOS_CFG_MAIL_QUEUE_EN > 0u + +cos_status_t cos_mq_create(const char* name, size_t msg_size, + size_t max_msgs, uint8_t flag, cos_mq_t* mq) +{ + +} + +cos_status_t cos_mq_delete(cos_mq_t mq) +{ + +} + +cos_status_t cos_mq_init(const char* name, void *msgpool, size_t msg_size, + size_t max_msgs, cos_mq_t mq) +{ + return errno_knl2cosit(tos_mail_q_create((k_mail_q_t *)mq, msgpool, max_msgs, msg_size)); +} + +cos_status_t cos_mq_deinit(cos_mq_t mq) +{ + return errno_knl2cosit(tos_mail_q_destroy((k_mail_q_t *)mq)); +} + +cos_status_t cos_mq_send(cos_mq_t mq, void* buffer, size_t size) +{ + return errno_knl2cosit(tos_mail_q_post((k_mail_q_t *)mq, buffer, size)); +} + +cos_status_t cos_mq_recv(cos_mq_t mq, void* buffer, + size_t size, int32_t timeout) +{ + //todo + // tossizedzΣݽյݴСCOSIT APIΣʾС. + size_t recv_size; + + return errno_knl2cosit(tos_mail_q_pend((k_mail_q_t *)mq, buffer, &recv_size, (k_tick_t)timeout)); +} + +#else + +cos_status_t cos_mq_create(const char* name, size_t msg_size, + size_t max_msgs, uint8_t flag, cos_mq_t* mq) +{ + return COS_ERR; +} + +cos_status_t cos_mq_delete(cos_mq_t mq) +{ + return COS_ERR; +} + +cos_status_t cos_mq_init(const char* name, void *msgpool, size_t msg_size, + size_t max_msgs, cos_mq_t mq) +{ + return COS_ERR; +} + +cos_status_t cos_mq_deinit(cos_mq_t mq) +{ + return COS_ERR; +} + +cos_status_t cos_mq_send(cos_mq_t mq, void* buffer, size_t size) +{ + return COS_ERR; +} + +cos_status_t cos_mq_recv(cos_mq_t mq, void* buffer, + size_t size, int32_t timeout) +{ + return COS_ERR; +} + +#endif + +cos_status_t cos_work_init(cos_work_t work, cos_work_handle_t work_func, void *work_data) +{ + return COS_ERR; +} + +cos_status_t cos_work_dowork(cos_work_t work, cos_tick_t time) +{ + return COS_ERR; +} + +cos_status_t cos_work_submit(cos_wq_t wq, cos_work_t work, cos_tick_t time) +{ + return COS_ERR; +} + +cos_status_t cos_work_cancel(cos_wq_t wq, cos_work_t work) +{ + return COS_ERR; +} + +cos_status_t cos_workqueue_create(cos_wq_t* wq, const char *name, uint16_t stack_size, uint8_t priority) +{ + return COS_ERR; +} + +cos_status_t cos_workqueue_destroy(cos_wq_t wq) +{ + return COS_ERR; +} + +#if TOS_CFG_MUTEX_EN > 0u + +cos_status_t cos_mutex_create(cos_mutex_t *mutex, char *name, uint32_t options) +{ + return errno_knl2cosit(tos_mutex_create_dyn((k_mutex_t **)mutex)); +} + +cos_status_t cos_mutex_delete(cos_mutex_t mutex) +{ + return errno_knl2cosit(tos_mutex_destroy_dyn((k_mutex_t *)mutex)); +} + +cos_status_t cos_mutex_init(cos_mutex_t mutex, char *name, uint32_t options) +{ + return errno_knl2cosit(tos_mutex_create((k_mutex_t *)mutex)); +} + +cos_status_t cos_mutex_deinit(cos_mutex_t mutex) +{ + return errno_knl2cosit(tos_mutex_destroy((k_mutex_t *)mutex)); +} + +cos_status_t cos_mutex_lock(cos_mutex_t mutex, cos_tick_t timeout) +{ + return errno_knl2cosit(tos_mutex_pend_timed((k_mutex_t *)mutex, (k_tick_t)timeout)); +} + +cos_status_t cos_mutex_unlock(cos_mutex_t mutex) +{ + return errno_knl2cosit(tos_mutex_post((k_mutex_t *)mutex)); +} + +#else + +cos_status_t cos_mutex_create(cos_mutex_t *mutex, char *name, uint32_t options) +{ + return COS_ERR; +} + +cos_status_t cos_mutex_delete(cos_mutex_t mutex) +{ + return COS_ERR; +} + +cos_status_t cos_mutex_init(cos_mutex_t mutex, char *name, uint32_t options) +{ + return COS_ERR; +} + +cos_status_t cos_mutex_deinit(cos_mutex_t mutex) +{ + return COS_ERR; +} + +cos_status_t cos_mutex_lock(cos_mutex_t mutex, cos_tick_t timeout) +{ + return COS_ERR; +} + +cos_status_t cos_mutex_unlock(cos_mutex_t mutex) +{ + return COS_ERR; +} + +#endif /* TOS_CFG_MUTEX_EN */ + +#if TOS_CFG_SEM_EN > 0u + +cos_status_t cos_sem_create(cos_sem_t *sem, char *name, uint32_t init_count, uint32_t max_count) +{ + return errno_knl2cosit(tos_sem_create_max_dyn((k_sem_t **)sem, (k_sem_cnt_t)init_count, (k_sem_cnt_t)max_count)); +} + +cos_status_t cos_sem_delete(cos_sem_t sem) +{ + return errno_knl2cosit(tos_sem_destroy_dyn((k_sem_t *)sem)); +} + +cos_status_t cos_sem_init(cos_sem_t sem, char *name, uint32_t init_count, uint32_t max_count) +{ + return errno_knl2cosit(tos_sem_create_max((k_sem_t *)sem, (k_sem_cnt_t)init_count, (k_sem_cnt_t)max_count)); +} + +cos_status_t cos_sem_deinit(cos_sem_t sem) +{ + return errno_knl2cosit(tos_sem_destroy((k_sem_t *)sem)); +} + +cos_status_t cos_sem_wait(cos_sem_t *sem, cos_tick_t timeout) +{ + return errno_knl2cosit(tos_sem_pend((k_sem_t *)sem, (k_tick_t)timeout)); +} + +cos_status_t cos_sem_release(cos_sem_t sem) +{ + return errno_knl2cosit(tos_sem_post((k_sem_t *)sem)); +} + +#else + +cos_status_t cos_sem_create(cos_sem_t *sem, char *name, uint32_t init_count, uint32_t max_count) +{ + return COS_ERR; +} + +cos_status_t cos_sem_delete(cos_sem_t sem) +{ + return COS_ERR; +} + +cos_status_t cos_sem_init(cos_sem_t sem, char *name, uint32_t init_count, uint32_t max_count) +{ + return COS_ERR; +} + +cos_status_t cos_sem_deinit(cos_sem_t sem) +{ + return COS_ERR; +} + +cos_status_t cos_sem_wait(cos_sem_t *sem, cos_tick_t timeout) +{ + return COS_ERR; +} + +cos_status_t cos_sem_release(cos_sem_t sem) +{ + return COS_ERR; +} + +#endif /* TOS_CFG_SEM_EN */ + +#if TOS_CFG_EVENT_EN > 0 + +cos_status_t cos_event_create(cos_event_t *event, char *name, cos_event_flag_t init_flag) +{ + return errno_knl2cosit(tos_event_create_dyn((k_event_t **)event, (k_event_flag_t)init_flag)); +} + +cos_status_t cos_event_delete(cos_event_t event) +{ + return errno_knl2cosit(tos_event_destroy_dyn((k_event_t *)event)); +} + +cos_status_t cos_event_init(cos_event_t event, char *name, cos_event_flag_t init_flag) +{ + return errno_knl2cosit(tos_event_create((k_event_t *)event, (k_event_flag_t)init_flag)); +} + +cos_status_t cos_event_deinit(cos_event_t event) +{ + return errno_knl2cosit(tos_event_destroy((k_event_t *)event)); +} + +cos_status_t cos_event_wait(cos_event_t event, cos_event_flag_t expect_flag, cos_tick_t timeout, cos_event_flag_t *match_flag, uint32_t options) +{ + k_opt_t opt; + + if (options == COS_EVENT_WAIT_ANY) { + opt |= TOS_OPT_EVENT_PEND_ANY; + } else if (options == COS_EVENT_WAIT_ALL) { + opt |= TOS_OPT_EVENT_PEND_ALL; + } + + if (options == COS_EVENT_WAIT_CLR) { + opt |= TOS_OPT_EVENT_PEND_CLR; + } + + return errno_knl2cosit(tos_event_pend((k_event_t *)event, + (k_event_flag_t)expect_flag, (k_event_flag_t *)match_flag, + (k_tick_t)timeout, opt)); +} + +cos_status_t cos_event_release(cos_event_t event, cos_event_flag_t flag, uint32_t options) +{ + if (options == COS_EVENT_RELEASE_KEEP) { + return errno_knl2cosit(tos_event_post_keep((k_event_t *)event, (k_event_flag_t)flag)); + } else { + return errno_knl2cosit(tos_event_post((k_event_t *)event, (k_event_flag_t)flag)); + } +} + +#else + +cos_status_t cos_event_create(cos_event_t *event, char *name, cos_event_flag_t init_flag) +{ + return COS_ERR; +} + +cos_status_t cos_event_delete(cos_event_t event) +{ + return COS_ERR; +} + +cos_status_t cos_event_init(cos_event_t event, char *name, cos_event_flag_t init_flag) +{ + return COS_ERR; +} + +cos_status_t cos_event_deinit(cos_event_t event) +{ + return COS_ERR; +} + +cos_status_t cos_event_wait(cos_event_t event, cos_event_flag_t expect_flag, cos_tick_t timeout, cos_event_flag_t *match_flag, uint32_t options) +{ + return COS_ERR; +} + +cos_status_t cos_event_release(cos_event_t event, cos_event_flag_t flag, uint32_t options) +{ + return COS_ERR; +} + +#endif \ No newline at end of file diff --git a/osal/cosit/test/greatest/LICENSE b/osal/cosit/test/greatest/LICENSE new file mode 100644 index 0000000..2fdef96 --- /dev/null +++ b/osal/cosit/test/greatest/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2011-2018 Scott Vokes + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/osal/cosit/test/greatest/greatest.h b/osal/cosit/test/greatest/greatest.h new file mode 100644 index 0000000..af0c053 --- /dev/null +++ b/osal/cosit/test/greatest/greatest.h @@ -0,0 +1,1266 @@ +/* + * Copyright (c) 2011-2021 Scott Vokes + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef GREATEST_H +#define GREATEST_H + +#if defined(__cplusplus) && !defined(GREATEST_NO_EXTERN_CPLUSPLUS) +extern "C" { +#endif + +/* 1.5.0 */ +#define GREATEST_VERSION_MAJOR 1 +#define GREATEST_VERSION_MINOR 5 +#define GREATEST_VERSION_PATCH 0 + +/* A unit testing system for C, contained in 1 file. + * It doesn't use dynamic allocation or depend on anything + * beyond ANSI C89. + * + * An up-to-date version can be found at: + * https://github.com/silentbicycle/greatest/ + */ + + +/********************************************************************* + * Minimal test runner template + *********************************************************************/ +#if 0 + +#include "greatest.h" + +TEST foo_should_foo(void) { + PASS(); +} + +static void setup_cb(void *data) { + printf("setup callback for each test case\n"); +} + +static void teardown_cb(void *data) { + printf("teardown callback for each test case\n"); +} + +SUITE(suite) { + /* Optional setup/teardown callbacks which will be run before/after + * every test case. If using a test suite, they will be cleared when + * the suite finishes. */ + SET_SETUP(setup_cb, voidp_to_callback_data); + SET_TEARDOWN(teardown_cb, voidp_to_callback_data); + + RUN_TEST(foo_should_foo); +} + +/* Add definitions that need to be in the test runner's main file. */ +GREATEST_MAIN_DEFS(); + +/* Set up, run suite(s) of tests, report pass/fail/skip stats. */ +int run_tests(void) { + GREATEST_INIT(); /* init. greatest internals */ + /* List of suites to run (if any). */ + RUN_SUITE(suite); + + /* Tests can also be run directly, without using test suites. */ + RUN_TEST(foo_should_foo); + + GREATEST_PRINT_REPORT(); /* display results */ + return greatest_all_passed(); +} + +/* main(), for a standalone command-line test runner. + * This replaces run_tests above, and adds command line option + * handling and exiting with a pass/fail status. */ +int main(int argc, char **argv) { + GREATEST_MAIN_BEGIN(); /* init & parse command-line args */ + RUN_SUITE(suite); + GREATEST_MAIN_END(); /* display results */ +} + +#endif +/*********************************************************************/ + + +#include +#include +#include +#include + +/*********** + * Options * + ***********/ + +/* Default column width for non-verbose output. */ +#ifndef GREATEST_DEFAULT_WIDTH +#define GREATEST_DEFAULT_WIDTH 72 +#endif + +/* FILE *, for test logging. */ +#ifndef GREATEST_STDOUT +#define GREATEST_STDOUT stdout +#endif + +/* Remove GREATEST_ prefix from most commonly used symbols? */ +#ifndef GREATEST_USE_ABBREVS +#define GREATEST_USE_ABBREVS 1 +#endif + +/* Set to 0 to disable all use of setjmp/longjmp. */ +#ifndef GREATEST_USE_LONGJMP +#define GREATEST_USE_LONGJMP 0 +#endif + +/* Make it possible to replace fprintf with another + * function with the same interface. */ +#ifndef GREATEST_FPRINTF +#define GREATEST_FPRINTF fprintf +#endif + +#if GREATEST_USE_LONGJMP +#include +#endif + +/* Set to 0 to disable all use of time.h / clock(). */ +#ifndef GREATEST_USE_TIME +#define GREATEST_USE_TIME 1 +#endif + +#if GREATEST_USE_TIME +#include +#endif + +/* Floating point type, for ASSERT_IN_RANGE. */ +#ifndef GREATEST_FLOAT +#define GREATEST_FLOAT double +#define GREATEST_FLOAT_FMT "%g" +#endif + +/* Size of buffer for test name + optional '_' separator and suffix */ +#ifndef GREATEST_TESTNAME_BUF_SIZE +#define GREATEST_TESTNAME_BUF_SIZE 128 +#endif + + +/********* + * Types * + *********/ + +/* Info for the current running suite. */ +typedef struct greatest_suite_info { + unsigned int tests_run; + unsigned int passed; + unsigned int failed; + unsigned int skipped; + +#if GREATEST_USE_TIME + /* timers, pre/post running suite and individual tests */ + clock_t pre_suite; + clock_t post_suite; + clock_t pre_test; + clock_t post_test; +#endif +} greatest_suite_info; + +/* Type for a suite function. */ +typedef void greatest_suite_cb(void); + +/* Types for setup/teardown callbacks. If non-NULL, these will be run + * and passed the pointer to their additional data. */ +typedef void greatest_setup_cb(void *udata); +typedef void greatest_teardown_cb(void *udata); + +/* Type for an equality comparison between two pointers of the same type. + * Should return non-0 if equal, otherwise 0. + * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */ +typedef int greatest_equal_cb(const void *expd, const void *got, void *udata); + +/* Type for a callback that prints a value pointed to by T. + * Return value has the same meaning as printf's. + * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */ +typedef int greatest_printf_cb(const void *t, void *udata); + +/* Callbacks for an arbitrary type; needed for type-specific + * comparisons via GREATEST_ASSERT_EQUAL_T[m].*/ +typedef struct greatest_type_info { + greatest_equal_cb *equal; + greatest_printf_cb *print; +} greatest_type_info; + +typedef struct greatest_memory_cmp_env { + const unsigned char *exp; + const unsigned char *got; + size_t size; +} greatest_memory_cmp_env; + +/* Callbacks for string and raw memory types. */ +extern greatest_type_info greatest_type_info_string; +extern greatest_type_info greatest_type_info_memory; + +typedef enum { + GREATEST_FLAG_FIRST_FAIL = 0x01, + GREATEST_FLAG_LIST_ONLY = 0x02, + GREATEST_FLAG_ABORT_ON_FAIL = 0x04 +} greatest_flag_t; + +/* Internal state for a PRNG, used to shuffle test order. */ +struct greatest_prng { + unsigned char random_order; /* use random ordering? */ + unsigned char initialized; /* is random ordering initialized? */ + unsigned char pad_0[6]; + unsigned long state; /* PRNG state */ + unsigned long count; /* how many tests, this pass */ + unsigned long count_ceil; /* total number of tests */ + unsigned long count_run; /* total tests run */ + unsigned long a; /* LCG multiplier */ + unsigned long c; /* LCG increment */ + unsigned long m; /* LCG modulus, based on count_ceil */ +}; + +/* Struct containing all test runner state. */ +typedef struct greatest_run_info { + unsigned char flags; + unsigned char verbosity; + unsigned char running_test; /* guard for nested RUN_TEST calls */ + unsigned char exact_name_match; + + unsigned int tests_run; /* total test count */ + + /* currently running test suite */ + greatest_suite_info suite; + + /* overall pass/fail/skip counts */ + unsigned int passed; + unsigned int failed; + unsigned int skipped; + unsigned int assertions; + + /* info to print about the most recent failure */ + unsigned int fail_line; + unsigned int pad_1; + const char *fail_file; + const char *msg; + + /* current setup/teardown hooks and userdata */ + greatest_setup_cb *setup; + void *setup_udata; + greatest_teardown_cb *teardown; + void *teardown_udata; + + /* formatting info for ".....s...F"-style output */ + unsigned int col; + unsigned int width; + + /* only run a specific suite or test */ + const char *suite_filter; + const char *test_filter; + const char *test_exclude; + const char *name_suffix; /* print suffix with test name */ + char name_buf[GREATEST_TESTNAME_BUF_SIZE]; + + struct greatest_prng prng[2]; /* 0: suites, 1: tests */ + +#if GREATEST_USE_TIME + /* overall timers */ + clock_t begin; + clock_t end; +#endif + +#if GREATEST_USE_LONGJMP + int pad_jmp_buf; + unsigned char pad_2[4]; + jmp_buf jump_dest; +#endif +} greatest_run_info; + +struct greatest_report_t { + /* overall pass/fail/skip counts */ + unsigned int passed; + unsigned int failed; + unsigned int skipped; + unsigned int assertions; +}; + +/* Global var for the current testing context. + * Initialized by GREATEST_MAIN_DEFS(). */ +extern greatest_run_info greatest_info; + +/* Type for ASSERT_ENUM_EQ's ENUM_STR argument. */ +typedef const char *greatest_enum_str_fun(int value); + + +/********************** + * Exported functions * + **********************/ + +/* These are used internally by greatest macros. */ +int greatest_test_pre(const char *name); +void greatest_test_post(int res); +int greatest_do_assert_equal_t(const void *expd, const void *got, + greatest_type_info *type_info, void *udata); +void greatest_prng_init_first_pass(int id); +int greatest_prng_init_second_pass(int id, unsigned long seed); +void greatest_prng_step(int id); + +/* These are part of the public greatest API. */ +void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata); +void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata); +void GREATEST_INIT(void); +void GREATEST_PRINT_REPORT(void); +int greatest_all_passed(void); +void greatest_set_suite_filter(const char *filter); +void greatest_set_test_filter(const char *filter); +void greatest_set_test_exclude(const char *filter); +void greatest_set_exact_name_match(void); +void greatest_stop_at_first_fail(void); +void greatest_abort_on_fail(void); +void greatest_list_only(void); +void greatest_get_report(struct greatest_report_t *report); +unsigned int greatest_get_verbosity(void); +void greatest_set_verbosity(unsigned int verbosity); +void greatest_set_flag(greatest_flag_t flag); +void greatest_set_test_suffix(const char *suffix); + + +/******************** +* Language Support * +********************/ + +/* If __VA_ARGS__ (C99) is supported, allow parametric testing +* without needing to manually manage the argument struct. */ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 19901L) || \ + (defined(_MSC_VER) && _MSC_VER >= 1800) +#define GREATEST_VA_ARGS +#endif + + +/********** + * Macros * + **********/ + +/* Define a suite. (The duplication is intentional -- it eliminates + * a warning from -Wmissing-declarations.) */ +#define GREATEST_SUITE(NAME) void NAME(void); void NAME(void) + +/* Declare a suite, provided by another compilation unit. */ +#define GREATEST_SUITE_EXTERN(NAME) void NAME(void) + +/* Start defining a test function. + * The arguments are not included, to allow parametric testing. */ +#define GREATEST_TEST static enum greatest_test_res + +/* PASS/FAIL/SKIP result from a test. Used internally. */ +typedef enum greatest_test_res { + GREATEST_TEST_RES_PASS = 0, + GREATEST_TEST_RES_FAIL = -1, + GREATEST_TEST_RES_SKIP = 1 +} greatest_test_res; + +/* Run a suite. */ +#define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME) + +/* Run a test in the current suite. */ +#define GREATEST_RUN_TEST(TEST) \ + do { \ + if (greatest_test_pre(#TEST) == 1) { \ + enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ + if (res == GREATEST_TEST_RES_PASS) { \ + res = TEST(); \ + } \ + greatest_test_post(res); \ + } \ + } while (0) + +/* Ignore a test, don't warn about it being unused. */ +#define GREATEST_IGNORE_TEST(TEST) (void)TEST + +/* Run a test in the current suite with one void * argument, + * which can be a pointer to a struct with multiple arguments. */ +#define GREATEST_RUN_TEST1(TEST, ENV) \ + do { \ + if (greatest_test_pre(#TEST) == 1) { \ + enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ + if (res == GREATEST_TEST_RES_PASS) { \ + res = TEST(ENV); \ + } \ + greatest_test_post(res); \ + } \ + } while (0) + +#ifdef GREATEST_VA_ARGS +#define GREATEST_RUN_TESTp(TEST, ...) \ + do { \ + if (greatest_test_pre(#TEST) == 1) { \ + enum greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ + if (res == GREATEST_TEST_RES_PASS) { \ + res = TEST(__VA_ARGS__); \ + } \ + greatest_test_post(res); \ + } \ + } while (0) +#endif + + +/* Check if the test runner is in verbose mode. */ +#define GREATEST_IS_VERBOSE() ((greatest_info.verbosity) > 0) +#define GREATEST_LIST_ONLY() \ + (greatest_info.flags & GREATEST_FLAG_LIST_ONLY) +#define GREATEST_FIRST_FAIL() \ + (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL) +#define GREATEST_ABORT_ON_FAIL() \ + (greatest_info.flags & GREATEST_FLAG_ABORT_ON_FAIL) +#define GREATEST_FAILURE_ABORT() \ + (GREATEST_FIRST_FAIL() && \ + (greatest_info.suite.failed > 0 || greatest_info.failed > 0)) + +/* Message-less forms of tests defined below. */ +#define GREATEST_PASS() GREATEST_PASSm(NULL) +#define GREATEST_FAIL() GREATEST_FAILm(NULL) +#define GREATEST_SKIP() GREATEST_SKIPm(NULL) +#define GREATEST_ASSERT(COND) \ + GREATEST_ASSERTm(#COND, COND) +#define GREATEST_ASSERT_OR_LONGJMP(COND) \ + GREATEST_ASSERT_OR_LONGJMPm(#COND, COND) +#define GREATEST_ASSERT_FALSE(COND) \ + GREATEST_ASSERT_FALSEm(#COND, COND) +#define GREATEST_ASSERT_EQ(EXP, GOT) \ + GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT) +#define GREATEST_ASSERT_NEQ(EXP, GOT) \ + GREATEST_ASSERT_NEQm(#EXP " == " #GOT, EXP, GOT) +#define GREATEST_ASSERT_GT(EXP, GOT) \ + GREATEST_ASSERT_GTm(#EXP " <= " #GOT, EXP, GOT) +#define GREATEST_ASSERT_GTE(EXP, GOT) \ + GREATEST_ASSERT_GTEm(#EXP " < " #GOT, EXP, GOT) +#define GREATEST_ASSERT_LT(EXP, GOT) \ + GREATEST_ASSERT_LTm(#EXP " >= " #GOT, EXP, GOT) +#define GREATEST_ASSERT_LTE(EXP, GOT) \ + GREATEST_ASSERT_LTEm(#EXP " > " #GOT, EXP, GOT) +#define GREATEST_ASSERT_EQ_FMT(EXP, GOT, FMT) \ + GREATEST_ASSERT_EQ_FMTm(#EXP " != " #GOT, EXP, GOT, FMT) +#define GREATEST_ASSERT_IN_RANGE(EXP, GOT, TOL) \ + GREATEST_ASSERT_IN_RANGEm(#EXP " != " #GOT " +/- " #TOL, EXP, GOT, TOL) +#define GREATEST_ASSERT_EQUAL_T(EXP, GOT, TYPE_INFO, UDATA) \ + GREATEST_ASSERT_EQUAL_Tm(#EXP " != " #GOT, EXP, GOT, TYPE_INFO, UDATA) +#define GREATEST_ASSERT_STR_EQ(EXP, GOT) \ + GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT) +#define GREATEST_ASSERT_STRN_EQ(EXP, GOT, SIZE) \ + GREATEST_ASSERT_STRN_EQm(#EXP " != " #GOT, EXP, GOT, SIZE) +#define GREATEST_ASSERT_MEM_EQ(EXP, GOT, SIZE) \ + GREATEST_ASSERT_MEM_EQm(#EXP " != " #GOT, EXP, GOT, SIZE) +#define GREATEST_ASSERT_ENUM_EQ(EXP, GOT, ENUM_STR) \ + GREATEST_ASSERT_ENUM_EQm(#EXP " != " #GOT, EXP, GOT, ENUM_STR) + +/* The following forms take an additional message argument first, + * to be displayed by the test runner. */ + +/* Fail if a condition is not true, with message. */ +#define GREATEST_ASSERTm(MSG, COND) \ + do { \ + greatest_info.assertions++; \ + if (!(COND)) { GREATEST_FAILm(MSG); } \ + } while (0) + +/* Fail if a condition is not true, longjmping out of test. */ +#define GREATEST_ASSERT_OR_LONGJMPm(MSG, COND) \ + do { \ + greatest_info.assertions++; \ + if (!(COND)) { GREATEST_FAIL_WITH_LONGJMPm(MSG); } \ + } while (0) + +/* Fail if a condition is not false, with message. */ +#define GREATEST_ASSERT_FALSEm(MSG, COND) \ + do { \ + greatest_info.assertions++; \ + if ((COND)) { GREATEST_FAILm(MSG); } \ + } while (0) + +/* Internal macro for relational assertions */ +#define GREATEST__REL(REL, MSG, EXP, GOT) \ + do { \ + greatest_info.assertions++; \ + if (!((EXP) REL (GOT))) { GREATEST_FAILm(MSG); } \ + } while (0) + +/* Fail if EXP is not ==, !=, >, <, >=, or <= to GOT. */ +#define GREATEST_ASSERT_EQm(MSG,E,G) GREATEST__REL(==, MSG,E,G) +#define GREATEST_ASSERT_NEQm(MSG,E,G) GREATEST__REL(!=, MSG,E,G) +#define GREATEST_ASSERT_GTm(MSG,E,G) GREATEST__REL(>, MSG,E,G) +#define GREATEST_ASSERT_GTEm(MSG,E,G) GREATEST__REL(>=, MSG,E,G) +#define GREATEST_ASSERT_LTm(MSG,E,G) GREATEST__REL(<, MSG,E,G) +#define GREATEST_ASSERT_LTEm(MSG,E,G) GREATEST__REL(<=, MSG,E,G) + +/* Fail if EXP != GOT (equality comparison by ==). + * Warning: FMT, EXP, and GOT will be evaluated more + * than once on failure. */ +#define GREATEST_ASSERT_EQ_FMTm(MSG, EXP, GOT, FMT) \ + do { \ + greatest_info.assertions++; \ + if ((EXP) != (GOT)) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: "); \ + GREATEST_FPRINTF(GREATEST_STDOUT, FMT, EXP); \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: "); \ + GREATEST_FPRINTF(GREATEST_STDOUT, FMT, GOT); \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ + GREATEST_FAILm(MSG); \ + } \ + } while (0) + +/* Fail if EXP is not equal to GOT, printing enum IDs. */ +#define GREATEST_ASSERT_ENUM_EQm(MSG, EXP, GOT, ENUM_STR) \ + do { \ + int greatest_EXP = (int)(EXP); \ + int greatest_GOT = (int)(GOT); \ + greatest_enum_str_fun *greatest_ENUM_STR = ENUM_STR; \ + if (greatest_EXP != greatest_GOT) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: %s", \ + greatest_ENUM_STR(greatest_EXP)); \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: %s\n", \ + greatest_ENUM_STR(greatest_GOT)); \ + GREATEST_FAILm(MSG); \ + } \ + } while (0) \ + +/* Fail if GOT not in range of EXP +|- TOL. */ +#define GREATEST_ASSERT_IN_RANGEm(MSG, EXP, GOT, TOL) \ + do { \ + GREATEST_FLOAT greatest_EXP = (EXP); \ + GREATEST_FLOAT greatest_GOT = (GOT); \ + GREATEST_FLOAT greatest_TOL = (TOL); \ + greatest_info.assertions++; \ + if ((greatest_EXP > greatest_GOT && \ + greatest_EXP - greatest_GOT > greatest_TOL) || \ + (greatest_EXP < greatest_GOT && \ + greatest_GOT - greatest_EXP > greatest_TOL)) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, \ + "\nExpected: " GREATEST_FLOAT_FMT \ + " +/- " GREATEST_FLOAT_FMT \ + "\n Got: " GREATEST_FLOAT_FMT \ + "\n", \ + greatest_EXP, greatest_TOL, greatest_GOT); \ + GREATEST_FAILm(MSG); \ + } \ + } while (0) + +/* Fail if EXP is not equal to GOT, according to strcmp. */ +#define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \ + do { \ + GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \ + &greatest_type_info_string, NULL); \ + } while (0) \ + +/* Fail if EXP is not equal to GOT, according to strncmp. */ +#define GREATEST_ASSERT_STRN_EQm(MSG, EXP, GOT, SIZE) \ + do { \ + size_t size = SIZE; \ + GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \ + &greatest_type_info_string, &size); \ + } while (0) \ + +/* Fail if EXP is not equal to GOT, according to memcmp. */ +#define GREATEST_ASSERT_MEM_EQm(MSG, EXP, GOT, SIZE) \ + do { \ + greatest_memory_cmp_env env; \ + env.exp = (const unsigned char *)EXP; \ + env.got = (const unsigned char *)GOT; \ + env.size = SIZE; \ + GREATEST_ASSERT_EQUAL_Tm(MSG, env.exp, env.got, \ + &greatest_type_info_memory, &env); \ + } while (0) \ + +/* Fail if EXP is not equal to GOT, according to a comparison + * callback in TYPE_INFO. If they are not equal, optionally use a + * print callback in TYPE_INFO to print them. */ +#define GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, TYPE_INFO, UDATA) \ + do { \ + greatest_type_info *type_info = (TYPE_INFO); \ + greatest_info.assertions++; \ + if (!greatest_do_assert_equal_t(EXP, GOT, \ + type_info, UDATA)) { \ + if (type_info == NULL || type_info->equal == NULL) { \ + GREATEST_FAILm("type_info->equal callback missing!"); \ + } else { \ + GREATEST_FAILm(MSG); \ + } \ + } \ + } while (0) \ + +/* Pass. */ +#define GREATEST_PASSm(MSG) \ + do { \ + greatest_info.msg = MSG; \ + return GREATEST_TEST_RES_PASS; \ + } while (0) + +/* Fail. */ +#define GREATEST_FAILm(MSG) \ + do { \ + greatest_info.fail_file = __FILE__; \ + greatest_info.fail_line = __LINE__; \ + greatest_info.msg = MSG; \ + if (GREATEST_ABORT_ON_FAIL()) { abort(); } \ + return GREATEST_TEST_RES_FAIL; \ + } while (0) + +/* Optional GREATEST_FAILm variant that longjmps. */ +#if GREATEST_USE_LONGJMP +#define GREATEST_FAIL_WITH_LONGJMP() GREATEST_FAIL_WITH_LONGJMPm(NULL) +#define GREATEST_FAIL_WITH_LONGJMPm(MSG) \ + do { \ + greatest_info.fail_file = __FILE__; \ + greatest_info.fail_line = __LINE__; \ + greatest_info.msg = MSG; \ + longjmp(greatest_info.jump_dest, GREATEST_TEST_RES_FAIL); \ + } while (0) +#endif + +/* Skip the current test. */ +#define GREATEST_SKIPm(MSG) \ + do { \ + greatest_info.msg = MSG; \ + return GREATEST_TEST_RES_SKIP; \ + } while (0) + +/* Check the result of a subfunction using ASSERT, etc. */ +#define GREATEST_CHECK_CALL(RES) \ + do { \ + enum greatest_test_res greatest_RES = RES; \ + if (greatest_RES != GREATEST_TEST_RES_PASS) { \ + return greatest_RES; \ + } \ + } while (0) \ + +#if GREATEST_USE_TIME +#define GREATEST_SET_TIME(NAME) \ + NAME = clock(); \ + if (NAME == (clock_t) -1) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, \ + "clock error: %s\n", #NAME); \ + exit(EXIT_FAILURE); \ + } + +#define GREATEST_CLOCK_DIFF(C1, C2) \ + GREATEST_FPRINTF(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \ + (long unsigned int) (C2) - (long unsigned int)(C1), \ + (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC)) +#else +#define GREATEST_SET_TIME(UNUSED) +#define GREATEST_CLOCK_DIFF(UNUSED1, UNUSED2) +#endif + +#if GREATEST_USE_LONGJMP +#define GREATEST_SAVE_CONTEXT() \ + /* setjmp returns 0 (GREATEST_TEST_RES_PASS) on first call * \ + * so the test runs, then RES_FAIL from FAIL_WITH_LONGJMP. */ \ + ((enum greatest_test_res)(setjmp(greatest_info.jump_dest))) +#else +#define GREATEST_SAVE_CONTEXT() \ + /*a no-op, since setjmp/longjmp aren't being used */ \ + GREATEST_TEST_RES_PASS +#endif + +/* Run every suite / test function run within BODY in pseudo-random + * order, seeded by SEED. (The top 3 bits of the seed are ignored.) + * + * This should be called like: + * GREATEST_SHUFFLE_TESTS(seed, { + * GREATEST_RUN_TEST(some_test); + * GREATEST_RUN_TEST(some_other_test); + * GREATEST_RUN_TEST(yet_another_test); + * }); + * + * Note that the body of the second argument will be evaluated + * multiple times. */ +#define GREATEST_SHUFFLE_SUITES(SD, BODY) GREATEST_SHUFFLE(0, SD, BODY) +#define GREATEST_SHUFFLE_TESTS(SD, BODY) GREATEST_SHUFFLE(1, SD, BODY) +#define GREATEST_SHUFFLE(ID, SD, BODY) \ + do { \ + struct greatest_prng *prng = &greatest_info.prng[ID]; \ + greatest_prng_init_first_pass(ID); \ + do { \ + prng->count = 0; \ + if (prng->initialized) { greatest_prng_step(ID); } \ + BODY; \ + if (!prng->initialized) { \ + if (!greatest_prng_init_second_pass(ID, SD)) { break; } \ + } else if (prng->count_run == prng->count_ceil) { \ + break; \ + } \ + } while (!GREATEST_FAILURE_ABORT()); \ + prng->count_run = prng->random_order = prng->initialized = 0; \ + } while(0) + +/* Include several function definitions in the main test file. */ +#define GREATEST_MAIN_DEFS() \ + \ +/* Is FILTER a subset of NAME? */ \ +static int greatest_name_match(const char *name, const char *filter, \ + int res_if_none) { \ + size_t offset = 0; \ + size_t filter_len = filter ? strlen(filter) : 0; \ + if (filter_len == 0) { return res_if_none; } /* no filter */ \ + if (greatest_info.exact_name_match && strlen(name) != filter_len) { \ + return 0; /* ignore substring matches */ \ + } \ + while (name[offset] != '\0') { \ + if (name[offset] == filter[0]) { \ + if (0 == strncmp(&name[offset], filter, filter_len)) { \ + return 1; \ + } \ + } \ + offset++; \ + } \ + \ + return 0; \ +} \ + \ +static void greatest_buffer_test_name(const char *name) { \ + struct greatest_run_info *g = &greatest_info; \ + size_t len = strlen(name), size = sizeof(g->name_buf); \ + memset(g->name_buf, 0x00, size); \ + (void)strncat(g->name_buf, name, size - 1); \ + if (g->name_suffix && (len + 1 < size)) { \ + g->name_buf[len] = '_'; \ + strncat(&g->name_buf[len+1], g->name_suffix, size-(len+2)); \ + } \ +} \ + \ +/* Before running a test, check the name filtering and \ + * test shuffling state, if applicable, and then call setup hooks. */ \ +int greatest_test_pre(const char *name) { \ + struct greatest_run_info *g = &greatest_info; \ + int match; \ + greatest_buffer_test_name(name); \ + match = greatest_name_match(g->name_buf, g->test_filter, 1) && \ + !greatest_name_match(g->name_buf, g->test_exclude, 0); \ + if (GREATEST_LIST_ONLY()) { /* just listing test names */ \ + if (match) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, " %s\n", g->name_buf); \ + } \ + goto clear; \ + } \ + if (match && (!GREATEST_FIRST_FAIL() || g->suite.failed == 0)) { \ + struct greatest_prng *p = &g->prng[1]; \ + if (p->random_order) { \ + p->count++; \ + if (!p->initialized || ((p->count - 1) != p->state)) { \ + goto clear; /* don't run this test yet */ \ + } \ + } \ + if (g->running_test) { \ + fprintf(stderr, "Error: Test run inside another test.\n"); \ + return 0; \ + } \ + GREATEST_SET_TIME(g->suite.pre_test); \ + if (g->setup) { g->setup(g->setup_udata); } \ + p->count_run++; \ + g->running_test = 1; \ + return 1; /* test should be run */ \ + } else { \ + goto clear; /* skipped */ \ + } \ +clear: \ + g->name_suffix = NULL; \ + return 0; \ +} \ + \ +static void greatest_do_pass(void) { \ + struct greatest_run_info *g = &greatest_info; \ + if (GREATEST_IS_VERBOSE()) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, "PASS %s: %s", \ + g->name_buf, g->msg ? g->msg : ""); \ + } else { \ + GREATEST_FPRINTF(GREATEST_STDOUT, "."); \ + } \ + g->suite.passed++; \ +} \ + \ +static void greatest_do_fail(void) { \ + struct greatest_run_info *g = &greatest_info; \ + if (GREATEST_IS_VERBOSE()) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, \ + "FAIL %s: %s (%s:%u)", g->name_buf, \ + g->msg ? g->msg : "", g->fail_file, g->fail_line); \ + } else { \ + GREATEST_FPRINTF(GREATEST_STDOUT, "F"); \ + g->col++; /* add linebreak if in line of '.'s */ \ + if (g->col != 0) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ + g->col = 0; \ + } \ + GREATEST_FPRINTF(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \ + g->name_buf, g->msg ? g->msg : "", \ + g->fail_file, g->fail_line); \ + } \ + g->suite.failed++; \ +} \ + \ +static void greatest_do_skip(void) { \ + struct greatest_run_info *g = &greatest_info; \ + if (GREATEST_IS_VERBOSE()) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, "SKIP %s: %s", \ + g->name_buf, g->msg ? g->msg : ""); \ + } else { \ + GREATEST_FPRINTF(GREATEST_STDOUT, "s"); \ + } \ + g->suite.skipped++; \ +} \ + \ +void greatest_test_post(int res) { \ + GREATEST_SET_TIME(greatest_info.suite.post_test); \ + if (greatest_info.teardown) { \ + void *udata = greatest_info.teardown_udata; \ + greatest_info.teardown(udata); \ + } \ + \ + greatest_info.running_test = 0; \ + if (res <= GREATEST_TEST_RES_FAIL) { \ + greatest_do_fail(); \ + } else if (res >= GREATEST_TEST_RES_SKIP) { \ + greatest_do_skip(); \ + } else if (res == GREATEST_TEST_RES_PASS) { \ + greatest_do_pass(); \ + } \ + greatest_info.name_suffix = NULL; \ + greatest_info.suite.tests_run++; \ + greatest_info.col++; \ + if (GREATEST_IS_VERBOSE()) { \ + GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \ + greatest_info.suite.post_test); \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ + } else if (greatest_info.col % greatest_info.width == 0) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ + greatest_info.col = 0; \ + } \ + fflush(GREATEST_STDOUT); \ +} \ + \ +static void report_suite(void) { \ + if (greatest_info.suite.tests_run > 0) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, \ + "\n%u test%s - %u passed, %u failed, %u skipped", \ + greatest_info.suite.tests_run, \ + greatest_info.suite.tests_run == 1 ? "" : "s", \ + greatest_info.suite.passed, \ + greatest_info.suite.failed, \ + greatest_info.suite.skipped); \ + GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \ + greatest_info.suite.post_suite); \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ + } \ +} \ + \ +static void update_counts_and_reset_suite(void) { \ + greatest_info.setup = NULL; \ + greatest_info.setup_udata = NULL; \ + greatest_info.teardown = NULL; \ + greatest_info.teardown_udata = NULL; \ + greatest_info.passed += greatest_info.suite.passed; \ + greatest_info.failed += greatest_info.suite.failed; \ + greatest_info.skipped += greatest_info.suite.skipped; \ + greatest_info.tests_run += greatest_info.suite.tests_run; \ + memset(&greatest_info.suite, 0, sizeof(greatest_info.suite)); \ + greatest_info.col = 0; \ +} \ + \ +static int greatest_suite_pre(const char *suite_name) { \ + struct greatest_prng *p = &greatest_info.prng[0]; \ + if (!greatest_name_match(suite_name, greatest_info.suite_filter, 1) \ + || (GREATEST_FAILURE_ABORT())) { return 0; } \ + if (p->random_order) { \ + p->count++; \ + if (!p->initialized || ((p->count - 1) != p->state)) { \ + return 0; /* don't run this suite yet */ \ + } \ + } \ + p->count_run++; \ + update_counts_and_reset_suite(); \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \ + GREATEST_SET_TIME(greatest_info.suite.pre_suite); \ + return 1; \ +} \ + \ +static void greatest_suite_post(void) { \ + GREATEST_SET_TIME(greatest_info.suite.post_suite); \ + report_suite(); \ +} \ + \ +static void greatest_run_suite(greatest_suite_cb *suite_cb, \ + const char *suite_name) { \ + if (greatest_suite_pre(suite_name)) { \ + suite_cb(); \ + greatest_suite_post(); \ + } \ +} \ + \ +int greatest_do_assert_equal_t(const void *expd, const void *got, \ + greatest_type_info *type_info, void *udata) { \ + int eq = 0; \ + if (type_info == NULL || type_info->equal == NULL) { return 0; } \ + eq = type_info->equal(expd, got, udata); \ + if (!eq) { \ + if (type_info->print != NULL) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\nExpected: "); \ + (void)type_info->print(expd, udata); \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\n Got: "); \ + (void)type_info->print(got, udata); \ + GREATEST_FPRINTF(GREATEST_STDOUT, "\n"); \ + } \ + } \ + return eq; \ +} \ + \ +static void greatest_usage(const char *name) { \ + GREATEST_FPRINTF(GREATEST_STDOUT, \ + "Usage: %s [-hlfavex] [-s SUITE] [-t TEST] [-x EXCLUDE]\n" \ + " -h, --help print this Help\n" \ + " -l List suites and tests, then exit (dry run)\n" \ + " -f Stop runner after first failure\n" \ + " -a Abort on first failure (implies -f)\n" \ + " -v Verbose output\n" \ + " -s SUITE only run suites containing substring SUITE\n" \ + " -t TEST only run tests containing substring TEST\n" \ + " -e only run exact name match for -s or -t\n" \ + " -x EXCLUDE exclude tests containing substring EXCLUDE\n", \ + name); \ +} \ + \ +static void greatest_parse_options(int argc, char **argv) { \ + int i = 0; \ + for (i = 1; i < argc; i++) { \ + if (argv[i][0] == '-') { \ + char f = argv[i][1]; \ + if ((f == 's' || f == 't' || f == 'x') && argc <= i + 1) { \ + greatest_usage(argv[0]); exit(EXIT_FAILURE); \ + } \ + switch (f) { \ + case 's': /* suite name filter */ \ + greatest_set_suite_filter(argv[i + 1]); i++; break; \ + case 't': /* test name filter */ \ + greatest_set_test_filter(argv[i + 1]); i++; break; \ + case 'x': /* test name exclusion */ \ + greatest_set_test_exclude(argv[i + 1]); i++; break; \ + case 'e': /* exact name match */ \ + greatest_set_exact_name_match(); break; \ + case 'f': /* first fail flag */ \ + greatest_stop_at_first_fail(); break; \ + case 'a': /* abort() on fail flag */ \ + greatest_abort_on_fail(); break; \ + case 'l': /* list only (dry run) */ \ + greatest_list_only(); break; \ + case 'v': /* first fail flag */ \ + greatest_info.verbosity++; break; \ + case 'h': /* help */ \ + greatest_usage(argv[0]); exit(EXIT_SUCCESS); \ + default: \ + case '-': \ + if (0 == strncmp("--help", argv[i], 6)) { \ + greatest_usage(argv[0]); exit(EXIT_SUCCESS); \ + } else if (0 == strcmp("--", argv[i])) { \ + return; /* ignore following arguments */ \ + } \ + GREATEST_FPRINTF(GREATEST_STDOUT, \ + "Unknown argument '%s'\n", argv[i]); \ + greatest_usage(argv[0]); \ + exit(EXIT_FAILURE); \ + } \ + } \ + } \ +} \ + \ +int greatest_all_passed(void) { return (greatest_info.failed == 0); } \ + \ +void greatest_set_test_filter(const char *filter) { \ + greatest_info.test_filter = filter; \ +} \ + \ +void greatest_set_test_exclude(const char *filter) { \ + greatest_info.test_exclude = filter; \ +} \ + \ +void greatest_set_suite_filter(const char *filter) { \ + greatest_info.suite_filter = filter; \ +} \ + \ +void greatest_set_exact_name_match(void) { \ + greatest_info.exact_name_match = 1; \ +} \ + \ +void greatest_stop_at_first_fail(void) { \ + greatest_set_flag(GREATEST_FLAG_FIRST_FAIL); \ +} \ + \ +void greatest_abort_on_fail(void) { \ + greatest_set_flag(GREATEST_FLAG_ABORT_ON_FAIL); \ +} \ + \ +void greatest_list_only(void) { \ + greatest_set_flag(GREATEST_FLAG_LIST_ONLY); \ +} \ + \ +void greatest_get_report(struct greatest_report_t *report) { \ + if (report) { \ + report->passed = greatest_info.passed; \ + report->failed = greatest_info.failed; \ + report->skipped = greatest_info.skipped; \ + report->assertions = greatest_info.assertions; \ + } \ +} \ + \ +unsigned int greatest_get_verbosity(void) { \ + return greatest_info.verbosity; \ +} \ + \ +void greatest_set_verbosity(unsigned int verbosity) { \ + greatest_info.verbosity = (unsigned char)verbosity; \ +} \ + \ +void greatest_set_flag(greatest_flag_t flag) { \ + greatest_info.flags = (unsigned char)(greatest_info.flags | flag); \ +} \ + \ +void greatest_set_test_suffix(const char *suffix) { \ + greatest_info.name_suffix = suffix; \ +} \ + \ +void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \ + greatest_info.setup = cb; \ + greatest_info.setup_udata = udata; \ +} \ + \ +void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata) { \ + greatest_info.teardown = cb; \ + greatest_info.teardown_udata = udata; \ +} \ + \ +static int greatest_string_equal_cb(const void *expd, const void *got, \ + void *udata) { \ + size_t *size = (size_t *)udata; \ + return (size != NULL \ + ? (0 == strncmp((const char *)expd, (const char *)got, *size)) \ + : (0 == strcmp((const char *)expd, (const char *)got))); \ +} \ + \ +static int greatest_string_printf_cb(const void *t, void *udata) { \ + (void)udata; /* note: does not check \0 termination. */ \ + return GREATEST_FPRINTF(GREATEST_STDOUT, "%s", (const char *)t); \ +} \ + \ +greatest_type_info greatest_type_info_string = { \ + greatest_string_equal_cb, greatest_string_printf_cb, \ +}; \ + \ +static int greatest_memory_equal_cb(const void *expd, const void *got, \ + void *udata) { \ + greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \ + return (0 == memcmp(expd, got, env->size)); \ +} \ + \ +/* Hexdump raw memory, with differences highlighted */ \ +static int greatest_memory_printf_cb(const void *t, void *udata) { \ + greatest_memory_cmp_env *env = (greatest_memory_cmp_env *)udata; \ + const unsigned char *buf = (const unsigned char *)t; \ + unsigned char diff_mark = ' '; \ + FILE *out = GREATEST_STDOUT; \ + size_t i, line_i, line_len = 0; \ + int len = 0; /* format hexdump with differences highlighted */ \ + for (i = 0; i < env->size; i+= line_len) { \ + diff_mark = ' '; \ + line_len = env->size - i; \ + if (line_len > 16) { line_len = 16; } \ + for (line_i = i; line_i < i + line_len; line_i++) { \ + if (env->exp[line_i] != env->got[line_i]) diff_mark = 'X'; \ + } \ + len += GREATEST_FPRINTF(out, "\n%04x %c ", \ + (unsigned int)i, diff_mark); \ + for (line_i = i; line_i < i + line_len; line_i++) { \ + int m = env->exp[line_i] == env->got[line_i]; /* match? */ \ + len += GREATEST_FPRINTF(out, "%02x%c", \ + buf[line_i], m ? ' ' : '<'); \ + } \ + for (line_i = 0; line_i < 16 - line_len; line_i++) { \ + len += GREATEST_FPRINTF(out, " "); \ + } \ + GREATEST_FPRINTF(out, " "); \ + for (line_i = i; line_i < i + line_len; line_i++) { \ + unsigned char c = buf[line_i]; \ + len += GREATEST_FPRINTF(out, "%c", isprint(c) ? c : '.'); \ + } \ + } \ + len += GREATEST_FPRINTF(out, "\n"); \ + return len; \ +} \ + \ +void greatest_prng_init_first_pass(int id) { \ + greatest_info.prng[id].random_order = 1; \ + greatest_info.prng[id].count_run = 0; \ +} \ + \ +int greatest_prng_init_second_pass(int id, unsigned long seed) { \ + struct greatest_prng *p = &greatest_info.prng[id]; \ + if (p->count == 0) { return 0; } \ + p->count_ceil = p->count; \ + for (p->m = 1; p->m < p->count; p->m <<= 1) {} \ + p->state = seed & 0x1fffffff; /* only use lower 29 bits */ \ + p->a = 4LU * p->state; /* to avoid overflow when */ \ + p->a = (p->a ? p->a : 4) | 1; /* multiplied by 4 */ \ + p->c = 2147483647; /* and so p->c ((2 ** 31) - 1) is */ \ + p->initialized = 1; /* always relatively prime to p->a. */ \ + fprintf(stderr, "init_second_pass: a %lu, c %lu, state %lu\n", \ + p->a, p->c, p->state); \ + return 1; \ +} \ + \ +/* Step the pseudorandom number generator until its state reaches \ + * another test ID between 0 and the test count. \ + * This use a linear congruential pseudorandom number generator, \ + * with the power-of-two ceiling of the test count as the modulus, the \ + * masked seed as the multiplier, and a prime as the increment. For \ + * each generated value < the test count, run the corresponding test. \ + * This will visit all IDs 0 <= X < mod once before repeating, \ + * with a starting position chosen based on the initial seed. \ + * For details, see: Knuth, The Art of Computer Programming \ + * Volume. 2, section 3.2.1. */ \ +void greatest_prng_step(int id) { \ + struct greatest_prng *p = &greatest_info.prng[id]; \ + do { \ + p->state = ((p->a * p->state) + p->c) & (p->m - 1); \ + } while (p->state >= p->count_ceil); \ +} \ + \ +void GREATEST_INIT(void) { \ + /* Suppress unused function warning if features aren't used */ \ + (void)greatest_run_suite; \ + (void)greatest_parse_options; \ + (void)greatest_prng_step; \ + (void)greatest_prng_init_first_pass; \ + (void)greatest_prng_init_second_pass; \ + (void)greatest_set_test_suffix; \ + \ + memset(&greatest_info, 0, sizeof(greatest_info)); \ + greatest_info.width = GREATEST_DEFAULT_WIDTH; \ + GREATEST_SET_TIME(greatest_info.begin); \ +} \ + \ +/* Report passes, failures, skipped tests, the number of \ + * assertions, and the overall run time. */ \ +void GREATEST_PRINT_REPORT(void) { \ + if (!GREATEST_LIST_ONLY()) { \ + update_counts_and_reset_suite(); \ + GREATEST_SET_TIME(greatest_info.end); \ + GREATEST_FPRINTF(GREATEST_STDOUT, \ + "\nTotal: %u test%s", \ + greatest_info.tests_run, \ + greatest_info.tests_run == 1 ? "" : "s"); \ + GREATEST_CLOCK_DIFF(greatest_info.begin, \ + greatest_info.end); \ + GREATEST_FPRINTF(GREATEST_STDOUT, ", %u assertion%s\n", \ + greatest_info.assertions, \ + greatest_info.assertions == 1 ? "" : "s"); \ + GREATEST_FPRINTF(GREATEST_STDOUT, \ + "Pass: %u, fail: %u, skip: %u.\n", \ + greatest_info.passed, \ + greatest_info.failed, greatest_info.skipped); \ + } \ +} \ + \ +greatest_type_info greatest_type_info_memory = { \ + greatest_memory_equal_cb, greatest_memory_printf_cb, \ +}; \ + \ +greatest_run_info greatest_info + +/* Handle command-line arguments, etc. */ +#define GREATEST_MAIN_BEGIN() \ + do { \ + GREATEST_INIT(); \ + greatest_parse_options(argc, argv); \ + } while (0) + +/* Report results, exit with exit status based on results. */ +#define GREATEST_MAIN_END() \ + do { \ + GREATEST_PRINT_REPORT(); \ + return (greatest_all_passed() ? EXIT_SUCCESS : EXIT_FAILURE); \ + } while (0) + +/* Make abbreviations without the GREATEST_ prefix for the + * most commonly used symbols. */ +#if GREATEST_USE_ABBREVS +#define TEST GREATEST_TEST +#define SUITE GREATEST_SUITE +#define SUITE_EXTERN GREATEST_SUITE_EXTERN +#define RUN_TEST GREATEST_RUN_TEST +#define RUN_TEST1 GREATEST_RUN_TEST1 +#define RUN_SUITE GREATEST_RUN_SUITE +#define IGNORE_TEST GREATEST_IGNORE_TEST +#define ASSERT GREATEST_ASSERT +#define ASSERTm GREATEST_ASSERTm +#define ASSERT_FALSE GREATEST_ASSERT_FALSE +#define ASSERT_EQ GREATEST_ASSERT_EQ +#define ASSERT_NEQ GREATEST_ASSERT_NEQ +#define ASSERT_GT GREATEST_ASSERT_GT +#define ASSERT_GTE GREATEST_ASSERT_GTE +#define ASSERT_LT GREATEST_ASSERT_LT +#define ASSERT_LTE GREATEST_ASSERT_LTE +#define ASSERT_EQ_FMT GREATEST_ASSERT_EQ_FMT +#define ASSERT_IN_RANGE GREATEST_ASSERT_IN_RANGE +#define ASSERT_EQUAL_T GREATEST_ASSERT_EQUAL_T +#define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ +#define ASSERT_STRN_EQ GREATEST_ASSERT_STRN_EQ +#define ASSERT_MEM_EQ GREATEST_ASSERT_MEM_EQ +#define ASSERT_ENUM_EQ GREATEST_ASSERT_ENUM_EQ +#define ASSERT_FALSEm GREATEST_ASSERT_FALSEm +#define ASSERT_EQm GREATEST_ASSERT_EQm +#define ASSERT_NEQm GREATEST_ASSERT_NEQm +#define ASSERT_GTm GREATEST_ASSERT_GTm +#define ASSERT_GTEm GREATEST_ASSERT_GTEm +#define ASSERT_LTm GREATEST_ASSERT_LTm +#define ASSERT_LTEm GREATEST_ASSERT_LTEm +#define ASSERT_EQ_FMTm GREATEST_ASSERT_EQ_FMTm +#define ASSERT_IN_RANGEm GREATEST_ASSERT_IN_RANGEm +#define ASSERT_EQUAL_Tm GREATEST_ASSERT_EQUAL_Tm +#define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm +#define ASSERT_STRN_EQm GREATEST_ASSERT_STRN_EQm +#define ASSERT_MEM_EQm GREATEST_ASSERT_MEM_EQm +#define ASSERT_ENUM_EQm GREATEST_ASSERT_ENUM_EQm +#define PASS GREATEST_PASS +#define FAIL GREATEST_FAIL +#define SKIP GREATEST_SKIP +#define PASSm GREATEST_PASSm +#define FAILm GREATEST_FAILm +#define SKIPm GREATEST_SKIPm +#define SET_SETUP GREATEST_SET_SETUP_CB +#define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB +#define CHECK_CALL GREATEST_CHECK_CALL +#define SHUFFLE_TESTS GREATEST_SHUFFLE_TESTS +#define SHUFFLE_SUITES GREATEST_SHUFFLE_SUITES + +#ifdef GREATEST_VA_ARGS +#define RUN_TESTp GREATEST_RUN_TESTp +#endif + +#if GREATEST_USE_LONGJMP +#define ASSERT_OR_LONGJMP GREATEST_ASSERT_OR_LONGJMP +#define ASSERT_OR_LONGJMPm GREATEST_ASSERT_OR_LONGJMPm +#define FAIL_WITH_LONGJMP GREATEST_FAIL_WITH_LONGJMP +#define FAIL_WITH_LONGJMPm GREATEST_FAIL_WITH_LONGJMPm +#endif + +#endif /* USE_ABBREVS */ + +#if defined(__cplusplus) && !defined(GREATEST_NO_EXTERN_CPLUSPLUS) +} +#endif + +#endif