www.pudn.com > r&s1.10°æ±¾£«Îĵµ.rar > task.c


/* 
=============================================================================== 
| Copyright (C) 2004 RuanHaiShen, All rights reserved. 
| SUMMARY:  
|   Task implementation. 
| 
| DESCRIPTION: 
|   See http://www.01s.org for documentation, latest information, license  
|   and contact details. 
|   email:ruanhaishen@01s.org 
=============================================================================*/ 
/*===========================================================================*/ 
#include "arch/arch.h" 
#include "inc/queue.h" 
#include "inc/kernel.h" 
#include "inc/memory.h" 
#include "inc/ipc.h" 
#include "inc/kapi.h" 
 
 
task_t                  _tasks[CFG_MAX_TASKS]; 
static byte             _idle_task_stack[CFG_IDLE_STACKSZ]; 
 
#if ARCH_STACK_GROW == UPWARDS 
# define IDLE_STK_TOP   &_idle_task_stack[0] 
#else 
# define IDLE_STK_TOP   &_idle_task_stack[CFG_IDLE_STACKSZ - 1] 
#endif 
 
#if CFG_TASK_INFO_EN > 0 
static u32              _idle_counter; 
static info_t           _task_info; 
static byte             _info_task_stack[CFG_INFO_STACKSZ]; 
 
#if ARCH_STACK_GROW == UPWARDS 
# define STATUS_STK_TOP &_info_task_stack[0] 
#else 
# define STATUS_STK_TOP &_info_task_stack[CFG_INFO_STACKSZ - 1] 
#endif 
#endif 
 
#if CFG_PRIO_MODE > 1 
# define __nprio_init(prio) \ 
do { \ 
    _tasks[prio].prio.normal = prio; \ 
    _tasks[prio].lock = 0; \ 
} while (0) 
#else 
# define __nprio_init(prio)    __MPT 
#endif 
 
#if CFG_IPC_EN > 0 
# define __pend_q_init(prio)   _tasks[prio].pend_q = NULL 
#else 
# define __pend_q_init(prio)   __MPT 
#endif 
 
#define __tcb_init(prio, stack_base) \ 
do { \ 
    _tasks[prio].stack       = stack_base; \ 
    _tasks[prio].delay       = 0; \ 
    __nprio_init(prio); \ 
    __pend_q_init(prio); \ 
} while(0) 
 
#define __tcb_free(prio) \ 
do { \ 
    _tasks[prio].delay  = 0; \ 
    _tasks[prio].state  = STATE_NONE; \ 
} while(0) 
 
 
void task_lock(void) 
{ 
    CRITICAL_ENTER; 
    if (_sched_lock < 0xff) { 
        _sched_lock++; 
    } 
    CRITICAL_EXIT; 
} 
 
void task_unlock(void) 
{ 
    CRITICAL_ENTER; 
    if (_sched_lock > 0x00) { 
        _sched_lock--; 
    } 
    CRITICAL_EXIT; 
 
    __schedule(); 
} 
 
 
err_t __task_create( 
    u8          prio,                   /* task priority                */ 
    entry_t     entry,                  /* entry point function         */ 
    arg_t       entry_arg,              /* entry data                   */ 
    sp_t        stack_base              /* stack base                   */ 
) 
{ 
    u8* pstk; 
 
    __ASSERT(entry != NULL); 
    __ASSERT(stack_base != NULL); 
 
#if TASK_IDLE_PRIO < 0xff && CFG_ARG_CHK > 1 
    if (prio > TASK_IDLE_PRIO) { 
        return EOUTRANGE; 
    } 
#elif TASK_IDLE_PRIO < 0xff 
    __ASSERT(prio <= TASK_IDLE_PRIO); 
#endif 
 
    CRITICAL_ENTER; 
    if (_tasks[prio].state != STATE_NONE) { 
        CRITICAL_EXIT; 
        return EEXIST; 
    } 
    _tasks[prio].state = STATE_ZOMBIE; 
    CRITICAL_EXIT; 
 
    hook_task_create(prio); 
    pstk = __stack_init(entry, entry_arg, stack_base); 
    __tcb_init(prio, pstk); 
 
    CRITICAL_ENTER; 
    _tasks[prio].state = STATE_READY; 
    __ready_que_add(prio); 
    CRITICAL_EXIT; 
 
    __schedule(); 
 
    return EOK; 
} 
 
#if CFG_TASK_DEL_EN > 0 && CFG_PRIO_MODE < 2 
err_t task_delete(u8 prio) 
{    
#if CFG_ARG_CHK > 1 
    if (prio >= TASK_IDLE_PRIO && prio != SELF_PRIO) { 
        return EOUTRANGE; 
    } 
#else 
    __ASSERT(prio < TASK_IDLE_PRIO || prio == SELF_PRIO); 
#endif 
 
    CRITICAL_ENTER; 
    if (prio == SELF_PRIO) { 
        prio = _current_prio; 
    } 
    if (!(_tasks[prio].state & STATE_READY)) { 
        CRITICAL_EXIT; 
        return ENOTEXIST; 
    } 
 
#if CFG_IPC_EN > 0 
    if (_tasks[prio].pend_q != NULL) { 
        __queue_remove_f(_tasks[prio].pend_q, prio); 
    } else  
#endif 
    { __ready_que_remove(prio); } 
     
    _sched_lock++; 
    CRITICAL_EXIT; 
 
    __tcb_free(prio); 
    hook_task_delete(prio); 
     
    CRITICAL_ENTER; 
    if (prio == _current_prio) { 
        _sched_lock = 0; 
    } else { 
        _sched_lock--; 
    } 
    CRITICAL_EXIT; 
 
    __schedule(); 
 
    return EOK; 
} 
 
#elif CFG_TASK_DEL_EN > 0 
err_t task_delete(u8 prio) 
{ 
    register u8 cprio; 
     
#if CFG_ARG_CHK > 1 
    if (prio >= TASK_IDLE_PRIO && prio != SELF_PRIO) { 
        return EOUTRANGE; 
    } 
#else 
    __ASSERT(prio < TASK_IDLE_PRIO || prio == SELF_PRIO); 
#endif 
 
    CRITICAL_ENTER; 
    if (prio == SELF_PRIO) { 
        prio = _tasks[_current_prio].prio.normal; 
    } else if (!__prio_if_normal(prio)) { 
        CRITICAL_EXIT; 
        return ENOTEXIST; 
    } 
 
    cprio = _tasks[prio].prio.current; 
    __nprio_lock(cprio); /*prevent normal-prio being changed*/ 
    CRITICAL_EXIT; 
 
    __dummy(); 
    CRITICAL_ENTER; 
    cprio = _tasks[prio].prio.current; 
 
#if CFG_IPC_EN > 0 
    if (_tasks[cprio].pend_q != NULL) { 
        __queue_remove_f(_tasks[cprio].pend_q, cprio); 
    } else  
#endif 
    { __ready_que_remove(cprio); } 
 
    _sched_lock++; 
    CRITICAL_EXIT; 
 
    __tcb_free(cprio); 
    hook_task_delete(prio); 
 
    CRITICAL_ENTER; 
    if (cprio == _current_prio) { 
        _sched_lock = 0; 
    } else { 
        _sched_lock--; 
    } 
    CRITICAL_EXIT; 
 
    __schedule(); 
 
    return EOK; 
} 
#endif 
 
 
#if CFG_PRIO_MODE == 1 
err_t task_change_prio(u8 old_prio, u8 new_prio) 
{     
#if CFG_ARG_CHK > 1 
    if ((old_prio >= TASK_IDLE_PRIO && old_prio != SELF_PRIO) || 
            new_prio >= TASK_IDLE_PRIO) { 
        return EOUTRANGE; 
    } 
#else 
    __ASSERT((old_prio < TASK_IDLE_PRIO || old_prio == SELF_PRIO) && 
            new_prio < TASK_IDLE_PRIO); 
#endif 
 
    CRITICAL_ENTER; 
    if (_tasks[new_prio].state != STATE_NONE) { 
        CRITICAL_EXIT; 
        return EEXIST; 
    } 
    _tasks[new_prio].state = STATE_ZOMBIE; 
    CRITICAL_EXIT; 
 
    __dummy(); 
    CRITICAL_ENTER; 
    if (old_prio == SELF_PRIO) { 
        old_prio = _current_prio; 
    } 
    if (!(_tasks[old_prio].state & STATE_READY)) { 
        _tasks[new_prio].state &= ~STATE_ZOMBIE; 
        CRITICAL_EXIT; 
        return ENOTEXIST; 
    } 
     
    __memcpy(&_tasks[new_prio], &_tasks[old_prio], sizeof(task_t)); 
    __tcb_free(old_prio); 
#if CFG_IPC_EN > 0 
    if (_tasks[new_prio].pend_q != NULL) { 
        __queue_remove_f(_tasks[new_prio].pend_q, old_prio); 
        __queue_add_f(_tasks[new_prio].pend_q, new_prio); 
    } else 
#endif 
    { __ready_que_move(old_prio, new_prio); } 
 
    if (_current_prio == old_prio) { 
        _current_prio = new_prio; 
    } 
    CRITICAL_EXIT; 
     
    __schedule(); 
    return EOK; 
} 
#endif 
 
 
#if CFG_PRIO_MODE == 2 
err_t task_change_prio(u8 old_prio, u8 new_prio) 
{ 
    register u8 cprio; 
     
#if CFG_ARG_CHK > 1 
    if ((old_prio >= TASK_IDLE_PRIO && old_prio != SELF_PRIO) || 
            new_prio >= TASK_IDLE_PRIO) { 
        return EOUTRANGE; 
    } 
#else 
    __ASSERT((old_prio < TASK_IDLE_PRIO || old_prio == SELF_PRIO) && 
            new_prio < TASK_IDLE_PRIO); 
#endif 
 
    CRITICAL_ENTER; 
    if (_tasks[new_prio].state != STATE_NONE) { 
        CRITICAL_EXIT; 
        return EEXIST; 
    } 
     
    if (old_prio == SELF_PRIO) { 
        cprio    = _current_prio; 
        old_prio = _tasks[cprio].prio.normal; 
    } else if (!__prio_if_normal(old_prio)) { 
        CRITICAL_EXIT; 
        return ENOTEXIST; 
    } else { 
        cprio = _tasks[old_prio].prio.current; 
    } 
     
    if (_tasks[cprio].lock > 0) { 
        CRITICAL_EXIT; 
        return ELOCK; 
    } 
    __nprio_lock(cprio); 
    _tasks[new_prio].state = STATE_ZOMBIE; 
    CRITICAL_EXIT; 
 
    __dummy(); 
    CRITICAL_ENTER; 
    cprio = _tasks[old_prio].prio.current;     
    __nprio_unlock(cprio); 
    _tasks[cprio].prio.normal = new_prio; 
     
    __memcpy(&_tasks[new_prio], &_tasks[old_prio], sizeof(task_t)); 
    __tcb_free(old_prio); 
    if (cprio == old_prio) { 
#if CFG_IPC_EN > 0 
        if (_tasks[new_prio].pend_q != NULL) { 
            __queue_remove_f(_tasks[new_prio].pend_q, cprio); 
            __queue_add_f(_tasks[new_prio].pend_q, new_prio); 
        } else 
#endif 
        { __ready_que_move(cprio, new_prio); } 
    } 
     
    if (_current_prio == old_prio) { 
        _current_prio = new_prio; 
    } 
    CRITICAL_EXIT; 
     
    __schedule(); 
    return EOK; 
} 
 
 
err_t task_change_cprio(u8 prio, u8 new_cprio) 
{ 
    register u8 old_cprio; 
 
#if CFG_ARG_CHK > 1 
    if ((prio >= TASK_IDLE_PRIO && prio != SELF_PRIO) || 
            new_cprio >= TASK_IDLE_PRIO) { 
        return EOUTRANGE; 
    } 
#else 
    __ASSERT((prio < TASK_IDLE_PRIO || prio == SELF_PRIO) && 
            new_cprio < TASK_IDLE_PRIO); 
#endif 
 
    /*see if prio is available*/ 
    CRITICAL_ENTER; 
    if (prio == SELF_PRIO) { 
        old_cprio = _current_prio; 
        prio      = _tasks[old_cprio].prio.normal; 
    } else if (!__prio_if_normal(prio)) { 
        CRITICAL_EXIT; 
        return ENOTEXIST; 
    } else { 
        old_cprio = _tasks[prio].prio.current; 
    } 
    __nprio_lock(old_cprio); 
    CRITICAL_EXIT; 
 
    __dummy(); 
    CRITICAL_ENTER; 
    if (_tasks[new_cprio].state == STATE_NONE || 
            (_tasks[new_cprio].state == STATE_RESERVED && 
            prio == new_cprio)) { 
        _tasks[new_cprio].state |= STATE_ZOMBIE; 
    } else { 
        old_cprio = _tasks[prio].prio.current; 
        __nprio_unlock(old_cprio); 
        CRITICAL_EXIT; 
        return EINVAL; 
    } 
    CRITICAL_EXIT; 
 
    __dummy(); 
    CRITICAL_ENTER; 
    old_cprio = _tasks[prio].prio.current; /**/ 
    if (!(_tasks[prio].state & STATE_ZOMBIE) || 
            prio == new_cprio) { 
        __nprio_unlock(old_cprio); 
 
        __memcpy(&_tasks[new_cprio], &_tasks[old_cprio], sizeof(task_t)); 
        /*free the old current tcb*/ 
        __tcb_free(old_cprio); 
        if (prio != new_cprio) { 
            _tasks[prio].delay  = 0; 
            _tasks[prio].state  = STATE_RESERVED; 
            _tasks[prio].prio.current = new_cprio; 
        } 
#if CFG_IPC_EN > 0 
        if (_tasks[new_cprio].pend_q != NULL) { 
            __queue_remove_f(_tasks[new_cprio].pend_q, old_cprio); 
            __queue_add_f(_tasks[new_cprio].pend_q, new_cprio); 
        } else 
#endif 
        { __ready_que_move(old_cprio, new_cprio); } 
 
        if (_current_prio == old_cprio) { 
            _current_prio = new_cprio; 
        } 
        CRITICAL_EXIT; 
 
        __schedule(); 
        return EOK; 
    } else { 
        __nprio_unlock(old_cprio); 
        _tasks[new_cprio].state &= ~STATE_ZOMBIE; 
        CRITICAL_EXIT; 
        return ENAVAIL; 
    } 
} 
#endif 
 
 
void task_sleep(tick_t ticks) 
{ 
    register u8 current; 
     
    if (ticks > 0) { 
        CRITICAL_ENTER; 
        current = _current_prio; 
        _tasks[current].delay = ticks; 
        __ready_que_remove(current); 
        CRITICAL_EXIT; 
         
        __schedule(); 
    } 
} 
 
 
#if CFG_PRIO_MODE < 2 
err_t task_wake(u8 prio) 
{ 
#if CFG_ARG_CHK > 1 
    if (prio >= TASK_IDLE_PRIO) { 
        return EOUTRANGE; 
    } 
#else 
    __ASSERT(prio < TASK_IDLE_PRIO); 
#endif 
 
    CRITICAL_ENTER; 
    if (_tasks[prio].delay != 0 && 
            !(_tasks[prio].state & STATE_SUSPEND)) { 
        _tasks[prio].delay = 0; 
        __ready_que_add(prio); 
        CRITICAL_EXIT; 
 
        __schedule(); 
        return EOK; 
    } else { 
        CRITICAL_EXIT; 
        return EUNKNOW; 
    } 
} 
 
#else 
err_t task_wake(u8 prio) 
{ 
    register u8 cprio; 
 
#if CFG_ARG_CHK > 1 
    if (prio >= TASK_IDLE_PRIO) { 
        return EOUTRANGE; 
    } 
#else 
    __ASSERT(prio < TASK_IDLE_PRIO); 
#endif 
 
    CRITICAL_ENTER; 
    if (!__prio_if_normal(prio)) { 
        CRITICAL_EXIT; 
        return ENOTEXIST; 
    } 
    cprio = _tasks[prio].prio.current; 
     
    if (_tasks[cprio].delay != 0 && 
            !(_tasks[cprio].state & STATE_SUSPEND)) { 
        _tasks[cprio].delay = 0; 
        __ready_que_add(cprio); 
        CRITICAL_EXIT; 
 
        __schedule(); 
        return EOK; 
    } else { 
        CRITICAL_EXIT; 
        return EUNKNOW; 
    } 
} 
#endif 
 
 
#if CFG_TASK_SUS_EN > 0 && CFG_PRIO_MODE < 2 
err_t task_suspend(u8 prio) 
{ 
#if CFG_ARG_CHK > 1 
    if (prio >= TASK_IDLE_PRIO && prio != SELF_PRIO) { 
        return EOUTRANGE; 
    } 
#else 
    __ASSERT(prio < TASK_IDLE_PRIO || prio == SELF_PRIO); 
#endif 
     
    CRITICAL_ENTER; 
    if (prio == SELF_PRIO) { 
        prio = _current_prio; 
    } 
 
    if (_tasks[prio].state & STATE_READY) { 
        _tasks[prio].state |= STATE_SUSPEND; 
        __ready_que_susremove(prio, _tasks[prio].state); 
        CRITICAL_EXIT; 
         
        __schedule(); 
        return EOK; 
    } else { 
        CRITICAL_EXIT; 
        return ENOTEXIST; 
    } 
} 
 
 
err_t task_resume(u8 prio) 
{ 
#if CFG_ARG_CHK > 1 
    if (prio >= TASK_IDLE_PRIO) { 
        return EOUTRANGE; 
    } 
#else 
    __ASSERT(prio < TASK_IDLE_PRIO); 
#endif 
 
    CRITICAL_ENTER; 
    if (!(_tasks[prio].state & STATE_READY)) { 
        CRITICAL_EXIT; 
        return ENOTEXIST; 
    } 
     
    _tasks[prio].state &= ~STATE_SUSPEND; 
    if ((_tasks[prio].state & STATE_PREREADY) || 
               (!(_tasks[prio].state & STATE_BLOCKED) && 
                _tasks[prio].delay == 0)) { 
        _tasks[prio].state &= ~STATE_PREREADY; 
        __ready_que_add(prio);    
    } 
    CRITICAL_EXIT; 
 
    __schedule(); 
    return EOK; 
} 
 
#elif CFG_TASK_SUS_EN > 0  
err_t task_suspend(u8 prio) 
{ 
    register u8 cprio; 
 
#if CFG_ARG_CHK > 1 
    if (prio >= TASK_IDLE_PRIO && prio != SELF_PRIO) { 
        return EOUTRANGE; 
    } 
#else 
    __ASSERT(prio < TASK_IDLE_PRIO || prio == SELF_PRIO); 
#endif 
     
    CRITICAL_ENTER; 
    if (prio == SELF_PRIO) { 
        cprio = _current_prio; 
        prio  = _tasks[cprio].prio.normal; 
    } else if (!__prio_if_normal(prio)) { 
        CRITICAL_EXIT; 
        return ENOTEXIST; 
    } else { 
        cprio = _tasks[prio].prio.current; 
    } 
 
    _tasks[cprio].state |= STATE_SUSPEND; 
    __ready_que_susremove(cprio, _tasks[cprio].state); 
    CRITICAL_EXIT; 
 
    __schedule(); 
    return EOK; 
} 
 
 
err_t task_resume(u8 prio) 
{ 
    register u8 cprio; 
 
#if CFG_ARG_CHK > 1 
    if (prio >= TASK_IDLE_PRIO) { 
        return EOUTRANGE; 
    } 
#else 
    __ASSERT(prio < TASK_IDLE_PRIO); 
#endif 
 
    CRITICAL_ENTER; 
    if (!__prio_if_normal(prio)) { 
        CRITICAL_EXIT; 
        return ENOTEXIST; 
    } 
    cprio = _tasks[prio].prio.current; 
     
    _tasks[cprio].state &= ~STATE_SUSPEND; 
    if ((_tasks[cprio].state & STATE_PREREADY) || 
               (!(_tasks[cprio].state & STATE_BLOCKED) && 
                _tasks[cprio].delay == 0)) { 
        _tasks[cprio].state &= ~STATE_PREREADY; 
        __ready_que_add(cprio);    
    } 
    CRITICAL_EXIT; 
 
    __schedule(); 
    return EOK; 
} 
#endif 
 
void __idle_task(arg_t arg) 
{ 
    arg = arg; 
 
    while (true) { 
        hook_idle_task(); 
 
#if CFG_TASK_INFO_EN>0 
        CRITICAL_ENTER; 
        _idle_counter++; 
        CRITICAL_EXIT; 
#endif 
    } 
} 
 
#if CFG_TASK_INFO_EN > 0 
u32 __get_max_idlecnt(void) 
{ 
    u32 idle_cnt_max; 
 
    task_sleep(2); 
 
    CRITICAL_ENTER; 
    _idle_counter = 0L; 
    CRITICAL_EXIT; 
 
    task_sleep(TICKS_PER_SEC); 
 
    CRITICAL_ENTER; 
    idle_cnt_max = _idle_counter; 
    CRITICAL_EXIT; 
 
    return idle_cnt_max; 
} 
 
void __usrtask_suspend(void) 
{ 
    u8 prio; 
 
    for (prio = 0; prio != TASK_INFO_PRIO; prio++) { 
        task_suspend(prio); 
    } 
} 
 
void __usrtask_resume(void) 
{ 
    u8 prio; 
 
    for (prio = 0; prio != TASK_INFO_PRIO; prio++) { 
        task_resume(prio); 
    } 
} 
 
void __info_task(arg_t arg) 
{ 
    u32 idle_cnt; 
    u32 idle_cnt_max; 
    s8 usage; 
 
    arg = arg; 
 
    idle_cnt_max = __get_max_idlecnt(); 
    __usrtask_resume(); 
 
    __ASSERT(idle_cnt_max != 0); 
 
    for (;;) { 
        hook_info_task(); 
         
        task_lock(); 
        idle_cnt = _idle_counter; 
        _idle_counter = 0L; 
 
        usage = (s8)(100L - 100L * idle_cnt / idle_cnt_max); 
        if (usage >= 0) { 
            _task_info.cpu_usage = usage; 
        } else { 
            _task_info.cpu_usage = 0; 
        } 
        task_unlock(); 
 
        task_sleep(TICKS_PER_SEC); 
    } 
} 
 
err_t task_getinfo(info_t* info) 
{ 
    register u8 prio; 
 
    __ASSERT(info != NULL); 
 
    prio = info->prio; 
 
#if TASK_IDLE_PRIO < 0xff && CFG_ARG_CHK > 1 
    if (prio > TASK_IDLE_PRIO) { 
        return EOUTRANGE; 
    } 
#elif TASK_IDLE_PRIO < 0xff 
    __ASSERT(prio <= TASK_IDLE_PRIO); 
#endif 
 
    task_lock(); 
    info->cpu_usage = _task_info.cpu_usage; 
    info->state = _tasks[prio].state; 
    info->delay = _tasks[prio].delay; 
    info->stack = _tasks[prio].stack; 
    task_unlock(); 
     
    return EOK; 
} 
#endif 
 
void __task_init(void) 
{ 
    u8 prio; 
 
    prio = TASK_IDLE_PRIO + 1; 
    do { 
        __memclr(&_tasks[--prio], sizeof(task_t)); 
    } while (prio != 0); 
 
    task_create(TASK_IDLE_PRIO, 
                __idle_task, 
                (arg_t)0, 
                "__idle", 
                (sp_t)IDLE_STK_TOP, 
                CFG_IDLE_STACKSZ, 
                0); 
 
#if CFG_TASK_INFO_EN>0 
    task_create(TASK_INFO_PRIO, 
                __info_task, 
                (arg_t)0, 
                "__info", 
                (sp_t)STATUS_STK_TOP, 
                CFG_INFO_STACKSZ, 
                0); 
#endif 
 
} 
 
 
/*===========================================================================*/