
1. a true wear-leveling kv fs for norflash, especially optimize for some onchip norflash with "write once on one single write unit" like stm32l4, a true "no earse before write" flash algorithm. 2. an "as less as possible" gc strategy, do best to save norflash's life. 3. full "power down protection" support 4. see "examples" of kv, project in "TencentOS_tiny_EVB_MX_Plus", with onchip flash and qspiflash sample.
599 lines
14 KiB
C
599 lines
14 KiB
C
/*----------------------------------------------------------------------------
|
|
* Tencent is pleased to support the open source community by making TencentOS
|
|
* available.
|
|
*
|
|
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
|
|
* If you have downloaded a copy of the TencentOS binary from Tencent, please
|
|
* note that the TencentOS binary is licensed under the BSD 3-Clause License.
|
|
*
|
|
* If you have downloaded a copy of the TencentOS source code from Tencent,
|
|
* please note that TencentOS source code is licensed under the BSD 3-Clause
|
|
* License, except for the third-party components listed below which are
|
|
* subject to different license terms. Your integration of TencentOS into your
|
|
* own projects may require compliance with the BSD 3-Clause License, as well
|
|
* as the other licenses applicable to the third-party components included
|
|
* within TencentOS.
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
#include <tos.h>
|
|
|
|
__STATIC_INLINE__ void task_reset(k_task_t *task)
|
|
{
|
|
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
|
|
knl_object_deinit(&task->knl_obj);
|
|
#endif
|
|
|
|
#if TOS_CFG_TASK_DYNAMIC_CREATE_EN > 0u
|
|
knl_object_alloc_reset(&task->knl_obj);
|
|
|
|
tos_list_init(&task->dead_list);
|
|
#endif
|
|
tos_list_init(&task->stat_list);
|
|
tos_list_init(&task->tick_list);
|
|
tos_list_init(&task->pend_list);
|
|
|
|
#if TOS_CFG_MUTEX_EN > 0u
|
|
tos_list_init(&task->mutex_own_list);
|
|
task->prio_pending = K_TASK_PRIO_INVALID;
|
|
#endif
|
|
|
|
task->pend_state = PEND_STATE_NONE;
|
|
task->pending_obj = (pend_obj_t *)K_NULL;
|
|
|
|
#if TOS_CFG_MESSAGE_QUEUE_EN > 0u
|
|
task->msg = K_NULL;
|
|
#endif
|
|
|
|
#if TOS_CFG_MAIL_QUEUE_EN > 0u
|
|
task->mail = K_NULL;
|
|
task->mail_size = 0;
|
|
#endif
|
|
|
|
}
|
|
|
|
__STATIC__ void task_exit(void)
|
|
{
|
|
tos_task_destroy(K_NULL);
|
|
}
|
|
|
|
#if TOS_CFG_MUTEX_EN > 0u
|
|
__STATIC__ k_prio_t task_highest_pending_prio_get(k_task_t *task)
|
|
{
|
|
k_list_t *curr;
|
|
k_mutex_t *mutex;
|
|
k_prio_t prio, highest_prio_pending = K_TASK_PRIO_INVALID;
|
|
|
|
TOS_LIST_FOR_EACH(curr, &task->mutex_own_list) {
|
|
mutex = TOS_LIST_ENTRY(curr, k_mutex_t, owner_anchor);
|
|
prio = pend_highest_pending_prio_get(&mutex->pend_obj);
|
|
if (prio < highest_prio_pending) {
|
|
highest_prio_pending = prio;
|
|
}
|
|
}
|
|
return highest_prio_pending;
|
|
}
|
|
|
|
__STATIC__ void task_mutex_release(k_task_t *task)
|
|
{
|
|
k_list_t *curr, *next;
|
|
|
|
TOS_LIST_FOR_EACH_SAFE(curr, next, &task->mutex_own_list) {
|
|
mutex_release(TOS_LIST_ENTRY(curr, k_mutex_t, owner_anchor));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
__API__ k_err_t tos_task_create(k_task_t *task,
|
|
char *name,
|
|
k_task_entry_t entry,
|
|
void *arg,
|
|
k_prio_t prio,
|
|
k_stack_t *stk_base,
|
|
size_t stk_size,
|
|
k_timeslice_t timeslice)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
|
|
TOS_IN_IRQ_CHECK();
|
|
|
|
TOS_PTR_SANITY_CHECK(task);
|
|
TOS_PTR_SANITY_CHECK(entry);
|
|
TOS_PTR_SANITY_CHECK(stk_base);
|
|
|
|
if (unlikely(stk_size < sizeof(cpu_context_t))) {
|
|
return K_ERR_TASK_STK_SIZE_INVALID;
|
|
}
|
|
|
|
if (unlikely(prio == K_TASK_PRIO_IDLE && !knl_is_idle(task))) {
|
|
return K_ERR_TASK_PRIO_INVALID;
|
|
}
|
|
|
|
if (unlikely(prio > K_TASK_PRIO_IDLE)) {
|
|
return K_ERR_TASK_PRIO_INVALID;
|
|
}
|
|
|
|
task_reset(task);
|
|
tos_list_add(&task->stat_list, &k_stat_list);
|
|
|
|
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
|
|
knl_object_init(&task->knl_obj, KNL_OBJ_TYPE_TASK);
|
|
#endif
|
|
|
|
#if TOS_CFG_TASK_DYNAMIC_CREATE_EN > 0u
|
|
knl_object_alloc_set_static(&task->knl_obj);
|
|
#endif
|
|
|
|
task->sp = cpu_task_stk_init((void *)entry, arg, (void *)task_exit, stk_base, stk_size);
|
|
task->entry = entry;
|
|
task->arg = arg;
|
|
task->name = name;
|
|
task->prio = prio;
|
|
task->stk_base = stk_base;
|
|
task->stk_size = stk_size;
|
|
|
|
#if TOS_CFG_ROUND_ROBIN_EN > 0u
|
|
task->timeslice_reload = timeslice;
|
|
|
|
if (timeslice == (k_timeslice_t)0u) {
|
|
task->timeslice = k_robin_default_timeslice;
|
|
} else {
|
|
task->timeslice = timeslice;
|
|
}
|
|
#endif
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
task_state_set_ready(task);
|
|
readyqueue_add_tail(task);
|
|
TOS_CPU_INT_ENABLE();
|
|
|
|
if (tos_knl_is_running()) {
|
|
knl_sched();
|
|
}
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
__STATIC__ k_err_t task_do_destroy(k_task_t *task)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
|
|
if (knl_is_idle(task)) {
|
|
return K_ERR_TASK_DESTROY_IDLE;
|
|
}
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
|
|
#if TOS_CFG_MUTEX_EN > 0u
|
|
// when we die, wakeup all the people in this land.
|
|
if (!tos_list_empty(&task->mutex_own_list)) {
|
|
task_mutex_release(task);
|
|
}
|
|
#endif
|
|
|
|
if (task_state_is_ready(task)) { // that's simple, good kid
|
|
readyqueue_remove(task);
|
|
}
|
|
if (task_state_is_sleeping(task)) {
|
|
tick_list_remove(task);
|
|
}
|
|
if (task_state_is_pending(task)) {
|
|
pend_list_remove(task);
|
|
}
|
|
|
|
tos_list_del(&task->stat_list);
|
|
task_reset(task);
|
|
|
|
task_state_set_deleted(task);
|
|
|
|
TOS_CPU_INT_ENABLE();
|
|
knl_sched();
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
__API__ k_err_t tos_task_destroy(k_task_t *task)
|
|
{
|
|
TOS_IN_IRQ_CHECK();
|
|
|
|
if (unlikely(!task)) {
|
|
task = k_curr_task;
|
|
}
|
|
|
|
TOS_OBJ_VERIFY(task, KNL_OBJ_TYPE_TASK);
|
|
|
|
if (knl_is_self(task) && knl_is_sched_locked()) {
|
|
return K_ERR_SCHED_LOCKED;
|
|
}
|
|
|
|
#if TOS_CFG_TASK_DYNAMIC_CREATE_EN
|
|
if (!knl_object_alloc_is_static(&task->knl_obj)) {
|
|
return K_ERR_OBJ_INVALID_ALLOC_TYPE;
|
|
}
|
|
#endif
|
|
|
|
return task_do_destroy(task);
|
|
}
|
|
|
|
#if TOS_CFG_TASK_DYNAMIC_CREATE_EN > 0u
|
|
|
|
__STATIC__ void task_free(k_task_t *task)
|
|
{
|
|
tos_mmheap_free(task->stk_base);
|
|
tos_mmheap_free(task);
|
|
}
|
|
|
|
__KERNEL__ void task_free_all(void)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
k_task_t *task;
|
|
k_list_t *curr, *next;
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
|
|
TOS_LIST_FOR_EACH_SAFE(curr, next, &k_dead_task_list) {
|
|
task = TOS_LIST_ENTRY(curr, k_task_t, dead_list);
|
|
tos_list_del(&task->dead_list);
|
|
task_free(task);
|
|
}
|
|
|
|
TOS_CPU_INT_ENABLE();
|
|
}
|
|
|
|
__API__ k_err_t tos_task_create_dyn(k_task_t **task,
|
|
char *name,
|
|
k_task_entry_t entry,
|
|
void *arg,
|
|
k_prio_t prio,
|
|
size_t stk_size,
|
|
k_timeslice_t timeslice)
|
|
{
|
|
k_err_t err;
|
|
k_task_t *the_task;
|
|
k_stack_t *stk_base;
|
|
|
|
TOS_IN_IRQ_CHECK();
|
|
|
|
TOS_PTR_SANITY_CHECK(task);
|
|
TOS_PTR_SANITY_CHECK(entry);
|
|
|
|
if (unlikely(stk_size < sizeof(cpu_context_t))) {
|
|
return K_ERR_TASK_STK_SIZE_INVALID;
|
|
}
|
|
|
|
if (unlikely(prio == K_TASK_PRIO_IDLE)) {
|
|
return K_ERR_TASK_PRIO_INVALID;
|
|
}
|
|
|
|
if (unlikely(prio > K_TASK_PRIO_IDLE)) {
|
|
return K_ERR_TASK_PRIO_INVALID;
|
|
}
|
|
|
|
the_task = tos_mmheap_alloc(sizeof(k_task_t));
|
|
if (!the_task) {
|
|
return K_ERR_TASK_OUT_OF_MEMORY;
|
|
}
|
|
|
|
stk_base = tos_mmheap_aligned_alloc(stk_size, sizeof(cpu_addr_t));
|
|
if (!stk_base) {
|
|
tos_mmheap_free(the_task);
|
|
return K_ERR_TASK_OUT_OF_MEMORY;
|
|
}
|
|
|
|
the_task->stk_base = stk_base;
|
|
err = tos_task_create(the_task, name, entry, arg, prio, stk_base, stk_size, timeslice);
|
|
if (err != K_ERR_NONE) {
|
|
task_free(the_task);
|
|
return err;
|
|
}
|
|
|
|
knl_object_alloc_set_dynamic(&the_task->knl_obj);
|
|
|
|
*task = the_task;
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
__API__ k_err_t tos_task_destroy_dyn(k_task_t *task)
|
|
{
|
|
k_err_t err;
|
|
|
|
TOS_IN_IRQ_CHECK();
|
|
|
|
if (unlikely(!task)) {
|
|
task = k_curr_task;
|
|
}
|
|
|
|
TOS_OBJ_VERIFY(task, KNL_OBJ_TYPE_TASK);
|
|
|
|
if (knl_is_self(task) && knl_is_sched_locked()) {
|
|
return K_ERR_SCHED_LOCKED;
|
|
}
|
|
|
|
if (!knl_object_alloc_is_dynamic(&task->knl_obj)) {
|
|
return K_ERR_OBJ_INVALID_ALLOC_TYPE;
|
|
}
|
|
|
|
tos_knl_sched_lock();
|
|
|
|
err = task_do_destroy(task);
|
|
if (err != K_ERR_NONE) {
|
|
tos_knl_sched_unlock();
|
|
return err;
|
|
}
|
|
|
|
if (knl_is_self(task)) { // we are destroying ourself
|
|
// in this situation, we cannot just free ourself's task stack because we are using it
|
|
// we count on the idle task to free the memory
|
|
tos_list_add(&task->dead_list, &k_dead_task_list);
|
|
} else {
|
|
task_free(task);
|
|
}
|
|
|
|
tos_knl_sched_unlock();
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
#endif
|
|
|
|
__API__ void tos_task_yield(void)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
|
|
if (knl_is_inirq()) {
|
|
return;
|
|
}
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
|
|
readyqueue_remove(k_curr_task);
|
|
readyqueue_add_tail(k_curr_task);
|
|
|
|
TOS_CPU_INT_ENABLE();
|
|
knl_sched();
|
|
}
|
|
|
|
__API__ k_err_t tos_task_prio_change(k_task_t *task, k_prio_t prio_new)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
#if TOS_CFG_MUTEX_EN > 0u
|
|
k_prio_t highest_pending_prio;
|
|
#endif
|
|
|
|
TOS_PTR_SANITY_CHECK(task);
|
|
TOS_OBJ_VERIFY(task, KNL_OBJ_TYPE_TASK);
|
|
TOS_IN_IRQ_CHECK();
|
|
|
|
if (unlikely(prio_new >= K_TASK_PRIO_IDLE)) {
|
|
return K_ERR_TASK_PRIO_INVALID;
|
|
}
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
|
|
if (task->prio == prio_new) { // just kidding
|
|
TOS_CPU_INT_ENABLE();
|
|
knl_sched();
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
#if TOS_CFG_MUTEX_EN > 0u
|
|
if (!tos_list_empty(&task->mutex_own_list)) {
|
|
highest_pending_prio = task_highest_pending_prio_get(task);
|
|
if (prio_new > highest_pending_prio) {
|
|
task->prio_pending = prio_new;
|
|
prio_new = highest_pending_prio;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (task_state_is_pending(task)) {
|
|
task->prio = prio_new;
|
|
pend_list_adjust(task);
|
|
} else if (task_state_is_sleeping(task)) {
|
|
task->prio = prio_new;
|
|
} else if (task_state_is_ready(task)) { // good kid
|
|
readyqueue_remove(task);
|
|
|
|
/* ATTENTION:
|
|
must do the prio assignment after readyqueue_remove
|
|
otherwise the k_rdyq.highest_prio refresh in readyqueue_remove will be wrong.
|
|
*/
|
|
task->prio = prio_new;
|
|
if (knl_is_self(task)) {
|
|
readyqueue_add_head(task);
|
|
} else {
|
|
readyqueue_add_tail(task);
|
|
}
|
|
}
|
|
|
|
TOS_CPU_INT_ENABLE();
|
|
knl_sched();
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
__API__ k_err_t tos_task_suspend(k_task_t *task)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
|
|
if (unlikely(!task)) {
|
|
task = k_curr_task;
|
|
}
|
|
|
|
TOS_OBJ_VERIFY(task, KNL_OBJ_TYPE_TASK);
|
|
|
|
if (knl_is_idle(task)) {
|
|
return K_ERR_TASK_SUSPEND_IDLE;
|
|
}
|
|
|
|
if (unlikely(knl_is_self(task)) && knl_is_sched_locked()) { // if not you, who?
|
|
return K_ERR_SCHED_LOCKED;
|
|
}
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
|
|
if (task_state_is_ready(task)) { // kill the good kid
|
|
readyqueue_remove(task);
|
|
}
|
|
task_state_set_suspended(task);
|
|
|
|
TOS_CPU_INT_ENABLE();
|
|
knl_sched();
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
__API__ k_err_t tos_task_resume(k_task_t *task)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
|
|
TOS_PTR_SANITY_CHECK(task);
|
|
TOS_OBJ_VERIFY(task, KNL_OBJ_TYPE_TASK);
|
|
|
|
if (unlikely(knl_is_self(task))) {
|
|
return K_ERR_TASK_RESUME_SELF;
|
|
}
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
|
|
if (!task_state_is_suspended(task)) {
|
|
TOS_CPU_INT_ENABLE();
|
|
knl_sched();
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
task_state_reset_suspended(task);
|
|
if (task_state_is_ready(task)) { // we are good kid now
|
|
readyqueue_add(task);
|
|
}
|
|
|
|
TOS_CPU_INT_ENABLE();
|
|
knl_sched();
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
__API__ k_err_t tos_task_delay(k_tick_t delay)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
|
|
TOS_IN_IRQ_CHECK();
|
|
|
|
if (knl_is_sched_locked()) {
|
|
return K_ERR_SCHED_LOCKED;
|
|
}
|
|
|
|
if (unlikely(delay == (k_tick_t)0u)) {
|
|
tos_task_yield();
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
if (unlikely(delay == TOS_TIME_FOREVER)) {
|
|
// if you wanna delay your task forever, why don't just suspend?
|
|
return K_ERR_DELAY_FOREVER;
|
|
}
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
|
|
tick_list_add(k_curr_task, delay);
|
|
readyqueue_remove(k_curr_task);
|
|
|
|
TOS_CPU_INT_ENABLE();
|
|
knl_sched();
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
__API__ k_err_t tos_task_delay_abort(k_task_t *task)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
|
|
TOS_PTR_SANITY_CHECK(task);
|
|
TOS_OBJ_VERIFY(task, KNL_OBJ_TYPE_TASK);
|
|
TOS_IN_IRQ_CHECK();
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
|
|
if (knl_is_self(task) || !task_state_is_sleeping(task)) {
|
|
TOS_CPU_INT_ENABLE();
|
|
return K_ERR_TASK_NOT_DELAY;
|
|
}
|
|
|
|
if (task_state_is_suspended(task)) {
|
|
TOS_CPU_INT_ENABLE();
|
|
return K_ERR_TASK_SUSPENDED;
|
|
}
|
|
|
|
tick_list_remove(task);
|
|
readyqueue_add(task);
|
|
|
|
TOS_CPU_INT_ENABLE();
|
|
knl_sched();
|
|
|
|
return K_ERR_NONE;
|
|
}
|
|
|
|
__API__ k_task_t *tos_task_curr_task_get(void)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
k_task_t *curr_task = K_NULL;
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
if (likely(tos_knl_is_running())) {
|
|
curr_task = k_curr_task;
|
|
}
|
|
TOS_CPU_INT_ENABLE();
|
|
|
|
return curr_task;
|
|
}
|
|
|
|
__API__ void tos_task_walkthru(k_task_walker_t walker)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
k_task_t *task;
|
|
k_list_t *curr;
|
|
|
|
if (!walker) {
|
|
return;
|
|
}
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
|
|
TOS_LIST_FOR_EACH(curr, &k_stat_list) {
|
|
task = TOS_LIST_ENTRY(curr, k_task_t, stat_list);
|
|
walker(task);
|
|
}
|
|
|
|
TOS_CPU_INT_ENABLE();
|
|
}
|
|
|
|
__DEBUG__ void tos_task_info_display(void)
|
|
{
|
|
tos_task_walkthru(task_default_walker);
|
|
}
|
|
|
|
#if TOS_CFG_TASK_STACK_DRAUGHT_DEPTH_DETACT_EN > 0u
|
|
|
|
__API__ k_err_t tos_task_stack_draught_depth(k_task_t *task, int *depth)
|
|
{
|
|
TOS_CPU_CPSR_ALLOC();
|
|
k_err_t rc;
|
|
|
|
TOS_PTR_SANITY_CHECK(depth);
|
|
|
|
if (unlikely(!task)) {
|
|
task = k_curr_task;
|
|
}
|
|
|
|
TOS_OBJ_VERIFY(task, KNL_OBJ_TYPE_TASK);
|
|
|
|
TOS_CPU_INT_DISABLE();
|
|
rc = cpu_task_stack_draught_depth(task->stk_base, task->stk_size, depth);
|
|
TOS_CPU_INT_ENABLE();
|
|
|
|
return rc;
|
|
}
|
|
|
|
#endif
|
|
|